From ace66343ceea1c965185d1d9960102712153b148 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 6 Jul 2026 03:07:48 -0400 Subject: [PATCH] [TEST] Force checks on subgroups --- .../backend/spirv/emit_spirv.cpp | 2 +- .../backend/spirv/emit_spirv_warp.cpp | 42 ++++++++++++++++++- .../backend/spirv/spirv_emit_context.cpp | 9 ++-- src/shader_recompiler/profile.h | 7 ++++ .../renderer_vulkan/vk_pipeline_cache.cpp | 15 +++++++ .../renderer_vulkan/vk_texture_cache.cpp | 9 +++- .../vulkan_common/vulkan_device.cpp | 2 + src/video_core/vulkan_common/vulkan_device.h | 5 +++ 8 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index f73fb233cb..7ab2bd4adf 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -435,7 +435,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct } if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles) && - profile.support_vote) { + profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) { ctx.AddCapability(spv::Capability::GroupNonUniformBallot); ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); if (!profile.warp_size_potentially_larger_than_guest) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp index 77ff8c5731..242426ec08 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.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 @@ -10,7 +13,14 @@ Id SubgroupScope(EmitContext& ctx) { return ctx.Const(static_cast(spv::Scope::Subgroup)); } +bool StageSupportsSubgroups(EmitContext& ctx) { + return ctx.profile.SupportsSubgroupStage(ctx.stage); +} + Id GetThreadId(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.u32_zero_value; + } return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); } @@ -68,6 +78,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask } Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { + if (!StageSupportsSubgroups(ctx)) { + return value; + } return ctx.OpSelect( ctx.U32[1], in_range, ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); @@ -89,6 +102,9 @@ Id EmitLaneId(EmitContext& ctx) { } Id EmitVoteAll(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return pred; + } if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); } @@ -102,6 +118,9 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) { } Id EmitVoteAny(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return pred; + } if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); } @@ -115,6 +134,9 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) { } Id EmitVoteEqual(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.true_value; + } if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); } @@ -129,6 +151,9 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) { } Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value); + } const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; if (!ctx.profile.warp_size_potentially_larger_than_guest) { return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); @@ -137,22 +162,37 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { } Id EmitSubgroupEqMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.Const(1u); + } return LoadMask(ctx, ctx.subgroup_mask_eq); } Id EmitSubgroupLtMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.u32_zero_value; + } return LoadMask(ctx, ctx.subgroup_mask_lt); } Id EmitSubgroupLeMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.Const(1u); + } return LoadMask(ctx, ctx.subgroup_mask_le); } Id EmitSubgroupGtMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.u32_zero_value; + } return LoadMask(ctx, ctx.subgroup_mask_gt); } Id EmitSubgroupGeMask(EmitContext& ctx) { + if (!StageSupportsSubgroups(ctx)) { + return ctx.Const(1u); + } return LoadMask(ctx, ctx.subgroup_mask_ge); } @@ -222,7 +262,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) { const Id three{ctx.Const(3U)}; - Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)}; + Id mask{GetThreadId(ctx)}; mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U)); mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b1bed263a6..eaa363a080 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -1447,7 +1447,7 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (info.uses_is_helper_invocation) { is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); } - if (info.uses_subgroup_mask) { + if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) { subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR); subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR); subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); @@ -1461,9 +1461,10 @@ void EmitContext::DefineInputs(const IR::Program& program) { Decorate(subgroup_mask_ge, spv::Decoration::Flat); } } - if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || - (profile.warp_size_potentially_larger_than_guest && - (info.uses_subgroup_vote || info.uses_subgroup_mask))) { + if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || + (profile.warp_size_potentially_larger_than_guest && + (info.uses_subgroup_vote || info.uses_subgroup_mask))) && + profile.SupportsSubgroupStage(stage)) { AddCapability(spv::Capability::GroupNonUniform); subgroup_local_invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index e95778a0de..0197a5f1d7 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -10,6 +10,8 @@ namespace Shader { +enum class Stage : u32; + struct Profile { u32 supported_spirv{0x00010000}; bool unified_descriptor_binding{}; @@ -32,6 +34,7 @@ struct Profile { bool support_fp64_signed_zero_nan_preserve{}; bool support_explicit_workgroup_layout{}; bool support_vote{}; + u32 supported_subgroup_stages{0x7F}; bool support_viewport_index_layer_non_geometry{}; bool support_viewport_mask{}; bool support_typeless_image_loads{}; @@ -95,6 +98,10 @@ struct Profile { u64 min_ssbo_alignment{}; u32 max_user_clip_distances{}; + + bool SupportsSubgroupStage(Stage stage) const { + return (supported_subgroup_stages & (1u << static_cast(stage))) != 0; + } }; } // namespace Shader diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7fd87b17cd..669d736218 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -353,6 +353,20 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; const VkDriverId driver_id{device.GetDriverID()}; + const VkShaderStageFlags subgroup_stages{device.GetSubgroupSupportedStages()}; + const auto subgroup_stage_bit{[subgroup_stages](VkShaderStageFlags flag, Shader::Stage stage) { + return (subgroup_stages & flag) != 0 ? (1u << static_cast(stage)) : 0u; + }}; + const u32 supported_subgroup_stages{ + subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexA) | + subgroup_stage_bit(VK_SHADER_STAGE_VERTEX_BIT, Shader::Stage::VertexB) | + subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, + Shader::Stage::TessellationControl) | + subgroup_stage_bit(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, + Shader::Stage::TessellationEval) | + subgroup_stage_bit(VK_SHADER_STAGE_GEOMETRY_BIT, Shader::Stage::Geometry) | + subgroup_stage_bit(VK_SHADER_STAGE_FRAGMENT_BIT, Shader::Stage::Fragment) | + subgroup_stage_bit(VK_SHADER_STAGE_COMPUTE_BIT, Shader::Stage::Compute)}; profile = Shader::Profile{ .supported_spirv = device.SupportedSpirvVersion(), .unified_descriptor_binding = true, @@ -382,6 +396,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT), + .supported_subgroup_stages = supported_subgroup_stages, .support_viewport_index_layer_non_geometry = device.IsExtShaderViewportIndexLayerSupported(), .support_viewport_mask = device.IsNvViewportArray2Supported(), diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 305cec454a..bc71038029 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -998,11 +998,18 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched compute_pass_descriptor_queue); } } + // Log which unswizzle passes are active, for debugging purposes. + LOG_INFO(Render_Vulkan, "GPU compute unswizzle passes active: 2d={}, 3d={}, pitch={}", + bl_unswizzle_2d_pass.has_value(), bl_unswizzle_3d_pass.has_value(), + pitch_unswizzle_pass.has_value()); for (size_t index_a = 0; index_a < VideoCore::Surface::MaxPixelFormat; index_a++) { const auto image_format = static_cast(index_a); if (IsPixelFormatASTC(image_format) && !device.IsOptimalAstcSupported()) { view_formats[index_a].push_back(VK_FORMAT_A8B8G8R8_UNORM_PACK32); - } else if (HasImageUnswizzlePasses() && !IsPixelFormatBCn(image_format) && + } else if (HasImageUnswizzlePasses() && !IsPixelFormatASTC(image_format) && + !IsPixelFormatBCn(image_format) && + VideoCore::Surface::DefaultBlockWidth(image_format) == 1 && + VideoCore::Surface::DefaultBlockHeight(image_format) == 1 && VideoCore::Surface::GetFormatType(image_format) == VideoCore::Surface::SurfaceType::ColorTexture) { const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image_format); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 00b30637ad..093cb9d87d 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -505,6 +505,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken CustomBorderColor."); RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle, + VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls."); RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64."); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 709fc49a6b..1333fcc58b 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -430,6 +430,11 @@ FN_MAX_LIMIT_LIST return properties.subgroup_properties.supportedOperations & feature; } + /// Returns the shader stages that support subgroup operations. + VkShaderStageFlags GetSubgroupSupportedStages() const { + return properties.subgroup_properties.supportedStages; + } + /// Returns the maximum number of push descriptors. u32 MaxPushDescriptors() const { return properties.push_descriptor.maxPushDescriptors;