From b18ef8e3a00980595f45c7fe184dcb160dcc3cb9 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 29 Mar 2020 09:48:39 -0300 Subject: [PATCH] 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 --- Ryujinx.Graphics.OpenGL/Debugger.cs | 2 +- Ryujinx.Graphics.OpenGL/Framebuffer.cs | 37 +++++++-- Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 41 +++++++-- Ryujinx.Graphics.OpenGL/Pipeline.cs | 44 ++++++---- .../TextureCopyUnscaled.cs | 58 +++++++------ Ryujinx.Graphics.OpenGL/TextureStorage.cs | 76 +++++++++-------- Ryujinx.Graphics.OpenGL/TextureView.cs | 83 +++++++++++++------ 7 files changed, 222 insertions(+), 119 deletions(-) diff --git a/Ryujinx.Graphics.OpenGL/Debugger.cs b/Ryujinx.Graphics.OpenGL/Debugger.cs index f34a5048a1..ff9fcd855a 100644 --- a/Ryujinx.Graphics.OpenGL/Debugger.cs +++ b/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); diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs index d416bd03ff..23f015b183 100644 --- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs +++ b/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]; diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index f97bd2ea47..3d72cb7da6 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -5,15 +5,25 @@ namespace Ryujinx.Graphics.OpenGL { static class HwCapabilities { - private static Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); + private static readonly Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); - private static Lazy _maximumComputeSharedMemorySize = new Lazy(() => GetLimit(All.MaxComputeSharedMemorySize)); - private static Lazy _storageBufferOffsetAlignment = new Lazy(() => GetLimit(All.ShaderStorageBufferOffsetAlignment)); + private static readonly Lazy _maximumComputeSharedMemorySize = new Lazy(() => GetLimit(All.MaxComputeSharedMemorySize)); + private static readonly Lazy _storageBufferOffsetAlignment = new Lazy(() => GetLimit(All.ShaderStorageBufferOffsetAlignment)); - private static Lazy _isNvidiaDriver = new Lazy(() => IsNvidiaDriver()); + public enum GpuVendor + { + Unknown, + Amd, + Intel, + Nvidia + } + + private static readonly Lazy _gpuVendor = new Lazy(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; + } } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index f01e7c7ad5..3480cf82cc 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/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) { diff --git a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs index cf13db8af4..5ae75d9c0f 100644 --- a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs +++ b/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); } diff --git a/Ryujinx.Graphics.OpenGL/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/TextureStorage.cs index 241b4116ff..b680f3a6b7 100644 --- a/Ryujinx.Graphics.OpenGL/TextureStorage.cs +++ b/Ryujinx.Graphics.OpenGL/TextureStorage.cs @@ -8,18 +8,16 @@ namespace Ryujinx.Graphics.OpenGL { public int Handle { get; private set; } + public TextureCreateInfo Info { get; } + private readonly Renderer _renderer; - private readonly TextureCreateInfo _info; - - public Target Target => _info.Target; - 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) diff --git a/Ryujinx.Graphics.OpenGL/TextureView.cs b/Ryujinx.Graphics.OpenGL/TextureView.cs index 8fa78e5b5f..cb872880ac 100644 --- a/Ryujinx.Graphics.OpenGL/TextureView.cs +++ b/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);