Browse Source

[vk] Refactor DynamicState and ExtendedState to granular per-feature enabling

* Also giving maintance to driver features and unused extensions

Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
pull/3059/head
CamilleLaVey 4 months ago
committed by Caio Oliveira
parent
commit
217759925e
No known key found for this signature in database GPG Key ID: 362DA3DC1901E080
  1. 12
      src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt
  2. 2
      src/android/app/src/main/res/values/strings.xml
  3. 2
      src/qt_common/config/shared_translation.cpp
  4. 59
      src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
  5. 100
      src/video_core/renderer_vulkan/fixed_pipeline_state.h
  6. 141
      src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
  7. 104
      src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
  8. 7
      src/video_core/renderer_vulkan/vk_pipeline_cache.h
  9. 91
      src/video_core/renderer_vulkan/vk_rasterizer.cpp
  10. 28
      src/video_core/vulkan_common/vulkan.h
  11. 409
      src/video_core/vulkan_common/vulkan_device.cpp
  12. 161
      src/video_core/vulkan_common/vulkan_device.h

12
src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt

@ -141,16 +141,16 @@ abstract class SettingsItem(
)
put(
SwitchSetting(
BooleanSetting.RENDERER_PROVOKING_VERTEX,
titleId = R.string.provoking_vertex,
descriptionId = R.string.provoking_vertex_description
BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE,
titleId = R.string.vertex_input_dynamic_state,
descriptionId = R.string.vertex_input_dynamic_state_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE,
titleId = R.string.vertex_input_dynamic_state,
descriptionId = R.string.vertex_input_dynamic_state_description
BooleanSetting.RENDERER_PROVOKING_VERTEX,
titleId = R.string.provoking_vertex,
descriptionId = R.string.provoking_vertex_description
)
)
put(

2
src/android/app/src/main/res/values/strings.xml

@ -95,7 +95,7 @@
<string name="dyna_state_description">Controls the number of features that can be used in Extended Dynamic State. Higher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors. The default value may vary depending on your system and hardware capabilities. This value can be changed until stability and a better visual quality are achieved.</string>
<string name="disabled">Disabled</string>
<string name="vertex_input_dynamic_state">Vertex Input Dynamic State</string>
<string name="vertex_input_dynamic_state_description">Enables vertex input dynamic state feature for better quality and performance.</string>
<string name="vertex_input_dynamic_state_description">Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs.</string>
<string name="provoking_vertex">Provoking Vertex</string>
<string name="provoking_vertex_description">Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs.</string>
<string name="descriptor_indexing">Descriptor Indexing</string>

2
src/qt_common/config/shared_translation.cpp

@ -332,7 +332,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent)
INSERT(Settings,
vertex_input_dynamic_state,
tr("Vertex Input Dynamic State"),
tr("Enables vertex input dynamic state feature for better quality and performance."));
tr("Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs."));
INSERT(Settings,
provoking_vertex,

59
src/video_core/renderer_vulkan/fixed_pipeline_state.cpp

@ -58,12 +58,57 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology;
raw1 = 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_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
raw1_eds3_extended = 0;
pad_align_u64 = 0;
// EDS1
extended_dynamic_state.Assign(features.has_extended_dynamic_state);
// EDS2
extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2);
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op);
extended_dynamic_state_2_patch_control_points.Assign(
features.has_extended_dynamic_state_2_patch_control_points);
// EDS3 - Blending/Enables
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables);
// EDS3 - Granular features
extended_dynamic_state_3_depth_clamp.Assign(features.has_extended_dynamic_state_3_depth_clamp);
extended_dynamic_state_3_logic_op_enable.Assign(
features.has_extended_dynamic_state_3_logic_op_enable);
extended_dynamic_state_3_tessellation_domain_origin.Assign(
features.has_extended_dynamic_state_3_tessellation_domain_origin);
extended_dynamic_state_3_polygon_mode.Assign(
features.has_extended_dynamic_state_3_polygon_mode);
extended_dynamic_state_3_rasterization_samples.Assign(
features.has_extended_dynamic_state_3_rasterization_samples);
extended_dynamic_state_3_sample_mask.Assign(features.has_extended_dynamic_state_3_sample_mask);
extended_dynamic_state_3_alpha_to_coverage_enable.Assign(
features.has_extended_dynamic_state_3_alpha_to_coverage_enable);
extended_dynamic_state_3_alpha_to_one_enable.Assign(
features.has_extended_dynamic_state_3_alpha_to_one_enable);
extended_dynamic_state_3_depth_clip_enable.Assign(
features.has_extended_dynamic_state_3_depth_clip_enable);
extended_dynamic_state_3_depth_clip_negative_one_to_one.Assign(
features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one);
extended_dynamic_state_3_line_rasterization_mode.Assign(
features.has_extended_dynamic_state_3_line_rasterization_mode);
extended_dynamic_state_3_line_stipple_enable.Assign(
features.has_extended_dynamic_state_3_line_stipple_enable);
extended_dynamic_state_3_provoking_vertex_mode.Assign(
features.has_extended_dynamic_state_3_provoking_vertex_mode);
extended_dynamic_state_3_conservative_rasterization_mode.Assign(
features.has_extended_dynamic_state_3_conservative_rasterization_mode);
extended_dynamic_state_3_sample_locations_enable.Assign(
features.has_extended_dynamic_state_3_sample_locations_enable);
extended_dynamic_state_3_rasterization_stream.Assign(
features.has_extended_dynamic_state_3_rasterization_stream);
// Vertex Input
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs)));
@ -158,7 +203,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
return static_cast<u16>(array.stride.Value());
});
}
if (!extended_dynamic_state_2_extra) {
if (!extended_dynamic_state_2_logic_op) {
dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2);
}
if (!extended_dynamic_state_3_blend) {

100
src/video_core/renderer_vulkan/fixed_pipeline_state.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -18,12 +21,35 @@ namespace Vulkan {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
struct DynamicFeatures {
bool has_extended_dynamic_state;
bool has_extended_dynamic_state_2;
bool has_extended_dynamic_state_2_extra;
bool has_extended_dynamic_state_3_blend;
bool has_extended_dynamic_state_3_enables;
bool has_dynamic_vertex_input;
// VK_EXT_extended_dynamic_state (EDS1) - Bit 0
bool has_extended_dynamic_state : 1;
// VK_EXT_extended_dynamic_state2 (EDS2) - Bits 1-3
bool has_extended_dynamic_state_2 : 1; // Core EDS2
bool has_extended_dynamic_state_2_logic_op : 1; // LogicOp
bool has_extended_dynamic_state_2_patch_control_points : 1; // Tessellation
// VK_EXT_extended_dynamic_state3 (EDS3) - Bits 4-5
bool has_extended_dynamic_state_3_blend : 1; // Blending composite
bool has_extended_dynamic_state_3_enables : 1; // Enables composite
// VK_EXT_vertex_input_dynamic_state - Bit 6
bool has_dynamic_vertex_input : 1;
// EDS3 Granular Features - Bits 7-15
bool has_extended_dynamic_state_3_depth_clamp;
bool has_extended_dynamic_state_3_logic_op_enable;
bool has_extended_dynamic_state_3_tessellation_domain_origin;
bool has_extended_dynamic_state_3_polygon_mode;
bool has_extended_dynamic_state_3_rasterization_samples;
bool has_extended_dynamic_state_3_sample_mask;
bool has_extended_dynamic_state_3_alpha_to_coverage_enable : 1;
bool has_extended_dynamic_state_3_alpha_to_one_enable : 1;
bool has_extended_dynamic_state_3_depth_clip_enable : 1;
// EDS3 Additional Features - Bits 16-22
bool has_extended_dynamic_state_3_depth_clip_negative_one_to_one : 1;
bool has_extended_dynamic_state_3_line_rasterization_mode : 1;
bool has_extended_dynamic_state_3_line_stipple_enable : 1;
bool has_extended_dynamic_state_3_provoking_vertex_mode : 1;
bool has_extended_dynamic_state_3_conservative_rasterization_mode : 1;
bool has_extended_dynamic_state_3_sample_locations_enable : 1;
bool has_extended_dynamic_state_3_rasterization_stream : 1;
};
struct FixedPipelineState {
@ -184,23 +210,56 @@ struct FixedPipelineState {
union {
u32 raw1;
// EDS1 - Bit 0
BitField<0, 1, u32> extended_dynamic_state;
BitField<1, 1, u32> extended_dynamic_state_2;
BitField<2, 1, u32> extended_dynamic_state_2_extra;
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;
// EDS2 - Bits 1-3
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_2_patch_control_points;
// EDS3 Blending/Enables - Bits 4-5
BitField<4, 1, u32> extended_dynamic_state_3_blend;
BitField<5, 1, u32> extended_dynamic_state_3_enables;
// Vertex Input - Bit 6
BitField<6, 1, u32> dynamic_vertex_input;
// Other state - Bits 7-19
BitField<7, 1, u32> xfb_enabled;
BitField<8, 1, u32> ndc_minus_one_to_one;
BitField<9, 2, u32> polygon_mode;
BitField<11, 2, u32> tessellation_primitive;
BitField<13, 2, u32> tessellation_spacing;
BitField<15, 1, u32> tessellation_clockwise;
BitField<16, 5, u32> patch_control_points_minus_one;
// Topology and MSAA - Bits 24-31
BitField<24, 4, Maxwell::PrimitiveTopology> topology;
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
};
union {
u32 raw1_eds3_extended;
// EDS3 Additional Features - Bits 0-15
BitField<0, 1, u32> extended_dynamic_state_3_depth_clamp;
BitField<1, 1, u32> extended_dynamic_state_3_logic_op_enable;
BitField<2, 1, u32> extended_dynamic_state_3_tessellation_domain_origin;
BitField<3, 1, u32> extended_dynamic_state_3_polygon_mode;
BitField<4, 1, u32> extended_dynamic_state_3_rasterization_samples;
BitField<5, 1, u32> extended_dynamic_state_3_sample_mask;
BitField<6, 1, u32> extended_dynamic_state_3_alpha_to_coverage_enable;
BitField<7, 1, u32> extended_dynamic_state_3_alpha_to_one_enable;
BitField<8, 1, u32> extended_dynamic_state_3_depth_clip_enable;
BitField<9, 1, u32> extended_dynamic_state_3_depth_clip_negative_one_to_one;
BitField<10, 1, u32> extended_dynamic_state_3_line_rasterization_mode;
BitField<11, 1, u32> extended_dynamic_state_3_line_stipple_enable;
BitField<12, 1, u32> extended_dynamic_state_3_provoking_vertex_mode;
BitField<13, 1, u32> extended_dynamic_state_3_conservative_rasterization_mode;
BitField<14, 1, u32> extended_dynamic_state_3_sample_locations_enable;
BitField<15, 1, u32> extended_dynamic_state_3_rasterization_stream;
};
union {
u32 raw2;
BitField<1, 3, u32> alpha_test_func;
@ -215,12 +274,13 @@ struct FixedPipelineState {
BitField<16, 1, u32> alpha_to_one_enabled;
BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage;
};
std::array<u8, Maxwell::NumRenderTargets> color_formats;
u32 alpha_test_ref;
u32 point_size;
std::array<u8, Maxwell::NumRenderTargets> color_formats;
std::array<u16, Maxwell::NumViewports> viewport_swizzles;
u32 pad_align_u64;
union {
u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state
u64 enabled_divisors;

141
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp

@ -837,13 +837,39 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
.pAttachments = cb_attachments.data(),
.blendConstants = {}
};
static_vector<VkDynamicState, 34> 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,
};
// Base Vulkan Dynamic States - Always active (independent of EDS)
// Granular fallback: Each state added only if device supports it (protection against broken
// drivers)
static_vector<VkDynamicState, 34> dynamic_states;
if (device.SupportsDynamicViewport()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT);
}
if (device.SupportsDynamicScissor()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR);
}
if (device.SupportsDynamicLineWidth()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH);
}
if (device.SupportsDynamicDepthBias()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
}
if (device.SupportsDynamicBlendConstants()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS);
}
if (device.SupportsDynamicDepthBounds()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS);
}
if (device.SupportsDynamicStencilCompareMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK);
}
if (device.SupportsDynamicStencilWriteMask()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK);
}
if (device.SupportsDynamicStencilReference()) {
dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE);
}
// EDS1 - Extended Dynamic State
if (key.state.extended_dynamic_state) {
std::vector<VkDynamicState> extended{
VK_DYNAMIC_STATE_CULL_MODE_EXT,
@ -855,54 +881,67 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) {
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_STENCIL_OP_EXT,
};
if (!device.IsExtVertexInputDynamicStateSupported()) {
extended.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
}
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end());
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 (key.state.extended_dynamic_state_2_extra) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
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,
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
// additional state3 extensions
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
// Note: VERTEX_INPUT_BINDING_STRIDE is part of EDS1, not VIDS
// When VIDS is disabled, we still need dynamic stride with BindVertexBuffers2EXT
if (!key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT);
}
}
// Vertex Input Dynamic State (independent toggle, replaces VERTEX_INPUT_BINDING_STRIDE when
// enabled)
if (key.state.dynamic_vertex_input) {
dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT);
}
// EDS2 - Extended Dynamic State 2 Core (3 states)
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());
}
// EDS2 - Logic Op (granular feature)
if (key.state.extended_dynamic_state_2_logic_op) {
dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT);
}
// EDS3 - Blending (composite: ColorBlendEnable + Equation + WriteMask)
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,
// VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
// EDS3 - Enables (granular: DepthClamp + LogicOpEnable + ...)
if (key.state.extended_dynamic_state_3_enables) {
static constexpr std::array extended3{
VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT,
VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT,
// additional state3 extensions
VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT,
VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT,
VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT,
VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT,
};
dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end());
}
const VkPipelineDynamicStateCreateInfo dynamic_state_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.pNext = nullptr,

