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 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); } } }
public static byte[] Generate(StructuredProgramInfo info, ShaderConfig config) { SpvInstructionPool instPool; SpvLiteralIntegerPool integerPool; lock (PoolLock) { instPool = InstructionPool.Allocate(); integerPool = IntegerPool.Allocate(); } CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool); context.AddCapability(Capability.GroupNonUniformBallot); context.AddCapability(Capability.ImageBuffer); context.AddCapability(Capability.ImageGatherExtended); context.AddCapability(Capability.ImageQuery); context.AddCapability(Capability.SampledBuffer); context.AddCapability(Capability.SubgroupBallotKHR); context.AddCapability(Capability.SubgroupVoteKHR); if (config.TransformFeedbackEnabled && config.LastInVertexPipeline) { context.AddCapability(Capability.TransformFeedback); } if (config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryHostSupportsFragmentShaderInterlock()) { context.AddCapability(Capability.FragmentShaderPixelInterlockEXT); context.AddExtension("SPV_EXT_fragment_shader_interlock"); } else if (config.Stage == ShaderStage.Geometry) { context.AddCapability(Capability.Geometry); if (config.GpPassthrough && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough()) { context.AddExtension("SPV_NV_geometry_shader_passthrough"); context.AddCapability(Capability.GeometryShaderPassthroughNV); } } else if (config.Stage == ShaderStage.TessellationControl || config.Stage == ShaderStage.TessellationEvaluation) { context.AddCapability(Capability.Tessellation); } 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); } byte[] result = context.Generate(); lock (PoolLock) { InstructionPool.Release(instPool); IntegerPool.Release(integerPool); } return(result); }
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}\".") });