From bb15bd20c07c368cd5092f63f8702527d96022bb Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 23 Apr 2021 22:38:11 +0100 Subject: [PATCH] Add support for 16 bit floating point textures. --- OpenRA.Game/Graphics/PlatformInterfaces.cs | 1 + OpenRA.Platforms.Default/OpenGL.cs | 2 ++ OpenRA.Platforms.Default/Texture.cs | 19 +++++++++++++++ .../ThreadedGraphicsContext.cs | 23 +++++++++++++++++++ 4 files changed, 45 insertions(+) diff --git a/OpenRA.Game/Graphics/PlatformInterfaces.cs b/OpenRA.Game/Graphics/PlatformInterfaces.cs index 3f30b9e7c6..5d8113d591 100644 --- a/OpenRA.Game/Graphics/PlatformInterfaces.cs +++ b/OpenRA.Game/Graphics/PlatformInterfaces.cs @@ -125,6 +125,7 @@ namespace OpenRA { void SetData(uint[,] colors); void SetData(byte[] colors, int width, int height); + void SetFloatData(float[] data, int width, int height); byte[] GetData(); Size Size { get; } TextureScaleFilter ScaleFilter { get; set; } diff --git a/OpenRA.Platforms.Default/OpenGL.cs b/OpenRA.Platforms.Default/OpenGL.cs index fc7adbee53..db6062f60b 100644 --- a/OpenRA.Platforms.Default/OpenGL.cs +++ b/OpenRA.Platforms.Default/OpenGL.cs @@ -144,6 +144,8 @@ namespace OpenRA.Platforms.Default public const int GL_INFO_LOG_LENGTH = 0x8B84; public const int GL_ACTIVE_UNIFORMS = 0x8B86; + public const int GL_RGBA16F = 0x881A; + // OpenGL 4.3 public const int GL_DEBUG_OUTPUT = 0x92E0; public const int GL_DEBUG_OUTPUT_SYNCHRONOUS = 0x8242; diff --git a/OpenRA.Platforms.Default/Texture.cs b/OpenRA.Platforms.Default/Texture.cs index a3feb44e2c..024f963522 100644 --- a/OpenRA.Platforms.Default/Texture.cs +++ b/OpenRA.Platforms.Default/Texture.cs @@ -110,6 +110,25 @@ namespace OpenRA.Platforms.Default } } + public void SetFloatData(float[] data, int width, int height) + { + VerifyThreadAffinity(); + if (!Exts.IsPowerOf2(width) || !Exts.IsPowerOf2(height)) + throw new InvalidDataException("Non-power-of-two array {0}x{1}".F(width, height)); + + Size = new Size(width, height); + unsafe + { + fixed (float* ptr = &data[0]) + { + PrepareTexture(); + OpenGL.glTexImage2D(OpenGL.GL_TEXTURE_2D, 0, OpenGL.GL_RGBA16F, width, height, + 0, OpenGL.GL_RGBA, OpenGL.GL_FLOAT, new IntPtr(ptr)); + OpenGL.CheckGLError(); + } + } + } + public byte[] GetData() { VerifyThreadAffinity(); diff --git a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs index a62575793c..62560099d2 100644 --- a/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs +++ b/OpenRA.Platforms.Default/ThreadedGraphicsContext.cs @@ -560,6 +560,8 @@ namespace OpenRA.Platforms.Default readonly Func setData1; readonly Action setData2; readonly Func setData3; + readonly Action setData4; + readonly Func setData5; readonly Action dispose; public ThreadedTexture(ThreadedGraphicsContext device, ITextureInternal texture) @@ -574,6 +576,8 @@ namespace OpenRA.Platforms.Default setData1 = colors => { texture.SetData((uint[,])colors); return null; }; setData2 = tuple => { var t = (ValueTuple)tuple; texture.SetData(t.Item1, t.Item2, t.Item3); }; setData3 = tuple => { setData2(tuple); return null; }; + setData4 = tuple => { var t = (ValueTuple)tuple; texture.SetFloatData(t.Item1, t.Item2, t.Item3); }; + setData5 = tuple => { setData4(tuple); return null; }; dispose = texture.Dispose; } @@ -623,6 +627,25 @@ namespace OpenRA.Platforms.Default } } + public void SetFloatData(float[] data, int width, int height) + { + // Objects 85000 bytes or more will be directly allocated in the Large Object Heap (LOH). + // https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/large-object-heap + if (data.Length < 21250) + { + // If we are able to create a small array the GC can collect easily, post a message to avoid blocking. + var temp = new float[data.Length]; + Array.Copy(data, temp, temp.Length); + device.Post(setData4, (temp, width, height)); + } + else + { + // If the length is large and would result in an array on the Large Object Heap (LOH), + // send a message and block to avoid LOH allocation as this requires a Gen2 collection. + device.Send(setData5, (data, width, height)); + } + } + public void Dispose() { device.Post(dispose);