MPSL Shader Basic Syntax

Differences Between MPSL and Rust Syntax

While MPSL shader language has similar syntax to Rust, there are some fundamental differences between them.

Type System Differences

MPSL's type system is simpler and focused on graphics computation:

1// Rust type system
2let x: i32 = 42;
3let y: f64 = 3.14;
4let v: Vec<i32> = vec![1, 2, 3];
5
6// MPSL type system
7let x: float = 42.0;
8let v: vec4 = vec4(1.0, 0.0, 0.0, 1.0);

MPSL primarily focuses on graphics-related basic types like float, vec2, vec3, vec4, mat4, etc., and doesn't support complex data structures like Vec, HashMap that exist in Rust. This simplification is designed to match GPU computing characteristics.

Memory Management

Rust's ownership system and default immutability are completely absent in MPSL:

1// Ownership and borrowing in Rust
2let mut s = String::from("hello");
3let r = &mut s;  // Mutable borrow
4r.push_str(" world");
5
6// Simple value passing in MPSL, default mutable
7let color = vec4(1.0, 0.0, 0.0, 1.0);
8let modified = color * 0.5;  // Direct operation, no ownership concept

MPSL doesn't need to consider memory management because shader program lifecycles are controlled by GPU execution processes.

Function Definition and Usage

MPSL's function system is simpler, primarily centered around vertex and pixel shading:

1// Rust function definition
2fn calculate_sum(a: i32, b: i32) -> i32 {
3    a + b
4}
5
6// MPSL shader functions
7fn vertex(self) -> vec4 {
8    // Must return clip space coordinates
9    return self.transform_position();
10}
11
12fn pixel(self) -> vec4 {
13    // Must return color value
14    return self.color;
15}

MPSL's self parameter is special, containing shader context information, which is quite different from Rust's self concept.

Variable Scope

MPSL introduces special variable scope concepts:

1// MPSL-specific variable declarations
2instance border_width: float  // Instance variable
3varying pos: vec2            // Variable passed from vertex to pixel shader
4uniform time: float          // Uniform variable
5
6// Rust variable declarations
7let border_width: f32 = 1.0;  // Regular variable
8static TIME: f32 = 0.0;       // Static variable

These special variable types reflect characteristics of the graphics rendering pipeline, concepts that don't exist in Rust.

Control Flow Structures

MPSL has more limited control flow:

1// Rust's flexible control flow
2match value {
3    0 => println!("zero"),
4    1 => println!("one"),
5    _ => println!("other"),
6}
7
8// MPSL's simplified control flow
9if value < 0.5 {
10    return vec4(1.0, 0.0, 0.0, 1.0);
11} else {
12    return vec4(0.0, 1.0, 0.0, 1.0);
13}

MPSL doesn't support advanced control flow structures like match, loop, etc., that exist in Rust, primarily due to GPU execution model limitations.

Special Feature Support

MPSL includes specialized graphics processing features:

1// MPSL's SDF functionality
2fn pixel(self) -> vec4 {
3    let sdf = Sdf2d::viewport(self.pos * self.rect_size);
4    sdf.circle(100.0, 100.0, 50.0);
5    return sdf.fill(self.color);
6}
7
8// Rust would need external libraries for similar functionality
9use graphics_lib::draw_circle;  // Hypothetical graphics library

These built-in graphics processing features are characteristic of MPSL; in Rust, you typically need to rely on external libraries.

Error Handling

Error handling approaches differ significantly:

1// Error handling in Rust
2fn process() -> Result<i32, Error> {
3    let value = some_operation()?;
4    Ok(value)
5}
6
7// MPSL basically has no error handling
8fn pixel(self) -> vec4 {
9    // Shader execution simply stops on failure
10    let color = self.compute_color();
11    return color;
12}

MPSL has no Result or Option types and doesn't need error handling mechanisms, as shader programs must execute correctly.

MPSL Basic Syntax

Basic Program Structure

MPSL shader programs primarily consist of these parts:

1// Vertex shader
2fn vertex(self) -> vec4 {
3    // Returns clip space coordinates
4    return self.clip_and_transform_vertex(self.rect_pos, self.rect_size);
5}
6
7// Fragment (pixel) shader
8fn pixel(self) -> vec4 {
9    // Returns RGBA color value
10    return self.color;
11}

