Jelajahi Sumber

Workaround for AMD and Intel view format bug (#1050)

* Workaround for Intel view format bug

* Dispose of the intermmediate texture aswell

* Apply workaround on AMD aswell
gdkchan 6 tahun lalu
induk
melakukan
b18ef8e3a0

+ 1 - 1
Ryujinx.Graphics.OpenGL/Debugger.cs

@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.OpenGL
             switch (type)
             {
                 case DebugType.DebugTypeError:
-                    Logger.PrintDebug(LogClass.Gpu, fullMessage);
+                    Logger.PrintError(LogClass.Gpu, fullMessage);
                     break;
                 case DebugType.DebugTypePerformance:
                     Logger.PrintWarning(LogClass.Gpu, fullMessage);

+ 32 - 5
Ryujinx.Graphics.OpenGL/Framebuffer.cs

@@ -10,9 +10,13 @@ namespace Ryujinx.Graphics.OpenGL
 
         private FramebufferAttachment _lastDsAttachment;
 
+        private readonly TextureView[] _colors;
+
         public Framebuffer()
         {
             Handle = GL.GenFramebuffer();
+
+            _colors = new TextureView[8];
         }
 
         public void Bind()
@@ -22,11 +26,19 @@ namespace Ryujinx.Graphics.OpenGL
 
         public void AttachColor(int index, TextureView color)
         {
-            GL.FramebufferTexture(
-                FramebufferTarget.Framebuffer,
-                FramebufferAttachment.ColorAttachment0 + index,
-                color?.Handle ?? 0,
-                0);
+            FramebufferAttachment attachment = FramebufferAttachment.ColorAttachment0 + index;
+
+            if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd ||
+                HwCapabilities.Vendor == HwCapabilities.GpuVendor.Intel)
+            {
+                GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.GetIncompatibleFormatViewHandle() ?? 0, 0);
+
+                _colors[index] = color;
+            }
+            else
+            {
+                GL.FramebufferTexture(FramebufferTarget.Framebuffer, attachment, color?.Handle ?? 0, 0);
+            }
         }
 
         public void AttachDepthStencil(TextureView depthStencil)
@@ -68,6 +80,21 @@ namespace Ryujinx.Graphics.OpenGL
             }
         }
 
+        public void SignalModified()
+        {
+            if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd ||
+                HwCapabilities.Vendor == HwCapabilities.GpuVendor.Intel)
+            {
+                for (int i = 0; i < 8; i++)
+                {
+                    if (_colors[i] != null)
+                    {
+                        _colors[i].SignalModified();
+                    }
+                }
+            }
+        }
+
         public void SetDrawBuffers(int colorsCount)
         {
             DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount];

+ 34 - 7
Ryujinx.Graphics.OpenGL/HwCapabilities.cs

@@ -5,15 +5,25 @@ namespace Ryujinx.Graphics.OpenGL
 {
     static class HwCapabilities
     {
-        private static Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
+        private static readonly Lazy<bool> _supportsAstcCompression = new Lazy<bool>(() => HasExtension("GL_KHR_texture_compression_astc_ldr"));
 
-        private static Lazy<int> _maximumComputeSharedMemorySize = new Lazy<int>(() => GetLimit(All.MaxComputeSharedMemorySize));
-        private static Lazy<int> _storageBufferOffsetAlignment   = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
+        private static readonly Lazy<int> _maximumComputeSharedMemorySize = new Lazy<int>(() => GetLimit(All.MaxComputeSharedMemorySize));
+        private static readonly Lazy<int> _storageBufferOffsetAlignment   = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
 
-        private static Lazy<bool> _isNvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
+        public enum GpuVendor
+        {
+            Unknown,
+            Amd,
+            Intel,
+            Nvidia
+        }
+
+        private static readonly Lazy<GpuVendor> _gpuVendor = new Lazy<GpuVendor>(GetGpuVendor);
+
+        public static GpuVendor Vendor => _gpuVendor.Value;
 
         public static bool SupportsAstcCompression          => _supportsAstcCompression.Value;
-        public static bool SupportsNonConstantTextureOffset => _isNvidiaDriver.Value;
+        public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia;
 
         public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value;
         public static int StorageBufferOffsetAlignment   => _storageBufferOffsetAlignment.Value;
@@ -38,9 +48,26 @@ namespace Ryujinx.Graphics.OpenGL
             return GL.GetInteger((GetPName)name);
         }
 
-        private static bool IsNvidiaDriver()
+        private static GpuVendor GetGpuVendor()
         {
-            return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
+            string vendor = GL.GetString(StringName.Vendor).ToLower();
+
+            if (vendor == "nvidia corporation")
+            {
+                return GpuVendor.Nvidia;
+            }
+            else if (vendor == "intel")
+            {
+                return GpuVendor.Intel;
+            }
+            else if (vendor == "ati technologies inc." || vendor == "advanced micro devices, inc.")
+            {
+                return GpuVendor.Amd;
+            }
+            else
+            {
+                return GpuVendor.Unknown;
+            }
         }
     }
 }

+ 26 - 18
Ryujinx.Graphics.OpenGL/Pipeline.cs

@@ -19,14 +19,14 @@ namespace Ryujinx.Graphics.OpenGL
 
         private PrimitiveType _primitiveType;
 
-        private int  _stencilFrontMask;
+        private int _stencilFrontMask;
         private bool _depthMask;
         private bool _depthTest;
         private bool _hasDepthBuffer;
 
         private TextureView _unit0Texture;
 
-        private ClipOrigin    _clipOrigin;
+        private ClipOrigin _clipOrigin;
         private ClipDepthMode _clipDepthMode;
 
         private uint[] _componentMasks;
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.OpenGL
 
         internal Pipeline()
         {
-            _clipOrigin    = ClipOrigin.LowerLeft;
+            _clipOrigin = ClipOrigin.LowerLeft;
             _clipDepthMode = ClipDepthMode.NegativeOneToOne;
 
             _scissorEnable = new bool[8];
@@ -60,6 +60,8 @@ namespace Ryujinx.Graphics.OpenGL
             GL.ClearBuffer(ClearBuffer.Color, index, colors);
 
             RestoreComponentMask(index);
+
+            _framebuffer.SignalModified();
         }
 
         public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
@@ -102,6 +104,8 @@ namespace Ryujinx.Graphics.OpenGL
             {
                 GL.DepthMask(_depthMask);
             }
+
+            _framebuffer.SignalModified();
         }
 
         public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
@@ -141,6 +145,8 @@ namespace Ryujinx.Graphics.OpenGL
             {
                 DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance);
             }
+
+            _framebuffer.SignalModified();
         }
 
         private void DrawQuadsImpl(
@@ -251,7 +257,7 @@ namespace Ryujinx.Graphics.OpenGL
             switch (_elementsType)
             {
                 case DrawElementsType.UnsignedShort: indexElemSize = 2; break;
-                case DrawElementsType.UnsignedInt:   indexElemSize = 4; break;
+                case DrawElementsType.UnsignedInt: indexElemSize = 4; break;
             }
 
             IntPtr indexBaseOffset = _indexBaseOffset + firstIndex * indexElemSize;
@@ -285,15 +291,17 @@ namespace Ryujinx.Graphics.OpenGL
                     firstVertex,
                     firstInstance);
             }
