Add fetch_gamepads() support on VRDisplay #70
Changes from all commits
1549d9e
9429332
bae9587
e84b4d2
42c0ab2
c4c37cb
c9ce954
8f331b9
7964f14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,13 @@ | ||
language: rust | ||
script: cargo build --features=googlevr,oculusvr | ||
|
||
rust: | ||
- stable | ||
|
||
before_script: | ||
- rustup target add armv7-linux-androideabi | ||
- rustup target add x86_64-pc-windows-gnu | ||
- sudo apt-get install libc6-dev-i386 | ||
|
||
script: | ||
- cargo check --target x86_64-pc-windows-gnu | ||
- cd rust-webvr && cargo check --features "mock oculusvr googlevr" --no-default-features --target=armv7-linux-androideabi | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
use {VRDisplayData, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRFutureFrameData, VRLayer}; | ||
use {VRDisplayData, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRFutureFrameData, VRGamepadPtr, VRLayer}; | ||
use gleam::gl::Gl; | ||
use std::sync::Arc; | ||
use std::cell::RefCell; | ||
|
@@ -13,6 +13,9 @@ pub trait VRDisplay: Send + Sync { | |
/// Returns the current display data. | ||
fn data(&self) -> VRDisplayData; | ||
|
||
/// Returns gamepads attached to this display | ||
fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>, String>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any chance of a spec link? (Probably not, but you never know.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't a specced method, I need it here to be able to safely fetch gamepads on the servo side without having to further complicate the unsafe code dealing with display pointers. |
||
|
||
/// Returns the immediate VRFrameData of the HMD | ||
/// Should be used when not presenting to the device. | ||
fn immediate_frame_data(&self, near_z: f64, far_z: f64) -> VRFrameData; | ||
|
@@ -80,4 +83,4 @@ impl PartialEq for VRDisplay { | |
fn eq(&self, other: &VRDisplay) -> bool { | ||
self.id() == other.id() | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
#![cfg(feature = "googlevr")] | ||
use {VRDisplay, VRDisplayData, VRDisplayCapabilities, VRFramebuffer, VRFramebufferAttributes, | ||
VREvent, VRDisplayEvent, VREyeParameters, VRFrameData, VRLayer, VRViewport}; | ||
VREvent, VRDisplayEvent, VREyeParameters, VRFrameData, VRLayer, VRViewport, VRGamepadPtr}; | ||
use super::service::GoogleVRService; | ||
use super::gamepad::{GoogleVRGamepad, GoogleVRGamepadPtr}; | ||
use rust_webvr_api::utils; | ||
#[cfg(target_os="android")] | ||
use rust_webvr_api::jni_utils::JNIScope; | ||
|
@@ -26,6 +27,7 @@ const PREDICTION_OFFSET_NANOS: i64 = 50000000; // 50ms | |
|
||
pub struct GoogleVRDisplay { | ||
service: *const GoogleVRService, | ||
gamepad: Option<GoogleVRGamepadPtr>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lenovo Mireage uses googlevr API and has 2 controllers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah but that's not how we use it right now -- I'd rather update the code to handle multiple controllers in a different issue. |
||
ctx: *mut gvr::gvr_context, | ||
viewport_list: *mut gvr::gvr_buffer_viewport_list, | ||
left_eye_vp: *mut gvr::gvr_buffer_viewport, | ||
|
@@ -78,6 +80,10 @@ impl VRDisplay for GoogleVRDisplay { | |
data | ||
} | ||
|
||
fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> { | ||
Ok(self.gamepad.clone().map(|d| d as VRGamepadPtr).into_iter().collect()) | ||
} | ||
|
||
fn immediate_frame_data(&self, near: f64, far: f64) -> VRFrameData { | ||
let mut data = VRFrameData::default(); | ||
unsafe { | ||
|
@@ -311,7 +317,8 @@ impl VRDisplay for GoogleVRDisplay { | |
|
||
impl GoogleVRDisplay { | ||
pub unsafe fn new(service: *const GoogleVRService, | ||
ctx: *mut gvr::gvr_context) -> Arc<RefCell<GoogleVRDisplay>> { | ||
ctx: *mut gvr::gvr_context, | ||
controller_ctx: *mut gvr::gvr_controller_context) -> Arc<RefCell<GoogleVRDisplay>> { | ||
let list = gvr::gvr_buffer_viewport_list_create(ctx); | ||
|
||
// gvr_refresh_viewer_profile must be called before getting recommended bufer viewports. | ||
|
@@ -328,6 +335,13 @@ impl GoogleVRDisplay { | |
let right_eye_vp = gvr::gvr_buffer_viewport_create(ctx); | ||
gvr::gvr_buffer_viewport_list_get_item(list, gvr::gvr_eye::GVR_RIGHT_EYE as usize, right_eye_vp); | ||
|
||
let display_id = utils::new_id(); | ||
let gamepad = GoogleVRGamepad::new(ctx, controller_ctx, display_id).ok(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log the error if this fails? |
||
|
||
if gamepad.is_none() { | ||
warn!("No googlevr gamepad found"); | ||
} | ||
|
||
Arc::new(RefCell::new(GoogleVRDisplay { | ||
service: service, | ||
ctx: ctx, | ||
|
@@ -344,16 +358,21 @@ impl GoogleVRDisplay { | |
synced_head_matrix: gvr_identity_matrix(), | ||
fbo_id: 0, | ||
fbo_texture: 0, | ||
display_id: utils::new_id(), | ||
display_id, | ||
presenting: false, | ||
paused: false, | ||
new_events_hint: false, | ||
pending_events: Mutex::new(Vec::new()), | ||
processed_events: Mutex::new(Vec::new()), | ||
attributes: Default::default(), | ||
gamepad | ||
})) | ||
} | ||
|
||
pub fn gamepad(&self) -> Option<&GoogleVRGamepadPtr> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this return a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't want to clone when it's not necessary, you don't need the new arc for |
||
self.gamepad.as_ref() | ||
} | ||
|
||
unsafe fn initialize_gl(&mut self) { | ||
// Note: In some scenarios gvr_initialize_gl crashes if gvr_refresh_viewer_profile call isn't called before. | ||
gvr::gvr_refresh_viewer_profile(self.ctx); | ||
|
@@ -513,21 +532,27 @@ impl GoogleVRDisplay { | |
// Warning: this function is called from java Main thread | ||
// Use mutexes to ensure thread safety and process the event in sync with the render loop. | ||
#[allow(dead_code)] | ||
pub fn pause(&mut self) { | ||
pub unsafe fn pause(&mut self) { | ||
let mut pending = self.pending_events.lock().unwrap(); | ||
pending.push(VRDisplayEvent::Pause(self.display_id).into()); | ||
|
||
self.new_events_hint = true; | ||
if let Some(ref gamepad) = self.gamepad { | ||
(*gamepad.as_ptr()).pause(); | ||
} | ||
} | ||
|
||
// Warning: this function is called from java Main thread | ||
// Use mutexes to ensure thread safety and process the event in sync with the render loop. | ||
#[allow(dead_code)] | ||
pub fn resume(&mut self) { | ||
pub unsafe fn resume(&mut self) { | ||
let mut pending = self.pending_events.lock().unwrap(); | ||
pending.push(VRDisplayEvent::Resume(self.display_id).into()); | ||
|
||
self.new_events_hint = true; | ||
if let Some(ref gamepad) = self.gamepad { | ||
(*gamepad.as_ptr()).resume(); | ||
} | ||
} | ||
|
||
fn handle_events(&mut self) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,6 @@ | |
|
||
use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRGamepadPtr}; | ||
use super::display::{GoogleVRDisplay, GoogleVRDisplayPtr}; | ||
use super::gamepad::{GoogleVRGamepad, GoogleVRGamepadPtr}; | ||
#[cfg(target_os="android")] | ||
use rust_webvr_api::jni_utils::JNIScope; | ||
#[cfg(target_os="android")] | ||
|
@@ -17,8 +16,7 @@ const SERVICE_CLASS_NAME:&'static str = "com/rust/webvr/GVRService"; | |
pub struct GoogleVRService { | ||
ctx: *mut gvr::gvr_context, | ||
controller_ctx: *mut gvr::gvr_controller_context, | ||
displays: Vec<GoogleVRDisplayPtr>, | ||
gamepads: Vec<GoogleVRGamepadPtr>, | ||
display: Option<GoogleVRDisplayPtr>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like an odd change, does Google VR really guarantee only one display? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From the API it seems so? Either way, our API usage guarantees one display, so the question is moot for the purposes of this PR. |
||
#[cfg(target_os="android")] | ||
pub java_object: ndk::jobject, | ||
#[cfg(target_os="android")] | ||
|
@@ -46,36 +44,13 @@ impl VRService for GoogleVRService { | |
} | ||
|
||
fn fetch_displays(&mut self) -> Result<Vec<VRDisplayPtr>,String> { | ||
// Return cached displays if available | ||
if self.is_initialized() && self.displays.len() > 0 { | ||
return Ok(self.clone_displays()); | ||
} | ||
|
||
// Ensure that there are not initialization errors | ||
try!(self.initialize()); | ||
let display = unsafe { GoogleVRDisplay::new(self, self.ctx) }; | ||
self.displays.push(display); | ||
|
||
Ok(self.clone_displays()) | ||
let display = self.init_display()?; | ||
Ok(vec![display.clone()]) | ||
} | ||
|
||
fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> { | ||
// Return cached gamepads if available | ||
if self.is_initialized() && self.gamepads.len() > 0 { | ||
return Ok(self.clone_gamepads()); | ||
} | ||
try!(self.initialize()); | ||
|
||
let gamepad = unsafe { | ||
let display_id = match self.displays.first() { | ||
Some(display) => display.borrow().id(), | ||
None => 0 | ||
}; | ||
try!(GoogleVRGamepad::new(self.ctx, self.controller_ctx, display_id)) | ||
}; | ||
self.gamepads.push(gamepad); | ||
|
||
Ok(self.clone_gamepads()) | ||
let display = self.init_display()?; | ||
display.borrow_mut().fetch_gamepads() | ||
} | ||
|
||
fn is_available(&self) -> bool { | ||
|
@@ -84,11 +59,12 @@ impl VRService for GoogleVRService { | |
|
||
fn poll_events(&self) -> Vec<VREvent> { | ||
let mut events = Vec::new(); | ||
for display in &self.displays { | ||
display.borrow_mut().poll_events(&mut events); | ||
} | ||
for gamepad in &self.gamepads { | ||
gamepad.borrow_mut().handle_events(); | ||
if let Some(ref display) = self.display { | ||
let mut d = display.borrow_mut(); | ||
d.poll_events(&mut events); | ||
if let Some(ref gp) = d.gamepad() { | ||
gp.borrow_mut().handle_events(); | ||
} | ||
} | ||
events | ||
} | ||
|
@@ -100,8 +76,7 @@ impl GoogleVRService { | |
GoogleVRService { | ||
ctx: ptr::null_mut(), | ||
controller_ctx: ptr::null_mut(), | ||
displays: Vec::new(), | ||
gamepads: Vec::new(), | ||
display: None, | ||
java_object: ptr::null_mut(), | ||
java_class: ptr::null_mut() | ||
} | ||
|
@@ -112,8 +87,7 @@ impl GoogleVRService { | |
GoogleVRService { | ||
ctx: ptr::null_mut(), | ||
controller_ctx: ptr::null_mut(), | ||
displays: Vec::new(), | ||
gamepads: Vec::new(), | ||
display: None, | ||
} | ||
} | ||
|
||
|
@@ -171,45 +145,37 @@ impl GoogleVRService { | |
return !self.ctx.is_null(); | ||
} | ||
|
||
fn clone_displays(&self) -> Vec<VRDisplayPtr> { | ||
self.displays.iter().map(|d| d.clone() as VRDisplayPtr).collect() | ||
} | ||
fn init_display(&mut self) -> Result<&GoogleVRDisplayPtr, String> { | ||
self.initialize()?; | ||
|
||
fn clone_gamepads(&self) -> Vec<VRGamepadPtr> { | ||
self.gamepads.iter().map(|d| d.clone() as VRGamepadPtr).collect() | ||
if let Some(ref d) = self.display { | ||
Ok(d) | ||
} else { | ||
self.display = unsafe { Some(GoogleVRDisplay::new(self, self.ctx, self.controller_ctx)) }; | ||
Ok(self.display.as_ref().unwrap()) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tried that, we borrow |
||
} | ||
|
||
// Called from Java main thread | ||
// Pause & resume methods are thread safe | ||
#[cfg(target_os="android")] | ||
fn on_pause(&mut self) { | ||
for display in &self.displays { | ||
if let Some(ref display) = self.display { | ||
unsafe { | ||
(*display.as_ptr()).pause(); | ||
} | ||
} | ||
|
||
for gamepad in &self.gamepads { | ||
unsafe { | ||
(*gamepad.as_ptr()).pause(); | ||
} | ||
} | ||
} | ||
|
||
// Called from Java main thread | ||
// Pause & resume methods are thread safe | ||
#[cfg(target_os="android")] | ||
fn on_resume(&mut self) { | ||
for display in &self.displays { | ||
if let Some(ref display) = self.display { | ||
unsafe { | ||
(*display.as_ptr()).resume(); | ||
} | ||
} | ||
for gamepad in &self.gamepads { | ||
unsafe { | ||
(*gamepad.as_ptr()).resume(); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not obvious why these targets?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We cfg(Android) for these, they can't be tested normally