Wasm + Rust + WebGl = 🤓🥰

Cecília Carneiro e Silva

Tema

  • WebGL

WebAssembly

(Lin Clark)

História

- JS não é um bom target de compilação.

- Código resultantes eram grandes.

(Lin Clark)

Evolução das "JS Engines"

JS Optimizado ~ 2 x nativo. (Incrível)

(Franziska Hinkelmann)

Why?

- JIT + tierting compilers.

- Baixa predictibilidade JS.

- Alto start up time.

 

Wat - S-expressions

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    local.get $lhs
    local.get $rhs
    i32.add)
  (export "add" (func $add))
)

MVP

  • Apenas o mínimo. Funcionando nos 4 principais browser. 
  • Algumas funcionalidade ficaram de fora:
    • SIMD, multithreading, CG... (for now)
  • Implementação é dependente do browser.

Rust

(Niko Matsakis)

Why specifically is it good?

- Lots of concurrency bugs are impossible
- No more use-after-free
- Awesome enums (can contain data!)
- Explicit error-handling (the Result type)
- No more NULL pointer (the Option type)
- Pattern matching (switch on steroids)
- The language tooling is really good

 

wasm32-unknown-unknown

  • Um dos pilares do meu mestrado.
    • Linguagem ajuda a entender coisas que você fez errado sua vida inteira.
  • Forte investimento da Mozilla.
  • Ferramentas e um ecossistema bem ativo.

DDD - Data Driven Design

(Azriel)

WebGL

Engine de Rasterização.

Vertex and Fragment Shaders

(WebGL Fundamentals)

Cellular Automata

- Modelo de computação baseado em células de um mundo estruturado.

- Células possuem estados.

- Estados podem mudar com o passar do tempo.

- Influência local.

(game of life)

Demo

Aplicação

- Incêndio na floresta.

 

Wasm + JS

Wasm + JS + Rust

JS -> Wasm

import * as wasm from "wasm-webgl";

const playPauseButton = document.getElementById("play-pause");

const play = () => {
    playPauseButton.textContent = "▶";
};

playPauseButton.addEventListener("click", event => {
    playPauseButton.disabled = true;
    wasm.start();
});

play();

Start()

#[wasm_bindgen]
pub fn start() -> Result<(), JsValue> {
    ...;
    let canvas: web_sys::HtmlCanvasElement = canvas
                            .dyn_into::<web_sys::HtmlCanvasElement>()?;

    ...;
    let universe = Rc::new(RefCell::new(Universe::new(WIDTH, HEIGHT, GAME_CONFIG)));
    {
        let f = Rc::new(RefCell::new(None));
        let g = f.clone();
        ...;
        *g.borrow_mut() = Some(Closure::wrap(Box::new(move || {
            ...;

            draw_universe(&context.clone(), &universe.borrow()).unwrap();
            request_animation_frame(f.borrow().as_ref().unwrap());
        }) as Box<FnMut()>));

        request_animation_frame(g.borrow().as_ref().unwrap());
    }
}

WebGL - Shaders

let vert_shader = compile_shader(
    &context,
    WebGlRenderingContext::VERTEX_SHADER,
    r#"
    attribute vec2 position;
    attribute float point_size;
    attribute vec3 color;
    varying vec3 u_color;

    void main() {
        gl_Position =  vec4(position, 0, 1);
        gl_PointSize = point_size;
        u_color = color;
    }
"#,
)?;
let frag_shader = compile_shader(
    &context,
    WebGlRenderingContext::FRAGMENT_SHADER,
    r#"
    precision mediump float;
    varying vec3 u_color;

    void main() {
        gl_FragColor = vec4(u_color, 1.0);
    }
"#,
)?;

draw_universe

for col in 0..WIDTH {
    for row in 0..HEIGHT {
        let x = (0.5 + col as f32) * size_square - 1.0;
        let y = (0.5 + row as f32) * size_square - 1.0;

        table_points.push(x);
        table_points.push(y);
    }
}

let vertices = table_points.as_slice();
context_array_bind(&context, &vertices, 0, 2)?;
let colors = colors.as_slice();
context_array_bind(context, &colors, 2, 3)?;

context.clear_color(0.0, 0.0, 0.0, 1.0);
context.clear(WebGlRenderingContext::COLOR_BUFFER_BIT);

context.draw_arrays(
    WebGlRenderingContext::POINTS,
    0,
    universe_size.try_into().unwrap(),
);

Wasm(Rust) -> JS

#[wasm_bindgen(module = "/defined-in-js.js")]
extern "C" {
    pub fn pause();
}
const playPauseButton = document.getElementById("play-pause");

export function pause() {
    playPauseButton.disabled = false;
}

Rust Closure + JS Callback

let fps = Rc::new(RefCell::new(new_fps_value().unwrap()));
{
    let fps = fps.clone();
    let a = Closure::wrap(Box::new(move || {
        *fps.borrow_mut() = new_fps_value().unwrap();
        // js::log(&fps.to_string());
    }) as Box<dyn FnMut()>);
    document
        .get_element_by_id("fps-control")
        .expect("Should have a #fps-control slider on the page")
        .dyn_ref::<HtmlElement>()
        .expect("#fps-control be an `HtmlElement`")
        .set_onchange(Some(a.as_ref().unchecked_ref()));
    a.forget();
}

The End

Made with Slides.com