tornavis/source/blender/gpu/vulkan/vk_command_buffer.hh

237 lines
8.0 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 VKBuffer;
struct VKBufferWithOffset;
class VKDescriptorSet;
class VKFrameBuffer;
class VKIndexBuffer;
class VKPipeline;
class VKPushConstants;
class VKStorageBuffer;
class VKTexture;
class VKVertexBuffer;
/** Command buffer to keep track of the life-time of a command buffer. */
class VKCommandBuffer : NonCopyable, NonMovable {
/** Not owning handle to the command buffer and device. Handle is owned by `GHOST_ContextVK`. */
VkDevice vk_device_ = VK_NULL_HANDLE;
VkCommandBuffer vk_command_buffer_ = VK_NULL_HANDLE;
VkQueue vk_queue_ = VK_NULL_HANDLE;
/**
* Timeout to use when waiting for fences in nanoseconds.
*
* Currently added as the fence will halt when there are no commands in the command buffer for
* the second time. This should be solved and this timeout should be removed.
*/
static constexpr uint64_t FenceTimeout = UINT64_MAX;
/** Owning handles */
VkFence vk_fence_ = VK_NULL_HANDLE;
VKSubmissionID submission_id_;
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 {
/* Reference to the last_framebuffer where begin_render_pass was called for. */
const VKFrameBuffer *framebuffer_ = nullptr;
/* Is last_framebuffer_ currently bound. Each call should ensure the correct state. */
bool framebuffer_active_ = false;
/* Amount of times a check has been requested. */
uint64_t checks_ = 0;
/* Amount of times a check required to change the render pass. */
uint64_t switches_ = 0;
/* Number of times a vkDraw command has been recorded. */
uint64_t draw_counts = 0;
/**
* Current stage of the command buffer to keep track of inconsistencies & incorrect usage.
*/
Stage stage = Stage::Initial;
} 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();
void init(const VkDevice vk_device, const VkQueue vk_queue, VkCommandBuffer vk_command_buffer);
void begin_recording();
void end_recording();
void bind(const VKPipeline &vk_pipeline, VkPipelineBindPoint bind_point);
void bind(const VKDescriptorSet &descriptor_set,
const VkPipelineLayout vk_pipeline_layout,
VkPipelineBindPoint bind_point);
void bind(const uint32_t binding,
const VKVertexBuffer &vertex_buffer,
const VkDeviceSize offset);
/* Bind the given buffer as a vertex buffer. */
void bind(const uint32_t binding, const VKBufferWithOffset &vertex_buffer);
void bind(const uint32_t binding, const VkBuffer &vk_vertex_buffer, const VkDeviceSize offset);
/* Bind the given buffer as an index buffer. */
void bind(const VKBufferWithOffset &index_buffer, VkIndexType index_type);
void begin_render_pass(const VKFrameBuffer &framebuffer);
void end_render_pass(const VKFrameBuffer &framebuffer);
/**
* Add a push constant command to the command buffer.
*
* Only valid when the storage type of push_constants is StorageType::PUSH_CONSTANTS.
*/
void push_constants(const VKPushConstants &push_constants,
const VkPipelineLayout vk_pipeline_layout,
const VkShaderStageFlags vk_shader_stages);
void dispatch(int groups_x_len, int groups_y_len, int groups_z_len);
void dispatch(VKStorageBuffer &command_buffer);
/** Copy the contents of a texture MIP level to the dst buffer. */
void copy(VKBuffer &dst_buffer, VKTexture &src_texture, Span<VkBufferImageCopy> regions);
void copy(VKTexture &dst_texture, VKBuffer &src_buffer, Span<VkBufferImageCopy> regions);
void copy(VKTexture &dst_texture, VKTexture &src_texture, Span<VkImageCopy> regions);
void blit(VKTexture &dst_texture, VKTexture &src_texture, Span<VkImageBlit> regions);
void blit(VKTexture &dst_texture,
VkImageLayout dst_layout,
VKTexture &src_texture,
VkImageLayout src_layout,
Span<VkImageBlit> regions);
void pipeline_barrier(VkPipelineStageFlags source_stages,
VkPipelineStageFlags destination_stages);
void pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers);
/**
* Clear color image resource.
*/
void clear(VkImage vk_image,
VkImageLayout vk_image_layout,
const VkClearColorValue &vk_clear_color,
Span<VkImageSubresourceRange> ranges);
/**
* Clear attachments of the active framebuffer.
*/
void clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas);
void fill(VKBuffer &buffer, uint32_t data);
void draw(int v_first, int v_count, int i_first, int i_count);
void draw(
int index_count, int instance_count, int first_index, int vertex_offset, int first_instance);
/**
* Stop recording commands, encode + send the recordings to Vulkan, wait for the until the
* commands have been executed and start the command buffer to accept recordings again.
*/
void submit();
const VKSubmissionID &submission_id_get() const
{
return submission_id_;
}
private:
void encode_recorded_commands();
void submit_encoded_commands();
/**
* Validate that there isn't a framebuffer being tracked (bound or not bound).
*
* Raises an assert in debug when a framebuffer is being tracked.
*/
void validate_framebuffer_not_exists();
/**
* Validate that there is a framebuffer being tracked (bound or not bound).
*
* Raises an assert in debug when no framebuffer is being tracked.
*/
void validate_framebuffer_exists();
/**
* Ensure that there is no framebuffer being tracked or the tracked framebuffer isn't bound.
*/
void ensure_no_active_framebuffer();
/**
* Ensure that the tracked framebuffer is bound.
*/
void ensure_active_framebuffer();
};
} // namespace blender::gpu