Merge pull request #1580 from Zheorgyan/feat/storage-buffers-update-after-bind
feat(agc): bind storage buffers update-after-bind above the per-stage limit
This commit is contained in:
commit
a9ac7bc4c3
|
|
@ -201,6 +201,8 @@ struct VulkanDevice::State {
|
|||
// VK_EXT_descriptor_indexing with non-uniform image array indexing (bindless image tables in
|
||||
// graphics stages, and compute workgroups wider than a wave).
|
||||
bool descriptorIndexing = false;
|
||||
bool storageBufferUpdateAfterBind = false;
|
||||
VkPhysicalDeviceDescriptorIndexingPropertiesEXT descriptorIndexingProperties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT};
|
||||
bool imageInt64Atomics = false;
|
||||
bool bufferInt64Atomics = false;
|
||||
bool primitiveListRestart = false;
|
||||
|
|
@ -1008,17 +1010,23 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
VkPhysicalDeviceFeatures2 features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, &descriptorIndexingFeatures};
|
||||
state->InstanceFunction<PFN_vkGetPhysicalDeviceFeatures2>("vkGetPhysicalDeviceFeatures2")(selected, &features);
|
||||
state->descriptorIndexing = descriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing == VK_TRUE && descriptorIndexingFeatures.shaderStorageImageArrayNonUniformIndexing == VK_TRUE;
|
||||
state->storageBufferUpdateAfterBind = descriptorIndexingFeatures.descriptorBindingStorageBufferUpdateAfterBind == VK_TRUE;
|
||||
if (state->storageBufferUpdateAfterBind) {
|
||||
VkPhysicalDeviceProperties2 properties{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, &state->descriptorIndexingProperties};
|
||||
state->InstanceFunction<PFN_vkGetPhysicalDeviceProperties2>("vkGetPhysicalDeviceProperties2")(selected, &properties);
|
||||
}
|
||||
}
|
||||
descriptorIndexingFeatures = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT};
|
||||
if (state->descriptorIndexing || state->storageBufferUpdateAfterBind) deviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
|
||||
if (state->descriptorIndexing) {
|
||||
descriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing = VK_TRUE;
|
||||
descriptorIndexingFeatures.shaderStorageImageArrayNonUniformIndexing = VK_TRUE;
|
||||
deviceExtensions.push_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
|
||||
state->capabilities.push_back(spv::CapabilityShaderNonUniform);
|
||||
state->capabilities.push_back(spv::CapabilitySampledImageArrayNonUniformIndexing);
|
||||
state->capabilities.push_back(spv::CapabilityStorageImageArrayNonUniformIndexing);
|
||||
state->spirvExtensions.push_back("SPV_EXT_descriptor_indexing");
|
||||
}
|
||||
descriptorIndexingFeatures.descriptorBindingStorageBufferUpdateAfterBind = state->storageBufferUpdateAfterBind ? VK_TRUE : VK_FALSE;
|
||||
state->samplerAnisotropy = true;
|
||||
state->textureCompressionBC = true;
|
||||
deviceInfo.pEnabledFeatures = &enabled;
|
||||
|
|
@ -1074,7 +1082,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window) : state(std::make_u
|
|||
imageRobustnessFeatures.pNext = byteFeatures.pNext;
|
||||
byteFeatures.pNext = &imageRobustnessFeatures;
|
||||
}
|
||||
if (state->descriptorIndexing) {
|
||||
if (state->descriptorIndexing || state->storageBufferUpdateAfterBind) {
|
||||
descriptorIndexingFeatures.pNext = byteFeatures.pNext;
|
||||
byteFeatures.pNext = &descriptorIndexingFeatures;
|
||||
}
|
||||
|
|
@ -2558,6 +2566,7 @@ Graphics::Context VulkanDevice::buildContext() const {
|
|||
context.copiedWriters = state->copiedWriters.get();
|
||||
context.functions = state->functionsReady ? &state->deviceFunctions : nullptr;
|
||||
context.descriptorIndexing = state->descriptorIndexing;
|
||||
context.descriptorIndexingLimits = state->descriptorIndexingProperties;
|
||||
context.imageInt64Atomics = state->imageInt64Atomics;
|
||||
context.geometryShader = state->geometryShader;
|
||||
context.sampleRateShading = state->sampleRateShading;
|
||||
|
|
|
|||
|
|
@ -137,6 +137,7 @@ struct Context {
|
|||
// VK_EXT_descriptor_indexing with non-uniform sampled/storage image array indexing enabled
|
||||
// (bindless image tables in graphics stages).
|
||||
bool descriptorIndexing = false;
|
||||
VkPhysicalDeviceDescriptorIndexingPropertiesEXT descriptorIndexingLimits{};
|
||||
bool imageInt64Atomics = false;
|
||||
bool geometryShader = false;
|
||||
bool sampleRateShading = false;
|
||||
|
|
|
|||
|
|
@ -68,12 +68,12 @@ public:
|
|||
|
||||
// The layout for `key` (binding, type, count, stage flags per binding, as ShaderResources builds
|
||||
// it from `bindings`), created on first use.
|
||||
VkDescriptorSetLayout Layout(std::span<const std::uint32_t> key, std::span<const VkDescriptorSetLayoutBinding> bindings);
|
||||
VkDescriptorSetLayout Layout(std::span<const std::uint32_t> key, std::span<const VkDescriptorSetLayoutBinding> bindings, bool updateAfterBind = false);
|
||||
struct SetAllocation {
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
};
|
||||
SetAllocation Allocate(VkDescriptorSetLayout layout, std::span<const VkDescriptorPoolSize> sizes);
|
||||
SetAllocation Allocate(VkDescriptorSetLayout layout, std::span<const VkDescriptorPoolSize> sizes, bool updateAfterBind = false);
|
||||
void Free(const SetAllocation& allocation) noexcept;
|
||||
// APS5_PROFILE_DRAW counters: layouts served from the map / created, sets allocated, pools opened.
|
||||
struct Stats {
|
||||
|
|
@ -93,6 +93,7 @@ private:
|
|||
mutable std::mutex mutex;
|
||||
std::map<std::vector<std::uint32_t>, VkDescriptorSetLayout> layouts;
|
||||
std::vector<VkDescriptorPool> pools;
|
||||
std::vector<VkDescriptorPool> updateAfterBindPools;
|
||||
std::vector<VkDescriptorPool> dedicated;
|
||||
Stats stats;
|
||||
};
|
||||
|
|
@ -458,6 +459,7 @@ private:
|
|||
};
|
||||
std::vector<DeferredImages> deferredImages;
|
||||
std::uint64_t storageBuffers = 0;
|
||||
bool updateAfterBind = false;
|
||||
std::uint32_t plannedSampledImages = 0;
|
||||
std::uint32_t plannedStorageImages = 0;
|
||||
// The compute constructor's shader and captured regions: the caller's objects, valid only until
|
||||
|
|
|
|||
|
|
@ -965,6 +965,33 @@ void ShaderResources::build(std::span<const CompiledShader> shaders, const Color
|
|||
|
||||
namespace {
|
||||
|
||||
VkDescriptorSetLayout CreateSetLayout(const Context& context, std::span<const VkDescriptorSetLayoutBinding> bindings, bool updateAfterBind) {
|
||||
std::vector<VkDescriptorBindingFlags> flags;
|
||||
for (const auto& binding : bindings) flags.push_back(updateAfterBind && binding.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ? VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT : 0);
|
||||
VkDescriptorSetLayoutBindingFlagsCreateInfo bindingFlags{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO};
|
||||
bindingFlags.bindingCount = static_cast<std::uint32_t>(flags.size());
|
||||
bindingFlags.pBindingFlags = flags.data();
|
||||
VkDescriptorSetLayoutCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
|
||||
info.pNext = updateAfterBind ? &bindingFlags : nullptr;
|
||||
info.flags = updateAfterBind ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT : 0;
|
||||
info.bindingCount = static_cast<std::uint32_t>(bindings.size());
|
||||
info.pBindings = bindings.data();
|
||||
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
||||
Check(context.Function<PFN_vkCreateDescriptorSetLayout>("vkCreateDescriptorSetLayout")(context.device, &info, nullptr, &layout), "vkCreateDescriptorSetLayout");
|
||||
return layout;
|
||||
}
|
||||
|
||||
VkDescriptorPool CreateSetPool(const Context& context, std::span<const VkDescriptorPoolSize> sizes, bool updateAfterBind) {
|
||||
VkDescriptorPoolCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
info.flags = updateAfterBind ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT : 0;
|
||||
info.maxSets = 1;
|
||||
info.poolSizeCount = static_cast<std::uint32_t>(sizes.size());
|
||||
info.pPoolSizes = sizes.data();
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
Check(context.Function<PFN_vkCreateDescriptorPool>("vkCreateDescriptorPool")(context.device, &info, nullptr, &pool), "vkCreateDescriptorPool");
|
||||
return pool;
|
||||
}
|
||||
|
||||
// APS5_PROFILE_DRAW: the [resources] phase totals. Each thread accumulates its builds' phases in
|
||||
// arrays of its own and merges them into the shared totals every 1000 of its builds (and when it
|
||||
// ends), so no build takes the shared mutex per phase; the totals lag by up to 999 builds per
|
||||
|
|
@ -1045,7 +1072,8 @@ double ShaderResources::phase(BuildPhase which) {
|
|||
|
||||
namespace {
|
||||
|
||||
void ValidateDescriptorLayout(const Context& context, std::span<const VkDescriptorSetLayoutBinding> bindings) {
|
||||
void ValidateDescriptorLayout(const Context& context, std::span<const VkDescriptorSetLayoutBinding> bindings, bool updateAfterBind) {
|
||||
const auto maxStageResources = updateAfterBind ? context.descriptorIndexingLimits.maxPerStageUpdateAfterBindResources : context.limits.maxPerStageResources;
|
||||
std::map<VkDescriptorType, std::uint64_t> heapTotals;
|
||||
std::map<std::pair<VkShaderStageFlags, VkDescriptorType>, std::uint64_t> stageTotals;
|
||||
std::map<VkShaderStageFlags, std::uint64_t> stageResources;
|
||||
|
|
@ -1056,7 +1084,7 @@ void ValidateDescriptorLayout(const Context& context, std::span<const VkDescript
|
|||
if ((layout.stageFlags & stage) == 0u) continue;
|
||||
const auto count = stageTotals[{stage, layout.descriptorType}] += layout.descriptorCount;
|
||||
const auto all = stageResources[stage] += layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER ? 0u : layout.descriptorCount;
|
||||
Require(all <= context.limits.maxPerStageResources, "typed heaps exceed per-stage resource capacity");
|
||||
Require(all <= maxStageResources, "typed heaps exceed per-stage resource capacity");
|
||||
if (layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE) Require(count <= context.limits.maxPerStageDescriptorSampledImages, "typed heaps exceed per-stage sampled image capacity");
|
||||
if (layout.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE) Require(count <= context.limits.maxPerStageDescriptorStorageImages, "typed heaps exceed per-stage storage image capacity");
|
||||
if (layout.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER) Require(count <= context.limits.maxPerStageDescriptorSamplers, "typed heaps exceed per-stage sampler capacity");
|
||||
|
|
@ -1118,6 +1146,12 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
std::int64_t shaderData = -1;
|
||||
const auto firstSampler = samplers.size();
|
||||
const auto firstDeferred = deferredImages.size();
|
||||
const auto requireStageLimits = [&] {
|
||||
if (stageStorageBuffers <= context.limits.maxPerStageDescriptorStorageBuffers && stageResources <= context.limits.maxPerStageResources) return;
|
||||
const auto& limits = context.descriptorIndexingLimits;
|
||||
Require(stageStorageBuffers <= limits.maxPerStageDescriptorUpdateAfterBindStorageBuffers && stageResources <= limits.maxPerStageUpdateAfterBindResources, "shader descriptors exceed per-stage limits");
|
||||
updateAfterBind = true;
|
||||
};
|
||||
for (const auto& binding : shader.program->bindings) {
|
||||
Require(binding.descriptorSet == 0, "unexpected descriptor set: every shader resource must use descriptor set zero");
|
||||
Require(occupied.insert(binding.binding).second, "duplicate shader binding");
|
||||
|
|
@ -1126,7 +1160,7 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
const bool imageRole = binding.role == ShaderRecompiler::DescriptorRole::GuestImages || binding.role == ShaderRecompiler::DescriptorRole::GuestSamplers;
|
||||
if (imageRole) {
|
||||
if (binding.kind == ShaderRecompiler::DescriptorKind::SampledImage || binding.kind == ShaderRecompiler::DescriptorKind::StorageImage) stageResources += binding.count;
|
||||
Require(stageResources <= context.limits.maxPerStageResources, "shader descriptors exceed per-stage limits");
|
||||
requireStageLimits();
|
||||
addImageBinding(binding, flags);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1137,7 +1171,7 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
stageStorageBuffers += binding.count;
|
||||
stageResources += binding.count;
|
||||
storageBuffers += binding.count;
|
||||
Require(stageStorageBuffers <= context.limits.maxPerStageDescriptorStorageBuffers && stageResources <= context.limits.maxPerStageResources, "shader descriptors exceed per-stage limits");
|
||||
requireStageLimits();
|
||||
Binding item{{binding.binding, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, binding.count, flags, nullptr}, {}};
|
||||
if (binding.role == ShaderRecompiler::DescriptorRole::GuestBuffers) {
|
||||
Require(binding.guestDescriptor.size() == static_cast<std::uint64_t>(binding.count) * 4, "guest buffer descriptor must contain four DWORDs per array element");
|
||||
|
|
@ -1184,7 +1218,10 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
deferredImages[index].samplerCount = samplers.size() - firstSampler;
|
||||
}
|
||||
}
|
||||
Require(storageBuffers <= context.limits.maxDescriptorSetStorageBuffers, "pipeline descriptors exceed device limits");
|
||||
if (storageBuffers > context.limits.maxDescriptorSetStorageBuffers) {
|
||||
Require(storageBuffers <= context.descriptorIndexingLimits.maxDescriptorSetUpdateAfterBindStorageBuffers, "pipeline descriptors exceed device limits");
|
||||
updateAfterBind = true;
|
||||
}
|
||||
timing.bindingsMs = phase(BuildPhase::Bindings);
|
||||
// For every build, locked ones included: their stage B then takes the fast path too, and the
|
||||
// collects cost the same wherever they run.
|
||||
|
|
@ -1197,16 +1234,14 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
description.push_back(binding.layout);
|
||||
layoutKey.insert(layoutKey.end(), {binding.layout.binding, static_cast<std::uint32_t>(binding.layout.descriptorType), binding.layout.descriptorCount, binding.layout.stageFlags});
|
||||
}
|
||||
if (updateAfterBind) layoutKey.push_back(VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||
static const bool noLayoutCache = std::getenv("APS5_NO_LAYOUT_CACHE") != nullptr;
|
||||
static const bool noPoolCache = std::getenv("APS5_NO_POOL_CACHE") != nullptr;
|
||||
if (context.descriptorCache != nullptr && !noLayoutCache) {
|
||||
_layout = context.descriptorCache->Layout(layoutKey, description);
|
||||
_layout = context.descriptorCache->Layout(layoutKey, description, updateAfterBind);
|
||||
} else {
|
||||
ValidateDescriptorLayout(context, description);
|
||||
VkDescriptorSetLayoutCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
|
||||
info.bindingCount = static_cast<std::uint32_t>(description.size());
|
||||
info.pBindings = description.data();
|
||||
Check(context.Function<PFN_vkCreateDescriptorSetLayout>("vkCreateDescriptorSetLayout")(context.device, &info, nullptr, &_layout), "vkCreateDescriptorSetLayout");
|
||||
ValidateDescriptorLayout(context, description, updateAfterBind);
|
||||
_layout = CreateSetLayout(context, description, updateAfterBind);
|
||||
ownsLayout = true;
|
||||
}
|
||||
if (!bindings.empty()) {
|
||||
|
|
@ -1218,16 +1253,12 @@ void ShaderResources::buildPrepare(std::span<const CompiledShader> shaders, cons
|
|||
if (plannedStorageImages != 0) sizes.push_back({VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, plannedStorageImages});
|
||||
if (!samplers.empty()) sizes.push_back({VK_DESCRIPTOR_TYPE_SAMPLER, static_cast<std::uint32_t>(samplers.size())});
|
||||
if (context.descriptorCache != nullptr && !noPoolCache) {
|
||||
const auto allocated = context.descriptorCache->Allocate(_layout, sizes);
|
||||
const auto allocated = context.descriptorCache->Allocate(_layout, sizes, updateAfterBind);
|
||||
_set = allocated.set;
|
||||
cachePool = allocated.pool;
|
||||
}
|
||||
if (_set == VK_NULL_HANDLE) {
|
||||
VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
poolInfo.maxSets = 1;
|
||||
poolInfo.poolSizeCount = static_cast<std::uint32_t>(sizes.size());
|
||||
poolInfo.pPoolSizes = sizes.data();
|
||||
Check(context.Function<PFN_vkCreateDescriptorPool>("vkCreateDescriptorPool")(context.device, &poolInfo, nullptr, &pool), "vkCreateDescriptorPool");
|
||||
pool = CreateSetPool(context, sizes, updateAfterBind);
|
||||
VkDescriptorSetAllocateInfo allocation{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
|
||||
allocation.descriptorPool = pool;
|
||||
allocation.descriptorSetCount = 1;
|
||||
|
|
@ -2356,24 +2387,21 @@ DescriptorCache::DescriptorCache(const Context& context) : context(context), des
|
|||
|
||||
DescriptorCache::~DescriptorCache() {
|
||||
for (const auto pool : pools) destroyPool(context.device, pool, nullptr);
|
||||
for (const auto pool : updateAfterBindPools) destroyPool(context.device, pool, nullptr);
|
||||
for (const auto pool : dedicated) destroyPool(context.device, pool, nullptr);
|
||||
for (const auto& [key, layout] : layouts) destroyLayout(context.device, layout, nullptr);
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout DescriptorCache::Layout(std::span<const std::uint32_t> key, std::span<const VkDescriptorSetLayoutBinding> bindings) {
|
||||
VkDescriptorSetLayout DescriptorCache::Layout(std::span<const std::uint32_t> key, std::span<const VkDescriptorSetLayoutBinding> bindings, bool updateAfterBind) {
|
||||
std::lock_guard lock(mutex);
|
||||
std::vector<std::uint32_t> keyCopy(key.begin(), key.end());
|
||||
if (const auto found = layouts.find(keyCopy); found != layouts.end()) {
|
||||
++stats.layoutHits;
|
||||
return found->second;
|
||||
}
|
||||
ValidateDescriptorLayout(context, bindings);
|
||||
ValidateDescriptorLayout(context, bindings, updateAfterBind);
|
||||
++stats.layoutMisses;
|
||||
VkDescriptorSetLayoutCreateInfo info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
|
||||
info.bindingCount = static_cast<std::uint32_t>(bindings.size());
|
||||
info.pBindings = bindings.data();
|
||||
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
|
||||
Check(context.Function<PFN_vkCreateDescriptorSetLayout>("vkCreateDescriptorSetLayout")(context.device, &info, nullptr, &layout), "vkCreateDescriptorSetLayout");
|
||||
const auto layout = CreateSetLayout(context, bindings, updateAfterBind);
|
||||
layouts.emplace(std::move(keyCopy), layout);
|
||||
return layout;
|
||||
}
|
||||
|
|
@ -2395,10 +2423,11 @@ constexpr std::array<VkDescriptorPoolSize, 4> ChainPoolSizes{{{VK_DESCRIPTOR_TYP
|
|||
constexpr std::array<VkDescriptorPoolSize, 4> ChainPoolCreateSizes{{{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 4096 + 2}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1024}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1024}, {VK_DESCRIPTOR_TYPE_SAMPLER, 512}}};
|
||||
}
|
||||
|
||||
DescriptorCache::SetAllocation DescriptorCache::Allocate(VkDescriptorSetLayout layout, std::span<const VkDescriptorPoolSize> sizes) {
|
||||
DescriptorCache::SetAllocation DescriptorCache::Allocate(VkDescriptorSetLayout layout, std::span<const VkDescriptorPoolSize> sizes, bool updateAfterBind) {
|
||||
bool fits = true;
|
||||
for (const auto& size : sizes) {
|
||||
Require(size.descriptorCount <= SetDescriptorLimit(context.limits, size.type), "descriptor set exceeds the device's per-set descriptor limit");
|
||||
const auto limit = updateAfterBind && size.type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ? context.descriptorIndexingLimits.maxDescriptorSetUpdateAfterBindStorageBuffers : SetDescriptorLimit(context.limits, size.type);
|
||||
Require(size.descriptorCount <= limit, "descriptor set exceeds the device's per-set descriptor limit");
|
||||
const auto capacity = std::find_if(ChainPoolSizes.begin(), ChainPoolSizes.end(), [&](const auto& item) { return item.type == size.type; });
|
||||
if (capacity == ChainPoolSizes.end() || size.descriptorCount > capacity->descriptorCount) fits = false;
|
||||
}
|
||||
|
|
@ -2409,12 +2438,7 @@ DescriptorCache::SetAllocation DescriptorCache::Allocate(VkDescriptorSetLayout l
|
|||
allocation.pSetLayouts = &layout;
|
||||
if (!fits) {
|
||||
dedicated.reserve(dedicated.size() + 1);
|
||||
VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
poolInfo.maxSets = 1;
|
||||
poolInfo.poolSizeCount = static_cast<std::uint32_t>(sizes.size());
|
||||
poolInfo.pPoolSizes = sizes.data();
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
Check(context.Function<PFN_vkCreateDescriptorPool>("vkCreateDescriptorPool")(context.device, &poolInfo, nullptr, &pool), "vkCreateDescriptorPool");
|
||||
const auto pool = CreateSetPool(context, sizes, updateAfterBind);
|
||||
allocation.descriptorPool = pool;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
const auto result = allocate(context.device, &allocation, &set);
|
||||
|
|
@ -2429,7 +2453,8 @@ DescriptorCache::SetAllocation DescriptorCache::Allocate(VkDescriptorSetLayout l
|
|||
}
|
||||
// Newest pool first: it has the most room; a full or fragmented pool is left for its sets to
|
||||
// drain and tried again later.
|
||||
for (auto it = pools.rbegin(); it != pools.rend(); ++it) {
|
||||
auto& chain = updateAfterBind ? updateAfterBindPools : pools;
|
||||
for (auto it = chain.rbegin(); it != chain.rend(); ++it) {
|
||||
allocation.descriptorPool = *it;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
const auto result = allocate(context.device, &allocation, &set);
|
||||
|
|
@ -2440,13 +2465,13 @@ DescriptorCache::SetAllocation DescriptorCache::Allocate(VkDescriptorSetLayout l
|
|||
if (result != VK_ERROR_OUT_OF_POOL_MEMORY && result != VK_ERROR_FRAGMENTED_POOL) Check(result, "vkAllocateDescriptorSets");
|
||||
}
|
||||
VkDescriptorPoolCreateInfo poolInfo{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
||||
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT | (updateAfterBind ? VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT : 0);
|
||||
poolInfo.maxSets = ChainPoolSets;
|
||||
poolInfo.poolSizeCount = static_cast<std::uint32_t>(ChainPoolCreateSizes.size());
|
||||
poolInfo.pPoolSizes = ChainPoolCreateSizes.data();
|
||||
VkDescriptorPool pool = VK_NULL_HANDLE;
|
||||
Check(context.Function<PFN_vkCreateDescriptorPool>("vkCreateDescriptorPool")(context.device, &poolInfo, nullptr, &pool), "vkCreateDescriptorPool");
|
||||
pools.push_back(pool);
|
||||
chain.push_back(pool);
|
||||
++stats.pools;
|
||||
allocation.descriptorPool = pool;
|
||||
VkDescriptorSet set = VK_NULL_HANDLE;
|
||||
|
|
|
|||
|
|
@ -1248,6 +1248,8 @@ struct MockVulkan {
|
|||
std::optional<VkDeviceSize> memoryLimit;
|
||||
std::uint64_t allocationAttempts = 0;
|
||||
std::vector<VkDescriptorSetLayoutBinding> layoutBindings;
|
||||
VkDescriptorSetLayoutCreateFlags layoutFlags = 0;
|
||||
std::vector<VkDescriptorBindingFlags> layoutBindingFlags;
|
||||
std::vector<VkDescriptorPoolSize> poolSizes;
|
||||
std::uint32_t poolMaxSets = 0;
|
||||
VkDescriptorPoolCreateFlags poolFlags = 0;
|
||||
|
|
@ -1330,6 +1332,10 @@ VKAPI_ATTR void VKAPI_CALL mockFreeMemory(VkDevice, VkDeviceMemory memory, const
|
|||
VKAPI_ATTR VkResult VKAPI_CALL mockCreateDescriptorSetLayout(VkDevice, const VkDescriptorSetLayoutCreateInfo* info, const VkAllocationCallbacks*, VkDescriptorSetLayout* layout) {
|
||||
*layout = makeHandle<VkDescriptorSetLayout>();
|
||||
mock.layoutBindings.assign(info->pBindings, info->pBindings + info->bindingCount);
|
||||
mock.layoutFlags = info->flags;
|
||||
const auto* bindingFlags = static_cast<const VkDescriptorSetLayoutBindingFlagsCreateInfo*>(info->pNext);
|
||||
if (bindingFlags != nullptr) mock.layoutBindingFlags.assign(bindingFlags->pBindingFlags, bindingFlags->pBindingFlags + bindingFlags->bindingCount);
|
||||
else mock.layoutBindingFlags.clear();
|
||||
++mock.live;
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
|
@ -1736,6 +1742,32 @@ void resourceTests() {
|
|||
expectSingleAccepted(changed([](auto& binding) { binding.count = 3; binding.guestDescriptor = join(join(vsharp(guestFirst.data(), 16), vsharp(guestSecond.data(), 32)), vsharp(reinterpret_cast<const void*>(0x1000), 8)); }), "an unmapped V# element");
|
||||
expectSingleFailure(changed([&](auto& binding) { binding.count = 2; binding.guestDescriptor = join(vsharp(guestFirst.data(), 16), vsharp(reinterpret_cast<const void*>(state.color.address), 64)); }), "aliases the render target");
|
||||
expectSingleFailure(changed([](auto& binding) { binding.count = 17; binding.guestDescriptor.assign(68, 0); }), "per-stage limits");
|
||||
{
|
||||
ShaderRecompiler::RecompileResult vertex;
|
||||
ShaderRecompiler::RecompileResult fragment;
|
||||
std::vector<std::uint32_t> words;
|
||||
for (int index = 0; index < 17; ++index) words = join(words, vsharp(reinterpret_cast<const void*>(0x1000), 8));
|
||||
vertex.bindings.push_back(makeBinding(Role::GuestBuffers, 0, 17, words));
|
||||
auto updateAfterBind = mockContext();
|
||||
updateAfterBind.descriptorIndexingLimits.maxPerStageDescriptorUpdateAfterBindStorageBuffers = 17;
|
||||
updateAfterBind.descriptorIndexingLimits.maxPerStageUpdateAfterBindResources = 128;
|
||||
updateAfterBind.descriptorIndexingLimits.maxDescriptorSetUpdateAfterBindStorageBuffers = 32;
|
||||
mock = MockVulkan{};
|
||||
{
|
||||
AgcDriver::Graphics::ShaderResources resources(updateAfterBind, vertex, fragment, state.color, 0, 0);
|
||||
Require(mock.layoutFlags == VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, "buffers above the per-stage limit did not get an update-after-bind layout");
|
||||
Require(mock.layoutBindingFlags.size() == 1 && mock.layoutBindingFlags[0] == VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT, "the guest buffers binding is not update-after-bind");
|
||||
Require(mock.poolFlags == VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, "the update-after-bind set did not come from an update-after-bind pool");
|
||||
}
|
||||
Require(mock.live == 0, "update-after-bind shader resources leaked Vulkan objects");
|
||||
updateAfterBind.descriptorIndexingLimits.maxPerStageDescriptorUpdateAfterBindStorageBuffers = 16;
|
||||
expectFailure([&] { AgcDriver::Graphics::ShaderResources resources(updateAfterBind, vertex, fragment, state.color, 0, 0); }, "shader descriptors exceed per-stage limits");
|
||||
Require(mock.live == 0, "failed update-after-bind shader resources leaked Vulkan objects");
|
||||
mock = MockVulkan{};
|
||||
vertex.bindings.front() = makeBinding(Role::GuestBuffers, 0, 1, vsharp(reinterpret_cast<const void*>(0x1000), 8));
|
||||
{ AgcDriver::Graphics::ShaderResources resources(updateAfterBind, vertex, fragment, state.color, 0, 0); }
|
||||
Require(mock.layoutFlags == 0 && mock.layoutBindingFlags.empty() && mock.poolFlags == 0, "buffers within the per-stage limit took the update-after-bind path");
|
||||
}
|
||||
{
|
||||
ShaderRecompiler::RecompileResult vertex;
|
||||
ShaderRecompiler::RecompileResult fragment;
|
||||
|
|
|
|||
Loading…
Reference in New Issue