Learn the language - Syntax
Shadeup is a type-checked, garbage-collected programming language designed for writing shader and CPU code in harmony.
If you’ve never done any shader programming before, typically the cpu and gpu code are written in different languages (think C++ and GLSL). Shadeup eliminates this distinction by allowing you to write both in the same language while magicly transpiling/glueing the code and data structures together behind the scenes.
Given that shader languages and CPU languages are designed for different purposes, Shadeup has to balance the two.
If you’re familiar with HLSL you’ll notice that Shadeup has a lot of the same functions and types exposed to the root scope.
If you’re new to shading languages, you’ll find that a bunch of fancy math functions have taken over the root scope (you can do cos()
instead of Math.cos()
like in js).
This is because we use a lot of math in shaders and the less typing the better.
The following tutorial will quickly get you up to speed with the syntax.
Under the hood
Shadeup is actually just an extension of typescript with some syntatic sugar sprinkled all over the place to make life easier. A lot of the APIs and patterns are just Javascript, so if you’re familiar with that you’ll be right at home.
Syntax
Let’s start by running through the syntax, I might not explain every little detail so I hope the code is relatively self-explanatory:
Comments
Functions
Functions are declared with the fn
keyword. The return type can be specified via ->
or inferred.
Arrow functions
Arrow functions are usable in all parts of CPU code. Unforunately the GPU side is far more limited (more on this later). They are declared with the =>
operator.
Variables
Variables are declared with the let
or const
keywords. let
is used for mutable variables and const
for immutable variables. The type can be specified via : type
or inferred.
Types
Shadeup inherits a number of types from HLSL. The following are available:
int
int2
,int3
,int4
float
float2
,float3
,float4
float2x2
,float3x3
,float4x4
uint
uint2
,uint3
,uint4
bool
string
(non-GPU).split(sep: string) -> string[]
.includes(substr: string) -> bool
.startsWith(str: string) -> bool
.endsWith(str: string) -> bool
.replace(from: string, to: string)
.trim(chars: string = ' \t\n') -> string
.lower() -> string
.upper() -> string
.substr(start: int, end: int) -> string
.len() -> int
[index: int] -> string
T[]
Variable length arrays (can be closed over in GPU code, but not created in GPU code).join(sep: string) -> string
.push(val: T) -> string
.len() -> int
.first() -> T
.last() -> T
.append(vals: T[])
.remove(index: int)
[index: int] -> T
T[3]
e.g.int[3]
Fixed length arrays same methods as above (can be created in gpu code)map<K extends Hashable, V>
(non-GPU).has(key: K) -> bool
.set(key: K, value: V)
.get(key: K) -> V
.delete(key: K)
.keys() -> K[]
.values() -> V[]
[index: K] -> V
shader
Instantiated shaderbuffer<T>
Statically sized buffer of type T where T = numeric primitive (float, float2, etc) or a user-defined structtexture2d<T>
2D texture of type T where T = numeric primitive (float, float2, etc)
Vectors
Vectors are declared with the ()
operator, or explicitly via floatN
, intN
, or uintN
keywords.
They can be indexed via .x
, .y
, .z
, .w
or .r
, .g
, .b
, .a
. They also support swizzlishous access (.xyz, .xy, .xw, etc…)
Math
Most HLSL math is supported (see the reference for more). Here’s a small sample:
Type casting
Numeric type casting is done via type(val)
Other types can be converted to strings via .toString()
when available
Loops
Shadeup supports for
and while
loops. break
and continue
are also supported.
Conditionals
Shadeup supports if
, else if
and else
conditionals. The ?
operator is also supported for simple conditionals.
Structs
Structs are declared with the struct
keyword. They can be instantiated by <structName> { props }
They can also carry methods via impl
much like Rust.
Member visibility
By default all struct members are private. They can be made public via the pub
keyword.
Methods
Instance/static methods are declared with the impl
keyword.
- Instance methods are declared with a first parameter of
self
. - Static methods are declared without a
self
Traits
Traits (interfaces) are declared with the trait
keyword. They can be implemented by structs via impl
.
Note: Traits are not supported on the GPU.
Imports/Exports
- Exports are functions declared with
pub
- Imports are declared with the
import
keyword. They can be aliased viaas
.
In the next part we’ll write a program