Vulkan: Refactor resource binding

Resource binding was over-complicated as I didn't understood the state
manager and vulkan to make the correct decisions at that time. This
refactor will remove a lot of the complexity and improves the performance.

**Performance**

The performance improvement is noticeable in complex grease pencil
scenes.

Grease pencil benchmark file picknick:
- `NVIDIA Quadro RTX 6000` 17 fps -> 24 fps
- `Intel(R) Arc(tm) A750 Graphics (DG2)` 6 -> 21 fps

**Bottle-neck**

The performance improvements originates from moving the update entry
point from state manager to shader interface. The previous implementation
(state manager) had to loop over all the bound resources and find in the
shader interface where it was located in the descriptor set. Ignoring
resources that were not used by the shader. But also making it hard
to determine if descriptor sets actually changed. Previous implementation
assumed descriptor sets always changed.

When descriptor set changed a new descriptor set needed to be allocated.
Most drivers this is a fast operation, but on Intel/Mesa this was measurable
slow. Using an allocation pool doesn't fit the Vulkan API as you are only
able to reuse when the layout matches exactly. Of course doable, but requires
another structure to keep track of the actual layouts.

**Solution**

By using the shader interface as entry point we can:
1. Keep track if there are any changes in the state manager. If not and the
   layout is the same, the previous shader can be reused.
2. In stead of looping over each bound resource, we loop over bind points.

**Future extensions**

Bundle all descriptor set uploads just before use. This would be more
in line with how 'modern' Vulkan should be implemented. This PR already
separates the uploading from the updating and technically allows to upload
more than one descriptor set.

Instead of looking 1 set back we should measure if we can handle multiple
or keep track of the different layouts resources to improve the performance
even further.

Optional use `VK_KHR_descriptor_buffer` when available.

Pull Request: https://projects.blender.org/blender/blender/pulls/128068
This commit is contained in:
Jeroen Bakker
2024-09-26 10:59:45 +02:00
parent 9eae533e89
commit d75cf2efd4
31 changed files with 598 additions and 647 deletions

View File

