dreamwell-runtime 1.0.0

Dreamwell Runtime — cross-platform GPU-accelerated game client
Documentation
// Window + surface creation and resize handling.

use std::sync::Arc;
use winit::event_loop::ActiveEventLoop;
use winit::window::Window;

/// Window state — owns the winit window and wgpu surface.
pub struct WindowState {
    pub window: Arc<Window>,
    pub surface: wgpu::Surface<'static>,
    pub device: wgpu::Device,
    pub queue: wgpu::Queue,
    pub surface_config: wgpu::SurfaceConfiguration,
    pub capabilities: dreamwell_gpu::features::GpuCapabilities,
}

impl WindowState {
    pub fn new(event_loop: &ActiveEventLoop, title: &str, width: u32, height: u32) -> Self {
        let window_attrs = Window::default_attributes()
            .with_title(title)
            .with_inner_size(winit::dpi::LogicalSize::new(width, height));
        let window = Arc::new(
            event_loop
                .create_window(window_attrs)
                .expect("window: failed to create OS window from event loop"),
        );

        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::new_with_display_handle(Box::new(
            event_loop.owned_display_handle(),
        )));

        let surface = instance
            .create_surface(window.clone())
            .expect("surface: failed to create wgpu surface for window");

        let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
            power_preference: wgpu::PowerPreference::HighPerformance,
            compatible_surface: Some(&surface),
            force_fallback_adapter: false,
        }))
        .expect("adapter: no suitable GPU adapter found (requires HighPerformance with surface compatibility)");

        let (features, limits, experimental) =
            dreamwell_gpu::features::GpuCapabilities::device_descriptor_parts(&adapter);

        let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
            label: Some("dreamwell_runtime"),
            required_features: features,
            required_limits: limits,
            memory_hints: Default::default(),
            experimental_features: experimental,
            trace: Default::default(),
        }))
        .expect("device: failed to create wgpu device from adapter — check required features and limits");

        // Make wgpu validation errors non-fatal during development.
        // Log errors instead of panicking so we can see partial rendering output
        // while debugging buffer alignment issues across the render pipeline.
        device.set_device_lost_callback(|reason, msg| {
            log::error!("wgpu device lost: {reason:?} — {msg}");
        });
        device.on_uncaptured_error(std::sync::Arc::new(|err| {
            log::error!("wgpu error (non-fatal): {err}");
        }));

        let capabilities = dreamwell_gpu::features::GpuCapabilities::from_adapter(&adapter);
        log::info!("GPU: {}", capabilities.summary());

        let surface_caps = surface.get_capabilities(&adapter);
        let surface_format = surface_caps
            .formats
            .iter()
            .find(|f| f.is_srgb())
            .copied()
            .unwrap_or(surface_caps.formats[0]);

        let size = window.inner_size();
        let surface_config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: surface_format,
            width: size.width.max(1),
            height: size.height.max(1),
            present_mode: wgpu::PresentMode::AutoVsync,
            alpha_mode: surface_caps.alpha_modes[0],
            view_formats: vec![],
            desired_maximum_frame_latency: 2,
        };
        surface.configure(&device, &surface_config);

        log::info!(
            "Surface: format={:?}, present_mode={:?}, size={}x{}",
            surface_config.format,
            surface_config.present_mode,
            surface_config.width,
            surface_config.height,
        );

        Self {
            window,
            surface,
            device,
            queue,
            surface_config,
            capabilities,
        }
    }

    pub fn resize(&mut self, width: u32, height: u32) {
        let max_dim = self.device.limits().max_texture_dimension_2d;
        let width = width.min(max_dim);
        let height = height.min(max_dim);
        if width > 0 && height > 0 {
            self.surface_config.width = width;
            self.surface_config.height = height;
            self.surface.configure(&self.device, &self.surface_config);
        }
    }

    pub fn aspect_ratio(&self) -> f32 {
        self.surface_config.width as f32 / self.surface_config.height.max(1) as f32
    }
}