1use crate::{
2 app::App,
3 ecs::plugin::Plugin,
4 rendering::{
5 backend::{Backend, ColorTarget, FrameOperations, Pass},
6 errors::AcquireError,
7 sync::InitSender,
8 window::{GPUSurfaceHandle, WindowConfig},
9 },
10 wgpu::window::WinitWindow,
11};
12
13pub struct WGPUBackend {
14 pub device: wgpu::Device,
15 pub queue: wgpu::Queue,
16 pub surface: wgpu::Surface<'static>,
17 pub config: wgpu::SurfaceConfiguration,
18}
19
20pub struct WGPUFrame {
21 encoder: wgpu::CommandEncoder,
22 view: wgpu::TextureView,
23 surface_texture: wgpu::SurfaceTexture,
24}
25
26impl FrameOperations for WGPUFrame {
27 type Context<'a> = wgpu::RenderPass<'a>;
28 type Attachment = wgpu::TextureView;
29 type DepthAttachment = wgpu::TextureView;
30
31 fn begin(&mut self, pass: Pass<'_, Self>) -> Self::Context<'_> {
32 let color_attachments: Vec<_> = pass
33 .colors
34 .iter()
35 .map(|target| {
36 let (view, clear) = match target {
37 ColorTarget::Default { clear } => (&self.view, clear),
38 ColorTarget::Custom { attachment, clear } => (*attachment, clear),
39 };
40 Some(wgpu::RenderPassColorAttachment {
41 view,
42 depth_slice: None,
43 resolve_target: None,
44 ops: wgpu::Operations {
45 load: clear
46 .map(|[r, g, b, a]| {
47 wgpu::LoadOp::Clear(wgpu::Color {
48 r: r as f64,
49 g: g as f64,
50 b: b as f64,
51 a: a as f64,
52 })
53 })
54 .unwrap_or(wgpu::LoadOp::Load),
55 store: wgpu::StoreOp::Store,
56 },
57 })
58 })
59 .collect();
60
61 let depth_stencil_attachment =
62 pass.depth
63 .as_ref()
64 .map(|d| wgpu::RenderPassDepthStencilAttachment {
65 view: d.attachment,
66 depth_ops: Some(wgpu::Operations {
67 load: d
68 .clear
69 .map(wgpu::LoadOp::Clear)
70 .unwrap_or(wgpu::LoadOp::Load),
71 store: wgpu::StoreOp::Store,
72 }),
73 stencil_ops: None,
74 });
75
76 self.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
77 label: None,
78 color_attachments: &color_attachments,
79 depth_stencil_attachment,
80 timestamp_writes: None,
81 occlusion_query_set: None,
82 multiview_mask: None,
83 })
84 }
85}
86
87impl WGPUFrame {
88 pub fn compute_pass(&mut self, label: Option<&str>) -> wgpu::ComputePass<'_> {
90 self.encoder
91 .begin_compute_pass(&wgpu::ComputePassDescriptor {
92 label,
93 timestamp_writes: None,
94 })
95 }
96}
97
98impl Backend for WGPUBackend {
99 type Frame = WGPUFrame;
100
101 fn init(handle: impl GPUSurfaceHandle, width: u32, height: u32, sender: InitSender<Self>) {
102 let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
103 display: None,
104 backends: wgpu::Backends::default(),
105 flags: wgpu::InstanceFlags::default(),
106 memory_budget_thresholds: wgpu::MemoryBudgetThresholds::default(),
107 backend_options: wgpu::BackendOptions::default(),
108 });
109
110 let surface = instance.create_surface(handle).unwrap();
111
112 let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
113 power_preference: wgpu::PowerPreference::HighPerformance,
114 force_fallback_adapter: false,
115 compatible_surface: Some(&surface),
116 }))
117 .unwrap();
118
119 let (device, queue) = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
120 label: None,
121 required_features: wgpu::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
122 required_limits: wgpu::Limits::default(),
123 ..Default::default()
124 }))
125 .unwrap();
126
127 let caps = surface.get_capabilities(&adapter);
128 let config = wgpu::SurfaceConfiguration {
129 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
130 format: caps.formats[0],
131 present_mode: caps.present_modes[0],
132 alpha_mode: caps.alpha_modes[0],
133 width,
134 height,
135 desired_maximum_frame_latency: 2,
136 view_formats: vec![],
137 };
138 surface.configure(&device, &config);
139
140 sender.send(WGPUBackend {
141 device,
142 queue,
143 surface,
144 config,
145 });
146 }
147
148 fn resize(&mut self, width: u32, height: u32) {
149 if width == 0 || height == 0 {
150 return; }
152 self.config.width = width;
153 self.config.height = height;
154 self.surface.configure(&self.device, &self.config);
155 }
156
157 fn acquire(&mut self) -> Result<Self::Frame, AcquireError> {
158 let surface_texture = match self.surface.get_current_texture() {
159 wgpu::CurrentSurfaceTexture::Success(texture) => texture,
160 wgpu::CurrentSurfaceTexture::Suboptimal(texture) => texture,
161 wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Outdated => {
162 return Err(AcquireError::Transient);
163 }
164 other => {
165 return Err(AcquireError::Fatal(format!(
166 "unexpected surface state: {other:?}"
167 )));
168 }
169 };
170
171 let view = surface_texture
172 .texture
173 .create_view(&wgpu::TextureViewDescriptor::default());
174 let encoder = self
175 .device
176 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
177
178 Ok(WGPUFrame {
179 encoder,
180 view,
181 surface_texture,
182 })
183 }
184
185 fn present(&mut self, frame: Self::Frame) {
186 self.queue.submit(std::iter::once(frame.encoder.finish()));
187 frame.surface_texture.present();
188 }
189}
190
191pub struct WGPUPlugin {
192 config: WindowConfig,
193}
194
195impl WGPUPlugin {
196 pub fn new(config: WindowConfig) -> Self {
197 Self { config }
198 }
199}
200
201impl Plugin for WGPUPlugin {
202 fn build(&self, app: &mut App) {
203 app.add_plugin(crate::prelude::WindowPlugin::<WinitWindow>::new(
204 WindowConfig {
205 title: self.config.title,
206 width: self.config.width,
207 height: self.config.height,
208 },
209 ))
210 .add_plugin(crate::prelude::GraphicsPlugin::<WGPUBackend, WinitWindow>::new())
211 .add_plugin(crate::prelude::RenderPlugin::<WGPUBackend>::new())
212 .add_plugin(crate::wgpu::textures::TexturePlugin)
213 .add_plugin(crate::wgpu::texture_array::TextureArrayPlugin)
214 .add_plugin(crate::wgpu::cubemap::CubemapPlugin)
215 .add_plugin(crate::wgpu::mesh::MeshPlugin::new())
216 .add_plugin(crate::wgpu::material::MaterialPlugin::new())
217 .add_plugin(crate::wgpu::material_instance::MaterialInstancePlugin::new())
218 .add_plugin(crate::wgpu::compute::ComputePlugin::new())
219 .add_plugin(crate::prelude::LazyResourcePlugin::<
220 WGPUBackend,
221 crate::wgpu::samplers::GlobalSamplers,
222 >::new());
223 }
224}