From ee833674fb07b99538b35cd2797038843d406e92 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 2 Jul 2026 19:37:50 -0400 Subject: [PATCH] Shader OpCapability fallback + fp16 ban adjustments --- .../backend/spirv/emit_spirv.cpp | 3 + .../backend/spirv/emit_spirv_memory.cpp | 15 ++- .../backend/spirv/spirv_emit_context.cpp | 112 +++++++++++++++++- .../backend/spirv/spirv_emit_context.h | 1 + .../renderer_vulkan/vk_pipeline_cache.cpp | 10 +- 5 files changed, 126 insertions(+), 15 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 38bce7f82e..53582a28c7 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -332,6 +332,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { void SetupDenormControl(const Profile& profile, const IR::Program& program, EmitContext& ctx, Id main_func) { const Info& info{program.info}; + if (profile.has_broken_fp16_float_controls && info.uses_fp16) { + return; + } if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) { LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader"); } else if (info.uses_fp32_denorms_flush) { diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp index cca1c911dc..b94389bb9f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_memory.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_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 @@ -92,7 +95,7 @@ void EmitLoadGlobalS16(EmitContext&) { } Id EmitLoadGlobal32(EmitContext& ctx, Id address) { - if (ctx.profile.support_int64 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int64) { return ctx.OpFunctionCall(ctx.U32[1], ctx.load_global_func_u32, address); } LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); @@ -100,7 +103,7 @@ Id EmitLoadGlobal32(EmitContext& ctx, Id address) { } Id EmitLoadGlobal64(EmitContext& ctx, Id address) { - if (ctx.profile.support_int64 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int64) { return ctx.OpFunctionCall(ctx.U32[2], ctx.load_global_func_u32x2, address); } LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); @@ -108,7 +111,7 @@ Id EmitLoadGlobal64(EmitContext& ctx, Id address) { } Id EmitLoadGlobal128(EmitContext& ctx, Id address) { - if (ctx.profile.support_int64 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int64) { return ctx.OpFunctionCall(ctx.U32[4], ctx.load_global_func_u32x4, address); } LOG_WARNING(Shader_SPIRV, "Int64 not supported, ignoring memory operation"); @@ -132,7 +135,7 @@ void EmitWriteGlobalS16(EmitContext&) { } void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) { - if (ctx.profile.support_int64 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int64) { ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32, address, value); return; } @@ -140,7 +143,7 @@ void EmitWriteGlobal32(EmitContext& ctx, Id address, Id value) { } void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) { - if (ctx.profile.support_int64 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int64) { ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x2, address, value); return; } @@ -148,7 +151,7 @@ void EmitWriteGlobal64(EmitContext& ctx, Id address, Id value) { } void EmitWriteGlobal128(EmitContext& ctx, Id address, Id value) { - if (ctx.profile.support_int64 && ctx.profile.support_descriptor_aliasing) { + if (ctx.profile.support_int64) { ctx.OpFunctionCall(ctx.void_id, ctx.write_global_func_u32x4, address, 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 62b7cb147a..35747424f9 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -939,8 +939,11 @@ void EmitContext::DefineWriteStorageCasLoopFunction(const Info& info) { } void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { - if (!info.uses_global_memory || !profile.support_int64 || - !profile.support_descriptor_aliasing) { + if (!info.uses_global_memory || !profile.support_int64) { + return; + } + if (!profile.support_descriptor_aliasing) { + DefineGlobalMemoryFunctionsU32Fallback(info); return; } using DefPtr = Id StorageDefinitions::*; @@ -1020,6 +1023,111 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) { define(&StorageDefinitions::U32x4, storage_types.U32x4, U32[4], sizeof(u32[4])); } +void EmitContext::DefineGlobalMemoryFunctionsU32Fallback(const Info& info) { + const Id zero{u32_zero_value}; + const auto define_body{[&](Id addr, u32 num_words, auto&& callback) { + AddLabel(); + const size_t num_buffers{info.storage_buffers_descriptors.size()}; + for (size_t index = 0; index < num_buffers; ++index) { + if (!info.nvn_buffer_used[index]) { + continue; + } + const auto& ssbo{info.storage_buffers_descriptors[index]}; + const u32 addr_word{ssbo.cbuf_offset / 4}; + const u32 addr_lo_comp{addr_word % 4}; + const Id cbuf{cbufs[ssbo.cbuf_index].U32x4}; + const Id addr_vec_pointer{ + OpAccessChain(uniform_types.U32x4, cbuf, zero, Const(addr_word / 4))}; + const Id addr_vec{OpLoad(U32[4], addr_vec_pointer)}; + const Id addr_lo{OpCompositeExtract(U32[1], addr_vec, addr_lo_comp)}; + const Id addr_hi{OpCompositeExtract(U32[1], addr_vec, addr_lo_comp + 1U)}; + const Id unaligned_addr{ + OpBitcast(U64, OpCompositeConstruct(U32[2], addr_lo, addr_hi))}; + + const u64 ssbo_align_mask{~(profile.min_ssbo_alignment - 1U)}; + const Id ssbo_addr{OpBitwiseAnd(U64, unaligned_addr, Constant(U64, ssbo_align_mask))}; + + const u32 size_word{addr_word + 2}; + Id size_vec{addr_vec}; + if (size_word / 4 != addr_word / 4) { + const Id size_vec_pointer{ + OpAccessChain(uniform_types.U32x4, cbuf, zero, Const(size_word / 4))}; + size_vec = OpLoad(U32[4], size_vec_pointer); + } + const Id ssbo_size{ + OpUConvert(U64, OpCompositeExtract(U32[1], size_vec, size_word % 4))}; + const Id ssbo_end{OpIAdd(U64, ssbo_addr, ssbo_size)}; + const Id cond{OpLogicalAnd(U1, OpUGreaterThanEqual(U1, addr, ssbo_addr), + OpULessThan(U1, addr, ssbo_end))}; + const Id then_label{OpLabel()}; + const Id else_label{OpLabel()}; + OpSelectionMerge(else_label, spv::SelectionControlMask::MaskNone); + OpBranchConditional(cond, then_label, else_label); + AddLabel(then_label); + const Id ssbo_id{ssbos[index].U32}; + const Id ssbo_offset{OpUConvert(U32[1], OpISub(U64, addr, ssbo_addr))}; + const Id base_word{OpShiftRightLogical(U32[1], ssbo_offset, Const(2U))}; + std::array word_pointers{}; + for (u32 word = 0; word < num_words; ++word) { + const Id word_index{word == 0 ? base_word + : OpIAdd(U32[1], base_word, Const(word))}; + word_pointers[word] = + OpAccessChain(storage_types.U32.element, ssbo_id, zero, word_index); + } + callback(word_pointers); + AddLabel(else_label); + } + }}; + const auto define_load{[&](Id type, u32 num_words) { + const Id function_type{TypeFunction(type, U64)}; + const Id func_id{OpFunction(type, spv::FunctionControlMask::MaskNone, function_type)}; + const Id addr{OpFunctionParameter(U64)}; + define_body(addr, num_words, [&](const std::array& pointers) { + std::array words{}; + for (u32 word = 0; word < num_words; ++word) { + words[word] = OpLoad(U32[1], pointers[word]); + } + switch (num_words) { + case 1: + OpReturnValue(words[0]); + break; + case 2: + OpReturnValue(OpCompositeConstruct(type, words[0], words[1])); + break; + default: + OpReturnValue( + OpCompositeConstruct(type, words[0], words[1], words[2], words[3])); + break; + } + }); + OpReturnValue(ConstantNull(type)); + OpFunctionEnd(); + return func_id; + }}; + const auto define_write{[&](Id type, u32 num_words) { + const Id function_type{TypeFunction(void_id, U64, type)}; + const Id func_id{OpFunction(void_id, spv::FunctionControlMask::MaskNone, function_type)}; + const Id addr{OpFunctionParameter(U64)}; + const Id data{OpFunctionParameter(type)}; + define_body(addr, num_words, [&](const std::array& pointers) { + for (u32 word = 0; word < num_words; ++word) { + const Id value{num_words == 1 ? data : OpCompositeExtract(U32[1], data, word)}; + OpStore(pointers[word], value); + } + OpReturn(); + }); + OpReturn(); + OpFunctionEnd(); + return func_id; + }}; + load_global_func_u32 = define_load(U32[1], 1); + load_global_func_u32x2 = define_load(U32[2], 2); + load_global_func_u32x4 = define_load(U32[4], 4); + write_global_func_u32 = define_write(U32[1], 1); + write_global_func_u32x2 = define_write(U32[2], 2); + write_global_func_u32x4 = define_write(U32[4], 4); +} + void EmitContext::DefineRescalingInput(const Info& info) { if (!info.uses_rescaling_uniform) { return; diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index e2d37a34c8..fe3d2051ad 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -391,6 +391,7 @@ private: void DefineAttributeMemAccess(const Info& info); void DefineWriteStorageCasLoopFunction(const Info& info); void DefineGlobalMemoryFunctions(const Info& info); + void DefineGlobalMemoryFunctionsU32Fallback(const Info& info); void DefineRescalingInput(const Info& info); void DefineRescalingInputPushConstant(); void DefineRescalingInputUniformConstant(); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 04d34ac92b..e98f6a204b 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -378,7 +378,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, serialization_thread(1, "VkPipelineSerialization") { const auto& float_control{device.FloatControlProperties()}; const VkDriverId driver_id{device.GetDriverID()}; - const bool has_broken_sz_inf_nan{driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY}; 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; @@ -411,14 +410,11 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .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 && - !has_broken_sz_inf_nan, + float_control.shaderSignedZeroInfNanPreserveFloat16 != VK_FALSE, .support_fp32_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE && - !has_broken_sz_inf_nan, + float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE, .support_fp64_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE && - !has_broken_sz_inf_nan, + float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), .support_workgroup_layout_8bit_access = device.IsWorkgroupMemoryExplicitLayout8BitSupported(),