@@ -201,7 +201,6 @@ set(OPENGL_SRC
set(VULKAN_SRC
vulkan/vk_backend.cc
vulkan/vk_batch.cc
vulkan/vk_bindable_resource.cc
vulkan/vk_buffer.cc
vulkan/vk_common.cc
vulkan/vk_context.cc
@@ -251,7 +250,6 @@ set(VULKAN_SRC
vulkan/vk_backend.hh
vulkan/vk_batch.hh
vulkan/vk_bindable_resource.hh
vulkan/vk_buffer.hh
vulkan/vk_common.hh
vulkan/vk_context.hh

View File

@@ -344,7 +344,7 @@ void VKBackend::samplers_update()
void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
VKContext &context = *VKContext::get();
render_graph::VKResourceAccessInfo &resources = context.update_and_get_access_info();
render_graph::VKResourceAccessInfo &resources = context.reset_and_get_access_info();
render_graph::VKDispatchNode::CreateInfo dispatch_info(resources);
context.update_pipeline_data(dispatch_info.dispatch_node.pipeline_data);
dispatch_info.dispatch_node.group_count_x = groups_x_len;
@@ -358,7 +358,7 @@ void VKBackend::compute_dispatch_indirect(StorageBuf *indirect_buf)
BLI_assert(indirect_buf);
VKContext &context = *VKContext::get();
VKStorageBuffer &indirect_buffer = *unwrap(indirect_buf);
render_graph::VKResourceAccessInfo &resources = context.update_and_get_access_info();
render_graph::VKResourceAccessInfo &resources = context.reset_and_get_access_info();
render_graph::VKDispatchIndirectNode::CreateInfo dispatch_indirect_info(resources);
context.update_pipeline_data(dispatch_indirect_info.dispatch_indirect_node.pipeline_data);
dispatch_indirect_info.dispatch_indirect_node.buffer = indirect_buffer.vk_handle();

View File

@@ -21,7 +21,7 @@ namespace blender::gpu {
void VKBatch::draw(int vertex_first, int vertex_count, int instance_first, int instance_count)
{
VKContext &context = *VKContext::get();
render_graph::VKResourceAccessInfo &resource_access_info = context.update_and_get_access_info();
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
VKStateManager &state_manager = context.state_manager_get();
state_manager.apply_state();
@@ -85,7 +85,7 @@ void VKBatch::multi_draw_indirect(const VkBuffer indirect_buffer,
const intptr_t stride)
{
VKContext &context = *VKContext::get();
render_graph::VKResourceAccessInfo &resource_access_info = context.update_and_get_access_info();
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
VKStateManager &state_manager = context.state_manager_get();
state_manager.apply_state();

View File

@@ -1,39 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#include "vk_bindable_resource.hh"
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_device.hh"
#include "vk_state_manager.hh"
namespace blender::gpu {
VKBindableResource::~VKBindableResource()
{
unbind_from_all_contexts();
}
void VKBindableResource::unbind_from_active_context()
{
const VKContext *context = VKContext::get();
if (context != nullptr) {
VKStateManager &state_manager = context->state_manager_get();
state_manager.unbind_from_all_namespaces(*this);
}
}
void VKBindableResource::unbind_from_all_contexts()
{
for (const VKContext &context : VKBackend::get().device.contexts_get()) {
VKStateManager &state_manager = context.state_manager_get();
state_manager.unbind_from_all_namespaces(*this);
}
}
} // namespace blender::gpu

View File

@@ -1,162 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#pragma once
#include "gpu_shader_create_info.hh"
#include "BLI_utility_mixins.hh"
namespace blender::gpu {
class VKDescriptorSetTracker;
class VKShaderInterface;
namespace render_graph {
struct VKResourceAccessInfo;
}
/**
* Access to the descriptor set, shader interface is needed when adding state manager bindings to a
* descriptor set.
*
* When adding the bindings to the descriptor set we also record the access flag in
* resource_access_info.\
*
* AddToDescriptorSetContext is a convenience structure so we don't need to pass the references to
* the descriptor set, shader interface and resource access info to each method call.
*/
struct AddToDescriptorSetContext : NonCopyable {
/** Descriptor set where to bind/add resources to. */
VKDescriptorSetTracker &descriptor_set;
/**
* Shader interface of the active shader to query shader binding locations and the used access
* flags.
*/
const VKShaderInterface &shader_interface;
/**
* When adding resources to the descriptor set, its access info should be added to the
* resource_access_info. When adding a dispatch/draw node to the render graph, this structure is
* passed to make links with the resources and the exact access.
*/
render_graph::VKResourceAccessInfo &resource_access_info;
AddToDescriptorSetContext(VKDescriptorSetTracker &descriptor_set,
const VKShaderInterface &shader_interface,
render_graph::VKResourceAccessInfo &resource_access_info)
: descriptor_set(descriptor_set),
shader_interface(shader_interface),
resource_access_info(resource_access_info)
{
}
};
/**
* Super class for resources that can be bound to a shader.
*/
class VKBindableResource {
protected:
virtual ~VKBindableResource();
public:
/**
* Add/bind a resource to a descriptor set (`data.descriptor_set`) and the access info
* (`data.resource_access_info`).
*
* `binding` parameter is the binding as specified in the ShaderCreateInfo.
* `bind_type` to make distinction between samples, image load/store, buffer texture binding.
*/
virtual void add_to_descriptor_set(
AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state = GPUSamplerState::default_sampler()) = 0;
protected:
void unbind_from_active_context();
private:
void unbind_from_all_contexts();
};
/**
* Offset when searching for bindings.
*
* When shaders combine images and samplers, the images have to be offset to find the correct
* shader input. Both textures and images are stored in the uniform list and their ID can be
* overlapping.
*/
static constexpr int BIND_SPACE_IMAGE_OFFSET = 512;
/**
* Blender binds resources at context level (VKStateManager). The bindings are organized in
* namespaces.
*/
template<shader::ShaderCreateInfo::Resource::BindType BindType, int BindOffset = 0>
class VKBindSpace {
static constexpr int offset = BindOffset;
class ResourceBinding {
public:
int binding;
VKBindableResource *resource;
GPUSamplerState sampler_state;
};
Vector<ResourceBinding> bindings_;
public:
/**
* Register a binding to this namespace.
*/
void bind(int binding_,
VKBindableResource &resource,
const GPUSamplerState sampler_state = GPUSamplerState::default_sampler())
{
int binding = binding_ >= offset ? binding_ : binding_ + offset;
for (ResourceBinding &bind : bindings_) {
if (bind.binding == binding) {
bind.resource = &resource;
bind.sampler_state = sampler_state;
return;
}
}
ResourceBinding bind = {binding, &resource, sampler_state};
bindings_.append(bind);
}
/**
* Apply registered bindings to the active shader.
*/
void add_to_descriptor_set(AddToDescriptorSetContext &data)
{
for (ResourceBinding &binding : bindings_) {
binding.resource->add_to_descriptor_set(
data, binding.binding, BindType, binding.sampler_state);
}
}
/**
* Unregister the given resource from this namespace.
*/
void unbind(VKBindableResource &resource)
{
bindings_.remove_if(
[&resource](const ResourceBinding &binding) { return binding.resource == &resource; });
}
/**
* Remove all bindings from this namespace.
*/
void unbind_all()
{
bindings_.clear();
}
};
} // namespace blender::gpu

View File

@@ -260,34 +260,23 @@ void VKContext::update_pipeline_data(VKShader &vk_shader,
r_pipeline_data.push_constants_size = 0;
const VKPushConstants::Layout &push_constants_layout =
vk_shader.interface_get().push_constants_layout_get();
vk_shader.push_constants.update(*this);
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::PUSH_CONSTANTS) {
r_pipeline_data.push_constants_size = push_constants_layout.size_in_bytes();
r_pipeline_data.push_constants_data = vk_shader.push_constants.data();
}
/* When using the push constant fallback we need to add a read access dependency to the uniform
* buffer after the buffer has been updated.
* NOTE: this alters the context instance variable `access_info_` which isn't clear from the API.
*/
if (push_constants_layout.storage_type_get() == VKPushConstants::StorageType::UNIFORM_BUFFER) {
access_info_.buffers.append(
{vk_shader.push_constants.uniform_buffer_get()->vk_handle(), VK_ACCESS_UNIFORM_READ_BIT});
}
/* Update descriptor set. */
r_pipeline_data.vk_descriptor_set = VK_NULL_HANDLE;
if (vk_shader.has_descriptor_set()) {
VKDescriptorSetTracker &descriptor_set = descriptor_set_get();
descriptor_set.update(*this);
r_pipeline_data.vk_descriptor_set = descriptor_set.active_descriptor_set()->vk_handle();
descriptor_set.update_descriptor_set(*this, access_info_);
r_pipeline_data.vk_descriptor_set = descriptor_set.vk_descriptor_set;
}
}
render_graph::VKResourceAccessInfo &VKContext::update_and_get_access_info()
render_graph::VKResourceAccessInfo &VKContext::reset_and_get_access_info()
{
access_info_.reset();
state_manager_get().apply_bindings(*this, access_info_);
return access_info_;
}

View File

@@ -80,7 +80,7 @@ class VKContext : public Context, NonCopyable {
*/
void rendering_end();
render_graph::VKResourceAccessInfo &update_and_get_access_info();
render_graph::VKResourceAccessInfo &reset_and_get_access_info();
/**
* Update the give shader data with the current state of the context.

View File

@@ -31,6 +31,11 @@ void VKDescriptorPools::init(const VKDevice &device)
void VKDescriptorPools::reset()
{
const VKDevice &device = VKBackend::get().device;
for (const VkDescriptorPool vk_descriptor_pool : pools_) {
vkResetDescriptorPool(device.vk_handle(), vk_descriptor_pool, 0);
}
active_pool_index_ = 0;
}
@@ -79,8 +84,7 @@ bool VKDescriptorPools::is_last_pool_active()
return active_pool_index_ == pools_.size() - 1;
}
std::unique_ptr<VKDescriptorSet> VKDescriptorPools::allocate(
const VkDescriptorSetLayout &descriptor_set_layout)
VkDescriptorSet VKDescriptorPools::allocate(const VkDescriptorSetLayout descriptor_set_layout)
{
BLI_assert(descriptor_set_layout != VK_NULL_HANDLE);
const VKDevice &device = VKBackend::get().device;
@@ -106,7 +110,7 @@ std::unique_ptr<VKDescriptorSet> VKDescriptorPools::allocate(
return allocate(descriptor_set_layout);
}
return std::make_unique<VKDescriptorSet>(pool, vk_descriptor_set);
return vk_descriptor_set;
}
} // namespace blender::gpu

View File

@@ -48,7 +48,7 @@ class VKDescriptorPools {
void init(const VKDevice &vk_device);
std::unique_ptr<VKDescriptorSet> allocate(const VkDescriptorSetLayout &descriptor_set_layout);
VkDescriptorSet allocate(const VkDescriptorSetLayout descriptor_set_layout);
/**
* Reset the pools to start looking for free space from the first descriptor pool.

View File

@@ -8,44 +8,15 @@
#include "vk_descriptor_set.hh"
#include "vk_index_buffer.hh"
#include "vk_sampler.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_state_manager.hh"
#include "vk_storage_buffer.hh"
#include "vk_texture.hh"
#include "vk_uniform_buffer.hh"
#include "vk_vertex_buffer.hh"
#include "BLI_assert.h"
namespace blender::gpu {
VKDescriptorSet::VKDescriptorSet(VKDescriptorSet &&other)
: vk_descriptor_pool_(other.vk_descriptor_pool_), vk_descriptor_set_(other.vk_descriptor_set_)
{
other.vk_descriptor_set_ = VK_NULL_HANDLE;
other.vk_descriptor_pool_ = VK_NULL_HANDLE;
}
VKDescriptorSet::~VKDescriptorSet()
{
if (vk_descriptor_set_ != VK_NULL_HANDLE) {
/* Handle should be given back to the pool. */
const VKDevice &device = VKBackend::get().device;
vkFreeDescriptorSets(device.vk_handle(), vk_descriptor_pool_, 1, &vk_descriptor_set_);
vk_descriptor_set_ = VK_NULL_HANDLE;
vk_descriptor_pool_ = VK_NULL_HANDLE;
}
}
void VKDescriptorSetTracker::reset()
{
vk_descriptor_image_infos_.clear();
vk_descriptor_buffer_infos_.clear();
vk_buffer_views_.clear();
vk_write_descriptor_sets_.clear();
}
void VKDescriptorSetTracker::bind_buffer(VkDescriptorType vk_descriptor_type,
VkBuffer vk_buffer,
VkDeviceSize size_in_bytes,
@@ -54,7 +25,7 @@ void VKDescriptorSetTracker::bind_buffer(VkDescriptorType vk_descriptor_type,
vk_descriptor_buffer_infos_.append({vk_buffer, 0, size_in_bytes});
vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
VK_NULL_HANDLE,
vk_descriptor_set,
location,
0,
1,
@@ -64,13 +35,13 @@ void VKDescriptorSetTracker::bind_buffer(VkDescriptorType vk_descriptor_type,
nullptr});
}
void VKDescriptorSetTracker::bind_texel_buffer(VKVertexBuffer &vertex_buffer,
void VKDescriptorSetTracker::bind_texel_buffer(VkBufferView vk_buffer_view,
const VKDescriptorSet::Location location)
{
vk_buffer_views_.append(vertex_buffer.vk_buffer_view_get());
vk_buffer_views_.append(vk_buffer_view);
vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
VK_NULL_HANDLE,
vk_descriptor_set,
location,
0,
1,
@@ -89,7 +60,7 @@ void VKDescriptorSetTracker::bind_image(VkDescriptorType vk_descriptor_type,
vk_descriptor_image_infos_.append({vk_sampler, vk_image_view, vk_image_layout});
vk_write_descriptor_sets_.append({VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
VK_NULL_HANDLE,
vk_descriptor_set,
location,
0,
1,
@@ -99,25 +70,226 @@ void VKDescriptorSetTracker::bind_image(VkDescriptorType vk_descriptor_type,
nullptr});
}
void VKDescriptorSetTracker::update(VKContext &context)
void VKDescriptorSetTracker::bind_image_resource(const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info)
{
const VKShader &shader = *unwrap(context.shader);
VKTexture &texture = *state_manager.images_.get(resource_binding.binding);
bind_image(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
VK_NULL_HANDLE,
texture.image_view_get(resource_binding.arrayed).vk_handle(),
VK_IMAGE_LAYOUT_GENERAL,
resource_binding.location);
/* Update access info. */
uint32_t layer_base = 0;
uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS;
if (resource_binding.arrayed == VKImageViewArrayed::ARRAYED && texture.is_texture_view()) {
IndexRange layer_range = texture.layer_range();
layer_base = layer_range.start();
layer_count = layer_range.size();
}
access_info.images.append({texture.vk_image_handle(),
resource_binding.access_mask,
to_vk_image_aspect_flag_bits(texture.device_format_get()),
layer_base,
layer_count});
}
void VKDescriptorSetTracker::bind_texture_resource(const VKDevice &device,
const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info)
{
const BindSpaceTextures::Elem &elem = state_manager.textures_.get(resource_binding.binding);
switch (elem.resource_type) {
case BindSpaceTextures::Type::VertexBuffer: {
VKVertexBuffer *vertex_buffer = static_cast<VKVertexBuffer *>(elem.resource);
vertex_buffer->ensure_updated();
vertex_buffer->ensure_buffer_view();
bind_texel_buffer(vertex_buffer->vk_buffer_view_get(), resource_binding.location);
access_info.buffers.append({vertex_buffer->vk_handle(), resource_binding.access_mask});
break;
}
case BindSpaceTextures::Type::Texture: {
VKTexture *texture = static_cast<VKTexture *>(elem.resource);
if (texture->type_ != GPU_TEXTURE_BUFFER) {
const VKSampler &sampler = device.samplers().get(elem.sampler);
bind_image(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
sampler.vk_handle(),
texture->image_view_get(resource_binding.arrayed).vk_handle(),
VK_IMAGE_LAYOUT_GENERAL,
resource_binding.location);
access_info.images.append({texture->vk_image_handle(),
resource_binding.access_mask,
to_vk_image_aspect_flag_bits(texture->device_format_get()),
0,
VK_REMAINING_ARRAY_LAYERS});
}
else {
/* Texture buffers are no textures, but wrap around vertex buffers and need to be
* bound as texel buffers. */
/* TODO: Investigate if this can be improved in the API. */
VKVertexBuffer *vertex_buffer = texture->source_buffer_;
vertex_buffer->ensure_updated();
vertex_buffer->ensure_buffer_view();
bind_texel_buffer(vertex_buffer->vk_buffer_view_get(), resource_binding.location);
access_info.buffers.append({vertex_buffer->vk_handle(), resource_binding.access_mask});
}
break;
}
case BindSpaceTextures::Type::Unused: {
BLI_assert_unreachable();
}
}
}
void VKDescriptorSetTracker::bind_storage_buffer_resource(
const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info)
{
const BindSpaceStorageBuffers::Elem &elem = state_manager.storage_buffers_.get(
resource_binding.binding);
VkBuffer vk_buffer = VK_NULL_HANDLE;
VkDeviceSize vk_device_size = 0;
switch (elem.resource_type) {
case BindSpaceStorageBuffers::Type::IndexBuffer: {
VKIndexBuffer *index_buffer = static_cast<VKIndexBuffer *>(elem.resource);
index_buffer->ensure_updated();
vk_buffer = index_buffer->vk_handle();
vk_device_size = index_buffer->size_get();
break;
}
case BindSpaceStorageBuffers::Type::VertexBuffer: {
VKVertexBuffer *vertex_buffer = static_cast<VKVertexBuffer *>(elem.resource);
vertex_buffer->ensure_updated();
vk_buffer = vertex_buffer->vk_handle();
vk_device_size = vertex_buffer->size_used_get();
break;
}
case BindSpaceStorageBuffers::Type::UniformBuffer: {
VKUniformBuffer *uniform_buffer = static_cast<VKUniformBuffer *>(elem.resource);
uniform_buffer->ensure_updated();
vk_buffer = uniform_buffer->vk_handle();
vk_device_size = uniform_buffer->size_in_bytes();
break;
}
case BindSpaceStorageBuffers::Type::StorageBuffer: {
VKStorageBuffer *storage_buffer = static_cast<VKStorageBuffer *>(elem.resource);
storage_buffer->ensure_allocated();
vk_buffer = storage_buffer->vk_handle();
vk_device_size = storage_buffer->size_in_bytes();
break;
}
case BindSpaceStorageBuffers::Type::Unused: {
BLI_assert_unreachable();
}
}
bind_buffer(
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk_buffer, vk_device_size, resource_binding.location);
access_info.buffers.append({vk_buffer, resource_binding.access_mask});
}
void VKDescriptorSetTracker::bind_uniform_buffer_resource(
const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info)
{
VKUniformBuffer &uniform_buffer = *state_manager.uniform_buffers_.get(resource_binding.binding);
uniform_buffer.ensure_updated();
bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
uniform_buffer.vk_handle(),
uniform_buffer.size_in_bytes(),
resource_binding.location);
access_info.buffers.append({uniform_buffer.vk_handle(), resource_binding.access_mask});
}
void VKDescriptorSetTracker::bind_push_constants(VKPushConstants &push_constants,
render_graph::VKResourceAccessInfo &access_info)
{
if (push_constants.layout_get().storage_type_get() !=
VKPushConstants::StorageType::UNIFORM_BUFFER)
{
return;
}
push_constants.update_uniform_buffer();
const VKUniformBuffer &uniform_buffer = *push_constants.uniform_buffer_get().get();
bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
uniform_buffer.vk_handle(),
uniform_buffer.size_in_bytes(),
push_constants.layout_get().descriptor_set_location_get());
access_info.buffers.append({uniform_buffer.vk_handle(), VK_ACCESS_UNIFORM_READ_BIT});
}
void VKDescriptorSetTracker::bind_shader_resources(const VKDevice &device,
const VKStateManager &state_manager,
VKShader &shader,
render_graph::VKResourceAccessInfo &access_info)
{
const VKShaderInterface &shader_interface = shader.interface_get();
for (const VKResourceBinding &resource_binding : shader_interface.resource_bindings_get()) {
if (resource_binding.binding == -1) {
continue;
}
switch (resource_binding.bind_type) {
case shader::ShaderCreateInfo::Resource::BindType::IMAGE:
bind_image_resource(state_manager, resource_binding, access_info);
break;
case shader::ShaderCreateInfo::Resource::BindType::SAMPLER:
bind_texture_resource(device, state_manager, resource_binding, access_info);
break;
case shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
bind_storage_buffer_resource(state_manager, resource_binding, access_info);
break;
case shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
bind_uniform_buffer_resource(state_manager, resource_binding, access_info);
break;
}
}
/* Bind uniform push constants to descriptor set. */
bind_push_constants(shader.push_constants, access_info);
}
void VKDescriptorSetTracker::update_descriptor_set(VKContext &context,
render_graph::VKResourceAccessInfo &access_info)
{
VKShader &shader = *unwrap(context.shader);
VKStateManager &state_manager = context.state_manager_get();
/* Can we reuse previous descriptor set. */
if (!state_manager.is_dirty &&
!assign_if_different(vk_descriptor_set_layout_, shader.vk_descriptor_set_layout_get()) &&
shader.push_constants.layout_get().storage_type_get() !=
VKPushConstants::StorageType::UNIFORM_BUFFER)
{
return;
}
state_manager.is_dirty = false;
/* Allocate a new descriptor set. */
VkDescriptorSetLayout vk_descriptor_set_layout = shader.vk_descriptor_set_layout_get();
active_vk_descriptor_set_layout = vk_descriptor_set_layout;
tracked_resource_for(context, true);
std::unique_ptr<VKDescriptorSet> &descriptor_set = active_descriptor_set();
VkDescriptorSet vk_descriptor_set = descriptor_set->vk_handle();
vk_descriptor_set = context.descriptor_pools_get().allocate(vk_descriptor_set_layout);
BLI_assert(vk_descriptor_set != VK_NULL_HANDLE);
debug::object_label(vk_descriptor_set, shader.name_get());
const VKDevice &device = VKBackend::get().device;
bind_shader_resources(device, state_manager, shader, access_info);
upload_descriptor_sets();
}
/* Populate the final addresses and handles */
void VKDescriptorSetTracker::upload_descriptor_sets()
{
/* Finalize pointers that could have changed due to reallocations. */
int buffer_index = 0;
int buffer_view_index = 0;
int image_index = 0;
for (int write_index : vk_write_descriptor_sets_.index_range()) {
VkWriteDescriptorSet &vk_write_descriptor_set = vk_write_descriptor_sets_[write_index++];
vk_write_descriptor_set.dstSet = vk_descriptor_set;
switch (vk_write_descriptor_set.descriptorType) {
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
@@ -146,12 +318,11 @@ void VKDescriptorSetTracker::update(VKContext &context)
vk_write_descriptor_sets_.data(),
0,
nullptr);
reset();
}
std::unique_ptr<VKDescriptorSet> VKDescriptorSetTracker::create_resource(VKContext &context)
{
return context.descriptor_pools_get().allocate(active_vk_descriptor_set_layout);
vk_descriptor_image_infos_.clear();
vk_descriptor_buffer_infos_.clear();
vk_buffer_views_.clear();
vk_write_descriptor_sets_.clear();
}
} // namespace blender::gpu

View File

@@ -13,20 +13,18 @@
#include "gpu_shader_private.hh"
#include "render_graph/vk_resource_access_info.hh"
#include "vk_buffer.hh"
#include "vk_common.hh"
#include "vk_resource_tracker.hh"
#include "vk_uniform_buffer.hh"
namespace blender::gpu {
class VKIndexBuffer;
class VKShaderInterface;
class VKStorageBuffer;
class VKTexture;
class VKUniformBuffer;
class VKVertexBuffer;
class VKDescriptorSetTracker;
class VKSampler;
struct VKResourceBinding;
class VKStateManager;
class VKDevice;
class VKPushConstants;
class VKShader;
/**
* In vulkan shader resources (images and buffers) are grouped in descriptor sets.
@@ -51,6 +49,10 @@ class VKDescriptorSet : NonCopyable {
* be centralized here. Location will then also contain the descriptor set index.
*/
struct Location {
friend class VKDescriptorSetTracker;
friend class VKShaderInterface;
friend struct VKResourceBinding;
private:
/**
* References to a binding in the descriptor set.
@@ -71,46 +73,10 @@ class VKDescriptorSet : NonCopyable {
{
return binding;
}
friend class VKDescriptorSetTracker;
friend class VKShaderInterface;
};
VkDescriptorPool vk_descriptor_pool_ = VK_NULL_HANDLE;
VkDescriptorSet vk_descriptor_set_ = VK_NULL_HANDLE;
public:
VKDescriptorSet() = default;
VKDescriptorSet(VkDescriptorPool vk_descriptor_pool, VkDescriptorSet vk_descriptor_set)
: vk_descriptor_pool_(vk_descriptor_pool), vk_descriptor_set_(vk_descriptor_set)
{
BLI_assert(vk_descriptor_set_ != VK_NULL_HANDLE);
}
VKDescriptorSet(VKDescriptorSet &&other);
virtual ~VKDescriptorSet();
VKDescriptorSet &operator=(VKDescriptorSet &&other)
{
BLI_assert(other.vk_descriptor_set_ != VK_NULL_HANDLE);
vk_descriptor_set_ = other.vk_descriptor_set_;
vk_descriptor_pool_ = other.vk_descriptor_pool_;
other.vk_descriptor_set_ = VK_NULL_HANDLE;
other.vk_descriptor_pool_ = VK_NULL_HANDLE;
return *this;
}
VkDescriptorSet vk_handle() const
{
return vk_descriptor_set_;
}
VkDescriptorPool vk_pool_handle() const
{
return vk_descriptor_pool_;
}
};
class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
class VKDescriptorSetTracker {
friend class VKDescriptorSet;
Vector<VkBufferView> vk_buffer_views_;
@@ -118,14 +84,46 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
Vector<VkDescriptorImageInfo> vk_descriptor_image_infos_;
Vector<VkWriteDescriptorSet> vk_write_descriptor_sets_;
/* Last used layout to identify changes. */
VkDescriptorSetLayout vk_descriptor_set_layout_ = VK_NULL_HANDLE;
public:
VkDescriptorSetLayout active_vk_descriptor_set_layout = VK_NULL_HANDLE;
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
VKDescriptorSetTracker() {}
void reset();
/**
* Update the descriptor set. Reuses previous descriptor set when no changes are detected. This
* improves performance when working with large grease pencil scenes.
*/
void update_descriptor_set(VKContext &context,
render_graph::VKResourceAccessInfo &resource_access_info);
void upload_descriptor_sets();
void bind_texel_buffer(VKVertexBuffer &vertex_buffer, VKDescriptorSet::Location location);
private:
void bind_shader_resources(const VKDevice &device,
const VKStateManager &state_manager,
VKShader &shader,
render_graph::VKResourceAccessInfo &access_info);
void bind_image_resource(const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info);
void bind_texture_resource(const VKDevice &device,
const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info);
void bind_storage_buffer_resource(const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info);
void bind_uniform_buffer_resource(const VKStateManager &state_manager,
const VKResourceBinding &resource_binding,
render_graph::VKResourceAccessInfo &access_info);
void bind_push_constants(VKPushConstants &push_constants,
render_graph::VKResourceAccessInfo &access_info);
void bind_texel_buffer(VkBufferView vk_buffer_view, VKDescriptorSet::Location location);
void bind_buffer(VkDescriptorType vk_descriptor_type,
VkBuffer vk_buffer,
VkDeviceSize size_in_bytes,
@@ -135,21 +133,6 @@ class VKDescriptorSetTracker : protected VKResourceTracker<VKDescriptorSet> {
VkImageView vk_image_view,
VkImageLayout vk_image_layout,
VKDescriptorSet::Location location);
std::unique_ptr<VKDescriptorSet> &active_descriptor_set()
{
return active_resource();
}
/**
* Update the descriptor set on the device.
*/
void update(VKContext &context);
protected:
std::unique_ptr<VKDescriptorSet> create_resource(VKContext &context) override;
private:
};
} // namespace blender::gpu

View File

@@ -249,7 +249,7 @@ class VKDevice : public NonCopyable {
return debugging_tools_;
}
VKSamplers &samplers()
const VKSamplers &samplers() const
{
return samplers_;
}

View File

@@ -53,7 +53,7 @@ void VKImmediate::end()
VKContext &context = *VKContext::get();
BLI_assert(context.shader == unwrap(shader));
render_graph::VKResourceAccessInfo &resource_access_info = context.update_and_get_access_info();
render_graph::VKResourceAccessInfo &resource_access_info = context.reset_and_get_access_info();
VKStateManager &state_manager = context.state_manager_get();
state_manager.apply_state();
vertex_attributes_.update_bindings(*this);

View File

@@ -43,28 +43,8 @@ void VKIndexBuffer::upload_data()
void VKIndexBuffer::bind_as_ssbo(uint binding)
{
VKContext::get()->state_manager_get().storage_buffer_bind(*this, binding);
}
void VKIndexBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
{
BLI_assert(bind_type == shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER);
ensure_updated();
const std::optional<VKDescriptorSet::Location> location =
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
data.descriptor_set.bind_buffer(
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk_handle(), size_get(), *location);
render_graph::VKBufferAccess buffer_access = {};
buffer_access.vk_buffer = buffer_.vk_handle();
buffer_access.vk_access_flags = data.shader_interface.access_mask(bind_type, binding);
data.resource_access_info.buffers.append(buffer_access);
}
VKContext::get()->state_manager_get().storage_buffer_bind(
BindSpaceStorageBuffers::Type::IndexBuffer, this, binding);
}
void VKIndexBuffer::read(uint32_t *data) const

View File

@@ -10,22 +10,17 @@
#include "GPU_index_buffer.hh"
#include "vk_bindable_resource.hh"
#include "vk_buffer.hh"
namespace blender::gpu {
class VKIndexBuffer : public IndexBuf, public VKBindableResource {
class VKIndexBuffer : public IndexBuf {
VKBuffer buffer_;
public:
void upload_data() override;
void bind_as_ssbo(uint binding) override;
void add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
void read(uint32_t *data) const override;
@@ -40,10 +35,11 @@ class VKIndexBuffer : public IndexBuf, public VKBindableResource {
return to_vk_index_type(index_type_);
}
void ensure_updated();
private:
void strip_restart_indices() override;
void allocate();
void ensure_updated();
VKBuffer &buffer_get();
const VKBuffer &buffer_get() const;
};

View File

@@ -145,28 +145,6 @@ VKPushConstants &VKPushConstants::operator=(VKPushConstants &&other)
return *this;
}
void VKPushConstants::update(VKContext &context)
{
VKDescriptorSetTracker &descriptor_set = context.descriptor_set_get();
switch (layout_get().storage_type_get()) {
case VKPushConstants::StorageType::NONE:
break;
case VKPushConstants::StorageType::PUSH_CONSTANTS:
break;
case VKPushConstants::StorageType::UNIFORM_BUFFER:
update_uniform_buffer();
const VKUniformBuffer &buffer = *uniform_buffer_get();
descriptor_set.bind_buffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
buffer.vk_handle(),
buffer.size_in_bytes(),
layout_get().descriptor_set_location_get());
break;
}
}
void VKPushConstants::update_uniform_buffer()
{
BLI_assert(layout_->storage_type_get() == StorageType::UNIFORM_BUFFER);

View File

@@ -259,12 +259,6 @@ class VKPushConstants : VKResourceTracker<VKUniformBuffer> {
is_dirty_ = true;
}
/**
* Update the GPU resources with the latest push constants.
*/
void update(VKContext &context);
private:
/**
* When storage type = StorageType::UNIFORM_BUFFER use this method to update the uniform
* buffer.

View File

@@ -45,7 +45,7 @@ void VKSamplers::free()
}
}
const VKSampler &VKSamplers::get(const GPUSamplerState &sampler_state)
const VKSampler &VKSamplers::get(const GPUSamplerState &sampler_state) const
{
BLI_assert(sampler_state.type != GPU_SAMPLER_STATE_TYPE_INTERNAL);

View File

@@ -29,7 +29,7 @@ class VKSamplers : NonCopyable {
void init();
void free();
const VKSampler &get(const GPUSamplerState &sampler_state);
const VKSampler &get(const GPUSamplerState &sampler_state) const;
};
} // namespace blender::gpu

View File

@@ -9,6 +9,7 @@
#include "vk_shader_interface.hh"
#include "vk_backend.hh"
#include "vk_context.hh"
#include "vk_state_manager.hh"
namespace blender::gpu {
@@ -178,14 +179,8 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
init_descriptor_set_layout_info(info, resources_len, all_resources, push_constants_storage_type);
/* Update the descriptor set locations, bind types and access masks. */
descriptor_set_locations_ = Array<VKDescriptorSet::Location>(resources_len);
descriptor_set_locations_.fill(-1);
descriptor_set_bind_types_ = Array<shader::ShaderCreateInfo::Resource::BindType>(resources_len);
descriptor_set_bind_types_.fill(shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER);
access_masks_ = Array<VkAccessFlags>(resources_len);
access_masks_.fill(VK_ACCESS_NONE);
arrayed_ = Array<VKImageViewArrayed>(resources_len);
arrayed_.fill(VKImageViewArrayed::DONT_CARE);
resource_bindings_ = Array<VKResourceBinding>(resources_len);
resource_bindings_.fill({});
uint32_t descriptor_set_location = 0;
for (const ShaderCreateInfo::SubpassIn &subpass_in : info.subpass_inputs_) {
@@ -282,10 +277,7 @@ void VKShaderInterface::descriptor_set_location_update(
"Incorrect parameter, bind types do not match.");
int32_t index = shader_input_index(inputs_, shader_input);
BLI_assert(descriptor_set_locations_[index].binding == -1);
descriptor_set_locations_[index] = location;
descriptor_set_bind_types_[index] = bind_type;
arrayed_[index] = arrayed;
BLI_assert(resource_bindings_[index].binding == -1);
VkAccessFlags vk_access_flags = VK_ACCESS_NONE;
if (resource.has_value()) {
@@ -323,21 +315,20 @@ void VKShaderInterface::descriptor_set_location_update(
else if (bind_type == shader::ShaderCreateInfo::Resource::BindType::SAMPLER) {
vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
}
access_masks_[index] = vk_access_flags;
VKResourceBinding &resource_binding = resource_bindings_[index];
resource_binding.bind_type = bind_type;
resource_binding.binding = shader_input->binding;
resource_binding.location = location;
resource_binding.arrayed = arrayed;
resource_binding.access_mask = vk_access_flags;
}
const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
const VKResourceBinding &VKShaderInterface::resource_binding_info(
const ShaderInput *shader_input) const
{
int32_t index = shader_input_index(inputs_, shader_input);
return descriptor_set_locations_[index];
}
const shader::ShaderCreateInfo::Resource::BindType VKShaderInterface::descriptor_set_bind_type(
const ShaderInput *shader_input) const
{
int32_t index = shader_input_index(inputs_, shader_input);
return descriptor_set_bind_types_[index];
return resource_bindings_[index];
}
const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
@@ -345,7 +336,7 @@ const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
{
const ShaderInput *shader_input = shader_input_get(resource);
BLI_assert(shader_input);
return descriptor_set_location(shader_input);
return resource_binding_info(shader_input).location;
}
const std::optional<VKDescriptorSet::Location> VKShaderInterface::descriptor_set_location(
@@ -355,16 +346,11 @@ const std::optional<VKDescriptorSet::Location> VKShaderInterface::descriptor_set
if (shader_input == nullptr) {
return std::nullopt;
}
if (descriptor_set_bind_type(shader_input) != bind_type) {
const VKResourceBinding &resource_binding = resource_binding_info(shader_input);
if (resource_binding.bind_type != bind_type) {
return std::nullopt;
}
return descriptor_set_location(shader_input);
}
const VkAccessFlags VKShaderInterface::access_mask(const ShaderInput *shader_input) const
{
int32_t index = shader_input_index(inputs_, shader_input);
return access_masks_[index];
return resource_binding.location;
}
const VkAccessFlags VKShaderInterface::access_mask(
@@ -374,10 +360,11 @@ const VkAccessFlags VKShaderInterface::access_mask(
if (shader_input == nullptr) {
return VK_ACCESS_NONE;
}
if (descriptor_set_bind_type(shader_input) != bind_type) {
const VKResourceBinding &resource_binding = resource_binding_info(shader_input);
if (resource_binding.bind_type != bind_type) {
return VK_ACCESS_NONE;
}
return access_mask(shader_input);
return resource_binding.access_mask;
}
const VKImageViewArrayed VKShaderInterface::arrayed(
@@ -387,8 +374,7 @@ const VKImageViewArrayed VKShaderInterface::arrayed(
if (shader_input == nullptr) {
return VKImageViewArrayed::DONT_CARE;
}
int32_t index = shader_input_index(inputs_, shader_input);
return arrayed_[index];
return resource_binding_info(shader_input).arrayed;
}
const ShaderInput *VKShaderInterface::shader_input_get(

View File

@@ -19,14 +19,21 @@
#include "vk_push_constants.hh"
namespace blender::gpu {
struct VKResourceBinding {
shader::ShaderCreateInfo::Resource::BindType bind_type =
shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER;
int binding = -1;
VKDescriptorSet::Location location;
VKImageViewArrayed arrayed = VKImageViewArrayed::DONT_CARE;
VkAccessFlags access_mask = VK_ACCESS_NONE;
};
class VKShaderInterface : public ShaderInterface {
private:
Array<VKDescriptorSet::Location> descriptor_set_locations_;
Array<shader::ShaderCreateInfo::Resource::BindType> descriptor_set_bind_types_;
/** Image views should match the binding arrayed aspect. */
Array<VKImageViewArrayed> arrayed_;
Array<VkAccessFlags> access_masks_;
/** Binding information for each shader input. */
Array<VKResourceBinding> resource_bindings_;
VKDescriptorSetLayoutInfo descriptor_set_layout_info_;
VKPushConstants::Layout push_constants_layout_;
@@ -77,6 +84,11 @@ class VKShaderInterface : public ShaderInterface {
return (shader_builtins_ & shader::BuiltinBits::POINT_SIZE) == shader::BuiltinBits::POINT_SIZE;
}
const Span<VKResourceBinding> resource_bindings_get() const
{
return resource_bindings_;
}
private:
void init_descriptor_set_layout_info(const shader::ShaderCreateInfo &info,
int64_t resources_len,
@@ -91,10 +103,8 @@ class VKShaderInterface : public ShaderInterface {
const ShaderInput *shader_input_get(const shader::ShaderCreateInfo::Resource &resource) const;
const ShaderInput *shader_input_get(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
const VKDescriptorSet::Location descriptor_set_location(const ShaderInput *shader_input) const;
const shader::ShaderCreateInfo::Resource::BindType descriptor_set_bind_type(
const ShaderInput *shader_input) const;
const VkAccessFlags access_mask(const ShaderInput *shader_input) const;
const VKResourceBinding &resource_binding_info(const ShaderInput *shader_input) const;
void descriptor_set_location_update(
const ShaderInput *shader_input,
const VKDescriptorSet::Location location,

View File

@@ -24,22 +24,6 @@ void VKStateManager::apply_state()
* If this leads to issues we should have an active state. */
}
void VKStateManager::apply_bindings(VKContext &context,
render_graph::VKResourceAccessInfo &resource_access_info)
{
VKShader *shader = unwrap(context.shader);
if (shader == nullptr) {
return;
}
AddToDescriptorSetContext data(
context.descriptor_set_get(), shader->interface_get(), resource_access_info);
context.descriptor_set_get().reset();
textures_.add_to_descriptor_set(data);
images_.add_to_descriptor_set(data);
uniform_buffers_.add_to_descriptor_set(data);
storage_buffers_.add_to_descriptor_set(data);
}
void VKStateManager::force_state()
{
/* Intentionally empty. State is polled during pipeline creation and is always forced. */
@@ -69,86 +53,104 @@ void VKStateManager::issue_barrier(eGPUBarrier barrier_bits)
}
}
void VKStateManager::texture_bind(Texture *tex, GPUSamplerState sampler, int unit)
void VKStateManager::texture_bind(Texture *texture, GPUSamplerState sampler, int binding)
{
VKTexture *texture = unwrap(tex);
textures_.bind(unit, *texture, sampler);
textures_.bind(BindSpaceTextures::Type::Texture, texture, sampler, binding);
is_dirty = true;
}
void VKStateManager::texture_unbind(Texture *tex)
void VKStateManager::texture_unbind(Texture *texture)
{
VKTexture *texture = unwrap(tex);
textures_.unbind(*texture);
textures_.unbind(texture);
is_dirty = true;
}
void VKStateManager::texture_unbind_all()
{
textures_.unbind_all();
is_dirty = true;
}
void VKStateManager::image_bind(Texture *tex, int binding)
{
VKTexture *texture = unwrap(tex);
images_.bind(binding, *texture);
images_.bind(texture, binding);
is_dirty = true;
}
void VKStateManager::image_unbind(Texture *tex)
{
VKTexture *texture = unwrap(tex);
images_.unbind(*texture);
images_.unbind(texture);
is_dirty = true;
}
void VKStateManager::image_unbind_all()
{
images_.unbind_all();
is_dirty = true;
}
void VKStateManager::uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int slot)
void VKStateManager::uniform_buffer_bind(VKUniformBuffer *uniform_buffer, int binding)
{
uniform_buffers_.bind(slot, *uniform_buffer);
uniform_buffers_.bind(uniform_buffer, binding);
is_dirty = true;
}
void VKStateManager::uniform_buffer_unbind(VKUniformBuffer *uniform_buffer)
{
uniform_buffers_.unbind(*uniform_buffer);
uniform_buffers_.unbind(uniform_buffer);
is_dirty = true;
}
void VKStateManager::uniform_buffer_unbind_all()
{
uniform_buffers_.unbind_all();
is_dirty = true;
}
void VKStateManager::unbind_from_all_namespaces(VKBindableResource &resource)
void VKStateManager::unbind_from_all_namespaces(void *resource)
{
uniform_buffers_.unbind(resource);
storage_buffers_.unbind(resource);
images_.unbind(resource);
textures_.unbind(resource);
is_dirty = true;
}
void VKStateManager::texel_buffer_bind(VKVertexBuffer &vertex_buffer, int slot)
void VKStateManager::texel_buffer_bind(VKVertexBuffer &vertex_buffer, int binding)
{
textures_.bind(slot, vertex_buffer);
textures_.bind(BindSpaceTextures::Type::VertexBuffer,
&vertex_buffer,
GPUSamplerState::default_sampler(),
binding);
is_dirty = true;
}
void VKStateManager::texel_buffer_unbind(VKVertexBuffer &vertex_buffer)
{
textures_.unbind(vertex_buffer);
textures_.unbind(&vertex_buffer);
is_dirty = true;
}
void VKStateManager::storage_buffer_bind(VKBindableResource &resource, int slot)
void VKStateManager::storage_buffer_bind(BindSpaceStorageBuffers::Type resource_type,
void *resource,
int binding)
{
storage_buffers_.bind(slot, resource);
storage_buffers_.bind(resource_type, resource, binding);
is_dirty = true;
}
void VKStateManager::storage_buffer_unbind(VKBindableResource &resource)
void VKStateManager::storage_buffer_unbind(void *resource)
{
storage_buffers_.unbind(resource);
is_dirty = true;
}
void VKStateManager::storage_buffer_unbind_all()
{
storage_buffers_.unbind_all();
is_dirty = true;
}
void VKStateManager::texture_unpack_row_length_set(uint len)

View File

@@ -13,7 +13,6 @@
#include "BLI_array.hh"
#include "render_graph/vk_resource_access_info.hh"
#include "vk_bindable_resource.hh"
namespace blender::gpu {
class VKTexture;
@@ -22,27 +21,202 @@ class VKVertexBuffer;
class VKStorageBuffer;
class VKIndexBuffer;
class VKContext;
class VKDescriptorSetTracker;
/**
* Offset when searching for bindings.
*
* When shaders combine images and samplers, the images have to be offset to find the correct
* shader input. Both textures and images are stored in the uniform list and their ID can be
* overlapping.
*/
static constexpr int BIND_SPACE_IMAGE_OFFSET = 512;
/** Bind space for a uniform buffers. */
class BindSpaceUniformBuffers {
public:
Vector<VKUniformBuffer *> bound_resources;
void bind(VKUniformBuffer *resource, int binding)
{
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding] = resource;
}
VKUniformBuffer *get(int binding) const
{
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index] == resource) {
bound_resources[index] = nullptr;
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
/**
* Bind space for image resources.
*/
template<int Offset> class BindSpaceImages {
public:
Vector<VKTexture *> bound_resources;
void bind(VKTexture *resource, int binding)
{
if (binding >= Offset) {
binding -= Offset;
}
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding] = resource;
}
VKTexture *get(int binding) const
{
if (binding >= Offset) {
binding -= Offset;
}
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index] == resource) {
bound_resources[index] = nullptr;
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
/** Bind space for storage buffers. */
class BindSpaceStorageBuffers {
public:
enum class Type {
Unused,
UniformBuffer,
VertexBuffer,
IndexBuffer,
StorageBuffer,
};
struct Elem {
Type resource_type;
void *resource;
};
Vector<Elem> bound_resources;
void bind(Type resource_type, void *resource, int binding)
{
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding].resource_type = resource_type;
bound_resources[binding].resource = resource;
}
const Elem &get(int binding) const
{
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index].resource == resource) {
bound_resources[index].resource = nullptr;
bound_resources[index].resource_type = Type::Unused;
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
/** Bind space for textures. */
class BindSpaceTextures {
public:
enum class Type {
Unused,
Texture,
VertexBuffer,
};
struct Elem {
Type resource_type;
void *resource;
GPUSamplerState sampler;
};
Vector<Elem> bound_resources;
void bind(Type resource_type, void *resource, GPUSamplerState sampler, int binding)
{
if (bound_resources.size() <= binding) {
bound_resources.resize(binding + 1);
}
bound_resources[binding].resource_type = resource_type;
bound_resources[binding].resource = resource;
bound_resources[binding].sampler = sampler;
}
const Elem &get(int binding) const
{
return bound_resources[binding];
}
void unbind(void *resource)
{
for (int index : IndexRange(bound_resources.size())) {
if (bound_resources[index].resource == resource) {
bound_resources[index].resource = nullptr;
bound_resources[index].resource_type = Type::Unused;
bound_resources[index].sampler = GPUSamplerState::default_sampler();
}
}
}
void unbind_all()
{
bound_resources.clear();
}
};
class VKStateManager : public StateManager {
friend class VKDescriptorSetTracker;
uint texture_unpack_row_length_ = 0;
VKBindSpace<shader::ShaderCreateInfo::Resource::BindType::SAMPLER> textures_;
VKBindSpace<shader::ShaderCreateInfo::Resource::BindType::IMAGE, BIND_SPACE_IMAGE_OFFSET>
images_;
VKBindSpace<shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER> uniform_buffers_;
VKBindSpace<shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER> storage_buffers_;
BindSpaceTextures textures_;
BindSpaceImages<BIND_SPACE_IMAGE_OFFSET> images_;
BindSpaceUniformBuffers uniform_buffers_;
BindSpaceStorageBuffers storage_buffers_;
public:
bool is_dirty = false;
void apply_state() override;
void force_state() override;
void issue_barrier(eGPUBarrier barrier_bits) override;
/** Apply resources to the bindings of the active shader. */
void apply_bindings(VKContext &context,
render_graph::VKResourceAccessInfo &resource_access_info);
void texture_bind(Texture *tex, GPUSamplerState sampler, int unit) override;
void texture_unbind(Texture *tex) override;
void texture_unbind_all() override;
@@ -58,11 +232,13 @@ class VKStateManager : public StateManager {
void texel_buffer_bind(VKVertexBuffer &vertex_buffer, int slot);
void texel_buffer_unbind(VKVertexBuffer &vertex_buffer);
void storage_buffer_bind(VKBindableResource &resource, int slot);
void storage_buffer_unbind(VKBindableResource &resource);
void storage_buffer_bind(BindSpaceStorageBuffers::Type resource_type,
void *resource,
int binding);
void storage_buffer_unbind(void *resource);
void storage_buffer_unbind_all();
void unbind_from_all_namespaces(VKBindableResource &bindable_resource);
void unbind_from_all_namespaces(void *resource);
void texture_unpack_row_length_set(uint len) override;

View File

@@ -50,30 +50,16 @@ void VKStorageBuffer::allocate()
void VKStorageBuffer::bind(int slot)
{
VKContext &context = *VKContext::get();
context.state_manager_get().storage_buffer_bind(*this, slot);
}
void VKStorageBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
{
ensure_allocated();
const std::optional<VKDescriptorSet::Location> location =
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
data.descriptor_set.bind_buffer(
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk_handle(), size_in_bytes(), *location);
render_graph::VKBufferAccess buffer_access = {};
buffer_access.vk_buffer = buffer_.vk_handle();
buffer_access.vk_access_flags = data.shader_interface.access_mask(bind_type, binding);
data.resource_access_info.buffers.append(buffer_access);
}
context.state_manager_get().storage_buffer_bind(
BindSpaceStorageBuffers::Type::StorageBuffer, this, slot);
}
void VKStorageBuffer::unbind()
{
unbind_from_active_context();
VKContext *context = VKContext::get();
if (context) {
context->state_manager_get().storage_buffer_unbind(this);
}
}
void VKStorageBuffer::clear(uint32_t clear_value)

View File

@@ -13,13 +13,12 @@
#include "GPU_vertex_buffer.hh"
#include "gpu_storage_buffer_private.hh"
#include "vk_bindable_resource.hh"
#include "vk_buffer.hh"
namespace blender::gpu {
class VertBuf;
class VKStorageBuffer : public StorageBuf, public VKBindableResource {
class VKStorageBuffer : public StorageBuf {
GPUUsageType usage_;
VKBuffer buffer_;
@@ -28,10 +27,6 @@ class VKStorageBuffer : public StorageBuf, public VKBindableResource {
void update(const void *data) override;
void bind(int slot) override;
void add_to_descriptor_set(AddToDescriptorSetContext &data,
int slot,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
void unbind() override;
void clear(uint32_t clear_value) override;
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;

View File

@@ -506,54 +506,6 @@ bool VKTexture::allocate()
return result == VK_SUCCESS;
}
void VKTexture::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state)
{
/* Forwarding the call to the source vertex buffer as in vulkan a texel buffer is a buffer(view)
* and not a texture. */
if (type_ == GPU_TEXTURE_BUFFER) {
source_buffer_->add_to_descriptor_set(data, binding, bind_type, sampler_state);
return;
}
/* TODO: Based on the actual usage we should use
* VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL or VK_IMAGE_LAYOUT_GENERAL. */
const std::optional<VKDescriptorSet::Location> location =
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
const VKImageViewArrayed arrayed = data.shader_interface.arrayed(bind_type, binding);
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::IMAGE) {
data.descriptor_set.bind_image(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
VK_NULL_HANDLE,
image_view_get(arrayed).vk_handle(),
VK_IMAGE_LAYOUT_GENERAL,
*location);
}
else {
VKDevice &device = VKBackend::get().device;
const VKSampler &sampler = device.samplers().get(sampler_state);
data.descriptor_set.bind_image(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
sampler.vk_handle(),
image_view_get(arrayed).vk_handle(),
VK_IMAGE_LAYOUT_GENERAL,
*location);
}
uint32_t layer_base = 0;
uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS;
if (arrayed == VKImageViewArrayed::ARRAYED && is_texture_view()) {
layer_base = layer_offset_;
layer_count = vk_layer_count(VK_REMAINING_ARRAY_LAYERS);
}
data.resource_access_info.images.append({vk_image_handle(),
data.shader_interface.access_mask(bind_type, binding),
to_vk_image_aspect_flag_bits(device_format_),
layer_base,
layer_count});
}
}
/* -------------------------------------------------------------------- */
/** \name Image Views
* \{ */

View File

@@ -10,15 +10,18 @@
#include "gpu_texture_private.hh"
#include "vk_bindable_resource.hh"
#include "vk_context.hh"
#include "vk_image_view.hh"
namespace blender::gpu {
class VKSampler;
class VKDescriptorSetTracker;
class VKVertexBuffer;
class VKTexture : public Texture {
friend class VKDescriptorSetTracker;
class VKTexture : public Texture, public VKBindableResource {
/**
* Texture format how the texture is stored on the device.
*
@@ -93,11 +96,6 @@ class VKTexture : public Texture, public VKBindableResource {
/* TODO(fclem): Legacy. Should be removed at some point. */
uint gl_bindcode_get() const override;
void add_to_descriptor_set(AddToDescriptorSetContext &data,
int location,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
VkImage vk_image_handle() const
{
if (is_texture_view()) {

View File

@@ -50,10 +50,7 @@ void VKUniformBuffer::clear_to_zero()
buffer_.clear(context, 0);
}
void VKUniformBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
void VKUniformBuffer::ensure_updated()
{
if (!buffer_.is_allocated()) {
allocate();
@@ -64,23 +61,6 @@ void VKUniformBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
update(data_);
MEM_SAFE_FREE(data_);
}
const std::optional<VKDescriptorSet::Location> location =
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
data.descriptor_set.bind_buffer(
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk_handle(), size_in_bytes(), *location);
}
else {
data.descriptor_set.bind_buffer(
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk_handle(), size_in_bytes(), *location);
}
render_graph::VKBufferAccess buffer_access = {};
buffer_access.vk_buffer = buffer_.vk_handle();
buffer_access.vk_access_flags = data.shader_interface.access_mask(bind_type, binding);
data.resource_access_info.buffers.append(buffer_access);
}
}
void VKUniformBuffer::bind(int slot)
@@ -92,7 +72,8 @@ void VKUniformBuffer::bind(int slot)
void VKUniformBuffer::bind_as_ssbo(int slot)
{
VKContext &context = *VKContext::get();
context.state_manager_get().storage_buffer_bind(*this, slot);
context.state_manager_get().storage_buffer_bind(
BindSpaceStorageBuffers::Type::UniformBuffer, this, slot);
}
void VKUniformBuffer::unbind()
@@ -101,7 +82,7 @@ void VKUniformBuffer::unbind()
if (context != nullptr) {
VKStateManager &state_manager = context->state_manager_get();
state_manager.uniform_buffer_unbind(this);
state_manager.storage_buffer_unbind(*this);
state_manager.storage_buffer_unbind(this);
}
}

View File

@@ -12,12 +12,11 @@
#include "gpu_uniform_buffer_private.hh"
#include "vk_bindable_resource.hh"
#include "vk_buffer.hh"
namespace blender::gpu {
class VKUniformBuffer : public UniformBuf, public VKBindableResource, NonCopyable {
class VKUniformBuffer : public UniformBuf, NonCopyable {
VKBuffer buffer_;
public:
@@ -43,11 +42,7 @@ class VKUniformBuffer : public UniformBuf, public VKBindableResource, NonCopyabl
return size_in_bytes_;
}
/* Bindable resource */
void add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
void ensure_updated();
private:
void allocate();

View File

@@ -27,7 +27,7 @@ void VKVertexBuffer::bind_as_ssbo(uint binding)
{
VKContext &context = *VKContext::get();
VKStateManager &state_manager = context.state_manager_get();
state_manager.storage_buffer_bind(*this, binding);
state_manager.storage_buffer_bind(BindSpaceStorageBuffers::Type::VertexBuffer, this, binding);
}
void VKVertexBuffer::bind_as_texture(uint binding)
@@ -37,49 +37,30 @@ void VKVertexBuffer::bind_as_texture(uint binding)
state_manager.texel_buffer_bind(*this, binding);
}
void VKVertexBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
void VKVertexBuffer::ensure_updated()
{
const std::optional<VKDescriptorSet::Location> location =
data.shader_interface.descriptor_set_location(bind_type, binding);
if (!location) {
upload_data();
}
void VKVertexBuffer::ensure_buffer_view()
{
if (vk_buffer_view_ != VK_NULL_HANDLE) {
return;
}
upload_data();
VkBufferViewCreateInfo buffer_view_info = {};
eGPUTextureFormat texture_format = to_texture_format(&format);
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::SAMPLER &&
vk_buffer_view_ == VK_NULL_HANDLE)
{
VkBufferViewCreateInfo buffer_view_info = {};
eGPUTextureFormat texture_format = to_texture_format(&format);
buffer_view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
buffer_view_info.buffer = buffer_.vk_handle();
buffer_view_info.format = to_vk_format(texture_format);
buffer_view_info.range = buffer_.size_in_bytes();
buffer_view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
buffer_view_info.buffer = buffer_.vk_handle();
buffer_view_info.format = to_vk_format(texture_format);
buffer_view_info.range = buffer_.size_in_bytes();
VK_ALLOCATION_CALLBACKS;
const VKDevice &device = VKBackend::get().device;
vkCreateBufferView(
device.vk_handle(), &buffer_view_info, vk_allocation_callbacks, &vk_buffer_view_);
debug::object_label(vk_buffer_view_, "VertexBufferView");
}
/* TODO: Check if we can move this check inside the descriptor set. */
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::SAMPLER) {
data.descriptor_set.bind_texel_buffer(*this, *location);
}
else {
data.descriptor_set.bind_buffer(
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk_handle(), size_used_get(), *location);
}
render_graph::VKBufferAccess buffer_access = {};
buffer_access.vk_buffer = buffer_.vk_handle();
buffer_access.vk_access_flags = data.shader_interface.access_mask(bind_type, binding);
data.resource_access_info.buffers.append(buffer_access);
VK_ALLOCATION_CALLBACKS;
const VKDevice &device = VKBackend::get().device;
vkCreateBufferView(
device.vk_handle(), &buffer_view_info, vk_allocation_callbacks, &vk_buffer_view_);
debug::object_label(vk_buffer_view_, "VertexBufferView");
}
void VKVertexBuffer::wrap_handle(uint64_t /*handle*/)

View File

@@ -10,13 +10,12 @@
#include "GPU_vertex_buffer.hh"
#include "vk_bindable_resource.hh"
#include "vk_buffer.hh"
#include "vk_data_conversion.hh"
namespace blender::gpu {
class VKVertexBuffer : public VertBuf, public VKBindableResource {
class VKVertexBuffer : public VertBuf {
VKBuffer buffer_;
/** When a vertex buffer is used as a UNIFORM_TEXEL_BUFFER the buffer requires a buffer view. */
VkBufferView vk_buffer_view_ = VK_NULL_HANDLE;
@@ -28,10 +27,6 @@ class VKVertexBuffer : public VertBuf, public VKBindableResource {
void bind_as_ssbo(uint binding) override;
void bind_as_texture(uint binding) override;
void add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
void wrap_handle(uint64_t handle) override;
void update_sub(uint start, uint len, const void *data) override;
@@ -51,6 +46,8 @@ class VKVertexBuffer : public VertBuf, public VKBindableResource {
void device_format_ensure();
const GPUVertFormat &device_format_get() const;
void ensure_updated();
void ensure_buffer_view();
protected:
void acquire_data() override;