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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
use std::error::Error;
use std::thread;
use std::time::{Duration, Instant};
use ash::vk;
use rosc::OscType;
use wgsl_view::gpu;
use wgsl_view::osc::OscServer;
use wgsl_view::renderer::Renderer;
fn main() {
env_logger::init();
let mut args = std::env::args().skip(1);
let mut width = 1920u32;
let mut height = 1080u32;
let mut port = 9000u16;
let mut name = "wgsl-view".to_string();
let mut fps = 60u32;
let mut continuous = false;
while let Some(arg) = args.next() {
match arg.as_str() {
"--width" => width = args.next().expect("--width VALUE").parse().expect("u32"),
"--height" => height = args.next().expect("--height VALUE").parse().expect("u32"),
"--port" => port = args.next().expect("--port VALUE").parse().expect("u16"),
"--name" => name = args.next().expect("--name VALUE"),
"--fps" => fps = args.next().expect("--fps VALUE").parse().expect("u32"),
"--continuous" => continuous = true,
other => panic!("unknown argument: {other}"),
}
}
let instance = gpu::create_instance();
let adapter = gpu::create_adapter(&instance, None);
let (device, queue) = gpu::create_device_with_features(
&adapter,
wgpu::Features::TEXTURE_COMPRESSION_BC | wgpu::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
);
let mut client = gpu::create_tsv_client(&device);
client
.init_image(
&name,
width,
height,
1,
gpu::TSV_FORMAT,
gpu::ImgType::D2,
true,
)
.expect("init tsv image");
let fence = gpu::create_fence(&client);
let mut renderer = Renderer::new(&device, &queue, width, height);
let mut osc = OscServer::new(&format!("0.0.0.0:{port}")).expect("bind OSC socket");
let frame_time = Duration::from_secs_f64(1.0 / fps as f64);
log::info!(
"rendering {width}x{height}{}, tsv image: {name}",
if continuous {
format!(" at {fps}fps")
} else {
" on OSC input".to_string()
}
);
loop {
let frame_start = Instant::now();
let handle_msg = |msg: rosc::OscMessage| -> Result<(), Box<dyn Error>> {
let path: Vec<_> = msg.addr.strip_prefix("/").unwrap().split('/').collect();
match (&path[..], &msg.args[..]) {
// create shaders
(["module", module_name], [OscType::String(code)]) => {
renderer.load_module(module_name, code)?
}
(["entrypoint"], [OscType::String(module_name)]) => {
renderer.compile(&device, module_name)?
}
// bind values
(["uniform", module, item, rest @ ..], args) => {
renderer.set_uniform(module, item, rest, args)?
}
(["binding", module, item], [OscType::String(target)]) => {
renderer.set_binding(&device, module, item, target)?
}
// create/destroy texture resources
(["texture", id], [OscType::String(tsv_name)]) => {
renderer.uniforms().create_texture(&device, id, tsv_name);
}
(["texture", id, "destroy"], []) => {
renderer.uniforms().destroy_texture(&device, id);
}
// create/destroy sampler resources
(["sampler", id], [OscType::String(filter), OscType::String(clamp)]) => {
let (filter_mode, address_mode) = parse_sampler_modes(filter, clamp)?;
renderer
.uniforms()
.create_sampler(&device, id, filter_mode, address_mode);
}
(["sampler", id, "destroy"], []) => {
renderer.uniforms().destroy_sampler(&device, id);
}
_ => Err(format!("unhandled OSC message {} {:?}", msg.addr, msg.args))?,
}
Ok(())
};
let dirty = if continuous {
osc.poll(handle_msg);
true
} else {
osc.recv(handle_msg)
};
if dirty {
gpu::refresh_textures(renderer.uniforms(), &device, &mut client, fence);
renderer.render(&device, &queue);
device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
let canvas_image = unsafe { gpu::raw_image(renderer.canvas_texture()) };
if let Err(e) = client.send_image(
&name,
canvas_image,
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
fence,
) {
log::warn!("send_image error: {e}");
}
}
if continuous {
let elapsed = frame_start.elapsed();
if elapsed < frame_time {
thread::sleep(frame_time - elapsed);
}
}
}
}
/// Set a uniform value from OSC args, navigating the path.
fn parse_sampler_modes(
filter: &str,
address: &str,
) -> Result<(wgpu::FilterMode, wgpu::AddressMode), String> {
let filter_mode = match filter {
"linear" => wgpu::FilterMode::Linear,
"nearest" => wgpu::FilterMode::Nearest,
_ => return Err(format!("unknown filter mode '{filter}'")),
};
let address_mode = match address {
"clamp" => wgpu::AddressMode::ClampToEdge,
"repeat" => wgpu::AddressMode::Repeat,
"mirror" => wgpu::AddressMode::MirrorRepeat,
_ => return Err(format!("unknown address mode mode '{address}'")),
};
Ok((filter_mode, address_mode))
}
|