Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/wgpu_bindings/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 94 kB image not shown  

Quelle  server.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::{
    command::{RecordedComputePass, RecordedRenderPass},
    error::{ErrMsg, ErrorBuffer, ErrorBufferType},
    wgpu_string, AdapterInformation, ByteBuf, CommandEncoderAction, DeviceAction, DropAction,
    QueueWriteAction, SwapChainId, TextureAction,
};

use nsstring::{nsACString, nsCString, nsString};

use wgc::{device::DeviceError, id};
use wgc::{pipeline::CreateShaderModuleError, resource::BufferAccessError};
#[allow(unused_imports)]
use wgh::Instance;

use std::borrow::Cow;
#[allow(unused_imports)]
use std::mem;
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
use std::os::fd::{FromRawFd, IntoRawFd, OwnedFd, RawFd};
use std::os::raw::{c_char, c_void};
use std::ptr;
use std::slice;
use std::sync::atomic::{AtomicU32, Ordering};

#[allow(unused_imports)]
use std::ffi::CString;
use std::ffi::{c_long, c_ulong};

#[cfg(target_os = "windows")]
use windows::Win32::{Foundation, Graphics::Direct3D12};

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
use ash::{khr, vk};

#[cfg(target_os = "macos")]
use objc::{class, msg_send, sel, sel_impl};

// The seemingly redundant u64 suffixes help cbindgen with generating the right C++ code.
// See https://github.com/mozilla/cbindgen/issues/849.

/// We limit the size of buffer allocations for stability reason.
/// We can reconsider this limit in the future. Note that some drivers (mesa for example),
/// have issues when the size of a buffer, mapping or copy command does not fit into a
/// signed 32 bits integer, so beyond a certain size, large allocations will need some form
/// of driver allow/blocklist.
pub const MAX_BUFFER_SIZE: wgt::BufferAddress = 1u64 << 30u64;
const MAX_BUFFER_SIZE_U32: u32 = MAX_BUFFER_SIZE as u32;

// Mesa has issues with height/depth that don't fit in a 16 bits signed integers.
const MAX_TEXTURE_EXTENT: u32 = std::i16::MAX as u32;
// We have to restrict the number of bindings for any given resource type so that
// the sum of these limits multiplied by the number of shader stages fits
// maxBindingsPerBindGroup (1000). This restriction is arbitrary and is likely to
// change eventually. See github.com/gpuweb/gpuweb/pull/4484
// For now it's impractical for users to have very large numbers of bindings so this
// limit should not be too restrictive until we add support for a bindless API.
// Then we may have to ignore the spec or get it changed.
const MAX_BINDINGS_PER_RESOURCE_TYPE: u32 = 64;

fn restrict_limits(limits: wgt::Limits) -> wgt::Limits {
    wgt::Limits {
        max_buffer_size: limits.max_buffer_size.min(MAX_BUFFER_SIZE),
        max_texture_dimension_1d: limits.max_texture_dimension_1d.min(MAX_TEXTURE_EXTENT),
        max_texture_dimension_2d: limits.max_texture_dimension_2d.min(MAX_TEXTURE_EXTENT),
        max_texture_dimension_3d: limits.max_texture_dimension_3d.min(MAX_TEXTURE_EXTENT),
        max_sampled_textures_per_shader_stage: limits
            .max_sampled_textures_per_shader_stage
            .min(MAX_BINDINGS_PER_RESOURCE_TYPE),
        max_samplers_per_shader_stage: limits
            .max_samplers_per_shader_stage
            .min(MAX_BINDINGS_PER_RESOURCE_TYPE),
        max_storage_textures_per_shader_stage: limits
            .max_storage_textures_per_shader_stage
            .min(MAX_BINDINGS_PER_RESOURCE_TYPE),
        max_uniform_buffers_per_shader_stage: limits
            .max_uniform_buffers_per_shader_stage
            .min(MAX_BINDINGS_PER_RESOURCE_TYPE),
        max_storage_buffers_per_shader_stage: limits
            .max_storage_buffers_per_shader_stage
            .min(MAX_BINDINGS_PER_RESOURCE_TYPE),
        max_uniform_buffer_binding_size: limits
            .max_uniform_buffer_binding_size
            .min(MAX_BUFFER_SIZE_U32),
        max_storage_buffer_binding_size: limits
            .max_storage_buffer_binding_size
            .min(MAX_BUFFER_SIZE_U32),
        max_non_sampler_bindings: 10_000,
        ..limits
    }
}

// hide wgc's global in private
pub struct Global {
    global: wgc::global::Global,
    #[allow(dead_code)]
    owner: *mut c_void,
}

impl std::ops::Deref for Global {
    type Target = wgc::global::Global;
    fn deref(&self) -> &Self::Target {
        &self.global
    }
}

#[no_mangle]
pub extern "C" fn wgpu_server_new(owner: *mut c_void, use_dxc: bool) -> *mut Global {
    log::info!("Initializing WGPU server");
    let backends_pref = static_prefs::pref!("dom.webgpu.wgpu-backend").to_string();
    let backends = if backends_pref.is_empty() {
        #[cfg(windows)]
        {
            wgt::Backends::DX12
        }
        #[cfg(not(windows))]
        {
            wgt::Backends::PRIMARY
        }
    } else {
        log::info!(
            "Selecting backends based on dom.webgpu.wgpu-backend pref: {:?}",
            backends_pref
        );
        wgc::instance::parse_backends_from_comma_list(&backends_pref)
    };

    let mut instance_flags = wgt::InstanceFlags::from_build_config().with_env();
    if !static_prefs::pref!("dom.webgpu.hal-labels") {
        instance_flags.insert(wgt::InstanceFlags::DISCARD_HAL_LABELS);
    }

    let dx12_shader_compiler = if use_dxc {
        wgt::Dx12Compiler::DynamicDxc {
            dxc_path: "dxcompiler.dll".into(),
            dxil_path: "dxil.dll".into(),
        }
    } else {
        wgt::Dx12Compiler::Fxc
    };

    let global = wgc::global::Global::new(
        "wgpu",
        &wgt::InstanceDescriptor {
            backends,
            flags: instance_flags,
            dx12_shader_compiler,
            gles_minor_version: wgt::Gles3MinorVersion::Automatic,
        },
    );
    let global = Global { global, owner };
    Box::into_raw(Box::new(global))
}

/// # Safety
///
/// This function is unsafe because improper use may lead to memory
/// problems. For example, a double-free may occur if the function is called
/// twice on the same raw pointer.
#[no_mangle]
pub unsafe extern "C" fn wgpu_server_delete(global: *mut Global) {
    log::info!("Terminating WGPU server");
    let _ = Box::from_raw(global);
}

#[no_mangle]
pub extern "C" fn wgpu_server_poll_all_devices(global: &Global, force_wait: bool) {
    global.poll_all_devices(force_wait).unwrap();
}

#[no_mangle]
pub extern "C" fn wgpu_server_device_poll(
    global: &Global,
    device_id: id::DeviceId,
    force_wait: bool,
) {
    let maintain = if force_wait {
        wgt::Maintain::Wait
    } else {
        wgt::Maintain::Poll
    };
    global.device_poll(device_id, maintain).unwrap();
}

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct FfiLUID {
    low_part: c_ulong,
    high_part: c_long,
}

