// Copyright (C) 2026 SharpEmu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later namespace SharpEmu.Libs.Agc; internal static class SpirvFixedShaders { public static byte[] CreateFullscreenVertex(uint attributeCount) { var module = new SpirvModuleBuilder(); module.AddCapability(SpirvCapability.Shader); var voidType = module.TypeVoid(); var boolType = module.TypeBool(); var uintType = module.TypeInt(32, signed: false); var floatType = module.TypeFloat(32); var vec4Type = module.TypeVector(floatType, 4); var inputUintPointer = module.TypePointer(SpirvStorageClass.Input, uintType); var outputVec4Pointer = module.TypePointer(SpirvStorageClass.Output, vec4Type); var vertexIndex = module.AddGlobalVariable(inputUintPointer, SpirvStorageClass.Input); module.AddName(vertexIndex, "vertexIndex"); module.AddDecoration( vertexIndex, SpirvDecoration.BuiltIn, (uint)SpirvBuiltIn.VertexIndex); var position = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output); module.AddName(position, "position"); module.AddDecoration(position, SpirvDecoration.BuiltIn, (uint)SpirvBuiltIn.Position); var attributes = new uint[attributeCount]; for (uint index = 0; index < attributeCount; index++) { attributes[index] = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output); module.AddName(attributes[index], $"attr{index}"); module.AddDecoration(attributes[index], SpirvDecoration.Location, index); module.AddDecoration(attributes[index], SpirvDecoration.NoPerspective); } var functionType = module.TypeFunction(voidType); var main = module.BeginFunction(voidType, functionType); module.AddName(main, "main"); module.AddLabel(); var indexValue = module.AddInstruction(SpirvOp.Load, uintType, vertexIndex); var one = module.Constant(uintType, 1); var two = module.Constant(uintType, 2); var shifted = module.AddInstruction(SpirvOp.ShiftLeftLogical, uintType, indexValue, one); var xBits = module.AddInstruction(SpirvOp.BitwiseAnd, uintType, shifted, two); var yBits = module.AddInstruction(SpirvOp.BitwiseAnd, uintType, indexValue, two); var x = module.AddInstruction(SpirvOp.ConvertUToF, floatType, xBits); var y = module.AddInstruction(SpirvOp.ConvertUToF, floatType, yBits); var zero = module.ConstantFloat(floatType, 0f); var oneFloat = module.ConstantFloat(floatType, 1f); var twoFloat = module.ConstantFloat(floatType, 2f); var xPosition = module.AddInstruction(SpirvOp.FMul, floatType, x, twoFloat); xPosition = module.AddInstruction(SpirvOp.FSub, floatType, xPosition, oneFloat); var yPosition = module.AddInstruction(SpirvOp.FMul, floatType, y, twoFloat); yPosition = module.AddInstruction(SpirvOp.FSub, floatType, yPosition, oneFloat); var positionValue = module.AddInstruction( SpirvOp.CompositeConstruct, vec4Type, xPosition, yPosition, zero, oneFloat); module.AddStatement(SpirvOp.Store, position, positionValue); var attributeValue = module.AddInstruction( SpirvOp.CompositeConstruct, vec4Type, x, y, zero, oneFloat); foreach (var attribute in attributes) { module.AddStatement(SpirvOp.Store, attribute, attributeValue); } module.AddStatement(SpirvOp.Return); module.EndFunction(); var interfaces = new uint[2 + attributes.Length]; interfaces[0] = vertexIndex; interfaces[1] = position; attributes.CopyTo(interfaces, 2); module.AddEntryPoint(SpirvExecutionModel.Vertex, main, "main", interfaces); _ = boolType; return module.Build(); } public static byte[] CreateCopyFragment() { var module = new SpirvModuleBuilder(); module.AddCapability(SpirvCapability.Shader); var voidType = module.TypeVoid(); var floatType = module.TypeFloat(32); var vec2Type = module.TypeVector(floatType, 2); var vec4Type = module.TypeVector(floatType, 4); var inputVec4Pointer = module.TypePointer(SpirvStorageClass.Input, vec4Type); var outputVec4Pointer = module.TypePointer(SpirvStorageClass.Output, vec4Type); var imageType = module.TypeImage( floatType, SpirvImageDim.Dim2D, depth: false, arrayed: false, multisampled: false, sampled: 1, SpirvImageFormat.Unknown); var sampledImageType = module.TypeSampledImage(imageType); var sampledImagePointer = module.TypePointer(SpirvStorageClass.UniformConstant, sampledImageType); var attribute = module.AddGlobalVariable(inputVec4Pointer, SpirvStorageClass.Input); module.AddName(attribute, "attr0"); module.AddDecoration(attribute, SpirvDecoration.Location, 0); var texture = module.AddGlobalVariable( sampledImagePointer, SpirvStorageClass.UniformConstant); module.AddName(texture, "tex0"); module.AddDecoration(texture, SpirvDecoration.DescriptorSet, 0); module.AddDecoration(texture, SpirvDecoration.Binding, 1); var output = module.AddGlobalVariable(outputVec4Pointer, SpirvStorageClass.Output); module.AddName(output, "outColor"); module.AddDecoration(output, SpirvDecoration.Location, 0); var functionType = module.TypeFunction(voidType); var main = module.BeginFunction(voidType, functionType); module.AddName(main, "main"); module.AddLabel(); var attributeValue = module.AddInstruction(SpirvOp.Load, vec4Type, attribute); var coordinates = module.AddInstruction( SpirvOp.VectorShuffle, vec2Type, attributeValue, attributeValue, 0, 1); var sampledImage = module.AddInstruction(SpirvOp.Load, sampledImageType, texture); var lod = module.ConstantFloat(floatType, 0f); var color = module.AddInstruction( SpirvOp.ImageSampleExplicitLod, vec4Type, sampledImage, coordinates, 2, lod); module.AddStatement(SpirvOp.Store, output, color); module.AddStatement(SpirvOp.Return); module.EndFunction(); module.AddEntryPoint( SpirvExecutionModel.Fragment, main, "main", [attribute, texture, output]); module.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft); return module.Build(); } }