WIP: Vulkan: Clearing Storage Buffers #105299

Closed
Jeroen Bakker wants to merge 73 commits from Jeroen-Bakker:gpu-storage-buffer-clear into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
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*/)