Skip to content

Commit 60ee95d

Browse files
committed
addressing more comments.
1 parent 625eb71 commit 60ee95d

File tree

2 files changed

+69
-1
lines changed

2 files changed

+69
-1
lines changed

en/Building_a_Simple_Engine/GUI/02_imgui_setup.adoc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,11 @@ Next, we set up the Vulkan pipeline objects that define how UI geometry is proce
111111

112112
The pipeline infrastructure creates a specialized graphics pipeline optimized for UI rendering, which differs significantly from typical 3D rendering pipelines. UI rendering typically requires alpha blending for transparency effects, operates in 2D screen space rather than 3D world space, and uses simpler shading models focused on texture sampling rather than complex lighting calculations.
113113

114+
[NOTE]
115+
====
116+
Frames-in-flight safety: If your renderer uses more than one frame in flight and you do not stall the GPU between frames, you must duplicate the dynamic ImGui buffers (vertex/index) per frame-in-flight. Using a single shared vertex/index buffer risks the CPU overwriting data still in use by the GPU from a previous frame. The simple single-buffer members shown above are for conceptual clarity; in production, store vectors of buffers/memories sized to the max frames in flight and update/bind the buffers for the current frame index.
117+
====
118+
114119
The descriptor system manages the connection between our CPU-side resources and the GPU shaders. For UI rendering, this primarily involves binding the font atlas texture to the fragment shader, though more complex UI systems might include additional textures for icons, backgrounds, or other visual elements.
115120

116121
=== ImGuiVulkanUtil Architecture: Device Context and System Integration
@@ -773,7 +778,7 @@ void drawFrame() {
773778
// Render scene using dynamic rendering
774779
// ...
775780

776-
// Render ImGui
781+
// Render ImGui (in multi-frame renderers, pass the current frame index to bind per-frame buffers)
777782
imGui.drawFrame(commandBuffer);
778783

779784
// ... submit command buffer ...

en/Building_a_Simple_Engine/GUI/04_ui_elements.adoc

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,69 @@ When integrating ImGui with Vulkan, consider these performance aspects:
111111
4. *Pipeline State*: Use a dedicated pipeline for ImGui to minimize state changes
112112
5. *Render Pass Integration*: Consider whether to use a separate render pass or subpass for the GUI
113113

114+
==== Frames-in-Flight: Duplicate Dynamic Buffers Per Frame
115+
116+
If your renderer uses multiple frames in flight (e.g., double/triple buffering) without a device wait-idle between frames, ImGui's dynamic vertex and index buffers must not be shared across frames. Otherwise, the CPU can overwrite data that the GPU from a previous frame is still reading.
117+
118+
- Allocate one vertex buffer and one index buffer per frame-in-flight.
119+
- Update/bind the buffers for the current frame index only.
120+
- Size each buffer to the frame's ImDrawData TotalVtxCount/TotalIdxCount, growing as needed.
121+
122+
Example sketch:
123+
124+
[source,cpp]
125+
----
126+
class ImGuiSystem {
127+
// ...
128+
std::vector<vk::raii::Buffer> vertexBuffers;
129+
std::vector<vk::raii::DeviceMemory> vertexMemories;
130+
std::vector<vk::raii::Buffer> indexBuffers;
131+
std::vector<vk::raii::DeviceMemory> indexMemories;
132+
std::vector<uint32_t> vertexCounts;
133+
std::vector<uint32_t> indexCounts;
134+
135+
bool Initialize(Renderer* renderer, uint32_t w, uint32_t h) {
136+
// ... create pipelines, font, descriptors ...
137+
const uint32_t frames = renderer->GetMaxFramesInFlight();
138+
vertexBuffers.resize(frames);
139+
vertexMemories.resize(frames);
140+
indexBuffers.resize(frames);
141+
indexMemories.resize(frames);
142+
vertexCounts.assign(frames, 0);
143+
indexCounts.assign(frames, 0);
144+
return true;
145+
}
146+
147+
void Render(vk::raii::CommandBuffer& cmd, uint32_t frameIndex) {
148+
ImGui::Render();
149+
updateBuffers(frameIndex);
150+
// bind per-frame buffers
151+
std::array vb = {*vertexBuffers[frameIndex]};
152+
std::array<vk::DeviceSize,1> offs{};
153+
cmd.bindVertexBuffers(0, vb, offs);
154+
cmd.bindIndexBuffer(*indexBuffers[frameIndex], 0, vk::IndexType::eUint16);
155+
// draw lists...
156+
}
157+
158+
void updateBuffers(uint32_t frameIndex) {
159+
ImDrawData* dd = ImGui::GetDrawData();
160+
if (!dd || dd->CmdListsCount == 0) return;
161+
vk::DeviceSize vbytes = dd->TotalVtxCount * sizeof(ImDrawVert);
162+
vk::DeviceSize ibytes = dd->TotalIdxCount * sizeof(ImDrawIdx);
163+
// grow-per-frame if needed, then map/copy for this frame only
164+
// ...
165+
}
166+
};
167+
----
168+
169+
When integrating with your main renderer, pass the current frame index to the ImGui render call:
170+
171+
[source,cpp]
172+
----
173+
// inside your frame loop after scene rendering
174+
imguiSystem->Render(commandBuffers[currentFrame], currentFrame);
175+
----
176+
114177
=== Organizing Your GUI Code
115178

116179
For maintainable GUI code, consider these organizational patterns:

0 commit comments

Comments
 (0)