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 required_features = adapter.features().intersection(desired_features); info!("enabled features: {required_features:?}");
167
168 let max_limits = adapter.limits();
171 #[allow(unused_mut)]
172 let mut limits_to_request = wgpu::Limits::default();
173 if cfg!(target_arch = "wasm32") {
174 limits_to_request = wgpu::Limits::downlevel_webgl2_defaults();
175 }
176 limits_to_request.max_texture_dimension_1d = max_limits.max_texture_dimension_1d;
177 limits_to_request.max_texture_dimension_2d = max_limits.max_texture_dimension_2d;
178 limits_to_request.max_buffer_size = max_limits.max_buffer_size;
179
180 let mut memory_hints = wgpu::MemoryHints::Performance;
181 if cfg!(target_arch = "wasm32") {
182 memory_hints = wgpu::MemoryHints::MemoryUsage;
185 }
186
187 let (device, queue) = adapter
188 .request_device(
189 &wgpu::DeviceDescriptor {
190 label: None,
191 required_features,
192 required_limits: limits_to_request,
193 memory_hints,
194 },
195 None, )
197 .block_on()
198 .expect("A device and queue could not be created. Maybe there's a driver issue on your machine?");
199
200 let runner = RunnerHeadless::default();
201
202 let gpu = Gpu::new(adapter, instance, device, queue);
203 let mut scene = Scene::new();
204 let camera = Camera::new(GLOSS_CAM_NAME, &mut scene, false);
205 let _ = scene.world.insert_one(
206 camera.entity,
207 TargetResolution {
208 width,
209 height,
210 update_mode: TargetResolutionUpdate::Fixed,
211 },
212 );
213 let renderer = Renderer::new(&gpu, &config.render, None); Self {
216 gpu,
217 renderer,
218 camera,
219 scene,
220 plugins: Plugins::new(),
221 config: config.clone(),
222 runner,
223 }
224 }
225
226 pub fn insert_plugin<T: Plugin + 'static>(&mut self, plugin: &T) {
227 self.plugins.insert_plugin(plugin);
228 }
229 #[allow(clippy::missing_panics_doc)]
230 pub fn run_manual_plugins(&mut self) {
231 self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, false);
232 }
233 pub fn update(&mut self) {
236 self.render(None);
237 }
238
239 pub fn render_from_cam(&mut self, cam: &mut Camera) {
240 self.render(Some(cam));
242 }
243
244 pub fn start_frame(&mut self) -> Duration {
247 if !self.runner.did_warmup {
249 self.runner.did_warmup = true; self.warmup();
252 self.warmup();
253 }
254
255 self.runner.update_dt();
256 debug!("after update dt it is {:?}", self.runner.dt());
257 self.runner.time_last_frame = Instant::now();
258
259 self.runner.frame_is_started = true;
260
261 self.runner.dt
262 }
263
264 fn render(&mut self, other_cam: Option<&mut Camera>) {
265 if self.runner.first_time {
266 self.runner.time_init = Instant::now();
267 }
268
269 self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, true);
270
271 let dt = self.runner.dt();
272
273 self.renderer
277 .render_to_texture(&self.gpu, other_cam.unwrap_or(&mut self.camera), &mut self.scene, &mut self.config, dt);
278
279 self.runner.first_time = false;
280 self.runner.frame_is_started = false;
281 }
282
283 pub fn warmup(&mut self) {
284 debug!("Starting warmup");
285 self.start_frame();
286 self.run_manual_plugins(); #[cfg(not(target_arch = "wasm32"))] self.update();
290 #[cfg(target_arch = "wasm32")] self.render();
292 self.reset_for_first_time();
293 debug!("finished warmup");
294 }
295
296 pub fn reset_for_first_time(&mut self) {
297 self.runner.first_time = true;
298 }
299
300 pub fn get_final_tex(&self) -> &Texture {
301 let tex = self.renderer.rendered_tex();
302 tex
303 }
304
305 pub fn get_final_depth(&self) -> &Texture {
306 let depth = self.renderer.depth_buffer();
307 depth
308 }
309
310 pub fn set_size(&mut self, width: u32, height: u32) {
311 self.camera.set_target_res(width, height, &mut self.scene);
312 }
313}