1
1
forked from 0ad/0ad

Fixes descriptor set overwrite when multiple textures reference it with delayed deletion. Fixes #6717

Refs #6636

Comments By: phosit, Stan
Tested By: phosit, Stan
Differential Revision: https://code.wildfiregames.com/D4916
This was SVN commit r27511.
This commit is contained in:
Vladislav Belov 2023-01-30 21:23:44 +00:00
parent c17bffff1e
commit ac75966d67
3 changed files with 69 additions and 34 deletions

View File

@ -39,7 +39,13 @@ namespace Vulkan
{
CDescriptorManager::CDescriptorManager(CDevice* device, const bool useDescriptorIndexing)
: m_Device(device), m_UseDescriptorIndexing(useDescriptorIndexing)
: m_Device(device), m_UseDescriptorIndexing(useDescriptorIndexing),
m_ErrorTexture(device->CreateTexture(
"DescriptorManagerErrorTexture", ITexture::Type::TEXTURE_2D,
ITexture::Usage::TRANSFER_DST | ITexture::Usage::SAMPLED,
Format::R8G8B8A8_UNORM, 1, 1,
Sampler::MakeDefaultSampler(Sampler::Filter::NEAREST, Sampler::AddressMode::REPEAT),
1, 1))
{
if (useDescriptorIndexing)
{
@ -190,8 +196,8 @@ CDescriptorManager::SingleTypePool& CDescriptorManager::GetSingleTypePool(
pool.firstFreeIndex = 0;
pool.elements.reserve(maxSets);
for (uint32_t index = 0; index < maxSets; ++index)
pool.elements.push_back({VK_NULL_HANDLE, static_cast<int16_t>(index + 1)});
pool.elements.back().second = -1;
pool.elements.push_back({VK_NULL_HANDLE, 1, static_cast<int16_t>(index + 1)});
pool.elements.back().nextFreeIndex = SingleTypePool::INVALID_INDEX;
}
return pool;
}
@ -224,11 +230,15 @@ VkDescriptorSet CDescriptorManager::GetSingleTypeDescritorSet(
{
SingleTypePool& pool = GetSingleTypePool(type, texturesUID.size());
const int16_t elementIndex = pool.firstFreeIndex;
ENSURE(elementIndex != -1);
std::pair<VkDescriptorSet, int16_t>& element = pool.elements[elementIndex];
pool.firstFreeIndex = element.second;
ENSURE(elementIndex != SingleTypePool::INVALID_INDEX);
SingleTypePool::Element& element = pool.elements[elementIndex];
ENSURE(pool.firstFreeIndex != element.nextFreeIndex);
pool.firstFreeIndex = element.nextFreeIndex;
++element.version;
// Occupy the index.
element.nextFreeIndex = SingleTypePool::INVALID_INDEX;
if (element.first == VK_NULL_HANDLE)
if (element.set == VK_NULL_HANDLE)
{
VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{};
descriptorSetAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
@ -237,41 +247,40 @@ VkDescriptorSet CDescriptorManager::GetSingleTypeDescritorSet(
descriptorSetAllocateInfo.pSetLayouts = &layout;
ENSURE_VK_SUCCESS(vkAllocateDescriptorSets(
m_Device->GetVkDevice(), &descriptorSetAllocateInfo, &element.first));
m_Device->GetVkDevice(), &descriptorSetAllocateInfo, &element.set));
}
it = m_SingleTypeSets.emplace(key, element.first).first;
it = m_SingleTypeSets.emplace(key, element.set).first;
for (const CTexture::UID uid : texturesUID)
m_TextureSingleTypePoolMap[uid].push_back({type, static_cast<uint8_t>(texturesUID.size()), elementIndex});
if (uid != CTexture::INVALID_UID)
m_TextureSingleTypePoolMap[uid].push_back({type, element.version, elementIndex, static_cast<uint8_t>(texturesUID.size())});
PS::StaticVector<VkDescriptorImageInfo, 16> infos;
PS::StaticVector<VkWriteDescriptorSet, 16> writes;
for (size_t index = 0; index < textures.size(); ++index)
for (CTexture* texture : textures)
{
if (!textures[index])
continue;
ENSURE(textures[index]->GetUsage() & ITexture::Usage::SAMPLED);
if (!texture)
texture = m_ErrorTexture->As<CTexture>();
ENSURE(texture->GetUsage() & ITexture::Usage::SAMPLED);
VkDescriptorImageInfo descriptorImageInfo{};
descriptorImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
descriptorImageInfo.imageView = textures[index]->GetSamplerImageView();
descriptorImageInfo.sampler = textures[index]->GetSampler();
descriptorImageInfo.imageView = texture->GetSamplerImageView();
descriptorImageInfo.sampler = texture->GetSampler();
infos.emplace_back(std::move(descriptorImageInfo));
VkWriteDescriptorSet writeDescriptorSet{};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.dstSet = element.first;
writeDescriptorSet.dstBinding = index;
writeDescriptorSet.dstArrayElement = 0;
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
writeDescriptorSet.descriptorCount = 1;
writeDescriptorSet.pImageInfo = &infos.back();
writes.emplace_back(std::move(writeDescriptorSet));
}
VkWriteDescriptorSet writeDescriptorSet{};
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
writeDescriptorSet.dstSet = element.set;
writeDescriptorSet.dstBinding = 0;
writeDescriptorSet.dstArrayElement = 0;
writeDescriptorSet.descriptorType = type;
writeDescriptorSet.descriptorCount = static_cast<uint32_t>(infos.size());
writeDescriptorSet.pImageInfo = infos.data();
vkUpdateDescriptorSets(
m_Device->GetVkDevice(), writes.size(), writes.data(), 0, nullptr);
m_Device->GetVkDevice(), 1, &writeDescriptorSet, 0, nullptr);
}
return it->second;
}
@ -330,6 +339,7 @@ uint32_t CDescriptorManager::GetTextureDescriptor(CTexture* texture)
void CDescriptorManager::OnTextureDestroy(const CTexture::UID uid)
{
ENSURE(uid != CTexture::INVALID_UID);
if (m_UseDescriptorIndexing)
{
DescriptorIndexingBindingMap& bindingMap =
@ -342,6 +352,7 @@ void CDescriptorManager::OnTextureDestroy(const CTexture::UID uid)
const int16_t index = it->second;
bindingMap.elements[index] = bindingMap.firstFreeIndex;
bindingMap.firstFreeIndex = index;
bindingMap.map.erase(it);
}
else
{
@ -350,11 +361,18 @@ void CDescriptorManager::OnTextureDestroy(const CTexture::UID uid)
return;
for (const auto& entry : it->second)
{
SingleTypePool& pool = GetSingleTypePool(std::get<0>(entry), std::get<1>(entry));
const int16_t elementIndex = std::get<2>(entry);
pool.elements[elementIndex].second = pool.firstFreeIndex;
pool.firstFreeIndex = elementIndex;
SingleTypePool& pool = GetSingleTypePool(entry.type, entry.size);
SingleTypePool::Element& element = pool.elements[entry.elementIndex];
// Multiple textures might be used by the same descriptor set and
// we don't need to reset it if it was already.
if (element.version == entry.version && element.nextFreeIndex == SingleTypePool::INVALID_INDEX)
{
ENSURE(pool.firstFreeIndex != entry.elementIndex);
element.nextFreeIndex = pool.firstFreeIndex;
pool.firstFreeIndex = entry.elementIndex;
}
}
m_TextureSingleTypePoolMap.erase(it);
}
}

