Skip to content

Commit

Permalink
Use variableDescriptorCount to avoid the pipeline recreation. (#21)
Browse files Browse the repository at this point in the history
* Use variableDescriptorCount to avoid the pipeline recreation.

* Make descriptorIndexingVariableDescriptorCount feature optional.
Workarounding Metal Argument Buffer bug.
  • Loading branch information
stripe2933 authored Feb 23, 2025
1 parent 7c7e686 commit bcefb3d
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 26 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ The extensions and feature used in this application are quite common in the mode
- `timelineSemaphore`
- `shaderInt8`
- (optional) `drawIndirectCount` (If not presented, GPU frustum culling will be unavailable and fallback to the CPU frustum culling.)
- (optional) `descriptorBindingVariableDescriptorCount` (If not presented, graphics pipelines are dependent to the asset texture count; for every asset loading, the pipelines will be recreated as their texture count is changing.)
- `VkPhysicalDeviceDynamicRenderingFeatures`
- `VkPhysicalDeviceSynchronization2Features`
- `VkPhysicalDeviceExtendedDynamicStateFeaturesEXT`
Expand Down
10 changes: 10 additions & 0 deletions impl/vulkan/Gpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ auto vk_gltf_viewer::vulkan::Gpu::selectPhysicalDevice(const vk::raii::Instance
!vulkan12Features.bufferDeviceAddress ||
!vulkan12Features.descriptorIndexing ||
!vulkan12Features.descriptorBindingSampledImageUpdateAfterBind ||
!vulkan12Features.descriptorBindingVariableDescriptorCount ||
!vulkan12Features.runtimeDescriptorArray ||
!vulkan12Features.storageBuffer8BitAccess ||
!vulkan12Features.uniformAndStorageBuffer8BitAccess ||
Expand Down Expand Up @@ -182,6 +183,14 @@ auto vk_gltf_viewer::vulkan::Gpu::createDevice() -> vk::raii::Device {

supportDrawIndirectCount = availableFeatures.template get<vk::PhysicalDeviceVulkan12Features>().drawIndirectCount;
supportUint8Index = availableFeatures.template get<vk::PhysicalDeviceIndexTypeUint8FeaturesKHR>().indexTypeUint8;
#if __APPLE__
// MoltenVK with Metal Argument Buffer does not work with variable descriptor count.
// Tracked issue: https://github.com/KhronosGroup/MoltenVK/issues/2343
// TODO: Remove this workaround when the issue is fixed.
supportVariableDescriptorCount = false;
#else
supportVariableDescriptorCount = availableFeatures.template get<vk::PhysicalDeviceVulkan12Features>().descriptorBindingVariableDescriptorCount;
#endif

const vku::RefHolder queueCreateInfos = Queues::getCreateInfos(physicalDevice, queueFamilies);
vk::StructureChain createInfo {
Expand All @@ -201,6 +210,7 @@ auto vk_gltf_viewer::vulkan::Gpu::createDevice() -> vk::raii::Device {
.setBufferDeviceAddress(true)
.setDescriptorIndexing(true)
.setDescriptorBindingSampledImageUpdateAfterBind(true)
.setDescriptorBindingVariableDescriptorCount(supportVariableDescriptorCount)
.setRuntimeDescriptorArray(true)
.setStorageBuffer8BitAccess(true)
.setUniformAndStorageBuffer8BitAccess(true)
Expand Down
1 change: 1 addition & 0 deletions interface/vulkan/Gpu.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ namespace vk_gltf_viewer::vulkan {
std::uint32_t subgroupSize;
std::uint32_t maxPerStageDescriptorUpdateAfterBindSamplers;
bool supportShaderImageLoadStoreLod;
bool supportVariableDescriptorCount;

Gpu(const vk::raii::Instance &instance [[clang::lifetimebound]], vk::SurfaceKHR surface);
~Gpu();
Expand Down
76 changes: 50 additions & 26 deletions interface/vulkan/SharedData.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ namespace vk_gltf_viewer::vulkan {
std::variant<ag::Swapchain, std::reference_wrapper<ag::Swapchain>> imGuiSwapchainAttachmentGroup;

// Descriptor pools.
vk::raii::DescriptorPool textureDescriptorPool;
std::optional<vk::raii::DescriptorPool> assetDescriptorPool; // nullopt if Gpu::supportVariableDescriptorCount is true.
vk::raii::DescriptorPool descriptorPool;

// Descriptor sets.
Expand All @@ -154,7 +154,14 @@ namespace vk_gltf_viewer::vulkan {
, cubeIndices { gpu.allocator }
, cubemapSampler { gpu.device }
, brdfLutSampler { gpu.device }
, assetDescriptorSetLayout { gpu, 1 } // TODO: set proper initial texture count.
, assetDescriptorSetLayout { [&]() {
if (gpu.supportVariableDescriptorCount) {
return dsl::Asset { gpu };
}
else {
return dsl::Asset { gpu, 1 }; // TODO: set proper initial texture count.
}
}() }
, imageBasedLightingDescriptorSetLayout { gpu.device, cubemapSampler, brdfLutSampler }
, skyboxDescriptorSetLayout { gpu.device, cubemapSampler }
, sceneRenderPass { gpu.device }
Expand All @@ -166,12 +173,16 @@ namespace vk_gltf_viewer::vulkan {
, weightedBlendedCompositionRenderer { gpu.device, sceneRenderPass }
, swapchainAttachmentGroup { gpu, swapchainExtent, swapchainImages }
, imGuiSwapchainAttachmentGroup { getImGuiSwapchainAttachmentGroup() }
, textureDescriptorPool { createTextureDescriptorPool() }
, descriptorPool { gpu.device, getPoolSizes(imageBasedLightingDescriptorSetLayout, skyboxDescriptorSetLayout).getDescriptorPoolCreateInfo() }
, descriptorPool { gpu.device, [&]() {
if (gpu.supportVariableDescriptorCount) {
return getPoolSizes(imageBasedLightingDescriptorSetLayout, skyboxDescriptorSetLayout, assetDescriptorSetLayout)
.getDescriptorPoolCreateInfo(vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet | vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind);
}
else {
return getPoolSizes(imageBasedLightingDescriptorSetLayout, skyboxDescriptorSetLayout).getDescriptorPoolCreateInfo();
}
}().get() }
, fallbackTexture { gpu }{
std::tie(assetDescriptorSet)
= vku::allocateDescriptorSets(*gpu.device, *textureDescriptorPool, std::tie(
assetDescriptorSetLayout));
std::tie(imageBasedLightingDescriptorSet, skyboxDescriptorSet)
= vku::allocateDescriptorSets(*gpu.device, *descriptorPool, std::tie(
imageBasedLightingDescriptorSetLayout,
Expand Down Expand Up @@ -247,21 +258,38 @@ namespace vk_gltf_viewer::vulkan {
}

const GltfAsset &inner = gltfAsset.emplace(asset, directory, nodeWorldTransforms, orderedPrimitives, gpu, adapter);
if (assetDescriptorSetLayout.descriptorCounts[5] != textureCount) {
// If texture count is different, descriptor set layouts, pipeline layouts and pipelines have to be recreated.
depthPipelines.clear();
maskDepthPipelines.clear();
jumpFloodSeedPipelines.clear();
maskJumpFloodSeedPipelines.clear();
primitivePipelines.clear();
unlitPrimitivePipelines.clear();

assetDescriptorSetLayout = { gpu, textureCount };
primitivePipelineLayout = { gpu.device, std::tie(imageBasedLightingDescriptorSetLayout, assetDescriptorSetLayout) };
primitiveNoShadingPipelineLayout = { gpu.device, assetDescriptorSetLayout };

textureDescriptorPool = createTextureDescriptorPool();
std::tie(assetDescriptorSet) = vku::allocateDescriptorSets(*gpu.device, *textureDescriptorPool, std::tie(assetDescriptorSetLayout));
if (gpu.supportVariableDescriptorCount) {
(*gpu.device).freeDescriptorSets(*descriptorPool, assetDescriptorSet);
assetDescriptorSet = decltype(assetDescriptorSet) {
vku::unsafe,
(*gpu.device).allocateDescriptorSets(vk::StructureChain {
vk::DescriptorSetAllocateInfo {
*descriptorPool,
*assetDescriptorSetLayout,
},
vk::DescriptorSetVariableDescriptorCountAllocateInfo {
vk::ArrayProxyNoTemporaries<const std::uint32_t> { textureCount },
}
}.get())[0],
};
}
else {
if (assetDescriptorSetLayout.descriptorCounts[5] != textureCount) {
// If texture count is different, descriptor set layouts, pipeline layouts and pipelines have to be recreated.
depthPipelines.clear();
maskDepthPipelines.clear();
jumpFloodSeedPipelines.clear();
maskJumpFloodSeedPipelines.clear();
primitivePipelines.clear();
unlitPrimitivePipelines.clear();

assetDescriptorSetLayout = { gpu, textureCount };
primitivePipelineLayout = { gpu.device, std::tie(imageBasedLightingDescriptorSetLayout, assetDescriptorSetLayout) };
primitiveNoShadingPipelineLayout = { gpu.device, assetDescriptorSetLayout };

assetDescriptorPool.emplace(gpu.device, getPoolSizes(assetDescriptorSetLayout).getDescriptorPoolCreateInfo(vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind));
std::tie(assetDescriptorSet) = vku::allocateDescriptorSets(*gpu.device, *assetDescriptorPool, std::tie(assetDescriptorSetLayout));
}
}

std::vector<vk::DescriptorImageInfo> imageInfos;
Expand Down Expand Up @@ -307,9 +335,5 @@ namespace vk_gltf_viewer::vulkan {
return decltype(imGuiSwapchainAttachmentGroup) { std::in_place_index<1>, swapchainAttachmentGroup };
}
}

[[nodiscard]] auto createTextureDescriptorPool() const -> vk::raii::DescriptorPool {
return { gpu.device, getPoolSizes(assetDescriptorSetLayout).getDescriptorPoolCreateInfo(vk::DescriptorPoolCreateFlagBits::eUpdateAfterBind) };
}
};
}
24 changes: 24 additions & 0 deletions interface/vulkan/descriptor_set_layout/Asset.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,30 @@ export import :vulkan.Gpu;

namespace vk_gltf_viewer::vulkan::dsl {
export struct Asset : vku::DescriptorSetLayout<vk::DescriptorType::eStorageBuffer, vk::DescriptorType::eStorageBuffer, vk::DescriptorType::eStorageBuffer, vk::DescriptorType::eStorageBuffer, vk::DescriptorType::eStorageBuffer, vk::DescriptorType::eCombinedImageSampler> {
explicit Asset(const Gpu &gpu [[clang::lifetimebound]])
: DescriptorSetLayout { gpu.device, vk::StructureChain {
vk::DescriptorSetLayoutCreateInfo {
vk::DescriptorSetLayoutCreateFlagBits::eUpdateAfterBindPool,
vku::unsafeProxy(getBindings(
{ 1, vk::ShaderStageFlagBits::eVertex },
{ 1, vk::ShaderStageFlagBits::eVertex },
{ 1, vk::ShaderStageFlagBits::eVertex },
{ 1, vk::ShaderStageFlagBits::eVertex },
{ 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment },
{ maxTextureCount(gpu), vk::ShaderStageFlagBits::eFragment })),
},
vk::DescriptorSetLayoutBindingFlagsCreateInfo {
vku::unsafeProxy<vk::DescriptorBindingFlags>({
{},
{},
{},
{},
{},
vk::DescriptorBindingFlagBits::eUpdateAfterBind | vk::DescriptorBindingFlagBits::eVariableDescriptorCount,
}),
},
}.get() } { }

Asset(const Gpu &gpu [[clang::lifetimebound]], std::uint32_t textureCount)
: DescriptorSetLayout { gpu.device, vk::StructureChain {
vk::DescriptorSetLayoutCreateInfo {
Expand Down

0 comments on commit bcefb3d

Please sign in to comment.