From 3344eb2d678f9c5973c8e38083760254b54c20fc Mon Sep 17 00:00:00 2001 From: metamuffin Date: Thu, 23 Jan 2025 22:45:35 +0100 Subject: split scene_prepare to many files --- client/src/render/mod.rs | 242 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 client/src/render/mod.rs (limited to 'client/src/render/mod.rs') diff --git a/client/src/render/mod.rs b/client/src/render/mod.rs new file mode 100644 index 0000000..eb9e089 --- /dev/null +++ b/client/src/render/mod.rs @@ -0,0 +1,242 @@ +/* + wearechat - generic multiplayer game with voip + Copyright (C) 2025 metamuffin + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, version 3 of the License only. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +pub mod scene; + +use crate::{ + camera::Camera, download::Downloader, interfaces::profiler::TimingProfiler, state::InputState, + ui::UiRenderer, +}; +use anyhow::{Result, anyhow}; +use log::{info, warn}; +use pollster::FutureExt; +use scene::{ScenePreparer, draw::ScenePipeline}; +use std::{ + mem::swap, + sync::{Arc, Mutex}, + thread::{sleep, spawn}, + time::Duration, +}; +use weareshared::tree::SceneTree; +use wgpu::{ + AdapterInfo, Backends, CommandEncoderDescriptor, Device, DeviceDescriptor, Extent3d, Features, + Instance, InstanceDescriptor, Limits, MaintainBase, PowerPreference, Queue, + RequestAdapterOptions, Surface, SurfaceConfiguration, TextureDescriptor, TextureDimension, + TextureFormat, TextureUsages, TextureView, TextureViewDescriptor, +}; +use winit::window::Window; + +pub struct Renderer<'a> { + surface: Surface<'a>, + queue: Arc, + device: Arc, + surface_configuration: SurfaceConfiguration, + scene_pipeline: ScenePipeline, + pub ui_renderer: UiRenderer, + pub scene_prepare: Arc, + surface_needs_reconfigure: bool, + depth: TextureView, + pub timing: TimingProfiler, + pub timing_submit: Arc>, + pub adapter_info: Arc, +} +impl<'a> Renderer<'a> { + pub fn new(window: &'a Window, downloader: Arc) -> Result { + info!("wgpu init"); + let instance = Instance::new(InstanceDescriptor { + backends: Backends::all(), + ..Default::default() + }); + + let surface = instance.create_surface(window)?; + let adapter = instance + .request_adapter(&RequestAdapterOptions { + compatible_surface: Some(&surface), + power_preference: PowerPreference::HighPerformance, + ..Default::default() + }) + .block_on() + .ok_or(anyhow!("no adapter found"))?; + + let (device, queue) = adapter + .request_device( + &DeviceDescriptor { + required_features: Features::PUSH_CONSTANTS, + required_limits: Limits { + max_push_constant_size: 112, + max_vertex_buffers: 16, + ..Limits::default() + }, + ..Default::default() + }, + None, + ) + .block_on()?; + + let surface_configuration = surface + .get_default_config(&adapter, 256, 256) + .ok_or(anyhow!("no default config"))?; + + surface.configure(&device, &surface_configuration); + + let device = Arc::new(device); + let queue = Arc::new(queue); + + let scene_prepare = Arc::new(ScenePreparer::new( + device.clone(), + queue.clone(), + surface_configuration.format, + downloader, + )); + + let ui_renderer = + UiRenderer::new(device.clone(), queue.clone(), surface_configuration.format); + + let depth = device.create_texture(&TextureDescriptor { + label: None, + size: Extent3d { + height: 256, + width: 256, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Depth32Float, + usage: TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }); + let depth = depth.create_view(&TextureViewDescriptor::default()); + + // TODO multithreading introduces double-loading some resources. fix that before increasing thread count + for _ in 0..1 { + let scene_prepare = scene_prepare.clone(); + spawn(move || { + loop { + let ndone = scene_prepare.update().unwrap(); + if ndone == 0 { + sleep(Duration::from_millis(50)); + } + } + }); + } + + let adapter_info = Arc::new(adapter.get_info()); + + Ok(Self { + scene_pipeline: ScenePipeline, + scene_prepare, + surface, + adapter_info, + depth, + device, + queue, + surface_configuration, + ui_renderer, + surface_needs_reconfigure: false, + timing: Default::default(), + timing_submit: Default::default(), + }) + } + + pub fn resize(&mut self, width: u32, height: u32) { + self.surface_configuration.width = width; + self.surface_configuration.height = height; + self.surface + .configure(&self.device, &self.surface_configuration); + + self.depth = self + .device + .create_texture(&TextureDescriptor { + label: None, + size: Extent3d { + height, + width, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Depth32Float, + usage: TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }) + .create_view(&TextureViewDescriptor::default()); + } + + pub fn draw( + &mut self, + scene: &SceneTree, + camera: &Camera, + input_state: &mut InputState, + ) -> Result<()> { + self.timing.begin("prepare"); + if self.surface_needs_reconfigure { + self.surface + .configure(&self.device, &self.surface_configuration); + self.surface_needs_reconfigure = false + } + let target = self.surface.get_current_texture()?; + if target.suboptimal { + warn!("suboptimal swapchain texture"); + self.surface_needs_reconfigure = true; + } + let target_view = target + .texture + .create_view(&TextureViewDescriptor::default()); + + let mut commands = self + .device + .create_command_encoder(&CommandEncoderDescriptor { label: None }); + + let projection = camera.to_matrix(); + + self.timing.checkpoint("draw scene"); + self.scene_pipeline.draw( + &mut commands, + &target_view, + &self.depth, + scene, + &self.scene_prepare.prefabs, + projection, + ); + + self.timing.checkpoint("draw ui"); + self.ui_renderer.draw( + &mut commands, + &target_view, + &self.depth, + projection, + input_state, + &self.surface_configuration, + ); + + self.timing.checkpoint("submit"); + let i = self.queue.submit(Some(commands.finish())); + self.timing.checkpoint("poll"); + self.device.poll(MaintainBase::WaitForSubmissionIndex(i)); + + self.timing.checkpoint("present"); + target.present(); + + self.timing.checkpoint(""); + + let mut ts = self.timing_submit.lock().unwrap(); + swap(&mut *ts, &mut self.timing); + + Ok(()) + } +} -- cgit v1.2.3-70-g09d2