Compare commits

...

25 Commits

Author SHA1 Message Date
lizzie
8e80396ee0 fix 2025-11-06 22:49:49 +00:00
CamilleLaVey
672011f302 Added Maintenance5 to 9 headers 2025-11-06 18:03:30 -04:00
CamilleLaVey
bb1d1e484d Added Maintenance EXT's for better driver compatibility 2025-11-06 17:47:05 -04:00
CamilleLaVey
5a03ef7fba Disabling BGR5 emulation format on 8 Elite stock drivers 2025-11-06 17:37:16 -04:00
CamilleLaVey
88555a17be Forcing hardware handling for decode formats on Android 2025-11-06 15:55:10 -04:00
CamilleLaVey
9600f99b1a Maintance for older driver statements and new handling on QCOM drivers 2025-11-06 14:39:39 -04:00
CamilleLaVey
7bed3bb947 Disabling VK_KHR_shader_float_controls on QCOM and ARM 2025-11-06 12:10:33 -04:00
CamilleLaVey
380112bcb4 Added QCOM helper for driver detections 2025-11-06 09:59:01 -04:00
CamilleLaVey
2685f832e5 Profilling FP16 to FTZ usage in QCOM drivers 2025-11-06 09:22:57 -04:00
CamilleLaVey
d65afcda81 revert Added FTZ optimizations and adjust pipeline float control handling for Qualcomm drivers
revert Added FTZ optimizations and adjust pipeline float control handling for Qualcomm drivers
2025-11-06 13:34:05 +01:00
CamilleLaVey
1cb88492a8 revert [vk] Add back VIDS but disable on EDS0 (#2957)
revert [vk] Add back VIDS but disable on EDS0 (#2957)

Should fix regressions from removing it while also fixing black screen with it enabled

Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2957
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: JPikachu <jpikachu@eden-emu.dev>
Co-committed-by: JPikachu <jpikachu@eden-emu.dev>
2025-11-06 03:12:38 +01:00
CamilleLaVey
1b06fa6658 revert [vk] disable VK_EXT_vertex_input_dynamic_state again (#2954)
revert [vk] disable VK_EXT_vertex_input_dynamic_state again (#2954)

DO NOT REMOVE THIS! EVER! EVEN IF MESA CLAIMS TO FIX IT!

A few months ago, Aleksandr and I did extensive testing on a 6600 and
6950XT and were able to confirm that VK_EXT_vertex_input_dynamic_state
is indeed broken beyond repair on RADV. MESA has claimed multiple times
to fix this, yet it's never budged once (average GitLab users)

Most games literally do not work without this. DO. NOT. REMOVE.
EVER!

Signed-off-by: crueter <crueter@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2954
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-11-06 03:12:11 +01:00
CamilleLaVey
af1b610fbb revert [vk] Clean up Extended Dynamic State code (#2947)
revert [vk] Clean up Extended Dynamic State code (#2947)

- Removed forced dynamic state 0 logic
- Restore and update the removal of broken states on certain drivers
- Inside 'vk_rasterizer.cpp' make 'UpdateDynamicStates' only check device
   capabilities directly instead of relying on user settings.
- Add a 'Force Unsupported Extensions' toggle that:
   "Bypasses all driver workarounds and safety checks.
     May cause crashes, graphical glitches, or instability.
     Only enable for testing purposes."

Cleans up EDS logic and adds new 'Force Unsupported Extensions' toggle,
Fixes vertex explosions in 'Pokemon: Legends ZA' when EDS is set to 0.

Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2947
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: JPikachu <jpikachu@eden-emu.dev>
Co-committed-by: JPikachu <jpikachu@eden-emu.dev>
2025-11-06 03:11:31 +01:00
CamilleLaVey
9a266e60b6 Merge branch 'floatopsfixes1' of https://git.eden-emu.dev/eden-emu/eden into floatopsfixes1 2025-11-05 22:10:11 -04:00
CamilleLaVey
1fc9c3f6ff Added FTZ optimizations and adjust pipeline float control handling for Qualcomm drivers 2025-11-05 22:10:02 -04:00
CamilleLaVey
a0f08704f1 revert 612da00d1b
revert [vk] Clean up Extended Dynamic State code (#2947)

- Removed forced dynamic state 0 logic
- Restore and update the removal of broken states on certain drivers
- Inside 'vk_rasterizer.cpp' make 'UpdateDynamicStates' only check device
   capabilities directly instead of relying on user settings.
- Add a 'Force Unsupported Extensions' toggle that:
   "Bypasses all driver workarounds and safety checks.
     May cause crashes, graphical glitches, or instability.
     Only enable for testing purposes."

Cleans up EDS logic and adds new 'Force Unsupported Extensions' toggle,
Fixes vertex explosions in 'Pokemon: Legends ZA' when EDS is set to 0.

Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2947
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: JPikachu <jpikachu@eden-emu.dev>
Co-committed-by: JPikachu <jpikachu@eden-emu.dev>
2025-11-06 02:58:21 +01:00
CamilleLaVey
c6c3edc95c revert 1c4dae066b
revert [vk] Add back VIDS but disable on EDS0 (#2957)

Should fix regressions from removing it while also fixing black screen with it enabled

Co-authored-by: JPikachu <jpikachu.eden@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2957
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: JPikachu <jpikachu@eden-emu.dev>
Co-committed-by: JPikachu <jpikachu@eden-emu.dev>
2025-11-06 02:57:49 +01:00
CamilleLaVey
76c5de0443 revert 9406438d51
revert [vk] disable VK_EXT_vertex_input_dynamic_state again (#2954)

DO NOT REMOVE THIS! EVER! EVEN IF MESA CLAIMS TO FIX IT!

A few months ago, Aleksandr and I did extensive testing on a 6600 and
6950XT and were able to confirm that VK_EXT_vertex_input_dynamic_state
is indeed broken beyond repair on RADV. MESA has claimed multiple times
to fix this, yet it's never budged once (average GitLab users)

Most games literally do not work without this. DO. NOT. REMOVE.
EVER!

Signed-off-by: crueter <crueter@eden-emu.dev>

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2954
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2025-11-06 02:57:19 +01:00
CamilleLaVey
9ffead7e7a Re-enabling VK_KHR_shader_float_controls for further testing 2025-11-05 19:23:37 -04:00
CamilleLaVey
22bb942947 Merge branch 'floatopsfixes1' of https://git.eden-emu.dev/eden-emu/eden into floatopsfixes1 2025-11-05 16:50:26 -04:00
CamilleLaVey
3af7aafc25 Add suitability for Robustness2 2025-11-05 16:50:18 -04:00
CamilleLaVey
3a8677597f Fix parenthesis/ build error 2025-11-05 21:25:42 +01:00
CamilleLaVey
16ae756da3 Improve float operations for ARM and Qualcomm drivers and other extensions workarounds 2025-11-05 21:25:42 +01:00
CamilleLaVey
004264d2a6 Fix parenthesis/ build error 2025-11-05 14:33:15 -04:00
CamilleLaVey
4bbde3e5ec Improve float operations for ARM and Qualcomm drivers and other extensions workarounds 2025-11-05 13:59:47 -04:00
9 changed files with 309 additions and 48 deletions

View File

@@ -10,6 +10,8 @@
#include "video_core/rasterizer_interface.h"
#include "common/android/multiplayer/multiplayer.h"
#include <network/network.h>
#include "common/settings.h"
#include "common/logging/log.h"
static JavaVM *s_java_vm;
@@ -524,6 +526,13 @@ namespace Common::Android {
s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;");
env->DeleteLocalRef(patch_class);
// Prefer hardware decoding on Android by default, forcing this setting will
// make the native side attempt GPU decoding first. If the platform lacks a usable
// FFmpeg HW device, FFmpeg will fall back to CPU automatically.
Settings::values.nvdec_emulation.SetValue(Settings::NvdecEmulation::Gpu);
LOG_INFO(HW_GPU, "Android JNI_OnLoad: forced nvdec_emulation = GPU");
const jclass double_class = env->FindClass("java/lang/Double");
s_double_class = reinterpret_cast<jclass>(env->NewGlobalRef(double_class));
s_double_constructor = env->GetMethodID(double_class, "<init>", "(D)V");

View File

@@ -349,7 +349,9 @@ struct Values {
linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer};
SwitchableSetting<AstcDecodeMode, true> accelerate_astc{linkage,
#ifdef ANDROID
AstcDecodeMode::Cpu,
// Most modern Android devices have native ASTC support
// and benefit from GPU decoding. Default to GPU there.
AstcDecodeMode::Gpu,
#else
AstcDecodeMode::Gpu,
#endif

View File

@@ -77,7 +77,12 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
ASSERT(false);
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
if (device.IsKhrSamplerMirrorClampToEdgeSupported()) {
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
}
// Fallback when the sampler mirror clamp extension isn't present.
// Use CLAMP_TO_EDGE as the safest approximation.
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case Tegra::Texture::WrapMode::MirrorOnceBorder:
UNIMPLEMENTED();
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;

View File

@@ -31,6 +31,10 @@ namespace Vulkan {
struct GraphicsPipelineCacheKey {
std::array<u64, 6> unique_hashes;
FixedPipelineState state;
// Per-pipeline float control choices (selected at pipeline key time).
// 0 = disabled, 1 = enabled
uint8_t use_ftz_f32{};
uint8_t use_ftz_f16{};
size_t Hash() const noexcept;
@@ -41,12 +45,12 @@ struct GraphicsPipelineCacheKey {
}
size_t Size() const noexcept {
return sizeof(unique_hashes) + state.Size();
return sizeof(unique_hashes) + state.Size() + sizeof(use_ftz_f32) + sizeof(use_ftz_f16);
}
};
static_assert(std::has_unique_object_representations_v<GraphicsPipelineCacheKey>);
// The key is compared/hashed using a custom Size() and memcmp over the
// meaningful bytes. Ensure it's trivially copyable so memcmp/hash are safe.
static_assert(std::is_trivially_copyable_v<GraphicsPipelineCacheKey>);
static_assert(std::is_trivially_constructible_v<GraphicsPipelineCacheKey>);
} // namespace Vulkan

View File

@@ -327,20 +327,28 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
.support_int64 = device.IsShaderInt64Supported(),
.support_vertex_instance_id = false,
.support_float_controls = device.IsKhrShaderFloatControlsSupported(),
.support_separate_denorm_behavior =
float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
.support_separate_rounding_mode =
float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL,
.support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE,
.support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE,
.support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE,
.support_fp32_denorm_flush = float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE,
.support_fp16_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat16 != VK_FALSE,
.support_fp32_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE,
.support_fp64_signed_zero_nan_preserve =
float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE,
// Only enable per-size float control capabilities when the KHR_shader_float_controls
// extension is actually enabled on the device and the driver reports explicit support
// for the individual properties. This avoids enabling functionality when the extension
// was removed due to driver workarounds.
.support_separate_denorm_behavior = device.IsKhrShaderFloatControlsSupported() &&
(float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL),
.support_separate_rounding_mode = device.IsKhrShaderFloatControlsSupported() &&
(float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL),
.support_fp16_denorm_preserve = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderDenormPreserveFloat16 == VK_TRUE),
.support_fp32_denorm_preserve = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderDenormPreserveFloat32 == VK_TRUE),
.support_fp16_denorm_flush = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE),
.support_fp32_denorm_flush = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE),
.support_fp16_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderSignedZeroInfNanPreserveFloat16 == VK_TRUE),
.support_fp32_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderSignedZeroInfNanPreserveFloat32 == VK_TRUE),
.support_fp64_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() &&
(float_control.shaderSignedZeroInfNanPreserveFloat64 == VK_TRUE),
.support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(),
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
.support_viewport_index_layer_non_geometry =
@@ -368,13 +376,13 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA,
.has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS,
.has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
.has_broken_spirv_position_input = device.IsQualcomm(),
.has_broken_unsigned_image_offsets = false,
.has_broken_signed_operations = false,
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
.ignore_nan_fp_comparisons = false,
.has_broken_spirv_subgroup_mask_vector_extract_dynamic =
driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
device.IsQualcomm(),
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
@@ -438,6 +446,21 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() {
}
graphics_key.state.Refresh(*maxwell3d, dynamic_features);
// Decide per-pipeline FTZ (flush-to-zero) usage based on device float-controls
// properties and vendor-specific workarounds, going initially for Qualcomm drivers
const bool force_extensions = Settings::values.force_unsupported_extensions.GetValue();
const bool is_qualcomm = device.IsQualcomm();
const auto& float_control = device.FloatControlProperties();
const bool has_khr_float_controls = device.IsKhrShaderFloatControlsSupported();
const bool denorm_indep_all = float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL;
const bool denorm_indep_32 = denorm_indep_all || float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY;
// Allow FTZ when device reports support the device is a Qualcomm driver
// (we allow Qualcomm to opt-in automatically to work around driver reporting differences).
const bool allow_ftz_override = force_extensions || is_qualcomm;
graphics_key.use_ftz_f32 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE) && denorm_indep_32 && allow_ftz_override) ? 1 : 0;
graphics_key.use_ftz_f16 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE) && denorm_indep_all && allow_ftz_override) ? 1 : 0;
if (current_pipeline) {
GraphicsPipeline* const next{current_pipeline->Next(graphics_key)};
if (next) {
@@ -682,7 +705,24 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)};
ConvertLegacyToGeneric(program, runtime_info);
const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)};
// Forward a pipeline-specific profile to the SPIR-V emitter so it can
// enable/disable fast-math (FTZ) optimizations per-pipeline. We use the
// GraphicsPipelineCacheKey's FTZ choice to decide whether to allow
// fast-math. When fast-math is enabled we keep need_fastmath_off=false
// (allow optimizations); otherwise we set it to true to prevent unsafe
// transformations.
Shader::Profile emit_profile = profile;
// Per-pipeline override: control fast-math (FTZ) for FP32 via the pipeline key.
emit_profile.need_fastmath_off = (key.use_ftz_f32 == 0);
// Per-pipeline FP16 FTZ selection is stored in the pipeline key but was not
// previously propagated to the SPIR-V emitter. Some drivers (notably certain
// Qualcomm builds) report FP16 float-control support incorrectly. Respect the
// pipeline's FP16 FTZ choice here and disable emitting FP16 DenormFlush when
// the pipeline explicitly disables it.
if (key.use_ftz_f16 == 0) {
emit_profile.support_fp16_denorm_flush = false;
}
const std::vector<u32> code{EmitSPIRV(emit_profile, runtime_info, program, binding, this->optimize_spirv_output)};
device.SaveShader(code);
modules[stage_index] = BuildShader(device, code);
if (device.HasDebuggingToolAttached()) {

View File

@@ -222,7 +222,13 @@ template <typename Key, typename Envs>
void SerializePipeline(const Key& key, const Envs& envs, const std::filesystem::path& filename,
u32 cache_version) {
static_assert(std::is_trivially_copyable_v<Key>);
static_assert(std::has_unique_object_representations_v<Key>);
// Note: we relax the unique object representation requirement because some
// pipeline/key types (e.g. GraphicsPipelineCacheKey) contain unions or
// bitfield-backed types that do not guarantee "unique object
// representations" across compilers/platforms. We still require
// trivially-copyable so the raw byte serialization is well-defined for a
// given build. Be aware that serialized blobs may not be portable across
// builds with different compilers or packing rules.
SerializePipeline(std::span(reinterpret_cast<const char*>(&key), sizeof(key)),
std::span(envs.data(), envs.size()), filename, cache_version);
}

View File

@@ -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

View File

@@ -501,24 +501,38 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
}
if ((is_qualcomm || is_turnip) && !force_extensions) {
LOG_WARNING(Render_Vulkan,
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
// Some Qualcomm/Turnip drivers have a broken implementation of
// VK_EXT_custom_border_color, but on certain devices the driver may
// still implement a subset of the extension's functions. If the
// driver reports any of the useful custom border color features, allow
// them; otherwise remove the extension to avoid crashes.
const bool has_custom_border_colors =
features.custom_border_color.customBorderColors ||
features.custom_border_color.customBorderColorWithoutFormat;
if (!has_custom_border_colors) {
LOG_WARNING(Render_Vulkan,
"Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color");
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
} else {
LOG_INFO(Render_Vulkan,
"Partial VK_EXT_custom_border_color support detected on driver '{}'; enabling available features",
properties.driver.driverName);
// Keep extensions.custom_border_color set based on RemoveUnsuitableExtensions later.
}
}
if (is_qualcomm) {
if (is_qualcomm || is_arm) {
if (!force_extensions) {
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);
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
LOG_WARNING(Render_Vulkan,
"Disabling 64-bit integer features on Qualcomm and ARM Mali proprietary drivers");
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
features.shader_atomic_int64.shaderBufferInt64Atomics = false;
features.shader_atomic_int64.shaderSharedInt64Atomics = false;
features.features.shaderInt64 = false;
@@ -585,13 +599,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
}
}
if (extensions.extended_dynamic_state2 && is_qualcomm) {
if (extensions.extended_dynamic_state2 && (is_qualcomm || is_arm)) {
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) && !force_extensions) {
// Arm Mali Inmortalis drivers have broken extendedDynamicState2LogicOp.
// Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2.
LOG_WARNING(Render_Vulkan,
"Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2");
"Qualcomm Adreno 7xx and Arm Mali Inmortalis drivers have broken VK_EXT_extended_dynamic_state2");
RemoveExtensionFeature(extensions.extended_dynamic_state2,
features.extended_dynamic_state2,
VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME);
@@ -700,10 +715,24 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
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 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;
}
}
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) &&
@@ -1040,13 +1069,13 @@ bool Device::GetSuitability(bool requires_swapchain) {
// Some extensions are mandatory. Check those.
#define CHECK_EXTENSION(extension_name) \
if (!loaded_extensions.contains(extension_name)) { \
LOG_ERROR(Render_Vulkan, "Missing required extension {}", extension_name); \
suitable = false; \
LOG_ERROR(Render_Vulkan, "Missing required extension " extension_name); \
suitable = false; \
}
#define LOG_EXTENSION(extension_name) \
if (!loaded_extensions.contains(extension_name)) { \
LOG_INFO(Render_Vulkan, "Device doesn't support extension {}", extension_name); \
LOG_INFO(Render_Vulkan, "Device doesn't support extension " extension_name); \
}
FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION);
@@ -1178,9 +1207,55 @@ bool Device::GetSuitability(bool requires_swapchain) {
// Store base properties
properties.properties = properties2.properties;
// Diagnostic logging for shader float controls on Qualcomm/ARM drivers.
// Print the reported per-float-size properties so we can debug denorm/flush issues.
{
const auto driver_id = properties.driver.driverID;
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) {
const auto& fc = properties.float_controls;
LOG_INFO(Render_Vulkan,
"Driver '{}' id={} reports VK_KHR_shader_float_controls extension present={} -- "
"denormPreserveF16={} denormPreserveF32={} flushToZeroF16={} flushToZeroF32={} "
"denormBehaviorIndependence={} roundingModeIndependence={}",
properties.driver.driverName, driver_id, extensions.shader_float_controls,
(fc.shaderDenormPreserveFloat16 == VK_TRUE), (fc.shaderDenormPreserveFloat32 == VK_TRUE),
(fc.shaderDenormFlushToZeroFloat16 == VK_TRUE), (fc.shaderDenormFlushToZeroFloat32 == VK_TRUE),
fc.denormBehaviorIndependence, fc.roundingModeIndependence);
}
}
// Some drivers (notably Qualcomm and certain ARM drivers) misreport the
// VK_KHR_shader_float_controls capabilities. Disable the extension and
// clear the reported properties unless the user explicitly forces
// unsupported extensions via settings.
{
const auto driver_id = properties.driver.driverID;
const bool force_extensions = Settings::values.force_unsupported_extensions.GetValue();
if ((driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY ||
driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) &&
!force_extensions) {
LOG_WARNING(Render_Vulkan,
"Disabling VK_KHR_shader_float_controls for driver '{}' id={} due to unreliable float-control reporting",
properties.driver.driverName, driver_id);
extensions.shader_float_controls = false;
// Zero-out the structure to avoid accidental use of reported values.
properties.float_controls = {};
}
}
// Unload extensions if feature support is insufficient.
RemoveUnsuitableExtensions();
// Log final state of shader float controls extension on Qualcomm/ARM for diagnostics.
{
const auto driver_id = properties.driver.driverID;
if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) {
LOG_INFO(Render_Vulkan,
"Final shader float controls extension enabled={} after suitability checks for driver '{}' id={}",
extensions.shader_float_controls, properties.driver.driverName, driver_id);
}
}
// Check limits.
struct Limit {
u32 minimum;
@@ -1208,9 +1283,30 @@ bool Device::GetSuitability(bool requires_swapchain) {
}
void Device::RemoveUnsuitableExtensions() {
// Restrict NV-specific extensions to NVIDIA drivers only.
if (!IsNvidia()) {
RemoveExtension(extensions.device_diagnostics_config,
VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME);
RemoveExtension(extensions.geometry_shader_passthrough,
VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME);
RemoveExtension(extensions.viewport_array2, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME);
RemoveExtension(extensions.viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME);
}
// VK_EXT_custom_border_color
extensions.custom_border_color = features.custom_border_color.customBorderColors &&
features.custom_border_color.customBorderColorWithoutFormat;
// On most drivers we require both customBorderColors and
// customBorderColorWithoutFormat. However, some Qualcomm drivers expose a
// subset of the functionality; when running on Qualcomm, enable the
// extension if the driver reports any useful feature.
if (IsQualcomm()) {
extensions.custom_border_color =
features.custom_border_color.customBorderColors ||
features.custom_border_color.customBorderColorWithoutFormat;
} else {
extensions.custom_border_color =
features.custom_border_color.customBorderColors &&
features.custom_border_color.customBorderColorWithoutFormat;
}
RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color,
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
@@ -1226,6 +1322,14 @@ void Device::RemoveUnsuitableExtensions() {
RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control,
VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME);
// VK_EXT_robustness2
// Enable the extension only if at least one of the useful robustness2 features is present.
extensions.robustness2 = features.robustness2.nullDescriptor ||
features.robustness2.robustBufferAccess2 ||
features.robustness2.robustImageAccess2;
RemoveExtensionFeatureIfUnsuitable(extensions.robustness2, features.robustness2,
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
// VK_EXT_extended_dynamic_state
extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState;
RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state,

View File

@@ -90,7 +90,16 @@ VK_DEFINE_HANDLE(VmaAllocator)
EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \
EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \
EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights)
EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) \
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) \
// Define extensions which must be supported.
#define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \
@@ -445,6 +454,56 @@ public:
return extensions.shader_float_controls;
}
/// Returns true if VK_KHR_maintenance1 is enabled.
bool IsKhrMaintenance1Supported() const {
return extensions.maintenance1;
}
/// Returns true if VK_KHR_maintenance2 is enabled.
bool IsKhrMaintenance2Supported() const {
return extensions.maintenance2;
}
/// Returns true if VK_KHR_maintenance3 is enabled.
bool IsKhrMaintenance3Supported() const {
return extensions.maintenance3;
}
/// Returns true if VK_KHR_maintenance4 is enabled.
bool IsKhrMaintenance4Supported() const {
return extensions.maintenance4;
}
/// Returns true if VK_KHR_maintenance5 is enabled.
bool IsKhrMaintenance5Supported() const {
return extensions.maintenance5;
}
/// Returns true if VK_KHR_maintenance6 is enabled.
bool IsKhrMaintenance6Supported() const {
return extensions.maintenance6;
}
/// Returns true if VK_KHR_maintenance7 is enabled.
bool IsKhrMaintenance7Supported() const {
return extensions.maintenance7;
}
/// Returns true if VK_KHR_maintenance8 is enabled.
bool IsKhrMaintenance8Supported() const {
return extensions.maintenance8;
}
/// Returns true if VK_KHR_maintenance9 is enabled.
bool IsKhrMaintenance9Supported() const {
return extensions.maintenance9;
}
/// Returns true if VK_KHR_sampler_mirror_clamp_to_edge is enabled.
bool IsKhrSamplerMirrorClampToEdgeSupported() const {
return extensions.sampler_mirror_clamp_to_edge;
}
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
return extensions.workgroup_memory_explicit_layout;
@@ -727,6 +786,10 @@ public:
return properties.driver.driverID == VK_DRIVER_ID_MOLTENVK;
}
bool IsQualcomm() const noexcept {
return properties.driver.driverID == VK_DRIVER_ID_QUALCOMM_PROPRIETARY;
}
NvidiaArchitecture GetNvidiaArch() const noexcept {
return nvidia_arch;
}
@@ -783,11 +846,11 @@ private:
#define EXTENSION(prefix, macro_name, var_name) bool var_name{};
#define FEATURE(prefix, struct_name, macro_name, var_name) bool var_name{};
FOR_EACH_VK_FEATURE_1_1(FEATURE);
FOR_EACH_VK_FEATURE_1_2(FEATURE);
FOR_EACH_VK_FEATURE_1_3(FEATURE);
FOR_EACH_VK_FEATURE_EXT(FEATURE);
FOR_EACH_VK_EXTENSION(EXTENSION);
FOR_EACH_VK_FEATURE_1_1(FEATURE);
FOR_EACH_VK_FEATURE_1_2(FEATURE);
FOR_EACH_VK_FEATURE_1_3(FEATURE);
FOR_EACH_VK_FEATURE_EXT(FEATURE);
FOR_EACH_VK_EXTENSION(EXTENSION);
#undef EXTENSION
#undef FEATURE