internal Shader(ShaderLayout info, ShaderProgram program) { Layout = info; Layout.IncRefCount(); Program = program; Program.IncRefCount(); }
// Parse a compiled VSL shader and return the info and modules public static void LoadStream(string path, Stream stream, out ShaderLayout info, out VkShaderModule vertMod, out VkShaderModule fragMod) { // Open the file and check the header using var file = new BinaryReader(stream); Span <byte> header = stackalloc byte[4]; file.Read(header); if ((header[0] != 'V') || (header[1] != 'B') || (header[2] != 'C')) { throw new InvalidShaderException(path, "Invalid magic number"); } if (header[3] != 1) { throw new InvalidShaderException(path, "Invalid shader file version"); } // Read rest of header file.Read(header.Slice(0, 1)); if (header[0] != 1) { throw new InvalidShaderException(path, "Invalid shader type, only graphics shaders supported"); } Span <ushort> bcLengths = stackalloc ushort[5]; file.Read(MemoryMarshal.AsBytes(bcLengths)); Span <ushort> tableSizes = stackalloc ushort[5]; file.Read(MemoryMarshal.AsBytes(tableSizes)); // Validate header values if (bcLengths[0] == 0) { throw new InvalidShaderException(path, "Shader has no vertex stage"); } if ((bcLengths[1] != 0) || (bcLengths[2] != 0) || (bcLengths[3] != 0)) { throw new InvalidShaderException(path, "Shader has unsupported stages"); } if (bcLengths[4] == 0) { throw new InvalidShaderException(path, "Shader has no fragment stage"); } if ((tableSizes[0] != BindingTable.DEFAULT_SIZE_SAMPLER) || (tableSizes[1] != BindingTable.DEFAULT_SIZE_IMAGE) || (tableSizes[2] != BindingTable.DEFAULT_SIZE_BUFFER) || (tableSizes[3] != BindingTable.DEFAULT_SIZE_ROTEXELS) || (tableSizes[4] != BindingTable.DEFAULT_SIZE_RWTEXELS)) { throw new InvalidShaderException(path, "Invalid binding table sizes"); } // Read the vertex inputs var inputCount = file.ReadUInt32(); Span <InterfaceVariable> inputs = stackalloc InterfaceVariable[(int)inputCount]; file.Read(MemoryMarshal.AsBytes(inputs)); ProcessVertexInputs(path, inputs, out var reflInputs); // Read the fragment outputs var outputCount = file.ReadUInt32(); Span <InterfaceVariable> outputs = stackalloc InterfaceVariable[(int)outputCount]; file.Read(MemoryMarshal.AsBytes(outputs)); ProcessFragmentOutputs(path, outputs, out var reflOutputs); // Read the bindings var bindingCount = file.ReadUInt32(); Span <BindingVariable> bindings = stackalloc BindingVariable[(int)bindingCount]; file.Read(MemoryMarshal.AsBytes(bindings)); ProcessBindings(path, bindings, out var reflBindings); // Read the uniform info var uniformSize = file.ReadUInt16(); ShaderLayout.UniformMember[]? reflUniformMembers = null; ShaderStages uniformStages = ShaderStages.None; if (uniformSize > 0) { uniformStages = (ShaderStages)file.ReadUInt16(); var uniformMemberCount = file.ReadUInt32(); Span <StructMember> members = stackalloc StructMember[(int)uniformMemberCount]; var memberNames = new string[uniformMemberCount]; Span <byte> nameBytes = stackalloc byte[64]; for (uint i = 0; i < uniformMemberCount; ++i) { uint nameLength = file.ReadByte(); var thisName = nameBytes.Slice(0, (int)nameLength); file.Read(thisName); file.Read(MemoryMarshal.AsBytes(members.Slice((int)i, 1))); memberNames[i] = Encoding.ASCII.GetString(thisName); } ProcessUniformMembers(path, members, memberNames, out reflUniformMembers); } // Read the subpass inputs var spiCount = file.ReadUInt32(); Span <SubpassInput> spi = stackalloc SubpassInput[(int)spiCount]; if (spiCount > 0) { file.Read(MemoryMarshal.AsBytes(spi)); } ProcessSubpassInputs(path, spi, out var reflSpi); // Read the bytecodes var vertBC = new uint[bcLengths[0]]; var fragBC = new uint[bcLengths[4]]; file.Read(MemoryMarshal.AsBytes(new Span <uint>(vertBC))); file.Read(MemoryMarshal.AsBytes(new Span <uint>(fragBC))); // Last validation if (file.BaseStream.Position != file.BaseStream.Length) { throw new InvalidShaderException(path, "File not fully consumed by parser"); } // Create shader modules info = new( ShaderStages.Vertex | ShaderStages.Fragment, reflInputs, reflOutputs, reflBindings, uniformSize, uniformStages, reflUniformMembers ?? Array.Empty <ShaderLayout.UniformMember>(), reflSpi ); CreateShaderModules(path, Core.Instance !.Graphics.VkDevice, vertBC, fragBC, out vertMod, out fragMod ); }