This commit finishes removing the uses of the integer to float vertex buffer fetch mode. Previous commits noted below already started that process. The last usage was geometry attributes. Now integers are converted to floats as part of the existing upload process. The change makes the Vulkan vertex buffer type conversion unused, so it's removed. That's nice because Vulkan vertex buffers go from 1040 to 568 bytes in size and have significantly less overhead on creation. Related: -153abc372e-1e1ac2bb9b-617858e453Pull Request: https://projects.blender.org/blender/blender/pulls/138873
337 lines
11 KiB
C++
337 lines
11 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "vk_vertex_attribute_object.hh"
|
|
|
|
#include "vk_batch.hh"
|
|
#include "vk_context.hh"
|
|
#include "vk_immediate.hh"
|
|
#include "vk_shader.hh"
|
|
#include "vk_shader_interface.hh"
|
|
#include "vk_vertex_buffer.hh"
|
|
|
|
#include "BLI_bit_vector.hh"
|
|
#include "BLI_math_vector_types.hh"
|
|
|
|
namespace blender::gpu {
|
|
VKVertexAttributeObject::VKVertexAttributeObject()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void VKVertexAttributeObject::clear()
|
|
{
|
|
is_valid = false;
|
|
info.pNext = nullptr;
|
|
bindings.clear();
|
|
attributes.clear();
|
|
vbos.clear();
|
|
buffers.clear();
|
|
}
|
|
|
|
VKVertexAttributeObject &VKVertexAttributeObject::operator=(const VKVertexAttributeObject &other)
|
|
{
|
|
if (this == &other) {
|
|
return *this;
|
|
}
|
|
|
|
is_valid = other.is_valid;
|
|
info = other.info;
|
|
bindings.clear();
|
|
bindings.extend(other.bindings);
|
|
attributes.clear();
|
|
attributes.extend(other.attributes);
|
|
vbos.clear();
|
|
vbos.extend(other.vbos);
|
|
buffers.clear();
|
|
buffers.extend(other.buffers);
|
|
return *this;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Bind resources
|
|
* \{ */
|
|
|
|
void VKVertexAttributeObject::bind(
|
|
render_graph::VKVertexBufferBindings &r_vertex_buffer_bindings) const
|
|
{
|
|
BitVector visited_bindings(bindings.size());
|
|
|
|
const VKBuffer &dummy = VKBackend::get().device.dummy_buffer;
|
|
for (VkVertexInputAttributeDescription attribute : attributes) {
|
|
if (visited_bindings[attribute.binding]) {
|
|
continue;
|
|
}
|
|
visited_bindings[attribute.binding].set(true);
|
|
|
|
VkBuffer buffer = dummy.vk_handle();
|
|
VkDeviceSize offset = 0;
|
|
|
|
if (attribute.binding < buffers.size()) {
|
|
buffer = buffers[attribute.binding].buffer;
|
|
offset = buffers[attribute.binding].offset;
|
|
}
|
|
|
|
r_vertex_buffer_bindings.buffer[attribute.binding] = buffer;
|
|
r_vertex_buffer_bindings.offset[attribute.binding] = offset;
|
|
r_vertex_buffer_bindings.buffer_count = max_ii(r_vertex_buffer_bindings.buffer_count,
|
|
attribute.binding + 1);
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Update bindings
|
|
* \{ */
|
|
|
|
void VKVertexAttributeObject::update_bindings(const VKContext &context, VKBatch &batch)
|
|
{
|
|
clear();
|
|
const VKShaderInterface &interface = unwrap(context.shader)->interface_get();
|
|
AttributeMask occupied_attributes = 0;
|
|
|
|
for (int v = 0; v < GPU_BATCH_INST_VBO_MAX_LEN; v++) {
|
|
VKVertexBuffer *vbo = batch.instance_buffer_get(v);
|
|
if (vbo) {
|
|
update_bindings(
|
|
vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, true);
|
|
}
|
|
}
|
|
for (int v = 0; v < GPU_BATCH_VBO_MAX_LEN; v++) {
|
|
VKVertexBuffer *vbo = batch.vertex_buffer_get(v);
|
|
if (vbo) {
|
|
update_bindings(
|
|
vbo->format, vbo, nullptr, vbo->vertex_len, interface, occupied_attributes, false);
|
|
}
|
|
}
|
|
|
|
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_t:
|
|
case shader::Type::float2_t:
|
|
case shader::Type::float3_t:
|
|
case shader::Type::float4_t:
|
|
case shader::Type::uint_t:
|
|
case shader::Type::uint2_t:
|
|
case shader::Type::uint3_t:
|
|
case shader::Type::uint4_t:
|
|
case shader::Type::int_t:
|
|
case shader::Type::int2_t:
|
|
case shader::Type::int3_t:
|
|
case shader::Type::int4_t:
|
|
case shader::Type::bool_t:
|
|
case shader::Type::float3_10_10_10_2_t:
|
|
case shader::Type::uchar_t:
|
|
case shader::Type::uchar2_t:
|
|
case shader::Type::uchar3_t:
|
|
case shader::Type::uchar4_t:
|
|
case shader::Type::char_t:
|
|
case shader::Type::char2_t:
|
|
case shader::Type::char3_t:
|
|
case shader::Type::char4_t:
|
|
case shader::Type::short_t:
|
|
case shader::Type::short2_t:
|
|
case shader::Type::short3_t:
|
|
case shader::Type::short4_t:
|
|
case shader::Type::ushort_t:
|
|
case shader::Type::ushort2_t:
|
|
case shader::Type::ushort3_t:
|
|
case shader::Type::ushort4_t:
|
|
return 1;
|
|
case shader::Type::float3x3_t:
|
|
return 3;
|
|
case shader::Type::float4x4_t:
|
|
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();
|
|
const VKShaderInterface &interface = unwrap(unwrap(immediate.shader))->interface_get();
|
|
AttributeMask occupied_attributes = 0;
|
|
|
|
VKBufferWithOffset immediate_buffer = immediate.active_buffer();
|
|
update_bindings(immediate.vertex_format,
|
|
nullptr,
|
|
&immediate_buffer,
|
|
immediate.vertex_len,
|
|
interface,
|
|
occupied_attributes,
|
|
false);
|
|
is_valid = true;
|
|
BLI_assert(interface.enabled_attr_mask_ == occupied_attributes);
|
|
}
|
|
|
|
void VKVertexAttributeObject::update_bindings(const GPUVertFormat &vertex_format,
|
|
VKVertexBuffer *vertex_buffer,
|
|
VKBufferWithOffset *immediate_vertex_buffer,
|
|
const int64_t vertex_len,
|
|
const VKShaderInterface &interface,
|
|
AttributeMask &r_occupied_attributes,
|
|
const bool use_instancing)
|
|
{
|
|
BLI_assert(vertex_buffer || immediate_vertex_buffer);
|
|
BLI_assert(!(vertex_buffer && immediate_vertex_buffer));
|
|
|
|
if (vertex_format.attr_len <= 0) {
|
|
return;
|
|
}
|
|
|
|
/* Interleaved offset is added to the buffer binding. Attribute offsets are hardware
|
|
* restricted (ref: VUID-VkVertexInputAttributeDescription-offset-00622). */
|
|
uint32_t buffer_offset = 0;
|
|
uint32_t attribute_offset = 0;
|
|
uint32_t stride = vertex_format.stride;
|
|
|
|
bool add_vbo = false;
|
|
|
|
for (uint32_t attribute_index = 0; attribute_index < vertex_format.attr_len; attribute_index++) {
|
|
const GPUVertAttr &attribute = vertex_format.attrs[attribute_index];
|
|
if (vertex_format.deinterleaved) {
|
|
buffer_offset += ((attribute_index == 0) ? 0 :
|
|
vertex_format.attrs[attribute_index - 1].size) *
|
|
vertex_len;
|
|
stride = attribute.size;
|
|
}
|
|
else {
|
|
attribute_offset = attribute.offset;
|
|
}
|
|
|
|
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);
|
|
if (shader_input == nullptr || shader_input->location == -1) {
|
|
continue;
|
|
}
|
|
|
|
/* Don't overwrite attributes that are already occupied. */
|
|
AttributeMask attribute_mask = 1 << shader_input->location;
|
|
if (r_occupied_attributes & attribute_mask) {
|
|
continue;
|
|
}
|
|
r_occupied_attributes |= attribute_mask;
|
|
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 = attribute_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);
|
|
|
|
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) {
|
|
add_vbo = true;
|
|
vertex_buffer->upload();
|
|
buffers.append({vertex_buffer->vk_handle(), buffer_offset});
|
|
}
|
|
if (immediate_vertex_buffer) {
|
|
buffers.append(*immediate_vertex_buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (add_vbo) {
|
|
BLI_assert(vertex_buffer != nullptr);
|
|
vbos.append(vertex_buffer);
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Debugging
|
|
* \{ */
|
|
|
|
void VKVertexAttributeObject::debug_print() const
|
|
{
|
|
std::cout << __FILE__ << "::" << __func__ << "\n";
|
|
BitVector visited_bindings(bindings.size());
|
|
|
|
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].set(true);
|
|
|
|
if (attribute.binding < vbos.size()) {
|
|
std::cout << " Attach to Buffer\n";
|
|
}
|
|
else {
|
|
std::cout << " WARNING: Attach to dummy\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::gpu
|