View File

@ -76,7 +76,14 @@ private:
VkDescriptorSetLayout layout;
VkDescriptorPool pool;
int16_t firstFreeIndex = 0;
std::vector<std::pair<VkDescriptorSet, int16_t>> elements;
static constexpr int16_t INVALID_INDEX = -1;
struct Element
{
VkDescriptorSet set = VK_NULL_HANDLE;
uint32_t version = 0;
int16_t nextFreeIndex = INVALID_INDEX;
};
std::vector<Element> elements;
};
SingleTypePool& GetSingleTypePool(const VkDescriptorType type, const uint32_t size);
@ -105,7 +112,14 @@ private:
std::unordered_map<CTexture::UID, uint32_t> m_TextureToBindingMap;
std::unordered_map<VkDescriptorType, std::vector<SingleTypePool>> m_SingleTypePools;
std::unordered_map<CTexture::UID, std::vector<std::tuple<VkDescriptorType, uint8_t, int16_t>>> m_TextureSingleTypePoolMap;
struct SingleTypePoolReference
{
VkDescriptorType type = VK_DESCRIPTOR_TYPE_MAX_ENUM;
uint32_t version = 0;
int16_t elementIndex = SingleTypePool::INVALID_INDEX;
uint8_t size = 0;
};
std::unordered_map<CTexture::UID, std::vector<SingleTypePoolReference>> m_TextureSingleTypePoolMap;
using SingleTypeCacheKey = std::pair<VkDescriptorSetLayout, std::vector<CTexture::UID>>;
struct SingleTypeCacheKeyHash
@ -113,6 +127,8 @@ private:
size_t operator()(const SingleTypeCacheKey& key) const;
};
std::unordered_map<SingleTypeCacheKey, VkDescriptorSet, SingleTypeCacheKeyHash> m_SingleTypeSets;
std::unique_ptr<ITexture> m_ErrorTexture;
};
} // namespace Vulkan

View File

@ -72,6 +72,7 @@ public:
* a too big texture flow.
*/
using UID = uint32_t;
static constexpr UID INVALID_UID = 0;
UID GetUID() const { return m_UID; }
private: