Files
Vulkan-Samples/samples/performance/command_buffer_usage/command_buffer_usage.h
T
2025-09-04 10:54:47 +08:00

235 lines
7.4 KiB
C++

/* Copyright (c) 2019-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "buffer_pool.h"
#include "common/utils.h"
#include "rendering/render_pipeline.h"
#include "rendering/subpasses/forward_subpass.h"
#include "scene_graph/components/material.h"
#include "scene_graph/components/mesh.h"
#include "scene_graph/components/perspective_camera.h"
#include "vulkan_sample.h"
class ThreadPool
{
public:
explicit ThreadPool() :
stop_flag(false)
{}
~ThreadPool()
{
shutdown();
}
template <class F, class... Args>
auto push(F &&f, Args &&...args) -> std::future<std::invoke_result_t<F, Args..., size_t>>
{
using return_type = std::invoke_result_t<F, Args..., size_t>;
auto task_ptr = std::make_shared<std::packaged_task<return_type(size_t)>>(std::bind(std::forward<F>(f), std::forward<Args>(args)..., std::placeholders::_1));
std::future<return_type> res = task_ptr->get_future();
{
std::unique_lock<std::mutex> lock(queue_mutex);
tasks.emplace([task_ptr](size_t thread_index) { (*task_ptr)(thread_index); });
}
condition.notify_one();
return res;
}
void resize(size_t thread_count)
{
if (thread_count != workers.size())
{
shutdown();
for (size_t i = 0; i < thread_count; ++i)
{
workers.emplace_back([this, i] {
size_t thread_index = i;
while (true)
{
std::function<void(size_t)> task;
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock, [this] { return stop_flag || !tasks.empty(); });
if (stop_flag && tasks.empty())
return;
task = std::move(tasks.front());
tasks.pop();
}
task(thread_index);
}
});
}
}
}
void shutdown()
{
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop_flag = true;
}
condition.notify_all();
for (auto &worker : workers)
worker.join();
workers.clear();
stop_flag = false;
}
size_t size() const
{
return workers.size();
}
private:
std::vector<std::thread> workers;
std::queue<std::function<void(size_t)>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
std::atomic<bool> stop_flag;
};
/**
* @brief Sample showing the use of secondary command buffers for
* multi-threaded recording, as well as the different
* strategies for recycling command buffers every frame
*/
class CommandBufferUsage : public vkb::VulkanSampleC
{
public:
CommandBufferUsage();
virtual ~CommandBufferUsage() = default;
virtual bool prepare(const vkb::ApplicationOptions &options) override;
virtual void update(float delta_time) override;
/**
* @brief Helper structure used to set subpass state
*/
struct ForwardSubpassSecondaryState
{
uint32_t secondary_cmd_buf_count = 0;
vkb::CommandBufferResetMode command_buffer_reset_mode = vkb::CommandBufferResetMode::ResetPool;
bool multi_threading = false;
uint32_t thread_count = 0;
};
/**
* @brief Overrides the draw method to allow for dividing draw calls
* into multiple secondary command buffers, optionally
* in different threads
*/
class ForwardSubpassSecondary : public vkb::ForwardSubpass
{
public:
ForwardSubpassSecondary(vkb::RenderContext &render_context,
vkb::ShaderSource &&vertex_source, vkb::ShaderSource &&fragment_source,
vkb::sg::Scene &scene, vkb::sg::Camera &camera);
void draw(vkb::core::CommandBufferC &primary_command_buffer) override;
void set_viewport(VkViewport &viewport);
void set_scissor(VkRect2D &scissor);
float get_avg_draws_per_buffer() const;
ForwardSubpassSecondaryState &get_state();
private:
/**
* @brief Records the necessary commands to draw the specified range of scene meshes
* @param command_buffer The primary command buffer to record
* @param nodes The meshes to draw
* @param mesh_start Index to the first mesh to draw
* @param mesh_end Index to the mesh where recording will stop (not included)
* @param thread_index Identifies the resources allocated for this thread
*/
void record_draw(vkb::core::CommandBufferC &command_buffer,
const std::vector<std::pair<vkb::sg::Node *, vkb::sg::SubMesh *>> &nodes,
uint32_t mesh_start,
uint32_t mesh_end,
size_t thread_index = 0);
/**
* @brief Records the necessary commands to draw the specified range of scene meshes
* The primary command buffer provided is used to initialize, record, end and return a
* pointer to a new secondary command buffer.
* @param primary_command_buffer The primary command buffer used to inherit a secondary
* @param nodes The meshes to draw
* @param mesh_start Index to the first mesh to draw
* @param mesh_end Index to the mesh where recording will stop (not included)
* @param thread_index Identifies the resources allocated for this thread
* @return a pointer to the recorded secondary command buffer
*/
std::shared_ptr<vkb::core::CommandBufferC> record_draw_secondary(vkb::core::CommandBufferC &primary_command_buffer,
const std::vector<std::pair<vkb::sg::Node *, vkb::sg::SubMesh *>> &nodes,
uint32_t mesh_start,
uint32_t mesh_end,
size_t thread_index = 0);
VkViewport viewport{};
VkRect2D scissor{};
vkb::ColorBlendAttachmentState color_blend_attachment{};
vkb::ColorBlendState color_blend_state{};
ForwardSubpassSecondaryState state{};
float avg_draws_per_buffer{0};
ThreadPool thread_pool;
};
private:
virtual void prepare_render_context() override;
vkb::sg::PerspectiveCamera *camera{nullptr};
void render(vkb::core::CommandBufferC &command_buffer) override;
void draw_renderpass(vkb::core::CommandBufferC &primary_command_buffer, vkb::RenderTarget &render_target) override;
void draw_gui() override;
int gui_secondary_cmd_buf_count{0};
uint32_t max_secondary_command_buffer_count{100};
bool use_secondary_command_buffers{false};
int gui_command_buffer_reset_mode{0};
bool gui_multi_threading{false};
const uint32_t MIN_THREAD_COUNT{4};
uint32_t max_thread_count{0};
};
std::unique_ptr<vkb::VulkanSampleC> create_command_buffer_usage();