/// Request an adapter according to the specified options.
///
/// Returns true if we successfully found an adapter.
#[allow(unused_variables)]
#[no_mangle]
pub unsafe extern "C" fn wgpu_server_instance_request_adapter(
    global: &Global,
    desc: &wgc::instance::RequestAdapterOptions,
    adapter_id: id::AdapterId,
    adapter_luid: Option<&FfiLUID>,
    mut error_buf: ErrorBuffer,
) -> bool {
    // Prefer to use the dx12 backend, if one exists, and use the same DXGI adapter as WebRender.
    // If wgpu uses a different adapter than WebRender, textures created by
    // webgpu::ExternalTexture do not work with wgpu.
    #[cfg(target_os = "windows")]
    if adapter_luid.is_some() && !desc.force_fallback_adapter {
        if let Some(instance) = global.global.instance_as_hal::<wgc::api::Dx12>() {
            for adapter in instance.enumerate_adapters(None) {
                let raw_adapter = adapter.adapter.raw_adapter();
                let desc = unsafe { raw_adapter.GetDesc() };
                if let Ok(desc) = desc {
                    if desc.AdapterLuid.LowPart == adapter_luid.unwrap().low_part
                        && desc.AdapterLuid.HighPart == adapter_luid.unwrap().high_part
                    {
                        global.create_adapter_from_hal(
                            wgh::DynExposedAdapter::from(adapter),
                            Some(adapter_id),
                        );
                        return true;
                    }
                }
            }
            error_buf.init(ErrMsg {
                message: "Failed to create adapter for dx12",
                r#type: ErrorBufferType::Internal,
            });
            return false;
        }
    }

    match global.request_adapter(desc, wgt::Backends::PRIMARY, Some(adapter_id)) {
        Ok(id) => return true,
        Err(e) => {
            error_buf.init(e);
            return false;
        }
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[allow(clippy::upper_case_acronyms)]
#[cfg(target_os = "macos")]
struct NSOperatingSystemVersion {
    major: usize,
    minor: usize,
    patch: usize,
}

#[cfg(target_os = "macos")]
impl NSOperatingSystemVersion {
    fn at_least(
        &self,
        mac_version: (usize, usize),
        ios_version: (usize, usize),
        is_mac: bool,
    ) -> bool {
        let version = if is_mac { mac_version } else { ios_version };

        self.major
            .cmp(&version.0)
            .then_with(|| self.minor.cmp(&version.1))
            .is_ge()
    }
}

#[allow(unreachable_code)]
#[allow(unused_variables)]
fn support_use_external_texture_in_swap_chain(
    global: &Global,
    self_id: id::AdapterId,
    backend: wgt::Backend,
    is_hardware: bool,
) -> bool {
    #[cfg(target_os = "windows")]
    {
        return backend == wgt::Backend::Dx12 && is_hardware;
    }

    #[cfg(target_os = "linux")]
    {
        let support = if backend != wgt::Backend::Vulkan {
            false
        } else {
            unsafe {
                global.adapter_as_hal::<wgc::api::Vulkan, _, bool>(self_id, |hal_adapter| {
                    let hal_adapter = match hal_adapter {
                        None => {
                            let msg = CString::new(format!("Vulkan adapter is invalid")).unwrap();
                            gfx_critical_note(msg.as_ptr());
                            return false;
                        }
                        Some(hal_adapter) => hal_adapter,
                    };

                    let capabilities = hal_adapter.physical_device_capabilities();

                    capabilities.supports_extension(khr::external_memory_fd::NAME)
                        && capabilities.supports_extension(ash::ext::external_memory_dma_buf::NAME)
                        && capabilities
                            .supports_extension(ash::ext::image_drm_format_modifier::NAME)
                        && capabilities.supports_extension(khr::external_semaphore_fd::NAME)
                })
            }
        };
        return support;
    }

    #[cfg(target_os = "macos")]
    {
        if backend != wgt::Backend::Metal || !is_hardware {
            return false;
        }

        let version: NSOperatingSystemVersion = unsafe {
            let process_info: *mut objc::runtime::Object =
                msg_send![class!(NSProcessInfo), processInfo];
            msg_send![process_info, operatingSystemVersion]
        };

        let supports_shared_event = version.at_least((10, 14), (12, 0), /* os_is_mac */ true);

        return supports_shared_event;
    }

    false
}

#[no_mangle]
pub unsafe extern "C" fn wgpu_server_adapter_pack_info(
    global: &Global,
    self_id: Option<id::AdapterId>,
    byte_buf: &mut ByteBuf,
) {
    let mut data = Vec::new();
    match self_id {
        Some(id) => {
            let wgt::AdapterInfo {
                name,
                vendor,
                device,
                device_type,
                driver,
                driver_info,
                backend,
            } = global.adapter_get_info(id);

            let is_hardware = match device_type {
                wgt::DeviceType::IntegratedGpu | wgt::DeviceType::DiscreteGpu => true,
                _ => false,
            };

            if static_prefs::pref!("dom.webgpu.testing.assert-hardware-adapter") {
                assert!(
                    is_hardware,
                    "Expected a hardware gpu adapter, got {:?}",
                    device_type
                );
            }

            let support_use_external_texture_in_swap_chain =
                support_use_external_texture_in_swap_chain(global, id, backend, is_hardware);

            let info = AdapterInformation {
                id,
                limits: restrict_limits(global.adapter_limits(id)),
                features: global.adapter_features(id),
                name,
                vendor,
                device,
                device_type,
                driver,
                driver_info,
                backend,
                support_use_external_texture_in_swap_chain,
            };
            bincode::serialize_into(&mut data, &info).unwrap();
        }
        None => {
            bincode::serialize_into(&mut data, &0u64).unwrap();
        }
    }
    *byte_buf = ByteBuf::from_vec(data);
}

static TRACE_IDX: AtomicU32 = AtomicU32::new(0);

#[no_mangle]
pub unsafe extern "C" fn wgpu_server_adapter_request_device(
    global: &Global,
    self_id: id::AdapterId,
    byte_buf: &ByteBuf,
    new_device_id: id::DeviceId,
    new_queue_id: id::QueueId,
    mut error_buf: ErrorBuffer,
) {
    let desc: wgc::device::DeviceDescriptor = bincode::deserialize(byte_buf.as_slice()).unwrap();
    let trace_string = std::env::var("WGPU_TRACE").ok().map(|s| {
        let idx = TRACE_IDX.fetch_add(1, Ordering::Relaxed);
        let path = format!("{}/{}/", s, idx);

        if std::fs::create_dir_all(&path).is_err() {
            log::warn!("Failed to create directory {:?} for wgpu recording.", path);
        }

        path
    });
    let trace_path = trace_string
        .as_ref()
        .map(|string| std::path::Path::new(string.as_str()));
    // TODO: in https://github.com/gfx-rs/wgpu/pull/3626/files#diff-033343814319f5a6bd781494692ea626f06f6c3acc0753a12c867b53a646c34eR97
    // which introduced the queue id parameter, the queue id is also the device id. I don't know how applicable this is to
    // other situations (this one in particular).

    #[cfg(target_os = "linux")]
    {
        let support_dma_buf =
            global.adapter_as_hal::<wgc::api::Vulkan, _, bool>(self_id, |hal_adapter| {
                let hal_adapter = match hal_adapter {
                    None => {
                        let msg = CString::new(format!("Vulkan adapter is invalid")).unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return false;
                    }
                    Some(hal_adapter) => hal_adapter,
                };

                let capabilities = hal_adapter.physical_device_capabilities();

                capabilities.supports_extension(khr::external_memory_fd::NAME)
                    && capabilities.supports_extension(ash::ext::external_memory_dma_buf::NAME)
                    && capabilities.supports_extension(ash::ext::image_drm_format_modifier::NAME)
                    && capabilities.supports_extension(khr::external_semaphore_fd::NAME)
            });

        if support_dma_buf {
            let hal_device = global
                .adapter_as_hal::<wgc::api::Vulkan, _, Option<wgh::OpenDevice<wgh::api::Vulkan>>>(
                    self_id,
                    |hal_adapter| {
                        let hal_adapter = match hal_adapter {
                            None => {
                                let msg =
                                    CString::new(format!("Vulkan adapter is invalid")).unwrap();
                                gfx_critical_note(msg.as_ptr());
                                return None;
                            }
                            Some(hal_adapter) => hal_adapter,
                        };

                        let mut enabled_extensions =
                            hal_adapter.required_device_extensions(desc.required_features);
                        enabled_extensions.push(khr::external_memory_fd::NAME);
                        enabled_extensions.push(ash::ext::external_memory_dma_buf::NAME);
                        enabled_extensions.push(ash::ext::image_drm_format_modifier::NAME);
                        enabled_extensions.push(khr::external_semaphore_fd::NAME);

                        let mut enabled_phd_features = hal_adapter
                            .physical_device_features(&enabled_extensions, desc.required_features);

                        let raw_instance = hal_adapter.shared_instance().raw_instance();
                        let raw_physical_device = hal_adapter.raw_physical_device();

                        let queue_family_index = raw_instance
                            .get_physical_device_queue_family_properties(raw_physical_device)
                            .into_iter()
                            .enumerate()
                            .find_map(|(queue_family_index, info)| {
                                if info.queue_flags.contains(vk::QueueFlags::GRAPHICS) {
                                    Some(queue_family_index as u32)
                                } else {
                                    None
                                }
                            });

                        let queue_family_index = match queue_family_index {
                            None => {
                                let msg =
                                    CString::new(format!("Vulkan device has no graphics queue"))
                                        .unwrap();
                                gfx_critical_note(msg.as_ptr());
                                return None;
                            }
                            Some(queue_family_index) => queue_family_index,
                        };

                        let family_info = vk::DeviceQueueCreateInfo::default()
                            .queue_family_index(queue_family_index)
                            .queue_priorities(&[1.0]);
                        let family_infos = [family_info];

                        let str_pointers = enabled_extensions
                            .iter()
                            .map(|&s| {
                                // Safe because `enabled_extensions` entries have static lifetime.
                                s.as_ptr()
                            })
                            .collect::<Vec<_>>();

                        let pre_info = vk::DeviceCreateInfo::default()
                            .queue_create_infos(&family_infos)
                            .enabled_extension_names(&str_pointers);
                        let info = enabled_phd_features.add_to_device_create(pre_info);

                        let raw_device =
                            match raw_instance.create_device(raw_physical_device, &info, None) {
                                Err(err) => {
                                    let msg =
                                        CString::new(format!("create_device() failed: {:?}", err))
                                            .unwrap();
                                    gfx_critical_note(msg.as_ptr());
                                    return None;
                                }
                                Ok(raw_device) => raw_device,
                            };

                        let hal_device = hal_adapter.device_from_raw(
                            raw_device,
                            None,
                            &enabled_extensions,
                            desc.required_features,
                            &desc.memory_hints,
                            family_info.queue_family_index,
                            0,
                        );
                        Some(hal_device.unwrap())
                    },
                );

            let hal_device = match hal_device {
                None => {
                    error_buf.init(ErrMsg {
                        message: "Failed to create ash::Device",
                        r#type: ErrorBufferType::Internal,
                    });
                    return;
                }
                Some(hal_device) => hal_device,
            };

            let res = global.create_device_from_hal(
                self_id,
                hal_device.into(),
                &desc,
                trace_path,
                Some(new_device_id),
                Some(new_queue_id),
            );
            if let Err(err) = res {
                error_buf.init(err);
            }
            return;
        }
    }

    let res = global.adapter_request_device(
        self_id,
        &desc,
        trace_path,
        Some(new_device_id),
        Some(new_queue_id),
    );
    if let Err(err) = res {
        error_buf.init(err);
    }
}

#[no_mangle]
pub extern "C" fn wgpu_server_adapter_drop(global: &Global, adapter_id: id::AdapterId) {
    global.adapter_drop(adapter_id)
}

#[no_mangle]
pub extern "C" fn wgpu_server_device_destroy(global: &Global, self_id: id::DeviceId) {
    global.device_destroy(self_id)
}

#[no_mangle]
pub extern "C" fn wgpu_server_device_drop(global: &Global, self_id: id::DeviceId) {
    global.device_drop(self_id)
}

#[repr(C)]
pub struct DeviceLostClosure {
    pub callback: unsafe extern "C" fn(user_data: *mut u8, reason: u8, message: *const c_char),
    pub cleanup_callback: unsafe extern "C" fn(user_data: *mut u8),
    pub user_data: *mut u8,
}
unsafe impl Send for DeviceLostClosure {}

impl DeviceLostClosure {
    fn call(self, reason: wgt::DeviceLostReason, message: String) {
        // Ensure message is structured as a null-terminated C string. It only
        // needs to live as long as the callback invocation.
        let message = std::ffi::CString::new(message).unwrap();
        unsafe {
            (self.callback)(self.user_data, reason as u8, message.as_ptr());
        }
        core::mem::forget(self);
    }
}

impl Drop for DeviceLostClosure {
    fn drop(&mut self) {
        unsafe {
            (self.cleanup_callback)(self.user_data);
        }
    }
}

#[no_mangle]
pub unsafe extern "C" fn wgpu_server_set_device_lost_callback(
    global: &Global,
    self_id: id::DeviceId,
    closure: DeviceLostClosure,
) {
    let closure = Box::new(move |reason, message| closure.call(reason, message));
    global.device_set_device_lost_closure(self_id, closure);
}

impl ShaderModuleCompilationMessage {
    fn set_error(&mut self, error: &CreateShaderModuleError, source: &str) {
        // The WebGPU spec says that if the message doesn't point to a particular position in
        // the source, the line number, position, offset and lengths should be zero.
        let line_number;
        let line_pos;
        let utf16_offset;
        let utf16_length;

        let location = match error {
            CreateShaderModuleError::Parsing(e) => e.inner.location(source),
            CreateShaderModuleError::Validation(e) => e.inner.location(source),
            _ => None,
        };

        if let Some(location) = location {
            let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum();
            let start = location.offset as usize;
            let end = start + location.length as usize;
            utf16_offset = len_utf16(&source[0..start]);
            utf16_length = len_utf16(&source[start..end]);

            line_number = location.line_number as u64;
            // Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it.
            let line_start = source[0..start].rfind('\n').map(|pos| pos + 1).unwrap_or(0);
            line_pos = len_utf16(&source[line_start..start]) + 1;
        } else {
            line_number = 0;
            line_pos = 0;
            utf16_offset = 0;
            utf16_length = 0;
        }

        let message = nsString::from(&error.to_string());

        *self = Self {
            line_number,
            line_pos,
            utf16_offset,
            utf16_length,
            message,
        };
    }
}

/// A compilation message representation for the ffi boundary.
/// the message is immediately copied into an equivalent C++
/// structure that owns its strings.
#[repr(C)]
#[derive(Clone)]
pub struct ShaderModuleCompilationMessage {
    pub line_number: u64,
    pub line_pos: u64,
    pub utf16_offset: u64,
    pub utf16_length: u64,
    pub message: nsString,
}

/// Creates a shader module and returns an object describing the errors if any.
///
/// If there was no error, the returned pointer is nil.
#[no_mangle]
pub extern "C" fn wgpu_server_device_create_shader_module(
    global: &Global,
    self_id: id::DeviceId,
    module_id: id::ShaderModuleId,
    label: Option<&nsACString>,
    code: &nsCString,
    out_message: &mut ShaderModuleCompilationMessage,
    mut error_buf: ErrorBuffer,
) -> bool {
    let utf8_label = label.map(|utf16| utf16.to_string());
    let label = utf8_label.as_ref().map(|s| Cow::from(&s[..]));

    let source_str = code.to_utf8();

    let source = wgc::pipeline::ShaderModuleSource::Wgsl(Cow::from(&source_str[..]));

    let desc = wgc::pipeline::ShaderModuleDescriptor {
        label,
        runtime_checks: Default::default(),
    };

    let (_, error) = global.device_create_shader_module(self_id, &desc, source, Some(module_id));

    if let Some(err) = error {
        out_message.set_error(&err, &source_str[..]);
        let err_type = match &err {
            CreateShaderModuleError::Device(DeviceError::OutOfMemory) => {
                ErrorBufferType::OutOfMemory
            }
            CreateShaderModuleError::Device(DeviceError::Lost) => ErrorBufferType::DeviceLost,
            _ => ErrorBufferType::Validation,
        };

        // Per spec: "User agents should not include detailed compiler error messages or
        // shader text in the message text of validation errors arising here: these details
        // are accessible via getCompilationInfo()"
        let message = match &err {
            CreateShaderModuleError::Parsing(_) => "Parsing error".to_string(),
            CreateShaderModuleError::Validation(_) => "Shader validation error".to_string(),
            CreateShaderModuleError::Device(device_err) => format!("{device_err:?}"),
            _ => format!("{err:?}"),
        };

        error_buf.init(ErrMsg {
            message: &format!("Shader module creation failed: {message}"),
            r#type: err_type,
        });
        return false;
    }

    // Avoid allocating the structure that holds errors in the common case (no errors).
    return true;
}

#[no_mangle]
pub extern "C" fn wgpu_server_device_create_buffer(
    global: &Global,
    self_id: id::DeviceId,
    buffer_id: id::BufferId,
    label: Option<&nsACString>,
    size: wgt::BufferAddress,
    usage: u32,
    mapped_at_creation: bool,
    shm_allocation_failed: bool,
    mut error_buf: ErrorBuffer,
) {
    let utf8_label = label.map(|utf16| utf16.to_string());
    let label = utf8_label.as_ref().map(|s| Cow::from(&s[..]));
    let usage = wgt::BufferUsages::from_bits_retain(usage);

    let desc = wgc::resource::BufferDescriptor {
        label,
        size,
        usage,
        mapped_at_creation,
    };

    // Don't trust the graphics driver with buffer sizes larger than our conservative max texture size.
    if shm_allocation_failed || size > MAX_BUFFER_SIZE {
        error_buf.init(ErrMsg {
            message: "Out of memory",
            r#type: ErrorBufferType::OutOfMemory,
        });
        global.create_buffer_error(Some(buffer_id), &desc);
        return;
    }

    let (_, error) = global.device_create_buffer(self_id, &desc, Some(buffer_id));
    if let Some(err) = error {
        error_buf.init(err);
    }
}

/// The status code provided to the buffer mapping closure.
///
/// This is very similar to `BufferAccessResult`, except that this is FFI-friendly.
#[repr(C)]
pub enum BufferMapAsyncStatus {
    /// The Buffer is successfully mapped, `get_mapped_range` can be called.
    ///
    /// All other variants of this enum represent failures to map the buffer.
    Success,
    /// The buffer is already mapped.
    ///
    /// While this is treated as an error, it does not prevent mapped range from being accessed.
    AlreadyMapped,
    /// Mapping was already requested.
    MapAlreadyPending,
    /// An unknown error.
    Error,
    /// The context is Lost.
    ContextLost,
    /// The buffer is in an invalid state.
    Invalid,
    /// The range isn't fully contained in the buffer.
    InvalidRange,
    /// The range isn't properly aligned.
    InvalidAlignment,
    /// Incompatible usage flags.
    InvalidUsageFlags,
}

#[repr(C)]
pub struct BufferMapClosure {
    pub callback: unsafe extern "C" fn(user_data: *mut u8, status: BufferMapAsyncStatus),
    pub user_data: *mut u8,
}
unsafe impl Send for BufferMapClosure {}

/// # Safety
///
/// Callers are responsible for ensuring `closure` is well-formed.
#[no_mangle]
pub unsafe extern "C" fn wgpu_server_buffer_map(
    global: &Global,
    buffer_id: id::BufferId,
    start: wgt::BufferAddress,
    size: wgt::BufferAddress,
    map_mode: wgc::device::HostMap,
    closure: BufferMapClosure,
    mut error_buf: ErrorBuffer,
) {
    let closure = Box::new(move |result| {
        let status = match result {
            Ok(_) => BufferMapAsyncStatus::Success,
            Err(BufferAccessError::Device(_)) => BufferMapAsyncStatus::ContextLost,
            Err(BufferAccessError::InvalidResource(_))
            | Err(BufferAccessError::DestroyedResource(_)) => BufferMapAsyncStatus::Invalid,
            Err(BufferAccessError::AlreadyMapped) => BufferMapAsyncStatus::AlreadyMapped,
            Err(BufferAccessError::MapAlreadyPending) => BufferMapAsyncStatus::MapAlreadyPending,
            Err(BufferAccessError::MissingBufferUsage(_)) => {
                BufferMapAsyncStatus::InvalidUsageFlags
            }
            Err(BufferAccessError::UnalignedRange)
            | Err(BufferAccessError::UnalignedRangeSize { .. })
            | Err(BufferAccessError::UnalignedOffset { .. }) => {
                BufferMapAsyncStatus::InvalidAlignment
            }
            Err(BufferAccessError::OutOfBoundsUnderrun { .. })
            | Err(BufferAccessError::OutOfBoundsOverrun { .. })
            | Err(BufferAccessError::NegativeRange { .. }) => BufferMapAsyncStatus::InvalidRange,
            Err(BufferAccessError::Failed)
            | Err(BufferAccessError::NotMapped)
            | Err(BufferAccessError::MapAborted) => BufferMapAsyncStatus::Error,
            Err(_) => BufferMapAsyncStatus::Invalid,
        };

        (closure.callback)(closure.user_data, status)
    });
    let operation = wgc::resource::BufferMapOperation {
        host: map_mode,
        callback: Some(closure),
    };
    let result = global.buffer_map_async(buffer_id, start, Some(size), operation);

    if let Err(error) = result {
        error_buf.init(error);
    }
}

#[repr(C)]
pub struct MappedBufferSlice {
    pub ptr: *mut u8,
    pub length: u64,
}

/// # Safety
///
/// This function is unsafe as there is no guarantee that the given pointer is
/// valid for `size` elements.
#[no_mangle]
pub unsafe extern "C" fn wgpu_server_buffer_get_mapped_range(
    global: &Global,
    buffer_id: id::BufferId,
    start: wgt::BufferAddress,
    size: wgt::BufferAddress,
    mut error_buf: ErrorBuffer,
) -> MappedBufferSlice {
    let result = global.buffer_get_mapped_range(buffer_id, start, Some(size));

    let (ptr, length) = result
        .map(|(ptr, len)| (ptr.as_ptr(), len))
        .unwrap_or_else(|error| {
            error_buf.init(error);
            (std::ptr::null_mut(), 0)
        });
    MappedBufferSlice { ptr, length }
}

#[no_mangle]
pub extern "C" fn wgpu_server_buffer_unmap(
    global: &Global,
    buffer_id: id::BufferId,
    mut error_buf: ErrorBuffer,
) {
    if let Err(e) = global.buffer_unmap(buffer_id) {
        match e {
            // NOTE: This is presumed by CTS test cases, and was even formally specified in the
            // WebGPU spec. previously, but this doesn't seem formally specified now. :confused:
            //
            // TODO: upstream this; see <https://bugzilla.mozilla.org/show_bug.cgi?id=1842297>.
            BufferAccessError::InvalidResource(_) => (),
            other => error_buf.init(other),
        }
    }
}

#[no_mangle]
pub extern "C" fn wgpu_server_buffer_destroy(global: &Global, self_id: id::BufferId) {
    // Per spec, there is no need for the buffer or even device to be in a valid state,
    // even calling calling destroy multiple times is fine, so no error to push into
    // an error scope.
    let _ = global.buffer_destroy(self_id);
}

#[no_mangle]
pub extern "C" fn wgpu_server_buffer_drop(global: &Global, self_id: id::BufferId) {
    global.buffer_drop(self_id);
}

#[allow(unused_variables)]
#[no_mangle]
pub extern "C" fn wgpu_server_get_device_fence_handle(
    global: &Global,
    device_id: id::DeviceId,
) -> *mut c_void {
    #[cfg(target_os = "windows")]
    {
        let dx12_device = unsafe {
            global.device_as_hal::<wgc::api::Dx12, _, Option<Direct3D12::ID3D12Device>>(
                device_id,
                |hal_device| hal_device.map(|device| device.raw_device().clone()),
            )
        };
        let dx12_device = match dx12_device {
            Some(device) => device,
            None => {
                return ptr::null_mut();
            }
        };

        let dx12_fence = unsafe {
            global.device_fence_as_hal::<wgc::api::Dx12, _, Option<Direct3D12::ID3D12Fence>>(
                device_id,
                |hal_fence| hal_fence.map(|fence| fence.raw_fence().clone()),
            )
        };
        let dx12_fence = match dx12_fence {
            Some(fence) => fence,
            None => {
                return ptr::null_mut();
            }
        };

        let res = unsafe {
            dx12_device.CreateSharedHandle(&dx12_fence, None, Foundation::GENERIC_ALL.0, None)
        };

        return match res {
            Ok(handle) => handle.0,
            Err(_) => ptr::null_mut(),
        };
    }
    #[cfg(not(target_os = "windows"))]
    ptr::null_mut()
}

#[derive(Debug)]
#[repr(C)]
pub struct DMABufInfo {
    pub is_valid: bool,
    pub modifier: u64,
    pub plane_count: u32,
    pub offsets: [u64; 3],
    pub strides: [u64; 3],
}

#[derive(Debug)]
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub struct VkImageHandle {
    pub device: vk::Device,
    pub image: vk::Image,
    pub memory: vk::DeviceMemory,
    pub memory_size: u64,
    pub memory_type_index: u32,
    pub modifier: u64,
    pub layouts: Vec<vk::SubresourceLayout>,
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
impl VkImageHandle {
    fn destroy(&self, global: &Global, device_id: id::DeviceId) {
        unsafe {
            global.device_as_hal::<wgc::api::Vulkan, _, ()>(device_id, |hal_device| {
                let hal_device = match hal_device {
                    None => {
                        return;
                    }
                    Some(hal_device) => hal_device,
                };
                let device = hal_device.raw_device();

                (device.fp_v1_0().destroy_image)(self.device, self.image, ptr::null());
                (device.fp_v1_0().free_memory)(self.device, self.memory, ptr::null());
            })
        };
    }
}

#[no_mangle]
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub extern "C" fn wgpu_vkimage_create_with_dma_buf(
    global: &Global,
    device_id: id::DeviceId,
    width: u32,
    height: u32,
    out_memory_size: *mut u64,
) -> *mut VkImageHandle {
    let image_handle = unsafe {
        global.device_as_hal::<wgc::api::Vulkan, _, Option<VkImageHandle>>(
            device_id,
            |hal_device| {
                let hal_device = match hal_device {
                    None => {
                        let msg = CString::new(format!("Vulkan device is invalid")).unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }
                    Some(hal_device) => hal_device,
                };

                let device = hal_device.raw_device();
                let physical_device = hal_device.raw_physical_device();
                let instance = hal_device.shared_instance().raw_instance();

                let count = {
                    let mut drm_format_modifier_props_list =
                        vk::DrmFormatModifierPropertiesListEXT::default();
                    let mut format_properties_2 = vk::FormatProperties2::default()
                        .push_next(&mut drm_format_modifier_props_list);

                    instance.get_physical_device_format_properties2(
                        physical_device,
                        vk::Format::R8G8B8A8_UNORM,
                        &mut format_properties_2,
                    );
                    drm_format_modifier_props_list.drm_format_modifier_count
                };

                if count == 0 {
                    let msg =
                        CString::new(format!("get_physical_device_format_properties2() failed"))
                            .unwrap();
                    gfx_critical_note(msg.as_ptr());
                    return None;
                }

                let mut modifier_props =
                    vec![vk::DrmFormatModifierPropertiesEXT::default(); count as usize];

                let mut drm_format_modifier_props_list =
                    vk::DrmFormatModifierPropertiesListEXT::default()
                        .drm_format_modifier_properties(&mut modifier_props);
                let mut format_properties_2 =
                    vk::FormatProperties2::default().push_next(&mut drm_format_modifier_props_list);

                instance.get_physical_device_format_properties2(
                    physical_device,
                    vk::Format::R8G8B8A8_UNORM,
                    &mut format_properties_2,
                );

                let mut usage_flags = vk::ImageUsageFlags::empty();
                usage_flags |= vk::ImageUsageFlags::COLOR_ATTACHMENT;

                modifier_props.retain(|modifier_prop| {
                    let support = is_dmabuf_supported(
                        instance,
                        physical_device,
                        vk::Format::R8G8B8A8_UNORM,
                        modifier_prop.drm_format_modifier,
                        usage_flags,
                    );
                    support
                });

                if modifier_props.is_empty() {
                    let msg =
                        CString::new(format!("format not supported for dmabuf import")).unwrap();
                    gfx_critical_note(msg.as_ptr());
                    return None;
                }

                let modifiers: Vec<u64> = modifier_props
                    .iter()
                    .map(|modifier_prop| modifier_prop.drm_format_modifier)
                    .collect();

                let mut modifier_list = vk::ImageDrmFormatModifierListCreateInfoEXT::default()
                    .drm_format_modifiers(&modifiers);

                let extent = vk::Extent3D {
                    width: width,
                    height: height,
                    depth: 1,
                };

                let mut external_image_create_info = vk::ExternalMemoryImageCreateInfo::default()
                    .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);

                let mut export_memory_alloc_info = vk::ExportMemoryAllocateInfo::default()
                    .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);

                let flags = vk::ImageCreateFlags::empty();

                let vk_info = vk::ImageCreateInfo::default()
                    .flags(flags)
                    .image_type(vk::ImageType::TYPE_2D)
                    .format(vk::Format::R8G8B8A8_UNORM)
                    .extent(extent)
                    .mip_levels(1)
                    .array_layers(1)
                    .samples(vk::SampleCountFlags::TYPE_1)
                    .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
                    .usage(usage_flags)
                    .sharing_mode(vk::SharingMode::EXCLUSIVE)
                    .initial_layout(vk::ImageLayout::UNDEFINED)
                    .push_next(&mut modifier_list)
                    .push_next(&mut external_image_create_info);

                let image = match device.create_image(&vk_info, None) {
                    Err(err) => {
                        let msg =
                            CString::new(format!("create_image() failed: {:?}", err)).unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }
                    Ok(image) => image,
                };

                let mut image_modifier_properties =
                    vk::ImageDrmFormatModifierPropertiesEXT::default();
                let image_drm_format_modifier =
                    ash::ext::image_drm_format_modifier::Device::new(instance, device);
                let ret = image_drm_format_modifier.get_image_drm_format_modifier_properties(
                    image,
                    &mut image_modifier_properties,
                );
                if ret.is_err() {
                    let msg = CString::new(format!(
                        "get_image_drm_format_modifier_properties() failed: {:?}",
                        ret
                    ))
                    .unwrap();
                    gfx_critical_note(msg.as_ptr());
                    return None;
                }

                let memory_req = device.get_image_memory_requirements(image);

                let mem_properties =
                    instance.get_physical_device_memory_properties(physical_device);

                let index = mem_properties
                    .memory_types
                    .iter()
                    .enumerate()
                    .position(|(i, t)| {
                        ((1 << i) & memory_req.memory_type_bits) != 0
                            && t.property_flags
                                .contains(vk::MemoryPropertyFlags::DEVICE_LOCAL)
                    });

                let index = match index {
                    None => {
                        let msg = CString::new(format!("Failed to get DEVICE_LOCAL memory index"))
                            .unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }
                    Some(index) => index,
                };

                let mut dedicated_memory_info =
                    vk::MemoryDedicatedAllocateInfo::default().image(image);

                let memory_allocate_info = vk::MemoryAllocateInfo::default()
                    .allocation_size(memory_req.size)
                    .memory_type_index(index as u32)
                    .push_next(&mut dedicated_memory_info)
                    .push_next(&mut export_memory_alloc_info);

                let memory = match device.allocate_memory(&memory_allocate_info, None) {
                    Err(err) => {
                        let msg =
                            CString::new(format!("allocate_memory() failed: {:?}", err)).unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }
                    Ok(memory) => memory,
                };

                let result = device.bind_image_memory(image, memory, /* offset */ 0);
                if result.is_err() {
                    let msg =
                        CString::new(format!("bind_image_memory() failed: {:?}", result)).unwrap();
                    gfx_critical_note(msg.as_ptr());
                    return None;
                }

                *out_memory_size = memory_req.size;

                let modifier_prop = modifier_props.iter().find(|prop| {
                    prop.drm_format_modifier == image_modifier_properties.drm_format_modifier
                });
                let modifier_prop = match modifier_prop {
                    None => {
                        let msg = CString::new(format!("failed to find modifier_prop")).unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }
                    Some(modifier_prop) => modifier_prop,
                };

                let plane_count = modifier_prop.drm_format_modifier_plane_count;

                let mut layouts = Vec::new();
                for i in 0..plane_count {
                    let flag = match i {
                        0 => vk::ImageAspectFlags::PLANE_0,
                        1 => vk::ImageAspectFlags::PLANE_1,
                        2 => vk::ImageAspectFlags::PLANE_2,
                        _ => unreachable!(),
                    };
                    let subresource = vk::ImageSubresource::default().aspect_mask(flag);
                    let layout = device.get_image_subresource_layout(image, subresource);
                    layouts.push(layout);
                }

                Some(VkImageHandle {
                    device: device.handle(),
                    image: image,
                    memory: memory,
                    memory_size: memory_req.size,
                    memory_type_index: index as u32,
                    modifier: image_modifier_properties.drm_format_modifier,
                    layouts,
                })
            },
        )
    };

    let image_handle = match image_handle {
        None => {
            return ptr::null_mut();
        }
        Some(image_handle) => image_handle,
    };

    Box::into_raw(Box::new(image_handle))
}

#[no_mangle]
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub unsafe extern "C" fn wgpu_vkimage_delete(
    global: &Global,
    device_id: id::DeviceId,
    handle: *mut VkImageHandle,
) {
    let handle = Box::from_raw(handle);
    handle.destroy(global, device_id);
}

#[no_mangle]
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub extern "C" fn wgpu_vkimage_get_file_descriptor(
    global: &Global,
    device_id: id::DeviceId,
    handle: &VkImageHandle,
) -> i32 {
    unsafe {
        global.device_as_hal::<wgc::api::Vulkan, _, i32>(device_id, |hal_device| {
            let hal_device = match hal_device {
                None => {
                    let msg = CString::new(format!("Vulkan device is invalid")).unwrap();
                    gfx_critical_note(msg.as_ptr());
                    return -1;
                }
                Some(hal_device) => hal_device,
            };

            let device = hal_device.raw_device();
            let instance = hal_device.shared_instance().raw_instance();

            let get_fd_info = vk::MemoryGetFdInfoKHR::default()
                .memory(handle.memory)
                .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);

            let loader = khr::external_memory_fd::Device::new(instance, device);

            return match loader.get_memory_fd(&get_fd_info) {
                Err(..) => -1,
                Ok(fd) => fd,
            };
        })
    }
}

