Skip to content

Commit cf6b36d

Browse files
committed
Add command buffer recorder
1 parent e5a471a commit cf6b36d

15 files changed

+585
-371
lines changed

layer_gpu_timeline/docs/command_buffer_model.md

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ performed.
2525

2626
* Set the current workload to a new render pass with the passed metadata.
2727

28-
**RENDERPASS_CONTINUE(const json\*):**
28+
**RENDERPASS_RESUME(const json\*):**
2929

3030
* Update the current workload, which must be a render pass, with extra
3131
draw count metadata.
@@ -72,13 +72,70 @@ real applications.
7272
The command stream for a secondary command buffer is inlined into the primary
7373
command buffer during recording.
7474

75+
### Recording sequence
76+
77+
When application records a new workload:
78+
79+
* A `tagID` is assigned and recorded using `vkCmdMarkerBegin()` label in the
80+
Vulkan command stream _before_ the new workload is written to the command
81+
stream.
82+
* If workload is using indirect parameters, then a transfer job to copy
83+
indirect parameters into a layer-owned buffer is emitted _before_ the new
84+
workload. No additional barrier is needed because application barriers must
85+
have already ensured that the indirect parameter buffer is valid.
86+
* A proxy workload object is created in the layer storing the assigned
87+
`tagID` and all settings that are known at command recording time.
88+
* A layer command stream command is recorded into the submit time stream
89+
indicating `<TYPE>_BEGIN` with a pointer to the proxy workload. Note that
90+
this JSON may be modified later for some workloads.
91+
* If workload is using indirect parameters, a layer command stream command is
92+
recorded into the resolve time stream, which will handle cleanup and
93+
emitting the `submitID.tagID` annex message for the indirect data.
94+
* If the command buffer is not ONE_TIME_SUBMIT, if any workload is using
95+
indirect parameters, or contains incomplete render passes, the command
96+
buffer is marked as needing a `submitID` wrapper.
97+
* The user command is written to the Vulkan command stream.
98+
99+
When application resumes a render pass workload:
100+
101+
* A `tagID` of zero is assigned, but not emitted to the command stream.
102+
* A layer command stream command is recorded into the submit time stream
103+
indicating `<TYPE>_RESUME` with a pointer to the proxy workload. Note that
104+
this JSON may be modified later for some workloads.
105+
* The user command is written to the Vulkan command stream.
106+
107+
When application ends a workload:
108+
109+
* For render pass workloads, any statistics accumulated since the last begin
110+
are rolled up into the proxy workload object.
111+
* For render pass workloads, the user command is written to the Vulkan
112+
command stream.
113+
* The command steam label scope is closed using `vkCmdMarkerEnd()`.
114+
75115
## Layer command playback
76116

77117
The persistent state for command playback belongs to the queues the command
78118
buffers are submitted to. The command stream bytecode is run by a bytecode
79119
interpreter associated with the state of the current queue, giving the
80120
interpreter access to the current `submitID` and queue debug label stack.
81121

122+
### Submitting sequence
123+
124+
For each command buffer in the user submit:
125+
126+
* If the command buffer needs a `submitID` we allocate a unique `submitID` and
127+
create two new command buffers that will wrap the user command buffer with an
128+
additional stack layer of debug label containing the `s<ID>` string. We will
129+
inject a layer command stream async command to handle freeing the command
130+
buffers.
131+
* The tool will process the submit-time layer commands, executing each command
132+
to either update some state or emit
133+
* If there are any async layer commands, either recorded in the command buffer
134+
or from the wrapping command buffers, we will need to add an async handler.
135+
This cannot safely use the user fence or depend on any user object lifetime,
136+
so we will add a layer-owned timeline semaphore to the submit which we can
137+
wait on to determine when it is safe trigger the async work.
138+
82139
## Future: Async commands
83140

84141
One of our longer-term goals is to be able to capture indirect parameters,

layer_gpu_timeline/source/layer_device_functions.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,16 @@
2727

2828
#include "framework/utils.hpp"
2929

30+
// Functions for device setup
31+
32+
/* See Vulkan API for documentation. */
33+
template <>
34+
VKAPI_ATTR void VKAPI_CALL layer_vkGetDeviceQueue<user_tag>(
35+
VkDevice device,
36+
uint32_t queueFamilyIndex,
37+
uint32_t queueIndex,
38+
VkQueue* pQueue);
39+
3040
// Functions for command pools
3141