104
src/video_core/renderer_vulkan/vk_pipeline_cache.cpp

@ -404,14 +404,63 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays);
}
dynamic_features = DynamicFeatures{
.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(),
.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(),
.has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(),
.has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(),
.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(),
.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(),
};
const u8 dynamic_state = Settings::values.dyna_state.GetValue();
LOG_INFO(Render_Vulkan, "DynamicState value is set to {}", (u32)dynamic_state);
dynamic_features = {};
// EDS1 - All-or-nothing (enabled if driver supports AND setting > 0)
dynamic_features.has_extended_dynamic_state =
device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0;
// EDS2 - Core features (enabled if driver supports AND setting > 1)
dynamic_features.has_extended_dynamic_state_2 =
device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1;
dynamic_features.has_extended_dynamic_state_2_logic_op =
device.IsExtExtendedDynamicState2LogicOpSupported() && dynamic_state > 1;
dynamic_features.has_extended_dynamic_state_2_patch_control_points =
device.IsExtExtendedDynamicState2PatchControlPointsSupported() && dynamic_state > 1;
// EDS3 - Granular features (enabled if driver supports AND setting > 2)
dynamic_features.has_extended_dynamic_state_3_blend =
device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_enables =
device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_depth_clamp =
device.IsExtExtendedDynamicState3DepthClampEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_logic_op_enable =
device.IsExtExtendedDynamicState3LogicOpEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin =
device.IsExtExtendedDynamicState3TessellationDomainOriginSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_polygon_mode =
device.IsExtExtendedDynamicState3PolygonModeSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_rasterization_samples =
device.IsExtExtendedDynamicState3RasterizationSamplesSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_sample_mask =
device.IsExtExtendedDynamicState3SampleMaskSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable =
device.IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable =
device.IsExtExtendedDynamicState3AlphaToOneEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_depth_clip_enable =
device.IsExtExtendedDynamicState3DepthClipEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one =
device.IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode =
device.IsExtExtendedDynamicState3LineRasterizationModeSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_line_stipple_enable =
device.IsExtExtendedDynamicState3LineStippleEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode =
device.IsExtExtendedDynamicState3ProvokingVertexModeSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode =
device.IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() &&
dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_sample_locations_enable =
device.IsExtExtendedDynamicState3SampleLocationsEnableSupported() && dynamic_state > 2;
dynamic_features.has_extended_dynamic_state_3_rasterization_stream =
device.IsExtExtendedDynamicState3RasterizationStreamSupported() && dynamic_state > 2;
// Vertex input dynamic state (independent toggle)
dynamic_features.has_dynamic_vertex_input =
device.IsExtVertexInputDynamicStateSupported() &&
Settings::values.vertex_input_dynamic_state.GetValue();
}
PipelineCache::~PipelineCache() {
@ -512,16 +561,51 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
GraphicsPipelineCacheKey key;
file.read(reinterpret_cast<char*>(&key), sizeof(key));
// Validate dynamic features compatibility - granular per-feature check
if ((key.state.extended_dynamic_state != 0) !=
dynamic_features.has_extended_dynamic_state ||
(key.state.extended_dynamic_state_2 != 0) !=
dynamic_features.has_extended_dynamic_state_2 ||
(key.state.extended_dynamic_state_2_extra != 0) !=
dynamic_features.has_extended_dynamic_state_2_extra ||
(key.state.extended_dynamic_state_2_logic_op != 0) !=
dynamic_features.has_extended_dynamic_state_2_logic_op ||
(key.state.extended_dynamic_state_2_patch_control_points != 0) !=
dynamic_features.has_extended_dynamic_state_2_patch_control_points ||
(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.extended_dynamic_state_3_depth_clamp != 0) !=
dynamic_features.has_extended_dynamic_state_3_depth_clamp ||
(key.state.extended_dynamic_state_3_logic_op_enable != 0) !=
dynamic_features.has_extended_dynamic_state_3_logic_op_enable ||
(key.state.extended_dynamic_state_3_tessellation_domain_origin != 0) !=
dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin ||
(key.state.extended_dynamic_state_3_polygon_mode != 0) !=
dynamic_features.has_extended_dynamic_state_3_polygon_mode ||
(key.state.extended_dynamic_state_3_rasterization_samples != 0) !=
dynamic_features.has_extended_dynamic_state_3_rasterization_samples ||
(key.state.extended_dynamic_state_3_sample_mask != 0) !=
dynamic_features.has_extended_dynamic_state_3_sample_mask ||
(key.state.extended_dynamic_state_3_alpha_to_coverage_enable != 0) !=
dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable ||
(key.state.extended_dynamic_state_3_alpha_to_one_enable != 0) !=
dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable ||
(key.state.extended_dynamic_state_3_depth_clip_enable != 0) !=
dynamic_features.has_extended_dynamic_state_3_depth_clip_enable ||
(key.state.extended_dynamic_state_3_depth_clip_negative_one_to_one != 0) !=
dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one ||
(key.state.extended_dynamic_state_3_line_rasterization_mode != 0) !=
dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode ||
(key.state.extended_dynamic_state_3_line_stipple_enable != 0) !=
dynamic_features.has_extended_dynamic_state_3_line_stipple_enable ||
(key.state.extended_dynamic_state_3_provoking_vertex_mode != 0) !=
dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode ||
(key.state.extended_dynamic_state_3_conservative_rasterization_mode != 0) !=
dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode ||
(key.state.extended_dynamic_state_3_sample_locations_enable != 0) !=
dynamic_features.has_extended_dynamic_state_3_sample_locations_enable ||
(key.state.extended_dynamic_state_3_rasterization_stream != 0) !=
dynamic_features.has_extended_dynamic_state_3_rasterization_stream ||
(key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) {
return;
}

7
src/video_core/renderer_vulkan/vk_pipeline_cache.h

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -113,6 +116,10 @@ public:
void LoadDiskResources(u64 title_id, std::stop_token stop_loading,
const VideoCore::DiskResourceLoadCallback& callback);
[[nodiscard]] const DynamicFeatures& GetDynamicFeatures() const noexcept {
return dynamic_features;
}
private:
[[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath();

91
src/video_core/renderer_vulkan/vk_rasterizer.cpp

@ -933,6 +933,7 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info,
void RasterizerVulkan::UpdateDynamicStates() {
auto& regs = maxwell3d->regs;
const auto& dynamic_features = pipeline_cache.GetDynamicFeatures();
UpdateViewportsState(regs);
UpdateScissorsState(regs);
UpdateDepthBias(regs);
@ -940,7 +941,8 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthBounds(regs);
UpdateStencilFaces(regs);
UpdateLineWidth(regs);
if (device.IsExtExtendedDynamicStateSupported()) {
// EDS1 - Extended Dynamic State 1
if (dynamic_features.has_extended_dynamic_state) {
UpdateCullMode(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
@ -950,41 +952,74 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthTestEnable(regs);
UpdateDepthWriteEnable(regs);
UpdateStencilTestEnable(regs);
if (device.IsExtExtendedDynamicState2Supported()) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
using namespace Tegra::Engines;
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<u32>(!has_float);
}
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
UpdateLineStippleEnable(regs);
UpdateConservativeRasterizationMode(regs);
}
}
// EDS2 - Extended Dynamic State 2 Core
if (dynamic_features.has_extended_dynamic_state_2) {
if (state_tracker.TouchStateEnable()) {
UpdatePrimitiveRestartEnable(regs);
UpdateRasterizerDiscardEnable(regs);
UpdateDepthBiasEnable(regs);
}
}
// EDS2 - LogicOp (granular feature)
if (dynamic_features.has_extended_dynamic_state_2_logic_op) {
UpdateLogicOp(regs);
}
// EDS3 - Depth Clamp Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_depth_clamp ||
dynamic_features.has_extended_dynamic_state_3_enables) {
if (state_tracker.TouchStateEnable()) {
UpdateDepthClampEnable(regs);
}
}
// EDS3 - Logic Op Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_logic_op_enable ||
dynamic_features.has_extended_dynamic_state_3_enables) {
if (state_tracker.TouchStateEnable()) {
using namespace Tegra::Engines;
// AMD workaround for logic op with float vertex attributes
if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE ||
device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) {
struct In {
const Maxwell3D::Regs::VertexAttribute::Type d;
In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {}
bool operator()(Maxwell3D::Regs::VertexAttribute n) const {
return n.type == d;
}
};
auto has_float =
std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(),
In(Maxwell3D::Regs::VertexAttribute::Type::Float));
if (regs.logic_op.enable) {
regs.logic_op.enable = static_cast<u32>(!has_float);
}
UpdateLogicOpEnable(regs);
UpdateDepthClampEnable(regs);
}
UpdateLogicOpEnable(regs);
}
if (device.IsExtExtendedDynamicState2ExtrasSupported()) {
UpdateLogicOp(regs);
}
if (device.IsExtExtendedDynamicState3BlendingSupported()) {
UpdateBlending(regs);
}
if (device.IsExtExtendedDynamicState3EnablesSupported()) {
}
// EDS3 - Line Stipple Enable (granular)
if (dynamic_features.has_extended_dynamic_state_3_line_stipple_enable) {
if (state_tracker.TouchStateEnable()) {
UpdateLineStippleEnable(regs);
}
}
// EDS3 - Conservative Rasterization Mode (granular)
if (dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode) {
if (state_tracker.TouchStateEnable()) {
UpdateConservativeRasterizationMode(regs);
}
}
if (device.IsExtVertexInputDynamicStateSupported()) {
// EDS3 - Blending (composite feature: ColorBlendEnable + ColorBlendEquation + ColorWriteMask)
if (dynamic_features.has_extended_dynamic_state_3_blend) {
UpdateBlending(regs);
}
// Vertex Input Dynamic State
if (dynamic_features.has_dynamic_vertex_input) {
if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) {
UpdateVertexInput(regs);
}

28
src/video_core/vulkan_common/vulkan.h

@ -22,6 +22,34 @@
#include <vulkan/vulkan.h>
#ifndef VK_KHR_MAINTENANCE_1_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_1_EXTENSION_NAME "VK_KHR_maintenance1"
#endif
#ifndef VK_KHR_MAINTENANCE_2_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_2_EXTENSION_NAME "VK_KHR_maintenance2"
#endif
#ifndef VK_KHR_MAINTENANCE_3_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_3_EXTENSION_NAME "VK_KHR_maintenance3"
#endif
#ifndef VK_KHR_MAINTENANCE_4_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_4_EXTENSION_NAME "VK_KHR_maintenance4"
#endif
#ifndef VK_KHR_MAINTENANCE_5_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_5_EXTENSION_NAME "VK_KHR_maintenance5"
#endif
#ifndef VK_KHR_MAINTENANCE_6_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_6_EXTENSION_NAME "VK_KHR_maintenance6"
#endif
#ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7"
#endif
#ifndef VK_KHR_MAINTENANCE_8_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_8_EXTENSION_NAME "VK_KHR_maintenance8"
#endif
#ifndef VK_KHR_MAINTENANCE_9_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_9_EXTENSION_NAME "VK_KHR_maintenance9"
#endif
// Sanitize macros
#undef CreateEvent
#undef CreateSemaphore

409
src/video_core/vulkan_common/vulkan_device.cpp

@ -416,7 +416,9 @@ 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;
// uncomment this if you want per-device overrides :P
// const u32 device_id = properties.properties.deviceID;
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
const bool is_amd_driver =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
@ -427,7 +429,6 @@ 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;
if ((is_mvk || is_qualcomm || is_turnip || is_arm) && !is_suitable) {
@ -480,11 +481,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
is_warp_potentially_bigger = !extensions.subgroup_size_control ||
properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize;
is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
is_virtual = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU;
is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER ||
properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU;
supports_d24_depth =
IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal);
@ -494,7 +490,32 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
CollectPhysicalMemoryInfo();
CollectToolingInfo();
// Driver-specific handling for VK_EXT_custom_border_color
// On some Qualcomm/Turnip/ARM drivers the extension may be partially implemented.
// Disable completely if no feature bits are reported to avoid crashes/undefined behavior.
if (is_qualcomm || is_turnip || is_arm) {
const bool has_any_custom_border_color =
features.custom_border_color.customBorderColors ||
features.custom_border_color.customBorderColorWithoutFormat;
if (!has_any_custom_border_color) {
LOG_WARNING(
Render_Vulkan,
"Disabling VK_EXT_custom_border_color on '{}' — no usable features reported",
properties.driver.driverName);
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
} else {
LOG_INFO(Render_Vulkan,
"VK_EXT_custom_border_color enabled on '{}' (partial support detected)",
properties.driver.driverName);
}
}
if (is_qualcomm) {
LOG_WARNING(Render_Vulkan,
"Qualcomm drivers have a slow VK_KHR_push_descriptor implementation");
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
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);
@ -533,37 +554,18 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) {
LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math");
features.shader_float16_int8.shaderFloat16 = false;
} else if (arch <= NvidiaArchitecture::Arch_Volta) {
if (nv_major_version < 527) {
LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor");
//RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
}
}
if (nv_major_version >= 510) {
LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits");
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;
}
}
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;
}
// Dynamic state blacklists moved to GetSuitability() for proper ordering
sets_per_pool = 64;
if (is_amd_driver) {
@ -576,14 +578,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
has_broken_cube_compatibility = true;
}
}
if (is_qualcomm) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) {
has_broken_parallel_compiling = true;
}
}
if (extensions.sampler_filter_minmax && is_amd) {
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
if (!features.shader_float16_int8.shaderFloat16) {
@ -594,23 +594,77 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
}
// VertexInputDynamicState blacklist moved to GetSuitability() for proper ordering
if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) {
// Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being.
LOG_WARNING(Render_Vulkan, "Intel has broken float16 math");
features.shader_float16_int8.shaderFloat16 = false;
}
if (is_intel_windows) {
LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits");
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) {
// Qualcomm driver version where VK_KHR_maintenance5 and A1B5G5R5 become reliable
constexpr uint32_t QUALCOMM_FIXED_DRIVER_VERSION = VK_MAKE_VERSION(512, 800, 1);
// Check if VK_KHR_maintenance5 is supported
if (extensions.maintenance5 &&
properties.properties.driverVersion >= QUALCOMM_FIXED_DRIVER_VERSION) {
LOG_INFO(Render_Vulkan,
"Qualcomm driver supports VK_KHR_maintenance5, disabling BGR emulation");
must_emulate_bgr565 = false;
} else {
LOG_WARNING(Render_Vulkan,
"Qualcomm driver doesn't support native BGR, emulating formats");
must_emulate_bgr565 = true;
}
} else if (is_turnip) {
// Mesa Turnip added support for maintenance5 in Mesa 25.0
if (extensions.maintenance5) {
LOG_INFO(Render_Vulkan,
"Turnip driver supports VK_KHR_maintenance5, disabling BGR emulation");
must_emulate_bgr565 = false;
} else {
LOG_WARNING(Render_Vulkan,
"Turnip driver doesn't support native BGR, emulating formats");
must_emulate_bgr565 = true;
}
} else if (is_arm) {
// ARM Mali: stop emulating BGR5 formats when VK_KHR_maintenance5 is available
if (extensions.maintenance5) {
LOG_INFO(Render_Vulkan,
"ARM driver supports VK_KHR_maintenance5, disabling BGR emulation");
must_emulate_bgr565 = false;
} else {
LOG_WARNING(Render_Vulkan, "ARM driver doesn't support native BGR, emulating formats");
must_emulate_bgr565 = true;
}
}
if (extensions.push_descriptor && is_intel_anv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version >= VK_MAKE_API_VERSION(0, 22, 3, 0) &&
version < VK_MAKE_API_VERSION(0, 23, 2, 0)) {
// Disable VK_KHR_push_descriptor due to
// mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc
LOG_WARNING(Render_Vulkan,
"ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor");
// RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
}
} else if (extensions.push_descriptor && is_nvidia) {
const auto arch = GetNvidiaArch();
if (arch <= NvidiaArchitecture::Arch_Pascal) {
LOG_WARNING(Render_Vulkan,
"Pascal and older architectures have broken VK_KHR_push_descriptor");
// RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
}
}
if (is_mvk) {
@ -622,8 +676,10 @@ 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) {
// Ensure proper vertex input bindings limit for Qualcomm hardware
LOG_WARNING(Render_Vulkan, "{}: Ensuring maxVertexInputBindings = 32",
is_turnip ? "Turnip" : "Qualcomm");
properties.properties.limits.maxVertexInputBindings = 32;
}
@ -634,45 +690,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
}
if (!extensions.extended_dynamic_state2 && extensions.extended_dynamic_state3) {
LOG_INFO(Render_Vulkan,
"Removing extendedDynamicState3 due to missing extendedDynamicState2");
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;
}
// 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_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) && Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(Render_Vulkan, "Driver has broken dynamic state, forcing to 0 to prevent graphical issues");
Settings::values.dyna_state.SetValue(0);
}
switch (Settings::values.dyna_state.GetValue()) {
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);
dynamic_state3_blending = false;
dynamic_state3_enables = false;
break;
}
if (!extensions.extended_dynamic_state) {
Settings::values.vertex_input_dynamic_state.SetValue(false);
}
// Intel iGPU/MoltenVK blacklist moved to GetSuitability() for proper ordering
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);
#ifdef ANDROID
// Stock Qualcomm and ARM Mali drivers don't report VK_FORMAT_*_SSCALED/USCALED formats
// Turnip implements them in software, so only force emulation for stock drivers
if ((is_qualcomm && !is_turnip) || is_arm) {
must_emulate_scaled_formats = true;
LOG_INFO(Render_Vulkan,
"Mobile GPU detected: forcing scaled format emulation (hardware limitation)");
} else {
must_emulate_scaled_formats = false;
}
#else
// Desktop GPUs support scaled formats natively
must_emulate_scaled_formats = false;
#endif
logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld);
@ -687,6 +720,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
if (extensions.memory_budget) {
flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
}
const bool is_integrated =
properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
const VmaAllocatorCreateInfo allocator_info{
.flags = flags,
.physicalDevice = physical,
@ -713,15 +748,32 @@ Device::~Device() {
VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage,
FormatType format_type) const {
if (IsFormatSupported(wanted_format, wanted_usage, format_type)) {
return wanted_format;
// Critical: Even if format is "supported", check for STORAGE + HDR + no MSAA support
// Driver may report STORAGE_IMAGE_BIT but shaderStorageImageMultisample=false means
// it will fail at runtime when used with MSAA (CopyImageMSAA silently fails)
const bool requests_storage = (wanted_usage & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0;
const bool is_hdr_format = wanted_format == VK_FORMAT_B10G11R11_UFLOAT_PACK32;
// If driver doesn't support shader storage image with MSAA, and we're requesting storage
// for an HDR format (which will likely be used with MSAA), force fallback
if (requests_storage && is_hdr_format && !features.features.shaderStorageImageMultisample) {
LOG_WARNING(Render_Vulkan,
"Format {} reports STORAGE_IMAGE_BIT but driver doesn't support "
"shaderStorageImageMultisample. Forcing fallback for MSAA compatibility.",
wanted_format);
// Continue to alternatives search below
} else {
return wanted_format;
}
}
// The wanted format is not supported by hardware, search for alternatives
const VkFormat* alternatives = GetFormatAlternatives(wanted_format);
if (alternatives == nullptr) {
LOG_ERROR(Render_Vulkan,
"Format={} with usage={} and type={} has no defined alternatives and host "
"hardware does not support it",
wanted_format, wanted_usage, format_type);
"Format={} (0x{:X}) with usage={} and type={} has no defined alternatives and host "
"hardware does not support it. Driver: {} Device: {}",
wanted_format, static_cast<u32>(wanted_format), wanted_usage, format_type,
GetDriverName(), properties.properties.deviceName);
return wanted_format;
}
@ -1115,6 +1167,153 @@ bool Device::GetSuitability(bool requires_swapchain) {
}
}
// CRITICAL: Apply driver-specific feature workarounds BEFORE validation
// These blacklists disable broken features on specific drivers to prevent
// rendering issues and crashes.
// MUST execute before RemoveUnsuitableExtensions() calculates feature flags.
const VkDriverId driver_id = properties.driver.driverID;
const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV;
const bool is_amd_driver =
driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE;
const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS;
const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA;
const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK;
const bool is_integrated =
properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
// VK_DYNAMIC_STATE
// 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 Disable dynamic
// state on affected drivers
if ((is_mvk || (is_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) &&
Settings::values.dyna_state.GetValue() != 0) {
LOG_WARNING(
Render_Vulkan,
"Intel iGPU/MoltenVK: Forcing dyna_state=0 due to broken dynamic state implementation");
Settings::values.dyna_state.SetValue(0);
}
// VK_EXT_extended_dynamic_state
// RADV < 21.2.0: Broken ExtendedDynamicState implementation
// Disable entire extension on old drivers
if (extensions.extended_dynamic_state && is_radv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) {
LOG_WARNING(Render_Vulkan,
"RADV < 21.2.0: Disabling broken VK_EXT_extended_dynamic_state");
features.extended_dynamic_state.extendedDynamicState = false;
}
}
// VK_EXT_extended_dynamic_state2
// RADV < 22.3.1: Broken ExtendedDynamicState2 implementation
// Disable entire extension on old drivers
if (extensions.extended_dynamic_state2 && is_radv) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) {
LOG_WARNING(Render_Vulkan,
"RADV < 22.3.1: Disabling broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false;
}
}
// Qualcomm Adreno 7xx (drivers 676.0 - 679.x): Broken ExtendedDynamicState2
// Disable ExtendedDynamicState2 on affected driver versions
if (extensions.extended_dynamic_state2 && is_qualcomm) {
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) &&
version < VK_MAKE_API_VERSION(0, 0, 680, 0)) {
LOG_WARNING(
Render_Vulkan,
"Qualcomm Adreno 7xx (676-679): Disabling broken VK_EXT_extended_dynamic_state2");
features.extended_dynamic_state2.extendedDynamicState2 = false;
}
}
// VK_EXT_extended_dynamic_state3
// AMD/Samsung: Broken extendedDynamicState3ColorBlendEquation
// Disable blend equation dynamic state, force static pipeline state
if (extensions.extended_dynamic_state3 &&
(is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) {
LOG_WARNING(Render_Vulkan,
"AMD/Samsung: Disabling broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
}
// RADV: Broken extendedDynamicState3ColorBlendEquation
// Disable problematic features based on driver version < 23.1.0
if (extensions.extended_dynamic_state3 && is_radv) {
LOG_WARNING(Render_Vulkan,
"RADV: Disabling broken extendedDynamicState3ColorBlendEquation");
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false;
features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false;
const u32 version = (properties.properties.driverVersion << 3) >> 3;
if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) {
LOG_WARNING(Render_Vulkan, "RADV < 23.1.0: Disabling broken depth clamp dynamic state");
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false;
}
}
// VK_EXT_vertex_input_dynamic_state
// RADV + RDNA2: Broken VertexInputDynamicState on RDNA2 hardware
// Disable VertexInputDynamicState on RDNA2
if (extensions.vertex_input_dynamic_state && is_radv) {
const bool is_rdna2 =
supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME);
if (is_rdna2) {
LOG_WARNING(Render_Vulkan,
"RADV + RDNA2: Disabling broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
}
}
// Qualcomm: Broken VertexInputDynamicState implementation
// Disable VertexInputDynamicState on all Qualcomm drivers
if (extensions.vertex_input_dynamic_state && is_qualcomm) {
LOG_WARNING(Render_Vulkan, "Qualcomm: Disabling broken VK_EXT_vertex_input_dynamic_state");
features.vertex_input_dynamic_state.vertexInputDynamicState = 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");
features.vertex_input_dynamic_state.vertexInputDynamicState = false;
}
}
// If user setting is dyna_state=0, disable all dynamic state features
if (Settings::values.dyna_state.GetValue() == 0) {
LOG_INFO(Render_Vulkan,
"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;
// Note: vertex_input_dynamic_state has independent toggle, NOT affected by dyna_state=0
}
// Return whether we were suitable.
return suitable;
}
@ -1168,15 +1367,13 @@ void Device::RemoveUnsuitableExtensions() {
// VK_EXT_provoking_vertex
if (Settings::values.provoking_vertex.GetValue()) {
extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast
&& features.provoking_vertex
.transformFeedbackPreservesProvokingVertex;
RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex,
features.provoking_vertex,
extensions.provoking_vertex =
features.provoking_vertex.provokingVertexLast &&
features.provoking_vertex.transformFeedbackPreservesProvokingVertex;
RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
} else {
RemoveExtensionFeature(extensions.provoking_vertex,
features.provoking_vertex,
RemoveExtensionFeature(extensions.provoking_vertex, features.provoking_vertex,
VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME);
}
@ -1213,12 +1410,36 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback,
VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME);
// VK_EXT_robustness2
extensions.robustness_2 =
features.robustness2.robustBufferAccess2 && features.robustness2.robustImageAccess2;
RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2,
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
// VK_EXT_image_robustness
extensions.image_robustness = features.image_robustness.robustImageAccess;
RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness,
VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME);
// VK_EXT_swapchain_maintenance1
extensions.swapchain_maintenance1 =
loaded_extensions.contains(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
RemoveExtensionIfUnsuitable(extensions.swapchain_maintenance1,
VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME);
// VK_EXT_vertex_input_dynamic_state
extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
if (Settings::values.vertex_input_dynamic_state.GetValue()) {
extensions.vertex_input_dynamic_state =
features.vertex_input_dynamic_state.vertexInputDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
} else {
RemoveExtensionFeature(extensions.vertex_input_dynamic_state,
features.vertex_input_dynamic_state,
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
LOG_INFO(Render_Vulkan, "Vertex Input Dynamic State disabled by user setting");
}
// VK_KHR_pipeline_executable_properties
if (Settings::values.renderer_shader_feedback.GetValue()) {
@ -1296,6 +1517,8 @@ void Device::CollectPhysicalMemoryInfo() {
// Calculate limits using memory budget
VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
const bool is_integrated =
properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;
const auto mem_info =
physical.GetMemoryProperties(extensions.memory_budget ? &budget : nullptr);
const auto& mem_properties = mem_info.memoryProperties;

161
src/video_core/vulkan_common/vulkan_device.h

@ -58,6 +58,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \
FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \
FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \
FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
pipeline_executable_properties) \
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
@ -75,6 +76,7 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \
EXTENSION(EXT, TOOLING_INFO, tooling_info) \
EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \
EXTENSION(EXT, SWAPCHAIN_MAINTENANCE_1, swapchain_maintenance1) \
EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \
EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \
EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \
@ -84,6 +86,15 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(KHR, SWAPCHAIN, swapchain) \
EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \
EXTENSION(KHR, IMAGE_FORMAT_LIST, image_format_list) \
EXTENSION(KHR, MAINTENANCE_1, maintenance1) \
EXTENSION(KHR, MAINTENANCE_2, maintenance2) \
EXTENSION(KHR, MAINTENANCE_3, maintenance3) \
EXTENSION(KHR, MAINTENANCE_4, maintenance4) \
EXTENSION(KHR, MAINTENANCE_5, maintenance5) \
EXTENSION(KHR, MAINTENANCE_6, maintenance6) \
EXTENSION(KHR, MAINTENANCE_7, maintenance7) \
EXTENSION(KHR, MAINTENANCE_8, maintenance8) \
EXTENSION(KHR, MAINTENANCE_9, maintenance9) \
EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \
EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \
EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \
@ -365,6 +376,12 @@ public:
return properties.subgroup_properties.supportedOperations & feature;
}
/// Returns true if subgroup operations are supported in the specified shader stage.
/// Mobile GPUs (Qualcomm Adreno) often only support subgroups in fragment/compute stages.
bool IsSubgroupSupportedForStage(VkShaderStageFlagBits stage) const {
return properties.subgroup_properties.supportedStages & stage;
}
/// Returns the maximum number of push descriptors.
u32 MaxPushDescriptors() const {
return properties.push_descriptor.maxPushDescriptors;
@ -520,6 +537,39 @@ public:
return extensions.custom_border_color;
}
/// Base Vulkan Dynamic State support checks.
/// These provide granular control over each base dynamic state, allowing individual states
/// to be disabled if broken driver implementations are detected at device initialization.
/// By default all states are enabled. If a specific driver has issues with certain states,
/// they can be disabled in vulkan_device.cpp constructor (see has_broken_compute pattern).
bool SupportsDynamicViewport() const {
return supports_dynamic_viewport;
}
bool SupportsDynamicScissor() const {
return supports_dynamic_scissor;
}
bool SupportsDynamicLineWidth() const {
return supports_dynamic_line_width;
}
bool SupportsDynamicDepthBias() const {
return supports_dynamic_depth_bias;
}
bool SupportsDynamicBlendConstants() const {
return supports_dynamic_blend_constants;
}
bool SupportsDynamicDepthBounds() const {
return supports_dynamic_depth_bounds;
}
bool SupportsDynamicStencilCompareMask() const {
return supports_dynamic_stencil_compare;
}
bool SupportsDynamicStencilWriteMask() const {
return supports_dynamic_stencil_write;
}
bool SupportsDynamicStencilReference() const {
return supports_dynamic_stencil_reference;
}
/// Returns true if the device supports VK_EXT_extended_dynamic_state.
bool IsExtExtendedDynamicStateSupported() const {
return extensions.extended_dynamic_state;
@ -554,6 +604,98 @@ public:
return dynamic_state3_enables;
}
// EDS2 granular feature checks
bool IsExtExtendedDynamicState2LogicOpSupported() const {
return extensions.extended_dynamic_state2 &&
features.extended_dynamic_state2.extendedDynamicState2LogicOp;
}
bool IsExtExtendedDynamicState2PatchControlPointsSupported() const {
return extensions.extended_dynamic_state2 &&
features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints;
}
// EDS3 granular feature checks
bool IsExtExtendedDynamicState3DepthClampEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable;
}
bool IsExtExtendedDynamicState3LogicOpEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable;
}
bool IsExtExtendedDynamicState3TessellationDomainOriginSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3TessellationDomainOrigin;
}
bool IsExtExtendedDynamicState3PolygonModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3PolygonMode;
}
bool IsExtExtendedDynamicState3RasterizationSamplesSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3RasterizationSamples;
}
bool IsExtExtendedDynamicState3SampleMaskSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3SampleMask;
}
bool IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable;
}
bool IsExtExtendedDynamicState3AlphaToOneEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable;
}
bool IsExtExtendedDynamicState3DepthClipEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3DepthClipEnable;
}
bool IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3DepthClipNegativeOneToOne;
}
bool IsExtExtendedDynamicState3LineRasterizationModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode;
}
bool IsExtExtendedDynamicState3LineStippleEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable;
}
bool IsExtExtendedDynamicState3ProvokingVertexModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3ProvokingVertexMode;
}
bool IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode;
}
bool IsExtExtendedDynamicState3SampleLocationsEnableSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3SampleLocationsEnable;
}
bool IsExtExtendedDynamicState3RasterizationStreamSupported() const {
return extensions.extended_dynamic_state3 &&
features.extended_dynamic_state3.extendedDynamicState3RasterizationStream;
}
/// Returns true if the device supports VK_EXT_filter_cubic
bool IsExtFilterCubicSupported() const {
return extensions.filter_cubic;
@ -833,9 +975,6 @@ private:
bool is_blit_depth24_stencil8_supported{}; ///< Support for blitting from and to D24S8.
bool is_blit_depth32_stencil8_supported{}; ///< Support for blitting from and to D32S8.
bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
bool is_integrated{}; ///< Is GPU an iGPU.
bool is_virtual{}; ///< Is GPU a virtual GPU.
bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device.
bool has_broken_compute{}; ///< Compute shaders can cause crashes
bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit
bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling.
@ -851,6 +990,22 @@ private:
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
u32 sets_per_pool{}; ///< Sets per Description Pool
/// Base Vulkan Dynamic State support flags (granular fallback for broken drivers).
/// All default to true. These can be individually disabled in vulkan_device.cpp
/// if specific broken driver implementations are detected during initialization.
/// This provides emergency protection against drivers that report support but crash/misbehave.
/// Pattern: Check driver/device and set to false in vulkan_device.cpp constructor.
bool supports_dynamic_viewport{true}; ///< VK_DYNAMIC_STATE_VIEWPORT
bool supports_dynamic_scissor{true}; ///< VK_DYNAMIC_STATE_SCISSOR
bool supports_dynamic_line_width{true}; ///< VK_DYNAMIC_STATE_LINE_WIDTH
bool supports_dynamic_depth_bias{true}; ///< VK_DYNAMIC_STATE_DEPTH_BIAS
bool supports_dynamic_blend_constants{true}; ///< VK_DYNAMIC_STATE_BLEND_CONSTANTS
bool supports_dynamic_depth_bounds{true}; ///< VK_DYNAMIC_STATE_DEPTH_BOUNDS
bool supports_dynamic_stencil_compare{true}; ///< VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK
bool supports_dynamic_stencil_write{true}; ///< VK_DYNAMIC_STATE_STENCIL_WRITE_MASK
bool supports_dynamic_stencil_reference{true}; ///< VK_DYNAMIC_STATE_STENCIL_REFERENCE
NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};
// Telemetry parameters

Loading…
Cancel
Save