1#[cfg(not(target_arch = "wasm32"))]
2use std::io::Write;
3use std::time::Instant;
4#[cfg(target_arch = "wasm32")]
5use wasm_bindgen::prelude::*;
6
7#[cfg(target_arch = "wasm32")]
8fn get_content_div() -> web_sys::Element {
9 web_sys::window()
10 .and_then(|window| window.document())
11 .and_then(|document| document.get_element_by_id("content"))
12 .expect("Could not get document / content.")
13}
14
15#[cfg(target_arch = "wasm32")]
17pub fn add_web_nothing_to_see_msg() {
18 get_content_div().set_inner_html(
19 "<h1>This is a compute example, so there's nothing to see here. Open the console!</h1>",
20 );
21}
22
23#[cfg(not(target_arch = "wasm32"))]
25pub fn output_image_native(image_data: Vec<u8>, texture_dims: (usize, usize), path: String) {
26 let mut png_data = Vec::<u8>::with_capacity(image_data.len());
27 let mut encoder = png::Encoder::new(
28 std::io::Cursor::new(&mut png_data),
29 texture_dims.0 as u32,
30 texture_dims.1 as u32,
31 );
32 encoder.set_color(png::ColorType::Rgba);
33 let mut png_writer = encoder.write_header().unwrap();
34 png_writer.write_image_data(&image_data[..]).unwrap();
35 png_writer.finish().unwrap();
36 log::info!("PNG file encoded in memory.");
37
38 let mut file = std::fs::File::create(&path).unwrap();
39 file.write_all(&png_data[..]).unwrap();
40 log::info!("PNG file written to disc as \"{path}\".");
41}
42
43#[cfg(target_arch = "wasm32")]
51pub fn output_image_wasm(image_data: Vec<u8>, texture_dims: (usize, usize)) {
52 let document = web_sys::window().unwrap().document().unwrap();
53 let content_div = get_content_div();
54
55 let canvas = if let Some(found_canvas) = document.get_element_by_id("staging-canvas") {
56 match found_canvas.dyn_into::<web_sys::HtmlCanvasElement>() {
57 Ok(canvas_as_canvas) => canvas_as_canvas,
58 Err(e) => {
59 log::error!(
60 "In searching for a staging canvas for outputting an image \
61 (element with id \"staging-canvas\"), found non-canvas element: {e:?}.
62 Replacing with standard staging canvas."
63 );
64 e.remove();
65 create_staging_canvas(&document)
66 }
67 }
68 } else {
69 log::info!("Output image staging canvas element not found; creating.");
70 create_staging_canvas(&document)
71 };
72 let image_dimension_strings = (texture_dims.0.to_string(), texture_dims.1.to_string());
75 canvas
76 .set_attribute("width", image_dimension_strings.0.as_str())
77 .unwrap();
78 canvas
79 .set_attribute("height", image_dimension_strings.1.as_str())
80 .unwrap();
81
82 let context = canvas
83 .get_context("2d")
84 .unwrap()
85 .unwrap()
86 .dyn_into::<web_sys::CanvasRenderingContext2d>()
87 .unwrap();
88 let image_data = web_sys::ImageData::new_with_u8_clamped_array(
89 wasm_bindgen::Clamped(&image_data),
90 texture_dims.0 as u32,
91 )
92 .unwrap();
93 context.put_image_data(&image_data, 0.0, 0.0).unwrap();
94
95 let image_element = if let Some(found_image_element) =
97 document.get_element_by_id("output-image-target")
98 {
99 match found_image_element.dyn_into::<web_sys::HtmlImageElement>() {
100 Ok(e) => e,
101 Err(e) => {
102 log::error!(
103 "Found an element with the id \"output-image-target\" but it was not an image: {e:?}.
104 Replacing with default image output element.",
105 );
106 e.remove();
107 create_output_image_element(&document)
108 }
109 }
110 } else {
111 log::info!("Output image element not found; creating.");
112 create_output_image_element(&document)
113 };
114 let data_url = canvas.to_data_url().unwrap();
116 image_element.set_src(&data_url);
117 log::info!("Copied image from staging canvas to image element.");
118
119 if document.get_element_by_id("image-for-you-text").is_none() {
120 log::info!("\"Image for you\" text not found; creating.");
121 let p = document
122 .create_element("p")
123 .expect("Failed to create p element for \"image for you text\".");
124 p.set_text_content(Some(
125 "The above image is for you!
126 You can drag it to your desktop to download.",
127 ));
128 p.set_id("image-for-you-text");
129 content_div
130 .append_child(&p)
131 .expect("Failed to append \"image for you text\" to document.");
132 }
133}
134
135#[cfg(target_arch = "wasm32")]
136fn create_staging_canvas(document: &web_sys::Document) -> web_sys::HtmlCanvasElement {
137 let content_div = get_content_div();
138 let new_canvas = document
139 .create_element("canvas")
140 .expect("Failed to create staging canvas.")
141 .dyn_into::<web_sys::HtmlCanvasElement>()
142 .unwrap();
143 new_canvas.set_attribute("hidden", "true").unwrap();
145 new_canvas.set_attribute("background-color", "red").unwrap();
146 content_div.append_child(&new_canvas).unwrap();
147 log::info!("Created new staging canvas: {:?}", &new_canvas);
148 new_canvas
149}
150
151#[cfg(target_arch = "wasm32")]
152fn create_output_image_element(document: &web_sys::Document) -> web_sys::HtmlImageElement {
153 let content_div = get_content_div();
154 let new_image = document
155 .create_element("img")
156 .expect("Failed to create output image element.")
157 .dyn_into::<web_sys::HtmlImageElement>()
158 .unwrap();
159 new_image.set_id("output-image-target");
160 content_div.replace_children_with_node_1(&new_image);
161 log::info!("Created new output target image: {:?}", &new_image);
162 new_image
163}
164
165#[cfg(not(target_arch = "wasm32"))]
166pub(crate) async fn get_adapter_with_capabilities_or_from_env(
170 instance: &wgpu::Instance,
171 required_features: &wgpu::Features,
172 required_downlevel_capabilities: &wgpu::DownlevelCapabilities,
173 surface: &Option<&wgpu::Surface<'_>>,
174) -> wgpu::Adapter {
175 use wgpu::Backends;
176 if std::env::var("WGPU_ADAPTER_NAME").is_ok() {
177 let adapter = wgpu::util::initialize_adapter_from_env_or_default(instance, *surface)
178 .await
179 .expect("No suitable GPU adapters found on the system!");
180
181 let adapter_info = adapter.get_info();
182 log::info!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
183
184 let adapter_features = adapter.features();
185 assert!(
186 adapter_features.contains(*required_features),
187 "Adapter does not support required features for this example: {:?}",
188 *required_features - adapter_features
189 );
190
191 let downlevel_capabilities = adapter.get_downlevel_capabilities();
192 assert!(
193 downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
194 "Adapter does not support the minimum shader model required to run this example: {:?}",
195 required_downlevel_capabilities.shader_model
196 );
197 assert!(
198 downlevel_capabilities
199 .flags
200 .contains(required_downlevel_capabilities.flags),
201 "Adapter does not support the downlevel capabilities required to run this example: {:?}",
202 required_downlevel_capabilities.flags - downlevel_capabilities.flags
203 );
204 adapter
205 } else {
206 let adapters = instance.enumerate_adapters(Backends::all());
207
208 let mut chosen_adapter = None;
209 for adapter in adapters {
210 if let Some(surface) = surface {
211 if !adapter.is_surface_supported(surface) {
212 continue;
213 }
214 }
215
216 let required_features = *required_features;
217 let adapter_features = adapter.features();
218 if !adapter_features.contains(required_features) {
219 continue;
220 } else {
221 chosen_adapter = Some(adapter);
222 break;
223 }
224 }
225
226 chosen_adapter.expect("No suitable GPU adapters found on the system!")
227 }
228}
229
230#[cfg(target_arch = "wasm32")]
231pub(crate) async fn get_adapter_with_capabilities_or_from_env(
232 instance: &wgpu::Instance,
233 required_features: &wgpu::Features,
234 required_downlevel_capabilities: &wgpu::DownlevelCapabilities,
235 surface: &Option<&wgpu::Surface<'_>>,
236) -> wgpu::Adapter {
237 let adapter = wgpu::util::initialize_adapter_from_env_or_default(instance, *surface)
238 .await
239 .expect("No suitable GPU adapters found on the system!");
240
241 let adapter_info = adapter.get_info();
242 log::info!("Using {} ({:?})", adapter_info.name, adapter_info.backend);
243
244 let adapter_features = adapter.features();
245 assert!(
246 adapter_features.contains(*required_features),
247 "Adapter does not support required features for this example: {:?}",
248 *required_features - adapter_features
249 );
250
251 let downlevel_capabilities = adapter.get_downlevel_capabilities();
252 assert!(
253 downlevel_capabilities.shader_model >= required_downlevel_capabilities.shader_model,
254 "Adapter does not support the minimum shader model required to run this example: {:?}",
255 required_downlevel_capabilities.shader_model
256 );
257 assert!(
258 downlevel_capabilities
259 .flags
260 .contains(required_downlevel_capabilities.flags),
261 "Adapter does not support the downlevel capabilities required to run this example: {:?}",
262 required_downlevel_capabilities.flags - downlevel_capabilities.flags
263 );
264 adapter
265}
266
267#[derive(Default)]
271pub struct AnimationTimer {
272 start_time: Option<Instant>,
273}
274
275impl AnimationTimer {
276 pub fn time(&mut self) -> f32 {
277 match self.start_time {
278 None => {
279 self.start_time = Some(Instant::now());
280 0.0
281 }
282 Some(ref instant) => instant.elapsed().as_secs_f32(),
283 }
284 }
285}