From dd58deae1559d1a27b76b9137152b8975fe352f3 Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 17 Jul 2026 08:39:57 +0200 Subject: [PATCH] D16S8 support --- .../host_gpu/renderer/depthRenderTarget.cpp | 66 ++++-- .../host_gpu/renderer/descriptors.cpp | 12 -- src/graphics/host_gpu/renderer/imageInfo.h | 146 ++++++++++++- src/graphics/host_gpu/renderer/imageView.h | 33 ++- .../host_gpu/renderer/renderCompute.cpp | 38 ++-- .../host_gpu/renderer/textureCache.cpp | 24 +-- src/graphics/host_gpu/renderer/tiler.cpp | 80 ++++++-- tests/ShaderRecompilerComputeTests.cpp | 194 ++++++++++++++++-- 8 files changed, 469 insertions(+), 124 deletions(-) diff --git a/src/graphics/host_gpu/renderer/depthRenderTarget.cpp b/src/graphics/host_gpu/renderer/depthRenderTarget.cpp index 2f8702eae..d23df577c 100644 --- a/src/graphics/host_gpu/renderer/depthRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/depthRenderTarget.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include namespace Libs::Graphics { @@ -59,6 +60,38 @@ static bool UsesStencilOpValue(uint8_t fail, uint8_t pass, uint8_t depth_fail) { depth_fail == Prospero::GpuEnumValue(Prospero::StencilOp::kReplaceOp); } +[[nodiscard]] static VkFormat ResolveHostDepthAttachmentFormat(GraphicContext* ctx, + const DepthFormatPolicy& policy, + bool has_stencil) { + if (!has_stencil) { + return policy.depth_attachment_format; + } + switch (policy.depth_format) { + case Prospero::DepthFormat::kZ32F: return policy.stencil_attachment_formats.front(); + case Prospero::DepthFormat::kZ16: { + if (ctx == nullptr) { + return VK_FORMAT_UNDEFINED; + } + // Kyty selects one physical device. Query its optional D16S8-compatible formats once. + static std::once_flag once; + static VkFormat selected = VK_FORMAT_UNDEFINED; + std::call_once(once, [&] { + for (const auto format: policy.stencil_attachment_formats) { + VkImageFormatProperties properties {}; + if (vkGetPhysicalDeviceImageFormatProperties( + ctx->physical_device, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, + DepthTargetImageUsage(), 0, &properties) == VK_SUCCESS) { + selected = format; + break; + } + } + }); + return selected; + } + default: return VK_FORMAT_UNDEFINED; + } +} + // NOLINTNEXTLINE(readability-function-cognitive-complexity) void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const HW::Context& hw, RenderDepthInfo* r) { @@ -200,26 +233,21 @@ void ResolveRenderDepthTarget(uint64_t submit_id, CommandBuffer* buffer, const H (z.pitch_div8_minus1 != 0 || z.height_div8_minus1 != 0 || z.slice_div64_minus1 != 0))) { DepthFatal("inconsistent depth extent or encoded layout"); } - uint32_t guest_format = 0; - uint32_t bytes = 0; - switch (static_cast(z.z_info.format)) { - case Prospero::DepthFormat::kZ16: - if (has_stencil) { - DepthFatal("Z16 plus stencil is unsupported"); - } - r->format = VK_FORMAT_D16_UNORM; - guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm); - bytes = 2; - break; - case Prospero::DepthFormat::kZ32F: - r->format = has_stencil ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT; - guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); - bytes = 4; - break; - default: DepthFatal("unsupported depth format"); + const auto* policy = FindDepthFormatPolicy(z.z_info.format); + if (policy == nullptr) { + DepthFatal("unsupported depth/stencil format pair"); } - const auto pitch = TileGetTexturePitch(guest_format, width, 1, - Prospero::GpuEnumValue(Prospero::TileMode::kDepth)); + const auto ideal_format = DepthAttachmentFormat(*policy, has_stencil); + r->format = + ResolveHostDepthAttachmentFormat(g_render_ctx->GetGraphicCtx(), *policy, has_stencil); + if (r->format == VK_FORMAT_UNDEFINED) { + DepthFatal("no host depth/stencil format supports required usage for %s", + string_VkFormat(ideal_format)); + } + const uint32_t guest_format = Prospero::GpuEnumValue(policy->guest_format); + const uint32_t bytes = policy->bytes_per_element; + const auto pitch = TileGetTexturePitch(guest_format, width, 1, + Prospero::GpuEnumValue(Prospero::TileMode::kDepth)); if (z.pitch_height_valid && ((static_cast(z.pitch_div8_minus1) + 1u) * 8u != pitch || (static_cast(z.height_div8_minus1) + 1u) * 8u != ((static_cast(height) + 7u) & ~7ull))) { diff --git a/src/graphics/host_gpu/renderer/descriptors.cpp b/src/graphics/host_gpu/renderer/descriptors.cpp index 7e3fa7ba9..ff5019431 100644 --- a/src/graphics/host_gpu/renderer/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/descriptors.cpp @@ -276,18 +276,6 @@ ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource, } } -static bool IsSupportedSampledDepthFormat(VkFormat image_format, uint32_t guest_format, - VkFormat view_format) { - const bool d16 = image_format == VK_FORMAT_D16_UNORM && - guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) && - view_format == VK_FORMAT_R16_UNORM; - const bool d32 = - (image_format == VK_FORMAT_D32_SFLOAT || image_format == VK_FORMAT_D32_SFLOAT_S8_UINT) && - guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) && - view_format == VK_FORMAT_R32_SFLOAT; - return d16 || d32; -} - bool IsSupportedDepthTargetDescriptor(const ShaderTextureResource& descriptor, const VulkanImage& image) { const auto width = static_cast(descriptor.Width5()) + 1u; diff --git a/src/graphics/host_gpu/renderer/imageInfo.h b/src/graphics/host_gpu/renderer/imageInfo.h index ee0086456..a24051a87 100644 --- a/src/graphics/host_gpu/renderer/imageInfo.h +++ b/src/graphics/host_gpu/renderer/imageInfo.h @@ -5,6 +5,7 @@ #include "graphics/guest_gpu/gpu_defs.h" #include "graphics/host_gpu/regionDefinitions.h" +#include #include #include #include @@ -91,6 +92,139 @@ struct DepthTargetInfo { bool stencil_htile_compressed = false; }; +struct DepthFormatPolicy { + Prospero::DepthFormat depth_format; + Prospero::BufferFormat guest_format; + uint32_t bytes_per_element; + VkFormat sampled_view_format; + VkFormat depth_attachment_format; + std::array stencil_attachment_formats; +}; + +inline constexpr std::array DEPTH_FORMAT_POLICIES {{ + {Prospero::DepthFormat::kZ16, + Prospero::BufferFormat::k16UNorm, + 2, + VK_FORMAT_R16_UNORM, + VK_FORMAT_D16_UNORM, + {VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT}}, + {Prospero::DepthFormat::kZ32F, + Prospero::BufferFormat::k32Float, + 4, + VK_FORMAT_R32_SFLOAT, + VK_FORMAT_D32_SFLOAT, + {VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_UNDEFINED, VK_FORMAT_UNDEFINED}}, +}}; + +[[nodiscard]] inline constexpr const DepthFormatPolicy* +FindDepthFormatPolicy(uint32_t depth_format) noexcept { + for (const auto& policy: DEPTH_FORMAT_POLICIES) { + if (Prospero::GpuEnumValue(policy.depth_format) == depth_format) { + return &policy; + } + } + return nullptr; +} + +[[nodiscard]] inline constexpr const DepthFormatPolicy* +FindGuestDepthFormatPolicy(uint32_t guest_format) noexcept { + for (const auto& policy: DEPTH_FORMAT_POLICIES) { + if (Prospero::GpuEnumValue(policy.guest_format) == guest_format) { + return &policy; + } + } + return nullptr; +} + +[[nodiscard]] inline constexpr bool IsStencilAttachmentFormat(const DepthFormatPolicy& policy, + VkFormat format) noexcept { + for (const auto candidate: policy.stencil_attachment_formats) { + if (candidate != VK_FORMAT_UNDEFINED && candidate == format) { + return true; + } + } + return false; +} + +[[nodiscard]] inline constexpr VkFormat DepthAttachmentFormat(const DepthFormatPolicy& policy, + bool has_stencil) noexcept { + return has_stencil ? policy.stencil_attachment_formats.front() : policy.depth_attachment_format; +} + +[[nodiscard]] inline constexpr VkFormat DepthAttachmentFormat(uint32_t depth_format, + uint32_t stencil_format) noexcept { + bool has_stencil = false; + switch (static_cast(stencil_format)) { + case Prospero::StencilFormat::kInvalid: break; + case Prospero::StencilFormat::k8UInt: has_stencil = true; break; + default: return VK_FORMAT_UNDEFINED; + } + const auto* policy = FindDepthFormatPolicy(depth_format); + return policy == nullptr ? VK_FORMAT_UNDEFINED : DepthAttachmentFormat(*policy, has_stencil); +} + +[[nodiscard]] inline constexpr VkImageUsageFlags DepthTargetImageUsage() noexcept { + return VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; +} + +[[nodiscard]] inline constexpr VkFormat DepthAspectTransferFormat(VkFormat format) noexcept { + switch (format) { + case VK_FORMAT_D16_UNORM: + case VK_FORMAT_D16_UNORM_S8_UINT: return VK_FORMAT_D16_UNORM; + case VK_FORMAT_D24_UNORM_S8_UINT: return VK_FORMAT_X8_D24_UNORM_PACK32; + case VK_FORMAT_D32_SFLOAT: + case VK_FORMAT_D32_SFLOAT_S8_UINT: return VK_FORMAT_D32_SFLOAT; + default: return VK_FORMAT_UNDEFINED; + } +} + +[[nodiscard]] inline constexpr uint32_t DepthAspectTransferBytes(VkFormat format) noexcept { + switch (DepthAspectTransferFormat(format)) { + case VK_FORMAT_D16_UNORM: return 2; + case VK_FORMAT_X8_D24_UNORM_PACK32: + case VK_FORMAT_D32_SFLOAT: return 4; + default: return 0; + } +} + +[[nodiscard]] inline constexpr uint32_t EncodeD16AsD24(uint16_t value) noexcept { + // Preserve the guest UNORM value when widening 16 bits to the 24-bit transfer plane. + return static_cast((static_cast(value) * 0x00ffffffu + 0x7fffu) / 0xffffu); +} + +[[nodiscard]] inline uint32_t EncodeD16AsD32(uint16_t value) noexcept { + return std::bit_cast(static_cast(value) / 65535.0f); +} + +[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(VkFormat image_format, + uint32_t guest_format, + VkFormat view_format) noexcept { + const auto* policy = FindGuestDepthFormatPolicy(guest_format); + return policy != nullptr && view_format == policy->sampled_view_format && + (image_format == policy->depth_attachment_format || + IsStencilAttachmentFormat(*policy, image_format)); +} + +[[nodiscard]] inline constexpr bool IsSupportedSampledDepthFormat(VkFormat image_format, + VkFormat view_format) noexcept { + for (const auto& policy: DEPTH_FORMAT_POLICIES) { + if (IsSupportedSampledDepthFormat(image_format, Prospero::GpuEnumValue(policy.guest_format), + view_format)) { + return true; + } + } + return false; +} + +[[nodiscard]] inline constexpr bool IsSupportedDepthTargetFormat(const DepthTargetInfo& info) { + const bool has_stencil = info.stencil_address != 0 || info.stencil_size != 0; + const auto* policy = FindGuestDepthFormatPolicy(info.guest_format); + return policy != nullptr && info.bytes_per_element == policy->bytes_per_element && + (has_stencil ? IsStencilAttachmentFormat(*policy, info.format) + : info.format == policy->depth_attachment_format); +} + enum class VideoOutCompression : uint8_t { Uncompressed, Dcc256_64_64, Unsupported }; struct VideoOutInfo { @@ -205,13 +339,13 @@ SelectStorageSampledViewShape(uint32_t type, uint32_t depth, uint32_t backing_la switch (static_cast(type)) { case Prospero::ImageType::kColor2D: return depth == 1 && backing_layers == 1 ? StorageSampledViewShape::Image2D - : StorageSampledViewShape::Unsupported; + : StorageSampledViewShape::Unsupported; case Prospero::ImageType::kColor2DArray: return depth != 0 && depth == backing_layers ? StorageSampledViewShape::Image2DArray - : StorageSampledViewShape::Unsupported; + : StorageSampledViewShape::Unsupported; case Prospero::ImageType::kColor3D: return depth != 0 && backing_layers == 1 ? StorageSampledViewShape::Image3D - : StorageSampledViewShape::Unsupported; + : StorageSampledViewShape::Unsupported; default: return StorageSampledViewShape::Unsupported; } } @@ -443,12 +577,12 @@ ClassifyBufferImageWrite(uint64_t buffer_address, uint64_t buffer_size, uint64_t return BufferImageWrite::None; } const bool exact = buffer_address == image_address && buffer_size == image_size; - const auto offset = buffer_address >= image_address ? buffer_address - image_address : UINT64_MAX; + const auto offset = + buffer_address >= image_address ? buffer_address - image_address : UINT64_MAX; const bool contained = offset <= image_size && buffer_size <= image_size - offset; const bool buffer_page_aligned = ((buffer_address | buffer_size) & (TRACKER_PAGE_SIZE - 1)) == 0; - const bool image_page_aligned = - ((image_address | image_size) & (TRACKER_PAGE_SIZE - 1)) == 0; + const bool image_page_aligned = ((image_address | image_size) & (TRACKER_PAGE_SIZE - 1)) == 0; switch (binding) { case BufferImageBinding::Texture: return contained && image_page_aligned && !image_gpu_modified diff --git a/src/graphics/host_gpu/renderer/imageView.h b/src/graphics/host_gpu/renderer/imageView.h index 59ff94ed9..5a1d8aef5 100644 --- a/src/graphics/host_gpu/renderer/imageView.h +++ b/src/graphics/host_gpu/renderer/imageView.h @@ -3,21 +3,22 @@ #include "common/assert.h" #include "graphics/host_gpu/graphicContext.h" +#include "graphics/host_gpu/renderer/imageInfo.h" #include "graphics/shader/recompiler/ShaderIR.h" #include "graphics/shader/shader.h" namespace Libs::Graphics { [[nodiscard]] inline bool IsSupportedStorageSwizzle(uint32_t format, uint32_t swizzle) noexcept { - const bool single_channel = format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm) || - format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt) || - format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UInt) || - format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt) || - format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16Float) || - format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); + const bool single_channel = + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8UNorm) || + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt) || + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UInt) || + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt) || + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16Float) || + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); return swizzle == DstSel(4, 5, 6, 7) || - (single_channel && - (swizzle == DstSel(4, 0, 0, 0) || swizzle == DstSel(4, 0, 0, 1))) || + (single_channel && (swizzle == DstSel(4, 0, 0, 0) || swizzle == DstSel(4, 0, 0, 1))) || (format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UNorm) && (swizzle == DstSel(4, 5, 6, 1) || swizzle == DstSel(6, 5, 4, 7))) || (format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32_32_32Float) && @@ -91,11 +92,7 @@ namespace Libs::Graphics { [[nodiscard]] inline int SelectSampledDepthView(VkFormat image_format, VkFormat view_format, uint32_t swizzle) noexcept { - const bool d16 = image_format == VK_FORMAT_D16_UNORM && view_format == VK_FORMAT_R16_UNORM; - const bool d32 = - (image_format == VK_FORMAT_D32_SFLOAT || image_format == VK_FORMAT_D32_SFLOAT_S8_UINT) && - view_format == VK_FORMAT_R32_SFLOAT; - if (d16 || d32) { + if (IsSupportedSampledDepthFormat(image_format, view_format)) { switch (swizzle) { case DstSel(4, 4, 4, 4): return VulkanImage::VIEW_DEPTH_TEXTURE; case DstSel(4, 0, 0, 0): return VulkanImage::VIEW_R000; @@ -117,12 +114,10 @@ IsSupportedSampledDepthResource(const ShaderRecompiler::IR::ImageResource& resou [[nodiscard]] inline int SelectStorageColorView(VkFormat image_format, VkFormat view_format, uint32_t swizzle) noexcept { - const bool single_channel = view_format == VK_FORMAT_R8_UNORM || - view_format == VK_FORMAT_R8_UINT || - view_format == VK_FORMAT_R16_UINT || - view_format == VK_FORMAT_R32_UINT || - view_format == VK_FORMAT_R16_SFLOAT || - view_format == VK_FORMAT_R32_SFLOAT; + const bool single_channel = + view_format == VK_FORMAT_R8_UNORM || view_format == VK_FORMAT_R8_UINT || + view_format == VK_FORMAT_R16_UINT || view_format == VK_FORMAT_R32_UINT || + view_format == VK_FORMAT_R16_SFLOAT || view_format == VK_FORMAT_R32_SFLOAT; const bool swizzle_ok = swizzle == DstSel(4, 5, 6, 7) || (single_channel && (swizzle == DstSel(4, 0, 0, 0) || swizzle == DstSel(4, 0, 0, 1))) || diff --git a/src/graphics/host_gpu/renderer/renderCompute.cpp b/src/graphics/host_gpu/renderer/renderCompute.cpp index 72bd98d65..36920643c 100644 --- a/src/graphics/host_gpu/renderer/renderCompute.cpp +++ b/src/graphics/host_gpu/renderer/renderCompute.cpp @@ -14,6 +14,7 @@ #include "graphics/host_gpu/objects/label.h" #include "graphics/host_gpu/renderer/descriptorCache.h" #include "graphics/host_gpu/renderer/framebufferCache.h" +#include "graphics/host_gpu/renderer/imageInfo.h" #include "graphics/host_gpu/renderer/pipelineCache.h" #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderContext.h" @@ -59,22 +60,19 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor *resolved = {}; const bool has_stencil = z.stencil_info.format != Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid); - const bool depth_format_supported = - z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16) || - z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F); - const bool msaa_compat = depth_msaa_single_sample_compatible(z.z_info.num_samples); - const bool supported_depth_state = - z.z_info.tile_surface_enable && depth_format_supported && - !(z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16) && has_stencil) && - z.z_info.tile_mode_index == 0 && (z.z_info.num_samples == 0 || msaa_compat) && - z.z_info.zrange_precision <= 1 && !z.z_info.expclear_enabled && - !z.z_info.embedded_sample_locations && !z.z_info.partially_resident && - z.z_info.num_mip_levels == 0 && z.z_info.plane_compression == 0 && - z.depth_view.current_mip_level == 0 && z.depth_view.slice_start == 0 && - z.depth_view.slice_max == 0 && z.depth_info.addr5_swizzle_mask == 0 && - z.depth_info.array_mode == 0 && z.depth_info.pipe_config == 0 && - z.depth_info.bank_width == 0 && z.depth_info.bank_height == 0 && - z.depth_info.macro_tile_aspect == 0 && z.depth_info.num_banks == 0; + const auto* depth_policy = FindDepthFormatPolicy(z.z_info.format); + const bool msaa_compat = depth_msaa_single_sample_compatible(z.z_info.num_samples); + const bool supported_depth_state = + z.z_info.tile_surface_enable && depth_policy != nullptr && z.z_info.tile_mode_index == 0 && + (z.z_info.num_samples == 0 || msaa_compat) && z.z_info.zrange_precision <= 1 && + !z.z_info.expclear_enabled && !z.z_info.embedded_sample_locations && + !z.z_info.partially_resident && z.z_info.num_mip_levels == 0 && + z.z_info.plane_compression == 0 && z.depth_view.current_mip_level == 0 && + z.depth_view.slice_start == 0 && z.depth_view.slice_max == 0 && + z.depth_info.addr5_swizzle_mask == 0 && z.depth_info.array_mode == 0 && + z.depth_info.pipe_config == 0 && z.depth_info.bank_width == 0 && + z.depth_info.bank_height == 0 && z.depth_info.macro_tile_aspect == 0 && + z.depth_info.num_banks == 0; const bool supported_stencil_state = z.stencil_info.tile_mode_index == 0 && z.stencil_info.tile_split == 0 && !z.stencil_info.expclear_enabled && @@ -138,10 +136,8 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor return false; } - const bool z16 = z.z_info.format == Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16); - const uint32_t guest_format = Prospero::GpuEnumValue(z16 ? Prospero::BufferFormat::k16UNorm - : Prospero::BufferFormat::k32Float); - const uint32_t bytes = z16 ? 2u : 4u; + const uint32_t guest_format = Prospero::GpuEnumValue(depth_policy->guest_format); + const uint32_t bytes = depth_policy->bytes_per_element; const uint32_t pitch = TileGetTexturePitch(guest_format, width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth)); if (z.pitch_height_valid && ((static_cast(z.pitch_div8_minus1) + 1u) * 8u != pitch || @@ -149,7 +145,7 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor ((static_cast(height) + 7u) & ~7ull))) { return false; } - const uint32_t block_width = z16 ? 256u : 128u; + const uint32_t block_width = bytes == 2 ? 256u : 128u; const uint64_t padded_width = (static_cast(pitch) + block_width - 1u) & ~(block_width - 1u); const uint64_t padded_height = (static_cast(height) + 127u) & ~127ull; diff --git a/src/graphics/host_gpu/renderer/textureCache.cpp b/src/graphics/host_gpu/renderer/textureCache.cpp index 58b2cb885..1934f613a 100644 --- a/src/graphics/host_gpu/renderer/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/textureCache.cpp @@ -1109,10 +1109,9 @@ DepthStencilVulkanImage* CreateDepthTarget(GraphicContext* ctx, const DepthTarge create.format = info.format; create.tiling = VK_IMAGE_TILING_OPTIMAL; create.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - create.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - create.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - create.samples = VK_SAMPLE_COUNT_1_BIT; + create.usage = DepthTargetImageUsage(); + create.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + create.samples = VK_SAMPLE_COUNT_1_BIT; VkImageFormatProperties properties {}; if (vkGetPhysicalDeviceImageFormatProperties(ctx->physical_device, info.format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, @@ -2299,12 +2298,7 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G (has_stencil && PageOverlaps(info.stencil_address, info.stencil_size, info.htile_address, info.htile_size)))) || (info.stencil_htile_compressed && (!has_stencil || !has_htile)) || - !(info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) - ? !has_stencil && info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2 - : info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) && - info.bytes_per_element == 4 && - info.format == - (has_stencil ? VK_FORMAT_D32_SFLOAT_S8_UINT : VK_FORMAT_D32_SFLOAT))) { + !IsSupportedDepthTargetFormat(info)) { EXIT("TextureCache: unsupported depth target, command=%p ctx=%p depth=0x%016" PRIx64 "+0x%016" PRIx64 " stencil=0x%016" PRIx64 "+0x%016" PRIx64 " htile=0x%016" PRIx64 "+0x%016" PRIx64 " extent=%ux%u pitch=%u tile=%u format=%d guest_format=%u bpe=%u\n", @@ -2449,8 +2443,14 @@ DepthStencilVulkanImage* TextureCache::FindDepthTarget(CommandBuffer* command, G } RequireRetirementIsolation(retire, "depth target", info.address, info.size); RetireImages(retire, native_depth_source.get()); - if (!CanLoadStencilAttachment(info, native_depth_source != nullptr && - native_depth_source->stencil_initialized)) { + const bool coherent_guest_stencil = + has_stencil && + IsCoherentGuestImageSource(stencil_source, info.stencil_address, info.stencil_size); + // Native expansion must preserve old layers. Guest data initializes only a wholly new image. + const bool stencil_contents_available = native_depth_source != nullptr + ? native_depth_source->stencil_initialized + : coherent_guest_stencil; + if (!CanLoadStencilAttachment(info, stencil_contents_available)) { EXIT("TextureCache: new stencil target requires a clear before stencil access, " "addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", info.stencil_address, info.stencil_size); diff --git a/src/graphics/host_gpu/renderer/tiler.cpp b/src/graphics/host_gpu/renderer/tiler.cpp index b9fdef0d2..bec5978da 100644 --- a/src/graphics/host_gpu/renderer/tiler.cpp +++ b/src/graphics/host_gpu/renderer/tiler.cpp @@ -12,6 +12,46 @@ namespace Libs::Graphics { +template +static void UploadPromotedD16Depth(GraphicContext* ctx, DepthStencilVulkanImage* image, + const DepthTargetInfo& info, const BufferImageCopySource& source, + uint32_t base_layer) { + const uint64_t slice_size = info.size / info.layers; + const uint64_t texels = static_cast(info.pitch) * info.height; + const uint64_t host_slice_size = texels * sizeof(uint32_t); + const uint64_t host_upload_size = host_slice_size * info.layers; + if (slice_size % sizeof(uint16_t) != 0 || texels > slice_size / sizeof(uint16_t) || + host_upload_size > UINT32_MAX) { + EXIT("Tiler: invalid D16 host-promotion footprint, guest_slice=0x%016" PRIx64 + " host_slice=0x%016" PRIx64 " layers=%u\n", + slice_size, host_slice_size, info.layers); + } + UtilScratchBuffer host_linear(host_upload_size); + std::vector guest_linear(slice_size / sizeof(uint16_t)); + std::vector regions; + regions.reserve(info.layers); + for (uint32_t layer = 0; layer < info.layers; layer++) { + auto* guest_slice = reinterpret_cast(source.address) + slice_size * layer; + TileConvertTiledToLinearDepth(guest_linear.data(), guest_slice, info.guest_format, + info.width, info.height, info.pitch, slice_size); + auto* host_slice = reinterpret_cast(static_cast(host_linear.Data()) + + host_slice_size * layer); + for (uint64_t texel = 0; texel < texels; texel++) { + host_slice[texel] = Encode(guest_linear[texel]); + } + BufferImageCopy region {}; + region.offset = static_cast(host_slice_size * layer); + region.pitch = info.pitch; + region.width = info.width; + region.height = info.height; + region.dst_layer = base_layer + layer; + region.aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + regions.push_back(region); + } + UtilFillImage(ctx, image, host_linear.Data(), host_upload_size, regions, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); +} + void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image, const ImageInfo& info, const BufferImageCopySource& source, bool refresh, bool storage) const { if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 || @@ -53,19 +93,13 @@ void Tiler::DetileImage(GraphicContext* ctx, GpuTextureVulkanImage* image, const void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image, const DepthTargetInfo& info, const BufferImageCopySource& source, bool refresh, uint32_t base_layer) const { - const bool d16 = - info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) && - info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2; - const bool d32 = - info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) && - (info.format == VK_FORMAT_D32_SFLOAT || info.format == VK_FORMAT_D32_SFLOAT_S8_UINT) && - info.bytes_per_element == 4; if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 || info.width == 0 || info.height == 0 || info.pitch < info.width || info.layers == 0 || info.size % info.layers != 0 || base_layer > image->layers || info.layers > image->layers - base_layer || info.size > UINT32_MAX || - info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || (!d16 && !d32) || - !source.cpu_current || source.address != info.address || source.size != info.size || + info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || + !IsSupportedDepthTargetFormat(info) || !source.cpu_current || + source.address != info.address || source.size != info.size || (source.buffer == nullptr && source.offset != 0)) { EXIT("Tiler: unsupported depth detile, ctx=%p image=%p source_buffer=%p " "source=0x%016" PRIx64 "+0x%016" PRIx64 " offset=0x%016" PRIx64 @@ -80,6 +114,19 @@ void Tiler::DetileImage(GraphicContext* ctx, DepthStencilVulkanImage* image, if (refresh) { VulkanDeviceWaitIdle(ctx); } + if (DepthAspectTransferBytes(info.format) != info.bytes_per_element) { + switch (info.format) { + case VK_FORMAT_D24_UNORM_S8_UINT: + UploadPromotedD16Depth(ctx, image, info, source, base_layer); + return; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + UploadPromotedD16Depth(ctx, image, info, source, base_layer); + return; + default: + EXIT("Tiler: unsupported depth transfer conversion, format=%d guest_bpe=%u\n", + static_cast(info.format), info.bytes_per_element); + } + } const auto slice_size = info.size / info.layers; UtilScratchBuffer linear(info.size); std::vector regions; @@ -109,11 +156,12 @@ void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image, TileSizeAlign htile_size {}; TileSizeAlign depth_size {}; const auto stencil_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8UInt); + const auto* policy = FindGuestDepthFormatPolicy(info.guest_format); const auto stencil_pitch = TileGetTexturePitch( stencil_format, info.width, 1, Prospero::GpuEnumValue(Prospero::TileMode::kDepth)); const bool supported_layout = - TileGetDepthSize(info.width, info.height, 0, - Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F), + policy != nullptr && IsSupportedDepthTargetFormat(info) && + TileGetDepthSize(info.width, info.height, 0, Prospero::GpuEnumValue(policy->depth_format), Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), info.htile_address != 0, &stencil_size, &htile_size, &depth_size) && info.layers != 0 && info.stencil_size % info.layers == 0 && @@ -121,9 +169,7 @@ void Tiler::DetileStencil(GraphicContext* ctx, DepthStencilVulkanImage* image, stencil_pitch != 0; if (ctx == nullptr || image == nullptr || info.address == 0 || info.size == 0 || info.stencil_address == 0 || info.stencil_size == 0 || info.width == 0 || - info.height == 0 || info.format != VK_FORMAT_D32_SFLOAT_S8_UINT || - info.guest_format != Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) || - info.bytes_per_element != 4 || base_layer > image->layers || + info.height == 0 || base_layer > image->layers || info.layers > image->layers - base_layer || info.stencil_size > UINT32_MAX || info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kDepth) || info.stencil_htile_compressed || !supported_layout || !source.cpu_current || @@ -187,11 +233,7 @@ void Tiler::TileImage(void* dst, const void* src, const RenderTargetInfo& info) } void Tiler::TileImage(void* dst, const void* src, const DepthTargetInfo& info) const { - const bool supported_format = - (info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm) && - info.format == VK_FORMAT_D16_UNORM && info.bytes_per_element == 2) || - (info.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float) && - info.format == VK_FORMAT_D32_SFLOAT && info.bytes_per_element == 4); + const bool supported_format = IsSupportedDepthTargetFormat(info); if (dst == nullptr || src == nullptr || info.address == 0 || info.size == 0 || info.stencil_address != 0 || info.stencil_size != 0 || info.width == 0 || info.height == 0 || info.pitch < info.width || diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 70e479a1f..79a980938 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -8710,8 +8710,8 @@ bool CacheFault(void *opaque, PageFaultAccess access, uint64_t vaddr, VK_FORMAT_A2R10G10B10_UNORM_PACK32, DstSel(6, 5, 4, 7)); } else if (std::strcmp(kind, "sampled-depth-format") == 0) { - (void)SelectSampledDepthView(VK_FORMAT_D32_SFLOAT_S8_UINT, - VK_FORMAT_R16_UNORM, DstSel(4, 4, 4, 4)); + (void)SelectSampledDepthView(VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_R32_SFLOAT, DstSel(4, 4, 4, 4)); } else if (std::strcmp(kind, "sampled-depth-swizzle") == 0) { (void)SelectSampledDepthView(VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_R32_SFLOAT, DstSel(4, 5, 6, 7)); @@ -8806,6 +8806,21 @@ void CheckSampledColorViews() { DstSel(4, 0, 0, 0)) == VulkanImage::VIEW_R000, "D16 depth target did not select its R000 depth-aspect view"); + Require("SampledColorViews", "D16S8 R001 depth target", + SelectSampledDepthView(VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_R16_UNORM, + DstSel(4, 0, 0, 1)) == + VulkanImage::VIEW_R001, + "D16S8 depth target did not select its R001 depth-aspect view"); + Require("SampledColorViews", "promoted D24S8 R001 depth target", + SelectSampledDepthView(VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_R16_UNORM, + DstSel(4, 0, 0, 1)) == + VulkanImage::VIEW_R001, + "D24S8 host fallback did not preserve the guest R16 depth view"); + Require("SampledColorViews", "promoted D32S8 R001 depth target", + SelectSampledDepthView(VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_R16_UNORM, + DstSel(4, 0, 0, 1)) == + VulkanImage::VIEW_R001, + "D32S8 host fallback did not preserve the guest R16 depth view"); Require("SampledColorViews", "D32S8 R001 depth target", SelectSampledDepthView(VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_R32_SFLOAT, @@ -11094,20 +11109,38 @@ void CheckImageOverlapResolution() { mismatched_depth) == DepthOverlap::Unsupported, "mipmapped or format-mismatched depth load was admitted"); - Require("ImageOverlapResolution", "depth transition source", - SelectDepthTransitionSource(true, true, false, false, false, true) == - DepthTransitionSource::None && - SelectDepthTransitionSource(false, true, false, false, false, true) == - DepthTransitionSource::Native && - SelectDepthTransitionSource(false, false, false, false, false, false) == - DepthTransitionSource::Guest && - SelectDepthTransitionSource(false, true, true, false, false, false) == - DepthTransitionSource::Guest && - SelectDepthTransitionSource(false, true, false, true, false, false) == - DepthTransitionSource::Guest && - SelectDepthTransitionSource(false, true, false, false, true, true) == - DepthTransitionSource::Guest, - "clear, clean-native, or overlapping dirty-buffer depth preservation policy regressed"); + struct DepthTransitionCase { + const char *name; + bool clear; + bool native; + bool sampled_cpu_dirty; + bool sampled_buffer_modified; + bool buffer_overlap; + bool buffer_cpu_dirty; + DepthTransitionSource expected; + }; + constexpr DepthTransitionCase transition_cases[] = { + {"clear", true, true, false, false, false, true, + DepthTransitionSource::None}, + {"clean native", false, true, false, false, false, true, + DepthTransitionSource::Native}, + {"missing native", false, false, false, false, false, false, + DepthTransitionSource::Guest}, + {"CPU-dirty native", false, true, true, false, false, false, + DepthTransitionSource::Guest}, + {"buffer-modified native", false, true, false, true, false, false, + DepthTransitionSource::Guest}, + {"dirty overlapping buffer", false, true, false, false, true, true, + DepthTransitionSource::Guest}, + }; + for (const auto &test : transition_cases) { + Require("ImageOverlapResolution", test.name, + SelectDepthTransitionSource( + test.clear, test.native, test.sampled_cpu_dirty, + test.sampled_buffer_modified, test.buffer_overlap, + test.buffer_cpu_dirty) == test.expected, + "depth preservation source changed"); + } depth.depth_load_clear = true; depth.stencil_address = sampled.address + 0x6000; depth.stencil_size = 0x2000; @@ -11136,6 +11169,7 @@ void CheckImageOverlapResolution() { Require("ImageOverlapResolution", "stencil clear initializes", CanLoadStencilAttachment(depth, false), "stencil clear did not initialize the attachment"); + depth.stencil_load_clear = false; depth.stencil_htile_compressed = false; Require("ImageOverlapResolution", "raw stencil load", CanLoadRawStencilPlane(depth), @@ -11253,6 +11287,122 @@ void CheckDepthTargetFootprints() { TileSizeAlign stencil{}; TileSizeAlign htile{}; TileSizeAlign depth{}; + struct AttachmentFormatCase { + const char *name; + uint32_t depth_format; + uint32_t stencil_format; + VkFormat expected; + }; + constexpr AttachmentFormatCase attachment_cases[] = { + {"Z16", Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16), + Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid), + VK_FORMAT_D16_UNORM}, + {"Z16S8", Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16), + Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), + VK_FORMAT_D16_UNORM_S8_UINT}, + {"Z32", Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F), + Prospero::GpuEnumValue(Prospero::StencilFormat::kInvalid), + VK_FORMAT_D32_SFLOAT}, + {"Z32S8", Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F), + Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), + VK_FORMAT_D32_SFLOAT_S8_UINT}, + {"invalid depth", 2, + Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), + VK_FORMAT_UNDEFINED}, + {"invalid stencil", Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16), + 2, VK_FORMAT_UNDEFINED}, + }; + for (const auto &test : attachment_cases) { + Require("DepthTargetFootprints", test.name, + DepthAttachmentFormat(test.depth_format, test.stencil_format) == + test.expected, + "PS5 depth/stencil attachment mapping changed"); + } + Require("DepthTargetFootprints", "1920x1080 Z16S8 without HTile", + TileGetDepthSize(1920, 1080, 0, + Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16), + Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), false, + &stencil, &htile, &depth) && + depth.size == 0x480000 && depth.align == 0x10000 && + stencil.size == 0x280000 && stencil.align == 0x10000 && htile.size == 0, + "captured Z16S8 footprint disagrees with Prospero block rules"); + struct TargetFormatCase { + const char *name; + VkFormat host_format; + uint32_t guest_format; + uint32_t bytes_per_element; + bool stencil; + bool supported; + }; + constexpr TargetFormatCase target_cases[] = { + {"D16", VK_FORMAT_D16_UNORM, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, false, true}, + {"D16S8", VK_FORMAT_D16_UNORM_S8_UINT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true}, + {"D16 via D24S8", VK_FORMAT_D24_UNORM_S8_UINT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true}, + {"D16 via D32S8", VK_FORMAT_D32_SFLOAT_S8_UINT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, true}, + {"D32", VK_FORMAT_D32_SFLOAT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, false, true}, + {"D32S8", VK_FORMAT_D32_SFLOAT_S8_UINT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, true, true}, + {"D16 plus stencil mismatch", VK_FORMAT_D16_UNORM, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, true, false}, + {"fallback without stencil", VK_FORMAT_D24_UNORM_S8_UINT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 2, false, false}, + {"D32 guest via D24", VK_FORMAT_D24_UNORM_S8_UINT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float), 4, true, false}, + {"D16 byte mismatch", VK_FORMAT_D16_UNORM, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16UNorm), 4, false, false}, + }; + for (const auto &test : target_cases) { + DepthTargetInfo target{}; + target.format = test.host_format; + target.guest_format = test.guest_format; + target.bytes_per_element = test.bytes_per_element; + target.stencil_address = test.stencil ? 0x10000 : 0; + target.stencil_size = test.stencil ? 0x10000 : 0; + Require("DepthTargetFootprints", test.name, + IsSupportedDepthTargetFormat(target) == test.supported, + "host/guest depth format policy changed"); + } + struct TransferPlaneCase { + const char *name; + VkFormat attachment; + VkFormat transfer; + uint32_t bytes; + }; + constexpr TransferPlaneCase transfer_cases[] = { + {"D16S8 transfer", VK_FORMAT_D16_UNORM_S8_UINT, VK_FORMAT_D16_UNORM, 2}, + {"D24S8 transfer", VK_FORMAT_D24_UNORM_S8_UINT, + VK_FORMAT_X8_D24_UNORM_PACK32, 4}, + {"D32S8 transfer", VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D32_SFLOAT, 4}, + {"invalid transfer", VK_FORMAT_R16_UNORM, VK_FORMAT_UNDEFINED, 0}, + }; + for (const auto &test : transfer_cases) { + Require("DepthTargetFootprints", test.name, + DepthAspectTransferFormat(test.attachment) == test.transfer && + DepthAspectTransferBytes(test.attachment) == test.bytes, + "combined depth transfer-plane layout changed"); + } + struct PromotionCase { + const char *name; + uint16_t source; + uint32_t d24; + uint32_t d32; + }; + constexpr PromotionCase promotion_cases[] = { + {"zero", 0, 0, 0}, + {"midpoint", 0x8000, 0x00800080, 0x3f000080}, + {"maximum", 0xffff, 0x00ffffff, 0x3f800000}, + }; + for (const auto &test : promotion_cases) { + Require("DepthTargetFootprints", test.name, + EncodeD16AsD24(test.source) == test.d24 && + EncodeD16AsD32(test.source) == test.d32, + "D16 host promotion changed the represented depth value"); + } Require("DepthTargetFootprints", "640x360 Z32S8 without HTile", TileGetDepthSize(640, 360, 0, Prospero::GpuEnumValue(Prospero::DepthFormat::kZ32F), Prospero::GpuEnumValue(Prospero::StencilFormat::k8UInt), false, @@ -11396,6 +11546,18 @@ void CheckHtileClearTargetResolution() { resolved.address == derived_z16.htile_data_base_addr && resolved.size == htile_size.size, "extent-derived Z16 depth-only HTile target was rejected"); + + auto derived_z16s8 = derived; + derived_z16s8.z_info.format = Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16); + Require("HtileClearTargetResolution", "derived Z16S8 target", + TileGetDepthSize(960, 540, 0, derived_z16s8.z_info.format, + derived_z16s8.stencil_info.format, true, &stencil_size, + &htile_size, &depth_size) && + stencil_size.size != 0 && stencil_size.align == 0x10000 && + ResolveHtileClearTarget(derived_z16s8, htile_size.size, &resolved) && + resolved.address == derived_z16s8.htile_data_base_addr && + resolved.size == htile_size.size, + "extent-derived Z16S8 HTile target was rejected"); std::printf("[host] %-32s ok\n", "HtileClearTargetResolution"); }