aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/vulkan_common/vulkan_device.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/vulkan_common/vulkan_device.cpp')
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp402
1 files changed, 314 insertions, 88 deletions
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<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
#if defined(ANDROID) && defined(ARCHITECTURE_arm64)
void OverrideBcnFormats(std::unordered_map<VkFormat, VkFormatProperties>& 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<const u32> 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<VkFormat, 28> 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) {