Goal is to reduce the number of command buffer flushes by tracking what is happening in the different command queues. This is an initial step towards advanced queue-ing strategies. The new (intermediate) strategy records commands to different command buffers based on what they do. There is a command buffer for data transfers, compute pipelines and graphics pipelines. When a compute command is recorded it ensures that all graphic commands are finished. When a graphic command is recorded it ensures all compute commands are finished. When a graphic or compute command is scheduled all recorded data transfer commands are scheduled as well. Some improvements are expected as multiple compute and data transfers commands can now be scheduled at the same time and don't need to unbind and rebind render passes. Especially when using EEVEE-Next which is compute centric the performance change is visible for the user. Pull Request: https://projects.blender.org/blender/blender/pulls/114104
132 lines
3.5 KiB
C++
132 lines
3.5 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup gpu
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "vk_common.hh"
|
|
#include "vk_resource_tracker.hh"
|
|
|
|
#include "BLI_utility_mixins.hh"
|
|
|
|
namespace blender::gpu {
|
|
class VKDevice;
|
|
|
|
/** Command buffer to keep track of the life-time of a command buffer. */
|
|
class VKCommandBuffer : NonCopyable, NonMovable {
|
|
VkCommandBuffer vk_command_buffer_ = VK_NULL_HANDLE;
|
|
|
|
private:
|
|
enum class Stage {
|
|
Initial,
|
|
Recording,
|
|
BetweenRecordingAndSubmitting,
|
|
Submitted,
|
|
Executed,
|
|
};
|
|
/*
|
|
* Some vulkan command require an active frame buffer. Others require no active frame-buffer. As
|
|
* our current API does not provide a solution for this we need to keep track of the actual state
|
|
* and do the changes when recording the next command.
|
|
*
|
|
* This is a temporary solution to get things rolling.
|
|
* TODO: In a future solution we should decide the scope of a command buffer.
|
|
*
|
|
* - command buffer per draw command.
|
|
* - minimize command buffers and track render passes.
|
|
* - add custom encoder to also track resource usages.
|
|
*
|
|
* Currently I expect the custom encoder has to be done eventually. But want to keep postponing
|
|
* the custom encoder for now to collect more use cases it should solve. (first pixel drawn on
|
|
* screen).
|
|
*
|
|
* Some command can also be encoded in another way when encoded as a first command. For example
|
|
* clearing a frame-buffer textures isn't allowed inside a render pass, but clearing the
|
|
* frame-buffer textures via ops is allowed. When clearing a frame-buffer texture directly after
|
|
* beginning a render pass could be re-encoded to do this in the same command.
|
|
*
|
|
* So for now we track the state and temporary switch to another state if the command requires
|
|
* it.
|
|
*/
|
|
struct {
|
|
/**
|
|
* Current stage of the command buffer to keep track of inconsistencies & incorrect usage.
|
|
*/
|
|
Stage stage = Stage::Initial;
|
|
|
|
/**
|
|
* The number of command added to the command buffer since last submission.
|
|
*/
|
|
uint64_t recorded_command_counts = 0;
|
|
} state;
|
|
|
|
bool is_in_stage(Stage stage)
|
|
{
|
|
return state.stage == stage;
|
|
}
|
|
void stage_set(Stage stage)
|
|
{
|
|
state.stage = stage;
|
|
}
|
|
std::string to_string(Stage stage)
|
|
{
|
|
switch (stage) {
|
|
case Stage::Initial:
|
|
return "INITIAL";
|
|
case Stage::Recording:
|
|
return "RECORDING";
|
|
case Stage::BetweenRecordingAndSubmitting:
|
|
return "BEFORE_SUBMIT";
|
|
case Stage::Submitted:
|
|
return "SUBMITTED";
|
|
case Stage::Executed:
|
|
return "EXECUTED";
|
|
}
|
|
return "UNKNOWN";
|
|
}
|
|
void stage_transfer(Stage stage_from, Stage stage_to)
|
|
{
|
|
BLI_assert(is_in_stage(stage_from));
|
|
UNUSED_VARS_NDEBUG(stage_from);
|
|
#if 0
|
|
printf(" *** Transfer stage from %s to %s\n",
|
|
to_string(stage_from).c_str(),
|
|
to_string(stage_to).c_str());
|
|
#endif
|
|
stage_set(stage_to);
|
|
}
|
|
|
|
public:
|
|
virtual ~VKCommandBuffer();
|
|
bool is_initialized() const;
|
|
void init(VkCommandBuffer vk_command_buffer);
|
|
void begin_recording();
|
|
void end_recording();
|
|
|
|
/**
|
|
* Receive the vulkan handle of the command buffer.
|
|
*/
|
|
VkCommandBuffer vk_command_buffer() const
|
|
{
|
|
return vk_command_buffer_;
|
|
}
|
|
|
|
bool has_recorded_commands() const
|
|
{
|
|
return state.recorded_command_counts != 0;
|
|
}
|
|
|
|
void command_recorded()
|
|
{
|
|
state.recorded_command_counts++;
|
|
}
|
|
|
|
void commands_submitted();
|
|
};
|
|
|
|
} // namespace blender::gpu
|