Browse Source

[vulkan] Relaxing Workgroup Memory Explicit Layout flags + INT64 Atomics

vk-experiments5
CamilleLaVey 3 months ago
parent
commit
1ba7705279
  1. 7
      src/shader_recompiler/backend/spirv/emit_spirv_atomic.cpp
  2. 27
      src/shader_recompiler/backend/spirv/emit_spirv_shared_memory.cpp
  3. 8
      src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
  4. 4
      src/shader_recompiler/backend/spirv/spirv_emit_context.h
  5. 4
      src/shader_recompiler/profile.h
  6. 5
      src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
  7. 32
      src/video_core/vulkan_common/vulkan_device.cpp
  8. 23
      src/video_core/vulkan_common/vulkan_device.h

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

27
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<Id, Id> 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;

8
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<u32>(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) {

4
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{};

4
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{};

5
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(),

32
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,

23
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 {

Loading…
Cancel
Save