use crate::skin_manager::{SkinManager, init_sprite_textures}; use anyhow::{Result, anyhow}; use log::warn; use pollster::FutureExt; use std::{sync::Arc, time::Instant}; use twgpu::{ Camera, GpuCamera, TwRenderPass, blit::Blit, buffer::GpuBuffer, map::{GpuMapData, GpuMapRender, GpuMapStatic}, sprites::{ParticleData, ParticleGroup, SpriteTextures, SpritesData, SpritesStatic}, textures::Samplers, }; use twmap::TwMap; use vek::Vec2; use wgpu::{ Backends, Color, CommandEncoderDescriptor, Device, DeviceDescriptor, Features, Instance, InstanceDescriptor, Limits, LoadOp, MaintainBase, MemoryHints, Operations, PowerPreference, Queue, RenderPassColorAttachment, RenderPassDescriptor, RequestAdapterOptions, StoreOp, Surface, SurfaceConfiguration, TextureFormat, TextureViewDescriptor, }; use winit::{dpi::PhysicalSize, window::Window}; pub struct Renderer<'a> { device: Arc, queue: Arc, window: &'a Window, surface: Surface<'a>, surface_configuration: SurfaceConfiguration, twmap: TwMap, map_render: GpuMapRender, map_data: GpuMapData, camera: Camera, sprites_data: SpritesData, particle_data: ParticleData, skin_manager: SkinManager, sprites_static: SpritesStatic, sprite_textures: SpriteTextures, gpu_camera: Arc>, start: Instant, need_reconfigure: bool, } impl<'a> Renderer<'a> { pub fn new(window: &'a Window) -> Result { let texture_format = TextureFormat::Bgra8UnormSrgb; let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), ..Default::default() }); let surface = instance.create_surface(window)?; let adapter = instance .request_adapter(&RequestAdapterOptions { power_preference: PowerPreference::HighPerformance, compatible_surface: Some(&surface), force_fallback_adapter: false, }) .block_on() .ok_or(anyhow!("no adapter found"))?; eprintln!("Adapter {:#?}", adapter.limits()); let (device, queue) = adapter .request_device( &DeviceDescriptor { label: None, required_features: Features::default(), required_limits: Limits::default(), memory_hints: MemoryHints::Performance, }, None, ) .block_on()?; let device = Arc::new(device); let queue = Arc::new(queue); let mut twmap = TwMap::parse(include_bytes!( "/home/muffin/etc/ddnet-maps/types/novice/maps/Mint.map" ))?; twmap.load()?; let samplers = Arc::new(Samplers::new(&device)); let camera = Camera::new(1.); let gpu_camera = Arc::new(GpuCamera::upload(&camera, &device)); let map_static = GpuMapStatic::new(texture_format, &device); let map_data = GpuMapData::upload(&twmap, &device, &queue); let map_render = map_static.prepare_render(&twmap, &map_data, &gpu_camera, &samplers, &device); let sprites_static = SpritesStatic::new(texture_format, &device); let sprites_data = SpritesData::new(&device); let particle_data = ParticleData::new(0.0, &device); let mut sprite_textures = SpriteTextures::new(&device, &queue, &gpu_camera, &samplers); init_sprite_textures(&mut sprite_textures, twmap.version, &device, &queue)?; let blit = Arc::new(Blit::new(&device)); let skin_manager = SkinManager::new(blit, &mut sprite_textures, device.clone(), queue.clone()); let surface_configuration = surface .get_default_config(&adapter, 256, 256) .ok_or(anyhow!("no surface config"))?; surface.configure(&device, &surface_configuration); Ok(Self { start: Instant::now(), map_data, sprites_static, window, device, queue, twmap, surface, surface_configuration, map_render, camera, gpu_camera, particle_data, skin_manager, sprites_data, sprite_textures, need_reconfigure: false, }) } pub fn resize(&mut self, size: PhysicalSize) { self.surface_configuration.width = size.width; self.surface_configuration.height = size.height; self.camera .switch_aspect_ratio(size.width as f32 / size.height as f32); self.reconfigure(); } pub fn reconfigure(&mut self) { self.surface .configure(&self.device, &self.surface_configuration); self.need_reconfigure = false; } pub fn redraw(&mut self) -> Result<()> { if self.need_reconfigure { self.reconfigure(); } let target = self.surface.get_current_texture()?; if target.suboptimal { warn!("suboptimal surface, need reconfigure"); self.need_reconfigure = true; } let time = self.start.elapsed().as_micros() as i64; let size = Vec2::new( self.surface_configuration.width, self.surface_configuration.height, ); self.map_data .update(&self.twmap, &self.camera, size, time, time, &self.queue); self.gpu_camera.update(&self.camera, &self.queue); let target_view = target .texture .create_view(&TextureViewDescriptor::default()); let mut commands = self .device .create_command_encoder(&CommandEncoderDescriptor { label: None }); { let rpass = commands.begin_render_pass(&RenderPassDescriptor { label: None, color_attachments: &[Some(RenderPassColorAttachment { view: &target_view, resolve_target: None, ops: Operations { load: LoadOp::Clear(Color::BLACK), store: StoreOp::Store, }, })], ..Default::default() }); let mut rpass = TwRenderPass::new(rpass, size, &self.camera); self.map_render.render_background(&mut rpass); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::Trails, &self.sprite_textures, &mut rpass.render_pass, ); self.sprites_static.render( &self.sprites_data, &self.sprite_textures, &mut rpass.render_pass, ); self.map_render.render_foreground(&mut rpass); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::Explosions, &self.sprite_textures, &mut rpass.render_pass, ); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::Extra, &self.sprite_textures, &mut rpass.render_pass, ); self.sprites_static.render_particles( &self.particle_data, ParticleGroup::General, &self.sprite_textures, &mut rpass.render_pass, ); } let submission = self.queue.submit(Some(commands.finish())); self.device .poll(MaintainBase::WaitForSubmissionIndex(submission)); target.present(); Ok(()) } }