cfg_if::cfg_if! {
if #[cfg(not(target_arch = "wasm32"))] {
use crate::components::{TargetResolution, TargetResolutionUpdate};
use crate::config::Config;
use crate::forward_renderer::renderer::Renderer;
use crate::logger::gloss_setup_logger_from_config;
use crate::plugin_manager::plugins::{Plugin, Plugins};
use crate::scene::Scene;
use crate::set_panic_hook;
use crate::viewer::supported_backends;
use crate::{camera::Camera, scene::GLOSS_CAM_NAME};
use easy_wgpu::gpu::Gpu;
use easy_wgpu::texture::Texture;
use log::debug;
use pollster::FutureExt;
}
}
use core::time::Duration;
#[allow(unused_imports)]
use log::{error, info, Level};
#[cfg(target_arch = "wasm32")]
#[allow(unused_imports)]
use wasm_bindgen::prelude::*;
use wasm_timer::Instant;
#[cfg(not(target_arch = "wasm32"))]
use crate::viewer::enumerate_adapters;
#[cfg(not(target_arch = "wasm32"))]
use crate::viewer::get_adapter;
#[derive(Debug)]
#[repr(C)]
pub struct RunnerHeadless {
pub is_running: bool,
pub do_render: bool,
pub first_time: bool,
pub did_warmup: bool,
frame_is_started: bool,
time_init: Instant, time_last_frame: Instant, dt: Duration,
}
impl Default for RunnerHeadless {
fn default() -> Self {
let time_init = Instant::now();
let time_last_frame = Instant::now();
Self {
is_running: false,
do_render: true,
first_time: true,
did_warmup: false,
frame_is_started: false,
time_init,
time_last_frame,
dt: Duration::ZERO,
}
}
}
#[allow(unused)]
impl RunnerHeadless {
pub fn time_since_init(&self) -> Duration {
if self.first_time {
Duration::ZERO
} else {
self.time_init.elapsed()
}
}
pub fn update_dt(&mut self) {
if self.first_time {
self.dt = Duration::ZERO;
} else {
self.dt = self.time_last_frame.elapsed();
}
}
pub fn dt(&self) -> Duration {
self.dt
}
}
#[cfg(not(target_arch = "wasm32"))] pub struct ViewerHeadless {
pub renderer: Renderer,
pub camera: Camera,
pub scene: Scene,
pub plugins: Plugins,
pub config: Config,
pub runner: RunnerHeadless,
pub gpu: Gpu,
}
#[cfg(not(target_arch = "wasm32"))]
impl ViewerHeadless {
pub fn new(width: u32, height: u32, config_path: Option<&str>) -> Self {
let config = Config::new(config_path);
Self::new_with_config(width, height, &config)
}
#[allow(clippy::too_many_lines)]
#[allow(clippy::missing_panics_doc)]
pub fn new_with_config(width: u32, height: u32, config: &Config) -> Self {
set_panic_hook();
if config.core.auto_create_logger {
gloss_setup_logger_from_config(config);
}
re_memory::accounting_allocator::set_tracking_callstacks(config.core.enable_memory_profiling_callstacks);
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: supported_backends(),
flags: wgpu::InstanceFlags::from_env_or_default(),
backend_options: wgpu::BackendOptions::default(),
});
cfg_if::cfg_if! {
if #[cfg(not(target_arch = "wasm32"))]{
let adapters = enumerate_adapters(&instance);
info!("Number of possible adapters: {:?}", adapters.len());
for (i, adapter) in adapters.iter().enumerate() {
info!("Adapter option {:?}: {:?}", i + 1, adapter.get_info());
}
}
}
let adapter = get_adapter(&instance, None).block_on();
info!("Selected adapter: {:?}", adapter.get_info());
let mut desired_features = wgpu::Features::empty();
cfg_if::cfg_if! {
if #[cfg(not(target_arch = "wasm32"))]{
desired_features = desired_features.union(wgpu::Features::TIMESTAMP_QUERY.union(wgpu::Features::TIMESTAMP_QUERY_INSIDE_PASSES));
desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_POINT);
desired_features = desired_features.union(wgpu::Features::POLYGON_MODE_LINE);
}
}
let mut required_features = adapter.features().intersection(desired_features); required_features = required_features.union(wgpu::Features::DEPTH32FLOAT_STENCIL8);
info!("Enabled Features: {required_features:?}");
let max_limits = adapter.limits();
#[allow(unused_mut)]
let mut limits_to_request = wgpu::Limits::default();
if cfg!(target_arch = "wasm32") {
limits_to_request = wgpu::Limits::downlevel_webgl2_defaults();
}
limits_to_request.max_texture_dimension_1d = max_limits.max_texture_dimension_1d;
limits_to_request.max_texture_dimension_2d = max_limits.max_texture_dimension_2d;
limits_to_request.max_buffer_size = max_limits.max_buffer_size;
let mut memory_hints = wgpu::MemoryHints::Performance;
if cfg!(target_arch = "wasm32") {
memory_hints = wgpu::MemoryHints::MemoryUsage;
}
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor {
label: None,
required_features,
required_limits: limits_to_request,
memory_hints,
trace: wgpu::Trace::Off,
})
.block_on()
.expect("A device and queue could not be created. Maybe there's a driver issue on your machine?");
let runner = RunnerHeadless::default();
let gpu = Gpu::new(adapter, instance, device, queue);
let mut scene = Scene::new();
let camera = Camera::new(GLOSS_CAM_NAME, &mut scene, false);
let _ = scene.world_mut().insert_one(
camera.entity,
TargetResolution {
width,
height,
update_mode: TargetResolutionUpdate::Fixed,
},
);
let renderer = Renderer::new(&gpu, &config.render, None);
Self {
gpu,
renderer,
camera,
scene,
plugins: Plugins::new(),
config: config.clone(),
runner,
}
}
pub fn insert_plugin<T: Plugin + 'static>(&mut self, plugin: &T) {
self.plugins.insert_plugin(plugin, &mut self.scene);
}
#[allow(clippy::missing_panics_doc)]
pub fn run_manual_plugins(&mut self) {
self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, false);
}
pub fn update(&mut self) {
self.render(None);
}
pub fn render_from_cam(&mut self, cam: &mut Camera) {
self.render(Some(cam));
}
pub fn start_frame(&mut self) -> Duration {
if !self.runner.did_warmup {
self.runner.did_warmup = true; self.warmup();
self.warmup();
}
self.runner.update_dt();
debug!("after update dt it is {:?}", self.runner.dt());
self.runner.time_last_frame = Instant::now();
self.runner.frame_is_started = true;
self.runner.dt
}
fn render(&mut self, other_cam: Option<&mut Camera>) {
if self.runner.first_time {
self.runner.time_init = Instant::now();
}
self.plugins.run_logic_systems_headless(&mut self.scene, &mut self.runner, true);
let dt = self.runner.dt();
self.renderer
.render_to_texture(&self.gpu, other_cam.unwrap_or(&mut self.camera), &mut self.scene, &mut self.config, dt);
self.runner.first_time = false;
self.runner.frame_is_started = false;
}
pub fn warmup(&mut self) {
debug!("Starting warmup");
self.start_frame();
self.run_manual_plugins(); #[cfg(not(target_arch = "wasm32"))] self.update();
#[cfg(target_arch = "wasm32")] self.render();
self.reset_for_first_time();
debug!("finished warmup");
}
pub fn reset_for_first_time(&mut self) {
self.runner.first_time = true;
}
pub fn get_final_tex(&self) -> &Texture {
let tex = self.renderer.rendered_tex();
tex
}
pub fn get_final_depth(&self) -> &Texture {
let depth = self.renderer.depth_buffer();
depth
}
pub fn set_size(&mut self, width: u32, height: u32) {
self.camera.set_target_res(width, height, &mut self.scene);
}
pub fn start_batch_net_sending(&mut self) {
if let Ok(mut sender) = self.scene.get_resource::<&mut crate::network::SceneSender>() {
sender.start_frame();
}
}
pub fn end_batch_net_sending(&mut self) {
if let Ok(mut sender) = self.scene.get_resource::<&mut crate::network::SceneSender>() {
sender.end_frame();
}
}
}