Skip to content

Commit eaad1cd

Browse files
committed
allow for full UI resources setup freedom, exchange descriptor set layout with pipeline layout in the creation parameters, update examples_tests submodule
1 parent 551c735 commit eaad1cd

File tree

5 files changed

+110
-105
lines changed

5 files changed

+110
-105
lines changed

examples_tests

include/nbl/ext/ImGui/ImGui.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,17 @@ class UI final : public core::IReferenceCounted
3333
{
3434
struct S_RESOURCE_PARAMETERS
3535
{
36-
nbl::video::IGPUDescriptorSetLayout* const descriptorSetLayout = nullptr; //! optional, if not provided then default layout will be created declaring:
37-
const uint32_t setIx = 0u, // -> following set for ImGUI resources which consists of textures (ImGUI font atlas + optional user provided textures) & corresponding *immutable* samplers
38-
count = 0x45u, // -> common amount of resources (since for a texture there is a sampler)
39-
texturesBindingIx = 0u, // -> binding index for textures
40-
samplersBindingIx = 1u; // -> binding index for samplers
36+
nbl::video::IGPUPipelineLayout* const pipelineLayout = nullptr; //! optional, default layout used if not provided declaring required UI resources such as textures (required font atlas + optional user defined textures) & corresponding samplers
37+
uint32_t count = 0x45u; //! amount of total UI textures (and corresponding samplers)
38+
39+
struct S_BINDING_REQUEST_INFO //! for a given pipeline layout we need to know what is intended for UI resources
40+
{
41+
uint32_t setIx, //! descriptor set index for a resource
42+
bindingIx; //! binding index for a given resource
43+
};
44+
45+
const S_BINDING_REQUEST_INFO textures = { .setIx = 0u, .bindingIx = 0u }, //! optional, default texture binding request info used if not provided (set & binding index)
46+
samplers = { .setIx = 0u, .bindingIx = 1u }; //! optional, default sampler binding request info used if not provided (set & binding index)
4147

4248
using binding_flags_t = nbl::video::IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS;
4349
static constexpr auto TEXTURES_REQUIRED_CREATE_FLAGS = nbl::core::bitflag(binding_flags_t::ECF_UPDATE_AFTER_BIND_BIT) | binding_flags_t::ECF_PARTIALLY_BOUND_BIT | binding_flags_t::ECF_UPDATE_UNUSED_WHILE_PENDING_BIT; //! required flags
@@ -83,8 +89,8 @@ class UI final : public core::IReferenceCounted
8389
//! updates ImGuiIO & records ImGUI *cpu* draw command lists, you have to call it before .render
8490
bool update(const S_UPDATE_PARAMETERS& params);
8591

86-
//! updates mapped mdi buffer & records *gpu* draw commands
87-
bool render(nbl::video::IGPUCommandBuffer* commandBuffer, nbl::video::ISemaphore::SWaitInfo waitInfo, const nbl::video::IGPUDescriptorSet* const descriptorSet, const std::span<const VkRect2D> scissors = {});
92+
//! updates mapped mdi buffer & records *gpu* draw command, you are required to bind UI's graphics pipeline & descriptor sets before calling this function - use getPipeline() to get the pipeline & getCreationParameters() to get info about your set resources
93+
bool render(nbl::video::IGPUCommandBuffer* commandBuffer, nbl::video::ISemaphore::SWaitInfo waitInfo, const std::span<const VkRect2D> scissors = {});
8894

8995
//! registers lambda listener in which ImGUI calls should be recorded
9096
size_t registerListener(std::function<void()> const& listener);

src/nbl/ext/ImGui/ImGui.cpp

Lines changed: 85 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ namespace nbl::ext::imgui
2222
{
2323
void UI::createPipeline()
2424
{
25-
// Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix
2625
SPushConstantRange pushConstantRanges[] =
2726
{
2827
{
@@ -32,87 +31,77 @@ namespace nbl::ext::imgui
3231
}
3332
};
3433

35-
auto createPipelineLayout = [&](const uint32_t setIx, core::smart_refctd_ptr<IGPUDescriptorSetLayout> descriptorSetLayout) -> core::smart_refctd_ptr<IGPUPipelineLayout>
34+
auto pipelineLayout = m_creationParams.resources.pipelineLayout ? smart_refctd_ptr<IGPUPipelineLayout>(m_creationParams.resources.pipelineLayout) /* provided? good, at this point its validated and we just use it */ :
35+
[&]() -> smart_refctd_ptr<IGPUPipelineLayout>
3636
{
37-
switch (setIx)
37+
//! if default pipeline layout is not provided, we create it here given your request binding info with certain constraints about samplers - they are immutable separate and part of the default layout
38+
smart_refctd_ptr<IGPUSampler> fontAtlasUISampler, userTexturesSampler;
39+
40+
using binding_flags_t = IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS;
3841
{
39-
case 0u:
40-
return m_creationParams.utilities->getLogicalDevice()->createPipelineLayout(pushConstantRanges, smart_refctd_ptr(descriptorSetLayout));
41-
case 1u:
42-
return m_creationParams.utilities->getLogicalDevice()->createPipelineLayout(pushConstantRanges, nullptr, smart_refctd_ptr(descriptorSetLayout));
43-
case 2u:
44-
return m_creationParams.utilities->getLogicalDevice()->createPipelineLayout(pushConstantRanges, nullptr, nullptr, smart_refctd_ptr(descriptorSetLayout));
45-
case 3u:
46-
return m_creationParams.utilities->getLogicalDevice()->createPipelineLayout(pushConstantRanges, nullptr, nullptr, nullptr, smart_refctd_ptr(descriptorSetLayout));
47-
default:
48-
assert(false);
49-
return nullptr;
42+
IGPUSampler::SParams params;
43+
params.AnisotropicFilter = 1u;
44+
params.TextureWrapU = ISampler::ETC_REPEAT;
45+
params.TextureWrapV = ISampler::ETC_REPEAT;
46+
params.TextureWrapW = ISampler::ETC_REPEAT;
47+
48+
fontAtlasUISampler = m_creationParams.utilities->getLogicalDevice()->createSampler(params);
49+
fontAtlasUISampler->setObjectDebugName("Nabla default ImGUI font UI sampler");
5050
}
51-
};
5251

53-
auto pipelineLayout = createPipelineLayout(m_creationParams.resources.setIx,
54-
[&]() -> smart_refctd_ptr<IGPUDescriptorSetLayout>
55-
{
56-
if (m_creationParams.resources.descriptorSetLayout)
57-
return smart_refctd_ptr<IGPUDescriptorSetLayout>(m_creationParams.resources.descriptorSetLayout); // provided? good we just use it, we are validated at this point
58-
else
5952
{
60-
//! if default descriptor set layout is not provided, we create it here
61-
smart_refctd_ptr<IGPUSampler> fontAtlasUISampler, userTexturesSampler;
53+
IGPUSampler::SParams params;
54+
params.MinLod = 0.f;
55+
params.MaxLod = 0.f;
56+
params.TextureWrapU = ISampler::ETC_CLAMP_TO_EDGE;
57+
params.TextureWrapV = ISampler::ETC_CLAMP_TO_EDGE;
58+
params.TextureWrapW = ISampler::ETC_CLAMP_TO_EDGE;
59+
60+
userTexturesSampler = m_creationParams.utilities->getLogicalDevice()->createSampler(params);
61+
userTexturesSampler->setObjectDebugName("Nabla default ImGUI custom texture sampler");
62+
}
6263

63-
using binding_flags_t = IGPUDescriptorSetLayout::SBinding::E_CREATE_FLAGS;
64-
{
65-
IGPUSampler::SParams params;
66-
params.AnisotropicFilter = 1u;
67-
params.TextureWrapU = ISampler::ETC_REPEAT;
68-
params.TextureWrapV = ISampler::ETC_REPEAT;
69-
params.TextureWrapW = ISampler::ETC_REPEAT;
70-
71-
fontAtlasUISampler = m_creationParams.utilities->getLogicalDevice()->createSampler(params);
72-
fontAtlasUISampler->setObjectDebugName("Nabla default ImGUI font UI sampler");
73-
}
64+
//! as mentioned we use immutable separate samplers and they are part of the descriptor set layout
65+
std::vector<core::smart_refctd_ptr<IGPUSampler>> immutableSamplers(m_creationParams.resources.count);
66+
for (auto& it : immutableSamplers)
67+
it = smart_refctd_ptr(userTexturesSampler);
7468

75-
{
76-
IGPUSampler::SParams params;
77-
params.MinLod = 0.f;
78-
params.MaxLod = 0.f;
79-
params.TextureWrapU = ISampler::ETC_CLAMP_TO_EDGE;
80-
params.TextureWrapV = ISampler::ETC_CLAMP_TO_EDGE;
81-
params.TextureWrapW = ISampler::ETC_CLAMP_TO_EDGE;
82-
83-
userTexturesSampler = m_creationParams.utilities->getLogicalDevice()->createSampler(params);
84-
userTexturesSampler->setObjectDebugName("Nabla default ImGUI custom texture sampler");
85-
}
69+
immutableSamplers[nbl::ext::imgui::UI::NBL_FONT_ATLAS_TEX_ID] = smart_refctd_ptr(fontAtlasUISampler);
8670

87-
//! note we use immutable separate samplers and they are part of the descriptor set layout
88-
std::vector<core::smart_refctd_ptr<IGPUSampler>> immutableSamplers(m_creationParams.resources.count);
89-
for (auto& it : immutableSamplers)
90-
it = smart_refctd_ptr(userTexturesSampler);
71+
auto textureBinding = IGPUDescriptorSetLayout::SBinding
72+
{
73+
.binding = m_creationParams.resources.textures.bindingIx,
74+
.type = IDescriptor::E_TYPE::ET_SAMPLED_IMAGE,
75+
.createFlags = m_creationParams.resources.TEXTURES_REQUIRED_CREATE_FLAGS,
76+
.stageFlags = m_creationParams.resources.RESOURCES_REQUIRED_STAGE_FLAGS,
77+
.count = m_creationParams.resources.count
78+
};
9179

92-
immutableSamplers[nbl::ext::imgui::UI::NBL_FONT_ATLAS_TEX_ID] = smart_refctd_ptr(fontAtlasUISampler);
80+
auto samplersBinding = IGPUDescriptorSetLayout::SBinding
81+
{
82+
.binding = m_creationParams.resources.samplers.bindingIx,
83+
.type = IDescriptor::E_TYPE::ET_SAMPLER,
84+
.createFlags = m_creationParams.resources.SAMPLERS_REQUIRED_CREATE_FLAGS,
85+
.stageFlags = m_creationParams.resources.RESOURCES_REQUIRED_STAGE_FLAGS,
86+
.count = m_creationParams.resources.count,
87+
.immutableSamplers = immutableSamplers.data()
88+
};
9389

94-
const IGPUDescriptorSetLayout::SBinding bindings[] =
95-
{
96-
{
97-
.binding = m_creationParams.resources.texturesBindingIx,
98-
.type = IDescriptor::E_TYPE::ET_SAMPLED_IMAGE,
99-
.createFlags = m_creationParams.resources.TEXTURES_REQUIRED_CREATE_FLAGS,
100-
.stageFlags = m_creationParams.resources.RESOURCES_REQUIRED_STAGE_FLAGS,
101-
.count = m_creationParams.resources.count
102-
},
103-
{
104-
.binding = m_creationParams.resources.samplersBindingIx,
105-
.type = IDescriptor::E_TYPE::ET_SAMPLER,
106-
.createFlags = m_creationParams.resources.SAMPLERS_REQUIRED_CREATE_FLAGS,
107-
.stageFlags = m_creationParams.resources.RESOURCES_REQUIRED_STAGE_FLAGS,
108-
.count = m_creationParams.resources.count,
109-
.immutableSamplers = immutableSamplers.data()
110-
}
111-
};
90+
auto layouts = std::to_array<smart_refctd_ptr<IGPUDescriptorSetLayout>>({nullptr, nullptr, nullptr, nullptr });
11291

113-
return m_creationParams.utilities->getLogicalDevice()->createDescriptorSetLayout(bindings);
92+
if (m_creationParams.resources.textures.setIx == m_creationParams.resources.samplers.setIx)
93+
layouts[m_creationParams.resources.textures.setIx] = m_creationParams.utilities->getLogicalDevice()->createDescriptorSetLayout({{textureBinding, samplersBinding}});
94+
else
95+
{
96+
layouts[m_creationParams.resources.textures.setIx] = m_creationParams.utilities->getLogicalDevice()->createDescriptorSetLayout({{textureBinding}});
97+
layouts[m_creationParams.resources.samplers.setIx] = m_creationParams.utilities->getLogicalDevice()->createDescriptorSetLayout({{samplersBinding}});
11498
}
115-
}());
99+
100+
assert(layouts[m_creationParams.resources.textures.setIx]);
101+
assert(layouts[m_creationParams.resources.samplers.setIx]);
102+
103+
return m_creationParams.utilities->getLogicalDevice()->createPipelineLayout(pushConstantRanges, std::move(layouts[0u]), std::move(layouts[1u]), std::move(layouts[2u]), std::move(layouts[3u]));
104+
}();
116105

117106
if (!pipelineLayout)
118107
{
@@ -178,9 +167,10 @@ namespace nbl::ext::imgui
178167
std::stringstream stream;
179168

180169
stream << "// -> this code has been autogenerated with Nabla ImGUI extension\n"
181-
<< "#define NBL_TEXTURES_BINDING " << m_creationParams.resources.texturesBindingIx << "\n"
182-
<< "#define NBL_SAMPLER_STATES_BINDING " << m_creationParams.resources.samplersBindingIx << "\n"
183-
<< "#define NBL_RESOURCES_SET " << m_creationParams.resources.setIx << "\n"
170+
<< "#define NBL_TEXTURES_BINDING_IX " << m_creationParams.resources.textures.bindingIx << "\n"
171+
<< "#define NBL_SAMPLER_STATES_BINDING_IX " << m_creationParams.resources.samplers.bindingIx << "\n"
172+
<< "#define NBL_TEXTURES_SET_IX " << m_creationParams.resources.textures.setIx << "\n"
173+
<< "#define NBL_SAMPLER_STATES_SET_IX " << m_creationParams.resources.samplers.setIx << "\n"
184174
<< "#define NBL_RESOURCES_COUNT " << m_creationParams.resources.count << "\n"
185175
<< "// <-\n\n";
186176

@@ -676,14 +666,23 @@ namespace nbl::ext::imgui
676666
{
677667
auto validateResourcesInfo = [&]() -> bool
678668
{
679-
if (m_creationParams.resources.descriptorSetLayout) // provided? we will validate your layout
669+
auto* pipelineLayout = m_creationParams.resources.pipelineLayout;
670+
671+
if (pipelineLayout) // provided? we will validate your pipeline layout to check if you declared required UI resources
680672
{
681-
auto validateResource = [&]<IDescriptor::E_TYPE descriptorType>()
673+
auto validateResource = [&]<IDescriptor::E_TYPE descriptorType>(const IGPUDescriptorSetLayout* const descriptorSetLayout)
682674
{
683675
constexpr std::string_view typeLiteral = descriptorType == IDescriptor::E_TYPE::ET_SAMPLED_IMAGE ? "ET_SAMPLED_IMAGE" : "ET_SAMPLER",
684676
ixLiteral = descriptorType == IDescriptor::E_TYPE::ET_SAMPLED_IMAGE ? "texturesBindingIx" : "samplersBindingIx";
685677

686-
const auto& redirect = descriptorType == IDescriptor::E_TYPE::ET_SAMPLED_IMAGE ? m_creationParams.resources.descriptorSetLayout->getDescriptorRedirect(descriptorType) : m_creationParams.resources.descriptorSetLayout->getImmutableSamplerRedirect();
678+
if(!descriptorSetLayout)
679+
{
680+
m_creationParams.utilities->getLogger()->log("Provided descriptor set layout for IDescriptor::E_TYPE::%s is nullptr!", system::ILogger::ELL_ERROR, typeLiteral.data());
681+
return false;
682+
}
683+
684+
const auto& redirect = descriptorType == IDescriptor::E_TYPE::ET_SAMPLED_IMAGE ? descriptorSetLayout->getDescriptorRedirect(descriptorType)
685+
: descriptorSetLayout->getImmutableSamplerRedirect(); // TODO: now I cannot assume it, need to check ordinary samplers as well
687686

688687
const auto bindingCount = redirect.getBindingCount();
689688

@@ -698,7 +697,7 @@ namespace nbl::ext::imgui
698697
{
699698
const auto rangeStorageIndex = IDescriptorSetLayoutBase::CBindingRedirect::storage_range_index_t(i);
700699
const auto binding = redirect.getBinding(rangeStorageIndex);
701-
const auto requestedBindingIx = descriptorType == IDescriptor::E_TYPE::ET_SAMPLED_IMAGE ? m_creationParams.resources.texturesBindingIx : m_creationParams.resources.samplersBindingIx;
700+
const auto requestedBindingIx = descriptorType == IDescriptor::E_TYPE::ET_SAMPLED_IMAGE ? m_creationParams.resources.textures.bindingIx : m_creationParams.resources.samplers.bindingIx;
702701

703702
if (binding.data == requestedBindingIx)
704703
{
@@ -739,8 +738,9 @@ namespace nbl::ext::imgui
739738

740739
return true;
741740
};
742-
743-
const bool ok = validateResource.template operator() < IDescriptor::E_TYPE::ET_SAMPLED_IMAGE > () && validateResource.template operator() < IDescriptor::E_TYPE::ET_SAMPLER > ();
741+
742+
const auto& layouts = pipelineLayout->getDescriptorSetLayouts();
743+
const bool ok = validateResource.template operator() < IDescriptor::E_TYPE::ET_SAMPLED_IMAGE > (layouts[m_creationParams.resources.textures.setIx]) && validateResource.template operator() < IDescriptor::E_TYPE::ET_SAMPLER > (layouts[m_creationParams.resources.samplers.setIx]);
744744

745745
if (!ok)
746746
return false;
@@ -757,9 +757,11 @@ namespace nbl::ext::imgui
757757
std::make_pair(bool(m_creationParams.transfer), "Invalid `m_creationParams.transfer` is nullptr!"),
758758
std::make_pair(bool(m_creationParams.renderpass), "Invalid `m_creationParams.renderpass` is nullptr!"),
759759
(m_creationParams.assetManager && m_creationParams.utilities && m_creationParams.transfer && m_creationParams.renderpass) ? std::make_pair(bool(m_creationParams.utilities->getLogicalDevice()->getPhysicalDevice()->getQueueFamilyProperties()[m_creationParams.transfer->getFamilyIndex()].queueFlags.hasFlags(IQueue::FAMILY_FLAGS::TRANSFER_BIT)), "Invalid `m_creationParams.transfer` is not capable of transfer operations!") : std::make_pair(false, "Pass valid required UI::S_CREATION_PARAMETERS!"),
760-
std::make_pair(bool(m_creationParams.resources.setIx <= 3u), "Invalid `m_creationParams.resources.setIx` is outside { 0u, 1u, 2u, 3u } set!"),
761-
std::make_pair(bool(m_creationParams.resources.texturesBindingIx != m_creationParams.resources.samplersBindingIx), "Invalid `m_creationParams.resources.texturesBindingIx` is equal to `m_creationParams.resources.samplersBindingIx`!"),
762-
std::make_pair(bool(validateResourcesInfo()), "Invalid `m_creationParams.resources`!")
760+
std::make_pair(bool(m_creationParams.resources.count >= 1u), "Invalid `m_creationParams.resources.count` is equal to 0!"),
761+
std::make_pair(bool(m_creationParams.resources.textures.setIx <= 3u), "Invalid `m_creationParams.resources.textures` is outside { 0u, 1u, 2u, 3u } set!"),
762+
std::make_pair(bool(m_creationParams.resources.samplers.setIx <= 3u), "Invalid `m_creationParams.resources.samplers` is outside { 0u, 1u, 2u, 3u } set!"),
763+
std::make_pair(bool(m_creationParams.resources.textures.bindingIx != m_creationParams.resources.samplers.bindingIx), "Invalid `m_creationParams.resources.textures.bindingIx` is equal to `m_creationParams.resources.samplers.bindingIx`!"),
764+
std::make_pair(bool(validateResourcesInfo()), "Invalid `m_creationParams.resources` content!")
763765
});
764766

765767
for (const auto& [ok, error] : validation)
@@ -864,7 +866,7 @@ namespace nbl::ext::imgui
864866
}
865867
}
866868

867-
bool UI::render(nbl::video::IGPUCommandBuffer* commandBuffer, nbl::video::ISemaphore::SWaitInfo waitInfo, const IGPUDescriptorSet* const descriptorSet, const std::span<const VkRect2D> scissors)
869+
bool UI::render(nbl::video::IGPUCommandBuffer* commandBuffer, nbl::video::ISemaphore::SWaitInfo waitInfo, const std::span<const VkRect2D> scissors)
868870
{
869871
if (!commandBuffer)
870872
{
@@ -1095,10 +1097,6 @@ namespace nbl::ext::imgui
10951097
}
10961098
}
10971099

1098-
auto* rawPipeline = pipeline.get();
1099-
commandBuffer->bindGraphicsPipeline(rawPipeline);
1100-
commandBuffer->bindDescriptorSets(EPBP_GRAPHICS, rawPipeline->getLayout(), 0, 1, &descriptorSet);
1101-
11021100
const auto offset = mdiBuffer->getBoundMemory().offset;
11031101
{
11041102
const asset::SBufferBinding<const video::IGPUBuffer> binding =

src/nbl/ext/ImGui/shaders/common.hlsl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// temporary, maybe we should ask user for it and the layout BUT only for sampler + texture binding IDs + set IDs they belong to and size of the texture array?
2-
#define NBL_MAX_IMGUI_TEXTURES 69
3-
41
#ifdef __HLSL_VERSION
52
struct VSInput
63
{

src/nbl/ext/ImGui/shaders/fragment.hlsl

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
#ifndef NBL_TEXTURES_BINDING
2-
#error "NBL_TEXTURES_BINDING must be defined!"
1+
#ifndef NBL_TEXTURES_BINDING_IX
2+
#error "NBL_TEXTURES_BINDING_IX must be defined!"
33
#endif
44

5-
#ifndef NBL_SAMPLER_STATES_BINDING
5+
#ifndef NBL_SAMPLER_STATES_BINDING_IX
66
#error "NBL_SAMPLER_STATES_BINDING must be defined!"
77
#endif
88

9-
#ifndef NBL_RESOURCES_SET
10-
#error "NBL_RESOURCES_SET must be defined!"
9+
#ifndef NBL_TEXTURES_SET_IX
10+
#error "NBL_TEXTURES_SET_IX must be defined!"
11+
#endif
12+
13+
#ifndef NBL_SAMPLER_STATES_SET_IX
14+
#error "NBL_SAMPLER_STATES_SET_IX must be defined!"
1115
#endif
1216

1317
#ifndef NBL_RESOURCES_COUNT
@@ -19,8 +23,8 @@
1923
[[vk::push_constant]] struct PushConstants pc;
2024

2125
// separable image samplers to handle textures we do descriptor-index
22-
[[vk::binding(NBL_TEXTURES_BINDING, NBL_RESOURCES_SET)]] Texture2D textures[NBL_RESOURCES_COUNT];
23-
[[vk::binding(NBL_SAMPLER_STATES_BINDING, NBL_RESOURCES_SET)]] SamplerState samplerStates[NBL_RESOURCES_COUNT];
26+
[[vk::binding(NBL_TEXTURES_BINDING_IX, NBL_TEXTURES_SET_IX)]] Texture2D textures[NBL_RESOURCES_COUNT];
27+
[[vk::binding(NBL_SAMPLER_STATES_BINDING_IX, NBL_SAMPLER_STATES_SET_IX)]] SamplerState samplerStates[NBL_RESOURCES_COUNT];
2428

2529
/*
2630
we use Indirect Indexed draw call to render whole GUI, note we do a cross

0 commit comments

Comments
 (0)