diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index f0c253dffb..014b4a318e 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -806,58 +806,6 @@ void BufferCache
::UpdateVertexBufferSlot(u32 index, const Binding& binding) {
template
void BufferCache::BindHostVertexBuffers() {
- bool needs_vertex_input_refresh = false;
- bool use_vertex_input_dynamic_state = false;
- bool use_dynamic_stride = false;
- u32 max_dynamic_stride = 0;
- std::array min_dynamic_stride{};
- if constexpr (!IS_OPENGL) {
- use_vertex_input_dynamic_state = runtime.UsesVertexInputDynamicState();
- if (use_vertex_input_dynamic_state) {
- for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
- if (maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) {
- needs_vertex_input_refresh = true;
- break;
- }
- }
- }
-
- use_dynamic_stride = runtime.UsesDynamicVertexBindingStride();
- if (use_dynamic_stride) {
- max_dynamic_stride = runtime.GetMaxVertexInputBindingStride();
- for (const auto& attribute : maxwell3d->regs.vertex_attrib_format) {
- if (attribute.constant != 0) {
- continue;
- }
- const u32 binding = attribute.buffer;
- if (binding >= NUM_VERTEX_BUFFERS) {
- continue;
- }
- const u32 extent = attribute.offset + attribute.SizeInBytes();
- min_dynamic_stride[binding] = (std::max)(min_dynamic_stride[binding], extent);
- }
- }
- }
-
- const auto sanitize_stride = [&](u32 binding, u32 stride) -> u32 {
- if constexpr (IS_OPENGL) {
- return stride;
- } else {
- if (!use_dynamic_stride || stride == 0) {
- return stride;
- }
- const u32 min_stride = min_dynamic_stride[binding];
- const u32 required_stride = (std::max)(stride, min_stride);
- if (required_stride <= max_dynamic_stride) {
- return required_stride;
- }
- if (min_stride > max_dynamic_stride) {
- return 0;
- }
- return max_dynamic_stride;
- }
- };
-
#ifdef ANDROID
const bool use_optimized_vertex_buffers = Settings::values.use_optimized_vertex_buffers.GetValue();
#else
@@ -890,8 +838,7 @@ void BufferCache::BindHostVertexBuffers() {
continue;
}
flags[Dirty::VertexBuffer0 + index] = false;
- const u32 stride =
- sanitize_stride(index, maxwell3d->regs.vertex_streams[index].stride);
+ const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
if (!bindings.buffers.empty() && index != last_index + 1) {
@@ -934,8 +881,7 @@ void BufferCache
::BindHostVertexBuffers() {
const Binding& binding = channel_state->vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
- const u32 stride =
- sanitize_stride(index, maxwell3d->regs.vertex_streams[index].stride);
+ const u32 stride = maxwell3d->regs.vertex_streams[index].stride;
const u32 offset = buffer.Offset(binding.device_addr);
buffer.MarkUsage(offset, binding.size);
@@ -947,12 +893,6 @@ void BufferCache
::BindHostVertexBuffers() {
runtime.BindVertexBuffers(host_bindings);
}
}
-
- if constexpr (!IS_OPENGL) {
- if (needs_vertex_input_refresh) {
- runtime.NotifyVertexInputBindingChange();
- }
- }
}
template
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 9f19f802f3..c8dd68e3dc 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -161,28 +161,6 @@ public:
return max_dynamic_storage_buffers;
}
- bool UsesDynamicVertexBindingStride() const {
- return device.IsExtExtendedDynamicStateSupported() && !use_vertex_input_dynamic_state;
- }
-
- bool UsesVertexInputDynamicState() const {
- return use_vertex_input_dynamic_state;
- }
-
- void NotifyVertexInputBindingChange() {
- needs_vertex_input_refresh = true;
- }
-
- bool ConsumeVertexInputBindingChange() {
- const bool refresh = needs_vertex_input_refresh;
- needs_vertex_input_refresh = false;
- return refresh;
- }
-
- u32 GetMaxVertexInputBindingStride() const {
- return device.GetMaxVertexInputBindingStride();
- }
-
private:
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
guest_descriptor_queue.AddBuffer(buffer, offset, size);
@@ -206,7 +184,6 @@ private:
QuadIndexedPass quad_index_pass;
bool use_vertex_input_dynamic_state = false;
- bool needs_vertex_input_refresh = false;
bool limit_dynamic_storage_buffers = false;
u32 max_dynamic_storage_buffers = (std::numeric_limits::max)();
};
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index e3e2737e3b..1a41e50a36 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -82,9 +82,6 @@ public:
const std::array& infos);
bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; }
- bool UsesVertexAttribute(size_t index) const noexcept {
- return index < Maxwell::NumVertexAttributes && stage_infos[0].loads.Generic(index);
- }
bool SupportsAlphaToCoverage() const noexcept {
return fragment_has_color0_output;
}
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 108e6d649e..ad431c9ac5 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1014,6 +1014,20 @@ void RasterizerVulkan::UpdateDynamicStates() {
// EDS3 Enables: LogicOpEnable, DepthClamp, LineStipple, ConservativeRaster
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
+ using namespace Tegra::Engines;
+ // AMD Workaround: LogicOp incompatible with float render targets
+ if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
+ device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
+ const auto has_float = std::any_of(
+ regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(),
+ [](const auto& attrib) {
+ return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float;
+ }
+ );
+ if (regs.logic_op.enable) {
+ regs.logic_op.enable = static_cast(!has_float);
+ }
+ }
UpdateLogicOpEnable(regs);
UpdateDepthClampEnable(regs);
UpdateLineRasterizationMode(regs);
@@ -1031,7 +1045,7 @@ void RasterizerVulkan::UpdateDynamicStates() {
// Vertex Input Dynamic State: Independent from EDS levels
if (device.IsExtVertexInputDynamicStateSupported()) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
- UpdateVertexInput(regs, *gp);
+ UpdateVertexInput(regs);
}
}
}
@@ -1711,11 +1725,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs&
});
}
-void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs,
- const GraphicsPipeline& pipeline) {
+void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) {
auto& dirty{maxwell3d->dirty.flags};
- const bool force_vertex_input_refresh = buffer_cache_runtime.ConsumeVertexInputBindingChange();
- if (!dirty[Dirty::VertexInput] && !force_vertex_input_refresh) {
+ if (!dirty[Dirty::VertexInput]) {
return;
}
dirty[Dirty::VertexInput] = false;
@@ -1746,9 +1758,6 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs,
if (attribute.constant) {
continue;
}
- if (!pipeline.UsesVertexAttribute(index)) {
- continue;
- }
const size_t binding{attribute.buffer};
if (binding >= max_vertex_bindings) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index b3bf6c8b7c..b689c6b660 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -191,8 +191,7 @@ private:
void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs);
- void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs,
- const GraphicsPipeline& pipeline);
+ void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs);
Tegra::GPU& gpu;
Tegra::MaxwellDeviceMemoryManager& device_memory;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index f974505b2d..226619d8d6 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -153,17 +153,11 @@ void Scheduler::RequestOutsideRenderPassOperationContext() {
}
bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
- const auto consume_eds_refresh = [this] {
- if (!state.needs_state_enable_refresh) {
- return;
- }
- state_tracker.InvalidateExtendedDynamicStateFlags();
- state.needs_state_enable_refresh = false;
- };
-
if (state.graphics_pipeline == pipeline) {
- if (pipeline && pipeline->UsesExtendedDynamicState()) {
- consume_eds_refresh();
+ if (pipeline && pipeline->UsesExtendedDynamicState() &&
+ state.needs_state_enable_refresh) {
+ state_tracker.InvalidateStateEnableFlag();
+ state.needs_state_enable_refresh = false;
}
return false;
}
@@ -176,8 +170,9 @@ bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
if (!pipeline->UsesExtendedDynamicState()) {
state.needs_state_enable_refresh = true;
- } else {
- consume_eds_refresh();
+ } else if (state.needs_state_enable_refresh) {
+ state_tracker.InvalidateStateEnableFlag();
+ state.needs_state_enable_refresh = false;
}
return true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index f7c61ed7ef..74bae9e181 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -1,4 +1,4 @@
-// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
+// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -101,37 +101,6 @@ public:
(*flags)[Dirty::StateEnable] = true;
}
- void InvalidateExtendedDynamicStateFlags() {
- InvalidateStateEnableFlag();
-
- (*flags)[Dirty::CullMode] = true;
- (*flags)[Dirty::DepthCompareOp] = true;
- (*flags)[Dirty::FrontFace] = true;
- (*flags)[Dirty::StencilOp] = true;
- (*flags)[Dirty::DepthBoundsEnable] = true;
- (*flags)[Dirty::DepthTestEnable] = true;
- (*flags)[Dirty::DepthWriteEnable] = true;
- (*flags)[Dirty::StencilTestEnable] = true;
-
- (*flags)[Dirty::PrimitiveRestartEnable] = true;
- (*flags)[Dirty::RasterizerDiscardEnable] = true;
- (*flags)[Dirty::DepthBiasEnable] = true;
- (*flags)[Dirty::LogicOp] = true;
-
- (*flags)[Dirty::LogicOpEnable] = true;
- (*flags)[Dirty::DepthClampEnable] = true;
- (*flags)[Dirty::LineRasterizationMode] = true;
- (*flags)[Dirty::LineStippleEnable] = true;
- (*flags)[Dirty::ConservativeRasterizationMode] = true;
- (*flags)[Dirty::AlphaToCoverageEnable] = true;
- (*flags)[Dirty::AlphaToOneEnable] = true;
-
- (*flags)[Dirty::Blending] = true;
- (*flags)[Dirty::ColorMask] = true;
- (*flags)[Dirty::BlendEnable] = true;
- (*flags)[Dirty::BlendEquations] = true;
- }
-
bool TouchViewports() {
const bool dirty_viewports = Exchange(Dirty::Viewports, false);
const bool rescale_viewports = Exchange(VideoCommon::Dirty::RescaleViewports, false);
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 7777f945f4..8914ef0eb3 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -783,10 +783,6 @@ public:
return properties.properties.limits.maxVertexInputBindings;
}
- u32 GetMaxVertexInputBindingStride() const {
- return properties.properties.limits.maxVertexInputBindingStride;
- }
-
u32 GetMaxViewports() const {
return properties.properties.limits.maxViewports;
}