1cfg_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 dx12_shader_compiler: wgpu::Dx12Compiler::default(),
134 flags: wgpu::InstanceFlags::default(),
135 gles_minor_version: wgpu::Gles3MinorVersion::Automatic,
136 });
137
138 cfg_if::cfg_if! {
140 if #[cfg(not(target_arch = "wasm32"))]{
141 let adapters = enumerate_adapters(&instance);
142 info!("Number of possible adapters: {:?}", adapters.len());
143 for (i, adapter) in adapters.iter().enumerate() {
144 info!("Adapter option {:?}: {:?}", i + 1, adapter.get_info());
145 }
146 }
147 }
148 let adapter = get_adapter(&instance, None);
149 info!("Selected adapter: {:?}", adapter.get_info());
150
151 let mut desired_features = wgpu::Features::empty();
157 cfg_if::cfg_if! {
158 if #[cfg(not(target_arch = "wasm32"))]{
159 desired_features = desired_features.union(wgpu::Features::TIMESTAMP_QUERY.union(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES));
161 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_POINT);
162 desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_LINE);
163 }
164 }
165 let mut required_features = adapter.features().intersection(desired_features); required_features = required_features.union(wgpu::Features::DEPTH32FLOAT_STENCIL8);
171 info!("Enabled Features: {required_features:?}");
172
173 let max_limits = adapter.limits();
176 #[allow(unused_mut)]
177 let mut limits_to_request = wgpu::Limits::default();
178 if cfg!(target_arch = "wasm32") {
179 limits_to_request = wgpu::Limits::downlevel_webgl2_defaults();
180 }
181 limits_to_request.max_texture_dimension_1d = max_limits.max_texture_dimension_1d;
182 limits_to_request.max_texture_dimension_2d = max_limits.max_texture_dimension_2d;
183 limits_to_request.max_buffer_size = max_limits.max_buffer_size;
184
185 let mut memory_hints = wgpu::MemoryHints::Performance;
186 if cfg!(target_arch = "wasm32") {
187 memory_hints = wgpu::MemoryHints::MemoryUsage;
190 }
191
192 let (device, queue) = adapter
193 .request_device(
194 &wgpu::DeviceDescriptor {
195 label: None,
196 required_features,
197 required_limits: limits_to_request,
198 memory_hints,
199 },
200 None, )
202 .block_on()
203 .expect("A device and queue could not be created. Maybe there's a driver issue on your machine?");
204
205 let runner = RunnerHeadless::default();
206
207 let gpu = Gpu::new(adapter, instance, device, queue);
208 let mut scene = Scene::new();
209 let camera = Camera::new(GLOSS_CAM_NAME, &mut scene, false);
210 let _ = scene.world.insert_one(
211 camera.entity,
212 TargetResolution {
213 width,
214 height,
215 update_mode: TargetResolutionUpdate::Fixed,
216 },
217 );
218 let renderer = Renderer::new(&gpu, &config.render, None); Self {
221 gpu,
222 renderer,
223 camera,
224 scene,
225 plugins: Plugins::new(),
226 config: config.clone(),
227 runner,
228 }
229 }
230
231 pub fn insert_plugin<T: Plugin + 'static>(&mut self, plugin: &T) {
232 self.plugins.insert_plugin(plugin);
233 }
234 #[allow(clippy::missing_panics_doc)]
235 pub fn run_manual_plugins(&mut self) {
236 self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, false);
237 }
238 pub fn update(&mut self) {
241 self.render(None);
242 }
243
244 pub fn render_from_cam(&mut self, cam: &mut Camera) {
245 self.render(Some(cam));
247 }
248
249 pub fn start_frame(&mut self) -> Duration {
252 if !self.runner.did_warmup {
254 self.runner.did_warmup = true; self.warmup();
257 self.warmup();
258 }
259
260 self.runner.update_dt();
261 debug!("after update dt it is {:?}", self.runner.dt());
262 self.runner.time_last_frame = Instant::now();
263
264 self.runner.frame_is_started = true;
265
266 self.runner.dt
267 }
268
269 fn render(&mut self, other_cam: Option<&mut Camera>) {
270 if self.runner.first_time {
271 self.runner.time_init = Instant::now();
272 }
273
274 self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, true);
275
276 let dt = self.runner.dt();
277
278 self.renderer
282 .render_to_texture(&self.gpu, other_cam.unwrap_or(&mut self.camera), &mut self.scene, &mut self.config, dt);
283
284 self.runner.first_time = false;
285 self.runner.frame_is_started = false;
286 }
287
288 pub fn warmup(&mut self) {
289 debug!("Starting warmup");
290 self.start_frame();
291 self.run_manual_plugins(); #[cfg(not(target_arch = "wasm32"))] self.update();
295 #[cfg(target_arch = "wasm32")] self.render();
297 self.reset_for_first_time();
298 debug!("finished warmup");
299 }
300
301 pub fn reset_for_first_time(&mut self) {
302 self.runner.first_time = true;
303 }
304
305 pub fn get_final_tex(&self) -> &Texture {
306 let tex = self.renderer.rendered_tex();
307 tex
308 }
309
310 pub fn get_final_depth(&self) -> &Texture {
311 let depth = self.renderer.depth_buffer();
312 depth
313 }
314
315 pub fn set_size(&mut self, width: u32, height: u32) {
316 self.camera.set_target_res(width, height, &mut self.scene);
317 }
318}