use std::sync::Arc;
use color_eyre::eyre::eyre;
use winit::window::Window;
pub enum Context {
Uninitialized {
creation_size: Option<winit::dpi::PhysicalSize<u32>>,
},
#[allow(private_interfaces)]
Initialized(ActiveContext),
}
impl Default for Context {
fn default() -> Self {
Self::Uninitialized {
creation_size: None,
}
}
}
impl Context {
pub async fn initialize(&mut self, window: Window) -> color_eyre::Result<()> {
match self {
Self::Uninitialized { creation_size } => {
let mut context = ActiveContext::new(window).await?;
if let Some(new_size) = creation_size.take() {
context.resize(new_size);
}
*self = Self::Initialized(context);
Ok(())
}
Self::Initialized(_) => {
*self = Self::default();
let context = ActiveContext::new(window).await?;
*self = Self::Initialized(context);
Ok(())
}
}
}
pub fn on_window<F: FnOnce(Arc<Window>) -> O, O>(&self, f: F) -> Option<O> {
self.window().map(f)
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
match self {
Self::Uninitialized { .. } => Ok(()),
Self::Initialized(ac) => ac.render(),
}
}
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
match self {
Self::Uninitialized { creation_size } => {
*creation_size = Some(new_size);
}
Self::Initialized(ac) => {
ac.resize(new_size);
}
}
}
pub fn window(&self) -> Option<Arc<Window>> {
match self {
Self::Uninitialized { .. } => None,
Self::Initialized(ctx) => Some(ctx.window.clone()),
}
}
}
struct ActiveContext {
instance: wgpu::Instance,
surface: wgpu::Surface<'static>,
config: wgpu::SurfaceConfiguration,
adapter: wgpu::Adapter,
device: wgpu::Device,
queue: wgpu::Queue,
window: Arc<Window>,
}
impl ActiveContext {
pub async fn new(window: Window) -> color_eyre::Result<Self> {
let size = window.inner_size();
let width = size.width.max(1);
let height = size.height.max(1);
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY,
..Default::default()
});
let window = Arc::new(window);
let surface = instance.create_surface(window.clone())?;
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::default(),
force_fallback_adapter: false,
compatible_surface: Some(&surface),
})
.await
.ok_or(eyre!("Failed to find an adapter for surface"))?;
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: wgpu::Limits::default().using_resolution(adapter.limits()),
},
None,
)
.await?;
let swapchain_caps = surface.get_capabilities(&adapter);
let _swapchain_format = swapchain_caps.formats[0];
let config = surface
.get_default_config(&adapter, width, height)
.ok_or(eyre!("Failed to configure surface for adapter"))?;
surface.configure(&device, &config);
Ok(Self {
instance,
surface,
adapter,
window,
device,
queue,
config,
})
}
pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
if new_size.width > 0 && new_size.height > 0 {
self.config.width = new_size.width;
self.config.height = new_size.height;
self.surface.configure(&self.device, &self.config);
}
}
pub fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
let output = self.surface.get_current_texture()?;
let view = output
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"),
});
{
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.01,
g: 0.005,
b: 0.005,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
occlusion_query_set: None,
timestamp_writes: None,
});
}
self.queue.submit(std::iter::once(encoder.finish()));
output.present();
Ok(())
}
}