diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index e2f458669d1..30f7c3b0cfe 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -212,14 +212,17 @@ 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 + vulkan/vk_pipeline_state.cc vulkan/vk_pipeline.cc vulkan/vk_pixel_buffer.cc vulkan/vk_push_constants.cc vulkan/vk_query.cc vulkan/vk_resource_tracker.cc + vulkan/vk_sampler.cc vulkan/vk_shader.cc vulkan/vk_shader_interface.cc vulkan/vk_shader_log.cc @@ -227,6 +230,7 @@ set(VULKAN_SRC vulkan/vk_storage_buffer.cc vulkan/vk_texture.cc vulkan/vk_uniform_buffer.cc + vulkan/vk_vertex_attribute_object.cc vulkan/vk_vertex_buffer.cc vulkan/vk_backend.hh @@ -243,14 +247,17 @@ 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 + vulkan/vk_pipeline_state.hh vulkan/vk_pipeline.hh vulkan/vk_pixel_buffer.hh vulkan/vk_push_constants.hh vulkan/vk_query.hh vulkan/vk_resource_tracker.hh + vulkan/vk_sampler.hh vulkan/vk_shader.hh vulkan/vk_shader_interface.hh vulkan/vk_shader_log.hh @@ -258,6 +265,7 @@ set(VULKAN_SRC vulkan/vk_storage_buffer.hh vulkan/vk_texture.hh vulkan/vk_uniform_buffer.hh + vulkan/vk_vertex_attribute_object.hh vulkan/vk_vertex_buffer.hh ) diff --git a/source/blender/gpu/vulkan/vk_backend.cc b/source/blender/gpu/vulkan/vk_backend.cc index 4f3e02fa716..a9bd54d310f 100644 --- a/source/blender/gpu/vulkan/vk_backend.cc +++ b/source/blender/gpu/vulkan/vk_backend.cc @@ -64,17 +64,8 @@ void VKBackend::samplers_update() {} void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len) { VKContext &context = *VKContext::get(); - VKShader *shader = static_cast(context.shader); + context.bind_compute_pipeline(); VKCommandBuffer &command_buffer = context.command_buffer_get(); - VKPipeline &pipeline = shader->pipeline_get(); - VKDescriptorSetTracker &descriptor_set = pipeline.descriptor_set_get(); - VKPushConstants &push_constants = pipeline.push_constants_get(); - - push_constants.update(context); - descriptor_set.update(context); - command_buffer.bind(*descriptor_set.active_descriptor_set(), - shader->vk_pipeline_layout_get(), - VK_PIPELINE_BIND_POINT_COMPUTE); command_buffer.dispatch(groups_x_len, groups_y_len, groups_z_len); } diff --git a/source/blender/gpu/vulkan/vk_batch.cc b/source/blender/gpu/vulkan/vk_batch.cc index 6ef6df476b1..33139cacf65 100644 --- a/source/blender/gpu/vulkan/vk_batch.cc +++ b/source/blender/gpu/vulkan/vk_batch.cc @@ -7,9 +7,42 @@ #include "vk_batch.hh" +#include "vk_context.hh" +#include "vk_index_buffer.hh" +#include "vk_vertex_attribute_object.hh" +#include "vk_vertex_buffer.hh" + namespace blender::gpu { -void VKBatch::draw(int /*v_first*/, int /*v_count*/, int /*i_first*/, int /*i_count*/) {} +void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int instance_count) +{ + /* Currently the pipeline is rebuild on each draw command. Clearing the dirty flag for + * consistency with the internals of GPU module. */ + flag &= ~GPU_BATCH_DIRTY; + + /* Finalize graphics pipeline */ + VKContext &context = *VKContext::get(); + context.state_manager->apply_state(); + VKVertexAttributeObject vao; + vao.update_bindings(context, *this); + context.bind_graphics_pipeline(prim_type, vao); + + /* Bind geometry resources. */ + vao.bind(context); + VKIndexBuffer *index_buffer = index_buffer_get(); + const bool draw_indexed = index_buffer != nullptr; + if (draw_indexed) { + index_buffer->upload_data(); + index_buffer->bind(context); + context.command_buffer_get().draw( + index_buffer->index_len_get(), instance_count, index_buffer->index_start_get(), vertex_first, instance_first); + } + else { + context.command_buffer_get().draw(vertex_first, vertex_count, instance_first, instance_count); + } + + context.command_buffer_get().submit(); +} void VKBatch::draw_indirect(GPUStorageBuf * /*indirect_buf*/, intptr_t /*offset*/) {} @@ -20,4 +53,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..59fcae6d81d 100644 --- a/source/blender/gpu/vulkan/vk_batch.hh +++ b/source/blender/gpu/vulkan/vk_batch.hh @@ -10,15 +10,21 @@ #include "gpu_batch_private.hh" namespace blender::gpu { +class VKVertexBuffer; +class VKIndexBuffer; class VKBatch : public Batch { public: - void draw(int v_first, int v_count, int i_first, int i_count) override; + void draw(int vertex_first, int vertex_count, int instance_first, int instance_count) override; void draw_indirect(GPUStorageBuf *indirect_buf, intptr_t offset) override; void multi_draw_indirect(GPUStorageBuf *indirect_buf, 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_buffer.hh b/source/blender/gpu/vulkan/vk_buffer.hh index 3fa263f1e8a..175bc363b7f 100644 --- a/source/blender/gpu/vulkan/vk_buffer.hh +++ b/source/blender/gpu/vulkan/vk_buffer.hh @@ -61,4 +61,15 @@ class VKBuffer { void unmap(); }; +/** + * Helper struct to enable buffers to be bound with an offset. + * + * VKImmediate mode uses a single VKBuffer with multiple vertex layouts. Those layouts are send to + * the command buffer containing an offset. + */ +struct VKBufferWithOffset { + VKBuffer &buffer; + VkDeviceSize offset; +}; + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_command_buffer.cc b/source/blender/gpu/vulkan/vk_command_buffer.cc index 003478edc94..70f88709362 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.cc +++ b/source/blender/gpu/vulkan/vk_command_buffer.cc @@ -38,6 +38,13 @@ void VKCommandBuffer::init(const VkDevice vk_device, submission_id_.reset(); state.stage = Stage::Initial; + /* When a the last GHOST context is destroyed the device is deallocate. A moment later the GPU + * context is destroyed. The first step is to activate it. Activating would retrieve the device + * from GHOST which in that case is a VK_NULL_HANDLE.*/ + if (vk_device == VK_NULL_HANDLE) { + return; + } + if (vk_fence_ == VK_NULL_HANDLE) { VK_ALLOCATION_CALLBACKS; VkFenceCreateInfo fenceInfo{}; @@ -95,6 +102,11 @@ void VKCommandBuffer::bind(const uint32_t binding, bind(binding, vertex_buffer.vk_handle(), offset); } +void VKCommandBuffer::bind(const uint32_t binding, const VKBufferWithOffset &vertex_buffer) +{ + bind(binding, vertex_buffer.buffer.vk_handle(), vertex_buffer.offset); +} + void VKCommandBuffer::bind(const uint32_t binding, const VkBuffer &vk_vertex_buffer, const VkDeviceSize offset) diff --git a/source/blender/gpu/vulkan/vk_command_buffer.hh b/source/blender/gpu/vulkan/vk_command_buffer.hh index f921ce39aa3..84783fa2f5d 100644 --- a/source/blender/gpu/vulkan/vk_command_buffer.hh +++ b/source/blender/gpu/vulkan/vk_command_buffer.hh @@ -14,6 +14,7 @@ namespace blender::gpu { class VKBuffer; +struct VKBufferWithOffset; class VKDescriptorSet; class VKFrameBuffer; class VKIndexBuffer; @@ -141,6 +142,7 @@ class VKCommandBuffer : NonCopyable, NonMovable { const VKVertexBuffer &vertex_buffer, const VkDeviceSize offset); /* Bind the given buffer as a vertex buffer. */ + void bind(const uint32_t binding, const VKBufferWithOffset &vertex_buffer); void bind(const uint32_t binding, const VkBuffer &vk_vertex_buffer, const VkDeviceSize offset); void bind(const VKIndexBuffer &index_buffer, VkIndexType index_type); diff --git a/source/blender/gpu/vulkan/vk_context.cc b/source/blender/gpu/vulkan/vk_context.cc index 9529addc8ce..234ca47adf9 100644 --- a/source/blender/gpu/vulkan/vk_context.cc +++ b/source/blender/gpu/vulkan/vk_context.cc @@ -9,8 +9,11 @@ #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" +#include "vk_texture.hh" #include "GHOST_C-api.h" @@ -29,6 +32,7 @@ VKContext::VKContext(void *ghost_window, void *ghost_context) } state_manager = new VKStateManager(); + imm = new VKImmediate(); /* For off-screen contexts. Default frame-buffer is empty. */ VKFrameBuffer *framebuffer = new VKFrameBuffer("back_left"); @@ -36,7 +40,11 @@ VKContext::VKContext(void *ghost_window, void *ghost_context) active_fb = framebuffer; } -VKContext::~VKContext() {} +VKContext::~VKContext() +{ + delete imm; + imm = nullptr; +} void VKContext::sync_backbuffer() { @@ -85,9 +93,15 @@ void VKContext::activate() is_active_ = true; sync_backbuffer(); + + immActivate(); } -void VKContext::deactivate() {} +void VKContext::deactivate() +{ + immDeactivate(); + is_active_ = false; +} void VKContext::begin_frame() { @@ -106,9 +120,6 @@ void VKContext::flush() void VKContext::finish() { - if (has_active_framebuffer()) { - deactivate_framebuffer(); - } command_buffer_.submit(); } @@ -123,8 +134,17 @@ const VKStateManager &VKContext::state_manager_get() const return *static_cast(state_manager); } +VKStateManager &VKContext::state_manager_get() +{ + return *static_cast(state_manager); +} + /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Framebuffer + * \{ */ + void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer) { if (has_active_framebuffer()) { @@ -148,12 +168,47 @@ bool VKContext::has_active_framebuffer() const void VKContext::deactivate_framebuffer() { - BLI_assert(active_fb != nullptr); VKFrameBuffer *framebuffer = active_framebuffer_get(); + BLI_assert(framebuffer != nullptr); if (framebuffer->is_valid()) { command_buffer_.end_render_pass(*framebuffer); } active_fb = nullptr; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Compute pipeline + * \{ */ + +void VKContext::bind_compute_pipeline() +{ + VKShader *shader = unwrap(this->shader); + BLI_assert(shader); + VKPipeline &pipeline = shader->pipeline_get(); + pipeline.update_and_bind( + *this, shader->vk_pipeline_layout_get(), VK_PIPELINE_BIND_POINT_COMPUTE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Graphics pipeline + * \{ */ + +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, prim_type, vertex_attribute_object); + + VKPipeline &pipeline = shader->pipeline_get(); + pipeline.update_and_bind( + *this, shader->vk_pipeline_layout_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 d6b9ff2a36d..817b5285bd7 100644 --- a/source/blender/gpu/vulkan/vk_context.hh +++ b/source/blender/gpu/vulkan/vk_context.hh @@ -15,6 +15,8 @@ namespace blender::gpu { class VKFrameBuffer; +class VKVertexAttributeObject; +class VKBatch; class VKStateManager; class VKContext : public Context, NonCopyable { @@ -50,6 +52,9 @@ class VKContext : public Context, NonCopyable { void deactivate_framebuffer(); VKFrameBuffer *active_framebuffer_get() const; + void bind_compute_pipeline(); + void bind_graphics_pipeline(const GPUPrimType prim_type, + const VKVertexAttributeObject &vertex_attribute_object); void sync_backbuffer(); static VKContext *get(void) @@ -63,6 +68,7 @@ class VKContext : public Context, NonCopyable { } const VKStateManager &state_manager_get() const; + VKStateManager &state_manager_get(); }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_debug.cc b/source/blender/gpu/vulkan/vk_debug.cc index 3a3d505ed35..960f5beb3e3 100644 --- a/source/blender/gpu/vulkan/vk_debug.cc +++ b/source/blender/gpu/vulkan/vk_debug.cc @@ -118,6 +118,7 @@ void object_label(VkObjectType vk_object_type, uint64_t object_handle, const cha const VKDevice &device = VKBackend::get().device_get(); const VKDebuggingTools &debugging_tools = device.debugging_tools_get(); if (debugging_tools.enabled) { + const VKDevice &device = VKBackend::get().device_get(); VkDebugUtilsObjectNameInfoEXT info = {}; info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; info.objectType = vk_object_type; diff --git a/source/blender/gpu/vulkan/vk_descriptor_pools.cc b/source/blender/gpu/vulkan/vk_descriptor_pools.cc index 87273ad7e14..80775deefd9 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_pools.cc +++ b/source/blender/gpu/vulkan/vk_descriptor_pools.cc @@ -81,6 +81,7 @@ bool VKDescriptorPools::is_last_pool_active() std::unique_ptr VKDescriptorPools::allocate( const VkDescriptorSetLayout &descriptor_set_layout) { + BLI_assert(descriptor_set_layout != VK_NULL_HANDLE); VkDescriptorSetAllocateInfo allocate_info = {}; VkDescriptorPool pool = active_pool_get(); allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; diff --git a/source/blender/gpu/vulkan/vk_descriptor_set.cc b/source/blender/gpu/vulkan/vk_descriptor_set.cc index edf6d7b54a7..b9847a74d05 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set.cc +++ b/source/blender/gpu/vulkan/vk_descriptor_set.cc @@ -7,6 +7,7 @@ #include "vk_descriptor_set.hh" #include "vk_index_buffer.hh" +#include "vk_sampler.hh" #include "vk_shader.hh" #include "vk_storage_buffer.hh" #include "vk_texture.hh" @@ -27,7 +28,6 @@ VKDescriptorSet::~VKDescriptorSet() { if (vk_descriptor_set_ != VK_NULL_HANDLE) { /* Handle should be given back to the pool. */ - BLI_assert(VKContext::get()); VKDevice &device = VKBackend::get().device_; device.descriptor_pools_get().free(*this); BLI_assert(vk_descriptor_set_ == VK_NULL_HANDLE); @@ -81,7 +81,17 @@ void VKDescriptorSetTracker::image_bind(VKTexture &texture, { Binding &binding = ensure_location(location); binding.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - binding.vk_image_view = texture.vk_image_view_handle(); + binding.texture = &texture; +} + +void VKDescriptorSetTracker::bind(VKTexture &texture, + const VKDescriptorSet::Location location, + VKSampler &sampler) +{ + Binding &binding = ensure_location(location); + binding.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding.texture = &texture; + binding.vk_sampler = sampler.vk_handle(); } VKDescriptorSetTracker::Binding &VKDescriptorSetTracker::ensure_location( @@ -101,6 +111,7 @@ VKDescriptorSetTracker::Binding &VKDescriptorSetTracker::ensure_location( void VKDescriptorSetTracker::update(VKContext &context) { + BLI_assert(layout_ != VK_NULL_HANDLE); tracked_resource_for(context, !bindings_.is_empty()); std::unique_ptr &descriptor_set = active_descriptor_set(); VkDescriptorSet vk_descriptor_set = descriptor_set->vk_handle(); @@ -132,9 +143,12 @@ void VKDescriptorSetTracker::update(VKContext &context) if (!binding.is_image()) { continue; } + /* When updating the descriptor sets the layout of the texture should already be updated. */ + binding.texture->layout_ensure(context, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); VkDescriptorImageInfo image_info = {}; - image_info.imageView = binding.vk_image_view; - image_info.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + image_info.sampler = binding.vk_sampler; + image_info.imageView = binding.texture->vk_image_view_handle(); + image_info.imageLayout = binding.texture->current_layout_get(); image_infos.append(image_info); VkWriteDescriptorSet write_descriptor = {}; @@ -150,7 +164,6 @@ void VKDescriptorSetTracker::update(VKContext &context) BLI_assert_msg(image_infos.size() + buffer_infos.size() == descriptor_writes.size(), "Not all changes have been converted to a write descriptor. Check " "`Binding::is_buffer` and `Binding::is_image`."); - const VKDevice &device = VKBackend::get().device_get(); vkUpdateDescriptorSets( device.device_get(), descriptor_writes.size(), descriptor_writes.data(), 0, nullptr); @@ -158,7 +171,7 @@ void VKDescriptorSetTracker::update(VKContext &context) bindings_.clear(); } -std::unique_ptr VKDescriptorSetTracker::create_resource(VKContext &context) +std::unique_ptr VKDescriptorSetTracker::create_resource(VKContext & /*context*/) { VKDevice &device = VKBackend::get().device_; return device.descriptor_pools_get().allocate(layout_); diff --git a/source/blender/gpu/vulkan/vk_descriptor_set.hh b/source/blender/gpu/vulkan/vk_descriptor_set.hh index 50239d07be1..8f7c984fcd1 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set.hh +++ b/source/blender/gpu/vulkan/vk_descriptor_set.hh @@ -25,6 +25,7 @@ class VKTexture; class VKUniformBuffer; class VKVertexBuffer; class VKDescriptorSetTracker; +class VKSampler; /** * In vulkan shader resources (images and buffers) are grouped in descriptor sets. @@ -117,7 +118,8 @@ class VKDescriptorSetTracker : protected VKResourceTracker { VkBuffer vk_buffer = VK_NULL_HANDLE; VkDeviceSize buffer_size = 0; - VkImageView vk_image_view = VK_NULL_HANDLE; + VKTexture *texture = nullptr; + VkSampler vk_sampler = VK_NULL_HANDLE; Binding() { @@ -131,14 +133,17 @@ class VKDescriptorSetTracker : protected VKResourceTracker { bool is_image() const { - return ELEM(type, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); + return ELEM(type, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) && + texture != nullptr; } }; private: /** A list of bindings that needs to be updated. */ Vector bindings_; - VkDescriptorSetLayout layout_; + VkDescriptorSetLayout layout_ = VK_NULL_HANDLE; public: VKDescriptorSetTracker() {} @@ -149,7 +154,20 @@ class VKDescriptorSetTracker : protected VKResourceTracker { void bind_as_ssbo(VKIndexBuffer &buffer, VKDescriptorSet::Location location); void bind(VKStorageBuffer &buffer, VKDescriptorSet::Location location); void bind(VKUniformBuffer &buffer, VKDescriptorSet::Location location); + /* TODO: bind as image */ void image_bind(VKTexture &texture, VKDescriptorSet::Location location); + void bind(VKTexture &texture, VKDescriptorSet::Location location, VKSampler &sampler); + + /** + * Some shaders don't need any descriptor sets so we don't need to bind them. + * + * The result of this function determines if the descriptor set has any layout assigned. + * TODO: we might want to make descriptor sets optional for pipelines. + */ + bool has_layout() const + { + return layout_ != VK_NULL_HANDLE; + } /** * Update the descriptor set on the device. diff --git a/source/blender/gpu/vulkan/vk_framebuffer.cc b/source/blender/gpu/vulkan/vk_framebuffer.cc index 8ba46aa38e9..ca0a5ff5da4 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.cc +++ b/source/blender/gpu/vulkan/vk_framebuffer.cc @@ -503,8 +503,10 @@ void VKFrameBuffer::render_pass_free() VK_ALLOCATION_CALLBACKS const VKDevice &device = VKBackend::get().device_get(); - vkDestroyRenderPass(device.device_get(), vk_render_pass_, vk_allocation_callbacks); - vkDestroyFramebuffer(device.device_get(), vk_framebuffer_, vk_allocation_callbacks); + if (device.is_initialized()) { + vkDestroyRenderPass(device.device_get(), vk_render_pass_, vk_allocation_callbacks); + vkDestroyFramebuffer(device.device_get(), vk_framebuffer_, vk_allocation_callbacks); + } vk_render_pass_ = VK_NULL_HANDLE; vk_framebuffer_ = VK_NULL_HANDLE; } diff --git a/source/blender/gpu/vulkan/vk_immediate.cc b/source/blender/gpu/vulkan/vk_immediate.cc new file mode 100644 index 00000000000..d5f7d2c1618 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_immediate.cc @@ -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. + */ + +#include "vk_immediate.hh" +#include "vk_data_conversion.hh" + +namespace blender::gpu { + +VKImmediate::VKImmediate() {} +VKImmediate::~VKImmediate() {} + +uchar *VKImmediate::begin() +{ + VKContext &context = *VKContext::get(); + const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len); + const bool new_buffer_needed = !has_active_resource() || buffer_bytes_free() < bytes_needed; + + std::unique_ptr &buffer = tracked_resource_for(context, new_buffer_needed); + current_subbuffer_len_ = bytes_needed; + + uchar *data = static_cast(buffer->mapped_memory_get()); + return data + subbuffer_offset_get(); +} + +void VKImmediate::end() +{ + BLI_assert_msg(prim_type != GPU_PRIM_NONE, "Illegal state: not between an immBegin/End pair."); + if (vertex_len == 0) { + return; + } + + if (conversion_needed(vertex_format)) { + // Slow path + /* Determine the start of the subbuffer. The `vertex_data` attribute changes when new vertices + * are loaded. + */ + uchar *data = static_cast(active_resource()->mapped_memory_get()) + + subbuffer_offset_get(); + convert_in_place(data, vertex_format, vertex_len); + } + + 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); + + context.command_buffer_get().draw(0, vertex_len, 0, 1); + buffer_offset_ += current_subbuffer_len_; + current_subbuffer_len_ = 0; +} + +VkDeviceSize VKImmediate::subbuffer_offset_get() +{ + return buffer_offset_; +} + +VkDeviceSize VKImmediate::buffer_bytes_free() +{ + return active_resource()->size_in_bytes() - subbuffer_offset_get(); +} + +static VkDeviceSize new_buffer_size(size_t sub_buffer_size) +{ + return max_ii(sub_buffer_size, DEFAULT_INTERNAL_BUFFER_SIZE); +} + +std::unique_ptr VKImmediate::create_resource(VKContext & /*context*/) +{ + const size_t bytes_needed = vertex_buffer_size(&vertex_format, vertex_len); + std::unique_ptr result = std::make_unique(); + result->create(new_buffer_size(bytes_needed), + GPU_USAGE_DYNAMIC, + static_cast(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | + VK_BUFFER_USAGE_TRANSFER_DST_BIT)); + debug::object_label(result->vk_handle(), "Immediate"); + buffer_offset_ = 0; + return result; +} + +} // 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..117146fe05e --- /dev/null +++ b/source/blender/gpu/vulkan/vk_immediate.hh @@ -0,0 +1,51 @@ +/* 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 "gpu_vertex_format_private.h" + +#include "vk_buffer.hh" +#include "vk_context.hh" +#include "vk_mem_alloc.h" +#include "vk_resource_tracker.hh" +#include "vk_vertex_attribute_object.hh" + +namespace blender::gpu { + +/* Size of internal buffer. */ +constexpr size_t DEFAULT_INTERNAL_BUFFER_SIZE = (4 * 1024 * 1024); + +class VKImmediate : public Immediate, VKResourceTracker { + private: + VKVertexAttributeObject vertex_attributes_; + + VkDeviceSize buffer_offset_ = 0; + VkDeviceSize current_subbuffer_len_ = 0; + + public: + VKImmediate(); + virtual ~VKImmediate(); + + uchar *begin(void) override; + void end(void) override; + + friend class VKVertexAttributeObject; + + private: + VkDeviceSize subbuffer_offset_get(); + VkDeviceSize buffer_bytes_free(); + + std::unique_ptr create_resource(VKContext &context) override; +}; + +} // 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 1b738029af9..0fcb543ab95 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.cc +++ b/source/blender/gpu/vulkan/vk_pipeline.cc @@ -7,26 +7,39 @@ #include "vk_pipeline.hh" #include "vk_backend.hh" +#include "vk_batch.hh" #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 { +VKPipeline::VKPipeline(VkDescriptorSetLayout vk_descriptor_set_layout, + VKPushConstants &&push_constants) + : active_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) - : vk_pipeline_(vk_pipeline), + : active_vk_pipeline_(vk_pipeline), descriptor_set_(vk_descriptor_set_layout), push_constants_(std::move(push_constants)) { + vk_pipelines_.append(vk_pipeline); } VKPipeline::~VKPipeline() { VK_ALLOCATION_CALLBACKS const VKDevice &device = VKBackend::get().device_get(); - if (vk_pipeline_ != VK_NULL_HANDLE) { - vkDestroyPipeline(device.device_get(), vk_pipeline_, vk_allocation_callbacks); + for (VkPipeline vk_pipeline : vk_pipelines_) { + vkDestroyPipeline(device.device_get(), vk_pipeline, vk_allocation_callbacks); } } @@ -64,14 +77,137 @@ 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_; + return active_vk_pipeline_; } bool VKPipeline::is_valid() const { - return vk_pipeline_ != VK_NULL_HANDLE; + return active_vk_pipeline_ != VK_NULL_HANDLE; +} + +void VKPipeline::finalize(VKContext &context, + VkShaderModule vertex_module, + VkShaderModule geometry_module, + VkShaderModule fragment_module, + VkPipelineLayout &pipeline_layout, + const GPUPrimType prim_type, + const VKVertexAttributeObject &vertex_attribute_object) +{ + 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 geometry_stage_info = {}; + geometry_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + geometry_stage_info.stage = VK_SHADER_STAGE_GEOMETRY_BIT; + geometry_stage_info.module = geometry_module; + geometry_stage_info.pName = "main"; + pipeline_stages.append(geometry_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 = vertex_attribute_object.bindings.size(); + vertex_input_state.pVertexBindingDescriptions = vertex_attribute_object.bindings.data(); + 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; + + /* 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(prim_type); + pipeline_create_info.pInputAssemblyState = &pipeline_input_assembly; + + /* 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; + + /* 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; + pipeline_create_info.pDepthStencilState = &state_manager.depth_stencil_state; + + const VKDevice &device = VKBackend::get().device_get(); + vkCreateGraphicsPipelines(device.device_get(), + VK_NULL_HANDLE, + 1, + &pipeline_create_info, + vk_allocation_callbacks, + &active_vk_pipeline_); + /* TODO: we should cache several pipeline instances and detect pipelines we can reuse. This might + * also be done using a VkPipelineCache. For now we just destroy any available pipeline so it + * won't be overwritten by the newly created one. */ + vk_pipelines_.append(active_vk_pipeline_); + debug::object_label(active_vk_pipeline_, "GraphicsPipeline"); +} + +void VKPipeline::update_and_bind(VKContext &context, + VkPipelineLayout vk_pipeline_layout, + VkPipelineBindPoint vk_pipeline_bind_point) +{ + VKCommandBuffer &command_buffer = context.command_buffer_get(); + command_buffer.bind(*this, vk_pipeline_bind_point); + push_constants_.update(context); + if (descriptor_set_.has_layout()) { + descriptor_set_.update(context); + command_buffer.bind( + *descriptor_set_.active_descriptor_set(), vk_pipeline_layout, vk_pipeline_bind_point); + } } } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_pipeline.hh b/source/blender/gpu/vulkan/vk_pipeline.hh index 565b41ba41d..c1c7c616b82 100644 --- a/source/blender/gpu/vulkan/vk_pipeline.hh +++ b/source/blender/gpu/vulkan/vk_pipeline.hh @@ -14,29 +14,49 @@ #include "vk_common.hh" #include "vk_descriptor_set.hh" +#include "vk_pipeline_state.hh" #include "vk_push_constants.hh" namespace blender::gpu { class VKContext; +class VKShader; +class VKVertexAttributeObject; +class VKBatch; +/** + * 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; + /* Active pipeline handle. */ + VkPipeline active_vk_pipeline_ = VK_NULL_HANDLE; + /** Keep track of all pipelines as they can still be in flight. */ + Vector vk_pipelines_; VKDescriptorSetTracker descriptor_set_; VKPushConstants push_constants_; + VKPipelineStateManager state_manager_; public: 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); VKPipeline &operator=(VKPipeline &&other) { - vk_pipeline_ = other.vk_pipeline_; - other.vk_pipeline_ = VK_NULL_HANDLE; + active_vk_pipeline_ = other.active_vk_pipeline_; + other.active_vk_pipeline_ = VK_NULL_HANDLE; descriptor_set_ = std::move(other.descriptor_set_); push_constants_ = std::move(other.push_constants_); + vk_pipelines_ = std::move(other.vk_pipelines_); + other.vk_pipelines_.clear(); return *this; } @@ -44,6 +64,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() { @@ -55,8 +77,28 @@ class VKPipeline : NonCopyable { return push_constants_; } + VKPipelineStateManager &state_manager_get() + { + return state_manager_; + } + VkPipeline vk_handle() const; bool is_valid() const; + + void finalize(VKContext &context, + VkShaderModule vertex_module, + VkShaderModule geometry_module, + VkShaderModule fragment_module, + VkPipelineLayout &pipeline_layout, + const GPUPrimType prim_type, + const VKVertexAttributeObject &vertex_attribute_object); + + /** + * Update PushConstants, DescriptorSets and bind pipeline to command buffer. + */ + void update_and_bind(VKContext &context, + VkPipelineLayout vk_pipeline_layout, + VkPipelineBindPoint vk_pipeline_bind_point); }; } // namespace blender::gpu 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..e4d78eee17c --- /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; + + /* TODO should be extracted from current framebuffer and should not be done here and now. */ + /* When the attachments differ the state should be forced. */ + 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(); +} + +void VKPipelineStateManager::set_state(const GPUState &state, const GPUStateMutable &mutable_state) +{ + 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_resource_tracker.hh b/source/blender/gpu/vulkan/vk_resource_tracker.hh index 8f5b9ffa1e8..9171fe9b475 100644 --- a/source/blender/gpu/vulkan/vk_resource_tracker.hh +++ b/source/blender/gpu/vulkan/vk_resource_tracker.hh @@ -158,6 +158,14 @@ template class VKResourceTracker : NonCopyable { */ virtual std::unique_ptr create_resource(VKContext &context) = 0; + /** + * Does this instance have an active resource. + */ + bool has_active_resource() + { + return !tracked_resources_.is_empty(); + } + /** * Return the active resource of the tracker. */ diff --git a/source/blender/gpu/vulkan/vk_sampler.cc b/source/blender/gpu/vulkan/vk_sampler.cc new file mode 100644 index 00000000000..3a22bd3d4b6 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_sampler.cc @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation */ + +/** \file + * \ingroup gpu + */ + +#include "vk_sampler.hh" +#include "vk_backend.hh" +#include "vk_context.hh" +#include "vk_memory.hh" + +namespace blender::gpu { +VKSampler::~VKSampler() +{ + free(); +} + +void VKSampler::create() +{ + BLI_assert(vk_sampler_ == VK_NULL_HANDLE); + + VK_ALLOCATION_CALLBACKS + + VkSamplerCreateInfo sampler_info = {}; + sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + const VKDevice &device = VKBackend::get().device_get(); + vkCreateSampler(device.device_get(), &sampler_info, vk_allocation_callbacks, &vk_sampler_); + debug::object_label(vk_sampler_, "DummySampler"); +} + +void VKSampler::free() +{ + VK_ALLOCATION_CALLBACKS + + if (vk_sampler_ != VK_NULL_HANDLE) { + const VKDevice &device = VKBackend::get().device_get(); + if (device.device_get() != VK_NULL_HANDLE) { + vkDestroySampler(device.device_get(), vk_sampler_, vk_allocation_callbacks); + } + vk_sampler_ = VK_NULL_HANDLE; + } +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_sampler.hh b/source/blender/gpu/vulkan/vk_sampler.hh new file mode 100644 index 00000000000..d817af49e13 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_sampler.hh @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "gpu_shader_private.hh" + +#include "vk_common.hh" + +#include "BLI_utility_mixins.hh" + +namespace blender::gpu { +class VKContext; + +class VKSampler : public NonCopyable { + VkSampler vk_sampler_ = VK_NULL_HANDLE; + + public: + virtual ~VKSampler(); + void create(); + void free(); + + VkSampler vk_handle() + { + BLI_assert(vk_sampler_ != VK_NULL_HANDLE); + return vk_sampler_; + } +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_shader.cc b/source/blender/gpu/vulkan/vk_shader.cc index 77036778191..805367e6de5 100644 --- a/source/blender/gpu/vulkan/vk_shader.cc +++ b/source/blender/gpu/vulkan/vk_shader.cc @@ -578,7 +578,6 @@ VKShader::VKShader(const char *name) : Shader(name) VKShader::~VKShader() { VK_ALLOCATION_CALLBACKS - const VKDevice &device = VKBackend::get().device_get(); if (vertex_module_ != VK_NULL_HANDLE) { vkDestroyShaderModule(device.device_get(), vertex_module_, vk_allocation_callbacks); @@ -667,16 +666,18 @@ 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(device.device_get()); + 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( + pipeline_ = VKPipeline::create_compute_pipeline( compute_module_, layout_, pipeline_layout_, vk_interface->push_constants_layout_get()); - result = compute_pipeline_.is_valid(); + result = pipeline_.is_valid(); } if (result) { @@ -688,36 +689,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) { @@ -958,26 +929,27 @@ bool VKShader::transform_feedback_enable(GPUVertBuf *) void VKShader::transform_feedback_disable() {} +void VKShader::update_graphics_pipeline(VKContext &context, + const GPUPrimType prim_type, + const VKVertexAttributeObject &vertex_attribute_object) +{ + BLI_assert(is_graphics_shader()); + pipeline_get().finalize(context, + vertex_module_, + geometry_module_, + fragment_module_, + pipeline_layout_, + prim_type, + vertex_attribute_object); +} + 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(); - } + /* Intentionally empty. Binding of the pipeline are done just before drawing/dispatching. + * See #VKPipeline.update_and_bind */ } -void VKShader::unbind() -{ - if (is_compute_shader()) { - } - else { - BLI_assert_unreachable(); - } -} +void VKShader::unbind() {} void VKShader::uniform_float(int location, int comp_len, int array_size, const float *data) { @@ -1217,7 +1189,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 d1f0e3a64cd..62b11d2b0d3 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,10 @@ class VKShader : public Shader { const VKShaderInterface &interface_get() const; + void update_graphics_pipeline(VKContext &context, + const GPUPrimType prim_type, + const VKVertexAttributeObject &vertex_attribute_object); + 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 +84,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 +96,14 @@ class VKShader : public Shader { } }; +static inline VKShader &unwrap(Shader &shader) +{ + return static_cast(shader); +} + +static inline VKShader *unwrap(Shader *shader) +{ + return static_cast(shader); +} + } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_state_manager.cc b/source/blender/gpu/vulkan/vk_state_manager.cc index 5815b9cae6a..5c5b82899d9 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.cc +++ b/source/blender/gpu/vulkan/vk_state_manager.cc @@ -6,12 +6,67 @@ */ #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() {} +#include "GPU_capabilities.h" -void VKStateManager::force_state() {} +namespace blender::gpu { + +VKStateManager::VKStateManager() +{ + sampler_.create(); + constexpr int max_bindings = 16; + image_bindings_ = Array(max_bindings); + image_bindings_.fill(ImageBinding()); + texture_bindings_ = Array(max_bindings); + texture_bindings_.fill(ImageBinding()); + uniform_buffer_bindings_ = Array(max_bindings); + uniform_buffer_bindings_.fill(UniformBufferBinding()); +} + +void VKStateManager::apply_state() +{ + VKContext &context = *VKContext::get(); + if (context.shader) { + VKShader &shader = unwrap(*context.shader); + VKPipeline &pipeline = shader.pipeline_get(); + pipeline.state_manager_get().set_state(state, mutable_state); + + for (int binding : IndexRange(image_bindings_.size())) { + if (image_bindings_[binding].texture == nullptr) { + continue; + } + image_bindings_[binding].texture->image_bind(binding); + } + + for (int binding : IndexRange(image_bindings_.size())) { + if (texture_bindings_[binding].texture == nullptr) { + continue; + } + texture_bindings_[binding].texture->bind(binding, sampler_); + } + + for (int binding : IndexRange(uniform_buffer_bindings_.size())) { + if (uniform_buffer_bindings_[binding].buffer == nullptr) { + continue; + } + uniform_buffer_bindings_[binding].buffer->bind( + binding, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER); + } + } +} + +void VKStateManager::force_state() +{ + VKContext &context = *VKContext::get(); + BLI_assert(context.shader); + 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*/) { @@ -22,21 +77,69 @@ void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/) command_buffer.submit(); } -void VKStateManager::texture_bind(Texture * /*tex*/, GPUSamplerState /*sampler*/, int /*unit*/) {} +void VKStateManager::texture_bind(Texture *tex, GPUSamplerState /*sampler*/, int unit) +{ + VKTexture *texture = unwrap(tex); + texture_bindings_[unit].texture = texture; +} -void VKStateManager::texture_unbind(Texture * /*tex*/) {} +void VKStateManager::texture_unbind(Texture *tex) +{ + VKTexture *texture = unwrap(tex); + for (ImageBinding &binding : texture_bindings_) { + if (binding.texture == texture) { + binding.texture = nullptr; + } + } +} -void VKStateManager::texture_unbind_all() {} +void VKStateManager::texture_unbind_all() +{ + for (ImageBinding &binding : texture_bindings_) { + if (binding.texture != nullptr) { + binding.texture = nullptr; + } + } +} void VKStateManager::image_bind(Texture *tex, int binding) { VKTexture *texture = unwrap(tex); - texture->image_bind(binding); + image_bindings_[binding].texture = texture; } -void VKStateManager::image_unbind(Texture * /*tex*/) {} +void VKStateManager::image_unbind(Texture *tex) +{ + VKTexture *texture = unwrap(tex); + for (ImageBinding &binding : image_bindings_) { + if (binding.texture == texture) { + binding.texture = nullptr; + } + } +} -void VKStateManager::image_unbind_all() {} +void VKStateManager::image_unbind_all() +{ + for (ImageBinding &binding : texture_bindings_) { + if (binding.texture != nullptr) { + binding.texture = nullptr; + } + } +} + +void VKStateManager::uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int slot) +{ + uniform_buffer_bindings_[slot].buffer = uniform_buffer; +} + +void VKStateManager::uniform_buffer_unbind(VKUniformBuffer *uniform_buffer) +{ + for (UniformBufferBinding &binding : uniform_buffer_bindings_) { + if (binding.buffer == uniform_buffer) { + binding.buffer = nullptr; + } + } +} void VKStateManager::texture_unpack_row_length_set(uint len) { diff --git a/source/blender/gpu/vulkan/vk_state_manager.hh b/source/blender/gpu/vulkan/vk_state_manager.hh index de12c0b5457..a454d74a7ef 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.hh +++ b/source/blender/gpu/vulkan/vk_state_manager.hh @@ -9,11 +9,33 @@ #include "gpu_state_private.hh" +#include "BLI_array.hh" + +#include "vk_sampler.hh" + namespace blender::gpu { +class VKTexture; +class VKUniformBuffer; + class VKStateManager : public StateManager { - uint texture_unpack_row_length_; + /* Dummy sampler for now.*/ + VKSampler sampler_; + + uint texture_unpack_row_length_ = 0; + + struct ImageBinding { + VKTexture *texture = nullptr; + }; + struct UniformBufferBinding { + VKUniformBuffer *buffer = nullptr; + }; + Array image_bindings_; + Array texture_bindings_; + Array uniform_buffer_bindings_; public: + VKStateManager(); + void apply_state() override; void force_state() override; @@ -27,6 +49,9 @@ class VKStateManager : public StateManager { void image_unbind(Texture *tex) override; void image_unbind_all() override; + void uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int slot); + void uniform_buffer_unbind(VKUniformBuffer *uniform_buffer); + void texture_unpack_row_length_set(uint len) override; /** diff --git a/source/blender/gpu/vulkan/vk_texture.cc b/source/blender/gpu/vulkan/vk_texture.cc index 311e3c193db..e636fe7172d 100644 --- a/source/blender/gpu/vulkan/vk_texture.cc +++ b/source/blender/gpu/vulkan/vk_texture.cc @@ -229,6 +229,16 @@ static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage, result = static_cast(result | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); } + /* Disable some usages based on the given format flag to support more devices. */ + if (format_flag & GPU_FORMAT_SRGB) { + /* NVIDIA devices don't create SRGB textures when it storage bit is set. */ + result = static_cast(result & ~VK_IMAGE_USAGE_STORAGE_BIT); + } + if (format_flag & (GPU_FORMAT_DEPTH | GPU_FORMAT_STENCIL)) { + /* NVIDIA devices don't create depth textures when it storage bit is set. */ + result = static_cast(result & ~VK_IMAGE_USAGE_STORAGE_BIT); + } + return result; } @@ -311,6 +321,24 @@ bool VKTexture::allocate() return result == VK_SUCCESS; } +// TODO: move texture/image bindings to shader. +void VKTexture::bind(int unit, VKSampler &sampler) +{ + if (!is_allocated()) { + allocate(); + } + VKContext &context = *VKContext::get(); + VKShader *shader = static_cast(context.shader); + const VKShaderInterface &shader_interface = shader->interface_get(); + const std::optional location = + shader_interface.descriptor_set_location( + shader::ShaderCreateInfo::Resource::BindType::SAMPLER, unit); + if (location) { + VKDescriptorSetTracker &descriptor_set = shader->pipeline_get().descriptor_set_get(); + descriptor_set.bind(*this, *location, sampler); + } +} + void VKTexture::image_bind(int binding) { if (!is_allocated()) { diff --git a/source/blender/gpu/vulkan/vk_texture.hh b/source/blender/gpu/vulkan/vk_texture.hh index adb65485825..7e9e803e596 100644 --- a/source/blender/gpu/vulkan/vk_texture.hh +++ b/source/blender/gpu/vulkan/vk_texture.hh @@ -12,6 +12,8 @@ namespace blender::gpu { +class VKSampler; + class VKTexture : public Texture { VkImage vk_image_ = VK_NULL_HANDLE; VkImageView vk_image_view_ = VK_NULL_HANDLE; @@ -25,6 +27,7 @@ class VKTexture : public Texture { public: VKTexture(const char *name) : Texture(name) {} + virtual ~VKTexture() override; void init(VkImage vk_image, VkImageLayout layout); @@ -46,7 +49,9 @@ class VKTexture : public Texture { /* TODO(fclem): Legacy. Should be removed at some point. */ uint gl_bindcode_get() const override; + void bind(int unit, VKSampler &sampler); void image_bind(int location); + VkImage vk_image_handle() const { BLI_assert(vk_image_ != VK_NULL_HANDLE); diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.cc b/source/blender/gpu/vulkan/vk_uniform_buffer.cc index 8c39b3f16de..840892986a0 100644 --- a/source/blender/gpu/vulkan/vk_uniform_buffer.cc +++ b/source/blender/gpu/vulkan/vk_uniform_buffer.cc @@ -9,6 +9,7 @@ #include "vk_context.hh" #include "vk_shader.hh" #include "vk_shader_interface.hh" +#include "vk_state_manager.hh" namespace blender::gpu { @@ -54,7 +55,9 @@ void VKUniformBuffer::bind(int slot, shader::ShaderCreateInfo::Resource::BindTyp void VKUniformBuffer::bind(int slot) { - bind(slot, shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER); + /* Uniform buffers can be bound without an shader. */ + VKContext &context = *VKContext::get(); + context.state_manager_get().uniform_buffer_bind(this, slot); } void VKUniformBuffer::bind_as_ssbo(int slot) @@ -62,6 +65,10 @@ void VKUniformBuffer::bind_as_ssbo(int slot) bind(slot, shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER); } -void VKUniformBuffer::unbind() {} +void VKUniformBuffer::unbind() +{ + VKContext &context = *VKContext::get(); + context.state_manager_get().uniform_buffer_unbind(this); +} } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_uniform_buffer.hh b/source/blender/gpu/vulkan/vk_uniform_buffer.hh index a31915cfce4..0b8b51bcd26 100644 --- a/source/blender/gpu/vulkan/vk_uniform_buffer.hh +++ b/source/blender/gpu/vulkan/vk_uniform_buffer.hh @@ -25,6 +25,7 @@ class VKUniformBuffer : public UniformBuf, NonCopyable { void clear_to_zero() override; void bind(int slot) override; void bind_as_ssbo(int slot) override; + void bind(int slot, shader::ShaderCreateInfo::Resource::BindType bind_type); void unbind() override; VkBuffer vk_handle() const @@ -39,7 +40,6 @@ class VKUniformBuffer : public UniformBuf, NonCopyable { private: void allocate(); - void bind(int slot, shader::ShaderCreateInfo::Resource::BindType bind_type); }; } // namespace blender::gpu 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..f3e3444f277 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.cc @@ -0,0 +1,198 @@ +/* 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_immediate.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(); + buffers.clear(); +} + +VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttributeObject &other) +{ + if (this == &other) { + return *this; + } + + is_valid = other.is_valid; + info = other.info; + bindings.clear(); + bindings.extend(other.bindings); + attributes.clear(); + attributes.extend(other.attributes); + vbos.clear(); + vbos.extend(other.vbos); + buffers.clear(); + buffers.extend(other.buffers); + 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; + + /* 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()) { + VKBufferWithOffset &buffer = buffers[attribute.binding]; + context.command_buffer_get().bind(attribute.binding, buffer); + } + } +} + +void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch) +{ + clear(); + const VKShaderInterface &interface = unwrap(context.shader)->interface_get(); + AttributeMask occupied_attributes = 0; + + for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) { + VKVertexBuffer *vbo = batch.instance_buffer_get(v); + if (vbo) { + update_bindings( + vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, true); + } + } + for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) { + VKVertexBuffer *vbo = batch.vertex_buffer_get(v); + if (vbo) { + update_bindings( + vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false); + } + } + + is_valid = true; + + BLI_assert(interface.enabled_attr_mask_ == occupied_attributes); +} + +void VKVertexAttributeObject::update_bindings(VKImmediate &immediate) +{ + clear(); + const VKShaderInterface &interface = unwrap(unwrap(immediate.shader))->interface_get(); + AttributeMask occupied_attributes = 0; + + VKBufferWithOffset immediate_buffer = {*immediate.active_resource(), + immediate.subbuffer_offset_get()}; + + 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, + VKBufferWithOffset *immediate_vertex_buffer, + const int64_t vertex_len, + const VKShaderInterface &interface, + AttributeMask &r_occupied_attributes, + const bool use_instancing) +{ + BLI_assert(vertex_buffer || immediate_vertex_buffer); + BLI_assert(!(vertex_buffer && immediate_vertex_buffer)); + + if (vertex_format.attr_len <= 0) { + return; + } + + uint32_t offset = 0; + uint32_t stride = vertex_format.stride; + + 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 { + offset = attribute.offset; + } + + 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(&vertex_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; + 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, + static_cast(attribute.fetch_mode)); + 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); + if (vertex_buffer) { + vbos.append(vertex_buffer); + } + if (immediate_vertex_buffer) { + buffers.append(*immediate_vertex_buffer); + } + } + } +} + +} // 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..fa14bdd31bb --- /dev/null +++ b/source/blender/gpu/vulkan/vk_vertex_attribute_object.hh @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2023 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup gpu + */ + +#include "vk_buffer.hh" +#include "vk_common.hh" + +#include "BLI_vector.hh" + +#pragma once + +namespace blender::gpu { + +class VKVertexBuffer; +class VKContext; +class VKBatch; +class VKShaderInterface; +class VKImmediate; + +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; + /* Used for batches. */ + Vector vbos; + /* Used for immediate mode. */ + Vector buffers; + + VKVertexAttributeObject(); + void clear(); + + void bind(VKContext &context); + + // Copy assignment operator. + VKVertexAttributeObject &operator=(const VKVertexAttributeObject &other); + + void update_bindings(const VKContext &context, VKBatch &batch); + void update_bindings(VKImmediate &immediate); + + private: + void update_bindings(const GPUVertFormat &vertex_format, + VKVertexBuffer *vertex_buffer, + VKBufferWithOffset *immediate_vertex_buffer, + const int64_t vertex_len, + const VKShaderInterface &interface, + AttributeMask &r_occupied_attributes, + const bool use_instancing); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.cc b/source/blender/gpu/vulkan/vk_vertex_buffer.cc index b28e0237c4b..7ffc5d99926 100644 --- a/source/blender/gpu/vulkan/vk_vertex_buffer.cc +++ b/source/blender/gpu/vulkan/vk_vertex_buffer.cc @@ -56,6 +56,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__); } diff --git a/source/blender/gpu/vulkan/vk_vertex_buffer.hh b/source/blender/gpu/vulkan/vk_vertex_buffer.hh index 9aa63261b3f..9e653a28b96 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(); }