From 729558cc660f6d45c8f6d4e0654e025310fa42ac Mon Sep 17 00:00:00 2001 From: hamb3r <48532909+hamb3r@users.noreply.github.com> Date: Fri, 16 Oct 2026 03:53:34 +0300 Subject: [PATCH] fix(agc): bind array image views for Image2DMultisampleArray descriptors --- .../Graphics/include/DepthSurface.hpp | 2 +- .../Graphics/include/MultisampleTarget.hpp | 2 +- .../Graphics/include/ShaderResources.hpp | 3 +- .../Graphics/src/DepthSurface.cpp | 17 +++++------ .../Graphics/src/MultisampleTarget.cpp | 14 +++++----- .../Graphics/src/ShaderResources.cpp | 28 +++++++++++-------- 6 files changed, 37 insertions(+), 29 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp index 2f54bee302..2cd8e2cf4b 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/DepthSurface.hpp @@ -42,7 +42,7 @@ void NoteDepthMetadataFill(std::uint64_t address, std::size_t bytes, std::uint32 VkImageAspectFlags HtileFillClears(std::uint32_t pattern, bool stencilInHtile); std::uint64_t HtileMetadataBytes(VkExtent2D extent); bool HtileFillCovers(std::uint64_t htile, VkExtent2D extent, std::uint64_t address, std::size_t bytes); -std::shared_ptr DepthSurfaceTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components); +std::shared_ptr DepthSurfaceTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D); VkFormat HostDepthFormat(const Context& context, VkFormat format); float HostDepthBiasConstant(const Context& context, VkFormat format, float constant); std::shared_ptr DepthSurfaceStorage(const Context& context, std::span words, const GuestTextureResource& resource, std::uint32_t mip); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp index 237fd10ad0..48471d9320 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/MultisampleTarget.hpp @@ -52,7 +52,7 @@ std::uint64_t MultisampledColorBytes(const ColorTarget& target); bool MultisampledFillClearColor(const ColorTarget& target, std::uint32_t fill, VkClearColorValue& clear); bool TakeMultisampledClear(MultisampledCmask& cmask, MultisampledColorFill& colorFill, const ColorTarget& use, VkClearColorValue& clear); VkImageView MultisampleTargetView(const Context& context, const ColorTarget& target); -std::shared_ptr MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components); +std::shared_ptr MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D); void MaterializeMultisampleTargetClear(const Context& context, const ColorTarget& target); void ResolveMultisampleTarget(const Context& context, const ColorTarget& source, StorageTexture& destination, VkImageView destinationView); bool MultisampledColorFillAbsorbs(std::uint64_t address, std::size_t bytes, std::uint32_t pattern); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp index efefda3a7f..455192e34a 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp @@ -39,7 +39,7 @@ struct TextureCacheUse { }; TextureCacheUse TextureCacheUsage(); std::uint64_t SampledTextureCacheBudget(const Context& context); -std::shared_ptr CachedSampledTexture(const Context& context, std::span words); +std::shared_ptr CachedSampledTexture(const Context& context, std::span words, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D); bool SampledTexturesShareEntry(std::span first, std::span second); bool CompletePendingCpuWrites(const Context& context, std::uint64_t address, std::size_t bytes); @@ -410,6 +410,7 @@ private: struct PreviousSampled { std::array words{}; bool depthCompare = false; + VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D; std::shared_ptr texture; }; PreviousSampled previousSampled; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp index b15945ee89..60f6476340 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/DepthSurface.cpp @@ -280,15 +280,16 @@ public: return (ResolveTextureFormat(resource.format) == expected || depthBits || UnormStencilView(resource)) && oneSlice && resource.width == target.extent.width && resource.height == target.extent.height && resource.baseLevel == 0 && resource.lastLevel == 0 && resource.baseArray == 0; } - std::shared_ptr Sampled(std::span words, const GuestTextureResource& resource, VkComponentMapping components, bool drawn = false) { + std::shared_ptr Sampled(std::span words, const GuestTextureResource& resource, VkComponentMapping components, bool drawn = false, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D) { CheckCurrent(); CheckResource(resource); - std::array key{}; + std::array key{}; std::copy_n(words.begin(), std::min(words.size(), 8), key.begin()); key[8] = components.r; key[9] = components.g; key[10] = components.b; key[11] = components.a; + key[12] = static_cast(multisampleViewType); if (const auto found = textures.find(key); found != textures.end()) return found->second; const bool multisampled = target.samples != 1; Require(!multisampled || sampleable, "the device cannot sample the native depth target's format and sample count"); @@ -312,7 +313,7 @@ public: static_cast(resource.tileMode), static_cast(resource.dimension), resource.baseLevel, resource.lastLevel, resource.baseArray, key[0], key[1], key[2], key[3], key[4], key[5], key[6], key[7]); throw std::runtime_error(text); } - const auto viewType = resource.dimension == TextureDimension::k2DArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + const auto viewType = multisampled ? multisampleViewType : resource.dimension == TextureDimension::k2DArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; auto texture = std::make_shared(context, image, hostFormat, stencil ? VK_IMAGE_ASPECT_STENCIL_BIT : VK_IMAGE_ASPECT_DEPTH_BIT, components, viewType, 0, owned); if (multisampled) { Require(samplingActive, "native multisample depth sampling requires its creation-time owner"); @@ -493,18 +494,18 @@ private: VkImage image = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; std::unique_ptr staging; - std::map, std::shared_ptr> textures; + std::map, std::shared_ptr> textures; std::uint64_t written = 0; bool filled = false; }; - std::map, std::shared_ptr> textures; + std::map, std::shared_ptr> textures; PlaneStorage depthPlane; PlaneStorage stencilPlane; std::uint64_t depthMemory = 0; std::uint64_t stencilMemory = 0; StencilCopy stencilCopy; - std::shared_ptr unormStencil(const std::array& key, VkComponentMapping components, VkImageViewType viewType, bool drawn) { + std::shared_ptr unormStencil(const std::array& key, VkComponentMapping components, VkImageViewType viewType, bool drawn) { if (stencilCopy.image == VK_NULL_HANDLE) { VkImageCreateInfo info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; info.imageType = VK_IMAGE_TYPE_2D; @@ -1317,7 +1318,7 @@ void ClearDepthSurfaces(VkDevice device) { UpdateResidentImageUnmapObserver_nid_no_patch(); } -std::shared_ptr DepthSurfaceTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components) { +std::shared_ptr DepthSurfaceTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType multisampleViewType) { const auto held = [&](const auto& surface) { return surface->active->load(std::memory_order_acquire) && surface->context.device == context.device && !surface->retired && (surface->target.address == resource.baseAddress || (surface->target.stencilAddress != 0 && surface->target.stencilAddress == resource.baseAddress)); }; @@ -1341,7 +1342,7 @@ std::shared_ptr DepthSurfaceTexture(const Context& context, std::spanpendingClear == 0u, "native multisample depth fast-clear transition is not implemented"); RecordMemoryBarrier(context, recorder->Commands(), VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); Recorder::CountBarriers(Recorder::CommandClass::Draw, 1); - return found->Sampled(words, resource, components); + return found->Sampled(words, resource, components, false, multisampleViewType); } if (resource.width != base.extent.width || resource.height != base.extent.height) return nullptr; const bool d16 = base.format == VK_FORMAT_D16_UNORM || base.format == VK_FORMAT_D16_UNORM_S8_UINT; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp index 15da911cfc..e743472f5a 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/MultisampleTarget.cpp @@ -126,7 +126,7 @@ public: }); } - std::shared_ptr Sampled(VkComponentMapping components) { + std::shared_ptr Sampled(VkComponentMapping components, VkImageViewType viewType = VK_IMAGE_VIEW_TYPE_2D) { Require(sampleable, "the device cannot sample the multisampled color target's format at its sample count"); Require(cmask.Refusal().empty(), cmask.Refusal()); Require(cmask.Keys() != DccKeys::Clear0000, "sampling a multisampled color target whose CMASK fast clear no draw has applied is not implemented"); @@ -136,9 +136,9 @@ public: RecordMemoryBarrier(context, commands, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT); Recorder::CountBarriers(Recorder::CommandClass::Draw, 1); }); - const std::array key{static_cast(components.r), static_cast(components.g), static_cast(components.b), static_cast(components.a)}; + const std::array key{static_cast(components.r), static_cast(components.g), static_cast(components.b), static_cast(components.a), static_cast(viewType)}; if (const auto found = textures.find(key); found != textures.end()) return found->second; - auto texture = std::make_shared(context, image, target.format, VK_IMAGE_ASPECT_COLOR_BIT, components); + auto texture = std::make_shared(context, image, target.format, VK_IMAGE_ASPECT_COLOR_BIT, components, viewType); textures.emplace(key, texture); return texture; } @@ -188,7 +188,7 @@ public: private: bool sampleable = false; - std::map, std::shared_ptr> textures; + std::map, std::shared_ptr> textures; void release() noexcept { textures.clear(); @@ -465,7 +465,7 @@ VkImageView MultisampleTargetView(const Context& context, const ColorTarget& tar return acquire(context, target).view; } -std::shared_ptr MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components) { +std::shared_ptr MultisampleTargetTexture(const Context& context, const GuestTextureResource& resource, VkComponentMapping components, VkImageViewType viewType) { std::lock_guard lock(targetsMutex()); const auto format = ResolveTextureFormat(resource.format); const MultisampleTarget* other = nullptr; @@ -473,12 +473,12 @@ std::shared_ptr MultisampleTargetTexture(const Context& context, const if (!target->active->load(std::memory_order_acquire)) continue; if (target->context.device != context.device || target->target.surfaceAddress != resource.baseAddress) continue; const auto& rendered = target->target; - if (rendered.format == format && rendered.extent.width == resource.width && rendered.extent.height == resource.height && rendered.samples == resource.samples && rendered.mip == 0) return target->Sampled(components); + if (rendered.format == format && rendered.extent.width == resource.width && rendered.extent.height == resource.height && rendered.samples == resource.samples && rendered.mip == 0) return target->Sampled(components, viewType); other = target.get(); } if (other == nullptr) { Require(Recorder::Active() != nullptr, "a multisampled texture is only sampled by recorded draws and dispatches"); - return createFilled(context, resource, format).Sampled(components); + return createFilled(context, resource, format).Sampled(components, viewType); } char text[320]; std::snprintf(text, sizeof(text), "AGC graphics: sampling multisampled color target 0x%llx (%ux%u, %u samples, vk format %d) as a %ux%u texture of %u samples and guest format %u (vk %d) is not implemented", static_cast(resource.baseAddress), other->target.extent.width, other->target.extent.height, other->target.samples, static_cast(other->target.format), resource.width, resource.height, resource.samples, resource.format, static_cast(format)); diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index e31084e0c4..8640477aaa 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -560,14 +560,14 @@ std::array untrackedKey(std::uint64_t address, std::size_t byt } // `guestBytes` is the surface size when the caller described the surface already (0: described here). -std::shared_ptr cachedTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components, std::uint64_t guestBytes = 0, bool depthCompare = false) { +std::shared_ptr cachedTexture(const Context& context, std::span words, const GuestTextureResource& resource, VkComponentMapping components, std::uint64_t guestBytes = 0, bool depthCompare = false, VkImageViewType multisampleViewType = VK_IMAGE_VIEW_TYPE_2D) { if (resource.dimension == TextureDimension::k2DMsaa) { Require(!depthCompare, "native multisampled depth comparison sampling is not implemented"); - if (auto depth = DepthSurfaceTexture(context, words, resource, components)) { + if (auto depth = DepthSurfaceTexture(context, words, resource, components, multisampleViewType)) { logLookup({depth.get(), resource, 0, DccKeys::Uncompressed, 0, nullptr, true}); return depth; } - auto target = MultisampleTargetTexture(context, resource, components); + auto target = MultisampleTargetTexture(context, resource, components, multisampleViewType); logLookup({target.get(), resource, 0, DccKeys::Uncompressed, 0, nullptr, true}); return target; } @@ -1044,9 +1044,9 @@ std::uint64_t SampledTextureCacheBudget(const Context& context) { return sampledBudget(context, cache); } -std::shared_ptr CachedSampledTexture(const Context& context, std::span words) { +std::shared_ptr CachedSampledTexture(const Context& context, std::span words, VkImageViewType multisampleViewType) { const auto resource = DecodeTextureResource(words); - return cachedTexture(context, words, resource, ViewComponents(resource)); + return cachedTexture(context, words, resource, ViewComponents(resource), 0, false, multisampleViewType); } bool SampledTexturesShareEntry(std::span first, std::span second) { @@ -2849,7 +2849,8 @@ bool ShaderResources::Revalidate(std::span shaders, ProofR if (IsNullTextureResource(words)) continue; const auto resource = DecodeTextureResource(words); const VkComponentMapping components = ViewComponents(resource); - if (textureIndex >= textures.size() || cachedTexture(context, words, resource, components, 0, !binding.imageDepthCompare.empty() && binding.imageDepthCompare.at(element)) != textures[textureIndex]) return false; + const VkImageViewType multisampleViewType = binding.imageShape == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + if (textureIndex >= textures.size() || cachedTexture(context, words, resource, components, 0, !binding.imageDepthCompare.empty() && binding.imageDepthCompare.at(element), multisampleViewType) != textures[textureIndex]) return false; ++textureIndex; } } else if (binding.kind == ShaderRecompiler::DescriptorKind::StorageImage) { @@ -2894,7 +2895,8 @@ bool ShaderResources::Revalidate(std::span shaders, ProofR if (table.elementTextures[element] == std::numeric_limits::max()) continue; const auto words = std::span(images->guestDescriptor).subspan(static_cast(element) * 8u, 8u); const auto resource = DecodeTextureResource(words); - if (textureIndex >= textures.size() || table.elementTextures[element] != textureIndex || cachedTexture(context, words, resource, ViewComponents(resource), 0, images->imageDepthCompare.at(element)) != textures[textureIndex]) return false; + const VkImageViewType multisampleViewType = images->imageShapes.at(element) == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + if (textureIndex >= textures.size() || table.elementTextures[element] != textureIndex || cachedTexture(context, words, resource, ViewComponents(resource), 0, images->imageDepthCompare.at(element), multisampleViewType) != textures[textureIndex]) return false; ++textureIndex; } } @@ -3989,12 +3991,13 @@ void ShaderResources::resolveImageBinding(const ShaderRecompiler::DescriptorBind if (resource.dccAddress != 0) CompletePendingCpuWrites(context, resource.dccAddress, DccKeyBytes(guestBytes)); std::shared_ptr texture; const bool depthCompare = !binding.imageDepthCompare.empty() && binding.imageDepthCompare.at(element); - if (previousSampled.texture != nullptr && previousSampled.depthCompare == depthCompare && words.size() == 8 && std::equal(words.begin(), words.end(), previousSampled.words.begin())) texture = previousSampled.texture; + const VkImageViewType multisampleViewType = binding.imageShape == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; + if (previousSampled.texture != nullptr && previousSampled.depthCompare == depthCompare && previousSampled.multisampleViewType == multisampleViewType && words.size() == 8 && std::equal(words.begin(), words.end(), previousSampled.words.begin())) texture = previousSampled.texture; if (texture == nullptr && record != nullptr && record->texture != nullptr) { texture = fastTexture(*record); (texture != nullptr ? counters.fastHits : counters.fastMisses).fetch_add(1, std::memory_order_relaxed); } - if (texture == nullptr) texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare); + if (texture == nullptr) texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare, multisampleViewType); if (resource.dimension == TextureDimension::k2DMsaa) multisampleSources.push_back(resource.baseAddress); bool baseLevel = false; if (element < binding.imageUnnormalized.size() && binding.imageUnnormalized[element]) { @@ -4020,6 +4023,7 @@ void ShaderResources::resolveImageBinding(const ShaderRecompiler::DescriptorBind if (words.size() == 8) { std::copy(words.begin(), words.end(), previousSampled.words.begin()); previousSampled.depthCompare = depthCompare; + previousSampled.multisampleViewType = multisampleViewType; previousSampled.texture = texture; } textures.push_back(std::move(texture)); @@ -4238,9 +4242,10 @@ void ShaderResources::resolveImageTables() { GuestTextureResource resource{}; std::uint64_t guestBytes = 0; bool firstLayer = false; + auto shape = ShaderRecompiler::DescriptorImageShape::Image2D; try { resource = DecodeTextureResource(words); - const auto shape = binding.imageShapes.at(element); + shape = binding.imageShapes.at(element); firstLayer = shape == ShaderRecompiler::DescriptorImageShape::Image2D && resource.dimension == TextureDimension::k2DArray; if (!firstLayer && !MatchesGuestDimension(shape, resource.dimension)) throw std::runtime_error("AGC graphics: guest texture dimension disagrees with the table entry's image shape (shape " + std::to_string(static_cast(shape)) + ", dimension " + std::to_string(static_cast(resource.dimension)) + ")"); guestBytes = DescribeSurface(resource).guestBytes; @@ -4256,8 +4261,9 @@ void ShaderResources::resolveImageTables() { std::shared_ptr texture; const auto components = ViewComponents(resource); const bool depthCompare = binding.imageDepthCompare.at(element); + const VkImageViewType multisampleViewType = shape == ShaderRecompiler::DescriptorImageShape::Image2DMultisampleArray ? VK_IMAGE_VIEW_TYPE_2D_ARRAY : VK_IMAGE_VIEW_TYPE_2D; try { - texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare); + texture = cachedTexture(context, words, resource, components, guestBytes, depthCompare, multisampleViewType); } catch (const std::runtime_error& error) { table.map[faults + element] = rejectTableElement(table, words, error.what()); if (reusable) fresh.elements.push_back({{}, {}, {}, 0, false, true, error.what()});