perf(agc): resolve the recorder's query and fold entry points once

Every occlusion counter dump the recorder handles on the GPU ends the
batch's occlusion query, copies the pending results, folds them with a
compute dispatch, then resets and begins a new query. Each of those
commands was resolved through vkGetDeviceProcAddr, a loader name lookup,
on every call: seven lookups per dump on the graphics worker, and a title
dumps the counter twice per occlusion query. The mesh argument and timing
paths resolved their bind, push, dispatch and reset per call too.

They join the entry points the recorder already resolves in its
constructor: vkCmdBeginQuery, vkCmdEndQuery, vkCmdResetQueryPool,
vkCmdCopyQueryPoolResults, vkCmdBindPipeline, vkCmdPushConstants and
vkCmdDispatch. The same functions are called with the same arguments;
APS5_NO_PROC_TABLE=1 still resolves every call.
This commit is contained in:
Dean Galvin 2026-10-05 21:27:15 +13:00
parent b0c2e05ba0
commit 2b388e0b0e
2 changed files with 25 additions and 11 deletions

View File

@ -641,6 +641,13 @@ private:
PFN_vkEndCommandBuffer endCommandBuffer = nullptr;
PFN_vkQueueSubmit queueSubmit = nullptr;
PFN_vkCmdPipelineBarrier cmdPipelineBarrier = nullptr;
PFN_vkCmdBeginQuery cmdBeginQuery = nullptr;
PFN_vkCmdEndQuery cmdEndQuery = nullptr;
PFN_vkCmdResetQueryPool cmdResetQueryPool = nullptr;
PFN_vkCmdCopyQueryPoolResults cmdCopyQueryPoolResults = nullptr;
PFN_vkCmdBindPipeline cmdBindPipeline = nullptr;
PFN_vkCmdPushConstants cmdPushConstants = nullptr;
PFN_vkCmdDispatch cmdDispatch = nullptr;
template<typename TFunction>
TFunction function(TFunction resolved, const char* name) const {
return resolved != nullptr ? resolved : context.Function<TFunction>(name);

View File

@ -1074,6 +1074,13 @@ Recorder::Recorder(const Context& context, bool timelineSemaphores) : context(co
endCommandBuffer = context.Function<PFN_vkEndCommandBuffer>("vkEndCommandBuffer");
queueSubmit = context.Function<PFN_vkQueueSubmit>("vkQueueSubmit");
cmdPipelineBarrier = context.Function<PFN_vkCmdPipelineBarrier>("vkCmdPipelineBarrier");
cmdBeginQuery = context.Function<PFN_vkCmdBeginQuery>("vkCmdBeginQuery");
cmdEndQuery = context.Function<PFN_vkCmdEndQuery>("vkCmdEndQuery");
cmdResetQueryPool = context.Function<PFN_vkCmdResetQueryPool>("vkCmdResetQueryPool");
cmdCopyQueryPoolResults = context.Function<PFN_vkCmdCopyQueryPoolResults>("vkCmdCopyQueryPoolResults");
cmdBindPipeline = context.Function<PFN_vkCmdBindPipeline>("vkCmdBindPipeline");
cmdPushConstants = context.Function<PFN_vkCmdPushConstants>("vkCmdPushConstants");
cmdDispatch = context.Function<PFN_vkCmdDispatch>("vkCmdDispatch");
}
if (!timelineSemaphores) return;
// The timeline starts at 0 and every Submit signals its serial (1, 2, ...): a value that only
@ -1837,7 +1844,7 @@ std::uint32_t Recorder::beginTiming(std::uint64_t key) {
return NoTiming;
}
}
context.Function<PFN_vkCmdResetQueryPool>("vkCmdResetQueryPool")(commands, open->queries, 0, queryCount);
function(cmdResetQueryPool, "vkCmdResetQueryPool")(commands, open->queries, 0, queryCount);
}
if (open->timedKeys.size() >= queryCount / 2) {
timingDropped.fetch_add(1, std::memory_order_relaxed);
@ -1926,7 +1933,7 @@ bool Recorder::gpuSampleCounter() {
void Recorder::endSamples(Batch& batch) {
if (!batch.sampleActive) return;
context.Function<PFN_vkCmdEndQuery>("vkCmdEndQuery")(batch.commands, batch.samples, 0);
function(cmdEndQuery, "vkCmdEndQuery")(batch.commands, batch.samples, 0);
batch.sampleActive = false;
if (sampleCounterState > 0 && batch.samplesDrawn) pendingSamples.push_back({batch.samplePool, batch.samples});
batch.samplesDrawn = false;
@ -1934,10 +1941,10 @@ void Recorder::endSamples(Batch& batch) {
void Recorder::foldSamples(Batch& batch, VkDeviceAddress target) {
const auto commands = batch.commands;
const auto copy = context.Function<PFN_vkCmdCopyQueryPoolResults>("vkCmdCopyQueryPoolResults");
const auto bind = context.Function<PFN_vkCmdBindPipeline>("vkCmdBindPipeline");
const auto push = context.Function<PFN_vkCmdPushConstants>("vkCmdPushConstants");
const auto dispatch = context.Function<PFN_vkCmdDispatch>("vkCmdDispatch");
const auto copy = function(cmdCopyQueryPoolResults, "vkCmdCopyQueryPoolResults");
const auto bind = function(cmdBindPipeline, "vkCmdBindPipeline");
const auto push = function(cmdPushConstants, "vkCmdPushConstants");
const auto dispatch = function(cmdDispatch, "vkCmdDispatch");
std::size_t done = 0;
do {
const auto count = static_cast<std::uint32_t>(std::min<std::size_t>(SampleFoldCapacity, pendingSamples.size() - done));
@ -2011,15 +2018,15 @@ bool Recorder::RecordMeshArguments(VkCommandBuffer commands, VkDeviceAddress rec
}
if (meshArgumentState < 0) return false;
recordBarrier(commands, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
context.Function<PFN_vkCmdBindPipeline>("vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, meshArgumentPipeline);
function(cmdBindPipeline, "vkCmdBindPipeline")(commands, VK_PIPELINE_BIND_POINT_COMPUTE, meshArgumentPipeline);
struct {
VkDeviceAddress record;
VkDeviceAddress arguments;
std::array<std::uint32_t, 8> rules;
} parameters{record, arguments, {rules[0], rules[1], rules[2], rules[3], rules[4], rules[5], rules[6], 0u}};
static_assert(sizeof(parameters) == PushBytes);
context.Function<PFN_vkCmdPushConstants>("vkCmdPushConstants")(commands, meshArgumentLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, PushBytes, &parameters);
context.Function<PFN_vkCmdDispatch>("vkCmdDispatch")(commands, 1, 1, 1);
function(cmdPushConstants, "vkCmdPushConstants")(commands, meshArgumentLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, PushBytes, &parameters);
function(cmdDispatch, "vkCmdDispatch")(commands, 1, 1, 1);
recordBarrier(commands, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_MESH_SHADER_BIT_EXT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_INDIRECT_COMMAND_READ_BIT | VK_ACCESS_SHADER_READ_BIT);
CountBarriers(CommandClass::Draw, 2);
return true;
@ -2049,8 +2056,8 @@ void Recorder::beginSamples(Batch& batch) {
throw;
}
}
context.Function<PFN_vkCmdResetQueryPool>("vkCmdResetQueryPool")(batch.commands, batch.samples, 0, 1);
context.Function<PFN_vkCmdBeginQuery>("vkCmdBeginQuery")(batch.commands, batch.samples, 0, context.occlusionQueryPrecise ? VK_QUERY_CONTROL_PRECISE_BIT : 0u);
function(cmdResetQueryPool, "vkCmdResetQueryPool")(batch.commands, batch.samples, 0, 1);
function(cmdBeginQuery, "vkCmdBeginQuery")(batch.commands, batch.samples, 0, context.occlusionQueryPrecise ? VK_QUERY_CONTROL_PRECISE_BIT : 0u);
batch.sampleActive = true;
batch.samplesDrawn = false;
}