private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex) { var function = info.Functions[funcIndex]; (_, var spvFunc) = context.GetFunction(funcIndex); context.AddFunction(spvFunc); context.StartFunction(); Declarations.DeclareParameters(context, function); context.EnterBlock(function.MainBlock); Declarations.DeclareLocals(context, function); Declarations.DeclareLocalForArgs(context, info.Functions); Generate(context, function.MainBlock); context.FunctionEnd(); if (funcIndex == 0) { context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface()); if (context.Config.Stage == ShaderStage.Fragment) { context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); } else if (context.Config.Stage == ShaderStage.Compute) { var localSizeX = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeX(); var localSizeY = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeY(); var localSizeZ = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeZ(); context.AddExecutionMode( spvFunc, ExecutionMode.LocalSize, localSizeX, localSizeY, localSizeZ); } } }
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex) { var function = info.Functions[funcIndex]; (_, var spvFunc) = context.GetFunction(funcIndex); context.AddFunction(spvFunc); context.StartFunction(); Declarations.DeclareParameters(context, function); context.EnterBlock(function.MainBlock); Declarations.DeclareLocals(context, function); Declarations.DeclareLocalForArgs(context, info.Functions); if (funcIndex == 0) { var v4Type = context.TypeVector(context.TypeFP32(), 4); var zero = context.Constant(context.TypeFP32(), 0f); var one = context.Constant(context.TypeFP32(), 1f); // Some games will leave some elements of gl_Position uninitialized, // in those cases, the elements will contain undefined values according // to the spec, but on NVIDIA they seems to be always initialized to (0, 0, 0, 1), // so we do explicit initialization to avoid UB on non-NVIDIA gpus. if (context.Config.Stage == ShaderStage.Vertex) { var elemPointer = context.GetAttributeVectorPointer(new AstOperand(OperandType.Attribute, AttributeConsts.PositionX), true); context.Store(elemPointer, context.CompositeConstruct(v4Type, zero, zero, zero, one)); } // Ensure that unused attributes are set, otherwise the downstream // compiler may eliminate them. // (Not needed for fragment shader as it is the last stage). if (context.Config.Stage != ShaderStage.Compute && context.Config.Stage != ShaderStage.Fragment && !context.Config.GpPassthrough) { for (int attr = 0; attr < Declarations.MaxAttributes; attr++) { if (info.Outputs.Contains(attr)) { continue; } if (context.Config.Options.Flags.HasFlag(TranslationFlags.Feedback)) { throw new NotImplementedException(); } else { int currAttr = AttributeConsts.UserAttributeBase + attr * 16; var elemPointer = context.GetAttributeVectorPointer(new AstOperand(OperandType.Attribute, currAttr), true); context.Store(elemPointer, context.CompositeConstruct(v4Type, zero, zero, zero, one)); } } } } Generate(context, function.MainBlock); context.FunctionEnd(); if (funcIndex == 0) { context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface()); if (context.Config.Stage == ShaderStage.Fragment) { context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan ? ExecutionMode.OriginUpperLeft : ExecutionMode.OriginLowerLeft); } else if (context.Config.Stage == ShaderStage.Compute) { var localSizeX = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeX(); var localSizeY = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeY(); var localSizeZ = (SpvLiteralInteger)context.Config.GpuAccessor.QueryComputeLocalSizeZ(); context.AddExecutionMode( spvFunc, ExecutionMode.LocalSize, localSizeX, localSizeY, localSizeZ); } } }
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex) { var function = info.Functions[funcIndex]; (_, var spvFunc) = context.GetFunction(funcIndex); context.AddFunction(spvFunc); context.StartFunction(); Declarations.DeclareParameters(context, function); context.EnterBlock(function.MainBlock); Declarations.DeclareLocals(context, function); Declarations.DeclareLocalForArgs(context, info.Functions); Generate(context, function.MainBlock); // Functions must always end with a return. if (!(function.MainBlock.Last is AstOperation operation) || (operation.Inst != Instruction.Return && operation.Inst != Instruction.Discard)) { context.Return(); } context.FunctionEnd(); if (funcIndex == 0) { context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface()); if (context.Config.Stage == ShaderStage.TessellationControl) { context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive); } else if (context.Config.Stage == ShaderStage.TessellationEvaluation) { switch (context.Config.GpuAccessor.QueryTessPatchType()) { case TessPatchType.Isolines: context.AddExecutionMode(spvFunc, ExecutionMode.Isolines); break; case TessPatchType.Triangles: context.AddExecutionMode(spvFunc, ExecutionMode.Triangles); break; case TessPatchType.Quads: context.AddExecutionMode(spvFunc, ExecutionMode.Quads); break; } switch (context.Config.GpuAccessor.QueryTessSpacing()) { case TessSpacing.EqualSpacing: context.AddExecutionMode(spvFunc, ExecutionMode.SpacingEqual); break; case TessSpacing.FractionalEventSpacing: context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalEven); break; case TessSpacing.FractionalOddSpacing: context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalOdd); break; } if (context.Config.GpuAccessor.QueryTessCw()) { context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw); } else { context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCcw); } } else if (context.Config.Stage == ShaderStage.Geometry) { InputTopology inputTopology = context.Config.GpuAccessor.QueryPrimitiveTopology(); context.AddExecutionMode(spvFunc, inputTopology switch { InputTopology.Points => ExecutionMode.InputPoints, InputTopology.Lines => ExecutionMode.InputLines, InputTopology.LinesAdjacency => ExecutionMode.InputLinesAdjacency, InputTopology.Triangles => ExecutionMode.Triangles, InputTopology.TrianglesAdjacency => ExecutionMode.InputTrianglesAdjacency, _ => throw new InvalidOperationException($"Invalid input topology \"{inputTopology}\".") });