#[no_mangle]
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub extern "C" fn wgpu_vkimage_get_dma_buf_info(handle: &VkImageHandle) -> DMABufInfo {
    let mut offsets: [u64; 3] = [0; 3];
    let mut strides: [u64; 3] = [0; 3];
    let plane_count = handle.layouts.len();
    for i in 0..plane_count {
        offsets[i] = handle.layouts[i].offset;
        strides[i] = handle.layouts[i].row_pitch;
    }

    DMABufInfo {
        is_valid: true,
        modifier: handle.modifier,
        plane_count: plane_count as u32,
        offsets,
        strides,
    }
}

#[cfg(target_os = "macos")]
pub struct MetalSharedEventHandle(metal::SharedEvent);
#[cfg(not(target_os = "macos"))]
pub struct MetalSharedEventHandle;

#[no_mangle]
#[allow(unreachable_code)]
#[allow(unused_variables)]
pub extern "C" fn wgpu_server_get_device_fence_metal_shared_event(
    global: &Global,
    device_id: id::DeviceId,
) -> *mut MetalSharedEventHandle {
    #[cfg(target_os = "macos")]
    {
        let shared_event = unsafe {
            global.device_fence_as_hal::<wgc::api::Metal, _, Option<metal::SharedEvent>>(
                device_id,
                |hal_fence| hal_fence.map(|fence| fence.raw_shared_event().unwrap().clone()),
            )
        };
        let shared_event = match shared_event {
            Some(shared_event) => shared_event,
            None => {
                return ptr::null_mut();
            }
        };
        return Box::into_raw(Box::new(MetalSharedEventHandle(shared_event)));
    }

    ptr::null_mut()
}

