From b5930465f2714fc7cbaccf2840bdebab6ba5b0e1 Mon Sep 17 00:00:00 2001 From: kostyaff Date: Thu, 16 Jul 2026 00:27:26 +0300 Subject: [PATCH] [AGC] Correct VReadlaneB32 decode and emission (#237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VReadlaneB32 (VOP3 0x360) had two bugs: 1. Decode: VOP3 decode always set destinations = Vector(word & 0xFF). For VReadlaneB32, bits 0-7 are unused — the scalar destination is in bits 8-14. Now decodes as Scalar((word >> 8) & 0x7F). 2. Emission: VReadlaneB32 was in the VMovB32 fall-through group, just returning GetRawSource(instruction, 0) — reading the current lane's value. By ISA, sdst = vsrc0[lane(src1)], which requires reading a different lane's value. Now uses SPIR-V GroupNonUniformBroadcast(scope=Subgroup, value=src0, lane=src1) when subgroup operations are available, with a fallback to the current-lane simplification when not. 3. Routing: TryEmitVectorAlu called TryGetVectorDestination first, which checks for VectorRegister kind. With the scalar destination fix, VReadlaneB32 now routes to a new TryEmitReadlane handler before the vector destination check. 4. Subgroup capability: UsesSubgroupShuffle now includes VReadlaneB32 so GroupNonUniform capability is enabled when needed. Verified: dotnet build 0 errors/0 warnings, 26/26 tests pass, ShaderDump all programs behaved as expected. --- .../Gen5SpirvTranslator.Alu.cs | 42 ++++++++++++++++++- .../Gen5SpirvTranslator.cs | 2 +- .../Gen5ShaderTranslator.cs | 6 +++ 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs index 76a054e..f5a1d52 100644 --- a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs +++ b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.Alu.cs @@ -24,6 +24,11 @@ public static partial class Gen5SpirvTranslator return TryEmitVectorCompare(instruction, out error); } + if (instruction.Opcode == "VReadlaneB32") + { + return TryEmitReadlane(instruction, out error); + } + if (!TryGetVectorDestination(instruction, out var destination)) { error = "missing vector destination"; @@ -34,7 +39,6 @@ public static partial class Gen5SpirvTranslator switch (instruction.Opcode) { case "VMovB32": - case "VReadlaneB32": case "VReadfirstlaneB32": result = GetRawSource(instruction, 0); break; @@ -2365,6 +2369,42 @@ public static partial class Gen5SpirvTranslator StoreWaveMask(106, carry); } + private bool TryEmitReadlane( + Gen5ShaderInstruction instruction, + out string error) + { + error = string.Empty; + if (instruction.Destinations.Count == 0 || + instruction.Destinations[0].Kind != Gen5OperandKind.ScalarRegister) + { + error = "VReadlaneB32 expects scalar destination"; + return false; + } + + var destination = instruction.Destinations[0].Value; + var src0 = GetRawSource(instruction, 0); + + if (_subgroupInvocationIdInput != 0) + { + // sdst = vsrc0[lane(src1)] — broadcast from the specified lane. + var laneSelect = GetRawSource(instruction, 1); + var broadcast = _module.AddInstruction( + SpirvOp.GroupNonUniformBroadcast, + _uintType, + UInt(3), // Subgroup scope + src0, + laneSelect); + StoreS(destination, broadcast); + } + else + { + // Fallback: no subgroup ops, read current lane's value. + StoreS(destination, src0); + } + + return true; + } + private uint EmitPermlane16( Gen5ShaderInstruction instruction, bool exchangeRows) diff --git a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs index 34478ee..c2cbebe 100644 --- a/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs +++ b/src/SharpEmu.ShaderCompiler.Vulkan/Gen5SpirvTranslator.cs @@ -2507,7 +2507,7 @@ public static partial class Gen5SpirvTranslator private bool UsesSubgroupShuffle() => _state.Program.Instructions.Any(instruction => - instruction.Opcode is "VPermlane16B32" or "VPermlanex16B32"); + instruction.Opcode is "VPermlane16B32" or "VPermlanex16B32" or "VReadlaneB32"); private bool UsesWaveControl() => _state.Program.Instructions.Any(instruction => diff --git a/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs b/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs index c4155bd..3df3d91 100644 --- a/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs +++ b/src/SharpEmu.ShaderCompiler/Gen5ShaderTranslator.cs @@ -1467,6 +1467,12 @@ public static class Gen5ShaderTranslator Gen5Operand.Source((extra >> 18) & 0x1FF, literal), ]; destinations = [Gen5Operand.Vector(word & 0xFF)]; + if (opcode == "VReadlaneB32") + { + // VReadlaneB32 writes to scalar destination (bits 8-14), not vector. + // Bits 0-7 are unused for this opcode. + destinations = [Gen5Operand.Scalar((word >> 8) & 0x7F)]; + } var isVop3B = IsVop3BOpcode((word >> 16) & 0x3FF); control = new Gen5Vop3Control( isVop3B ? 0 : (word >> 8) & 0x7,