/*
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, Features, Instance,
InstanceDescriptor, Limits, MaintainBase, PowerPreference, Queue, RequestAdapterOptions,
Surface, SurfaceConfiguration, TextureViewDescriptor,
};
use winit::window::Window;
pub struct Renderer<'a> {
surface: Surface<'a>,
queue: Arc,
device: Arc,
surface_configuration: SurfaceConfiguration,
scene_pipeline: ScenePipeline,
ui_renderer: UiRenderer,
pub scene_prepare: ScenePreparer,
surface_needs_reconfigure: bool,
}
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);
Ok(Self {
scene_pipeline,
scene_prepare,
surface,
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.scene_pipeline.resize(&self.device, width, height);
}
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,
scene,
&mut self.scene_prepare.prefabs,
projection,
);
self.ui_renderer.draw(
&self.device,
&self.queue,
&mut commands,
&target_view,
projection,
);
let i = self.queue.submit(Some(commands.finish()));
self.device.poll(MaintainBase::WaitForSubmissionIndex(i));
target.present();
Ok(())
}
}