Vulkan: Refactor shader interface. #105073

Merged
Jeroen Bakker merged 1 commits from Jeroen-Bakker/blender:vulkan-shader-interface-descriptor-set into main 2023-02-22 14:42:14 +01:00
10 changed files with 97 additions and 47 deletions

View File

@ -225,7 +225,7 @@ static void test_gpu_shader_compute_ssbo()
EXPECT_NE(shader, nullptr); EXPECT_NE(shader, nullptr);
GPU_shader_bind(shader); GPU_shader_bind(shader);
/* Construct IBO. */ /* Construct SSBO. */
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex( GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
SIZE * sizeof(uint32_t), nullptr, GPU_USAGE_DEVICE_ONLY, __func__); SIZE * sizeof(uint32_t), nullptr, GPU_USAGE_DEVICE_ONLY, __func__);
GPU_storagebuf_bind(ssbo, GPU_shader_get_ssbo_binding(shader, "data_out")); GPU_storagebuf_bind(ssbo, GPU_shader_get_ssbo_binding(shader, "data_out"));

View File

@ -119,6 +119,10 @@ void VKDescriptorSet::update(VkDevice vk_device)
descriptor_writes.append(write_descriptor); descriptor_writes.append(write_descriptor);
} }
BLI_assert_msg(image_infos.size() + buffer_infos.size() == descriptor_writes.size(),
"Not all changes have been converted to a write descriptor. Check "
"`Binding::is_buffer` and `Binding::is_image`.");
vkUpdateDescriptorSets( vkUpdateDescriptorSets(
vk_device, descriptor_writes.size(), descriptor_writes.data(), 0, nullptr); vk_device, descriptor_writes.size(), descriptor_writes.data(), 0, nullptr);

View File

@ -15,10 +15,11 @@
#include "vk_common.hh" #include "vk_common.hh"
namespace blender::gpu { namespace blender::gpu {
class VKStorageBuffer;
class VKVertexBuffer;
class VKIndexBuffer; class VKIndexBuffer;
class VKShaderInterface;
class VKStorageBuffer;
class VKTexture; class VKTexture;
class VKVertexBuffer;
/** /**
* In vulkan shader resources (images and buffers) are grouped in descriptor sets. * In vulkan shader resources (images and buffers) are grouped in descriptor sets.
@ -26,7 +27,7 @@ class VKTexture;
* The resources inside a descriptor set can be updated and bound per set. * The resources inside a descriptor set can be updated and bound per set.
* *
* Currently Blender only supports a single descriptor set per shader, but it is planned to be able * Currently Blender only supports a single descriptor set per shader, but it is planned to be able
* to use 2 descriptor sets per shader. Only for each #blender::gpu::shader::Frequency. * to use 2 descriptor sets per shader. One for each #blender::gpu::shader::Frequency.
*/ */
class VKDescriptorSet : NonCopyable { class VKDescriptorSet : NonCopyable {
struct Binding; struct Binding;
@ -50,9 +51,12 @@ class VKDescriptorSet : NonCopyable {
*/ */
uint32_t binding; uint32_t binding;
Location() = default; Location(uint32_t binding) : binding(binding)
{
}
public: public:
Location() = default;
Location(const ShaderInput *shader_input) : binding(shader_input->location) Location(const ShaderInput *shader_input) : binding(shader_input->location)
{ {
} }
@ -68,6 +72,7 @@ class VKDescriptorSet : NonCopyable {
} }
friend struct Binding; friend struct Binding;
friend class VKShaderInterface;
}; };
private: private:

View File

@ -24,9 +24,9 @@ void VKIndexBuffer::bind_as_ssbo(uint binding)
VKShader *shader = static_cast<VKShader *>(context.shader); VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get(); const VKShaderInterface &shader_interface = shader->interface_get();
const ShaderInput *shader_input = shader_interface.shader_input_get( const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(
shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, binding); shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, binding);
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, shader_input); shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, location);
} }
void VKIndexBuffer::read(uint32_t *data) const void VKIndexBuffer::read(uint32_t *data) const

View File

