diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp index c9f1c2b97..c2275fb89 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp @@ -53,6 +53,14 @@ uint32_t Builder::Import(const char* name) { return id; } +uint32_t Builder::DebugString(const char* text) { + const auto id = AllocateId(); + std::vector operands = {id}; + AppendString(operands, text); + AppendInstruction(m_debug_strings, 7u, operands); + return id; +} + uint32_t Builder::Type(uint32_t opcode, std::initializer_list operands) { return Type(opcode, std::vector(operands)); } @@ -244,7 +252,8 @@ std::vector Builder::Build() const { std::vector module; module.reserve(5u + m_capabilities.size() + m_extensions.size() + m_ext_inst_imports.size() + m_memory_model.size() + m_entry_points.size() + m_execution_modes.size() + - m_debug.size() + m_annotations.size() + m_declarations.size() + + m_debug_strings.size() + m_debug.size() + m_annotations.size() + + m_declarations.size() + m_functions.size()); module.push_back(0x07230203u); @@ -259,6 +268,7 @@ std::vector Builder::Build() const { module.insert(module.end(), m_memory_model.begin(), m_memory_model.end()); module.insert(module.end(), m_entry_points.begin(), m_entry_points.end()); module.insert(module.end(), m_execution_modes.begin(), m_execution_modes.end()); + module.insert(module.end(), m_debug_strings.begin(), m_debug_strings.end()); module.insert(module.end(), m_debug.begin(), m_debug.end()); module.insert(module.end(), m_annotations.begin(), m_annotations.end()); module.insert(module.end(), m_declarations.begin(), m_declarations.end()); diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h index 06bac8a91..756ec6c83 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h @@ -31,6 +31,7 @@ public: void RequireCapability(uint32_t capability); void RequireExtension(const char* name); uint32_t Import(const char* name); + uint32_t DebugString(const char* text); uint32_t Type(uint32_t opcode, std::initializer_list operands = {}); uint32_t Type(uint32_t opcode, const std::vector& operands); uint32_t DecoratedType(uint32_t opcode, std::initializer_list operands, @@ -73,6 +74,7 @@ private: std::vector m_memory_model; std::vector m_entry_points; std::vector m_execution_modes; + std::vector m_debug_strings; std::vector m_debug; std::vector m_annotations; std::vector m_declarations; diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp index e3d945525..7cdbb5bf8 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp @@ -245,6 +245,9 @@ void AnalyzeProgramRequirements(IR::Program& program) { } } switch (inst.GetOpcode()) { + case IR::ValueOpcode::ReadClockRealtime64: + requirements.shader_clock = true; + break; case IR::ValueOpcode::Ballot: MarkBallot(); break; case IR::ValueOpcode::DppMoveU32: case IR::ValueOpcode::ReadFirstLane: diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp index bb32d3b53..7e5871d90 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAlu.cpp @@ -75,6 +75,28 @@ uint32_t EmitAdd64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) return MakePair(state, low, high); } +uint32_t EmitReadClockRealtime64(EmitterState& state) { + const auto clock = state.builder.AllocateId(); + state.builder.AddFunction( + {OpReadClockKHR, TypeU32Vector(state, 2), clock, ConstantU32(state, ScopeDevice)}); + + const auto low = state.builder.AllocateId(); + const auto high = state.builder.AllocateId(); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), low, clock, 0}); + state.builder.AddFunction({OpCompositeExtract, TypeU32(state), high, clock, 1}); + + constexpr uint32_t ClockShift = 3; + + const auto low_shifted = NewBinary(state, OpShiftRightLogical, TypeU32(state), low, + ConstantU32(state, ClockShift)); + const auto high_carried = NewBinary(state, OpShiftLeftLogical, TypeU32(state), high, + ConstantU32(state, 32u - ClockShift)); + const auto low_result = NewBinary(state, OpBitwiseOr, TypeU32(state), low_shifted, high_carried); + const auto high_result = NewBinary(state, OpShiftRightLogical, TypeU32(state), high, + ConstantU32(state, ClockShift)); + return MakePair(state, low_result, high_result); +} + uint32_t EmitSub64(EmitterState& state, uint32_t lhs_value, uint32_t rhs_value) { const auto lhs = ExtractPair(state, lhs_value); const auto rhs = ExtractPair(state, rhs_value); @@ -438,6 +460,9 @@ bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst) { case IR::ValueOpcode::ISub32: return binary(OpISub, IR::Type::U32); case IR::ValueOpcode::IMul32: return binary(OpIMul, IR::Type::U32); case IR::ValueOpcode::UDiv32: return binary(OpUDiv, IR::Type::U32); + case IR::ValueOpcode::ReadClockRealtime64: + ctx.Define(inst, EmitReadClockRealtime64(state)); + return true; case IR::ValueOpcode::IAdd64: ctx.Define(inst, EmitAdd64(state, ctx.Arg(inst, 0), ctx.Arg(inst, 1))); return true; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp index dfabff995..e1e7378ec 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterAnalysis.cpp @@ -219,9 +219,12 @@ uint32_t LoadSampledImageDescriptor(EmitterState& state, const IR::MemoryInfo& m ImageViewKind view) { (void)use_pc; const bool integer = mem.kind == IR::ResourceKind::ImageUint; - const auto kind = SampledBindingKind(integer, view); + const bool depth = mem.resource < state.program.info.images.size() && + state.program.info.images[mem.resource].depth_compare; + const auto kind = SampledBindingKind(integer, view, depth); const auto binding = ResourceForDescriptor(state, kind, mem.resource); - const auto variable = state.sampled_image_variables[SampledImageIndex(integer, view)]; + const auto variable = + state.sampled_image_variables[SampledImageIndex(integer, view, depth)]; const auto pointer_type = state.builder.Type( OpTypePointer, {StorageClassUniformConstant, ImageViewImageType(state, view, integer)}); const auto pointer = @@ -252,7 +255,11 @@ uint32_t MakeSampledImage(EmitterState& state, const IR::MemoryInfo& mem, uint32 const auto sampler = LoadSamplerDescriptor(state, mem.sampler, use_pc); if (image == 0 || sampler == 0) { ExitDescriptorBindingFailure( - state, SampledBindingKind(mem.kind == IR::ResourceKind::ImageUint, view), mem.resource, + state, + SampledBindingKind(mem.kind == IR::ResourceKind::ImageUint, view, + mem.resource < state.program.info.images.size() && + state.program.info.images[mem.resource].depth_compare), + mem.resource, "sampled image or sampler descriptor load failed"); } const auto sampled_image = state.builder.AllocateId(); diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp index bc281777d..014c442e7 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterImage.cpp @@ -1,3 +1,5 @@ +#include +#include #include "graphics/guest_gpu/gpu_format.h" #include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h" diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp index 8ee262650..32d8dee00 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemory.cpp @@ -293,18 +293,37 @@ PreparedMemoryElement PrepareMemoryElement(ValueEmitContext& ctx, const IR::Memo return {.resource = resource, .index = EmitMemoryElementIndex(ctx.state, resource, raw_index)}; } +constexpr uint32_t MemoryAccessVolatile = 0x1; + +void AddLoad(ValueEmitContext& ctx, uint32_t type, uint32_t result, uint32_t pointer, bool glc) { + if (glc) { + ctx.state.builder.AddFunction({OpLoad, type, result, pointer, MemoryAccessVolatile}); + } else { + ctx.state.builder.AddFunction({OpLoad, type, result, pointer}); + } +} + +void AddStore(ValueEmitContext& ctx, uint32_t pointer, uint32_t data, bool glc) { + if (glc) { + ctx.state.builder.AddFunction({OpStore, pointer, data, MemoryAccessVolatile}); + } else { + ctx.state.builder.AddFunction({OpStore, pointer, data}); + } +} + uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource, - uint32_t index); + uint32_t index, bool glc); uint32_t LoadSubwordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource, - uint32_t address, uint32_t index, uint32_t bits, bool sign_extend); + uint32_t address, uint32_t index, uint32_t bits, bool sign_extend, + bool glc); uint32_t LoadWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem, const MemoryResourceAccess& resource) { const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem)); return EmitValueOrZeroIfCondition( ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), - [&]() { return LoadWordInBounds(ctx, access.resource, access.index); }); + [&]() { return LoadWordInBounds(ctx, access.resource, access.index, mem.glc); }); } uint32_t LoadWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem) { @@ -324,21 +343,38 @@ uint32_t LoadSubwordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const return EmitValueOrZeroIfCondition( ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() { return LoadSubwordInBounds(ctx, access.resource, address, access.index, bits, - sign_extend); + sign_extend, mem.glc); }); } +void EmitLoadPrintf(ValueEmitContext& ctx, uint32_t index, uint32_t value) { + constexpr uint64_t wanted = 0ull; + if (wanted == 0 || ctx.state.program.shader_hash != wanted) { + return; + } + auto& state = ctx.state; + state.builder.RequireExtension("SPV_KHR_non_semantic_info"); + const auto set = state.builder.Import("NonSemantic.DebugPrintf"); + const auto fmt = state.builder.DebugString("kyty glc load idx=%u val=%u"); + const auto result = state.builder.AllocateId(); + state.builder.AddFunction({OpExtInst, TypeVoid(state), result, set, 1u, fmt, index, value}); +} + uint32_t LoadWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource, - uint32_t index) { + uint32_t index, bool glc) { const auto value = ctx.state.builder.AllocateId(); const auto pointer = EmitMemoryElementPointer(ctx.state, resource, index); - ctx.state.builder.AddFunction({OpLoad, TypeU32(ctx.state), value, pointer}); + AddLoad(ctx, TypeU32(ctx.state), value, pointer, glc); + if (glc) { + EmitLoadPrintf(ctx, index, value); + } return value; } uint32_t LoadSubwordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource, - uint32_t address, uint32_t index, uint32_t bits, bool sign_extend) { - const auto word = LoadWordInBounds(ctx, resource, index); + uint32_t address, uint32_t index, uint32_t bits, bool sign_extend, + bool glc) { + const auto word = LoadWordInBounds(ctx, resource, index, glc); const auto byte = Binary(ctx.state, OpBitwiseAnd, TypeU32(ctx.state), address, ConstantU32(ctx.state, 3)); const auto shift = @@ -486,6 +522,7 @@ uint32_t AtomicUpdate(EmitterState& state, uint32_t pointer, IR::ResourceKind ki const auto scope = kind == IR::ResourceKind::Lds ? ScopeWorkgroup : ScopeDevice; const auto memory = kind == IR::ResourceKind::Lds ? MemorySemanticsWorkgroupMemory : MemorySemanticsUniformMemory; + const auto preheader = state.builder.AllocateId(); const auto header = state.builder.AllocateId(); const auto cont = state.builder.AllocateId(); @@ -569,16 +606,14 @@ void StoreWordPrepared(ValueEmitContext& ctx, const IR::Inst& inst, const IR::Me const auto access = PrepareMemoryElement(ctx, mem, resource, DwordIndex(ctx, inst, mem)); EmitIfCondition( ctx.state, EmitMemoryElementInBounds(ctx.state, access.resource, access.index), [&]() { - ctx.state.builder.AddFunction( - {OpStore, EmitMemoryElementPointer(ctx.state, access.resource, access.index), - data}); + AddStore(ctx, EmitMemoryElementPointer(ctx.state, access.resource, access.index), + data, mem.glc); }); } void StoreWordInBounds(ValueEmitContext& ctx, const MemoryResourceAccess& resource, uint32_t index, - uint32_t data) { - ctx.state.builder.AddFunction( - {OpStore, EmitMemoryElementPointer(ctx.state, resource, index), data}); + uint32_t data, bool glc) { + AddStore(ctx, EmitMemoryElementPointer(ctx.state, resource, index), data, glc); } void StoreWord(ValueEmitContext& ctx, const IR::Inst& inst, IR::MemoryInfo mem) { @@ -874,11 +909,11 @@ uint32_t LoadFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem, return LoadFormattedComponent( ctx, mem, plan.format, plan.info, output_component, [&](uint32_t component) { - return LoadWordInBounds(ctx, plan.resource, plan.indices[component]); + return LoadWordInBounds(ctx, plan.resource, plan.indices[component], mem.glc); }, [&](uint32_t component, uint32_t bits, bool sign_extend) { return LoadSubwordInBounds(ctx, plan.resource, plan.addresses[component], - plan.indices[component], bits, sign_extend); + plan.indices[component], bits, sign_extend, mem.glc); }); } @@ -910,7 +945,7 @@ void StoreFormattedInBounds(ValueEmitContext& ctx, const IR::MemoryInfo& mem, StoreSubwordInBounds(ctx, mem, plan.resource, plan.addresses[component], plan.indices[component], bits, data); } else { - StoreWordInBounds(ctx, plan.resource, plan.indices[component], data); + StoreWordInBounds(ctx, plan.resource, plan.indices[component], data, mem.glc); } } @@ -993,7 +1028,7 @@ uint32_t LoadWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t co const auto access = PrepareMemoryElement(ctx, mem, resource, raw_index); values[component] = EmitValueOrZeroIfCondition( state, EmitMemoryElementInBounds(state, access.resource, access.index), - [&]() { return LoadWordInBounds(ctx, access.resource, access.index); }); + [&]() { return LoadWordInBounds(ctx, access.resource, access.index, mem.glc); }); } return ConstructU32Composite(state, components, values); }); @@ -1015,7 +1050,7 @@ void StoreWideShared(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t compo EmitIfCondition(state, EmitMemoryElementInBounds(state, access.resource, access.index), [&]() { StoreWordInBounds(ctx, access.resource, access.index, - ctx.Arg(inst, component + 1u)); + ctx.Arg(inst, component + 1u), mem.glc); }); } }); @@ -1124,9 +1159,8 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { const auto condition = EmitMemoryElementInBounds(state, access, element); ctx.Define(inst, EmitValueOrZeroIfCondition(state, condition, [&]() { const auto value = state.builder.AllocateId(); - state.builder.AddFunction( - {OpLoad, TypeU32(state), value, - EmitMemoryElementPointer(state, access, element)}); + AddLoad(ctx, TypeU32(state), value, + EmitMemoryElementPointer(state, access, element), mem.glc); return value; })); return true; @@ -1201,7 +1235,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { state.builder.AddFunction({OpStore, ctx.scratch_u32_variable, source}); source = state.builder.AllocateId(); state.builder.AddFunction({OpLoad, TypeU32(state), source, ctx.scratch_u32_variable}); - target = EmitDsSwizzleTargetLane(state, EmitSubgroupLocalInvocationId(state), + target = EmitDsSwizzleTargetLane(state, EmitRawSubgroupInvocationId(state), inst.Arg(1).IsImmediate() ? inst.Arg(1).U32() : 0); } else { const auto index = Binary( @@ -1210,7 +1244,7 @@ bool EmitValueMemory(ValueEmitContext& ctx, const IR::Inst& inst) { ConstantU32(state, 2)), ConstantU32(state, 31)); const auto base = - Binary(state, OpBitwiseAnd, TypeU32(state), EmitSubgroupLocalInvocationId(state), + Binary(state, OpBitwiseAnd, TypeU32(state), EmitRawSubgroupInvocationId(state), ConstantU32(state, ~31u)); target = Binary(state, OpBitwiseOr, TypeU32(state), base, index); } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp index 342d03b8f..9ae23194c 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp @@ -188,9 +188,12 @@ void DefineDescriptorVariables(EmitterState& state) { TypeStorageBufferPointer(state), StorageClassStorageBuffer); } for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) { - const auto view = static_cast(i % SampledImageViewKindCount); - const bool integer = i >= SampledImageViewKindCount; - const auto kind = SampledBindingKind(integer, view); + const bool depth = i == SampledDepthImageIndex; + const auto view = + depth ? ImageViewKind::Dim2D + : static_cast(i % SampledImageViewKindCount); + const bool integer = !depth && i >= SampledImageViewKindCount; + const auto kind = SampledBindingKind(integer, view, depth); if (DescriptorBinding(state, kind) == nullptr) { continue; } @@ -607,11 +610,15 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state) { "sampled_uint_2d_array", "sampled_uint_3d", "sampled_uint_2d_msaa", - "sampled_uint_2d_msaa_array"}; + "sampled_uint_2d_msaa_array", + "sampled_depth_2d"}; for (uint32_t i = 0; i < state.sampled_image_variables.size(); i++) { - const auto view = static_cast(i % SampledImageViewKindCount); + const bool depth = i == SampledDepthImageIndex; + const auto view = + depth ? ImageViewKind::Dim2D + : static_cast(i % SampledImageViewKindCount); Decorate(state.sampled_image_variables[i], SampledNames[i], - SampledBindingKind(i >= SampledImageViewKindCount, view)); + SampledBindingKind(!depth && i >= SampledImageViewKindCount, view, depth)); } constexpr const char* StorageNames[] = {"storage_1d", "storage_1d_array", @@ -695,6 +702,10 @@ void DefineModule(EmitterState& state) { if (state.requirements.subgroup_ballot) { state.builder.RequireCapability(CapabilityGroupNonUniformBallot); } + if (state.requirements.shader_clock) { + state.builder.RequireCapability(CapabilityShaderClockKHR); + state.builder.RequireExtension("SPV_KHR_shader_clock"); + } if (state.requirements.subgroup_shuffle) { state.builder.RequireCapability(CapabilityGroupNonUniformShuffle); } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp index 48e0fb66a..469900ce3 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp @@ -94,9 +94,17 @@ void EmitReturn(ValueEmitContext& ctx) { ctx.state.builder.AddFunction({OpReturn}); } +constexpr uint32_t kMaxLoopIterations = 0u; + +constexpr size_t kLoopBoundMaxBlocks = 200ULL; + +constexpr size_t kUnmergedLogMinBlocks = 100ULL; +constexpr uint32_t kUnmergedLogLimit = 24u; + void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block, const IR::BlockInfo& info) { const auto& term = info.terminator; + bool merged = false; const auto emit_merge = [&]() { if (term.loop_header) { const auto* merge = TargetBlock(ctx.program, term.merge_block); @@ -104,12 +112,14 @@ void EmitStructuredTerminator(ValueEmitContext& ctx, const IR::Block* block, if (merge != nullptr && cont != nullptr) { ctx.state.builder.AddFunction( {OpLoopMerge, ctx.Label(merge), ctx.Label(cont), LoopControlNone}); + merged = true; } } else if (term.kind == CFG::TerminatorKind::ConditionalBranch && term.merge_block != UINT32_MAX) { if (const auto* merge = TargetBlock(ctx.program, term.merge_block); merge != nullptr) { ctx.state.builder.AddFunction( {OpSelectionMerge, ctx.Label(merge), SelectionControlNone}); + merged = true; } } }; @@ -305,13 +315,34 @@ void EmitDispatcherFunction(ValueEmitContext& ctx, const DispatcherFunctionState const auto next_pc = state.builder.AllocateId(); state.builder.AddFunction({OpPhi, TypeU32(state), pc, initial_pc, initial_parent, next_pc, dispatcher.continue_label}); + const bool bound_loop = + kMaxLoopIterations != 0 && ctx.program.blocks.size() <= kLoopBoundMaxBlocks; + const auto iter = bound_loop ? state.builder.AllocateId() : 0; + const auto next_iter = bound_loop ? state.builder.AllocateId() : 0; + if (bound_loop) { + state.builder.AddFunction({OpPhi, TypeU32(state), iter, ConstantU32(state, 0), + initial_parent, next_iter, dispatcher.continue_label}); + } const auto done = state.builder.AllocateId(); state.builder.AddFunction( {OpIEqual, TypeBool(state), done, pc, ConstantU32(ctx.state, UINT32_MAX)}); + auto stop = done; + if (bound_loop) { + const auto under = state.builder.AllocateId(); + const auto over = state.builder.AllocateId(); + const auto stopped = state.builder.AllocateId(); + state.builder.AddFunction( + {OpIAdd, TypeU32(state), next_iter, iter, ConstantU32(state, 1)}); + state.builder.AddFunction({OpULessThan, TypeBool(state), under, next_iter, + ConstantU32(state, kMaxLoopIterations)}); + state.builder.AddFunction({OpLogicalNot, TypeBool(state), over, under}); + state.builder.AddFunction({OpLogicalOr, TypeBool(state), stopped, done, over}); + stop = stopped; + } state.builder.AddFunction( {OpLoopMerge, dispatcher.merge_label, dispatcher.continue_label, LoopControlNone}); state.builder.AddFunction( - {OpBranchConditional, done, dispatcher.merge_label, dispatcher.select_label}); + {OpBranchConditional, stop, dispatcher.merge_label, dispatcher.select_label}); EmitLabel(state, dispatcher.select_label); state.builder.AddFunction( diff --git a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp index 6a8b02f58..b4ca70e4a 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ImageOps.cpp @@ -244,6 +244,7 @@ Opcode DecodeMimgOpcode(uint32_t opcode, const MimgSampleInfo* sample, const Mim case 0x08u: return Opcode::IMAGE_STORE; case 0x09u: return Opcode::IMAGE_STORE_MIP; case 0x0eu: return Opcode::IMAGE_GET_RESINFO; + case 0xe6u: return Opcode::IMAGE_BVH_INTERSECT_RAY; case 0x60u: return Opcode::IMAGE_GET_LOD; default: return Opcode::UNSUPPORTED; } diff --git a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp index 92bd8a59c..045a1021c 100644 --- a/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/MemoryOps.cpp @@ -234,6 +234,14 @@ void DecodeSmem(uint32_t pc, std::span code, uint32_t word_index inst.glc = ((word0 >> 16u) & 1u) != 0; inst.family = Family::SMEM; inst.opcode_id = opcode; + if (opcode == 0x25u) { + inst.opcode = Opcode::S_MEMREALTIME; + SetRawWords(inst, code, word_index, 2); + DecodeScalarDestination(sdst, pc, inst.dst); + inst.src_count = 0; + return; + } + const auto* info = Detail::FindOpcode(SMEM_OPS, opcode); ApplyMemoryInfo(inst, info); SetRawWords(inst, code, word_index, 2); diff --git a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp index 404f3bb05..b18102ba5 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ScalarAluOps.cpp @@ -45,6 +45,7 @@ constexpr OpcodeMap SOP1_OPCODE_LIST[] = { {0x10u, Opcode::S_BCNT1_I32_B64}, {0x13u, Opcode::S_FF1_I32_B32}, {0x14u, Opcode::S_FF1_I32_B64}, + {0x15u, Opcode::S_FLBIT_I32_B32}, {0x16u, Opcode::S_FLBIT_I32_B64}, {0x1bu, Opcode::S_BITSET0_B32}, {0x1du, Opcode::S_BITSET1_B32}, diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp index f73dec858..7345b3150 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.cpp @@ -476,6 +476,7 @@ std::string InstructionToString(const Instruction& inst) { case Opcode::S_ABS_I32: case Opcode::S_BREV_B32: case Opcode::S_BCNT1_I32_B32: + case Opcode::S_FLBIT_I32_B32: case Opcode::S_FF1_I32_B32: case Opcode::S_FF1_I32_B64: case Opcode::S_NOT_B64: diff --git a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h index 3c5a1bebb..ab3c0450c 100644 --- a/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h +++ b/src/graphics/shader/recompiler/frontend/decode/ShaderDecoder.h @@ -45,6 +45,7 @@ enum class Opcode { S_BCNT1_I32_B64, S_FF1_I32_B32, S_FF1_I32_B64, + S_FLBIT_I32_B32, S_FLBIT_I32_B64, S_BITREPLICATE_B64_B32, S_QUADMASK_B64, @@ -400,6 +401,8 @@ enum class Opcode { V_CMP_NE_U64, V_CMPX_NE_I64, V_CMPX_NE_U64, + V_CMP_LT_U64, + S_MEMREALTIME, V_CMPX_LT_U32, V_CMPX_EQ_U32, V_CMPX_LE_U32, @@ -526,6 +529,7 @@ enum class Opcode { DS_WRITE_ADDTID_B32, DS_READ_ADDTID_B32, IMAGE_GET_RESINFO, + IMAGE_BVH_INTERSECT_RAY, IMAGE_GET_LOD, IMAGE_LOAD, IMAGE_LOAD_MIP, diff --git a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp index ae9ff28d5..27f37f4b8 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Scalar.cpp @@ -89,9 +89,15 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) { EmitIntegerCompare(inst, IR::ValueOpcode::SLessThanEqual32, IR::Type::U32, true, false); return true; case O::S_CMP_EQ_U64: + if (S_CMP_MASK_ANY(inst, false)) { + return true; + } EmitIntegerCompare(inst, IR::ValueOpcode::IEqual64, IR::Type::U64, true, false); return true; case O::S_CMP_LG_U64: + if (S_CMP_MASK_ANY(inst, true)) { + return true; + } EmitIntegerCompare(inst, IR::ValueOpcode::INotEqual64, IR::Type::U64, true, false); return true; diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index c09352a26..779dd963c 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -283,6 +283,7 @@ enum class DescriptorBindingKind { Sampled2DMsaa, Sampled2DMsaaArray, Sampled3D, + SampledDepth2D, SampledUint1D, SampledUint1DArray, SampledUint2D, @@ -328,6 +329,9 @@ DescriptorBindingForImage(const ImageResource& image) { switch (image.kind) { case ResourceKind::Image: + if (image.depth_compare && image.dimension == Dimension::Dim2D) { + return Kind::SampledDepth2D; + } switch (image.dimension) { case Dimension::Dim1D: return Kind::Sampled1D; case Dimension::Dim1DArray: return Kind::Sampled1DArray; @@ -417,7 +421,8 @@ struct BindingLayout { }; struct ShaderInfo { - static constexpr uint32_t MaxBuffers = 32; + static constexpr uint32_t MaxBuffers = 64; + static constexpr uint32_t MaxAddresses = 32; static constexpr uint32_t MaxImages = 32; static constexpr uint32_t MaxSamplers = 32; static constexpr uint32_t MaxSampledPairs = 64; @@ -445,6 +450,7 @@ struct SpirvRequirements { bool function_lds = false; bool function_scratch = false; bool pixel_valid_mask = false; + bool shader_clock = false; }; struct BlockInfo { diff --git a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc index c3a7e81d3..e03f433c2 100644 --- a/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc +++ b/src/graphics/shader/recompiler/ir/opcodes/ValueOpcodes.inc @@ -132,6 +132,7 @@ VALUE_OPCODE(SLessThan32, U1, U32, U32) VALUE_OPCODE(SLessThan64, U1, U64, U64) VALUE_OPCODE(ULessThan32, U1, U32, U32) VALUE_OPCODE(ULessThan64, U1, U64, U64) +VALUE_OPCODE(ReadClockRealtime64, U64) VALUE_OPCODE(IEqual32, U1, U32, U32) VALUE_OPCODE(IEqual64, U1, U64, U64) VALUE_OPCODE(SLessThanEqual32, U1, U32, U32) diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp index d3da029e1..27149be88 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace Libs::Graphics::ShaderRecompiler::IR { @@ -412,6 +413,21 @@ bool ValidateResourceSpecialization(const Program& program, const ResourceSnapsh return true; } +std::optional DeriveFlatWindowBase(const Program& program, const ResourceSnapshot& next) { + uint64_t lowest = 0; + for (uint32_t index = 0; index < program.info.buffers.size() && index < next.buffers.size(); + index++) { + ShaderBufferResource descriptor {}; + if (!DecodeBufferDescriptor(next.buffers[index], descriptor)) { + continue; + } + if (const auto base = descriptor.Base48(); base != 0 && (lowest == 0 || base < lowest)) { + lowest = base; + } + } + return lowest != 0 ? std::optional {lowest} : std::nullopt; +} + bool MaterializeResources(const Program& program, const SrtRuntime& runtime, ResourceSnapshot& snapshot) { if (!program.resource_tracking_complete) {