From 644bc4ed5edd4e3ffa88750bdacb147c75df9546 Mon Sep 17 00:00:00 2001 From: Nicolas James Date: Mon, 30 Mar 2026 15:54:10 +1100 Subject: Fix AL2 via VK_NV_low_latency2's async implementation, fix race in TimestampPool --- src/timestamp_pool.cc | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) (limited to 'src/timestamp_pool.cc') diff --git a/src/timestamp_pool.cc b/src/timestamp_pool.cc index a37b2bc..4bb236b 100644 --- a/src/timestamp_pool.cc +++ b/src/timestamp_pool.cc @@ -1,7 +1,9 @@ #include "timestamp_pool.hh" #include "device_context.hh" +#include "helper.hh" #include "queue_context.hh" +#include #include #include #include @@ -13,18 +15,18 @@ TimestampPool::QueryChunk::QueryPoolOwner::QueryPoolOwner( const QueueContext& queue_context) : queue_context(queue_context) { - const auto& device_context = this->queue_context.device_context; + const auto& device_context = this->queue_context.device; const auto qpci = VkQueryPoolCreateInfo{.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, .queryType = VK_QUERY_TYPE_TIMESTAMP, .queryCount = QueryChunk::CHUNK_SIZE}; - THROW_NON_VKSUCCESS(device_context.vtable.CreateQueryPool( + THROW_NOT_VKSUCCESS(device_context.vtable.CreateQueryPool( device_context.device, &qpci, nullptr, &this->query_pool)); } TimestampPool::QueryChunk::QueryPoolOwner::~QueryPoolOwner() { - const auto& device_context = this->queue_context.device_context; + const auto& device_context = this->queue_context.device; device_context.vtable.DestroyQueryPool(device_context.device, this->query_pool, nullptr); } @@ -43,7 +45,7 @@ TimestampPool::QueryChunk::CommandBuffersOwner::CommandBuffersOwner( const QueueContext& queue_context) : queue_context(queue_context), command_buffers(CHUNK_SIZE) { - const auto& device_context = queue_context.device_context; + const auto& device_context = queue_context.device; const auto cbai = VkCommandBufferAllocateInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, @@ -51,12 +53,12 @@ TimestampPool::QueryChunk::CommandBuffersOwner::CommandBuffersOwner( .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, .commandBufferCount = CHUNK_SIZE, }; - THROW_NON_VKSUCCESS(device_context.vtable.AllocateCommandBuffers( + THROW_NOT_VKSUCCESS(device_context.vtable.AllocateCommandBuffers( device_context.device, &cbai, std::data(this->command_buffers))); } TimestampPool::QueryChunk::CommandBuffersOwner::~CommandBuffersOwner() { - const auto& device_context = this->queue_context.device_context; + const auto& device_context = this->queue_context.device; device_context.vtable.FreeCommandBuffers( device_context.device, *this->queue_context.command_pool, @@ -64,6 +66,13 @@ TimestampPool::QueryChunk::CommandBuffersOwner::~CommandBuffersOwner() { std::data(this->command_buffers)); } +VkCommandBuffer TimestampPool::QueryChunk::CommandBuffersOwner::operator[]( + const std::size_t& i) { + + assert(i < CHUNK_SIZE); + return this->command_buffers[i]; +} + TimestampPool::QueryChunk::~QueryChunk() {} TimestampPool::TimestampPool(QueueContext& queue_context) @@ -75,6 +84,7 @@ TimestampPool::TimestampPool(QueueContext& queue_context) } std::shared_ptr TimestampPool::acquire() { + const auto lock = std::scoped_lock{this->mutex}; // Gets the empty one, or inserts a new one and returns it. const auto not_empty_iter = [this]() -> auto { @@ -97,12 +107,12 @@ std::shared_ptr TimestampPool::acquire() { // Grab any element from our set and erase it immediately after. auto& indices = *(*not_empty_iter)->free_indices; const auto query_index = *std::begin(indices); - assert(indices.erase(query_index)); + indices.erase(query_index); return std::make_shared(*this, *not_empty_iter, query_index); } -TimestampPool::Handle::Handle(const TimestampPool& timestamp_pool, +TimestampPool::Handle::Handle(TimestampPool& timestamp_pool, const std::shared_ptr& origin_chunk, const std::uint64_t& query_index) : timestamp_pool(timestamp_pool), origin_chunk(origin_chunk), @@ -110,10 +120,12 @@ TimestampPool::Handle::Handle(const TimestampPool& timestamp_pool, command_buffer((*origin_chunk->command_buffers)[query_index]) {} TimestampPool::Handle::~Handle() { + const auto lock = std::scoped_lock{this->timestamp_pool.mutex}; + // Parent destructing shouldn't mean we should have a bunch of // insertions for zero reason. if (const auto ptr = this->origin_chunk.lock(); ptr) { - assert(ptr->free_indices->insert(this->query_index).second); + ptr->free_indices->insert(this->query_index); } } @@ -124,32 +136,32 @@ void TimestampPool::Handle::setup_command_buffers( .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; - const auto& device_context = queue_context.device_context; + const auto& device_context = queue_context.device; const auto& vtable = device_context.vtable; vtable.ResetQueryPoolEXT(device_context.device, this->query_pool, static_cast(this->query_index), 1); - THROW_NON_VKSUCCESS(vtable.ResetCommandBuffer(this->command_buffer, 0)); - THROW_NON_VKSUCCESS(vtable.BeginCommandBuffer(this->command_buffer, &cbbi)); + THROW_NOT_VKSUCCESS(vtable.ResetCommandBuffer(this->command_buffer, 0)); + THROW_NOT_VKSUCCESS(vtable.BeginCommandBuffer(this->command_buffer, &cbbi)); vtable.CmdWriteTimestamp2KHR( this->command_buffer, VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT, this->query_pool, static_cast(this->query_index)); - THROW_NON_VKSUCCESS(vtable.EndCommandBuffer(this->command_buffer)); + THROW_NOT_VKSUCCESS(vtable.EndCommandBuffer(this->command_buffer)); vtable.ResetQueryPoolEXT(device_context.device, tail.query_pool, static_cast(tail.query_index), 1); - THROW_NON_VKSUCCESS(vtable.ResetCommandBuffer(tail.command_buffer, 0)); - THROW_NON_VKSUCCESS(vtable.BeginCommandBuffer(tail.command_buffer, &cbbi)); + THROW_NOT_VKSUCCESS(vtable.ResetCommandBuffer(tail.command_buffer, 0)); + THROW_NOT_VKSUCCESS(vtable.BeginCommandBuffer(tail.command_buffer, &cbbi)); vtable.CmdWriteTimestamp2KHR( tail.command_buffer, VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT, tail.query_pool, static_cast(tail.query_index)); - THROW_NON_VKSUCCESS(vtable.EndCommandBuffer(tail.command_buffer)); + THROW_NOT_VKSUCCESS(vtable.EndCommandBuffer(tail.command_buffer)); } struct QueryResult { @@ -157,7 +169,7 @@ struct QueryResult { std::uint64_t available; }; std::optional TimestampPool::Handle::get_time() { - const auto& context = this->timestamp_pool.queue_context.device_context; + const auto& context = this->timestamp_pool.queue_context.device; const auto& vtable = context.vtable; auto query_result = QueryResult{}; @@ -180,7 +192,7 @@ std::optional TimestampPool::Handle::get_time() { } DeviceClock::time_point_t TimestampPool::Handle::await_time() { - const auto& context = this->timestamp_pool.queue_context.device_context; + const auto& context = this->timestamp_pool.queue_context.device; const auto& vtable = context.vtable; struct QueryResult { @@ -189,7 +201,7 @@ DeviceClock::time_point_t TimestampPool::Handle::await_time() { }; auto query_result = QueryResult{}; - THROW_NON_VKSUCCESS(vtable.GetQueryPoolResults( + THROW_NOT_VKSUCCESS(vtable.GetQueryPoolResults( context.device, query_pool, static_cast(this->query_index), 1, sizeof(query_result), &query_result, sizeof(query_result), -- cgit v1.2.3