From 99d3f588f84937b74df1657fb941611b8de08c4b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 8 Apr 2026 20:17:57 -0400 Subject: [PATCH 01/31] [vulkan] Removal of VK_EXT_unified_image_layouts --- src/video_core/vulkan_common/vulkan_device.cpp | 4 ---- src/video_core/vulkan_common/vulkan_device.h | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 5075a79bcd..1cd94bcec3 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1162,10 +1162,6 @@ void Device::RemoveUnsuitableExtensions() { } RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); - // VK_KHR_unified_image_layouts - extensions.unified_image_layouts = features.unified_image_layouts.unifiedImageLayouts; - RemoveExtensionFeatureIfUnsuitable(extensions.unified_image_layouts, features.unified_image_layouts, - VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME); // VK_EXT_depth_bias_control extensions.depth_bias_control = diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index ad9d53ce16..3fa119c553 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -68,8 +68,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ pipeline_executable_properties) \ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ - workgroup_memory_explicit_layout) \ - FEATURE(KHR, UnifiedImageLayouts, UNIFIED_IMAGE_LAYOUTS, unified_image_layouts) + workgroup_memory_explicit_layout) // Define miscellaneous extensions which may be used by the implementation here. From 4753abd9f4d994c0662230b301ca8bc9cc52572c Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 8 Apr 2026 20:54:57 -0400 Subject: [PATCH 02/31] [vulkan] Adjusted VK_EXT_Custom_Border_Color implementation --- .../renderer_vulkan/vk_texture_cache.cpp | 17 ++++++----------- src/video_core/vulkan_common/vulkan_device.cpp | 15 +++------------ 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 48aa5ec476..2dbaab4127 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -2300,23 +2300,18 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { const auto& device = runtime.device; - // Check if custom border colors are supported - const bool has_custom_border_colors = runtime.device.IsCustomBorderColorsSupported(); - const bool has_format_undefined = runtime.device.IsCustomBorderColorWithoutFormatSupported(); + const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported(); + const bool has_format_undefined = + has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported(); + const bool has_custom_border_colors = + has_format_undefined && runtime.device.IsCustomBorderColorsSupported(); const auto color = tsc.BorderColor(); - // Determine border format based on available features: - // - If customBorderColorWithoutFormat is available: use VK_FORMAT_UNDEFINED (most flexible) - // - If only customBorderColors is available: use concrete format (R8G8B8A8_UNORM) - // - If neither is available: use standard border colors (handled by ConvertBorderColor) - const VkFormat border_format = has_format_undefined ? VK_FORMAT_UNDEFINED - : VK_FORMAT_R8G8B8A8_UNORM; - const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, .pNext = nullptr, .customBorderColor = std::bit_cast(color), - .format = border_format, + .format = VK_FORMAT_UNDEFINED, }; const void* pnext = nullptr; if (has_custom_border_colors) { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 1cd94bcec3..5a5a50c077 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1131,8 +1131,6 @@ bool Device::GetSuitability(bool requires_swapchain) { if (u32(Settings::values.dyna_state.GetValue()) == 0) { LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features"); - features.custom_border_color.customBorderColors = false; - features.custom_border_color.customBorderColorWithoutFormat = false; features.extended_dynamic_state.extendedDynamicState = false; features.extended_dynamic_state2.extendedDynamicState2 = false; features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; @@ -1148,17 +1146,10 @@ bool Device::GetSuitability(bool requires_swapchain) { void Device::RemoveUnsuitableExtensions() { // VK_EXT_custom_border_color - // Enable extension if driver supports it, then check individual features - // - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT - // - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED - // If only customBorderColors is available, we must provide a specific format if (extensions.custom_border_color) { - // Verify that at least customBorderColors is available - if (!features.custom_border_color.customBorderColors) { - LOG_WARNING(Render_Vulkan, - "VK_EXT_custom_border_color reported but customBorderColors feature not available, disabling"); - extensions.custom_border_color = false; - } + extensions.custom_border_color = + features.custom_border_color.customBorderColors && + features.custom_border_color.customBorderColorWithoutFormat; } RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); From baf51950e690327b2e5d5bd120532d0d9210bcb6 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 8 Apr 2026 21:53:29 -0400 Subject: [PATCH 03/31] [vulkan] Adjusted VK_EXT_robustness2 --- .../vulkan_common/vulkan_device.cpp | 24 +++++++++++++++---- src/video_core/vulkan_common/vulkan_device.h | 2 -- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 5a5a50c077..bbd7f14c81 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -919,6 +919,17 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { + loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME); + extensions.robustness_2 = true; + } else if (supported_extensions.contains(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) { + loaded_extensions.insert(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + extensions.robustness_2 = true; + } else { + extensions.robustness_2 = false; + } + #undef FEATURE_EXTENSION #undef EXTENSION @@ -1238,12 +1249,17 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); // VK_EXT_robustness2 - extensions.robustness_2 = features.robustness2.robustBufferAccess2 || - features.robustness2.robustImageAccess2 || - features.robustness2.nullDescriptor; + features.robustness2.robustBufferAccess2 = VK_FALSE; + features.robustness2.robustImageAccess2 = VK_FALSE; + extensions.robustness_2 = features.robustness2.nullDescriptor; + + const char* robustness2_extension_name = + loaded_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME) + ? VK_KHR_ROBUSTNESS_2_EXTENSION_NAME + : VK_EXT_ROBUSTNESS_2_EXTENSION_NAME; RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, - VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + robustness2_extension_name); // VK_EXT_image_robustness extensions.image_robustness = features.image_robustness.robustImageAccess; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 3fa119c553..f905a1fa52 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -178,8 +178,6 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \ FEATURE_NAME(provoking_vertex, provokingVertexLast) \ FEATURE_NAME(robustness2, nullDescriptor) \ - FEATURE_NAME(robustness2, robustBufferAccess2) \ - FEATURE_NAME(robustness2, robustImageAccess2) \ FEATURE_NAME(shader_float16_int8, shaderFloat16) \ FEATURE_NAME(shader_float16_int8, shaderInt8) \ FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ From 4394321aaaf43c2c11dc1433109ae77ac7a3ae88 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 8 Apr 2026 22:24:31 -0400 Subject: [PATCH 04/31] [vulkan] Adjusted RobustImageAccess --- .../vulkan_common/vulkan_device.cpp | 7 ++-- src/video_core/vulkan_common/vulkan_device.h | 35 ++----------------- 2 files changed, 6 insertions(+), 36 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index bbd7f14c81..8e5ca6ac6c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1261,9 +1261,10 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, robustness2_extension_name); - // VK_EXT_image_robustness - extensions.image_robustness = features.image_robustness.robustImageAccess; - RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, + // Image robustness + extensions.robust_image_access = features.robust_image_access.robustImageAccess; + RemoveExtensionFeatureIfUnsuitable(extensions.robust_image_access, + features.robust_image_access, VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); // VK_KHR_shader_atomic_int64 diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index f905a1fa52..8914ef0eb3 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -38,7 +38,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) #define FOR_EACH_VK_FEATURE_1_3(FEATURE) \ - FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \ + FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, robust_image_access) \ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ shader_demote_to_helper_invocation) \ FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \ @@ -123,7 +123,6 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ - EXTENSION_NAME(VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \ @@ -173,7 +172,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE_NAME(depth_bias_control, depthBiasExact) \ FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \ FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \ - FEATURE_NAME(image_robustness, robustImageAccess) \ + FEATURE_NAME(robust_image_access, robustImageAccess) \ FEATURE_NAME(index_type_uint8, indexTypeUint8) \ FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \ FEATURE_NAME(provoking_vertex, provokingVertexLast) \ @@ -549,36 +548,6 @@ public: return extensions.custom_border_color; } - /// Returns true if the device supports VK_EXT_image_robustness. - bool IsExtImageRobustnessSupported() const { - return extensions.image_robustness; - } - - /// Returns true if robustImageAccess is supported. - bool IsRobustImageAccessSupported() const { - return features.image_robustness.robustImageAccess; - } - - /// Returns true if the device supports VK_EXT_robustness2. - bool IsExtRobustness2Supported() const { - return extensions.robustness_2; - } - - /// Returns true if robustBufferAccess2 is supported. - bool IsRobustBufferAccess2Supported() const { - return features.robustness2.robustBufferAccess2; - } - - /// Returns true if robustImageAccess2 is supported. - bool IsRobustImageAccess2Supported() const { - return features.robustness2.robustImageAccess2; - } - - /// Returns true if nullDescriptor is supported. - bool IsNullDescriptorSupported() const { - return features.robustness2.nullDescriptor; - } - /// Returns true if customBorderColors feature is available. bool IsCustomBorderColorsSupported() const { return features.custom_border_color.customBorderColors; From f085a85fbdb56b662c59dff1da112f0b2951cfc7 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Apr 2026 00:04:51 -0400 Subject: [PATCH 05/31] [vulkan] Adjusted VK_EXT_vertex_input_dynamic_state --- .../renderer_vulkan/vk_buffer_cache.cpp | 14 +++- .../renderer_vulkan/vk_buffer_cache.h | 1 + .../renderer_vulkan/vk_rasterizer.cpp | 71 ++++++++++++------- .../vulkan_common/vulkan_device.cpp | 23 ------ 4 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 74f06427dd..dce6c55cd0 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -10,6 +10,7 @@ #include #include +#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& 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()); }); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index b73fcd162b..c8dd68e3dc 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -183,6 +183,7 @@ private: std::unique_ptr 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::max)(); }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ba7b5d8c1b..ad431c9ac5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -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(device.GetMaxVertexInputAttributes())); + const size_t max_vertex_bindings = + (std::min)(Maxwell::NumVertexArrays, + static_cast(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 bindings; boost::container::static_vector 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 used_bindings{}; + for (size_t index = 0; index < max_vertex_attributes; ++index) { dirty[Dirty::VertexAttribute0 + index] = false; - dirty[Dirty::VertexBinding0 + static_cast(binding)] = true; - if (!attribute.constant) { - attributes.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, - .pNext = nullptr, - .location = static_cast(index), - .binding = 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]) { + + 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(index), + .binding = static_cast(binding), + .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), + .offset = attribute.offset, + }); + } + + for (size_t index = 0; index < max_vertex_bindings; ++index) { dirty[Dirty::VertexBinding0 + index] = false; + if (!used_bindings[index]) { + continue; + } const u32 binding{static_cast(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); }); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8e5ca6ac6c..05ac56f011 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -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; From c68de909b8e36b66f5ac2aea41a5df2965be16a3 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Apr 2026 02:11:13 -0400 Subject: [PATCH 06/31] =?UTF-8?q?[vulkan]=20Adjustment=20ExtendedDynamicSt?= =?UTF-8?q?ate=20System=20N=C2=AA1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/video_core/buffer_cache/buffer_cache.h | 64 ++++++++++++++++++- .../renderer_vulkan/vk_buffer_cache.h | 25 +++++++- .../renderer_vulkan/vk_graphics_pipeline.h | 3 + .../renderer_vulkan/vk_rasterizer.cpp | 25 +++----- .../renderer_vulkan/vk_rasterizer.h | 3 +- .../renderer_vulkan/vk_scheduler.cpp | 19 ++++-- .../renderer_vulkan/vk_state_tracker.h | 33 +++++++++- src/video_core/vulkan_common/vulkan_device.h | 4 ++ 8 files changed, 147 insertions(+), 29 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 014b4a318e..f0c253dffb 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -806,6 +806,58 @@ 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 @@ -838,7 +890,8 @@ void BufferCache

