Opening a Window
Everything up to this point has run headless, no window at all — Part I’s examples are all tested that way (ecs_basics sets its own runner and calls app.update() in a plain loop). Real graphics needs three things: a window, a GPU device, and a render loop tied to the two. pebble::wgpu::backend::WGPUPlugin sets up all three at once.
WGPUPlugin
use pebble::prelude::*;
use pebble::wgpu::backend::WGPUPlugin;
fn main() {
App::new()
.add_plugin(WGPUPlugin::new(WindowConfig {
title: "My Game".to_string(),
width: 1280,
height: 720,
}))
.add_system(SystemStage::Render, render)
.build()
.run();
}
fn render(mut frame: ResMut<CurrentFrame<WGPUBackend>>) {
if let Some(mut active) = frame.active() {
let mut _pass = active.render_context([0.05, 0.05, 0.08, 1.0]);
// draw calls go here
}
}
WGPUPlugin is a convenience bundle — under the hood it registers a window plugin, the graphics backend, the render loop, and every asset pipeline this book’s Part II uses (mesh, material, material instance, texture, texture array, cubemap, compute, sampler). One plugin instead of one add_plugin call per asset type.
Why Option-shaped access to the frame
frame.active() returns Option<ActiveFrame<...>>, not the frame directly. Backend initialization is asynchronous (creating a wgpu::Device is itself an async operation, and on native it may also be handed off to a background thread) — for the first several ticks after build(), there simply isn’t a frame yet. render just does nothing on those ticks; there’s no error to handle, because nothing has gone wrong. This is the same Option<Res<T>> pattern from Chapter 2, applied to the one resource (CurrentFrame<B>) that’s guaranteed to start out absent in every graphical app.
render_context(clear_color) is a shortcut for the common case: one color attachment, cleared to clear_color, no depth buffer. Camera, Depth, and Lazy Resources uses the more general begin_pass once a depth attachment enters the picture.
What you should see
Run this and you get a window, cleared every frame to a dark blue-gray — nothing drawn yet, because nothing has been given to draw. That’s Your First Triangle.