Shader Data and Binding
Shader compilation
ZenithCompiler compiles a named Slang entry point into a ShaderDesc. The context's GraphicsApi selects the required target: DXIL for DirectX 12, a Metal library for Metal, or SPIR-V for Vulkan.
context already created, compile the triangle shader from the copy placed beside the executable:
string shaderPath = Path.Combine(AppContext.BaseDirectory, "Triangle.slang");
Shader vertexShader = context.CreateShader(ZenithCompiler.CompileFromFile(context.GraphicsApi, shaderPath, "VSMain"));
VSMain must match the declaration in the shader file. Compile shaders and create pipelines during initialization, then reuse the pipelines while rendering. Once pipeline creation has completed, the shader objects may be released, as shown in
Data layout
InputLayout describes each attribute's format, offset, and semantic, together with the stride between vertices. The triangle places a Float3 position before a Float4 color: the color starts at byte offset 12, and each vertex occupies 28 bytes. InputLayout.Add calculates this packed layout from the element formats. If the C# struct contains padding, supply offsets and a stride that account for it.
InputLayout. Match the C# field offsets, element sizes, and padding to the compiled shader layout. A C# unmanaged type can be copied as bytes, but its memory layout does not necessarily match the shader's layout; the tightly packed float3 used for a vertex may require different alignment in a shader buffer.
System.Runtime.InteropServices and Zenith.NET:
[StructLayout(LayoutKind.Explicit, Size = 256)]
file struct Constants
{
[FieldOffset(0)]
public uint Width;
[FieldOffset(4)]
public uint Height;
[FieldOffset(8)]
public ResourceHandle Input;
[FieldOffset(16)]
public ResourceHandle Output;
}
struct Constants
{
uint Width;
uint Height;
DescriptorHandle<Texture2D> Input;
DescriptorHandle<RWTexture2D<float4>> Output;
};
ConstantBuffer<Constants> constants;
Matrix conventions
Matrix4x4 values at offsets 0, 64, and 128. Its mul(vector, matrix) in model, view, and projection order.
Constant buffers and resource handles
SetConstantBuffer selects an existing buffer and byte offset from which the current pipeline reads its constants. Upload the parameter data into that storage before it is used by the shader. If the same bytes are still being read by earlier GPU work, wait for that work before updating them.
commandBuffer.SetPipeline(computePipeline);
commandBuffer.SetConstantBuffer(constantBuffer, 0);
commandBuffer.Dispatch(groupCountX, groupCountY, 1);
commandBuffer records a dispatch using computePipeline and the parameters stored in constantBuffer. The buffer requires BufferUsages.Constant usage, and offset 0 selects its beginning. If several constant records share one buffer, each record's starting offset must also meet the backend's binding alignment, independently of the layout of fields within the record.
inputTexture.SampledHandle to Input and outputTexture.StorageHandle to Output. In Slang, the type parameter of DescriptorHandle defines how the shader accesses each resource:
|
|
|
|
|---|---|---|
texture.SampledHandle
|
DescriptorHandle<Texture2D>
|
|
texture.StorageHandle
|
DescriptorHandle<RWTexture2D<float4>>
|
|
buffer.StorageReadOnlyHandle
|
DescriptorHandle<StructuredBuffer<T>>
|
T |
buffer.StorageReadWriteHandle
|
DescriptorHandle<RWStructuredBuffer<T>>
|
T |
sampler.Handle
|
DescriptorHandle<SamplerState>
|
|
ResourceHandle contains two 32-bit fields whose interpretation depends on the backend. Its values are valid only within the context that created the resource.
Thread groups
Dispatch specifies how many thread groups to execute. The compute sample declares [numthreads(16, 16, 1)], so each group contains 16 × 16 threads. To cover an image, divide its width and height by 16 and round each result up: a 17 × 19 image requires 2 × 2 groups. The shader checks each thread's coordinates and skips those outside the image.
ShaderDesc.ThreadGroupSize and can be used to calculate the group counts passed to Dispatch. ComputeShaderRenderer.cs shows the complete calculation. After the dispatch, access to the resulting image must follow the requirements described in