Skip to content

Commit

Permalink
Add Support for Floating Point Textures on Mobile Platforms. (MonoGam…
Browse files Browse the repository at this point in the history
…e#6264)

* Add Support for Floating Point Textures on Mobile Platforms.

We currently restrict Android and iOS to NEVER support
`SurfaceFormat` values of `SurfaceFormat.Single` etc.
Even if the device supports it either directly or via
and OpenGL extension.

This commit reworks the GraphicsCapabilities and GraphicsExtensions
to use the queried extensions to figure out if we support the
`SurfaceFormat` requested. If it doesn't then we throw a
`NotSupportedException` as we current do. If we do however then
we will run as normal.

Also fixed up a few hardcoded `PixelFormat` values so they have
proper enumerations.

* Fixed Desktop Detection

* Added a decent error message

* add more extensions

* add more extensions

* Updated to check for Normalized formats

* GLES 3 supports them all

* Fixed typo

* Updated GL checks

* Allow Instance Rendering on GLES

* Allow Instancing on mobile GL plaforms if it supports it

* fix error

* Fixed up comments

* Updated based on feedback
  • Loading branch information
dellis1972 authored and Jjagg committed Apr 25, 2018
1 parent 84536a6 commit b814336
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 80 deletions.
3 changes: 3 additions & 0 deletions MonoGame.Framework/Graphics/GraphicsCapabilities.DirectX.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ private void PlatformInitialize(GraphicsDevice device)
SupportsTextureArrays = device.GraphicsProfile == GraphicsProfile.HiDef;
SupportsDepthClamp = device.GraphicsProfile == GraphicsProfile.HiDef;
SupportsVertexTextures = device.GraphicsProfile == GraphicsProfile.HiDef;
SupportsFloatTextures = true;
SupportsHalfFloatTextures = true;
SupportsNormalized = true;

SupportsInstancing = true;

Expand Down
10 changes: 6 additions & 4 deletions MonoGame.Framework/Graphics/GraphicsCapabilities.OpenGL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,14 @@ private void PlatformInitialize(GraphicsDevice device)
// sRGB
#if GLES
SupportsSRgb = GL.Extensions.Contains("GL_EXT_sRGB");
SupportsFloatTextures = GL.BoundApi == GL.RenderApi.ES && (device.glMajorVersion >= 3 || GL.Extensions.Contains("GL_EXT_color_buffer_float"));
SupportsHalfFloatTextures = GL.BoundApi == GL.RenderApi.ES && (device.glMajorVersion >= 3 || GL.Extensions.Contains("GL_EXT_color_buffer_half_float"));
SupportsNormalized = GL.BoundApi == GL.RenderApi.ES && (device.glMajorVersion >= 3 && GL.Extensions.Contains("GL_EXT_texture_norm16"));
#else
SupportsSRgb = GL.Extensions.Contains("GL_EXT_texture_sRGB") && GL.Extensions.Contains("GL_EXT_framebuffer_sRGB");
SupportsFloatTextures = GL.BoundApi == GL.RenderApi.GL && (device.glMajorVersion >= 3 || GL.Extensions.Contains("GL_ARB_texture_float"));
SupportsHalfFloatTextures = GL.BoundApi == GL.RenderApi.GL && (device.glMajorVersion >= 3 || GL.Extensions.Contains("GL_ARB_half_float_pixel"));;
SupportsNormalized = GL.BoundApi == GL.RenderApi.GL && (device.glMajorVersion >= 3 || GL.Extensions.Contains("GL_EXT_texture_norm16"));;
#endif

// TODO: Implement OpenGL support for texture arrays
Expand All @@ -107,11 +113,7 @@ private void PlatformInitialize(GraphicsDevice device)

GL.GetInteger((GetPName)GetParamName.MaxSamples, out _maxMultiSampleCount);

#if GLES
SupportsInstancing = false;
#else
SupportsInstancing = GL.VertexAttribDivisor != null;
#endif
}

}
Expand Down
30 changes: 26 additions & 4 deletions MonoGame.Framework/Graphics/GraphicsCapabilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ internal void Initialize(GraphicsDevice device)
/// </summary>
internal bool SupportsTextureFilterAnisotropic { get; private set; }

internal bool SupportsDepth24 { get; private set; }
internal bool SupportsDepth24 { get; private set; }

