Vulkan: Bind Dummy Attribute Resources #111350
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
* \{ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue