Merge pull request #7802 from ionut27001/fix/agc-kept-set-weak-textures

fix(agc): let a kept descriptor set's table record hold its textures weakly
This commit is contained in:
Dean authored and GitHub committed 2026-10-20 03:52:02 +13:00
commit 7b18fa8d5d
3 files changed
+28 -6

No files matched your search

@@ -72,7 +72,7 @@ struct TableDescriptors {
std::uint32_t binding = 0;
std::vector<std::pair<std::uint32_t, std::uint32_t>> runs;
std::vector<VkDescriptorImageInfo> images;
std::shared_ptr<const std::vector<std::shared_ptr<Texture>>> textures;
std::shared_ptr<const std::vector<std::weak_ptr<Texture>>> textures;
};
class DescriptorCache {
@@ -570,7 +570,7 @@ private:
std::vector<std::pair<std::uint32_t, std::uint32_t>> runs;
};
std::array<TableWrites, 2> tableWrites;
std::shared_ptr<const TableDescriptors> keptTable(std::size_t set, std::shared_ptr<const std::vector<std::shared_ptr<Texture>>>& held) noexcept;
std::shared_ptr<const TableDescriptors> keptTable(std::size_t set, std::shared_ptr<const std::vector<std::weak_ptr<Texture>>>& held) noexcept;
std::uint32_t setOf(const Binding& binding) const;
std::vector<Allocation> allocations;
std::vector<VkDescriptorBufferInfo> descriptorBuffers;
@@ -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<std::ptrdiff_t>(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>& 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<std::ptrdiff_t>(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<const TableDescriptors> ShaderResources::keptTable(std::size_t set, std::shared_ptr<const std::vector<std::shared_ptr<Texture>>>& held) noexcept {
std::shared_ptr<const TableDescriptors> ShaderResources::keptTable(std::size_t set, std::shared_ptr<const std::vector<std::weak_ptr<Texture>>>& 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<const std::vector<std::shared_ptr<Texture>>>(std::move(textures));
if (held == nullptr) {
std::vector<std::weak_ptr<Texture>> weak;
weak.reserve(textures.size());
for (const auto& texture : textures) {
if (texture != nullptr) weak.push_back(texture);
}
held = std::make_shared<const std::vector<std::weak_ptr<Texture>>>(std::move(weak));
}
auto kept = std::make_shared<TableDescriptors>();
kept->binding = table.binding;
kept->runs = table.runs;
@@ -5062,7 +5070,7 @@ std::shared_ptr<const TableDescriptors> 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<const std::vector<std::shared_ptr<Texture>>> held;
std::shared_ptr<const std::vector<std::weak_ptr<Texture>>> held;
if (cacheAllocation.pool && context.descriptorCache != nullptr) context.descriptorCache->Free(cacheAllocation, keptTable(0, held));
if (pool) context.Function<PFN_vkDestroyDescriptorPool>("vkDestroyDescriptorPool")(context.device, pool, nullptr);
if (_layout && ownsLayout) context.Function<PFN_vkDestroyDescriptorSetLayout>("vkDestroyDescriptorSetLayout")(context.device, _layout, nullptr);
@@ -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");
}
}