gfx_backend_gl/
lib.rs

1/*!
2# OpenGL backend internals.
3
4The main target is OpenGL ES3 on Linux and Android.
5
6## Binding model
7
8Binding model is the biggest and most painful difference with gfx-hal API.
9
10First, the bindings get linearized into the pipeline layout (see `PipelineLayoutSet`).
11Then a pipeline gets created, and we track all the texture-sampler associations.
12We only support at most one sampler used with each texture so far. The linear index
13of this sampler is stored per texture slot in `SamplerBindMap` array.
14
15The texture-sampler pairs get potentially invalidated in 2 places:
16  - when a new pipeline is bound, we update the linear indices of associated samplers
17  - when a new descriptor set is bound, we update both the textures and the samplers
18
19We expect that the changes to sampler states between any 2 pipelines of the same layout
20will be minimal, if any.
21*/
22
23#![allow(missing_docs, missing_copy_implementations)]
24
25use std::{
26    cell::Cell,
27    collections::HashMap,
28    fmt,
29    hash::BuildHasherDefault,
30    ops::{Deref, Range},
31    sync::{Arc, Weak},
32    thread,
33};
34
35use hal::{adapter, buffer, display, image, memory, queue as q};
36
37pub use self::device::Device;
38pub use self::info::{Info, PlatformName, Version};
39
40mod command;
41mod conv;
42mod device;
43mod info;
44mod native;
45mod pool;
46mod queue;
47mod state;
48mod window;
49
50// Web implementation
51#[cfg(target_arch = "wasm32")]
52pub use window::web::{Instance, Surface, Swapchain};
53
54#[cfg(not(target_arch = "wasm32"))]
55pub use window::egl::{Instance, Surface, Swapchain};
56
57pub use glow::Context as GlContext;
58use glow::HasContext;
59
60type ColorSlot = u8;
61type FastHashMap<K, V> = HashMap<K, V, BuildHasherDefault<fxhash::FxHasher>>;
62
63// we can support more samplers if not every one of them is used at a time,
64// but it probably doesn't worth it.
65const MAX_SAMPLERS: usize = 16;
66//TODO: has to be within glow::MAX_COMBINED_TEXTURE_IMAGE_UNITS
67const MAX_TEXTURE_SLOTS: usize = 16;
68const MAX_COLOR_ATTACHMENTS: usize = 16;
69
70struct GlContainer {
71    context: GlContext,
72}
73
74impl Deref for GlContainer {
75    type Target = GlContext;
76    fn deref(&self) -> &GlContext {
77        &self.context
78    }
79}
80
81#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
82pub enum Backend {}
83
84impl hal::Backend for Backend {
85    type Instance = Instance;
86
87    type PhysicalDevice = PhysicalDevice;
88    type Device = Device;
89    type Surface = Surface;
90
91    type QueueFamily = QueueFamily;
92    type Queue = queue::Queue;
93    type CommandBuffer = command::CommandBuffer;
94
95    type Memory = native::Memory;
96    type CommandPool = pool::CommandPool;
97
98    type ShaderModule = native::ShaderModule;
99    type RenderPass = native::RenderPass;
100    type Framebuffer = native::Framebuffer;
101
102    type Buffer = native::Buffer;
103    type BufferView = native::BufferView;
104    type Image = native::Image;
105    type ImageView = native::ImageView;
106    type Sampler = native::FatSampler;
107
108    type ComputePipeline = native::ComputePipeline;
109    type GraphicsPipeline = native::GraphicsPipeline;
110    type PipelineLayout = native::PipelineLayout;
111    type PipelineCache = ();
112    type DescriptorSetLayout = native::DescriptorSetLayout;
113    type DescriptorPool = native::DescriptorPool;
114    type DescriptorSet = native::DescriptorSet;
115
116    type Fence = native::Fence;
117    type Semaphore = native::Semaphore;
118    type Event = ();
119    type QueryPool = ();
120
121    type Display = ();
122    type DisplayMode = ();
123}
124
125#[derive(Copy, Clone, Eq, PartialEq, Debug)]
126pub enum Error {
127    NoError,
128    InvalidEnum,
129    InvalidValue,
130    InvalidOperation,
131    InvalidFramebufferOperation,
132    OutOfMemory,
133    UnknownError,
134}
135
136impl Error {
137    pub fn from_error_code(error_code: u32) -> Error {
138        match error_code {
139            glow::NO_ERROR => Error::NoError,
140            glow::INVALID_ENUM => Error::InvalidEnum,
141            glow::INVALID_VALUE => Error::InvalidValue,
142            glow::INVALID_OPERATION => Error::InvalidOperation,
143            glow::INVALID_FRAMEBUFFER_OPERATION => Error::InvalidFramebufferOperation,
144            glow::OUT_OF_MEMORY => Error::OutOfMemory,
145            _ => Error::UnknownError,
146        }
147    }
148}
149
150fn debug_message_callback(source: u32, gltype: u32, id: u32, severity: u32, message: &str) {
151    let source_str = match source {
152        glow::DEBUG_SOURCE_API => "API",
153        glow::DEBUG_SOURCE_WINDOW_SYSTEM => "Window System",
154        glow::DEBUG_SOURCE_SHADER_COMPILER => "ShaderCompiler",
155        glow::DEBUG_SOURCE_THIRD_PARTY => "Third Party",
156        glow::DEBUG_SOURCE_APPLICATION => "Application",
157        glow::DEBUG_SOURCE_OTHER => "Other",
158        _ => unreachable!(),
159    };
160
161    let log_severity = match severity {
162        glow::DEBUG_SEVERITY_HIGH => log::Level::Error,
163        glow::DEBUG_SEVERITY_MEDIUM => log::Level::Warn,
164        glow::DEBUG_SEVERITY_LOW => log::Level::Info,
165        glow::DEBUG_SEVERITY_NOTIFICATION => log::Level::Trace,
166        _ => unreachable!(),
167    };
168
169    let type_str = match gltype {
170        glow::DEBUG_TYPE_DEPRECATED_BEHAVIOR => "Deprecated Behavior",
171        glow::DEBUG_TYPE_ERROR => "Error",
172        glow::DEBUG_TYPE_MARKER => "Marker",
173        glow::DEBUG_TYPE_OTHER => "Other",
174        glow::DEBUG_TYPE_PERFORMANCE => "Performance",
175        glow::DEBUG_TYPE_POP_GROUP => "Pop Group",
176        glow::DEBUG_TYPE_PORTABILITY => "Portability",
177        glow::DEBUG_TYPE_PUSH_GROUP => "Push Group",
178        glow::DEBUG_TYPE_UNDEFINED_BEHAVIOR => "Undefined Behavior",
179        _ => unreachable!(),
180    };
181
182    log::log!(
183        log_severity,
184        "[{}/{}] ID {} : {}",
185        source_str,
186        type_str,
187        id,
188        message
189    );
190}
191
192const DEVICE_LOCAL_HEAP: usize = 0;
193const CPU_VISIBLE_HEAP: usize = 1;
194
195/// Memory types in the OpenGL backend are either usable for buffers and are backed by a real OpenGL
196/// buffer, or are used for images and are fake and not backed by any real raw buffer.
197#[derive(Copy, Clone, Debug)]
198enum MemoryUsage {
199    Buffer(buffer::Usage),
200    Image,
201}
202
203/// Internal struct of shared data between the physical and logical device.
204struct Share {
205    context: GlContainer,
206    info: Info,
207    supported_features: hal::Features,
208    legacy_features: info::LegacyFeatures,
209    public_caps: hal::PhysicalDeviceProperties,
210    private_caps: info::PrivateCaps,
211    // Indicates if there is an active logical device.
212    open: Cell<bool>,
213    memory_types: Vec<(adapter::MemoryType, MemoryUsage)>,
214    texture_format_filter: info::TextureFormatFilter,
215}
216
217impl Share {
218    /// Fails during a debug build if the implementation's error flag was set.
219    fn check(&self) -> Result<(), Error> {
220        if cfg!(debug_assertions) {
221            let gl = &self.context;
222            let err = Error::from_error_code(unsafe { gl.get_error() });
223            if err != Error::NoError {
224                return Err(err);
225            }
226        }
227        Ok(())
228    }
229
230    fn buffer_memory_type_mask(&self, usage: buffer::Usage) -> u32 {
231        let mut type_mask = 0;
232        for (type_index, &(_, kind)) in self.memory_types.iter().enumerate() {
233            match kind {
234                MemoryUsage::Buffer(buffer_usage) => {
235                    if buffer_usage.contains(usage) {
236                        type_mask |= 1 << type_index;
237                    }
238                }
239                MemoryUsage::Image => {}
240            }
241        }
242        if type_mask == 0 {
243            log::error!(
244                "gl backend capability does not allow a buffer with usage {:?}",
245                usage
246            );
247        }
248        type_mask
249    }
250
251    fn image_memory_type_mask(&self) -> u32 {
252        let mut type_mask = 0;
253        for (type_index, &(_, kind)) in self.memory_types.iter().enumerate() {
254            match kind {
255                MemoryUsage::Buffer(_) => {}
256                MemoryUsage::Image => {
257                    type_mask |= 1 << type_index;
258                }
259            }
260        }
261        assert_ne!(type_mask, 0);
262        type_mask
263    }
264}
265
266/// Single-threaded `Arc`.
267/// Wrapper for `Arc` that allows you to `Send` it even if `T: !Sync`.
268/// Yet internal data cannot be accessed outside of the thread where it was created.
269pub struct Starc<T: ?Sized> {
270    arc: Arc<T>,
271    thread: thread::ThreadId,
272}
273
274impl<T: ?Sized> Clone for Starc<T> {
275    fn clone(&self) -> Self {
276        Self {
277            arc: self.arc.clone(),
278            thread: self.thread,
279        }
280    }
281}
282
283impl<T: ?Sized> fmt::Debug for Starc<T> {
284    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
285        write!(fmt, "{:p}@{:?}", self.arc, self.thread)
286    }
287}
288
289impl<T> Starc<T> {
290    #[inline]
291    fn new(value: T) -> Self {
292        Starc {
293            arc: Arc::new(value),
294            thread: thread::current().id(),
295        }
296    }
297    #[inline]
298    pub fn try_unwrap(self) -> Result<T, Self> {
299        let a = Arc::try_unwrap(self.arc);
300        let thread = self.thread;
301        a.map_err(|a| Starc {
302            arc: a,
303            thread: thread,
304        })
305    }
306}
307
308impl<T> Starc<T>
309where
310    T: ?Sized,
311{
312    #[inline]
313    pub fn downgrade(this: &Starc<T>) -> Wstarc<T> {
314        Wstarc {
315            weak: Arc::downgrade(&this.arc),
316            thread: this.thread,
317        }
318    }
319
320    #[inline]
321    pub fn get_mut(this: &mut Starc<T>) -> Option<&mut T> {
322        Arc::get_mut(&mut this.arc)
323    }
324}
325
326unsafe impl<T: ?Sized> Send for Starc<T> {}
327unsafe impl<T: ?Sized> Sync for Starc<T> {}
328
329impl<T: ?Sized> Deref for Starc<T> {
330    type Target = T;
331    fn deref(&self) -> &T {
332        assert_eq!(thread::current().id(), self.thread);
333        &*self.arc
334    }
335}
336
337/// Single-threaded `Weak`.
338/// Wrapper for `Weak` that allows you to `Send` it even if `T: !Sync`.
339/// Yet internal data cannot be accessed outside of the thread where it was created.
340pub struct Wstarc<T: ?Sized> {
341    weak: Weak<T>,
342    thread: thread::ThreadId,
343}
344impl<T> Wstarc<T> {
345    pub fn upgrade(&self) -> Option<Starc<T>> {
346        let thread = self.thread;
347        self.weak.upgrade().map(|arc| Starc { arc, thread })
348    }
349}
350unsafe impl<T: ?Sized> Send for Wstarc<T> {}
351unsafe impl<T: ?Sized> Sync for Wstarc<T> {}
352
353#[derive(Debug)]
354pub struct PhysicalDevice(Starc<Share>);
355
356impl PhysicalDevice {
357    fn new_adapter(context: GlContext) -> adapter::Adapter<Backend> {
358        let gl = GlContainer { context };
359        // query information
360        let (
361            info,
362            supported_features,
363            legacy_features,
364            public_caps,
365            private_caps,
366            texture_format_filter,
367        ) = info::query_all(&gl);
368        log::info!("Vendor: {:?}", info.platform_name.vendor);
369        log::info!("Renderer: {:?}", info.platform_name.renderer);
370        log::info!("Version: {:?}", info.version);
371        log::info!("Shading Language: {:?}", info.shading_language);
372        log::info!("Supported Features: {:?}", supported_features);
373        log::info!("Legacy Features: {:?}", legacy_features);
374        log::debug!("Public capabilities: {:#?}", public_caps);
375        log::debug!("Private capabilities: {:#?}", private_caps);
376        log::debug!("Texture format filter: {:#?}", texture_format_filter);
377        log::debug!("Loaded Extensions:");
378        for extension in info.extensions.iter() {
379            log::debug!("- {}", *extension);
380        }
381        let name = info.platform_name.renderer.clone();
382        let vendor: std::string::String = info.platform_name.vendor.clone();
383        let renderer: std::string::String = info.platform_name.renderer.clone();
384
385        let mut memory_types = Vec::new();
386
387        let mut add_buffer_memory_type = |memory_type: adapter::MemoryType| {
388            if private_caps.index_buffer_role_change {
389                // If `index_buffer_role_change` is true, we can use a buffer for any role
390                memory_types.push((memory_type, MemoryUsage::Buffer(buffer::Usage::all())));
391            } else {
392                // If `index_buffer_role_change` is false, ELEMENT_ARRAY_BUFFER buffers may not be
393                // mixed with other targets, so we need to provide one type of memory for INDEX
394                // usage only and another type for all other uses.
395                memory_types.push((
396                    memory_type,
397                    MemoryUsage::Buffer(buffer::Usage::INDEX | buffer::Usage::TRANSFER_DST),
398                ));
399                memory_types.push((
400                    memory_type,
401                    MemoryUsage::Buffer(buffer::Usage::all() - buffer::Usage::INDEX),
402                ));
403            }
404        };
405
406        // Coherent memory is only available if we have `glBufferStorage`
407        let coherent_flag = if private_caps.buffer_storage {
408            memory::Properties::COHERENT
409        } else {
410            memory::Properties::empty()
411        };
412        // Mimicking vulkan, memory types with more flags should come before those with fewer flags
413        add_buffer_memory_type(adapter::MemoryType {
414            properties: coherent_flag
415                | memory::Properties::CPU_VISIBLE
416                | memory::Properties::CPU_CACHED,
417            heap_index: CPU_VISIBLE_HEAP,
418        });
419        add_buffer_memory_type(adapter::MemoryType {
420            properties: coherent_flag | memory::Properties::CPU_VISIBLE,
421            heap_index: CPU_VISIBLE_HEAP,
422        });
423
424        add_buffer_memory_type(adapter::MemoryType {
425            properties: memory::Properties::DEVICE_LOCAL,
426            heap_index: DEVICE_LOCAL_HEAP,
427        });
428
429        // There is always a single device-local memory type for images
430        memory_types.push((
431            adapter::MemoryType {
432                properties: memory::Properties::DEVICE_LOCAL,
433                heap_index: DEVICE_LOCAL_HEAP,
434            },
435            MemoryUsage::Image,
436        ));
437
438        assert!(memory_types.len() <= 64);
439
440        log::info!("Memory types: {:#?}", memory_types);
441
442        // create the shared context
443        let share = Share {
444            context: gl,
445            info,
446            supported_features,
447            legacy_features,
448            public_caps,
449            texture_format_filter,
450            private_caps,
451            open: Cell::new(false),
452            memory_types,
453        };
454        if let Err(err) = share.check() {
455            panic!("Error querying info: {:?}", err);
456        }
457
458        // opengl has no way to discern device_type, so we can try to infer it from the renderer string
459        let vendor_lower = vendor.to_lowercase();
460        let renderer_lower = renderer.to_lowercase();
461        let strings_that_imply_integrated = [
462            " xpress", // space here is on purpose so we don't match express
463            "radeon hd 4200",
464            "radeon hd 4250",
465            "radeon hd 4290",
466            "radeon hd 4270",
467            "radeon hd 4225",
468            "radeon hd 3100",
469            "radeon hd 3200",
470            "radeon hd 3000",
471            "radeon hd 3300",
472            "radeon(tm) r4 graphics",
473            "radeon(tm) r5 graphics",
474            "radeon(tm) r6 graphics",
475            "radeon(tm) r7 graphics",
476            "radeon r7 graphics",
477            "nforce", // all nvidia nforce are integrated
478            "tegra",  // all nvidia tegra are integrated
479            "shield", // all nvidia shield are integrated
480            "igp",
481            "mali",
482            "intel",
483        ];
484        let strings_that_imply_cpu = ["mesa offscreen", "swiftshader"];
485        // todo: Intel will release a discrete gpu soon, and we will need to update this logic when they do
486        let inferred_device_type = if vendor_lower.contains("qualcomm")
487            || vendor_lower.contains("intel")
488            || strings_that_imply_integrated
489                .iter()
490                .any(|&s| renderer_lower.contains(s))
491        {
492            hal::adapter::DeviceType::IntegratedGpu
493        } else if strings_that_imply_cpu
494            .iter()
495            .any(|&s| renderer_lower.contains(s))
496        {
497            hal::adapter::DeviceType::Cpu
498        } else {
499            hal::adapter::DeviceType::DiscreteGpu
500        };
501
502        // source: Sascha Willems at Vulkan
503        let vendor_id = if vendor_lower.contains("amd") {
504            0x1002
505        } else if vendor_lower.contains("imgtec") {
506            0x1010
507        } else if vendor_lower.contains("nvidia") {
508            0x10DE
509        } else if vendor_lower.contains("arm") {
510            0x13B5
511        } else if vendor_lower.contains("qualcomm") {
512            0x5143
513        } else if vendor_lower.contains("intel") {
514            0x8086
515        } else {
516            0
517        };
518
519        adapter::Adapter {
520            info: adapter::AdapterInfo {
521                name,
522                vendor: vendor_id,
523                device: 0,
524                device_type: inferred_device_type,
525            },
526            physical_device: PhysicalDevice(Starc::new(share)),
527            queue_families: vec![QueueFamily],
528        }
529    }
530
531    /// Get GL-specific legacy feature flags.
532    pub fn legacy_features(&self) -> &info::LegacyFeatures {
533        &self.0.legacy_features
534    }
535}
536
537impl adapter::PhysicalDevice<Backend> for PhysicalDevice {
538    unsafe fn open(
539        &self,
540        families: &[(&QueueFamily, &[q::QueuePriority])],
541        requested_features: hal::Features,
542    ) -> Result<adapter::Gpu<Backend>, hal::device::CreationError> {
543        // Can't have multiple logical devices at the same time
544        // as they would share the same context.
545        if self.0.open.get() {
546            return Err(hal::device::CreationError::TooManyObjects);
547        }
548        self.0.open.set(true);
549
550        // TODO: Check for support in the LegacyFeatures struct too
551        if !self.features().contains(requested_features) {
552            return Err(hal::device::CreationError::MissingFeature);
553        }
554
555        // initialize permanent states
556        let gl = &self.0.context;
557
558        if cfg!(debug_assertions) && !cfg!(target_arch = "wasm32") && gl.supports_debug() {
559            log::info!("Debug output is enabled");
560            gl.enable(glow::DEBUG_OUTPUT);
561            gl.debug_message_callback(debug_message_callback);
562        }
563
564        if self
565            .0
566            .legacy_features
567            .contains(info::LegacyFeatures::SRGB_COLOR)
568            && !self.0.info.version.is_embedded
569        {
570            // `FRAMEBUFFER_SRGB` is enabled by default on embedded targets.
571            // TODO: Find way to emulate this on older Opengl versions.
572            gl.enable(glow::FRAMEBUFFER_SRGB);
573        }
574
575        gl.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
576
577        // create main VAO and bind it
578        let mut vao = None;
579        if self.0.private_caps.vertex_array {
580            vao = Some(gl.create_vertex_array().unwrap());
581            gl.bind_vertex_array(vao);
582        }
583
584        if let Err(err) = self.0.check() {
585            panic!("Error opening adapter: {:?}", err);
586        }
587
588        Ok(adapter::Gpu {
589            device: Device::new(self.0.clone(), requested_features),
590            queue_groups: families
591                .iter()
592                .map(|&(_family, priorities)| {
593                    assert_eq!(priorities.len(), 1);
594                    let mut family = q::QueueGroup::new(q::QueueFamilyId(0));
595                    let queue = queue::Queue::new(&self.0, requested_features, vao);
596                    family.add_queue(queue);
597                    family
598                })
599                .collect(),
600        })
601    }
602
603    fn format_properties(&self, _: Option<hal::format::Format>) -> hal::format::Properties {
604        use hal::format::{BufferFeature as Bf, ImageFeature as If};
605
606        // TODO: These are for show
607        hal::format::Properties {
608            linear_tiling: If::TRANSFER_SRC | If::TRANSFER_DST | If::empty(),
609            optimal_tiling: If::TRANSFER_SRC | If::TRANSFER_DST | If::SAMPLED,
610            buffer_features: Bf::VERTEX,
611            drm_format_properties: Vec::new(),
612        }
613    }
614
615    fn image_format_properties(
616        &self,
617        format: hal::format::Format,
618        _dimensions: u8,
619        _tiling: image::Tiling,
620        _usage: image::Usage,
621        _view_caps: image::ViewCapabilities,
622    ) -> Option<image::FormatProperties> {
623        let conv::FormatDescription {
624            tex_external,
625            tex_internal,
626            data_type,
627            ..
628        } = conv::describe_format(format)?;
629
630        if !self
631            .0
632            .texture_format_filter
633            .check(tex_internal, tex_external, data_type)
634        {
635            /* This format is not supported. */
636            return None;
637        }
638
639        Some(image::FormatProperties {
640            max_extent: image::Extent {
641                width: !0,
642                height: !0,
643                depth: !0,
644            },
645            max_levels: !0,
646            max_layers: !0,
647            sample_count_mask: 127,
648            max_resource_size: !0,
649        })
650    }
651
652    fn memory_properties(&self) -> adapter::MemoryProperties {
653        adapter::MemoryProperties {
654            memory_types: self
655                .0
656                .memory_types
657                .iter()
658                .map(|(mem_type, _)| *mem_type)
659                .collect(),
660            memory_heaps: vec![
661                adapter::MemoryHeap {
662                    size: !0,
663                    flags: memory::HeapFlags::DEVICE_LOCAL,
664                },
665                adapter::MemoryHeap {
666                    size: !0,
667                    flags: memory::HeapFlags::empty(),
668                },
669            ],
670        }
671    }
672
673    fn external_buffer_properties(
674        &self,
675        _usage: hal::buffer::Usage,
676        _sparse: hal::memory::SparseFlags,
677        _memory_type: hal::external_memory::ExternalMemoryType,
678    ) -> hal::external_memory::ExternalMemoryProperties {
679        unimplemented!()
680    }
681
682    fn external_image_properties(
683        &self,
684        _format: hal::format::Format,
685        _dimensions: u8,
686        _tiling: image::Tiling,
687        _usage: image::Usage,
688        _view_caps: image::ViewCapabilities,
689        _memory_type: hal::external_memory::ExternalMemoryType,
690    ) -> Result<
691        hal::external_memory::ExternalMemoryProperties,
692        hal::external_memory::ExternalImagePropertiesError,
693    > {
694        unimplemented!()
695    }
696
697    fn features(&self) -> hal::Features {
698        self.0.supported_features
699    }
700
701    fn properties(&self) -> hal::PhysicalDeviceProperties {
702        self.0.public_caps
703    }
704
705    unsafe fn enumerate_displays(&self) -> Vec<display::Display<crate::Backend>> {
706        unimplemented!();
707    }
708
709    unsafe fn enumerate_compatible_planes(
710        &self,
711        _display: &display::Display<crate::Backend>,
712    ) -> Vec<display::Plane> {
713        unimplemented!();
714    }
715
716    unsafe fn create_display_mode(
717        &self,
718        _display: &display::Display<crate::Backend>,
719        _resolution: (u32, u32),
720        _refresh_rate: u32,
721    ) -> Result<display::DisplayMode<crate::Backend>, display::DisplayModeError> {
722        unimplemented!();
723    }
724
725    unsafe fn create_display_plane<'a>(
726        &self,
727        _display: &'a display::DisplayMode<crate::Backend>,
728        _plane: &'a display::Plane,
729    ) -> Result<display::DisplayPlane<'a, crate::Backend>, hal::device::OutOfMemory> {
730        unimplemented!();
731    }
732}
733
734#[derive(Debug, Clone, Copy)]
735pub struct QueueFamily;
736
737impl q::QueueFamily for QueueFamily {
738    fn queue_type(&self) -> q::QueueType {
739        q::QueueType::General
740    }
741    fn max_queues(&self) -> usize {
742        1
743    }
744    fn id(&self) -> q::QueueFamilyId {
745        q::QueueFamilyId(0)
746    }
747    fn supports_sparse_binding(&self) -> bool {
748        false
749    }
750}
751
752fn resolve_sub_range(
753    sub: &buffer::SubRange,
754    whole: Range<buffer::Offset>,
755) -> Range<buffer::Offset> {
756    let end = sub.size.map_or(whole.end, |s| whole.start + sub.offset + s);
757    whole.start + sub.offset..end
758}
759
760const fn is_webgl() -> bool {
761    cfg!(target_arch = "wasm32")
762}