From 513250051e641ccceae8033bb66f5c7017f9b633 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Thu, 8 Jun 2023 16:07:38 +0200 Subject: [PATCH] Vulkan: Image Views The ownership of image views depends on how they are used. When used as an framebuffer attachment the image view is owned by the framebuffer. When used as a image/texture in a shader the ownership of the image view is the texture itself. Pull Request: https://projects.blender.org/blender/blender/pulls/108765 --- source/blender/gpu/CMakeLists.txt | 2 + .../blender/gpu/vulkan/vk_descriptor_set.cc | 2 +- source/blender/gpu/vulkan/vk_framebuffer.cc | 14 ++-- source/blender/gpu/vulkan/vk_framebuffer.hh | 3 + source/blender/gpu/vulkan/vk_image_view.cc | 68 +++++++++++++++++++ source/blender/gpu/vulkan/vk_image_view.hh | 44 ++++++++++++ source/blender/gpu/vulkan/vk_texture.cc | 68 ++++++++++++++----- source/blender/gpu/vulkan/vk_texture.hh | 34 ++++++++-- 8 files changed, 205 insertions(+), 30 deletions(-) create mode 100644 source/blender/gpu/vulkan/vk_image_view.cc create mode 100644 source/blender/gpu/vulkan/vk_image_view.hh diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 30addfaa0b7..9d7e453210e 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -215,6 +215,7 @@ set(VULKAN_SRC vulkan/vk_drawlist.cc vulkan/vk_fence.cc vulkan/vk_framebuffer.cc + vulkan/vk_image_view.cc vulkan/vk_immediate.cc vulkan/vk_index_buffer.cc vulkan/vk_memory.cc @@ -250,6 +251,7 @@ set(VULKAN_SRC vulkan/vk_drawlist.hh vulkan/vk_fence.hh vulkan/vk_framebuffer.hh + vulkan/vk_image_view.hh vulkan/vk_immediate.hh vulkan/vk_index_buffer.hh vulkan/vk_memory.hh diff --git a/source/blender/gpu/vulkan/vk_descriptor_set.cc b/source/blender/gpu/vulkan/vk_descriptor_set.cc index 5751162bba1..3698aecd6fa 100644 --- a/source/blender/gpu/vulkan/vk_descriptor_set.cc +++ b/source/blender/gpu/vulkan/vk_descriptor_set.cc @@ -174,7 +174,7 @@ void VKDescriptorSetTracker::update(VKContext &context) binding.texture->layout_ensure(context, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); VkDescriptorImageInfo image_info = {}; image_info.sampler = binding.vk_sampler; - image_info.imageView = binding.texture->vk_image_view_handle(); + image_info.imageView = binding.texture->image_view_get().vk_handle(); image_info.imageLayout = binding.texture->current_layout_get(); image_infos.append(image_info); diff --git a/source/blender/gpu/vulkan/vk_framebuffer.cc b/source/blender/gpu/vulkan/vk_framebuffer.cc index 5a584a8d97d..f0ccf9ad5da 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.cc +++ b/source/blender/gpu/vulkan/vk_framebuffer.cc @@ -372,6 +372,8 @@ void VKFrameBuffer::render_pass_create() std::array attachment_descriptions; std::array image_views; std::array attachment_references; + image_views_.clear(); + bool has_depth_attachment = false; bool found_attachment = false; int depth_location = -1; @@ -402,7 +404,8 @@ void VKFrameBuffer::render_pass_create() /* Ensure texture is allocated to ensure the image view. */ VKTexture &texture = *static_cast(unwrap(attachment.tex)); texture.ensure_allocated(); - image_views[attachment_location] = texture.vk_image_view_handle(); + image_views_.append(VKImageView(texture, attachment.mip, name_)); + image_views[attachment_location] = image_views_.last().vk_handle(); VkAttachmentDescription &attachment_description = attachment_descriptions[attachment_location]; @@ -411,10 +414,10 @@ void VKFrameBuffer::render_pass_create() attachment_description.samples = VK_SAMPLE_COUNT_1_BIT; attachment_description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachment_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment_description.initialLayout = VK_IMAGE_LAYOUT_GENERAL; - attachment_description.finalLayout = VK_IMAGE_LAYOUT_GENERAL; + attachment_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + attachment_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment_description.initialLayout = texture.current_layout_get(); + attachment_description.finalLayout = texture.current_layout_get(); /* Create the attachment reference. */ const bool is_depth_attachment = ELEM( @@ -497,6 +500,7 @@ void VKFrameBuffer::render_pass_free() vkDestroyRenderPass(device.device_get(), vk_render_pass_, vk_allocation_callbacks); vkDestroyFramebuffer(device.device_get(), vk_framebuffer_, vk_allocation_callbacks); } + image_views_.clear(); vk_render_pass_ = VK_NULL_HANDLE; vk_framebuffer_ = VK_NULL_HANDLE; } diff --git a/source/blender/gpu/vulkan/vk_framebuffer.hh b/source/blender/gpu/vulkan/vk_framebuffer.hh index d0addb9c8af..7f6d1c44314 100644 --- a/source/blender/gpu/vulkan/vk_framebuffer.hh +++ b/source/blender/gpu/vulkan/vk_framebuffer.hh @@ -15,6 +15,7 @@ #include "gpu_framebuffer_private.hh" #include "vk_common.hh" +#include "vk_image_view.hh" namespace blender::gpu { @@ -41,6 +42,8 @@ class VKFrameBuffer : public FrameBuffer { */ bool flip_viewport_ = false; + Vector image_views_; + public: /** * Create a conventional framebuffer to attach texture to. diff --git a/source/blender/gpu/vulkan/vk_image_view.cc b/source/blender/gpu/vulkan/vk_image_view.cc new file mode 100644 index 00000000000..c8370753f40 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_image_view.cc @@ -0,0 +1,68 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#include "vk_image_view.hh" +#include "vk_backend.hh" +#include "vk_debug.hh" +#include "vk_device.hh" +#include "vk_memory.hh" +#include "vk_texture.hh" + +namespace blender::gpu { + +VKImageView::VKImageView(VKTexture &texture, int mip_level, StringRefNull name) + : vk_image_view_(create_vk_image_view(texture, mip_level, name)) +{ + BLI_assert(vk_image_view_ != VK_NULL_HANDLE); +} + +VKImageView::VKImageView(VkImageView vk_image_view) : vk_image_view_(vk_image_view) +{ + BLI_assert(vk_image_view_ != VK_NULL_HANDLE); +} + +VKImageView::VKImageView(VKImageView &&other) +{ + vk_image_view_ = other.vk_image_view_; + other.vk_image_view_ = VK_NULL_HANDLE; +} + +VKImageView::~VKImageView() +{ + if (vk_image_view_ != VK_NULL_HANDLE) { + VK_ALLOCATION_CALLBACKS + const VKDevice &device = VKBackend::get().device_get(); + vkDestroyImageView(device.device_get(), vk_image_view_, vk_allocation_callbacks); + } +} +VkImageView VKImageView::create_vk_image_view(VKTexture &texture, + int mip_level, + StringRefNull name) +{ + + VK_ALLOCATION_CALLBACKS + VkImageViewCreateInfo image_view_info = {}; + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.image = texture.vk_image_handle(); + image_view_info.viewType = to_vk_image_view_type(texture.type_get()); + image_view_info.format = to_vk_format(texture.format_get()); + image_view_info.components = to_vk_component_mapping(texture.format_get()); + image_view_info.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(texture.format_get()); + image_view_info.subresourceRange.baseMipLevel = mip_level; + image_view_info.subresourceRange.levelCount = 1; + image_view_info.subresourceRange.baseArrayLayer = 0; + image_view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + const VKDevice &device = VKBackend::get().device_get(); + VkImageView image_view = VK_NULL_HANDLE; + vkCreateImageView(device.device_get(), &image_view_info, vk_allocation_callbacks, &image_view); + debug::object_label(image_view, name.c_str()); + return image_view; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_image_view.hh b/source/blender/gpu/vulkan/vk_image_view.hh new file mode 100644 index 00000000000..d1e6f78d2f0 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_image_view.hh @@ -0,0 +1,44 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "vk_common.hh" + +#include "BLI_string_ref.hh" +#include "BLI_utility_mixins.hh" + +namespace blender::gpu { +class VKTexture; + +class VKImageView : NonCopyable { + VkImageView vk_image_view_ = VK_NULL_HANDLE; + + public: + VKImageView(VKTexture &texture, int mip_level, StringRefNull name); + + /** + * Wrap the given vk_image_view handle. Note that the vk_image_view handle ownership is + * transferred to VKImageView. + */ + VKImageView(VkImageView vk_image_view); + + VKImageView(VKImageView &&other); + ~VKImageView(); + + VkImageView vk_handle() const + { + BLI_assert(vk_image_view_ != VK_NULL_HANDLE); + return vk_image_view_; + } + + private: + static VkImageView create_vk_image_view(VKTexture &texture, int mip_level, StringRefNull name); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_texture.cc b/source/blender/gpu/vulkan/vk_texture.cc index 81ed84c80c0..e0f8f395f76 100644 --- a/source/blender/gpu/vulkan/vk_texture.cc +++ b/source/blender/gpu/vulkan/vk_texture.cc @@ -25,11 +25,9 @@ namespace blender::gpu { VKTexture::~VKTexture() { - VK_ALLOCATION_CALLBACKS if (is_allocated()) { const VKDevice &device = VKBackend::get().device_get(); vmaDestroyImage(device.mem_allocator_get(), vk_image_, allocation_); - vkDestroyImageView(device.device_get(), vk_image_view_, vk_allocation_callbacks); } } @@ -83,6 +81,9 @@ void VKTexture::generate_mipmap() *this, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, Span(&image_blit, 1)); + /* TODO: Until we do actual command encoding we need to submit each transfer operation + * individually. */ + command_buffer.submit(); } /* Ensure that all mipmap levels are in `VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL`. All miplevels are * except the last one. */ @@ -148,9 +149,12 @@ void VKTexture::swizzle_set(const char /*swizzle_mask*/[4]) NOT_YET_IMPLEMENTED; } -void VKTexture::mip_range_set(int /*min*/, int /*max*/) +void VKTexture::mip_range_set(int min, int max) { - NOT_YET_IMPLEMENTED; + mip_min_ = min; + mip_max_ = max; + + flags_ |= IMAGE_VIEW_DIRTY; } void VKTexture::read_sub(int mip, eGPUDataFormat format, const int area[4], void *r_data) @@ -430,20 +434,6 @@ bool VKTexture::allocate() /* Promote image to the correct layout. */ layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL); - VK_ALLOCATION_CALLBACKS - VkImageViewCreateInfo image_view_info = {}; - image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - image_view_info.image = vk_image_; - image_view_info.viewType = to_vk_image_view_type(type_); - image_view_info.format = to_vk_format(format_); - image_view_info.components = to_vk_component_mapping(format_); - image_view_info.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_); - image_view_info.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; - image_view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; - - result = vkCreateImageView( - device.device_get(), &image_view_info, vk_allocation_callbacks, &vk_image_view_); - debug::object_label(vk_image_view_, name_); return result == VK_SUCCESS; } @@ -525,6 +515,48 @@ void VKTexture::layout_ensure(VKContext &context, barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; context.command_buffer_get().pipeline_barrier(Span(&barrier, 1)); } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Views + * \{ */ + +void VKTexture::image_view_ensure() +{ + if (flags_ & IMAGE_VIEW_DIRTY) { + image_view_update(); + flags_ &= ~IMAGE_VIEW_DIRTY; + } +} + +void VKTexture::image_view_update() +{ + VK_ALLOCATION_CALLBACKS + VkImageViewCreateInfo image_view_info = {}; + image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_info.image = vk_image_; + image_view_info.viewType = to_vk_image_view_type(type_); + image_view_info.format = to_vk_format(format_); + image_view_info.components = to_vk_component_mapping(format_); + image_view_info.subresourceRange.aspectMask = to_vk_image_aspect_flag_bits(format_); + IndexRange mip_range = mip_map_range(); + image_view_info.subresourceRange.baseMipLevel = mip_range.first(); + image_view_info.subresourceRange.levelCount = mip_range.size(); + image_view_info.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + const VKDevice &device = VKBackend::get().device_get(); + VkImageView image_view = VK_NULL_HANDLE; + vkCreateImageView(device.device_get(), &image_view_info, vk_allocation_callbacks, &image_view); + debug::object_label(image_view, name_); + image_view_.emplace(image_view); +} + +IndexRange VKTexture::mip_map_range() const +{ + return IndexRange(mip_min_, mip_max_ - mip_min_ + 1); +} + /** \} */ } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_texture.hh b/source/blender/gpu/vulkan/vk_texture.hh index c2bc41485d1..f94faba0669 100644 --- a/source/blender/gpu/vulkan/vk_texture.hh +++ b/source/blender/gpu/vulkan/vk_texture.hh @@ -9,7 +9,9 @@ #pragma once #include "gpu_texture_private.hh" + #include "vk_context.hh" +#include "vk_image_view.hh" namespace blender::gpu { @@ -17,15 +19,23 @@ class VKSampler; class VKTexture : public Texture { VkImage vk_image_ = VK_NULL_HANDLE; - VkImageView vk_image_view_ = VK_NULL_HANDLE; VmaAllocation allocation_ = VK_NULL_HANDLE; + /* Image view when used in a shader. */ + std::optional image_view_; + /* Last image layout of the texture. Frame-buffer and barriers can alter/require the actual * layout to be changed. During this it requires to set the current layout in order to know which * conversion should happen. #current_layout_ keep track of the layout so the correct conversion * can be done. */ VkImageLayout current_layout_ = VK_IMAGE_LAYOUT_UNDEFINED; + enum eDirtyFlags { + IMAGE_VIEW_DIRTY = (1 << 0), + }; + + int flags_ = IMAGE_VIEW_DIRTY; + public: VKTexture(const char *name) : Texture(name) {} @@ -58,11 +68,6 @@ class VKTexture : public Texture { BLI_assert(vk_image_ != VK_NULL_HANDLE); return vk_image_; } - VkImageView vk_image_view_handle() const - { - BLI_assert(is_allocated()); - return vk_image_view_; - } void ensure_allocated(); @@ -123,6 +128,23 @@ class VKTexture : public Texture { VkImageLayout requested_layout); /** \} */ + + /* -------------------------------------------------------------------- */ + /** \name Image Views + * \{ */ + public: + VKImageView &image_view_get() + { + image_view_ensure(); + return *image_view_; + } + + private: + IndexRange mip_map_range() const; + void image_view_ensure(); + void image_view_update(); + + /** \} */ }; static inline VKTexture *unwrap(Texture *tex)