1use core::fmt::Debug;
7use std::{
8 ops::Deref,
9 sync::{Arc, RwLock},
10};
11
12use glam::{UVec2, UVec3};
13use snafu::prelude::*;
14
15use crate::{
16 stage::Stage,
17 texture::{BufferDimensions, CopiedTextureBuffer, Texture, TextureError},
18};
19pub use craballoc::runtime::WgpuRuntime;
20
21pub(crate) enum RenderTargetInner {
24 Surface {
25 surface: wgpu::Surface<'static>,
26 surface_config: wgpu::SurfaceConfiguration,
27 },
28 Texture {
29 texture: Arc<wgpu::Texture>,
30 },
31}
32
33#[repr(transparent)]
34pub struct RenderTarget(pub(crate) RenderTargetInner);
38
39impl From<wgpu::Texture> for RenderTarget {
41 fn from(value: wgpu::Texture) -> Self {
42 RenderTarget(RenderTargetInner::Texture {
43 texture: Arc::new(value),
44 })
45 }
46}
47
48impl RenderTarget {
49 pub fn resize(&mut self, width: u32, height: u32, device: &wgpu::Device) {
52 match &mut self.0 {
53 RenderTargetInner::Surface {
54 surface,
55 surface_config,
56 } => {
57 surface_config.width = width;
58 surface_config.height = height;
59 surface.configure(device, surface_config)
60 }
61 RenderTargetInner::Texture { texture } => {
62 let usage = texture.usage();
63 let format = texture.format();
64 let texture_desc = wgpu::TextureDescriptor {
65 size: wgpu::Extent3d {
66 width,
67 height,
68 depth_or_array_layers: 1,
69 },
70 mip_level_count: 1,
71 sample_count: 1,
72 dimension: wgpu::TextureDimension::D2,
73 format,
74 usage,
75 label: Some("RenderTarget texture"),
76 view_formats: &[],
77 };
78 *texture = Arc::new(device.create_texture(&texture_desc));
79 }
80 }
81 }
82
83 pub fn format(&self) -> wgpu::TextureFormat {
85 match &self.0 {
86 RenderTargetInner::Surface { surface_config, .. } => surface_config.format,
87 RenderTargetInner::Texture { texture } => texture.format(),
88 }
89 }
90
91 pub fn is_headless(&self) -> bool {
93 match &self.0 {
94 RenderTargetInner::Surface { .. } => false,
95 RenderTargetInner::Texture { .. } => true,
96 }
97 }
98
99 pub fn as_texture(&self) -> Option<&wgpu::Texture> {
101 match &self.0 {
102 RenderTargetInner::Surface { .. } => None,
103 RenderTargetInner::Texture { texture } => Some(texture),
104 }
105 }
106
107 pub fn get_size(&self) -> UVec2 {
109 match &self.0 {
110 RenderTargetInner::Surface {
111 surface: _,
112 surface_config,
113 } => UVec2::new(surface_config.width, surface_config.height),
114 RenderTargetInner::Texture { texture } => {
115 let s = texture.size();
116 UVec2::new(s.width, s.height)
117 }
118 }
119 }
120}
121
122#[derive(Debug, Snafu)]
123#[snafu(visibility(pub(crate)))]
124pub enum ContextError {
126 #[snafu(display("missing surface texture: {}", source))]
127 Surface { source: wgpu::SurfaceError },
128
129 #[snafu(display("cannot create adaptor: {source}"))]
130 CannotCreateAdaptor { source: wgpu::RequestAdapterError },
131
132 #[snafu(display("cannot request device: {}", source))]
133 CannotRequestDevice { source: wgpu::RequestDeviceError },
134
135 #[snafu(display("surface is incompatible with adapter"))]
136 IncompatibleSurface,
137
138 #[snafu(display("could not create surface: {}", source))]
139 CreateSurface { source: wgpu::CreateSurfaceError },
140}
141
142pub struct FrameTextureView {
144 pub view: Arc<wgpu::TextureView>,
145 pub format: wgpu::TextureFormat,
146}
147
148impl Deref for FrameTextureView {
149 type Target = wgpu::TextureView;
150
151 fn deref(&self) -> &Self::Target {
152 &self.view
153 }
154}
155
156pub(crate) enum FrameSurface {
159 Surface(wgpu::SurfaceTexture),
160 Texture(Arc<wgpu::Texture>),
161}
162
163pub struct Frame {
167 pub(crate) runtime: WgpuRuntime,
168 pub(crate) surface: FrameSurface,
169}
170
171impl Frame {
172 pub fn texture(&self) -> &wgpu::Texture {
174 match &self.surface {
175 FrameSurface::Surface(s) => &s.texture,
176 FrameSurface::Texture(t) => t,
177 }
178 }
179
180 pub fn view(&self) -> wgpu::TextureView {
182 let texture = self.texture();
183 let format = texture.format().add_srgb_suffix();
184 texture.create_view(&wgpu::TextureViewDescriptor {
185 label: Some("Frame::view"),
186 format: Some(format),
187 ..Default::default()
188 })
189 }
190
191 pub fn copy_to_buffer(&self, width: u32, height: u32) -> CopiedTextureBuffer {
193 let dimensions = BufferDimensions::new(4, 1, width as usize, height as usize);
194 let buffer = self.runtime.device.create_buffer(&wgpu::BufferDescriptor {
196 label: Some("RenderTarget::copy_to_buffer"),
197 size: (dimensions.padded_bytes_per_row * dimensions.height) as u64,
198 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
199 mapped_at_creation: false,
200 });
201 let mut encoder =
202 self.runtime
203 .device
204 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
205 label: Some("post render screen capture encoder"),
206 });
207 let texture = self.texture();
208 encoder.copy_texture_to_buffer(
210 texture.as_image_copy(),
211 wgpu::TexelCopyBufferInfo {
212 buffer: &buffer,
213 layout: wgpu::TexelCopyBufferLayout {
214 offset: 0,
215 bytes_per_row: Some(dimensions.padded_bytes_per_row as u32),
216 rows_per_image: None,
217 },
218 },
219 wgpu::Extent3d {
220 width: dimensions.width as u32,
221 height: dimensions.height as u32,
222 depth_or_array_layers: 1,
223 },
224 );
225
226 self.runtime.queue.submit(std::iter::once(encoder.finish()));
227
228 CopiedTextureBuffer {
229 dimensions,
230 buffer,
231 format: texture.format(),
232 }
233 }
234
235 pub fn get_size(&self) -> UVec2 {
236 let s = self.texture().size();
237 UVec2::new(s.width, s.height)
238 }
239
240 pub async fn read_image(&self) -> Result<image::RgbaImage, TextureError> {
250 let size = self.get_size();
251 let buffer = self.copy_to_buffer(size.x, size.y);
252 let is_srgb = self.texture().format().is_srgb();
253 let img = if is_srgb {
254 buffer.into_srgba(&self.runtime.device).await?
255 } else {
256 buffer.into_linear_rgba(&self.runtime.device).await?
257 };
258 Ok(img)
259 }
260
261 pub async fn read_srgb_image(&self) -> Result<image::RgbaImage, TextureError> {
269 let size = self.get_size();
270 let buffer = self.copy_to_buffer(size.x, size.y);
271 log::trace!("read image has the format: {:?}", buffer.format);
272 buffer.into_srgba(&self.runtime.device).await
273 }
274 pub async fn read_linear_image(&self) -> Result<image::RgbaImage, TextureError> {
282 let size = self.get_size();
283 let buffer = self.copy_to_buffer(size.x, size.y);
284 buffer.into_linear_rgba(&self.runtime.device).await
285 }
286
287 pub fn present(self) {
290 match self.surface {
291 FrameSurface::Surface(s) => s.present(),
292 FrameSurface::Texture(_) => {}
293 }
294 }
295}
296
297#[derive(Debug, Clone, Copy)]
299pub(crate) struct GlobalStageConfig {
300 pub(crate) atlas_size: wgpu::Extent3d,
301 pub(crate) shadow_map_atlas_size: wgpu::Extent3d,
302 pub(crate) use_compute_culling: bool,
303}
304
305pub struct Context {
316 runtime: WgpuRuntime,
317 adapter: Arc<wgpu::Adapter>,
318 render_target: RenderTarget,
319 pub(crate) stage_config: Arc<RwLock<GlobalStageConfig>>,
320}
321
322impl AsRef<WgpuRuntime> for Context {
323 fn as_ref(&self) -> &WgpuRuntime {
324 &self.runtime
325 }
326}
327
328impl Context {
329 pub fn new(
332 target: RenderTarget,
333 adapter: impl Into<Arc<wgpu::Adapter>>,
334 device: impl Into<Arc<wgpu::Device>>,
335 queue: impl Into<Arc<wgpu::Queue>>,
336 ) -> Self {
337 let adapter: Arc<wgpu::Adapter> = adapter.into();
338 let limits = adapter.limits();
339 let w = limits
340 .max_texture_dimension_2d
341 .min(crate::atlas::ATLAS_SUGGESTED_SIZE);
342 let stage_config = Arc::new(RwLock::new(GlobalStageConfig {
343 atlas_size: wgpu::Extent3d {
344 width: w,
345 height: w,
346 depth_or_array_layers: adapter
347 .limits()
348 .max_texture_array_layers
349 .min(crate::atlas::ATLAS_SUGGESTED_LAYERS),
350 },
351 shadow_map_atlas_size: wgpu::Extent3d {
352 width: w,
353 height: w,
354 depth_or_array_layers: 4,
355 },
356 use_compute_culling: false,
357 }));
358 Self {
359 adapter,
360 runtime: WgpuRuntime {
361 device: device.into(),
362 queue: queue.into(),
363 },
364 render_target: target,
365 stage_config,
366 }
367 }
368
369 pub async fn try_new_headless(
372 width: u32,
373 height: u32,
374 backends: Option<wgpu::Backends>,
375 ) -> Result<Self, ContextError> {
376 log::trace!("creating headless context of size ({width}, {height})");
377 let instance = crate::internal::new_instance(backends);
378 let (adapter, device, queue, target) =
379 crate::internal::new_headless_device_queue_and_target(width, height, &instance).await?;
380 Ok(Self::new(target, adapter, device, queue))
381 }
382
383 pub async fn try_new_with_surface(
386 width: u32,
387 height: u32,
388 backends: Option<wgpu::Backends>,
389 window: impl Into<wgpu::SurfaceTarget<'static>>,
390 ) -> Result<Self, ContextError> {
391 let instance = crate::internal::new_instance(backends);
392 let (adapter, device, queue, target) =
393 crate::internal::new_windowed_adapter_device_queue(width, height, &instance, window)
394 .await?;
395 Ok(Self::new(target, adapter, device, queue))
396 }
397
398 #[cfg(feature = "winit")]
399 pub async fn from_winit_window(
404 backends: Option<wgpu::Backends>,
405 window: Arc<winit::window::Window>,
406 ) -> Self {
407 let inner_size = window.inner_size();
408 Self::try_new_with_surface(inner_size.width, inner_size.height, backends, window)
409 .await
410 .unwrap()
411 }
412
413 pub async fn headless(width: u32, height: u32) -> Self {
421 let result = Self::try_new_headless(width, height, None).await;
422 #[cfg(target_arch = "wasm32")]
423 {
424 use wasm_bindgen::UnwrapThrowExt;
425 result.expect_throw("Could not create context")
426 }
427 #[cfg(not(target_arch = "wasm32"))]
428 {
429 result.expect("Could not create context")
430 }
431 }
432
433 pub fn get_size(&self) -> UVec2 {
434 self.render_target.get_size()
435 }
436
437 pub fn set_size(&mut self, size: UVec2) {
439 self.render_target
440 .resize(size.x, size.y, &self.runtime.device);
441 }
442
443 pub fn create_texture<P>(
445 &self,
446 label: Option<&str>,
447 img: &image::ImageBuffer<P, Vec<u8>>,
448 ) -> Result<Texture, TextureError>
449 where
450 P: image::PixelWithColorType,
451 image::ImageBuffer<P, Vec<u8>>: image::GenericImage + std::ops::Deref<Target = [u8]>,
452 {
453 let name = label.unwrap_or("unknown");
454 Texture::from_image_buffer(
455 self,
456 img,
457 Some(&format!("Renderling::create_texture {}", name)),
458 None,
459 None,
460 )
461 }
462
463 pub fn texture_from_wgpu_tex(
465 &self,
466 texture: impl Into<Arc<wgpu::Texture>>,
467 sampler: Option<wgpu::SamplerDescriptor>,
468 ) -> Texture {
469 Texture::from_wgpu_tex(self.get_device(), texture, sampler, None)
470 }
471
472 pub fn runtime(&self) -> &WgpuRuntime {
474 &self.runtime
475 }
476
477 pub fn get_device(&self) -> &wgpu::Device {
479 &self.runtime.device
480 }
481
482 pub fn get_queue(&self) -> &wgpu::Queue {
484 &self.runtime.queue
485 }
486
487 pub fn get_adapter(&self) -> &wgpu::Adapter {
489 &self.adapter
490 }
491
492 pub fn get_adapter_owned(&self) -> Arc<wgpu::Adapter> {
494 self.adapter.clone()
495 }
496
497 pub fn get_render_target(&self) -> &RenderTarget {
499 &self.render_target
500 }
501
502 pub fn get_next_frame(&self) -> Result<Frame, ContextError> {
514 Ok(Frame {
515 runtime: self.runtime.clone(),
516 surface: match &self.render_target.0 {
517 RenderTargetInner::Surface { surface, .. } => {
518 let surface_texture = surface.get_current_texture().context(SurfaceSnafu)?;
519 FrameSurface::Surface(surface_texture)
520 }
521 RenderTargetInner::Texture { texture, .. } => {
522 FrameSurface::Texture(texture.clone())
523 }
524 },
525 })
526 }
527
528 pub fn set_default_atlas_texture_size(&self, size: impl Into<UVec3>) -> &Self {
534 let size = size.into();
535 let size = wgpu::Extent3d {
536 width: size.x,
537 height: size.y,
538 depth_or_array_layers: size.z,
539 };
540 crate::atlas::check_size(size);
541 self.stage_config
542 .write()
543 .expect("stage_config write")
544 .atlas_size = size;
545 self
546 }
547
548 pub fn with_default_atlas_texture_size(self, size: impl Into<UVec3>) -> Self {
557 self.set_default_atlas_texture_size(size);
558 self
559 }
560
561 pub fn set_shadow_mapping_atlas_texture_size(&self, size: impl Into<UVec3>) -> &Self {
567 let size = size.into();
568 let size = wgpu::Extent3d {
569 width: size.x,
570 height: size.y,
571 depth_or_array_layers: size.z,
572 };
573 crate::atlas::check_size(size);
574 self.stage_config
575 .write()
576 .expect("stage_config write")
577 .shadow_map_atlas_size = size;
578 self
579 }
580
581 pub fn with_shadow_mapping_atlas_texture_size(self, size: impl Into<UVec3>) -> Self {
590 self.set_shadow_mapping_atlas_texture_size(size);
591 self
592 }
593
594 pub fn set_use_direct_draw(&self, use_direct_drawing: bool) {
601 self.stage_config
602 .write()
603 .expect("stage_config write")
604 .use_compute_culling = !use_direct_drawing;
605 }
606
607 pub fn with_use_direct_draw(self, use_direct_drawing: bool) -> Self {
614 self.set_use_direct_draw(use_direct_drawing);
615 self
616 }
617
618 pub fn get_use_direct_draw(&self) -> bool {
620 !self
621 .stage_config
622 .read()
623 .expect("stage_config read")
624 .use_compute_culling
625 }
626
627 pub fn new_stage(&self) -> Stage {
629 Stage::new(self)
630 }
631}