/* 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 . */ use crate::{ camera::Camera, scene_prepare::ScenePreparer, scene_render::ScenePipeline, ui::UiRenderer, }; use anyhow::{Result, anyhow}; use log::{info, warn}; use pollster::FutureExt; use std::sync::Arc; use weareshared::tree::SceneTree; use wgpu::{ 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: ScenePreparer, surface_needs_reconfigure: bool, depth: TextureView, } impl<'a> Renderer<'a> { pub fn new(window: &'a Window) -> 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: 64, 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_pipeline, texture_bgl) = ScenePipeline::new(&device, surface_configuration.format); let scene_prepare = ScenePreparer::new(device.clone(), queue.clone(), texture_bgl); let ui_renderer = UiRenderer::new(&device, 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()); Ok(Self { scene_pipeline, scene_prepare, surface, depth, device, queue, surface_configuration, ui_renderer, surface_needs_reconfigure: false, }) } 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) -> Result<()> { 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.scene_pipeline.draw( &mut commands, &target_view, &self.depth, scene, &mut self.scene_prepare.prefabs, projection, ); self.ui_renderer.draw( &self.device, &self.queue, &mut commands, &target_view, &self.depth, projection, ); let i = self.queue.submit(Some(commands.finish())); self.device.poll(MaintainBase::WaitForSubmissionIndex(i)); target.present(); Ok(()) } }