@ -326,10 +326,10 @@ static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifie
} }
static void print_resource(std::ostream &os, static void print_resource(std::ostream &os,
const ShaderInput &shader_input, const VKDescriptorSet::Location location,
const ShaderCreateInfo::Resource &res) const ShaderCreateInfo::Resource &res)
{ {
os << "layout(binding = " << shader_input.location; os << "layout(binding = " << static_cast<uint32_t>(location);
if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) { if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
os << ", " << to_string(res.image.format); os << ", " << to_string(res.image.format);
} }
@ -379,12 +379,8 @@ static void print_resource(std::ostream &os,
const VKShaderInterface &shader_interface, const VKShaderInterface &shader_interface,
const ShaderCreateInfo::Resource &res) const ShaderCreateInfo::Resource &res)
{ {
const ShaderInput *shader_input = shader_interface.shader_input_get(res); const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(res);
if (shader_input == nullptr) { print_resource(os, location, res);
BLI_assert_msg(shader_input, "Cannot find shader input for resource");
return;
}
print_resource(os, *shader_input, res);
} }
static void print_resource_alias(std::ostream &os, const ShaderCreateInfo::Resource &res) static void print_resource_alias(std::ostream &os, const ShaderCreateInfo::Resource &res)
@ -860,10 +856,10 @@ static VkDescriptorType descriptor_type(const shader::ShaderCreateInfo::Resource
} }
static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding( static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
const ShaderInput &shader_input, const shader::ShaderCreateInfo::Resource &resource) const VKDescriptorSet::Location location, const shader::ShaderCreateInfo::Resource &resource)
{ {
VkDescriptorSetLayoutBinding binding = {}; VkDescriptorSetLayoutBinding binding = {};
binding.binding = shader_input.location; binding.binding = location;
binding.descriptorType = descriptor_type(resource); binding.descriptorType = descriptor_type(resource);
binding.descriptorCount = 1; binding.descriptorCount = 1;
binding.stageFlags = VK_SHADER_STAGE_ALL; binding.stageFlags = VK_SHADER_STAGE_ALL;
@ -878,13 +874,8 @@ static void add_descriptor_set_layout_bindings(
Vector<VkDescriptorSetLayoutBinding> &r_bindings) Vector<VkDescriptorSetLayoutBinding> &r_bindings)
{ {
for (const shader::ShaderCreateInfo::Resource &resource : resources) { for (const shader::ShaderCreateInfo::Resource &resource : resources) {
const ShaderInput *shader_input = interface.shader_input_get(resource); const VKDescriptorSet::Location location = interface.descriptor_set_location(resource);
if (shader_input == nullptr) { r_bindings.append(create_descriptor_set_layout_binding(location, resource));
BLI_assert_msg(shader_input, "Cannot find shader input for resource.");
continue;
}
r_bindings.append(create_descriptor_set_layout_binding(*shader_input, resource));
} }
} }
@ -1033,12 +1024,6 @@ std::string VKShader::vertex_interface_declare(const shader::ShaderCreateInfo &i
ss << "layout(location = " << attr.index << ") "; ss << "layout(location = " << attr.index << ") ";
ss << "in " << to_string(attr.type) << " " << attr.name << ";\n"; ss << "in " << to_string(attr.type) << " " << attr.name << ";\n";
} }
/* NOTE(D4490): Fix a bug where shader without any vertex attributes do not behave correctly.
*/
if (GPU_type_matches_ex(GPU_DEVICE_APPLE, GPU_OS_MAC, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL) &&
info.vertex_inputs_.is_empty()) {
ss << "in float gpu_dummy_workaround;\n";
}
ss << "\n/* Interfaces. */\n"; ss << "\n/* Interfaces. */\n";
int location = 0; int location = 0;
for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) { for (const StageInterfaceInfo *iface : info.vertex_out_interfaces_) {

View File

@ -41,7 +41,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
} }
} }
/* Make sure that the image slots don't overlap with the sampler slots.*/ /* Make sure that the image slots don't overlap with the sampler slots.*/
image_offset_ += 1; image_offset_++;
int32_t input_tot_len = ubo_len_ + uniform_len_ + ssbo_len_; int32_t input_tot_len = ubo_len_ + uniform_len_ + ssbo_len_;
inputs_ = static_cast<ShaderInput *>( inputs_ = static_cast<ShaderInput *>(
@ -51,14 +51,11 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer"); name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer");
uint32_t name_buffer_offset = 0; uint32_t name_buffer_offset = 0;
int location = 0;
/* Uniform blocks */ /* Uniform blocks */
for (const ShaderCreateInfo::Resource &res : all_resources) { for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) { if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset); copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
input->location = location++; input->location = input->binding = res.slot;
input->binding = res.slot;
input++; input++;
} }
} }
@ -67,14 +64,12 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
for (const ShaderCreateInfo::Resource &res : all_resources) { for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) { if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset); copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset);
input->location = location++; input->location = input->binding = res.slot;
input->binding = res.slot;
input++; input++;
} }
else if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) { else if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset); copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
input->location = location++; input->location = input->binding = res.slot + image_offset_;
input->binding = res.slot + image_offset_;
input++; input++;
} }
} }
@ -83,13 +78,57 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
for (const ShaderCreateInfo::Resource &res : all_resources) { for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) { if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset); copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
input->location = location++; input->location = input->binding = res.slot;
input->binding = res.slot;
input++; input++;
} }
} }
sort_inputs(); sort_inputs();
/* Determine the descriptor set locations after the inputs have been sorted.*/
descriptor_set_locations_ = Array<VKDescriptorSet::Location>(input_tot_len);
uint32_t descriptor_set_location = 0;
for (ShaderCreateInfo::Resource &res : all_resources) {
const ShaderInput *input = shader_input_get(res);
descriptor_set_location_update(input, descriptor_set_location++);
}
}
static int32_t shader_input_index(const ShaderInput *shader_inputs,
const ShaderInput *shader_input)
{
int32_t index = (shader_input - shader_inputs);
return index;
}
void VKShaderInterface::descriptor_set_location_update(const ShaderInput *shader_input,
const VKDescriptorSet::Location location)
{
int32_t index = shader_input_index(inputs_, shader_input);
descriptor_set_locations_[index] = location;
}
const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
const ShaderInput *shader_input) const
{
int32_t index = shader_input_index(inputs_, shader_input);
return descriptor_set_locations_[index];
}
const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
const shader::ShaderCreateInfo::Resource &resource) const
{
const ShaderInput *shader_input = shader_input_get(resource);
BLI_assert(shader_input);
return descriptor_set_location(shader_input);
}
const VKDescriptorSet::Location VKShaderInterface::descriptor_set_location(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
{
const ShaderInput *shader_input = shader_input_get(bind_type, binding);
BLI_assert(shader_input);
return descriptor_set_location(shader_input);
} }
const ShaderInput *VKShaderInterface::shader_input_get( const ShaderInput *VKShaderInterface::shader_input_get(

View File

@ -7,9 +7,13 @@
#pragma once #pragma once
#include "BLI_array.hh"
#include "gpu_shader_create_info.hh" #include "gpu_shader_create_info.hh"
#include "gpu_shader_interface.hh" #include "gpu_shader_interface.hh"
#include "vk_descriptor_set.hh"
namespace blender::gpu { namespace blender::gpu {
class VKShaderInterface : public ShaderInterface { class VKShaderInterface : public ShaderInterface {
private: private:
@ -21,11 +25,19 @@ class VKShaderInterface : public ShaderInterface {
* overlapping. * overlapping.
*/ */
uint32_t image_offset_ = 0; uint32_t image_offset_ = 0;
Array<VKDescriptorSet::Location> descriptor_set_locations_;
public: public:
VKShaderInterface() = default; VKShaderInterface() = default;
void init(const shader::ShaderCreateInfo &info); void init(const shader::ShaderCreateInfo &info);
const VKDescriptorSet::Location descriptor_set_location(
const shader::ShaderCreateInfo::Resource &resource) const;
const VKDescriptorSet::Location descriptor_set_location(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
private:
/** /**
* Retrieve the shader input for the given resource. * Retrieve the shader input for the given resource.
* *
@ -35,5 +47,9 @@ class VKShaderInterface : public ShaderInterface {
const ShaderInput *shader_input_get(const shader::ShaderCreateInfo::Resource &resource) const; const ShaderInput *shader_input_get(const shader::ShaderCreateInfo::Resource &resource) const;
const ShaderInput *shader_input_get( const ShaderInput *shader_input_get(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const; const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const;
const VKDescriptorSet::Location descriptor_set_location(const ShaderInput *shader_input) const;
void descriptor_set_location_update(const ShaderInput *shader_input,
const VKDescriptorSet::Location location);
}; };
} // namespace blender::gpu } // namespace blender::gpu

View File

@ -34,9 +34,9 @@ void VKStorageBuffer::bind(int slot)
} }
VKShader *shader = static_cast<VKShader *>(context.shader); VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get(); const VKShaderInterface &shader_interface = shader->interface_get();
const ShaderInput *shader_input = shader_interface.shader_input_get( const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(
shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, slot); shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, slot);
shader->pipeline_get().descriptor_set_get().bind(*this, shader_input); shader->pipeline_get().descriptor_set_get().bind(*this, location);
} }
void VKStorageBuffer::unbind() void VKStorageBuffer::unbind()

View File

@ -227,8 +227,9 @@ void VKTexture::image_bind(int binding)
} }
VKContext &context = *VKContext::get(); VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader); VKShader *shader = static_cast<VKShader *>(context.shader);
VKDescriptorSet::Location location(shader->interface_get().shader_input_get( const VKShaderInterface &shader_interface = shader->interface_get();
shader::ShaderCreateInfo::Resource::BindType::IMAGE, binding)); const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(
shader::ShaderCreateInfo::Resource::BindType::IMAGE, binding);
shader->pipeline_get().descriptor_set_get().image_bind(*this, location); shader->pipeline_get().descriptor_set_get().image_bind(*this, location);
} }

View File

@ -27,9 +27,9 @@ void VKVertexBuffer::bind_as_ssbo(uint binding)
VKShader *shader = static_cast<VKShader *>(context.shader); VKShader *shader = static_cast<VKShader *>(context.shader);
const VKShaderInterface &shader_interface = shader->interface_get(); const VKShaderInterface &shader_interface = shader->interface_get();
const ShaderInput *shader_input = shader_interface.shader_input_get( const VKDescriptorSet::Location location = shader_interface.descriptor_set_location(
shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, binding); shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, binding);
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, shader_input); shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, location);
} }
void VKVertexBuffer::bind_as_texture(uint /*binding*/) void VKVertexBuffer::bind_as_texture(uint /*binding*/)