From ceb7c6765e9ae1b66ac0714ec2b958422db96bc2 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 16 Jul 2026 11:02:23 +0200 Subject: [PATCH] Advances to rendering: broad guards, fix tiling (thin 64KB), ignore noperspective --- src/graphics/guest_gpu/tile.cpp | 67 ++++++++--------- src/graphics/host_gpu/renderer/debug.cpp | 4 +- .../host_gpu/renderer/descriptors.cpp | 12 ++- src/graphics/host_gpu/renderer/imageView.h | 8 ++ .../host_gpu/renderer/textureCache.cpp | 6 +- tests/ShaderRecompilerComputeTests.cpp | 75 ++++++++++++++++++- 6 files changed, 132 insertions(+), 40 deletions(-) diff --git a/src/graphics/guest_gpu/tile.cpp b/src/graphics/guest_gpu/tile.cpp index f28502581..c14ac0907 100644 --- a/src/graphics/guest_gpu/tile.cpp +++ b/src/graphics/guest_gpu/tile.cpp @@ -299,10 +299,10 @@ static bool Gen5Standard64KBLayout(uint32_t format, uint32_t* bytes_per_element, } } -static bool Gen5RenderTargetBlockSizeFromElementBytes(uint32_t bytes_per_element, - uint32_t* block_width, - uint32_t* block_height) { - // AGC render-target +static bool Gen5Thin64KBBlockSizeFromElementBytes(uint32_t bytes_per_element, + uint32_t* block_width, + uint32_t* block_height) { + // AGC thin 64 KiB block table, shared by depth and render-target tiles. switch (bytes_per_element) { case 1: *block_width = 256; @@ -1182,7 +1182,7 @@ static void ConvertRenderTargetTyped(uint32_t width, uint32_t height, uint32_t p uint32_t block_width = 0; uint32_t block_height = 0; - EXIT_NOT_IMPLEMENTED(!Gen5RenderTargetBlockSizeFromElementBytes( + EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes( static_cast(sizeof(T)), &block_width, &block_height)); const uint64_t blocks_per_row = (static_cast(pitch) + block_width - 1u) / block_width; @@ -2448,8 +2448,8 @@ bool TileGetDepthSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t uint32_t TileGetRenderTargetPitch(uint32_t width, uint32_t bytes_per_element) { uint32_t block_width = 0; uint32_t block_height = 0; - if (width == 0 || !Gen5RenderTargetBlockSizeFromElementBytes(bytes_per_element, &block_width, - &block_height)) { + if (width == 0 || !Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, &block_width, + &block_height)) { return 0; } const uint64_t pitch = (static_cast(width) + block_width - 1u) & @@ -2463,8 +2463,8 @@ bool TileGetRenderTargetSize(uint32_t width, uint32_t height, uint32_t pitch, uint32_t block_width = 0; uint32_t block_height = 0; if (height == 0 || pitch == 0 || - !Gen5RenderTargetBlockSizeFromElementBytes(bytes_per_element, &block_width, - &block_height) || + !Gen5Thin64KBBlockSizeFromElementBytes(bytes_per_element, &block_width, + &block_height) || pitch != TileGetRenderTargetPitch(width, bytes_per_element)) { return false; } @@ -2590,7 +2590,7 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32 bytes_per_element != 0 && tile == 27 && levels == 1) { uint32_t block_width = 0; uint32_t block_height = 0; - EXIT_NOT_IMPLEMENTED(!Gen5RenderTargetBlockSizeFromElementBytes( + EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes( bytes_per_element, &block_width, &block_height)); const uint32_t padded_width = AlignUp(pitch, block_width); @@ -2619,7 +2619,7 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32 bytes_per_element != 0 && tile == 27 && levels > 1) { uint32_t block_width = 0; uint32_t block_height = 0; - EXIT_NOT_IMPLEMENTED(!Gen5RenderTargetBlockSizeFromElementBytes( + EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes( bytes_per_element, &block_width, &block_height)); const uint32_t bytes_log2 = IntLog2(bytes_per_element); @@ -2842,17 +2842,15 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32 if (tile == 24 && levels == 1) { const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format); if (bytes_per_element != 0) { - uint32_t block_width = 0; - switch (bytes_per_element) { - case 1: block_width = 512; break; - case 2: block_width = 256; break; - case 4: block_width = 128; break; - default: break; - } + uint32_t block_width = 0; + uint32_t block_height = 0; + const bool supported = bytes_per_element <= 4 && + Gen5Thin64KBBlockSizeFromElementBytes( + bytes_per_element, &block_width, &block_height); - if (block_width != 0) { + if (supported) { const uint32_t padded_width = (pitch + block_width - 1u) & ~(block_width - 1u); - const uint32_t padded_height = (height + 127u) & ~127u; + const uint32_t padded_height = AlignUp(height, block_height); const uint32_t size = padded_width * padded_height * bytes_per_element; if (total_size != nullptr) { @@ -2878,15 +2876,13 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32 if (tile == 24 && levels > 1) { const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format); if (bytes_per_element != 0) { - uint32_t block_width = 0; - switch (bytes_per_element) { - case 1: block_width = 512; break; - case 2: block_width = 256; break; - case 4: block_width = 128; break; - default: break; - } + uint32_t block_width = 0; + uint32_t block_height = 0; + const bool supported = bytes_per_element <= 4 && + Gen5Thin64KBBlockSizeFromElementBytes( + bytes_per_element, &block_width, &block_height); - if (block_width != 0) { + if (supported) { static bool logged = false; if (!logged) { LOGF("\t temporary: sizing PS5 depth tiled texture with mips, format = %u, " @@ -2902,7 +2898,7 @@ void TileGetTextureSize(uint32_t format, uint32_t width, uint32_t height, uint32 offset = AlignUp(offset, 65536u); const uint32_t padded_width = AlignUp(mip_pitch, block_width); - const uint32_t padded_height = AlignUp(mip_height, 128u); + const uint32_t padded_height = AlignUp(mip_height, block_height); const uint32_t size = padded_width * padded_height * bytes_per_element; if (level_sizes != nullptr) { @@ -3130,7 +3126,7 @@ uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t levels, u if (const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format); bytes_per_element != 0) { uint32_t block_width = 0; uint32_t block_height = 0; - EXIT_NOT_IMPLEMENTED(!Gen5RenderTargetBlockSizeFromElementBytes( + EXIT_NOT_IMPLEMENTED(!Gen5Thin64KBBlockSizeFromElementBytes( bytes_per_element, &block_width, &block_height)); pitch = AlignUp(pitch, block_width); } @@ -3152,11 +3148,12 @@ uint32_t TileGetTexturePitch(uint32_t format, uint32_t width, uint32_t levels, u pitch = AlignUp(pitch, (1u << block_width_log2) * texels_per_element_wide); } if (tile == 24) { - switch (Prospero::NumBytesPerElement(format)) { - case 1: pitch = (pitch + 511u) & ~511u; break; - case 2: pitch = (pitch + 255u) & ~255u; break; - case 4: pitch = (pitch + 127u) & ~127u; break; - default: break; + const uint32_t bytes_per_element = Prospero::NumBytesPerElement(format); + uint32_t block_width = 0; + uint32_t block_height = 0; + if (bytes_per_element <= 4 && Gen5Thin64KBBlockSizeFromElementBytes( + bytes_per_element, &block_width, &block_height)) { + pitch = AlignUp(pitch, block_width); } } diff --git a/src/graphics/host_gpu/renderer/debug.cpp b/src/graphics/host_gpu/renderer/debug.cpp index a1a985be3..390a0a9d9 100644 --- a/src/graphics/host_gpu/renderer/debug.cpp +++ b/src/graphics/host_gpu/renderer/debug.cpp @@ -692,11 +692,13 @@ static void ClipPrint(const char* func, const HW::ClipControl& c) { } static void ClipCheck(const HW::ClipControl& c) { + // dx_linear_attr_clip_enable preserves linear (noperspective) attributes at clip-generated + // vertices, which Vulkan provides as part of clipping and interpolation. EXIT_NOT_IMPLEMENTED(c.user_clip_planes != 0 || c.user_clip_plane_mode != 0 || c.vertex_kill_any || c.min_z_clip_disable || c.max_z_clip_disable || c.user_clip_plane_negate_y || c.clip_disable || c.user_clip_plane_cull_only || c.cull_on_clipping_error_disable || - c.linear_attribute_clip_enable || c.force_viewport_index_from_vs_enable); + c.force_viewport_index_from_vs_enable); } static void RcPrint(const char* func, const HW::RenderControl& c) { diff --git a/src/graphics/host_gpu/renderer/descriptors.cpp b/src/graphics/host_gpu/renderer/descriptors.cpp index 35f8c07d1..fb4807356 100644 --- a/src/graphics/host_gpu/renderer/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/descriptors.cpp @@ -310,7 +310,10 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource, const ShaderTextureResource& descriptor) { - const auto tile = descriptor.TileMode(); + const auto tile = descriptor.TileMode(); + const auto width = static_cast(descriptor.Width5()) + 1u; + const auto height = static_cast(descriptor.Height5()) + 1u; + const auto depth = static_cast(descriptor.Depth()) + 1u; const bool is_2d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) && descriptor.Depth() == 0; @@ -320,8 +323,13 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag descriptor.BaseArray5() <= descriptor.Depth(); const bool is_3d = resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim3D && descriptor.Type() == Prospero::GpuEnumValue(Prospero::ImageType::kColor3D); + const bool supported_depth_tile = + tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && !resource.read && + resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint && + IsSupportedStorageDepthTile(descriptor.Format(), descriptor.Type(), width, height, depth); const bool supported_tile = tile == Prospero::GpuEnumValue(Prospero::TileMode::kLinear) || - tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); + tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) || + supported_depth_tile; const bool supported_swizzle = IsSupportedStorageSwizzle(descriptor.Format(), descriptor.DstSelXYZW()) && (descriptor.DstSelXYZW() == DstSel(4, 5, 6, 7) || !resource.read); diff --git a/src/graphics/host_gpu/renderer/imageView.h b/src/graphics/host_gpu/renderer/imageView.h index 207184fd0..79c015d34 100644 --- a/src/graphics/host_gpu/renderer/imageView.h +++ b/src/graphics/host_gpu/renderer/imageView.h @@ -21,6 +21,14 @@ namespace Libs::Graphics { swizzle == DstSel(5, 6, 7, 4)); } +[[nodiscard]] inline bool IsSupportedStorageDepthTile(uint32_t format, uint32_t type, + uint32_t width, uint32_t height, + uint32_t depth) noexcept { + return format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt) && + type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) && width == 1 && + height == 1 && depth == 1; +} + [[noreturn]] inline void UnsupportedColorView(const char* usage, VkFormat image_format, VkFormat view_format, uint32_t swizzle) noexcept { EXIT("unsupported %s color image view: image_format=%d view_format=%d swizzle=0x%03x\n", usage, diff --git a/src/graphics/host_gpu/renderer/textureCache.cpp b/src/graphics/host_gpu/renderer/textureCache.cpp index 46227466a..acb1769e1 100644 --- a/src/graphics/host_gpu/renderer/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/textureCache.cpp @@ -1549,12 +1549,16 @@ StorageTextureVulkanImage* TextureCache::FindStorageTexture(CommandBuffer* com const bool supported_type = info.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) || info.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) || info.type == Prospero::GpuEnumValue(Prospero::ImageType::kColor3D); + const bool supported_depth_tile = + info.tile == Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && + IsSupportedStorageDepthTile(info.format, info.type, info.width, info.height, info.depth); if (command == nullptr || ctx == nullptr || info.address == 0 || info.size == 0 || info.address >= TRACKER_ADDRESS_SIZE || info.size > TRACKER_ADDRESS_SIZE - info.address || info.width == 0 || info.height == 0 || info.depth == 0 || info.levels != 1 || info.base_level != 0 || info.view_levels != 1 || info.base_array != 0 || !supported_type || (info.tile != Prospero::GpuEnumValue(Prospero::TileMode::kLinear) && - info.tile != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget)) || + info.tile != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) && + !supported_depth_tile) || !IsSupportedStorageSwizzle(info.format, info.swizzle)) { EXIT("TextureCache: unsupported storage-image request, command=%p ctx=%p " "addr=0x%016" PRIx64 " size=0x%016" PRIx64 diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 0ef9f4a3d..16582552f 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -9064,6 +9064,15 @@ ShaderTextureResource BasicUintArrayStorageTextureDescriptor() { 0x00000000u, 0x00700000u, 0x00000000u, 0x00000000u}}; } +ShaderRecompiler::IR::ImageResource Ppsa14053DepthTileStorageTextureResource() { + return BasicUintArrayStorageTextureResource(); +} + +ShaderTextureResource Ppsa14053DepthTileStorageTextureDescriptor() { + return {{0x20144c00u, 0x00500000u, 0x00000000u, 0xd1800204u, + 0x00000000u, 0x00700000u, 0x00000000u, 0x00000000u}}; +} + ShaderRecompiler::IR::ImageResource BasicUintVolumeStorageTextureResource() { auto resource = BasicStorageTextureResource(); resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; @@ -9127,6 +9136,14 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() { } else if (std::strcmp(kind, "uint-resource-float-format") == 0) { resource = BasicUintArrayStorageTextureResource(); descriptor = BasicArrayStorageTextureDescriptor(); + } else if (std::strcmp(kind, "depth-tile-read") == 0) { + resource = Ppsa14053DepthTileStorageTextureResource(); + descriptor = Ppsa14053DepthTileStorageTextureDescriptor(); + resource.read = true; + } else if (std::strcmp(kind, "depth-tile-extent") == 0) { + resource = Ppsa14053DepthTileStorageTextureResource(); + descriptor = Ppsa14053DepthTileStorageTextureDescriptor(); + descriptor.fields[2] |= 1u; } else { std::_Exit(0x7e); } @@ -9207,6 +9224,21 @@ void CheckBasicStorageTextureDescriptor() { "PPSA21268 uint 3D storage descriptor fixture is malformed"); ValidateStorageTexture(BasicUintVolumeStorageTextureResource(), uint_volume, 0x10000); + const auto depth_tile = Ppsa14053DepthTileStorageTextureDescriptor(); + Require("BasicStorageTexture", "PPSA14053 depth-tile descriptor", + depth_tile.Base40() == 0x20144c0000ull && depth_tile.Width5() + 1u == 1 && + depth_tile.Height5() + 1u == 1 && depth_tile.Depth() + 1u == 1 && + depth_tile.BaseArray5() == 0 && + depth_tile.Type() == + Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray) && + depth_tile.Format() == + Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt) && + depth_tile.TileMode() == + Prospero::GpuEnumValue(Prospero::TileMode::kDepth) && + depth_tile.DstSelXYZW() == DstSel(4, 0, 0, 1), + "PPSA14053 write-only depth-tile storage descriptor fixture is malformed"); + ValidateStorageTexture(Ppsa14053DepthTileStorageTextureResource(), depth_tile, 0x10000); + char path[MAX_PATH]{}; Require("BasicStorageTexture", "host", GetModuleFileNameA(nullptr, path, MAX_PATH) != 0, @@ -9214,7 +9246,8 @@ void CheckBasicStorageTextureDescriptor() { for (const char *kind : {"resource", "type", "tile", "mip", "swizzle", "linear-rgb1-read", "bgra-read", "yzwx-read", "yzwx-format", "array-base-view", "reserved", "metadata", "uint-format", - "uint-resource-float-format"}) { + "uint-resource-float-format", "depth-tile-read", + "depth-tile-extent"}) { std::string command = std::string("\"") + path + "\" --storage-texture-descriptor-death " + kind; std::vector mutable_command(command.begin(), command.end()); @@ -9268,6 +9301,45 @@ void CheckStorageTextureLinearUploadLayout() { std::printf("[host] %-32s ok\n", "StorageTextureLinearUpload"); } +void CheckStorageTextureDepthTileUploadLayout() { + constexpr uint32_t format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt); + constexpr uint32_t width = 1; + constexpr uint32_t height = 1; + constexpr uint32_t depth = 1; + constexpr uint32_t tile = Prospero::GpuEnumValue(Prospero::TileMode::kDepth); + const auto pitch = TileGetTexturePitch(format, width, 1, tile); + TileSizeAlign slice{}; + TileSizeAlign total{}; + TileSizeOffset level{}; + TilePaddedSize padded{}; + TileGetTextureSize(format, width, height, pitch, 1, tile, &slice, &level, + &padded); + TileGetTextureTotalSize(format, width, height, depth, pitch, 1, tile, false, + &total); + const auto layout = TextureCalcUploadLayout( + format, width, height, 1, depth, pitch, tile, total.size, true, false, + false, "StorageTextureDepthTileTest"); + const auto regions = TextureBuildUploadRegions( + layout, VK_FORMAT_R8_UINT, width, height, depth, 1, true, false, + TextureUploadDestination::MipLevels, + TextureUploadSliceLayout::MipChainPerSlice); + Require("StorageTextureDepthTileUpload", "PPSA14053 layout", + pitch == 256 && padded.width == 256 && padded.height == 256 && + slice.size == 0x10000 && slice.align == 0x10000 && + level.size == slice.size && level.offset == 0 && + total.size == slice.size && total.align == slice.align && + layout.tile == tile && layout.fmt_tiled_depth && + !layout.fmt_tiled_render_target && layout.pitch == pitch && + layout.slice_stride == total.size && regions.size() == 1 && + regions[0].offset == 0 && regions[0].width == width && + regions[0].height == height && regions[0].pitch == pitch && + TextureCalcUploadSize(layout, regions, 1, depth, + TextureUploadSliceLayout::MipChainPerSlice) == + total.size, + "1x1 R8_UINT depth tile lost its PS5 64 KiB block footprint"); + std::printf("[host] %-32s ok\n", "StorageTextureDepthTileUpload"); +} + void CheckStorageTextureLinearReadbackLayout() { ImageInfo info{}; info.address = 0x4bcc40100ull; @@ -11049,6 +11121,7 @@ int main(int argc, char **argv) { CheckBufferCacheRangeMerge(); CheckBasicStorageTextureDescriptor(); CheckStorageTextureLinearUploadLayout(); + CheckStorageTextureDepthTileUploadLayout(); CheckStorageTextureLinearReadbackLayout(); CheckStorageImageSwizzleSpecializationId(); CheckRenderTargetTileRoundTrip();