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
This commit is contained in:
Jeroen Bakker 2023-11-14 11:12:19 +01:00
parent 2020f35981
commit 00f5ae2a8f
13 changed files with 199 additions and 23 deletions

View File

@ -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

View File

@ -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<shader::ShaderCreateInfo::Resource::BindType BindType> class VKBindSpac
public:
int binding;
VKBindableResource *resource;
GPUSamplerState sampler_state;
};
Vector<ResourceBinding> bindings_;
@ -49,15 +56,18 @@ template<shader::ShaderCreateInfo::Resource::BindType BindType> 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<shader::ShaderCreateInfo::Resource::BindType BindType> class VKBindSpac
void apply_bindings()
{
for (ResourceBinding &binding : bindings_) {
binding.resource->bind(binding.binding, BindType);
binding.resource->bind(binding.binding, BindType, binding.sampler_state);
}
}

View File

@ -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)
{

View File

@ -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<typename T> VkObjectType to_vk_object_type(T /*vk_obj*/)

View File

@ -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");

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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<GPUSamplerExtendMode>(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<GPUSamplerExtendMode>(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

View File

@ -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

View File

@ -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)

View File

@ -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<VKShader *>(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);
}
}
}

View File

@ -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
{