internal bool SupportsPackedDepthStencil { get; private set; }
internal bool SupportsPackedDepthStencil { get; private set; }

internal bool SupportsDepthNonLinear { get; private set; }
internal bool SupportsDepthNonLinear { get; private set; }

/// <summary>
/// Gets the support for DXT1
Expand Down Expand Up @@ -69,13 +69,35 @@ internal void Initialize(GraphicsDevice device)
/// and texture sRGB are supported.
/// </summary>
internal bool SupportsSRgb { get; private set; }

internal bool SupportsTextureArrays { get; private set; }

internal bool SupportsDepthClamp { get; private set; }

internal bool SupportsVertexTextures { get; private set; }

/// <summary>
/// True, if the underlying platform supports floating point textures.
/// For Direct3D platforms this is always <code>true</code>.
/// For OpenGL Desktop platforms it is always <code>true</code>.
/// For OpenGL Mobile platforms it requires `GL_EXT_color_buffer_float`.
/// If the requested format is not supported an <code>NotSupportedException</code>
/// will be thrown.
/// </summary>
internal bool SupportsFloatTextures { get; private set; }

/// <summary>
/// True, if the underlying platform supports half floating point textures.
/// For Direct3D platforms this is always <code>true</code>.
/// For OpenGL Desktop platforms it is always <code>true</code>.
/// For OpenGL Mobile platforms it requires `GL_EXT_color_buffer_half_float`.
/// If the requested format is not supported an <code>NotSupportedException</code>
/// will be thrown.
/// </summary>
internal bool SupportsHalfFloatTextures { get; private set; }

internal bool SupportsNormalized { get; private set; }

/// <summary>
/// Gets the max texture anisotropy. This value typically lies
/// between 0 and 16, where 0 means anisotropic filtering is not
Expand Down
10 changes: 2 additions & 8 deletions MonoGame.Framework/Graphics/GraphicsDevice.OpenGL.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private void ApplyAttribs(Shader shader, int baseVertex)

// If instancing is not supported, but InstanceFrequency of the buffer is not zero, throw an exception
if (!GraphicsCapabilities.SupportsInstancing && vertexBufferBinding.InstanceFrequency > 0)
throw new PlatformNotSupportedException("Instanced geometry drawing requires at least OpenGL 3.2. Try upgrading your graphics drivers.");
throw new PlatformNotSupportedException("Instanced geometry drawing requires at least OpenGL 3.2 or GLES 3.2. Try upgrading your graphics drivers.");

foreach (var element in attrInfo.Elements)
{
Expand All @@ -211,11 +211,9 @@ private void ApplyAttribs(Shader shader, int baseVertex)
vertexStride,
(IntPtr)(offset.ToInt64() + element.Offset));

#if !(GLES || MONOMAC)
// only set the divisor if instancing is supported
if (GraphicsCapabilities.SupportsInstancing)
GL.VertexAttribDivisor(element.AttributeLocation, vertexBufferBinding.InstanceFrequency);
#endif

GraphicsExtensions.CheckGLError();
}
Expand Down Expand Up @@ -1173,11 +1171,8 @@ private void PlatformDrawUserIndexedPrimitives<T>(PrimitiveType primitiveType, T

private void PlatformDrawInstancedPrimitives(PrimitiveType primitiveType, int baseVertex, int startIndex, int primitiveCount, int instanceCount)
{
#if GLES || MONOMAC
throw new PlatformNotSupportedException("Instanced geometry drawing is not supported yet for GLES or MONOMAC.");
#else
if (!GraphicsCapabilities.SupportsInstancing)
throw new PlatformNotSupportedException("Instanced geometry drawing requires at least OpenGL 3.2. Try upgrading your graphics card drivers.");
throw new PlatformNotSupportedException("Instanced geometry drawing requires at least OpenGL 3.2 or GLES 3.2. Try upgrading your graphics card drivers.");
ApplyState(true);

var shortIndices = _indexBuffer.IndexElementSize == IndexElementSize.SixteenBits;
Expand All @@ -1196,7 +1191,6 @@ private void PlatformDrawInstancedPrimitives(PrimitiveType primitiveType, int ba
indexOffsetInBytes,
instanceCount);
GraphicsExtensions.CheckGLError();
#endif
}

private void PlatformGetBackBufferData<T>(Rectangle? rectangle, T[] data, int startIndex, int count) where T : struct
Expand Down
133 changes: 70 additions & 63 deletions MonoGame.Framework/Graphics/GraphicsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ internal static int GetSwapInterval(this PresentInterval interval)
}
}
#endif

