From bbf430e22daa824761df65014eec52f90e87e7fe Mon Sep 17 00:00:00 2001 From: Michon <231849@buas.nl> Date: Wed, 30 Sep 2026 17:53:52 +0200 Subject: [PATCH] fix(agc): read a 16-bit integer view of a depth plane as its stored values Co-Authored-By: Claude Opus 5.5 (1M context) --- .../prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp | 2 +- .../libSceAgcDriver/Graphics/src/ShaderResources.cpp | 9 ++++++++- .../IrMetadata/Resources.hpp | 1 + .../include/Optimization/ResourceMaterializer.hpp | 1 + .../Optimization/src/ResourceMaterializer.cpp | 6 +++++- .../include/RdnaDecoder/RdnaDescriptorFormat.hpp | 1 + .../RdnaDecoder/src/RdnaDescriptorFormat.cpp | 10 ++++++++-- core/shader/recompiler/ShaderDiskCache.cpp | 3 +++ .../recompiler/SpirvBackend/src/SpirvImageEmitter.cpp | 5 +++++ 9 files changed, 33 insertions(+), 5 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp index f8ae28b76..7f3a0cdcc 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp @@ -92,7 +92,7 @@ public: const bool d16 = target.format == VK_FORMAT_D16_UNORM || target.format == VK_FORMAT_D16_UNORM_S8_UINT; const auto expected = stencil ? VK_FORMAT_R8_UINT : d16 ? VK_FORMAT_R16_UNORM : VK_FORMAT_R32_SFLOAT; const auto format = ResolveTextureFormat(resource.format); - const bool depthBits = !stencil && !d16 && words.size() >= 4 && ShaderRecompiler::IsDepthBitsTexture(words[1], words[3]); + const bool depthBits = !stencil && words.size() >= 4 && ShaderRecompiler::DepthBitsTextureWidth(words[1], words[3]) == (d16 ? 16u : 32u); if ((format != expected && !depthBits) || resource.dimension != TextureDimension::k2D || resource.width != target.extent.width || resource.height != target.extent.height || resource.baseLevel != 0 || resource.lastLevel != 0 || resource.baseArray != 0) { char text[448]; std::snprintf(text, sizeof(text), "AGC graphics: sampling the %s plane of depth surface 0x%llx (%ux%u, vk format %d) as a %ux%u texture of guest format %u (vk %d), tile mode %u, dimension %d, levels %u-%u, slice %u is not implemented (T# %08x %08x %08x %08x %08x %08x %08x %08x)", diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index 95e471b7a..062cb1127 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -265,11 +265,18 @@ std::shared_ptr sampledStorageSource(const Context& context, con std::shared_ptr cachedTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components, std::uint64_t guestBytes = 0) { CaptureTrace::Log("sampled-lookup address=%llx width=%u height=%u dcc=%llx", static_cast(resource.baseAddress), resource.width, resource.height, static_cast(resource.dccAddress)); if (auto depth = DepthSurfaceTexture(context, words, resource, components)) return depth; - if (words.size() >= 4 && ShaderRecompiler::IsDepthBitsTexture(words[1], words[3])) { + const auto depthBitsWidth = words.size() >= 4 ? ShaderRecompiler::DepthBitsTextureWidth(words[1], words[3]) : 0u; + if (depthBitsWidth == 32u) { char text[160]; std::snprintf(text, sizeof(text), "AGC graphics: 32-bit integer read of the depth-layout texture 0x%llx, which is no depth surface drawn with, is not implemented", static_cast(resource.baseAddress)); throw std::runtime_error(text); } + constexpr auto unorm16 = static_cast(ShaderRecompiler::IrBufferFormat::Format16UNorm); + if (depthBitsWidth == 16u && resource.format != unorm16) { + auto normalized = resource; + normalized.format = unorm16; + return cachedTexture(context, words, normalized, components, guestBytes); + } static const bool disabled = std::getenv("APS5_NO_TEXTURE_CACHE") != nullptr; const bool profile = LookupOutcomes::Profiled(); const auto start = profile ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; diff --git a/core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp b/core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp index 5d20bc6ac..bbe52b016 100644 --- a/core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp +++ b/core/shader/recompiler/IntermediateRepresentation/include/IntermediateRepresentation/IrMetadata/Resources.hpp @@ -51,6 +51,7 @@ struct ImageResource { bool cube = false; bool r128 = false; bool depthBits = false; + bool depthUnorm16 = false; std::uint32_t indirectRoot = NoIndirectImage; std::uint32_t indirectMappingOffset = 0; std::uint32_t indirectSearchIterations = 0; diff --git a/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp b/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp index 8ef7c5cdb..0bbff3eb4 100644 --- a/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp +++ b/core/shader/recompiler/Optimization/include/Optimization/ResourceMaterializer.hpp @@ -30,6 +30,7 @@ struct ResourceSpecialization { bool cube = false; bool fmask = false; bool depthBits = false; + bool depthUnorm16 = false; bool operator==(const Image& other) const; }; diff --git a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp index 4fb6ca805..73ffff561 100644 --- a/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp +++ b/core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp @@ -43,6 +43,7 @@ struct DecodedImage { bool cube = false; bool fmask = false; bool depthBits = false; + bool depthUnorm16 = false; }; ShaderBufferResource decodeBufferDescriptor(const DescriptorValue& value) { @@ -164,6 +165,7 @@ DecodedImage decodeImageDescriptor(const DescriptorValue& descriptor, const Imag decoded.numericClass = SampledTextureNumericClass(format); if (!storage && !base.depthCompare && IsDepthBitsTexture(descriptor.dwords[1], descriptor.dwords[3])) { decoded.depthBits = true; + decoded.depthUnorm16 = DepthBitsTextureWidth(descriptor.dwords[1], descriptor.dwords[3]) == 16u; decoded.numericClass = IrTextureNumericClass::Float; decoded.shaderSwizzle = descriptorImageSwizzle(descriptor); } @@ -530,6 +532,7 @@ void buildResourceSpecialization(const IrResourcePlan& plan, ResourceSnapshot& s entry.cube = decoded.cube; entry.fmask = decoded.fmask; entry.depthBits = decoded.depthBits; + entry.depthUnorm16 = decoded.depthUnorm16; result.images.push_back(entry); } @@ -615,6 +618,7 @@ void ResourceMaterializer::Apply(IrProgram& program, const ResourceSpecializatio image.indirectSearchIterations = source.indirectSearchIterations; image.cube = source.cube; image.depthBits = source.depthBits; + image.depthUnorm16 = source.depthUnorm16; image.indirectResources.clear(); } for (std::uint32_t index = 0; index < images.size(); index++) { @@ -880,7 +884,7 @@ bool ResourceSpecialization::Buffer::operator==(const Buffer& other) const { } bool ResourceSpecialization::Image::operator==(const Image& other) const { - return numericClass == other.numericClass && dimension == other.dimension && mipCount == other.mipCount && conversionFormat == other.conversionFormat && shaderSwizzle == other.shaderSwizzle && indirectRoot == other.indirectRoot && indirectMappingOffset == other.indirectMappingOffset && indirectSearchIterations == other.indirectSearchIterations && cube == other.cube && fmask == other.fmask && depthBits == other.depthBits; + return numericClass == other.numericClass && dimension == other.dimension && mipCount == other.mipCount && conversionFormat == other.conversionFormat && shaderSwizzle == other.shaderSwizzle && indirectRoot == other.indirectRoot && indirectMappingOffset == other.indirectMappingOffset && indirectSearchIterations == other.indirectSearchIterations && cube == other.cube && fmask == other.fmask && depthBits == other.depthBits && depthUnorm16 == other.depthUnorm16; } bool ResourceSpecialization::operator==(const ResourceSpecialization& other) const { diff --git a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp index f043c6c55..c700ee099 100644 --- a/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp +++ b/core/shader/recompiler/RdnaDecoder/include/RdnaDecoder/RdnaDescriptorFormat.hpp @@ -20,6 +20,7 @@ enum class ImageType : std::uint32_t { [[nodiscard]] bool IsFmaskTextureFormat(IrBufferFormat format); [[nodiscard]] IrTextureNumericClass SampledTextureNumericClass(IrBufferFormat format); [[nodiscard]] IrBufferFormat RemapTextureFormat(IrBufferFormat format); +[[nodiscard]] std::uint32_t DepthBitsTextureWidth(std::uint32_t word1, std::uint32_t word3); [[nodiscard]] bool IsDepthBitsTexture(std::uint32_t word1, std::uint32_t word3); } diff --git a/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp b/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp index e8d2f594c..acc3b3135 100644 --- a/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp +++ b/core/shader/recompiler/RdnaDecoder/src/RdnaDescriptorFormat.cpp @@ -121,11 +121,17 @@ IrBufferFormat RemapTextureFormat(IrBufferFormat format) { return format == IrBufferFormat::Format11_11_10UInt ? IrBufferFormat::Format32UInt : format; } -bool IsDepthBitsTexture(std::uint32_t word1, std::uint32_t word3) { +std::uint32_t DepthBitsTextureWidth(std::uint32_t word1, std::uint32_t word3) { const auto format = static_cast((word1 >> 20u) & 0x1ffu); const auto swizzle = (word3 >> 20u) & 0x1fu; const bool depthLayout = swizzle == 4u || swizzle == 8u || swizzle == 16u || swizzle == 20u || swizzle == 24u; - return depthLayout && (format == IrBufferFormat::Format32UInt || format == IrBufferFormat::Format32SInt); + if (!depthLayout) return 0u; + if (format == IrBufferFormat::Format32UInt || format == IrBufferFormat::Format32SInt) return 32u; + return format == IrBufferFormat::Format16UInt ? 16u : 0u; +} + +bool IsDepthBitsTexture(std::uint32_t word1, std::uint32_t word3) { + return DepthBitsTextureWidth(word1, word3) != 0u; } } diff --git a/core/shader/recompiler/ShaderDiskCache.cpp b/core/shader/recompiler/ShaderDiskCache.cpp index 36cbe22ed..1eac07ad0 100644 --- a/core/shader/recompiler/ShaderDiskCache.cpp +++ b/core/shader/recompiler/ShaderDiskCache.cpp @@ -396,6 +396,7 @@ void encodeInfo(Writer& writer, const CompiledShaderInfo& compiled) { out.Value(image.cube); out.Value(image.r128); out.Value(image.depthBits); + out.Value(image.depthUnorm16); out.Value(image.indirectRoot); out.Value(image.indirectMappingOffset); out.Value(image.indirectSearchIterations); @@ -482,6 +483,7 @@ void decodeInfo(Reader& reader, CompiledShaderInfo& compiled) { in.Value(image.cube); in.Value(image.r128); in.Value(image.depthBits); + in.Value(image.depthUnorm16); in.Value(image.indirectRoot); in.Value(image.indirectMappingOffset); in.Value(image.indirectSearchIterations); @@ -793,6 +795,7 @@ void BuildKey(const RecompileRequest& request, std::uint32_t hostSubgroupSize, c out.Value(image.cube); out.Value(image.fmask); out.Value(image.depthBits); + out.Value(image.depthUnorm16); }); writer.Values(std::span(specialization.boundDescriptors)); const auto& switches = switchKey(); diff --git a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp index d02d65c3b..9dd6f5654 100644 --- a/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp +++ b/core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp @@ -308,6 +308,11 @@ std::uint32_t ResultVector(SpirvValueEmitContext& ctx, const ImageEmitAccess& ac } const auto scalar = state.module.AllocateId(); state.module.AddFunction(spv::OpCompositeExtract, ImageScalarType(state, valueClass), scalar, value, index); + if (access.image.depthUnorm16) { + const auto scaled = Binary(state, spv::OpFMul, TypeF32(state), scalar, ConstantF32Value(state, 65535.0f)); + component[index] = Unary(state, spv::OpConvertFToU, TypeU32(state), Binary(state, spv::OpFAdd, TypeF32(state), scaled, ConstantF32Value(state, 0.5f))); + continue; + } component[index] = SampledComponentBits(ctx, scalar, valueClass); } const auto result = state.module.AllocateId();