Vulkan: Shader interface access mask #120908

Merged
Jeroen Bakker merged 3 commits from Jeroen-Bakker/blender:vulkan/shader-interface-access-masks into main 2024-04-22 20:47:41 +02:00
18 changed files with 228 additions and 85 deletions

View File

@ -140,7 +140,8 @@ void VKBackend::samplers_update()
void VKBackend::compute_dispatch(int groups_x_len, int groups_y_len, int groups_z_len)
{
VKContext &context = *VKContext::get();
context.state_manager_get().apply_bindings();
render_graph::VKResourceAccessInfo resource_access_info = {};
context.state_manager_get().apply_bindings(context, resource_access_info);
context.bind_compute_pipeline();
VKCommandBuffers &command_buffers = context.command_buffers_get();
command_buffers.dispatch(groups_x_len, groups_y_len, groups_z_len);
@ -150,7 +151,8 @@ void VKBackend::compute_dispatch_indirect(StorageBuf *indirect_buf)
{
BLI_assert(indirect_buf);
VKContext &context = *VKContext::get();
context.state_manager_get().apply_bindings();
render_graph::VKResourceAccessInfo resource_access_info = {};
context.state_manager_get().apply_bindings(context, resource_access_info);
context.bind_compute_pipeline();
VKStorageBuffer &indirect_buffer = *unwrap(indirect_buf);
VKCommandBuffers &command_buffers = context.command_buffers_get();

View File

@ -29,7 +29,8 @@ void VKBatch::draw_setup()
VKIndexBuffer *index_buffer = index_buffer_get();
const bool draw_indexed = index_buffer != nullptr;
state_manager.apply_state();
state_manager.apply_bindings();
render_graph::VKResourceAccessInfo resource_access_info = {};
state_manager.apply_bindings(context, resource_access_info);
/*
* The next statements are order dependent. VBOs and IBOs must be uploaded, before resources can
* be bound. Uploading device located buffers flush the graphics pipeline and already bound

View File

@ -10,7 +10,52 @@
#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.
@ -21,11 +66,17 @@ class VKBindableResource {
public:
/**
* Bind the resource to the shader.
* 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 bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) = 0;
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();
@ -70,10 +121,11 @@ template<shader::ShaderCreateInfo::Resource::BindType BindType> class VKBindSpac
/**
* Apply registered bindings to the active shader.
*/
void apply_bindings()
void add_to_descriptor_set(AddToDescriptorSetContext &data)
{
for (ResourceBinding &binding : bindings_) {
binding.resource->bind(binding.binding, BindType, binding.sampler_state);
binding.resource->add_to_descriptor_set(
data, binding.binding, BindType, binding.sampler_state);
}
}

View File

@ -54,7 +54,8 @@ void VKImmediate::end()
BLI_assert(context.shader == unwrap(shader));
VKStateManager &state_manager = context.state_manager_get();
state_manager.apply_state();
state_manager.apply_bindings();
render_graph::VKResourceAccessInfo resource_access_info = {};
state_manager.apply_bindings(context, resource_access_info);
vertex_attributes_.update_bindings(*this);
context.bind_graphics_pipeline(prim_type, vertex_attributes_);
vertex_attributes_.bind(context);

View File

@ -51,20 +51,22 @@ void VKIndexBuffer::bind_as_ssbo(uint binding)
VKContext::get()->state_manager_get().storage_buffer_bind(*this, binding);
}
void VKIndexBuffer::bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
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();
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();
const std::optional<VKDescriptorSet::Location> location =
shader_interface.descriptor_set_location(bind_type, binding);
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
context.descriptor_set_get().bind_as_ssbo(*this, *location);
data.descriptor_set.bind_as_ssbo(*this, *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);
}
}

View File

@ -23,9 +23,10 @@ class VKIndexBuffer : public IndexBuf, public VKBindableResource {
void bind_as_ssbo(uint binding) override;
void bind(VKContext &context);
void bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) 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;

View File

