vk: Implement state tracking for command buffers

- Only tracks the heavy items for now. Binding the same pipeline or descriptor set causes some drivers to perform expensive operations (RADV, AMDVLK)
This commit is contained in:
kd-11 2025-08-03 21:35:32 +03:00 committed by kd-11
parent 3452aaf5d5
commit b1365a051d
6 changed files with 79 additions and 29 deletions

View file

@ -74,7 +74,7 @@ namespace vk
}
++reset_id;
CHECK_RESULT(vkResetCommandBuffer(commands, 0));
vk::command_buffer::reset();
}
bool poke()

View file

@ -414,8 +414,8 @@ namespace vk
bind_sets[count++] = set.commit(); // Commit variable changes and return handle to the new set
}
vkCmdBindPipeline(cmd, bind_point, m_pipeline);
vkCmdBindDescriptorSets(cmd, bind_point, m_pipeline_layout, 0, count, bind_sets, 0, nullptr);
cmd.bind_pipeline(m_pipeline, bind_point);
cmd.bind_descriptor_sets({ bind_sets, count }, bind_point, m_pipeline_layout);
return *this;
}

View file

@ -1339,7 +1339,7 @@ namespace vk
cmd.submit(submit_info, VK_TRUE);
vk::wait_for_fence(&submit_fence, GENERAL_WAIT_TIMEOUT);
CHECK_RESULT(vkResetCommandBuffer(cmd, 0));
cmd.reset();
cmd.begin();
}
else

View file

@ -70,6 +70,16 @@ namespace vk
}
}
void command_buffer::reset()
{
// Nuke the state cache
m_bound_pipeline = VK_NULL_HANDLE;
m_bound_descriptor_sets[0] = VK_NULL_HANDLE;
// Do the driver reset
CHECK_RESULT(vkResetCommandBuffer(commands, 0));
}
void command_buffer::begin()
{
if (m_submit_fence && is_pending)
@ -79,7 +89,7 @@ namespace vk
//CHECK_RESULT(vkResetFences(pool->get_owner(), 1, &m_submit_fence));
m_submit_fence->reset();
CHECK_RESULT(vkResetCommandBuffer(commands, 0));
reset();
}
if (is_open)
@ -129,4 +139,51 @@ namespace vk
queue_submit(submit_info, flush);
clear_flags();
}
void command_buffer::bind_pipeline(VkPipeline pipeline, VkPipelineBindPoint bind_point) const
{
if (m_bound_pipeline == pipeline)
{
return;
}
m_bound_pipeline = pipeline;
vkCmdBindPipeline(commands, bind_point, pipeline);
}
void command_buffer::bind_descriptor_sets(
const std::span<VkDescriptorSet>& sets,
const std::span<u32>& dynamic_offsets,
VkPipelineBindPoint bind_point,
VkPipelineLayout pipe_layout) const
{
const u32 num_sets = ::size32(sets);
ensure(num_sets <= 2);
if (dynamic_offsets.empty() &&
!memcmp(sets.data(), m_bound_descriptor_sets.data(), sets.size_bytes()))
{
return;
}
std::memcpy(m_bound_descriptor_sets.data(), sets.data(), sets.size_bytes());
vkCmdBindDescriptorSets(commands, bind_point, pipe_layout, 0, num_sets, sets.data(), ::size32(dynamic_offsets), dynamic_offsets.data());
}
void command_buffer::bind_descriptor_sets(
const std::span<VkDescriptorSet>& sets,
VkPipelineBindPoint bind_point,
VkPipelineLayout pipe_layout) const
{
const u32 num_sets = ::size32(sets);
ensure(num_sets <= 2);
if (!memcmp(sets.data(), m_bound_descriptor_sets.data(), sets.size_bytes()))
{
return;
}
std::memcpy(m_bound_descriptor_sets.data(), sets.data(), sets.size_bytes());
vkCmdBindDescriptorSets(commands, bind_point, pipe_layout, 0, num_sets, sets.data(), 0, nullptr);
}
}

View file

@ -71,6 +71,10 @@ namespace vk
command_pool* pool = nullptr;
VkCommandBuffer commands = nullptr;
// State cache
mutable std::array<VkDescriptorSet, 2> m_bound_descriptor_sets {{ VK_NULL_HANDLE }};
mutable VkPipeline m_bound_pipeline = VK_NULL_HANDLE;
public:
enum access_type_hint
{
@ -97,40 +101,28 @@ namespace vk
void create(command_pool& cmd_pool);
void destroy();
void reset();
void begin();
void end();
void submit(queue_submit_t& submit_info, VkBool32 flush = VK_FALSE);
// Abstractions with caching
void bind_pipeline(VkPipeline pipeline, VkPipelineBindPoint bind_point) const;
void bind_descriptor_sets(const std::span<VkDescriptorSet>& sets, VkPipelineBindPoint bind_point, VkPipelineLayout pipe_layout) const;
void bind_descriptor_sets(const std::span<VkDescriptorSet>& sets, const std::span<u32>& dynamic_offsets, VkPipelineBindPoint bind_point, VkPipelineLayout pipe_layout) const;
// Properties
command_pool& get_command_pool() const
{
return *pool;
}
command_pool& get_command_pool() const { return *pool; }
u32 get_queue_family() const { return pool->get_queue_family(); }
bool is_recording() const { return is_open; }
u32 get_queue_family() const
{
return pool->get_queue_family();
}
void clear_flags()
{
flags = 0;
}
void set_flag(command_buffer_data_flag flag)
{
flags |= flag;
}
void clear_flags() { flags = 0; }
void set_flag(command_buffer_data_flag flag) { flags |= flag; }
operator VkCommandBuffer() const
{
return commands;
}
bool is_recording() const
{
return is_open;
}
};
}

View file

@ -453,7 +453,8 @@ namespace vk
// Notify
on_bind();
vkCmdBindDescriptorSets(cmd, bind_point, layout, 0, 1, &m_handle, ::size32(m_dynamic_offsets), m_dynamic_offsets.data());
VkDescriptorSet sets[1] = { m_handle };
cmd.bind_descriptor_sets(sets, m_dynamic_offsets, bind_point, layout);
}
void descriptor_set::flush()