summaryrefslogtreecommitdiff
path: root/client/src/render/mod.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-23 22:45:35 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-23 22:45:35 +0100
commit3344eb2d678f9c5973c8e38083760254b54c20fc (patch)
tree793678d4230dd012285169ba34005064690c7af0 /client/src/render/mod.rs
parentdd40803458695abcd4100fffb874cc25a71ea758 (diff)
downloadweareserver-3344eb2d678f9c5973c8e38083760254b54c20fc.tar
weareserver-3344eb2d678f9c5973c8e38083760254b54c20fc.tar.bz2
weareserver-3344eb2d678f9c5973c8e38083760254b54c20fc.tar.zst
split scene_prepare to many files
Diffstat (limited to 'client/src/render/mod.rs')
-rw-r--r--client/src/render/mod.rs242
1 files changed, 242 insertions, 0 deletions
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 <https://www.gnu.org/licenses/>.
+*/
+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<Queue>,
+ device: Arc<Device>,
+ surface_configuration: SurfaceConfiguration,
+ scene_pipeline: ScenePipeline,
+ pub ui_renderer: UiRenderer,
+ pub scene_prepare: Arc<ScenePreparer>,
+ surface_needs_reconfigure: bool,
+ depth: TextureView,
+ pub timing: TimingProfiler,
+ pub timing_submit: Arc<Mutex<TimingProfiler>>,
+ pub adapter_info: Arc<AdapterInfo>,
+}
+impl<'a> Renderer<'a> {
+ pub fn new(window: &'a Window, downloader: Arc<Downloader>) -> Result<Self> {
+ 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(())
+ }
+}