ThoughtStuff.GLSourceGen 2.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package ThoughtStuff.GLSourceGen --version 2.0.0                
NuGet\Install-Package ThoughtStuff.GLSourceGen -Version 2.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ThoughtStuff.GLSourceGen" Version="2.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ThoughtStuff.GLSourceGen --version 2.0.0                
#r "nuget: ThoughtStuff.GLSourceGen, 2.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ThoughtStuff.GLSourceGen as a Cake Addin
#addin nuget:?package=ThoughtStuff.GLSourceGen&version=2.0.0

// Install ThoughtStuff.GLSourceGen as a Cake Tool
#tool nuget:?package=ThoughtStuff.GLSourceGen&version=2.0.0                

ThoughtStuff.GLSourceGen

ThoughtStuff.GLSourceGen is a source generator that automates OpenGL/WebGL calls for mapping vertex data structures to shaders. Specifically, it generates calls to:

  • GL.BindBuffer
  • GL.BufferData
  • GL.GetAttribLocation
  • GL.EnableVertexAttribArray
  • GL.VertexAttribPointer

Benefits of Source Generation

  • Compile-Time Generation: Code is generated during compilation, eliminating without using reflection.
  • Performance: No reflection means faster execution and lower memory usage.
  • AOT Compatibility: Fully supports Ahead-of-Time (AOT) compilation for platforms like WebAssembly.

Get Started

  • Add Package Reference
    dotnet add package ThoughtStuff.GLSourceGen
    
  • Add shaders as AdditionalFiles in csproj
    <ItemGroup>
    
        <AdditionalFiles Include="Shaders\**\*.glsl" />
    </ItemGroup>
    

Example

Given a typical vertex structure for 2D colored vertices with Position and Color:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct ColorVertex2(Vector2 position, Vector4 color)
{
    public Vector2 Position = position;
    public Vector4 Color = color;
}

And shader with a_VertexPosition and a_VertexColor input attribute variables. (The source generator will parse the shader to identify attribute names that match the vertex structure fields.)

attribute vec4 a_VertexPosition;
attribute vec4 a_VertexColor;

varying mediump vec4 v_Color;

void main(void) {
    gl_Position = a_VertexPosition;
    v_Color = a_VertexColor;
}

We can leverage the SetupVertexAttrib attribute to generate the necessary OpenGL calls:

// Declare the partial class which will be implemented by the source generator.
[SetupVertexAttrib("Shaders/Basic_vert.glsl", typeof(ColorVertex2))]
partial class ColorVertex2ShaderBinding
{
    // While not strictly necessary, these method declarations can help the IDE with code completion

    // Call SetVertexData during initialization to set up the vertex buffer and pass the data to the GPU.
    internal static partial void SetVertexData(JSObject shaderProgram,
                                               JSObject vertexBuffer,
                                               Span<ColorVertex2> vertices,
                                               List<int> vertexAttributeLocations);

    // Call EnableVertexBuffer during rendering if the vertex buffer needs to be re-enabled,
    // or if you are switching between multiple vertex buffers.
    internal static void EnableVertexBuffer(JSObject vertexBuffer,
                                            List<int>? vertexAttributeLocations = null);
}

In the main program, we can now use the generated code to set up the vertex buffer:

partial class HelloTriangle
{
    public void InitializeScene(IShaderLoader shaderLoader)
    {
        var shaderProgram = shaderLoader.LoadShaderProgram("Shaders/Basic_vert.glsl", ...);

        // Define the vertices for the triangle. Assume NDC coordinates [-1 ... 1].
        Span<ColorVertex2> vertices =
        [
            new ColorVertex2(new(0, 1), new(1, 0, 0, 1)),    // Red vertex
            new ColorVertex2(new(-1, -1), new(0, 1, 0, 1)),  // Green vertex
            new ColorVertex2(new(1, -1), new(0, 0, 1, 1))    // Blue vertex
        ];

        // Create a buffer for the triangle's vertex positions.
        var positionBuffer = GL.CreateBuffer();
        // Call the generated function which sets up the vertex buffer and passes the data to the GPU.
        ColorVertex2ShaderBinding.SetBufferData(shaderProgram, positionBuffer, vertices, vertexAttributeLocations);
    }

