diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp index 0ce73f289b..d1c08c1131 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,7 +18,7 @@ Id SharedPointer(EmitContext& ctx, Id offset, u32 index_offset = 0) { if (index_offset > 0) { index = ctx.OpIAdd(ctx.U32[1], index, ctx.Const(index_offset)); } - return ctx.profile.support_explicit_workgroup_layout + return ctx.uses_explicit_workgroup_layout ? ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, ctx.u32_zero_value, index) : ctx.OpAccessChain(ctx.shared_u32, ctx.shared_memory_u32, index); } @@ -155,7 +158,7 @@ Id EmitSharedAtomicExchange32(EmitContext& ctx, Id offset, Id value) { } Id EmitSharedAtomicExchange64(EmitContext& ctx, Id offset, Id value) { - if (ctx.profile.support_int64_atomics && ctx.profile.support_explicit_workgroup_layout) { + if (ctx.profile.support_shared_int64_atomics && ctx.uses_explicit_workgroup_layout) { const Id shift_id{ctx.Const(3U)}; const Id index{ctx.OpShiftRightArithmetic(ctx.U32[1], offset, shift_id)}; const Id pointer{ diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp index df05dad74a..dc398aa296 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -28,7 +31,7 @@ std::pair ExtractArgs(EmitContext& ctx, Id offset, u32 mask, u32 count) } // Anonymous namespace Id EmitLoadSharedU8(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{ ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer)); @@ -39,7 +42,7 @@ Id EmitLoadSharedU8(EmitContext& ctx, Id offset) { } Id EmitLoadSharedS8(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{ ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U8, pointer)); @@ -50,7 +53,7 @@ Id EmitLoadSharedS8(EmitContext& ctx, Id offset) { } Id EmitLoadSharedU16(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; return ctx.OpUConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer)); } else { @@ -60,7 +63,7 @@ Id EmitLoadSharedU16(EmitContext& ctx, Id offset) { } Id EmitLoadSharedS16(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; return ctx.OpSConvert(ctx.U32[1], ctx.OpLoad(ctx.U16, pointer)); } else { @@ -70,7 +73,7 @@ Id EmitLoadSharedS16(EmitContext& ctx, Id offset) { } Id EmitLoadSharedU32(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2)}; return ctx.OpLoad(ctx.U32[1], pointer); } else { @@ -79,7 +82,7 @@ Id EmitLoadSharedU32(EmitContext& ctx, Id offset) { } Id EmitLoadSharedU64(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)}; return ctx.OpLoad(ctx.U32[2], pointer); } else { @@ -94,7 +97,7 @@ Id EmitLoadSharedU64(EmitContext& ctx, Id offset) { } Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)}; return ctx.OpLoad(ctx.U32[4], pointer); } @@ -110,7 +113,7 @@ Id EmitLoadSharedU128(EmitContext& ctx, Id offset) { } void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{ ctx.OpAccessChain(ctx.shared_u8, ctx.shared_memory_u8, ctx.u32_zero_value, offset)}; ctx.OpStore(pointer, ctx.OpUConvert(ctx.U8, value)); @@ -120,7 +123,7 @@ void EmitWriteSharedU8(EmitContext& ctx, Id offset, Id value) { } void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u16, ctx.shared_memory_u16, offset, 1)}; ctx.OpStore(pointer, ctx.OpUConvert(ctx.U16, value)); } else { @@ -130,7 +133,7 @@ void EmitWriteSharedU16(EmitContext& ctx, Id offset, Id value) { void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { Id pointer{}; - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { pointer = Pointer(ctx, ctx.shared_u32, ctx.shared_memory_u32, offset, 2); } else { const Id shift{ctx.Const(2U)}; @@ -141,7 +144,7 @@ void EmitWriteSharedU32(EmitContext& ctx, Id offset, Id value) { } void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u32x2, ctx.shared_memory_u32x2, offset, 3)}; ctx.OpStore(pointer, value); return; @@ -156,7 +159,7 @@ void EmitWriteSharedU64(EmitContext& ctx, Id offset, Id value) { } void EmitWriteSharedU128(EmitContext& ctx, Id offset, Id value) { - if (ctx.profile.support_explicit_workgroup_layout) { + if (ctx.uses_explicit_workgroup_layout) { const Id pointer{Pointer(ctx, ctx.shared_u32x4, ctx.shared_memory_u32x4, offset, 4)}; ctx.OpStore(pointer, value); return; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 9078cc22e5..5b92744806 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -371,7 +371,7 @@ Id CasFunction(EmitContext& ctx, Operation operation, Id value_type) { Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_pointer, Id value_type, Id memory_type, spv::Scope scope) { const bool is_shared{scope == spv::Scope::Workgroup}; - const bool is_struct{!is_shared || ctx.profile.support_explicit_workgroup_layout}; + const bool is_struct{!is_shared || ctx.uses_explicit_workgroup_layout}; const Id cas_func{CasFunction(ctx, operation, value_type)}; const Id zero{ctx.u32_zero_value}; const Id scope_id{ctx.Const(static_cast(scope))}; @@ -620,7 +620,11 @@ void EmitContext::DefineSharedMemory(const IR::Program& program) { return std::make_tuple(variable, element_pointer, pointer); }}; - if (profile.support_explicit_workgroup_layout) { + uses_explicit_workgroup_layout = + profile.support_explicit_workgroup_layout && + (!program.info.uses_int8 || profile.support_workgroup_layout_8bit_access) && + (!program.info.uses_int16 || profile.support_workgroup_layout_16bit_access); + if (uses_explicit_workgroup_layout) { AddExtension("SPV_KHR_workgroup_memory_explicit_layout"); AddCapability(spv::Capability::WorkgroupMemoryExplicitLayoutKHR); if (program.info.uses_int8) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 55385de0b0..e2d37a34c8 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -310,6 +310,10 @@ public: Id local_memory{}; + /// True when this shader's shared memory uses SPV_KHR_workgroup_memory_explicit_layout. + /// False when the host lacks the extension or a width this shader accesses natively. + bool uses_explicit_workgroup_layout{}; + Id shared_memory_u8{}; Id shared_memory_u16{}; Id shared_memory_u32{}; diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 8b6f7453d0..2699d5da8c 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -31,14 +31,16 @@ struct Profile { bool support_fp32_signed_zero_nan_preserve{}; bool support_fp64_signed_zero_nan_preserve{}; bool support_explicit_workgroup_layout{}; + bool support_workgroup_layout_8bit_access{}; + bool support_workgroup_layout_16bit_access{}; bool support_vote{}; - /// Bitmask over Shader::Stage of stages where the host supports subgroup operations u32 supported_subgroup_stages{0x7F}; bool support_viewport_index_layer_non_geometry{}; bool support_viewport_mask{}; bool support_typeless_image_loads{}; bool support_demote_to_helper_invocation{}; bool support_int64_atomics{}; + bool support_shared_int64_atomics{}; bool support_derivative_control{}; bool support_geometry_shader_passthrough{}; bool support_native_ndc{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 9a29599531..c8aa2f7b40 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -420,6 +420,10 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE && !has_broken_sz_inf_nan, .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), + .support_workgroup_layout_8bit_access = + device.IsWorkgroupMemoryExplicitLayout8BitSupported(), + .support_workgroup_layout_16bit_access = + device.IsWorkgroupMemoryExplicitLayout16BitSupported(), .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT), .supported_subgroup_stages = supported_subgroup_stages, .support_viewport_index_layer_non_geometry = @@ -429,6 +433,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .support_demote_to_helper_invocation = device.IsExtShaderDemoteToHelperInvocationSupported(), .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), + .support_shared_int64_atomics = device.IsSharedInt64AtomicsSupported(), .support_derivative_control = true, .support_geometry_shader_passthrough = device.IsNvGeometryShaderPassthroughSupported(), .support_native_ndc = device.IsExtDepthClipControlSupported(), diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 5c9bcf175a..cce4c7b6c7 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -501,13 +501,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Qualcomm drivers have slow push descriptor implementation"); RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); - LOG_WARNING(Render_Vulkan, - "Disabling 64-bit integer features on Qualcomm 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; #if defined(__ANDROID__) && defined(ARCHITECTURE_arm64) // BCn patching only safe on Android 9+ (API 28+). Older versions crash on driver load. @@ -560,7 +553,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR features.shader_float16_int8.shaderFloat16 = false; } - // Mali/ NVIDIA proprietary drivers: Shader stencil export not supported // Use hardware depth/stencil blits instead when available if (!extensions.shader_stencil_export) { LOG_INFO(Render_Vulkan, @@ -573,8 +565,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (!is_blit_depth24_stencil8_supported && !is_blit_depth32_stencil8_supported) { LOG_WARNING(Render_Vulkan, - "NVIDIA: Neither shader export nor hardware blits available for " - "depth/stencil. Performance may be degraded."); + "Neither shader export nor hardware blits available for " + "depth/stencil."); } } } @@ -656,17 +648,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } const auto dyna_state = Settings::values.dyna_state.GetValue(); - - // Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp - // This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs: - // Level 0 = Core Dynamic States only (Vulkan 1.0) - // Level 1 = Core + VK_EXT_extended_dynamic_state - // Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 - // Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3 - switch (dyna_state) { case Settings::ExtendedDynamicState::Disabled: - // Level 0: Disable all extended dynamic state extensions RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, @@ -677,7 +660,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR dynamic_state3_enables = false; break; case Settings::ExtendedDynamicState::EDS1: - // Level 1: Enable EDS1, disable EDS2 and EDS3 RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, @@ -686,7 +668,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR dynamic_state3_enables = false; break; case Settings::ExtendedDynamicState::EDS2: - // Level 2: Enable EDS1 + EDS2, disable EDS3 RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; @@ -694,12 +675,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR break; case Settings::ExtendedDynamicState::EDS3: default: - // Level 3: Enable all (EDS1 + EDS2 + EDS3) break; } - // VK_EXT_vertex_input_dynamic_state is independent from EDS - // It can be enabled even without extended_dynamic_state if (!Settings::values.vertex_input_dynamic_state.GetValue()) { RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); } @@ -1281,8 +1259,7 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); // VK_KHR_shader_atomic_int64 - extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics && - features.shader_atomic_int64.shaderSharedInt64Atomics; + extensions.shader_atomic_int64 = features.shader_atomic_int64.shaderBufferInt64Atomics; RemoveExtensionFeatureIfUnsuitable(extensions.shader_atomic_int64, features.shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); @@ -1331,10 +1308,7 @@ void Device::RemoveUnsuitableExtensions() { // VK_KHR_workgroup_memory_explicit_layout extensions.workgroup_memory_explicit_layout = - features.features.shaderInt16 && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout && - features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess && - features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess && features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayoutScalarBlockLayout; RemoveExtensionFeatureIfUnsuitable(extensions.workgroup_memory_explicit_layout, features.workgroup_memory_explicit_layout, diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index e0e6dde015..d941ca5b30 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -488,6 +488,18 @@ FN_MAX_LIMIT_LIST return extensions.workgroup_memory_explicit_layout; } + /// Returns true if the device supports 8-bit accesses to workgroup explicit layout memory. + bool IsWorkgroupMemoryExplicitLayout8BitSupported() const { + return extensions.workgroup_memory_explicit_layout && + features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout8BitAccess; + } + + /// Returns true if the device supports 16-bit accesses to workgroup explicit layout memory. + bool IsWorkgroupMemoryExplicitLayout16BitSupported() const { + return extensions.workgroup_memory_explicit_layout && features.features.shaderInt16 && + features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout16BitAccess; + } + /// Returns true if the device supports VK_KHR_image_format_list. bool IsKhrImageFormatListSupported() const { return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2; @@ -716,9 +728,16 @@ FN_MAX_LIMIT_LIST features.provoking_vertex.transformFeedbackPreservesProvokingVertex; } - /// Returns true if the device supports VK_KHR_shader_atomic_int64. + /// Returns true if the device supports int64 atomics on storage buffers. bool IsExtShaderAtomicInt64Supported() const { - return extensions.shader_atomic_int64; + return extensions.shader_atomic_int64 && + features.shader_atomic_int64.shaderBufferInt64Atomics; + } + + /// Returns true if the device supports int64 atomics on workgroup (shared) memory. + bool IsSharedInt64AtomicsSupported() const { + return extensions.shader_atomic_int64 && + features.shader_atomic_int64.shaderSharedInt64Atomics; } bool IsExtConditionalRendering() const {