Fix BCn 4/5 conversion, GetTextureTarget

BCn 4/5 could generate invalid data when a line's size in bytes was not divisible by 4, which both backends expect.

GetTextureTarget was not creating a view with the replacement format.
This commit is contained in:
riperiperi 2022-05-22 16:45:18 +01:00
parent 782a0c4e93
commit 6ba93addf7
2 changed files with 30 additions and 18 deletions

View file

@ -1229,16 +1229,18 @@ namespace Ryujinx.Graphics.Gpu.Image
if (_arrayViewTexture == null && IsSameDimensionsTarget(target)) if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
{ {
FormatInfo formatInfo = TextureCompatibility.ToHostCompatibleFormat(Info, _context.Capabilities);
TextureCreateInfo createInfo = new TextureCreateInfo( TextureCreateInfo createInfo = new TextureCreateInfo(
Info.Width, Info.Width,
Info.Height, Info.Height,
target == Target.CubemapArray ? 6 : 1, target == Target.CubemapArray ? 6 : 1,
Info.Levels, Info.Levels,
Info.Samples, Info.Samples,
Info.FormatInfo.BlockWidth, formatInfo.BlockWidth,
Info.FormatInfo.BlockHeight, formatInfo.BlockHeight,
Info.FormatInfo.BytesPerPixel, formatInfo.BytesPerPixel,
Info.FormatInfo.Format, formatInfo.Format,
Info.DepthStencilMode, Info.DepthStencilMode,
target, target,
Info.SwizzleR, Info.SwizzleR,

View file

@ -298,9 +298,12 @@ namespace Ryujinx.Graphics.Texture
for (int l = 0; l < levels; l++) for (int l = 0; l < levels; l++)
{ {
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers; size += BitUtils.AlignUp(Math.Max(1, width >> l), 4) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers;
} }
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 4);
byte[] output = new byte[size]; byte[] output = new byte[size];
Span<byte> outputSpan = new Span<byte>(output); Span<byte> outputSpan = new Span<byte>(output);
@ -331,14 +334,14 @@ namespace Ryujinx.Graphics.Texture
{ {
int baseY = y * BlockHeight; int baseY = y * BlockHeight;
int copyHeight = Math.Min(BlockHeight, height - baseY); int copyHeight = Math.Min(BlockHeight, height - baseY);
int lineBaseOOffs = imageBaseOOffs + baseY * width; int lineBaseOOffs = imageBaseOOffs + baseY * alignedWidth;
if (copyHeight == 4) if (copyHeight == 4)
{ {
outputLine0 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs)); outputLine0 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs));
outputLine1 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + width)); outputLine1 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth));
outputLine2 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + width * 2)); outputLine2 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth * 2));
outputLine3 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + width * 3)); outputLine3 = MemoryMarshal.Cast<byte, uint>(outputSpan.Slice(lineBaseOOffs + alignedWidth * 3));
} }
for (int x = 0; x < w; x++) for (int x = 0; x < w; x++)
@ -375,7 +378,7 @@ namespace Ryujinx.Graphics.Texture
for (int tY = 0; tY < copyHeight; tY++) for (int tY = 0; tY < copyHeight; tY++)
{ {
tile.Slice(tY * 4, copyWidth).CopyTo(outputSpan.Slice(pixelBaseOOffs + width * tY, copyWidth)); tile.Slice(tY * 4, copyWidth).CopyTo(outputSpan.Slice(pixelBaseOOffs + alignedWidth * tY, copyWidth));
} }
} }
@ -383,13 +386,15 @@ namespace Ryujinx.Graphics.Texture
} }
} }
imageBaseOOffs += width * height; imageBaseOOffs += alignedWidth * height;
} }
} }
width = Math.Max(1, width >> 1); width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1); height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1); depth = Math.Max(1, depth >> 1);
alignedWidth = BitUtils.AlignUp(width, 4);
} }
return output; return output;
@ -401,9 +406,12 @@ namespace Ryujinx.Graphics.Texture
for (int l = 0; l < levels; l++) for (int l = 0; l < levels; l++)
{ {
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 2; size += BitUtils.AlignUp(Math.Max(1, width >> l), 2) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 2;
} }
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 2);
byte[] output = new byte[size]; byte[] output = new byte[size];
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data); ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
@ -438,14 +446,14 @@ namespace Ryujinx.Graphics.Texture
{ {
int baseY = y * BlockHeight; int baseY = y * BlockHeight;
int copyHeight = Math.Min(BlockHeight, height - baseY); int copyHeight = Math.Min(BlockHeight, height - baseY);
int lineBaseOOffs = imageBaseOOffs + baseY * width; int lineBaseOOffs = imageBaseOOffs + baseY * alignedWidth;
if (copyHeight == 4) if (copyHeight == 4)
{ {
outputLine0 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs)); outputLine0 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs));
outputLine1 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + width)); outputLine1 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth));
outputLine2 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + width * 2)); outputLine2 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth * 2));
outputLine3 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + width * 3)); outputLine3 = MemoryMarshal.Cast<ushort, ulong>(outputAsUshort.Slice(lineBaseOOffs + alignedWidth * 3));
} }
for (int x = 0; x < w; x++) for (int x = 0; x < w; x++)
@ -488,7 +496,7 @@ namespace Ryujinx.Graphics.Texture
for (int tY = 0; tY < copyHeight; tY++) for (int tY = 0; tY < copyHeight; tY++)
{ {
int line = pixelBaseOOffs + width * tY; int line = pixelBaseOOffs + alignedWidth * tY;
for (int tX = 0; tX < copyWidth; tX++) for (int tX = 0; tX < copyWidth; tX++)
{ {
@ -503,13 +511,15 @@ namespace Ryujinx.Graphics.Texture
} }
} }
imageBaseOOffs += width * height; imageBaseOOffs += alignedWidth * height;
} }
} }
width = Math.Max(1, width >> 1); width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1); height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1); depth = Math.Max(1, depth >> 1);
alignedWidth = BitUtils.AlignUp(width, 2);
} }
return output; return output;