+
+            _framebuffer.SignalModified();
         }
 
         private void DrawQuadsIndexedImpl(
-            int    indexCount,
-            int    instanceCount,
+            int indexCount,
+            int instanceCount,
             IntPtr indexBaseOffset,
-            int    indexElemSize,
-            int    firstVertex,
-            int    firstInstance)
+            int indexElemSize,
+            int firstVertex,
+            int firstInstance)
         {
             int quadsCount = indexCount / 4;
 
@@ -367,12 +375,12 @@ namespace Ryujinx.Graphics.OpenGL
         }
 
         private void DrawQuadStripIndexedImpl(
-            int    indexCount,
-            int    instanceCount,
+            int indexCount,
+            int instanceCount,
             IntPtr indexBaseOffset,
-            int    indexElemSize,
-            int    firstVertex,
-            int    firstInstance)
+            int indexElemSize,
+            int firstVertex,
+            int firstInstance)
         {
             // TODO: Instanced rendering.
             int quadsCount = (indexCount - 2) / 2;
@@ -408,11 +416,11 @@ namespace Ryujinx.Graphics.OpenGL
         }
 
         private void DrawIndexedImpl(
-            int    indexCount,
-            int    instanceCount,
+            int indexCount,
+            int instanceCount,
             IntPtr indexBaseOffset,
-            int    firstVertex,
-            int    firstInstance)
+            int firstVertex,
+            int firstInstance)
         {
             if (firstInstance == 0 && firstVertex == 0 && instanceCount == 1)
             {

+ 33 - 25
Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs

@@ -7,22 +7,30 @@ namespace Ryujinx.Graphics.OpenGL
 {
     static class TextureCopyUnscaled
     {
-        public static void Copy(TextureView src, TextureView dst, int dstLayer, int dstLevel)
+        public static void Copy(
+            TextureCreateInfo srcInfo,
+            TextureCreateInfo dstInfo,
+            int srcHandle,
+            int dstHandle,
+            int srcLayer,
+            int dstLayer,
+            int srcLevel,
+            int dstLevel)
         {
-            int srcWidth  = src.Width;
-            int srcHeight = src.Height;
-            int srcDepth  = src.DepthOrLayers;
-            int srcLevels = src.Levels;
+            int srcWidth  = srcInfo.Width;
+            int srcHeight = srcInfo.Height;
+            int srcDepth  = srcInfo.GetDepthOrLayers();
+            int srcLevels = srcInfo.Levels;
 
-            int dstWidth  = dst.Width;
-            int dstHeight = dst.Height;
-            int dstDepth  = dst.DepthOrLayers;
-            int dstLevels = dst.Levels;
+            int dstWidth  = dstInfo.Width;
+            int dstHeight = dstInfo.Height;
+            int dstDepth  = dstInfo.GetDepthOrLayers();
+            int dstLevels = dstInfo.Levels;
 
             dstWidth = Math.Max(1, dstWidth >> dstLevel);
             dstHeight = Math.Max(1, dstHeight >> dstLevel);
 
-            if (dst.Target == Target.Texture3D)
+            if (dstInfo.Target == Target.Texture3D)
             {
                 dstDepth = Math.Max(1, dstDepth >> dstLevel);
             }
@@ -31,15 +39,15 @@ namespace Ryujinx.Graphics.OpenGL
             // the non-compressed texture will have the size of the texture
             // in blocks (not in texels), so we must adjust that size to
             // match the size in texels of the compressed texture.
-            if (!src.IsCompressed && dst.IsCompressed)
+            if (!srcInfo.IsCompressed && dstInfo.IsCompressed)
             {
-                dstWidth  = BitUtils.DivRoundUp(dstWidth,  dst.BlockWidth);
-                dstHeight = BitUtils.DivRoundUp(dstHeight, dst.BlockHeight);
+                dstWidth  = BitUtils.DivRoundUp(dstWidth,  dstInfo.BlockWidth);
+                dstHeight = BitUtils.DivRoundUp(dstHeight, dstInfo.BlockHeight);
             }
-            else if (src.IsCompressed && !dst.IsCompressed)
+            else if (srcInfo.IsCompressed && !dstInfo.IsCompressed)
             {
-                dstWidth  *= dst.BlockWidth;
-                dstHeight *= dst.BlockHeight;
+                dstWidth  *= dstInfo.BlockWidth;
+                dstHeight *= dstInfo.BlockHeight;
             }
 
             int width  = Math.Min(srcWidth,  dstWidth);
@@ -50,20 +58,20 @@ namespace Ryujinx.Graphics.OpenGL
             for (int level = 0; level < levels; level++)
             {
                 // Stop copy if we are already out of the levels range.
-                if (level >= src.Levels || dstLevel + level >= dst.Levels)
+                if (level >= srcInfo.Levels || dstLevel + level >= dstInfo.Levels)
                 {
                     break;
                 }
 
                 GL.CopyImageSubData(
-                    src.Handle,
-                    src.Target.ConvertToImageTarget(),
-                    level,
+                    srcHandle,
+                    srcInfo.Target.ConvertToImageTarget(),
+                    srcLevel + level,
                     0,
                     0,
-                    0,
-                    dst.Handle,
-                    dst.Target.ConvertToImageTarget(),
+                    srcLayer,
+                    dstHandle,
+                    dstInfo.Target.ConvertToImageTarget(),
                     dstLevel + level,
                     0,
                     0,
@@ -72,10 +80,10 @@ namespace Ryujinx.Graphics.OpenGL
                     height,
                     depth);
 
-                width  = Math.Max(1, width  >> 1);
+                width = Math.Max(1, width >> 1);
                 height = Math.Max(1, height >> 1);
 
-                if (src.Target == Target.Texture3D)
+                if (srcInfo.Target == Target.Texture3D)
                 {
                     depth = Math.Max(1, depth >> 1);
                 }

+ 37 - 39
Ryujinx.Graphics.OpenGL/TextureStorage.cs

@@ -8,18 +8,16 @@ namespace Ryujinx.Graphics.OpenGL
     {
         public int Handle { get; private set; }
 
-        private readonly Renderer _renderer;
-
-        private readonly TextureCreateInfo _info;
+        public TextureCreateInfo Info { get; }
 
-        public Target Target => _info.Target;
+        private readonly Renderer _renderer;
 
         private int _viewsCount;
 
         public TextureStorage(Renderer renderer, TextureCreateInfo info)
         {
             _renderer = renderer;
-            _info     = info;
+            Info      = info;
 
             Handle = GL.GenTexture();
 
@@ -28,13 +26,13 @@ namespace Ryujinx.Graphics.OpenGL
 
         private void CreateImmutableStorage()
         {
-            TextureTarget target = _info.Target.Convert();
+            TextureTarget target = Info.Target.Convert();
 
             GL.ActiveTexture(TextureUnit.Texture0);
 
             GL.BindTexture(target, Handle);
 
-            FormatInfo format = FormatTable.GetFormatInfo(_info.Format);
+            FormatInfo format = FormatTable.GetFormatInfo(Info.Format);
 
             SizedInternalFormat internalFormat;
 
@@ -47,92 +45,92 @@ namespace Ryujinx.Graphics.OpenGL
                 internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
             }
 
-            switch (_info.Target)
+            switch (Info.Target)
             {
                 case Target.Texture1D:
                     GL.TexStorage1D(
                         TextureTarget1d.Texture1D,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width);
+                        Info.Width);
                     break;
 
                 case Target.Texture1DArray:
                     GL.TexStorage2D(
                         TextureTarget2d.Texture1DArray,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width,
-                        _info.Height);
+                        Info.Width,
+                        Info.Height);
                     break;
 
                 case Target.Texture2D:
                     GL.TexStorage2D(
                         TextureTarget2d.Texture2D,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width,
-                        _info.Height);
+                        Info.Width,
+                        Info.Height);
                     break;
 
                 case Target.Texture2DArray:
                     GL.TexStorage3D(
                         TextureTarget3d.Texture2DArray,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width,
-                        _info.Height,
-                        _info.Depth);
+                        Info.Width,
+                        Info.Height,
+                        Info.Depth);
                     break;
 
                 case Target.Texture2DMultisample:
                     GL.TexStorage2DMultisample(
                         TextureTargetMultisample2d.Texture2DMultisample,
-                        _info.Samples,
+                        Info.Samples,
                         internalFormat,
-                        _info.Width,
-                        _info.Height,
+                        Info.Width,
+                        Info.Height,
                         true);
                     break;
 
                 case Target.Texture2DMultisampleArray:
                     GL.TexStorage3DMultisample(
                         TextureTargetMultisample3d.Texture2DMultisampleArray,
-                        _info.Samples,
+                        Info.Samples,
                         internalFormat,
-                        _info.Width,
-                        _info.Height,
-                        _info.Depth,
+                        Info.Width,
+                        Info.Height,
+                        Info.Depth,
                         true);
                     break;
 
                 case Target.Texture3D:
                     GL.TexStorage3D(
                         TextureTarget3d.Texture3D,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width,
-                        _info.Height,
-                        _info.Depth);
+                        Info.Width,
+                        Info.Height,
+                        Info.Depth);
                     break;
 
                 case Target.Cubemap:
                     GL.TexStorage2D(
                         TextureTarget2d.TextureCubeMap,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width,
-                        _info.Height);
+                        Info.Width,
+                        Info.Height);
                     break;
 
                 case Target.CubemapArray:
                     GL.TexStorage3D(
                         (TextureTarget3d)All.TextureCubeMapArray,
-                        _info.Levels,
+                        Info.Levels,
                         internalFormat,
-                        _info.Width,
-                        _info.Height,
-                        _info.Depth);
+                        Info.Width,
+                        Info.Height,
+                        Info.Depth);
                     break;
 
                 default:
@@ -143,7 +141,7 @@ namespace Ryujinx.Graphics.OpenGL
 
         public ITexture CreateDefaultView()
         {
-            return CreateView(_info, 0, 0);
+            return CreateView(Info, 0, 0);
         }
 
         public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel)

+ 59 - 24
Ryujinx.Graphics.OpenGL/TextureView.cs

@@ -14,24 +14,19 @@ namespace Ryujinx.Graphics.OpenGL
 
         private TextureView _emulatedViewParent;
 
+        private TextureView _incompatibleFormatView;
+
         private readonly TextureCreateInfo _info;
 
-        private int _firstLayer;
-        private int _firstLevel;
+        public int FirstLayer { get; private set; }
+        public int FirstLevel { get; private set; }
 
-        public int Width         => _info.Width;
-        public int Height        => _info.Height;
-        public int DepthOrLayers => _info.GetDepthOrLayers();
-        public int Levels        => _info.Levels;
+        public int Width  => _info.Width;
+        public int Height => _info.Height;
 
         public Target Target => _info.Target;
         public Format Format => _info.Format;
 
-        public int BlockWidth  => _info.BlockWidth;
-        public int BlockHeight => _info.BlockHeight;
-
-        public bool IsCompressed => _info.IsCompressed;
-
         public TextureView(
             Renderer          renderer,
             TextureStorage    parent,
@@ -43,8 +38,8 @@ namespace Ryujinx.Graphics.OpenGL
             _parent   = parent;
             _info     = info;
 
-            _firstLayer = firstLayer;
-            _firstLevel = firstLevel;
+            FirstLayer = firstLayer;
+            FirstLevel = firstLevel;
 
             Handle = GL.GenTexture();
 
@@ -73,9 +68,9 @@ namespace Ryujinx.Graphics.OpenGL
                 target,
                 _parent.Handle,
                 pixelInternalFormat,
-                _firstLevel,
+                FirstLevel,
                 _info.Levels,
-                _firstLayer,
+                FirstLayer,
                 _info.GetLayers());
 
             GL.ActiveTexture(TextureUnit.Texture0);
@@ -107,8 +102,8 @@ namespace Ryujinx.Graphics.OpenGL
         {
             if (_info.IsCompressed == info.IsCompressed)
             {
-                firstLayer += _firstLayer;
-                firstLevel += _firstLevel;
+                firstLayer += FirstLayer;
+                firstLevel += FirstLevel;
 
                 return _parent.CreateView(info, firstLayer, firstLevel);
             }
@@ -123,26 +118,59 @@ namespace Ryujinx.Graphics.OpenGL
 
                 emulatedView._emulatedViewParent = this;
 
-                emulatedView._firstLayer = firstLayer;
-                emulatedView._firstLevel = firstLevel;
+                emulatedView.FirstLayer = firstLayer;
+                emulatedView.FirstLevel = firstLevel;
 
                 return emulatedView;
             }
         }
 
