StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.
| 1 | // === STRUCTS & BINDINGS === |
| 2 | |
| 3 | struct Uniforms { |
| 4 | projection: mat4x4<f32>, |
| 5 | // You could also add `resolution: vec2<f32>` for shaders needing the window size |
| 6 | } |
| 7 | |
| 8 | struct VertexInput { |
| 9 | @location(0) position: vec2<f32>, // 2D position is sufficient |
| 10 | @location(1) color: vec4<f32>, // Primary color |
| 11 | @location(2) uv: vec2<f32>, // UV coordinates or geometric data |
| 12 | @location(3) params: vec4<f32>, // Additional parameters (e.g., corner radius) |
| 13 | @location(4) flags: u32, // Bitmask for the rendering type |
| 14 | } |
| 15 | |
| 16 | struct VertexOutput { |
| 17 | @builtin(position) clip_position: vec4<f32>, |
| 18 | @location(0) @interpolate(flat) flags: u32, // Use 'flat' for flags; they don't need interpolation |
| 19 | @location(1) @interpolate(perspective) color: vec4<f32>, |
| 20 | @location(2) @interpolate(perspective) uv: vec2<f32>, |
| 21 | @location(3) @interpolate(perspective) params: vec4<f32>, |
| 22 | @location(4) @interpolate(perspective) world_position: vec2<f32>, // Original position in pixels for geometric calculations |
| 23 | } |
| 24 | |
| 25 | @group(0) @binding(0) var<uniform> uniforms: Uniforms; |
| 26 | @group(0) @binding(1) var main_sampler: sampler; |
| 27 | @group(0) @binding(2) var main_texture: texture_2d<f32>; // Texture Atlas for text, icons, etc. |
| 28 | |
| 29 | // === BITMASK FLAGS === |
| 30 | // (These should also be defined as constants in your Rust code) |
| 31 | const FLAG_TYPE_SOLID: u32 = 0u; |
| 32 | const FLAG_TYPE_TEXTURED: u32 = 1u; |
| 33 | const FLAG_TYPE_SDF_TEXT: u32 = 2u; |
| 34 | const FLAG_TYPE_ROUNDED_RECT: u32 = 3u; |
| 35 | const FLAG_TYPE_MASK: u32 = 3u; // Mask to extract the type (first 2 bits) |
| 36 | |
| 37 | |
| 38 | // === HELPER FUNCTIONS === |
| 39 | |
| 40 | // Renders a rounded rectangle using a Signed Distance Function |
| 41 | fn sdf_rounded_box(point: vec2<f32>, size: vec2<f32>, radius: f32) -> f32 { |
| 42 | let q = abs(point) - size + vec2<f32>(radius); |
| 43 | return min(max(q.x, q.y), 0.0) + length(max(q, vec2<f32>(0.0))) - radius; |
| 44 | } |
| 45 | |
| 46 | |
| 47 | // === VERTEX SHADER === |
| 48 | |
| 49 | @vertex |
| 50 | fn vs_main(in: VertexInput) -> VertexOutput { |
| 51 | var out: VertexOutput; |
| 52 | out.clip_position = uniforms.projection * vec4<f32>(in.position, 0.0, 1.0); |
| 53 | out.world_position = in.position; |
| 54 | out.flags = in.flags; |
| 55 | out.color = in.color; |
| 56 | out.uv = in.uv; |
| 57 | out.params = in.params; |
| 58 | return out; |
| 59 | } |
| 60 | |
| 61 | |
| 62 | // === FRAGMENT SHADER === |
| 63 | |
| 64 | @fragment |
| 65 | fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { |
| 66 | let render_type = in.flags & FLAG_TYPE_MASK; |
| 67 | var final_color: vec4<f32>; |
| 68 | |
| 69 | switch (render_type) { |
| 70 | // --- Path 1: Solid color shapes --- |
| 71 | case FLAG_TYPE_SOLID: { |
| 72 | final_color = in.color; |
| 73 | } |
| 74 | |
| 75 | // --- Path 2: Simple textured shapes (images, icons) --- |
| 76 | case FLAG_TYPE_TEXTURED: { |
| 77 | let texture_color = textureSample(main_texture, main_sampler, in.uv); |
| 78 | final_color = in.color * texture_color; |
| 79 | } |
| 80 | |
| 81 | // --- Path 3: Text rendered with Signed Distance Fields (SDF) --- |
| 82 | case FLAG_TYPE_SDF_TEXT: { |
| 83 | // Sample the distance value from the glyph atlas (stored in the 'r' channel) |
| 84 | let distance = textureSample(main_texture, main_sampler, in.uv).r; |
| 85 | // `smoothstep` creates a soft, antialiased edge |
| 86 | // `in.params.x` can hold the font's edge "width" or "smoothing" factor |
| 87 | let edge_width = in.params.x; |
| 88 | let alpha = smoothstep(0.5 - edge_width, 0.5 + edge_width, distance); |
| 89 | final_color = vec4<f32>(in.color.rgb, in.color.a * alpha); |
| 90 | } |
| 91 | |
| 92 | // --- Path 4: Rounded rectangles --- |
| 93 | case FLAG_TYPE_ROUNDED_RECT: { |
| 94 | // Necessary parameters are passed via vertex fields |
| 95 | let rect_size = in.params.xy; |
| 96 | let corner_radius = in.params.z; |
| 97 | |
| 98 | // Calculate the distance from the rounded rectangle's border |
| 99 | let dist = sdf_rounded_box(in.world_position - rect_size * 0.5, rect_size * 0.5, corner_radius); |
| 100 | |
| 101 | // `smoothstep` provides high-quality antialiasing |
| 102 | // A value of 1.0 represents a 1-pixel feather. |
| 103 | let alpha = 1.0 - smoothstep(0.0, 1.0, dist); |
| 104 | final_color = vec4<f32>(in.color.rgb, in.color.a * alpha); |
| 105 | } |
| 106 | |
| 107 | default: { |
| 108 | // Fallback color for debugging (e.g., magenta) |
| 109 | final_color = vec4<f32>(1.0, 0.0, 1.0, 1.0); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | // No premult multiplied alpha conversion - blend mode handles this |
| 114 | return final_color; |
| 115 | } |