From da75a9a6ea89787c551b20e068a2bed8a8dc4f92 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Mon, 19 Sep 2022 16:12:56 -0300
Subject: [PATCH] OpenGL: Fix blit from non-multisample to multisample texture
 (#3596)

* OpenGL: Fix blit from non-multisample to multisample texture

* New approach for multisample copy using compute shaders
---
 .../Image/IntermmediatePool.cs                |  98 -------
 Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs  |   5 -
 .../Image/TextureCopyMS.cs                    | 276 ++++++++++++++++++
 Ryujinx.Graphics.OpenGL/Image/TextureView.cs  |  67 +----
 Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs     |   3 +
 Ryujinx.Graphics.OpenGL/Pipeline.cs           |  36 +++
 6 files changed, 329 insertions(+), 156 deletions(-)
 delete mode 100644 Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
 create mode 100644 Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs

diff --git a/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs b/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
deleted file mode 100644
index 218a245e14..0000000000
--- a/Ryujinx.Graphics.OpenGL/Image/IntermmediatePool.cs
+++ /dev/null
@@ -1,98 +0,0 @@
-using Ryujinx.Graphics.GAL;
-using System;
-using System.Collections.Generic;
-
-namespace Ryujinx.Graphics.OpenGL.Image
-{
-    class IntermmediatePool : IDisposable
-    {
-        private readonly OpenGLRenderer _renderer;
-        private readonly List<TextureView> _entries;
-
-        public IntermmediatePool(OpenGLRenderer renderer)
-        {
-            _renderer = renderer;
-            _entries = new List<TextureView>();
-        }
-
-        public TextureView GetOrCreateWithAtLeast(
-            Target target,
-            int blockWidth,
-            int blockHeight,
-            int bytesPerPixel,
-            Format format,
-            int width,
-            int height,
-            int depth,
-            int levels)
-        {
-            TextureView entry;
-
-            for (int i = 0; i < _entries.Count; i++)
-            {
-                entry = _entries[i];
-
-                if (entry.Target == target && entry.Format == format)
-                {
-                    if (entry.Width < width || entry.Height < height || entry.Info.Depth < depth || entry.Info.Levels < levels)
-                    {
-                        width = Math.Max(width, entry.Width);
-                        height = Math.Max(height, entry.Height);
-                        depth = Math.Max(depth, entry.Info.Depth);
-                        levels = Math.Max(levels, entry.Info.Levels);
-
-                        entry.Dispose();
-                        entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
-                        _entries[i] = entry;
-                    }
-
-                    return entry;
-                }
-            }
-
-            entry = CreateNew(target, blockWidth, blockHeight, bytesPerPixel, format, width, height, depth, levels);
-            _entries.Add(entry);
-
-            return entry;
-        }
-
-        private TextureView CreateNew(
-            Target target,
-            int blockWidth,
-            int blockHeight,
-            int bytesPerPixel,
-            Format format,
-            int width,
-            int height,
-            int depth,
-            int levels)
-        {
-            return (TextureView)_renderer.CreateTexture(new TextureCreateInfo(
-                width,
-                height,
-                depth,
-                levels,
-                1,
-                blockWidth,
-                blockHeight,
-                bytesPerPixel,
-                format,
-                DepthStencilMode.Depth,
-                target,
-                SwizzleComponent.Red,
-                SwizzleComponent.Green,
-                SwizzleComponent.Blue,
-                SwizzleComponent.Alpha), 1f);
-        }
-
-        public void Dispose()
-        {
-            foreach (TextureView entry in _entries)
-            {
-                entry.Dispose();
-            }
-
-            _entries.Clear();
-        }
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
index d791544523..75c4d8708e 100644
--- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
@@ -9,8 +9,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
     {
         private readonly OpenGLRenderer _renderer;
 
-        public IntermmediatePool IntermmediatePool { get; }
-
         private int _srcFramebuffer;
         private int _dstFramebuffer;
 
@@ -20,7 +18,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
         public TextureCopy(OpenGLRenderer renderer)
         {
             _renderer = renderer;
-            IntermmediatePool = new IntermmediatePool(renderer);
         }
 
         public void Copy(
@@ -517,8 +514,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
 
                 _copyPboHandle = 0;
             }
-
-            IntermmediatePool.Dispose();
         }
     }
 }
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs
new file mode 100644
index 0000000000..9963dc661f
--- /dev/null
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopyMS.cs
@@ -0,0 +1,276 @@
+using OpenTK.Graphics.OpenGL;
+using Ryujinx.Graphics.GAL;
+using System;
+using System.Numerics;
+
+namespace Ryujinx.Graphics.OpenGL.Image
+{
+    class TextureCopyMS
+    {
+        private const string ComputeShaderMSToNonMS = @"#version 450 core
+
+layout (binding = 0, $FORMAT$) uniform uimage2DMS imgIn;
+layout (binding = 1, $FORMAT$) uniform uimage2D imgOut;
+
+layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
+
+void main()
+{
+    uvec2 coords = gl_GlobalInvocationID.xy;
+    ivec2 imageSz = imageSize(imgOut);
+    if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
+    {
+        return;
+    }
+    int inSamples = imageSamples(imgIn);
+    int samplesInXLog2 = 0;
+    int samplesInYLog2 = 0;
+    switch (inSamples)
+    {
+        case 2:
+            samplesInXLog2 = 1;
+            break;
+        case 4:
+            samplesInXLog2 = 1;
+            samplesInYLog2 = 1;
+            break;
+        case 8:
+            samplesInXLog2 = 2;
+            samplesInYLog2 = 1;
+            break;
+        case 16:
+            samplesInXLog2 = 2;
+            samplesInYLog2 = 2;
+            break;
+    }
+    int samplesInX = 1 << samplesInXLog2;
+    int samplesInY = 1 << samplesInYLog2;
+    int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
+    uvec4 value = imageLoad(imgIn, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx);
+    imageStore(imgOut, ivec2(coords), value);
+}";
+
+        private const string ComputeShaderNonMSToMS = @"#version 450 core
+
+layout (binding = 0, $FORMAT$) uniform uimage2D imgIn;
+layout (binding = 1, $FORMAT$) uniform uimage2DMS imgOut;
+
+layout (local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
+
+void main()
+{
+    uvec2 coords = gl_GlobalInvocationID.xy;
+    ivec2 imageSz = imageSize(imgIn);
+    if (int(coords.x) >= imageSz.x || int(coords.y) >= imageSz.y)
+    {
+        return;
+    }
+    int outSamples = imageSamples(imgOut);
+    int samplesInXLog2 = 0;
+    int samplesInYLog2 = 0;
+    switch (outSamples)
+    {
+        case 2:
+            samplesInXLog2 = 1;
+            break;
+        case 4:
+            samplesInXLog2 = 1;
+            samplesInYLog2 = 1;
+            break;
+        case 8:
+            samplesInXLog2 = 2;
+            samplesInYLog2 = 1;
+            break;
+        case 16:
+            samplesInXLog2 = 2;
+            samplesInYLog2 = 2;
+            break;
+    }
+    int samplesInX = 1 << samplesInXLog2;
+    int samplesInY = 1 << samplesInYLog2;
+    int sampleIdx = (int(coords.x) & (samplesInX - 1)) | ((int(coords.y) & (samplesInY - 1)) << samplesInXLog2);
+    uvec4 value = imageLoad(imgIn, ivec2(coords));
+    imageStore(imgOut, ivec2(int(coords.x) >> samplesInXLog2, int(coords.y) >> samplesInYLog2), sampleIdx, value);
+}";
+
+        private readonly OpenGLRenderer _renderer;
+        private int[] _msToNonMSProgramHandles;
+        private int[] _nonMSToMSProgramHandles;
+
+        public TextureCopyMS(OpenGLRenderer renderer)
+        {
+            _renderer = renderer;
+            _msToNonMSProgramHandles = new int[5];
+            _nonMSToMSProgramHandles = new int[5];
+        }
+
+        public void CopyMSToNonMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
+        {
+            TextureCreateInfo srcInfo = src.Info;
+            TextureCreateInfo dstInfo = dst.Info;
+
+            int srcHandle = CreateViewIfNeeded(src);
+            int dstHandle = CreateViewIfNeeded(dst);
+
+            int dstWidth = dstInfo.Width;
+            int dstHeight = dstInfo.Height;
+
+            GL.UseProgram(GetMSToNonMSShader(srcInfo.BytesPerPixel));
+
+            for (int z = 0; z < depth; z++)
+            {
+                GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
+                GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
+
+                GL.DispatchCompute((dstWidth + 31) / 32, (dstHeight + 31) / 32, 1);
+            }
+
+            Pipeline pipeline = (Pipeline)_renderer.Pipeline;
+
+            pipeline.RestoreProgram();
+            pipeline.RestoreImages1And2();
+
+            DestroyViewIfNeeded(src, srcHandle);
+            DestroyViewIfNeeded(dst, dstHandle);
+        }
+
+        public void CopyNonMSToMS(ITextureInfo src, ITextureInfo dst, int srcLayer, int dstLayer, int depth)
+        {
+            TextureCreateInfo srcInfo = src.Info;
+            TextureCreateInfo dstInfo = dst.Info;
+
+            int srcHandle = CreateViewIfNeeded(src);
+            int dstHandle = CreateViewIfNeeded(dst);
+
+            int srcWidth = srcInfo.Width;
+            int srcHeight = srcInfo.Height;
+
+            GL.UseProgram(GetNonMSToMSShader(srcInfo.BytesPerPixel));
+
+            for (int z = 0; z < depth; z++)
+            {
+                GL.BindImageTexture(0, srcHandle, 0, false, srcLayer + z, TextureAccess.ReadOnly, GetFormat(srcInfo.BytesPerPixel));
+                GL.BindImageTexture(1, dstHandle, 0, false, dstLayer + z, TextureAccess.WriteOnly, GetFormat(dstInfo.BytesPerPixel));
+
+                GL.DispatchCompute((srcWidth + 31) / 32, (srcHeight + 31) / 32, 1);
+            }
+
+            Pipeline pipeline = (Pipeline)_renderer.Pipeline;
+
+            pipeline.RestoreProgram();
+            pipeline.RestoreImages1And2();
+
+            DestroyViewIfNeeded(src, srcHandle);
+            DestroyViewIfNeeded(dst, dstHandle);
+        }
+
+        private static SizedInternalFormat GetFormat(int bytesPerPixel)
+        {
+            return bytesPerPixel switch
+            {
+                1 => SizedInternalFormat.R8ui,
+                2 => SizedInternalFormat.R16ui,
+                4 => SizedInternalFormat.R32ui,
+                8 => SizedInternalFormat.Rg32ui,
+                16 => SizedInternalFormat.Rgba32ui,
+                _ => throw new ArgumentException($"Invalid bytes per pixel {bytesPerPixel}.")
+            };
+        }
+
+        private static int CreateViewIfNeeded(ITextureInfo texture)
+        {
+            // Binding sRGB textures as images doesn't work on NVIDIA,
+            // we need to create and bind a RGBA view for it to work.
+            if (texture.Info.Format == Format.R8G8B8A8Srgb)
+            {
+                int handle = GL.GenTexture();
+
+                GL.TextureView(
+                    handle,
+                    texture.Info.Target.Convert(),
+                    texture.Storage.Handle,
+                    PixelInternalFormat.Rgba8,
+                    texture.FirstLevel,
+                    1,
+                    texture.FirstLayer,
+                    texture.Info.GetLayers());
+
+                return handle;
+            }
+
+            return texture.Handle;
+        }
+
+        private static void DestroyViewIfNeeded(ITextureInfo info, int handle)
+        {
+            if (info.Handle != handle)
+            {
+                GL.DeleteTexture(handle);
+            }
+        }
+
+        private int GetMSToNonMSShader(int bytesPerPixel)
+        {
+            return GetShader(ComputeShaderMSToNonMS, _msToNonMSProgramHandles, bytesPerPixel);
+        }
+
+        private int GetNonMSToMSShader(int bytesPerPixel)
+        {
+            return GetShader(ComputeShaderNonMSToMS, _nonMSToMSProgramHandles, bytesPerPixel);
+        }
+
+        private int GetShader(string code, int[] programHandles, int bytesPerPixel)
+        {
+            int index = BitOperations.Log2((uint)bytesPerPixel);
+
+            if (programHandles[index] == 0)
+            {
+                int csHandle = GL.CreateShader(ShaderType.ComputeShader);
+
+                string format = new[] { "r8ui", "r16ui", "r32ui", "rg32ui", "rgba32ui" }[index];
+
+                GL.ShaderSource(csHandle, code.Replace("$FORMAT$", format));
+                GL.CompileShader(csHandle);
+
+                int programHandle = GL.CreateProgram();
+
+                GL.AttachShader(programHandle, csHandle);
+                GL.LinkProgram(programHandle);
+                GL.DetachShader(programHandle, csHandle);
+                GL.DeleteShader(csHandle);
+
+                GL.GetProgram(programHandle, GetProgramParameterName.LinkStatus, out int status);
+
+                if (status == 0)
+                {
+                    throw new Exception(GL.GetProgramInfoLog(programHandle));
+                }
+
+                programHandles[index] = programHandle;
+            }
+
+            return programHandles[index];
+        }
+
+        public void Dispose()
+        {
+            for (int i = 0; i < _msToNonMSProgramHandles.Length; i++)
+            {
+                if (_msToNonMSProgramHandles[i] != 0)
+                {
+                    GL.DeleteProgram(_msToNonMSProgramHandles[i]);
+                    _msToNonMSProgramHandles[i] = 0;
+                }
+            }
+
+            for (int i = 0; i < _nonMSToMSProgramHandles.Length; i++)
+            {
+                if (_nonMSToMSProgramHandles[i] != 0)
+                {
+                    GL.DeleteProgram(_nonMSToMSProgramHandles[i]);
+                    _nonMSToMSProgramHandles[i] = 0;
+                }
+            }
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index 243ca1b3e9..f17243d2bf 100644
--- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -116,28 +116,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
         {
             TextureView destinationView = (TextureView)destination;
 
-            if (destinationView.Target.IsMultisample() || Target.IsMultisample())
+            if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
             {
-                Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
-                Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
-
-                TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
-                    GetIntermmediateTarget(Target),
-                    Info.BlockWidth,
-                    Info.BlockHeight,
-                    Info.BytesPerPixel,
-                    Format,
-                    Width,
-                    Height,
-                    Info.Depth,
-                    Info.Levels);
-
-                GL.Disable(EnableCap.FramebufferSrgb);
-
-                _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true);
-                _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, firstLayer, 0, firstLevel);
-
-                GL.Enable(EnableCap.FramebufferSrgb);
+                int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
+                _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, 0, firstLayer, layers);
+            }
+            else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
+            {
+                int layers = Math.Min(Info.GetLayers(), destinationView.Info.GetLayers() - firstLayer);
+                _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, 0, firstLayer, layers);
             }
             else
             {
@@ -149,28 +136,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
         {
             TextureView destinationView = (TextureView)destination;
 
-            if (destinationView.Target.IsMultisample() || Target.IsMultisample())
+            if (!destinationView.Target.IsMultisample() && Target.IsMultisample())
             {
-                Extents2D srcRegion = new Extents2D(0, 0, Width, Height);
-                Extents2D dstRegion = new Extents2D(0, 0, destinationView.Width, destinationView.Height);
-
-                TextureView intermmediate = _renderer.TextureCopy.IntermmediatePool.GetOrCreateWithAtLeast(
-                    GetIntermmediateTarget(Target),
-                    Info.BlockWidth,
-                    Info.BlockHeight,
-                    Info.BytesPerPixel,
-                    Format,
-                    Math.Max(1, Width >> srcLevel),
-                    Math.Max(1, Height >> srcLevel),
-                    1,
-                    1);
-
-                GL.Disable(EnableCap.FramebufferSrgb);
-
-                _renderer.TextureCopy.Copy(this, intermmediate, srcRegion, srcRegion, true, srcLayer, 0, srcLevel, 0, 1, 1);
-                _renderer.TextureCopy.Copy(intermmediate, destinationView, srcRegion, dstRegion, true, 0, dstLayer, 0, dstLevel, 1, 1);
-
-                GL.Enable(EnableCap.FramebufferSrgb);
+                _renderer.TextureCopyMS.CopyMSToNonMS(this, destinationView, srcLayer, dstLayer,1);
+            }
+            else if (destinationView.Target.IsMultisample() && !Target.IsMultisample())
+            {
+                _renderer.TextureCopyMS.CopyNonMSToMS(this, destinationView, srcLayer, dstLayer, 1);
             }
             else
             {
@@ -178,17 +150,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
             }
         }
 
-        private static Target GetIntermmediateTarget(Target srcTarget)
-        {
-            return srcTarget switch
-            {
-                Target.Texture2D => Target.Texture2DMultisample,
-                Target.Texture2DArray => Target.Texture2DMultisampleArray,
-                Target.Texture2DMultisampleArray => Target.Texture2DArray,
-                _ => Target.Texture2D
-            };
-        }
-
         public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter)
         {
             _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter);
diff --git a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
index 0b9acf10b9..418976e663 100644
--- a/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
+++ b/Ryujinx.Graphics.OpenGL/OpenGLRenderer.cs
@@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.OpenGL
         private TextureCopy _textureCopy;
         private TextureCopy _backgroundTextureCopy;
         internal TextureCopy TextureCopy => BackgroundContextWorker.InBackground ? _backgroundTextureCopy : _textureCopy;
+        internal TextureCopyMS TextureCopyMS { get; }
 
         private Sync _sync;
 
@@ -48,6 +49,7 @@ namespace Ryujinx.Graphics.OpenGL
             _window = new Window(this);
             _textureCopy = new TextureCopy(this);
             _backgroundTextureCopy = new TextureCopy(this);
+            TextureCopyMS = new TextureCopyMS(this);
             _sync = new Sync();
             PersistentBuffers = new PersistentBuffers();
             ResourcePool = new ResourcePool();
@@ -211,6 +213,7 @@ namespace Ryujinx.Graphics.OpenGL
         {
             _textureCopy.Dispose();
             _backgroundTextureCopy.Dispose();
+            TextureCopyMS.Dispose();
             PersistentBuffers.Dispose();
             ResourcePool.Dispose();
             _pipeline.Dispose();
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index d7cd6fbdd5..5f22462530 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -11,6 +11,8 @@ namespace Ryujinx.Graphics.OpenGL
 {
     class Pipeline : IPipeline, IDisposable
     {
+        private const int SavedImages = 2;
+
         private readonly DrawTextureEmulation _drawTexture;
 
         internal ulong DrawCount { get; private set; }
@@ -46,6 +48,7 @@ namespace Ryujinx.Graphics.OpenGL
         private Vector4<float>[] _renderScale = new Vector4<float>[73];
         private int _fragmentScaleCount;
 
+        private (TextureBase, Format)[] _images;
         private TextureBase _unit0Texture;
         private Sampler _unit0Sampler;
 
@@ -78,6 +81,8 @@ namespace Ryujinx.Graphics.OpenGL
             _fragmentOutputMap = uint.MaxValue;
             _componentMasks = uint.MaxValue;
 
+            _images = new (TextureBase, Format)[SavedImages];
+
             var defaultScale = new Vector4<float> { X = 1f, Y = 0f, Z = 0f, W = 0f };
             new Span<Vector4<float>>(_renderScale).Fill(defaultScale);
 
@@ -907,6 +912,11 @@ namespace Ryujinx.Graphics.OpenGL
 
         public void SetImage(int binding, ITexture texture, Format imageFormat)
         {
+            if ((uint)binding < SavedImages)
+            {
+                _images[binding] = (texture as TextureBase, imageFormat);
+            }
+
             if (texture == null)
             {
                 return;
@@ -1608,6 +1618,32 @@ namespace Ryujinx.Graphics.OpenGL
             }
         }
 
+        public void RestoreProgram()
+        {
+            _program?.Bind();
+        }
+
+        public void RestoreImages1And2()
+        {
+            for (int i = 0; i < SavedImages; i++)
+            {
+                (TextureBase texBase, Format imageFormat) = _images[i];
+
+                if (texBase != null)
+                {
+                    SizedInternalFormat format = FormatTable.GetImageFormat(imageFormat);
+
+                    if (format != 0)
+                    {
+                        GL.BindImageTexture(i, texBase.Handle, 0, true, 0, TextureAccess.ReadWrite, format);
+                        continue;
+                    }
+                }
+
+                GL.BindImageTexture(i, 0, 0, true, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
+            }
+        }
+
         public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
         {
             if (value is CounterQueueEvent)