Data Types

MPSL provides the following basic data types:

1// Scalar types
2float           // Floating point number
3int             // Integer
4bool            // Boolean value
5
6// Vector types
7vec2            // 2D vector (x,y)
8vec3            // 3D vector (x,y,z)
9vec4            // 4D vector (x,y,z,w) commonly used for color (r,g,b,a)
10
11// Matrix types
12mat4            // 4x4 matrix
13
14// Sampler types
15texture2d       // 2D texture
16textureOES      // External texture (video etc.)

Variable Declaration

1// Instance variables - can be changed for each draw instance
2instance border_width: float
3instance color: vec4
4
5// Varying variables - pass data between vertex and pixel shaders
6varying pos: vec2
7
8// Uniform variables - globally uniform values
9uniform time: float
10
11// Texture declaration
12texture tex: texture2d

Built-in Functions

1// Mathematical functions
2sin(x)          // Sine
3cos(x)          // Cosine
4pow(x,y)        // x to the power of y
5sqrt(x)         // Square root
6mix(x,y,a)      // Linear interpolation
7clamp(x,min,max) // Range limitation
8
9// Texture sampling
10sample2d(tex, uv)         // 2D texture sampling
11sample2d_rt(tex, uv)      // Render target texture sampling
12sample2dOES(tex, uv)      // External texture sampling

SDF (Signed Distance Field)

MPSL provides powerful SDF functionality for shape drawing

1// Create SDF viewport
2let sdf = Sdf2d::viewport(self.pos * self.rect_size);
3
4// Basic shapes
5sdf.circle(x, y, radius)              // Circle
6sdf.box(x, y, width, height, radius)  // Rounded rectangle
7sdf.rect(x, y, width, height)         // Rectangle
8
9// Operations
10sdf.fill(color)           // Fill
11sdf.fill_keep(color)      // Fill and preserve shape
12sdf.stroke(color, width)  // Stroke

Control Flow Statements

1// Conditional statements
2if condition {
3    // Code block
4} else {
5    // Code block
6}
7
8// Loops
9for i in 0..count {
10    // Loop body
11}
12
13// Early return
14return value;

Shader Attributes

1draw_bg: {
2    // Available shape types
3    shape: Solid,          // Solid shape
4    shape: Rectangle,      // Rectangle
5    shape: Circle,         // Circle
6    shape: RoundedRect,    // Rounded rectangle
7
8    // Basic fill types
9    fill: Color,           // Solid color fill
10    fill: Texture,         // Texture fill
11    fill: Pattern,         // Pattern fill
12    fill: Gradient,        // Gradient fill
13}
Note:
  • shape and fill properties are more like declarative markers, informing the system and other developers of the shader's intent
  • The actual drawing effect needs to be implemented through SDF API in the pixel function

Practical Example

A complete rounded rectangle shader example:

1// 1. Rounded Rectangle
2shapes = <View> {
3    show_bg: true,  // Enable background drawing
4    draw_bg: {      // Main shader configuration
5        // Instance variables that can be modified per instance
6        instance stroke_width: 1.0    // Stroke width
7        instance border_width: 2.0    // Border width
8        instance border_color: #000   // Border color
9        instance fill_color: #f00f    // Fill color
10
11        // Helper function: create rounded rectangle
12        fn rounded_rectangle(self) -> vec4 {
13            // Create a 2D signed distance field viewport, mapped to our rectangle size
14            let sdf = Sdf2d::viewport(self.pos * self.rect_size);
15
16            // Draw a box (rounded rectangle), parameters are:
17            sdf.box(
18                0.0,                // x position (left edge)
19                0.0,                // y position (top edge)
20                self.rect_size.x,   // rectangle width
21                self.rect_size.y,   // rectangle height
22                10.0               // corner radius - makes corners round
23            );
24
25            // Fill shape with fill_color while preserving SDF for border drawing
26            sdf.fill_keep(self.fill_color);
27
28            // Add border with specified color and width
29            sdf.stroke(self.border_color, self.border_width);
30
31            // Return final composite color result
32            return sdf.result;
33        }
34
35        // Main pixel shader function
36        fn pixel(self) -> vec4 {
37            // Must include return
38            return self.rounded_rectangle();
39        }
40    }
41}