diff --git a/src/SharpEmu.Libs/Gpu/Images/GpuTiler.cs b/src/SharpEmu.Libs/Gpu/Images/GpuTiler.cs index 5db6194..72a42cd 100644 --- a/src/SharpEmu.Libs/Gpu/Images/GpuTiler.cs +++ b/src/SharpEmu.Libs/Gpu/Images/GpuTiler.cs @@ -63,7 +63,7 @@ public struct TileTransferArguments // Runs the tiling, detiling, depth conversion and channel swap compute passes. public sealed unsafe class GpuTiler : IDisposable { - private const uint KindCount = 9; + private const uint KindCount = 10; private const uint ElementSizeCount = 5; private const uint DirectionCount = 2; private const uint PipelineCount = KindCount * ElementSizeCount * DirectionCount; diff --git a/src/SharpEmu.Libs/Gpu/Images/TextureTransferLayout.cs b/src/SharpEmu.Libs/Gpu/Images/TextureTransferLayout.cs index 2009c67..989e5e3 100644 --- a/src/SharpEmu.Libs/Gpu/Images/TextureTransferLayout.cs +++ b/src/SharpEmu.Libs/Gpu/Images/TextureTransferLayout.cs @@ -452,7 +452,9 @@ public sealed class TextureTransferLayout transfer.Width = Math.Max((region.ImageExtent.Width + texture.TexelWidth - 1) / texture.TexelWidth, 1); transfer.Height = Math.Max((logicalHeight + texture.TexelHeight - 1) / texture.TexelHeight, 1); transfer.Depth = 1; - transfer.SurfaceZ = block.Kind is TileBlockKind.RenderTarget64KB or TileBlockKind.Depth64KB ? region.ImageSubresource.BaseArrayLayer : 0; + transfer.SurfaceZ = block.Kind is TileBlockKind.RenderTarget64KB or TileBlockKind.RenderTarget64KBGen5 or TileBlockKind.Depth64KB + ? region.ImageSubresource.BaseArrayLayer + : 0; transfer.Pitch = Math.Max((pitch + texture.TexelWidth - 1) / texture.TexelWidth, 1); transfer.Tail = tail; transfer.TailX = tail ? mip.TailX : 0; diff --git a/src/SharpEmu.Libs/Gpu/Images/TileGeometry.Blocks.cs b/src/SharpEmu.Libs/Gpu/Images/TileGeometry.Blocks.cs index 4485662..04d604d 100644 --- a/src/SharpEmu.Libs/Gpu/Images/TileGeometry.Blocks.cs +++ b/src/SharpEmu.Libs/Gpu/Images/TileGeometry.Blocks.cs @@ -230,6 +230,75 @@ public static partial class TileGeometry } } + private static uint Gen5RenderTargetOffset(uint x, uint y, uint bytesPerElement) + { + uint offset = 0; + switch (bytesPerElement) + { + case 1: + offset ^= (y << 2) & 0x0008; + offset ^= (y << 4) & 0x0010; + offset ^= (y << 3) & 0x00a0; + offset ^= (y << 5) & 0x0f00; + offset ^= (y << 6) & 0x1000; + offset ^= (y << 7) & 0x4000; + offset ^= x & 0x0007; + offset ^= (x << 3) & 0x0040; + offset ^= (x << 5) & 0x0300; + offset ^= (x << 4) & 0x0400; + offset ^= (x << 6) & 0x0800; + offset ^= (x << 7) & 0x2000; + offset ^= (x << 8) & 0x8000; + return offset; + case 2: + offset ^= (y << 4) & 0x0070; + offset ^= (y << 5) & 0x0f00; + offset ^= (y << 8) & 0x5000; + offset ^= (x << 1) & 0x000e; + offset ^= (x << 4) & 0x0480; + offset ^= (x << 5) & 0x0300; + offset ^= (x << 6) & 0x0800; + offset ^= (x << 7) & 0x2000; + offset ^= (x << 8) & 0x8000; + return offset; + case 4: + offset ^= (y << 4) & 0x0070; + offset ^= (y << 5) & 0x0f00; + offset ^= (y << 9) & 0x1000; + offset ^= (y << 8) & 0x4000; + offset ^= (x << 2) & 0x000c; + offset ^= (x << 5) & 0x0380; + offset ^= (x << 4) & 0x0400; + offset ^= (x << 6) & 0x0800; + offset ^= (x << 9) & 0xa000; + return offset; + case 8: + offset ^= (y << 4) & 0x0010; + offset ^= (y << 6) & 0x0080; + offset ^= (y << 5) & 0x0f00; + offset ^= (y << 10) & 0x5000; + offset ^= (x << 3) & 0x0008; + offset ^= (x << 4) & 0x0460; + offset ^= (x << 5) & 0x0300; + offset ^= (x << 6) & 0x0800; + offset ^= (x << 10) & 0x2000; + offset ^= (x << 9) & 0x8000; + return offset; + case 16: + offset ^= (x << 4) & 0x0410; + offset ^= (x << 5) & 0x0340; + offset ^= (x << 6) & 0x0800; + offset ^= (x << 11) & 0xa000; + offset ^= (y << 5) & 0x0f20; + offset ^= (y << 6) & 0x0080; + offset ^= (y << 10) & 0x1000; + offset ^= (y << 11) & 0x4000; + return offset; + default: + throw SubmissionScheduler.Fatal($"The Gen5 render-target tile block does not support this element size: bytes={bytesPerElement}."); + } + } + private static uint Depth64KB8X(uint x) => (x & 0x0001) ^ ((x << 1) & 0x0004) ^ ((x << 2) & 0x0010) ^ ((x << 3) & 0x0040) ^ ((x << 5) & 0x0300) ^ ((x << 4) & 0x0400) ^ ((x << 6) & 0x0800) ^ ((x << 7) & 0x2000) ^ ((x << 8) & 0x8000); @@ -319,6 +388,14 @@ public static partial class TileGeometry offset = RenderTargetOffset(x, y, bytes); break; + case TileBlockKind.RenderTarget64KBGen5: + if (bytes > 16) + { + return false; + } + + offset = Gen5RenderTargetOffset(x, y, bytes); + break; case TileBlockKind.Depth64KB: switch (bytes) { @@ -364,17 +441,19 @@ public static partial class TileGeometry byteOffset = Depth64KB64X(blockX * layout.BlockWidth) ^ Depth64KB64Y(blockY * layout.BlockHeight); } - else if (layout.Kind == TileBlockKind.RenderTarget64KB) + else if (layout.Kind is TileBlockKind.RenderTarget64KB or TileBlockKind.RenderTarget64KBGen5) { if (blockX > uint.MaxValue / layout.BlockWidth || blockY > uint.MaxValue / layout.BlockHeight || layout.BytesPerElement > 16) { return false; } - byteOffset = RenderTargetOffset(blockX * layout.BlockWidth, blockY * layout.BlockHeight, layout.BytesPerElement); + byteOffset = layout.Kind == TileBlockKind.RenderTarget64KBGen5 + ? Gen5RenderTargetOffset(blockX * layout.BlockWidth, blockY * layout.BlockHeight, layout.BytesPerElement) + : RenderTargetOffset(blockX * layout.BlockWidth, blockY * layout.BlockHeight, layout.BytesPerElement); } - if (layout.Kind is TileBlockKind.RenderTarget64KB or TileBlockKind.Depth64KB) + if (layout.Kind is TileBlockKind.RenderTarget64KB or TileBlockKind.RenderTarget64KBGen5 or TileBlockKind.Depth64KB) { byteOffset ^= ((blockZ & 8) << 5) ^ ((blockZ & 4) << 7) ^ ((blockZ & 2) << 9) ^ ((blockZ & 1) << 11); } diff --git a/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs b/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs index 9871cd7..989e3a8 100644 --- a/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs +++ b/src/SharpEmu.Libs/Gpu/Images/TileGeometry.cs @@ -32,6 +32,7 @@ public enum TileBlockKind : uint Prt64KB3D, RenderTarget64KB, Depth64KB, + RenderTarget64KBGen5, } public readonly record struct TileBlockLayout(TileBlockKind Kind, uint BytesPerElement, uint BlockSize, uint BlockWidth, uint BlockHeight, uint BlockDepth) @@ -109,6 +110,7 @@ public static partial class TileGeometry new(65536, Thick64KB, 16), new(65536, Thin64KB, 16), new(65536, Thin64KB, 8), + new(65536, Thin64KB, 16), ]; private static readonly Log2BlockDimensions[][] MsaaBlocks = @@ -247,6 +249,7 @@ public static partial class TileGeometry case TileBlockKind.Standard64KB: case TileBlockKind.Prt64KB: case TileBlockKind.RenderTarget64KB: + case TileBlockKind.RenderTarget64KBGen5: rule = new MipTailRule(TailThin64KB[index], block.BlockWidth >> 1, block.BlockHeight); return true; default: @@ -297,7 +300,7 @@ public static partial class TileGeometry kind = TileBlockKind.Depth64KB; break; case GuestTileMode.RenderTarget: - kind = TileBlockKind.RenderTarget64KB; + kind = TileBlockKind.RenderTarget64KBGen5; break; default: return false; @@ -308,7 +311,7 @@ public static partial class TileGeometry return false; } - if (kind is TileBlockKind.Depth64KB or TileBlockKind.RenderTarget64KB && + if (kind is TileBlockKind.Depth64KB or TileBlockKind.RenderTarget64KB or TileBlockKind.RenderTarget64KBGen5 && GuestPixelFormats.RenderTargetBytesPerElement(format) != element.Bytes) { return false; diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/TilerShaders.cs b/src/SharpEmu.ShaderCompiler.Vulkan/TilerShaders.cs index 0662b24..3c9e8bc 100644 --- a/src/SharpEmu.ShaderCompiler.Vulkan/TilerShaders.cs +++ b/src/SharpEmu.ShaderCompiler.Vulkan/TilerShaders.cs @@ -15,6 +15,7 @@ public enum TilerBlockShape Prt64KB3D, RenderTarget64KB, Depth64KB, + RenderTarget64KBGen5, } // Assembles the tiler compute shaders: block copies per shape and element size, @@ -251,6 +252,15 @@ public static class TilerShaders return offset; } + private static Term[] Gen5RenderTargetTerms(uint bytes) => bytes switch + { + 1 => [Y(2, 0x008), Y(4, 0x010), Y(3, 0x0a0), Y(5, 0xf00), Y(6, 0x1000), Y(7, 0x4000), X(0, 0x007), X(3, 0x040), X(5, 0x300), X(4, 0x400), X(6, 0x800), X(7, 0x2000), X(8, 0x8000)], + 2 => [Y(4, 0x070), Y(5, 0xf00), Y(8, 0x5000), X(1, 0x00e), X(4, 0x480), X(5, 0x300), X(6, 0x800), X(7, 0x2000), X(8, 0x8000)], + 4 => [Y(4, 0x070), Y(5, 0xf00), Y(9, 0x1000), Y(8, 0x4000), X(2, 0x00c), X(5, 0x380), X(4, 0x400), X(6, 0x800), X(9, 0xa000)], + 8 => [Y(4, 0x010), Y(6, 0x080), Y(5, 0xf00), Y(10, 0x5000), X(3, 0x008), X(4, 0x460), X(5, 0x300), X(6, 0x800), X(10, 0x2000), X(9, 0x8000)], + _ => [X(4, 0x410), X(5, 0x340), X(6, 0x800), X(11, 0xa000), Y(5, 0xf20), Y(6, 0x080), Y(10, 0x1000), Y(11, 0x4000)], + }; + private static Term[] DepthTerms(uint bytes) => bytes switch { 1 => [X(0, 1), X(1, 0x004), X(2, 0x010), X(3, 0x040), X(5, 0x300), X(4, 0x400), X(6, 0x800), X(7, 0x2000), X(8, 0x8000), Y(1, 0x002), Y(2, 0x008), Y(3, 0x0a0), Y(5, 0xf00), Y(6, 0x1000), Y(7, 0x4000)], @@ -314,7 +324,7 @@ public static class TilerShaders TilerBlockShape.Standard256B => (ThinExtent(256, bytes, 16, 8, 4), 256), TilerBlockShape.Standard4KB => (ThinExtent(4096, bytes, 64, 32, 16), 4096), TilerBlockShape.Standard4KB3D => (Thick4KBExtent(bytes), 4096), - TilerBlockShape.Standard64KB or TilerBlockShape.Prt64KB or TilerBlockShape.RenderTarget64KB => (ThinExtent(65536, bytes, 256, 128, 64), 65536), + TilerBlockShape.Standard64KB or TilerBlockShape.Prt64KB or TilerBlockShape.RenderTarget64KB or TilerBlockShape.RenderTarget64KBGen5 => (ThinExtent(65536, bytes, 256, 128, 64), 65536), TilerBlockShape.Standard64KB3D or TilerBlockShape.Prt64KB3D => (Thick64KBExtent(bytes), 65536), TilerBlockShape.Depth64KB => (ThinExtent(65536, bytes, 256, 128, 128), 65536), _ => throw new ArgumentOutOfRangeException(nameof(shape)), @@ -348,6 +358,8 @@ public static class TilerShaders } case TilerBlockShape.RenderTarget64KB: return shaderModule.Xor(RenderTargetOffset(shaderModule, x, y, bytes), ZSpread(shaderModule, z)); + case TilerBlockShape.RenderTarget64KBGen5: + return shaderModule.Xor(XorTerms(shaderModule, x, y, z, Gen5RenderTargetTerms(bytes)), ZSpread(shaderModule, z)); case TilerBlockShape.Depth64KB: return shaderModule.Xor(ZSpread(shaderModule, z), XorTerms(shaderModule, x, y, z, DepthTerms(bytes))); default: