WIP: Vulkan: Initial Immediate Mode Support. #106954
|
@ -61,7 +61,7 @@ static void test_blend_alpha()
|
||||||
{
|
{
|
||||||
blend_test<GPU_BLEND_ALPHA>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
blend_test<GPU_BLEND_ALPHA>(float4(1.0f, 0.0f, 1.0f, 1.0f),
|
||||||
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
float4(0.0f, 1.0f, 0.0f, 0.5f),
|
||||||
float4(0.5f, 0.5f, 0.5f, 0.5f));
|
float4(0.5f, 0.5f, 0.5f, 1.0f));
|
||||||
}
|
}
|
||||||
GPU_TEST(blend_alpha)
|
GPU_TEST(blend_alpha)
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ namespace blender::gpu {
|
||||||
|
|
||||||
VKCommandBuffer::~VKCommandBuffer()
|
VKCommandBuffer::~VKCommandBuffer()
|
||||||
{
|
{
|
||||||
|
validate_framebuffer_not_exists();
|
||||||
if (vk_device_ != VK_NULL_HANDLE) {
|
if (vk_device_ != VK_NULL_HANDLE) {
|
||||||
VK_ALLOCATION_CALLBACKS;
|
VK_ALLOCATION_CALLBACKS;
|
||||||
vkDestroyFence(vk_device_, vk_fence_, vk_allocation_callbacks);
|
vkDestroyFence(vk_device_, vk_fence_, vk_allocation_callbacks);
|
||||||
|
@ -44,6 +45,7 @@ void VKCommandBuffer::init(const VkDevice vk_device,
|
||||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||||
vkCreateFence(vk_device_, &fenceInfo, vk_allocation_callbacks, &vk_fence_);
|
vkCreateFence(vk_device_, &fenceInfo, vk_allocation_callbacks, &vk_fence_);
|
||||||
}
|
}
|
||||||
|
validate_framebuffer_not_exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::begin_recording()
|
void VKCommandBuffer::begin_recording()
|
||||||
|
@ -80,29 +82,33 @@ void VKCommandBuffer::bind(const uint32_t binding,
|
||||||
const VKVertexBuffer &vertex_buffer,
|
const VKVertexBuffer &vertex_buffer,
|
||||||
const VkDeviceSize offset)
|
const VkDeviceSize offset)
|
||||||
{
|
{
|
||||||
|
validate_framebuffer_exists();
|
||||||
|
ensure_active_framebuffer();
|
||||||
VkBuffer vk_buffer = vertex_buffer.vk_handle();
|
VkBuffer vk_buffer = vertex_buffer.vk_handle();
|
||||||
vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_buffer, &offset);
|
vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_buffer, &offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::bind(const VKIndexBuffer &index_buffer, VkIndexType index_type)
|
void VKCommandBuffer::bind(const VKIndexBuffer &index_buffer, VkIndexType index_type)
|
||||||
{
|
{
|
||||||
|
validate_framebuffer_exists();
|
||||||
|
ensure_active_framebuffer();
|
||||||
VkBuffer vk_buffer = index_buffer.vk_handle();
|
VkBuffer vk_buffer = index_buffer.vk_handle();
|
||||||
vkCmdBindIndexBuffer(vk_command_buffer_, vk_buffer, 0, index_type);
|
vkCmdBindIndexBuffer(vk_command_buffer_, vk_buffer, 0, index_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer)
|
void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer)
|
||||||
{
|
{
|
||||||
VkRenderPassBeginInfo render_pass_begin_info = {};
|
validate_framebuffer_not_exists();
|
||||||
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
state.framebuffer_ = &framebuffer;
|
||||||
render_pass_begin_info.renderPass = framebuffer.vk_render_pass_get();
|
|
||||||
render_pass_begin_info.framebuffer = framebuffer.vk_framebuffer_get();
|
|
||||||
render_pass_begin_info.renderArea = framebuffer.vk_render_area_get();
|
|
||||||
vkCmdBeginRenderPass(vk_command_buffer_, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::end_render_pass(const VKFrameBuffer & /*framebuffer*/)
|
void VKCommandBuffer::end_render_pass(const VKFrameBuffer &framebuffer)
|
||||||
{
|
{
|
||||||
vkCmdEndRenderPass(vk_command_buffer_);
|
UNUSED_VARS_NDEBUG(framebuffer)
|
||||||
|
validate_framebuffer_exists();
|
||||||
|
BLI_assert(state.framebuffer_ == &framebuffer);
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
|
state.framebuffer_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
||||||
|
@ -121,6 +127,7 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
||||||
|
|
||||||
void VKCommandBuffer::fill(VKBuffer &buffer, uint32_t clear_data)
|
void VKCommandBuffer::fill(VKBuffer &buffer, uint32_t clear_data)
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
vkCmdFillBuffer(vk_command_buffer_, buffer.vk_handle(), 0, buffer.size_in_bytes(), clear_data);
|
vkCmdFillBuffer(vk_command_buffer_, buffer.vk_handle(), 0, buffer.size_in_bytes(), clear_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,6 +135,7 @@ void VKCommandBuffer::copy(VKBuffer &dst_buffer,
|
||||||
VKTexture &src_texture,
|
VKTexture &src_texture,
|
||||||
Span<VkBufferImageCopy> regions)
|
Span<VkBufferImageCopy> regions)
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
vkCmdCopyImageToBuffer(vk_command_buffer_,
|
vkCmdCopyImageToBuffer(vk_command_buffer_,
|
||||||
src_texture.vk_image_handle(),
|
src_texture.vk_image_handle(),
|
||||||
src_texture.current_layout_get(),
|
src_texture.current_layout_get(),
|
||||||
|
@ -139,6 +147,7 @@ void VKCommandBuffer::copy(VKTexture &dst_texture,
|
||||||
VKBuffer &src_buffer,
|
VKBuffer &src_buffer,
|
||||||
Span<VkBufferImageCopy> regions)
|
Span<VkBufferImageCopy> regions)
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
vkCmdCopyBufferToImage(vk_command_buffer_,
|
vkCmdCopyBufferToImage(vk_command_buffer_,
|
||||||
src_buffer.vk_handle(),
|
src_buffer.vk_handle(),
|
||||||
dst_texture.vk_image_handle(),
|
dst_texture.vk_image_handle(),
|
||||||
|
@ -152,6 +161,7 @@ void VKCommandBuffer::clear(VkImage vk_image,
|
||||||
const VkClearColorValue &vk_clear_color,
|
const VkClearColorValue &vk_clear_color,
|
||||||
Span<VkImageSubresourceRange> ranges)
|
Span<VkImageSubresourceRange> ranges)
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
vkCmdClearColorImage(vk_command_buffer_,
|
vkCmdClearColorImage(vk_command_buffer_,
|
||||||
vk_image,
|
vk_image,
|
||||||
vk_image_layout,
|
vk_image_layout,
|
||||||
|
@ -162,18 +172,25 @@ void VKCommandBuffer::clear(VkImage vk_image,
|
||||||
|
|
||||||
void VKCommandBuffer::clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas)
|
void VKCommandBuffer::clear(Span<VkClearAttachment> attachments, Span<VkClearRect> areas)
|
||||||
{
|
{
|
||||||
|
validate_framebuffer_exists();
|
||||||
|
ensure_active_framebuffer();
|
||||||
vkCmdClearAttachments(
|
vkCmdClearAttachments(
|
||||||
vk_command_buffer_, attachments.size(), attachments.data(), areas.size(), areas.data());
|
vk_command_buffer_, attachments.size(), attachments.data(), areas.size(), areas.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::draw(int v_first, int v_count, int i_first, int i_count)
|
void VKCommandBuffer::draw(int v_first, int v_count, int i_first, int i_count)
|
||||||
{
|
{
|
||||||
|
validate_framebuffer_exists();
|
||||||
|
ensure_active_framebuffer();
|
||||||
vkCmdDraw(vk_command_buffer_, v_count, i_count, v_first, i_first);
|
vkCmdDraw(vk_command_buffer_, v_count, i_count, v_first, i_first);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
|
void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
|
||||||
VkPipelineStageFlags destination_stages)
|
VkPipelineStageFlags destination_stages)
|
||||||
{
|
{
|
||||||
|
if (state.framebuffer_) {
|
||||||
|
ensure_active_framebuffer();
|
||||||
|
}
|
||||||
vkCmdPipelineBarrier(vk_command_buffer_,
|
vkCmdPipelineBarrier(vk_command_buffer_,
|
||||||
source_stages,
|
source_stages,
|
||||||
destination_stages,
|
destination_stages,
|
||||||
|
@ -188,6 +205,7 @@ void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages,
|
||||||
|
|
||||||
void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers)
|
void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers)
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
vkCmdPipelineBarrier(vk_command_buffer_,
|
vkCmdPipelineBarrier(vk_command_buffer_,
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
|
@ -202,11 +220,13 @@ void VKCommandBuffer::pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_b
|
||||||
|
|
||||||
void VKCommandBuffer::dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
|
void VKCommandBuffer::dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
vkCmdDispatch(vk_command_buffer_, groups_x_len, groups_y_len, groups_z_len);
|
vkCmdDispatch(vk_command_buffer_, groups_x_len, groups_y_len, groups_z_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::submit()
|
void VKCommandBuffer::submit()
|
||||||
{
|
{
|
||||||
|
ensure_no_active_framebuffer();
|
||||||
end_recording();
|
end_recording();
|
||||||
encode_recorded_commands();
|
encode_recorded_commands();
|
||||||
submit_encoded_commands();
|
submit_encoded_commands();
|
||||||
|
@ -231,4 +251,47 @@ void VKCommandBuffer::submit_encoded_commands()
|
||||||
submission_id_.next();
|
submission_id_.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------- */
|
||||||
|
/** \name Framebuffer/RenderPass state tracking
|
||||||
|
* \{ */
|
||||||
|
|
||||||
|
void VKCommandBuffer::validate_framebuffer_not_exists()
|
||||||
|
{
|
||||||
|
BLI_assert_msg(state.framebuffer_ == nullptr && state.framebuffer_active_ == false,
|
||||||
|
"State error: expected no framebuffer being tracked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKCommandBuffer::validate_framebuffer_exists()
|
||||||
|
{
|
||||||
|
BLI_assert_msg(state.framebuffer_, "State error: expected framebuffer being tracked.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKCommandBuffer::ensure_no_active_framebuffer()
|
||||||
|
{
|
||||||
|
state.checks_++;
|
||||||
|
if (state.framebuffer_ && state.framebuffer_active_) {
|
||||||
|
vkCmdEndRenderPass(vk_command_buffer_);
|
||||||
|
state.framebuffer_active_ = false;
|
||||||
|
state.switches_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKCommandBuffer::ensure_active_framebuffer()
|
||||||
|
{
|
||||||
|
BLI_assert(state.framebuffer_);
|
||||||
|
state.checks_++;
|
||||||
|
if (!state.framebuffer_active_) {
|
||||||
|
VkRenderPassBeginInfo render_pass_begin_info = {};
|
||||||
|
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||||
|
render_pass_begin_info.renderPass = state.framebuffer_->vk_render_pass_get();
|
||||||
|
render_pass_begin_info.framebuffer = state.framebuffer_->vk_framebuffer_get();
|
||||||
|
render_pass_begin_info.renderArea = state.framebuffer_->vk_render_area_get();
|
||||||
|
vkCmdBeginRenderPass(vk_command_buffer_, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
|
state.framebuffer_active_ = true;
|
||||||
|
state.switches_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \} */
|
||||||
|
|
||||||
} // namespace blender::gpu
|
} // namespace blender::gpu
|
||||||
|
|
|
@ -33,6 +33,42 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
||||||
VkFence vk_fence_ = VK_NULL_HANDLE;
|
VkFence vk_fence_ = VK_NULL_HANDLE;
|
||||||
VKSubmissionID submission_id_;
|
VKSubmissionID submission_id_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/*
|
||||||
|
* 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 post-poning
|
||||||
|
* 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 framebuffer textures isn't allowed inside a render pass, but clearing the
|
||||||
|
* framebuffer textures via ops is allowed. When clearing a framebuffer 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;
|
||||||
|
} state;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ~VKCommandBuffer();
|
virtual ~VKCommandBuffer();
|
||||||
void init(const VkDevice vk_device, const VkQueue vk_queue, VkCommandBuffer vk_command_buffer);
|
void init(const VkDevice vk_device, const VkQueue vk_queue, VkCommandBuffer vk_command_buffer);
|
||||||
|
@ -94,6 +130,30 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
||||||
private:
|
private:
|
||||||
void encode_recorded_commands();
|
void encode_recorded_commands();
|
||||||
void submit_encoded_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
|
} // namespace blender::gpu
|
||||||
|
|
|
@ -115,30 +115,12 @@ void VKContext::end_frame()
|
||||||
|
|
||||||
void VKContext::flush()
|
void VKContext::flush()
|
||||||
{
|
{
|
||||||
VKFrameBuffer *previous_framebuffer = active_framebuffer_get();
|
|
||||||
if (has_active_framebuffer()) {
|
|
||||||
deactivate_framebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
command_buffer_.submit();
|
command_buffer_.submit();
|
||||||
|
|
||||||
if (previous_framebuffer != nullptr) {
|
|
||||||
activate_framebuffer(*previous_framebuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKContext::finish()
|
void VKContext::finish()
|
||||||
{
|
{
|
||||||
VKFrameBuffer *previous_framebuffer = active_framebuffer_get();
|
|
||||||
if (has_active_framebuffer()) {
|
|
||||||
deactivate_framebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
command_buffer_.submit();
|
command_buffer_.submit();
|
||||||
|
|
||||||
if (previous_framebuffer != nullptr) {
|
|
||||||
activate_framebuffer(*previous_framebuffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {}
|
void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {}
|
||||||
|
|
Loading…
Reference in New Issue