From 5edcdea78fa60a8cf67c2b99cf2bc827865de906 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 7 Jan 2026 06:49:32 +0100 Subject: [refactor, vk] DynamicState, ExtendedDynamicState and VertexInputDynamicState (#3074) This PR rewrites the DynamicState, ExtendedDynamicState and VertexInputDynamicState logic: - Adds proper handling on how features should be loaded based on driver available features for ExtendedDynamicState/ VertexInputDynamicState. - Fixes some old regressions with emulated formats for Android. - Adds better formatting for tiling format features. - Adds better formatting for format features. - Adds NonWritable buffers handling for Spir-v. - Updates Maintenance features calling. - Adds new features: Multidraw, Robustness2, Image Robustness. - Removes dead code/ duplicated on Vulkan device related to ExtendedDynamicState handling. - Adjusts and conditions with better handling for some features callings: SwapchainMaintenance1, ConditionalRendering, ShaderExtencilExport, CustomBorderColor, TransformFeedback, VertexInputDynamicState. - Removes some older feature ban logic. - Adds hardware resolve path for MSAA Image Blits on Nvidia cards. - Adds flat decorations for input interfaces on Spir-v. - Reduces flushwork within drawcalls. - Clamps render limits on out-of-area for rasterizer. Co-authored-by: lizzie Co-authored-by: Caio Oliveira Co-authored-by: DraVee Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3074 Reviewed-by: DraVee Reviewed-by: Lizzie Co-authored-by: CamilleLaVey Co-committed-by: CamilleLaVey --- src/video_core/vulkan_common/vulkan_device.cpp | 402 +++++++++++++++++++------ 1 file changed, 314 insertions(+), 88 deletions(-) (limited to 'src/video_core/vulkan_common/vulkan_device.cpp') diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 3fc56d35d6..d68cc4b9a4 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -294,9 +294,10 @@ std::unordered_map GetFormatProperties(vk::Physica #if defined(ANDROID) && defined(ARCHITECTURE_arm64) void OverrideBcnFormats(std::unordered_map& format_properties) { // These properties are extracted from Adreno driver 512.687.0 - constexpr VkFormatFeatureFlags tiling_features{ - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | - VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | + constexpr VkFormatFeatureFlags tiling_features{VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | + VK_FORMAT_FEATURE_BLIT_SRC_BIT | + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT | + VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT}; constexpr VkFormatFeatureFlags buffer_features{VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT}; @@ -418,7 +419,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_suitable = GetSuitability(surface != nullptr); const VkDriverId driver_id = properties.driver.driverID; - const auto device_id = properties.properties.deviceID; const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; const bool is_amd_driver = @@ -432,8 +432,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK; const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP; - const bool is_s8gen2 = device_id == 0x43050a01; - //const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; + const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; if (!is_suitable) LOG_WARNING(Render_Vulkan, "Unsuitable driver - continuing anyways"); @@ -487,8 +486,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; - const bool is_intel_igpu = is_integrated && (is_intel_anv || is_intel_windows); - supports_d24_depth = IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal); @@ -499,6 +496,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR CollectToolingInfo(); if (is_qualcomm) { + // Qualcomm Adreno GPUs doesn't handle scaled vertex attributes; keep emulation enabled + must_emulate_scaled_formats = true; + LOG_WARNING(Render_Vulkan, + "Qualcomm drivers require scaled vertex format emulation; forcing fallback"); + LOG_WARNING(Render_Vulkan, "Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers"); RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); @@ -561,35 +563,31 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } if (nv_major_version >= 510) { - LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); + LOG_WARNING(Render_Vulkan, + "NVIDIA Drivers >= 510 do not support MSAA->MSAA image blits. " + "MSAA scaling will use 3D helpers. MSAA resolves work normally."); cant_blit_msaa = true; } - } - if (extensions.extended_dynamic_state3 && is_radv) { - LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation"); - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; - dynamic_state3_blending = false; - - const u32 version = (properties.properties.driverVersion << 3) >> 3; - if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) { - LOG_WARNING(Render_Vulkan, - "RADV versions older than 23.1.0 have broken depth clamp dynamic state"); - features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false; - dynamic_state3_enables = false; + // Mali/ NVIDIA proprietary drivers: Shader stencil export not supported + // Use hardware depth/stencil blits instead when available + if (!extensions.shader_stencil_export) { + LOG_INFO(Render_Vulkan, + "NVIDIA: VK_EXT_shader_stencil_export not supported, using hardware blits " + "for depth/stencil operations"); + LOG_INFO(Render_Vulkan, " D24S8 hardware blit support: {}", + is_blit_depth24_stencil8_supported); + LOG_INFO(Render_Vulkan, " D32S8 hardware blit support: {}", + is_blit_depth32_stencil8_supported); + + if (!is_blit_depth24_stencil8_supported && !is_blit_depth32_stencil8_supported) { + LOG_WARNING(Render_Vulkan, + "NVIDIA: Neither shader export nor hardware blits available for " + "depth/stencil. Performance may be degraded."); + } } } - if (extensions.extended_dynamic_state3 && (is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) { - // AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation - LOG_WARNING(Render_Vulkan, - "AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation"); - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; - dynamic_state3_blending = false; - } - sets_per_pool = 64; if (is_amd_driver) { // AMD drivers need a higher amount of Sets per Pool in certain circumstances like in XC2. @@ -626,15 +624,27 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } if (is_intel_windows) { - LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); + LOG_WARNING(Render_Vulkan, + "Intel proprietary drivers do not support MSAA->MSAA image blits. " + "MSAA scaling will use 3D helpers. MSAA resolves work normally."); cant_blit_msaa = true; } has_broken_compute = CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && !Settings::values.enable_compute_pipelines.GetValue(); - if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { - LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); + must_emulate_bgr565 = false; // Default: assume emulation isn't required + + if (is_intel_anv) { + LOG_WARNING(Render_Vulkan, "Intel ANV driver does not support native BGR format"); + must_emulate_bgr565 = true; + } else if (is_qualcomm) { + LOG_WARNING(Render_Vulkan, + "Qualcomm driver mishandles BGR5 formats even with VK_KHR_maintenance5, forcing emulation"); + must_emulate_bgr565 = true; + } else if (is_arm) { + LOG_WARNING(Render_Vulkan, + "ARM Mali driver mishandles BGR5 formats even with VK_KHR_maintenance5, forcing emulation"); must_emulate_bgr565 = true; } @@ -647,53 +657,57 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR (std::min)(properties.properties.limits.maxVertexInputBindings, 16U); } - if (is_turnip) { - LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits"); + if (is_turnip || is_qualcomm) { + LOG_WARNING(Render_Vulkan, "Driver requires higher-than-reported binding limits"); properties.properties.limits.maxVertexInputBindings = 32; } - if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) { - LOG_INFO(Render_Vulkan, - "Removing extendedDynamicState2 due to missing extendedDynamicState"); - RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, - VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - } + 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 - if (!extensions.extended_dynamic_state2 && extensions.extended_dynamic_state3) { - LOG_INFO(Render_Vulkan, - "Removing extendedDynamicState3 due to missing extendedDynamicState2"); + switch (dyna_state) { + case Settings::ExtendedDynamicState::Disabled: + // 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, + VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, - VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; dynamic_state3_enables = false; - } - - const auto dyna_state = u32(Settings::values.dyna_state.GetValue()); - - // Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs - // ALSO affects ALL versions of UHD drivers on Windows 10+, seems to cause even worse issues like straight up crashing - // So... Yeah, UHD drivers fucking suck -- maybe one day we can work past this, maybe; some driver hacking? - // And then we can rest in peace by doing `< VK_MAKE_API_VERSION(26, 0, 0)` for our beloved mesa drivers... one day - if ((is_mvk || is_intel_igpu) && dyna_state != 0) { - LOG_WARNING(Render_Vulkan, "Driver has broken dynamic state, forcing to 0 to prevent graphical issues"); - Settings::values.dyna_state.SetValue(Settings::ExtendedDynamicState::Disabled); - } - - switch (dyna_state) { - case 0: - RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - [[fallthrough]]; - case 1: - RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - [[fallthrough]]; - case 2: - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + break; + case Settings::ExtendedDynamicState::EDS1: + // Level 1: Enable EDS1, disable EDS2 and EDS3 + 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, + VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + dynamic_state3_blending = false; + dynamic_state3_enables = false; + break; + case Settings::ExtendedDynamicState::EDS2: + // Level 2: Enable EDS1 + EDS2, disable EDS3 + RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, + VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; dynamic_state3_enables = false; break; + case Settings::ExtendedDynamicState::EDS3: + default: + // Level 3: Enable all (EDS1 + EDS2 + EDS3) + break; } - if (!Settings::values.vertex_input_dynamic_state.GetValue() || !extensions.extended_dynamic_state) { + // 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); } @@ -781,8 +795,8 @@ void Device::SaveShader(std::span spirv) const { } bool Device::ComputeIsOptimalAstcSupported() const { - // Disable for now to avoid converting ASTC twice. - static constexpr std::array astc_formats = { + // Verify hardware supports all ASTC formats with optimal tiling to avoid software conversion + static constexpr std::array astc_formats = { VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_4x4_SRGB_BLOCK, VK_FORMAT_ASTC_5x4_UNORM_BLOCK, VK_FORMAT_ASTC_5x4_SRGB_BLOCK, VK_FORMAT_ASTC_5x5_UNORM_BLOCK, VK_FORMAT_ASTC_5x5_SRGB_BLOCK, @@ -801,9 +815,10 @@ bool Device::ComputeIsOptimalAstcSupported() const { if (!features.features.textureCompressionASTC_LDR) { return false; } - const auto format_feature_usage{ - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT | - VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | + const auto format_feature_usage{VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | + VK_FORMAT_FEATURE_BLIT_SRC_BIT | + VK_FORMAT_FEATURE_BLIT_DST_BIT | + VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT}; for (const auto format : astc_formats) { const auto physical_format_properties{physical.GetFormatProperties(format)}; @@ -1000,7 +1015,7 @@ bool Device::GetSuitability(bool requires_swapchain) { // Set next pointer. void** next = &features2.pNext; - // Vulkan 1.2, 1.3 and 1.4 features + // Vulkan 1.2 and 1.3 features if (instance_version >= VK_API_VERSION_1_2) { features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; features_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; @@ -1106,6 +1121,16 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT; SetNext(next, properties.transform_feedback); } + if (extensions.maintenance5) { + properties.maintenance5.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR; + SetNext(next, properties.maintenance5); + } + if (extensions.multi_draw) { + properties.multi_draw.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT; + SetNext(next, properties.multi_draw); + } // Perform the property fetch. physical.GetProperties2(properties2); @@ -1138,14 +1163,70 @@ bool Device::GetSuitability(bool requires_swapchain) { } } + // VK_DYNAMIC_STATE + + // Driver detection variables for workarounds in GetSuitability + const VkDriverId driver_id = properties.driver.driverID; + const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; + + // 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 + // Disable VertexInputDynamicState on old Intel Windows drivers + if (extensions.vertex_input_dynamic_state && is_intel_windows) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { + LOG_WARNING(Render_Vulkan, + "Intel Windows < 27.20.100.0: 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.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; + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = false; + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false; + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false; + } + // Return whether we were suitable. return suitable; } void Device::RemoveUnsuitableExtensions() { // VK_EXT_custom_border_color - extensions.custom_border_color = features.custom_border_color.customBorderColors && - features.custom_border_color.customBorderColorWithoutFormat; + // 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; + } + } RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); @@ -1174,21 +1255,79 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); // VK_EXT_extended_dynamic_state3 - dynamic_state3_blending = - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation && + const bool supports_color_blend_enable = + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable; + const bool supports_color_blend_equation = + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation; + const bool supports_color_write_mask = features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; - dynamic_state3_enables = - features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable && + dynamic_state3_blending = supports_color_blend_enable && supports_color_blend_equation && + supports_color_write_mask; + + const bool supports_depth_clamp_enable = + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable; + const bool supports_logic_op_enable = features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable; + const bool supports_line_raster_mode = + features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode && + extensions.line_rasterization && features.line_rasterization.rectangularLines; + const bool supports_conservative_raster_mode = + features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode && + extensions.conservative_rasterization; + const bool supports_line_stipple_enable = + features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable && + extensions.line_rasterization && features.line_rasterization.stippledRectangularLines; + const bool supports_alpha_to_coverage = + features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable; + const bool supports_alpha_to_one = + features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable && + features.features.alphaToOne; + + dynamic_state3_depth_clamp_enable = supports_depth_clamp_enable; + dynamic_state3_logic_op_enable = supports_logic_op_enable; + dynamic_state3_line_raster_mode = supports_line_raster_mode; + dynamic_state3_conservative_raster_mode = supports_conservative_raster_mode; + dynamic_state3_line_stipple_enable = supports_line_stipple_enable; + dynamic_state3_alpha_to_coverage = supports_alpha_to_coverage; + dynamic_state3_alpha_to_one = supports_alpha_to_one; + + dynamic_state3_enables = dynamic_state3_depth_clamp_enable || dynamic_state3_logic_op_enable || + dynamic_state3_line_raster_mode || + dynamic_state3_conservative_raster_mode || + dynamic_state3_line_stipple_enable || + dynamic_state3_alpha_to_coverage || dynamic_state3_alpha_to_one; extensions.extended_dynamic_state3 = dynamic_state3_blending || dynamic_state3_enables; - dynamic_state3_blending = dynamic_state3_blending && extensions.extended_dynamic_state3; - dynamic_state3_enables = dynamic_state3_enables && extensions.extended_dynamic_state3; + if (!extensions.extended_dynamic_state3) { + dynamic_state3_blending = false; + dynamic_state3_enables = false; + dynamic_state3_depth_clamp_enable = false; + dynamic_state3_logic_op_enable = false; + dynamic_state3_line_raster_mode = false; + dynamic_state3_conservative_raster_mode = false; + dynamic_state3_line_stipple_enable = false; + dynamic_state3_alpha_to_coverage = false; + dynamic_state3_alpha_to_one = false; + } RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + // VK_EXT_robustness2 + // Enable if at least one robustness2 feature is available + extensions.robustness_2 = features.robustness2.robustBufferAccess2 || + features.robustness2.robustImageAccess2 || + features.robustness2.nullDescriptor; + + RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + + // VK_EXT_image_robustness + // Enable if robustImageAccess is available + extensions.image_robustness = features.image_robustness.robustImageAccess; + RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, + VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); + // VK_EXT_provoking_vertex if (Settings::values.provoking_vertex.GetValue()) { extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast @@ -1226,15 +1365,21 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME); // VK_EXT_transform_feedback + // We only require the basic transformFeedback feature and at least + // one transform feedback buffer. We keep transformFeedbackQueries as it's used by + // the streaming byte count implementation. GeometryStreams and multiple streams + // are not strictly required since we currently support only stream 0. extensions.transform_feedback = features.transform_feedback.transformFeedback && - features.transform_feedback.geometryStreams && - properties.transform_feedback.maxTransformFeedbackStreams >= 4 && properties.transform_feedback.maxTransformFeedbackBuffers > 0 && - properties.transform_feedback.transformFeedbackQueries && - properties.transform_feedback.transformFeedbackDraw; + properties.transform_feedback.transformFeedbackQueries; RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); + if (extensions.transform_feedback) { + LOG_INFO(Render_Vulkan, "VK_EXT_transform_feedback enabled (buffers={}, queries={})", + properties.transform_feedback.maxTransformFeedbackBuffers, + properties.transform_feedback.transformFeedbackQueries); + } // VK_EXT_vertex_input_dynamic_state extensions.vertex_input_dynamic_state = @@ -1243,6 +1388,17 @@ void Device::RemoveUnsuitableExtensions() { features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + // VK_EXT_multi_draw + extensions.multi_draw = features.multi_draw.multiDraw; + + if (extensions.multi_draw) { + LOG_INFO(Render_Vulkan, "VK_EXT_multi_draw: maxMultiDrawCount={}", + properties.multi_draw.maxMultiDrawCount); + } + + RemoveExtensionFeatureIfUnsuitable(extensions.multi_draw, features.multi_draw, + VK_EXT_MULTI_DRAW_EXTENSION_NAME); + // VK_KHR_pipeline_executable_properties if (Settings::values.renderer_shader_feedback.GetValue()) { extensions.pipeline_executable_properties = @@ -1266,6 +1422,76 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout, features.workgroup_memory_explicit_layout, VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME); + + // VK_EXT_swapchain_maintenance1 (extension only, has features) + // Requires VK_EXT_surface_maintenance1 instance extension + extensions.swapchain_maintenance1 = features.swapchain_maintenance1.swapchainMaintenance1; + if (extensions.swapchain_maintenance1) { + // Check if VK_EXT_surface_maintenance1 instance extension is available + const auto instance_extensions = vk::EnumerateInstanceExtensionProperties(dld); + const bool has_surface_maintenance1 = instance_extensions && std::ranges::any_of(*instance_extensions, + [](const VkExtensionProperties& prop) { + return std::strcmp(prop.extensionName, VK_EXT_SURFACE_MAINTENANCE_1_EXTENSION_NAME) == 0; + }); + if (!has_surface_maintenance1) { + LOG_WARNING(Render_Vulkan, + "VK_EXT_swapchain_maintenance1 requires VK_EXT_surface_maintenance1, disabling"); + extensions.swapchain_maintenance1 = false; + features.swapchain_maintenance1.swapchainMaintenance1 = false; + } + } + RemoveExtensionFeatureIfUnsuitable(extensions.swapchain_maintenance1, features.swapchain_maintenance1, + VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + + // VK_KHR_maintenance1 (core in Vulkan 1.1, no features) + extensions.maintenance1 = loaded_extensions.contains(VK_KHR_MAINTENANCE_1_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance1, VK_KHR_MAINTENANCE_1_EXTENSION_NAME); + + // VK_KHR_maintenance2 (core in Vulkan 1.1, no features) + extensions.maintenance2 = loaded_extensions.contains(VK_KHR_MAINTENANCE_2_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance2, VK_KHR_MAINTENANCE_2_EXTENSION_NAME); + + // VK_KHR_maintenance3 (core in Vulkan 1.1, no features) + extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME); + + // VK_KHR_maintenance4 + extensions.maintenance4 = features.maintenance4.maintenance4; + RemoveExtensionFeatureIfUnsuitable(extensions.maintenance4, features.maintenance4, + VK_KHR_MAINTENANCE_4_EXTENSION_NAME); + + // VK_KHR_maintenance5 + extensions.maintenance5 = features.maintenance5.maintenance5; + + if (extensions.maintenance5) { + LOG_INFO(Render_Vulkan, "VK_KHR_maintenance5 properties: polygonModePointSize={} " + "depthStencilSwizzleOne={} earlyFragmentTests={} nonStrictWideLines={}", + properties.maintenance5.polygonModePointSize, + properties.maintenance5.depthStencilSwizzleOneSupport, + properties.maintenance5.earlyFragmentMultisampleCoverageAfterSampleCounting && + properties.maintenance5.earlyFragmentSampleMaskTestBeforeSampleCounting, + properties.maintenance5.nonStrictWideLinesUseParallelogram); + } + + RemoveExtensionFeatureIfUnsuitable(extensions.maintenance5, features.maintenance5, + VK_KHR_MAINTENANCE_5_EXTENSION_NAME); + + // VK_KHR_maintenance6 + extensions.maintenance6 = features.maintenance6.maintenance6; + RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6, + VK_KHR_MAINTENANCE_6_EXTENSION_NAME); + + // VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features) + extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME); + + // VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features) + extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME); + + // VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features) + extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME); } void Device::SetupFamilies(VkSurfaceKHR surface) { -- cgit v1.2.3