1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
#![cfg(target_arch = "wasm32")]
use effects::{ApplyEffectsOptions, EffectPass, UniformValue};
use gpu::wgpu;
use js_sys::Object;
use serde::Deserialize;
use wasm_bindgen::{JsCast, JsValue, prelude::wasm_bindgen};
use crate::gpu::{
import_canvas_texture, read_offscreen_canvas_property, read_serde_property, read_u32_property,
render_texture_to_canvas, with_gpu_runtime,
};
struct ApplyEffectPassesOptions {
source: wgpu::web_sys::OffscreenCanvas,
width: u32,
height: u32,
passes: Vec<EffectPassInput>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct EffectPassInput {
shader: String,
uniforms: Vec<EffectUniformInput>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct EffectUniformInput {
name: String,
value: Vec<f32>,
}
#[wasm_bindgen(js_name = applyEffectPasses)]
pub fn apply_effect_passes(options: JsValue) -> Result<wgpu::web_sys::OffscreenCanvas, JsValue> {
let ApplyEffectPassesOptions {
source,
width,
height,
passes,
} = parse_apply_effect_passes_options(options)?;
with_gpu_runtime(|runtime| {
let source_texture = import_canvas_texture(
&runtime.context,
&source,
width,
height,
"effects-input-texture",
);
let effect_passes = map_effect_passes(passes);
let result_texture = runtime
.effects
.apply(
&runtime.context,
ApplyEffectsOptions {
source: &source_texture,
width,
height,
passes: &effect_passes,
},
)
.map_err(|error| JsValue::from_str(&error.to_string()))?;
render_texture_to_canvas(&runtime.context, &result_texture, width, height)
})
}
fn map_effect_passes(effect_passes: Vec<EffectPassInput>) -> Vec<EffectPass> {
effect_passes
.into_iter()
.map(|pass| EffectPass {
shader: pass.shader,
uniforms: pass
.uniforms
.into_iter()
.map(|uniform| {
let value = if uniform.value.len() == 1 {
UniformValue::Number(uniform.value[0])
} else {
UniformValue::Vector(uniform.value)
};
(uniform.name, value)
})
.collect(),
})
.collect()
}
fn parse_apply_effect_passes_options(value: JsValue) -> Result<ApplyEffectPassesOptions, JsValue> {
let object: Object = value
.dyn_into()
.map_err(|_| JsValue::from_str("applyEffectPasses expects an options object"))?;
Ok(ApplyEffectPassesOptions {
source: read_offscreen_canvas_property(&object, "source")?,
width: read_u32_property(&object, "width")?,
height: read_u32_property(&object, "height")?,
passes: read_serde_property(&object, "passes")?,
})
}
|