renderer_vulkan: Simplify vertex binding logic and properly handle null buffers. (#2104)

* renderer_vulkan: Simplify vertex binding logic and properly handle null buffers.

* renderer_vulkan: Remove need for empty bindVertexBuffers2EXT.
This commit is contained in:
squidbus
2025-01-10 00:52:12 -08:00
committed by GitHub
parent 4563b6379d
commit 562ed2a025
6 changed files with 137 additions and 138 deletions

View File

@@ -57,35 +57,11 @@ GraphicsPipeline::GraphicsPipeline(
pipeline_layout = std::move(layout);
SetObjectName(device, *pipeline_layout, "Graphics PipelineLayout {}", debug_str);
boost::container::static_vector<vk::VertexInputBindingDescription, 32> vertex_bindings;
boost::container::static_vector<vk::VertexInputAttributeDescription, 32> vertex_attributes;
if (fetch_shader && !instance.IsVertexInputDynamicState()) {
const auto& vs_info = GetStage(Shader::LogicalStage::Vertex);
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
// Skip attribute binding as the data will be pulled by shader
continue;
}
const auto buffer = attrib.GetSharp(vs_info);
if (buffer.GetSize() == 0) {
continue;
}
vertex_attributes.push_back({
.location = attrib.semantic,
.binding = attrib.semantic,
.format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()),
.offset = 0,
});
vertex_bindings.push_back({
.binding = attrib.semantic,
.stride = buffer.GetStride(),
.inputRate =
attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None
? vk::VertexInputRate::eVertex
: vk::VertexInputRate::eInstance,
});
}
VertexInputs<vk::VertexInputAttributeDescription> vertex_attributes;
VertexInputs<vk::VertexInputBindingDescription> vertex_bindings;
VertexInputs<AmdGpu::Buffer> guest_buffers;
if (!instance.IsVertexInputDynamicState()) {
GetVertexInputs(vertex_attributes, vertex_bindings, guest_buffers);
}
const vk::PipelineVertexInputStateCreateInfo vertex_input_info = {
@@ -161,7 +137,7 @@ GraphicsPipeline::GraphicsPipeline(
}
if (instance.IsVertexInputDynamicState()) {
dynamic_states.push_back(vk::DynamicState::eVertexInputEXT);
} else {
} else if (!vertex_bindings.empty()) {
dynamic_states.push_back(vk::DynamicState::eVertexInputBindingStrideEXT);
}
@@ -329,6 +305,51 @@ GraphicsPipeline::GraphicsPipeline(
GraphicsPipeline::~GraphicsPipeline() = default;
template <typename Attribute, typename Binding>
void GraphicsPipeline::GetVertexInputs(VertexInputs<Attribute>& attributes,
VertexInputs<Binding>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const {
if (!fetch_shader || fetch_shader->attributes.empty()) {
return;
}
const auto& vs_info = GetStage(Shader::LogicalStage::Vertex);
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
// Skip attribute binding as the data will be pulled by shader.
continue;
}
const auto& buffer = attrib.GetSharp(vs_info);
attributes.push_back(Attribute{
.location = attrib.semantic,
.binding = attrib.semantic,
.format = LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt()),
.offset = 0,
});
bindings.push_back(Binding{
.binding = attrib.semantic,
.stride = buffer.GetStride(),
.inputRate = attrib.GetStepRate() == Shader::Gcn::VertexAttribute::InstanceIdType::None
? vk::VertexInputRate::eVertex
: vk::VertexInputRate::eInstance,
});
if constexpr (std::is_same_v<Attribute, vk::VertexInputBindingDescription2EXT>) {
bindings.back().divisor = 1;
}
guest_buffers.emplace_back(buffer);
}
}
// Declare templated GetVertexInputs for necessary types.
template void GraphicsPipeline::GetVertexInputs(
VertexInputs<vk::VertexInputAttributeDescription>& attributes,
VertexInputs<vk::VertexInputBindingDescription>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const;
template void GraphicsPipeline::GetVertexInputs(
VertexInputs<vk::VertexInputAttributeDescription2EXT>& attributes,
VertexInputs<vk::VertexInputBindingDescription2EXT>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const;
void GraphicsPipeline::BuildDescSetLayout() {
boost::container::small_vector<vk::DescriptorSetLayoutBinding, 32> bindings;
u32 binding{};

View File

@@ -3,6 +3,7 @@
#pragma once
#include <boost/container/static_vector.hpp>
#include <xxhash.h>
#include "common/types.h"
@@ -27,6 +28,9 @@ class DescriptorHeap;
using Liverpool = AmdGpu::Liverpool;
template <typename T>
using VertexInputs = boost::container::static_vector<T, MaxVertexBufferCount>;
struct GraphicsPipelineKey {
std::array<size_t, MaxShaderStages> stage_hashes;
u32 num_color_attachments;
@@ -100,6 +104,11 @@ public:
key.prim_type == AmdGpu::PrimitiveType::QuadList;
}
/// Gets the attributes and bindings for vertex inputs.
template <typename Attribute, typename Binding>
void GetVertexInputs(VertexInputs<Attribute>& attributes, VertexInputs<Binding>& bindings,
VertexInputs<AmdGpu::Buffer>& guest_buffers) const;
private:
void BuildDescSetLayout();

View File

@@ -420,17 +420,17 @@ bool PipelineCache::RefreshGraphicsKey() {
}
}
const auto vs_info = infos[static_cast<u32>(Shader::LogicalStage::Vertex)];
const auto* vs_info = infos[static_cast<u32>(Shader::LogicalStage::Vertex)];
if (vs_info && fetch_shader && !instance.IsVertexInputDynamicState()) {
// Without vertex input dynamic state, the pipeline needs to specialize on format.
// Stride will still be handled outside the pipeline using dynamic state.
u32 vertex_binding = 0;
for (const auto& attrib : fetch_shader->attributes) {
if (attrib.UsesStepRates()) {
// Skip attribute binding as the data will be pulled by shader.
continue;
}
const auto& buffer = attrib.GetSharp(*vs_info);
if (buffer.GetSize() == 0) {
continue;
}
ASSERT(vertex_binding < MaxVertexBufferCount);
key.vertex_buffer_formats[vertex_binding++] =
Vulkan::LiverpoolToVK::SurfaceFormat(buffer.GetDataFmt(), buffer.GetNumberFmt());

View File

@@ -248,9 +248,7 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
return;
}
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
const auto& fetch_shader = pipeline->GetFetchShader();
buffer_cache.BindVertexBuffers(vs_info, fetch_shader);
buffer_cache.BindVertexBuffers(*pipeline);
if (is_indexed) {
buffer_cache.BindIndexBuffer(index_offset);
}
@@ -258,6 +256,8 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) {
BeginRendering(*pipeline, state);
UpdateDynamicState(*pipeline);
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
const auto& fetch_shader = pipeline->GetFetchShader();
const auto [vertex_offset, instance_offset] = GetDrawOffsets(regs, vs_info, fetch_shader);
const auto cmdbuf = scheduler.CommandBuffer();
@@ -292,9 +292,7 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3
return;
}
const auto& vs_info = pipeline->GetStage(Shader::LogicalStage::Vertex);
const auto& fetch_shader = pipeline->GetFetchShader();
buffer_cache.BindVertexBuffers(vs_info, fetch_shader);
buffer_cache.BindVertexBuffers(*pipeline);
if (is_indexed) {
buffer_cache.BindIndexBuffer(0);
}