nightshade-renderer 0.55.0

GPU-driven wgpu renderer with a built-in frame graph.
Documentation
//! Opens a window and renders a spinning triangle.
//!
//! This is the smallest end-to-end use of `nightshade-renderer` on a real
//! surface. It shows the whole host loop:
//!
//! 1. Create a winit window and build a [`WgpuRenderer`] over it with
//!    [`WgpuRenderer::new_async`].
//! 2. Append the presentation passes and compile the graph with
//!    [`install_presentation_passes`]. The renderer's constructor builds the
//!    scene passes but leaves the graph open so a host can add its own; this
//!    call closes it.
//! 3. Each frame, compose a [`RenderInputs`], call [`render_frame`], and move
//!    the persistent state back out.
//!
//! Run it with:
//!
//! ```sh
//! cargo run --example window --features wgpu
//! ```
//!
//! The scene itself (mesh, camera, per-entity state) is built in the shared
//! `common/scene.rs` module, which is where the renderer's input contract is
//! documented.

use std::sync::Arc;

use nightshade_renderer::wgpu::WgpuRenderer;
use nightshade_renderer::wgpu::frame::render_frame;
use nightshade_renderer::wgpu::presentation::install_presentation_passes;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
use winit::window::{Window, WindowId};

#[path = "common/scene.rs"]
mod scene;

const INITIAL_WIDTH: u32 = 960;
const INITIAL_HEIGHT: u32 = 540;

struct App {
    window: Option<Arc<Window>>,
    renderer: Option<WgpuRenderer>,
    scene: scene::Scene,
    rotation_radians: f32,
    last_frame: std::time::Instant,
}

impl App {
    fn new() -> Self {
        Self {
            window: None,
            renderer: None,
            scene: scene::build_scene(),
            rotation_radians: 0.0,
            last_frame: std::time::Instant::now(),
        }
    }

    fn render(&mut self) {
        let (Some(window), Some(renderer)) = (self.window.as_ref(), self.renderer.as_mut()) else {
            return;
        };
        let size = window.inner_size();
        if size.width == 0 || size.height == 0 {
            return;
        }

        let now = std::time::Instant::now();
        let delta_time = (now - self.last_frame).as_secs_f32();
        self.last_frame = now;
        self.rotation_radians += delta_time;

        let mut inputs = scene::compose_frame(
            &mut self.scene,
            self.rotation_radians,
            size.width,
            size.height,
            delta_time,
        );
        let mut outputs = Default::default();
        if let Err(error) = render_frame(renderer, &mut inputs, &mut outputs) {
            eprintln!("render_frame failed: {error}");
        }
        scene::restore_frame(&mut self.scene, inputs);
    }
}

impl ApplicationHandler for App {
    fn resumed(&mut self, event_loop: &ActiveEventLoop) {
        if self.window.is_some() {
            return;
        }
        let attributes = Window::default_attributes()
            .with_title("nightshade-renderer: spinning triangle")
            .with_inner_size(winit::dpi::PhysicalSize::new(INITIAL_WIDTH, INITIAL_HEIGHT));
        let window = Arc::new(
            event_loop
                .create_window(attributes)
                .expect("failed to create window"),
        );
        let size = window.inner_size();

        let mut renderer = pollster::block_on(WgpuRenderer::new_async(
            window.clone(),
            size.width.max(1),
            size.height.max(1),
        ))
        .expect("failed to create renderer");
        install_presentation_passes(&mut renderer).expect("failed to install presentation passes");

        self.window = Some(window);
        self.renderer = Some(renderer);
    }

    fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) {
        match event {
            WindowEvent::CloseRequested => event_loop.exit(),
            WindowEvent::Resized(size) if size.width > 0 && size.height > 0 => {
                if let Some(renderer) = self.renderer.as_mut()
                    && let Err(error) = renderer.resize_surface(size.width, size.height)
                {
                    eprintln!("resize_surface failed: {error}");
                }
            }
            WindowEvent::RedrawRequested => {
                self.render();
                if let Some(window) = self.window.as_ref() {
                    window.request_redraw();
                }
            }
            _ => {}
        }
    }
}

fn main() {
    let event_loop = EventLoop::new().expect("failed to create event loop");
    event_loop.set_control_flow(ControlFlow::Poll);
    event_loop
        .run_app(&mut App::new())
        .expect("event loop failed");
}