diff --git a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp index 12abe8bbd2..7b4aaa4858 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/include/ShaderResources.hpp @@ -72,7 +72,7 @@ struct TableDescriptors { std::uint32_t binding = 0; std::vector> runs; std::vector images; - std::shared_ptr>> textures; + std::shared_ptr>> textures; }; class DescriptorCache { @@ -570,7 +570,7 @@ private: std::vector> runs; }; std::array tableWrites; - std::shared_ptr keptTable(std::size_t set, std::shared_ptr>>& held) noexcept; + std::shared_ptr keptTable(std::size_t set, std::shared_ptr>>& held) noexcept; std::uint32_t setOf(const Binding& binding) const; std::vector allocations; std::vector descriptorBuffers; diff --git a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp index 64762c8541..56ba82e241 100644 --- a/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp +++ b/core/libs/prx/libSceAgcDriver/Graphics/src/ShaderResources.cpp @@ -2267,7 +2267,8 @@ void ShaderResources::buildComplete() { if (!recorded) continue; const auto& held = allocation.table; auto& counters = TextureCounts(); - if (held != nullptr && held->binding == binding.layout.binding && held->runs == runs && std::equal(held->images.begin(), held->images.end(), images.begin() + static_cast(firstImage), images.end(), [](const VkDescriptorImageInfo& a, const VkDescriptorImageInfo& b) { return a.sampler == b.sampler && a.imageView == b.imageView && a.imageLayout == b.imageLayout; })) { + const bool heldAlive = held != nullptr && held->textures != nullptr && std::none_of(held->textures->begin(), held->textures->end(), [](const std::weak_ptr& texture) { return texture.expired(); }); + if (heldAlive && held->binding == binding.layout.binding && held->runs == runs && std::equal(held->images.begin(), held->images.end(), images.begin() + static_cast(firstImage), images.end(), [](const VkDescriptorImageInfo& a, const VkDescriptorImageInfo& b) { return a.sampler == b.sampler && a.imageView == b.imageView && a.imageLayout == b.imageLayout; })) { keptWrites.emplace_back(firstWrite, writes.size()); counters.tableWritesKept.fetch_add(1, std::memory_order_relaxed); } else { @@ -5044,11 +5045,18 @@ ShaderResources::~ShaderResources() { release(); } -std::shared_ptr ShaderResources::keptTable(std::size_t set, std::shared_ptr>>& held) noexcept { +std::shared_ptr ShaderResources::keptTable(std::size_t set, std::shared_ptr>>& held) noexcept { const auto& table = tableWrites[set]; if (!table.written || KeptSets() == 0u || table.firstImage + table.images > descriptorImages.size()) return nullptr; try { - if (held == nullptr) held = std::make_shared>>(std::move(textures)); + if (held == nullptr) { + std::vector> weak; + weak.reserve(textures.size()); + for (const auto& texture : textures) { + if (texture != nullptr) weak.push_back(texture); + } + held = std::make_shared>>(std::move(weak)); + } auto kept = std::make_shared(); kept->binding = table.binding; kept->runs = table.runs; @@ -5062,7 +5070,7 @@ std::shared_ptr ShaderResources::keptTable(std::size_t s void ShaderResources::release() noexcept { // A set from the cache's pool chain goes back to it; a dedicated pool dies with its set. - std::shared_ptr>> held; + std::shared_ptr>> held; if (cacheAllocation.pool && context.descriptorCache != nullptr) context.descriptorCache->Free(cacheAllocation, keptTable(0, held)); if (pool) context.Function("vkDestroyDescriptorPool")(context.device, pool, nullptr); if (_layout && ownsLayout) context.Function("vkDestroyDescriptorSetLayout")(context.device, _layout, nullptr); diff --git a/core/libs/prx/libSceAgcDriver/tests/execution/ImageTableReuse.cpp b/core/libs/prx/libSceAgcDriver/tests/execution/ImageTableReuse.cpp index d5a6b46847..a46aeb00be 100644 --- a/core/libs/prx/libSceAgcDriver/tests/execution/ImageTableReuse.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/execution/ImageTableReuse.cpp @@ -330,6 +330,20 @@ void RunTests(AgcDriver::VulkanDevice& device, const GuestBlock& block, const Gu Require(Dispatch(device, result, 15) == Row(block.Texels(15)), "image table reuse: the table after a dispatch wrote an entry sampled the wrong entry"); RunImageStore(device, stores); + + AgcDriver::Graphics::ReleaseTextureCaches(~std::uint64_t{0}); + Require(AgcDriver::Graphics::SampledTextureMemory() == 0u, "image table reuse: " + std::to_string(AgcDriver::Graphics::SampledTextureMemory()) + " bytes of sampled textures outlived a full release of the texture caches after the kept-table steps"); + + AgcDriver::GuestMemory::BumpCollectEpoch(); + Require(Dispatch(device, result, 29) == Row(block.Texels(29)), "image table reuse: the table before its textures were destroyed sampled the wrong entry"); + before = AgcDriver::Graphics::ImageTableReuses(); + Require(Dispatch(device, result, 29) == Row(block.Texels(29)), "image table reuse: the table built again before its textures were destroyed sampled the wrong entry"); + ExpectWrites(before, 1, 0, "a kept table built again before its textures were destroyed"); + AgcDriver::Graphics::ReleaseTextureCaches(~std::uint64_t{0}); + Require(AgcDriver::Graphics::SampledTextureMemory() == 0u, "image table reuse: " + std::to_string(AgcDriver::Graphics::SampledTextureMemory()) + " bytes of sampled textures outlived a full release of the texture caches after a table's set was kept"); + before = AgcDriver::Graphics::ImageTableReuses(); + Require(Dispatch(device, result, 29) == Row(block.Texels(29)), "image table reuse: a kept table whose textures were destroyed sampled the wrong entry"); + ExpectWrites(before, 0, 1, "a kept table whose textures were destroyed"); } }