Skip to content
Snippets Groups Projects
lib.rs 8.28 KiB
Newer Older
Noxim's avatar
Noxim committed
mod anim;
Noxim's avatar
Noxim committed
mod asset;
pub mod prelude;
Noxim's avatar
Noxim committed
mod sprite;
mod state;
Noxim's avatar
Noxim committed

use wgpu::*;
use winit::{
    dpi::PhysicalSize,
    event::{Event, WindowEvent},
    event_loop::ControlFlow,
    window::Window,
};

Noxim's avatar
Noxim committed
use crate::{prelude::*, state::State};
Noxim's avatar
Noxim committed

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(),
Noxim's avatar
Noxim committed
                limits: Limits::downlevel_webgl2_defaults().using_resolution(adapter.limits()),
Noxim's avatar
Noxim committed
            },
            None,
        )
        .await?;

    let format = if surface
        .get_capabilities(&adapter)
        .formats
        .contains(&TextureFormat::Bgra8Unorm)
    {
        TextureFormat::Bgra8Unorm
    } else {
        TextureFormat::Rgba8Unorm
    };
    let view_formats = if adapter
        .get_downlevel_capabilities()
        .flags
        .contains(DownlevelFlags::SURFACE_VIEW_FORMATS)
    {
        vec![format.add_srgb_suffix()]
    } else {
        vec![]
    };

    debug!("Using {format:?} with sRGB: {}", !view_formats.is_empty());

    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: &[],
    });

Noxim's avatar
Noxim committed
    let mut state = State::new(&queue, &device).await?;
Noxim's avatar
Noxim committed

    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: view_formats.clone(),
                },
            );

            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: &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"),
                });

Noxim's avatar
Noxim committed
                {
                    let fb_view = framebuffer.create_view(&TextureViewDescriptor {
                        label: Some("Antialias target"),
                        format: view_formats.get(0).copied(),
                        ..Default::default()
                    });
                    let rs_view = swapchain.texture.create_view(&TextureViewDescriptor {
                        label: Some("Resolve target"),
                        format: view_formats.get(0).copied(),
                        ..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,
Noxim's avatar
Noxim committed
                            },
Noxim's avatar
Noxim committed
                        })],
                        depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
                            view: &db_view,
                            depth_ops: Some(Operations {
                                load: LoadOp::Clear(1.0),
                                store: true,
                            }),
                            stencil_ops: None,
Noxim's avatar
Noxim committed
                        }),
Noxim's avatar
Noxim committed
                    });

                    let pipeline_fmt = view_formats.get(0).copied().unwrap_or(format);
                    let aspect =
                        window.inner_size().width as f32 / window.inner_size().height as f32;

                    state.update(&queue, &device, &mut pass, pipeline_fmt, aspect);
                }
Noxim's avatar
Noxim committed

                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());
}