1use std::sync::Arc;
2
3use wgpu::{PipelineCache, Surface};
4use winit::dpi::PhysicalSize;
5
6use crate::{
7 runner::Handle, utils::{ArcMut, ArcRef}, window::Window
8};
9
10use pipeline::{
11 render::RenderPipelineBuilder,
12 compute::ComputePipelineBuilder,
13 manager::PipelineManager,
14};
15
16use shader::{
17 bind_group_manager::{BindGroupManager, BindGroupCreateInfo},
18 graphics::GraphicsShaderBuilder,
19 compute::ComputeShaderBuilder,
20};
21
22use command::{
23 CommandBuffer, CommandBufferBuildError,
24 SurfaceTexture,
25 drawing::DrawingGlobalState
26};
27
28use texture::{
29 TextureBuilder, TextureFormat,
30 atlas::TextureAtlasBuilder
31};
32
33use pipeline::manager::{ComputePipelineDesc, GraphicsPipelineDesc};
34
35use buffer::{
36 BufferBuilder,
37 staging_buffer::StagingBuffer,
38};
39
40pub mod buffer;
41pub mod command;
42pub mod pipeline;
43pub mod shader;
44pub mod texture;
45
46pub fn new<'a>(window: Option<&'a mut crate::window::Window>) -> GPUBuilder<'a> {
51 let builder = GPUBuilder::new();
52
53 if let Some(window) = window {
54 builder.set_window(window)
55 } else {
56 builder
57 }
58}
59
60pub fn query_gpu_adapter(window: Option<&crate::window::Window>) -> Vec<GPUAdapter> {
67 let mut window_arc = None;
68 if let Some(window) = window {
69 window_arc = Some(
70 window
71 .inner
72 .borrow()
73 .window_pointer
74 .as_ref()
75 .unwrap()
76 .clone(),
77 );
78 }
79
80 GPU::query_gpu(window_arc)
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum AdapterBackend {
85 None,
86 Vulkan,
87 Metal,
88 Dx12,
89 Gl,
90 BrowserWebGpu,
91}
92
93#[derive(Clone, Debug)]
94pub enum GPUWaitType {
95 Wait,
96 Poll,
97}
98
99#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
100pub(crate) enum SwapchainError {
101 NotAvailable,
102 ConfigNeeded,
103 DeviceLost,
104 Suboptimal(wgpu::SurfaceTexture),
105}
106
107impl std::fmt::Display for SwapchainError {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 match self {
110 SwapchainError::NotAvailable => write!(f, "Swapchain not available"),
111 SwapchainError::ConfigNeeded => write!(f, "Swapchain config needed"),
112 SwapchainError::DeviceLost => write!(f, "Device lost"),
113 SwapchainError::Suboptimal(_) => write!(f, "Swapchain suboptimal"),
114 }
115 }
116}
117
118#[derive(Debug, Clone)]
119pub struct GPUAdapter {
120 pub name: String,
121 pub vendor: String,
122 pub vendor_id: u32,
123
124 pub backend: String,
125 pub backend_enum: AdapterBackend,
126 pub is_high_performance: bool,
127}
128
129#[derive(Debug, Clone)]
130pub struct GPU {
131 pub(crate) inner: ArcRef<GPUInner>,
132}
133
134impl GPU {
135 pub(crate) async fn new(
136 window: ArcMut<Handle>,
137 adapter: Option<&GPUAdapter>,
138 limits: Option<Limits>,
139 ) -> Result<GPU, String> {
140 let inner = ArcRef::new(GPUInner::new(window, adapter, limits).await?);
141
142 Ok(GPU { inner })
143 }
144
145 pub(crate) async fn new_headless(
146 adapter: Option<&GPUAdapter>,
147 limits: Option<Limits>,
148 ) -> Result<GPU, String> {
149 let inner = ArcRef::new(GPUInner::new_headless(adapter, limits).await?);
150
151 Ok(GPU { inner })
152 }
153
154 pub(crate) fn query_gpu(window: Option<ArcMut<Handle>>) -> Vec<GPUAdapter> {
155 let adapter = GPUInner::query_gpu(window);
156
157 adapter
158 .into_iter()
159 .map(|adapter| {
160 let info = adapter.get_info();
161
162 let vendor_name = match info.vendor {
163 0x1002 => "AMD",
164 0x10DE => "NVIDIA",
165 0x8086 => "Intel",
166 0x13B5 => "ARM",
167 _ => "Unknown",
168 };
169
170 let backend_string = match info.backend {
171 wgpu::Backend::Vulkan => "Vulkan",
172 wgpu::Backend::Metal => "Metal",
173 wgpu::Backend::Dx12 => "DirectX 12",
174 wgpu::Backend::Gl => "OpenGL",
175 wgpu::Backend::BrowserWebGpu => "WebGPU",
176 _ => "Unknown",
177 };
178
179 let is_high_performance = matches!(info.device_type, wgpu::DeviceType::DiscreteGpu);
180
181 let backend = match info.backend {
182 wgpu::Backend::Vulkan => AdapterBackend::Vulkan,
183 wgpu::Backend::Metal => AdapterBackend::Metal,
184 wgpu::Backend::Dx12 => AdapterBackend::Dx12,
185 wgpu::Backend::Gl => AdapterBackend::Gl,
186 wgpu::Backend::BrowserWebGpu => AdapterBackend::BrowserWebGpu,
187 _ => AdapterBackend::None,
188 };
189
190 GPUAdapter {
191 name: info.name,
192 vendor: vendor_name.to_string(),
193 vendor_id: info.vendor,
194
195 backend: backend_string.to_string(),
196 backend_enum: backend,
197 is_high_performance,
198 }
199 })
200 .collect()
201 }
202
203 pub fn swapchain_format(&self) -> TextureFormat {
205 let inner = self.inner.borrow();
206 let format = inner.config.as_ref().unwrap().format;
207
208 format.into()
209 }
210
211 pub fn set_vsync(&mut self, vsync: bool) {
213 let mut inner = self.inner.borrow_mut();
214 inner.set_vsync(vsync);
215 }
216
217 pub fn is_vsync(&self) -> bool {
219 let inner = self.inner.borrow();
220 inner.is_vsync()
221 }
222
223 pub fn is_surface_srgb(&self) -> bool {
227 let inner = self.inner.borrow();
228 inner.is_srgb()
229 }
230
231 pub fn set_panic_callback<F>(&mut self, _callback: F)
232 where
233 F: Fn(&str) + Send + Sync + 'static,
234 {
235 }
237
238 pub fn begin_command(&mut self) -> Result<CommandBuffer, CommandBufferBuildError> {
240 CommandBuffer::new(self.inner.clone())
241 }
242
243 pub fn begin_command_with_surface(
248 &mut self,
249 surface: SurfaceTexture,
250 ) -> Result<CommandBuffer, CommandBufferBuildError> {
251 CommandBuffer::new_with_surface(
252 self.inner.clone(),
253 surface,
254 )
255 }
256
257 pub fn create_texture(&mut self) -> TextureBuilder {
259 TextureBuilder::new(self.inner.clone())
260 }
261
262 pub fn create_texture_atlas(&mut self) -> TextureAtlasBuilder {
264 TextureAtlasBuilder::new(self.inner.clone())
265 }
266
267 pub fn create_graphics_shader(&mut self) -> GraphicsShaderBuilder {
269 GraphicsShaderBuilder::new(self.inner.clone())
270 }
271
272 pub fn create_compute_shader(&mut self) -> ComputeShaderBuilder {
274 ComputeShaderBuilder::new(self.inner.clone())
275 }
276
277 pub fn create_buffer<T: bytemuck::Pod + bytemuck::Zeroable>(
279 &mut self,
280 ) -> BufferBuilder<T> {
281 BufferBuilder::new(self.inner.clone())
282 }
283
284 pub fn create_render_pipeline(&mut self) -> RenderPipelineBuilder {
286 RenderPipelineBuilder::new(self.inner.clone())
287 }
288
289 pub fn create_compute_pipeline(&mut self) -> ComputePipelineBuilder {
291 ComputePipelineBuilder::new(self.inner.clone())
292 }
293
294 pub fn wait(&mut self, wait_type: GPUWaitType) {
296 let inner = self.inner.borrow();
297 let poll_type = match wait_type {
298 GPUWaitType::Wait => wgpu::PollType::Wait,
299 GPUWaitType::Poll => wgpu::PollType::Poll,
300 };
301
302 _ = inner.device().poll(poll_type);
303 }
304}
305
306#[derive(Clone, Debug)]
307pub struct Limits {
308 pub max_texture_dimension_1d: u32,
309 pub max_texture_dimension_2d: u32,
310 pub max_texture_dimension_3d: u32,
311 pub max_texture_array_layers: u32,
312 pub max_bind_groups: u32,
313 pub max_bindings_per_bind_group: u32,
314 pub max_dynamic_uniform_buffers_per_pipeline_layout: u32,
315 pub max_dynamic_storage_buffers_per_pipeline_layout: u32,
316 pub max_sampled_textures_per_shader_stage: u32,
317 pub max_samplers_per_shader_stage: u32,
318 pub max_storage_buffers_per_shader_stage: u32,
319 pub max_storage_textures_per_shader_stage: u32,
320 pub max_uniform_buffers_per_shader_stage: u32,
321 pub max_binding_array_elements_per_shader_stage: u32,
322 pub max_binding_array_sampler_elements_per_shader_stage: u32,
323 pub max_uniform_buffer_binding_size: u32,
324 pub max_storage_buffer_binding_size: u32,
325 pub max_vertex_buffers: u32,
326 pub max_buffer_size: u64,
327 pub max_vertex_attributes: u32,
328 pub max_vertex_buffer_array_stride: u32,
329 pub min_uniform_buffer_offset_alignment: u32,
330 pub min_storage_buffer_offset_alignment: u32,
331 pub max_inter_stage_shader_components: u32,
332 pub max_color_attachments: u32,
333 pub max_color_attachment_bytes_per_sample: u32,
334 pub max_compute_workgroup_storage_size: u32,
335 pub max_compute_invocations_per_workgroup: u32,
336 pub max_compute_workgroup_size_x: u32,
337 pub max_compute_workgroup_size_y: u32,
338 pub max_compute_workgroup_size_z: u32,
339 pub max_compute_workgroups_per_dimension: u32,
340 pub min_subgroup_size: u32,
341 pub max_subgroup_size: u32,
342 pub max_push_constant_size: u32,
343 pub max_non_sampler_bindings: u32,
344}
345
346impl Default for Limits {
347 fn default() -> Self {
348 Self {
349 max_texture_dimension_1d: 8192,
350 max_texture_dimension_2d: 8192,
351 max_texture_dimension_3d: 2048,
352 max_texture_array_layers: 256,
353 max_bind_groups: 4,
354 max_bindings_per_bind_group: 1000,
355 max_dynamic_uniform_buffers_per_pipeline_layout: 8,
356 max_dynamic_storage_buffers_per_pipeline_layout: 4,
357 max_sampled_textures_per_shader_stage: 16,
358 max_samplers_per_shader_stage: 16,
359 max_storage_buffers_per_shader_stage: 8,
360 max_storage_textures_per_shader_stage: 4,
361 max_uniform_buffers_per_shader_stage: 12,
362 max_binding_array_elements_per_shader_stage: 0,
363 max_binding_array_sampler_elements_per_shader_stage: 0,
364 max_uniform_buffer_binding_size: 64 << 10, max_storage_buffer_binding_size: 128 << 20, max_vertex_buffers: 8,
367 max_buffer_size: 256 << 20, max_vertex_attributes: 16,
369 max_vertex_buffer_array_stride: 2048,
370 min_uniform_buffer_offset_alignment: 256,
371 min_storage_buffer_offset_alignment: 256,
372 max_inter_stage_shader_components: 60,
373 max_color_attachments: 8,
374 max_color_attachment_bytes_per_sample: 32,
375 max_compute_workgroup_storage_size: 16384,
376 max_compute_invocations_per_workgroup: 256,
377 max_compute_workgroup_size_x: 256,
378 max_compute_workgroup_size_y: 256,
379 max_compute_workgroup_size_z: 64,
380 max_compute_workgroups_per_dimension: 65535,
381 min_subgroup_size: 0,
382 max_subgroup_size: 0,
383 max_push_constant_size: 0,
384 max_non_sampler_bindings: 1_000_000,
385 }
386 }
387}
388
389pub struct GPUBuilder<'a> {
390 window: Option<&'a mut Window>,
391 adapter: Option<&'a GPUAdapter>,
392 limits: Option<Limits>,
393}
394
395impl<'a> GPUBuilder<'a> {
396 pub(crate) fn new() -> Self {
397 GPUBuilder {
398 window: None,
399 adapter: None,
400 limits: None,
401 }
402 }
403
404 pub fn set_window(mut self, window: &'a mut Window) -> Self {
409 self.window = Some(window);
410 self
411 }
412
413 pub fn set_adapter(mut self, adapter: &'a GPUAdapter) -> Self {
418 self.adapter = Some(adapter);
419 self
420 }
421
422 pub fn set_limits(mut self, limits: Limits) -> Self {
423 self.limits = Some(limits);
424 self
425 }
426
427 pub fn build(self) -> Result<GPU, String> {
428 let gpu;
429
430 if self.window.is_some() {
431 let window_ref = self.window.unwrap();
432 let mut window_inner = window_ref.inner.borrow_mut();
433
434 #[cfg(feature = "software")]
435 if window_inner.pixelbuffer.is_some() {
436 return Err(
437 "GPU cannot be created along side PixelBuffer (software rendering)".to_string(),
438 );
439 }
440
441 let window_cloned = window_inner.window_pointer.as_ref().unwrap().clone();
442
443 gpu = futures::executor::block_on(GPU::new(window_cloned, self.adapter, self.limits))?;
444
445 window_inner.graphics = Some(gpu.inner.clone());
446 } else {
447 gpu = futures::executor::block_on(GPU::new_headless(self.adapter, self.limits))?;
448 }
449
450 Ok(gpu)
451 }
452}
453
454lazy_static::lazy_static! {
455 pub(crate) static ref INSTANCE_ID: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
456}
457
458#[allow(unused)]
459#[derive(Debug, Clone)]
460pub(crate) struct GPUInner {
461 pub is_invalid: bool,
462 pub instance_id: usize,
463
464 pub instance: Option<wgpu::Instance>,
465 pub window: Option<ArcMut<Handle>>,
466 pub surface: Option<Arc<Surface<'static>>>,
467
468 pub device: Option<wgpu::Device>,
469 pub queue: Option<wgpu::Queue>,
470 pub adapter: Option<wgpu::Adapter>,
471 pub config: Option<wgpu::SurfaceConfiguration>,
472 pub pipeline_cache: Option<PipelineCache>,
473
474 pub pipeline_manager: Option<PipelineManager>,
475 pub bind_group_manager: Option<BindGroupManager>,
476 pub staging_buffer: Option<StagingBuffer>,
477
478 pub drawing_state: Option<ArcRef<DrawingGlobalState>>,
479}
480
481#[allow(unused)]
482impl GPUInner {
483 pub fn query_gpu(window: Option<ArcMut<Handle>>) -> Vec<wgpu::Adapter> {
484 let instance_descriptor = wgpu::InstanceDescriptor {
485 backends: wgpu::Backends::PRIMARY,
486 ..Default::default()
487 };
488
489 let instance = wgpu::Instance::new(&instance_descriptor);
490
491 if let Some(window) = window {
492 let window = window.lock();
493
494 if window.is_closed() {
495 panic!("Window is closed");
496 }
497
498 let surface = instance.create_surface(window.get_window());
499 let surface = surface.unwrap();
500
501 let adapter = instance.enumerate_adapters(wgpu::Backends::PRIMARY);
502 let mut result = Vec::new();
503
504 for adapter in adapter {
505 if adapter.is_surface_supported(&surface) {
506 result.push(adapter);
507 }
508 }
509
510 result
511 } else {
512 instance.enumerate_adapters(wgpu::Backends::PRIMARY)
513 }
514 }
515
516 pub async fn new(
517 window: ArcMut<Handle>,
518 adapter: Option<&GPUAdapter>,
519 limits: Option<Limits>,
520 ) -> Result<Self, String> {
521 let mut window_lock = window.lock();
522
523 if window_lock.is_closed() {
524 return Err("Window is closed".to_string());
525 }
526
527 if window_lock.is_pinned() {
528 return Err("Window is already pinned to existing softbuffer/gpu".to_string());
529 }
530
531 let mut instance = Self::new_headless(adapter.clone(), limits).await?;
532
533 let surface = instance
534 .instance
535 .as_ref()
536 .unwrap()
537 .create_surface(Arc::clone(window_lock.get_window()));
538
539 if let Err(e) = surface {
540 return Err(format!("Failed to create surface: {:?}", e));
541 }
542
543 let surface = surface.unwrap();
544 let surface_capabilities = surface.get_capabilities(instance.adapter.as_ref().unwrap());
545 let surface_format = surface_capabilities
546 .formats
547 .iter()
548 .copied()
549 .find(|f| f.is_srgb())
550 .unwrap_or(surface_capabilities.formats[0]);
551
552 let config = wgpu::SurfaceConfiguration {
553 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
554 format: surface_format,
555 width: 0,
556 height: 0,
557 present_mode: surface_capabilities.present_modes[0],
558 view_formats: vec![],
559 alpha_mode: surface_capabilities.alpha_modes[0],
560 desired_maximum_frame_latency: 2,
561 };
562
563 window_lock.set_pinned(true);
564
565 drop(window_lock);
566
567 instance.surface = Some(Arc::new(surface));
568 instance.window = Some(window);
569 instance.config = Some(config);
570
571 Ok(instance)
572 }
573
574 pub async fn new_headless(
575 adapter: Option<&GPUAdapter>,
576 limits: Option<Limits>,
577 ) -> Result<Self, String> {
578 let instance_descriptor = wgpu::InstanceDescriptor {
579 backends: wgpu::Backends::PRIMARY,
580 ..Default::default()
581 };
582
583 let instance = wgpu::Instance::new(&instance_descriptor);
584
585 let adapter = {
586 if adapter.is_none() {
587 let adapter_descriptor = wgpu::RequestAdapterOptionsBase {
588 power_preference: wgpu::PowerPreference::default(),
589 compatible_surface: None,
590 force_fallback_adapter: false,
591 };
592
593 let adapter = instance.request_adapter(&adapter_descriptor).await;
594
595 if adapter.is_err() {
596 return Err(format!("Failed to request adapter: {:?}", adapter.err()));
597 }
598
599 adapter.unwrap()
600 } else {
601 let gpu_adapter = adapter.unwrap();
602
603 let adapters = instance.enumerate_adapters(wgpu::Backends::PRIMARY);
605 let mut found = false;
606
607 let desired_backend = match gpu_adapter.backend_enum {
608 AdapterBackend::Vulkan => wgpu::Backend::Vulkan,
609 AdapterBackend::Metal => wgpu::Backend::Metal,
610 AdapterBackend::Dx12 => wgpu::Backend::Dx12,
611 AdapterBackend::Gl => wgpu::Backend::Gl,
612 AdapterBackend::BrowserWebGpu => wgpu::Backend::BrowserWebGpu,
613 AdapterBackend::None => wgpu::Backend::Noop,
614 };
615
616 let mut adapter = None;
617 for a in adapters {
618 let backend = a.get_info().backend;
619 if backend == desired_backend
620 && a.get_info().name == gpu_adapter.name
621 && a.get_info().vendor == gpu_adapter.vendor_id
622 {
623 adapter = Some(a);
624 found = true;
625 break;
626 }
627 }
628
629 if !found {
630 return Err("Adapter not found".to_string());
631 }
632
633 adapter.unwrap()
634 }
635 };
636
637 let mut device_descriptor = wgpu::DeviceDescriptor {
638 required_features: wgpu::Features::empty(),
639 required_limits: if cfg!(target_arch = "wasm32") {
640 wgpu::Limits::downlevel_webgl2_defaults()
641 } else {
642 wgpu::Limits::default()
643 },
644 label: Some("Device"),
645 memory_hints: Default::default(),
646 ..Default::default()
647 };
648
649 if limits.is_some() {
650 let limits = limits.unwrap();
651 let wgpu_limits = wgpu::Limits {
652 max_texture_dimension_1d: limits.max_texture_dimension_1d,
653 max_texture_dimension_2d: limits.max_texture_dimension_2d,
654 max_texture_dimension_3d: limits.max_texture_dimension_3d,
655 max_texture_array_layers: limits.max_texture_array_layers,
656 max_bind_groups: limits.max_bind_groups,
657 max_bindings_per_bind_group: limits.max_bindings_per_bind_group,
658 max_dynamic_uniform_buffers_per_pipeline_layout: limits
659 .max_dynamic_uniform_buffers_per_pipeline_layout,
660 max_dynamic_storage_buffers_per_pipeline_layout: limits
661 .max_dynamic_storage_buffers_per_pipeline_layout,
662 max_sampled_textures_per_shader_stage: limits.max_sampled_textures_per_shader_stage,
663 max_samplers_per_shader_stage: limits.max_samplers_per_shader_stage,
664 max_storage_buffers_per_shader_stage: limits.max_storage_buffers_per_shader_stage,
665 max_storage_textures_per_shader_stage: limits.max_storage_textures_per_shader_stage,
666 max_uniform_buffers_per_shader_stage: limits.max_uniform_buffers_per_shader_stage,
667 max_binding_array_elements_per_shader_stage: limits
668 .max_binding_array_elements_per_shader_stage,
669 max_binding_array_sampler_elements_per_shader_stage: limits
670 .max_binding_array_sampler_elements_per_shader_stage,
671 max_uniform_buffer_binding_size: limits.max_uniform_buffer_binding_size,
672 max_storage_buffer_binding_size: limits.max_storage_buffer_binding_size,
673 max_vertex_buffers: limits.max_vertex_buffers,
674 max_buffer_size: limits.max_buffer_size,
675 max_vertex_attributes: limits.max_vertex_attributes,
676 max_vertex_buffer_array_stride: limits.max_vertex_buffer_array_stride,
677 min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment,
678 min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment,
679 max_inter_stage_shader_components: limits.max_inter_stage_shader_components,
680 max_color_attachments: limits.max_color_attachments,
681 max_color_attachment_bytes_per_sample: limits.max_color_attachment_bytes_per_sample,
682 max_compute_workgroup_storage_size: limits.max_compute_workgroup_storage_size,
683 max_compute_invocations_per_workgroup: limits.max_compute_invocations_per_workgroup,
684 max_compute_workgroup_size_x: limits.max_compute_workgroup_size_x,
685 max_compute_workgroup_size_y: limits.max_compute_workgroup_size_y,
686 max_compute_workgroup_size_z: limits.max_compute_workgroup_size_z,
687 max_compute_workgroups_per_dimension: limits.max_compute_workgroups_per_dimension,
688 min_subgroup_size: limits.min_subgroup_size,
689 max_subgroup_size: limits.max_subgroup_size,
690 max_push_constant_size: limits.max_push_constant_size,
691 max_non_sampler_bindings: limits.max_non_sampler_bindings,
692 };
693
694 device_descriptor.required_limits = wgpu_limits;
695 }
696
697 let mut optional_features = vec![
698 wgpu::Features::DEPTH32FLOAT_STENCIL8,
699 wgpu::Features::VERTEX_WRITABLE_STORAGE,
700 ];
701
702 #[cfg(not(target_arch = "wasm32"))]
703 {
704 optional_features.push(wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES);
705 }
706
707 for feature in optional_features.iter() {
708 if adapter.features().contains(*feature) {
709 device_descriptor.required_features |= *feature;
710 }
711 }
712
713 #[cfg(not(target_arch = "wasm32"))]
714 if adapter.get_info().backend == wgpu::Backend::Vulkan {
715 device_descriptor.required_features |=
716 wgpu::Features::PIPELINE_CACHE | wgpu::Features::PUSH_CONSTANTS;
717 }
718
719 let req_dev = adapter.request_device(&device_descriptor).await;
720
721 if req_dev.is_err() {
722 return Err(format!("Failed to request device: {:?}", req_dev.err()));
723 }
724
725 let (device, queue) = req_dev.unwrap();
726
727 let mut pipeline_cache: Option<PipelineCache> = None;
728
729 #[cfg(not(target_arch = "wasm32"))]
730 if adapter.get_info().backend == wgpu::Backend::Vulkan {
731 let path = std::env::current_exe().unwrap();
732 let path = path.parent().unwrap();
733
734 let data = std::fs::read(path.join("cache/pipeline_cache.wgpu")).unwrap_or_default();
735
736 let pipeline_cache_desc = wgpu::PipelineCacheDescriptor {
737 label: Some("Pipeline_cache"),
738 data: if data.len() > 0 {
739 Some(&data[..])
740 } else {
741 None
742 },
743 fallback: true,
744 };
745
746 pipeline_cache = Some(unsafe { device.create_pipeline_cache(&pipeline_cache_desc) });
747 }
748
749 let pipeline_manager = PipelineManager::new();
750 let bind_group_manager = BindGroupManager::new();
751 let staging_buffer = StagingBuffer::new();
752
753 let id = INSTANCE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
754
755 Ok(Self {
756 is_invalid: false,
757 instance_id: id,
758
759 instance: Some(instance),
760 window: None,
761 surface: None,
762 config: None,
763
764 device: Some(device),
765 queue: Some(queue),
766 adapter: Some(adapter),
767 pipeline_cache,
768 pipeline_manager: Some(pipeline_manager),
769 bind_group_manager: Some(bind_group_manager),
770 staging_buffer: Some(staging_buffer),
771
772 drawing_state: None,
773 })
774 }
775
776 pub fn cycle(&mut self) {
777 if self.is_invalid {
778 panic!("Invalid GPU context");
779 }
780
781 if let Some(ref mut pipeline_manager) = self.pipeline_manager {
782 pipeline_manager.cycle();
783 }
784
785 if let Some(ref mut bind_group_manager) = self.bind_group_manager {
786 bind_group_manager.cycle();
787 }
788
789 if let Some(ref mut staging_buffer) = self.staging_buffer {
790 staging_buffer.cycle();
791 }
792 }
793
794 pub fn is_srgb(&self) -> bool {
795 if self.is_invalid {
796 panic!("Invalid GPU context");
797 }
798
799 if self.config.is_none() {
800 panic!("GPU config not initialized");
801 }
802
803 self.config.as_ref().unwrap().format.is_srgb()
804 }
805
806 pub fn is_vsync(&self) -> bool {
807 if self.is_invalid {
808 panic!("Invalid GPU context");
809 }
810
811 if self.config.is_none() {
812 panic!("GPU config not initialized");
813 }
814
815 self.config.as_ref().unwrap().present_mode == wgpu::PresentMode::Fifo
816 }
817
818 pub fn get_swapchain(&self) -> Result<wgpu::SurfaceTexture, SwapchainError> {
819 if self.surface.is_none() {
820 return Err(SwapchainError::NotAvailable);
821 }
822
823 let config = self.config.as_ref().unwrap();
824 let surface = self.surface.as_ref().unwrap();
825
826 if config.width == 0 || config.height == 0 {
827 return Err(SwapchainError::ConfigNeeded);
828 }
829
830 let swapchain = surface.get_current_texture();
831 if swapchain.is_err() {
832 return Err(SwapchainError::DeviceLost);
833 }
834
835 let swapchain = swapchain.unwrap();
836
837 if swapchain.suboptimal {
838 return Err(SwapchainError::Suboptimal(swapchain));
839 } else {
840 return Ok(swapchain);
841 }
842 }
843
844 pub fn device(&self) -> &wgpu::Device {
845 if self.is_invalid {
846 panic!("Invalid GPU context");
847 }
848
849 self.device.as_ref().unwrap()
850 }
851
852 pub fn queue(&self) -> &wgpu::Queue {
853 if self.is_invalid {
854 panic!("Invalid GPU context");
855 }
856
857 self.queue.as_ref().unwrap()
858 }
859
860 pub fn surface(&self) -> &Surface<'static> {
861 if self.is_invalid {
862 panic!("Invalid GPU context");
863 }
864
865 self.surface.as_ref().unwrap()
866 }
867
868 pub fn limits(&self) -> wgpu::Limits {
869 if self.is_invalid {
870 panic!("Invalid GPU context");
871 }
872
873 self.device.as_ref().unwrap().limits()
874 }
875
876 pub fn cycle_manager(&mut self) {
877 if self.is_invalid {
878 return;
879 }
880
881 if let Some(ref mut pipeline_manager) = self.pipeline_manager {
882 pipeline_manager.cycle();
883 }
884
885 if let Some(ref mut bind_group_manager) = self.bind_group_manager {
886 bind_group_manager.cycle();
887 }
888 }
889
890 pub fn resize(&mut self, size: PhysicalSize<u32>) {
891 if self.is_invalid {
892 return;
893 }
894
895 if self.window.is_none() || self.surface.is_none() {
896 panic!("Graphics not initialized with window");
897 }
898
899 if size.width == 0 || size.height == 0 {
900 let config = self.config.as_mut().unwrap();
901 config.width = 0;
902 config.height = 0;
903 return;
904 }
905
906 let config = self.config.as_mut().unwrap();
907 if config.width == size.width && config.height == size.height {
908 return;
909 }
910
911 config.width = size.width;
912 config.height = size.height;
913
914 self.surface
915 .as_mut()
916 .unwrap()
917 .configure(self.device.as_ref().unwrap(), config);
918 }
919
920 pub fn set_vsync(&mut self, vsync: bool) {
921 if self.is_invalid {
922 return;
923 }
924
925 if self.window.is_none() || self.surface.is_none() {
926 panic!("Graphics not initialized with window");
927 }
928
929 let config = self.config.as_mut().unwrap();
930 config.present_mode = if vsync {
931 wgpu::PresentMode::Fifo
932 } else {
933 wgpu::PresentMode::Immediate
934 };
935
936 if config.width == 0 || config.height == 0 {
937 return;
938 }
939
940 self.surface
941 .as_mut()
942 .unwrap()
943 .configure(self.device.as_ref().unwrap(), config);
944 }
945
946 pub fn create_buffer(
947 &mut self,
948 size: wgpu::BufferAddress,
949 usage: wgpu::BufferUsages,
950 mapped_at_creation: bool,
951 ) -> wgpu::Buffer {
952 if self.is_invalid {
953 panic!("Invalid GPU context");
954 }
955
956 if size == 0 {
957 panic!("Buffer size must be greater than 0");
958 }
959
960 let buffer = self.internal_make_buffer(size, usage, mapped_at_creation);
961
962 buffer
963 }
964
965 pub fn create_buffer_with<T: bytemuck::Pod + bytemuck::Zeroable>(
966 &mut self,
967 data: &[T],
968 usage: wgpu::BufferUsages,
969 ) -> wgpu::Buffer {
970 if self.is_invalid {
971 panic!("Invalid GPU context");
972 }
973
974 if data.is_empty() {
975 panic!("Data slice cannot be empty");
976 }
977
978 let buffer = self.internal_make_buffer(
979 (data.len() * std::mem::size_of::<T>()) as wgpu::BufferAddress,
980 usage,
981 true,
982 );
983
984 let mut mapped_range = buffer.slice(..).get_mapped_range_mut();
985 let dst = &mut mapped_range[..data.len() * std::mem::size_of::<T>()];
986 dst.copy_from_slice(bytemuck::cast_slice(data));
987
988 drop(mapped_range);
989
990 buffer.unmap();
991
992 buffer
993 }
994
995 fn internal_make_buffer(
996 &mut self,
997 size: wgpu::BufferAddress,
998 usage: wgpu::BufferUsages,
999 mapped_at_creation: bool,
1000 ) -> wgpu::Buffer {
1001 if size == 0 {
1002 panic!("Buffer size must be greater than 0");
1003 }
1004
1005 let device = self.device.as_ref().unwrap();
1006
1007 let unaligned_size = wgpu::COPY_BUFFER_ALIGNMENT - 1;
1009 let size = ((size + unaligned_size) & !unaligned_size).max(wgpu::COPY_BUFFER_ALIGNMENT);
1010
1011 let buffer = device.create_buffer(&wgpu::BufferDescriptor {
1012 label: Some(
1013 format!("Internal Buffer, usage: {}, size: {}", usage.bits(), size).as_str(),
1014 ),
1015 size,
1016 usage,
1017 mapped_at_creation,
1018 });
1019
1020 buffer
1021 }
1022
1023 pub fn get_graphics_pipeline(&mut self, key: u64) -> Option<wgpu::RenderPipeline> {
1024 if self.is_invalid {
1025 panic!("Invalid GPU context");
1026 }
1027
1028 let pipeline_manager_ref = self.pipeline_manager.as_mut().unwrap();
1029
1030 pipeline_manager_ref.get_graphics_pipeline(key as usize)
1031 }
1032
1033 pub fn create_graphics_pipeline(
1034 &mut self,
1035 key: u64,
1036 desc: GraphicsPipelineDesc,
1037 ) -> wgpu::RenderPipeline {
1038 if self.is_invalid {
1039 panic!("Invalid GPU context");
1040 }
1041
1042 let device_ref = self.device.as_ref().unwrap();
1043 let pipeline_manager_ref = self.pipeline_manager.as_mut().unwrap();
1044
1045 pipeline_manager_ref.create_graphics_pipeline(
1046 key as usize,
1047 device_ref,
1048 self.pipeline_cache.as_ref(),
1049 desc,
1050 )
1051 }
1052
1053 pub fn get_compute_pipeline(&mut self, key: u64) -> Option<wgpu::ComputePipeline> {
1054 if self.is_invalid {
1055 panic!("Invalid GPU context");
1056 }
1057
1058 let pipeline_manager_ref = self.pipeline_manager.as_mut().unwrap();
1059
1060 pipeline_manager_ref.get_compute_pipeline(key as usize)
1061 }
1062
1063 pub fn create_compute_pipeline(
1064 &mut self,
1065 key: u64,
1066 desc: ComputePipelineDesc,
1067 ) -> wgpu::ComputePipeline {
1068 if self.is_invalid {
1069 panic!("Invalid GPU context");
1070 }
1071
1072 let device_ref = self.device.as_ref().unwrap();
1073 let pipeline_manager_ref = self.pipeline_manager.as_mut().unwrap();
1074
1075 pipeline_manager_ref.create_compute_pipeline(
1076 key as usize,
1077 device_ref,
1078 self.pipeline_cache.as_ref(),
1079 desc,
1080 )
1081 }
1082
1083 pub fn create_bind_group(
1084 &mut self,
1085 key: u64,
1086 attachment: BindGroupCreateInfo,
1087 ) -> Vec<(u32, wgpu::BindGroup)> {
1088 if self.is_invalid {
1089 panic!("Invalid GPU context");
1090 }
1091
1092 let device_ref = self.device.as_ref().unwrap();
1093 let bind_group_manager_ref = self.bind_group_manager.as_mut().unwrap();
1094
1095 bind_group_manager_ref.create(key as usize, device_ref, attachment)
1096 }
1097
1098 pub fn get_bind_group(&mut self, key: u64) -> Option<Vec<(u32, wgpu::BindGroup)>> {
1099 if self.is_invalid {
1100 panic!("Invalid GPU context");
1101 }
1102
1103 let bind_group_manager_ref = self.bind_group_manager.as_mut().unwrap();
1104
1105 bind_group_manager_ref.get(key as usize)
1106 }
1107
1108 pub fn create_staging_buffer(
1109 &mut self,
1110 data: &[u8],
1111 usage: wgpu::BufferUsages,
1112 ) -> wgpu::Buffer {
1113 if self.is_invalid {
1114 panic!("Invalid GPU context");
1115 }
1116
1117 let device = self.device.as_ref().unwrap();
1118 let queue = self.queue.as_ref().unwrap();
1119 let staging_buffer_ref = self.staging_buffer.as_mut().unwrap();
1120
1121 staging_buffer_ref.allocate(device, queue, data, usage)
1122 }
1123}
1124
1125impl Drop for GPUInner {
1126 fn drop(&mut self) {
1127 #[cfg(not(target_arch = "wasm32"))]
1128 if let Some(pipeline_cache) = &self.pipeline_cache {
1129 let data = pipeline_cache.get_data();
1130 if let Some(data) = data {
1131 let path = std::env::current_exe().unwrap();
1132 let path = path.parent().unwrap();
1133
1134 std::fs::create_dir_all(path.join("cache")).unwrap();
1135 let pipeline_cache_path = path.join("cache/pipeline_cache.wgpu");
1136
1137 std::fs::write(&pipeline_cache_path, data).unwrap();
1138
1139 crate::dbg_log!("Saving pipeline cache to {:?}", pipeline_cache_path);
1140 }
1141 }
1142
1143 crate::dbg_log!("GPU destroyed");
1144 }
1145}
1146
1147impl PartialEq for GPUInner {
1148 fn eq(&self, other: &Self) -> bool {
1149 self.device == other.device
1150 && self.queue == other.queue
1151 && self.adapter == other.adapter
1152 && self.config == other.config
1153 && self.pipeline_cache == other.pipeline_cache
1154 && self.pipeline_manager == other.pipeline_manager
1155 && self.bind_group_manager == other.bind_group_manager
1156 }
1157}