From b0563f520ea05983ccf9e3ae64b3b45bd2475456 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 28 Mar 2023 11:49:52 +0200 Subject: [PATCH 01/19] Added test for offscreen rendering. It doesn't validate anything yet, it should (for now) be used with renderdoc. --- source/blender/draw/intern/draw_common.h | 2 +- source/blender/gpu/CMakeLists.txt | 1 + source/blender/gpu/tests/offscreen_test.cc | 43 ++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 source/blender/gpu/tests/offscreen_test.cc diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 07d245e7dfe..5ce442a57d6 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -48,7 +48,7 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); /* draw_hair.cc */ /** - * This creates a shading group with display hairs. + * This creates a shading group with disW_b * The draw call is already added by this function, just add additional uniforms. */ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index aefa41ccdc5..d0ca4a1e43c 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -833,6 +833,7 @@ if(WITH_GTESTS) tests/gpu_testing.cc tests/index_buffer_test.cc + tests/offscreen_test.cc tests/push_constants_test.cc tests/shader_test.cc tests/storage_buffer_test.cc diff --git a/source/blender/gpu/tests/offscreen_test.cc b/source/blender/gpu/tests/offscreen_test.cc new file mode 100644 index 00000000000..af226a35424 --- /dev/null +++ b/source/blender/gpu/tests/offscreen_test.cc @@ -0,0 +1,43 @@ +#include "testing/testing.h" + +#include "gpu_testing.hh" + +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_framebuffer.h" +#include "GPU_matrix.h" + +#include "BLI_math_vector.hh" + +#include "intern/draw_cache.h" + +namespace blender::gpu::tests { +const size_t Size = 512; + +static void test_offscreen_draw_batch() +{ + GPUOffScreen *offscreen = GPU_offscreen_create(Size, Size, false, GPU_RGBA16F, nullptr); + BLI_assert(offscreen != nullptr); + GPU_offscreen_bind(offscreen, false); + + GPUBatch *batch = DRW_cache_quad_get(); + float4 color(1.0f, 0.5f, 0.0f, 1.0f); + + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(batch, "color", color); + + GPU_matrix_push(); + GPU_matrix_scale_1f(0.5f); + GPU_matrix_rotate_2d(45.0f); + + GPU_batch_draw(batch); + GPU_offscreen_unbind(offscreen, false); + GPU_flush(); + GPU_matrix_pop(); + + GPU_offscreen_free(offscreen); + DRW_shape_cache_free(); +} +GPU_TEST(offscreen_draw_batch); + +} // namespace blender::gpu::tests \ No newline at end of file -- 2.30.2 From 2b4988b18d0867b9fa30e7e7b0459dea8eca2a6b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 28 Mar 2023 16:01:04 +0200 Subject: [PATCH 02/19] Hardcoded graphics pipeline. --- source/blender/gpu/vulkan/vk_batch.cc | 8 +- .../blender/gpu/vulkan/vk_command_buffer.cc | 5 + .../blender/gpu/vulkan/vk_command_buffer.hh | 2 + source/blender/gpu/vulkan/vk_context.cc | 49 ++++++- source/blender/gpu/vulkan/vk_context.hh | 6 +- source/blender/gpu/vulkan/vk_framebuffer.cc | 41 ++++-- source/blender/gpu/vulkan/vk_framebuffer.hh | 1 + source/blender/gpu/vulkan/vk_pipeline.cc | 127 ++++++++++++++++++ source/blender/gpu/vulkan/vk_pipeline.hh | 18 +++ source/blender/gpu/vulkan/vk_shader.cc | 67 +++------ source/blender/gpu/vulkan/vk_shader.hh | 10 +- 11 files changed, 269 insertions(+), 65 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index bdcf626864b..819e3cb3e01 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -7,10 +7,16 @@ #include "vk_batch.hh" +#include "vk_context.hh" + namespace blender::gpu { -void VKBatch::draw(int /*v_first*/, int /*v_count*/, int /*i_first*/, int /*i_count*/) +void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) { + // Based on the context construct the pipeline. + VKContext &context = *VKContext::get(); + context.bind_graphics_pipeline(); + context.command_buffer_get().draw(v_first, v_count, i_first, i_count); } void VKBatch::draw_indirect(GPUStorageBuf * /*indirect_buf*/, intptr_t /*offset*/) diff --git a/source/blender/gpu/vulkan/vk_command_buffer.cc b/source/blender/gpu/vulkan/vk_command_buffer.cc index e41dd71df0a..8487c602326 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.cc +++ b/source/blender/gpu/vulkan/vk_command_buffer.cc @@ -150,6 +150,11 @@ void VKCommandBuffer::clear(Span attachments, Span attachments, Span areas); void fill(VKBuffer &buffer, uint32_t data); + void draw(int v_first, int v_count, int i_first, int i_count); + /** * 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. diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index 3812bf0b9a0..af7d38f666d 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -10,6 +10,7 @@ #include "vk_backend.hh" #include "vk_framebuffer.hh" #include "vk_memory.hh" +#include "vk_shader.hh" #include "vk_state_manager.hh" #include "GHOST_C-api.h" @@ -108,26 +109,48 @@ void VKContext::begin_frame() void VKContext::end_frame() { + if (has_active_framebuffer()) { + deactivate_framebuffer(); + } command_buffer_.end_recording(); } void VKContext::flush() { + VKFrameBuffer *previous_framebuffer = active_framebuffer_get(); + if (has_active_framebuffer()) { + deactivate_framebuffer(); + } + command_buffer_.submit(); + + if (previous_framebuffer != nullptr) { + activate_framebuffer(*previous_framebuffer); + } } void VKContext::finish() { + VKFrameBuffer *previous_framebuffer = active_framebuffer_get(); if (has_active_framebuffer()) { deactivate_framebuffer(); } + command_buffer_.submit(); + + if (previous_framebuffer != nullptr) { + activate_framebuffer(*previous_framebuffer); + } } void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) { } +/* -------------------------------------------------------------------- */ +/** \name Framebuffer + * \{ */ + void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer) { if (has_active_framebuffer()) { @@ -139,17 +162,37 @@ void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer) command_buffer_.begin_render_pass(framebuffer); } +VKFrameBuffer *VKContext::active_framebuffer_get() const +{ + return unwrap(active_fb); +} + bool VKContext::has_active_framebuffer() const { - return active_fb != nullptr; + return active_framebuffer_get() != nullptr; } void VKContext::deactivate_framebuffer() { - BLI_assert(active_fb != nullptr); - VKFrameBuffer *framebuffer = unwrap(active_fb); + VKFrameBuffer *framebuffer = active_framebuffer_get(); + BLI_assert(framebuffer != nullptr); command_buffer_.end_render_pass(*framebuffer); active_fb = nullptr; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Graphics pipeline + * \{ */ +void VKContext::bind_graphics_pipeline() +{ + VKShader *shader = unwrap(this->shader); + BLI_assert(shader); + shader->update_graphics_pipeline(*this); + command_buffer_get().bind(shader->pipeline_get(), VK_PIPELINE_BIND_POINT_GRAPHICS); +} + +/** \} */ + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index d5c0a03dcba..544c32b8071 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -56,8 +56,12 @@ class VKContext : public Context { bool debug_capture_scope_begin(void *scope) override; void debug_capture_scope_end(void *scope) override; + bool has_active_framebuffer() const; void activate_framebuffer(VKFrameBuffer &framebuffer); void deactivate_framebuffer(); + VKFrameBuffer *active_framebuffer_get() const; + + void bind_graphics_pipeline(); static VKContext *get(void) { @@ -106,8 +110,6 @@ class VKContext : public Context { private: void init_physical_device_limits(); - - bool has_active_framebuffer() const; }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_framebuffer.cc b/source/blender/gpu/vulkan/vk_framebuffer.cc index 026373794df..5656abc9087 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.cc +++ b/source/blender/gpu/vulkan/vk_framebuffer.cc @@ -18,6 +18,7 @@ namespace blender::gpu { VKFrameBuffer::VKFrameBuffer(const char *name) : FrameBuffer(name) { immutable_ = false; + size_set(1, 1); } VKFrameBuffer::VKFrameBuffer(const char *name, @@ -29,15 +30,12 @@ VKFrameBuffer::VKFrameBuffer(const char *name, immutable_ = true; /* Never update an internal frame-buffer. */ dirty_attachments_ = false; - width_ = vk_extent.width; - height_ = vk_extent.height; vk_framebuffer_ = vk_framebuffer; vk_render_pass_ = vk_render_pass; - viewport_[0] = scissor_[0] = 0; - viewport_[1] = scissor_[1] = 0; - viewport_[2] = scissor_[2] = width_; - viewport_[3] = scissor_[3] = height_; + size_set(vk_extent.width, vk_extent.height); + viewport_reset(); + scissor_reset(); } VKFrameBuffer::~VKFrameBuffer() @@ -51,12 +49,36 @@ VKFrameBuffer::~VKFrameBuffer() void VKFrameBuffer::bind(bool /*enabled_srgb*/) { + VKContext &context = *VKContext::get(); + /* Updating attachments can issue pipeline barriers, this should be done outside the render pass. + * When done inside a render pass there should be a self-dependency between sub-passes on the + * active render pass. As the active render pass isn't aware of the new render pass (and should + * not) it is better to deactivate it before updating the attachments. For more information check + * `VkSubpassDependency`. */ + if (context.has_active_framebuffer()) { + context.deactivate_framebuffer(); + } + update_attachments(); - VKContext &context = *VKContext::get(); context.activate_framebuffer(*this); } +VkViewport VKFrameBuffer::vk_viewport_get() const +{ + VkViewport viewport; + int viewport_rect[4]; + viewport_get(viewport_rect); + + viewport.x = viewport_rect[0]; + viewport.y = viewport_rect[1]; + viewport.width = viewport_rect[2]; + viewport.height = viewport_rect[3]; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + return viewport; +} + VkRect2D VKFrameBuffer::vk_render_area_get() const { VkRect2D render_area = {}; @@ -81,7 +103,7 @@ VkRect2D VKFrameBuffer::vk_render_area_get() const bool VKFrameBuffer::check(char /*err_out*/[256]) { - return false; + return true; } void VKFrameBuffer::build_clear_attachments_depth_stencil( @@ -318,7 +340,8 @@ void VKFrameBuffer::render_pass_create() size_set(size[0], size[1]); } else { - this->size_set(0, 0); + /* A framebuffer should at least be 1 by 1.*/ + this->size_set(1, 1); } viewport_reset(); scissor_reset(); diff --git a/source/blender/gpu/vulkan/vk_framebuffer.hh b/source/blender/gpu/vulkan/vk_framebuffer.hh index 15ed5bb3f83..a136375c560 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.hh +++ b/source/blender/gpu/vulkan/vk_framebuffer.hh @@ -87,6 +87,7 @@ class VKFrameBuffer : public FrameBuffer { BLI_assert(vk_render_pass_ != VK_NULL_HANDLE); return vk_render_pass_; } + VkViewport vk_viewport_get() const; VkRect2D vk_render_area_get() const; private: diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index 4d13aad1dbd..6e0c0588d10 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -7,10 +7,19 @@ #include "vk_pipeline.hh" #include "vk_context.hh" +#include "vk_framebuffer.hh" #include "vk_memory.hh" namespace blender::gpu { +VKPipeline::VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout, + VKPushConstants &&push_constants) + : vk_pipeline_(VK_NULL_HANDLE), + descriptor_set_(vk_descriptor_set_layout), + push_constants_(std::move(push_constants)) +{ +} + VKPipeline::VKPipeline(VkPipeline vk_pipeline, VkDescriptorSetLayout vk_descriptor_set_layout, VKPushConstants &&push_constants) @@ -60,6 +69,14 @@ VKPipeline VKPipeline::create_compute_pipeline( return VKPipeline(vk_pipeline, descriptor_set_layout, std::move(push_constants)); } +VKPipeline VKPipeline::create_graphics_pipeline( + VkDescriptorSetLayout &descriptor_set_layout, + const VKPushConstants::Layout &push_constants_layout) +{ + VKPushConstants push_constants(&push_constants_layout); + return VKPipeline(descriptor_set_layout, std::move(push_constants)); +} + VkPipeline VKPipeline::vk_handle() const { return vk_pipeline_; @@ -70,4 +87,114 @@ bool VKPipeline::is_valid() const return vk_pipeline_ != VK_NULL_HANDLE; } +void VKPipeline::finalize(VKContext &context, + VkShaderModule vertex_module, + VkShaderModule fragment_module, + VkPipelineLayout &pipeline_layout) +{ + BLI_assert(vertex_module != VK_NULL_HANDLE); + + VK_ALLOCATION_CALLBACKS + + Vector pipeline_stages; + VkPipelineShaderStageCreateInfo vertex_stage_info = {}; + vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertex_stage_info.module = vertex_module; + vertex_stage_info.pName = "main"; + pipeline_stages.append(vertex_stage_info); + /* + if (geometry_module_ != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo geo_stage_info = {}; + geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT; + geo_stage_info.module = geometry_module_; + geo_stage_info.pName = "main"; + pipeline_stages.append(geo_stage_info); + }*/ + if (fragment_module != VK_NULL_HANDLE) { + VkPipelineShaderStageCreateInfo fragment_stage_info = {}; + fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragment_stage_info.module = fragment_module; + fragment_stage_info.pName = "main"; + pipeline_stages.append(fragment_stage_info); + } + + VKFrameBuffer &framebuffer = *context.active_framebuffer_get(); + + VkGraphicsPipelineCreateInfo pipeline_create_info = {}; + pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_create_info.stageCount = pipeline_stages.size(); + pipeline_create_info.pStages = pipeline_stages.data(); + pipeline_create_info.layout = pipeline_layout; + pipeline_create_info.renderPass = framebuffer.vk_render_pass_get(); + pipeline_create_info.subpass = 0; + + /* Vertex input state. */ + VkPipelineVertexInputStateCreateInfo vertex_input_state = {}; + vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_input_state.vertexBindingDescriptionCount = 0; + /* Dummy attribute containing the vertex positions. These should be extracted from shader create + * infos. */ + VkVertexInputBindingDescription binding_description = {}; + binding_description.binding = 0; + binding_description.stride = 4 * 3; + binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + vertex_input_state.vertexBindingDescriptionCount = 1; + vertex_input_state.pVertexBindingDescriptions = &binding_description; + VkVertexInputAttributeDescription attribute_description = {}; + attribute_description.location = 0; + attribute_description.binding = 0; + attribute_description.format = VK_FORMAT_R32G32B32_SFLOAT; + attribute_description.offset = 0; + vertex_input_state.vertexAttributeDescriptionCount = 1; + vertex_input_state.pVertexAttributeDescriptions = &attribute_description; + pipeline_create_info.pVertexInputState = &vertex_input_state; + + /* Input assembly state. */ + VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly = {}; + pipeline_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + pipeline_input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly; + + VkPipelineRasterizationStateCreateInfo rasterization_state = {}; + rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + pipeline_create_info.pRasterizationState = &rasterization_state; + /* TODO: Needs to be sourced from GPU_state. */ + rasterization_state.lineWidth = 1.0; + + /* Viewport state. */ + VkPipelineViewportStateCreateInfo viewport_state = {}; + viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + VkViewport viewport = framebuffer.vk_viewport_get(); + viewport_state.pViewports = &viewport; + viewport_state.viewportCount = 1; + VkRect2D scissor = framebuffer.vk_render_area_get(); + viewport_state.pScissors = &scissor; + viewport_state.scissorCount = 1; + pipeline_create_info.pViewportState = &viewport_state; + + /* Multisample state. */ + VkPipelineMultisampleStateCreateInfo multisample_state = {}; + multisample_state.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisample_state.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + multisample_state.minSampleShading = 1.0f; + pipeline_create_info.pMultisampleState = &multisample_state; + + /* Color blend state. */ + VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state = {}; + pipeline_color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + VkPipelineColorBlendAttachmentState color_blend_attachment = {}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + pipeline_color_blend_state.attachmentCount = 1; + pipeline_color_blend_state.pAttachments = &color_blend_attachment; + pipeline_create_info.pColorBlendState = &pipeline_color_blend_state; + + VkDevice vk_device = context.device_get(); + vkCreateGraphicsPipelines( + vk_device, VK_NULL_HANDLE, 1, &pipeline_create_info, vk_allocation_callbacks, &vk_pipeline_); +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_pipeline.hh b/source/blender/gpu/vulkan/vk_pipeline.hh index bb2a9d6ec17..ee90e4b74e7 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.hh +++ b/source/blender/gpu/vulkan/vk_pipeline.hh @@ -18,7 +18,17 @@ namespace blender::gpu { class VKContext; +class VKShader; +/** + * Pipeline can be a compute pipeline or a graphic pipeline. + * + * Compute pipelines can be constructed early on, but graphics + * pipelines depends on the actual GPU state/context. + * + * - TODO: we should sanitize the interface. There we can also + * use late construction for compute pipelines. + */ class VKPipeline : NonCopyable { VkPipeline vk_pipeline_ = VK_NULL_HANDLE; VKDescriptorSetTracker descriptor_set_; @@ -28,6 +38,7 @@ class VKPipeline : NonCopyable { VKPipeline() = default; virtual ~VKPipeline(); + VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout, VKPushConstants &&push_constants); VKPipeline(VkPipeline vk_pipeline, VkDescriptorSetLayout vk_descriptor_set_layout, VKPushConstants &&push_constants); @@ -45,6 +56,8 @@ class VKPipeline : NonCopyable { VkDescriptorSetLayout &descriptor_set_layout, VkPipelineLayout &pipeline_layouts, const VKPushConstants::Layout &push_constants_layout); + static VKPipeline create_graphics_pipeline(VkDescriptorSetLayout &descriptor_set_layout, + const VKPushConstants::Layout &push_constants_layout); VKDescriptorSetTracker &descriptor_set_get() { @@ -58,6 +71,11 @@ class VKPipeline : NonCopyable { VkPipeline vk_handle() const; bool is_valid() const; + + void finalize(VKContext &context, + VkShaderModule vertex_module, + VkShaderModule fragment_module, + VkPipelineLayout &pipeline_layout); }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index 69d7f8014ad..42ddff72ec8 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -687,20 +687,21 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info) BLI_assert((fragment_module_ != VK_NULL_HANDLE && info->tf_type_ == GPU_SHADER_TFB_NONE) || (fragment_module_ == VK_NULL_HANDLE && info->tf_type_ != GPU_SHADER_TFB_NONE)); BLI_assert(compute_module_ == VK_NULL_HANDLE); - result = finalize_graphics_pipeline(vk_device); + pipeline_ = VKPipeline::create_graphics_pipeline(layout_, + vk_interface->push_constants_layout_get()); + result = true; } else { BLI_assert(vertex_module_ == VK_NULL_HANDLE); BLI_assert(geometry_module_ == VK_NULL_HANDLE); BLI_assert(fragment_module_ == VK_NULL_HANDLE); BLI_assert(compute_module_ != VK_NULL_HANDLE); - compute_pipeline_ = VKPipeline::create_compute_pipeline( - *context_, - compute_module_, - layout_, - pipeline_layout_, - vk_interface->push_constants_layout_get()); - result = compute_pipeline_.is_valid(); + pipeline_ = VKPipeline::create_compute_pipeline(*context_, + compute_module_, + layout_, + pipeline_layout_, + vk_interface->push_constants_layout_get()); + result = pipeline_.is_valid(); } if (result) { @@ -712,36 +713,6 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info) return result; } -bool VKShader::finalize_graphics_pipeline(VkDevice /*vk_device */) -{ - Vector pipeline_stages; - VkPipelineShaderStageCreateInfo vertex_stage_info = {}; - vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT; - vertex_stage_info.module = vertex_module_; - vertex_stage_info.pName = "main"; - pipeline_stages.append(vertex_stage_info); - - if (geometry_module_ != VK_NULL_HANDLE) { - VkPipelineShaderStageCreateInfo geo_stage_info = {}; - geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT; - geo_stage_info.module = geometry_module_; - geo_stage_info.pName = "main"; - pipeline_stages.append(geo_stage_info); - } - if (fragment_module_ != VK_NULL_HANDLE) { - VkPipelineShaderStageCreateInfo fragment_stage_info = {}; - fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT; - fragment_stage_info.module = fragment_module_; - fragment_stage_info.pName = "main"; - pipeline_stages.append(fragment_stage_info); - } - - return true; -} - bool VKShader::finalize_pipeline_layout(VkDevice vk_device, const VKShaderInterface &shader_interface) { @@ -981,26 +952,26 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *) void VKShader::transform_feedback_disable() { } +void VKShader::update_graphics_pipeline(VKContext &context) +{ + BLI_assert(is_graphics_shader()); + pipeline_get().finalize(context, vertex_module_, fragment_module_, pipeline_layout_); +} void VKShader::bind() { VKContext *context = VKContext::get(); if (is_compute_shader()) { - context->command_buffer_get().bind(compute_pipeline_, VK_PIPELINE_BIND_POINT_COMPUTE); - } - else { - BLI_assert_unreachable(); + context->command_buffer_get().bind(pipeline_, VK_PIPELINE_BIND_POINT_COMPUTE); } + + /* Graphics pipeline needs to be constructed just before drawing in order to take the GPU state + * into account. GPU state can be changed after binding the shader. */ } void VKShader::unbind() { - if (is_compute_shader()) { - } - else { - BLI_assert_unreachable(); - } } void VKShader::uniform_float(int location, int comp_len, int array_size, const float *data) @@ -1244,7 +1215,7 @@ int VKShader::program_handle_get() const VKPipeline &VKShader::pipeline_get() { - return compute_pipeline_; + return pipeline_; } const VKShaderInterface &VKShader::interface_get() const diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh index 21a99269f5c..982a9505fec 100644 --- a/source/blender/gpu/vulkan/vk_shader.hh +++ b/source/blender/gpu/vulkan/vk_shader.hh @@ -28,7 +28,7 @@ class VKShader : public Shader { bool compilation_failed_ = false; VkDescriptorSetLayout layout_ = VK_NULL_HANDLE; VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE; - VKPipeline compute_pipeline_; + VKPipeline pipeline_; public: VKShader(const char *name); @@ -70,6 +70,8 @@ class VKShader : public Shader { const VKShaderInterface &interface_get() const; + void update_graphics_pipeline(VKContext &context); + private: Vector compile_glsl_to_spirv(Span sources, shaderc_shader_kind kind); void build_shader_module(Span spirv_module, VkShaderModule *r_shader_module); @@ -80,7 +82,6 @@ class VKShader : public Shader { const VKShaderInterface &shader_interface, const shader::ShaderCreateInfo &info); bool finalize_pipeline_layout(VkDevice vk_device, const VKShaderInterface &shader_interface); - bool finalize_graphics_pipeline(VkDevice vk_device); bool is_graphics_shader() const { @@ -93,4 +94,9 @@ class VKShader : public Shader { } }; +static inline VKShader *unwrap(Shader *shader) +{ + return static_cast(shader); +} + } // namespace blender::gpu -- 2.30.2 From 12e9e90b6c2a112160e461bdd2adf3622524fa09 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 6 Apr 2023 16:21:39 +0200 Subject: [PATCH 03/19] Vertex buffer binding. --- source/blender/gpu/CMakeLists.txt | 4 + source/blender/gpu/vulkan/vk_batch.cc | 32 +++ source/blender/gpu/vulkan/vk_batch.hh | 9 + .../blender/gpu/vulkan/vk_command_buffer.cc | 16 ++ .../blender/gpu/vulkan/vk_command_buffer.hh | 6 + source/blender/gpu/vulkan/vk_common.cc | 135 ++++++++++++ source/blender/gpu/vulkan/vk_common.hh | 3 + source/blender/gpu/vulkan/vk_index_buffer.cc | 30 ++- source/blender/gpu/vulkan/vk_index_buffer.hh | 9 +- .../blender/gpu/vulkan/vk_shader_interface.cc | 18 +- .../gpu/vulkan/vk_vertex_attribute_object.cc | 154 ++++++++++++++ .../gpu/vulkan/vk_vertex_attribute_object.hh | 48 +++++ .../vk_vertex_attribute_object_cache.cc | 193 ++++++++++++++++++ .../vk_vertex_attribute_object_cache.hh | 74 +++++++ source/blender/gpu/vulkan/vk_vertex_buffer.cc | 19 +- source/blender/gpu/vulkan/vk_vertex_buffer.hh | 6 + 16 files changed, 749 insertions(+), 7 deletions(-) create mode 100644 source/blender/gpu/vulkan/vk_vertex_attribute_object.cc create mode 100644 source/blender/gpu/vulkan/vk_vertex_attribute_object.hh create mode 100644 source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc create mode 100644 source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.hh diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 8681dace750..5575665845d 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -226,6 +226,8 @@ set(VULKAN_SRC vulkan/vk_storage_buffer.cc vulkan/vk_texture.cc vulkan/vk_uniform_buffer.cc + vulkan/vk_vertex_attribute_object_cache.cc + vulkan/vk_vertex_attribute_object.cc vulkan/vk_vertex_buffer.cc vulkan/vk_backend.hh @@ -255,6 +257,8 @@ set(VULKAN_SRC vulkan/vk_storage_buffer.hh vulkan/vk_texture.hh vulkan/vk_uniform_buffer.hh + vulkan/vk_vertex_attribute_object_cache.hh + vulkan/vk_vertex_attribute_object.hh vulkan/vk_vertex_buffer.hh ) diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index 16d96dfc31c..415cd88eeae 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -8,14 +8,31 @@ #include "vk_batch.hh" #include "vk_context.hh" +#include "vk_index_buffer.hh" +#include "vk_vertex_buffer.hh" namespace blender::gpu { void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) { + if (flag & GPU_BATCH_DIRTY) { + vao_cache_.clear(); + flag &= ~GPU_BATCH_DIRTY; + } + // Based on the context construct the pipeline. VKContext &context = *VKContext::get(); + VKVertexAttributeObject &vao = vao_cache_.vao_get(this); + vao.update_bindings(context, *this); context.bind_graphics_pipeline(); + vao.bind(context); + + VKIndexBuffer *index_buffer = index_buffer_get(); + if (index_buffer) { + index_buffer->upload_data(); + index_buffer->bind(context); + } + context.command_buffer_get().draw(v_first, v_count, i_first, i_count); } @@ -28,4 +45,19 @@ void VKBatch::multi_draw_indirect(GPUStorageBuf * /*indirect_buf*/, { } +VKVertexBuffer *VKBatch::vertex_buffer_get(int index) +{ + return unwrap(verts_(index)); +} + +VKVertexBuffer *VKBatch::instance_buffer_get(int index) +{ + return unwrap(inst_(index)); +} + +VKIndexBuffer *VKBatch::index_buffer_get() +{ + return unwrap(unwrap(elem)); +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_batch.hh b/source/blender/gpu/vulkan/vk_batch.hh index 6813e981b18..342ea8de850 100644 --- a/source/blender/gpu/vulkan/vk_batch.hh +++ b/source/blender/gpu/vulkan/vk_batch.hh @@ -9,9 +9,14 @@ #include "gpu_batch_private.hh" +#include "vk_vertex_attribute_object_cache.hh" + namespace blender::gpu { class VKBatch : public Batch { + private: + VKVaoCache vao_cache_; + public: void draw(int v_first, int v_count, int i_first, int i_count) override; void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override; @@ -19,6 +24,10 @@ class VKBatch : public Batch { int count, intptr_t offset, intptr_t stride) override; + + VKVertexBuffer *vertex_buffer_get(int index); + VKVertexBuffer *instance_buffer_get(int index); + VKIndexBuffer *index_buffer_get(); }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_command_buffer.cc b/source/blender/gpu/vulkan/vk_command_buffer.cc index d5883d421c1..02d17d285c9 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.cc +++ b/source/blender/gpu/vulkan/vk_command_buffer.cc @@ -9,9 +9,11 @@ #include "vk_buffer.hh" #include "vk_context.hh" #include "vk_framebuffer.hh" +#include "vk_index_buffer.hh" #include "vk_memory.hh" #include "vk_pipeline.hh" #include "vk_texture.hh" +#include "vk_vertex_buffer.hh" #include "BLI_assert.h" @@ -74,6 +76,20 @@ void VKCommandBuffer::bind(const VKDescriptorSet &descriptor_set, vk_command_buffer_, bind_point, vk_pipeline_layout, 0, 1, &vk_descriptor_set, 0, 0); } +void VKCommandBuffer::bind(const uint32_t binding, + const VKVertexBuffer &vertex_buffer, + const VkDeviceSize offset) +{ + VkBuffer vk_buffer = vertex_buffer.vk_handle(); + vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_buffer, &offset); +} + +void VKCommandBuffer::bind(const VKIndexBuffer &index_buffer, VkIndexType index_type) +{ + VkBuffer vk_buffer = index_buffer.vk_handle(); + vkCmdBindIndexBuffer(vk_command_buffer_, vk_buffer, 0, index_type); +} + void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer) { VkRenderPassBeginInfo render_pass_begin_info = {}; diff --git a/source/blender/gpu/vulkan/vk_command_buffer.hh b/source/blender/gpu/vulkan/vk_command_buffer.hh index d8fce532921..0efa9dc8260 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.hh +++ b/source/blender/gpu/vulkan/vk_command_buffer.hh @@ -16,9 +16,11 @@ namespace blender::gpu { class VKBuffer; class VKDescriptorSet; class VKFrameBuffer; +class VKIndexBuffer; class VKPipeline; class VKPushConstants; class VKTexture; +class VKVertexBuffer; /** Command buffer to keep track of the life-time of a command buffer. */ class VKCommandBuffer : NonCopyable, NonMovable { @@ -40,6 +42,10 @@ class VKCommandBuffer : NonCopyable, NonMovable { 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); + void bind(const VKIndexBuffer &index_buffer, VkIndexType index_type); void begin_render_pass(const VKFrameBuffer &framebuffer); void end_render_pass(const VKFrameBuffer &framebuffer); diff --git a/source/blender/gpu/vulkan/vk_common.cc b/source/blender/gpu/vulkan/vk_common.cc index 534be431507..441a60527ee 100644 --- a/source/blender/gpu/vulkan/vk_common.cc +++ b/source/blender/gpu/vulkan/vk_common.cc @@ -243,6 +243,127 @@ VkFormat to_vk_format(const eGPUTextureFormat format) return VK_FORMAT_UNDEFINED; } +VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size) +{ + switch (type) { + case GPU_COMP_I8: + switch (size) { + case 1: + return VK_FORMAT_R8_SNORM; + case 2: + return VK_FORMAT_R8G8_SNORM; + case 3: + return VK_FORMAT_R8G8B8_SNORM; + case 4: + return VK_FORMAT_R8G8B8A8_SNORM; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_U8: + switch (size) { + case 1: + return VK_FORMAT_R8_UNORM; + case 2: + return VK_FORMAT_R8G8_UNORM; + case 3: + return VK_FORMAT_R8G8B8_UNORM; + case 4: + return VK_FORMAT_R8G8B8A8_UNORM; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_I16: + switch (size) { + case 2: + return VK_FORMAT_R16_SNORM; + case 4: + return VK_FORMAT_R16G16_SNORM; + case 6: + return VK_FORMAT_R16G16B16_SNORM; + case 8: + return VK_FORMAT_R16G16B16A16_SNORM; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_U16: + switch (size) { + case 2: + return VK_FORMAT_R16_UNORM; + case 4: + return VK_FORMAT_R16G16_UNORM; + case 6: + return VK_FORMAT_R16G16B16_UNORM; + case 8: + return VK_FORMAT_R16G16B16A16_UNORM; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_I32: + switch (size) { + case 4: + return VK_FORMAT_R32_SINT; + case 8: + return VK_FORMAT_R32G32_SINT; + case 12: + return VK_FORMAT_R32G32B32_SINT; + case 16: + return VK_FORMAT_R32G32B32A32_SINT; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_U32: + switch (size) { + case 4: + return VK_FORMAT_R32_UINT; + case 8: + return VK_FORMAT_R32G32_UINT; + case 12: + return VK_FORMAT_R32G32B32_UINT; + case 16: + return VK_FORMAT_R32G32B32A32_UINT; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_F32: + switch (size) { + case 4: + return VK_FORMAT_R32_SFLOAT; + case 8: + return VK_FORMAT_R32G32_SFLOAT; + case 12: + return VK_FORMAT_R32G32B32_SFLOAT; + case 16: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case 64: + return VK_FORMAT_R32G32B32A32_SFLOAT; + default: + BLI_assert(false); + return VK_FORMAT_R32_SFLOAT; + } + + case GPU_COMP_I10: + BLI_assert(size == 4); + // return VK_FORMAT_A2B10G10R10_SINT_PACK32; + return VK_FORMAT_A2B10G10R10_UNORM_PACK32; + + default: + BLI_assert(0); + return VK_FORMAT_R32_SFLOAT; + } +} + VkImageType to_vk_image_type(const eGPUTextureType type) { switch (type) { @@ -349,4 +470,18 @@ VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const voi return result; } +VkIndexType to_vk_index_type(const GPUIndexBufType index_type) +{ + switch (index_type) { + case GPU_INDEX_U16: + return VK_INDEX_TYPE_UINT16; + case GPU_INDEX_U32: + return VK_INDEX_TYPE_UINT32; + default: + BLI_assert_unreachable(); + break; + } + return VK_INDEX_TYPE_UINT16; +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_common.hh b/source/blender/gpu/vulkan/vk_common.hh index d8b5df769fd..da2ed41dca7 100644 --- a/source/blender/gpu/vulkan/vk_common.hh +++ b/source/blender/gpu/vulkan/vk_common.hh @@ -15,15 +15,18 @@ #include "vk_mem_alloc.h" +#include "gpu_index_buffer_private.hh" #include "gpu_texture_private.hh" namespace blender::gpu { VkImageAspectFlagBits to_vk_image_aspect_flag_bits(const eGPUTextureFormat format); VkFormat to_vk_format(const eGPUTextureFormat format); +VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size); VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat format); VkImageViewType to_vk_image_view_type(const eGPUTextureType type); VkImageType to_vk_image_type(const eGPUTextureType type); VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data); +VkIndexType to_vk_index_type(const GPUIndexBufType index_type); } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_index_buffer.cc b/source/blender/gpu/vulkan/vk_index_buffer.cc index 0f5c5246507..ca8c7a95fa8 100644 --- a/source/blender/gpu/vulkan/vk_index_buffer.cc +++ b/source/blender/gpu/vulkan/vk_index_buffer.cc @@ -11,15 +11,39 @@ namespace blender::gpu { -void VKIndexBuffer::upload_data() {} - -void VKIndexBuffer::bind_as_ssbo(uint binding) +void VKIndexBuffer::ensure_updated() { + if (is_subrange_) { + src_->upload_data(); + return; + } + VKContext &context = *VKContext::get(); if (!buffer_.is_allocated()) { allocate(context); } + if (data_ != nullptr) { + buffer_.update(data_); + MEM_SAFE_FREE(data_); + } +} + +void VKIndexBuffer::upload_data() +{ + ensure_updated(); +} + +void VKIndexBuffer::bind(VKContext &context) +{ + context.command_buffer_get().bind(*this, to_vk_index_type(index_type_)); +} + +void VKIndexBuffer::bind_as_ssbo(uint binding) +{ + ensure_updated(); + + VKContext &context = *VKContext::get(); VKShader *shader = static_cast(context.shader); const VKShaderInterface &shader_interface = shader->interface_get(); const VKDescriptorSet::Location location = shader_interface.descriptor_set_location( diff --git a/source/blender/gpu/vulkan/vk_index_buffer.hh b/source/blender/gpu/vulkan/vk_index_buffer.hh index 97de4207e1d..761b538cf8d 100644 --- a/source/blender/gpu/vulkan/vk_index_buffer.hh +++ b/source/blender/gpu/vulkan/vk_index_buffer.hh @@ -20,12 +20,13 @@ class VKIndexBuffer : public IndexBuf { void upload_data() override; void bind_as_ssbo(uint binding) override; + void bind(VKContext &context); void read(uint32_t *data) const override; void update_sub(uint start, uint len, const void *data) override; - VkBuffer vk_handle() + VkBuffer vk_handle() const { return buffer_.vk_handle(); } @@ -33,6 +34,12 @@ class VKIndexBuffer : public IndexBuf { private: void strip_restart_indices() override; void allocate(VKContext &context); + void ensure_updated(); }; +static inline VKIndexBuffer *unwrap(IndexBuf *index_buffer) +{ + return static_cast(index_buffer); +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_shader_interface.cc b/source/blender/gpu/vulkan/vk_shader_interface.cc index ba972bc289f..6c8e07d138d 100644 --- a/source/blender/gpu/vulkan/vk_shader_interface.cc +++ b/source/blender/gpu/vulkan/vk_shader_interface.cc @@ -17,7 +17,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info) using namespace blender::gpu::shader; - attr_len_ = 0; + attr_len_ = info.vertex_inputs_.size(); uniform_len_ = info.push_constants_.size(); ssbo_len_ = 0; ubo_len_ = 0; @@ -58,7 +58,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info) /* Make sure that the image slots don't overlap with the sampler slots. */ image_offset_++; - int32_t input_tot_len = ubo_len_ + uniform_len_ + ssbo_len_; + int32_t input_tot_len = attr_len_ + ubo_len_ + uniform_len_ + ssbo_len_; inputs_ = static_cast( MEM_calloc_arrayN(input_tot_len, sizeof(ShaderInput), __func__)); ShaderInput *input = inputs_; @@ -66,6 +66,20 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info) name_buffer_ = (char *)MEM_mallocN(names_size, "name_buffer"); uint32_t name_buffer_offset = 0; + /* Attributes */ + for (const ShaderCreateInfo::VertIn &attr : info.vertex_inputs_) { + copy_input_name(input, attr.name, name_buffer_, name_buffer_offset); + input->location = input->binding = attr.index; + if (input->location != -1) { + enabled_attr_mask_ |= (1 << input->location); + + /* Used in `GPU_shader_get_attribute_info`. */ + attr_types_[input->location] = uint8_t(attr.type); + } + + input++; + } + /* Uniform blocks */ for (const ShaderCreateInfo::Resource &res : all_resources) { if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) { diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc new file mode 100644 index 00000000000..22441621e31 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. All rights reserved. */ + +#include "vk_vertex_attribute_object.hh" + +#include "vk_batch.hh" +#include "vk_context.hh" +#include "vk_shader.hh" +#include "vk_shader_interface.hh" +#include "vk_vertex_buffer.hh" + +#include "BLI_array.hh" + +namespace blender::gpu { +VKVertexAttributeObject::VKVertexAttributeObject() +{ + clear(); +} + +void VKVertexAttributeObject::clear() +{ + is_valid = false; + info.pNext = NULL; + bindings.clear(); + attributes.clear(); + vbos.clear(); +} + +VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttributeObject &other) +{ + if (this == &other) { + return *this; + } + + /* TODO: resize is an exact resize. Might introduce unneeded re-allocations. */ + /* TODO: This might copy data, that isn't needed. */ + + is_valid = other.is_valid; + info = other.info; + bindings.resize(other.bindings.size()); + int i = 0; + for (const VkVertexInputBindingDescription &e : other.bindings) { + bindings[i++] = e; + } + i = 0; + attributes.resize(other.attributes.size()); + for (const VkVertexInputAttributeDescription e : other.attributes) { + attributes[i++] = e; + } + i = 0; + vbos.resize(other.vbos.size()); + for (VKVertexBuffer *e : other.vbos) { + vbos[i++] = e; + } + return *this; +} + +void VKVertexAttributeObject::bind(VKContext &context) +{ + Array visited_bindings(bindings.size()); + visited_bindings.fill(false); + + for (VkVertexInputAttributeDescription attribute : attributes) { + if (visited_bindings[attribute.binding]) { + continue; + } + visited_bindings[attribute.binding] = true; + BLI_assert(vbos[attribute.binding]); + VKVertexBuffer &vbo = *vbos[attribute.binding]; + vbo.upload(); + context.command_buffer_get().bind(attribute.binding, vbo, 0); + } +} + +void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch) +{ + const VKShaderInterface &interface = unwrap(context.shader)->interface_get(); + + /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */ + AttributeMask attribute_mask = interface.enabled_attr_mask_; + + for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) { + VKVertexBuffer *vbo = batch.vertex_buffer_get(v); + if (vbo) { + attribute_mask &= ~update_bindings(*vbo, interface, false); + } + } + + for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) { + VKVertexBuffer *vbo = batch.instance_buffer_get(v); + if (vbo) { + attribute_mask &= ~update_bindings(*vbo, interface, true); + } + } +} + +AttributeMask VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, + const VKShaderInterface &interface, + const bool use_instancing) +{ + + uint enabled_attributes = 0; + const GPUVertFormat &format = vertex_buffer.format; + + if (format.attr_len <= 0) { + return enabled_attributes; + } + + uint32_t offset = 0; + uint32_t stride = format.stride; + const uint32_t binding = bindings.size(); + + for (uint32_t attribute_index = 0; attribute_index < format.attr_len; attribute_index++) { + const GPUVertAttr &attribute = format.attrs[attribute_index]; + if (format.deinterleaved) { + offset += ((attribute_index == 0) ? 0 : format.attrs[attribute_index - 1].size) * + vertex_buffer.vertex_len; + stride = attribute.size; + } + else { + offset = attribute.offset; + } + + VkVertexInputBindingDescription vk_binding_descriptor = {}; + vk_binding_descriptor.binding = binding; + vk_binding_descriptor.stride = stride; + vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE : + VK_VERTEX_INPUT_RATE_VERTEX; + bindings.append(vk_binding_descriptor); + vbos.append(&vertex_buffer); + + for (uint32_t name_index = 0; name_index <= attribute.name_len; name_index++) { + const char *name = GPU_vertformat_attr_name_get(&format, &attribute, name_index); + const ShaderInput *shader_input = interface.attr_get(name); + if (shader_input == nullptr || shader_input->location == -1) { + continue; + } + + VkVertexInputAttributeDescription attribute_description = {}; + enabled_attributes |= (1 << shader_input->location); + attribute_description.binding = binding; + attribute_description.location = shader_input->location; + attribute_description.format = to_vk_format( + static_cast(attribute.comp_type), attribute.size); + attributes.append(attribute_description); + } + + // TODO: resolving conflicting location numbers. + } + + return enabled_attributes; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh new file mode 100644 index 00000000000..d62be394690 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "vk_common.hh" + +#include "BLI_vector.hh" + +#pragma once + +namespace blender::gpu { + +class VKVertexBuffer; +class VKContext; +class VKBatch; +class VKShaderInterface; + +using AttributeMask = uint16_t; + +struct VKVertexAttributeObject { + bool is_valid = false; + VkPipelineVertexInputStateCreateInfo info = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL}; + + Vector bindings; + Vector attributes; + Vector vbos; + + VKVertexAttributeObject(); + void clear(); + + void bind(VKContext &context); + + // Copy assignment operator. + VKVertexAttributeObject &operator=(const VKVertexAttributeObject &other); + + void update_bindings(const VKContext &context, VKBatch &batch); + + private: + AttributeMask update_bindings(VKVertexBuffer &vertex_buffer, + const VKShaderInterface &interface, + const bool use_instancing); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc new file mode 100644 index 00000000000..b48726c4304 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. All rights reserved. */ + +#include "vk_vertex_attribute_object_cache.hh" + +#include "vk_shader_interface.hh" + +#include "BLI_span.hh" + +namespace blender::gpu { + +VKVaoCache::VKVaoCache() +{ + init(); +} + +VKVaoCache::~VKVaoCache() +{ + this->clear(); +} + +void VKVaoCache::init() +{ + context_ = VKContext::get(); + interface_ = nullptr; + is_dynamic_vao_count = false; + for (int i = 0; i < VK_GPU_VAO_STATIC_LEN; i++) { + static_vaos.interfaces[i] = nullptr; + static_vaos.vao_ids[i].is_valid = false; + } + base_instance_ = 0; + vao_id_.is_valid = false; +} + +void VKVaoCache::insert(const VKShaderInterface *interface, VKVertexAttributeObject &vao) +{ + + BLI_assert(vao.is_valid); + + /* Now insert the cache. */ + if (!is_dynamic_vao_count) { + int i; /* find first unused slot */ + for (i = 0; i < VK_GPU_VAO_STATIC_LEN; i++) { + if (static_vaos.vao_ids[i].is_valid == false) { + break; + } + } + + if (i < VK_GPU_VAO_STATIC_LEN) { + static_vaos.interfaces[i] = interface; + static_vaos.vao_ids[i] = vao; + } + else { + /* Erase previous entries, they will be added back if drawn again. */ + for (int i = 0; i < VK_GPU_VAO_STATIC_LEN; i++) { + if (static_vaos.interfaces[i] != nullptr) { + static_vaos.vao_ids[i].clear(); + /* context_->vao_free(static_vaos.vao_ids[i]); */ + } + } + /* Not enough place switch to dynamic. */ + is_dynamic_vao_count = true; + /* Init dynamic arrays and let the branch below set the values. */ + dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; + dynamic_vaos.interfaces = (const VKShaderInterface **)MEM_callocN( + dynamic_vaos.count * sizeof(VKShaderInterface *), "dyn vaos interfaces"); + dynamic_vaos.vao_ids = (VKVertexAttributeObject *)MEM_callocN( + dynamic_vaos.count * sizeof(VKVertexAttributeObject), "dyn vaos ids"); + for (int j = 0; j < dynamic_vaos.count; j++) { + dynamic_vaos.interfaces[j] = nullptr; + } + } + } + + if (is_dynamic_vao_count) { + int i; /* find first unused slot */ + for (i = 0; i < dynamic_vaos.count; i++) { + if (dynamic_vaos.vao_ids[i].is_valid == false) { + break; + } + } + + if (i == dynamic_vaos.count) { + /* Not enough place, realloc the array. */ + i = dynamic_vaos.count; + dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; + dynamic_vaos.interfaces = (const VKShaderInterface **)MEM_recallocN( + (void *)dynamic_vaos.interfaces, sizeof(VKShaderInterface *) * dynamic_vaos.count); + dynamic_vaos.vao_ids = (VKVertexAttributeObject *)MEM_recallocN( + dynamic_vaos.vao_ids, sizeof(VKVertexAttributeObject) * dynamic_vaos.count); + for (int j = 0; j < GPU_BATCH_VAO_DYN_ALLOC_COUNT; j++) { + dynamic_vaos.interfaces[dynamic_vaos.count - j - 1] = nullptr; + } + } + + dynamic_vaos.interfaces[i] = interface; + dynamic_vaos.vao_ids[i] = vao; + } +} + +void VKVaoCache::remove(const VKShaderInterface *interface) +{ + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : VK_GPU_VAO_STATIC_LEN; + VKVertexAttributeObject *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : + static_vaos.vao_ids; + + const VKShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; + for (int i = 0; i < count; i++) { + if (interfaces[i] == interface) { + vaos[i].clear(); + interfaces[i] = nullptr; + break; /* cannot have duplicates */ + } + } +} + +void VKVaoCache::clear() +{ + + const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : VK_GPU_VAO_STATIC_LEN; + VKVertexAttributeObject *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : + static_vaos.vao_ids; + /* Early out, nothing to free. */ + if (context_ == nullptr) { + return; + } + + /* TODO(fclem): Slow way. Could avoid multiple mutex lock here */ + for (int i = 0; i < count; i++) { + vaos[i].clear(); + // context_->vao_free(vaos[i]); + } + + if (is_dynamic_vao_count) { + MEM_freeN((void *)dynamic_vaos.interfaces); + MEM_freeN((void *)dynamic_vaos.vao_ids); + } + + /* Reinit. */ + this->init(); +} + +static VKVertexAttributeObject &lookup_interface(int64_t element_len, + const VKShaderInterface **interfaces, + VKVertexAttributeObject *vaos, + const VKShaderInterface *interface) +{ + for (int i = 0; i < element_len; i++) { + if (interfaces[i] == interface) { + return vaos[i]; + } + } + + static VKVertexAttributeObject vao = {}; + vao.is_valid = false; + return vao; +} + +VKVertexAttributeObject &VKVaoCache::lookup(const VKShaderInterface *interface) +{ + if (is_dynamic_vao_count) { + return lookup_interface( + dynamic_vaos.count, dynamic_vaos.interfaces, dynamic_vaos.vao_ids, interface); + } + else { + return lookup_interface( + VK_GPU_VAO_STATIC_LEN, static_vaos.interfaces, static_vaos.vao_ids, interface); + } +} + +VKVertexAttributeObject &VKVaoCache::vao_get(GPUBatch *batch) +{ + Shader *shader = VKContext::get()->shader; + VKShaderInterface *interface = static_cast(shader->interface); + if (interface_ != interface) { + interface_ = interface; + }; + + vao_id_ = this->lookup(interface_); + + if (!vao_id_.is_valid) { + vao_id_.is_valid = true; + this->insert(interface_, vao_id_); + } + + vao_id_.clear(); + vao_id_.is_valid = true; + + return vao_id_; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.hh b/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.hh new file mode 100644 index 00000000000..d55f82894b3 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.hh @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "gpu_batch_private.hh" + +#include "vk_context.hh" +#include "vk_vertex_attribute_object.hh" + +namespace blender::gpu { +const uint32_t VK_GPU_VAO_STATIC_LEN = 3; + +/** + * VAO management: remembers all geometry state (vertex attribute bindings & element buffer) + * for each shader interface. Start with a static number of VAO's and fallback to dynamic count + * if necessary. Once a batch goes dynamic it does not go back. + */ +class VKVaoCache { + + private: + /** Context for which the vao_cache_ was generated. */ + VKContext *context_ = nullptr; + + /* TODO: why are these needed? */ + /** Last interface this batch was drawn with. */ + VKShaderInterface *interface_ = nullptr; + /** Cached VAO for the last interface. */ + VKVertexAttributeObject vao_id_ = {}; + + int base_instance_ = 0; + + bool is_dynamic_vao_count = false; + union { + /** Static handle count */ + struct { + const VKShaderInterface *interfaces[VK_GPU_VAO_STATIC_LEN]; + VKVertexAttributeObject vao_ids[VK_GPU_VAO_STATIC_LEN]; + } static_vaos; + /** Dynamic handle count */ + struct { + uint32_t count; + const VKShaderInterface **interfaces; + VKVertexAttributeObject *vao_ids; + } dynamic_vaos; + }; + + public: + bool is_dirty = false; + VKVaoCache(); + ~VKVaoCache(); + + VKVertexAttributeObject &vao_get(GPUBatch *batch); + + /** + * Return nullptr on cache miss (invalid VAO). + */ + VKVertexAttributeObject &lookup(const VKShaderInterface *interface); + /** + * Create a new VAO object and store it in the cache. + */ + void insert(const VKShaderInterface *interface, VKVertexAttributeObject &vao_id); + void remove(const VKShaderInterface *interface); + void clear(); + + private: + void init(); +}; + +} // namespace blender::gpu \ No newline at end of file diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.cc b/source/blender/gpu/vulkan/vk_vertex_buffer.cc index dc7ae6aed74..ca36618691d 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.cc +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.cc @@ -53,6 +53,7 @@ void VKVertexBuffer::acquire_data() } /* Discard previous data if any. */ + /* TODO: Use mapped memory. */ MEM_SAFE_FREE(data); data = (uchar *)MEM_mallocN(sizeof(uchar) * this->size_alloc_get(), __func__); } @@ -64,7 +65,23 @@ void VKVertexBuffer::release_data() MEM_SAFE_FREE(data); } -void VKVertexBuffer::upload_data() {} +void VKVertexBuffer::upload_data() +{ + VKContext &context = *VKContext::get(); + if (!buffer_.is_allocated()) { + allocate(context); + } + + if (flag &= GPU_VERTBUF_DATA_DIRTY) { + buffer_.update(data); + if (usage_ == GPU_USAGE_STATIC) { + MEM_SAFE_FREE(data); + } + + flag &= ~GPU_VERTBUF_DATA_DIRTY; + flag |= GPU_VERTBUF_DATA_UPLOADED; + } +} void VKVertexBuffer::duplicate_data(VertBuf * /*dst*/) {} diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.hh b/source/blender/gpu/vulkan/vk_vertex_buffer.hh index bbae86ae07f..20d736b11a3 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.hh +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.hh @@ -28,6 +28,7 @@ class VKVertexBuffer : public VertBuf { VkBuffer vk_handle() const { + BLI_assert(buffer_.is_allocated()); return buffer_.vk_handle(); } @@ -42,4 +43,9 @@ class VKVertexBuffer : public VertBuf { void allocate(VKContext &context); }; +static inline VKVertexBuffer *unwrap(VertBuf *vertex_buffer) +{ + return static_cast(vertex_buffer); +} + } // namespace blender::gpu -- 2.30.2 From 85ffcfb04f0308d519c375601ab9354e126ae367 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 08:22:36 +0200 Subject: [PATCH 04/19] Fix push constants and use VAO in pipeline --- source/blender/gpu/vulkan/vk_batch.cc | 2 +- source/blender/gpu/vulkan/vk_context.cc | 6 ++++-- source/blender/gpu/vulkan/vk_context.hh | 3 ++- source/blender/gpu/vulkan/vk_pipeline.cc | 23 +++++++---------------- source/blender/gpu/vulkan/vk_pipeline.hh | 4 +++- source/blender/gpu/vulkan/vk_shader.cc | 4 ++-- source/blender/gpu/vulkan/vk_shader.hh | 3 ++- 7 files changed, 21 insertions(+), 24 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index 415cd88eeae..10f3a14a935 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -24,7 +24,7 @@ void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) VKContext &context = *VKContext::get(); VKVertexAttributeObject &vao = vao_cache_.vao_get(this); vao.update_bindings(context, *this); - context.bind_graphics_pipeline(); + context.bind_graphics_pipeline(vao); vao.bind(context); VKIndexBuffer *index_buffer = index_buffer_get(); diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index ecd3381dbcb..df36f284df6 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -181,12 +181,14 @@ void VKContext::deactivate_framebuffer() /* -------------------------------------------------------------------- */ /** \name Graphics pipeline * \{ */ -void VKContext::bind_graphics_pipeline() + +void VKContext::bind_graphics_pipeline(const VKVertexAttributeObject &vertex_attribute_object) { VKShader *shader = unwrap(this->shader); BLI_assert(shader); - shader->update_graphics_pipeline(*this); + shader->update_graphics_pipeline(*this, vertex_attribute_object); command_buffer_get().bind(shader->pipeline_get(), VK_PIPELINE_BIND_POINT_GRAPHICS); + shader->pipeline_get().push_constants_get().update(*this); } /** \} */ diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index 47ab0762639..e45eb91a079 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -14,6 +14,7 @@ namespace blender::gpu { class VKFrameBuffer; +class VKVertexAttributeObject; class VKContext : public Context { private: @@ -61,7 +62,7 @@ class VKContext : public Context { void deactivate_framebuffer(); VKFrameBuffer *active_framebuffer_get() const; - void bind_graphics_pipeline(); + void bind_graphics_pipeline(const VKVertexAttributeObject &vertex_attribute_object); static VKContext *get(void) { diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index 9095b17de34..2a7827ff713 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -9,6 +9,7 @@ #include "vk_context.hh" #include "vk_framebuffer.hh" #include "vk_memory.hh" +#include "vk_vertex_attribute_object.hh" namespace blender::gpu { @@ -90,7 +91,8 @@ bool VKPipeline::is_valid() const void VKPipeline::finalize(VKContext &context, VkShaderModule vertex_module, VkShaderModule fragment_module, - VkPipelineLayout &pipeline_layout) + VkPipelineLayout &pipeline_layout, + const VKVertexAttributeObject &vertex_attribute_object) { BLI_assert(vertex_module != VK_NULL_HANDLE); @@ -135,21 +137,10 @@ void VKPipeline::finalize(VKContext &context, VkPipelineVertexInputStateCreateInfo vertex_input_state = {}; vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_state.vertexBindingDescriptionCount = 0; - /* Dummy attribute containing the vertex positions. These should be extracted from shader create - * infos. */ - VkVertexInputBindingDescription binding_description = {}; - binding_description.binding = 0; - binding_description.stride = 4 * 3; - binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - vertex_input_state.vertexBindingDescriptionCount = 1; - vertex_input_state.pVertexBindingDescriptions = &binding_description; - VkVertexInputAttributeDescription attribute_description = {}; - attribute_description.location = 0; - attribute_description.binding = 0; - attribute_description.format = VK_FORMAT_R32G32B32_SFLOAT; - attribute_description.offset = 0; - vertex_input_state.vertexAttributeDescriptionCount = 1; - vertex_input_state.pVertexAttributeDescriptions = &attribute_description; + vertex_input_state.vertexBindingDescriptionCount = 1;//vertex_attribute_object.bindings.size(); + vertex_input_state.pVertexBindingDescriptions = vertex_attribute_object.bindings.data(); + vertex_input_state.vertexAttributeDescriptionCount = 1;//vertex_attribute_object.attributes.size(); + vertex_input_state.pVertexAttributeDescriptions = vertex_attribute_object.attributes.data(); pipeline_create_info.pVertexInputState = &vertex_input_state; /* Input assembly state. */ diff --git a/source/blender/gpu/vulkan/vk_pipeline.hh b/source/blender/gpu/vulkan/vk_pipeline.hh index beca16cd9b9..9b21a972cf5 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.hh +++ b/source/blender/gpu/vulkan/vk_pipeline.hh @@ -19,6 +19,7 @@ namespace blender::gpu { class VKContext; class VKShader; +class VKVertexAttributeObject; /** * Pipeline can be a compute pipeline or a graphic pipeline. @@ -75,7 +76,8 @@ class VKPipeline : NonCopyable { void finalize(VKContext &context, VkShaderModule vertex_module, VkShaderModule fragment_module, - VkPipelineLayout &pipeline_layout); + VkPipelineLayout &pipeline_layout, + const VKVertexAttributeObject &vertex_attribute_object); }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index 6966cc11665..4788714f6ca 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -932,10 +932,10 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *) void VKShader::transform_feedback_disable() {} -void VKShader::update_graphics_pipeline(VKContext &context) +void VKShader::update_graphics_pipeline(VKContext &context, const VKVertexAttributeObject &vertex_attribute_object) { BLI_assert(is_graphics_shader()); - pipeline_get().finalize(context, vertex_module_, fragment_module_, pipeline_layout_); + pipeline_get().finalize(context, vertex_module_, fragment_module_, pipeline_layout_, vertex_attribute_object); } void VKShader::bind() diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh index 12107e2febb..4e75d006635 100644 --- a/source/blender/gpu/vulkan/vk_shader.hh +++ b/source/blender/gpu/vulkan/vk_shader.hh @@ -70,7 +70,8 @@ class VKShader : public Shader { const VKShaderInterface &interface_get() const; - void update_graphics_pipeline(VKContext &context); + void update_graphics_pipeline(VKContext &context, + const VKVertexAttributeObject &vertex_attribute_object); private: Vector compile_glsl_to_spirv(Span sources, shaderc_shader_kind kind); -- 2.30.2 From 1e0b6a470777f12f7bbb0cbb7a44995d7f9d040b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 09:04:26 +0200 Subject: [PATCH 05/19] Use correct primitive topology --- source/blender/gpu/tests/offscreen_test.cc | 29 ++++++++++++++++-- source/blender/gpu/vulkan/vk_batch.cc | 2 +- source/blender/gpu/vulkan/vk_common.cc | 34 ++++++++++++++++++++++ source/blender/gpu/vulkan/vk_common.hh | 1 + source/blender/gpu/vulkan/vk_context.cc | 5 ++-- source/blender/gpu/vulkan/vk_context.hh | 4 ++- source/blender/gpu/vulkan/vk_pipeline.cc | 10 +++++-- source/blender/gpu/vulkan/vk_pipeline.hh | 4 ++- source/blender/gpu/vulkan/vk_shader.cc | 7 +++-- source/blender/gpu/vulkan/vk_shader.hh | 1 + 10 files changed, 85 insertions(+), 12 deletions(-) diff --git a/source/blender/gpu/tests/offscreen_test.cc b/source/blender/gpu/tests/offscreen_test.cc index af226a35424..bbea015a4b3 100644 --- a/source/blender/gpu/tests/offscreen_test.cc +++ b/source/blender/gpu/tests/offscreen_test.cc @@ -14,7 +14,7 @@ namespace blender::gpu::tests { const size_t Size = 512; -static void test_offscreen_draw_batch() +static void test_offscreen_draw_batch_quad() { GPUOffScreen *offscreen = GPU_offscreen_create(Size, Size, false, GPU_RGBA16F, nullptr); BLI_assert(offscreen != nullptr); @@ -38,6 +38,31 @@ static void test_offscreen_draw_batch() GPU_offscreen_free(offscreen); DRW_shape_cache_free(); } -GPU_TEST(offscreen_draw_batch); +GPU_TEST(offscreen_draw_batch_quad); +static void test_offscreen_draw_batch_sphere() +{ + GPUOffScreen *offscreen = GPU_offscreen_create(Size, Size, false, GPU_RGBA16F, nullptr); + BLI_assert(offscreen != nullptr); + GPU_offscreen_bind(offscreen, false); + + GPUBatch *batch = DRW_cache_sphere_get(DRW_LOD_MEDIUM); + float4 color(1.0f, 0.5f, 0.0f, 1.0f); + + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(batch, "color", color); + + GPU_matrix_push(); + GPU_matrix_scale_1f(0.5f); + GPU_matrix_rotate_2d(45.0f); + + GPU_batch_draw(batch); + GPU_offscreen_unbind(offscreen, false); + GPU_flush(); + GPU_matrix_pop(); + + GPU_offscreen_free(offscreen); + DRW_shape_cache_free(); +} +GPU_TEST(offscreen_draw_batch_sphere); } // namespace blender::gpu::tests \ No newline at end of file diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index 10f3a14a935..48ac0e0af7c 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -24,7 +24,7 @@ void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) VKContext &context = *VKContext::get(); VKVertexAttributeObject &vao = vao_cache_.vao_get(this); vao.update_bindings(context, *this); - context.bind_graphics_pipeline(vao); + context.bind_graphics_pipeline(*this, vao); vao.bind(context); VKIndexBuffer *index_buffer = index_buffer_get(); diff --git a/source/blender/gpu/vulkan/vk_common.cc b/source/blender/gpu/vulkan/vk_common.cc index 441a60527ee..d21fa286b4b 100644 --- a/source/blender/gpu/vulkan/vk_common.cc +++ b/source/blender/gpu/vulkan/vk_common.cc @@ -484,4 +484,38 @@ VkIndexType to_vk_index_type(const GPUIndexBufType index_type) return VK_INDEX_TYPE_UINT16; } +VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type) +{ + switch (prim_type) { + case GPU_PRIM_POINTS: + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; + case GPU_PRIM_LINES: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case GPU_PRIM_TRIS: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + case GPU_PRIM_LINE_STRIP: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; + case GPU_PRIM_LINE_LOOP: + /* TODO: Line loop should add an additional index?*/ + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; + case GPU_PRIM_TRI_STRIP: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + case GPU_PRIM_TRI_FAN: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN; + case GPU_PRIM_LINES_ADJ: + return VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY; + case GPU_PRIM_TRIS_ADJ: + return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY; + case GPU_PRIM_LINE_STRIP_ADJ: + return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY; + + case GPU_PRIM_NONE: + break; + } + + BLI_assert_unreachable(); + + return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_common.hh b/source/blender/gpu/vulkan/vk_common.hh index da2ed41dca7..47288d3bfc1 100644 --- a/source/blender/gpu/vulkan/vk_common.hh +++ b/source/blender/gpu/vulkan/vk_common.hh @@ -28,5 +28,6 @@ VkImageViewType to_vk_image_view_type(const eGPUTextureType type); VkImageType to_vk_image_type(const eGPUTextureType type); VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data); VkIndexType to_vk_index_type(const GPUIndexBufType index_type); +VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type); } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index df36f284df6..4006d4ca23a 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -182,11 +182,12 @@ void VKContext::deactivate_framebuffer() /** \name Graphics pipeline * \{ */ -void VKContext::bind_graphics_pipeline(const VKVertexAttributeObject &vertex_attribute_object) +void VKContext::bind_graphics_pipeline(const VKBatch &batch, + const VKVertexAttributeObject &vertex_attribute_object) { VKShader *shader = unwrap(this->shader); BLI_assert(shader); - shader->update_graphics_pipeline(*this, vertex_attribute_object); + shader->update_graphics_pipeline(*this, batch, vertex_attribute_object); command_buffer_get().bind(shader->pipeline_get(), VK_PIPELINE_BIND_POINT_GRAPHICS); shader->pipeline_get().push_constants_get().update(*this); } diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index e45eb91a079..a18c1dba2b2 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -15,6 +15,7 @@ namespace blender::gpu { class VKFrameBuffer; class VKVertexAttributeObject; +class VKBatch; class VKContext : public Context { private: @@ -62,7 +63,8 @@ class VKContext : public Context { void deactivate_framebuffer(); VKFrameBuffer *active_framebuffer_get() const; - void bind_graphics_pipeline(const VKVertexAttributeObject &vertex_attribute_object); + void bind_graphics_pipeline(const VKBatch &batch, + const VKVertexAttributeObject &vertex_attribute_object); static VKContext *get(void) { diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index 2a7827ff713..f57538cd9da 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -6,6 +6,7 @@ */ #include "vk_pipeline.hh" +#include "vk_batch.hh" #include "vk_context.hh" #include "vk_framebuffer.hh" #include "vk_memory.hh" @@ -92,6 +93,7 @@ void VKPipeline::finalize(VKContext &context, VkShaderModule vertex_module, VkShaderModule fragment_module, VkPipelineLayout &pipeline_layout, + const VKBatch &batch, const VKVertexAttributeObject &vertex_attribute_object) { BLI_assert(vertex_module != VK_NULL_HANDLE); @@ -137,16 +139,18 @@ void VKPipeline::finalize(VKContext &context, VkPipelineVertexInputStateCreateInfo vertex_input_state = {}; vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_state.vertexBindingDescriptionCount = 0; - vertex_input_state.vertexBindingDescriptionCount = 1;//vertex_attribute_object.bindings.size(); + vertex_input_state.vertexBindingDescriptionCount = + 1; // vertex_attribute_object.bindings.size(); vertex_input_state.pVertexBindingDescriptions = vertex_attribute_object.bindings.data(); - vertex_input_state.vertexAttributeDescriptionCount = 1;//vertex_attribute_object.attributes.size(); + vertex_input_state.vertexAttributeDescriptionCount = + 1; // vertex_attribute_object.attributes.size(); vertex_input_state.pVertexAttributeDescriptions = vertex_attribute_object.attributes.data(); pipeline_create_info.pVertexInputState = &vertex_input_state; /* Input assembly state. */ VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly = {}; pipeline_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - pipeline_input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + pipeline_input_assembly.topology = to_vk_primitive_topology(batch.prim_type); pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly; VkPipelineRasterizationStateCreateInfo rasterization_state = {}; diff --git a/source/blender/gpu/vulkan/vk_pipeline.hh b/source/blender/gpu/vulkan/vk_pipeline.hh index 9b21a972cf5..7b3069f2931 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.hh +++ b/source/blender/gpu/vulkan/vk_pipeline.hh @@ -20,6 +20,7 @@ namespace blender::gpu { class VKContext; class VKShader; class VKVertexAttributeObject; +class VKBatch; /** * Pipeline can be a compute pipeline or a graphic pipeline. @@ -77,7 +78,8 @@ class VKPipeline : NonCopyable { VkShaderModule vertex_module, VkShaderModule fragment_module, VkPipelineLayout &pipeline_layout, - const VKVertexAttributeObject &vertex_attribute_object); + const VKBatch &batch, + const VKVertexAttributeObject &vertex_attribute_object); }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index 4788714f6ca..bc9876d46ce 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -932,10 +932,13 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *) void VKShader::transform_feedback_disable() {} -void VKShader::update_graphics_pipeline(VKContext &context, const VKVertexAttributeObject &vertex_attribute_object) +void VKShader::update_graphics_pipeline(VKContext &context, + const VKBatch &batch, + const VKVertexAttributeObject &vertex_attribute_object) { BLI_assert(is_graphics_shader()); - pipeline_get().finalize(context, vertex_module_, fragment_module_, pipeline_layout_, vertex_attribute_object); + pipeline_get().finalize( + context, vertex_module_, fragment_module_, pipeline_layout_, batch, vertex_attribute_object); } void VKShader::bind() diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh index 4e75d006635..ade53edf9b7 100644 --- a/source/blender/gpu/vulkan/vk_shader.hh +++ b/source/blender/gpu/vulkan/vk_shader.hh @@ -71,6 +71,7 @@ class VKShader : public Shader { const VKShaderInterface &interface_get() const; void update_graphics_pipeline(VKContext &context, + const VKBatch &batch, const VKVertexAttributeObject &vertex_attribute_object); private: -- 2.30.2 From f00e82d5dc13325562ffcfc75c83b6ba68ddcab1 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 12:09:53 +0200 Subject: [PATCH 06/19] Migrated to pipeline specific state manager. --- source/blender/gpu/CMakeLists.txt | 2 + source/blender/gpu/tests/offscreen_test.cc | 3 +- source/blender/gpu/vulkan/vk_batch.cc | 1 + source/blender/gpu/vulkan/vk_common.cc | 15 +- source/blender/gpu/vulkan/vk_common.hh | 1 + source/blender/gpu/vulkan/vk_context.cc | 11 + source/blender/gpu/vulkan/vk_context.hh | 3 + source/blender/gpu/vulkan/vk_pipeline.cc | 19 +- source/blender/gpu/vulkan/vk_pipeline.hh | 7 + .../blender/gpu/vulkan/vk_pipeline_state.cc | 367 ++++++++++++++++++ .../blender/gpu/vulkan/vk_pipeline_state.hh | 46 +++ source/blender/gpu/vulkan/vk_shader.hh | 5 + source/blender/gpu/vulkan/vk_state_manager.cc | 20 +- source/blender/gpu/vulkan/vk_state_manager.hh | 5 + 14 files changed, 486 insertions(+), 19 deletions(-) create mode 100644 source/blender/gpu/vulkan/vk_pipeline_state.cc create mode 100644 source/blender/gpu/vulkan/vk_pipeline_state.hh diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 5575665845d..960ffbcc921 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -214,6 +214,7 @@ set(VULKAN_SRC vulkan/vk_index_buffer.cc vulkan/vk_memory_layout.cc vulkan/vk_memory.cc + vulkan/vk_pipeline_state.cc vulkan/vk_pipeline.cc vulkan/vk_pixel_buffer.cc vulkan/vk_push_constants.cc @@ -245,6 +246,7 @@ set(VULKAN_SRC vulkan/vk_index_buffer.hh vulkan/vk_memory_layout.hh vulkan/vk_memory.hh + vulkan/vk_pipeline_state.hh vulkan/vk_pipeline.hh vulkan/vk_pixel_buffer.hh vulkan/vk_push_constants.hh diff --git a/source/blender/gpu/tests/offscreen_test.cc b/source/blender/gpu/tests/offscreen_test.cc index bbea015a4b3..706b6534cf8 100644 --- a/source/blender/gpu/tests/offscreen_test.cc +++ b/source/blender/gpu/tests/offscreen_test.cc @@ -47,10 +47,11 @@ static void test_offscreen_draw_batch_sphere() GPU_offscreen_bind(offscreen, false); GPUBatch *batch = DRW_cache_sphere_get(DRW_LOD_MEDIUM); - float4 color(1.0f, 0.5f, 0.0f, 1.0f); + float4 color(1.0f, 0.5f, 0.0f, 0.8f); GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); GPU_batch_uniform_4fv(batch, "color", color); + GPU_blend(GPU_BLEND_ALPHA); GPU_matrix_push(); GPU_matrix_scale_1f(0.5f); diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index 48ac0e0af7c..ff92dca44b2 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -22,6 +22,7 @@ void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) // Based on the context construct the pipeline. VKContext &context = *VKContext::get(); + context.state_manager->apply_state(); VKVertexAttributeObject &vao = vao_cache_.vao_get(this); vao.update_bindings(context, *this); context.bind_graphics_pipeline(*this, vao); diff --git a/source/blender/gpu/vulkan/vk_common.cc b/source/blender/gpu/vulkan/vk_common.cc index d21fa286b4b..2215dc0ffe7 100644 --- a/source/blender/gpu/vulkan/vk_common.cc +++ b/source/blender/gpu/vulkan/vk_common.cc @@ -496,7 +496,6 @@ VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type) case GPU_PRIM_LINE_STRIP: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; case GPU_PRIM_LINE_LOOP: - /* TODO: Line loop should add an additional index?*/ return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; case GPU_PRIM_TRI_STRIP: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; @@ -514,8 +513,20 @@ VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type) } BLI_assert_unreachable(); - return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; } +VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test) +{ + switch (cull_test) { + case GPU_CULL_FRONT: + return VK_CULL_MODE_FRONT_BIT; + case GPU_CULL_BACK: + return VK_CULL_MODE_BACK_BIT; + case GPU_CULL_NONE: + return VK_CULL_MODE_NONE; + } + BLI_assert_unreachable(); + return VK_CULL_MODE_NONE; +} } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_common.hh b/source/blender/gpu/vulkan/vk_common.hh index 47288d3bfc1..09cd1347d03 100644 --- a/source/blender/gpu/vulkan/vk_common.hh +++ b/source/blender/gpu/vulkan/vk_common.hh @@ -29,5 +29,6 @@ VkImageType to_vk_image_type(const eGPUTextureType type); VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const void *data); VkIndexType to_vk_index_type(const GPUIndexBufType index_type); VkPrimitiveTopology to_vk_primitive_topology(const GPUPrimType prim_type); +VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test); } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index 4006d4ca23a..b310219fe41 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -143,6 +143,17 @@ void VKContext::finish() void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {} +/* -------------------------------------------------------------------- */ +/** \name State manager + * \{ */ + +const VKStateManager &VKContext::state_manager_get() const +{ + return *static_cast(state_manager); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Framebuffer * \{ */ diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index a18c1dba2b2..bfca6d14021 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -16,6 +16,7 @@ namespace blender::gpu { class VKFrameBuffer; class VKVertexAttributeObject; class VKBatch; +class VKStateManager; class VKContext : public Context { private: @@ -106,6 +107,8 @@ class VKContext : public Context { return descriptor_pools_; } + const VKStateManager &state_manager_get() const; + VmaAllocator mem_allocator_get() const { return mem_allocator_; diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index f57538cd9da..0c007a61673 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -10,6 +10,7 @@ #include "vk_context.hh" #include "vk_framebuffer.hh" #include "vk_memory.hh" +#include "vk_state_manager.hh" #include "vk_vertex_attribute_object.hh" namespace blender::gpu { @@ -153,12 +154,6 @@ void VKPipeline::finalize(VKContext &context, pipeline_input_assembly.topology = to_vk_primitive_topology(batch.prim_type); pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly; - VkPipelineRasterizationStateCreateInfo rasterization_state = {}; - rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - pipeline_create_info.pRasterizationState = &rasterization_state; - /* TODO: Needs to be sourced from GPU_state. */ - rasterization_state.lineWidth = 1.0; - /* Viewport state. */ VkPipelineViewportStateCreateInfo viewport_state = {}; viewport_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; @@ -178,14 +173,10 @@ void VKPipeline::finalize(VKContext &context, pipeline_create_info.pMultisampleState = &multisample_state; /* Color blend state. */ - VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state = {}; - pipeline_color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - VkPipelineColorBlendAttachmentState color_blend_attachment = {}; - color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; - pipeline_color_blend_state.attachmentCount = 1; - pipeline_color_blend_state.pAttachments = &color_blend_attachment; - pipeline_create_info.pColorBlendState = &pipeline_color_blend_state; + const VKPipelineStateManager &state_manager = state_manager_get(); + pipeline_create_info.pColorBlendState = &state_manager.pipeline_color_blend_state; + pipeline_create_info.pRasterizationState = &state_manager.rasterization_state; + pipeline_create_info.pDepthStencilState = &state_manager.depth_stencil_state; VkDevice vk_device = context.device_get(); vkCreateGraphicsPipelines( diff --git a/source/blender/gpu/vulkan/vk_pipeline.hh b/source/blender/gpu/vulkan/vk_pipeline.hh index 7b3069f2931..958c5b43f09 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.hh +++ b/source/blender/gpu/vulkan/vk_pipeline.hh @@ -14,6 +14,7 @@ #include "vk_common.hh" #include "vk_descriptor_set.hh" +#include "vk_pipeline_state.hh" #include "vk_push_constants.hh" namespace blender::gpu { @@ -35,6 +36,7 @@ class VKPipeline : NonCopyable { VkPipeline vk_pipeline_ = VK_NULL_HANDLE; VKDescriptorSetTracker descriptor_set_; VKPushConstants push_constants_; + VKPipelineStateManager state_manager_; public: VKPipeline() = default; @@ -71,6 +73,11 @@ class VKPipeline : NonCopyable { return push_constants_; } + VKPipelineStateManager &state_manager_get() + { + return state_manager_; + } + VkPipeline vk_handle() const; bool is_valid() const; diff --git a/source/blender/gpu/vulkan/vk_pipeline_state.cc b/source/blender/gpu/vulkan/vk_pipeline_state.cc new file mode 100644 index 00000000000..51c7146e246 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_pipeline_state.cc @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation */ + +/** \file + * \ingroup gpu + */ + +#include "vk_pipeline_state.hh" + +namespace blender::gpu { +VKPipelineStateManager::VKPipelineStateManager() +{ + rasterization_state = {}; + rasterization_state.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterization_state.lineWidth = 1.0f; + + pipeline_color_blend_state = {}; + pipeline_color_blend_state.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + + depth_stencil_state = {}; + depth_stencil_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; +} + +void VKPipelineStateManager::set_state(const GPUState &state, const GPUStateMutable &mutable_state) +{ + /* TODO should be extracted from current framebuffer and should not be done here and now. */ + color_blend_attachments.clear(); + VkPipelineColorBlendAttachmentState color_blend_attachment = {}; + color_blend_attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachments.append(color_blend_attachment); + pipeline_color_blend_state.attachmentCount = color_blend_attachments.size(); + pipeline_color_blend_state.pAttachments = color_blend_attachments.data(); + + GPUState changed = state ^ current_; + if (changed.blend) { + set_blend(static_cast(state.blend)); + } + if (changed.write_mask != 0) { + set_write_mask((eGPUWriteMask)state.write_mask); + } + if (changed.depth_test != 0) { + set_depth_test((eGPUDepthTest)state.depth_test); + } + if (changed.stencil_test != 0 || changed.stencil_op != 0) { + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state); + } + if (changed.clip_distances != 0) { + set_clip_distances(state.clip_distances, current_.clip_distances); + } + if (changed.culling_test != 0) { + set_backface_culling((eGPUFaceCullTest)state.culling_test); + } + if (changed.logic_op_xor != 0) { + set_logic_op(state.logic_op_xor); + } + if (changed.invert_facing != 0) { + set_facing(state.invert_facing); + } + if (changed.provoking_vert != 0) { + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); + } + if (changed.shadow_bias != 0) { + set_shadow_bias(state.shadow_bias); + } + current_ = state; +} + +void VKPipelineStateManager::force_state(const GPUState &state, + const GPUStateMutable &mutable_state) +{ + current_ = ~state; + set_state(state, mutable_state); +} + +void VKPipelineStateManager::set_blend(const eGPUBlend blend) +{ + VkPipelineColorBlendStateCreateInfo &cb = pipeline_color_blend_state; + VkPipelineColorBlendAttachmentState &att_state = color_blend_attachments.last(); + + att_state.blendEnable = VK_TRUE; + att_state.alphaBlendOp = VK_BLEND_OP_ADD; + att_state.colorBlendOp = VK_BLEND_OP_ADD; + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_ALPHA; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + cb.blendConstants[0] = 1.0f; + cb.blendConstants[1] = 1.0f; + cb.blendConstants[2] = 1.0f; + cb.blendConstants[3] = 1.0f; + + switch (blend) { + default: + case GPU_BLEND_ALPHA: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + break; + + case GPU_BLEND_ALPHA_PREMULT: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + break; + + case GPU_BLEND_ADDITIVE: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + break; + + case GPU_BLEND_SUBTRACT: + case GPU_BLEND_ADDITIVE_PREMULT: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + break; + + case GPU_BLEND_MULTIPLY: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_DST_COLOR; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + break; + + case GPU_BLEND_INVERT: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + break; + + case GPU_BLEND_OIT: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + break; + + case GPU_BLEND_BACKGROUND: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + break; + + case GPU_BLEND_ALPHA_UNDER_PREMUL: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + break; + + case GPU_BLEND_CUSTOM: + att_state.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstColorBlendFactor = VK_BLEND_FACTOR_SRC1_COLOR; + att_state.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + att_state.dstAlphaBlendFactor = VK_BLEND_FACTOR_SRC1_ALPHA; + break; + } + + if (blend == GPU_BLEND_SUBTRACT) { + att_state.alphaBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT; + att_state.colorBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT; + } + else { + att_state.alphaBlendOp = VK_BLEND_OP_ADD; + att_state.colorBlendOp = VK_BLEND_OP_ADD; + } + + if (blend != GPU_BLEND_NONE) { + att_state.blendEnable = VK_TRUE; + } + else { + att_state.blendEnable = VK_FALSE; + } +} + +void VKPipelineStateManager::set_write_mask(const eGPUWriteMask write_mask) +{ + depth_stencil_state.depthWriteEnable = (write_mask & GPU_WRITE_DEPTH) ? VK_TRUE : VK_FALSE; + + VkPipelineColorBlendAttachmentState &att_state = color_blend_attachments.last(); + att_state.colorWriteMask = 0; + + if ((write_mask & GPU_WRITE_RED) != 0) { + att_state.colorWriteMask |= VK_COLOR_COMPONENT_R_BIT; + } + if ((write_mask & GPU_WRITE_GREEN) != 0) { + att_state.colorWriteMask |= VK_COLOR_COMPONENT_G_BIT; + } + if ((write_mask & GPU_WRITE_BLUE) != 0) { + att_state.colorWriteMask |= VK_COLOR_COMPONENT_B_BIT; + } + if ((write_mask & GPU_WRITE_ALPHA) != 0) { + att_state.colorWriteMask |= VK_COLOR_COMPONENT_A_BIT; + } +} + +void VKPipelineStateManager::set_depth_test(const eGPUDepthTest value) +{ + switch (value) { + case GPU_DEPTH_LESS: + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS; + break; + case GPU_DEPTH_LESS_EQUAL: + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + break; + case GPU_DEPTH_EQUAL: + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_EQUAL; + break; + case GPU_DEPTH_GREATER: + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER; + break; + case GPU_DEPTH_GREATER_EQUAL: + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL; + break; + case GPU_DEPTH_ALWAYS: + default: + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_ALWAYS; + break; + } + + if (value != GPU_DEPTH_NONE) { + depth_stencil_state.depthTestEnable = VK_TRUE; + } + else { + depth_stencil_state.depthTestEnable = VK_FALSE; + depth_stencil_state.depthCompareOp = VK_COMPARE_OP_NEVER; + } + + depth_stencil_state.depthBoundsTestEnable = VK_TRUE; +} + +void VKPipelineStateManager::set_stencil_test(const eGPUStencilTest test, + const eGPUStencilOp operation) +{ + depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + depth_stencil_state.front.compareMask = 0; + depth_stencil_state.front.reference = 0; + + switch (operation) { + case GPU_STENCIL_OP_REPLACE: + depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_REPLACE; + depth_stencil_state.back = depth_stencil_state.front; + break; + + case GPU_STENCIL_OP_COUNT_DEPTH_PASS: + depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP; + depth_stencil_state.back = depth_stencil_state.front; + depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_INCREMENT_AND_WRAP; + break; + + case GPU_STENCIL_OP_COUNT_DEPTH_FAIL: + depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.passOp = VK_STENCIL_OP_INCREMENT_AND_WRAP; + depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.back = depth_stencil_state.front; + depth_stencil_state.back.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP; + break; + + case GPU_STENCIL_OP_NONE: + default: + depth_stencil_state.front.failOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.passOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.front.depthFailOp = VK_STENCIL_OP_KEEP; + depth_stencil_state.back = depth_stencil_state.front; + break; + } + + if (test != GPU_STENCIL_NONE) { + depth_stencil_state.stencilTestEnable = VK_TRUE; + } + else { + depth_stencil_state.stencilTestEnable = VK_FALSE; + } +} + +void VKPipelineStateManager::set_stencil_mask(const eGPUStencilTest test, + const GPUStateMutable &mutable_state) +{ + depth_stencil_state.front.writeMask = static_cast(mutable_state.stencil_write_mask); + depth_stencil_state.front.reference = static_cast(mutable_state.stencil_reference); + + depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + depth_stencil_state.front.compareMask = static_cast( + mutable_state.stencil_compare_mask); + + switch (test) { + case GPU_STENCIL_NEQUAL: + depth_stencil_state.front.compareOp = VK_COMPARE_OP_NOT_EQUAL; + break; + case GPU_STENCIL_EQUAL: + depth_stencil_state.front.compareOp = VK_COMPARE_OP_EQUAL; + break; + case GPU_STENCIL_ALWAYS: + depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + break; + case GPU_STENCIL_NONE: + default: + depth_stencil_state.front.compareMask = 0x00; + depth_stencil_state.front.compareOp = VK_COMPARE_OP_ALWAYS; + return; + } + + depth_stencil_state.back = depth_stencil_state.front; +} + +void VKPipelineStateManager::set_clip_distances(const int /*new_dist_len*/, + const int /*old_dist_len*/) +{ + /* TODO: needs to be implemented. */ +} + +void VKPipelineStateManager::set_logic_op(const bool enable) +{ + if (enable) { + pipeline_color_blend_state.logicOpEnable = VK_TRUE; + pipeline_color_blend_state.logicOp = VK_LOGIC_OP_XOR; + } + else { + pipeline_color_blend_state.logicOpEnable = VK_FALSE; + } +} + +void VKPipelineStateManager::set_facing(const bool invert) +{ + rasterization_state.frontFace = invert ? VK_FRONT_FACE_COUNTER_CLOCKWISE : + VK_FRONT_FACE_CLOCKWISE; +} + +void VKPipelineStateManager::set_backface_culling(const eGPUFaceCullTest cull_test) +{ + rasterization_state.cullMode = to_vk_cull_mode_flags(cull_test); +} + +void VKPipelineStateManager::set_provoking_vert(const eGPUProvokingVertex /*vert*/) +{ + /* TODO: Requires VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME, See: + * https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineRasterizationProvokingVertexStateCreateInfoEXT.html + */ +} + +void VKPipelineStateManager::set_shadow_bias(const bool enable) +{ + if (enable) { + rasterization_state.depthBiasEnable = VK_TRUE; + rasterization_state.depthBiasSlopeFactor = 2.f; + rasterization_state.depthBiasConstantFactor = 1.f; + rasterization_state.depthBiasClamp = 0.f; + } + else { + rasterization_state.depthBiasEnable = VK_FALSE; + } +} + +} // namespace blender::gpu \ No newline at end of file diff --git a/source/blender/gpu/vulkan/vk_pipeline_state.hh b/source/blender/gpu/vulkan/vk_pipeline_state.hh new file mode 100644 index 00000000000..06bc5069978 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_pipeline_state.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation */ + +/** \file + * \ingroup gpu + */ + +#include "gpu_state_private.hh" + +#include "vk_common.hh" + +#include "BLI_vector.hh" + +namespace blender::gpu { + +class VKPipelineStateManager { + private: + GPUState current_; + GPUStateMutable current_mutable_; + + public: + VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state; + Vector color_blend_attachments; + VkPipelineRasterizationStateCreateInfo rasterization_state; + VkPipelineDepthStencilStateCreateInfo depth_stencil_state; + + VKPipelineStateManager(); + + void set_state(const GPUState &state, const GPUStateMutable &mutable_state); + void force_state(const GPUState &state, const GPUStateMutable &mutable_state); + + private: + void set_blend(eGPUBlend blend); + void set_write_mask(eGPUWriteMask write_mask); + void set_depth_test(eGPUDepthTest value); + void set_stencil_test(eGPUStencilTest test, eGPUStencilOp operation); + void set_stencil_mask(eGPUStencilTest test, const GPUStateMutable &mutable_state); + void set_clip_distances(int new_dist_len, int old_dist_len); + void set_logic_op(bool enable); + void set_facing(bool invert); + void set_backface_culling(eGPUFaceCullTest test); + void set_provoking_vert(eGPUProvokingVertex vert); + void set_shadow_bias(bool enable); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh index ade53edf9b7..eee268d8b44 100644 --- a/source/blender/gpu/vulkan/vk_shader.hh +++ b/source/blender/gpu/vulkan/vk_shader.hh @@ -96,6 +96,11 @@ class VKShader : public Shader { } }; +static inline VKShader &unwrap(Shader &shader) +{ + return static_cast(shader); +} + static inline VKShader *unwrap(Shader *shader) { return static_cast(shader); diff --git a/source/blender/gpu/vulkan/vk_state_manager.cc b/source/blender/gpu/vulkan/vk_state_manager.cc index 3c284e07a61..ecfe5cba78a 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.cc +++ b/source/blender/gpu/vulkan/vk_state_manager.cc @@ -6,12 +6,28 @@ */ #include "vk_state_manager.hh" +#include "vk_context.hh" +#include "vk_pipeline.hh" +#include "vk_shader.hh" #include "vk_texture.hh" namespace blender::gpu { -void VKStateManager::apply_state() {} -void VKStateManager::force_state() {} +void VKStateManager::apply_state() +{ + VKContext &context = *VKContext::get(); + VKShader &shader = unwrap(*context.shader); + VKPipeline &pipeline = shader.pipeline_get(); + pipeline.state_manager_get().set_state(state, mutable_state); +} + +void VKStateManager::force_state() +{ + VKContext &context = *VKContext::get(); + VKShader &shader = unwrap(*context.shader); + VKPipeline &pipeline = shader.pipeline_get(); + pipeline.state_manager_get().force_state(state, mutable_state); +} void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/) { diff --git a/source/blender/gpu/vulkan/vk_state_manager.hh b/source/blender/gpu/vulkan/vk_state_manager.hh index f6aae2db04f..8367a511436 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.hh +++ b/source/blender/gpu/vulkan/vk_state_manager.hh @@ -9,6 +9,10 @@ #include "gpu_state_private.hh" +#include "vk_common.hh" + +#include "BLI_vector.hh" + namespace blender::gpu { class VKStateManager : public StateManager { public: @@ -27,4 +31,5 @@ class VKStateManager : public StateManager { void texture_unpack_row_length_set(uint len) override; }; + } // namespace blender::gpu -- 2.30.2 From 3100807739a677b125f5475fc9629c3c1f984e4e Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 12:14:34 +0200 Subject: [PATCH 07/19] Revert unneeded change. --- source/blender/draw/intern/draw_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 5ce442a57d6..07d245e7dfe 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -48,7 +48,7 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); /* draw_hair.cc */ /** - * This creates a shading group with disW_b + * This creates a shading group with display hairs. * The draw call is already added by this function, just add additional uniforms. */ struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, -- 2.30.2 From 812217fb6779f1425d3e566984b89114f5c282dd Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 12:51:21 +0200 Subject: [PATCH 08/19] Remove unneeded changes. --- source/blender/gpu/vulkan/vk_state_manager.hh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_state_manager.hh b/source/blender/gpu/vulkan/vk_state_manager.hh index 8367a511436..f6aae2db04f 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.hh +++ b/source/blender/gpu/vulkan/vk_state_manager.hh @@ -9,10 +9,6 @@ #include "gpu_state_private.hh" -#include "vk_common.hh" - -#include "BLI_vector.hh" - namespace blender::gpu { class VKStateManager : public StateManager { public: @@ -31,5 +27,4 @@ class VKStateManager : public StateManager { void texture_unpack_row_length_set(uint len) override; }; - } // namespace blender::gpu -- 2.30.2 From 21b49096d6d317d29da8417c353aebd2e93e87a0 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 14:14:02 +0200 Subject: [PATCH 09/19] Fix issues with vertex attribute objects. --- source/blender/gpu/vulkan/vk_pipeline.cc | 6 +-- .../gpu/vulkan/vk_vertex_attribute_object.cc | 39 ++++++++++--------- .../gpu/vulkan/vk_vertex_attribute_object.hh | 3 +- .../vk_vertex_attribute_object_cache.cc | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index 0c007a61673..39fcf7fa36c 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -140,11 +140,9 @@ void VKPipeline::finalize(VKContext &context, VkPipelineVertexInputStateCreateInfo vertex_input_state = {}; vertex_input_state.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_state.vertexBindingDescriptionCount = 0; - vertex_input_state.vertexBindingDescriptionCount = - 1; // vertex_attribute_object.bindings.size(); + vertex_input_state.vertexBindingDescriptionCount = vertex_attribute_object.bindings.size(); vertex_input_state.pVertexBindingDescriptions = vertex_attribute_object.bindings.data(); - vertex_input_state.vertexAttributeDescriptionCount = - 1; // vertex_attribute_object.attributes.size(); + vertex_input_state.vertexAttributeDescriptionCount = vertex_attribute_object.attributes.size(); vertex_input_state.pVertexAttributeDescriptions = vertex_attribute_object.attributes.data(); pipeline_create_info.pVertexInputState = &vertex_input_state; diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc index 22441621e31..5ae3676e928 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc @@ -75,35 +75,34 @@ void VKVertexAttributeObject::bind(VKContext &context) void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch) { const VKShaderInterface &interface = unwrap(context.shader)->interface_get(); + AttributeMask occupied_attributes = 0; - /* Reverse order so first VBO'S have more prevalence (in term of attribute override). */ - AttributeMask attribute_mask = interface.enabled_attr_mask_; - - for (int v = GPU_BATCH_VBO_MAX_LEN - 1; v > -1; v--) { + for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { VKVertexBuffer *vbo = batch.vertex_buffer_get(v); if (vbo) { - attribute_mask &= ~update_bindings(*vbo, interface, false); + update_bindings(*vbo, interface, occupied_attributes, false); } } - for (int v = GPU_BATCH_INST_VBO_MAX_LEN - 1; v > -1; v--) { + for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) { VKVertexBuffer *vbo = batch.instance_buffer_get(v); if (vbo) { - attribute_mask &= ~update_bindings(*vbo, interface, true); + update_bindings(*vbo, interface, occupied_attributes, false); } } + + BLI_assert(interface.enabled_attr_mask_ == occupied_attributes); } -AttributeMask VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, - const VKShaderInterface &interface, - const bool use_instancing) +void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, + const VKShaderInterface &interface, + AttributeMask &r_occupied_attributes, + const bool use_instancing) { - - uint enabled_attributes = 0; const GPUVertFormat &format = vertex_buffer.format; if (format.attr_len <= 0) { - return enabled_attributes; + return; } uint32_t offset = 0; @@ -129,26 +128,28 @@ AttributeMask VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_bu bindings.append(vk_binding_descriptor); vbos.append(&vertex_buffer); - for (uint32_t name_index = 0; name_index <= attribute.name_len; name_index++) { + for (uint32_t name_index = 0; name_index < attribute.name_len; name_index++) { const char *name = GPU_vertformat_attr_name_get(&format, &attribute, name_index); const ShaderInput *shader_input = interface.attr_get(name); if (shader_input == nullptr || shader_input->location == -1) { continue; } + /* Don't overwrite attributes that are already occupied. */ + AttributeMask attribute_mask = 1 << shader_input->location; + if (r_occupied_attributes & attribute_mask) { + continue; + } + r_occupied_attributes |= attribute_mask; + VkVertexInputAttributeDescription attribute_description = {}; - enabled_attributes |= (1 << shader_input->location); attribute_description.binding = binding; attribute_description.location = shader_input->location; attribute_description.format = to_vk_format( static_cast(attribute.comp_type), attribute.size); attributes.append(attribute_description); } - - // TODO: resolving conflicting location numbers. } - - return enabled_attributes; } } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh index d62be394690..e6d9ce26226 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh @@ -40,8 +40,9 @@ struct VKVertexAttributeObject { void update_bindings(const VKContext &context, VKBatch &batch); private: - AttributeMask update_bindings(VKVertexBuffer &vertex_buffer, + void update_bindings(VKVertexBuffer &vertex_buffer, const VKShaderInterface &interface, + AttributeMask &r_occupied_attributes, const bool use_instancing); }; diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc index b48726c4304..a753a4f9750 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object_cache.cc @@ -169,7 +169,7 @@ VKVertexAttributeObject &VKVaoCache::lookup(const VKShaderInterface *interface) } } -VKVertexAttributeObject &VKVaoCache::vao_get(GPUBatch *batch) +VKVertexAttributeObject &VKVaoCache::vao_get(GPUBatch * /*batch*/) { Shader *shader = VKContext::get()->shader; VKShaderInterface *interface = static_cast(shader->interface); -- 2.30.2 From 560ba241feeabe67bc585950bba441ce67f216b9 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 7 Apr 2023 15:01:36 +0200 Subject: [PATCH 10/19] Fix overbind vbos. --- source/blender/gpu/vulkan/vk_batch.cc | 1 - source/blender/gpu/vulkan/vk_pipeline.cc | 12 +---- .../gpu/vulkan/vk_vertex_attribute_object.cc | 49 +++++++++---------- 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index ff92dca44b2..bd6a0749b3d 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -20,7 +20,6 @@ void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) flag &= ~GPU_BATCH_DIRTY; } - // Based on the context construct the pipeline. VKContext &context = *VKContext::get(); context.state_manager->apply_state(); VKVertexAttributeObject &vao = vao_cache_.vao_get(this); diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index 39fcf7fa36c..8e3cfc486f8 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -108,15 +108,7 @@ void VKPipeline::finalize(VKContext &context, vertex_stage_info.module = vertex_module; vertex_stage_info.pName = "main"; pipeline_stages.append(vertex_stage_info); - /* - if (geometry_module_ != VK_NULL_HANDLE) { - VkPipelineShaderStageCreateInfo geo_stage_info = {}; - geo_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - geo_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT; - geo_stage_info.module = geometry_module_; - geo_stage_info.pName = "main"; - pipeline_stages.append(geo_stage_info); - }*/ + if (fragment_module != VK_NULL_HANDLE) { VkPipelineShaderStageCreateInfo fragment_stage_info = {}; fragment_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -170,7 +162,7 @@ void VKPipeline::finalize(VKContext &context, multisample_state.minSampleShading = 1.0f; pipeline_create_info.pMultisampleState = &multisample_state; - /* Color blend state. */ + /* States from the state manager. */ const VKPipelineStateManager &state_manager = state_manager_get(); pipeline_create_info.pColorBlendState = &state_manager.pipeline_color_blend_state; pipeline_create_info.pRasterizationState = &state_manager.rasterization_state; diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc index 5ae3676e928..4e72c25aa67 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc @@ -32,26 +32,14 @@ VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttrib return *this; } - /* TODO: resize is an exact resize. Might introduce unneeded re-allocations. */ - /* TODO: This might copy data, that isn't needed. */ - is_valid = other.is_valid; info = other.info; - bindings.resize(other.bindings.size()); - int i = 0; - for (const VkVertexInputBindingDescription &e : other.bindings) { - bindings[i++] = e; - } - i = 0; - attributes.resize(other.attributes.size()); - for (const VkVertexInputAttributeDescription e : other.attributes) { - attributes[i++] = e; - } - i = 0; - vbos.resize(other.vbos.size()); - for (VKVertexBuffer *e : other.vbos) { - vbos[i++] = e; - } + bindings.clear(); + bindings.extend(other.bindings); + attributes.clear(); + attributes.extend(other.attributes); + vbos.clear(); + vbos.extend(other.vbos); return *this; } @@ -74,6 +62,7 @@ void VKVertexAttributeObject::bind(VKContext &context) void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch) { + clear(); const VKShaderInterface &interface = unwrap(context.shader)->interface_get(); AttributeMask occupied_attributes = 0; @@ -91,6 +80,8 @@ void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch } } + is_valid = true; + BLI_assert(interface.enabled_attr_mask_ == occupied_attributes); } @@ -107,7 +98,6 @@ void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, uint32_t offset = 0; uint32_t stride = format.stride; - const uint32_t binding = bindings.size(); for (uint32_t attribute_index = 0; attribute_index < format.attr_len; attribute_index++) { const GPUVertAttr &attribute = format.attrs[attribute_index]; @@ -120,14 +110,9 @@ void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, offset = attribute.offset; } - VkVertexInputBindingDescription vk_binding_descriptor = {}; - vk_binding_descriptor.binding = binding; - vk_binding_descriptor.stride = stride; - vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE : - VK_VERTEX_INPUT_RATE_VERTEX; - bindings.append(vk_binding_descriptor); - vbos.append(&vertex_buffer); + const uint32_t binding = bindings.size(); + bool attribute_used_by_shader = false; for (uint32_t name_index = 0; name_index < attribute.name_len; name_index++) { const char *name = GPU_vertformat_attr_name_get(&format, &attribute, name_index); const ShaderInput *shader_input = interface.attr_get(name); @@ -141,14 +126,26 @@ void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, continue; } r_occupied_attributes |= attribute_mask; + attribute_used_by_shader = true; VkVertexInputAttributeDescription attribute_description = {}; attribute_description.binding = binding; attribute_description.location = shader_input->location; + attribute_description.offset = offset; attribute_description.format = to_vk_format( static_cast(attribute.comp_type), attribute.size); attributes.append(attribute_description); } + + if (attribute_used_by_shader) { + VkVertexInputBindingDescription vk_binding_descriptor = {}; + vk_binding_descriptor.binding = binding; + vk_binding_descriptor.stride = stride; + vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE : + VK_VERTEX_INPUT_RATE_VERTEX; + bindings.append(vk_binding_descriptor); + vbos.append(&vertex_buffer); + } } } -- 2.30.2 From c9a1be0fc9398a4962b6095a8defd217ee81dcda Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 13 Apr 2023 12:24:18 +0200 Subject: [PATCH 11/19] Temp commit as changes to GPU api are needed to proceed. --- source/blender/gpu/CMakeLists.txt | 1 + source/blender/gpu/intern/gpu_framebuffer.cc | 3 +- source/blender/gpu/tests/offscreen_test.cc | 44 ++++++++++++++ source/blender/gpu/tests/state_blend_test.cc | 62 ++++++++++++++++++++ source/blender/gpu/vulkan/vk_framebuffer.cc | 40 +++++++++++-- 5 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 source/blender/gpu/tests/state_blend_test.cc diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index e22c5de29c3..b18b90493da 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -833,6 +833,7 @@ if(WITH_GTESTS) set(TEST_SRC tests/gpu_testing.cc + tests/state_blend_test.cc tests/framebuffer_test.cc tests/index_buffer_test.cc tests/offscreen_test.cc diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 4720941e1f2..89c65d0dec7 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -662,7 +662,8 @@ GPUOffScreen *GPU_offscreen_create( height = max_ii(1, height); width = max_ii(1, width); - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT; + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | + GPU_TEXTURE_USAGE_HOST_READ; ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, usage, nullptr); if (depth) { diff --git a/source/blender/gpu/tests/offscreen_test.cc b/source/blender/gpu/tests/offscreen_test.cc index 706b6534cf8..78b1caecaf7 100644 --- a/source/blender/gpu/tests/offscreen_test.cc +++ b/source/blender/gpu/tests/offscreen_test.cc @@ -1,3 +1,5 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + #include "testing/testing.h" #include "gpu_testing.hh" @@ -19,6 +21,11 @@ static void test_offscreen_draw_batch_quad() GPUOffScreen *offscreen = GPU_offscreen_create(Size, Size, false, GPU_RGBA16F, nullptr); BLI_assert(offscreen != nullptr); GPU_offscreen_bind(offscreen, false); + /* + GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen); + float4 clear_color(0.0f); + GPU_texture_clear(color_texture, GPU_DATA_FLOAT, clear_color); + */ GPUBatch *batch = DRW_cache_quad_get(); float4 color(1.0f, 0.5f, 0.0f, 1.0f); @@ -66,4 +73,41 @@ static void test_offscreen_draw_batch_sphere() DRW_shape_cache_free(); } GPU_TEST(offscreen_draw_batch_sphere); + +template +void offscreen_blending_test(float4 source_a, float4 source_b, float4 expected_result) +{ + GPUOffScreen *offscreen = GPU_offscreen_create(1, 1, false, GPU_RGBA16F, nullptr); + BLI_assert(offscreen != nullptr); + GPU_offscreen_bind(offscreen, false); + GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen); + GPU_texture_clear(color_texture, GPU_DATA_FLOAT, source_a); + + GPUBatch *batch = DRW_cache_quad_get(); + + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(batch, "color", source_b); + GPU_blend(blend_type); + + GPU_batch_draw(batch); + GPU_offscreen_unbind(offscreen, false); + GPU_flush(); + + float4 read_back; + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, &read_back); + EXPECT_EQ(read_back, expected_result); + + GPU_offscreen_free(offscreen); + DRW_shape_cache_free(); +} + +static void test_blending_none() +{ + offscreen_blending_test(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)); +} +GPU_TEST(blending_none) + } // namespace blender::gpu::tests \ No newline at end of file diff --git a/source/blender/gpu/tests/state_blend_test.cc b/source/blender/gpu/tests/state_blend_test.cc new file mode 100644 index 00000000000..411b7f3a6c3 --- /dev/null +++ b/source/blender/gpu/tests/state_blend_test.cc @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "gpu_testing.hh" + +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_framebuffer.h" +#include "GPU_matrix.h" + +#include "BLI_math_vector.hh" + +#include "intern/draw_cache.h" + +namespace blender::gpu::tests { + +template +void blend_test(float4 source_a, float4 source_b, float4 expected_result) +{ + GPUOffScreen *offscreen = GPU_offscreen_create(1, 1, false, GPU_RGBA16F, nullptr); + BLI_assert(offscreen != nullptr); + GPU_offscreen_bind(offscreen, false); + GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen); + GPU_texture_clear(color_texture, GPU_DATA_FLOAT, source_a); + + GPUBatch *batch = DRW_cache_quad_get(); + + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(batch, "color", source_b); + GPU_blend(blend_type); + + GPU_batch_draw(batch); + GPU_offscreen_unbind(offscreen, false); + GPU_flush(); + + float4 read_back; + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, &read_back); + EXPECT_EQ(read_back, expected_result); + + GPU_offscreen_free(offscreen); + DRW_shape_cache_free(); +} + +static void test_blend_none() +{ + blend_test(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)); +} +GPU_TEST(blend_none) + +static void test_blend_alpha() +{ + blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(0.5f, 0.5f, 0.5f, 0.5f)); +} +GPU_TEST(blend_alpha) + +} // namespace blender::gpu::tests diff --git a/source/blender/gpu/vulkan/vk_framebuffer.cc b/source/blender/gpu/vulkan/vk_framebuffer.cc index 662fa316d07..7a44a12c25d 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.cc +++ b/source/blender/gpu/vulkan/vk_framebuffer.cc @@ -210,13 +210,43 @@ void VKFrameBuffer::attachment_set_loadstore_op(GPUAttachmentType /*type*/, /** \name Read back * \{ */ -void VKFrameBuffer::read(eGPUFrameBufferBits /*planes*/, - eGPUDataFormat /*format*/, +void VKFrameBuffer::read(eGPUFrameBufferBits plane, + eGPUDataFormat format, const int /*area*/[4], - int /*channel_len*/, - int /*slot*/, - void * /*r_data*/) + int channel_len, + int slot, + void *r_data) { + VKTexture *texture = nullptr; + switch (plane) { + case GPU_COLOR_BIT: + texture = unwrap(unwrap(attachments_[GPU_FB_COLOR_ATTACHMENT0 + slot].tex)); + break; + + default: + BLI_assert_unreachable(); + return; + } + + BLI_assert_msg(texture, + "Trying to read back color texture from framebuffer, but no color texture is " + "available in requested slot."); + void *data = texture->read(0, format); + + /* + * TODO: + * - Add support for area. + * - Add support for channel_len. + * Best option would be to add this to a specific interface in VKTexture so we don't + * over-allocate and reduce number of times copies are made. + */ + BLI_assert(format == GPU_DATA_FLOAT); + BLI_assert(channel_len == 4); + int mip_size[3] = {1, 1, 1}; + texture->mip_size_get(0, mip_size); + const size_t mem_size = mip_size[0] * mip_size[1] * mip_size[2] * sizeof(float) * channel_len; + memcpy(r_data, data, mem_size); + MEM_freeN(data); } /** \} */ -- 2.30.2 From 29cc37cf225f12efbd4da053b66bfcc1cc1e91bf Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 13 Apr 2023 12:33:50 +0200 Subject: [PATCH 12/19] GPU: Add texture usage parameter to GPUOffscreen. Currently the Textures used for offscreen rendering don't have the `GPU_TEXTURE_USAGE_HOST_READ` flag. But some cases it is needed. This PR adds a parameter when creating an offscreen buffer. Other solution could be to add this flag to all textures, but we chose not to do this as that reduces the amount of fine-tuning options for Metal/Vulkan backends. GPU can store textures differently based on its actual usage. This option isn't available in the python API as we don't expect add-on developers to fine-tune texture usages to this extent. --- source/blender/editors/gpencil/gpencil_fill.c | 9 +++++-- .../blender/editors/render/render_opengl.cc | 8 +++++- source/blender/editors/screen/screen_draw.c | 8 +++++- .../editors/space_view3d/view3d_draw.cc | 8 +++++- source/blender/gpu/GPU_framebuffer.h | 8 ++++-- source/blender/gpu/intern/gpu_framebuffer.cc | 9 ++++--- source/blender/python/gpu/gpu_py_offscreen.c | 8 +++++- source/blender/windowmanager/intern/wm_draw.c | 25 ++++++++++++++++--- .../windowmanager/xr/intern/wm_xr_session.c | 9 +++++-- 9 files changed, 75 insertions(+), 17 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 397f4d7c924..7db1a4e1686 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1234,8 +1234,13 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) tgpf->sizey = (int)tgpf->region->winy; char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create( - tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, err_out); + GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, + tgpf->sizey, + true, + GPU_RGBA8, + GPU_TEXTURE_USAGE_ATTACHMENT | + GPU_TEXTURE_USAGE_HOST_READ, + err_out); if (offscreen == NULL) { printf("GPencil - Fill - Unable to create fill buffer\n"); return false; diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 1dcbfd26535..702e660dfbf 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -757,7 +757,13 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) /* corrects render size with actual size, not every card supports non-power-of-two dimensions */ DRW_opengl_context_enable(); /* Off-screen creation needs to be done in DRW context. */ - ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA16F, err_out); + ofs = GPU_offscreen_create(sizex, + sizey, + true, + GPU_RGBA16F, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | + GPU_TEXTURE_USAGE_HOST_READ, + err_out); DRW_opengl_context_disable(); if (!ofs) { diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 384aeccfc4c..c1ea6a74e35 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -425,7 +425,13 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y) void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect) { char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, GPU_RGBA8, err_out); + GPUOffScreen *offscreen = GPU_offscreen_create( + size_x, + size_y, + true, + GPU_RGBA8, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ, + err_out); GPU_offscreen_bind(offscreen, true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc index 9755599e989..9aeffe097c1 100644 --- a/source/blender/editors/space_view3d/view3d_draw.cc +++ b/source/blender/editors/space_view3d/view3d_draw.cc @@ -1886,7 +1886,13 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, if (own_ofs) { /* bind */ - ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA8, err_out); + ofs = GPU_offscreen_create(sizex, + sizey, + true, + GPU_RGBA8, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | + GPU_TEXTURE_USAGE_HOST_READ, + err_out); if (ofs == nullptr) { DRW_opengl_context_disable(); return nullptr; diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index f40144ef8a8..76187f5b049 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -589,8 +589,12 @@ typedef struct GPUOffScreen GPUOffScreen; * If \a err_out is not `nullptr` it will be use to write any configuration error message.. * \note This function binds the framebuffer to the active context. */ -GPUOffScreen *GPU_offscreen_create( - int width, int height, bool with_depth_buffer, eGPUTextureFormat format, char err_out[256]); +GPUOffScreen *GPU_offscreen_create(int width, + int height, + bool with_depth_buffer, + eGPUTextureFormat format, + eGPUTextureUsage usage, + char err_out[256]); /** * Free a #GPUOffScreen. diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 4720941e1f2..64c07b4c4aa 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -652,8 +652,12 @@ static GPUFrameBuffer *gpu_offscreen_fb_get(GPUOffScreen *ofs) return gpu_offscreen_fb_get(ofs); } -GPUOffScreen *GPU_offscreen_create( - int width, int height, bool depth, eGPUTextureFormat format, char err_out[256]) +GPUOffScreen *GPU_offscreen_create(int width, + int height, + bool depth, + eGPUTextureFormat format, + eGPUTextureUsage usage, + char err_out[256]) { GPUOffScreen *ofs = MEM_cnew(__func__); @@ -662,7 +666,6 @@ GPUOffScreen *GPU_offscreen_create( height = max_ii(1, height); width = max_ii(1, width); - eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT; ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, usage, nullptr); if (depth) { diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 6e50a7f323d..7fb7e076761 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -232,7 +232,13 @@ static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self), } if (GPU_context_active_get()) { - ofs = GPU_offscreen_create(width, height, true, pygpu_textureformat.value_found, err_out); + ofs = GPU_offscreen_create(width, + height, + true, + pygpu_textureformat.value_found, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | + GPU_TEXTURE_USAGE_HOST_READ, + err_out); } else { STRNCPY(err_out, "No active GPU context found"); diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 1f8b434d08d..03faed2e3f7 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -684,8 +684,13 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_ /* Allocate offscreen buffer if it does not exist. This one has no * depth or multisample buffers. 3D view creates own buffers with * the data it needs. */ - GPUOffScreen *offscreen = GPU_offscreen_create( - region->winx, region->winy, false, GPU_RGBA8, NULL); + GPUOffScreen *offscreen = GPU_offscreen_create(region->winx, + region->winy, + false, + GPU_RGBA8, + GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_ATTACHMENT, + NULL); if (!offscreen) { WM_report(RPT_ERROR, "Region could not be drawn!"); return; @@ -1151,7 +1156,13 @@ static void wm_draw_window(bContext *C, wmWindow *win) * stereo methods, but it's less efficient than drawing directly. */ const int width = WM_window_pixels_x(win); const int height = WM_window_pixels_y(win); - GPUOffScreen *offscreen = GPU_offscreen_create(width, height, false, GPU_RGBA8, NULL); + GPUOffScreen *offscreen = GPU_offscreen_create(width, + height, + false, + GPU_RGBA8, + GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_ATTACHMENT, + NULL); if (offscreen) { GPUTexture *texture = GPU_offscreen_color_texture(offscreen); @@ -1226,7 +1237,13 @@ uint *WM_window_pixels_read_offscreen(bContext *C, wmWindow *win, int r_size[2]) r_size[0] = WM_window_pixels_x(win); r_size[1] = WM_window_pixels_y(win); - GPUOffScreen *offscreen = GPU_offscreen_create(r_size[0], r_size[1], false, GPU_RGBA8, NULL); + GPUOffScreen *offscreen = GPU_offscreen_create(r_size[0], + r_size[1], + false, + GPU_RGBA8, + GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_ATTACHMENT, + NULL); if (UNLIKELY(!offscreen)) { return NULL; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index aefc3afff66..506533851c0 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1387,8 +1387,13 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, } BLI_assert(format != GPU_R8); - offscreen = vp->offscreen = GPU_offscreen_create( - draw_view->width, draw_view->height, true, format, err_out); + offscreen = vp->offscreen = GPU_offscreen_create(draw_view->width, + draw_view->height, + true, + format, + GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_ATTACHMENT, + err_out); if (offscreen) { viewport = vp->viewport = GPU_viewport_create(); if (!viewport) { -- 2.30.2 From a5f7d44115357a3e13b0f4835b8f48ddeeacb9c4 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 13 Apr 2023 15:35:07 +0200 Subject: [PATCH 13/19] Track framebuffer state in command buffer. --- source/blender/gpu/tests/state_blend_test.cc | 2 +- .../blender/gpu/vulkan/vk_command_buffer.cc | 79 +++++++++++++++++-- .../blender/gpu/vulkan/vk_command_buffer.hh | 60 ++++++++++++++ source/blender/gpu/vulkan/vk_context.cc | 18 ----- 4 files changed, 132 insertions(+), 27 deletions(-) diff --git a/source/blender/gpu/tests/state_blend_test.cc b/source/blender/gpu/tests/state_blend_test.cc index d2cbd85cc7d..b8647568a16 100644 --- a/source/blender/gpu/tests/state_blend_test.cc +++ b/source/blender/gpu/tests/state_blend_test.cc @@ -61,7 +61,7 @@ static void test_blend_alpha() { blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), 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) diff --git a/source/blender/gpu/vulkan/vk_command_buffer.cc b/source/blender/gpu/vulkan/vk_command_buffer.cc index 02d17d285c9..675ba3a0002 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.cc +++ b/source/blender/gpu/vulkan/vk_command_buffer.cc @@ -21,6 +21,7 @@ namespace blender::gpu { VKCommandBuffer::~VKCommandBuffer() { + validate_framebuffer_not_exists(); if (vk_device_ != VK_NULL_HANDLE) { 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; vkCreateFence(vk_device_, &fenceInfo, vk_allocation_callbacks, &vk_fence_); } + validate_framebuffer_not_exists(); } void VKCommandBuffer::begin_recording() @@ -80,29 +82,33 @@ void VKCommandBuffer::bind(const uint32_t binding, const VKVertexBuffer &vertex_buffer, const VkDeviceSize offset) { + validate_framebuffer_exists(); + ensure_active_framebuffer(); VkBuffer vk_buffer = vertex_buffer.vk_handle(); vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_buffer, &offset); } void VKCommandBuffer::bind(const VKIndexBuffer &index_buffer, VkIndexType index_type) { + validate_framebuffer_exists(); + ensure_active_framebuffer(); VkBuffer vk_buffer = index_buffer.vk_handle(); vkCmdBindIndexBuffer(vk_command_buffer_, vk_buffer, 0, index_type); } void VKCommandBuffer::begin_render_pass(const VKFrameBuffer &framebuffer) { - VkRenderPassBeginInfo render_pass_begin_info = {}; - render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - 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); + validate_framebuffer_not_exists(); + state.framebuffer_ = &framebuffer; } -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, @@ -121,6 +127,7 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants, 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); } @@ -128,6 +135,7 @@ void VKCommandBuffer::copy(VKBuffer &dst_buffer, VKTexture &src_texture, Span regions) { + ensure_no_active_framebuffer(); vkCmdCopyImageToBuffer(vk_command_buffer_, src_texture.vk_image_handle(), src_texture.current_layout_get(), @@ -139,6 +147,7 @@ void VKCommandBuffer::copy(VKTexture &dst_texture, VKBuffer &src_buffer, Span regions) { + ensure_no_active_framebuffer(); vkCmdCopyBufferToImage(vk_command_buffer_, src_buffer.vk_handle(), dst_texture.vk_image_handle(), @@ -152,6 +161,7 @@ void VKCommandBuffer::clear(VkImage vk_image, const VkClearColorValue &vk_clear_color, Span ranges) { + ensure_no_active_framebuffer(); vkCmdClearColorImage(vk_command_buffer_, vk_image, vk_image_layout, @@ -162,18 +172,25 @@ void VKCommandBuffer::clear(VkImage vk_image, void VKCommandBuffer::clear(Span attachments, Span areas) { + validate_framebuffer_exists(); + ensure_active_framebuffer(); vkCmdClearAttachments( 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) { + validate_framebuffer_exists(); + ensure_active_framebuffer(); vkCmdDraw(vk_command_buffer_, v_count, i_count, v_first, i_first); } void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages, VkPipelineStageFlags destination_stages) { + if (state.framebuffer_) { + ensure_active_framebuffer(); + } vkCmdPipelineBarrier(vk_command_buffer_, source_stages, destination_stages, @@ -188,6 +205,7 @@ void VKCommandBuffer::pipeline_barrier(VkPipelineStageFlags source_stages, void VKCommandBuffer::pipeline_barrier(Span image_memory_barriers) { + ensure_no_active_framebuffer(); vkCmdPipelineBarrier(vk_command_buffer_, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, @@ -202,11 +220,13 @@ void VKCommandBuffer::pipeline_barrier(Span image_memory_b 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); } void VKCommandBuffer::submit() { + ensure_no_active_framebuffer(); end_recording(); encode_recorded_commands(); submit_encoded_commands(); @@ -231,4 +251,47 @@ void VKCommandBuffer::submit_encoded_commands() 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 diff --git a/source/blender/gpu/vulkan/vk_command_buffer.hh b/source/blender/gpu/vulkan/vk_command_buffer.hh index 0efa9dc8260..da817763bda 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.hh +++ b/source/blender/gpu/vulkan/vk_command_buffer.hh @@ -33,6 +33,42 @@ class VKCommandBuffer : NonCopyable, NonMovable { VkFence vk_fence_ = VK_NULL_HANDLE; 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: virtual ~VKCommandBuffer(); void init(const VkDevice vk_device, const VkQueue vk_queue, VkCommandBuffer vk_command_buffer); @@ -94,6 +130,30 @@ class VKCommandBuffer : NonCopyable, NonMovable { 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 diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index b310219fe41..38ed36abced 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -115,30 +115,12 @@ void VKContext::end_frame() void VKContext::flush() { - VKFrameBuffer *previous_framebuffer = active_framebuffer_get(); - if (has_active_framebuffer()) { - deactivate_framebuffer(); - } - command_buffer_.submit(); - - if (previous_framebuffer != nullptr) { - activate_framebuffer(*previous_framebuffer); - } } void VKContext::finish() { - VKFrameBuffer *previous_framebuffer = active_framebuffer_get(); - if (has_active_framebuffer()) { - deactivate_framebuffer(); - } - command_buffer_.submit(); - - if (previous_framebuffer != nullptr) { - activate_framebuffer(*previous_framebuffer); - } } void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {} -- 2.30.2 From 36578ac4ee1a83afc53c80f51ed3592b66aef070 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 13 Apr 2023 15:40:21 +0200 Subject: [PATCH 14/19] Automatic add GPU_TEXTURE_USAGE_ATTACHMENT. --- source/blender/editors/gpencil/gpencil_fill.c | 9 +-- .../blender/editors/render/render_opengl.cc | 3 +- source/blender/editors/screen/screen_draw.c | 14 ++--- .../editors/space_view3d/view3d_draw.cc | 3 +- source/blender/gpu/GPU_framebuffer.h | 1 + source/blender/gpu/intern/gpu_framebuffer.cc | 3 +- source/blender/gpu/tests/state_blend_test.cc | 63 +++++++++++++++++++ source/blender/python/gpu/gpu_py_offscreen.c | 3 +- source/blender/windowmanager/intern/wm_draw.c | 27 ++------ .../windowmanager/xr/intern/wm_xr_session.c | 9 +-- 10 files changed, 86 insertions(+), 49 deletions(-) create mode 100644 source/blender/gpu/tests/state_blend_test.cc diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 7db1a4e1686..0dade3cee38 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1234,13 +1234,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) tgpf->sizey = (int)tgpf->region->winy; char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, - tgpf->sizey, - true, - GPU_RGBA8, - GPU_TEXTURE_USAGE_ATTACHMENT | - GPU_TEXTURE_USAGE_HOST_READ, - err_out); + GPUOffScreen *offscreen = GPU_offscreen_create( + tgpf->sizex, tgpf->sizey, true, GPU_RGBA8, GPU_TEXTURE_USAGE_HOST_READ, err_out); if (offscreen == NULL) { printf("GPencil - Fill - Unable to create fill buffer\n"); return false; diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 702e660dfbf..b2ce627fbe3 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -761,8 +761,7 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) sizey, true, GPU_RGBA16F, - GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | - GPU_TEXTURE_USAGE_HOST_READ, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ, err_out); DRW_opengl_context_disable(); diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index c1ea6a74e35..95b82131789 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -425,13 +425,13 @@ static void screen_preview_draw(const bScreen *screen, int size_x, int size_y) void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uint *r_rect) { char err_out[256] = "unknown"; - GPUOffScreen *offscreen = GPU_offscreen_create( - size_x, - size_y, - true, - GPU_RGBA8, - GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | GPU_TEXTURE_USAGE_HOST_READ, - err_out); + GPUOffScreen *offscreen = GPU_offscreen_create(size_x, + size_y, + true, + GPU_RGBA8, + GPU_TEXTURE_USAGE_SHADER_READ | + GPU_TEXTURE_USAGE_HOST_READ, + err_out); GPU_offscreen_bind(offscreen, true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); diff --git a/source/blender/editors/space_view3d/view3d_draw.cc b/source/blender/editors/space_view3d/view3d_draw.cc index 9aeffe097c1..57a98d1f265 100644 --- a/source/blender/editors/space_view3d/view3d_draw.cc +++ b/source/blender/editors/space_view3d/view3d_draw.cc @@ -1890,8 +1890,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph, sizey, true, GPU_RGBA8, - GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | - GPU_TEXTURE_USAGE_HOST_READ, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ, err_out); if (ofs == nullptr) { DRW_opengl_context_disable(); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 76187f5b049..f4a71e0811d 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -588,6 +588,7 @@ typedef struct GPUOffScreen GPUOffScreen; * \a format is the format of the color buffer. * If \a err_out is not `nullptr` it will be use to write any configuration error message.. * \note This function binds the framebuffer to the active context. + * \note `GPU_TEXTURE_USAGE_ATTACHMENT` is added to the usage parameter by default. */ GPUOffScreen *GPU_offscreen_create(int width, int height, diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index 64c07b4c4aa..9bdf945b45b 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -656,7 +656,7 @@ GPUOffScreen *GPU_offscreen_create(int width, int height, bool depth, eGPUTextureFormat format, - eGPUTextureUsage usage, + const eGPUTextureUsage usage_, char err_out[256]) { GPUOffScreen *ofs = MEM_cnew(__func__); @@ -666,6 +666,7 @@ GPUOffScreen *GPU_offscreen_create(int width, height = max_ii(1, height); width = max_ii(1, width); + const eGPUTextureUsage usage = usage_ | GPU_TEXTURE_USAGE_ATTACHMENT; ofs->color = GPU_texture_create_2d("ofs_color", width, height, 1, format, usage, nullptr); if (depth) { diff --git a/source/blender/gpu/tests/state_blend_test.cc b/source/blender/gpu/tests/state_blend_test.cc new file mode 100644 index 00000000000..17e99045541 --- /dev/null +++ b/source/blender/gpu/tests/state_blend_test.cc @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "gpu_testing.hh" + +#include "GPU_batch.h" +#include "GPU_batch_presets.h" +#include "GPU_framebuffer.h" +#include "GPU_matrix.h" + +#include "BLI_math_vector.hh" + +#include "intern/draw_cache.h" + +namespace blender::gpu::tests { + +template +void blend_test(float4 source_a, float4 source_b, float4 expected_result) +{ + GPUOffScreen *offscreen = GPU_offscreen_create( + 1, 1, false, GPU_RGBA16F, GPU_TEXTURE_USAGE_HOST_READ, nullptr); + BLI_assert(offscreen != nullptr); + GPU_offscreen_bind(offscreen, false); + GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen); + GPU_texture_clear(color_texture, GPU_DATA_FLOAT, source_a); + + GPUBatch *batch = DRW_cache_quad_get(); + + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(batch, "color", source_b); + GPU_blend(blend_type); + + GPU_batch_draw(batch); + GPU_offscreen_unbind(offscreen, false); + GPU_flush(); + + float4 read_back; + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, &read_back); + EXPECT_EQ(read_back, expected_result); + + GPU_offscreen_free(offscreen); + DRW_shape_cache_free(); +} + +static void test_blend_none() +{ + blend_test(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)); +} +GPU_TEST(blend_none) + +static void test_blend_alpha() +{ + blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(0.5f, 0.5f, 0.5f, 1.0f)); +} +GPU_TEST(blend_alpha) + +} // namespace blender::gpu::tests diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index 7fb7e076761..f88085edd5a 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -236,8 +236,7 @@ static PyObject *pygpu_offscreen__tp_new(PyTypeObject *UNUSED(self), height, true, pygpu_textureformat.value_found, - GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT | - GPU_TEXTURE_USAGE_HOST_READ, + GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ, err_out); } else { diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 03faed2e3f7..b63756dd886 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -684,13 +684,8 @@ static void wm_draw_region_buffer_create(ARegion *region, bool stereo, bool use_ /* Allocate offscreen buffer if it does not exist. This one has no * depth or multisample buffers. 3D view creates own buffers with * the data it needs. */ - GPUOffScreen *offscreen = GPU_offscreen_create(region->winx, - region->winy, - false, - GPU_RGBA8, - GPU_TEXTURE_USAGE_SHADER_READ | - GPU_TEXTURE_USAGE_ATTACHMENT, - NULL); + GPUOffScreen *offscreen = GPU_offscreen_create( + region->winx, region->winy, false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL); if (!offscreen) { WM_report(RPT_ERROR, "Region could not be drawn!"); return; @@ -1156,13 +1151,8 @@ static void wm_draw_window(bContext *C, wmWindow *win) * stereo methods, but it's less efficient than drawing directly. */ const int width = WM_window_pixels_x(win); const int height = WM_window_pixels_y(win); - GPUOffScreen *offscreen = GPU_offscreen_create(width, - height, - false, - GPU_RGBA8, - GPU_TEXTURE_USAGE_SHADER_READ | - GPU_TEXTURE_USAGE_ATTACHMENT, - NULL); + GPUOffScreen *offscreen = GPU_offscreen_create( + width, height, false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL); if (offscreen) { GPUTexture *texture = GPU_offscreen_color_texture(offscreen); @@ -1237,13 +1227,8 @@ uint *WM_window_pixels_read_offscreen(bContext *C, wmWindow *win, int r_size[2]) r_size[0] = WM_window_pixels_x(win); r_size[1] = WM_window_pixels_y(win); - GPUOffScreen *offscreen = GPU_offscreen_create(r_size[0], - r_size[1], - false, - GPU_RGBA8, - GPU_TEXTURE_USAGE_SHADER_READ | - GPU_TEXTURE_USAGE_ATTACHMENT, - NULL); + GPUOffScreen *offscreen = GPU_offscreen_create( + r_size[0], r_size[1], false, GPU_RGBA8, GPU_TEXTURE_USAGE_SHADER_READ, NULL); if (UNLIKELY(!offscreen)) { return NULL; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 506533851c0..64d687b956a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -1387,13 +1387,8 @@ bool wm_xr_session_surface_offscreen_ensure(wmXrSurfaceData *surface_data, } BLI_assert(format != GPU_R8); - offscreen = vp->offscreen = GPU_offscreen_create(draw_view->width, - draw_view->height, - true, - format, - GPU_TEXTURE_USAGE_SHADER_READ | - GPU_TEXTURE_USAGE_ATTACHMENT, - err_out); + offscreen = vp->offscreen = GPU_offscreen_create( + draw_view->width, draw_view->height, true, format, GPU_TEXTURE_USAGE_SHADER_READ, err_out); if (offscreen) { viewport = vp->viewport = GPU_viewport_create(); if (!viewport) { -- 2.30.2 From a01991b5387a6b4ae0df96481e31c7e2d304754b Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 13 Apr 2023 15:42:24 +0200 Subject: [PATCH 15/19] Remove unneeded file. --- source/blender/gpu/tests/state_blend_test.cc | 63 -------------------- 1 file changed, 63 deletions(-) delete mode 100644 source/blender/gpu/tests/state_blend_test.cc diff --git a/source/blender/gpu/tests/state_blend_test.cc b/source/blender/gpu/tests/state_blend_test.cc deleted file mode 100644 index 17e99045541..00000000000 --- a/source/blender/gpu/tests/state_blend_test.cc +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 */ - -#include "testing/testing.h" - -#include "gpu_testing.hh" - -#include "GPU_batch.h" -#include "GPU_batch_presets.h" -#include "GPU_framebuffer.h" -#include "GPU_matrix.h" - -#include "BLI_math_vector.hh" - -#include "intern/draw_cache.h" - -namespace blender::gpu::tests { - -template -void blend_test(float4 source_a, float4 source_b, float4 expected_result) -{ - GPUOffScreen *offscreen = GPU_offscreen_create( - 1, 1, false, GPU_RGBA16F, GPU_TEXTURE_USAGE_HOST_READ, nullptr); - BLI_assert(offscreen != nullptr); - GPU_offscreen_bind(offscreen, false); - GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen); - GPU_texture_clear(color_texture, GPU_DATA_FLOAT, source_a); - - GPUBatch *batch = DRW_cache_quad_get(); - - GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); - GPU_batch_uniform_4fv(batch, "color", source_b); - GPU_blend(blend_type); - - GPU_batch_draw(batch); - GPU_offscreen_unbind(offscreen, false); - GPU_flush(); - - float4 read_back; - GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); - GPU_offscreen_read_pixels(offscreen, GPU_DATA_FLOAT, &read_back); - EXPECT_EQ(read_back, expected_result); - - GPU_offscreen_free(offscreen); - DRW_shape_cache_free(); -} - -static void test_blend_none() -{ - blend_test(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)); -} -GPU_TEST(blend_none) - -static void test_blend_alpha() -{ - blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), - float4(0.0f, 1.0f, 0.0f, 0.5f), - float4(0.5f, 0.5f, 0.5f, 1.0f)); -} -GPU_TEST(blend_alpha) - -} // namespace blender::gpu::tests -- 2.30.2 From a32ee85b51e0175b9f31ad92a3cf127ed77b6fce Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 13 Apr 2023 16:14:54 +0200 Subject: [PATCH 16/19] Added all non-custom blending test cases. --- source/blender/gpu/tests/state_blend_test.cc | 64 ++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/source/blender/gpu/tests/state_blend_test.cc b/source/blender/gpu/tests/state_blend_test.cc index b8647568a16..dc431724517 100644 --- a/source/blender/gpu/tests/state_blend_test.cc +++ b/source/blender/gpu/tests/state_blend_test.cc @@ -65,4 +65,68 @@ static void test_blend_alpha() } GPU_TEST(blend_alpha) +static void test_blend_alpha_premult() +{ + blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(0.5f, 1.0f, 0.5f, 1.0f)); +} +GPU_TEST(blend_alpha_premult) + +static void test_blend_additive() +{ + blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(1.0f, 0.5f, 1.0f, 1.0f)); +} +GPU_TEST(blend_additive) + +static void test_blend_additive_premult() +{ + blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(1.0f, 1.0f, 1.0f, 1.5f)); +} +GPU_TEST(blend_additive_premult) + +static void test_blend_multiply() +{ + blend_test(float4(1.0f, 0.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(0.0f, 0.0f, 0.0f, 0.5f)); +} +GPU_TEST(blend_multiply) + +static void test_blend_subtract() +{ + blend_test(float4(1.0f, 1.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(1.0f, 0.0f, 1.0f, 0.5f)); +} +GPU_TEST(blend_subtract) + +static void test_blend_invert() +{ + blend_test(float4(1.0f, 1.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(0.0f, 0.0f, 0.0f, 1.0f)); +} +GPU_TEST(blend_invert) + +static void test_blend_oit() +{ + blend_test(float4(1.0f, 1.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(1.0f, 2.0f, 1.0f, 0.5f)); +} +GPU_TEST(blend_oit) + +static void test_blend_background() +{ + blend_test(float4(1.0f, 1.0f, 1.0f, 1.0f), + float4(0.0f, 1.0f, 0.0f, 0.5f), + float4(0.5f, 0.5f, 0.5f, 0.5f)); +} +GPU_TEST(blend_background) + } // namespace blender::gpu::tests -- 2.30.2 From 25873c49634ede7aeb06fea20363b4d3b8827235 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 14 Apr 2023 09:05:38 +0200 Subject: [PATCH 17/19] Fix state issue --- source/blender/gpu/vulkan/vk_command_buffer.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/blender/gpu/vulkan/vk_command_buffer.cc b/source/blender/gpu/vulkan/vk_command_buffer.cc index 675ba3a0002..80d3aa57eaa 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.cc +++ b/source/blender/gpu/vulkan/vk_command_buffer.cc @@ -21,7 +21,6 @@ namespace blender::gpu { VKCommandBuffer::~VKCommandBuffer() { - validate_framebuffer_not_exists(); if (vk_device_ != VK_NULL_HANDLE) { VK_ALLOCATION_CALLBACKS; vkDestroyFence(vk_device_, vk_fence_, vk_allocation_callbacks); @@ -45,7 +44,6 @@ void VKCommandBuffer::init(const VkDevice vk_device, fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; vkCreateFence(vk_device_, &fenceInfo, vk_allocation_callbacks, &vk_fence_); } - validate_framebuffer_not_exists(); } void VKCommandBuffer::begin_recording() -- 2.30.2 From 8fe177425efb126eaad6f36e3b816b1a7593b0a1 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 14 Apr 2023 11:43:09 +0200 Subject: [PATCH 18/19] Added test case for immediate mode drawing. --- source/blender/gpu/CMakeLists.txt | 3 + source/blender/gpu/tests/immediate_test.cc | 52 ++++ source/blender/gpu/vulkan/vk_immediate.cc | 294 +++++++++++++++++++++ source/blender/gpu/vulkan/vk_immediate.hh | 88 ++++++ 4 files changed, 437 insertions(+) create mode 100644 source/blender/gpu/tests/immediate_test.cc create mode 100644 source/blender/gpu/vulkan/vk_immediate.cc create mode 100644 source/blender/gpu/vulkan/vk_immediate.hh diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index b18b90493da..74db1933070 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -211,6 +211,7 @@ set(VULKAN_SRC vulkan/vk_drawlist.cc vulkan/vk_fence.cc vulkan/vk_framebuffer.cc + vulkan/vk_immediate.cc vulkan/vk_index_buffer.cc vulkan/vk_memory.cc vulkan/vk_memory_layout.cc @@ -243,6 +244,7 @@ set(VULKAN_SRC vulkan/vk_drawlist.hh vulkan/vk_fence.hh vulkan/vk_framebuffer.hh + vulkan/vk_immediate.hh vulkan/vk_index_buffer.hh vulkan/vk_memory.hh vulkan/vk_memory_layout.hh @@ -835,6 +837,7 @@ if(WITH_GTESTS) tests/state_blend_test.cc tests/framebuffer_test.cc + tests/immediate_test.cc tests/index_buffer_test.cc tests/offscreen_test.cc tests/push_constants_test.cc diff --git a/source/blender/gpu/tests/immediate_test.cc b/source/blender/gpu/tests/immediate_test.cc new file mode 100644 index 00000000000..b64dc1c99c7 --- /dev/null +++ b/source/blender/gpu/tests/immediate_test.cc @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "testing/testing.h" + +#include "GPU_framebuffer.h" +#include "GPU_immediate.h" +#include "GPU_shader_builtin.h" +#include "gpu_testing.hh" + +#include "BLI_math_vector.hh" + +namespace blender::gpu::tests { + +static constexpr int Size = 10; + +static void test_immediate_crosshair() +{ + GPUOffScreen *offscreen = GPU_offscreen_create(Size, + Size, + false, + GPU_RGBA16F, + GPU_TEXTURE_USAGE_ATTACHMENT | + GPU_TEXTURE_USAGE_HOST_READ, + nullptr); + BLI_assert(offscreen != nullptr); + GPU_offscreen_bind(offscreen, false); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + float2 viewport_size(Size); + float4 color(1.0, 0.5, 0.25, 1.0); + immUniform2fv("viewportSize", viewport_size); + immUniform1f("lineWidth", 1.0f); + immUniformColor4fv(color); + + immBegin(GPU_PRIM_LINES, 4); + immVertex3f(pos, -1.0f, 0.0f, 0.0f); + immVertex3f(pos, 1.0f, 0.0f, 0.0f); + immVertex3f(pos, 0.0f, -1.0f, 0.0f); + immVertex3f(pos, 0.0f, 1.0f, 0.0f); + immEnd(); + + GPU_offscreen_unbind(offscreen, false); + GPU_flush(); + + GPU_offscreen_free(offscreen); +} +GPU_TEST(immediate_crosshair) + +} // namespace blender::gpu::tests diff --git a/source/blender/gpu/vulkan/vk_immediate.cc b/source/blender/gpu/vulkan/vk_immediate.cc new file mode 100644 index 00000000000..e16656d1527 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_immediate.cc @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation */ + +/** \file + * \ingroup gpu + * + * Mimics old style OpenGL immediate mode drawing. + */ + +#include "vk_immediate.hh" + +namespace blender::gpu { + +VKImmediate::VKImmediate(VKContext *context) : context_(context) {} + +VKImmediate::~VKImmediate() +{ + + if (vkbuffer_) + delete vkbuffer_; + + vao.vertexInputAttributes.clear(); + vao.vertexInputBindings.clear(); +} + +uint16_t VKImmediate::descript_vao(const ShaderInterface *interface_, + const GPUVertFormat *format, + uint v_first, + uint v_len, + const bool use_instancing) +{ + /// Vertex Input Information <--> OpenGL VertexArray + vao.vertexInputBindings.resize(1); + auto &bindingDescription = vao.vertexInputBindings[0]; + bindingDescription.binding = 0; + + if (format->deinterleaved) { + bindingDescription.stride = format->attrs[0].size; + } + else { + bindingDescription.stride = format->stride; + } + bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + const uint attr_len = format->attr_len; + uint stride = format->stride; + + uint16_t enabled_attrib = 0; + uint offset; + + GLuint divisor = (use_instancing) ? 1 : 0; + vao.vertexInputAttributes.resize(attr_len); + vao.divisors.resize(attr_len); + + for (uint a_idx = 0; a_idx < attr_len; a_idx++) { + const GPUVertAttr *a = &format->attrs[a_idx]; + + BLI_assert((a->name_len == 1 && "TODO atrribute deinterleaved. ")); + auto &attr = vao.vertexInputAttributes[a_idx]; + auto &divs = vao.divisors[a_idx]; + attr.binding = divs.binding = bindingDescription.binding; + + if (format->deinterleaved) { + BLI_assert((false && "TODO atrribute deinterleaved. ")); + if (a_idx == 0) { + offset = (v_first * a->size); + } + else { + if (a_idx == 1) { + offset = format->attrs[a_idx - 1].size * v_len; + } + else + offset += format->attrs[a_idx - 1].size * v_len; + } + + stride = a->size; + } + else { + offset = a->offset; + } + + /* This is in fact an offset in memory. */ + /// const GLvoid *pointer = (const GLubyte *)intptr_t(offset + v_first * stride); + /// const GLenum type = to_gl(static_cast(a->comp_type)); + + attr.offset = offset; + + for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { + const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); + const ShaderInput *input = interface_->attr_get(name); + + if (input == nullptr || input->location == -1) { + continue; + } + + enabled_attrib |= (1 << input->location); + + if (ELEM(a->comp_len, 16, 12, 8)) { + BLI_assert((false && "TODO atrribute deinterleaved. ")); + BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); + BLI_assert(a->comp_type == GPU_COMP_F32); + + for (int i = 0; i < a->comp_len / 4; i++) { + /// glEnableVertexAttribArray(input->location + i); + attr.location = input->location + i; + /// glVertexAttribDivisor(input->location + i, divisor); + divs.divisor = divisor; + attr.format = VK_FORMAT_R32G32B32A32_SFLOAT; + /// glVertexAttribPointer( input->location + i, 4, type, GL_FALSE, stride, (const GLubyte + /// *)pointer + i * 16); + } + } + else { + glEnableVertexAttribArray(input->location); + glVertexAttribDivisor(input->location, divisor); + attr.location = input->location; + divs.divisor = divisor; + + switch (a->fetch_mode) { + case GPU_FETCH_FLOAT: + case GPU_FETCH_INT_TO_FLOAT: + switch (a->comp_len) { + case 4: + attr.format = VK_FORMAT_R32G32B32A32_SFLOAT; + break; + case 3: + attr.format = VK_FORMAT_R32G32B32_SFLOAT; + break; + case 2: + attr.format = VK_FORMAT_R32G32_SFLOAT; + break; + case 1: + attr.format = VK_FORMAT_R32_SFLOAT; + break; + default: + BLI_assert((false && "Unreachable VAO Information")); + break; + } + /// glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, + /// pointer); + break; + case GPU_FETCH_INT_TO_FLOAT_UNIT: + switch (a->comp_len) { + case 4: + attr.format = VK_FORMAT_R32G32B32A32_UINT; + break; + case 3: + attr.format = VK_FORMAT_R32G32B32_UINT; + break; + case 2: + attr.format = VK_FORMAT_R32G32_UINT; + break; + case 1: + attr.format = VK_FORMAT_R32_UINT; + break; + default: + BLI_assert((false && "Unreachable VAO Information")); + break; + } + + /// glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, + /// pointer); + break; + case GPU_FETCH_INT: + switch (a->comp_len) { + case 4: + attr.format = VK_FORMAT_R32G32B32A32_SINT; + break; + case 3: + attr.format = VK_FORMAT_R32G32B32_SINT; + break; + case 2: + attr.format = VK_FORMAT_R32G32_SINT; + break; + case 1: + attr.format = VK_FORMAT_R32_SINT; + break; + default: + BLI_assert((false && "Unreachable VAO Information")); + break; + } + /// glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); + break; + } + } + } + } + return enabled_attrib; +} + +void VKImmediate::update_bindings(const uint v_first, + const GPUVertFormat *format, + const ShaderInterface *interface_) +{ + // glBindVertexArray(vao); + + descript_vao(interface_, format, v_first, 0, false); +} + +uchar *VKImmediate::begin() +{ + + const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len); + GL_CHECK_RESOURCES("Immediate"); + vkstaging_ = context_->get_buffer_manager()->Create(bytes_needed, 256); + + bytes_mapped_ = bytes_needed; + void *ptr = vkstaging_->get_host_ptr(); + BLI_assert(ptr); + if (!vkbuffer_) { + VKResourceOptions options; + options.setDeviceLocal(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + vkbuffer_ = new VKBuffer(bytes_needed, 256, "VKImmediate::vbo", options); + } + else + vkbuffer_->Resize(bytes_needed, 256); + + strict_vertex_len = false; + VKStateManager::set_prim_type(prim_type); + + return (uchar *)ptr; +} + +void VKImmediate::end() +{ + BLI_assert(prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ + + vkstaging_->unmap(); + if (vertex_len > 0) { + VkBufferCopy region_ = {0, 0, vkbuffer_->get_buffer_size()}; + context_->get_buffer_manager()->Copy(*vkbuffer_, region_); + + context_->state_manager->apply_state(); + auto fb = static_cast(context_->active_fb); + context_->pipeline_state.active_shader->CreatePipeline(fb); + + record(); + } +} + +void VKImmediate::record() +{ + + if (vertex_len <= 0) + return; + VKShader *vkshader = reinterpret_cast(shader); + VkPipeline ¤t_pipe = vkshader->get_pipeline(); + BLI_assert(current_pipe != VK_NULL_HANDLE); + VKFrameBuffer *fb = static_cast(context_->active_fb); + if (fb->is_swapchain_) { + if (fb->is_blit_begin_) { + fb->render_end(); + } + } + vkshader->current_cmd_ = VK_NULL_HANDLE; + vkshader->current_cmd_ = fb->render_begin(vkshader->current_cmd_, + VK_COMMAND_BUFFER_LEVEL_PRIMARY); + vkshader->update_descriptor_set(vkshader->current_cmd_, vkshader->current_layout_); + auto vkinterface = (VKShaderInterface *)vkshader->interface; + + auto vert = vkbuffer_->get_vk_buffer(); + VkDeviceSize offsets[1] = {0}; + + fb->set_dirty_render(true); + vkCmdBindPipeline(vkshader->current_cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, current_pipe); + + VKStateManager::cmd_dynamic_state(vkshader->current_cmd_); + + if (vkinterface->push_range_.size > 0) { + vkCmdPushConstants(vkshader->current_cmd_, + vkshader->current_layout_, + vkinterface->push_range_.stageFlags, + vkinterface->push_range_.offset, + vkinterface->push_range_.size, + vkinterface->push_cache_); + } + + vkCmdBindVertexBuffers(vkshader->current_cmd_, 0, 1, &vert, offsets); + vkCmdDraw(vkshader->current_cmd_, vertex_len, 1, 0, 0); + + fb->is_dirty_render_ = true; + + if (!fb->is_swapchain_) { + fb->render_end(); + } + else { + // fb->move_pipe(current_pipe); + fb->render_end(); + } +}; + +/** \} */ + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_immediate.hh b/source/blender/gpu/vulkan/vk_immediate.hh new file mode 100644 index 00000000000..d96f36c397f --- /dev/null +++ b/source/blender/gpu/vulkan/vk_immediate.hh @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation */ + +/** \file + * \ingroup gpu + * + * Mimics old style OpenGL immediate mode drawing. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_immediate_private.hh" + +#include "vk_context.hh" +#include "vk_mem_alloc.h" + +namespace blender::gpu { + +/* Size of internal buffer. */ +#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024) + +struct VBO_INFO { + /** Vulkan Handle for this buffer. */ + VkBuffer vbo_id = VK_NULL_HANDLE; + /** Offset of the mapped data in data. */ + VkDeviceSize buffer_offset = 0; + /** Size of the whole buffer in bytes. */ + VkDeviceSize buffer_size = 0; + + VmaAllocation allocation = nullptr; +}; + +class VKImmediate : public Immediate { + public: + void record(); + + private: + VkPipeline current_pipe_ = VK_NULL_HANDLE; + + /** Size in bytes of the mapped region. */ + size_t bytes_mapped_ = 0; + /** Vertex array for this immediate mode instance. */ + /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_vertex_attribute_divisor.html + + struct vaoInfo { + VkPipelineVertexInputStateCreateInfo info = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL}; + + VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = { + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT, NULL}; + + Vector divisors; + Vector vertexInputBindings; + Vector vertexInputAttributes; + + vaoInfo() + { + info.pNext = &divisorInfo; + divisorInfo.vertexBindingDivisorCount = 0; + divisorInfo.pVertexBindingDivisors = NULL; + } + } vao; + + uint16_t descript_vao(const ShaderInterface *interface_, + const GPUVertFormat *format, + uint v_first, + uint v_len, + const bool use_instancing); + void update_bindings(const uint v_first, + const GPUVertFormat *format, + const ShaderInterface *interface_); + uchar data_[4 * 1024 * 1024]; + VKContext *context_; + VKBuffer *vkbuffer_ = nullptr, *vkstaging_ = nullptr; + + public: + VKImmediate(VKContext *context_); + ~VKImmediate(); + + uchar *begin(void) override; + void end(void) override; + + /// private: +}; + +} // namespace blender::gpu \ No newline at end of file -- 2.30.2 From c5e4e760086084a6cb091449ba9f0e2698d68aab Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 14 Apr 2023 15:48:04 +0200 Subject: [PATCH 19/19] Vulkan: Immediate mode emulation. --- source/blender/gpu/tests/immediate_test.cc | 6 +- source/blender/gpu/vulkan/vk_batch.cc | 2 +- .../blender/gpu/vulkan/vk_command_buffer.cc | 8 +- .../blender/gpu/vulkan/vk_command_buffer.hh | 2 + source/blender/gpu/vulkan/vk_context.cc | 17 +- source/blender/gpu/vulkan/vk_context.hh | 2 +- source/blender/gpu/vulkan/vk_immediate.cc | 287 ++---------------- source/blender/gpu/vulkan/vk_immediate.hh | 63 +--- source/blender/gpu/vulkan/vk_pipeline.cc | 4 +- source/blender/gpu/vulkan/vk_pipeline.hh | 2 +- source/blender/gpu/vulkan/vk_shader.cc | 10 +- source/blender/gpu/vulkan/vk_shader.hh | 2 +- .../gpu/vulkan/vk_vertex_attribute_object.cc | 77 +++-- .../gpu/vulkan/vk_vertex_attribute_object.hh | 17 +- 14 files changed, 145 insertions(+), 354 deletions(-) diff --git a/source/blender/gpu/tests/immediate_test.cc b/source/blender/gpu/tests/immediate_test.cc index b64dc1c99c7..3c82ae76103 100644 --- a/source/blender/gpu/tests/immediate_test.cc +++ b/source/blender/gpu/tests/immediate_test.cc @@ -28,11 +28,11 @@ static void test_immediate_crosshair() GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); float2 viewport_size(Size); float4 color(1.0, 0.5, 0.25, 1.0); - immUniform2fv("viewportSize", viewport_size); - immUniform1f("lineWidth", 1.0f); + // immUniform2fv("viewportSize", viewport_size); + // immUniform1f("lineWidth", 1.0f); immUniformColor4fv(color); immBegin(GPU_PRIM_LINES, 4); diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index bd6a0749b3d..6e7566fa6b8 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -24,7 +24,7 @@ void VKBatch::draw(int v_first, int v_count, int i_first, int i_count) context.state_manager->apply_state(); VKVertexAttributeObject &vao = vao_cache_.vao_get(this); vao.update_bindings(context, *this); - context.bind_graphics_pipeline(*this, vao); + context.bind_graphics_pipeline(prim_type, vao); vao.bind(context); VKIndexBuffer *index_buffer = index_buffer_get(); diff --git a/source/blender/gpu/vulkan/vk_command_buffer.cc b/source/blender/gpu/vulkan/vk_command_buffer.cc index 80d3aa57eaa..a5c514c007d 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.cc +++ b/source/blender/gpu/vulkan/vk_command_buffer.cc @@ -79,10 +79,16 @@ void VKCommandBuffer::bind(const VKDescriptorSet &descriptor_set, void VKCommandBuffer::bind(const uint32_t binding, const VKVertexBuffer &vertex_buffer, const VkDeviceSize offset) +{ + bind(binding, vertex_buffer.vk_handle(), offset); +} + +void VKCommandBuffer::bind(const uint32_t binding, + const VkBuffer &vk_buffer, + const VkDeviceSize offset) { validate_framebuffer_exists(); ensure_active_framebuffer(); - VkBuffer vk_buffer = vertex_buffer.vk_handle(); vkCmdBindVertexBuffers(vk_command_buffer_, binding, 1, &vk_buffer, &offset); } diff --git a/source/blender/gpu/vulkan/vk_command_buffer.hh b/source/blender/gpu/vulkan/vk_command_buffer.hh index da817763bda..8684fab39b7 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.hh +++ b/source/blender/gpu/vulkan/vk_command_buffer.hh @@ -81,6 +81,8 @@ class VKCommandBuffer : NonCopyable, NonMovable { 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 VkBuffer &buffer, const VkDeviceSize offset); void bind(const VKIndexBuffer &index_buffer, VkIndexType index_type); void begin_render_pass(const VKFrameBuffer &framebuffer); void end_render_pass(const VKFrameBuffer &framebuffer); diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index 38ed36abced..584a5ec0b52 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -9,6 +9,7 @@ #include "vk_backend.hh" #include "vk_framebuffer.hh" +#include "vk_immediate.hh" #include "vk_memory.hh" #include "vk_shader.hh" #include "vk_state_manager.hh" @@ -48,6 +49,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context) descriptor_pools_.init(vk_device_); state_manager = new VKStateManager(); + imm = new VKImmediate(); VKBackend::capabilities_init(*this); @@ -57,6 +59,10 @@ VKContext::VKContext(void *ghost_window, void *ghost_context) VKContext::~VKContext() { + /* IMM owns a buffer that should be freed before the memory allocator is freed. */ + /* TODO: Introduce a buffer pool for temporary buffers. */ + delete imm; + imm = nullptr; vmaDestroyAllocator(mem_allocator_); } @@ -91,9 +97,14 @@ void VKContext::activate() back_left = framebuffer; framebuffer->bind(false); } + + immActivate(); } -void VKContext::deactivate() {} +void VKContext::deactivate() +{ + immDeactivate(); +} void VKContext::begin_frame() { @@ -175,12 +186,12 @@ void VKContext::deactivate_framebuffer() /** \name Graphics pipeline * \{ */ -void VKContext::bind_graphics_pipeline(const VKBatch &batch, +void VKContext::bind_graphics_pipeline(const GPUPrimType prim_type, const VKVertexAttributeObject &vertex_attribute_object) { VKShader *shader = unwrap(this->shader); BLI_assert(shader); - shader->update_graphics_pipeline(*this, batch, vertex_attribute_object); + shader->update_graphics_pipeline(*this, prim_type, vertex_attribute_object); command_buffer_get().bind(shader->pipeline_get(), VK_PIPELINE_BIND_POINT_GRAPHICS); shader->pipeline_get().push_constants_get().update(*this); } diff --git a/source/blender/gpu/vulkan/vk_context.hh b/source/blender/gpu/vulkan/vk_context.hh index bfca6d14021..d918c5cd4ef 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -64,7 +64,7 @@ class VKContext : public Context { void deactivate_framebuffer(); VKFrameBuffer *active_framebuffer_get() const; - void bind_graphics_pipeline(const VKBatch &batch, + void bind_graphics_pipeline(const GPUPrimType prim_type, const VKVertexAttributeObject &vertex_attribute_object); static VKContext *get(void) diff --git a/source/blender/gpu/vulkan/vk_immediate.cc b/source/blender/gpu/vulkan/vk_immediate.cc index e16656d1527..4b371952323 100644 --- a/source/blender/gpu/vulkan/vk_immediate.cc +++ b/source/blender/gpu/vulkan/vk_immediate.cc @@ -9,285 +9,48 @@ #include "vk_immediate.hh" +#include "gpu_vertex_format_private.h" + namespace blender::gpu { -VKImmediate::VKImmediate(VKContext *context) : context_(context) {} - -VKImmediate::~VKImmediate() -{ - - if (vkbuffer_) - delete vkbuffer_; - - vao.vertexInputAttributes.clear(); - vao.vertexInputBindings.clear(); -} - -uint16_t VKImmediate::descript_vao(const ShaderInterface *interface_, - const GPUVertFormat *format, - uint v_first, - uint v_len, - const bool use_instancing) -{ - /// Vertex Input Information <--> OpenGL VertexArray - vao.vertexInputBindings.resize(1); - auto &bindingDescription = vao.vertexInputBindings[0]; - bindingDescription.binding = 0; - - if (format->deinterleaved) { - bindingDescription.stride = format->attrs[0].size; - } - else { - bindingDescription.stride = format->stride; - } - bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; - - const uint attr_len = format->attr_len; - uint stride = format->stride; - - uint16_t enabled_attrib = 0; - uint offset; - - GLuint divisor = (use_instancing) ? 1 : 0; - vao.vertexInputAttributes.resize(attr_len); - vao.divisors.resize(attr_len); - - for (uint a_idx = 0; a_idx < attr_len; a_idx++) { - const GPUVertAttr *a = &format->attrs[a_idx]; - - BLI_assert((a->name_len == 1 && "TODO atrribute deinterleaved. ")); - auto &attr = vao.vertexInputAttributes[a_idx]; - auto &divs = vao.divisors[a_idx]; - attr.binding = divs.binding = bindingDescription.binding; - - if (format->deinterleaved) { - BLI_assert((false && "TODO atrribute deinterleaved. ")); - if (a_idx == 0) { - offset = (v_first * a->size); - } - else { - if (a_idx == 1) { - offset = format->attrs[a_idx - 1].size * v_len; - } - else - offset += format->attrs[a_idx - 1].size * v_len; - } - - stride = a->size; - } - else { - offset = a->offset; - } - - /* This is in fact an offset in memory. */ - /// const GLvoid *pointer = (const GLubyte *)intptr_t(offset + v_first * stride); - /// const GLenum type = to_gl(static_cast(a->comp_type)); - - attr.offset = offset; - - for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { - const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const ShaderInput *input = interface_->attr_get(name); - - if (input == nullptr || input->location == -1) { - continue; - } - - enabled_attrib |= (1 << input->location); - - if (ELEM(a->comp_len, 16, 12, 8)) { - BLI_assert((false && "TODO atrribute deinterleaved. ")); - BLI_assert(a->fetch_mode == GPU_FETCH_FLOAT); - BLI_assert(a->comp_type == GPU_COMP_F32); - - for (int i = 0; i < a->comp_len / 4; i++) { - /// glEnableVertexAttribArray(input->location + i); - attr.location = input->location + i; - /// glVertexAttribDivisor(input->location + i, divisor); - divs.divisor = divisor; - attr.format = VK_FORMAT_R32G32B32A32_SFLOAT; - /// glVertexAttribPointer( input->location + i, 4, type, GL_FALSE, stride, (const GLubyte - /// *)pointer + i * 16); - } - } - else { - glEnableVertexAttribArray(input->location); - glVertexAttribDivisor(input->location, divisor); - attr.location = input->location; - divs.divisor = divisor; - - switch (a->fetch_mode) { - case GPU_FETCH_FLOAT: - case GPU_FETCH_INT_TO_FLOAT: - switch (a->comp_len) { - case 4: - attr.format = VK_FORMAT_R32G32B32A32_SFLOAT; - break; - case 3: - attr.format = VK_FORMAT_R32G32B32_SFLOAT; - break; - case 2: - attr.format = VK_FORMAT_R32G32_SFLOAT; - break; - case 1: - attr.format = VK_FORMAT_R32_SFLOAT; - break; - default: - BLI_assert((false && "Unreachable VAO Information")); - break; - } - /// glVertexAttribPointer(input->location, a->comp_len, type, GL_FALSE, stride, - /// pointer); - break; - case GPU_FETCH_INT_TO_FLOAT_UNIT: - switch (a->comp_len) { - case 4: - attr.format = VK_FORMAT_R32G32B32A32_UINT; - break; - case 3: - attr.format = VK_FORMAT_R32G32B32_UINT; - break; - case 2: - attr.format = VK_FORMAT_R32G32_UINT; - break; - case 1: - attr.format = VK_FORMAT_R32_UINT; - break; - default: - BLI_assert((false && "Unreachable VAO Information")); - break; - } - - /// glVertexAttribPointer(input->location, a->comp_len, type, GL_TRUE, stride, - /// pointer); - break; - case GPU_FETCH_INT: - switch (a->comp_len) { - case 4: - attr.format = VK_FORMAT_R32G32B32A32_SINT; - break; - case 3: - attr.format = VK_FORMAT_R32G32B32_SINT; - break; - case 2: - attr.format = VK_FORMAT_R32G32_SINT; - break; - case 1: - attr.format = VK_FORMAT_R32_SINT; - break; - default: - BLI_assert((false && "Unreachable VAO Information")); - break; - } - /// glVertexAttribIPointer(input->location, a->comp_len, type, stride, pointer); - break; - } - } - } - } - return enabled_attrib; -} - -void VKImmediate::update_bindings(const uint v_first, - const GPUVertFormat *format, - const ShaderInterface *interface_) -{ - // glBindVertexArray(vao); - - descript_vao(interface_, format, v_first, 0, false); -} +VKImmediate::VKImmediate() {} +VKImmediate::~VKImmediate() {} uchar *VKImmediate::begin() { - + VKContext &context = *VKContext::get(); const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len); - GL_CHECK_RESOURCES("Immediate"); - vkstaging_ = context_->get_buffer_manager()->Create(bytes_needed, 256); - - bytes_mapped_ = bytes_needed; - void *ptr = vkstaging_->get_host_ptr(); - BLI_assert(ptr); - if (!vkbuffer_) { - VKResourceOptions options; - options.setDeviceLocal(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT); - vkbuffer_ = new VKBuffer(bytes_needed, 256, "VKImmediate::vbo", options); + if (!buffer_.is_allocated()) { + buffer_.create(context, + DEFAULT_INTERNAL_BUFFER_SIZE, + GPU_USAGE_DYNAMIC, + static_cast(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT)); } - else - vkbuffer_->Resize(bytes_needed, 256); - strict_vertex_len = false; - VKStateManager::set_prim_type(prim_type); + /* TODO: Resize buffer when more is needed. Currently assert as we haven't implemented it yet. */ + BLI_assert(bytes_needed < DEFAULT_INTERNAL_BUFFER_SIZE); - return (uchar *)ptr; + uchar *data = static_cast(buffer_.mapped_memory_get()); + return data; } void VKImmediate::end() { - BLI_assert(prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */ - - vkstaging_->unmap(); - if (vertex_len > 0) { - VkBufferCopy region_ = {0, 0, vkbuffer_->get_buffer_size()}; - context_->get_buffer_manager()->Copy(*vkbuffer_, region_); - - context_->state_manager->apply_state(); - auto fb = static_cast(context_->active_fb); - context_->pipeline_state.active_shader->CreatePipeline(fb); - - record(); - } -} - -void VKImmediate::record() -{ - - if (vertex_len <= 0) + BLI_assert_msg(prim_type != GPU_PRIM_NONE, "Illegal state: not between an immBegin/End pair."); + if (vertex_len == 0) { return; - VKShader *vkshader = reinterpret_cast(shader); - VkPipeline ¤t_pipe = vkshader->get_pipeline(); - BLI_assert(current_pipe != VK_NULL_HANDLE); - VKFrameBuffer *fb = static_cast(context_->active_fb); - if (fb->is_swapchain_) { - if (fb->is_blit_begin_) { - fb->render_end(); - } - } - vkshader->current_cmd_ = VK_NULL_HANDLE; - vkshader->current_cmd_ = fb->render_begin(vkshader->current_cmd_, - VK_COMMAND_BUFFER_LEVEL_PRIMARY); - vkshader->update_descriptor_set(vkshader->current_cmd_, vkshader->current_layout_); - auto vkinterface = (VKShaderInterface *)vkshader->interface; - - auto vert = vkbuffer_->get_vk_buffer(); - VkDeviceSize offsets[1] = {0}; - - fb->set_dirty_render(true); - vkCmdBindPipeline(vkshader->current_cmd_, VK_PIPELINE_BIND_POINT_GRAPHICS, current_pipe); - - VKStateManager::cmd_dynamic_state(vkshader->current_cmd_); - - if (vkinterface->push_range_.size > 0) { - vkCmdPushConstants(vkshader->current_cmd_, - vkshader->current_layout_, - vkinterface->push_range_.stageFlags, - vkinterface->push_range_.offset, - vkinterface->push_range_.size, - vkinterface->push_cache_); } - vkCmdBindVertexBuffers(vkshader->current_cmd_, 0, 1, &vert, offsets); - vkCmdDraw(vkshader->current_cmd_, vertex_len, 1, 0, 0); + VKContext &context = *VKContext::get(); + BLI_assert(context.shader == unwrap(shader)); + context.state_manager->apply_state(); + vertex_attributes_.update_bindings(*this); + context.bind_graphics_pipeline(prim_type, vertex_attributes_); + vertex_attributes_.bind(context); - fb->is_dirty_render_ = true; - - if (!fb->is_swapchain_) { - fb->render_end(); - } - else { - // fb->move_pipe(current_pipe); - fb->render_end(); - } -}; + context.command_buffer_get().draw(0, vertex_len, 0, 1); +} /** \} */ diff --git a/source/blender/gpu/vulkan/vk_immediate.hh b/source/blender/gpu/vulkan/vk_immediate.hh index d96f36c397f..232ca865e8c 100644 --- a/source/blender/gpu/vulkan/vk_immediate.hh +++ b/source/blender/gpu/vulkan/vk_immediate.hh @@ -13,76 +13,29 @@ #include "gpu_immediate_private.hh" +#include "vk_buffer.hh" #include "vk_context.hh" #include "vk_mem_alloc.h" +#include "vk_vertex_attribute_object.hh" namespace blender::gpu { /* Size of internal buffer. */ -#define DEFAULT_INTERNAL_BUFFER_SIZE (4 * 1024 * 1024) - -struct VBO_INFO { - /** Vulkan Handle for this buffer. */ - VkBuffer vbo_id = VK_NULL_HANDLE; - /** Offset of the mapped data in data. */ - VkDeviceSize buffer_offset = 0; - /** Size of the whole buffer in bytes. */ - VkDeviceSize buffer_size = 0; - - VmaAllocation allocation = nullptr; -}; +constexpr size_t DEFAULT_INTERNAL_BUFFER_SIZE = (4 * 1024 * 1024); class VKImmediate : public Immediate { - public: - void record(); - private: - VkPipeline current_pipe_ = VK_NULL_HANDLE; - - /** Size in bytes of the mapped region. */ - size_t bytes_mapped_ = 0; - /** Vertex array for this immediate mode instance. */ - /// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_EXT_vertex_attribute_divisor.html - - struct vaoInfo { - VkPipelineVertexInputStateCreateInfo info = { - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, NULL}; - - VkPipelineVertexInputDivisorStateCreateInfoEXT divisorInfo = { - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT, NULL}; - - Vector divisors; - Vector vertexInputBindings; - Vector vertexInputAttributes; - - vaoInfo() - { - info.pNext = &divisorInfo; - divisorInfo.vertexBindingDivisorCount = 0; - divisorInfo.pVertexBindingDivisors = NULL; - } - } vao; - - uint16_t descript_vao(const ShaderInterface *interface_, - const GPUVertFormat *format, - uint v_first, - uint v_len, - const bool use_instancing); - void update_bindings(const uint v_first, - const GPUVertFormat *format, - const ShaderInterface *interface_); - uchar data_[4 * 1024 * 1024]; - VKContext *context_; - VKBuffer *vkbuffer_ = nullptr, *vkstaging_ = nullptr; + VKBuffer buffer_; + VKVertexAttributeObject vertex_attributes_; public: - VKImmediate(VKContext *context_); - ~VKImmediate(); + VKImmediate(); + virtual ~VKImmediate(); uchar *begin(void) override; void end(void) override; - /// private: + friend class VKVertexAttributeObject; }; } // namespace blender::gpu \ No newline at end of file diff --git a/source/blender/gpu/vulkan/vk_pipeline.cc b/source/blender/gpu/vulkan/vk_pipeline.cc index 8e3cfc486f8..d3f32dfc3b5 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -94,7 +94,7 @@ void VKPipeline::finalize(VKContext &context, VkShaderModule vertex_module, VkShaderModule fragment_module, VkPipelineLayout &pipeline_layout, - const VKBatch &batch, + const GPUPrimType prim_type, const VKVertexAttributeObject &vertex_attribute_object) { BLI_assert(vertex_module != VK_NULL_HANDLE); @@ -141,7 +141,7 @@ void VKPipeline::finalize(VKContext &context, /* Input assembly state. */ VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly = {}; pipeline_input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - pipeline_input_assembly.topology = to_vk_primitive_topology(batch.prim_type); + pipeline_input_assembly.topology = to_vk_primitive_topology(prim_type); pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly; /* Viewport state. */ diff --git a/source/blender/gpu/vulkan/vk_pipeline.hh b/source/blender/gpu/vulkan/vk_pipeline.hh index 958c5b43f09..7bdee9e2231 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.hh +++ b/source/blender/gpu/vulkan/vk_pipeline.hh @@ -85,7 +85,7 @@ class VKPipeline : NonCopyable { VkShaderModule vertex_module, VkShaderModule fragment_module, VkPipelineLayout &pipeline_layout, - const VKBatch &batch, + const GPUPrimType prim_type, const VKVertexAttributeObject &vertex_attribute_object); }; diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index bc9876d46ce..75f32ebcbee 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -933,12 +933,16 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *) void VKShader::transform_feedback_disable() {} void VKShader::update_graphics_pipeline(VKContext &context, - const VKBatch &batch, + const GPUPrimType prim_type, const VKVertexAttributeObject &vertex_attribute_object) { BLI_assert(is_graphics_shader()); - pipeline_get().finalize( - context, vertex_module_, fragment_module_, pipeline_layout_, batch, vertex_attribute_object); + pipeline_get().finalize(context, + vertex_module_, + fragment_module_, + pipeline_layout_, + prim_type, + vertex_attribute_object); } void VKShader::bind() diff --git a/source/blender/gpu/vulkan/vk_shader.hh b/source/blender/gpu/vulkan/vk_shader.hh index eee268d8b44..62b11d2b0d3 100644 --- a/source/blender/gpu/vulkan/vk_shader.hh +++ b/source/blender/gpu/vulkan/vk_shader.hh @@ -71,7 +71,7 @@ class VKShader : public Shader { const VKShaderInterface &interface_get() const; void update_graphics_pipeline(VKContext &context, - const VKBatch &batch, + const GPUPrimType prim_type, const VKVertexAttributeObject &vertex_attribute_object); private: diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc index 4e72c25aa67..161ce5d259d 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc @@ -5,6 +5,7 @@ #include "vk_batch.hh" #include "vk_context.hh" +#include "vk_immediate.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" #include "vk_vertex_buffer.hh" @@ -24,6 +25,7 @@ void VKVertexAttributeObject::clear() bindings.clear(); attributes.clear(); vbos.clear(); + buffers.clear(); } VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttributeObject &other) @@ -40,6 +42,8 @@ VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttrib attributes.extend(other.attributes); vbos.clear(); vbos.extend(other.vbos); + buffers.clear(); + buffers.extend(other.buffers); return *this; } @@ -53,10 +57,21 @@ void VKVertexAttributeObject::bind(VKContext &context) continue; } visited_bindings[attribute.binding] = true; - BLI_assert(vbos[attribute.binding]); - VKVertexBuffer &vbo = *vbos[attribute.binding]; - vbo.upload(); - context.command_buffer_get().bind(attribute.binding, vbo, 0); + + /* Bind VBOS from batches. */ + if (attribute.binding < vbos.size()) { + BLI_assert(vbos[attribute.binding]); + VKVertexBuffer &vbo = *vbos[attribute.binding]; + vbo.upload(); + context.command_buffer_get().bind(attribute.binding, vbo, 0); + } + + /* Bind dynamic buffers from immediate mode. */ + if (attribute.binding < buffers.size()) { + BLI_assert(buffers[attribute.binding]); + VKBuffer &buffer = *buffers[attribute.binding]; + context.command_buffer_get().bind(attribute.binding, buffer.vk_handle(), 0); + } } } @@ -69,14 +84,16 @@ void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { VKVertexBuffer *vbo = batch.vertex_buffer_get(v); if (vbo) { - update_bindings(*vbo, interface, occupied_attributes, false); + update_bindings( + vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false); } } for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) { VKVertexBuffer *vbo = batch.instance_buffer_get(v); if (vbo) { - update_bindings(*vbo, interface, occupied_attributes, false); + update_bindings( + vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false); } } @@ -85,25 +102,46 @@ void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch BLI_assert(interface.enabled_attr_mask_ == occupied_attributes); } -void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, +void VKVertexAttributeObject::update_bindings(VKImmediate &immediate) +{ + clear(); + const VKShaderInterface &interface = unwrap(unwrap(immediate.shader))->interface_get(); + AttributeMask occupied_attributes = 0; + + update_bindings(immediate.vertex_format, + nullptr, + &immediate.buffer_, + immediate.vertex_len, + interface, + occupied_attributes, + false); + is_valid = true; + BLI_assert(interface.enabled_attr_mask_ == occupied_attributes); +} + +void VKVertexAttributeObject::update_bindings(const GPUVertFormat &vertex_format, + VKVertexBuffer *vertex_buffer, + VKBuffer *immediate_vertex_buffer, + const int64_t vertex_len, const VKShaderInterface &interface, AttributeMask &r_occupied_attributes, const bool use_instancing) { - const GPUVertFormat &format = vertex_buffer.format; + BLI_assert(vertex_buffer || immediate_vertex_buffer); + BLI_assert(!(vertex_buffer && immediate_vertex_buffer)); - if (format.attr_len <= 0) { + if (vertex_format.attr_len <= 0) { return; } uint32_t offset = 0; - uint32_t stride = format.stride; + uint32_t stride = vertex_format.stride; - for (uint32_t attribute_index = 0; attribute_index < format.attr_len; attribute_index++) { - const GPUVertAttr &attribute = format.attrs[attribute_index]; - if (format.deinterleaved) { - offset += ((attribute_index == 0) ? 0 : format.attrs[attribute_index - 1].size) * - vertex_buffer.vertex_len; + for (uint32_t attribute_index = 0; attribute_index < vertex_format.attr_len; attribute_index++) { + const GPUVertAttr &attribute = vertex_format.attrs[attribute_index]; + if (vertex_format.deinterleaved) { + offset += ((attribute_index == 0) ? 0 : vertex_format.attrs[attribute_index - 1].size) * + vertex_len; stride = attribute.size; } else { @@ -114,7 +152,7 @@ void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, bool attribute_used_by_shader = false; for (uint32_t name_index = 0; name_index < attribute.name_len; name_index++) { - const char *name = GPU_vertformat_attr_name_get(&format, &attribute, name_index); + const char *name = GPU_vertformat_attr_name_get(&vertex_format, &attribute, name_index); const ShaderInput *shader_input = interface.attr_get(name); if (shader_input == nullptr || shader_input->location == -1) { continue; @@ -144,7 +182,12 @@ void VKVertexAttributeObject::update_bindings(VKVertexBuffer &vertex_buffer, vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX; bindings.append(vk_binding_descriptor); - vbos.append(&vertex_buffer); + if (vertex_buffer) { + vbos.append(vertex_buffer); + } + if (immediate_vertex_buffer) { + buffers.append(immediate_vertex_buffer); + } } } } diff --git a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh index e6d9ce26226..8492349cc7a 100644 --- a/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh @@ -16,7 +16,9 @@ namespace blender::gpu { class VKVertexBuffer; class VKContext; class VKBatch; +class VKBuffer; class VKShaderInterface; +class VKImmediate; using AttributeMask = uint16_t; @@ -27,7 +29,10 @@ struct VKVertexAttributeObject { Vector bindings; Vector attributes; + /* Used for batches. */ Vector vbos; + /* Used for immediate mode. */ + Vector buffers; VKVertexAttributeObject(); void clear(); @@ -38,12 +43,16 @@ struct VKVertexAttributeObject { VKVertexAttributeObject &operator=(const VKVertexAttributeObject &other); void update_bindings(const VKContext &context, VKBatch &batch); + void update_bindings(VKImmediate &immediate); private: - void update_bindings(VKVertexBuffer &vertex_buffer, - const VKShaderInterface &interface, - AttributeMask &r_occupied_attributes, - const bool use_instancing); + void update_bindings(const GPUVertFormat &vertex_format, + VKVertexBuffer *vertex_buffer, + VKBuffer *immediate_vertex_buffer, + const int64_t vertex_len, + const VKShaderInterface &interface, + AttributeMask &r_occupied_attributes, + const bool use_instancing); }; } // namespace blender::gpu -- 2.30.2