old school music tracker
at dev 169 lines 5.3 kB view raw
1#[cfg(feature = "gpu_scaling")] 2pub mod gpu; 3pub mod palettes; 4 5use std::sync::Arc; 6 7use winit::{dpi::PhysicalSize, event_loop::ActiveEventLoop, window::Window}; 8 9use crate::coordinates::WINDOW_SIZE as WINDOW_SIZE16; 10const WINDOW_SIZE: (usize, usize) = (WINDOW_SIZE16.0 as usize, WINDOW_SIZE16.1 as usize); 11use palettes::{Palette, RGB8}; 12 13#[cfg(not(any(feature = "gpu_scaling", feature = "soft_scaling")))] 14compile_error!("at least one of gpu_scaling or soft_scaling needs to be active"); 15 16#[expect( 17 clippy::large_enum_variant, 18 reason = "this doesn't get moved a lot, so it doesn't matter. If it's not in the enum i also don't box it" 19)] 20#[cfg(all(feature = "gpu_scaling", feature = "soft_scaling"))] 21pub enum BothRenderBackend { 22 GPU(GPURenderBackend), 23 Soft(SoftRenderBackend), 24} 25 26#[cfg(all(feature = "gpu_scaling", feature = "soft_scaling"))] 27impl BothRenderBackend { 28 pub fn new(window: Arc<Window>, palette: Palette<RGB8>) -> Self { 29 match GPURenderBackend::try_new(window.clone(), palette) { 30 Ok(b) => Self::GPU(b), 31 Err(e) => { 32 eprintln!( 33 "GPU render backend creation failed: {e}.\n\nFalling back to software scaling" 34 ); 35 Self::Soft(SoftRenderBackend::new(window, palette)) 36 } 37 } 38 } 39 40 pub fn resize(&mut self, size: PhysicalSize<u32>) { 41 match self { 42 BothRenderBackend::GPU(b) => b.resize(size), 43 BothRenderBackend::Soft(b) => b.resize(size), 44 } 45 } 46 47 pub fn render( 48 &mut self, 49 frame_buffer: &[[u8; WINDOW_SIZE.0]; WINDOW_SIZE.1], 50 event_loop: &ActiveEventLoop, 51 ) { 52 match self { 53 BothRenderBackend::GPU(b) => b.render(frame_buffer, event_loop), 54 BothRenderBackend::Soft(b) => b.render(frame_buffer, event_loop), 55 } 56 } 57 58 pub fn get_size(&self) -> PhysicalSize<u32> { 59 match self { 60 BothRenderBackend::GPU(b) => b.get_size(), 61 BothRenderBackend::Soft(b) => b.get_size(), 62 } 63 } 64} 65 66#[cfg(feature = "gpu_scaling")] 67pub struct GPURenderBackend { 68 backend: gpu::GPUState, 69} 70 71#[cfg(feature = "gpu_scaling")] 72impl GPURenderBackend { 73 fn try_new(window: Arc<Window>, palette: Palette<RGB8>) -> Result<Self, String> { 74 let mut backend = smol::block_on(gpu::GPUState::new(window))?; 75 backend.queue_palette_update(palette.into()); 76 77 Ok(Self { backend }) 78 } 79 80 pub fn new(window: Arc<Window>, palette: Palette<RGB8>) -> Self { 81 Self::try_new(window, palette).unwrap() 82 } 83 84 pub fn resize(&mut self, size: PhysicalSize<u32>) { 85 self.backend.resize(size); 86 } 87 88 pub fn render( 89 &mut self, 90 frame_buffer: &[[u8; WINDOW_SIZE.0]; WINDOW_SIZE.1], 91 event_loop: &ActiveEventLoop, 92 ) { 93 match self.backend.render(frame_buffer) { 94 Ok(_) => {} 95 Err(wgpu::SurfaceError::Lost) => self.backend.reinit_surface(), 96 Err(wgpu::SurfaceError::OutOfMemory) => event_loop.exit(), 97 Err(e) => eprint!("{:?}", e), 98 } 99 } 100 101 pub fn get_size(&self) -> PhysicalSize<u32> { 102 self.backend.size() 103 } 104} 105 106// softscaling is small enough to not have its own file / module 107#[cfg(feature = "soft_scaling")] 108pub struct SoftRenderBackend { 109 backend: softbuffer::Surface<Arc<Window>, Arc<Window>>, 110 width: u32, 111 height: u32, 112 palette: Palette<palettes::ZRGB>, 113} 114 115#[cfg(feature = "soft_scaling")] 116impl SoftRenderBackend { 117 pub fn new(window: Arc<Window>, palette: Palette<RGB8>) -> Self { 118 let size = window.inner_size(); 119 let context = softbuffer::Context::new(window.clone()).unwrap(); 120 Self { 121 backend: softbuffer::Surface::new(&context, window).unwrap(), 122 width: size.width, 123 height: size.height, 124 palette: palette.into(), 125 } 126 } 127 128 pub fn resize(&mut self, size: PhysicalSize<u32>) { 129 self.width = size.width; 130 self.height = size.height; 131 self.backend 132 .resize( 133 std::num::NonZeroU32::new(size.width).unwrap(), 134 std::num::NonZeroU32::new(size.height).unwrap(), 135 ) 136 .unwrap() 137 } 138 139 pub fn render( 140 &mut self, 141 frame_buffer: &[[u8; WINDOW_SIZE.0]; WINDOW_SIZE.1], 142 _: &ActiveEventLoop, 143 ) { 144 let mut buffer = self.backend.buffer_mut().unwrap(); 145 assert!(buffer.len() == usize::try_from(self.width * self.height).unwrap()); 146 let x_step = WINDOW_SIZE.0 as f32 / self.width as f32; 147 let y_step = WINDOW_SIZE.1 as f32 / self.height as f32; 148 for (y_idx, row) in buffer 149 .chunks_exact_mut(self.width.try_into().unwrap()) 150 .enumerate() 151 { 152 for (x_idx, pixel) in row.iter_mut().enumerate() { 153 let x_idx = x_idx as f32 * x_step; 154 let y_idx = y_idx as f32 * y_step; 155 *pixel = self 156 .palette 157 .get_raw(frame_buffer[y_idx.floor() as usize][x_idx.floor() as usize]); 158 } 159 } 160 buffer.present().unwrap(); 161 } 162 163 pub fn get_size(&self) -> PhysicalSize<u32> { 164 PhysicalSize { 165 width: self.width, 166 height: self.height, 167 } 168 } 169}