+        public int GetIncompatibleFormatViewHandle()
+        {
+            // AMD and Intel has a bug where the view format is always ignored,
+            // it uses the parent format instead.
+            // As workaround we create a new texture with the correct
+            // format, and then do a copy after the draw.
+            if (_parent.Info.Format != Format)
+            {
+                if (_incompatibleFormatView == null)
+                {
+                    _incompatibleFormatView = (TextureView)_renderer.CreateTexture(_info);
+                }
+
+                TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView._info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0);
+
+                return _incompatibleFormatView.Handle;
+            }
+
+            return Handle;
+        }
+
+        public void SignalModified()
+        {
+            if (_incompatibleFormatView != null)
+            {
+                TextureCopyUnscaled.Copy(_incompatibleFormatView._info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel);
+            }
+        }
+
         public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
         {
             TextureView destinationView = (TextureView)destination;
 
-            TextureCopyUnscaled.Copy(this, destinationView, firstLayer, firstLevel);
+            TextureCopyUnscaled.Copy(_info, destinationView._info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel);
 
             if (destinationView._emulatedViewParent != null)
             {
                 TextureCopyUnscaled.Copy(
-                    this,
-                    destinationView._emulatedViewParent,
-                    destinationView._firstLayer,
-                    destinationView._firstLevel);
+                    _info,
+                    destinationView._emulatedViewParent._info,
+                    Handle,
+                    destinationView._emulatedViewParent.Handle,
+                    0,
+                    destinationView.FirstLayer,
+                    0,
+                    destinationView.FirstLevel);
             }
         }
 
@@ -405,6 +433,13 @@ namespace Ryujinx.Graphics.OpenGL
 
         public void Dispose()
         {
+            if (_incompatibleFormatView != null)
+            {
+                _incompatibleFormatView.Dispose();
+
+                _incompatibleFormatView = null;
+            }
+
             if (Handle != 0)
             {
                 GL.DeleteTexture(Handle);