From 00f5ae2a8fdc812bcdedec2bd06d91e736beb43f Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 14 Nov 2023 11:12:19 +0100 Subject: [PATCH] Vulkan: Implement Samplers Until now the Vulkan backend supported a single pre-configured sampler. This PR realizes creation, caching and freeing of samplers based on what is required by the state manager. The implementation is similar to OpenGL or Metal. This fixes many issues including: - Textures in workbench and eevee use the correct extend and filtering - Custom icons render correctly - Depth sampling issues - Removes artifacts using EEVEE world shader, lighting and indirect lighting. Pull Request: https://projects.blender.org/blender/blender/pulls/114827 --- source/blender/gpu/CMakeLists.txt | 2 + .../gpu/vulkan/vk_bindable_resource.hh | 18 ++++-- source/blender/gpu/vulkan/vk_common.cc | 17 ++++++ source/blender/gpu/vulkan/vk_common.hh | 1 + source/blender/gpu/vulkan/vk_device.cc | 7 ++- source/blender/gpu/vulkan/vk_device.hh | 9 ++- source/blender/gpu/vulkan/vk_sampler.cc | 51 ++++++++++++++-- source/blender/gpu/vulkan/vk_sampler.hh | 7 ++- source/blender/gpu/vulkan/vk_samplers.cc | 58 +++++++++++++++++++ source/blender/gpu/vulkan/vk_samplers.hh | 35 +++++++++++ source/blender/gpu/vulkan/vk_state_manager.cc | 4 +- source/blender/gpu/vulkan/vk_texture.cc | 9 ++- source/blender/gpu/vulkan/vk_texture.hh | 4 +- 13 files changed, 199 insertions(+), 23 deletions(-) create mode 100644 source/blender/gpu/vulkan/vk_samplers.cc create mode 100644 source/blender/gpu/vulkan/vk_samplers.hh diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index e6c3d1d1b31..b67eefcc71b 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -223,6 +223,7 @@ set(VULKAN_SRC vulkan/vk_query.cc vulkan/vk_resource_tracker.cc vulkan/vk_sampler.cc + vulkan/vk_samplers.cc vulkan/vk_shader.cc vulkan/vk_shader_interface.cc vulkan/vk_shader_log.cc @@ -261,6 +262,7 @@ set(VULKAN_SRC vulkan/vk_query.hh vulkan/vk_resource_tracker.hh vulkan/vk_sampler.hh + vulkan/vk_samplers.hh vulkan/vk_shader.hh vulkan/vk_shader_interface.hh vulkan/vk_shader_log.hh diff --git a/source/blender/gpu/vulkan/vk_bindable_resource.hh b/source/blender/gpu/vulkan/vk_bindable_resource.hh index a75f5d79423..713691f772d 100644 --- a/source/blender/gpu/vulkan/vk_bindable_resource.hh +++ b/source/blender/gpu/vulkan/vk_bindable_resource.hh @@ -23,7 +23,13 @@ class VKBindableResource { /** * Bind the resource to the shader. */ - virtual void bind(int binding, shader::ShaderCreateInfo::Resource::BindType bind_type) = 0; + virtual void bind(int binding, + shader::ShaderCreateInfo::Resource::BindType bind_type, + const GPUSamplerState /*sampler_state*/) + { + bind(binding, bind_type); + }; + virtual void bind(int /*binding*/, shader::ShaderCreateInfo::Resource::BindType /*bind_type*/){}; protected: void unbind_from_active_context(); @@ -41,6 +47,7 @@ template class VKBindSpac public: int binding; VKBindableResource *resource; + GPUSamplerState sampler_state; }; Vector bindings_; @@ -49,15 +56,18 @@ template class VKBindSpac /** * Register a binding to this namespace. */ - void bind(int binding, VKBindableResource &resource) + void bind(int binding, + VKBindableResource &resource, + const GPUSamplerState sampler_state = GPUSamplerState::default_sampler()) { for (ResourceBinding &bind : bindings_) { if (bind.binding == binding) { bind.resource = &resource; + bind.sampler_state = sampler_state; return; } } - ResourceBinding bind = {binding, &resource}; + ResourceBinding bind = {binding, &resource, sampler_state}; bindings_.append(bind); } @@ -67,7 +77,7 @@ template class VKBindSpac void apply_bindings() { for (ResourceBinding &binding : bindings_) { - binding.resource->bind(binding.binding, BindType); + binding.resource->bind(binding.binding, BindType, binding.sampler_state); } } diff --git a/source/blender/gpu/vulkan/vk_common.cc b/source/blender/gpu/vulkan/vk_common.cc index b75a62c1631..d835897a1e0 100644 --- a/source/blender/gpu/vulkan/vk_common.cc +++ b/source/blender/gpu/vulkan/vk_common.cc @@ -853,6 +853,23 @@ VkCullModeFlags to_vk_cull_mode_flags(const eGPUFaceCullTest cull_test) return VK_CULL_MODE_NONE; } +VkSamplerAddressMode to_vk_sampler_address_mode(const GPUSamplerExtendMode extend_mode) +{ + switch (extend_mode) { + case GPU_SAMPLER_EXTEND_MODE_EXTEND: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case GPU_SAMPLER_EXTEND_MODE_REPEAT: + return VK_SAMPLER_ADDRESS_MODE_REPEAT; + case GPU_SAMPLER_EXTEND_MODE_MIRRORED_REPEAT: + return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; + case GPU_SAMPLER_EXTEND_MODE_CLAMP_TO_BORDER: + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER; + } + + BLI_assert_unreachable(); + return VK_SAMPLER_ADDRESS_MODE_REPEAT; +} + const char *to_string(VkObjectType type) { diff --git a/source/blender/gpu/vulkan/vk_common.hh b/source/blender/gpu/vulkan/vk_common.hh index 93c3b73a47b..f6499d27a1d 100644 --- a/source/blender/gpu/vulkan/vk_common.hh +++ b/source/blender/gpu/vulkan/vk_common.hh @@ -54,6 +54,7 @@ VkClearColorValue to_vk_clear_color_value(const eGPUDataFormat format, const voi 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); +VkSamplerAddressMode to_vk_sampler_address_mode(const GPUSamplerExtendMode extend_mode); const char *to_string(VkObjectType type); template VkObjectType to_vk_object_type(T /*vk_obj*/) diff --git a/source/blender/gpu/vulkan/vk_device.cc b/source/blender/gpu/vulkan/vk_device.cc index e5bf2951d68..cef1c35621c 100644 --- a/source/blender/gpu/vulkan/vk_device.cc +++ b/source/blender/gpu/vulkan/vk_device.cc @@ -23,6 +23,9 @@ namespace blender::gpu { void VKDevice::deinit() { + if (!is_initialized()) { + return; + } VK_ALLOCATION_CALLBACKS; vkDestroyCommandPool(vk_device_, vk_command_pool_, vk_allocation_callbacks); @@ -31,7 +34,7 @@ void VKDevice::deinit() delete &(*dummy_color_attachment_).get(); dummy_color_attachment_.reset(); } - sampler_.free(); + samplers_.free(); destroy_discarded_resources(); vmaDestroyAllocator(mem_allocator_); mem_allocator_ = VK_NULL_HANDLE; @@ -69,7 +72,7 @@ void VKDevice::init(void *ghost_context) init_command_pools(); init_descriptor_pools(); - sampler_.create(); + samplers_.init(); debug::object_label(device_get(), "LogicalDevice"); debug::object_label(queue_get(), "GenericQueue"); diff --git a/source/blender/gpu/vulkan/vk_device.hh b/source/blender/gpu/vulkan/vk_device.hh index fece612808d..e01217212a4 100644 --- a/source/blender/gpu/vulkan/vk_device.hh +++ b/source/blender/gpu/vulkan/vk_device.hh @@ -15,7 +15,7 @@ #include "vk_common.hh" #include "vk_debug.hh" #include "vk_descriptor_pools.hh" -#include "vk_sampler.hh" +#include "vk_samplers.hh" namespace blender::gpu { class VKBackend; @@ -60,8 +60,7 @@ class VKDevice : public NonCopyable { VkQueue vk_queue_ = VK_NULL_HANDLE; VkCommandPool vk_command_pool_ = VK_NULL_HANDLE; - /* Dummy sampler for now. */ - VKSampler sampler_; + VKSamplers samplers_; /** * Available Contexts for this device. @@ -167,9 +166,9 @@ class VKDevice : public NonCopyable { return debugging_tools_; } - const VKSampler &sampler_get() const + VKSamplers &samplers() { - return sampler_; + return samplers_; } const VkCommandPool vk_command_pool_get() const diff --git a/source/blender/gpu/vulkan/vk_sampler.cc b/source/blender/gpu/vulkan/vk_sampler.cc index 6cb4bc04dde..f89e72d405c 100644 --- a/source/blender/gpu/vulkan/vk_sampler.cc +++ b/source/blender/gpu/vulkan/vk_sampler.cc @@ -11,30 +11,71 @@ #include "vk_context.hh" #include "vk_memory.hh" +#include "DNA_userdef_types.h" + namespace blender::gpu { VKSampler::~VKSampler() { free(); } -void VKSampler::create() +void VKSampler::create(const GPUSamplerState &sampler_state) { + BLI_assert(sampler_state.type != GPU_SAMPLER_STATE_TYPE_INTERNAL); BLI_assert(vk_sampler_ == VK_NULL_HANDLE); - VK_ALLOCATION_CALLBACKS - VkSamplerCreateInfo sampler_info = {}; sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + + if (sampler_state.type == GPU_SAMPLER_STATE_TYPE_PARAMETERS) { + /* Apply filtering. */ + if (sampler_state.filtering & GPU_SAMPLER_FILTERING_LINEAR) { + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + } + if (sampler_state.filtering & GPU_SAMPLER_FILTERING_MIPMAP) { + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + } + if (sampler_state.filtering & GPU_SAMPLER_FILTERING_ANISOTROPIC) { + sampler_info.anisotropyEnable = VK_TRUE; + sampler_info.maxAnisotropy = min_ff(1.0f, U.anisotropic_filter); + } + + /* Extend */ + sampler_info.addressModeU = to_vk_sampler_address_mode(sampler_state.extend_x); + sampler_info.addressModeV = sampler_info.addressModeW = to_vk_sampler_address_mode( + sampler_state.extend_yz); + } + else if (sampler_state.type == GPU_SAMPLER_STATE_TYPE_CUSTOM) { + if (sampler_state.custom_type == GPU_SAMPLER_CUSTOM_ICON) { + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; + sampler_info.minLod = 0; + sampler_info.maxLod = 1; + } + else if (sampler_state.custom_type == GPU_SAMPLER_CUSTOM_COMPARE) { + sampler_info.magFilter = VK_FILTER_LINEAR; + sampler_info.minFilter = VK_FILTER_LINEAR; + sampler_info.compareEnable = VK_TRUE; + sampler_info.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + sampler_info.minLod = -1000; + sampler_info.maxLod = 1000; + } + } + + VK_ALLOCATION_CALLBACKS const VKDevice &device = VKBackend::get().device_get(); vkCreateSampler(device.device_get(), &sampler_info, vk_allocation_callbacks, &vk_sampler_); - debug::object_label(vk_sampler_, "DummySampler"); + debug::object_label(vk_sampler_, sampler_state.to_string().c_str()); } void VKSampler::free() { - VK_ALLOCATION_CALLBACKS if (vk_sampler_ != VK_NULL_HANDLE) { + VK_ALLOCATION_CALLBACKS + const VKDevice &device = VKBackend::get().device_get(); if (device.device_get() != VK_NULL_HANDLE) { vkDestroySampler(device.device_get(), vk_sampler_, vk_allocation_callbacks); diff --git a/source/blender/gpu/vulkan/vk_sampler.hh b/source/blender/gpu/vulkan/vk_sampler.hh index 9b350eef0df..fccf448a533 100644 --- a/source/blender/gpu/vulkan/vk_sampler.hh +++ b/source/blender/gpu/vulkan/vk_sampler.hh @@ -22,7 +22,7 @@ class VKSampler : public NonCopyable { public: virtual ~VKSampler(); - void create(); + void create(const GPUSamplerState &sampler_state); void free(); VkSampler vk_handle() const @@ -30,6 +30,11 @@ class VKSampler : public NonCopyable { BLI_assert(vk_sampler_ != VK_NULL_HANDLE); return vk_sampler_; } + + bool is_initialized() const + { + return vk_sampler_ != VK_NULL_HANDLE; + } }; } // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_samplers.cc b/source/blender/gpu/vulkan/vk_samplers.cc new file mode 100644 index 00000000000..5f725993551 --- /dev/null +++ b/source/blender/gpu/vulkan/vk_samplers.cc @@ -0,0 +1,58 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#include "vk_samplers.hh" + +namespace blender::gpu { + +void VKSamplers::init() +{ + if (custom_sampler_cache_[0].is_initialized()) { + return; + } + custom_sampler_cache_[GPU_SAMPLER_CUSTOM_COMPARE].create(GPUSamplerState::compare_sampler()); + custom_sampler_cache_[GPU_SAMPLER_CUSTOM_ICON].create(GPUSamplerState::icon_sampler()); + + GPUSamplerState state = {}; + for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) { + state.extend_yz = static_cast(extend_yz_i); + for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) { + state.extend_x = static_cast(extend_x_i); + for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) { + state.filtering = GPUSamplerFiltering(filtering_i); + sampler_cache_[extend_yz_i][extend_x_i][filtering_i].create(state); + } + } + } +} + +void VKSamplers::free() +{ + custom_sampler_cache_[GPU_SAMPLER_CUSTOM_COMPARE].free(); + custom_sampler_cache_[GPU_SAMPLER_CUSTOM_ICON].free(); + + for (int extend_yz_i = 0; extend_yz_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_yz_i++) { + for (int extend_x_i = 0; extend_x_i < GPU_SAMPLER_EXTEND_MODES_COUNT; extend_x_i++) { + for (int filtering_i = 0; filtering_i < GPU_SAMPLER_FILTERING_TYPES_COUNT; filtering_i++) { + sampler_cache_[extend_yz_i][extend_x_i][filtering_i].free(); + } + } + } +} + +const VKSampler &VKSamplers::get(const GPUSamplerState &sampler_state) +{ + BLI_assert(sampler_state.type != GPU_SAMPLER_STATE_TYPE_INTERNAL); + + if (sampler_state.type == GPU_SAMPLER_STATE_TYPE_CUSTOM) { + return custom_sampler_cache_[sampler_state.custom_type]; + } + return sampler_cache_[sampler_state.extend_yz][sampler_state.extend_x][sampler_state.filtering]; +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_samplers.hh b/source/blender/gpu/vulkan/vk_samplers.hh new file mode 100644 index 00000000000..38a3db1937b --- /dev/null +++ b/source/blender/gpu/vulkan/vk_samplers.hh @@ -0,0 +1,35 @@ +/* SPDX-FileCopyrightText: 2023 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "vk_sampler.hh" +#include "vk_samplers.hh" + +#include "BLI_map.hh" + +namespace blender::gpu { + +/** + * Collection of samplers. + * + * Samplers are device owned and can be shared between contexts. + */ +class VKSamplers : NonCopyable { + VKSampler sampler_cache_[GPU_SAMPLER_EXTEND_MODES_COUNT][GPU_SAMPLER_EXTEND_MODES_COUNT] + [GPU_SAMPLER_FILTERING_TYPES_COUNT]; + VKSampler custom_sampler_cache_[GPU_SAMPLER_CUSTOM_TYPES_COUNT]; + + public: + void init(); + void free(); + + const VKSampler &get(const GPUSamplerState &sampler_state); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/vulkan/vk_state_manager.cc b/source/blender/gpu/vulkan/vk_state_manager.cc index 1234dff12f1..85aaf86d9dc 100644 --- a/source/blender/gpu/vulkan/vk_state_manager.cc +++ b/source/blender/gpu/vulkan/vk_state_manager.cc @@ -57,10 +57,10 @@ void VKStateManager::issue_barrier(eGPUBarrier /*barrier_bits*/) context.flush(); } -void VKStateManager::texture_bind(Texture *tex, GPUSamplerState /*sampler*/, int unit) +void VKStateManager::texture_bind(Texture *tex, GPUSamplerState sampler, int unit) { VKTexture *texture = unwrap(tex); - textures_.bind(unit, *texture); + textures_.bind(unit, *texture, sampler); } void VKStateManager::texture_unbind(Texture *tex) diff --git a/source/blender/gpu/vulkan/vk_texture.cc b/source/blender/gpu/vulkan/vk_texture.cc index 31a0fbb1e1b..eff9be5f969 100644 --- a/source/blender/gpu/vulkan/vk_texture.cc +++ b/source/blender/gpu/vulkan/vk_texture.cc @@ -514,7 +514,9 @@ bool VKTexture::allocate() return result == VK_SUCCESS; } -void VKTexture::bind(int binding, shader::ShaderCreateInfo::Resource::BindType bind_type) +void VKTexture::bind(int binding, + shader::ShaderCreateInfo::Resource::BindType bind_type, + const GPUSamplerState sampler_state) { VKContext &context = *VKContext::get(); VKShader *shader = static_cast(context.shader); @@ -527,8 +529,9 @@ void VKTexture::bind(int binding, shader::ShaderCreateInfo::Resource::BindType b descriptor_set.image_bind(*this, *location); } else { - const VKDevice &device = VKBackend::get().device_get(); - descriptor_set.bind(*this, *location, device.sampler_get()); + VKDevice &device = VKBackend::get().device_get(); + const VKSampler &sampler = device.samplers().get(sampler_state); + descriptor_set.bind(*this, *location, sampler); } } } diff --git a/source/blender/gpu/vulkan/vk_texture.hh b/source/blender/gpu/vulkan/vk_texture.hh index 1e7d78d882a..c109daf9c41 100644 --- a/source/blender/gpu/vulkan/vk_texture.hh +++ b/source/blender/gpu/vulkan/vk_texture.hh @@ -84,7 +84,9 @@ class VKTexture : public Texture, public VKBindableResource { /* TODO(fclem): Legacy. Should be removed at some point. */ uint gl_bindcode_get() const override; - void bind(int location, shader::ShaderCreateInfo::Resource::BindType bind_type) override; + void bind(int location, + shader::ShaderCreateInfo::Resource::BindType bind_type, + const GPUSamplerState sampler_state) override; VkImage vk_image_handle() const {