Safety fixes

For OpenVR needs to be both initialized before Vulkan is, and shut
down before Vulkan is.
This commit is contained in:
Benjamin Saunders
2017-07-23 16:32:33 -07:00
parent d6d3b40fc3
commit d20c4de1c5
2 changed files with 31 additions and 9 deletions

View File

@ -16,7 +16,7 @@ fn print_matrix_4x3(offset: u32, mat: [[f32; 4]; 3]) {
} }
fn main() { fn main() {
let context = match openvr::init(openvr::ApplicationType::Other) { let context = match unsafe { openvr::init(openvr::ApplicationType::Other) } {
Ok(ivr) => ivr, Ok(ivr) => ivr,
Err(err) => { Err(err) => {
println!("Failed to initialize openvr {:?}", err); println!("Failed to initialize openvr {:?}", err);

View File

@ -3,6 +3,7 @@ extern crate openvr_sys;
use std::sync::atomic::{Ordering, AtomicBool, ATOMIC_BOOL_INIT}; use std::sync::atomic::{Ordering, AtomicBool, ATOMIC_BOOL_INIT};
use std::{fmt, error, ptr, mem}; use std::{fmt, error, ptr, mem};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::cell::Cell;
use openvr_sys as sys; use openvr_sys as sys;
@ -25,22 +26,27 @@ static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;
/// Initialize OpenVR /// Initialize OpenVR
/// ///
/// # Panics /// # Panics
///
/// When the library has already been initialized /// When the library has already been initialized
pub fn init(ty: ApplicationType) -> Result<Context, InitError> { ///
/// # Safety
///
/// The `Context` MUST be dropped or shut down with `Context::shutdown` before shutting down the graphics API.
pub unsafe fn init(ty: ApplicationType) -> Result<Context, InitError> {
if INITIALIZED.swap(true, Ordering::Acquire) { if INITIALIZED.swap(true, Ordering::Acquire) {
panic!("OpenVR has already been initialized!"); panic!("OpenVR has already been initialized!");
} }
let mut error = sys::EVRInitError_VRInitError_None; let mut error = sys::EVRInitError_VRInitError_None;
unsafe { sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType) }; sys::VR_InitInternal(&mut error, ty as sys::EVRApplicationType);
if error != sys::EVRInitError_VRInitError_None { if error != sys::EVRInitError_VRInitError_None {
return Err(InitError(error)); return Err(InitError(error));
} }
if !unsafe { sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) } { if !sys::VR_IsInterfaceVersionValid(sys::IVRSystem_Version.as_ptr() as *const i8) {
unsafe { sys::VR_ShutdownInternal() } sys::VR_ShutdownInternal();
return Err(InitError(sys::EVRInitError_VRInitError_Init_InterfaceNotFound)); return Err(InitError(sys::EVRInitError_VRInitError_Init_InterfaceNotFound));
} }
Ok(Context {}) Ok(Context { live: Cell::new(true) })
} }
pub struct System<'a>(&'a sys::VR_IVRSystem_FnTable); pub struct System<'a>(&'a sys::VR_IVRSystem_FnTable);
@ -50,7 +56,7 @@ pub struct RenderModels<'a>(&'a sys::VR_IVRRenderModels_FnTable);
/// Entry points into OpenVR. /// Entry points into OpenVR.
/// ///
/// At most one of this object may exist at a time. /// At most one of this object may exist at a time.
pub struct Context {} pub struct Context { live: Cell<bool> }
fn load<T>(suffix: &[u8]) -> Result<*const T, InitError> { fn load<T>(suffix: &[u8]) -> Result<*const T, InitError> {
let mut magic = Vec::from(b"FnTable:".as_ref()); let mut magic = Vec::from(b"FnTable:".as_ref());
@ -71,10 +77,26 @@ impl Context {
impl Drop for Context { impl Drop for Context {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { sys::VR_ShutdownInternal() } unsafe { self.shutdown() }
}
}
impl Context {
/// Shut down OpenVR. Repeated calls are safe.
///
/// Called implicitly by `Context::drop`. This MUST be called before shutting down the graphics API, or OpenVR may
/// invoke undefined behavior.
///
/// # Safety
///
/// No OpenVR calls may be made after this has been called unless a new `Context` is subsequently constructed.
pub unsafe fn shutdown(&self) {
if self.live.replace(false) {
sys::VR_ShutdownInternal();
INITIALIZED.store(false, Ordering::Release); INITIALIZED.store(false, Ordering::Release);
} }
} }
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ApplicationType { pub enum ApplicationType {