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
This commit is contained in:
gdkchan 2020-03-29 09:48:39 -03:00 committed by GitHub
parent 5c1757f7c2
commit b18ef8e3a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 222 additions and 119 deletions

View file

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

View file

@ -10,9 +10,13 @@ namespace Ryujinx.Graphics.OpenGL
private FramebufferAttachment _lastDsAttachment; private FramebufferAttachment _lastDsAttachment;
private readonly TextureView[] _colors;
public Framebuffer() public Framebuffer()
{ {
Handle = GL.GenFramebuffer(); Handle = GL.GenFramebuffer();
_colors = new TextureView[8];
} }
public void Bind() public void Bind()
@ -22,11 +26,19 @@ namespace Ryujinx.Graphics.OpenGL
public void AttachColor(int index, TextureView color) public void AttachColor(int index, TextureView color)
{ {
GL.FramebufferTexture( FramebufferAttachment attachment = FramebufferAttachment.ColorAttachment0 + index;
FramebufferTarget.Framebuffer,
FramebufferAttachment.ColorAttachment0 + index, if (HwCapabilities.Vendor == HwCapabilities.GpuVendor.Amd ||
color?.Handle ?? 0, HwCapabilities.Vendor == HwCapabilities.GpuVendor.Intel)
0); {
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) 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) public void SetDrawBuffers(int colorsCount)
{ {
DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount]; DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount];

View file

@ -5,15 +5,25 @@ namespace Ryujinx.Graphics.OpenGL
{ {
static class HwCapabilities 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 readonly 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> _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 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 MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value;
public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value; public static int StorageBufferOffsetAlignment => _storageBufferOffsetAlignment.Value;
@ -38,9 +48,26 @@ namespace Ryujinx.Graphics.OpenGL
return GL.GetInteger((GetPName)name); 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;
}
} }
} }
} }

View file

@ -60,6 +60,8 @@ namespace Ryujinx.Graphics.OpenGL
GL.ClearBuffer(ClearBuffer.Color, index, colors); GL.ClearBuffer(ClearBuffer.Color, index, colors);
RestoreComponentMask(index); RestoreComponentMask(index);
_framebuffer.SignalModified();
} }
public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask) public void ClearRenderTargetDepthStencil(float depthValue, bool depthMask, int stencilValue, int stencilMask)
@ -102,6 +104,8 @@ namespace Ryujinx.Graphics.OpenGL
{ {
GL.DepthMask(_depthMask); GL.DepthMask(_depthMask);
} }
_framebuffer.SignalModified();
} }
public void DispatchCompute(int groupsX, int groupsY, int groupsZ) public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
@ -141,6 +145,8 @@ namespace Ryujinx.Graphics.OpenGL
{ {
DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance); DrawImpl(vertexCount, instanceCount, firstVertex, firstInstance);
} }
_framebuffer.SignalModified();
} }
private void DrawQuadsImpl( private void DrawQuadsImpl(
@ -285,6 +291,8 @@ namespace Ryujinx.Graphics.OpenGL
firstVertex, firstVertex,
firstInstance); firstInstance);
} }
_framebuffer.SignalModified();
} }
private void DrawQuadsIndexedImpl( private void DrawQuadsIndexedImpl(

View file

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

View file

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

View file

@ -14,24 +14,19 @@ namespace Ryujinx.Graphics.OpenGL
private TextureView _emulatedViewParent; private TextureView _emulatedViewParent;
private TextureView _incompatibleFormatView;
private readonly TextureCreateInfo _info; private readonly TextureCreateInfo _info;
private int _firstLayer; public int FirstLayer { get; private set; }
private int _firstLevel; public int FirstLevel { get; private set; }
public int Width => _info.Width; public int Width => _info.Width;
public int Height => _info.Height; public int Height => _info.Height;
public int DepthOrLayers => _info.GetDepthOrLayers();
public int Levels => _info.Levels;
public Target Target => _info.Target; public Target Target => _info.Target;
public Format Format => _info.Format; public Format Format => _info.Format;
public int BlockWidth => _info.BlockWidth;
public int BlockHeight => _info.BlockHeight;
public bool IsCompressed => _info.IsCompressed;
public TextureView( public TextureView(
Renderer renderer, Renderer renderer,
TextureStorage parent, TextureStorage parent,
@ -43,8 +38,8 @@ namespace Ryujinx.Graphics.OpenGL
_parent = parent; _parent = parent;
_info = info; _info = info;
_firstLayer = firstLayer; FirstLayer = firstLayer;
_firstLevel = firstLevel; FirstLevel = firstLevel;
Handle = GL.GenTexture(); Handle = GL.GenTexture();
@ -73,9 +68,9 @@ namespace Ryujinx.Graphics.OpenGL
target, target,
_parent.Handle, _parent.Handle,
pixelInternalFormat, pixelInternalFormat,
_firstLevel, FirstLevel,
_info.Levels, _info.Levels,
_firstLayer, FirstLayer,
_info.GetLayers()); _info.GetLayers());
GL.ActiveTexture(TextureUnit.Texture0); GL.ActiveTexture(TextureUnit.Texture0);
@ -107,8 +102,8 @@ namespace Ryujinx.Graphics.OpenGL
{ {
if (_info.IsCompressed == info.IsCompressed) if (_info.IsCompressed == info.IsCompressed)
{ {
firstLayer += _firstLayer; firstLayer += FirstLayer;
firstLevel += _firstLevel; firstLevel += FirstLevel;
return _parent.CreateView(info, firstLayer, firstLevel); return _parent.CreateView(info, firstLayer, firstLevel);
} }
@ -123,26 +118,59 @@ namespace Ryujinx.Graphics.OpenGL
emulatedView._emulatedViewParent = this; emulatedView._emulatedViewParent = this;
emulatedView._firstLayer = firstLayer; emulatedView.FirstLayer = firstLayer;
emulatedView._firstLevel = firstLevel; emulatedView.FirstLevel = firstLevel;
return emulatedView; 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) public void CopyTo(ITexture destination, int firstLayer, int firstLevel)
{ {
TextureView destinationView = (TextureView)destination; 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) if (destinationView._emulatedViewParent != null)
{ {
TextureCopyUnscaled.Copy( TextureCopyUnscaled.Copy(
this, _info,
destinationView._emulatedViewParent, destinationView._emulatedViewParent._info,
destinationView._firstLayer, Handle,
destinationView._firstLevel); destinationView._emulatedViewParent.Handle,
0,
destinationView.FirstLayer,
0,
destinationView.FirstLevel);
} }
} }
@ -405,6 +433,13 @@ namespace Ryujinx.Graphics.OpenGL
public void Dispose() public void Dispose()
{ {
if (_incompatibleFormatView != null)
{
_incompatibleFormatView.Dispose();
_incompatibleFormatView = null;
}
if (Handle != 0) if (Handle != 0)
{ {
GL.DeleteTexture(Handle); GL.DeleteTexture(Handle);