---
title: "Generate C# shader bindings from HLSL"
url: https://recursiveai.org/oneshader/cookbook/generate-csharp-shader-bindings/
description: "Generate typed C# buffer structs and binding constants from HLSL during your .NET build. Complete .NET example with source and output."
version: "1.7.1"
---

# Generate C# shader bindings from HLSL

**OneShader** is a free, open-source HLSL compiler library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneShader.Build) · [GitHub](https://github.com/recursive-ai-oss/oneshader-csharp) · [Docs](https://recursiveai.org/oneshader/docs/) · [Download example (.zip)](https://recursiveai.org/oneshader/cookbook/generate-csharp-shader-bindings/generate-csharp-shader-bindings.zip)

Generate typed C# buffer structs and binding constants from HLSL during your .NET build.

```shell
dotnet add package RecursiveAI.OneShader.Build
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

`Fill` is a generated type, not a class you write. The snippet requires the shader and project configuration shown below.

```csharp
using Example.Shaders;

// Fill: generated from Shaders/Fill.hlsl.
Console.WriteLine(Fill.Settings.Size);
Console.WriteLine(Fill.Bindings.Output.Binding);
```

```text
16
1
```

Unzip [the example](https://recursiveai.org/oneshader/cookbook/generate-csharp-shader-bindings/generate-csharp-shader-bindings.zip), then `dotnet run`.

`Fill` is produced by this configuration in `project.csproj` (the download pins version 1.7.1):

```xml
<PropertyGroup>
  <RootNamespace>Example</RootNamespace>
</PropertyGroup>
<ItemGroup>
  <PackageReference Include="RecursiveAI.OneShader.Build" Version="1.7.1" />
  <OneShader Include="Shaders/Fill.hlsl" EntryPoints="CSMain:Compute"
             Namespace="Example.Shaders" ClassName="Fill" />
</ItemGroup>
```

`Shaders/Fill.hlsl`, included in the download:

```hlsl
cbuffer Settings : register(b0) { float4 Color; };
RWStructuredBuffer<float4> Output : register(u1);
[shader("compute")]
[numthreads(8, 1, 1)]
void CSMain(uint3 id : SV_DispatchThreadID)
{
    Output[id.x] = Color;
}
```

## The full program

The full program constructs the generated constant-buffer struct and prints its size, output binding and compute workgroup dimensions. It compiles the shader; it does not dispatch work on a GPU.

```csharp
using Example.Shaders;

var settings = new Fill.Settings { Color = new(1, 0, 0, 1) };
Console.WriteLine($"Settings: {Fill.Settings.Size} bytes");
Console.WriteLine($"Output: set {Fill.Bindings.Output.Set}, binding {Fill.Bindings.Output.Binding}");
Console.WriteLine($"Workgroup: {Fill.CSMain.ThreadGroupSizeX} x {Fill.CSMain.ThreadGroupSizeY} x {Fill.CSMain.ThreadGroupSizeZ}");
Console.WriteLine($"Red: {settings.Color.X}");
```

`dotnet run` prints:

```text
Settings: 16 bytes
Output: set 0, binding 1
Workgroup: 8 x 1 x 1
Red: 1
```

An HLSL compile error appears as an MSBuild diagnostic at the shader’s file and line; fix that error before consuming the generated types.

## Variations

Read the constant-buffer binding instead. In the same shader project, this prints `0`.

```csharp
using Example.Shaders;

Console.WriteLine(Fill.Bindings.Settings.Binding);
```

## Related recipes

- [Compile HLSL to SPIR-V in C#](https://recursiveai.org/oneshader/cookbook/hlsl-to-spirv/)
- [Compile HLSL in a C# project](https://recursiveai.org/oneshader/cookbook/compile-hlsl-csharp/)

## The short answer

Reference `RecursiveAI.OneShader.Build`, include an HLSL shader in the project, and build with the .NET CLI. Use the generated C# structs and binding constants instead of manually duplicating shader layouts.

## How do I generate C# shader bindings?

Add `RecursiveAI.OneShader.Build` to the project that contains your HLSL files. Its default item glob includes every `*.hlsl` file in that project. During `dotnet build`, the package compiles the shaders and generates a static partial C# class per file. `Fill.hlsl` in this example becomes `Example.Shaders.Fill` because the project specifies the `Example` root namespace.

The generated class exposes nested constant-buffer types, resource-binding constants and entry-point classes. You do not declare `Fill.Settings` or copy its members into a hand-written duplicate. Reference the generated type directly, as the full program does. This makes the compiled shader’s layout the source of the C# field offsets rather than relying on a separate packing calculation.

The build package brings the support types from `RecursiveAI.OneShader` with it. Use the .NET CLI’s build, not Visual Studio’s .NET Framework MSBuild: the build task requires .NET 10 (`dotnet build`). Only the projects containing shaders need the build package; it does not flow to consumers. See [generated bindings](https://recursiveai.org/oneshader/docs/generated-bindings/) for the full generated surface.

## How do I keep shader code and C# in sync?

Keep the HLSL file in the same project as the generated binding consumer. Editing the shader causes the build to regenerate the binding information. Includes, compiler changes and changed compilation options also participate in recompilation. A renamed buffer or entry point then changes the generated C# API, allowing a consumer that still uses the old name to fail at compilation instead of carrying an obsolete layout silently.

For each constant buffer the generator emits a blittable, explicitly laid-out struct. Its `Size` constant and field offsets match the compiled shader. Float vectors and matrices use `System.Numerics` types; this shader’s `float4 Color` becomes a field that accepts a four-component vector. The example’s 16-byte size comes from this shader, not a fixed size for all constant buffers.

Resource bindings expose set, binding, count and kind, plus a span describing the resources and stage use. The sample’s `register(u1)` is reflected as output binding 1. Consume the generated values when creating your graphics API resources; the console program only prints them. Binding generation does not allocate a GPU buffer, upload the settings or execute a compute dispatch.

## How do I configure shader entry points in a C# project?

The build finds entry points from HLSL `shader` attributes for vertex, pixel and compute functions. Here `[shader("compute")]` marks `CSMain`, and `numthreads` supplies its workgroup dimensions. Those dimensions are available as generated constants, so the host can read them without duplicating the values from HLSL.

You can instead provide explicit entry points with `OneShader` item metadata. The configuration above names the compute entry point, output namespace and class. Listing a file explicitly does not compile it twice: the default glob skips files already listed. The same metadata mechanism supports defines, include directories, target backends and accessibility.

Generated classes are public by default. Set accessibility to internal when bindings are an implementation detail rather than an API your consumers should reference. The default target embeds SPIR-V; additional targets expose their own generated output properties. Accessing generated shader bytes is separate from consuming binding constants: this sample needs no device and does not claim to test GPU execution.
