[vulkan] Adjusted VK_EXT_vertex_input_dynamic_state

This commit is contained in:
CamilleLaVey 2026-04-09 00:04:51 -04:00
parent 415a62c37b
commit 3ca8bdce0d
4 changed files with 58 additions and 51 deletions

View file

@ -10,6 +10,7 @@
#include <span>
#include <vector>
#include "common/settings.h"
#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/renderer_vulkan/vk_buffer_cache.h"
@ -334,6 +335,9 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_, MemoryAllocator& m
staging_pool{staging_pool_}, guest_descriptor_queue{guest_descriptor_queue_},
quad_index_pass(device, scheduler, descriptor_pool, staging_pool,
compute_pass_descriptor_queue) {
use_vertex_input_dynamic_state = device.IsExtVertexInputDynamicStateSupported() &&
Settings::values.vertex_input_dynamic_state.GetValue();
const VkDriverIdKHR driver_id = device.GetDriverID();
limit_dynamic_storage_buffers = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY;
@ -556,7 +560,10 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
if (index >= device.GetMaxVertexInputBindings()) {
return;
}
if (device.IsExtExtendedDynamicStateSupported()) {
const bool use_bind_vertex_buffers2_stride =
device.IsExtExtendedDynamicStateSupported() && !use_vertex_input_dynamic_state;
if (use_bind_vertex_buffers2_stride) {
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
@ -596,7 +603,10 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
if (binding_count == 0) {
return;
}
if (device.IsExtExtendedDynamicStateSupported()) {
const bool use_bind_vertex_buffers2_stride =
device.IsExtExtendedDynamicStateSupported() && !use_vertex_input_dynamic_state;
if (use_bind_vertex_buffers2_stride) {
scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data());
});

View file

@ -183,6 +183,7 @@ private:
std::unique_ptr<Uint8Pass> uint8_pass;
QuadIndexedPass quad_index_pass;
bool use_vertex_input_dynamic_state = false;
bool limit_dynamic_storage_buffers = false;
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
};

View file

@ -1732,39 +1732,54 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
}
dirty[Dirty::VertexInput] = false;
const size_t max_vertex_attributes =
(std::min)(Maxwell::NumVertexAttributes,
static_cast<size_t>(device.GetMaxVertexInputAttributes()));
const size_t max_vertex_bindings =
(std::min)(Maxwell::NumVertexArrays,
static_cast<size_t>(device.GetMaxVertexInputBindings()));
// Dynamic state path must respect device limits, same as static pipeline creation.
for (size_t index = max_vertex_attributes; index < Maxwell::NumVertexAttributes; ++index) {
dirty[Dirty::VertexAttribute0 + index] = false;
}
for (size_t index = max_vertex_bindings; index < Maxwell::NumVertexArrays; ++index) {
dirty[Dirty::VertexBinding0 + index] = false;
}
boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings;
boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes;
// There seems to be a bug on Nvidia's driver where updating only higher attributes ends up
// generating dirty state. Track the highest dirty attribute and update all attributes until
// that one.
size_t highest_dirty_attr{};
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
if (dirty[Dirty::VertexAttribute0 + index]) {
highest_dirty_attr = index;
}
}
for (size_t index = 0; index < highest_dirty_attr; ++index) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
const u32 binding{attribute.buffer};
std::array<bool, Maxwell::NumVertexArrays> used_bindings{};
for (size_t index = 0; index < max_vertex_attributes; ++index) {
dirty[Dirty::VertexAttribute0 + index] = false;
dirty[Dirty::VertexBinding0 + static_cast<size_t>(binding)] = true;
if (!attribute.constant) {
const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]};
if (attribute.constant) {
continue;
}
const size_t binding{attribute.buffer};
if (binding >= max_vertex_bindings) {
continue;
}
used_bindings[binding] = true;
attributes.push_back({
.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT,
.pNext = nullptr,
.location = static_cast<u32>(index),
.binding = binding,
.binding = static_cast<u32>(binding),
.format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size),
.offset = attribute.offset,
});
}
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
if (!dirty[Dirty::VertexBinding0 + index]) {
for (size_t index = 0; index < max_vertex_bindings; ++index) {
dirty[Dirty::VertexBinding0 + index] = false;
if (!used_bindings[index]) {
continue;
}
dirty[Dirty::VertexBinding0 + index] = false;
const u32 binding{static_cast<u32>(index)};
const auto& input_binding{regs.vertex_streams[binding]};
@ -1778,6 +1793,10 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs)
.divisor = is_instanced ? input_binding.frequency : 1,
});
}
ASSERT(attributes.size() <= max_vertex_attributes);
ASSERT(bindings.size() <= max_vertex_bindings);
scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) {
cmdbuf.SetVertexInputEXT(bindings, attributes);
});

View file

@ -1112,34 +1112,11 @@ bool Device::GetSuitability(bool requires_swapchain) {
// VK_DYNAMIC_STATE
// Driver detection variables for workarounds in GetSuitability
const VkDriverId driver_id = properties.driver.driverID;
// VK_EXT_extended_dynamic_state2 below this will appear drivers that need workarounds.
// VK_EXT_extended_dynamic_state3 below this will appear drivers that need workarounds.
// Samsung: Broken extendedDynamicState3ColorBlendEquation
// Disable blend equation dynamic state, force static pipeline state
if (extensions.extended_dynamic_state3 &&
(driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
LOG_WARNING(Render_Vulkan,
"Samsung: Disabling broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
}
// Intel Windows < 27.20.100.0: Broken VertexInputDynamicState
// Same for NVIDIA Proprietary < 580.119.02, unknown when VIDS was first NOT broken
// Disable VertexInputDynamicState on old Intel Windows drivers
if (extensions.vertex_input_dynamic_state) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if ((driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS && version < VK_MAKE_API_VERSION(27, 20, 100, 0))
|| (driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY && version < VK_MAKE_API_VERSION(580, 119, 02, 0))) {
LOG_WARNING(Render_Vulkan, "Disabling broken VK_EXT_vertex_input_dynamic_state");
RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
}
}
if (u32(Settings::values.dyna_state.GetValue()) == 0) {
LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features");
features.extended_dynamic_state.extendedDynamicState = false;