#[no_mangle]
#[allow(unreachable_code)]
#[allow(unused_variables)]
pub extern "C" fn wgpu_server_metal_shared_event_signaled_value(
    shared_event: &mut MetalSharedEventHandle,
) -> u64 {
    #[cfg(target_os = "macos")]
    {
        return shared_event.0.signaled_value();
    }

    u64::MAX
}

#[no_mangle]
#[allow(unreachable_code)]
#[allow(unused_variables)]
pub extern "C" fn wgpu_server_delete_metal_shared_event(shared_event: *mut MetalSharedEventHandle) {
    #[cfg(target_os = "macos")]
    {
        let _ = unsafe { Box::from_raw(shared_event) };
    }
}

extern "C" {
    #[allow(dead_code)]
    fn gfx_critical_note(msg: *const c_char);
    #[allow(dead_code)]
    fn wgpu_server_use_external_texture_for_swap_chain(
        param: *mut c_void,
        swap_chain_id: SwapChainId,
    ) -> bool;
    #[allow(dead_code)]
    fn wgpu_server_disable_external_texture_for_swap_chain(
        param: *mut c_void,
        swap_chain_id: SwapChainId,
    );
    #[allow(dead_code)]
    fn wgpu_server_ensure_external_texture_for_swap_chain(
        param: *mut c_void,
        swap_chain_id: SwapChainId,
        device_id: id::DeviceId,
        texture_id: id::TextureId,
        width: u32,
        height: u32,
        format: wgt::TextureFormat,
        usage: wgt::TextureUsages,
    ) -> bool;
    #[allow(dead_code)]
    fn wgpu_server_ensure_external_texture_for_readback(
        param: *mut c_void,
        swap_chain_id: SwapChainId,
        device_id: id::DeviceId,
        texture_id: id::TextureId,
        width: u32,
        height: u32,
        format: wgt::TextureFormat,
        usage: wgt::TextureUsages,
    );
    #[allow(dead_code)]
    fn wgpu_server_get_external_texture_handle(
        param: *mut c_void,
        id: id::TextureId,
    ) -> *mut c_void;
    #[allow(improper_ctypes)]
    #[allow(dead_code)]
    #[cfg(not(any(target_os = "macos", target_os = "ios")))]
    fn wgpu_server_get_vk_image_handle(
        param: *mut c_void,
        texture_id: id::TextureId,
    ) -> *const VkImageHandle;
    #[allow(dead_code)]
    fn wgpu_server_get_dma_buf_fd(param: *mut c_void, id: id::TextureId) -> i32;
    #[allow(dead_code)]
    fn wgpu_server_get_external_io_surface_id(param: *mut c_void, id: id::TextureId) -> u32;
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub unsafe fn is_dmabuf_supported(
    instance: &ash::Instance,
    physical_device: vk::PhysicalDevice,
    format: vk::Format,
    modifier: u64,
    usage: vk::ImageUsageFlags,
) -> bool {
    let mut drm_props = vk::ExternalImageFormatProperties::default();
    let mut props = vk::ImageFormatProperties2::default().push_next(&mut drm_props);

    let mut modifier_info =
        vk::PhysicalDeviceImageDrmFormatModifierInfoEXT::default().drm_format_modifier(modifier);

    let mut external_format_info = vk::PhysicalDeviceExternalImageFormatInfo::default()
        .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);

    let format_info = vk::PhysicalDeviceImageFormatInfo2::default()
        .format(format)
        .ty(vk::ImageType::TYPE_2D)
        .usage(usage)
        .tiling(vk::ImageTiling::DRM_FORMAT_MODIFIER_EXT)
        .push_next(&mut external_format_info)
        .push_next(&mut modifier_info);

    match instance.get_physical_device_image_format_properties2(
        physical_device,
        &format_info,
        &mut props,
    ) {
        Ok(_) => (),
        Err(_) => {
            //debug!(?format, ?modifier, "format not supported for dma import");
            return false;
        }
    }

    drm_props
        .external_memory_properties
        .compatible_handle_types
        .contains(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub fn select_memory_type(
    props: &vk::PhysicalDeviceMemoryProperties,
    flags: vk::MemoryPropertyFlags,
    memory_type_bits: Option<u32>,
) -> Option<u32> {
    for i in 0..props.memory_type_count {
        if let Some(mask) = memory_type_bits {
            if mask & (1 << i) == 0 {
                continue;
            }
        }

        if flags.is_empty()
            || props.memory_types[i as usize]
                .property_flags
                .contains(flags)
        {
            return Some(i);
        }
    }

    None
}

#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
struct VkImageHolder {
    pub device: vk::Device,
    pub image: vk::Image,
    pub memory: vk::DeviceMemory,
    pub fn_destroy_image: vk::PFN_vkDestroyImage,
    pub fn_free_memory: vk::PFN_vkFreeMemory,
}

#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
impl VkImageHolder {
    fn destroy(&self) {
        unsafe {
            (self.fn_destroy_image)(self.device, self.image, ptr::null());
            (self.fn_free_memory)(self.device, self.memory, ptr::null());
        }
    }
}

impl Global {
    #[cfg(target_os = "windows")]
    fn create_texture_with_external_texture_d3d11(
        &self,
        device_id: id::DeviceId,
        texture_id: id::TextureId,
        desc: &wgc::resource::TextureDescriptor,
        swap_chain_id: Option<SwapChainId>,
    ) -> bool {
        let dx12_device = unsafe {
            self.device_as_hal::<wgc::api::Dx12, _, Option<Direct3D12::ID3D12Device>>(
                device_id,
                |hal_device| {
                    if hal_device.is_none() {
                        return None;
                    }
                    hal_device.map(|hal_device| hal_device.raw_device().clone())
                },
            )
        };

        if dx12_device.is_none() {
            let msg = CString::new(format!("dx12 device is none")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        let dx12_device = dx12_device.unwrap();
        let ret = unsafe {
            wgpu_server_ensure_external_texture_for_swap_chain(
                self.owner,
                swap_chain_id.unwrap(),
                device_id,
                texture_id,
                desc.size.width,
                desc.size.height,
                desc.format,
                desc.usage,
            )
        };
        if ret != true {
            let msg = CString::new(format!("Failed to create external texture")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        let handle = unsafe { wgpu_server_get_external_texture_handle(self.owner, texture_id) };
        if handle.is_null() {
            let msg = CString::new(format!("Failed to get external texture handle")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }
        let mut resource: Option<Direct3D12::ID3D12Resource> = None;
        let res =
            unsafe { dx12_device.OpenSharedHandle(Foundation::HANDLE(handle), &mut resource) };
        if res.is_err() || resource.is_none() {
            let msg = CString::new(format!("Failed to open shared handle")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        let hal_texture = unsafe {
            <wgh::api::Dx12 as wgh::Api>::Device::texture_from_raw(
                resource.unwrap(),
                wgt::TextureFormat::Bgra8Unorm,
                wgt::TextureDimension::D2,
                desc.size,
                1,
                1,
            )
        };
        let (_, error) = unsafe {
            self.create_texture_from_hal(Box::new(hal_texture), device_id, &desc, Some(texture_id))
        };
        if let Some(err) = error {
            let msg = CString::new(format!("create_texture_from_hal() failed: {:?}", err)).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        true
    }

    #[allow(dead_code)]
    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
    fn create_texture_with_external_texture_dmabuf(
        &self,
        device_id: id::DeviceId,
        texture_id: id::TextureId,
        desc: &wgc::resource::TextureDescriptor,
        swap_chain_id: Option<SwapChainId>,
    ) -> bool {
        let ret = unsafe {
            wgpu_server_ensure_external_texture_for_swap_chain(
                self.owner,
                swap_chain_id.unwrap(),
                device_id,
                texture_id,
                desc.size.width,
                desc.size.height,
                desc.format,
                desc.usage,
            )
        };
        if ret != true {
            let msg = CString::new(format!("Failed to create external texture")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        let handle = unsafe { wgpu_server_get_vk_image_handle(self.owner, texture_id) };
        if handle.is_null() {
            let msg = CString::new(format!("Failed to get VkImageHandle")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        let vk_image_wrapper = unsafe { &*handle };

        let fd = unsafe { wgpu_server_get_dma_buf_fd(self.owner, texture_id) };
        if fd < 0 {
            let msg = CString::new(format!("Failed to get DMABuf fd")).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        // Ensure to close file descriptor
        let owned_fd = unsafe { OwnedFd::from_raw_fd(fd as RawFd) };

        let image_holder = unsafe {
            self.device_as_hal::<wgc::api::Vulkan, _, Option<VkImageHolder>>(
                device_id,
                |hal_device| {
                    let hal_device = match hal_device {
                        None => {
                            let msg = CString::new(format!("Vulkan device is invalid")).unwrap();
                            gfx_critical_note(msg.as_ptr());
                            return None;
                        }
                        Some(hal_device) => hal_device,
                    };

                    let device = hal_device.raw_device();

                    let extent = vk::Extent3D {
                        width: desc.size.width,
                        height: desc.size.height,
                        depth: 1,
                    };
                    let mut usage_flags = vk::ImageUsageFlags::empty();
                    usage_flags |= vk::ImageUsageFlags::COLOR_ATTACHMENT;

                    let mut external_image_create_info =
                        vk::ExternalMemoryImageCreateInfo::default()
                            .handle_types(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT);

                    let vk_info = vk::ImageCreateInfo::default()
                        .flags(vk::ImageCreateFlags::ALIAS)
                        .image_type(vk::ImageType::TYPE_2D)
                        .format(vk::Format::R8G8B8A8_UNORM)
                        .extent(extent)
                        .mip_levels(1)
                        .array_layers(1)
                        .samples(vk::SampleCountFlags::TYPE_1)
                        .tiling(vk::ImageTiling::OPTIMAL)
                        .usage(usage_flags)
                        .sharing_mode(vk::SharingMode::EXCLUSIVE)
                        .initial_layout(vk::ImageLayout::UNDEFINED)
                        .push_next(&mut external_image_create_info);

                    let image = match device.create_image(&vk_info, None) {
                        Err(err) => {
                            let msg =
                                CString::new(format!("create_image() failed: {:?}", err)).unwrap();
                            gfx_critical_note(msg.as_ptr());
                            return None;
                        }
                        Ok(image) => image,
                    };

                    let memory_req = device.get_image_memory_requirements(image);
                    if memory_req.size > vk_image_wrapper.memory_size {
                        let msg = CString::new(format!("Invalid memory size")).unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }

                    let mut dedicated_memory_info =
                        vk::MemoryDedicatedAllocateInfo::default().image(image);

                    let mut import_memory_fd_info = vk::ImportMemoryFdInfoKHR::default()
                        .handle_type(vk::ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
                        .fd(owned_fd.into_raw_fd());

                    let memory_allocate_info = vk::MemoryAllocateInfo::default()
                        .allocation_size(vk_image_wrapper.memory_size)
                        .memory_type_index(vk_image_wrapper.memory_type_index)
                        .push_next(&mut dedicated_memory_info)
                        .push_next(&mut import_memory_fd_info);

                    let memory = match device.allocate_memory(&memory_allocate_info, None) {
                        Err(err) => {
                            let msg = CString::new(format!("allocate_memory() failed: {:?}", err))
                                .unwrap();
                            gfx_critical_note(msg.as_ptr());
                            return None;
                        }
                        Ok(memory) => memory,
                    };

                    let result = device.bind_image_memory(image, memory, /* offset */ 0);
                    if result.is_err() {
                        let msg = CString::new(format!("bind_image_memory() failed: {:?}", result))
                            .unwrap();
                        gfx_critical_note(msg.as_ptr());
                        return None;
                    }

                    Some(VkImageHolder {
                        device: device.handle(),
                        image: image,
                        memory: memory,
                        fn_destroy_image: device.fp_v1_0().destroy_image,
                        fn_free_memory: device.fp_v1_0().free_memory,
                    })
                },
            )
        };

        let image_holder = match image_holder {
            None => {
                let msg = CString::new(format!("Failed to get vk::Image")).unwrap();
                unsafe {
                    gfx_critical_note(msg.as_ptr());
                }
                return false;
            }
            Some(image_holder) => image_holder,
        };

        let hal_desc = wgh::TextureDescriptor {
            label: None,
            size: desc.size,
            mip_level_count: desc.mip_level_count,
            sample_count: desc.sample_count,
            dimension: desc.dimension,
            format: desc.format,
            usage: wgh::TextureUses::COPY_DST | wgh::TextureUses::COLOR_TARGET,
            memory_flags: wgh::MemoryFlags::empty(),
            view_formats: vec![],
        };

        let image = image_holder.image;

        let hal_texture = unsafe {
            <wgh::api::Vulkan as wgh::Api>::Device::texture_from_raw(
                image,
                &hal_desc,
                Some(Box::new(move || {
                    image_holder.destroy();
                })),
            )
        };

        let (_, error) = unsafe {
            self.create_texture_from_hal(Box::new(hal_texture), device_id, &desc, Some(texture_id))
        };
        if let Some(err) = error {
            let msg = CString::new(format!("create_texture_from_hal() failed: {:?}", err)).unwrap();
            unsafe {
                gfx_critical_note(msg.as_ptr());
            }
            return false;
        }

        true
    }

    #[cfg(target_os = "macos")]
    fn create_texture_with_external_texture_iosurface(
        &self,
        device_id: id::DeviceId,
        texture_id: id::TextureId,
        desc: &wgc::resource::TextureDescriptor,
        swap_chain_id: Option<SwapChainId>,
    ) -> bool {
        let ret = unsafe {
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet)  ]