Vulkan: Push constants #104880

Merged
Jeroen Bakker merged 73 commits from Jeroen-Bakker/blender:vulkan-push-constants into main 2023-03-06 12:29:06 +01:00
11 changed files with 211 additions and 46 deletions
Showing only changes of commit 74c505c25d - Show all commits

View File

@ -26,6 +26,14 @@ namespace blender::gpu {
typedef struct ShaderInput {
uint32_t name_offset;
uint32_t name_hash;
/**
* Location is openGl legacy and its legacy usages should be phased out in Blender 3.7.
*
* Vulkan backend use location to encode the descriptor set binding. This binding is different
* than the binding stored in the binding attribute. In Vulkan the binding inside a descriptor
* set must be unique. In future the location will also be used to select the right descriptor
* set.
*/
int32_t location;
/** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attributes. */
int32_t binding;

View File

@ -30,7 +30,7 @@ void VKDescriptorSet::mark_freed()
vk_descriptor_pool_ = VK_NULL_HANDLE;
}
void VKDescriptorSet::bind(VKStorageBuffer &buffer, int location)
void VKDescriptorSet::bind(VKStorageBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
@ -38,7 +38,7 @@ void VKDescriptorSet::bind(VKStorageBuffer &buffer, int location)
binding.buffer_size = buffer.size_in_bytes();
}
void VKDescriptorSet::bind_as_ssbo(VKVertexBuffer &buffer, int location)
void VKDescriptorSet::bind_as_ssbo(VKVertexBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
@ -46,7 +46,7 @@ void VKDescriptorSet::bind_as_ssbo(VKVertexBuffer &buffer, int location)
binding.buffer_size = buffer.size_used_get();
}
void VKDescriptorSet::bind_as_ssbo(VKIndexBuffer &buffer, int location)
void VKDescriptorSet::bind_as_ssbo(VKIndexBuffer &buffer, const Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
@ -54,14 +54,14 @@ void VKDescriptorSet::bind_as_ssbo(VKIndexBuffer &buffer, int location)
binding.buffer_size = buffer.size_get();
}
void VKDescriptorSet::image_bind(VKTexture &texture, int location)
void VKDescriptorSet::image_bind(VKTexture &texture, const Location location)
{
Binding &binding = ensure_location(location);
binding.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
binding.vk_image_view = texture.vk_image_view_handle();
}
VKDescriptorSet::Binding &VKDescriptorSet::ensure_location(int location)
VKDescriptorSet::Binding &VKDescriptorSet::ensure_location(const Location location)
{
for (Binding &binding : bindings_) {
if (binding.location == location) {
@ -121,6 +121,8 @@ void VKDescriptorSet::update(VkDevice vk_device)
vkUpdateDescriptorSets(
vk_device, descriptor_writes.size(), descriptor_writes.data(), 0, nullptr);
bindings_.clear();
}
} // namespace blender::gpu

View File

@ -10,6 +10,8 @@
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "gpu_shader_private.hh"
#include "vk_common.hh"
namespace blender::gpu {
@ -18,9 +20,59 @@ class VKVertexBuffer;
class VKIndexBuffer;
class VKTexture;
/**
* In vulkan shader resources (images and buffers) are grouped in descriptor sets.
*
* 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
* to use 2 descriptor sets per shader. Only for each #blender::gpu::shader::Frequency.
*/
class VKDescriptorSet : NonCopyable {
struct Binding;
public:
/**
* Binding location of a resource in a descriptor set.
*
* Locations and bindings are used for different reasons. In the Vulkan backend we use
* ShaderInput.location to store the descriptor set + the resource binding inside the descriptor
* set. To ease the development the VKDescriptorSet::Location will be used to hide this
* confusion.
*
* NOTE: [future development] When supporting multiple descriptor sets the encoding/decoding can
* be centralized here. Location will then also contain the descriptor set index.
*/
struct Location {
private:
/**
* References to a binding in the descriptor set.
*/
uint32_t binding;
Location() = default;
public:
Location(const ShaderInput *shader_input) : binding(shader_input->location)
{
}
bool operator==(const Location &other) const
{
return binding == other.binding;
}
operator uint32_t() const
{
return binding;
}
friend struct Binding;
};
private:
struct Binding {
int location = -1;
Location location;
VkDescriptorType type;
VkBuffer vk_buffer = VK_NULL_HANDLE;
@ -28,6 +80,11 @@ class VKDescriptorSet : NonCopyable {
VkImageView vk_image_view = VK_NULL_HANDLE;
Binding()
{
location.binding = 0;
}
bool is_buffer() const
{
return ELEM(type, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
@ -42,6 +99,7 @@ class VKDescriptorSet : NonCopyable {
VkDescriptorPool vk_descriptor_pool_ = VK_NULL_HANDLE;
VkDescriptorSet vk_descriptor_set_ = VK_NULL_HANDLE;
/** A list of bindings that needs to be updated.*/
Vector<Binding> bindings_;
public:
@ -70,10 +128,10 @@ class VKDescriptorSet : NonCopyable {
return vk_descriptor_pool_;
}
void bind_as_ssbo(VKVertexBuffer &buffer, int location);
void bind_as_ssbo(VKIndexBuffer &buffer, int location);
void bind(VKStorageBuffer &buffer, int location);
void image_bind(VKTexture &texture, int location);
void bind_as_ssbo(VKVertexBuffer &buffer, Location location);
void bind_as_ssbo(VKIndexBuffer &buffer, Location location);
void bind(VKStorageBuffer &buffer, Location location);
void image_bind(VKTexture &texture, Location location);
/**
* Update the descriptor set on the device.
@ -83,7 +141,7 @@ class VKDescriptorSet : NonCopyable {
void mark_freed();
private:
Binding &ensure_location(int location);
Binding &ensure_location(Location location);
};
} // namespace blender::gpu

View File

@ -7,6 +7,7 @@
#include "vk_index_buffer.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
namespace blender::gpu {
@ -22,7 +23,10 @@ void VKIndexBuffer::bind_as_ssbo(uint binding)
}
VKShader *shader = static_cast<VKShader *>(context.shader);
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, binding);
const VKShaderInterface &shader_interface = shader->interface_get();
const ShaderInput *shader_input = shader_interface.shader_input_get(
shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, binding);
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, shader_input);
}
void VKIndexBuffer::read(uint32_t *data) const

View File

@ -325,9 +325,11 @@ static std::ostream &print_qualifier(std::ostream &os, const Qualifier &qualifie
return os;
}
static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &res)
static void print_resource(std::ostream &os,
const ShaderInput &shader_input,
const ShaderCreateInfo::Resource &res)
{
os << "layout(binding = " << res.slot;
os << "layout(binding = " << shader_input.location;
if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
os << ", " << to_string(res.image.format);
}
@ -373,6 +375,18 @@ static void print_resource(std::ostream &os, const ShaderCreateInfo::Resource &r
}
}
static void print_resource(std::ostream &os,
const VKShaderInterface &shader_interface,
const ShaderCreateInfo::Resource &res)
{
const ShaderInput *shader_input = shader_interface.shader_input_get(res);
if (shader_input == nullptr) {
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)
{
int64_t array_offset;
@ -655,8 +669,11 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
return false;
}
VKShaderInterface *vk_interface = new VKShaderInterface();
vk_interface->init(*info);
VkDevice vk_device = context_->device_get();
if (!finalize_descriptor_set_layouts(vk_device, *info)) {
if (!finalize_descriptor_set_layouts(vk_device, *vk_interface, *info)) {
return false;
}
if (!finalize_pipeline_layout(vk_device, *info)) {
@ -683,10 +700,11 @@ bool VKShader::finalize(const shader::ShaderCreateInfo *info)
}
if (result) {
VKShaderInterface *vk_interface = new VKShaderInterface();
vk_interface->init(*info);
interface = vk_interface;
}
else {
delete vk_interface;
}
return result;
}
@ -756,10 +774,10 @@ static VkDescriptorType descriptor_type(
}
static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
const shader::ShaderCreateInfo::Resource &resource)
const ShaderInput &shader_input, const shader::ShaderCreateInfo::Resource &resource)
{
VkDescriptorSetLayoutBinding binding = {};
binding.binding = resource.slot;
binding.binding = shader_input.location;
binding.descriptorType = descriptor_type(resource.bind_type);
binding.descriptorCount = 1;
binding.stageFlags = VK_SHADER_STAGE_ALL;
@ -769,19 +787,27 @@ static VkDescriptorSetLayoutBinding create_descriptor_set_layout_binding(
}
static void add_descriptor_set_layout_bindings(
const VKShaderInterface &interface,
const Vector<shader::ShaderCreateInfo::Resource> &resources,
Vector<VkDescriptorSetLayoutBinding> &r_bindings)
{
for (const shader::ShaderCreateInfo::Resource &resource : resources) {
r_bindings.append(create_descriptor_set_layout_binding(resource));
const ShaderInput *shader_input = interface.shader_input_get(resource);
if (shader_input == nullptr) {
BLI_assert_msg(shader_input, "Cannot find shader input for resource.");
continue;
}
r_bindings.append(create_descriptor_set_layout_binding(*shader_input, resource));
}
}
static VkDescriptorSetLayoutCreateInfo create_descriptor_set_layout(
const VKShaderInterface &interface,
const Vector<shader::ShaderCreateInfo::Resource> &resources,
Vector<VkDescriptorSetLayoutBinding> &r_bindings)
{
add_descriptor_set_layout_bindings(resources, r_bindings);
add_descriptor_set_layout_bindings(interface, resources, r_bindings);
VkDescriptorSetLayoutCreateInfo set_info = {};
set_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
set_info.flags = 0;
@ -792,6 +818,7 @@ static VkDescriptorSetLayoutCreateInfo create_descriptor_set_layout(
}
bool VKShader::finalize_descriptor_set_layouts(VkDevice vk_device,
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info)
{
if (info.pass_resources_.is_empty() && info.batch_resources_.is_empty()) {
@ -809,8 +836,8 @@ bool VKShader::finalize_descriptor_set_layouts(VkDevice vk_device,
all_resources.extend(info.batch_resources_);
Vector<VkDescriptorSetLayoutBinding> bindings;
VkDescriptorSetLayoutCreateInfo layout_info = create_descriptor_set_layout(all_resources,
bindings);
VkDescriptorSetLayoutCreateInfo layout_info = create_descriptor_set_layout(
shader_interface, all_resources, bindings);
if (vkCreateDescriptorSetLayout(vk_device, &layout_info, vk_allocation_callbacks, &layout_) !=
VK_SUCCESS) {
return false;
@ -869,11 +896,13 @@ void VKShader::uniform_int(int /*location*/,
std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) const
{
VKShaderInterface interface;
interface.init(info);
std::stringstream ss;
ss << "\n/* Pass Resources. */\n";
for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
print_resource(ss, res);
print_resource(ss, interface, res);
}
for (const ShaderCreateInfo::Resource &res : info.pass_resources_) {
print_resource_alias(ss, res);
@ -881,7 +910,7 @@ std::string VKShader::resources_declare(const shader::ShaderCreateInfo &info) co
ss << "\n/* Batch Resources. */\n";
for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
print_resource(ss, res);
print_resource(ss, interface, res);
}
for (const ShaderCreateInfo::Resource &res : info.batch_resources_) {
print_resource_alias(ss, res);
@ -1095,4 +1124,9 @@ VKPipeline &VKShader::pipeline_get()
return compute_pipeline_;
}
const VKShaderInterface &VKShader::interface_get() const
{
return *static_cast<const VKShaderInterface *>(interface);
}
} // namespace blender::gpu

View File

@ -15,6 +15,7 @@
#include "BLI_string_ref.hh"
namespace blender::gpu {
class VKShaderInterface;
class VKShader : public Shader {
private:
@ -65,13 +66,17 @@ class VKShader : public Shader {
return pipeline_layout_;
}
const VKShaderInterface &interface_get() const;
private:
Vector<uint32_t> compile_glsl_to_spirv(Span<const char *> sources, shaderc_shader_kind kind);
void build_shader_module(Span<uint32_t> spirv_module, VkShaderModule *r_shader_module);
void build_shader_module(MutableSpan<const char *> sources,
shaderc_shader_kind stage,
VkShaderModule *r_shader_module);
bool finalize_descriptor_set_layouts(VkDevice vk_device, const shader::ShaderCreateInfo &info);
bool finalize_descriptor_set_layouts(VkDevice vk_device,
const VKShaderInterface &shader_interface,
const shader::ShaderCreateInfo &info);
bool finalize_pipeline_layout(VkDevice vk_device, const shader::ShaderCreateInfo &info);
bool finalize_graphics_pipeline(VkDevice vk_device);

View File

@ -13,6 +13,7 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
{
using namespace blender::gpu::shader;
attr_len_ = 0;
uniform_len_ = 0;
ssbo_len_ = 0;
ubo_len_ = 0;
@ -46,11 +47,14 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
name_buffer_ = (char *)MEM_mallocN(info.interface_names_size_, "name_buffer");
uint32_t name_buffer_offset = 0;
int location = 0;
/* Uniform blocks */
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER) {
copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
input->location = input->binding = res.slot;
input->location = location++;
input->binding = res.slot;
enabled_ubo_mask_ |= (1 << input->binding);
input++;
}
@ -60,13 +64,15 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::SAMPLER) {
copy_input_name(input, res.sampler.name, name_buffer_, name_buffer_offset);
input->location = input->binding = res.slot;
input->location = location++;
input->binding = res.slot;
enabled_tex_mask_ |= (1 << input->binding);
input++;
}
if (res.bind_type == ShaderCreateInfo::Resource::BindType::IMAGE) {
copy_input_name(input, res.image.name, name_buffer_, name_buffer_offset);
input->location = input->binding = res.slot;
input->location = location++;
input->binding = res.slot;
enabled_ima_mask_ |= (1 << input->binding);
input++;
}
@ -76,7 +82,8 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
for (const ShaderCreateInfo::Resource &res : all_resources) {
if (res.bind_type == ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER) {
copy_input_name(input, res.storagebuf.name, name_buffer_, name_buffer_offset);
input->location = input->binding = res.slot;
input->location = location++;
input->binding = res.slot;
enabled_ssbo_mask_ |= (1 << input->binding);
input++;
}
@ -85,4 +92,26 @@ void VKShaderInterface::init(const shader::ShaderCreateInfo &info)
sort_inputs();
}
const ShaderInput *VKShaderInterface::shader_input_get(
const shader::ShaderCreateInfo::Resource &resource) const
{
return shader_input_get(resource.bind_type, resource.slot);
}
const ShaderInput *VKShaderInterface::shader_input_get(
const shader::ShaderCreateInfo::Resource::BindType &bind_type, int binding) const
{
switch (bind_type) {
case shader::ShaderCreateInfo::Resource::BindType::IMAGE:
return texture_get(binding);
case shader::ShaderCreateInfo::Resource::BindType::SAMPLER:
return texture_get(binding);
case shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER:
return ssbo_get(binding);
case shader::ShaderCreateInfo::Resource::BindType::UNIFORM_BUFFER:
return ubo_get(binding);
}
return nullptr;
}
} // namespace blender::gpu

View File

@ -16,5 +16,14 @@ class VKShaderInterface : public ShaderInterface {
VKShaderInterface() = default;
void init(const shader::ShaderCreateInfo &info);
/**
* Retrieve the shader input for the given resource.
*
* nullptr is returned when resource could not be found.
* Should only happen when still developing the Vulkan shader.
*/
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;
};
} // namespace blender::gpu

View File

@ -5,6 +5,7 @@
* \ingroup gpu
*/
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_vertex_buffer.hh"
#include "vk_storage_buffer.hh"
@ -32,7 +33,10 @@ void VKStorageBuffer::bind(int slot)
allocate(context);
}
VKShader *shader = static_cast<VKShader *>(context.shader);
shader->pipeline_get().descriptor_set_get().bind(*this, slot);
const VKShaderInterface &shader_interface = shader->interface_get();
const ShaderInput *shader_input = shader_interface.shader_input_get(
shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, slot);
shader->pipeline_get().descriptor_set_get().bind(*this, shader_input);
}
void VKStorageBuffer::unbind()

View File

@ -11,6 +11,9 @@
#include "vk_context.hh"
#include "vk_memory.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "BKE_global.h"
namespace blender::gpu {
@ -159,24 +162,27 @@ bool VKTexture::allocate()
VK_IMAGE_USAGE_STORAGE_BIT;
image_info.samples = VK_SAMPLE_COUNT_1_BIT;
VkResult result;
if (G.debug &= G_DEBUG_GPU) {
VkImageFormatProperties image_format = {};
result = vkGetPhysicalDeviceImageFormatProperties(context.physical_device_get(),
image_info.format,
image_info.imageType,
image_info.tiling,
image_info.usage,
image_info.flags,
&image_format);
if (result != VK_SUCCESS) {
printf("Image type not supported on device.\n");
return false;
}
}
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = static_cast<VmaAllocationCreateFlagBits>(
VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT);
allocCreateInfo.priority = 1.0f;
VkImageFormatProperties image_format = {};
VkResult result = vkGetPhysicalDeviceImageFormatProperties(context.physical_device_get(),
image_info.format,
image_info.imageType,
image_info.tiling,
image_info.usage,
image_info.flags,
&image_format);
if (result != VK_SUCCESS) {
return false;
}
result = vmaCreateImage(context.mem_allocator_get(),
&image_info,
&allocCreateInfo,
@ -214,13 +220,15 @@ bool VKTexture::allocate()
return result == VK_SUCCESS;
}
void VKTexture::image_bind(int location)
void VKTexture::image_bind(int binding)
{
if (!is_allocated()) {
allocate();
}
VKContext &context = *VKContext::get();
VKShader *shader = static_cast<VKShader *>(context.shader);
VKDescriptorSet::Location location(shader->interface_get().shader_input_get(
shader::ShaderCreateInfo::Resource::BindType::IMAGE, binding));
shader->pipeline_get().descriptor_set_get().image_bind(*this, location);
}

View File

@ -8,6 +8,7 @@
#include "MEM_guardedalloc.h"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_vertex_buffer.hh"
namespace blender::gpu {
@ -25,7 +26,10 @@ void VKVertexBuffer::bind_as_ssbo(uint binding)
}
VKShader *shader = static_cast<VKShader *>(context.shader);
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, binding);
const VKShaderInterface &shader_interface = shader->interface_get();
const ShaderInput *shader_input = shader_interface.shader_input_get(
shader::ShaderCreateInfo::Resource::BindType::STORAGE_BUFFER, binding);
shader->pipeline_get().descriptor_set_get().bind_as_ssbo(*this, shader_input);
}
void VKVertexBuffer::bind_as_texture(uint /*binding*/)