3242
/* See Vulkan API for documentation. */
@@ -153,6 +163,21 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderingKHR<user_tag>(
153163
VkCommandBuffer commandBuffer,
154164
const VkRenderingInfo* pRenderingInfo);
155165

166+
/* See Vulkan API for documentation. */
167+
template <>
168+
VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndRenderPass<user_tag>(
169+
VkCommandBuffer commandBuffer);
170+
171+
/* See Vulkan API for documentation. */
172+
template <>
173+
VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndRendering<user_tag>(
174+
VkCommandBuffer commandBuffer);
175+
176+
/* See Vulkan API for documentation. */
177+
template <>
178+
VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndRenderingKHR<user_tag>(
179+
VkCommandBuffer commandBuffer);
180+
156181
// Functions for draw calls
157182

158183
/* See Vulkan API for documentation. */

layer_gpu_timeline/source/layer_device_functions_queue.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,30 @@
3232

3333
extern std::mutex g_vulkanLock;
3434

35+
/* See Vulkan API for documentation. */
36+
template <>
37+
VKAPI_ATTR void VKAPI_CALL layer_vkGetDeviceQueue<user_tag>(
38+
VkDevice device,
39+
uint32_t queueFamilyIndex,
40+
uint32_t queueIndex,
41+
VkQueue* pQueue
42+
) {
43+
LAYER_TRACE(__func__);
44+
45+
// Hold the lock to access layer-wide global store
46+
std::unique_lock<std::mutex> lock { g_vulkanLock };
47+
auto* layer = Device::retrieve(device);
48+
49+
// Release the lock to call into the driver
50+
lock.unlock();
51+
layer->driver.vkGetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
52+
53+
// Retake the lock to access layer-wide global store
54+
lock.lock();
55+
auto& tracker = layer->getStateTracker();
56+
tracker.createQueue(*pQueue);
57+
}
58+
3559
/* See Vulkan API for documentation. */
3660
template<>
3761
VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueuePresentKHR<user_tag>(

layer_gpu_timeline/source/layer_device_functions_render_pass.cpp

Lines changed: 162 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,10 @@
2929

3030
#include "device.hpp"
3131
#include "layer_device_functions.hpp"
32+
#include "framework/utils.hpp"
3233

3334
extern std::mutex g_vulkanLock;
3435

35-
static void registerRenderPass(
36-
Device* layer,
37-
VkCommandBuffer commandBuffer
38-
) {
39-
auto& state = layer->getStateTracker();
40-
auto& stats = state.getCommandBuffer(commandBuffer).getStats();
41-
stats.incRenderPassCount();
42-
}
43-
4436
/* See Vulkan API for documentation. */
4537
template <>
4638
VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateRenderPass<user_tag>(
@@ -129,10 +121,28 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderPass<user_tag>(
129121
std::unique_lock<std::mutex> lock { g_vulkanLock };
130122
auto* layer = Device::retrieve(commandBuffer);
131123

132-
registerRenderPass(layer, commandBuffer);
124+
auto& tracker = layer->getStateTracker();
125+
auto& cb = tracker.getCommandBuffer(commandBuffer);
126+
127+
// Increment the render pass counter in the tracker
128+
cb.getStats().incRenderPassCount();
129+
130+
// Update the layer command stream in the tracker
131+
uint64_t tagID = Tracker::LCSWorkload::getTagID();
132+
cb.renderPassBegin(tagID);
133+
134+
// Emit the unique workload tag into the command stream
135+
std::string tagLabel = fmt_string("t%llu", tagID);
136+
VkDebugMarkerMarkerInfoEXT tagInfo {
137+
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
138+
.pNext = nullptr,
139+
.pMarkerName = tagLabel.c_str(),
140+
.color = { 0.0f, 0.0f, 0.0f, 0.0f }
141+
};
133142

134143
// Release the lock to call into the driver
135144
lock.unlock();
145+
layer->driver.vkCmdDebugMarkerBeginEXT(commandBuffer, &tagInfo);
136146
layer->driver.vkCmdBeginRenderPass(commandBuffer, pRenderPassBegin, contents);
137147
}
138148

@@ -149,10 +159,28 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderPass2<user_tag>(
149159
std::unique_lock<std::mutex> lock { g_vulkanLock };
150160
auto* layer = Device::retrieve(commandBuffer);
151161

152-
registerRenderPass(layer, commandBuffer);
162+
auto& tracker = layer->getStateTracker();
163+
auto& cb = tracker.getCommandBuffer(commandBuffer);
164+
165+
// Increment the render pass counter in the tracker
166+
cb.getStats().incRenderPassCount();
167+
168+
// Update the layer command stream in the tracker
169+
uint64_t tagID = Tracker::LCSWorkload::getTagID();
170+
cb.renderPassBegin(tagID);
171+
172+
// Emit the unique workload tag into the command stream
173+
std::string tagLabel = fmt_string("t%llu", tagID);
174+
VkDebugMarkerMarkerInfoEXT tagInfo {
175+
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
176+
.pNext = nullptr,
177+
.pMarkerName = tagLabel.c_str(),
178+
.color = { 0.0f, 0.0f, 0.0f, 0.0f }
179+
};
153180

154181
// Release the lock to call into the driver
155182
lock.unlock();
183+
layer->driver.vkCmdDebugMarkerBeginEXT(commandBuffer, &tagInfo);
156184
layer->driver.vkCmdBeginRenderPass2(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
157185
}
158186

@@ -169,10 +197,28 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderPass2KHR<user_tag>(
169197
std::unique_lock<std::mutex> lock { g_vulkanLock };
170198
auto* layer = Device::retrieve(commandBuffer);
171199

172-
registerRenderPass(layer, commandBuffer);
200+
auto& tracker = layer->getStateTracker();
201+
auto& cb = tracker.getCommandBuffer(commandBuffer);
202+
203+
// Increment the render pass counter in the tracker
204+
cb.getStats().incRenderPassCount();
205+
206+
// Update the layer command stream in the tracker
207+
uint64_t tagID = Tracker::LCSWorkload::getTagID();
208+
cb.renderPassBegin(tagID);
209+
210+
// Emit the unique workload tag into the command stream
211+
std::string tagLabel = fmt_string("t%llu", tagID);
212+
VkDebugMarkerMarkerInfoEXT tagInfo {
213+
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
214+
.pNext = nullptr,
215+
.pMarkerName = tagLabel.c_str(),
216+
.color = { 0.0f, 0.0f, 0.0f, 0.0f }
217+
};
173218

174219
// Release the lock to call into the driver
175220
lock.unlock();
221+
layer->driver.vkCmdDebugMarkerBeginEXT(commandBuffer, &tagInfo);
176222
layer->driver.vkCmdBeginRenderPass2KHR(commandBuffer, pRenderPassBegin, pSubpassBeginInfo);
177223
}
178224

@@ -188,10 +234,28 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRendering<user_tag>(
188234
std::unique_lock<std::mutex> lock { g_vulkanLock };
189235
auto* layer = Device::retrieve(commandBuffer);
190236

191-
registerRenderPass(layer, commandBuffer);
237+
auto& tracker = layer->getStateTracker();
238+
auto& cb = tracker.getCommandBuffer(commandBuffer);
239+
240+
// Increment the render pass counter in the tracker
241+
cb.getStats().incRenderPassCount();
242+
243+
// Update the layer command stream in the tracker
244+
uint64_t tagID = Tracker::LCSWorkload::getTagID();
245+
cb.renderPassBegin(tagID);
246+
247+
// Emit the unique workload tag into the command stream
248+
std::string tagLabel = fmt_string("t%llu", tagID);
249+
VkDebugMarkerMarkerInfoEXT tagInfo {
250+
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
251+
.pNext = nullptr,
252+
.pMarkerName = tagLabel.c_str(),
253+
.color = { 0.0f, 0.0f, 0.0f, 0.0f }
254+
};
192255

193256
// Release the lock to call into the driver
194257
lock.unlock();
258+
layer->driver.vkCmdDebugMarkerBeginEXT(commandBuffer, &tagInfo);
195259
layer->driver.vkCmdBeginRendering(commandBuffer, pRenderingInfo);
196260
}
197261

@@ -207,9 +271,93 @@ VKAPI_ATTR void VKAPI_CALL layer_vkCmdBeginRenderingKHR<user_tag>(
207271
std::unique_lock<std::mutex> lock { g_vulkanLock };
208272
auto* layer = Device::retrieve(commandBuffer);
209273

210-
registerRenderPass(layer, commandBuffer);
274+
auto& tracker = layer->getStateTracker();
275+
auto& cb = tracker.getCommandBuffer(commandBuffer);
276+
277+
// Increment the render pass counter in the tracker
278+
cb.getStats().incRenderPassCount();
279+
280+
// Update the layer command stream in the tracker
281+
uint64_t tagID = Tracker::LCSWorkload::getTagID();
282+
cb.renderPassBegin(tagID);
283+
284+
// Emit the unique workload tag into the command stream
285+
std::string tagLabel = fmt_string("t%llu", tagID);
286+
VkDebugMarkerMarkerInfoEXT tagInfo {
287+
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
288+
.pNext = nullptr,
289+
.pMarkerName = tagLabel.c_str(),
290+
.color = { 0.0f, 0.0f, 0.0f, 0.0f }
291+
};
211292

212293
// Release the lock to call into the driver
213294
lock.unlock();
295+
layer->driver.vkCmdDebugMarkerBeginEXT(commandBuffer, &tagInfo);
214296
layer->driver.vkCmdBeginRenderingKHR(commandBuffer, pRenderingInfo);
215297
}
298+
299+
/* See Vulkan API for documentation. */
300+
template <>
301+
VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndRenderPass<user_tag>(
302+
VkCommandBuffer commandBuffer
303+
) {
304+
LAYER_TRACE(__func__);
305+
306+
// Hold the lock to access layer-wide global store
307+
std::unique_lock<std::mutex> lock { g_vulkanLock };
308+
auto* layer = Device::retrieve(commandBuffer);
309+
310+
// Update the layer command stream in the tracker
311+
auto& tracker = layer->getStateTracker();
312+
auto& cb = tracker.getCommandBuffer(commandBuffer);
313+
cb.workloadEnd();
314+
315+
// Release the lock to call into the driver
316+
lock.unlock();
317+
layer->driver.vkCmdEndRenderPass(commandBuffer);
318+
layer->driver.vkCmdDebugMarkerEndEXT(commandBuffer);
319+
}
320+
321+
/* See Vulkan API for documentation. */
322+
template <>
323+
VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndRendering<user_tag>(
324+
VkCommandBuffer commandBuffer
325+
) {
326+
LAYER_TRACE(__func__);
327+
328+
// Hold the lock to access layer-wide global store
329+
std::unique_lock<std::mutex> lock { g_vulkanLock };
330+
auto* layer = Device::retrieve(commandBuffer);
331+
332+
// Update the layer command stream in the tracker
333+
auto& tracker = layer->getStateTracker();
334+
auto& cb = tracker.getCommandBuffer(commandBuffer);
335+
cb.workloadEnd();
336+
337+
// Release the lock to call into the driver
338+
lock.unlock();
339+
layer->driver.vkCmdEndRendering(commandBuffer);
340+
layer->driver.vkCmdDebugMarkerEndEXT(commandBuffer);
341+
}
342+
343+
/* See Vulkan API for documentation. */
344+
template <>
345+
VKAPI_ATTR void VKAPI_CALL layer_vkCmdEndRenderingKHR<user_tag>(
346+
VkCommandBuffer commandBuffer
347+
) {
348+
LAYER_TRACE(__func__);
349+
350+
// Hold the lock to access layer-wide global store
351+
std::unique_lock<std::mutex> lock { g_vulkanLock };
352+
auto* layer = Device::retrieve(commandBuffer);
353+
354+
// Update the layer command stream in the tracker
355+
auto& tracker = layer->getStateTracker();
356+
auto& cb = tracker.getCommandBuffer(commandBuffer);
357+
cb.workloadEnd();
358+
359+
// Release the lock to call into the driver
360+
lock.unlock();
361+
layer->driver.vkCmdEndRenderingKHR(commandBuffer);
362+
layer->driver.vkCmdDebugMarkerEndEXT(commandBuffer);
363+
}

source_common/compiler_helper.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ macro(lgl_set_build_options BUILD_TARGET_NAME)
6969
$<${is_gnu_fe}:-Wno-unused-private-field>
7070

7171
# Feature disabled
72-
$<${is_gnu_fe}:-fno-exceptions>
73-
$<${is_gnu_fe}:-fno-rtti>)
72+
$<${is_gnu_fe}:-fPIC>
73+
$<${is_gnu_fe}:-fno-exceptions>)
7474

7575
endmacro()

0 commit comments

Comments
 (0)