@ -167,15 +167,18 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
/* Initialize the descriptor set layout. */
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);
uint32_t descriptor_set_location = 0;
for (ShaderCreateInfo::Resource &res : all_resources) {
const ShaderInput *input = shader_input_get(res);
BLI_assert(input);
descriptor_set_location_update(input, descriptor_set_location++, res.bind_type);
descriptor_set_location_update(input, descriptor_set_location++, res.bind_type, res);
}
/* Post initializing push constants. */
@ -186,7 +189,8 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
const ShaderInput *push_constant_input = ubo_get(PUSH_CONSTANTS_FALLBACK_NAME);
descriptor_set_location_update(push_constant_input,
push_constants_fallback_location,
shader::ShaderCreateInfo::Resource::UNIFORM_BUFFER);
shader::ShaderCreateInfo::Resource::UNIFORM_BUFFER,
std::nullopt);
}
push_constants_layout_.init(
info, *this, push_constants_storage_type, push_constant_descriptor_set_location);
@ -202,12 +206,45 @@ static int32_t shader_input_index(const ShaderInput *shader_inputs,
void VKShaderInterface::descriptor_set_location_update(
const ShaderInput *shader_input,
const VKDescriptorSet::Location location,
const shader::ShaderCreateInfo::Resource::BindType bind_type)
const shader::ShaderCreateInfo::Resource::BindType bind_type,
std::optional<const shader::ShaderCreateInfo::Resource> resource)
{
BLI_assert_msg(resource.has_value() ||
bind_type == shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER,
"Incorrect parameters, when no resource is given, it must be the uniform buffer "
"for storing push constants.");
BLI_assert_msg(!resource.has_value() || resource->bind_type == bind_type,
"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;
VkAccessFlags vk_access_flags = VK_ACCESS_NONE;
if (resource.has_value()) {
switch (resource->bind_type) {
case shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
vk_access_flags |= VK_ACCESS_UNIFORM_READ_BIT;
break;
case shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
case shader::ShaderCreateInfo::Resource::BindType::IMAGE:
if (bool(resource->storagebuf.qualifiers & shader::Qualifier::READ) == true) {
vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
}
if (bool(resource->storagebuf.qualifiers & shader::Qualifier::WRITE) == true) {
vk_access_flags |= VK_ACCESS_SHADER_WRITE_BIT;
}
break;
case shader::ShaderCreateInfo::Resource::BindType::SAMPLER:
vk_access_flags |= VK_ACCESS_SHADER_READ_BIT;
break;
};
}
else if (bind_type == shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
access_masks_[index] = VK_ACCESS_UNIFORM_READ_BIT;
}
access_masks_[index] = vk_access_flags;
}
const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
@ -245,6 +282,25 @@ const std::optional<VKDescriptorSet::Location> VKShaderInterface::descriptor_set
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];
}
const VkAccessFlags VKShaderInterface::access_mask(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
{
const ShaderInput *shader_input = shader_input_get(bind_type, binding);
if (shader_input == nullptr) {
return VK_ACCESS_NONE;
}
if (descriptor_set_bind_type(shader_input) != bind_type) {
return VK_ACCESS_NONE;
}
return access_mask(shader_input);
}
const ShaderInput *VKShaderInterface::shader_input_get(
const shader::ShaderCreateInfo::Resource &resource) const
{

View File

@ -31,6 +31,7 @@ class VKShaderInterface : public ShaderInterface {
uint32_t image_offset_ = 0;
Array<VKDescriptorSet::Location> descriptor_set_locations_;
Array<shader::ShaderCreateInfo::Resource::BindType> descriptor_set_bind_types_;
Array<VkAccessFlags> access_masks_;
VKDescriptorSetLayoutInfo descriptor_set_layout_info_;
VKPushConstants::Layout push_constants_layout_;
@ -47,6 +48,17 @@ class VKShaderInterface : public ShaderInterface {
const std::optional<VKDescriptorSet::Location> descriptor_set_location(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
/**
* Get the access mask for a binding.
*
* Is used to build the correct resource accesses in the render graph (dispatch/draw nodes).
*
* Will return VK_ACCESS_NONE when binding isn't found or not compatible with the given bind
* type.
*/
const VkAccessFlags access_mask(const shader::ShaderCreateInfo::Resource::BindType &bind_type,
int binding) const;
/** Get the Layout of the shader. */
const VKPushConstants::Layout &push_constants_layout_get() const
{
@ -85,10 +97,12 @@ class VKShaderInterface : public ShaderInterface {
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;
void descriptor_set_location_update(
const ShaderInput *shader_input,
const VKDescriptorSet::Location location,
const shader::ShaderCreateInfo::Resource::BindType bind_type);
const shader::ShaderCreateInfo::Resource::BindType bind_type,
std::optional<const shader::ShaderCreateInfo::Resource> resource);
};
} // namespace blender::gpu

View File

@ -29,15 +29,19 @@ void VKStateManager::apply_state()
}
}
void VKStateManager::apply_bindings()
void VKStateManager::apply_bindings(VKContext &context,
render_graph::VKResourceAccessInfo &resource_access_info)
{
VKContext &context = *VKContext::get();
if (context.shader) {
textures_.apply_bindings();
images_.apply_bindings();
uniform_buffers_.apply_bindings();
storage_buffers_.apply_bindings();
VKShader *shader = unwrap(context.shader);
if (shader == nullptr) {
return;
}
AddToDescriptorSetContext data(
context.descriptor_set_get(), shader->interface_get(), resource_access_info);
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()

View File

@ -12,6 +12,7 @@
#include "BLI_array.hh"
#include "render_graph/vk_resource_access_info.hh"
#include "vk_bindable_resource.hh"
namespace blender::gpu {
@ -20,6 +21,7 @@ class VKUniformBuffer;
class VKVertexBuffer;
class VKStorageBuffer;
class VKIndexBuffer;
class VKContext;
class VKStateManager : public StateManager {
@ -37,7 +39,8 @@ class VKStateManager : public StateManager {
void issue_barrier(eGPUBarrier barrier_bits) override;
/** Apply resources to the bindings of the active shader. */
void apply_bindings();
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;

View File

@ -53,19 +53,20 @@ void VKStorageBuffer::bind(int slot)
context.state_manager_get().storage_buffer_bind(*this, slot);
}
void VKStorageBuffer::bind(int slot,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
void VKStorageBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
{
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
ensure_allocated();
const VKShaderInterface &shader_interface = shader->interface_get();
const std::optional<VKDescriptorSet::Location> location =
shader_interface.descriptor_set_location(bind_type, slot);
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
VKDescriptorSetTracker &descriptor_set = context.descriptor_set_get();
descriptor_set.bind(*this, *location);
data.descriptor_set.bind(*this, *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);
}
}

View File

@ -28,9 +28,10 @@ class VKStorageBuffer : public StorageBuf, public VKBindableResource {
void update(const void *data) override;
void bind(int slot) override;
void bind(int slot,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) 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

@ -625,25 +625,26 @@ bool VKTexture::allocate()
return result == VK_SUCCESS;
}
void VKTexture::bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state)
void VKTexture::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state)
{
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();
const std::optional<VKDescriptorSet::Location> location =
shader_interface.descriptor_set_location(bind_type, binding);
data.shader_interface.descriptor_set_location(bind_type, binding);
if (location) {
VKDescriptorSetTracker &descriptor_set = context.descriptor_set_get();
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::IMAGE) {
descriptor_set.image_bind(*this, *location);
data.descriptor_set.image_bind(*this, *location);
}
else {
VKDevice &device = VKBackend::get().device_get();
const VKSampler &sampler = device.samplers().get(sampler_state);
descriptor_set.bind(*this, *location, sampler);
data.descriptor_set.bind(*this, *location, sampler);
}
render_graph::VKImageAccess image_access = {};
image_access.vk_image = vk_image_handle();
image_access.vk_access_flags = data.shader_interface.access_mask(bind_type, *location);
data.resource_access_info.images.append(image_access);
}
}

View File

@ -84,9 +84,10 @@ 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,
const GPUSamplerState sampler_state) 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
{

View File

@ -56,9 +56,10 @@ void VKUniformBuffer::clear_to_zero()
buffer_.clear(context, 0);
}
void VKUniformBuffer::bind(int slot,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
void VKUniformBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int slot,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
{
if (!buffer_.is_allocated()) {
allocate();
@ -70,20 +71,19 @@ void VKUniformBuffer::bind(int slot,
MEM_SAFE_FREE(data_);
}
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();
const std::optional<VKDescriptorSet::Location> location =
shader_interface.descriptor_set_location(bind_type, slot);
data.shader_interface.descriptor_set_location(bind_type, slot);
if (location) {
VKDescriptorSetTracker &descriptor_set = context.descriptor_set_get();
/* TODO: move to descriptor set. */
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
descriptor_set.bind(*this, *location);
data.descriptor_set.bind(*this, *location);
}
else {
descriptor_set.bind_as_ssbo(*this, *location);
data.descriptor_set.bind_as_ssbo(*this, *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, *location);
data.resource_access_info.buffers.append(buffer_access);
}
}

View File

@ -44,9 +44,10 @@ class VKUniformBuffer : public UniformBuf, public VKBindableResource, NonCopyabl
}
/* Bindable resource */
void bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
void add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) override;
private:
void allocate();

View File

@ -37,15 +37,13 @@ void VKVertexBuffer::bind_as_texture(uint binding)
state_manager.texel_buffer_bind(*this, binding);
}
void VKVertexBuffer::bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
void VKVertexBuffer::add_to_descriptor_set(AddToDescriptorSetContext &data,
int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState /*sampler_state*/)
{
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get();
const std::optional<VKDescriptorSet::Location> location =
shader_interface.descriptor_set_location(bind_type, binding);
data.shader_interface.descriptor_set_location(bind_type, binding);
if (!location) {
return;
}
@ -70,13 +68,16 @@ void VKVertexBuffer::bind(int binding,
}
/* TODO: Check if we can move this check inside the descriptor set. */
VKDescriptorSetTracker &descriptor_set = context.descriptor_set_get();
if (bind_type == shader::ShaderCreateInfo::Resource::BindType::SAMPLER) {
descriptor_set.bind(*this, *location);
data.descriptor_set.bind(*this, *location);
}
else {
descriptor_set.bind_as_ssbo(*this, *location);
data.descriptor_set.bind_as_ssbo(*this, *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, *location);
data.resource_access_info.buffers.append(buffer_access);
}
void VKVertexBuffer::wrap_handle(uint64_t /*handle*/)

View File

@ -28,9 +28,10 @@ class VKVertexBuffer : public VertBuf, public VKBindableResource {
void bind_as_ssbo(uint binding) override;
void bind_as_texture(uint binding) override;
void bind(int binding,
shader::ShaderCreateInfo::Resource::BindType bind_type,
const GPUSamplerState sampler_state) 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;