est_render/gpu/
mod.rs

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
46/// Creates a new [GPU] instance.
47///
48/// This is thread-safe and can be called from any thread, except when using
49/// the [GPUBuilder::set_window] method, which binds the GPU to the window's thread.
50pub 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
60/// Queries the available GPU's [GPUAdapter].
61///
62/// This is useful for checking the available GPU adapters on the system and the supported \
63/// graphics APIs, allowing you to choose the best GPU and graphics API for your application.
64///
65/// This function can be called from any thread.
66pub 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    /// Return the swapchain's format.
204    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    /// Set the swapchain vsync.
212    pub fn set_vsync(&mut self, vsync: bool) {
213        let mut inner = self.inner.borrow_mut();
214        inner.set_vsync(vsync);
215    }
216
217    /// Returns the vsync setting of the swapchain.
218    pub fn is_vsync(&self) -> bool {
219        let inner = self.inner.borrow();
220        inner.is_vsync()
221    }
222
223    /// Check if the swapchain is using sRGB format.
224    ///
225    /// This is useful for determining if you want to use sRGB textures or not.
226    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        // self.inner.borrow().set_panic_callback(callback);
236    }
237
238    /// Begins a new command buffer.
239    pub fn begin_command(&mut self) -> Result<CommandBuffer, CommandBufferBuildError> {
240        CommandBuffer::new(self.inner.clone())
241    }
242
243    /// Begins a new command buffer with a surface texture.
244    ///
245    /// This is useful if you reuse the surface texture from previous command buffer, but
246    /// not yet presented to the screen.
247    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    /// Create a new texture.
258    pub fn create_texture(&mut self) -> TextureBuilder {
259        TextureBuilder::new(self.inner.clone())
260    }
261
262    /// Create a new texture atlas.
263    pub fn create_texture_atlas(&mut self) -> TextureAtlasBuilder {
264        TextureAtlasBuilder::new(self.inner.clone())
265    }
266
267    /// Create a new graphics shader.
268    pub fn create_graphics_shader(&mut self) -> GraphicsShaderBuilder {
269        GraphicsShaderBuilder::new(self.inner.clone())
270    }
271
272    /// Create a new compute shader.
273    pub fn create_compute_shader(&mut self) -> ComputeShaderBuilder {
274        ComputeShaderBuilder::new(self.inner.clone())
275    }
276
277    /// Create a new buffer.
278    pub fn create_buffer<T: bytemuck::Pod + bytemuck::Zeroable>(
279        &mut self,
280    ) -> BufferBuilder<T> {
281        BufferBuilder::new(self.inner.clone())
282    }
283
284    /// Create a render pipeline.
285    pub fn create_render_pipeline(&mut self) -> RenderPipelineBuilder {
286        RenderPipelineBuilder::new(self.inner.clone())
287    }
288
289    /// Create a compute pipeline.
290    pub fn create_compute_pipeline(&mut self) -> ComputePipelineBuilder {
291        ComputePipelineBuilder::new(self.inner.clone())
292    }
293
294    /// Wait for the GPU to finish processing commands.
295    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, // (64 KiB)
365            max_storage_buffer_binding_size: 128 << 20, // (128 MiB)
366            max_vertex_buffers: 8,
367            max_buffer_size: 256 << 20, // (256 MiB)
368            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    /// Sets the window for this GPU instance.
405    ///
406    /// This is useful for creating a GPU instance that is bound to a specific window.
407    /// The window must be created before this GPU instance.
408    pub fn set_window(mut self, window: &'a mut Window) -> Self {
409        self.window = Some(window);
410        self
411    }
412
413    /// Sets the GPU adapter for this GPU instance.
414    ///
415    /// This is useful for creating a GPU instance that uses a specific GPU adapter.
416    /// The adapter can be queried using the `Engine::query_gpu_adapter` function.
417    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                // query again
604                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        // This is to honor vulkan's requirement that buffer sizes must be a multiple of COPY_BUFFER_ALIGNMENT.
1008        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}