private static bool FindBrxTargets(ShaderConfig config, IEnumerable <Block> blocks, Func <ulong, Block> getBlock) { bool hasNewTarget = false; foreach (Block block in blocks) { InstOp lastOp = block.GetLastOp(); bool hasNext = block.HasNext(); if (lastOp.Name == InstName.Brx && block.Successors.Count == (hasNext ? 1 : 0)) { InstBrx opBrx = new InstBrx(lastOp.RawOpCode); ulong baseOffset = lastOp.GetAbsoluteAddress(); // An indirect branch could go anywhere, // try to get the possible target offsets from the constant buffer. (int cbBaseOffset, int cbOffsetsCount) = FindBrxTargetRange(block, opBrx.SrcA); if (cbOffsetsCount != 0) { hasNewTarget = true; } for (int i = 0; i < cbOffsetsCount; i++) { uint targetOffset = config.GpuAccessor.ConstantBuffer1Read(cbBaseOffset + i * 4); Block target = getBlock(baseOffset + targetOffset); target.Predecessors.Add(block); block.Successors.Add(target); } } } return(hasNewTarget); }
public static void RunPass(BasicBlock block, ShaderConfig config) { int sbStart = GetStorageBaseCbOffset(config.Stage); int sbEnd = sbStart + StorageDescsSize; for (LinkedListNode <INode> node = block.Operations.First; node != null; node = node.Next) { if (!(node.Value is Operation operation)) { continue; } if (UsesGlobalMemory(operation.Inst)) { Operand source = operation.GetSource(0); if (source.AsgOp is Operation asgOperation) { int storageIndex = SearchForStorageBase(asgOperation, sbStart, sbEnd); if (storageIndex >= 0) { node = ReplaceGlobalWithStorage(node, config, storageIndex); } } } } }
static void Main(string[] args) { if (args.Length == 2) { GalShaderType type = GalShaderType.Vertex; switch (args[0].ToLower()) { case "v": type = GalShaderType.Vertex; break; case "tc": type = GalShaderType.TessControl; break; case "te": type = GalShaderType.TessEvaluation; break; case "g": type = GalShaderType.Geometry; break; case "f": type = GalShaderType.Fragment; break; } using (FileStream fs = new FileStream(args[1], FileMode.Open, FileAccess.Read)) { Memory mem = new Memory(fs); ShaderConfig config = new ShaderConfig(type, 65536); string code = Translator.Translate(mem, 0, config).Code; Console.WriteLine(code); } } else { Console.WriteLine("Usage: Ryujinx.ShaderTools [v|tc|te|g|f] shader.bin"); } }
public static GlslProgram Generate(StructuredProgramInfo info, ShaderConfig config) { CodeGenContext context = new CodeGenContext(info, config); Declarations.Declare(context, info); if (info.Functions.Count != 0) { for (int i = 1; i < info.Functions.Count; i++) { context.AppendLine($"{GetFunctionSignature(info.Functions[i])};"); } context.AppendLine(); for (int i = 1; i < info.Functions.Count; i++) { PrintFunction(context, info, info.Functions[i]); context.AppendLine(); } } PrintFunction(context, info, info.Functions[0], MainFunctionName); return(new GlslProgram( context.CBufferDescriptors.ToArray(), context.SBufferDescriptors.ToArray(), context.TextureDescriptors.ToArray(), context.ImageDescriptors.ToArray(), context.GetCode())); }
public string GetExpression(AstOperand operand, ShaderConfig config, bool cbIndexable) { switch (operand.Type) { case OperandType.Argument: return(GetArgumentName(operand.Value)); case OperandType.Attribute: return(GetAttributeName(operand, config)); case OperandType.Constant: return(NumberFormatter.FormatInt(operand.Value)); case OperandType.ConstantBuffer: return(GetConstantBufferName(operand.CbufSlot, operand.CbufOffset, config.Stage, cbIndexable)); case OperandType.LocalVariable: return(_locals[operand]); case OperandType.Undefined: return(DefaultNames.UndefinedName); } throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); }
public static string Generate(StructuredProgramInfo info, ShaderConfig config) { CodeGenContext context = new CodeGenContext(info, config); Declarations.Declare(context, info); if (info.Functions.Count != 0) { for (int i = 1; i < info.Functions.Count; i++) { context.AppendLine($"{GetFunctionSignature(info.Functions[i])};"); } context.AppendLine(); for (int i = 1; i < info.Functions.Count; i++) { PrintFunction(context, info, info.Functions[i]); context.AppendLine(); } } PrintFunction(context, info, info.Functions[0], MainFunctionName); return(context.GetCode()); }
private static void SetUserAttributeUses(ShaderConfig config, IOpCodeAttribute opAttr) { if (opAttr.Indexed) { if (opAttr.Emitter == InstEmit.Ast) { config.SetAllOutputUserAttributes(); } else { config.SetAllInputUserAttributes(); } } else { for (int elemIndex = 0; elemIndex < opAttr.Count; elemIndex++) { int attr = opAttr.AttributeOffset + elemIndex * 4; if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) { int index = (attr - AttributeConsts.UserAttributeBase) / 16; if (opAttr.Emitter == InstEmit.Ast) { config.SetOutputUserAttribute(index); } else { config.SetInputUserAttribute(index); } } } } }
public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) { _info = info; Config = config; OperandManager = new OperandManager(); _sb = new StringBuilder(); }
private static void FillBlock(ShaderConfig config, Block block, ulong limitAddress, ulong startAddress) { IGpuAccessor gpuAccessor = config.GpuAccessor; ulong address = block.Address; int bufferOffset = 0; ReadOnlySpan <ulong> buffer = ReadOnlySpan <ulong> .Empty; InstOp op = default; do { if (address + 7 >= limitAddress) { break; } // Ignore scheduling instructions, which are written every 32 bytes. if ((address & 0x1f) == 0) { address += 8; bufferOffset++; continue; } if (bufferOffset >= buffer.Length) { buffer = gpuAccessor.GetCode(startAddress + address, 8); bufferOffset = 0; } ulong opCode = buffer[bufferOffset++]; op = InstTable.GetOp(address, opCode); if (op.Props.HasFlag(InstProps.TexB)) { config.SetUsedFeature(FeatureFlags.Bindless); } if (op.Name == InstName.Ald || op.Name == InstName.Ast || op.Name == InstName.Ipa) { SetUserAttributeUses(config, op.Name, opCode); } else if (op.Name == InstName.Ssy || op.Name == InstName.Pbk) { block.AddPushOp(op); } block.OpCodes.Add(op); address += 8; }while (!op.Props.HasFlag(InstProps.Bra)); block.EndAddress = address; }
public static void RunPass(BasicBlock block, ShaderConfig config) { // We can turn a bindless into regular access by recognizing the pattern // produced by the compiler for separate texture and sampler. // We check for the following conditions: // - The handle is the result of a bitwise OR logical operation. // - Both sources of the OR operation comes from CB2 (used by NVN to hold texture handles). for (LinkedListNode <INode> node = block.Operations.First; node != null; node = node.Next) { if (!(node.Value is TextureOperation texOp)) { continue; } if ((texOp.Flags & TextureFlags.Bindless) == 0) { continue; } if (texOp.Inst == Instruction.TextureSample) { if (!(texOp.GetSource(0).AsgOp is Operation handleCombineOp)) { continue; } if (handleCombineOp.Inst != Instruction.BitwiseOr) { continue; } Operand src0 = handleCombineOp.GetSource(0); Operand src1 = handleCombineOp.GetSource(1); if (src0.Type != OperandType.ConstantBuffer || src0.GetCbufSlot() != NvnTextureBufferSlot || src1.Type != OperandType.ConstantBuffer || src1.GetCbufSlot() != NvnTextureBufferSlot) { continue; } texOp.SetHandle(src0.GetCbufOffset() | (src1.GetCbufOffset() << 16)); } else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore) { Operand src0 = texOp.GetSource(0); if (src0.Type == OperandType.ConstantBuffer && src0.GetCbufSlot() == NvnTextureBufferSlot) { texOp.SetHandle(src0.GetCbufOffset()); texOp.Format = config.GetTextureFormat(texOp.Handle); } } } }
public CodeGenContext(ShaderConfig config) { Config = config; CBufferDescriptors = new List <CBufferDescriptor>(); TextureDescriptors = new List <TextureDescriptor>(); OperandManager = new OperandManager(); _sb = new StringBuilder(); }
private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot, bool rewriteSamplerType) { texOp.SetHandle(cbufOffset, cbufSlot); if (rewriteSamplerType) { texOp.Type = config.GpuAccessor.QuerySamplerType(cbufOffset, cbufSlot); } config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset); }
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput) { if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) { attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, 0, isOutput); } else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd) { attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, 4, isOutput); } return(attr); }
public static GlslProgram Generate(StructuredProgramInfo info, ShaderConfig config) { CodeGenContext context = new CodeGenContext(config); Declarations.Declare(context, info); PrintMainBlock(context, info); return(new GlslProgram( context.CBufferDescriptors.ToArray(), context.TextureDescriptors.ToArray(), context.GetCode())); }
public string GetExpression(AstOperand operand, ShaderConfig config) { return(operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), OperandType.Attribute => GetAttributeName(operand.Value, config, perPatch: false), OperandType.AttributePerPatch => GetAttributeName(operand.Value, config, perPatch: true), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.ConstantBuffer => GetConstantBufferName(operand, config), OperandType.LocalVariable => _locals[operand], OperandType.Undefined => DefaultNames.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") });
public CodeGenContext(ShaderConfig config, bool cbIndexable) { Config = config; CbIndexable = cbIndexable; CBufferDescriptors = new List <BufferDescriptor>(); SBufferDescriptors = new List <BufferDescriptor>(); TextureDescriptors = new List <TextureDescriptor>(); ImageDescriptors = new List <TextureDescriptor>(); OperandManager = new OperandManager(); _sb = new StringBuilder(); }
public CodeGenContext(StructuredProgramInfo info, ShaderConfig config) { _info = info; Config = config; CBufferDescriptors = new List <BufferDescriptor>(); SBufferDescriptors = new List <BufferDescriptor>(); TextureDescriptors = new List <TextureDescriptor>(); ImageDescriptors = new List <TextureDescriptor>(); OperandManager = new OperandManager(); _sb = new StringBuilder(); }
public static void RunPass(BasicBlock[] blocks, ShaderConfig config) { RunOptimizationPasses(blocks); // Those passes are looking for specific patterns and only needs to run once. for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { GlobalToStorage.RunPass(blocks[blkIndex], config); BindlessToIndexed.RunPass(blocks[blkIndex]); BindlessElimination.RunPass(blocks[blkIndex], config); } // Run optimizations one last time to remove any code that is now optimizable after above passes. RunOptimizationPasses(blocks); }
public static void RunPass(BasicBlock block, ShaderConfig config) { int sbStart = GetStorageBaseCbOffset(config.Stage); int sbEnd = sbStart + StorageDescsSize; for (LinkedListNode <INode> node = block.Operations.First; node != null; node = node.Next) { if (!(node.Value is Operation operation)) { continue; } if (UsesGlobalMemory(operation.Inst)) { Operand source = operation.GetSource(0); if (source.AsgOp is Operation asgOperation) { int storageIndex = SearchForStorageBase(asgOperation, sbStart, sbEnd); if (storageIndex >= 0) { // Storage buffers are implemented using global memory access. // If we know from where the base address of the access is loaded, // we can guess which storage buffer it is accessing. // We can then replace the global memory access with a storage // buffer access. node = ReplaceGlobalWithStorage(node, config, storageIndex); } else if (config.Stage == ShaderStage.Compute && operation.Inst == Instruction.LoadGlobal) { // Here we effectively try to replace a LDG instruction with LDC. // The hardware only supports a limited amount of constant buffers // so NVN "emulates" more constant buffers using global memory access. // Here we try to replace the global access back to a constant buffer // load. storageIndex = SearchForStorageBase(asgOperation, UbeBaseOffset, UbeBaseOffset + UbeDescsSize); if (storageIndex >= 0) { node = ReplaceLdgWithLdc(node, config, storageIndex); } } } } } }
public string GetExpression(AstOperand operand, ShaderConfig config) { return(operand.Type switch { OperandType.Argument => GetArgumentName(operand.Value), OperandType.Attribute => GetAttributeName(operand, config), OperandType.Constant => NumberFormatter.FormatInt(operand.Value), OperandType.ConstantBuffer => GetConstantBufferName( operand.CbufSlot, operand.CbufOffset, config.Stage, config.UsedFeatures.HasFlag(FeatureFlags.CbIndexing)), OperandType.LocalVariable => _locals[operand], OperandType.Undefined => DefaultNames.UndefinedName, _ => throw new ArgumentException($"Invalid operand type \"{operand.Type}\".") });
public static byte[] Generate(StructuredProgramInfo info, ShaderConfig config) { CodeGenContext context = new CodeGenContext(config); context.AddCapability(Capability.GroupNonUniformBallot); context.AddCapability(Capability.ImageBuffer); context.AddCapability(Capability.SampledBuffer); context.AddCapability(Capability.SubgroupBallotKHR); context.AddCapability(Capability.SubgroupVoteKHR); context.AddExtension("SPV_KHR_shader_ballot"); context.AddExtension("SPV_KHR_subgroup_vote"); Declarations.DeclareAll(context, info); if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0) { Declarations.DeclareInvocationId(context); } for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++) { var function = info.Functions[funcIndex]; var retType = context.GetType(function.ReturnType.Convert()); var funcArgs = new SpvInstruction[function.InArguments.Length + function.OutArguments.Length]; for (int argIndex = 0; argIndex < funcArgs.Length; argIndex++) { var argType = context.GetType(function.GetArgumentType(argIndex).Convert()); var argPointerType = context.TypePointer(StorageClass.Function, argType); funcArgs[argIndex] = argPointerType; } var funcType = context.TypeFunction(retType, false, funcArgs); var spvFunc = context.Function(retType, FunctionControlMask.MaskNone, funcType); context.DeclareFunction(funcIndex, function, spvFunc); } for (int funcIndex = 0; funcIndex < info.Functions.Count; funcIndex++) { Generate(context, info, funcIndex); } return(context.Generate()); }
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, int baseAttr, int baseIndex, bool isOutput) { int index = (attr - baseAttr) >> 4; int userAttrIndex = config.GetFreeUserAttribute(isOutput, index); if ((uint)userAttrIndex < Constants.MaxAttributes) { userAttrIndex += baseIndex; attr = AttributeConsts.UserAttributeBase + userAttrIndex * 16 + (attr & 0xf); if (isOutput) { config.SetOutputUserAttributeFixedFunc(userAttrIndex); } else { config.SetInputUserAttributeFixedFunc(userAttrIndex); } } return(attr); }
private OglShaderStage ShaderStageFactory( IGalMemory memory, long position, long positionB, bool isDualVp, GalShaderType type) { ShaderConfig config = new ShaderConfig(type, OglLimit.MaxUboSize); ShaderProgram program; if (isDualVp) { ShaderDumper.Dump(memory, position, type, "a"); ShaderDumper.Dump(memory, positionB, type, "b"); program = Translator.Translate(memory, (ulong)position, (ulong)positionB, config); } else { ShaderDumper.Dump(memory, position, type); program = Translator.Translate(memory, (ulong)position, config); } string code = program.Code; if (ShaderDumper.IsDumpEnabled()) { int shaderDumpIndex = ShaderDumper.DumpIndex; code = "//Shader " + shaderDumpIndex + Environment.NewLine + code; } return(new OglShaderStage(type, code, program.Info.CBuffers, program.Info.Textures)); }
public static void RunPass(BasicBlock[] blocks, ShaderConfig config) { for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { GlobalToStorage.RunPass(blocks[blkIndex], config); } bool modified; do { modified = false; for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { BasicBlock block = blocks[blkIndex]; LinkedListNode <INode> node = block.Operations.First; while (node != null) { LinkedListNode <INode> nextNode = node.Next; bool isUnused = IsUnused(node.Value); if (!(node.Value is Operation operation) || isUnused) { if (isUnused) { RemoveNode(block, node); modified = true; } node = nextNode; continue; } ConstantFolding.RunPass(operation); Simplification.RunPass(operation); if (DestIsLocalVar(operation)) { if (operation.Inst == Instruction.Copy) { PropagateCopy(operation); RemoveNode(block, node); modified = true; } else if ((operation.Inst == Instruction.PackHalf2x16 && PropagatePack(operation)) || (operation.Inst == Instruction.ShuffleXor && MatchDdxOrDdy(operation))) { if (operation.Dest.UseOps.Count == 0) { RemoveNode(block, node); } modified = true; } } node = nextNode; } if (BranchElimination.RunPass(block)) { RemoveNode(block, block.Operations.Last); modified = true; } } }while (modified); for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { BindlessToIndexed.RunPass(blocks[blkIndex]); BindlessElimination.RunPass(blocks[blkIndex]); // Try to eliminate any operations that are now unused. LinkedListNode <INode> node = blocks[blkIndex].Operations.First; while (node != null) { LinkedListNode <INode> nextNode = node.Next; if (IsUnused(node.Value)) { RemoveNode(blocks[blkIndex], node); } node = nextNode; } } }
public static string GetAttributeName(AstOperand attr, ShaderConfig config, bool isOutAttr = false, string indexExpr = "0") { int value = attr.Value; char swzMask = GetSwizzleMask((value >> 2) & 3); if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) { value -= AttributeConsts.UserAttributeBase; string prefix = isOutAttr ? DefaultNames.OAttributePrefix : DefaultNames.IAttributePrefix; if ((config.Flags & TranslationFlags.Feedback) != 0) { string name = $"{prefix}{(value >> 4)}_{swzMask}"; if (config.Stage == ShaderStage.Geometry && !isOutAttr) { name += $"[{indexExpr}]"; } return(name); } else { string name = $"{prefix}{(value >> 4)}"; if (config.Stage == ShaderStage.Geometry && !isOutAttr) { name += $"[{indexExpr}]"; } return(name + '.' + swzMask); } } else { if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) { value -= AttributeConsts.FragmentOutputColorBase; return($"{DefaultNames.OAttributePrefix}{(value >> 4)}.{swzMask}"); } else if (_builtInAttributes.TryGetValue(value & ~3, out BuiltInAttribute builtInAttr)) { // TODO: There must be a better way to handle this... if (config.Stage == ShaderStage.Fragment) { switch (value & ~3) { case AttributeConsts.PositionX: return("(gl_FragCoord.x / fp_renderScale[0])"); case AttributeConsts.PositionY: return("(gl_FragCoord.y / fp_renderScale[0])"); case AttributeConsts.PositionZ: return("gl_FragCoord.z"); case AttributeConsts.PositionW: return("gl_FragCoord.w"); } } string name = builtInAttr.Name; if (config.Stage == ShaderStage.Geometry && (value & AttributeConsts.SpecialMask) == 0 && !isOutAttr) { name = $"gl_in[{indexExpr}].{name}"; } return(name); } } // TODO: Warn about unknown built-in attribute. return(isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0"); }
public static string GetOutAttributeName(AstOperand attr, ShaderConfig config) { return(GetAttributeName(attr, config, isOutAttr: true)); }
private static void SetUserAttributeUses(ShaderConfig config, InstName name, ulong opCode) { int offset; int count = 1; bool isStore = false; bool indexed = false; bool perPatch = false; if (name == InstName.Ast) { InstAst opAst = new InstAst(opCode); count = (int)opAst.AlSize + 1; offset = opAst.Imm11; indexed = opAst.Phys; perPatch = opAst.P; isStore = true; } else if (name == InstName.Ald) { InstAld opAld = new InstAld(opCode); count = (int)opAld.AlSize + 1; offset = opAld.Imm11; indexed = opAld.Phys; perPatch = opAld.P; isStore = opAld.O; } else /* if (name == InstName.Ipa) */ { InstIpa opIpa = new InstIpa(opCode); offset = opIpa.Imm10; indexed = opIpa.Idx; } if (indexed) { if (isStore) { config.SetAllOutputUserAttributes(); } else { config.SetAllInputUserAttributes(); } } else { for (int elemIndex = 0; elemIndex < count; elemIndex++) { int attr = offset + elemIndex * 4; if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) { int index = (attr - AttributeConsts.UserAttributeBase) / 16; if (isStore) { config.SetOutputUserAttribute(index, perPatch); } else { config.SetInputUserAttribute(index, perPatch); } } if (!isStore && ((attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0) || (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd))) { config.SetUsedFeature(FeatureFlags.FixedFuncAttr); } } } }
private static LinkedListNode <INode> ReplaceLdgWithLdc(LinkedListNode <INode> node, ShaderConfig config, int storageIndex) { Operation operation = (Operation)node.Value; Operand GetCbufOffset() { Operand addrLow = operation.GetSource(0); Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize); Operand baseAddrTrunc = Local(); Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); node.List.AddBefore(node, andOp); Operand byteOffset = Local(); Operand wordOffset = Local(); Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc); Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); node.List.AddBefore(node, subOp); node.List.AddBefore(node, shrOp); return(wordOffset); } Operand[] sources = new Operand[operation.SourcesCount]; sources[0] = Const(UbeFirstCbuf + storageIndex); sources[1] = GetCbufOffset(); for (int index = 2; index < operation.SourcesCount; index++) { sources[index] = operation.GetSource(index); } Operation ldcOp = new Operation(Instruction.LoadConstant, operation.Dest, sources); for (int index = 0; index < operation.SourcesCount; index++) { operation.SetSource(index, null); } LinkedListNode <INode> oldNode = node; node = node.List.AddBefore(node, ldcOp); node.List.Remove(oldNode); return(node); }
private static LinkedListNode <INode> ReplaceGlobalWithStorage(LinkedListNode <INode> node, ShaderConfig config, int storageIndex) { Operation operation = (Operation)node.Value; Operand GetStorageOffset() { Operand addrLow = operation.GetSource(0); Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex)); Operand baseAddrTrunc = Local(); Operand alignMask = Const(-config.QueryInfo(QueryInfoName.StorageBufferOffsetAlignment)); Operation andOp = new Operation(Instruction.BitwiseAnd, baseAddrTrunc, baseAddrLow, alignMask); node.List.AddBefore(node, andOp); Operand byteOffset = Local(); Operand wordOffset = Local(); Operation subOp = new Operation(Instruction.Subtract, byteOffset, addrLow, baseAddrTrunc); Operation shrOp = new Operation(Instruction.ShiftRightU32, wordOffset, byteOffset, Const(2)); node.List.AddBefore(node, subOp); node.List.AddBefore(node, shrOp); return(wordOffset); } Operand[] sources = new Operand[operation.SourcesCount]; sources[0] = Const(storageIndex); sources[1] = GetStorageOffset(); for (int index = 2; index < operation.SourcesCount; index++) { sources[index] = operation.GetSource(index); } Operation storageOp; if (operation.Inst.IsAtomic()) { Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; storageOp = new Operation(inst, operation.Dest, sources); } else if (operation.Inst == Instruction.LoadGlobal) { storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources); } else { storageOp = new Operation(Instruction.StoreStorage, null, sources); } for (int index = 0; index < operation.SourcesCount; index++) { operation.SetSource(index, null); } LinkedListNode <INode> oldNode = node; node = node.List.AddBefore(node, storageOp); node.List.Remove(oldNode); return(node); }
public static void RunPass(BasicBlock block, ShaderConfig config) { // We can turn a bindless into regular access by recognizing the pattern // produced by the compiler for separate texture and sampler. // We check for the following conditions: // - The handle is a constant buffer value. // - The handle is the result of a bitwise OR logical operation. // - Both sources of the OR operation comes from a constant buffer. for (LinkedListNode <INode> node = block.Operations.First; node != null; node = node.Next) { if (!(node.Value is TextureOperation texOp)) { continue; } if ((texOp.Flags & TextureFlags.Bindless) == 0) { continue; } if (texOp.Inst == Instruction.Lod || texOp.Inst == Instruction.TextureSample || texOp.Inst == Instruction.TextureSize) { Operand bindlessHandle = Utils.FindLastOperation(texOp.GetSource(0), block); bool rewriteSamplerType = texOp.Inst == Instruction.TextureSize; if (bindlessHandle.Type == OperandType.ConstantBuffer) { SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot(), rewriteSamplerType); continue; } if (!(bindlessHandle.AsgOp is Operation handleCombineOp)) { continue; } if (handleCombineOp.Inst != Instruction.BitwiseOr) { continue; } Operand src0 = Utils.FindLastOperation(handleCombineOp.GetSource(0), block); Operand src1 = Utils.FindLastOperation(handleCombineOp.GetSource(1), block); if (src0.Type != OperandType.ConstantBuffer || src1.Type != OperandType.ConstantBuffer) { continue; } SetHandle( config, texOp, src0.GetCbufOffset() | ((src1.GetCbufOffset() + 1) << 16), src0.GetCbufSlot() | ((src1.GetCbufSlot() + 1) << 16), rewriteSamplerType); } else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore || texOp.Inst == Instruction.ImageAtomic) { Operand src0 = Utils.FindLastOperation(texOp.GetSource(0), block); if (src0.Type == OperandType.ConstantBuffer) { int cbufOffset = src0.GetCbufOffset(); int cbufSlot = src0.GetCbufSlot(); if (texOp.Inst == Instruction.ImageAtomic) { texOp.Format = config.GetTextureFormatAtomic(cbufOffset, cbufSlot); } else { texOp.Format = config.GetTextureFormat(cbufOffset, cbufSlot); } SetHandle(config, texOp, cbufOffset, cbufSlot, false); } } } }