Vulkan: Bind Dummy Attribute Resources #111350

Merged
Jeroen Bakker merged 6 commits from Jeroen-Bakker/blender:vulkan/bind-dummy-attributes into main 2023-08-22 13:35:08 +02:00
10 changed files with 288 additions and 31 deletions

View File

@ -135,6 +135,7 @@ Context *VKBackend::context_alloc(void *ghost_window, void *ghost_context)
VKContext *context = new VKContext(ghost_window, ghost_context);
device_.context_register(*context);
device_.init_dummy_buffer(*context);
return context;
}

View File

@ -71,7 +71,7 @@ class VKBuffer {
* VKIndexBuffer uses this when it is a subrange of another buffer.
*/
struct VKBufferWithOffset {
VKBuffer &buffer;
const VKBuffer &buffer;
VkDeviceSize offset;
};

View File

@ -583,6 +583,54 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size, GPUVertFe
return VK_FORMAT_R32_SFLOAT;
}
VkFormat to_vk_format(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
return VK_FORMAT_R32_SFLOAT;
case shader::Type::VEC2:
return VK_FORMAT_R32G32_SFLOAT;
case shader::Type::VEC3:
return VK_FORMAT_R32G32B32_SFLOAT;
case shader::Type::VEC4:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case shader::Type::UINT:
return VK_FORMAT_R32_UINT;
case shader::Type::UVEC2:
return VK_FORMAT_R32G32_UINT;
case shader::Type::UVEC3:
return VK_FORMAT_R32G32B32_UINT;
case shader::Type::UVEC4:
return VK_FORMAT_R32G32B32A32_UINT;
case shader::Type::INT:
return VK_FORMAT_R32_SINT;
case shader::Type::IVEC2:
return VK_FORMAT_R32G32_SINT;
case shader::Type::IVEC3:
return VK_FORMAT_R32G32B32_SINT;
case shader::Type::IVEC4:
return VK_FORMAT_R32G32B32A32_SINT;
case shader::Type::MAT4:
return VK_FORMAT_R32G32B32A32_SFLOAT;
case shader::Type::MAT3:
case shader::Type::BOOL:
case shader::Type::VEC3_101010I2:
case shader::Type::UCHAR:
case shader::Type::UCHAR2:
case shader::Type::UCHAR3:
case shader::Type::UCHAR4:
case shader::Type::CHAR:
case shader::Type::CHAR2:
case shader::Type::CHAR3:
case shader::Type::CHAR4:
break;
}
BLI_assert_unreachable();
return VK_FORMAT_R32G32B32A32_SFLOAT;
}
VkImageType to_vk_image_type(const eGPUTextureType type)
{
/* See

View File

@ -19,6 +19,7 @@
#include "vk_mem_alloc.h"
#include "gpu_index_buffer_private.hh"
#include "gpu_shader_create_info.hh"
#include "gpu_texture_private.hh"
namespace blender::gpu {
@ -42,6 +43,8 @@ VkFormat to_vk_format(const eGPUTextureFormat format);
VkFormat to_vk_format(const GPUVertCompType type,
const uint32_t size,
const GPUVertFetchMode fetch_mode);
VkFormat to_vk_format(const shader::Type type);
VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat format);
VkImageViewType to_vk_image_view_type(const eGPUTextureType type, eImageViewUsage view_type);
VkImageType to_vk_image_type(const eGPUTextureType type);

View File

@ -15,12 +15,15 @@
#include "vk_texture.hh"
#include "vk_vertex_buffer.hh"
#include "BLI_math_matrix_types.hh"
#include "GHOST_C-api.h"
namespace blender::gpu {
void VKDevice::deinit()
{
dummy_buffer_.free();
sampler_.free();
vmaDestroyAllocator(mem_allocator_);
mem_allocator_ = VK_NULL_HANDLE;
@ -90,6 +93,16 @@ void VKDevice::init_descriptor_pools()
descriptor_pools_.init(vk_device_);
}
void VKDevice::init_dummy_buffer(VKContext &context)
{
if (dummy_buffer_.is_allocated()) {
return;
}
dummy_buffer_.create(sizeof(float4x4), GPU_USAGE_DEVICE_ONLY, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
dummy_buffer_.clear(context, 0);
}
/* -------------------------------------------------------------------- */
/** \name Platform/driver/device information
* \{ */

View File

@ -11,6 +11,7 @@
#include "BLI_utility_mixins.hh"
#include "BLI_vector.hh"
#include "vk_buffer.hh"
#include "vk_common.hh"
#include "vk_debug.hh"
#include "vk_descriptor_pools.hh"
@ -65,6 +66,9 @@ class VKDevice : public NonCopyable {
/* Workarounds */
VKWorkarounds workarounds_;
/** Buffer to bind to unbound resource locations. */
VKBuffer dummy_buffer_;
public:
VkPhysicalDevice physical_device_get() const
{
@ -143,6 +147,11 @@ class VKDevice : public NonCopyable {
void context_unregister(VKContext &context);
const Vector<std::reference_wrapper<VKContext>> &contexts_get() const;
const VKBuffer &dummy_buffer_get() const
{
return dummy_buffer_;
}
/** \} */
private:
@ -150,6 +159,7 @@ class VKDevice : public NonCopyable {
void init_debug_callbacks();
void init_memory_allocator();
void init_descriptor_pools();
void init_dummy_buffer(VKContext &context);
/* During initialization the backend requires access to update the workarounds. */
friend VKBackend;

View File

@ -573,7 +573,10 @@ void VKShader::build_shader_module(Span<uint32_t> spirv_module, VkShaderModule *
const VKDevice &device = VKBackend::get().device_get();
VkResult result = vkCreateShaderModule(
device.device_get(), &create_info, vk_allocation_callbacks, r_shader_module);
if (result != VK_SUCCESS) {
if (result == VK_SUCCESS) {
debug::object_label(*r_shader_module, name);
}
else {
compilation_failed_ = true;
*r_shader_module = VK_NULL_HANDLE;
}

View File

@ -48,6 +48,11 @@ class VKShaderInterface : public ShaderInterface {
return push_constants_layout_;
}
shader::Type get_attribute_type(int location) const
{
return static_cast<shader::Type>(attr_types_[location]);
}
private:
/**
* Retrieve the shader input for the given resource.

View File

@ -12,6 +12,7 @@
#include "vk_vertex_buffer.hh"
#include "BLI_array.hh"
#include "BLI_math_vector_types.hh"
namespace blender::gpu {
VKVertexAttributeObject::VKVertexAttributeObject()
@ -48,8 +49,24 @@ VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttrib
return *this;
}
/* -------------------------------------------------------------------- */
/** \name Bind resources
* \{ */
void VKVertexAttributeObject::bind(VKContext &context)
{
const bool use_vbos = !vbos.is_empty();
if (use_vbos) {
bind_vbos(context);
}
else {
bind_buffers(context);
}
}
void VKVertexAttributeObject::bind_vbos(VKContext &context)
{
/* Bind VBOS from batches. */
Array<bool> visited_bindings(bindings.size());
visited_bindings.fill(false);
@ -59,22 +76,50 @@ void VKVertexAttributeObject::bind(VKContext &context)
}
visited_bindings[attribute.binding] = true;
/* Bind VBOS from batches. */
if (attribute.binding < vbos.size()) {
BLI_assert(vbos[attribute.binding]);
VKVertexBuffer &vbo = *vbos[attribute.binding];
vbo.upload();
context.command_buffer_get().bind(attribute.binding, vbo, 0);
}
else {
const VKBuffer &buffer = VKBackend::get().device_get().dummy_buffer_get();
const VKBufferWithOffset buffer_with_offset = {buffer, 0};
context.command_buffer_get().bind(attribute.binding, buffer_with_offset);
}
}
}
void VKVertexAttributeObject::bind_buffers(VKContext &context)
{
/* Bind dynamic buffers from immediate mode. */
Array<bool> visited_bindings(bindings.size());
visited_bindings.fill(false);
for (VkVertexInputAttributeDescription attribute : attributes) {
if (visited_bindings[attribute.binding]) {
continue;
}
visited_bindings[attribute.binding] = true;
/* Bind dynamic buffers from immediate mode. */
if (attribute.binding < buffers.size()) {
VKBufferWithOffset &buffer = buffers[attribute.binding];
context.command_buffer_get().bind(attribute.binding, buffer);
}
else {
const VKBuffer &buffer = VKBackend::get().device_get().dummy_buffer_get();
const VKBufferWithOffset buffer_with_offset = {buffer, 0};
context.command_buffer_get().bind(attribute.binding, buffer_with_offset);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Update bindings
* \{ */
void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch)
{
clear();
@ -96,9 +141,89 @@ void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch
}
}
if (occupied_attributes != interface.enabled_attr_mask_) {
fill_unused_bindings(interface, occupied_attributes);
}
is_valid = true;
}
/* Determine the number of binding location the given attribute uses. */
static uint32_t to_binding_location_len(const GPUVertAttr &attribute)
{
return ceil_division(attribute.comp_len, 4u);
}
/* Determine the number of binding location the given type uses. */
static uint32_t to_binding_location_len(const shader::Type type)
{
switch (type) {
case shader::Type::FLOAT:
case shader::Type::VEC2:
case shader::Type::VEC3:
case shader::Type::VEC4:
case shader::Type::UINT:
case shader::Type::UVEC2:
case shader::Type::UVEC3:
case shader::Type::UVEC4:
case shader::Type::INT:
case shader::Type::IVEC2:
case shader::Type::IVEC3:
case shader::Type::IVEC4:
case shader::Type::BOOL:
case shader::Type::VEC3_101010I2:
case shader::Type::UCHAR:
case shader::Type::UCHAR2:
case shader::Type::UCHAR3:
case shader::Type::UCHAR4:
case shader::Type::CHAR:
case shader::Type::CHAR2:
case shader::Type::CHAR3:
case shader::Type::CHAR4:
return 1;
case shader::Type::MAT3:
return 3;
case shader::Type::MAT4:
return 4;
}
return 1;
}
void VKVertexAttributeObject::fill_unused_bindings(const VKShaderInterface &interface,
const AttributeMask occupied_attributes)
{
for (int location : IndexRange(16)) {
AttributeMask location_mask = 1 << location;
/* Skip occupied slots */
if (occupied_attributes & location_mask) {
continue;
}
/* Skip slots that are not used by the vertex shader. */
if ((interface.enabled_attr_mask_ & location_mask) == 0) {
continue;
}
/* Use dummy binding...*/
shader::Type attribute_type = interface.get_attribute_type(location);
const uint32_t num_locations = to_binding_location_len(attribute_type);
for (const uint32_t location_offset : IndexRange(num_locations)) {
const uint32_t binding = bindings.size();
VkVertexInputAttributeDescription attribute_description = {};
attribute_description.binding = binding;
attribute_description.location = location + location_offset;
attribute_description.offset = 0;
attribute_description.format = to_vk_format(attribute_type);
attributes.append(attribute_description);
VkVertexInputBindingDescription vk_binding_descriptor = {};
vk_binding_descriptor.binding = binding;
vk_binding_descriptor.stride = 0;
vk_binding_descriptor.inputRate = VK_VERTEX_INPUT_RATE_INSTANCE;
bindings.append(vk_binding_descriptor);
}
}
}
void VKVertexAttributeObject::update_bindings(VKImmediate &immediate)
{
clear();
@ -148,9 +273,6 @@ void VKVertexAttributeObject::update_bindings(const GPUVertFormat &vertex_format
offset = attribute.offset;
}
const uint32_t binding = bindings.size();
bool attribute_used_by_shader = false;
for (uint32_t name_index = 0; name_index < attribute.name_len; name_index++) {
const char *name = GPU_vertformat_attr_name_get(&vertex_format, &attribute, name_index);
const ShaderInput *shader_input = interface.attr_get(name);
@ -164,34 +286,78 @@ void VKVertexAttributeObject::update_bindings(const GPUVertFormat &vertex_format
continue;
}
r_occupied_attributes |= attribute_mask;
attribute_used_by_shader = true;
const uint32_t num_locations = to_binding_location_len(attribute);
for (const uint32_t location_offset : IndexRange(num_locations)) {
const uint32_t binding = bindings.size();
VkVertexInputAttributeDescription attribute_description = {};
attribute_description.binding = binding;
attribute_description.location = shader_input->location + location_offset;
attribute_description.offset = offset + location_offset * sizeof(float4);
attribute_description.format = to_vk_format(
static_cast<GPUVertCompType>(attribute.comp_type),
attribute.size,
static_cast<GPUVertFetchMode>(attribute.fetch_mode));
attributes.append(attribute_description);
VkVertexInputAttributeDescription attribute_description = {};
attribute_description.binding = binding;
attribute_description.location = shader_input->location;
attribute_description.offset = offset;
attribute_description.format = to_vk_format(
static_cast<GPUVertCompType>(attribute.comp_type),
attribute.size,
static_cast<GPUVertFetchMode>(attribute.fetch_mode));
attributes.append(attribute_description);
}
if (attribute_used_by_shader) {
VkVertexInputBindingDescription vk_binding_descriptor = {};
vk_binding_descriptor.binding = binding;
vk_binding_descriptor.stride = stride;
vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE :
VK_VERTEX_INPUT_RATE_VERTEX;
bindings.append(vk_binding_descriptor);
if (vertex_buffer) {
vbos.append(vertex_buffer);
}
if (immediate_vertex_buffer) {
buffers.append(*immediate_vertex_buffer);
VkVertexInputBindingDescription vk_binding_descriptor = {};
vk_binding_descriptor.binding = binding;
vk_binding_descriptor.stride = stride;
vk_binding_descriptor.inputRate = use_instancing ? VK_VERTEX_INPUT_RATE_INSTANCE :
VK_VERTEX_INPUT_RATE_VERTEX;
bindings.append(vk_binding_descriptor);
if (vertex_buffer) {
vbos.append(vertex_buffer);
}
if (immediate_vertex_buffer) {
buffers.append(*immediate_vertex_buffer);
}
}
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Debugging
* \{ */
void VKVertexAttributeObject::debug_print() const
{
std::cout << __FILE__ << "::" << __func__ << "\n";
Array<bool> visited_bindings(bindings.size());
visited_bindings.fill(false);
for (VkVertexInputAttributeDescription attribute : attributes) {
std::cout << " - attribute(binding=" << attribute.binding
<< ", location=" << attribute.location << ")";
if (visited_bindings[attribute.binding]) {
std::cout << " WARNING: Already bound\n";
continue;
}
visited_bindings[attribute.binding] = true;
/* Bind VBOS from batches. */
if (!vbos.is_empty()) {
if (attribute.binding < vbos.size()) {
std::cout << " Attach to VBO [" << vbos[attribute.binding] << "]\n";
}
else {
std::cout << " WARNING: Attach to dummy\n";
}
}
else if (!buffers.is_empty()) {
if (attribute.binding < vbos.size()) {
std::cout << " Attach to ImmediateModeVBO\n";
}
else {
std::cout << " WARNING: Attach to dummy\n";
}
}
}
}
/** \} */
} // namespace blender::gpu

View File

@ -47,7 +47,12 @@ class VKVertexAttributeObject {
void update_bindings(const VKContext &context, VKBatch &batch);
void update_bindings(VKImmediate &immediate);
void debug_print() const;
private:
/** Update unused bindings with a dummy binding. */
void fill_unused_bindings(const VKShaderInterface &interface,
const AttributeMask occupied_attributes);
void update_bindings(const GPUVertFormat &vertex_format,
VKVertexBuffer *vertex_buffer,
VKBufferWithOffset *immediate_vertex_buffer,
@ -55,6 +60,9 @@ class VKVertexAttributeObject {
const VKShaderInterface &interface,
AttributeMask &r_occupied_attributes,
const bool use_instancing);
void bind_vbos(VKContext &context);
void bind_buffers(VKContext &context);
};
} // namespace blender::gpu