1use crate::color::IntoLinSrgba;
4use crate::wgpu;
5use std::ops;
6use std::path::PathBuf;
7use std::sync::Mutex;
8use std::time::Duration;
9
10pub mod raw;
11
12pub use self::raw::RawFrame;
13
14pub struct Frame<'swap_chain> {
23 raw_frame: RawFrame<'swap_chain>,
24 render_data: &'swap_chain RenderData,
25 capture_data: &'swap_chain CaptureData,
26}
27
28#[derive(Debug)]
30pub struct RenderData {
31 intermediary_lin_srgba: IntermediaryLinSrgba,
32 msaa_samples: u32,
33 size: [u32; 2],
34 texture_reshaper: wgpu::TextureReshaper,
36}
37
38#[derive(Debug)]
40pub(crate) struct CaptureData {
41 pub(crate) next_frame_path: Mutex<Option<PathBuf>>,
43 pub(crate) texture_capturer: wgpu::TextureCapturer,
45}
46
47#[derive(Debug)]
50pub(crate) struct IntermediaryLinSrgba {
51 msaa_texture: Option<(wgpu::Texture, wgpu::TextureView)>,
52 texture: wgpu::Texture,
53 texture_view: wgpu::TextureView,
54}
55
56impl<'swap_chain> ops::Deref for Frame<'swap_chain> {
57 type Target = RawFrame<'swap_chain>;
58 fn deref(&self) -> &Self::Target {
59 &self.raw_frame
60 }
61}
62
63impl<'swap_chain> Frame<'swap_chain> {
64 pub const DEFAULT_MSAA_SAMPLES: u32 = 4;
67 pub const TEXTURE_FORMAT: wgpu::TextureFormat =
74 wgpu::RenderPipelineBuilder::DEFAULT_COLOR_FORMAT;
75
76 pub(crate) fn new_empty(
78 raw_frame: RawFrame<'swap_chain>,
79 render_data: &'swap_chain RenderData,
80 capture_data: &'swap_chain CaptureData,
81 ) -> Self {
82 Frame {
83 raw_frame,
84 render_data,
85 capture_data,
86 }
87 }
88
89 fn submit_inner(&mut self) {
92 let Frame {
93 ref capture_data,
94 ref render_data,
95 ref mut raw_frame,
96 } = *self;
97
98 if let Some((_, ref msaa_texture_view)) = render_data.intermediary_lin_srgba.msaa_texture {
100 let mut encoder = raw_frame.command_encoder();
101 wgpu::resolve_texture(
102 msaa_texture_view,
103 &render_data.intermediary_lin_srgba.texture_view,
104 &mut *encoder,
105 );
106 }
107
108 let mut snapshot_capture = None;
110 if let Ok(mut guard) = capture_data.next_frame_path.lock() {
111 if let Some(path) = guard.take() {
112 let device = raw_frame.device_queue_pair().device();
113 let mut encoder = raw_frame.command_encoder();
114 let snapshot = capture_data.texture_capturer.capture(
115 device,
116 &mut *encoder,
117 &render_data.intermediary_lin_srgba.texture,
118 );
119 snapshot_capture = Some((path, snapshot));
120 }
121 }
122
123 {
128 let mut encoder = raw_frame.command_encoder();
129 render_data
130 .texture_reshaper
131 .encode_render_pass(raw_frame.swap_chain_texture(), &mut *encoder);
132 }
133
134 raw_frame.submit_inner();
136
137 if let Some((path, snapshot)) = snapshot_capture {
139 let result = snapshot.read(move |result| match result {
140 Err(e) => eprintln!("failed to async read captured frame: {:?}", e),
142 Ok(image) => {
143 let image = image.to_owned();
144 if let Err(e) = image.save(&path) {
145 eprintln!(
147 "failed to save captured frame to \"{}\": {}",
148 path.display(),
149 e
150 );
151 }
152 }
153 });
154 if let Err(wgpu::TextureCapturerAwaitWorkerTimeout(_)) = result {
155 eprintln!("timed out while waiting for a worker thread to capture the frame");
157 }
158 }
159 }
160
161 pub fn texture(&self) -> &wgpu::Texture {
179 self.render_data
180 .intermediary_lin_srgba
181 .msaa_texture
182 .as_ref()
183 .map(|(tex, _)| tex)
184 .unwrap_or(&self.render_data.intermediary_lin_srgba.texture)
185 }
186
187 pub fn texture_view(&self) -> &wgpu::TextureView {
191 self.render_data.texture_view()
192 }
193
194 pub fn resolve_target(&self) -> Option<&wgpu::TextureView> {
196 if self.render_data.msaa_samples <= 1 {
197 None
198 } else {
199 Some(&self.render_data.intermediary_lin_srgba.texture_view)
200 }
201 }
202
203 pub fn texture_format(&self) -> wgpu::TextureFormat {
206 Self::TEXTURE_FORMAT
207 }
208
209 pub fn texture_msaa_samples(&self) -> u32 {
211 self.render_data.msaa_samples
212 }
213
214 pub fn texture_size(&self) -> [u32; 2] {
216 self.render_data.size
217 }
218
219 pub fn color_attachment_descriptor(&self) -> wgpu::RenderPassColorAttachment {
227 let load = wgpu::LoadOp::Load;
228 let store = true;
229 let attachment = match self.render_data.intermediary_lin_srgba.msaa_texture {
230 None => &self.render_data.intermediary_lin_srgba.texture_view,
231 Some((_, ref msaa_texture_view)) => msaa_texture_view,
232 };
233 let resolve_target = None;
234 wgpu::RenderPassColorAttachment {
235 view: attachment,
236 resolve_target,
237 ops: wgpu::Operations { load, store },
238 }
239 }
240
241 pub fn clear<C>(&self, color: C)
243 where
244 C: IntoLinSrgba<f32>,
245 {
246 let lin_srgba = color.into_lin_srgba();
247 let (r, g, b, a) = lin_srgba.into_components();
248 let (r, g, b, a) = (r as f64, g as f64, b as f64, a as f64);
249 let color = wgpu::Color { r, g, b, a };
250
251 self.raw_frame.clear(self.texture_view(), color);
252 }
253
254 pub fn submit(mut self) {
267 self.submit_inner();
268 }
269}
270
271impl CaptureData {
272 pub(crate) fn new(max_jobs: u32, timeout: Option<Duration>) -> Self {
273 CaptureData {
274 next_frame_path: Default::default(),
275 texture_capturer: wgpu::TextureCapturer::new(Some(max_jobs), timeout),
276 }
277 }
278}
279
280impl RenderData {
281 pub(crate) fn new(
288 device: &wgpu::Device,
289 swap_chain_dims: [u32; 2],
290 swap_chain_format: wgpu::TextureFormat,
291 msaa_samples: u32,
292 ) -> Self {
293 let intermediary_lin_srgba =
294 create_intermediary_lin_srgba(device, swap_chain_dims, msaa_samples);
295 let src_sample_count = 1;
296 let swap_chain_sample_count = 1;
297 let texture_reshaper = wgpu::TextureReshaper::new(
298 device,
299 &intermediary_lin_srgba.texture_view,
300 src_sample_count,
301 intermediary_lin_srgba.texture_view.sample_type(),
302 swap_chain_sample_count,
303 swap_chain_format,
304 );
305 RenderData {
306 intermediary_lin_srgba,
307 texture_reshaper,
308 size: swap_chain_dims,
309 msaa_samples,
310 }
311 }
312
313 pub(crate) fn texture_view(&self) -> &wgpu::TextureView {
317 self.intermediary_lin_srgba
318 .msaa_texture
319 .as_ref()
320 .map(|(_, view)| view)
321 .unwrap_or(&self.intermediary_lin_srgba.texture_view)
322 }
323}
324
325impl<'swap_chain> Drop for Frame<'swap_chain> {
326 fn drop(&mut self) {
327 if !self.raw_frame.is_submitted() {
328 self.submit_inner();
329 }
330 }
331}
332
333fn create_lin_srgba_msaa_texture(
334 device: &wgpu::Device,
335 swap_chain_dims: [u32; 2],
336 msaa_samples: u32,
337) -> wgpu::Texture {
338 wgpu::TextureBuilder::new()
339 .size(swap_chain_dims)
340 .sample_count(msaa_samples)
341 .usage(wgpu::TextureUsages::RENDER_ATTACHMENT)
342 .format(Frame::TEXTURE_FORMAT)
343 .build(device)
344}
345
346fn create_lin_srgba_texture(device: &wgpu::Device, swap_chain_dims: [u32; 2]) -> wgpu::Texture {
347 wgpu::TextureBuilder::new()
348 .size(swap_chain_dims)
349 .format(Frame::TEXTURE_FORMAT)
350 .usage(wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING)
351 .build(device)
352}
353
354fn create_intermediary_lin_srgba(
355 device: &wgpu::Device,
356 swap_chain_dims: [u32; 2],
357 msaa_samples: u32,
358) -> IntermediaryLinSrgba {
359 let msaa_texture = match msaa_samples {
360 0 | 1 => None,
361 _ => {
362 let texture = create_lin_srgba_msaa_texture(device, swap_chain_dims, msaa_samples);
363 let texture_view = texture.view().build();
364 Some((texture, texture_view))
365 }
366 };
367 let texture = create_lin_srgba_texture(device, swap_chain_dims);
368 let texture_view = texture.view().build();
369 IntermediaryLinSrgba {
370 msaa_texture,
371 texture,
372 texture_view,
373 }
374}