1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error;
use std::fmt;
use std::mem;

use vk;

#[cfg(any(target_os = "macos", target_os = "ios"))]
#[allow(non_snake_case)]
fn load_static() -> Result<vk::Static, LoadingError> {
    use std::os::raw::c_char;

    extern "C" {
        fn vkGetInstanceProcAddr(instance: vk::Instance, pName: *const c_char)
                                 -> vk::PFN_vkVoidFunction;
    }

    extern "system" fn wrapper(instance: vk::Instance, pName: *const c_char)
                               -> vk::PFN_vkVoidFunction {
        unsafe { vkGetInstanceProcAddr(instance, pName) }
    }

    Ok(vk::Static { GetInstanceProcAddr: wrapper })
}

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn load_static() -> Result<vk::Static, LoadingError> {
    use std::path::Path;
    use std::ptr;
    use shared_library;
    lazy_static! {
        static ref VK_LIB: Result<shared_library::dynamic_library::DynamicLibrary, LoadingError> = {
            #[cfg(windows)] fn get_path() -> &'static Path { Path::new("vulkan-1.dll") }
            #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] fn get_path() -> &'static Path { Path::new("libvulkan.so.1") }
            #[cfg(target_os = "android")] fn get_path() -> &'static Path { Path::new("libvulkan.so") }
            let path = get_path();
            shared_library::dynamic_library::DynamicLibrary::open(Some(path))
                                        .map_err(|err| LoadingError::LibraryLoadFailure(err))
        };
    }

    match *VK_LIB {
        Ok(ref lib) => {
            let mut err = None;
            let result = vk::Static::load(|name| unsafe {
                let name = name.to_str().unwrap();
                match lib.symbol(name) {
                    Ok(s) => s,
                    Err(_) => {
                        // TODO: return error?
                        err = Some(LoadingError::MissingEntryPoint(name.to_owned()));
                        ptr::null()
                    },
                }
            });

            if let Some(err) = err {
                Err(err)
            } else {
                Ok(result)
            }
        },
        Err(ref err) => Err(err.clone()),
    }
}

lazy_static! {
    static ref VK_STATIC: Result<vk::Static, LoadingError> = load_static();

    static ref VK_ENTRY: Result<vk::EntryPoints, LoadingError> = {
        match *VK_STATIC {
            Ok(ref lib) => {
                // At this point we assume that if one of the functions fails to load, it is an
                // implementation bug and not a real-life situation that could be handled by
                // an error.
                Ok(vk::EntryPoints::load(|name| unsafe {
                    mem::transmute(lib.GetInstanceProcAddr(0, name.as_ptr()))
                }))
            },
            Err(ref err) => Err(err.clone()),
        }
    };
}

/// Returns the collection of static functions from the Vulkan loader, or an error if failed to
/// open the loader.
pub fn static_functions() -> Result<&'static vk::Static, LoadingError> {
    VK_STATIC.as_ref().map_err(|err| err.clone())
}

/// Returns the collection of Vulkan entry points from the Vulkan loader, or an error if failed to
/// open the loader.
pub fn entry_points() -> Result<&'static vk::EntryPoints, LoadingError> {
    VK_ENTRY.as_ref().map_err(|err| err.clone())
}

/// Error that can happen when loading the Vulkan loader.
#[derive(Debug, Clone)]
pub enum LoadingError {
    /// Failed to load the Vulkan shared library.
    LibraryLoadFailure(String), // TODO: meh for error type, but this needs changes in shared_library

    /// One of the entry points required to be supported by the Vulkan implementation is missing.
    MissingEntryPoint(String),
}

impl error::Error for LoadingError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            LoadingError::LibraryLoadFailure(_) => {
                "failed to load the Vulkan shared library"
            },
            LoadingError::MissingEntryPoint(_) => {
                "one of the entry points required to be supported by the Vulkan implementation \
                 is missing"
            },
        }
    }

    /*#[inline]
    fn cause(&self) -> Option<&error::Error> {
        match *self {
            LoadingError::LibraryLoadFailure(ref err) => Some(err),
            _ => None
        }
    }*/
}

impl fmt::Display for LoadingError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}