use rust_webvr_api::VRDisplay;
use rust_webvr_api::VRDisplayData;
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 sparkle::gl;
use sparkle::gl::Gl;
use sparkle::gl::types::GLint;
use sparkle::gl::types::GLuint;
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::mpsc::Sender;
use super::heartbeat::MagicLeapVRMessage;
pub type MagicLeapVRDisplayPtr = Arc<RefCell<MagicLeapVRDisplay>>;
pub struct MagicLeapVRDisplay {
display_data: VRDisplayData,
sender: Sender<MagicLeapVRMessage>,
fbos: Vec<GLuint>,
texture_id_pool: ArcPool<GLuint>,
}
unsafe impl Sync for MagicLeapVRDisplay {}
impl Drop for MagicLeapVRDisplay {
fn drop(&mut self) {
self.stop_present();
}
}
impl VRDisplay for MagicLeapVRDisplay {
fn id(&self) -> u32 {
self.display_data.display_id
}
fn data(&self) -> VRDisplayData {
self.display_data.clone()
}
fn immediate_frame_data(&self, _near: f64, _far: f64) -> VRFrameData {
VRFrameData::default()
}
fn synced_frame_data(&self, _near: f64, _far: f64) -> VRFrameData {
unimplemented!()
}
fn reset_pose(&mut self) {
unimplemented!()
}
fn sync_poses(&mut self) {
unimplemented!()
}
fn future_frame_data(&mut self, near: f64, far: f64) -> VRFutureFrameData {
let (resolver, result) = VRFutureFrameData::blocked();
let _ = self.sender.send(MagicLeapVRMessage::StartFrame(near as f32, far as f32, resolver));
result
}
fn bind_framebuffer(&mut self, _eye_index: u32) {
unimplemented!()
}
fn get_framebuffers(&self) -> Vec<VRFramebuffer> {
unimplemented!()
}
fn render_layer(&mut self, _layer: &VRLayer) {
unreachable!()
}
fn submit_frame(&mut self) {
unreachable!()
}
fn submit_layer(&mut self, gl: &Gl, layer: &VRLayer) {
let pooled_id = self.blit_texture(gl, layer);
let texture_id = pooled_id.texture_id();
let layer = VRLayer { texture_id, ..layer.clone() };
let _ = self.sender.send(MagicLeapVRMessage::StopFrame(layer, pooled_id));
}
fn start_present(&mut self, _attributes: Option<VRFramebufferAttributes>) {
let _ = self.sender.send(MagicLeapVRMessage::StartPresenting);
}
fn stop_present(&mut self) {
let _ = self.sender.send(MagicLeapVRMessage::StopPresenting);
}
fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>, String> {
Ok(vec![])
}
}
impl MagicLeapVRDisplay {
pub(crate) fn new(
display_data: VRDisplayData,
sender: Sender<MagicLeapVRMessage>
) -> MagicLeapVRDisplay {
info!("Creating VRDisplay");
let fbos = Vec::new();
let texture_id_pool = ArcPool::new();
MagicLeapVRDisplay { display_data, sender, fbos, texture_id_pool }
}
fn blit_texture(&mut self, gl: &Gl, layer: &VRLayer) -> PooledGLTextureId {
let (texture_w, texture_h) = layer
.texture_size
.map(|(w, h)| (w as GLint, h as GLint))
.unwrap_or((2560, 960));
let mut current_fbos = [0, 0];
unsafe { gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut current_fbos[0..]) };
unsafe { gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut current_fbos[1..]) };
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
if self.fbos.len() < 2 { self.fbos = gl.gen_framebuffers(2); }
gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.fbos[0]);
gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.fbos[1]);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.framebuffer_texture_2d(gl::READ_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
layer.texture_id, 0);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.viewport(0, 0, texture_w, texture_h);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
let pooled_id = self.pooled_texture_id(gl, texture_w, texture_h);
let texture_id = pooled_id.texture_id();
gl.framebuffer_texture_2d(gl::DRAW_FRAMEBUFFER,
gl::COLOR_ATTACHMENT0,
gl::TEXTURE_2D,
texture_id, 0);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
debug!("Blitting from {} to {} ({}x{})", layer.texture_id, texture_id, texture_w, texture_h);
gl.blit_framebuffer(0, 0, texture_w, texture_h,
0, 0, texture_w, texture_h,
gl::COLOR_BUFFER_BIT, gl::LINEAR);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, current_fbos[0] as GLuint);
gl.bind_framebuffer(gl::READ_FRAMEBUFFER, current_fbos[1] as GLuint);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.flush();
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
pooled_id
}
fn pooled_texture_id(&mut self, gl: &Gl, width: GLint, height: GLint) -> PooledGLTextureId {
let texture_id = self.texture_id_pool.remove().unwrap_or_else(|| {
let texture_id = gl.gen_textures(1)[0];
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.bind_texture(gl::TEXTURE_2D, texture_id);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.tex_image_2d(gl::TEXTURE_2D,
0,
gl::RGBA as GLint,
width, height,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
None);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
gl.bind_texture(gl::TEXTURE_2D, 0);
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
texture_id
});
PooledGLTextureId(self.texture_id_pool.add(texture_id))
}
}
struct ArcPool<T>(Vec<Arc<T>>);
impl<T> ArcPool<T> {
fn new() -> ArcPool<T> {
ArcPool(Vec::new())
}
fn add(&mut self, val: T) -> Arc<T> {
let result = Arc::new(val);
self.0.push(result.clone());
result
}
fn remove(&mut self) -> Option<T> {
let i = self.0.iter().position(|arc| Arc::strong_count(arc) == 1);
i.and_then(|i| Arc::try_unwrap(self.0.swap_remove(i)).ok())
}
}
pub struct PooledGLTextureId(Arc<GLuint>);
impl PooledGLTextureId {
pub fn texture_id(&self) -> GLuint {
*self.0
}
}