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 ui::Ui,
19};
20
21pub use craballoc::runtime::WgpuRuntime;
22
23pub(crate) enum RenderTargetInner {
25 Surface {
26 surface: wgpu::Surface<'static>,
27 surface_config: wgpu::SurfaceConfiguration,
28 },
29 Texture {
30 texture: Arc<wgpu::Texture>,
31 },
32}
33
34#[repr(transparent)]
35pub struct RenderTarget(pub(crate) RenderTargetInner);
39
40impl From<wgpu::Texture> for RenderTarget {
42 fn from(value: wgpu::Texture) -> Self {
43 RenderTarget(RenderTargetInner::Texture {
44 texture: Arc::new(value),
45 })
46 }
47}
48
49impl RenderTarget {
50 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 {
158 Surface(wgpu::SurfaceTexture),
159 Texture(Arc<wgpu::Texture>),
160}
161
162pub struct Frame {
166 pub(crate) runtime: WgpuRuntime,
167 pub(crate) surface: FrameSurface,
168}
169
170impl Frame {
171 pub fn texture(&self) -> &wgpu::Texture {
173 match &self.surface {
174 FrameSurface::Surface(s) => &s.texture,
175 FrameSurface::Texture(t) => t,
176 }
177 }
178
179 pub fn view(&self) -> wgpu::TextureView {
181 let texture = self.texture();
182 let format = texture.format().add_srgb_suffix();
183 texture.create_view(&wgpu::TextureViewDescriptor {
184 label: Some("Frame::view"),
185 format: Some(format),
186 ..Default::default()
187 })
188 }
189
190 pub fn copy_to_buffer(&self, width: u32, height: u32) -> CopiedTextureBuffer {
192 let dimensions = BufferDimensions::new(4, 1, width as usize, height as usize);
193 let buffer = self.runtime.device.create_buffer(&wgpu::BufferDescriptor {
195 label: Some("RenderTarget::copy_to_buffer"),
196 size: (dimensions.padded_bytes_per_row * dimensions.height) as u64,
197 usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
198 mapped_at_creation: false,
199 });
200 let mut encoder =
201 self.runtime
202 .device
203 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
204 label: Some("post render screen capture encoder"),
205 });
206 let texture = self.texture();
207 encoder.copy_texture_to_buffer(
209 texture.as_image_copy(),
210 wgpu::TexelCopyBufferInfo {
211 buffer: &buffer,
212 layout: wgpu::TexelCopyBufferLayout {
213 offset: 0,
214 bytes_per_row: Some(dimensions.padded_bytes_per_row as u32),
215 rows_per_image: None,
216 },
217 },
218 wgpu::Extent3d {
219 width: dimensions.width as u32,
220 height: dimensions.height as u32,
221 depth_or_array_layers: 1,
222 },
223 );
224
225 self.runtime.queue.submit(std::iter::once(encoder.finish()));
226
227 CopiedTextureBuffer {
228 dimensions,
229 buffer,
230 format: texture.format(),
231 }
232 }
233
234 pub fn get_size(&self) -> UVec2 {
235 let s = self.texture().size();
236 UVec2::new(s.width, s.height)
237 }
238
239 pub async fn read_image(&self) -> Result<image::RgbaImage, TextureError> {
249 let size = self.get_size();
250 let buffer = self.copy_to_buffer(size.x, size.y);
251 let is_srgb = self.texture().format().is_srgb();
252 let img = if is_srgb {
253 buffer.into_srgba(&self.runtime.device).await?
254 } else {
255 buffer.into_linear_rgba(&self.runtime.device).await?
256 };
257 Ok(img)
258 }
259
260 pub async fn read_srgb_image(&self) -> Result<image::RgbaImage, TextureError> {
268 let size = self.get_size();
269 let buffer = self.copy_to_buffer(size.x, size.y);
270 log::trace!("read image has the format: {:?}", buffer.format);
271 buffer.into_srgba(&self.runtime.device).await
272 }
273 pub async fn read_linear_image(&self) -> Result<image::RgbaImage, TextureError> {
281 let size = self.get_size();
282 let buffer = self.copy_to_buffer(size.x, size.y);
283 buffer.into_linear_rgba(&self.runtime.device).await
284 }
285
286 pub fn present(self) {
289 match self.surface {
290 FrameSurface::Surface(s) => s.present(),
291 FrameSurface::Texture(_) => {}
292 }
293 }
294}
295
296#[derive(Debug, Clone, Copy)]
298pub(crate) struct GlobalStageConfig {
299 pub(crate) atlas_size: wgpu::Extent3d,
300 pub(crate) shadow_map_atlas_size: wgpu::Extent3d,
301 pub(crate) use_compute_culling: bool,
302}
303
304pub struct Context {
315 runtime: WgpuRuntime,
316 adapter: Arc<wgpu::Adapter>,
317 render_target: RenderTarget,
318 pub(crate) stage_config: Arc<RwLock<GlobalStageConfig>>,
319}
320
321impl AsRef<WgpuRuntime> for Context {
322 fn as_ref(&self) -> &WgpuRuntime {
323 &self.runtime
324 }
325}
326
327impl Context {
328 pub fn new(
330 target: RenderTarget,
331 adapter: impl Into<Arc<wgpu::Adapter>>,
332 device: impl Into<Arc<wgpu::Device>>,
333 queue: impl Into<Arc<wgpu::Queue>>,
334 ) -> Self {
335 let adapter: Arc<wgpu::Adapter> = adapter.into();
336 let limits = adapter.limits();
337 let w = limits
338 .max_texture_dimension_2d
339 .min(crate::atlas::ATLAS_SUGGESTED_SIZE);
340 let stage_config = Arc::new(RwLock::new(GlobalStageConfig {
341 atlas_size: wgpu::Extent3d {
342 width: w,
343 height: w,
344 depth_or_array_layers: adapter
345 .limits()
346 .max_texture_array_layers
347 .min(crate::atlas::ATLAS_SUGGESTED_LAYERS),
348 },
349 shadow_map_atlas_size: wgpu::Extent3d {
350 width: w,
351 height: w,
352 depth_or_array_layers: 4,
353 },
354 use_compute_culling: false,
355 }));
356 Self {
357 adapter,
358 runtime: WgpuRuntime {
359 device: device.into(),
360 queue: queue.into(),
361 },
362 render_target: target,
363 stage_config,
364 }
365 }
366
367 pub async fn try_new_headless(
369 width: u32,
370 height: u32,
371 backends: Option<wgpu::Backends>,
372 ) -> Result<Self, ContextError> {
373 log::trace!("creating headless context of size ({width}, {height})");
374 let instance = crate::internal::new_instance(backends);
375 let (adapter, device, queue, target) =
376 crate::internal::new_headless_device_queue_and_target(width, height, &instance).await?;
377 Ok(Self::new(target, adapter, device, queue))
378 }
379
380 pub async fn try_new_with_surface(
382 width: u32,
383 height: u32,
384 backends: Option<wgpu::Backends>,
385 window: impl Into<wgpu::SurfaceTarget<'static>>,
386 ) -> Result<Self, ContextError> {
387 let instance = crate::internal::new_instance(backends);
388 let (adapter, device, queue, target) =
389 crate::internal::new_windowed_adapter_device_queue(width, height, &instance, window)
390 .await?;
391 Ok(Self::new(target, adapter, device, queue))
392 }
393
394 #[cfg(feature = "winit")]
395 pub async fn from_winit_window(
400 backends: Option<wgpu::Backends>,
401 window: Arc<winit::window::Window>,
402 ) -> Self {
403 let inner_size = window.inner_size();
404 Self::try_new_with_surface(inner_size.width, inner_size.height, backends, window)
405 .await
406 .unwrap()
407 }
408
409 pub async fn headless(width: u32, height: u32) -> Self {
417 let result = Self::try_new_headless(width, height, None).await;
418 #[cfg(target_arch = "wasm32")]
419 {
420 use wasm_bindgen::UnwrapThrowExt;
421 result.expect_throw("Could not create context")
422 }
423 #[cfg(not(target_arch = "wasm32"))]
424 {
425 result.expect("Could not create context")
426 }
427 }
428
429 pub fn get_size(&self) -> UVec2 {
430 self.render_target.get_size()
431 }
432
433 pub fn set_size(&mut self, size: UVec2) {
435 self.render_target
436 .resize(size.x, size.y, &self.runtime.device);
437 }
438
439 pub fn create_texture<P>(
441 &self,
442 label: Option<&str>,
443 img: &image::ImageBuffer<P, Vec<u8>>,
444 ) -> Result<Texture, TextureError>
445 where
446 P: image::PixelWithColorType,
447 image::ImageBuffer<P, Vec<u8>>: image::GenericImage + std::ops::Deref<Target = [u8]>,
448 {
449 let name = label.unwrap_or("unknown");
450 Texture::from_image_buffer(
451 self,
452 img,
453 Some(&format!("Renderling::create_texture {}", name)),
454 None,
455 None,
456 )
457 }
458
459 pub fn texture_from_wgpu_tex(
461 &self,
462 texture: impl Into<Arc<wgpu::Texture>>,
463 sampler: Option<wgpu::SamplerDescriptor>,
464 ) -> Texture {
465 Texture::from_wgpu_tex(self.get_device(), texture, sampler, None)
466 }
467
468 pub fn runtime(&self) -> &WgpuRuntime {
470 &self.runtime
471 }
472
473 pub fn get_device(&self) -> &wgpu::Device {
475 &self.runtime.device
476 }
477
478 pub fn get_queue(&self) -> &wgpu::Queue {
480 &self.runtime.queue
481 }
482
483 pub fn get_adapter(&self) -> &wgpu::Adapter {
485 &self.adapter
486 }
487
488 pub fn get_adapter_owned(&self) -> Arc<wgpu::Adapter> {
490 self.adapter.clone()
491 }
492
493 pub fn get_render_target(&self) -> &RenderTarget {
495 &self.render_target
496 }
497
498 pub fn get_next_frame(&self) -> Result<Frame, ContextError> {
510 Ok(Frame {
511 runtime: self.runtime.clone(),
512 surface: match &self.render_target.0 {
513 RenderTargetInner::Surface { surface, .. } => {
514 let surface_texture = surface.get_current_texture().context(SurfaceSnafu)?;
515 FrameSurface::Surface(surface_texture)
516 }
517 RenderTargetInner::Texture { texture, .. } => {
518 FrameSurface::Texture(texture.clone())
519 }
520 },
521 })
522 }
523
524 pub fn set_default_atlas_texture_size(&self, size: impl Into<UVec3>) -> &Self {
530 let size = size.into();
531 let size = wgpu::Extent3d {
532 width: size.x,
533 height: size.y,
534 depth_or_array_layers: size.z,
535 };
536 crate::atlas::check_size(size);
537 self.stage_config.write().unwrap().atlas_size = size;
538 self
539 }
540
541 pub fn with_default_atlas_texture_size(self, size: impl Into<UVec3>) -> Self {
550 self.set_default_atlas_texture_size(size);
551 self
552 }
553
554 pub fn set_shadow_mapping_atlas_texture_size(&self, size: impl Into<UVec3>) -> &Self {
560 let size = size.into();
561 let size = wgpu::Extent3d {
562 width: size.x,
563 height: size.y,
564 depth_or_array_layers: size.z,
565 };
566 crate::atlas::check_size(size);
567 self.stage_config.write().unwrap().shadow_map_atlas_size = size;
568 self
569 }
570
571 pub fn with_shadow_mapping_atlas_texture_size(self, size: impl Into<UVec3>) -> Self {
580 self.set_shadow_mapping_atlas_texture_size(size);
581 self
582 }
583
584 pub fn set_use_direct_draw(&self, use_direct_drawing: bool) {
591 self.stage_config.write().unwrap().use_compute_culling = !use_direct_drawing;
592 }
593
594 pub fn with_use_direct_draw(self, use_direct_drawing: bool) -> Self {
601 self.set_use_direct_draw(use_direct_drawing);
602 self
603 }
604
605 pub fn get_use_direct_draw(&self) -> bool {
607 !self.stage_config.read().unwrap().use_compute_culling
608 }
609
610 pub fn new_stage(&self) -> Stage {
612 Stage::new(self)
613 }
614
615 pub fn new_ui(&self) -> Ui {
617 Ui::new(self)
618 }
619}