    public void Render()
    {
        GL.Clear(GL.COLOR_BUFFER_BIT);
        GL.DrawArrays(GL.TRIANGLES, 0, 3);
    }

The source generator will generate the following code:


partial class ColorVertex2ShaderBinding
{
    // Private "cache" fields for attribute locations, strides, and offsets
    private static int _ColorVertex2_Position_location;
    private static int _ColorVertex2_Position_stride;
    private static int _ColorVertex2_Position_offset;
    private static int _ColorVertex2_Color_location;
    private static int _ColorVertex2_Color_stride;
    private static int _ColorVertex2_Color_offset;
    private static bool _ColorVertex2_vertexLayoutInitialized;

    private static void _InitVertexLayoutFields_ColorVertex2(JSObject shaderProgram)
    {
        if (_ColorVertex2_vertexLayoutInitialized)
            return;

        // Cache the attribute locations, strides, and offsets
        _ColorVertex2_Position_location = GL.GetAttribLocation(shaderProgram, "a_VertexPosition");
        if (_ColorVertex2_Position_location == -1)
            throw new InvalidOperationException($"Could not find shader attribute location for a_VertexPosition.");
        _ColorVertex2_Position_stride = Marshal.SizeOf<GenShaderBinding.GameApp.Examples.ColorVertex2>();
        _ColorVertex2_Position_offset = Marshal.OffsetOf<GenShaderBinding.GameApp.Examples.ColorVertex2>(nameof(GenShaderBinding.GameApp.Examples.ColorVertex2.Position)).ToInt32();

        _ColorVertex2_Color_location = GL.GetAttribLocation(shaderProgram, "a_VertexColor");
        if (_ColorVertex2_Color_location == -1)
            throw new InvalidOperationException($"Could not find shader attribute location for a_VertexColor.");
        _ColorVertex2_Color_stride = Marshal.SizeOf<GenShaderBinding.GameApp.Examples.ColorVertex2>();
        _ColorVertex2_Color_offset = Marshal.OffsetOf<GenShaderBinding.GameApp.Examples.ColorVertex2>(nameof(GenShaderBinding.GameApp.Examples.ColorVertex2.Color)).ToInt32();

        _ColorVertex2_vertexLayoutInitialized = true;
    }

    internal static void EnableVertexBuffer(JSObject vertexBuffer,
                                                          List<int>? vertexAttributeLocations = null)
    {
        if (!_ColorVertex2_vertexLayoutInitialized)
            throw new InvalidOperationException("Vertex layout fields have not been initialized.");

        // Bind the vertex buffer
        GL.BindBuffer(GL.ARRAY_BUFFER, vertexBuffer);

        // Enable the vertex attributes
        vertexAttributeLocations?.Add(_ColorVertex2_Position_location);
        GL.VertexAttribPointer(_ColorVertex2_Position_location,
                            size: 2,
                            type: GL.FLOAT,
                            normalized: false,
                            stride: _ColorVertex2_Position_stride,
                            offset: _ColorVertex2_Position_offset);
        GL.EnableVertexAttribArray(_ColorVertex2_Position_location);

        vertexAttributeLocations?.Add(_ColorVertex2_Color_location);
        GL.VertexAttribPointer(_ColorVertex2_Color_location,
                            size: 4,
                            type: GL.FLOAT,
                            normalized: false,
                            stride: _ColorVertex2_Color_stride,
                            offset: _ColorVertex2_Color_offset);
        GL.EnableVertexAttribArray(_ColorVertex2_Color_location);

    }

    internal static partial void SetVertexData(JSObject shaderProgram,
                                                    JSObject vertexBuffer,
                                                    Span<GenShaderBinding.GameApp.Examples.ColorVertex2> vertices,
                                                    List<int> vertexAttributeLocations)
    {
        // Initialize the vertex layout fields
        _InitVertexLayoutFields_ColorVertex2(shaderProgram);

        // Enable the vertex buffer and attributes
        EnableVertexBuffer(vertexBuffer, vertexAttributeLocations);

        // Upload the vertex data to the GPU
        GL.BufferData(GL.ARRAY_BUFFER, vertices, GL.STATIC_DRAW);
    }
}
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.1.0 120 10/12/2024
2.0.1 88 10/12/2024
2.0.0 98 9/30/2024
1.0.1 122 9/17/2024
1.0.0 115 9/14/2024