gloss_renderer/
viewer_headless.rs1cfg_if::cfg_if! {
2 if #[cfg(not(target_arch = "wasm32"))] {
3 use crate::components::{TargetResolution, TargetResolutionUpdate};
4 use crate::config::Config;
5 use crate::forward_renderer::renderer::Renderer;
6 use crate::logger::gloss_setup_logger_from_config;
7 use crate::plugin_manager::plugins::{Plugin, Plugins};
8 use crate::scene::Scene;
9 use crate::set_panic_hook;
10 use crate::viewer::supported_backends;
11 use crate::{camera::Camera, scene::GLOSS_CAM_NAME};
12
13 use easy_wgpu::gpu::Gpu;
14 use easy_wgpu::texture::Texture;
15
16 use log::debug;
17 use pollster::FutureExt;
18 }
19}
20use core::time::Duration;
24#[allow(unused_imports)]
25use log::{error, info, Level};
26
27#[cfg(target_arch = "wasm32")]
28#[allow(unused_imports)]
29use wasm_bindgen::prelude::*;
30use wasm_timer::Instant;
31
32#[cfg(not(target_arch = "wasm32"))]
33use crate::viewer::enumerate_adapters;
34#[cfg(not(target_arch = "wasm32"))]
35use crate::viewer::get_adapter;
36
37#[derive(Debug)]
38#[repr(C)]
39pub struct RunnerHeadless {
40 pub is_running: bool,
41 pub do_render: bool,
42 pub first_time: bool,
43 pub did_warmup: bool,
44 frame_is_started: bool, time_init: Instant, time_last_frame: Instant, dt: Duration, }
54impl Default for RunnerHeadless {
55 fn default() -> Self {
56 let time_init = Instant::now();
57 let time_last_frame = Instant::now();
58 Self {
59 is_running: false,
60 do_render: true,
61 first_time: true,
62 did_warmup: false,
63 frame_is_started: false,
64 time_init,
65 time_last_frame,
66 dt: Duration::ZERO,
67 }
68 }
69}
70#[allow(unused)]
71impl RunnerHeadless {
72 pub fn time_since_init(&self) -> Duration {
73 if self.first_time {
74 Duration::ZERO
75 } else {
76 self.time_init.elapsed()
77 }
78 }
79 pub fn update_dt(&mut self) {
80 if self.first_time {
81 self.dt = Duration::ZERO;
82 } else {
83 self.dt = self.time_last_frame.elapsed();
84 }
85 }
86 pub fn dt(&self) -> Duration {
87 self.dt
88 }
89}
90
91#[cfg(not(target_arch = "wasm32"))] pub struct ViewerHeadless {
100 pub renderer: Renderer,
102 pub camera: Camera,
103 pub scene: Scene,
104 pub plugins: Plugins,
105 pub config: Config,
106 pub runner: RunnerHeadless,
107 pub gpu: Gpu,
111}
112
113#[cfg(not(target_arch = "wasm32"))]
114impl ViewerHeadless {
115 pub fn new(width: u32, height: u32, config_path: Option<&str>) -> Self {
116 let config = Config::new(config_path);
117 Self::new_with_config(width, height, &config)
118 }
119
120 #[allow(clippy::too_many_lines)]
121 #[allow(clippy::missing_panics_doc)]
122 pub fn new_with_config(width: u32, height: u32, config: &Config) -> Self {
123 set_panic_hook();
124 if config.core.auto_create_logger {
125 gloss_setup_logger_from_config(config);
126 }
127
128 re_memory::accounting_allocator::set_tracking_callstacks(config.core.enable_memory_profiling_callstacks);
130
131 let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
132 backends: supported_backends(),
133 flags: wgpu::InstanceFlags::default(),
134 backend_options: wgpu::BackendOptions::default(),
135 });
136
137 cfg_if::cfg_if! {
139 if #[cfg(not(target_arch = "wasm32"))]{
140 let adapters = enumerate_adapters(&instance);
141 info!("Number of possible adapters: {:?}", adapters.len());
142 for (i, adapter) in adapters.iter().enumerate() {
143 info!("Adapter option {:?}: {:?}", i + 1, adapter.get_info());
144 }
145 }
146 }
147 let adapter = get_adapter(&instance, None).block_on();
148 info!("Selected adapter: {:?}", adapter.get_info());
149
150 let mut desired_features = wgpu::Features::empty();
156 cfg_if::cfg_if! {
157 if #[cfg(not(target_arch = "wasm32"))]{
158 desired_features = desired_features.union(wgpu::Features::TIMESTAMP_QUERY.union(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES));
160 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_POINT);
161 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_LINE);
162 }
163 }
164 let mut required_features = adapter.features().intersection(desired_features); required_features = required_features.union(wgpu::Features::DEPTH32FLOAT_STENCIL8);
170 info!("Enabled Features: {required_features:?}");
171
172 let max_limits = adapter.limits();
175 #[allow(unused_mut)]
176 let mut limits_to_request = wgpu::Limits::default();
177 if cfg!(target_arch = "wasm32") {
178 limits_to_request = wgpu::Limits::downlevel_webgl2_defaults();
179 }
180 limits_to_request.max_texture_dimension_1d = max_limits.max_texture_dimension_1d;
181 limits_to_request.max_texture_dimension_2d = max_limits.max_texture_dimension_2d;
182 limits_to_request.max_buffer_size = max_limits.max_buffer_size;
183
184 let mut memory_hints = wgpu::MemoryHints::Performance;
185 if cfg!(target_arch = "wasm32") {
186 memory_hints = wgpu::MemoryHints::MemoryUsage;
189 }
190
191 let (device, queue) = adapter
192 .request_device(&wgpu::DeviceDescriptor {
193 label: None,
194 required_features,
195 required_limits: limits_to_request,
196 memory_hints,
197 trace: wgpu::Trace::Off,
198 })
199 .block_on()
200 .expect("A device and queue could not be created. Maybe there's a driver issue on your machine?");
201
202 let runner = RunnerHeadless::default();
203
204 let gpu = Gpu::new(adapter, instance, device, queue);
205 let mut scene = Scene::new();
206 let camera = Camera::new(GLOSS_CAM_NAME, &mut scene, false);
207 let _ = scene.world.insert_one(
208 camera.entity,
209 TargetResolution {
210 width,
211 height,
212 update_mode: TargetResolutionUpdate::Fixed,
213 },
214 );
215 let renderer = Renderer::new(&gpu, &config.render, None); Self {
218 gpu,
219 renderer,
220 camera,
221 scene,
222 plugins: Plugins::new(),
223 config: config.clone(),
224 runner,
225 }
226 }
227
228 pub fn insert_plugin<T: Plugin + 'static>(&mut self, plugin: &T) {
229 self.plugins.insert_plugin(plugin);
230 }
231 #[allow(clippy::missing_panics_doc)]
232 pub fn run_manual_plugins(&mut self) {
233 self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, false);
234 }
235 pub fn update(&mut self) {
238 self.render(None);
239 }
240
241 pub fn render_from_cam(&mut self, cam: &mut Camera) {
242 self.render(Some(cam));
244 }
245
246 pub fn start_frame(&mut self) -> Duration {
249 if !self.runner.did_warmup {
251 self.runner.did_warmup = true; self.warmup();
254 self.warmup();
255 }
256
257 self.runner.update_dt();
258 debug!("after update dt it is {:?}", self.runner.dt());
259 self.runner.time_last_frame = Instant::now();
260
261 self.runner.frame_is_started = true;
262
263 self.runner.dt
264 }
265
266 fn render(&mut self, other_cam: Option<&mut Camera>) {
267 if self.runner.first_time {
268 self.runner.time_init = Instant::now();
269 }
270
271 self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, true);
272
273 let dt = self.runner.dt();
274
275 self.renderer
279 .render_to_texture(&self.gpu, other_cam.unwrap_or(&mut self.camera), &mut self.scene, &mut self.config, dt);
280
281 self.runner.first_time = false;
282 self.runner.frame_is_started = false;
283 }
284
285 pub fn warmup(&mut self) {
286 debug!("Starting warmup");
287 self.start_frame();
288 self.run_manual_plugins(); #[cfg(not(target_arch = "wasm32"))] self.update();
292 #[cfg(target_arch = "wasm32")] self.render();
294 self.reset_for_first_time();
295 debug!("finished warmup");
296 }
297
298 pub fn reset_for_first_time(&mut self) {
299 self.runner.first_time = true;
300 }
301
302 pub fn get_final_tex(&self) -> &Texture {
303 let tex = self.renderer.rendered_tex();
304 tex
305 }
306
307 pub fn get_final_depth(&self) -> &Texture {
308 let depth = self.renderer.depth_buffer();
309 depth
310 }
311
312 pub fn set_size(&mut self, width: u32, height: u32) {
313 self.camera.set_target_res(width, height, &mut self.scene);
314 }
315}