const SurfaceFormat InvalidFormat = (SurfaceFormat)int.MaxValue;
internal static void GetGLFormat (this SurfaceFormat format,
GraphicsDevice graphicsDevice,
out PixelInternalFormat glInternalFormat,
Expand All @@ -508,7 +508,15 @@ internal static void GetGLFormat (this SurfaceFormat format,
glType = PixelType.UnsignedByte;

var supportsSRgb = graphicsDevice.GraphicsCapabilities.SupportsSRgb;

var supportsS3tc = graphicsDevice.GraphicsCapabilities.SupportsS3tc;
var supportsPvrtc = graphicsDevice.GraphicsCapabilities.SupportsPvrtc;
var supportsEtc1 = graphicsDevice.GraphicsCapabilities.SupportsEtc1;
var supportsAtitc = graphicsDevice.GraphicsCapabilities.SupportsAtitc;
var supportsFloat = graphicsDevice.GraphicsCapabilities.SupportsFloatTextures;
var supportsHalfFloat = graphicsDevice.GraphicsCapabilities.SupportsHalfFloatTextures;
var supportsNormalized = graphicsDevice.GraphicsCapabilities.SupportsNormalized;
var isGLES2 = GL.BoundApi == GL.RenderApi.ES && graphicsDevice.glMajorVersion == 2;

switch (format) {
case SurfaceFormat.Color:
glInternalFormat = PixelInternalFormat.Rgba;
Expand All @@ -518,7 +526,7 @@ internal static void GetGLFormat (this SurfaceFormat format,
case SurfaceFormat.ColorSRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Color;
glInternalFormat = (PixelInternalFormat) 0x8C40; // PixelInternalFormat.Srgb;
glInternalFormat = PixelInternalFormat.Srgb;
glFormat = PixelFormat.Rgba;
glType = PixelType.UnsignedByte;
break;
Expand Down Expand Up @@ -546,8 +554,9 @@ internal static void GetGLFormat (this SurfaceFormat format,
glFormat = PixelFormat.Luminance;
glType = PixelType.UnsignedByte;
break;
#if !IOS && !ANDROID && !ANGLE
case SurfaceFormat.Dxt1:
if (!supportsS3tc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbS3tcDxt1Ext;
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
Expand All @@ -558,10 +567,14 @@ internal static void GetGLFormat (this SurfaceFormat format,
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt1a:
if (!supportsS3tc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt1Ext;
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt3:
if (!supportsS3tc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt3Ext;
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
Expand All @@ -572,6 +585,8 @@ internal static void GetGLFormat (this SurfaceFormat format,
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt5:
if (!supportsS3tc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbaS3tcDxt5Ext;
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;
Expand All @@ -581,14 +596,24 @@ internal static void GetGLFormat (this SurfaceFormat format,
glInternalFormat = PixelInternalFormat.CompressedSrgbAlphaS3tcDxt5Ext;
glFormat = (PixelFormat)GLPixelFormat.CompressedTextureFormats;
break;

case SurfaceFormat.Single:
glInternalFormat = PixelInternalFormat.R32f;
glFormat = PixelFormat.Red;
glType = PixelType.Float;
break;
#if !IOS && !ANDROID && !ANGLE
case SurfaceFormat.Rgba1010102:
glInternalFormat = PixelInternalFormat.Rgb10A2ui;
glFormat = PixelFormat.Rgba;
glType = PixelType.UnsignedInt1010102;
break;
#endif
case SurfaceFormat.Single:
if (!supportsFloat)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.R32f;
glFormat = PixelFormat.Red;
glType = PixelType.Float;
break;

case SurfaceFormat.HalfVector2:
if (!supportsHalfFloat)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rg16f;
glFormat = PixelFormat.Rg;
glType = PixelType.HalfFloat;
Expand All @@ -597,131 +622,113 @@ internal static void GetGLFormat (this SurfaceFormat format,
// HdrBlendable implemented as HalfVector4 (see http://blogs.msdn.com/b/shawnhar/archive/2010/07/09/surfaceformat-hdrblendable.aspx)
case SurfaceFormat.HdrBlendable:
case SurfaceFormat.HalfVector4:
if (!supportsHalfFloat)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rgba16f;
glFormat = PixelFormat.Rgba;
glType = PixelType.HalfFloat;
break;

case SurfaceFormat.HalfSingle:
if (!supportsHalfFloat)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.R16f;
glFormat = PixelFormat.Red;
glType = PixelType.HalfFloat;
glType = isGLES2 ? PixelType.HalfFloatOES : PixelType.HalfFloat;
break;

case SurfaceFormat.Vector2:
if (!supportsFloat)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rg32f;
glFormat = PixelFormat.Rg;
glType = PixelType.Float;
break;

case SurfaceFormat.Vector4:
if (!supportsFloat)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rgba32f;
glFormat = PixelFormat.Rgba;
glType = PixelType.Float;
break;

case SurfaceFormat.NormalizedByte2:
if (!supportsNormalized)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rg8i;
glFormat = PixelFormat.Rg;
glType = PixelType.Byte;
break;

case SurfaceFormat.NormalizedByte4:
if (!supportsNormalized)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rgba8i;
glFormat = PixelFormat.Rgba;
glType = PixelType.Byte;
break;

case SurfaceFormat.Rg32:
if (!supportsNormalized)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rg16ui;
glFormat = PixelFormat.Rg;
glType = PixelType.UnsignedShort;
break;

case SurfaceFormat.Rgba64:
if (!supportsNormalized)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Rgba16;
glFormat = PixelFormat.Rgba;
glType = PixelType.UnsignedShort;
break;

case SurfaceFormat.Rgba1010102:
glInternalFormat = PixelInternalFormat.Rgb10A2ui;
glFormat = PixelFormat.Rgba;
glType = PixelType.UnsignedInt1010102;
break;
#endif

#if ANDROID
case SurfaceFormat.Dxt1:
// 0x83F0 is the RGB version, 0x83F1 is the RGBA version (1-bit alpha)
// XNA uses the RGB version.
glInternalFormat = (PixelInternalFormat)0x83F0;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt1SRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Dxt1;
glInternalFormat = (PixelInternalFormat)0x8C4C;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt1a:
// 0x83F0 is the RGB version, 0x83F1 is the RGBA version (1-bit alpha)
glInternalFormat = (PixelInternalFormat)0x83F1;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt3:
glInternalFormat = (PixelInternalFormat)0x83F2;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt3SRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Dxt3;
glInternalFormat = (PixelInternalFormat)0x8C4E;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt5:
glInternalFormat = (PixelInternalFormat)0x83F3;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.Dxt5SRgb:
if (!supportsSRgb)
goto case SurfaceFormat.Dxt5;
glInternalFormat = (PixelInternalFormat)0x8C4F;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.RgbaAtcExplicitAlpha:
if (!supportsAtitc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.AtcRgbaExplicitAlphaAmd;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.RgbaAtcInterpolatedAlpha:
if (!supportsAtitc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.AtcRgbaInterpolatedAlphaAmd;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.RgbEtc1:
glInternalFormat = (PixelInternalFormat)0x8D64; // GL_ETC1_RGB8_OES
if (!supportsEtc1)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.Etc1; // GL_ETC1_RGB8_OES
glFormat = PixelFormat.CompressedTextureFormats;
break;
#endif
#if IOS || ANDROID
case SurfaceFormat.RgbPvrtc2Bpp:
if (!supportsPvrtc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbPvrtc2Bppv1Img;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.RgbPvrtc4Bpp:
if (!supportsPvrtc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbPvrtc4Bppv1Img;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.RgbaPvrtc2Bpp:
if (!supportsPvrtc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbaPvrtc2Bppv1Img;
glFormat = PixelFormat.CompressedTextureFormats;
break;
case SurfaceFormat.RgbaPvrtc4Bpp:
if (!supportsPvrtc)
goto case InvalidFormat;
glInternalFormat = PixelInternalFormat.CompressedRgbaPvrtc4Bppv1Img;
glFormat = PixelFormat.CompressedTextureFormats;
break;
#endif
case InvalidFormat:
default:
throw new NotSupportedException();
throw new NotSupportedException(string.Format("The requested SurfaceFormat `{0}` is not supported.", format));
}
}

Expand Down
Loading

0 comments on commit b814336

Please sign in to comment.