Skip to content
This repository has been archived by the owner on Jul 10, 2023. It is now read-only.

Add fetch_gamepads() support on VRDisplay #70

Merged
merged 9 commits into from Apr 23, 2019
13 changes: 12 additions & 1 deletion .travis.yml
@@ -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
Copy link
Member

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?

Copy link
Member Author

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

7 changes: 5 additions & 2 deletions rust-webvr-api/src/vr_display.rs
@@ -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;
Expand All @@ -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>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance of a spec link? (Probably not, but you never know.)

Copy link
Member Author

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -80,4 +83,4 @@ impl PartialEq for VRDisplay {
fn eq(&self, other: &VRDisplay) -> bool {
self.id() == other.id()
}
}
}
4 changes: 3 additions & 1 deletion rust-webvr/Cargo.toml
Expand Up @@ -30,7 +30,6 @@ oculusvr = ["ovr-mobile-sys"]
[dependencies]
rust-webvr-api = { path = "../rust-webvr-api", version = "0.10.4" }
log = "0.4"
libloading = { version = "0.5", optional = true, default-features = false }
gvr-sys = { version = "0.7", optional = true }
ovr-mobile-sys = { version = "0.4", optional = true }
libc = "0.2"
Expand All @@ -39,6 +38,9 @@ gleam = { version = "0.6", optional = true }
glutin = { version = "0.19", optional = true }
winit = { version = "0.18", optional = true }

[target.'cfg(target_os="windows")'.dependencies]
libloading = { version = "0.5", optional = true, default-features = false }

[build-dependencies]
gl_generator = "0.10"
bindgen = "0.49.0"
5 changes: 5 additions & 0 deletions rust-webvr/src/api/glwindow/display.rs
Expand Up @@ -12,6 +12,7 @@ use rust_webvr_api::VRFrameData;
use rust_webvr_api::VRFutureFrameData;
use rust_webvr_api::VRFramebuffer;
use rust_webvr_api::VRFramebufferAttributes;
use rust_webvr_api::VRGamepadPtr;
use rust_webvr_api::VRLayer;
use rust_webvr_api::VRViewport;
use std::cell::RefCell;
Expand Down Expand Up @@ -172,6 +173,10 @@ impl VRDisplay for GlWindowVRDisplay {
fn stop_present(&mut self) {
let _ = self.sender.send(GlWindowVRMessage::StopPresenting);
}

fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>, String> {
Ok(vec![])
}
}

impl GlWindowVRDisplay {
Expand Down
35 changes: 30 additions & 5 deletions rust-webvr/src/api/googlevr/display.rs
@@ -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;
Expand All @@ -26,6 +27,7 @@ const PREDICTION_OFFSET_NANOS: i64 = 50000000; // 50ms

pub struct GoogleVRDisplay {
service: *const GoogleVRService,
gamepad: Option<GoogleVRGamepadPtr>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lenovo Mireage uses googlevr API and has 2 controllers

Copy link
Member Author

Choose a reason for hiding this comment

The 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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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();
Copy link
Member

Choose a reason for hiding this comment

The 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,
Expand All @@ -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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this return a Option<GoogleVRGamepadPtr>?

Copy link
Member Author

Choose a reason for hiding this comment

The 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 poll_events()

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);
Expand Down Expand Up @@ -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) {
Expand Down
80 changes: 23 additions & 57 deletions rust-webvr/src/api/googlevr/service.rs
Expand Up @@ -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")]
Expand All @@ -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>,
Copy link
Member

Choose a reason for hiding this comment

The 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?

Copy link
Member Author

Choose a reason for hiding this comment

The 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")]
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
Expand All @@ -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()
}
Expand All @@ -112,8 +87,7 @@ impl GoogleVRService {
GoogleVRService {
ctx: ptr::null_mut(),
controller_ctx: ptr::null_mut(),
displays: Vec::new(),
gamepads: Vec::new(),
display: None,
}
}

Expand Down Expand Up @@ -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())
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use Option::get_or_insert_with?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried that, we borrow self here so we can't

}

// 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();
}
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion rust-webvr/src/api/mock/display.rs
@@ -1,4 +1,4 @@
use {VRDisplay, VRDisplayData, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRStageParameters, VRLayer, VRViewport};
use {VRDisplay, VRDisplayData, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRGamepadPtr, VRStageParameters, VRLayer, VRViewport};
use rust_webvr_api::utils;
use std::sync::Arc;
use std::cell::RefCell;
Expand Down Expand Up @@ -138,6 +138,10 @@ impl VRDisplay for MockVRDisplay {
// No op
}

fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
Ok(Vec::new())
}

fn submit_frame(&mut self) {
// No op
}
Expand Down
10 changes: 5 additions & 5 deletions rust-webvr/src/api/oculusvr/display.rs
Expand Up @@ -2,7 +2,7 @@
#![cfg(feature = "oculusvr")]

use {VRDisplay, VRDisplayData, VRDisplayCapabilities, VREvent, VRDisplayEvent,
VREyeParameters, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRLayer, VRViewport};
VREyeParameters, VRFramebuffer, VRFramebufferAttributes, VRFrameData, VRGamepadPtr, VRLayer, VRViewport};
use android_injected_glue::ffi as ndk;
use gl;
use egl;
Expand Down Expand Up @@ -113,6 +113,10 @@ impl VRDisplay for OculusVRDisplay {
data
}

fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
Ok(self.gamepads.iter().cloned().map(|g| g as VRGamepadPtr).collect())
}

fn reset_pose(&mut self) {
if !self.activity_paused && self.is_in_vr_mode() {
unsafe {
Expand Down Expand Up @@ -639,10 +643,6 @@ impl OculusVRDisplay {
out.extend(events.drain(..));
self.new_events_hint = false;
}

pub fn fetch_gamepads(&self, out: &mut Vec<OculusVRGamepadPtr>) {
out.extend(self.gamepads.iter().cloned());
}
}

struct OculusEyeFramebuffer {
Expand Down