mod anim; mod asset; pub mod prelude; mod sprite; mod state; use wgpu::*; use winit::{ dpi::PhysicalSize, event::{Event, WindowEvent}, event_loop::ControlFlow, window::Window, }; use crate::{prelude::*, state::State}; pub async fn run() -> Result<()> { let event_loop = winit::event_loop::EventLoop::new(); let window = Window::new(&event_loop)?; window.set_inner_size(PhysicalSize::new(1280, 720)); #[cfg(target_arch = "wasm32")] { use winit::platform::web::WindowExtWebSys; web_sys::window() .and_then(|win| win.document()) .and_then(|doc| { let dst = doc.get_element_by_id("gmtk-game")?; let canvas = web_sys::Element::from(window.canvas()); dst.append_child(&canvas).ok()?; Some(()) }) .unwrap(); } let instance = Instance::new(InstanceDescriptor { backends: Backends::all(), dx12_shader_compiler: Default::default(), }); let surface = unsafe { instance.create_surface(&window) }?; let adapter = instance .request_adapter(&RequestAdapterOptions { power_preference: PowerPreference::HighPerformance, compatible_surface: Some(&surface), force_fallback_adapter: false, }) .await .ok_or(anyhow!("No compatible adapter"))?; info!("{:#?}", adapter.get_info()); let (device, queue) = adapter .request_device( &DeviceDescriptor { label: Some("GMTK"), features: Features::empty(), limits: Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()), }, None, ) .await?; let format = if surface .get_capabilities(&adapter) .formats .contains(&TextureFormat::Bgra8Unorm) { TextureFormat::Bgra8Unorm } else { TextureFormat::Rgba8Unorm }; debug!("Using {format:?}"); let mut reconfigure = true; let mut framebuffer = device.create_texture(&TextureDescriptor { label: Some("Framebuffer"), size: Default::default(), mip_level_count: 1, sample_count: 4, dimension: TextureDimension::D2, format, usage: TextureUsages::RENDER_ATTACHMENT, view_formats: &[], }); let mut depthbuffer = device.create_texture(&TextureDescriptor { label: Some("Depthbuffer"), size: Default::default(), mip_level_count: 1, sample_count: 4, dimension: TextureDimension::D2, format: TextureFormat::Depth24Plus, usage: TextureUsages::RENDER_ATTACHMENT, view_formats: &[], }); let mut state = State::new(&queue, &device).await?; event_loop.run(move |event, _target, control_flow| { *control_flow = ControlFlow::Poll; if reconfigure { let (width, height) = window.inner_size().into(); surface.configure( &device, &SurfaceConfiguration { usage: TextureUsages::RENDER_ATTACHMENT, format, width, height, present_mode: PresentMode::AutoNoVsync, alpha_mode: CompositeAlphaMode::Opaque, view_formats: vec![], }, ); framebuffer = device.create_texture(&TextureDescriptor { label: Some("Framebuffer"), size: Extent3d { width, height, depth_or_array_layers: 1, }, mip_level_count: framebuffer.mip_level_count(), sample_count: framebuffer.sample_count(), dimension: framebuffer.dimension(), format: framebuffer.format(), usage: framebuffer.usage(), view_formats: &[], }); depthbuffer = device.create_texture(&TextureDescriptor { label: Some("Depthbuffer"), size: Extent3d { width, height, depth_or_array_layers: 1, }, mip_level_count: depthbuffer.mip_level_count(), sample_count: depthbuffer.sample_count(), dimension: depthbuffer.dimension(), format: depthbuffer.format(), usage: depthbuffer.usage(), view_formats: &[], }); reconfigure = false; } match event { Event::MainEventsCleared => { let Ok(swapchain) = surface.get_current_texture() else { reconfigure = true; warn!("Swapchain acquisition failed, reconfiguring surface"); return }; // Recreate swapchain if suboptimal configuration reconfigure |= swapchain.suboptimal; let mut enc = device.create_command_encoder(&CommandEncoderDescriptor { label: Some("Main encoder"), }); { let fb_view = framebuffer.create_view(&Default::default()); let rs_view = swapchain.texture.create_view(&Default::default()); let db_view = depthbuffer.create_view(&Default::default()); let mut pass = enc.begin_render_pass(&RenderPassDescriptor { label: Some("Clear"), color_attachments: &[Some(RenderPassColorAttachment { view: &fb_view, resolve_target: Some(&rs_view), ops: Operations { load: LoadOp::Clear(Color::BLACK), store: true, }, })], depth_stencil_attachment: Some(RenderPassDepthStencilAttachment { view: &db_view, depth_ops: Some(Operations { load: LoadOp::Clear(1.0), store: true, }), stencil_ops: None, }), }); let aspect = window.inner_size().width as f32 / window.inner_size().height as f32; state.update(&queue, &device, &mut pass, format, aspect); } queue.submit([enc.finish()]); swapchain.present(); } Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::Resized(_) => reconfigure = true, _ => (), }, _ => (), } }); } async fn test() { match run().await { Ok(()) => info!("Clean exit"), Err(err) => error!("Runtime error: {err}"), } } #[cfg(target_arch = "wasm32")] pub fn main() { console_log::init_with_level(log::Level::Info).expect("Failed to initialize logger"); std::panic::set_hook(Box::new(console_error_panic_hook::hook)); wasm_bindgen_futures::spawn_local(test()); } #[cfg(not(target_arch = "wasm32"))] pub fn main() { env_logger::builder() .filter_level(log::LevelFilter::Trace) .filter(Some("wgpu_core"), log::LevelFilter::Warn) .parse_default_env() .init(); pollster::block_on(test()); }