::BindHostVertexBuffers() { continue; } flags[Dirty::VertexBuffer0 + index] = false; - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + const u32 stride = + sanitize_stride(index, 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) { @@ -881,7 +934,8 @@ void BufferCache

::BindHostVertexBuffers() { const Binding& binding = channel_state->vertex_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + const u32 stride = + sanitize_stride(index, maxwell3d->regs.vertex_streams[index].stride); const u32 offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, binding.size); @@ -893,6 +947,12 @@ 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 c8dd68e3dc..9f19f802f3 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 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -161,6 +161,28 @@ 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); @@ -184,6 +206,7 @@ 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 1a41e50a36..e3e2737e3b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -82,6 +82,9 @@ 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 ad431c9ac5..108e6d649e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1014,20 +1014,6 @@ 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); @@ -1045,7 +1031,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); + UpdateVertexInput(regs, *gp); } } } @@ -1725,9 +1711,11 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& }); } -void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { +void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline) { auto& dirty{maxwell3d->dirty.flags}; - if (!dirty[Dirty::VertexInput]) { + const bool force_vertex_input_refresh = buffer_cache_runtime.ConsumeVertexInputBindingChange(); + if (!dirty[Dirty::VertexInput] && !force_vertex_input_refresh) { return; } dirty[Dirty::VertexInput] = false; @@ -1758,6 +1746,9 @@ 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 b689c6b660..b3bf6c8b7c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -191,7 +191,8 @@ private: void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline); 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 226619d8d6..f974505b2d 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -153,11 +153,17 @@ 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() && - state.needs_state_enable_refresh) { - state_tracker.InvalidateStateEnableFlag(); - state.needs_state_enable_refresh = false; + if (pipeline && pipeline->UsesExtendedDynamicState()) { + consume_eds_refresh(); } return false; } @@ -170,9 +176,8 @@ bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) { if (!pipeline->UsesExtendedDynamicState()) { state.needs_state_enable_refresh = true; - } else if (state.needs_state_enable_refresh) { - state_tracker.InvalidateStateEnableFlag(); - state.needs_state_enable_refresh = false; + } else { + consume_eds_refresh(); } 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 74bae9e181..f7c61ed7ef 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 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -101,6 +101,37 @@ 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 8914ef0eb3..7777f945f4 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -783,6 +783,10 @@ public: return properties.properties.limits.maxVertexInputBindings; } + u32 GetMaxVertexInputBindingStride() const { + return properties.properties.limits.maxVertexInputBindingStride; + } + u32 GetMaxViewports() const { return properties.properties.limits.maxViewports; } From f1e4f69fc95937ac5970c217e9d65f5f1a1f348e Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Apr 2026 23:28:05 -0400 Subject: [PATCH 07/31] =?UTF-8?q?Revert=20"[vulkan]=20Adjustment=20Extende?= =?UTF-8?q?dDynamicState=20System=20N=C2=AA1"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/video_core/buffer_cache/buffer_cache.h | 64 +------------------ .../renderer_vulkan/vk_buffer_cache.h | 25 +------- .../renderer_vulkan/vk_graphics_pipeline.h | 3 - .../renderer_vulkan/vk_rasterizer.cpp | 25 +++++--- .../renderer_vulkan/vk_rasterizer.h | 3 +- .../renderer_vulkan/vk_scheduler.cpp | 19 ++---- .../renderer_vulkan/vk_state_tracker.h | 33 +--------- src/video_core/vulkan_common/vulkan_device.h | 4 -- 8 files changed, 29 insertions(+), 147 deletions(-) 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; } From a7cfd8b6a5f73052cae620deb03530fed71052bc Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Apr 2026 23:28:43 -0400 Subject: [PATCH 08/31] Revert "[vulkan] Adjusted VK_EXT_vertex_input_dynamic_state" --- .../renderer_vulkan/vk_buffer_cache.cpp | 14 +--- .../renderer_vulkan/vk_buffer_cache.h | 1 - .../renderer_vulkan/vk_rasterizer.cpp | 71 +++++++------------ .../vulkan_common/vulkan_device.cpp | 23 ++++++ 4 files changed, 51 insertions(+), 58 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index dce6c55cd0..74f06427dd 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -10,7 +10,6 @@ #include #include -#include "common/settings.h" #include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" @@ -335,9 +334,6 @@ 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; @@ -560,10 +556,7 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset if (index >= device.GetMaxVertexInputBindings()) { return; } - const bool use_bind_vertex_buffers2_stride = - device.IsExtExtendedDynamicStateSupported() && !use_vertex_input_dynamic_state; - - if (use_bind_vertex_buffers2_stride) { + if (device.IsExtExtendedDynamicStateSupported()) { 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; @@ -603,10 +596,7 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bi if (binding_count == 0) { return; } - const bool use_bind_vertex_buffers2_stride = - device.IsExtExtendedDynamicStateSupported() && !use_vertex_input_dynamic_state; - - if (use_bind_vertex_buffers2_stride) { + if (device.IsExtExtendedDynamicStateSupported()) { 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()); }); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index c8dd68e3dc..b73fcd162b 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -183,7 +183,6 @@ private: std::unique_ptr 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::max)(); }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ad431c9ac5..ba7b5d8c1b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1732,54 +1732,39 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) } dirty[Dirty::VertexInput] = false; - const size_t max_vertex_attributes = - (std::min)(Maxwell::NumVertexAttributes, - static_cast(device.GetMaxVertexInputAttributes())); - const size_t max_vertex_bindings = - (std::min)(Maxwell::NumVertexArrays, - static_cast(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 bindings; boost::container::static_vector attributes; - std::array used_bindings{}; - for (size_t index = 0; index < max_vertex_attributes; ++index) { - dirty[Dirty::VertexAttribute0 + index] = false; - - const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; - if (attribute.constant) { - continue; + // 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; } - - 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(index), - .binding = static_cast(binding), - .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), - .offset = attribute.offset, - }); } - - for (size_t index = 0; index < max_vertex_bindings; ++index) { - dirty[Dirty::VertexBinding0 + index] = false; - if (!used_bindings[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}; + dirty[Dirty::VertexAttribute0 + index] = false; + dirty[Dirty::VertexBinding0 + static_cast(binding)] = true; + if (!attribute.constant) { + attributes.push_back({ + .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, + .pNext = nullptr, + .location = static_cast(index), + .binding = 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]) { continue; } + dirty[Dirty::VertexBinding0 + index] = false; const u32 binding{static_cast(index)}; const auto& input_binding{regs.vertex_streams[binding]}; @@ -1793,10 +1778,6 @@ 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); }); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 05ac56f011..8e5ca6ac6c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1112,11 +1112,34 @@ 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; From 0a690baa3adfb583cce1a3d5b2c6d536282c28e3 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 10 Apr 2026 01:37:52 -0400 Subject: [PATCH 09/31] [vulkan] Restore gatting flush operation on removed gpu accuracy to new gpu accuracy logic --- src/video_core/gpu_thread.cpp | 13 +++++++++++-- src/video_core/renderer_opengl/gl_rasterizer.cpp | 3 +++ src/video_core/renderer_vulkan/vk_rasterizer.cpp | 3 +++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 8d8d857a02..63a2516399 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -92,7 +92,16 @@ void ThreadManager::InvalidateRegion(DAddr addr, u64 size) { } void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) { - // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important + if (Settings::IsGPULevelHigh()) { + if (!is_async) { + PushCommand(FlushRegionCommand(addr, size)); + } else { + auto& gpu = system.GPU(); + const u64 fence = gpu.RequestFlush(addr, size); + TickGPU(); + gpu.WaitForSyncOperation(fence); + } + } rasterizer->OnCacheInvalidation(addr, size); } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e268c4d2c6..26826a8f78 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -629,6 +629,9 @@ void RasterizerOpenGL::ReleaseFences(bool force) { void RasterizerOpenGL::FlushAndInvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType which) { + if (Settings::IsGPULevelHigh()) { + FlushRegion(addr, size, which); + } InvalidateRegion(addr, size, which); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ba7b5d8c1b..f1d208e711 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -766,6 +766,9 @@ void RasterizerVulkan::ReleaseFences(bool force) { void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType which) { + if (Settings::IsGPULevelHigh()) { + FlushRegion(addr, size, which); + } InvalidateRegion(addr, size, which); } From 7872ae9985dda601d5a3ff09000f0a12ccabe772 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 10 Apr 2026 20:28:02 -0400 Subject: [PATCH 10/31] [vulkan] Adjustment VK_EXT_conditional_rendering #1 --- .../resolve_conditional_render.comp | 21 ++++++++---- src/video_core/query_cache/query_cache.h | 1 + .../renderer_vulkan/vk_compute_pass.cpp | 13 ++++++-- .../renderer_vulkan/vk_query_cache.cpp | 32 +++++++++++++------ .../renderer_vulkan/vk_query_cache.h | 3 +- .../renderer_vulkan/vk_rasterizer.cpp | 2 ++ 6 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/video_core/host_shaders/resolve_conditional_render.comp b/src/video_core/host_shaders/resolve_conditional_render.comp index 307e77d1ad..3bc92f94fa 100644 --- a/src/video_core/host_shaders/resolve_conditional_render.comp +++ b/src/video_core/host_shaders/resolve_conditional_render.comp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -5,16 +8,22 @@ layout(local_size_x = 1) in; -layout(std430, binding = 0) buffer Query { - uvec2 initial; - uvec2 unknown; - uvec2 current; +layout(std430, binding = 0) readonly buffer Query { + uint data[]; }; -layout(std430, binding = 1) buffer Result { +layout(std430, binding = 1) writeonly buffer Result { uint result; }; +layout(push_constant) uniform PushConstants { + uint compare_to_zero; +}; + void main() { - result = all(equal(initial, current)) ? 1 : 0; + if (compare_to_zero != 0u) { + result = (data[0] != 0u && data[1] != 0u) ? 1u : 0u; + } else { + result = (data[0] == data[4] && data[1] == data[5]) ? 1u : 0u; + } } diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index 4ed42487aa..6bed91a53e 100644 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h @@ -412,6 +412,7 @@ bool QueryCacheBase::AccelerateHostConditionalRendering() { .found_query = nullptr, }; } + it_current = it_current_2; } auto* query = impl->ObtainQuery(it_current->second); qc_dirty |= True(query->flags & QueryFlagBits::IsHostManaged) && diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index d45a57f7bb..79f006305c 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -228,6 +228,10 @@ struct QueriesPrefixScanPushConstants { u32 accumulation_limit; u32 buffer_offset; }; + +struct ConditionalRenderingResolvePushConstants { + u32 compare_to_zero; +}; } // Anonymous namespace ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, @@ -413,7 +417,8 @@ ConditionalRenderingResolvePass::ConditionalRenderingResolvePass( const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, - INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, nullptr, + INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE, RESOLVE_CONDITIONAL_RENDER_COMP_SPV), scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} @@ -430,7 +435,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, descriptor_data](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, descriptor_data, compare_to_zero](vk::CommandBuffer cmdbuf) { static constexpr VkMemoryBarrier read_barrier{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, @@ -443,6 +448,9 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, }; + const ConditionalRenderingResolvePushConstants uniforms{ + .compare_to_zero = compare_to_zero ? 1U : 0U, + }; const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); @@ -450,6 +458,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 8518d89eee..29e5385c65 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -1202,16 +1202,24 @@ struct QueryCacheRuntimeImpl { hcr_setup.pNext = nullptr; hcr_setup.flags = 0; - conditional_resolve_pass = std::make_unique( - device, scheduler, descriptor_pool, compute_pass_descriptor_queue); + const bool has_conditional_rendering = device.IsExtConditionalRendering(); + if (has_conditional_rendering) { + conditional_resolve_pass = std::make_unique( + device, scheduler, descriptor_pool, compute_pass_descriptor_queue); + } + + VkBufferUsageFlags hcr_buffer_usage = + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (has_conditional_rendering) { + hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT; + } const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = sizeof(u32), - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT, + .usage = hcr_buffer_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1338,15 +1346,18 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo } } -void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal) { +void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal, + bool compare_to_zero) { VkBuffer to_resolve; u32 to_resolve_offset; + const u32 resolve_size = compare_to_zero ? 8 : 24; { std::scoped_lock lk(impl->buffer_cache.mutex); - static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::NoSynchronize; + const auto sync_info = compare_to_zero ? VideoCommon::ObtainBufferSynchronize::FullSynchronize + : VideoCommon::ObtainBufferSynchronize::NoSynchronize; const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto [buffer, offset] = - impl->buffer_cache.ObtainCPUBuffer(address, 24, sync_info, post_op); + impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op); to_resolve = buffer->Handle(); to_resolve_offset = static_cast(offset); } @@ -1355,7 +1366,7 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo PauseHostConditionalRendering(); } impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve, - to_resolve_offset, false); + to_resolve_offset, compare_to_zero); impl->hcr_setup.buffer = *impl->hcr_resolve_buffer; impl->hcr_setup.offset = 0; impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; @@ -1371,7 +1382,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup if (!impl->device.IsExtConditionalRendering()) { return false; } - HostConditionalRenderingCompareValueImpl(object_1, false); + HostConditionalRenderingCompareBCImpl(object_1.address, true, true); return true; } @@ -1421,6 +1432,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku const bool is_gpu_high = Settings::IsGPULevelHigh(); if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + EndHostConditionalRendering(); return true; } @@ -1437,10 +1449,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku } if (!is_gpu_high) { + EndHostConditionalRendering(); return true; } if (!is_in_bc[0] && !is_in_bc[1]) { + EndHostConditionalRendering(); return true; } HostConditionalRenderingCompareBCImpl(object_1.address, equal_check); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h index e2aa4d991e..bbb5234e11 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.h +++ b/src/video_core/renderer_vulkan/vk_query_cache.h @@ -63,7 +63,8 @@ public: private: void HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal); - void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal); + void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal, + bool compare_to_zero = false); friend struct QueryCacheRuntimeImpl; std::unique_ptr impl; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index f1d208e711..38dc23ff43 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -225,6 +225,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { UpdateDynamicStates(); + query_cache.NotifySegment(true); HandleTransformFeedback(); query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); @@ -336,6 +337,7 @@ void RasterizerVulkan::DrawTexture() { UpdateDynamicStates(); + query_cache.NotifySegment(true); query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); From 9e2013d7f87dbd740d23ceb5a5c52558aca8e05b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 10 Apr 2026 20:31:25 -0400 Subject: [PATCH 11/31] [test] Removed QCOM from presync workaround --- src/video_core/renderer_vulkan/vk_query_cache.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 29e5385c65..fa031bf4d0 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -220,8 +220,7 @@ public: } PauseCounter(); const auto driver_id = device.GetDriverID(); - if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || - driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { pending_sync.clear(); sync_values_stash.clear(); return; @@ -1431,7 +1430,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { EndHostConditionalRendering(); return true; } From da938123e1f8f5ae8e2bec60bc961a7071974ee7 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 11 Apr 2026 01:46:00 -0400 Subject: [PATCH 12/31] [vulkan] Adjustment VK_EXT_transform_feedback --- .../backend/spirv/spirv_emit_context.cpp | 3 + src/shader_recompiler/runtime_info.h | 1 + src/video_core/buffer_cache/buffer_cache.h | 33 +++---- src/video_core/macro.cpp | 4 +- src/video_core/query_cache/query_stream.h | 2 +- .../renderer_vulkan/vk_buffer_cache.cpp | 8 +- .../renderer_vulkan/vk_graphics_pipeline.cpp | 4 + .../renderer_vulkan/vk_query_cache.cpp | 87 +++++++++++++++++-- .../renderer_vulkan/vk_rasterizer.cpp | 14 ++- .../renderer_vulkan/vk_rasterizer.h | 1 + .../renderer_vulkan/vk_scheduler.cpp | 2 + src/video_core/renderer_vulkan/vk_scheduler.h | 5 ++ src/video_core/transform_feedback.cpp | 4 +- .../vulkan_common/vulkan_device.cpp | 3 +- src/video_core/vulkan_common/vulkan_device.h | 11 +++ 15 files changed, 144 insertions(+), 38 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b9a24496c9..169e83d9fd 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -178,6 +178,9 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional invo ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer); ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride); ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); + if (ctx.stage == Stage::Geometry && xfb_varying->stream != 0) { + ctx.Decorate(id, spv::Decoration::Stream, xfb_varying->stream); + } } if (num_components < 4 || element > 0) { const std::string_view subswizzle{swizzle.substr(element, num_components)}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index be10a9bb08..e6e1284762 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -76,6 +76,7 @@ enum class TessSpacing { struct TransformFeedbackVarying { u32 buffer{}; + u32 stream{}; u32 stride{}; u32 offset{}; u32 components{}; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 014b4a318e..c857e90e02 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1067,26 +1067,29 @@ void BufferCache

