Immediate mode uses the old 'resource tracker' which has been replaced by swap chain resource pools. This PR optimizes immediate mode buffers by utilizing resource pools. Pull Request: https://projects.blender.org/blender/blender/pulls/128188
349 lines
11 KiB
C++
349 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) {
|
|
vbo->device_format_ensure();
|
|
update_bindings(vbo->device_format_get(),
|
|
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) {
|
|
vbo->device_format_ensure();
|
|
update_bindings(vbo->device_format_get(),
|
|
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:
|
|
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:
|
|
case shader::Type::SHORT:
|
|
case shader::Type::SHORT2:
|
|
case shader::Type::SHORT3:
|
|
case shader::Type::SHORT4:
|
|
case shader::Type::USHORT:
|
|
case shader::Type::USHORT2:
|
|
case shader::Type::USHORT3:
|
|
case shader::Type::USHORT4:
|
|
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();
|
|
const VKShaderInterface &interface = unwrap(unwrap(immediate.shader))->interface_get();
|
|
AttributeMask occupied_attributes = 0;
|
|
|
|
VKBufferWithOffset immediate_buffer = immediate.active_buffer();
|
|
update_bindings(immediate.vertex_format_converter.device_format_get(),
|
|
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
|