202 lines
4.2 KiB
Markdown
202 lines
4.2 KiB
Markdown
# Shader Compiler
|
|
|
|
A compiler for a custom, platform-agnostic shader language that compiles to platform specific shader languages (currently only HLSL).
|
|
|
|
## Features
|
|
|
|
A simple passthrough shader (`passthrough.shd`) can look as follows
|
|
```hlsl
|
|
vertex main :: (position : float4 @position) -> float4 {
|
|
return position;
|
|
}
|
|
|
|
pixel main :: () -> float4 @target {
|
|
return float4(1, 1, 1, 1);
|
|
}
|
|
```
|
|
The shader contains both vertex and pixel shader code, where you specify the entry points as above. Entry points can have any and overlapping names, which will be exposed in the meta data of a compiled shader.
|
|
There is basic support for most HLSL built-in math functions for the following types:
|
|
- Scalar types: int, float
|
|
- Vector types: float2, float3, float4, int2, int3, int4
|
|
- Matrices: float4x4
|
|
All of the above can be constructed with their namesake constructors i.e. `float4(x, y, z, w);`.
|
|
We don't yet support textures and samplers.
|
|
|
|
If you want to declare and use variables you can do it as follows
|
|
```hlsl
|
|
x : float = 2.0; // no 'f' suffix required or even supported (it gives an error)
|
|
y : float = 4.0;
|
|
v : float2 = float2(x, y);
|
|
```
|
|
For now it is required to specify the type of the variable (no type inference).
|
|
|
|
You can also do arithmetic as you would expect
|
|
```
|
|
x : float = 2.0 * 4.0 + (3 * 2); // int literals automatically convert to floats.
|
|
```
|
|
|
|
There is basic struct support
|
|
```hlsl
|
|
Camera_Data :: struct {
|
|
projection : float4x4;
|
|
view : float4x4;
|
|
}
|
|
```
|
|
And there is a special struct called `properties`, which is used for custom data you want to pass in.
|
|
```hlsl
|
|
properties {
|
|
projection : float4x4;
|
|
view : float4x4;
|
|
}
|
|
```
|
|
which will be exposed in the compiled result. `properties` can be renamed to a custom/shorter name like
|
|
```
|
|
p :: properties {
|
|
...
|
|
}
|
|
```
|
|
|
|
You can also define constant buffers
|
|
|
|
```
|
|
camera :: Constant_Buffer {
|
|
projection : float4x4;
|
|
view : float4x4;
|
|
}
|
|
```
|
|
|
|
## Jai Usage Example
|
|
|
|
To compile a shader and use the result, you can do the following in jai
|
|
```jai
|
|
|
|
parse_shader :: (path : string, allocator : Allocator) -> Compilation_Result {
|
|
// In the future, you can pass environment defines to the compiler.
|
|
compiler : Shader_Compiler;
|
|
|
|
return compile_file(*compiler, path,, allocator);
|
|
}
|
|
|
|
result := parse_shader("shader.shd", allocator);
|
|
if result.had_error {
|
|
log_error("%\n", report_messages(result.messages),, temp);
|
|
return;
|
|
}
|
|
|
|
collection := result.collection;
|
|
variant := collection.variants[0];
|
|
}
|
|
```
|
|
|
|
When parsing a shader you get the following struct as a result
|
|
```
|
|
Compilation_Result :: struct {
|
|
messages : [..]Compiler_Message;
|
|
|
|
had_error : bool;
|
|
|
|
collection : Shader_Variant_Collection;
|
|
}
|
|
```
|
|
|
|
A `Shader_Variant_Collection` looks as follows
|
|
```
|
|
Shader_Variant_Collection :: struct {
|
|
properties : Properties;
|
|
|
|
max_constant_buffers :: 16;
|
|
cbuffers : Static_Array(Constant_Buffer, max_constant_buffers);
|
|
|
|
variants : [..]Shader_Variant;
|
|
}
|
|
|
|
Shader_Variant :: struct {
|
|
text : string;
|
|
|
|
vertex_entry_point : struct {
|
|
name : string;
|
|
|
|
input : [..]Field;
|
|
}
|
|
|
|
pixel_entry_point : struct {
|
|
name : string;
|
|
|
|
return_value : Field;
|
|
}
|
|
}
|
|
|
|
Constant_Buffer :: struct {
|
|
register : int;
|
|
|
|
name : string;
|
|
|
|
fields : Static_Array(Property_Field, 16);
|
|
|
|
buffer_index : u32;
|
|
}
|
|
|
|
Properties :: struct {
|
|
fields : [..]Property_Field;
|
|
|
|
buffer_index : u32;
|
|
}
|
|
```
|
|
|
|
A field is just a simple struct with a name and type (and hints such as semantics or custom hints in the future)
|
|
```
|
|
Field_Hint :: struct {
|
|
kind : Hint_Kind;
|
|
|
|
target_index : int;
|
|
custom_hint_name : string;
|
|
}
|
|
|
|
Field :: struct {
|
|
name : string;
|
|
|
|
type : Field_Type;
|
|
hints : [..]Field_Hint;
|
|
}
|
|
|
|
Field_Kind :: enum {
|
|
Int :: 0;
|
|
Half :: 1;
|
|
Float :: 2;
|
|
Double :: 3;
|
|
Texture2D :: 8;
|
|
Sampler :: 9;
|
|
|
|
Function;
|
|
Struct;
|
|
Array; // Not yet supported
|
|
}
|
|
|
|
Field_Type :: struct {
|
|
kind : Field_Kind;
|
|
|
|
name : string; //@Note(niels): for structs
|
|
|
|
children : [..]Field;
|
|
}
|
|
|
|
Hint_Kind :: enum {
|
|
None;
|
|
|
|
Position;
|
|
Target;
|
|
|
|
Custom;
|
|
}
|
|
```
|
|
|
|
## Notable missing features
|
|
|
|
- Control flow: if/else, for, while, switch etc.
|
|
- Arrays
|
|
- Textures and samplers
|
|
- Multiple render targets
|
|
- Custom buffers/structured buffers
|
|
- Interpolation specifiers
|
|
- Proper variant handling with environment defines
|
|
- Include/importing files such as shared utils etc. |