::BindHostTransformFeedbackBuffers() { HostBindings host_bindings; for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { const Binding& binding = channel_state->transform_feedback_buffers[index]; - if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 && - maxwell3d->regs.transform_feedback.controls[index].stride == 0) { - break; + const auto& control = maxwell3d->regs.transform_feedback.controls[index]; + const bool has_layout = control.varying_count != 0 || control.stride != 0; + + Buffer* host_buffer = &slot_buffers[NULL_BUFFER_ID]; + u32 offset = 0; + u32 size = 0; + + if (has_layout && binding.buffer_id != NULL_BUFFER_ID && binding.size != 0) { + Buffer& buffer = slot_buffers[binding.buffer_id]; + TouchBuffer(buffer, binding.buffer_id); + size = binding.size; + SynchronizeBuffer(buffer, binding.device_addr, size); + MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); + offset = buffer.Offset(binding.device_addr); + buffer.MarkUsage(offset, size); + host_buffer = &buffer; } - Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer, binding.buffer_id); - const u32 size = binding.size; - SynchronizeBuffer(buffer, binding.device_addr, size); - MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); - - const u32 offset = buffer.Offset(binding.device_addr); - buffer.MarkUsage(offset, size); - host_bindings.buffers.push_back(&buffer); + host_bindings.buffers.push_back(host_buffer); host_bindings.offsets.push_back(offset); host_bindings.sizes.push_back(size); } - if (host_bindings.buffers.size() > 0) { - runtime.BindTransformFeedbackBuffers(host_bindings); - } + runtime.BindTransformFeedbackBuffers(host_bindings); } template diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp index 66cea5afbd..2cda78c459 100644 --- a/src/video_core/macro.cpp +++ b/src/video_core/macro.cpp @@ -285,11 +285,11 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, } void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback(); - auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); - if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) { + if (!force) { Fallback(maxwell3d, parameters); return; } + auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); auto& params = maxwell3d.draw_manager->GetIndirectParams(); params.is_byte_count = true; params.is_indexed = false; diff --git a/src/video_core/query_cache/query_stream.h b/src/video_core/query_cache/query_stream.h index 1d11b12752..130c079eac 100644 --- a/src/video_core/query_cache/query_stream.h +++ b/src/video_core/query_cache/query_stream.h @@ -75,7 +75,7 @@ public: } u64 GetDependentMask() const { - return dependence_mask; + return dependent_mask; } u64 GetAmendValue() const { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 74f06427dd..a359502046 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -637,12 +637,10 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings< for (u32 i = 0; i < bindings.buffers.size(); ++i) { auto handle = bindings.buffers[i]->Handle(); if (handle == VK_NULL_HANDLE) { + ReserveNullBuffer(); + handle = *null_buffer; bindings.offsets[i] = 0; - bindings.sizes[i] = VK_WHOLE_SIZE; - if (!device.HasNullDescriptor()) { - ReserveNullBuffer(); - handle = *null_buffer; - } + bindings.sizes[i] = 0; } buffer_handles[i] = handle; } diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8f706a02c8..43fbefe425 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -467,6 +467,10 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { bind_stage_info(4); } + if (regs.transform_feedback_enabled != 0) { + scheduler.RequestOutsideRenderPassOperationContext(); + } + buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.BindHostGeometryBuffers(is_indexed); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index fa031bf4d0..281983168e 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -665,13 +665,17 @@ public: offsets.fill(0); last_queries.fill(0); last_queries_stride.fill(1); + VkBufferUsageFlags counter_buffer_usage = + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + if (device.IsExtTransformFeedbackSupported()) { + counter_buffer_usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT; + } const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = TFBQueryBank::QUERY_SIZE * NUM_STREAMS, - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT, + .usage = counter_buffer_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -691,6 +695,9 @@ public: ~TFBCounterStreamer() = default; void StartCounter() override { + if (!device.IsExtTransformFeedbackSupported()) { + return; + } FlushBeginTFB(); has_started = true; } @@ -705,7 +712,9 @@ public: void CloseCounter() override { if (has_flushed_end_pending) { - FlushEndTFB(); + if (scheduler.IsRenderPassActive()) { + FlushEndTFB(); + } } runtime.View3DRegs([this](Maxwell3D& maxwell3d) { if (maxwell3d.regs.transform_feedback_enabled == 0) { @@ -755,6 +764,10 @@ public: if (has_timestamp) { new_query->flags |= VideoCommon::QueryFlagBits::HasTimestamp; } + if (!device.IsExtTransformFeedbackSupported()) { + new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; + return index; + } if (!subreport_) { new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; return index; @@ -765,6 +778,8 @@ public: new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; return index; } + + scheduler.RequestOutsideRenderPassOperationContext(); CloseCounter(); auto [bank_slot, data_slot] = ProduceCounterBuffer(subreport); new_query->start_bank_id = static_cast(bank_slot); @@ -788,6 +803,10 @@ public: return out_topology; } + u32 GetPatchVertices() const { + return patch_vertices; + } + bool HasUnsyncedQueries() const override { return !pending_flush_queries.empty(); } @@ -854,6 +873,9 @@ public: private: void FlushBeginTFB() { + if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] { + return; + } if (has_flushed_end_pending) [[unlikely]] { return; } @@ -867,12 +889,24 @@ private: }); return; } + static constexpr VkMemoryBarrier COUNTER_RESUME_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT, + }; scheduler.Record([this, total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, 0, + COUNTER_RESUME_BARRIER); cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); }); } void FlushEndTFB() { + if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] { + return; + } if (!has_flushed_end_pending) [[unlikely]] { UNREACHABLE(); return; @@ -902,6 +936,21 @@ private: runtime.View3DRegs([this](Maxwell3D& maxwell3d) { buffers_count = 0; out_topology = maxwell3d.draw_manager->GetDrawState().topology; + patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U); + if (out_topology == Maxwell3D::Regs::PrimitiveTopology::Patches) { + switch (maxwell3d.regs.tessellation.params.output_primitives.Value()) { + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points: + out_topology = Maxwell3D::Regs::PrimitiveTopology::Points; + break; + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Lines: + out_topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip; + break; + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CW: + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CCW: + out_topology = Maxwell3D::Regs::PrimitiveTopology::TriangleStrip; + break; + } + } for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) { const auto& tf = maxwell3d.regs.transform_feedback; if (tf.buffers[i].enable == 0) { @@ -994,6 +1043,7 @@ private: std::array last_queries; std::array last_queries_stride; Maxwell3D::Regs::PrimitiveTopology out_topology; + u32 patch_vertices{1}; u64 streams_mask; }; @@ -1014,6 +1064,7 @@ public: u64 stride{}; DAddr dependant_address{}; Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points}; + u32 patch_vertices{1}; size_t dependant_index{}; bool dependant_manage{}; }; @@ -1030,6 +1081,10 @@ public: ~PrimitivesSucceededStreamer() = default; + void ResetCounter() override { + tfb_streamer.ResetCounter(); + } + size_t WriteCounter(DAddr address, bool has_timestamp, u32 value, std::optional subreport_) override { auto index = BuildQuery(); @@ -1047,6 +1102,7 @@ public: auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport); bool must_manage_dependance = false; new_query->topology = tfb_streamer.GetOutputTopology(); + new_query->patch_vertices = tfb_streamer.GetPatchVertices(); if (dependant_address_opt) { auto [dep_address, stride] = *dependant_address_opt; new_query->dependant_address = dep_address; @@ -1067,6 +1123,7 @@ public: } new_query->stride = 1; runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) { + new_query->patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U); for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) { const auto& tf = maxwell3d.regs.transform_feedback; if (tf.buffers[i].enable == 0) { @@ -1130,27 +1187,39 @@ public: } } query->value = [&]() -> u64 { + const auto saturating_subtract = [](u64 value, u64 amount) { + return value > amount ? value - amount : 0; + }; switch (query->topology) { case Maxwell3D::Regs::PrimitiveTopology::Points: return num_vertices; case Maxwell3D::Regs::PrimitiveTopology::Lines: return num_vertices / 2; case Maxwell3D::Regs::PrimitiveTopology::LineLoop: - return (num_vertices / 2) + 1; + return num_vertices > 1 ? num_vertices : 0; case Maxwell3D::Regs::PrimitiveTopology::LineStrip: - return num_vertices - 1; - case Maxwell3D::Regs::PrimitiveTopology::Patches: + return saturating_subtract(num_vertices, 1); + case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency: + return num_vertices / 4; + case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency: + return saturating_subtract(num_vertices, 3); case Maxwell3D::Regs::PrimitiveTopology::Triangles: - case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency: return num_vertices / 3; + case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency: + return num_vertices / 6; case Maxwell3D::Regs::PrimitiveTopology::TriangleFan: case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip: + return saturating_subtract(num_vertices, 2); case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency: - return num_vertices - 2; + return num_vertices > 4 ? (num_vertices - 4) / 2 : 0; case Maxwell3D::Regs::PrimitiveTopology::Quads: return num_vertices / 4; + case Maxwell3D::Regs::PrimitiveTopology::QuadStrip: + return num_vertices > 2 ? (num_vertices - 2) / 2 : 0; case Maxwell3D::Regs::PrimitiveTopology::Polygon: - return 1U; + return num_vertices >= 3 ? 1U : 0U; + case Maxwell3D::Regs::PrimitiveTopology::Patches: + return num_vertices / std::max(query->patch_vertices, 1U); default: return num_vertices; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 38dc23ff43..743af16634 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -577,11 +577,17 @@ void RasterizerVulkan::DispatchCompute() { } void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) { - if (type != VideoCommon::QueryType::ZPassPixelCount64) { + switch (type) { + case VideoCommon::QueryType::ZPassPixelCount64: + case VideoCommon::QueryType::StreamingByteCount: + case VideoCommon::QueryType::StreamingPrimitivesSucceeded: + case VideoCommon::QueryType::VtgPrimitivesOut: + query_cache.CounterReset(type); + return; + default: LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type); return; } - query_cache.CounterReset(type); } void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, @@ -835,6 +841,10 @@ bool RasterizerVulkan::AccelerateConditionalRendering() { return query_cache.AccelerateHostConditionalRendering(); } +bool RasterizerVulkan::HasDrawTransformFeedback() { + return device.IsTransformFeedbackDrawSupported(); +} + bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b689c6b660..06cbdd2679 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -122,6 +122,7 @@ public: void FlushCommands() override; void TickFrame() override; bool AccelerateConditionalRendering() override; + bool HasDrawTransformFeedback() override; bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 226619d8d6..fdaf9baacc 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -324,6 +324,8 @@ void Scheduler::EndRenderPass() return; } + query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); + // Log render pass end if (Settings::values.gpu_logging_enabled.GetValue() && Settings::values.gpu_log_vulkan_calls.GetValue()) { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 00a912f2cd..0709c3a370 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -63,6 +63,11 @@ public: /// of a renderpass. void RequestOutsideRenderPassOperationContext(); + /// Returns true when a render pass is currently active in the scheduler state. + bool IsRenderPassActive() const { + return state.renderpass != VK_NULL_HANDLE; + } + /// Update the pipeline to the current execution context. bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline); diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index a8f9da9853..53d29c08e2 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project @@ -88,13 +88,13 @@ std::pair, u32> MakeTransformF return 0; }; - UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream); Shader::TransformFeedbackVarying varying{ .buffer = static_cast(buffer), .stride = layout.stride, .offset = offset * 4, .components = 1, }; + varying.stream = layout.stream; const u32 base_offset = offset; const auto attribute{get_attribute(offset)}; if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8e5ca6ac6c..f48fe39e4e 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1292,8 +1292,7 @@ void Device::RemoveUnsuitableExtensions() { // VK_EXT_transform_feedback extensions.transform_feedback = features.transform_feedback.transformFeedback && - properties.transform_feedback.maxTransformFeedbackBuffers > 0 && - properties.transform_feedback.transformFeedbackQueries; + properties.transform_feedback.maxTransformFeedbackBuffers > 0; RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 8914ef0eb3..cf341726a7 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -538,6 +538,17 @@ public: return extensions.transform_feedback; } + /// Returns true if transform feedback draw commands are supported. + bool IsTransformFeedbackDrawSupported() const { + return extensions.transform_feedback && properties.transform_feedback.transformFeedbackDraw; + } + + /// Returns true if transform feedback query types are supported. + bool IsTransformFeedbackQueriesSupported() const { + return extensions.transform_feedback && + properties.transform_feedback.transformFeedbackQueries; + } + /// Returns true if the device supports VK_EXT_transform_feedback properly. bool AreTransformFeedbackGeometryStreamsSupported() const { return features.transform_feedback.geometryStreams; From de273aefa5748198803d7b836618440b50a84e16 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 11 Apr 2026 17:15:57 -0400 Subject: [PATCH 13/31] =?UTF-8?q?[vulkan]=20Adjustment=20VK=5FEXT=5Ftransf?= =?UTF-8?q?orm=5Ffeedback=20N=C2=BA=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../renderer_vulkan/vk_present_manager.cpp | 2 +- .../renderer_vulkan/vk_query_cache.cpp | 29 ++++++++++++++++--- .../renderer_vulkan/vk_texture_cache.cpp | 3 +- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 80853362ad..de854554c7 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -189,7 +189,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat frame->image = memory_allocator.CreateImage({ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, - .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, + .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT, .imageType = VK_IMAGE_TYPE_2D, .format = swapchain.GetImageFormat(), .extent = diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 281983168e..30ac002ef6 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -665,6 +665,7 @@ public: offsets.fill(0); last_queries.fill(0); last_queries_stride.fill(1); + stream_to_slot.fill(INVALID_SLOT); VkBufferUsageFlags counter_buffer_usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; if (device.IsExtTransformFeedbackSupported()) { @@ -773,15 +774,24 @@ public: return index; } const size_t subreport = static_cast(*subreport_); + if (subreport >= NUM_STREAMS) { + new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; + return index; + } last_queries[subreport] = address; if ((streams_mask & (1ULL << subreport)) == 0) { new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; return index; } + const size_t slot = stream_to_slot[subreport]; + if (slot >= NUM_STREAMS) { + new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; + return index; + } scheduler.RequestOutsideRenderPassOperationContext(); CloseCounter(); - auto [bank_slot, data_slot] = ProduceCounterBuffer(subreport); + auto [bank_slot, data_slot] = ProduceCounterBuffer(slot); new_query->start_bank_id = static_cast(bank_slot); new_query->size_banks = 1; new_query->start_slot = static_cast(data_slot); @@ -792,6 +802,9 @@ public: } std::optional> GetLastQueryStream(size_t stream) { + if (stream >= NUM_STREAMS) { + return std::nullopt; + } if (last_queries[stream] != 0) { std::pair result(last_queries[stream], last_queries_stride[stream]); return result; @@ -932,6 +945,7 @@ private: void UpdateBuffers() { last_queries.fill(0); last_queries_stride.fill(1); + stream_to_slot.fill(INVALID_SLOT); streams_mask = 0; // reset previously recorded streams runtime.View3DRegs([this](Maxwell3D& maxwell3d) { buffers_count = 0; @@ -956,19 +970,23 @@ private: if (tf.buffers[i].enable == 0) { continue; } + buffers_count = std::max(buffers_count, i + 1); const size_t stream = tf.controls[i].stream; if (stream >= last_queries_stride.size()) { LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream); continue; } + if ((streams_mask & (1ULL << stream)) != 0) { + continue; + } last_queries_stride[stream] = tf.controls[i].stride; + stream_to_slot[stream] = i; streams_mask |= 1ULL << stream; - buffers_count = std::max(buffers_count, stream + 1); } }); } - std::pair ProduceCounterBuffer(size_t stream) { + std::pair ProduceCounterBuffer(size_t slot_index) { if (current_bank == nullptr || current_bank->IsClosed()) { current_bank_id = bank_pool.ReserveBank([this](std::deque& queue, size_t index) { @@ -994,7 +1012,8 @@ private: }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dst_buffer = current_bank->GetBuffer(), - src_buffer = counter_buffers[stream], src_offset = offsets[stream], + src_buffer = counter_buffers[slot_index], + src_offset = offsets[slot_index], slot](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); @@ -1013,6 +1032,7 @@ private: friend class PrimitivesSucceededStreamer; static constexpr size_t NUM_STREAMS = 4; + static constexpr size_t INVALID_SLOT = NUM_STREAMS; QueryCacheRuntime& runtime; const Device& device; @@ -1042,6 +1062,7 @@ private: std::array offsets{}; std::array last_queries; std::array last_queries_stride; + std::array stream_to_slot; Maxwell3D::Regs::PrimitiveTopology out_topology; u32 patch_vertices{1}; u64 streams_mask; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 2dbaab4127..534587ee94 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -176,7 +176,8 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { .pViewFormats = view_formats.data(), }; if (view_formats.size() > 1) { - image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_ci.flags |= + VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; if (device.IsKhrImageFormatListSupported()) { image_ci.pNext = &image_format_list; } From 5a0c7095c7556cb88e8ab0fa9412ac85a1892d96 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sat, 11 Apr 2026 20:33:20 -0400 Subject: [PATCH 14/31] [vulkan] Adjustment VK_EXT_transform_feedback & VK_EXT_conditional_rendering --- .../renderer_vulkan/vk_buffer_cache.cpp | 9 ++- .../renderer_vulkan/vk_compute_pass.cpp | 5 +- .../renderer_vulkan/vk_rasterizer.cpp | 55 ++++++++++--------- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index a359502046..74748f6fc1 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -10,6 +10,7 @@ #include #include +#include "common/settings.h" #include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" @@ -556,7 +557,9 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset if (index >= device.GetMaxVertexInputBindings()) { return; } - if (device.IsExtExtendedDynamicStateSupported()) { + const bool use_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && + Settings::values.vertex_input_dynamic_state.GetValue(); + if (device.IsExtExtendedDynamicStateSupported() && !use_dynamic_vertex_input) { 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 +599,9 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bi if (binding_count == 0) { return; } - if (device.IsExtExtendedDynamicStateSupported()) { + const bool use_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && + Settings::values.vertex_input_dynamic_state.GetValue(); + if (device.IsExtExtendedDynamicStateSupported() && !use_dynamic_vertex_input) { 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()); }); diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index 79f006305c..f198b65d69 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -461,7 +461,8 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); + VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0, + write_barrier); }); } @@ -529,7 +530,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 743af16634..8c01a3ad66 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1750,38 +1750,31 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) boost::container::static_vector bindings; boost::container::static_vector 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 u32 max_attributes = + static_cast(std::min(Maxwell::NumVertexAttributes, + device.GetMaxVertexInputAttributes())); + const u32 max_bindings = + static_cast(std::min(Maxwell::NumVertexArrays, + device.GetMaxVertexInputBindings())); + + + for (u32 index = 0; index < max_attributes; ++index) { const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; const u32 binding{attribute.buffer}; - dirty[Dirty::VertexAttribute0 + index] = false; - dirty[Dirty::VertexBinding0 + static_cast(binding)] = true; - if (!attribute.constant) { - attributes.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, - .pNext = nullptr, - .location = static_cast(index), - .binding = 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]) { + if (attribute.constant || binding >= max_bindings) { continue; } - dirty[Dirty::VertexBinding0 + index] = false; + attributes.push_back({ + .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, + .pNext = nullptr, + .location = index, + .binding = binding, + .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), + .offset = attribute.offset, + }); + } - const u32 binding{static_cast(index)}; + for (u32 binding = 0; binding < max_bindings; ++binding) { const auto& input_binding{regs.vertex_streams[binding]}; const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; bindings.push_back({ @@ -1793,6 +1786,14 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) .divisor = is_instanced ? input_binding.frequency : 1, }); } + + for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { + dirty[Dirty::VertexAttribute0 + index] = false; + } + for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { + dirty[Dirty::VertexBinding0 + index] = false; + } + scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { cmdbuf.SetVertexInputEXT(bindings, attributes); }); From d7c1edbde289cb252e10b9a609407b56c39ffb34 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 12 Apr 2026 01:11:38 -0400 Subject: [PATCH 15/31] [test] Force Synchronization of buffers --- src/video_core/renderer_vulkan/vk_query_cache.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 30ac002ef6..0a06806d91 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -1442,8 +1442,7 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo const u32 resolve_size = compare_to_zero ? 8 : 24; { std::scoped_lock lk(impl->buffer_cache.mutex); - const auto sync_info = compare_to_zero ? VideoCommon::ObtainBufferSynchronize::FullSynchronize - : VideoCommon::ObtainBufferSynchronize::NoSynchronize; + const auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto [buffer, offset] = impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op); From e754ed90187ebad85337b2b2866a5f794acf8f28 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 12 Apr 2026 01:37:48 -0400 Subject: [PATCH 16/31] [vulkan] Adjustment ResetQueryPool --- src/video_core/renderer_vulkan/renderer_vulkan.cpp | 7 ++++++- src/video_core/renderer_vulkan/vk_blit_screen.cpp | 6 +++++- src/video_core/renderer_vulkan/vk_query_cache.cpp | 3 ++- src/video_core/vulkan_common/vulkan_wrapper.cpp | 1 + src/video_core/vulkan_common/vulkan_wrapper.h | 5 +++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 010cfd225d..2deec13ace 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -171,7 +172,11 @@ try RendererVulkan::~RendererVulkan() { scheduler.RegisterOnSubmit([] {}); - void(device.GetLogical().WaitIdle()); + scheduler.Finish(); + { + std::scoped_lock lock{scheduler.submit_mutex}; + void(device.GetLogical().WaitIdle()); + } } void RendererVulkan::Composite(std::span framebuffers) { diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index bb7eb9bdaa..75a8c3bf91 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -8,6 +8,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "video_core/framebuffer_config.h" #include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" @@ -31,7 +32,10 @@ BlitScreen::~BlitScreen() = default; void BlitScreen::WaitIdle() { present_manager.WaitPresent(); scheduler.Finish(); - device.GetLogical().WaitIdle(); + { + std::scoped_lock lock{scheduler.submit_mutex}; + device.GetLogical().WaitIdle(); + } } void BlitScreen::SetWindowAdaptPass() { diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 0a06806d91..6aea5d18a8 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -157,8 +157,9 @@ public: ReserveHostQuery(); scheduler.Record([query_pool = current_query_pool, - query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { + query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { const bool use_precise = Settings::IsGPULevelHigh(); + cmdbuf.ResetQueryPool(query_pool, static_cast(query_index), 1); cmdbuf.BeginQuery(query_pool, static_cast(query_index), use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); }); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index f59ac7d6bc..871ce52678 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -123,6 +123,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdEndDebugUtilsLabelEXT); X(vkCmdFillBuffer); X(vkCmdPipelineBarrier); + X(vkCmdResetQueryPool); X(vkCmdPushConstants); X(vkCmdPushDescriptorSetWithTemplateKHR); X(vkCmdSetBlendConstants); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index aaff66359e..4a3baad2c4 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -225,6 +225,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; PFN_vkCmdFillBuffer vkCmdFillBuffer{}; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; + PFN_vkCmdResetQueryPool vkCmdResetQueryPool{}; PFN_vkCmdPushConstants vkCmdPushConstants{}; PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{}; PFN_vkCmdResolveImage vkCmdResolveImage{}; @@ -1168,6 +1169,10 @@ public: dld->vkCmdEndQuery(handle, query_pool, query); } + void ResetQueryPool(VkQueryPool query_pool, u32 first_query, u32 query_count) const noexcept { + dld->vkCmdResetQueryPool(handle, query_pool, first_query, query_count); + } + void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first, Span sets, Span dynamic_offsets) const noexcept { dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(), From 0e2ff87fb273d34a697897d099910805aa9f5ca2 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 12 Apr 2026 01:52:46 -0400 Subject: [PATCH 17/31] [vulkan] Adjustment ImageView creation flags --- .../renderer_vulkan/vk_texture_cache.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 534587ee94..6adfd09e8f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -178,6 +178,16 @@ constexpr VkBorderColor ConvertBorderColor(const std::array& color) { if (view_formats.size() > 1) { image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; + + const bool has_storage_compatible_view = + std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) { + return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + FormatType::Optimal); + }); + if (has_storage_compatible_view) { + image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + if (device.IsKhrImageFormatListSupported()) { image_ci.pNext = &image_format_list; } @@ -2127,15 +2137,13 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI } } const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); - if (ImageUsageFlags(format_info, format) != image.UsageFlags()) { - LOG_WARNING(Render_Vulkan, - "Image view format {} has different usage flags than image format {}", format, - image.info.format); - } + const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); + const VkImageUsageFlags image_usage = image.UsageFlags(); + const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; const VkImageViewUsageCreateInfo image_view_usage{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, - .usage = ImageUsageFlags(format_info, format), + .usage = clamped_view_usage, }; const VkImageViewCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, From e3725a160e8c707e44de16144216ccff21e78330 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 12 Apr 2026 03:41:09 -0400 Subject: [PATCH 18/31] [vulkan] Adjustment Viewport/Scissor in DynamicState --- .../renderer_vulkan/fixed_pipeline_state.cpp | 30 +++++---- .../renderer_vulkan/fixed_pipeline_state.h | 8 ++- .../renderer_vulkan/vk_pipeline_cache.cpp | 6 ++ .../renderer_vulkan/vk_rasterizer.cpp | 63 +++++++++++++++++-- .../renderer_vulkan/vk_state_tracker.cpp | 1 + 5 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 866b721a84..d53cf70b37 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -190,9 +190,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } } } - if (!extended_dynamic_state_3_enables) { - dynamic_state.Refresh3(regs); - } + dynamic_state.Refresh3(regs, features); if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } @@ -295,16 +293,22 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); } -void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) { - logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::Passthrough || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumZ); - - line_stipple_enable.Assign(regs.line_stipple_enable); +void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs, + const DynamicFeatures& features) { + if (!features.has_dynamic_state3_logic_op_enable) { + logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_depth_clamp_enable) { + depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); + } + if (!features.has_dynamic_state3_line_stipple_enable) { + line_stipple_enable.Assign(regs.line_stipple_enable); + } } size_t FixedPipelineState::Hash() const noexcept { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index ffc91e9a55..44157d686d 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -27,6 +27,9 @@ struct DynamicFeatures { bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_3_blend; bool has_extended_dynamic_state_3_enables; + bool has_dynamic_state3_depth_clamp_enable; + bool has_dynamic_state3_logic_op_enable; + bool has_dynamic_state3_line_stipple_enable; bool has_dynamic_vertex_input; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; @@ -175,7 +178,7 @@ struct FixedPipelineState { void Refresh(const Maxwell& regs); void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, bool base_features_supported); - void Refresh3(const Maxwell& regs); + void Refresh3(const Maxwell& regs, const DynamicFeatures& features); Maxwell::ComparisonOp DepthTestFunc() const noexcept { return UnpackComparisonOp(depth_test_func); @@ -265,8 +268,7 @@ struct FixedPipelineState { return sizeof(*this); } if (dynamic_vertex_input && extended_dynamic_state_3_blend) { - // Exclude dynamic state and attributes - return offsetof(FixedPipelineState, dynamic_state); + return offsetof(FixedPipelineState, attachments); } if (dynamic_vertex_input) { // Exclude dynamic state diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 5f86506961..7b5ac7fac0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -485,6 +485,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.IsExtExtendedDynamicState3BlendingSupported(); dynamic_features.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(); + dynamic_features.has_dynamic_state3_depth_clamp_enable = + device.SupportsDynamicState3DepthClampEnable(); + dynamic_features.has_dynamic_state3_logic_op_enable = + device.SupportsDynamicState3LogicOpEnable(); + dynamic_features.has_dynamic_state3_line_stipple_enable = + device.SupportsDynamicState3LineStippleEnable(); // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 8c01a3ad66..78337b3ebe 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -173,6 +173,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, } return params; } + +bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) { + switch (topology) { + case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: + return false; + default: + return true; + } +} + +bool IsPrimitiveRestartSupported(const Device& device, VkPrimitiveTopology topology) { + return ((topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsTopologyListPrimitiveRestartSupported()) || + SupportsPrimitiveRestart(topology) || + (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsPatchListPrimitiveRestartSupported())); +} } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -991,6 +1013,12 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, void RasterizerVulkan::UpdateDynamicStates() { auto& regs = maxwell3d->regs; + auto& flags = maxwell3d->dirty.flags; + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (state_tracker.ChangePrimitiveTopology(topology)) { + flags[Dirty::DepthBiasEnable] = true; + flags[Dirty::PrimitiveRestartEnable] = true; + } // Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting UpdateViewportsState(regs); @@ -1099,6 +1127,9 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg if (!state_tracker.TouchViewports()) { return; } + + maxwell3d->dirty.flags[Dirty::Scissors] = true; + if (!regs.viewport_scale_offset_enabled) { float x = static_cast(regs.surface_clip.x); float y = static_cast(regs.surface_clip.y); @@ -1116,8 +1147,12 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg .minDepth = 0.0f, .maxDepth = 1.0f, }; - scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { - cmdbuf.SetViewport(0, viewport); + scheduler.Record([this, viewport](vk::CommandBuffer cmdbuf) { + const u32 num_viewports = std::min(device.GetMaxViewports(), Maxwell::NumViewports); + std::array viewport_list{}; + viewport_list.fill(viewport); + const vk::Span viewports(viewport_list.data(), num_viewports); + cmdbuf.SetViewport(0, viewports); }); return; } @@ -1157,8 +1192,12 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs scissor.offset.y = static_cast(y); scissor.extent.width = width; scissor.extent.height = height; - scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { - cmdbuf.SetScissor(0, scissor); + scheduler.Record([this, scissor](vk::CommandBuffer cmdbuf) { + const u32 num_scissors = std::min(device.GetMaxViewports(), Maxwell::NumViewports); + std::array scissor_list{}; + scissor_list.fill(scissor); + const vk::Span scissors(scissor_list.data(), num_scissors); + cmdbuf.SetScissor(0, scissors); }); return; } @@ -1403,7 +1442,17 @@ void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::R if (!state_tracker.TouchPrimitiveRestartEnable()) { return; } - scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { + + bool enable = regs.primitive_restart.enabled != 0; + if (device.IsMoltenVK()) { + enable = true; + } else if (enable) { + const auto topology = + MaxwellToVK::PrimitiveTopology(device, maxwell3d->draw_manager->GetDrawState().topology); + enable = IsPrimitiveRestartSupported(device, topology); + } + + scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetPrimitiveRestartEnableEXT(enable); }); } @@ -1742,7 +1791,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { auto& dirty{maxwell3d->dirty.flags}; - if (!dirty[Dirty::VertexInput]) { + const bool vertex_input_dirty = dirty[Dirty::VertexInput]; + const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers]; + if (!vertex_input_dirty && !vertex_buffers_dirty) { return; } dirty[Dirty::VertexInput] = false; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 79967d540a..1e8530f288 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -87,6 +87,7 @@ Flags MakeInvalidationFlags() { void SetupDirtyViewports(Tables& tables) { FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); + FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports); tables[0][OFF(viewport_scale_offset_enabled)] = Viewports; tables[1][OFF(window_origin)] = Viewports; } From 657c43225411ff4e7f0efa0ff3879d1c40e99ba5 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 13 Apr 2026 18:07:35 -0400 Subject: [PATCH 19/31] Fix license headers --- src/common/settings.h | 2 +- src/video_core/query_cache/query_stream.h | 3 +++ src/video_core/renderer_vulkan/vk_rasterizer.h | 2 +- src/video_core/renderer_vulkan/vk_state_tracker.cpp | 2 +- src/video_core/renderer_vulkan/vk_state_tracker.h | 2 +- 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index ca61ac906e..eb837923b3 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -584,7 +584,7 @@ struct Values { SwitchableSetting dyna_state{linkage, #if defined(ANDROID) - ExtendedDynamicState::EDS1, + ExtendedDynamicState::Disabled, #elif defined(__APPLE__) ExtendedDynamicState::Disabled, #else diff --git a/src/video_core/query_cache/query_stream.h b/src/video_core/query_cache/query_stream.h index 130c079eac..b4dc9d1815 100644 --- a/src/video_core/query_cache/query_stream.h +++ b/src/video_core/query_cache/query_stream.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 06cbdd2679..841933d31d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 1e8530f288..3f4dd89c7e 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 74bae9e181..6b47ba4176 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 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project From fee2c7edeb8e1f91827f0ee717571b2b485e17e1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Tue, 14 Apr 2026 16:53:32 -0400 Subject: [PATCH 20/31] [vulkan] Updated VertexInput Stream offset --- src/video_core/renderer_vulkan/vk_state_tracker.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 3f4dd89c7e..391473273f 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -217,14 +217,17 @@ void SetupDirtyVertexAttributes(Tables& tables) { } void SetupDirtyVertexBindings(Tables& tables) { - // Do NOT include stride here, it's implicit in VertexBuffer + static constexpr size_t stride_enable_offset = 0; static constexpr size_t divisor_offset = 3; for (size_t i = 0; i < Regs::NumVertexArrays; ++i) { + const size_t stream_base = OFF(vertex_streams) + i * NUM(vertex_streams[0]); const u8 flag = static_cast(VertexBinding0 + i); tables[0][OFF(vertex_stream_instances) + i] = VertexInput; tables[1][OFF(vertex_stream_instances) + i] = flag; - tables[0][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = VertexInput; - tables[1][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = flag; + tables[0][stream_base + stride_enable_offset] = VertexInput; + tables[1][stream_base + stride_enable_offset] = flag; + tables[0][stream_base + divisor_offset] = VertexInput; + tables[1][stream_base + divisor_offset] = flag; } } From 0f749028b4c62cb3656f7a068f0f818d8786ee23 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 02:24:19 -0400 Subject: [PATCH 21/31] [vulkan] Rework DynamicState - ExtendedDynamicState --- .../renderer_vulkan/fixed_pipeline_state.cpp | 55 +++++- .../renderer_vulkan/fixed_pipeline_state.h | 59 ++++-- .../renderer_vulkan/vk_graphics_pipeline.cpp | 106 +++++++---- .../renderer_vulkan/vk_pipeline_cache.cpp | 71 +++++-- .../vulkan_common/vulkan_device.cpp | 173 +++++++++++++++++- src/video_core/vulkan_common/vulkan_device.h | 114 +++++++++++- 6 files changed, 486 insertions(+), 92 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index d53cf70b37..67d3affacb 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -57,6 +57,11 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; raw1 = 0; + core_dynamic_viewport_scissor.Assign(features.has_core_dynamic_viewport_scissor ? 1 : 0); + core_dynamic_depth_bias.Assign(features.has_core_dynamic_depth_bias ? 1 : 0); + core_dynamic_depth_bounds.Assign(features.has_core_dynamic_depth_bounds ? 1 : 0); + core_dynamic_line_width.Assign(features.has_core_dynamic_line_width ? 1 : 0); + core_dynamic_stencil_masks.Assign(features.has_core_dynamic_stencil_masks ? 1 : 0); extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0); extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0); extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0); @@ -76,6 +81,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe raw2 = 0; + const auto test_func = regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); @@ -106,10 +112,18 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } provoking_vertex_last.Assign(use_last_provoking_vertex ? 1 : 0); - conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); - smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); - alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); - alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); + if (!features.has_dynamic_state3_conservative_rasterization_mode) { + conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_line_rasterization_mode) { + smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_alpha_to_coverage_enable) { + alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_alpha_to_one_enable) { + alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); + } app_stage.Assign(maxwell3d.engine_state); depth_bounds_min = static_cast(regs.depth_bounds[0]); @@ -173,24 +187,49 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } dynamic_state.raw1 = 0; dynamic_state.raw2 = 0; - if (!extended_dynamic_state) { + + const bool any_eds1_baked = !features.has_extended_dynamic_state || + !features.has_eds1_cull_mode || !features.has_eds1_front_face || + !features.has_eds1_depth_test_enable || !features.has_eds1_depth_write_enable || + !features.has_eds1_depth_compare_op || + !(features.has_eds1_depth_bounds_test_enable || features.has_core_dynamic_depth_bounds) || + !features.has_eds1_stencil_test_enable || !features.has_eds1_stencil_op; + + if (any_eds1_baked) { dynamic_state.Refresh(regs); + } + + if (!features.has_extended_dynamic_state || !features.has_eds1_vertex_input_binding_stride) { std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { return static_cast(array.stride.Value()); }); } - if (!extended_dynamic_state_2_logic_op) { + + const bool any_eds2_baked = !features.has_extended_dynamic_state_2 || + !(features.has_eds2_depth_bias_enable || features.has_core_dynamic_depth_bias) || + !features.has_eds2_primitive_restart_enable || + !features.has_eds2_rasterizer_discard_enable || !features.has_extended_dynamic_state_2_logic_op; + + if (any_eds2_baked) { dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); } - if (!extended_dynamic_state_3_blend) { + + const bool any_eds3_baked = !features.has_extended_dynamic_state_3_blend || + !features.has_dynamic_state3_depth_clamp_enable || !features.has_dynamic_state3_logic_op_enable || + !features.has_dynamic_state3_line_stipple_enable || !features.has_dynamic_state3_alpha_to_coverage_enable || + !features.has_dynamic_state3_alpha_to_one_enable || !features.has_dynamic_state3_line_rasterization_mode || + !features.has_dynamic_state3_conservative_rasterization_mode; + + if (any_eds3_baked) { if (maxwell3d.dirty.flags[Dirty::Blending]) { maxwell3d.dirty.flags[Dirty::Blending] = false; for (size_t index = 0; index < attachments.size(); ++index) { attachments[index].Refresh(regs, index); } } + dynamic_state.Refresh3(regs, features); } - dynamic_state.Refresh3(regs, features); + if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 44157d686d..28fe2ccb5c 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,8 +21,25 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { + bool has_core_dynamic_viewport_scissor; + bool has_core_dynamic_depth_bias; + bool has_core_dynamic_depth_bounds; + bool has_core_dynamic_line_width; + bool has_core_dynamic_stencil_masks; bool has_extended_dynamic_state; + bool has_eds1_cull_mode; + bool has_eds1_front_face; + bool has_eds1_depth_test_enable; + bool has_eds1_depth_write_enable; + bool has_eds1_depth_compare_op; + bool has_eds1_depth_bounds_test_enable; + bool has_eds1_stencil_test_enable; + bool has_eds1_stencil_op; + bool has_eds1_vertex_input_binding_stride; bool has_extended_dynamic_state_2; + bool has_eds2_depth_bias_enable; + bool has_eds2_primitive_restart_enable; + bool has_eds2_rasterizer_discard_enable; bool has_extended_dynamic_state_2_logic_op; bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_3_blend; @@ -30,6 +47,10 @@ struct DynamicFeatures { bool has_dynamic_state3_depth_clamp_enable; bool has_dynamic_state3_logic_op_enable; bool has_dynamic_state3_line_stipple_enable; + bool has_dynamic_state3_alpha_to_coverage_enable; + bool has_dynamic_state3_alpha_to_one_enable; + bool has_dynamic_state3_line_rasterization_mode; + bool has_dynamic_state3_conservative_rasterization_mode; bool has_dynamic_vertex_input; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; @@ -194,23 +215,27 @@ struct FixedPipelineState { }; union { - u32 raw1; - BitField<0, 1, u32> extended_dynamic_state; - BitField<1, 1, u32> extended_dynamic_state_2; - BitField<2, 1, u32> extended_dynamic_state_2_logic_op; - BitField<3, 1, u32> extended_dynamic_state_3_blend; - BitField<4, 1, u32> extended_dynamic_state_3_enables; - BitField<5, 1, u32> dynamic_vertex_input; - BitField<6, 1, u32> xfb_enabled; - BitField<7, 1, u32> ndc_minus_one_to_one; - BitField<8, 2, u32> polygon_mode; - BitField<10, 2, u32> tessellation_primitive; - BitField<12, 2, u32> tessellation_spacing; - BitField<14, 1, u32> tessellation_clockwise; - BitField<15, 5, u32> patch_control_points_minus_one; - - BitField<24, 4, Maxwell::PrimitiveTopology> topology; - BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; + u64 raw1; + BitField<0, 1, u64> core_dynamic_viewport_scissor; + BitField<1, 1, u64> core_dynamic_depth_bias; + BitField<2, 1, u64> core_dynamic_depth_bounds; + BitField<3, 1, u64> core_dynamic_line_width; + BitField<4, 1, u64> core_dynamic_stencil_masks; + BitField<5, 1, u64> extended_dynamic_state; + BitField<6, 1, u64> extended_dynamic_state_2; + BitField<7, 1, u64> extended_dynamic_state_2_logic_op; + BitField<8, 1, u64> extended_dynamic_state_3_blend; + BitField<9, 1, u64> extended_dynamic_state_3_enables; + BitField<10, 1, u64> dynamic_vertex_input; + BitField<11, 1, u64> xfb_enabled; + BitField<12, 1, u64> ndc_minus_one_to_one; + BitField<13, 2, u64> polygon_mode; + BitField<15, 2, u64> tessellation_primitive; + BitField<17, 2, u64> tessellation_spacing; + BitField<19, 1, u64> tessellation_clockwise; + BitField<20, 5, u64> patch_control_points_minus_one; + BitField<25, 4, Maxwell::PrimitiveTopology> topology; + BitField<29, 4, Tegra::Texture::MsaaMode> msaa_mode; }; union { u32 raw2; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 43fbefe425..8f231a4352 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -848,62 +848,94 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{ - VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, - VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, - VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, - VK_DYNAMIC_STATE_LINE_WIDTH, - }; + static_vector dynamic_states{}; + // Core dynamic states (Vulkan 1.0) + if (device.SupportsCoreDynamicViewportScissor()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); + } + if (device.SupportsCoreDynamicDepthBias()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); + } + // Blend constants are commonly available; keep as default dynamic state. + dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); + if (device.SupportsCoreDynamicDepthBounds()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); + } + if (device.SupportsCoreDynamicStencilMasks()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + } + if (device.SupportsCoreDynamicLineWidth()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); + } if (key.state.extended_dynamic_state) { - static constexpr std::array extended{ - VK_DYNAMIC_STATE_CULL_MODE_EXT, - VK_DYNAMIC_STATE_FRONT_FACE_EXT, - VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, - VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_STENCIL_OP_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); + if (device.SupportsEds1CullMode()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_CULL_MODE_EXT); + } + if (device.SupportsEds1FrontFace()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_FRONT_FACE_EXT); + } + if (device.SupportsEds1DepthTestEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT); + } + if (device.SupportsEds1DepthWriteEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT); + } + if (device.SupportsEds1DepthCompareOp()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT); + } + if (device.SupportsEds1DepthBoundsTestEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT); + } + if (device.SupportsEds1StencilTestEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT); + } + if (device.SupportsEds1StencilOp()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_OP_EXT); + } - // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1 - // Only use it if VIDS is not active (VIDS replaces it with full vertex input control) - if (!key.state.dynamic_vertex_input) { + // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT + if (!key.state.dynamic_vertex_input && device.SupportsEds1VertexInputBindingStride()) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); } } - // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS - // Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE + // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) replaces VERTEX_INPUT_BINDING_STRIDE if (key.state.dynamic_vertex_input) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); } - // EDS2 - Core (3 states) + // EDS2 - Core (3 states) — include only supported subfeatures if (key.state.extended_dynamic_state_2) { - static constexpr std::array extended2{ - VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, - VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, - VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end()); + if (device.SupportsEds2DepthBiasEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT); + } + if (device.SupportsEds2PrimitiveRestartEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT); + } + if (device.SupportsEds2RasterizerDiscardEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT); + } } // EDS2 - LogicOp (granular) - if (key.state.extended_dynamic_state_2_logic_op) { + if (key.state.extended_dynamic_state_2_logic_op && device.SupportsDynamicState2LogicOp()) { dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); } // EDS3 - Blending (composite: 3 states) if (key.state.extended_dynamic_state_3_blend) { - static constexpr std::array extended3{ - VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, - VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, - VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + if (device.SupportsDynamicState3ColorBlendEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT); + } + if (device.SupportsDynamicState3ColorBlendEquation()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT); + } + if (device.SupportsDynamicState3ColorWriteMask()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT); + } } // EDS3 - Enables (composite: per-feature) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7b5ac7fac0..bcfc4b8b4d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -58,7 +58,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 16; +constexpr u32 CACHE_VERSION = 17; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template @@ -465,18 +465,46 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, dynamic_features = {}; - // User granularity enforced in vulkan_device.cpp switch statement: - // Level 0: Core Dynamic States only - // Level 1: Core + EDS1 - // Level 2: Core + EDS1 + EDS2 (accumulative) - // Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative) - // Here we only verify if extensions were successfully loaded by the device + dynamic_features.has_core_dynamic_viewport_scissor = + device.SupportsCoreDynamicViewportScissor(); + dynamic_features.has_core_dynamic_depth_bias = + device.SupportsCoreDynamicDepthBias(); + dynamic_features.has_core_dynamic_depth_bounds = + device.SupportsCoreDynamicDepthBounds(); + dynamic_features.has_core_dynamic_line_width = + device.SupportsCoreDynamicLineWidth(); + dynamic_features.has_core_dynamic_stencil_masks = + device.SupportsCoreDynamicStencilMasks(); dynamic_features.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(); + dynamic_features.has_eds1_cull_mode = + device.SupportsEds1CullMode(); + dynamic_features.has_eds1_front_face = + device.SupportsEds1FrontFace(); + dynamic_features.has_eds1_depth_test_enable = + device.SupportsEds1DepthTestEnable(); + dynamic_features.has_eds1_depth_write_enable = + device.SupportsEds1DepthWriteEnable(); + dynamic_features.has_eds1_depth_compare_op = + device.SupportsEds1DepthCompareOp(); + dynamic_features.has_eds1_depth_bounds_test_enable = + device.SupportsEds1DepthBoundsTestEnable(); + dynamic_features.has_eds1_stencil_test_enable = + device.SupportsEds1StencilTestEnable(); + dynamic_features.has_eds1_stencil_op = + device.SupportsEds1StencilOp(); + dynamic_features.has_eds1_vertex_input_binding_stride = + device.SupportsEds1VertexInputBindingStride(); dynamic_features.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(); + dynamic_features.has_eds2_depth_bias_enable = + device.SupportsEds2DepthBiasEnable(); + dynamic_features.has_eds2_primitive_restart_enable = + device.SupportsEds2PrimitiveRestartEnable(); + dynamic_features.has_eds2_rasterizer_discard_enable = + device.SupportsEds2RasterizerDiscardEnable(); dynamic_features.has_extended_dynamic_state_2_logic_op = device.IsExtExtendedDynamicState2ExtrasSupported(); dynamic_features.has_extended_dynamic_state_2_patch_control_points = false; @@ -491,13 +519,21 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.SupportsDynamicState3LogicOpEnable(); dynamic_features.has_dynamic_state3_line_stipple_enable = device.SupportsDynamicState3LineStippleEnable(); + dynamic_features.has_dynamic_state3_alpha_to_coverage_enable = + device.SupportsDynamicState3AlphaToCoverageEnable(); + dynamic_features.has_dynamic_state3_alpha_to_one_enable = + device.SupportsDynamicState3AlphaToOneEnable(); + dynamic_features.has_dynamic_state3_line_rasterization_mode = + device.SupportsDynamicState3LineRasterizationMode(); + dynamic_features.has_dynamic_state3_conservative_rasterization_mode = + device.SupportsDynamicState3ConservativeRasterizationMode(); - // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && Settings::values.vertex_input_dynamic_state.GetValue(); - dynamic_features.has_provoking_vertex = device.IsExtProvokingVertexSupported(); + dynamic_features.has_provoking_vertex = + device.IsExtProvokingVertexSupported(); dynamic_features.has_provoking_vertex_first_mode = device.SupportsProvokingVertexFirstMode(); dynamic_features.has_provoking_vertex_last_mode = @@ -604,8 +640,20 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading GraphicsPipelineCacheKey key; file.read(reinterpret_cast(&key), sizeof(key)); - if ((key.state.extended_dynamic_state != 0) != + if ((key.state.core_dynamic_viewport_scissor != 0) != + dynamic_features.has_core_dynamic_viewport_scissor || + (key.state.core_dynamic_depth_bias != 0) != + dynamic_features.has_core_dynamic_depth_bias || + (key.state.core_dynamic_depth_bounds != 0) != + dynamic_features.has_core_dynamic_depth_bounds || + (key.state.core_dynamic_line_width != 0) != + dynamic_features.has_core_dynamic_line_width || + (key.state.core_dynamic_stencil_masks != 0) != + dynamic_features.has_core_dynamic_stencil_masks || + (key.state.extended_dynamic_state != 0) != dynamic_features.has_extended_dynamic_state || + (key.state.dynamic_vertex_input != 0) != + dynamic_features.has_dynamic_vertex_input || (key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 || (key.state.extended_dynamic_state_2_logic_op != 0) != @@ -613,8 +661,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading (key.state.extended_dynamic_state_3_blend != 0) != dynamic_features.has_extended_dynamic_state_3_blend || (key.state.extended_dynamic_state_3_enables != 0) != - dynamic_features.has_extended_dynamic_state_3_enables || - (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { + dynamic_features.has_extended_dynamic_state_3_enables) { return; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index f48fe39e4e..ba088441d2 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -648,16 +648,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const auto dyna_state = Settings::values.dyna_state.GetValue(); - // Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp - // This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs: - // Level 0 = Core Dynamic States only (Vulkan 1.0) - // Level 1 = Core + VK_EXT_extended_dynamic_state - // Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 - // Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3 - switch (dyna_state) { case Settings::ExtendedDynamicState::Disabled: - // Level 0: Disable all extended dynamic state extensions + // Level 0: Enable DynamicState (Static) + Disable all extended dynamic state extensions RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, @@ -666,9 +659,60 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; dynamic_state3_enables = false; + + core_dynamic_viewport_scissor = (dld.vkCmdSetViewport != nullptr) && (dld.vkCmdSetScissor != nullptr); + core_dynamic_depth_bias = (dld.vkCmdSetDepthBias != nullptr) || (dld.vkCmdSetDepthBias2EXT != nullptr) || + (features.depth_bias_control.depthBiasControl != VK_FALSE); + + if (features.core.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { + features.core.features.depthBounds = VK_FALSE; + core_dynamic_depth_bounds = false; + } else { + core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.core.features.depthBounds; + } + + if (features.core.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { + features.core.features.wideLines = VK_FALSE; + core_dynamic_line_width = false; + } else { + core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.core.features.wideLines; + } + + core_dynamic_stencil_masks = (dld.vkCmdSetStencilCompareMask != nullptr) && + (dld.vkCmdSetStencilWriteMask != nullptr) && + (dld.vkCmdSetStencilReference != nullptr); break; case Settings::ExtendedDynamicState::EDS1: // Level 1: Enable EDS1, disable EDS2 and EDS3 + if (dyna_state_enabled && extensions.extended_dynamic_state) { + eds1_cull_mode = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetCullModeEXT != nullptr); + eds1_front_face = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetFrontFaceEXT != nullptr); + eds1_depth_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthTestEnableEXT != nullptr); + eds1_depth_write_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthWriteEnableEXT != nullptr); + eds1_depth_compare_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthCompareOpEXT != nullptr); + eds1_depth_bounds_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthBoundsTestEnableEXT != nullptr); + eds1_stencil_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilTestEnableEXT != nullptr); + eds1_stencil_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilOpEXT != nullptr); + + vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); + + const bool eds1_any = eds1_cull_mode || eds1_front_face || eds1_depth_test_enable || + eds1_depth_write_enable || eds1_depth_compare_op || eds1_depth_bounds_test_enable || + eds1_stencil_test_enable || eds1_stencil_op || vertex_input_binding_stride; + if (!eds1_any) { + extensions.extended_dynamic_state = false; + RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + // Clear flags + eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; + eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; + vertex_input_binding_stride = false; + } + } else { + extensions.extended_dynamic_state = false; + eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; + eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; + vertex_input_binding_stride = false; + } RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, @@ -678,6 +722,32 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR break; case Settings::ExtendedDynamicState::EDS2: // Level 2: Enable EDS1 + EDS2, disable EDS3 + { + bool eds1_any = (features.extended_dynamic_state.extendedDynamicState && + ((dld.vkCmdBindVertexBuffers2EXT != nullptr) || (dld.vkCmdSetCullModeEXT != nullptr))); + if (!eds1_any) { + extensions.extended_dynamic_state2 = false; + } + } + if (dyna_state_enabled && extensions.extended_dynamic_state2) { + eds2_depth_bias_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetDepthBiasEnableEXT != nullptr); + eds2_primitive_restart_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr); + eds2_rasterizer_discard_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetRasterizerDiscardEnableEXT != nullptr); + + if (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT == nullptr)) { + features.extended_dynamic_state2.extendedDynamicState2LogicOp = VK_FALSE; + } + + const bool eds2_any = eds2_depth_bias_enable || eds2_primitive_restart_enable || eds2_rasterizer_discard_enable || + (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT != nullptr)); + if (!eds2_any) { + extensions.extended_dynamic_state2 = false; + RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); + eds2_depth_bias_enable = eds2_primitive_restart_enable = eds2_rasterizer_discard_enable = false; + } + } else { + extensions.extended_dynamic_state2 = false; + } RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; @@ -686,13 +756,96 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR case Settings::ExtendedDynamicState::EDS3: default: // Level 3: Enable all (EDS1 + EDS2 + EDS3) + { + bool eds1_any = (features.extended_dynamic_state.extendedDynamicState && + ((dld.vkCmdBindVertexBuffers2EXT != nullptr) || (dld.vkCmdSetCullModeEXT != nullptr))); + bool eds2_any = ((features.extended_dynamic_state2.extendedDynamicState2 && + ((dld.vkCmdSetDepthBiasEnableEXT != nullptr) || (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr))) || + (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT != nullptr))); + if (!(eds1_any || eds2_any)) { + extensions.extended_dynamic_state3 = false; + } + } + if (dyna_state_enabled && extensions.extended_dynamic_state3) { + if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && (dld.vkCmdSetColorBlendEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation && (dld.vkCmdSetColorBlendEquationEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask && (dld.vkCmdSetColorWriteMaskEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable && (dld.vkCmdSetDepthClampEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable && (dld.vkCmdSetLogicOpEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable && (dld.vkCmdSetAlphaToCoverageEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable && (dld.vkCmdSetAlphaToOneEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode && (dld.vkCmdSetLineRasterizationModeEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode && (dld.vkCmdSetConservativeRasterizationModeEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable && (dld.vkCmdSetLineStippleEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable = VK_FALSE; + } + + bool blending = features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable || + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation || + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; + bool enables = features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable || + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable || + features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable || + features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable || + features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable || + features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode || + features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode; + if (!blending && !enables) { + extensions.extended_dynamic_state3 = false; + RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + } else { + dynamic_state3_blending = blending; + dynamic_state3_enables = enables; + } + } else { + extensions.extended_dynamic_state3 = false; + } break; } - // VK_EXT_vertex_input_dynamic_state is independent from EDS - // It can be enabled even without extended_dynamic_state + // VK_EXT_vertex_input_dynamic_state and features.vertex_input_binding_stride if (!Settings::values.vertex_input_dynamic_state.GetValue()) { RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + } else { + bool vids_entrypoint = dld.vkCmdSetVertexInputEXT != nullptr; + bool vb2_entrypoint = dld.vkCmdBindVertexBuffers2EXT != nullptr; + + if (!vids_entrypoint) { + features.vertex_input_dynamic_state.vertexInputDynamicState = VK_FALSE; + } + + if (vids_entrypoint && features.vertex_input_dynamic_state.vertexInputDynamicState) { + vertex_input_binding_stride = false; + } else { + // Only enable binding stride if EDS1 and the VB2 entrypoint are available. + vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; + } + + if (!features.vertex_input_dynamic_state.vertexInputDynamicState) { + extensions.vertex_input_dynamic_state = false; + } + } + + if (!features.extended_dynamic_state.extendedDynamicState) { + extensions.extended_dynamic_state = false; } logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cf341726a7..d435126a2f 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -569,31 +569,112 @@ public: return features.custom_border_color.customBorderColorWithoutFormat; } + /// Core dynamic states availability (Vulkan 1.0) + bool SupportsCoreDynamicViewportScissor() const { + return core_dynamic_viewport_scissor; + } + bool SupportsCoreDynamicDepthBias() const { + return core_dynamic_depth_bias; + } + bool SupportsCoreDynamicDepthBounds() const { + return core_dynamic_depth_bounds; + } + bool SupportsCoreDynamicLineWidth() const { + return core_dynamic_line_width; + } + bool SupportsCoreDynamicStencilMasks() const { + return core_dynamic_stencil_masks; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; } + /// VK_EXT_extended_dynamic_state core dynamic states availability + bool SupportsEds1CullMode() const { + return eds1_cull_mode; + } + bool SupportsEds1FrontFace() const { + return eds1_front_face; + } + bool SupportsEds1DepthTestEnable() const { + return eds1_depth_test_enable; + } + bool SupportsEds1DepthWriteEnable() const { + return eds1_depth_write_enable; + } + bool SupportsEds1DepthCompareOp() const { + return eds1_depth_compare_op; + } + bool SupportsEds1DepthBoundsTestEnable() const { + return eds1_depth_bounds_test_enable; + } + bool SupportsEds1StencilTestEnable() const { + return eds1_stencil_test_enable; + } + bool SupportsEds1StencilOp() const { + return eds1_stencil_op; + } + bool SupportsEds1VertexInputBindingStride() const { + return eds1_vertex_input_binding_stride; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state2. bool IsExtExtendedDynamicState2Supported() const { return extensions.extended_dynamic_state2; } + /// VK_EXT_extended_dynamic_state2 core dynamic states availability + bool SupportsEds2DepthBiasEnable() const { + return eds2_depth_bias_enable; + } + bool SupportsEds2PrimitiveRestartEnable() const { + return eds2_primitive_restart_enable; + } + bool SupportsEds2RasterizerDiscardEnable() const { + return eds2_rasterizer_discard_enable; + } + bool IsExtExtendedDynamicState2ExtrasSupported() const { return features.extended_dynamic_state2.extendedDynamicState2LogicOp; } + bool SupportsDynamicState2LogicOp() const { + return extensions.extended_dynamic_state2 && + features.extended_dynamic_state2.extendedDynamicState2LogicOp; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. bool IsExtExtendedDynamicState3Supported() const { return extensions.extended_dynamic_state3; } + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color blend enable. + bool SupportsDynamicState3ColorBlendEnable() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color blend equation. + bool SupportsDynamicState3ColorBlendEquation() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color write mask. + bool SupportsDynamicState3ColorWriteMask() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; + } + /// Returns true if the device supports VK_EXT_4444_formats. bool IsExt4444FormatsSupported() const { return features.format_a4b4g4r4.formatA4B4G4R4; } - /// Returns true if the device supports VK_EXT_extended_dynamic_state3. + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 blending. bool IsExtExtendedDynamicState3BlendingSupported() const { return dynamic_state3_blending; } @@ -1022,15 +1103,32 @@ private: bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation + bool core_dynamic_viewport_scissor{}; ///< vkCmdSetViewport && vkCmdSetScissor + bool core_dynamic_depth_bias{}; ///< vkCmdSetDepthBias (or alternative) + bool core_dynamic_depth_bounds{}; ///< vkCmdSetDepthBounds + bool core_dynamic_line_width{}; ///< vkCmdSetLineWidth + bool core_dynamic_stencil_masks{}; ///< vkCmdSetStencilCompareMask/WriteMask/Reference + bool eds1_cull_mode{}; ///< Has EDS1 support for dynamic cull mode state. + bool eds1_front_face{}; ///< Has EDS1 support for dynamic front face state. + bool eds1_depth_test_enable{}; ///< Has EDS1 support for dynamic depth test enable state. + bool eds1_depth_write_enable{}; ///< Has EDS1 support for dynamic depth write enable state. + bool eds1_depth_compare_op{}; ///< Has EDS1 support for dynamic depth compare op state. + bool eds1_depth_bounds_test_enable{}; ///< Has EDS1 support for dynamic depth bounds test enable state. + bool eds1_stencil_test_enable{}; ///< Has EDS1 support for dynamic stencil test enable state. + bool eds1_stencil_op{}; ///< Has EDS1 support for dynamic stencil op state. + bool eds1_vertex_input_binding_stride{}; ///< Has EDS1 support for dynamic vertex input binding stride state. + bool eds2_depth_bias_enable{}; ///< Has EDS2 support for depth bias enable state. + bool eds2_primitive_restart_enable{}; ///< Has EDS2 support for primitive restart enable state. + bool eds2_rasterizer_discard_enable{}; ///< Has EDS2 support for rasterizer discard enable state. bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3. - bool dynamic_state3_depth_clamp_enable{}; - bool dynamic_state3_logic_op_enable{}; - bool dynamic_state3_line_raster_mode{}; - bool dynamic_state3_conservative_raster_mode{}; - bool dynamic_state3_line_stipple_enable{}; - bool dynamic_state3_alpha_to_coverage{}; - bool dynamic_state3_alpha_to_one{}; + bool dynamic_state3_depth_clamp_enable{}; ///< Has dynamic_state3 support for depth clamp enable state. + bool dynamic_state3_logic_op_enable{}; ///< Has dynamic_state3 support for logic op enable state. + bool dynamic_state3_line_raster_mode{}; ///< Has dynamic_state3 support for line raster mode state. + bool dynamic_state3_conservative_raster_mode{}; ///< Has dynamic_state3 support for conservative raster mode state. + bool dynamic_state3_line_stipple_enable{}; ///< Has dynamic_state3 support for line stipple enable state. + bool dynamic_state3_alpha_to_coverage{}; ///< Has dynamic_state3 support for alpha to coverage state. + bool dynamic_state3_alpha_to_one{}; ///< Has dynamic_state3 support for alpha to one state. bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited). u64 device_access_memory{}; ///< Total size of device local memory in bytes. From 9c53463b8193d5fe2534306b8cf3d5d494b73e61 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 02:47:02 -0400 Subject: [PATCH 22/31] [vulkan] Adjustment ExtendedDynamicState --- .../renderer_vulkan/fixed_pipeline_state.h | 4 +-- .../vulkan_common/vulkan_device.cpp | 30 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 28fe2ccb5c..cd1d9017dc 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -234,8 +234,8 @@ struct FixedPipelineState { BitField<17, 2, u64> tessellation_spacing; BitField<19, 1, u64> tessellation_clockwise; BitField<20, 5, u64> patch_control_points_minus_one; - BitField<25, 4, Maxwell::PrimitiveTopology> topology; - BitField<29, 4, Tegra::Texture::MsaaMode> msaa_mode; + BitField<25, 3, Maxwell::PrimitiveTopology> topology; + BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; }; union { u32 raw2; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ba088441d2..31ae345f1e 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -664,18 +664,18 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR core_dynamic_depth_bias = (dld.vkCmdSetDepthBias != nullptr) || (dld.vkCmdSetDepthBias2EXT != nullptr) || (features.depth_bias_control.depthBiasControl != VK_FALSE); - if (features.core.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { - features.core.features.depthBounds = VK_FALSE; + if (features.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { + features.features.depthBounds = VK_FALSE; core_dynamic_depth_bounds = false; } else { - core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.core.features.depthBounds; + core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.features.depthBounds; } - if (features.core.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { - features.core.features.wideLines = VK_FALSE; + if (features.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { + features.features.wideLines = VK_FALSE; core_dynamic_line_width = false; } else { - core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.core.features.wideLines; + core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.features.wideLines; } core_dynamic_stencil_masks = (dld.vkCmdSetStencilCompareMask != nullptr) && @@ -684,7 +684,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR break; case Settings::ExtendedDynamicState::EDS1: // Level 1: Enable EDS1, disable EDS2 and EDS3 - if (dyna_state_enabled && extensions.extended_dynamic_state) { + if (dyna_state != Settings::ExtendedDynamicState::Disabled && extensions.extended_dynamic_state) { eds1_cull_mode = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetCullModeEXT != nullptr); eds1_front_face = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetFrontFaceEXT != nullptr); eds1_depth_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthTestEnableEXT != nullptr); @@ -694,24 +694,24 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR eds1_stencil_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilTestEnableEXT != nullptr); eds1_stencil_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilOpEXT != nullptr); - vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); + eds1_vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); const bool eds1_any = eds1_cull_mode || eds1_front_face || eds1_depth_test_enable || eds1_depth_write_enable || eds1_depth_compare_op || eds1_depth_bounds_test_enable || - eds1_stencil_test_enable || eds1_stencil_op || vertex_input_binding_stride; + eds1_stencil_test_enable || eds1_stencil_op || eds1_vertex_input_binding_stride; if (!eds1_any) { extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); // Clear flags eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; - vertex_input_binding_stride = false; + eds1_vertex_input_binding_stride = false; } } else { extensions.extended_dynamic_state = false; eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; - vertex_input_binding_stride = false; + eds1_vertex_input_binding_stride = false; } RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); @@ -729,7 +729,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR extensions.extended_dynamic_state2 = false; } } - if (dyna_state_enabled && extensions.extended_dynamic_state2) { + if (dyna_state != Settings::ExtendedDynamicState::Disabled && extensions.extended_dynamic_state2) { eds2_depth_bias_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetDepthBiasEnableEXT != nullptr); eds2_primitive_restart_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr); eds2_rasterizer_discard_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetRasterizerDiscardEnableEXT != nullptr); @@ -766,7 +766,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR extensions.extended_dynamic_state3 = false; } } - if (dyna_state_enabled && extensions.extended_dynamic_state3) { + if (dyna_state != Settings::ExtendedDynamicState::Disabled && extensions.extended_dynamic_state3) { if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && (dld.vkCmdSetColorBlendEnableEXT != nullptr))) { features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = VK_FALSE; } @@ -833,10 +833,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } if (vids_entrypoint && features.vertex_input_dynamic_state.vertexInputDynamicState) { - vertex_input_binding_stride = false; + eds1_vertex_input_binding_stride = false; } else { // Only enable binding stride if EDS1 and the VB2 entrypoint are available. - vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; + eds1_vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; } if (!features.vertex_input_dynamic_state.vertexInputDynamicState) { From 69e846f7ad503b3a56df1b3399b60ae166b9914b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 03:00:53 -0400 Subject: [PATCH 23/31] [vulkan] Adjusted pipeline keys inside patchControlPoints --- src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | 2 +- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8f231a4352..31f8cab38f 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -677,7 +677,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, - .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1, + .patchControlPoints = static_cast(key.state.patch_control_points_minus_one.Value()) + 1, }; std::array swizzles; std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index bcfc4b8b4d..e14d037858 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -195,7 +195,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program case Shader::Stage::TessellationEval: info.tess_clockwise = key.state.tessellation_clockwise != 0; info.tess_primitive = [&key] { - const u32 raw{key.state.tessellation_primitive.Value()}; + const u32 raw = static_cast(key.state.tessellation_primitive.Value()); switch (static_cast(raw)) { case Maxwell::Tessellation::DomainType::Isolines: return Shader::TessPrimitive::Isolines; @@ -208,7 +208,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program return Shader::TessPrimitive::Triangles; }(); info.tess_spacing = [&] { - const u32 raw{key.state.tessellation_spacing}; + const u32 raw = static_cast(key.state.tessellation_spacing.Value()); switch (static_cast(raw)) { case Maxwell::Tessellation::Spacing::Integer: return Shader::TessSpacing::Equal; From b0c04cfab4de36720b8bc326e90aa16181b3e0e5 Mon Sep 17 00:00:00 2001 From: lizzie Date: Wed, 15 Apr 2026 16:14:12 +0000 Subject: [PATCH 24/31] fix comp err --- .../renderer_vulkan/fixed_pipeline_state.h | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index cd1d9017dc..6344fe6eb7 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,41 +21,41 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { - bool has_core_dynamic_viewport_scissor; - bool has_core_dynamic_depth_bias; - bool has_core_dynamic_depth_bounds; - bool has_core_dynamic_line_width; - bool has_core_dynamic_stencil_masks; - bool has_extended_dynamic_state; - bool has_eds1_cull_mode; - bool has_eds1_front_face; - bool has_eds1_depth_test_enable; - bool has_eds1_depth_write_enable; - bool has_eds1_depth_compare_op; - bool has_eds1_depth_bounds_test_enable; - bool has_eds1_stencil_test_enable; - bool has_eds1_stencil_op; - bool has_eds1_vertex_input_binding_stride; - bool has_extended_dynamic_state_2; - bool has_eds2_depth_bias_enable; - bool has_eds2_primitive_restart_enable; - bool has_eds2_rasterizer_discard_enable; - bool has_extended_dynamic_state_2_logic_op; - bool has_extended_dynamic_state_2_patch_control_points; - bool has_extended_dynamic_state_3_blend; - bool has_extended_dynamic_state_3_enables; - bool has_dynamic_state3_depth_clamp_enable; - bool has_dynamic_state3_logic_op_enable; - bool has_dynamic_state3_line_stipple_enable; - bool has_dynamic_state3_alpha_to_coverage_enable; - bool has_dynamic_state3_alpha_to_one_enable; - bool has_dynamic_state3_line_rasterization_mode; - bool has_dynamic_state3_conservative_rasterization_mode; - bool has_dynamic_vertex_input; - bool has_provoking_vertex; - bool has_provoking_vertex_first_mode; - bool has_provoking_vertex_last_mode; - bool has_provoking_vertex_tf_preserve; + bool has_core_dynamic_viewport_scissor : 1; + bool has_core_dynamic_depth_bias : 1; + bool has_core_dynamic_depth_bounds : 1; + bool has_core_dynamic_line_width : 1; + bool has_core_dynamic_stencil_masks : 1; + bool has_extended_dynamic_state : 1; + bool has_eds1_cull_mode : 1; + bool has_eds1_front_face : 1; + bool has_eds1_depth_test_enable : 1; + bool has_eds1_depth_write_enable : 1; + bool has_eds1_depth_compare_op : 1; + bool has_eds1_depth_bounds_test_enable : 1; + bool has_eds1_stencil_test_enable : 1; + bool has_eds1_stencil_op : 1; + bool has_eds1_vertex_input_binding_stride : 1; + bool has_extended_dynamic_state_2 : 1; + bool has_eds2_depth_bias_enable : 1; + bool has_eds2_primitive_restart_enable : 1; + bool has_eds2_rasterizer_discard_enable : 1; + bool has_extended_dynamic_state_2_logic_op : 1; + bool has_extended_dynamic_state_2_patch_control_points : 1; + bool has_extended_dynamic_state_3_blend : 1; + bool has_extended_dynamic_state_3_enables : 1; + bool has_dynamic_state3_depth_clamp_enable : 1; + bool has_dynamic_state3_logic_op_enable : 1; + bool has_dynamic_state3_line_stipple_enable : 1; + bool has_dynamic_state3_alpha_to_coverage_enable : 1; + bool has_dynamic_state3_alpha_to_one_enable : 1; + bool has_dynamic_state3_line_rasterization_mode : 1; + bool has_dynamic_state3_conservative_rasterization_mode : 1; + bool has_dynamic_vertex_input : 1; + bool has_provoking_vertex : 1; + bool has_provoking_vertex_first_mode : 1; + bool has_provoking_vertex_last_mode : 1; + bool has_provoking_vertex_tf_preserve : 1; }; struct FixedPipelineState { @@ -215,25 +215,25 @@ struct FixedPipelineState { }; union { - u64 raw1; - BitField<0, 1, u64> core_dynamic_viewport_scissor; - BitField<1, 1, u64> core_dynamic_depth_bias; - BitField<2, 1, u64> core_dynamic_depth_bounds; - BitField<3, 1, u64> core_dynamic_line_width; - BitField<4, 1, u64> core_dynamic_stencil_masks; - BitField<5, 1, u64> extended_dynamic_state; - BitField<6, 1, u64> extended_dynamic_state_2; - BitField<7, 1, u64> extended_dynamic_state_2_logic_op; - BitField<8, 1, u64> extended_dynamic_state_3_blend; - BitField<9, 1, u64> extended_dynamic_state_3_enables; - BitField<10, 1, u64> dynamic_vertex_input; - BitField<11, 1, u64> xfb_enabled; - BitField<12, 1, u64> ndc_minus_one_to_one; - BitField<13, 2, u64> polygon_mode; - BitField<15, 2, u64> tessellation_primitive; - BitField<17, 2, u64> tessellation_spacing; - BitField<19, 1, u64> tessellation_clockwise; - BitField<20, 5, u64> patch_control_points_minus_one; + u32 raw1; + BitField<0, 1, u32> core_dynamic_viewport_scissor; + BitField<1, 1, u32> core_dynamic_depth_bias; + BitField<2, 1, u32> core_dynamic_depth_bounds; + BitField<3, 1, u32> core_dynamic_line_width; + BitField<4, 1, u32> core_dynamic_stencil_masks; + BitField<5, 1, u32> extended_dynamic_state; + BitField<6, 1, u32> extended_dynamic_state_2; + BitField<7, 1, u32> extended_dynamic_state_2_logic_op; + BitField<8, 1, u32> extended_dynamic_state_3_blend; + BitField<9, 1, u32> extended_dynamic_state_3_enables; + BitField<10, 1, u32> dynamic_vertex_input; + BitField<11, 1, u32> xfb_enabled; + BitField<12, 1, u32> ndc_minus_one_to_one; + BitField<13, 2, u32> polygon_mode; + BitField<15, 2, u32> tessellation_primitive; + BitField<17, 2, u32> tessellation_spacing; + BitField<19, 1, u32> tessellation_clockwise; + BitField<20, 5, u32> patch_control_points_minus_one; BitField<25, 3, Maxwell::PrimitiveTopology> topology; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; }; From e776e337f2318ce5082b5b1ea13397b15a3d7a3a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 18:50:32 -0400 Subject: [PATCH 25/31] Revert "fix comp err" This reverts commit 470faa62551294e8073478552ccec437b0d4a9c9. --- .../renderer_vulkan/fixed_pipeline_state.h | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 6344fe6eb7..cd1d9017dc 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,41 +21,41 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { - bool has_core_dynamic_viewport_scissor : 1; - bool has_core_dynamic_depth_bias : 1; - bool has_core_dynamic_depth_bounds : 1; - bool has_core_dynamic_line_width : 1; - bool has_core_dynamic_stencil_masks : 1; - bool has_extended_dynamic_state : 1; - bool has_eds1_cull_mode : 1; - bool has_eds1_front_face : 1; - bool has_eds1_depth_test_enable : 1; - bool has_eds1_depth_write_enable : 1; - bool has_eds1_depth_compare_op : 1; - bool has_eds1_depth_bounds_test_enable : 1; - bool has_eds1_stencil_test_enable : 1; - bool has_eds1_stencil_op : 1; - bool has_eds1_vertex_input_binding_stride : 1; - bool has_extended_dynamic_state_2 : 1; - bool has_eds2_depth_bias_enable : 1; - bool has_eds2_primitive_restart_enable : 1; - bool has_eds2_rasterizer_discard_enable : 1; - bool has_extended_dynamic_state_2_logic_op : 1; - bool has_extended_dynamic_state_2_patch_control_points : 1; - bool has_extended_dynamic_state_3_blend : 1; - bool has_extended_dynamic_state_3_enables : 1; - bool has_dynamic_state3_depth_clamp_enable : 1; - bool has_dynamic_state3_logic_op_enable : 1; - bool has_dynamic_state3_line_stipple_enable : 1; - bool has_dynamic_state3_alpha_to_coverage_enable : 1; - bool has_dynamic_state3_alpha_to_one_enable : 1; - bool has_dynamic_state3_line_rasterization_mode : 1; - bool has_dynamic_state3_conservative_rasterization_mode : 1; - bool has_dynamic_vertex_input : 1; - bool has_provoking_vertex : 1; - bool has_provoking_vertex_first_mode : 1; - bool has_provoking_vertex_last_mode : 1; - bool has_provoking_vertex_tf_preserve : 1; + bool has_core_dynamic_viewport_scissor; + bool has_core_dynamic_depth_bias; + bool has_core_dynamic_depth_bounds; + bool has_core_dynamic_line_width; + bool has_core_dynamic_stencil_masks; + bool has_extended_dynamic_state; + bool has_eds1_cull_mode; + bool has_eds1_front_face; + bool has_eds1_depth_test_enable; + bool has_eds1_depth_write_enable; + bool has_eds1_depth_compare_op; + bool has_eds1_depth_bounds_test_enable; + bool has_eds1_stencil_test_enable; + bool has_eds1_stencil_op; + bool has_eds1_vertex_input_binding_stride; + bool has_extended_dynamic_state_2; + bool has_eds2_depth_bias_enable; + bool has_eds2_primitive_restart_enable; + bool has_eds2_rasterizer_discard_enable; + bool has_extended_dynamic_state_2_logic_op; + bool has_extended_dynamic_state_2_patch_control_points; + bool has_extended_dynamic_state_3_blend; + bool has_extended_dynamic_state_3_enables; + bool has_dynamic_state3_depth_clamp_enable; + bool has_dynamic_state3_logic_op_enable; + bool has_dynamic_state3_line_stipple_enable; + bool has_dynamic_state3_alpha_to_coverage_enable; + bool has_dynamic_state3_alpha_to_one_enable; + bool has_dynamic_state3_line_rasterization_mode; + bool has_dynamic_state3_conservative_rasterization_mode; + bool has_dynamic_vertex_input; + bool has_provoking_vertex; + bool has_provoking_vertex_first_mode; + bool has_provoking_vertex_last_mode; + bool has_provoking_vertex_tf_preserve; }; struct FixedPipelineState { @@ -215,25 +215,25 @@ struct FixedPipelineState { }; union { - u32 raw1; - BitField<0, 1, u32> core_dynamic_viewport_scissor; - BitField<1, 1, u32> core_dynamic_depth_bias; - BitField<2, 1, u32> core_dynamic_depth_bounds; - BitField<3, 1, u32> core_dynamic_line_width; - BitField<4, 1, u32> core_dynamic_stencil_masks; - BitField<5, 1, u32> extended_dynamic_state; - BitField<6, 1, u32> extended_dynamic_state_2; - BitField<7, 1, u32> extended_dynamic_state_2_logic_op; - BitField<8, 1, u32> extended_dynamic_state_3_blend; - BitField<9, 1, u32> extended_dynamic_state_3_enables; - BitField<10, 1, u32> dynamic_vertex_input; - BitField<11, 1, u32> xfb_enabled; - BitField<12, 1, u32> ndc_minus_one_to_one; - BitField<13, 2, u32> polygon_mode; - BitField<15, 2, u32> tessellation_primitive; - BitField<17, 2, u32> tessellation_spacing; - BitField<19, 1, u32> tessellation_clockwise; - BitField<20, 5, u32> patch_control_points_minus_one; + u64 raw1; + BitField<0, 1, u64> core_dynamic_viewport_scissor; + BitField<1, 1, u64> core_dynamic_depth_bias; + BitField<2, 1, u64> core_dynamic_depth_bounds; + BitField<3, 1, u64> core_dynamic_line_width; + BitField<4, 1, u64> core_dynamic_stencil_masks; + BitField<5, 1, u64> extended_dynamic_state; + BitField<6, 1, u64> extended_dynamic_state_2; + BitField<7, 1, u64> extended_dynamic_state_2_logic_op; + BitField<8, 1, u64> extended_dynamic_state_3_blend; + BitField<9, 1, u64> extended_dynamic_state_3_enables; + BitField<10, 1, u64> dynamic_vertex_input; + BitField<11, 1, u64> xfb_enabled; + BitField<12, 1, u64> ndc_minus_one_to_one; + BitField<13, 2, u64> polygon_mode; + BitField<15, 2, u64> tessellation_primitive; + BitField<17, 2, u64> tessellation_spacing; + BitField<19, 1, u64> tessellation_clockwise; + BitField<20, 5, u64> patch_control_points_minus_one; BitField<25, 3, Maxwell::PrimitiveTopology> topology; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; }; From 6770330d8e3c73e1fc77e71f0056b7a549ce3d76 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 18:52:31 -0400 Subject: [PATCH 26/31] Revert "[vulkan] Adjusted pipeline keys inside patchControlPoints" --- src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp | 2 +- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 31f8cab38f..8f231a4352 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -677,7 +677,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, - .patchControlPoints = static_cast(key.state.patch_control_points_minus_one.Value()) + 1, + .patchControlPoints = key.state.patch_control_points_minus_one.Value() + 1, }; std::array swizzles; std::ranges::transform(key.state.viewport_swizzles, swizzles.begin(), UnpackViewportSwizzle); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index e14d037858..bcfc4b8b4d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -195,7 +195,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program case Shader::Stage::TessellationEval: info.tess_clockwise = key.state.tessellation_clockwise != 0; info.tess_primitive = [&key] { - const u32 raw = static_cast(key.state.tessellation_primitive.Value()); + const u32 raw{key.state.tessellation_primitive.Value()}; switch (static_cast(raw)) { case Maxwell::Tessellation::DomainType::Isolines: return Shader::TessPrimitive::Isolines; @@ -208,7 +208,7 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span program return Shader::TessPrimitive::Triangles; }(); info.tess_spacing = [&] { - const u32 raw = static_cast(key.state.tessellation_spacing.Value()); + const u32 raw{key.state.tessellation_spacing}; switch (static_cast(raw)) { case Maxwell::Tessellation::Spacing::Integer: return Shader::TessSpacing::Equal; From 22bd48273226159feb2d84c8fccdbdac8570fba1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 18:53:18 -0400 Subject: [PATCH 27/31] Revert "[vulkan] Adjustment ExtendedDynamicState" --- .../renderer_vulkan/fixed_pipeline_state.h | 4 +-- .../vulkan_common/vulkan_device.cpp | 30 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index cd1d9017dc..28fe2ccb5c 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -234,8 +234,8 @@ struct FixedPipelineState { BitField<17, 2, u64> tessellation_spacing; BitField<19, 1, u64> tessellation_clockwise; BitField<20, 5, u64> patch_control_points_minus_one; - BitField<25, 3, Maxwell::PrimitiveTopology> topology; - BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; + BitField<25, 4, Maxwell::PrimitiveTopology> topology; + BitField<29, 4, Tegra::Texture::MsaaMode> msaa_mode; }; union { u32 raw2; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 31ae345f1e..ba088441d2 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -664,18 +664,18 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR core_dynamic_depth_bias = (dld.vkCmdSetDepthBias != nullptr) || (dld.vkCmdSetDepthBias2EXT != nullptr) || (features.depth_bias_control.depthBiasControl != VK_FALSE); - if (features.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { - features.features.depthBounds = VK_FALSE; + if (features.core.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { + features.core.features.depthBounds = VK_FALSE; core_dynamic_depth_bounds = false; } else { - core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.features.depthBounds; + core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.core.features.depthBounds; } - if (features.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { - features.features.wideLines = VK_FALSE; + if (features.core.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { + features.core.features.wideLines = VK_FALSE; core_dynamic_line_width = false; } else { - core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.features.wideLines; + core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.core.features.wideLines; } core_dynamic_stencil_masks = (dld.vkCmdSetStencilCompareMask != nullptr) && @@ -684,7 +684,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR break; case Settings::ExtendedDynamicState::EDS1: // Level 1: Enable EDS1, disable EDS2 and EDS3 - if (dyna_state != Settings::ExtendedDynamicState::Disabled && extensions.extended_dynamic_state) { + if (dyna_state_enabled && extensions.extended_dynamic_state) { eds1_cull_mode = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetCullModeEXT != nullptr); eds1_front_face = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetFrontFaceEXT != nullptr); eds1_depth_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthTestEnableEXT != nullptr); @@ -694,24 +694,24 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR eds1_stencil_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilTestEnableEXT != nullptr); eds1_stencil_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilOpEXT != nullptr); - eds1_vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); + vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); const bool eds1_any = eds1_cull_mode || eds1_front_face || eds1_depth_test_enable || eds1_depth_write_enable || eds1_depth_compare_op || eds1_depth_bounds_test_enable || - eds1_stencil_test_enable || eds1_stencil_op || eds1_vertex_input_binding_stride; + eds1_stencil_test_enable || eds1_stencil_op || vertex_input_binding_stride; if (!eds1_any) { extensions.extended_dynamic_state = false; RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); // Clear flags eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; - eds1_vertex_input_binding_stride = false; + vertex_input_binding_stride = false; } } else { extensions.extended_dynamic_state = false; eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; - eds1_vertex_input_binding_stride = false; + vertex_input_binding_stride = false; } RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); @@ -729,7 +729,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR extensions.extended_dynamic_state2 = false; } } - if (dyna_state != Settings::ExtendedDynamicState::Disabled && extensions.extended_dynamic_state2) { + if (dyna_state_enabled && extensions.extended_dynamic_state2) { eds2_depth_bias_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetDepthBiasEnableEXT != nullptr); eds2_primitive_restart_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr); eds2_rasterizer_discard_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetRasterizerDiscardEnableEXT != nullptr); @@ -766,7 +766,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR extensions.extended_dynamic_state3 = false; } } - if (dyna_state != Settings::ExtendedDynamicState::Disabled && extensions.extended_dynamic_state3) { + if (dyna_state_enabled && extensions.extended_dynamic_state3) { if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && (dld.vkCmdSetColorBlendEnableEXT != nullptr))) { features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = VK_FALSE; } @@ -833,10 +833,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } if (vids_entrypoint && features.vertex_input_dynamic_state.vertexInputDynamicState) { - eds1_vertex_input_binding_stride = false; + vertex_input_binding_stride = false; } else { // Only enable binding stride if EDS1 and the VB2 entrypoint are available. - eds1_vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; + vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; } if (!features.vertex_input_dynamic_state.vertexInputDynamicState) { From d6105c52225bf70f1943c7b46e1d5c636541841d Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 18:54:10 -0400 Subject: [PATCH 28/31] Revert "[vulkan] Rework DynamicState - ExtendedDynamicState" --- .../renderer_vulkan/fixed_pipeline_state.cpp | 55 +----- .../renderer_vulkan/fixed_pipeline_state.h | 59 ++---- .../renderer_vulkan/vk_graphics_pipeline.cpp | 106 ++++------- .../renderer_vulkan/vk_pipeline_cache.cpp | 71 ++----- .../vulkan_common/vulkan_device.cpp | 173 +----------------- src/video_core/vulkan_common/vulkan_device.h | 114 +----------- 6 files changed, 92 insertions(+), 486 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 67d3affacb..d53cf70b37 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -57,11 +57,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; raw1 = 0; - core_dynamic_viewport_scissor.Assign(features.has_core_dynamic_viewport_scissor ? 1 : 0); - core_dynamic_depth_bias.Assign(features.has_core_dynamic_depth_bias ? 1 : 0); - core_dynamic_depth_bounds.Assign(features.has_core_dynamic_depth_bounds ? 1 : 0); - core_dynamic_line_width.Assign(features.has_core_dynamic_line_width ? 1 : 0); - core_dynamic_stencil_masks.Assign(features.has_core_dynamic_stencil_masks ? 1 : 0); extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0); extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0); extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0); @@ -81,7 +76,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe raw2 = 0; - const auto test_func = regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); @@ -112,18 +106,10 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } provoking_vertex_last.Assign(use_last_provoking_vertex ? 1 : 0); - if (!features.has_dynamic_state3_conservative_rasterization_mode) { - conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); - } - if (!features.has_dynamic_state3_line_rasterization_mode) { - smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); - } - if (!features.has_dynamic_state3_alpha_to_coverage_enable) { - alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); - } - if (!features.has_dynamic_state3_alpha_to_one_enable) { - alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); - } + conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); + smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); + alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); + alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); app_stage.Assign(maxwell3d.engine_state); depth_bounds_min = static_cast(regs.depth_bounds[0]); @@ -187,49 +173,24 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } dynamic_state.raw1 = 0; dynamic_state.raw2 = 0; - - const bool any_eds1_baked = !features.has_extended_dynamic_state || - !features.has_eds1_cull_mode || !features.has_eds1_front_face || - !features.has_eds1_depth_test_enable || !features.has_eds1_depth_write_enable || - !features.has_eds1_depth_compare_op || - !(features.has_eds1_depth_bounds_test_enable || features.has_core_dynamic_depth_bounds) || - !features.has_eds1_stencil_test_enable || !features.has_eds1_stencil_op; - - if (any_eds1_baked) { + if (!extended_dynamic_state) { dynamic_state.Refresh(regs); - } - - if (!features.has_extended_dynamic_state || !features.has_eds1_vertex_input_binding_stride) { std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { return static_cast(array.stride.Value()); }); } - - const bool any_eds2_baked = !features.has_extended_dynamic_state_2 || - !(features.has_eds2_depth_bias_enable || features.has_core_dynamic_depth_bias) || - !features.has_eds2_primitive_restart_enable || - !features.has_eds2_rasterizer_discard_enable || !features.has_extended_dynamic_state_2_logic_op; - - if (any_eds2_baked) { + if (!extended_dynamic_state_2_logic_op) { dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); } - - const bool any_eds3_baked = !features.has_extended_dynamic_state_3_blend || - !features.has_dynamic_state3_depth_clamp_enable || !features.has_dynamic_state3_logic_op_enable || - !features.has_dynamic_state3_line_stipple_enable || !features.has_dynamic_state3_alpha_to_coverage_enable || - !features.has_dynamic_state3_alpha_to_one_enable || !features.has_dynamic_state3_line_rasterization_mode || - !features.has_dynamic_state3_conservative_rasterization_mode; - - if (any_eds3_baked) { + if (!extended_dynamic_state_3_blend) { if (maxwell3d.dirty.flags[Dirty::Blending]) { maxwell3d.dirty.flags[Dirty::Blending] = false; for (size_t index = 0; index < attachments.size(); ++index) { attachments[index].Refresh(regs, index); } } - dynamic_state.Refresh3(regs, features); } - + dynamic_state.Refresh3(regs, features); if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 28fe2ccb5c..44157d686d 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,25 +21,8 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { - bool has_core_dynamic_viewport_scissor; - bool has_core_dynamic_depth_bias; - bool has_core_dynamic_depth_bounds; - bool has_core_dynamic_line_width; - bool has_core_dynamic_stencil_masks; bool has_extended_dynamic_state; - bool has_eds1_cull_mode; - bool has_eds1_front_face; - bool has_eds1_depth_test_enable; - bool has_eds1_depth_write_enable; - bool has_eds1_depth_compare_op; - bool has_eds1_depth_bounds_test_enable; - bool has_eds1_stencil_test_enable; - bool has_eds1_stencil_op; - bool has_eds1_vertex_input_binding_stride; bool has_extended_dynamic_state_2; - bool has_eds2_depth_bias_enable; - bool has_eds2_primitive_restart_enable; - bool has_eds2_rasterizer_discard_enable; bool has_extended_dynamic_state_2_logic_op; bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_3_blend; @@ -47,10 +30,6 @@ struct DynamicFeatures { bool has_dynamic_state3_depth_clamp_enable; bool has_dynamic_state3_logic_op_enable; bool has_dynamic_state3_line_stipple_enable; - bool has_dynamic_state3_alpha_to_coverage_enable; - bool has_dynamic_state3_alpha_to_one_enable; - bool has_dynamic_state3_line_rasterization_mode; - bool has_dynamic_state3_conservative_rasterization_mode; bool has_dynamic_vertex_input; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; @@ -215,27 +194,23 @@ struct FixedPipelineState { }; union { - u64 raw1; - BitField<0, 1, u64> core_dynamic_viewport_scissor; - BitField<1, 1, u64> core_dynamic_depth_bias; - BitField<2, 1, u64> core_dynamic_depth_bounds; - BitField<3, 1, u64> core_dynamic_line_width; - BitField<4, 1, u64> core_dynamic_stencil_masks; - BitField<5, 1, u64> extended_dynamic_state; - BitField<6, 1, u64> extended_dynamic_state_2; - BitField<7, 1, u64> extended_dynamic_state_2_logic_op; - BitField<8, 1, u64> extended_dynamic_state_3_blend; - BitField<9, 1, u64> extended_dynamic_state_3_enables; - BitField<10, 1, u64> dynamic_vertex_input; - BitField<11, 1, u64> xfb_enabled; - BitField<12, 1, u64> ndc_minus_one_to_one; - BitField<13, 2, u64> polygon_mode; - BitField<15, 2, u64> tessellation_primitive; - BitField<17, 2, u64> tessellation_spacing; - BitField<19, 1, u64> tessellation_clockwise; - BitField<20, 5, u64> patch_control_points_minus_one; - BitField<25, 4, Maxwell::PrimitiveTopology> topology; - BitField<29, 4, Tegra::Texture::MsaaMode> msaa_mode; + u32 raw1; + BitField<0, 1, u32> extended_dynamic_state; + BitField<1, 1, u32> extended_dynamic_state_2; + BitField<2, 1, u32> extended_dynamic_state_2_logic_op; + BitField<3, 1, u32> extended_dynamic_state_3_blend; + BitField<4, 1, u32> extended_dynamic_state_3_enables; + BitField<5, 1, u32> dynamic_vertex_input; + BitField<6, 1, u32> xfb_enabled; + BitField<7, 1, u32> ndc_minus_one_to_one; + BitField<8, 2, u32> polygon_mode; + BitField<10, 2, u32> tessellation_primitive; + BitField<12, 2, u32> tessellation_spacing; + BitField<14, 1, u32> tessellation_clockwise; + BitField<15, 5, u32> patch_control_points_minus_one; + + BitField<24, 4, Maxwell::PrimitiveTopology> topology; + BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; }; union { u32 raw2; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8f231a4352..43fbefe425 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -848,94 +848,62 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{}; - // Core dynamic states (Vulkan 1.0) - if (device.SupportsCoreDynamicViewportScissor()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); - dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); - } - if (device.SupportsCoreDynamicDepthBias()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); - } - // Blend constants are commonly available; keep as default dynamic state. - dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); - if (device.SupportsCoreDynamicDepthBounds()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); - } - if (device.SupportsCoreDynamicStencilMasks()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); - } - if (device.SupportsCoreDynamicLineWidth()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); - } + static_vector dynamic_states{ + VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, + VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, + VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, + VK_DYNAMIC_STATE_LINE_WIDTH, + }; if (key.state.extended_dynamic_state) { - if (device.SupportsEds1CullMode()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_CULL_MODE_EXT); - } - if (device.SupportsEds1FrontFace()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_FRONT_FACE_EXT); - } - if (device.SupportsEds1DepthTestEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT); - } - if (device.SupportsEds1DepthWriteEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT); - } - if (device.SupportsEds1DepthCompareOp()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT); - } - if (device.SupportsEds1DepthBoundsTestEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT); - } - if (device.SupportsEds1StencilTestEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT); - } - if (device.SupportsEds1StencilOp()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_OP_EXT); - } + static constexpr std::array extended{ + VK_DYNAMIC_STATE_CULL_MODE_EXT, + VK_DYNAMIC_STATE_FRONT_FACE_EXT, + VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, + VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, + VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, + VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, + VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, + VK_DYNAMIC_STATE_STENCIL_OP_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); - // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT - if (!key.state.dynamic_vertex_input && device.SupportsEds1VertexInputBindingStride()) { + // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1 + // Only use it if VIDS is not active (VIDS replaces it with full vertex input control) + if (!key.state.dynamic_vertex_input) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); } } - // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) replaces VERTEX_INPUT_BINDING_STRIDE + // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS + // Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE if (key.state.dynamic_vertex_input) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); } - // EDS2 - Core (3 states) — include only supported subfeatures + // EDS2 - Core (3 states) if (key.state.extended_dynamic_state_2) { - if (device.SupportsEds2DepthBiasEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT); - } - if (device.SupportsEds2PrimitiveRestartEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT); - } - if (device.SupportsEds2RasterizerDiscardEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT); - } + static constexpr std::array extended2{ + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end()); } // EDS2 - LogicOp (granular) - if (key.state.extended_dynamic_state_2_logic_op && device.SupportsDynamicState2LogicOp()) { + if (key.state.extended_dynamic_state_2_logic_op) { dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); } // EDS3 - Blending (composite: 3 states) if (key.state.extended_dynamic_state_3_blend) { - if (device.SupportsDynamicState3ColorBlendEnable()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT); - } - if (device.SupportsDynamicState3ColorBlendEquation()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT); - } - if (device.SupportsDynamicState3ColorWriteMask()) { - dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT); - } + static constexpr std::array extended3{ + VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, + VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, + VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); } // EDS3 - Enables (composite: per-feature) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index bcfc4b8b4d..7b5ac7fac0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -58,7 +58,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 17; +constexpr u32 CACHE_VERSION = 16; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template @@ -465,46 +465,18 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, dynamic_features = {}; - dynamic_features.has_core_dynamic_viewport_scissor = - device.SupportsCoreDynamicViewportScissor(); - dynamic_features.has_core_dynamic_depth_bias = - device.SupportsCoreDynamicDepthBias(); - dynamic_features.has_core_dynamic_depth_bounds = - device.SupportsCoreDynamicDepthBounds(); - dynamic_features.has_core_dynamic_line_width = - device.SupportsCoreDynamicLineWidth(); - dynamic_features.has_core_dynamic_stencil_masks = - device.SupportsCoreDynamicStencilMasks(); + // User granularity enforced in vulkan_device.cpp switch statement: + // Level 0: Core Dynamic States only + // Level 1: Core + EDS1 + // Level 2: Core + EDS1 + EDS2 (accumulative) + // Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative) + // Here we only verify if extensions were successfully loaded by the device dynamic_features.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(); - dynamic_features.has_eds1_cull_mode = - device.SupportsEds1CullMode(); - dynamic_features.has_eds1_front_face = - device.SupportsEds1FrontFace(); - dynamic_features.has_eds1_depth_test_enable = - device.SupportsEds1DepthTestEnable(); - dynamic_features.has_eds1_depth_write_enable = - device.SupportsEds1DepthWriteEnable(); - dynamic_features.has_eds1_depth_compare_op = - device.SupportsEds1DepthCompareOp(); - dynamic_features.has_eds1_depth_bounds_test_enable = - device.SupportsEds1DepthBoundsTestEnable(); - dynamic_features.has_eds1_stencil_test_enable = - device.SupportsEds1StencilTestEnable(); - dynamic_features.has_eds1_stencil_op = - device.SupportsEds1StencilOp(); - dynamic_features.has_eds1_vertex_input_binding_stride = - device.SupportsEds1VertexInputBindingStride(); dynamic_features.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(); - dynamic_features.has_eds2_depth_bias_enable = - device.SupportsEds2DepthBiasEnable(); - dynamic_features.has_eds2_primitive_restart_enable = - device.SupportsEds2PrimitiveRestartEnable(); - dynamic_features.has_eds2_rasterizer_discard_enable = - device.SupportsEds2RasterizerDiscardEnable(); dynamic_features.has_extended_dynamic_state_2_logic_op = device.IsExtExtendedDynamicState2ExtrasSupported(); dynamic_features.has_extended_dynamic_state_2_patch_control_points = false; @@ -519,21 +491,13 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.SupportsDynamicState3LogicOpEnable(); dynamic_features.has_dynamic_state3_line_stipple_enable = device.SupportsDynamicState3LineStippleEnable(); - dynamic_features.has_dynamic_state3_alpha_to_coverage_enable = - device.SupportsDynamicState3AlphaToCoverageEnable(); - dynamic_features.has_dynamic_state3_alpha_to_one_enable = - device.SupportsDynamicState3AlphaToOneEnable(); - dynamic_features.has_dynamic_state3_line_rasterization_mode = - device.SupportsDynamicState3LineRasterizationMode(); - dynamic_features.has_dynamic_state3_conservative_rasterization_mode = - device.SupportsDynamicState3ConservativeRasterizationMode(); + // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && Settings::values.vertex_input_dynamic_state.GetValue(); - dynamic_features.has_provoking_vertex = - device.IsExtProvokingVertexSupported(); + dynamic_features.has_provoking_vertex = device.IsExtProvokingVertexSupported(); dynamic_features.has_provoking_vertex_first_mode = device.SupportsProvokingVertexFirstMode(); dynamic_features.has_provoking_vertex_last_mode = @@ -640,20 +604,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading GraphicsPipelineCacheKey key; file.read(reinterpret_cast(&key), sizeof(key)); - if ((key.state.core_dynamic_viewport_scissor != 0) != - dynamic_features.has_core_dynamic_viewport_scissor || - (key.state.core_dynamic_depth_bias != 0) != - dynamic_features.has_core_dynamic_depth_bias || - (key.state.core_dynamic_depth_bounds != 0) != - dynamic_features.has_core_dynamic_depth_bounds || - (key.state.core_dynamic_line_width != 0) != - dynamic_features.has_core_dynamic_line_width || - (key.state.core_dynamic_stencil_masks != 0) != - dynamic_features.has_core_dynamic_stencil_masks || - (key.state.extended_dynamic_state != 0) != + if ((key.state.extended_dynamic_state != 0) != dynamic_features.has_extended_dynamic_state || - (key.state.dynamic_vertex_input != 0) != - dynamic_features.has_dynamic_vertex_input || (key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 || (key.state.extended_dynamic_state_2_logic_op != 0) != @@ -661,7 +613,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading (key.state.extended_dynamic_state_3_blend != 0) != dynamic_features.has_extended_dynamic_state_3_blend || (key.state.extended_dynamic_state_3_enables != 0) != - dynamic_features.has_extended_dynamic_state_3_enables) { + dynamic_features.has_extended_dynamic_state_3_enables || + (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { return; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index ba088441d2..f48fe39e4e 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -648,9 +648,16 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const auto dyna_state = Settings::values.dyna_state.GetValue(); + // Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp + // This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs: + // Level 0 = Core Dynamic States only (Vulkan 1.0) + // Level 1 = Core + VK_EXT_extended_dynamic_state + // Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + // Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3 + switch (dyna_state) { case Settings::ExtendedDynamicState::Disabled: - // Level 0: Enable DynamicState (Static) + Disable all extended dynamic state extensions + // Level 0: Disable all extended dynamic state extensions RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, @@ -659,60 +666,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; dynamic_state3_enables = false; - - core_dynamic_viewport_scissor = (dld.vkCmdSetViewport != nullptr) && (dld.vkCmdSetScissor != nullptr); - core_dynamic_depth_bias = (dld.vkCmdSetDepthBias != nullptr) || (dld.vkCmdSetDepthBias2EXT != nullptr) || - (features.depth_bias_control.depthBiasControl != VK_FALSE); - - if (features.core.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { - features.core.features.depthBounds = VK_FALSE; - core_dynamic_depth_bounds = false; - } else { - core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.core.features.depthBounds; - } - - if (features.core.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { - features.core.features.wideLines = VK_FALSE; - core_dynamic_line_width = false; - } else { - core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.core.features.wideLines; - } - - core_dynamic_stencil_masks = (dld.vkCmdSetStencilCompareMask != nullptr) && - (dld.vkCmdSetStencilWriteMask != nullptr) && - (dld.vkCmdSetStencilReference != nullptr); break; case Settings::ExtendedDynamicState::EDS1: // Level 1: Enable EDS1, disable EDS2 and EDS3 - if (dyna_state_enabled && extensions.extended_dynamic_state) { - eds1_cull_mode = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetCullModeEXT != nullptr); - eds1_front_face = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetFrontFaceEXT != nullptr); - eds1_depth_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthTestEnableEXT != nullptr); - eds1_depth_write_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthWriteEnableEXT != nullptr); - eds1_depth_compare_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthCompareOpEXT != nullptr); - eds1_depth_bounds_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthBoundsTestEnableEXT != nullptr); - eds1_stencil_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilTestEnableEXT != nullptr); - eds1_stencil_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilOpEXT != nullptr); - - vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); - - const bool eds1_any = eds1_cull_mode || eds1_front_face || eds1_depth_test_enable || - eds1_depth_write_enable || eds1_depth_compare_op || eds1_depth_bounds_test_enable || - eds1_stencil_test_enable || eds1_stencil_op || vertex_input_binding_stride; - if (!eds1_any) { - extensions.extended_dynamic_state = false; - RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - // Clear flags - eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; - eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; - vertex_input_binding_stride = false; - } - } else { - extensions.extended_dynamic_state = false; - eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; - eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; - vertex_input_binding_stride = false; - } RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, @@ -722,32 +678,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR break; case Settings::ExtendedDynamicState::EDS2: // Level 2: Enable EDS1 + EDS2, disable EDS3 - { - bool eds1_any = (features.extended_dynamic_state.extendedDynamicState && - ((dld.vkCmdBindVertexBuffers2EXT != nullptr) || (dld.vkCmdSetCullModeEXT != nullptr))); - if (!eds1_any) { - extensions.extended_dynamic_state2 = false; - } - } - if (dyna_state_enabled && extensions.extended_dynamic_state2) { - eds2_depth_bias_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetDepthBiasEnableEXT != nullptr); - eds2_primitive_restart_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr); - eds2_rasterizer_discard_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetRasterizerDiscardEnableEXT != nullptr); - - if (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT == nullptr)) { - features.extended_dynamic_state2.extendedDynamicState2LogicOp = VK_FALSE; - } - - const bool eds2_any = eds2_depth_bias_enable || eds2_primitive_restart_enable || eds2_rasterizer_discard_enable || - (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT != nullptr)); - if (!eds2_any) { - extensions.extended_dynamic_state2 = false; - RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - eds2_depth_bias_enable = eds2_primitive_restart_enable = eds2_rasterizer_discard_enable = false; - } - } else { - extensions.extended_dynamic_state2 = false; - } RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; @@ -756,96 +686,13 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR case Settings::ExtendedDynamicState::EDS3: default: // Level 3: Enable all (EDS1 + EDS2 + EDS3) - { - bool eds1_any = (features.extended_dynamic_state.extendedDynamicState && - ((dld.vkCmdBindVertexBuffers2EXT != nullptr) || (dld.vkCmdSetCullModeEXT != nullptr))); - bool eds2_any = ((features.extended_dynamic_state2.extendedDynamicState2 && - ((dld.vkCmdSetDepthBiasEnableEXT != nullptr) || (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr))) || - (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT != nullptr))); - if (!(eds1_any || eds2_any)) { - extensions.extended_dynamic_state3 = false; - } - } - if (dyna_state_enabled && extensions.extended_dynamic_state3) { - if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && (dld.vkCmdSetColorBlendEnableEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation && (dld.vkCmdSetColorBlendEquationEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask && (dld.vkCmdSetColorWriteMaskEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable && (dld.vkCmdSetDepthClampEnableEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable && (dld.vkCmdSetLogicOpEnableEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable && (dld.vkCmdSetAlphaToCoverageEnableEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable && (dld.vkCmdSetAlphaToOneEnableEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode && (dld.vkCmdSetLineRasterizationModeEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode && (dld.vkCmdSetConservativeRasterizationModeEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode = VK_FALSE; - } - if (!(features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable && (dld.vkCmdSetLineStippleEnableEXT != nullptr))) { - features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable = VK_FALSE; - } - - bool blending = features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable || - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation || - features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; - bool enables = features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable || - features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable || - features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable || - features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable || - features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable || - features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode || - features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode; - if (!blending && !enables) { - extensions.extended_dynamic_state3 = false; - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - } else { - dynamic_state3_blending = blending; - dynamic_state3_enables = enables; - } - } else { - extensions.extended_dynamic_state3 = false; - } break; } - // VK_EXT_vertex_input_dynamic_state and features.vertex_input_binding_stride + // VK_EXT_vertex_input_dynamic_state is independent from EDS + // It can be enabled even without extended_dynamic_state if (!Settings::values.vertex_input_dynamic_state.GetValue()) { RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); - } else { - bool vids_entrypoint = dld.vkCmdSetVertexInputEXT != nullptr; - bool vb2_entrypoint = dld.vkCmdBindVertexBuffers2EXT != nullptr; - - if (!vids_entrypoint) { - features.vertex_input_dynamic_state.vertexInputDynamicState = VK_FALSE; - } - - if (vids_entrypoint && features.vertex_input_dynamic_state.vertexInputDynamicState) { - vertex_input_binding_stride = false; - } else { - // Only enable binding stride if EDS1 and the VB2 entrypoint are available. - vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; - } - - if (!features.vertex_input_dynamic_state.vertexInputDynamicState) { - extensions.vertex_input_dynamic_state = false; - } - } - - if (!features.extended_dynamic_state.extendedDynamicState) { - extensions.extended_dynamic_state = false; } logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index d435126a2f..cf341726a7 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -569,112 +569,31 @@ public: return features.custom_border_color.customBorderColorWithoutFormat; } - /// Core dynamic states availability (Vulkan 1.0) - bool SupportsCoreDynamicViewportScissor() const { - return core_dynamic_viewport_scissor; - } - bool SupportsCoreDynamicDepthBias() const { - return core_dynamic_depth_bias; - } - bool SupportsCoreDynamicDepthBounds() const { - return core_dynamic_depth_bounds; - } - bool SupportsCoreDynamicLineWidth() const { - return core_dynamic_line_width; - } - bool SupportsCoreDynamicStencilMasks() const { - return core_dynamic_stencil_masks; - } - /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; } - /// VK_EXT_extended_dynamic_state core dynamic states availability - bool SupportsEds1CullMode() const { - return eds1_cull_mode; - } - bool SupportsEds1FrontFace() const { - return eds1_front_face; - } - bool SupportsEds1DepthTestEnable() const { - return eds1_depth_test_enable; - } - bool SupportsEds1DepthWriteEnable() const { - return eds1_depth_write_enable; - } - bool SupportsEds1DepthCompareOp() const { - return eds1_depth_compare_op; - } - bool SupportsEds1DepthBoundsTestEnable() const { - return eds1_depth_bounds_test_enable; - } - bool SupportsEds1StencilTestEnable() const { - return eds1_stencil_test_enable; - } - bool SupportsEds1StencilOp() const { - return eds1_stencil_op; - } - bool SupportsEds1VertexInputBindingStride() const { - return eds1_vertex_input_binding_stride; - } - /// Returns true if the device supports VK_EXT_extended_dynamic_state2. bool IsExtExtendedDynamicState2Supported() const { return extensions.extended_dynamic_state2; } - /// VK_EXT_extended_dynamic_state2 core dynamic states availability - bool SupportsEds2DepthBiasEnable() const { - return eds2_depth_bias_enable; - } - bool SupportsEds2PrimitiveRestartEnable() const { - return eds2_primitive_restart_enable; - } - bool SupportsEds2RasterizerDiscardEnable() const { - return eds2_rasterizer_discard_enable; - } - bool IsExtExtendedDynamicState2ExtrasSupported() const { return features.extended_dynamic_state2.extendedDynamicState2LogicOp; } - bool SupportsDynamicState2LogicOp() const { - return extensions.extended_dynamic_state2 && - features.extended_dynamic_state2.extendedDynamicState2LogicOp; - } - - /// Returns true if the device supports VK_EXT_extended_dynamic_state3. bool IsExtExtendedDynamicState3Supported() const { return extensions.extended_dynamic_state3; } - /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color blend enable. - bool SupportsDynamicState3ColorBlendEnable() const { - return extensions.extended_dynamic_state3 && - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable; - } - - /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color blend equation. - bool SupportsDynamicState3ColorBlendEquation() const { - return extensions.extended_dynamic_state3 && - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation; - } - - /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color write mask. - bool SupportsDynamicState3ColorWriteMask() const { - return extensions.extended_dynamic_state3 && - features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; - } - /// Returns true if the device supports VK_EXT_4444_formats. bool IsExt4444FormatsSupported() const { return features.format_a4b4g4r4.formatA4B4G4R4; } - /// Returns true if the device supports VK_EXT_extended_dynamic_state3 blending. + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. bool IsExtExtendedDynamicState3BlendingSupported() const { return dynamic_state3_blending; } @@ -1103,32 +1022,15 @@ private: bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation - bool core_dynamic_viewport_scissor{}; ///< vkCmdSetViewport && vkCmdSetScissor - bool core_dynamic_depth_bias{}; ///< vkCmdSetDepthBias (or alternative) - bool core_dynamic_depth_bounds{}; ///< vkCmdSetDepthBounds - bool core_dynamic_line_width{}; ///< vkCmdSetLineWidth - bool core_dynamic_stencil_masks{}; ///< vkCmdSetStencilCompareMask/WriteMask/Reference - bool eds1_cull_mode{}; ///< Has EDS1 support for dynamic cull mode state. - bool eds1_front_face{}; ///< Has EDS1 support for dynamic front face state. - bool eds1_depth_test_enable{}; ///< Has EDS1 support for dynamic depth test enable state. - bool eds1_depth_write_enable{}; ///< Has EDS1 support for dynamic depth write enable state. - bool eds1_depth_compare_op{}; ///< Has EDS1 support for dynamic depth compare op state. - bool eds1_depth_bounds_test_enable{}; ///< Has EDS1 support for dynamic depth bounds test enable state. - bool eds1_stencil_test_enable{}; ///< Has EDS1 support for dynamic stencil test enable state. - bool eds1_stencil_op{}; ///< Has EDS1 support for dynamic stencil op state. - bool eds1_vertex_input_binding_stride{}; ///< Has EDS1 support for dynamic vertex input binding stride state. - bool eds2_depth_bias_enable{}; ///< Has EDS2 support for depth bias enable state. - bool eds2_primitive_restart_enable{}; ///< Has EDS2 support for primitive restart enable state. - bool eds2_rasterizer_discard_enable{}; ///< Has EDS2 support for rasterizer discard enable state. bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3. - bool dynamic_state3_depth_clamp_enable{}; ///< Has dynamic_state3 support for depth clamp enable state. - bool dynamic_state3_logic_op_enable{}; ///< Has dynamic_state3 support for logic op enable state. - bool dynamic_state3_line_raster_mode{}; ///< Has dynamic_state3 support for line raster mode state. - bool dynamic_state3_conservative_raster_mode{}; ///< Has dynamic_state3 support for conservative raster mode state. - bool dynamic_state3_line_stipple_enable{}; ///< Has dynamic_state3 support for line stipple enable state. - bool dynamic_state3_alpha_to_coverage{}; ///< Has dynamic_state3 support for alpha to coverage state. - bool dynamic_state3_alpha_to_one{}; ///< Has dynamic_state3 support for alpha to one state. + bool dynamic_state3_depth_clamp_enable{}; + bool dynamic_state3_logic_op_enable{}; + bool dynamic_state3_line_raster_mode{}; + bool dynamic_state3_conservative_raster_mode{}; + bool dynamic_state3_line_stipple_enable{}; + bool dynamic_state3_alpha_to_coverage{}; + bool dynamic_state3_alpha_to_one{}; bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited). u64 device_access_memory{}; ///< Total size of device local memory in bytes. From 52281d16e18f3fecad5fe882bf926f54416fe563 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 16 Apr 2026 22:09:21 -0400 Subject: [PATCH 29/31] Revert "[vulkan] Updated VertexInput Stream offset" --- src/video_core/renderer_vulkan/vk_state_tracker.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 391473273f..3f4dd89c7e 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -217,17 +217,14 @@ void SetupDirtyVertexAttributes(Tables& tables) { } void SetupDirtyVertexBindings(Tables& tables) { - static constexpr size_t stride_enable_offset = 0; + // Do NOT include stride here, it's implicit in VertexBuffer static constexpr size_t divisor_offset = 3; for (size_t i = 0; i < Regs::NumVertexArrays; ++i) { - const size_t stream_base = OFF(vertex_streams) + i * NUM(vertex_streams[0]); const u8 flag = static_cast(VertexBinding0 + i); tables[0][OFF(vertex_stream_instances) + i] = VertexInput; tables[1][OFF(vertex_stream_instances) + i] = flag; - tables[0][stream_base + stride_enable_offset] = VertexInput; - tables[1][stream_base + stride_enable_offset] = flag; - tables[0][stream_base + divisor_offset] = VertexInput; - tables[1][stream_base + divisor_offset] = flag; + tables[0][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = VertexInput; + tables[1][OFF(vertex_streams) + i * NUM(vertex_streams[0]) + divisor_offset] = flag; } } From 2b1f16348766e84af83961fd487b26c7ce00725a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 22 Apr 2026 23:36:33 -0400 Subject: [PATCH 30/31] [vulkan] Small adjustments to dynamic_vertex_input setting --- src/video_core/renderer_vulkan/vk_buffer_cache.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 74748f6fc1..a359502046 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -10,7 +10,6 @@ #include #include -#include "common/settings.h" #include "video_core/buffer_cache/buffer_cache_base.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" @@ -557,9 +556,7 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset if (index >= device.GetMaxVertexInputBindings()) { return; } - const bool use_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && - Settings::values.vertex_input_dynamic_state.GetValue(); - if (device.IsExtExtendedDynamicStateSupported() && !use_dynamic_vertex_input) { + if (device.IsExtExtendedDynamicStateSupported()) { 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; @@ -599,9 +596,7 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings& bi if (binding_count == 0) { return; } - const bool use_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && - Settings::values.vertex_input_dynamic_state.GetValue(); - if (device.IsExtExtendedDynamicStateSupported() && !use_dynamic_vertex_input) { + if (device.IsExtExtendedDynamicStateSupported()) { 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()); }); From 51dc28500b42f923a11aae34359ad8d029db8971 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 23 Apr 2026 07:51:58 -0400 Subject: [PATCH 31/31] [vulkan, android] Add BGR565 emulated toggle --- .../yuzu_emu/features/settings/model/BooleanSetting.kt | 1 + .../features/settings/model/view/SettingsItem.kt | 7 +++++++ .../features/settings/ui/SettingsFragmentPresenter.kt | 1 + src/android/app/src/main/res/values/strings.xml | 6 ++++-- src/common/settings.h | 3 +++ src/video_core/renderer_vulkan/vk_texture_cache.cpp | 10 ++++++++-- src/video_core/vulkan_common/vulkan_device.cpp | 4 ++++ src/video_core/vulkan_common/vulkan_device.h | 2 ++ 8 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index e47263bfb2..e482725196 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -17,6 +17,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"), SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"), FIX_BLOOM_EFFECTS("fix_bloom_effects"), + EMULATE_BGR565("emulate_bgr565"), CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"), USE_DOCKED_MODE("use_docked_mode"), USE_AUTO_STUB("use_auto_stub"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 8ca9533f83..daeee398d4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -756,6 +756,13 @@ abstract class SettingsItem( descriptionId = R.string.fix_bloom_effects_description ) ) + put( + SwitchSetting( + BooleanSetting.EMULATE_BGR565, + titleId = R.string.emulate_bgr565, + descriptionId = R.string.emulate_bgr565_description + ) + ) put( SwitchSetting( BooleanSetting.CPUOPT_UNSAFE_HOST_MMU, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 542215fa97..0487339f68 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -293,6 +293,7 @@ class SettingsFragmentPresenter( add(IntSetting.FAST_GPU_TIME.key) add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key) + add(BooleanSetting.EMULATE_BGR565.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key) add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key) diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index d42fb37d58..02860364a9 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -501,7 +501,7 @@ Enable buffer history Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Optimized Vertex Buffers - Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers. Will crash on older drivers. + Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers. Hacks @@ -510,7 +510,9 @@ Skip CPU Inner Invalidation Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games. Fix Bloom Effects - Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. + Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. + Emulate BGR565 + Fixes problems with inverted colors in games or strange artifacts or strange shadows. Use asynchronous shaders Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches. GPU Unswizzle Settings diff --git a/src/common/settings.h b/src/common/settings.h index eb837923b3..13ccf5a1d5 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -555,6 +555,9 @@ struct Values { SwitchableSetting fix_bloom_effects{linkage, false, "fix_bloom_effects", Category::RendererHacks}; + SwitchableSetting emulate_bgr565{linkage, false, "emulate_bgr565", + Category::RendererHacks}; + SwitchableSetting rescale_hack{linkage, false, "rescale_hack", Category::RendererHacks}; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 6adfd09e8f..f099db74cb 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -679,11 +679,16 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im } void TryTransformSwizzleIfNeeded(PixelFormat format, std::array& swizzle, - bool emulate_a4b4g4r4) { + bool emulate_bgr565, bool emulate_a4b4g4r4) { switch (format) { case PixelFormat::A1B5G5R5_UNORM: std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); break; + case PixelFormat::B5G6R5_UNORM: + if (emulate_bgr565) { + std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); + } + break; case PixelFormat::A5B5G5R1_UNORM: std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial); break; @@ -2130,7 +2135,8 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI if (!info.IsRenderTarget()) { swizzle = info.Swizzle(); TryTransformSwizzleIfNeeded(format, swizzle, - !device->IsExt4444FormatsSupported()); + device->MustEmulateBGR565(), + !device->IsExt4444FormatsSupported()); if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne()); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index f48fe39e4e..6e55306079 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -869,6 +869,10 @@ bool Device::HasTimelineSemaphore() const { return features.timeline_semaphore.timelineSemaphore; } +bool Device::MustEmulateBGR565() const { + return Settings::values.emulate_bgr565.GetValue(); +} + bool Device::GetSuitability(bool requires_swapchain) { // Assume we will be suitable. bool suitable = true; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cf341726a7..a8a89aee89 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -782,6 +782,8 @@ public: return features.robustness2.nullDescriptor; } + bool MustEmulateBGR565() const; + bool HasExactDepthBiasControl() const { return features.depth_bias_control.depthBiasExact; }