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)
{
case DebugType.DebugTypeError:
Logger.PrintDebug(LogClass.Gpu, fullMessage);
Logger.PrintError(LogClass.Gpu, fullMessage);
break;
case DebugType.DebugTypePerformance:
Logger.PrintWarning(LogClass.Gpu, fullMessage);

View file

@ -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];

View file

@ -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;
}
}
}
}

View file

@ -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(
@ -285,6 +291,8 @@ namespace Ryujinx.Graphics.OpenGL
firstVertex,
firstInstance);
}
_framebuffer.SignalModified();
}
private void DrawQuadsIndexedImpl(

View file

@ -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,
@ -75,7 +83,7 @@ namespace Ryujinx.Graphics.OpenGL
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);
}

View file

@ -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)

View file

@ -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 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);