use crate::{
app::App,
ecs::plugin::Plugin,
rendering::{
backend::{Backend, ColorTarget, FrameOperations, Pass},
errors::AcquireError,
sync::InitSender,
window::{GPUSurfaceHandle, WindowConfig},
},
wgpu::window::WinitWindow,
};
pub struct WGPUBackend {
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub surface: wgpu::Surface<'static>,
pub config: wgpu::SurfaceConfiguration,
}
pub struct WGPUFrame {
encoder: wgpu::CommandEncoder,
view: wgpu::TextureView,
surface_texture: wgpu::SurfaceTexture,
}
impl FrameOperations for WGPUFrame {
type Context<'a> = wgpu::RenderPass<'a>;
type Attachment = wgpu::TextureView;
type DepthAttachment = wgpu::TextureView;
fn begin(&mut self, pass: Pass<'_, Self>) -> Self::Context<'_> {
let color_attachments: Vec<_> = pass
.colors
.iter()
.map(|target| {
let (view, clear) = match target {
ColorTarget::Default { clear } => (&self.view, clear),
ColorTarget::Custom { attachment, clear } => (*attachment, clear),
};
Some(wgpu::RenderPassColorAttachment {
view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: clear
.map(|[r, g, b, a]| {
wgpu::LoadOp::Clear(wgpu::Color {
r: r as f64,
g: g as f64,
b: b as f64,
a: a as f64,
})
})
.unwrap_or(wgpu::LoadOp::Load),
store: wgpu::StoreOp::Store,
},
})
})
.collect();
let depth_stencil_attachment =
pass.depth
.as_ref()
.map(|d| wgpu::RenderPassDepthStencilAttachment {
view: d.attachment,
depth_ops: Some(wgpu::Operations {
load: d
.clear
.map(wgpu::LoadOp::Clear)
.unwrap_or(wgpu::LoadOp::Load),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
});
self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &color_attachments,
depth_stencil_attachment,
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
})
}
}
impl Backend for WGPUBackend {
type Frame = WGPUFrame;
fn init(handle: impl GPUSurfaceHandle, width: u32, height: u32, sender: InitSender<Self>) {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
display: None,
backends: wgpu::Backends::default(),
flags: wgpu::InstanceFlags::default(),
memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
backend_options: wgpu::BackendOptions::default(),
});
let surface = instance.create_surface(handle).unwrap();
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
force_fallback_adapter: false,
compatible_surface: Some(&surface),
}))
.unwrap();
let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
required_limits: wgpu::Limits::default(),
..Default::default()
}))
.unwrap();
let caps = surface.get_capabilities(&adapter);
let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: caps.formats[0],
present_mode: caps.present_modes[0],
alpha_mode: caps.alpha_modes[0],
width,
height,
desired_maximum_frame_latency: 2,
view_formats: vec![],
};
surface.configure(&device, &config);
sender.send(WGPUBackend {
device,
queue,
surface,
config,
});
}
fn resize(&mut self, width: u32, height: u32) {
if width == 0 || height == 0 {
return; }
self.config.width = width;
self.config.height = height;
self.surface.configure(&self.device, &self.config);
}
fn acquire(&mut self) -> Result<Self::Frame, AcquireError> {
let surface_texture = match self.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(texture) => texture,
wgpu::CurrentSurfaceTexture::Suboptimal(texture) => texture,
wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Outdated => {
return Err(AcquireError::Transient);
}
other => {
return Err(AcquireError::Fatal(format!(
"unexpected surface state: {other:?}"
)));
}
};
let view = surface_texture
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
Ok(WGPUFrame {
encoder,
view,
surface_texture,
})
}
fn present(&mut self, frame: Self::Frame) {
self.queue.submit(std::iter::once(frame.encoder.finish()));
frame.surface_texture.present();
}
}
pub struct WGPUPlugin {
config: WindowConfig,
}
impl WGPUPlugin {
pub fn new(config: WindowConfig) -> Self {
Self { config }
}
}
impl Plugin for WGPUPlugin {
fn build(&self, app: &mut App) {
app.add_plugin(crate::prelude::WindowPlugin::<WinitWindow>::new(
WindowConfig {
title: self.config.title,
width: self.config.width,
height: self.config.height,
},
))
.add_plugin(crate::prelude::GraphicsPlugin::<WGPUBackend, WinitWindow>::new())
.add_plugin(crate::prelude::RenderPlugin::<WGPUBackend>::new())
.add_plugin(crate::wgpu::textures::TexturePlugin)
.add_plugin(crate::wgpu::texture_array::TextureArrayPlugin)
.add_plugin(crate::wgpu::cubemap::CubemapPlugin)
.add_plugin(crate::wgpu::mesh::MeshPlugin::new())
.add_plugin(crate::wgpu::material::MaterialPlugin::new())
.add_plugin(crate::wgpu::material_instance::MaterialInstancePlugin::new())
.add_plugin(crate::prelude::LazyResourcePlugin::<
WGPUBackend,
crate::wgpu::samplers::GlobalSamplers,
>::new());
}
}