internal void TranslateInstruction(InstructionToken token) { DebugLog(token); switch (token.Header.OpcodeType) { case OpcodeType.Dtoi: case OpcodeType.Dtou: case OpcodeType.FtoI: case OpcodeType.FtoU: { var constructorType = (token.Header.OpcodeType == OpcodeType.FtoI || token.Header.OpcodeType == OpcodeType.Dtoi) ? ShaderVariableType.Int : ShaderVariableType.UInt; var dest = token.Operands[0]; var src = token.Operands[1]; var destCount = dest.GetNumSwizzleElements(); var srcCount = src.GetNumSwizzleElements(); AddIndent(); AddAssignToDest(token.Operands[0]); Output.Append(GetConstructorForType(constructorType, srcCount == destCount ? destCount : 4)); Output.Append("("); WriteOperand(src); Output.AppendLine(");"); break; } case OpcodeType.DToF: case OpcodeType.ItoF: case OpcodeType.Utof: { var dest = token.Operands[0]; var src = token.Operands[1]; var destCount = dest.GetNumSwizzleElements(); var srcCount = src.GetNumSwizzleElements(); AddIndent(); AddAssignToDest(token.Operands[0]); Output.Append(GetConstructorForType(ShaderVariableType.Float, srcCount == destCount ? destCount : 4)); Output.Append("("); WriteOperand(src); Output.AppendLine(");"); break; } case OpcodeType.Itod: case OpcodeType.Utod: case OpcodeType.FToD: { var dest = token.Operands[0]; var src = token.Operands[1]; var destCount = dest.GetNumSwizzleElements(); var srcCount = src.GetNumSwizzleElements(); AddIndent(); AddAssignToDest(token.Operands[0]); Output.Append(GetConstructorForType(ShaderVariableType.Double, srcCount == destCount ? destCount : 4)); Output.Append("("); WriteOperand(src); Output.AppendLine(");"); break; } case OpcodeType.DMov: case OpcodeType.Mov: { WriteMoveBinaryOp(token.Operands[0], token.Operands[1]); break; } case OpcodeType.DAdd: case OpcodeType.IAdd: case OpcodeType.Add: { CallBinaryOp("+", token, 0, 1, 2); break; } case OpcodeType.Dfma: case OpcodeType.IMad: case OpcodeType.Mad: { CallTernaryOp("*", "+", token, 0, 1, 2, 3); break; } case OpcodeType.DMul: case OpcodeType.IMul: case OpcodeType.Mul: { CallBinaryOp("*", token, 0, 1, 2); break; } case OpcodeType.UDiv: case OpcodeType.Ddiv: case OpcodeType.Div: { CallBinaryOp("/", token, 0, 1, 2); break; } // Bitwise operations case OpcodeType.Or: { CallBinaryOp("|", token, 0, 1, 2); break; } case OpcodeType.And: { CallBinaryOp("&", token, 0, 1, 2); break; } case OpcodeType.FirstBitHi: { CallHelper("firstbithigh", token, 0, 1); break; } case OpcodeType.FirstBitSHi: { CallHelper("firstbithigh", token, 0, 1); break; } case OpcodeType.BfRev: { CallHelper("reversebits ", token, 0, 1); break; } case OpcodeType.Bfi: { string bfiFunc = @"int4 BFI(uint4 src0, uint4 src1, uint4 src2, uint4 src3){ uint4 width = src0 & 0x1f; uint4 offset = src1 & 0x1f; uint4 mask = (((1 << width)-1) << offset) & 0xffffffff; int4 dest = ((src2 << offset) & bitmask) | (src3 & ~bitmask) return dest; }"; AddFunction("BFI", bfiFunc); CallHelper("BFI", token, 0, 1, 2, 3, 4); break; } case OpcodeType.UShr: { CallBinaryOp(">>", token, 0, 1, 2); break; } case OpcodeType.IShr: { CallBinaryOp(">>", token, 0, 1, 2); break; } case OpcodeType.IShl: { CallBinaryOp("<<", token, 0, 1, 2); break; } case OpcodeType.UBfe: case OpcodeType.IBfe: { string name = token.Header.OpcodeType == OpcodeType.UBfe ? "UBFE" : "IBFE"; string returnType = token.Header.OpcodeType == OpcodeType.UBfe ? "uint4" : "int4"; string template = @"{0} {1}(uint4 src1, uint4 src2, {0} value){{ {0} result = 0; for(uint i = 0; i < 4; i++){{ uint width = src1[i] & 0x1f; uint offset =src2[i] & 0x1f; if(width == 0){{ result[i] = 0; }} else if(width + offset < 32){{ result[i] = value << (32-(width + offset)); result[i] = result[i] >> (32 - width); }} else {{ result[i] = value >> (32 - width); }} }} return result; }}"; AddFunction(name, string.Format(template, returnType, name)); CallHelper(name, token, 0, 1, 2, 3); break; } case OpcodeType.Not: { AddIndent(); AddAssignToDest(token.Operands[0]); Output.Append("~"); WriteOperand(token.Operands[1]); Output.AppendLine(";"); break; } case OpcodeType.Xor: { CallBinaryOp("^", token, 0, 1, 2); break; } case OpcodeType.CountBits: { AddIndent(); AddAssignToDest(token.Operands[0]); Output.Append(" = bitCount("); WriteOperand(token.Operands[1]); Output.AppendLine(");"); break; } //Comparisons case OpcodeType.DNe: case OpcodeType.INe: case OpcodeType.Ne: { AddComparision(token, ComparisonType.Ne); break; } case OpcodeType.ILt: case OpcodeType.DLt: case OpcodeType.ULt: case OpcodeType.Lt: { AddComparision(token, ComparisonType.Lt); break; } case OpcodeType.IGe: case OpcodeType.UGe: case OpcodeType.DGe: case OpcodeType.Ge: { AddComparision(token, ComparisonType.Ge); break; } case OpcodeType.DEq: case OpcodeType.IEq: case OpcodeType.Eq: { AddComparision(token, ComparisonType.Eq); break; } case OpcodeType.DMovC: case OpcodeType.MovC: { AddMOVCBinaryOp(token.Operands[0], token.Operands[1], token.Operands[2], token.Operands[3]); break; } case OpcodeType.SwapC: { // TODO needs temps!! AddMOVCBinaryOp(token.Operands[0], token.Operands[2], token.Operands[4], token.Operands[3]); AddMOVCBinaryOp(token.Operands[1], token.Operands[2], token.Operands[3], token.Operands[4]); break; } //ControlFlow case OpcodeType.Loop: { AddIndent(); Output.AppendLine("while(true){"); indent++; break; } case OpcodeType.EndLoop: { indent--; AddIndent(); Output.AppendLine("}"); break; } case OpcodeType.Break: { AddIndent(); Output.AppendLine("break;"); break; } case OpcodeType.BreakC: { WriteConditional(token); break; } case OpcodeType.ContinueC: { WriteConditional(token); break; } case OpcodeType.If: { WriteConditional(token); break; } case OpcodeType.RetC: { WriteConditional(token); break; } case OpcodeType.Else: { indent--; AddIndent(); Output.AppendLine("} else {"); indent++; break; } case OpcodeType.EndSwitch: case OpcodeType.EndIf: { indent--; AddIndent(); Output.AppendLine("}"); break; } case OpcodeType.Continue: { Output.AppendLine("continue;"); break; } case OpcodeType.Switch: { AddIndent(); indent++; Output.Append("switch(int("); WriteOperand(token.Operands[0]); Output.AppendLine(")){"); break; } case OpcodeType.Case: { AddIndent(); indent++; Output.Append("case "); WriteOperand(token.Operands[0]); Output.AppendLine(":"); break; } case OpcodeType.Default: { AddIndent(); Output.AppendLine("default:"); break; } case OpcodeType.Ret: { if (Container.Shader.Version.ProgramType != ProgramType.ComputeShader && Container.Shader.Version.ProgramType != ProgramType.GeometryShader) { AddIndent(); Output.AppendLine($"return output;"); } break; } case OpcodeType.Label: { break; } //Functions case OpcodeType.Sincos: { if (token.Operands[0].OperandType == token.Operands[2].OperandType && token.Operands[0].Indices[0].Value == token.Operands[2].Indices[0].Value) { if (token.Operands[1].OperandType != OperandType.Null) { CallHelper("cos", token, 1, 2); } if (token.Operands[0].OperandType != OperandType.Null) { CallHelper("cos", token, 0, 2); } } else { if (token.Operands[0].OperandType != OperandType.Null) { CallHelper("sin", token, 0, 2); } if (token.Operands[1].OperandType != OperandType.Null) { CallHelper("cos", token, 1, 2); } } break; } case OpcodeType.Dp2: { CallHelper("dot", token, 0, 1, 2); break; } case OpcodeType.Dp3: { CallHelper("dot", token, 0, 1, 2); break; } case OpcodeType.Dp4: { CallHelper("dot", token, 0, 1, 2); break; } case OpcodeType.Log: { CallHelper("log", token, 0, 1); break; } case OpcodeType.Rsq: { CallHelper("normalize", token, 0, 1); break; } case OpcodeType.Exp: { CallHelper("exp2", token, 0, 1); break; } case OpcodeType.Sqrt: { CallHelper("sqrt", token, 0, 1); break; } case OpcodeType.RoundPi: { CallHelper("ceil", token, 0, 1); break; } case OpcodeType.RoundNi: { CallHelper("floor", token, 0, 1); break; } case OpcodeType.RoundZ: { CallHelper("trunc", token, 0, 1); break; } case OpcodeType.RoundNe: { CallHelper("roundEven", token, 0, 1); break; } case OpcodeType.Frc: { CallHelper("frac", token, 0, 1); break; } case OpcodeType.IMax: case OpcodeType.UMax: case OpcodeType.DMax: case OpcodeType.Max: { CallHelper("max", token, 0, 1, 2); break; } case OpcodeType.IMin: case OpcodeType.UMin: case OpcodeType.DMin: case OpcodeType.Min: { CallHelper("min", token, 0, 1, 2); break; } //Resource Operations case OpcodeType.Gather4: case OpcodeType.Gather4S: case OpcodeType.Gather4Po: case OpcodeType.Gather4C: case OpcodeType.Gather4PoC: case OpcodeType.Gather4PoS: case OpcodeType.Gather4CS: case OpcodeType.Gather4PoCS: { TranslateTextureSample(token); break; } case OpcodeType.SampleLS: { TranslateTextureSample(token); break; } case OpcodeType.SampleDClS: { TranslateTextureSample(token); break; } case OpcodeType.SampleCClS: { TranslateTextureSample(token); break; } case OpcodeType.SampleBClS: { TranslateTextureSample(token); break; } case OpcodeType.SampleClS: { TranslateTextureSample(token); break; } case OpcodeType.SampleCLzS: { TranslateTextureSample(token); break; } case OpcodeType.Sample: { TranslateTextureSample(token); break; } case OpcodeType.SampleL: { TranslateTextureSample(token); break; } case OpcodeType.SampleC: { TranslateTextureSample(token); break; } case OpcodeType.SampleCLz: { TranslateTextureSample(token); break; } case OpcodeType.SampleD: { TranslateTextureSample(token); break; } case OpcodeType.SampleB: { TranslateTextureSample(token); break; } case OpcodeType.StoreRaw: { if (token.Operands[0].OperandType == OperandType.UnorderedAccessView) { AddCallInterfaceNoDest("Store", token, 0, 1, 2); } else { //TODO: use dstByteOffset (Operands[0]) to select the correct dest AddIndent(); AddAssignToDest(token.Operands[0]); WriteOperand(token.Operands[2]); Output.AppendLine(";"); } break; } case OpcodeType.StoreStructured: { AddCallInterfaceNoDest("Store", token, 0, 1, 2, 3); break; } case OpcodeType.StoreUavTyped: { AddCallInterfaceNoDest("Store", token, 0, 1, 2); break; } case OpcodeType.Ld: { AddLoad(token); break; } case OpcodeType.LdS: AddIndent(); AddCallInterface("Load", token, 0, 2, 1, 3); break; case OpcodeType.LdMsS: case OpcodeType.LdMs: { AddCallInterface("LoadMs", token, 0, 2, 1); break; } case OpcodeType.LdStructuredS: case OpcodeType.LdStructured: { AddLoadStructured(token); break; } case OpcodeType.LdUavTypedS: case OpcodeType.LdUavTyped: { AddCallInterface("Load", token, 0, 2, 1); break; } case OpcodeType.LdRawS: case OpcodeType.LdRaw: { AddCallInterface("Load", token, 0, 2, 1); break; } case OpcodeType.Lod: { TranslateTextureSample(token); break; } case OpcodeType.Resinfo: { int parameterCount = token.Operands[0].GetNumSwizzleElements(); for (int i = 0; i < 3 - parameterCount; i++) { AddIndent(); Output.AppendLine($"float unused{i};"); } AddIndent(); WriteOperandWithMask(token.Operands[2], ComponentMask.None); Output.Append(".GetDimensions("); WriteOperand(token.Operands[1]); var usedMask = token.Operands[0].GetUsedComponents(); for (int i = 0; i < 4; i++) { var mask = (ComponentMask)(1 << i); if (!usedMask.HasFlag(mask)) { continue; } Output.Append(", "); WriteOperandWithMask(token.Operands[0], mask); } for (int i = 0; i < 3 - parameterCount; i++) { Output.Append($", unused{i}"); } Output.AppendLine(");"); break; } //Atomic Operations case OpcodeType.AtomicAnd: { AddCallInterfaceNoDest("InterlockedAnd", token, 0, 1, 2); break; } case OpcodeType.AtomicOr: { AddCallInterfaceNoDest("InterlockedOr", token, 0, 1, 2); break; } case OpcodeType.AtomicXor: { AddCallInterfaceNoDest("InterlockedXor", token, 0, 1, 2); break; } case OpcodeType.AtomicCmpStore: { AddCallInterfaceNoDest("InterlockedCompareStore", token, 0, 1, 2, 3); break; } case OpcodeType.AtomicIAdd: { AddCallInterfaceNoDest("InterlockedAdd", token, 0, 1, 2); break; } case OpcodeType.AtomicIMax: { AddCallInterfaceNoDest("InterlockedMax", token, 0, 1, 2); break; } case OpcodeType.AtomicIMin: { AddCallInterfaceNoDest("InterlockedMin", token, 0, 1, 2); break; } case OpcodeType.AtomicUMax: { AddCallInterfaceNoDest("InterlockedMax", token, 0, 1, 2); break; } case OpcodeType.AtomicUMin: { AddCallInterfaceNoDest("InterlockedMin", token, 0, 1, 2); break; } case OpcodeType.ImmAtomicIAdd: { AddCallInterfaceNoDest("InterlockedAnd", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicAnd: { AddCallInterfaceNoDest("InterlockedAnd", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicOr: { AddCallInterfaceNoDest("InterlockedOr", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicXor: { AddCallInterfaceNoDest("InterlockedXor", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicExch: { AddCallInterfaceNoDest("InterlockedExchange", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicCmpExch: { AddCallInterfaceNoDest("InterlockedCompareExchange", token, 1, 2, 3, 4, 0); break; } case OpcodeType.ImmAtomicIMax: { AddCallInterfaceNoDest("InterlockedMax", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicIMin: { AddCallInterfaceNoDest("InterlockedMin", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicUMax: { AddCallInterfaceNoDest("InterlockedMax", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicUMin: { AddCallInterfaceNoDest("InterlockedMin", token, 1, 2, 3, 0); break; } case OpcodeType.ImmAtomicAlloc: { AddCallInterfaceNoDest("Append", token, 1, 0); break; } case OpcodeType.ImmAtomicConsume: { AddCallInterface("Consume", token, 0, 1); break; } // Misc case OpcodeType.Nop: { break; } case OpcodeType.InterfaceCall: { AddIndent(); var register = RegisterState.GetRegister(token); Output.AppendFormat("{0}();\n", register?.Name ?? token.ToString()); break; } case OpcodeType.Emit: { AddIndent(); Output.Append("TODO_Get_Stream"); Output.AppendLine(".Append(output);"); break; } case OpcodeType.Cut: { AddIndent(); Output.Append("TODO_Get_Stream"); Output.AppendLine(".RestartStrip();"); break; } case OpcodeType.CutStream: { AddIndent(); WriteOperand(token.Operands[0]); Output.AppendLine(".RestartStrip();"); break; } case OpcodeType.EmitStream: { AddIndent(); WriteOperand(token.Operands[0]); Output.AppendLine(".Append(output);"); break; } case OpcodeType.EmitThenCutStream: { break; } case OpcodeType.Sync: { AddIndent(); if (token.SyncFlags == SyncFlags.UnorderedAccessViewGlobal) { Output.AppendLine("DeviceMemoryBarrier();"); } else if (token.SyncFlags == SyncFlags.SharedMemory) { Output.AppendLine("GroupMemoryBarrier();"); } else if (token.SyncFlags == (SyncFlags.SharedMemory | SyncFlags.UnorderedAccessViewGlobal)) { Output.AppendLine("AllMemoryBarrier();"); } else if (token.SyncFlags == (SyncFlags.ThreadsInGroup | SyncFlags.UnorderedAccessViewGlobal)) { Output.AppendLine("DeviceMemoryBarrierWithGroupSync();"); } else if (token.SyncFlags == (SyncFlags.ThreadsInGroup | SyncFlags.SharedMemory)) { Output.AppendLine("GroupMemoryBarrierWithGroupSync();"); } else if (token.SyncFlags == (SyncFlags.ThreadsInGroup | SyncFlags.SharedMemory | SyncFlags.UnorderedAccessViewGlobal)) { Output.AppendLine("AllMemoryBarrierWithGroupSync();"); } else { throw new Exception($"Unknown Memory Sync Flags {token.SyncFlags}"); } break; } case OpcodeType.Discard: { if (token.Operands[0].OperandType == OperandType.Immediate32 || token.Operands[0].OperandType == OperandType.Immediate64) { AddIndent(); Output.Append("discard;"); } else { WriteConditional(token); } break; } case OpcodeType.EvalCentroid: { CallHelper("EvaluateAttributeCentroid", token, 0, 1); break; } case OpcodeType.EvalSampleIndex: { CallHelper("EvaluateAttributeAtSample", token, 0, 1, 2); break; } case OpcodeType.EvalSnapped: { CallHelper("EvaluateAttributeSnapped", token, 0, 1, 2); break; } case OpcodeType.Drcp: case OpcodeType.Rcp: { CallHelper("rcp", token, 0, 1); break; } case OpcodeType.F32ToF16: { CallHelper("f32tof16", token, 0, 1); break; } case OpcodeType.F16ToF32: { CallHelper("f16tof32", token, 0, 1); break; } case OpcodeType.INeg: { CallHelper("negate", token, 0, 1); break; } case OpcodeType.DerivRtx: { CallHelper("ddx", token, 0, 1); break; } case OpcodeType.RtxCoarse: { CallHelper("ddx_coarse", token, 0, 1); break; } case OpcodeType.RtxFine: { CallHelper("ddx_fine", token, 0, 1); break; } case OpcodeType.DerivRty: { CallHelper("ddy", token, 0, 1); break; } case OpcodeType.RtyCoarse: { CallHelper("ddy_coarse", token, 0, 1); break; } case OpcodeType.RtyFine: { CallHelper("ddy_fine", token, 0, 1); break; } case OpcodeType.SamplePos: { if (token.Operands[1].OperandType == OperandType.Rasterizer) { CallHelper("GetRenderTargetSamplePosition", token, 0, 2); } else { AddCallInterface("GetSamplePosition", token, 0, 1, 2); } break; } case OpcodeType.Abort: { AddIndent(); Output.AppendLine("abort();"); break; } case OpcodeType.Msad: CallHelper("msad4", token, 0, 1, 2); break; case OpcodeType.Bufinfo: //TODO: need to mask op[0] with no swizzle AddCallInterfaceNoDest("GetDimensions", token, 1, 0); break; case OpcodeType.FirstBitLo: CallHelper("firstbitlow", token, 0, 1); break; case OpcodeType.SampleInfo: AddIndent(); AddAssignToDest(token.Operands[0]); Output.AppendLine("GetRenderTargetSampleCount();"); break; case OpcodeType.HsForkPhase: break; case OpcodeType.HsJoinPhase: break; case OpcodeType.CheckAccessFullyMapped: CallHelper("CheckAccessFullyMapped", token, 0, 1); break; default: throw new Exception($"Unexpected token {token}"); } }
string GetOperandName(Operand operand) { try { switch (operand.OperandType) { case OperandType.Immediate32: case OperandType.Immediate64: { var parentType = operand.ParentType; var numComponents = operand.NumComponents; var immediateValues = operand.ImmediateValues; if (numComponents == 1) { return(operand.OperandType == OperandType.Immediate64 ? immediateValues.GetDouble(0).ToString() : immediateValues.GetNumber(0).ToString(parentType.GetNumberType())); } string result = (operand.OperandType == OperandType.Immediate64) ? $"double{numComponents}(" : $"float{numComponents}("; for (int i = 0; i < numComponents; i++) { result += operand.OperandType == OperandType.Immediate64 ? immediateValues.GetDouble(i).ToString() : immediateValues.GetNumber(i).ToString(parentType.GetNumberType()); if (i < numComponents - 1) { result += ", "; } } result += ")"; return(result); } case OperandType.Temp: return($"r{operand.Indices[0].Value}"); case OperandType.InputCoverageMask: { var reg = RegisterState.GetRegister(operand); return($"input.{reg.Name}"); } case OperandType.Input: { var input = Container.InputSignature.Parameters .Single(p => p.Register == operand.Indices[0].Value && p.ReadWriteMask.HasFlag(operand.GetUsedComponents())); return($"input.{input.GetName()}"); } case OperandType.InputControlPoint: { var input = Container.InputSignature.Parameters .Single(p => p.Register == operand.Indices[1].Value); return($"{operand.OperandType.GetDescription()}[{operand.Indices[1].Value}].field{operand.Indices[1].Value}{input.ReadWriteMask.GetDescription().Trim()}"); } case OperandType.Output: { var elements = Container.OutputSignature.Parameters .Where(p => p.Register == operand.Indices[0].Value && p.ReadWriteMask.HasFlag(operand.GetUsedComponents())) .ToArray(); var output = elements.Length == 1 ? elements[0] : null; return($"output.{output.GetName()}"); } case OperandType.ConstantBuffer: { //return RegisterState.GetRegister(operand).Name; uint bindPoint = (uint)operand.Indices[0].Value; uint fieldIndex = (uint)operand.Indices[1].Value; var cb = GetConstantBuffer(OperandType.ConstantBuffer, bindPoint); var offset = fieldIndex * 16; GetShaderVariableByOffset(cb, offset, out string fullname); if (fullname == null) { GetShaderVariableByOffset(cb, offset, out string foo); return($"cb{bindPoint}[{fieldIndex}]"); } return(fullname); } case OperandType.Interface: { var register = RegisterState.GetRegister(operand); return(register?.Name ?? operand.ToString()); } case OperandType.Resource: { var binding = GetResourceBinding(operand.OperandType, (uint)operand.Indices[0].Value); return(binding.Name); } case OperandType.Sampler: { var binding = GetResourceBinding(operand.OperandType, (uint)operand.Indices[0].Value); return(binding.Name); } case OperandType.ImmediateConstantBuffer: { var reg = GetOperandDescriptionWithMask(operand.Indices[0].Register, ComponentMask.All); return($"{operand.OperandType.GetDescription()}[{reg} + {operand.Indices[0].Value}]"); } case OperandType.UnorderedAccessView: { var binding = GetResourceBinding(operand.OperandType, (uint)operand.Indices[0].Value); return(binding.Name); } } } catch (Exception ex) { return($"error_{operand.ToString()}"); } return($"{operand.OperandType.GetDescription()}{GetOperandIndex(operand)}"); }