зеркало из https://github.com/mozilla/gecko-dev.git
816 строки
24 KiB
C++
816 строки
24 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <math.h>
|
|
|
|
#include "GLBlitHelper.h"
|
|
#include "GLContextEGL.h"
|
|
#include "GLContextProvider.h"
|
|
#include "GLContextTypes.h"
|
|
#include "GLImages.h"
|
|
#include "GLLibraryEGL.h"
|
|
|
|
#include "gfxPrefs.h"
|
|
#include "gfxVRGVRAPI.h"
|
|
#include "gfxVRGVR.h"
|
|
|
|
#include "mozilla/dom/GamepadEventTypes.h"
|
|
#include "mozilla/dom/GamepadBinding.h"
|
|
#include "mozilla/gfx/Matrix.h"
|
|
#include "mozilla/gfx/Quaternion.h"
|
|
#include "mozilla/jni/Utils.h"
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/layers/TextureHostOGL.h"
|
|
#include "mozilla/Preferences.h"
|
|
|
|
#include "GeckoVRManager.h"
|
|
#include "nsString.h"
|
|
|
|
#include "SurfaceTypes.h"
|
|
|
|
#include "VRManager.h"
|
|
|
|
#define MOZ_CHECK_GVR_ERRORS
|
|
|
|
#if defined(MOZ_CHECK_GVR_ERRORS)
|
|
#define GVR_LOGTAG "GeckoWebVR"
|
|
#include <android/log.h>
|
|
#define GVR_CHECK(X) X; \
|
|
{ \
|
|
gvr_context* context = (mPresentingContext ? mPresentingContext : GetNonPresentingContext()); \
|
|
if (context && (gvr_get_error(context) != GVR_ERROR_NONE)) { \
|
|
__android_log_print(ANDROID_LOG_ERROR, GVR_LOGTAG, \
|
|
"GVR ERROR: %s at:%s:%s:%d", \
|
|
gvr_get_error_string(gvr_get_error(context)), \
|
|
__FILE__, __FUNCTION__, __LINE__); \
|
|
gvr_clear_error(context); \
|
|
} else if (!context) { \
|
|
__android_log_print(ANDROID_LOG_ERROR, GVR_LOGTAG, \
|
|
"UNABLE TO CHECK GVR ERROR: NO CONTEXT"); \
|
|
} \
|
|
}
|
|
#define GVR_LOG(format, ...) __android_log_print(ANDROID_LOG_INFO, GVR_LOGTAG, format, ##__VA_ARGS__);
|
|
#else
|
|
#define GVR_CHECK(X) X
|
|
#define GVR_LOG(...)
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::gl;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::gfx::impl;
|
|
using namespace mozilla::layers;
|
|
using namespace mozilla::dom;
|
|
|
|
namespace {
|
|
static VRDisplayGVR* sContextObserver;
|
|
static RefPtr<GLContextEGL> sGLContextEGL;
|
|
static gvr_context* sNonPresentingContext;
|
|
|
|
gvr_context*
|
|
GetNonPresentingContext() {
|
|
if (!sNonPresentingContext) {
|
|
// Try and restore if it has been lost
|
|
sNonPresentingContext = (gvr_context*)GeckoVRManager::CreateGVRNonPresentingContext();
|
|
}
|
|
return sNonPresentingContext;
|
|
}
|
|
|
|
class SynchronousRunnable : public nsIRunnable {
|
|
public:
|
|
enum class Type {
|
|
PresentingContext,
|
|
NonPresentingContext,
|
|
Pause,
|
|
Resume
|
|
};
|
|
SynchronousRunnable(const Type aType, void* aContext)
|
|
: mType(aType)
|
|
, mContext(aContext)
|
|
, mUpdateMonitor(new Monitor("SynchronousRunnable_for_Android"))
|
|
, mUpdated(false)
|
|
{}
|
|
NS_DECL_THREADSAFE_ISUPPORTS
|
|
nsresult Run() override
|
|
{
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
MonitorAutoLock lock(*mUpdateMonitor);
|
|
if (mType == Type::PresentingContext) {
|
|
SetGVRPresentingContext(mContext);
|
|
} else if (mType == Type::NonPresentingContext) {
|
|
CleanupGVRNonPresentingContext();
|
|
} else if (mType == Type::Pause) {
|
|
SetGVRPaused(true);
|
|
} else if (mType == Type::Resume) {
|
|
SetGVRPaused(false);
|
|
} else {
|
|
GVR_LOG("UNKNOWN SynchronousRunnable::Type!");
|
|
}
|
|
mUpdated = true;
|
|
lock.NotifyAll();
|
|
return NS_OK;
|
|
}
|
|
void Wait()
|
|
{
|
|
MonitorAutoLock lock(*mUpdateMonitor);
|
|
while(!mUpdated) {
|
|
lock.Wait();
|
|
}
|
|
}
|
|
|
|
static bool Dispatch(const Type aType, void* aContext)
|
|
{
|
|
if (!CompositorThreadHolder::IsInCompositorThread()) {
|
|
RefPtr<SynchronousRunnable> runnable = new SynchronousRunnable(aType, aContext);
|
|
CompositorThreadHolder::Loop()->PostTask(do_AddRef(runnable));
|
|
runnable->Wait();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
virtual ~SynchronousRunnable()
|
|
{
|
|
delete mUpdateMonitor;
|
|
}
|
|
|
|
Type mType;
|
|
void* mContext;
|
|
Monitor* mUpdateMonitor;
|
|
bool mUpdated;
|
|
};
|
|
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(SynchronousRunnable, nsIRunnable)
|
|
|
|
void
|
|
mozilla::gfx::SetGVRPresentingContext(void* aGVRPresentingContext)
|
|
{
|
|
if (SynchronousRunnable::Dispatch(SynchronousRunnable::Type::PresentingContext, aGVRPresentingContext)) {
|
|
GVR_LOG("Done waiting for compositor thread to set presenting context.");
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT(sContextObserver);
|
|
if (!sGLContextEGL && aGVRPresentingContext) {
|
|
CreateContextFlags flags = CreateContextFlags::NONE;
|
|
SurfaceCaps caps = SurfaceCaps::ForRGBA();
|
|
nsCString str;
|
|
sGLContextEGL = GLContextEGL::CreateEGLPBufferOffscreenContext(flags, IntSize(4, 4), caps, &str);
|
|
if (!sGLContextEGL->MakeCurrent()) {
|
|
GVR_LOG("Failed to make GL context current");
|
|
}
|
|
}
|
|
sContextObserver->SetPresentingContext(aGVRPresentingContext);
|
|
}
|
|
|
|
void
|
|
mozilla::gfx::CleanupGVRNonPresentingContext()
|
|
{
|
|
if (SynchronousRunnable::Dispatch(SynchronousRunnable::Type::NonPresentingContext, nullptr)) {
|
|
GVR_LOG("Done waiting for compositor thread to set non presenting context.");
|
|
return;
|
|
}
|
|
|
|
if (sNonPresentingContext) {
|
|
sNonPresentingContext = nullptr;
|
|
GeckoVRManager::DestroyGVRNonPresentingContext();
|
|
}
|
|
}
|
|
|
|
void
|
|
mozilla::gfx::SetGVRPaused(const bool aPaused)
|
|
{
|
|
if (SynchronousRunnable::Dispatch((aPaused ? SynchronousRunnable::Type::Pause : SynchronousRunnable::Type::Resume), nullptr)) {
|
|
GVR_LOG("Done waiting for GVR in compositor to: %s",(aPaused ? "Pause" : "Resume"));
|
|
return;
|
|
}
|
|
MOZ_ASSERT(sContextObserver);
|
|
sContextObserver->SetPaused(aPaused);
|
|
}
|
|
|
|
VRDisplayGVR::VRDisplayGVR()
|
|
: VRDisplayHost(VRDeviceType::GVR)
|
|
, mIsPresenting(false)
|
|
, mControllerAdded(false)
|
|
, mPresentingContext(nullptr)
|
|
, mControllerContext(nullptr)
|
|
, mControllerState(nullptr)
|
|
, mViewportList(nullptr)
|
|
, mLeftViewport(nullptr)
|
|
, mRightViewport(nullptr)
|
|
, mSwapChain(nullptr)
|
|
, mFrameBufferSize{0, 0}
|
|
{
|
|
MOZ_COUNT_CTOR_INHERITED(VRDisplayGVR, VRDisplayHost);
|
|
MOZ_ASSERT(GetNonPresentingContext());
|
|
MOZ_ASSERT(!sContextObserver); // There can be only one GVR display at a time.
|
|
sContextObserver = this;
|
|
|
|
strncpy(mDisplayInfo.mDisplayName, "GVR HMD", kVRDisplayNameMaxLen);
|
|
mDisplayInfo.mIsConnected = true;
|
|
mDisplayInfo.mIsMounted = true;
|
|
mDisplayInfo.mCapabilityFlags = VRDisplayCapabilityFlags::Cap_None |
|
|
VRDisplayCapabilityFlags::Cap_Orientation |
|
|
VRDisplayCapabilityFlags::Cap_Position | // Not yet...
|
|
VRDisplayCapabilityFlags::Cap_Present;
|
|
|
|
GVR_CHECK(gvr_refresh_viewer_profile(GetNonPresentingContext()));
|
|
mViewportList = GVR_CHECK(gvr_buffer_viewport_list_create(GetNonPresentingContext()));
|
|
mLeftViewport = GVR_CHECK(gvr_buffer_viewport_create(GetNonPresentingContext()));
|
|
mRightViewport = GVR_CHECK(gvr_buffer_viewport_create(GetNonPresentingContext()));
|
|
UpdateViewport();
|
|
|
|
dom::GamepadHand hand = dom::GamepadHand::Right;
|
|
const gvr_user_prefs* prefs = GVR_CHECK(gvr_get_user_prefs(GetNonPresentingContext()));
|
|
if (prefs) {
|
|
hand = ((gvr_user_prefs_get_controller_handedness(prefs) == GVR_CONTROLLER_RIGHT_HANDED) ?
|
|
dom::GamepadHand::Right : dom::GamepadHand::Left);
|
|
}
|
|
mController = new VRControllerGVR(hand, mDisplayInfo.mDisplayID);
|
|
}
|
|
|
|
VRDisplayGVR::~VRDisplayGVR()
|
|
{
|
|
MOZ_COUNT_DTOR_INHERITED(VRDisplayGVR, VRDisplayHost);
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::ZeroSensor()
|
|
{
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::StartPresentation()
|
|
{
|
|
if (mIsPresenting) {
|
|
return;
|
|
}
|
|
|
|
mIsPresenting = true;
|
|
GeckoVRManager::EnableVRMode();
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::StopPresentation()
|
|
{
|
|
if (!mIsPresenting) {
|
|
return;
|
|
}
|
|
|
|
mIsPresenting = false;
|
|
GeckoVRManager::DisableVRMode();
|
|
}
|
|
|
|
bool
|
|
VRDisplayGVR::SubmitFrame(const mozilla::layers::EGLImageDescriptor* aDescriptor,
|
|
const gfx::Rect& aLeftEyeRect,
|
|
const gfx::Rect& aRightEyeRect)
|
|
{
|
|
if (!mPresentingContext) {
|
|
GVR_LOG("Unable to submit frame. No presenting context")
|
|
return false;
|
|
}
|
|
|
|
if (!sGLContextEGL) {
|
|
GVR_LOG("Unable to submit frame. No GL Context");
|
|
return false;
|
|
}
|
|
|
|
if (!sGLContextEGL->MakeCurrent()) {
|
|
GVR_LOG("Failed to make GL context current");
|
|
return false;
|
|
}
|
|
|
|
EGLImage image = (EGLImage)aDescriptor->image();
|
|
EGLSync sync = (EGLSync)aDescriptor->fence();
|
|
gfx::IntSize size = aDescriptor->size();
|
|
MOZ_ASSERT(mSwapChain);
|
|
GVR_CHECK(gvr_get_recommended_buffer_viewports(mPresentingContext, mViewportList));
|
|
if ((size.width != mFrameBufferSize.width) || (size.height != mFrameBufferSize.height)) {
|
|
mFrameBufferSize.width = size.width;
|
|
mFrameBufferSize.height = size.height;
|
|
GVR_CHECK(gvr_swap_chain_resize_buffer(mSwapChain, 0, mFrameBufferSize));
|
|
GVR_LOG("Resize Swap Chain %d,%d", mFrameBufferSize.width, mFrameBufferSize.height);
|
|
}
|
|
gvr_frame* frame = GVR_CHECK(gvr_swap_chain_acquire_frame(mSwapChain));
|
|
if (!frame) {
|
|
// Sometimes the swap chain seems to not initialized correctly so that
|
|
// frames can not be acquired. Recreating the swap chain seems to fix the
|
|
// issue.
|
|
GVR_LOG("Unable to acquire GVR frame. Recreating swap chain.");
|
|
RecreateSwapChain();
|
|
return false;
|
|
}
|
|
GVR_CHECK(gvr_frame_bind_buffer(frame, 0));
|
|
|
|
EGLint status = LOCAL_EGL_CONDITION_SATISFIED;
|
|
|
|
if (sync) {
|
|
MOZ_ASSERT(sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync));
|
|
status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), sync, 0, LOCAL_EGL_FOREVER);
|
|
}
|
|
|
|
if (status != LOCAL_EGL_CONDITION_SATISFIED) {
|
|
MOZ_ASSERT(status != 0,
|
|
"ClientWaitSync generated an error. Has sync already been destroyed?");
|
|
return false;
|
|
}
|
|
|
|
if (image) {
|
|
GLuint tex = 0;
|
|
sGLContextEGL->fGenTextures(1, &tex);
|
|
|
|
const ScopedSaveMultiTex saveTex(sGLContextEGL, 1, LOCAL_GL_TEXTURE_2D);
|
|
sGLContextEGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex);
|
|
sGLContextEGL->TexParams_SetClampNoMips();
|
|
sGLContextEGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, image);
|
|
sGLContextEGL->BlitHelper()->DrawBlitTextureToFramebuffer(tex, gfx::IntSize(size.width, size.height), gfx::IntSize(mFrameBufferSize.width, mFrameBufferSize.height));
|
|
sGLContextEGL->fDeleteTextures(1, &tex);
|
|
} else {
|
|
GVR_LOG("Unable to submit frame. Unable to extract EGLImage");
|
|
return false;
|
|
}
|
|
GVR_CHECK(gvr_frame_unbind(frame));
|
|
GVR_CHECK(gvr_frame_submit(&frame, mViewportList, mHeadMatrix));
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
FillMatrix(gfx::Matrix4x4 &target, const gvr_mat4f& source)
|
|
{
|
|
target._11 = source.m[0][0];
|
|
target._12 = source.m[0][1];
|
|
target._13 = source.m[0][2];
|
|
target._14 = source.m[0][3];
|
|
target._21 = source.m[1][0];
|
|
target._22 = source.m[1][1];
|
|
target._23 = source.m[1][2];
|
|
target._24 = source.m[1][3];
|
|
target._31 = source.m[2][0];
|
|
target._32 = source.m[2][1];
|
|
target._33 = source.m[2][2];
|
|
target._34 = source.m[2][3];
|
|
target._41 = source.m[3][0];
|
|
target._42 = source.m[3][1];
|
|
target._43 = source.m[3][2];
|
|
target._44 = source.m[3][3];
|
|
}
|
|
|
|
VRHMDSensorState
|
|
VRDisplayGVR::GetSensorState()
|
|
{
|
|
VRHMDSensorState result{};
|
|
|
|
gvr_context* context = (mPresentingContext ? mPresentingContext : GetNonPresentingContext());
|
|
|
|
if (!context) {
|
|
GVR_LOG("Unable to get sensor state. Context is null");
|
|
return result;
|
|
}
|
|
|
|
gvr_clock_time_point when = GVR_CHECK(gvr_get_time_point_now());
|
|
if (mIsPresenting) {
|
|
// 50ms into the future is what GVR docs recommends using for head rotation
|
|
// prediction.
|
|
when.monotonic_system_time_nanos += 50000000;
|
|
}
|
|
mHeadMatrix = GVR_CHECK(gvr_get_head_space_from_start_space_rotation(context, when));
|
|
gvr_mat4f neck = GVR_CHECK(gvr_apply_neck_model(context, mHeadMatrix, 1.0));;
|
|
|
|
gfx::Matrix4x4 m;
|
|
|
|
FillMatrix(m, neck);
|
|
m.Invert();
|
|
gfx::Quaternion rot;
|
|
rot.SetFromRotationMatrix(m);
|
|
|
|
result.flags |= VRDisplayCapabilityFlags::Cap_Orientation;
|
|
result.orientation[0] = rot.x;
|
|
result.orientation[1] = rot.y;
|
|
result.orientation[2] = rot.z;
|
|
result.orientation[3] = rot.w;
|
|
result.angularVelocity[0] = 0.0f;
|
|
result.angularVelocity[1] = 0.0f;
|
|
result.angularVelocity[2] = 0.0f;
|
|
|
|
result.flags |= VRDisplayCapabilityFlags::Cap_Position;
|
|
result.position[0] = m._14;
|
|
result.position[1] = m._24;
|
|
result.position[2] = m._34;
|
|
result.linearVelocity[0] = 0.0f;
|
|
result.linearVelocity[1] = 0.0f;
|
|
result.linearVelocity[2] = 0.0f;
|
|
|
|
UpdateHeadToEye(context);
|
|
CalcViewMatrices(&result, mHeadToEyes);
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::SetPaused(const bool aPaused)
|
|
{
|
|
if (aPaused) {
|
|
if (mPresentingContext) {
|
|
GVR_CHECK(gvr_pause_tracking(mPresentingContext));
|
|
} else if (sNonPresentingContext) {
|
|
GVR_CHECK(gvr_pause_tracking(sNonPresentingContext));
|
|
}
|
|
|
|
if (mControllerContext) {
|
|
GVR_CHECK(gvr_controller_pause(mControllerContext));
|
|
}
|
|
} else {
|
|
if (mPresentingContext) {
|
|
GVR_CHECK(gvr_refresh_viewer_profile(mPresentingContext));
|
|
GVR_CHECK(gvr_resume_tracking(mPresentingContext));
|
|
} else if (sNonPresentingContext) {
|
|
GVR_CHECK(gvr_resume_tracking(sNonPresentingContext));
|
|
}
|
|
|
|
if (mControllerContext) {
|
|
GVR_CHECK(gvr_controller_resume(mControllerContext));
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::SetPresentingContext(void* aGVRPresentingContext)
|
|
{
|
|
MOZ_ASSERT(sGLContextEGL);
|
|
sGLContextEGL->MakeCurrent();
|
|
mPresentingContext = (gvr_context*)aGVRPresentingContext;
|
|
if (mPresentingContext) {
|
|
GVR_CHECK(gvr_initialize_gl(mPresentingContext));
|
|
RecreateSwapChain();
|
|
} else {
|
|
|
|
if (mSwapChain) {
|
|
// gvr_swap_chain_destroy will set the pointer to null
|
|
GVR_CHECK(gvr_swap_chain_destroy(&mSwapChain));
|
|
MOZ_ASSERT(!mSwapChain);
|
|
}
|
|
|
|
// The presentation context has been destroy, probably by the user so increment the presenting
|
|
// generation if we are presenting so that the DOM knows to end the current presentation.
|
|
if (mIsPresenting) {
|
|
mDisplayInfo.mPresentingGeneration++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::UpdateHeadToEye(gvr_context* aContext)
|
|
{
|
|
if (!aContext) {
|
|
return;
|
|
}
|
|
|
|
for (uint32_t eyeIndex = 0; eyeIndex < 2; eyeIndex++) {
|
|
gvr_mat4f eye = GVR_CHECK(gvr_get_eye_from_head_matrix(aContext, eyeIndex));
|
|
mDisplayInfo.mEyeTranslation[eyeIndex].x = -eye.m[0][3];
|
|
mDisplayInfo.mEyeTranslation[eyeIndex].y = -eye.m[1][3];
|
|
mDisplayInfo.mEyeTranslation[eyeIndex].z = -eye.m[2][3];
|
|
mHeadToEyes[eyeIndex] = gfx::Matrix4x4();
|
|
mHeadToEyes[eyeIndex].PreTranslate(eye.m[0][3], eye.m[1][3], eye.m[2][3]);
|
|
}
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::UpdateViewport()
|
|
{
|
|
gvr_context* context = (mPresentingContext ? mPresentingContext : GetNonPresentingContext());
|
|
|
|
if (!context) {
|
|
return;
|
|
}
|
|
|
|
GVR_CHECK(gvr_get_recommended_buffer_viewports(context, mViewportList));
|
|
GVR_CHECK(gvr_buffer_viewport_list_get_item(mViewportList, 0, mLeftViewport));
|
|
GVR_CHECK(gvr_buffer_viewport_list_get_item(mViewportList, 1, mRightViewport));
|
|
|
|
gvr_rectf fov = GVR_CHECK(gvr_buffer_viewport_get_source_fov(mLeftViewport));
|
|
mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Left] = VRFieldOfView(fov.top, fov.right, fov.bottom, fov.left);
|
|
GVR_LOG("FOV:L top:%f right:%f bottom:%f left:%f",(float)fov.top, (float)fov.left, (float)fov.bottom, (float)fov.right);
|
|
|
|
fov = GVR_CHECK(gvr_buffer_viewport_get_source_fov(mRightViewport));
|
|
mDisplayInfo.mEyeFOV[VRDisplayInfo::Eye_Right] = VRFieldOfView(fov.top, fov.right, fov.bottom, fov.left);
|
|
GVR_LOG("FOV:R top:%f right:%f bottom:%f left:%f",(float)fov.top, (float)fov.left, (float)fov.bottom, (float)fov.right);
|
|
|
|
gvr_sizei size = GVR_CHECK(gvr_get_maximum_effective_render_target_size(context));
|
|
mDisplayInfo.mEyeResolution = IntSize(size.width / 2, size.height);
|
|
GVR_LOG("Eye Resolution: %dx%d",mDisplayInfo.mEyeResolution.width,mDisplayInfo.mEyeResolution.height);
|
|
|
|
UpdateHeadToEye(context);
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::RecreateSwapChain()
|
|
{
|
|
MOZ_ASSERT(sGLContextEGL);
|
|
sGLContextEGL->MakeCurrent();
|
|
if (mSwapChain) {
|
|
// gvr_swap_chain_destroy will set the pointer to null
|
|
GVR_CHECK(gvr_swap_chain_destroy(&mSwapChain));
|
|
MOZ_ASSERT(!mSwapChain);
|
|
}
|
|
gvr_buffer_spec* spec = GVR_CHECK(gvr_buffer_spec_create(mPresentingContext));
|
|
mFrameBufferSize = GVR_CHECK(gvr_get_maximum_effective_render_target_size(mPresentingContext));
|
|
GVR_CHECK(gvr_buffer_spec_set_size(spec, mFrameBufferSize));
|
|
GVR_CHECK(gvr_buffer_spec_set_samples(spec, 0));
|
|
GVR_CHECK(gvr_buffer_spec_set_color_format(spec, GVR_COLOR_FORMAT_RGBA_8888));
|
|
GVR_CHECK(gvr_buffer_spec_set_depth_stencil_format(spec, GVR_DEPTH_STENCIL_FORMAT_NONE));
|
|
mSwapChain = GVR_CHECK(gvr_swap_chain_create(mPresentingContext, (const gvr_buffer_spec**)&spec, 1));
|
|
GVR_CHECK(gvr_buffer_spec_destroy(&spec));
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::EnableControllers(const bool aEnable, VRSystemManager* aManager)
|
|
{
|
|
if (aEnable && !mControllerAdded) {
|
|
// Sometimes the gamepad doesn't get removed cleanly so just try to remove it before adding it.
|
|
aManager->RemoveGamepad(mController->GetControllerInfo().mControllerID);
|
|
aManager->AddGamepad(mController->GetControllerInfo());
|
|
mControllerAdded = true;
|
|
} else if (!aEnable && mControllerAdded) {
|
|
mControllerAdded = false;
|
|
aManager->RemoveGamepad(mController->GetControllerInfo().mControllerID);
|
|
}
|
|
|
|
gvr_context* context = mPresentingContext;
|
|
|
|
if (!context) {
|
|
if (mControllerContext) {
|
|
GVR_CHECK(gvr_controller_destroy(&mControllerContext));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((aEnable && mControllerContext) || (!aEnable && !mControllerContext)) {
|
|
return;
|
|
}
|
|
|
|
if (aEnable) {
|
|
if (!mControllerContext) {
|
|
int32_t options = GVR_CHECK(gvr_controller_get_default_options());
|
|
options |= GVR_CONTROLLER_ENABLE_GYRO | GVR_CONTROLLER_ENABLE_ACCEL | GVR_CONTROLLER_ENABLE_ARM_MODEL;
|
|
mControllerContext = GVR_CHECK(gvr_controller_create_and_init(options, context));
|
|
GVR_CHECK(gvr_controller_resume(mControllerContext));
|
|
}
|
|
if (!mControllerState) {
|
|
mControllerState = GVR_CHECK(gvr_controller_state_create());
|
|
}
|
|
} else {
|
|
GVR_CHECK(gvr_controller_pause(mControllerContext));
|
|
GVR_CHECK(gvr_controller_destroy(&mControllerContext));
|
|
}
|
|
}
|
|
|
|
void
|
|
VRDisplayGVR::UpdateControllers(VRSystemManager* aManager)
|
|
{
|
|
if (!mControllerContext) {
|
|
return;
|
|
}
|
|
|
|
GVR_CHECK(gvr_controller_apply_arm_model(mControllerContext, 0, mController->GetHand() == dom::GamepadHand::Right ? GVR_CONTROLLER_RIGHT_HANDED : GVR_CONTROLLER_LEFT_HANDED, GVR_ARM_MODEL_FOLLOW_GAZE, mHeadMatrix));
|
|
GVR_CHECK(gvr_controller_state_update(mControllerContext, 0, mControllerState));
|
|
mController->Update(mControllerState, aManager);
|
|
}
|
|
|
|
|
|
void
|
|
VRDisplayGVR::GetControllers(nsTArray<RefPtr<VRControllerHost> >& aControllerResult)
|
|
{
|
|
aControllerResult.AppendElement(mController.get());
|
|
}
|
|
|
|
VRControllerGVR::VRControllerGVR(dom::GamepadHand aHand, uint32_t aDisplayID)
|
|
: VRControllerHost(VRDeviceType::GVR, aHand, aDisplayID)
|
|
{
|
|
MOZ_COUNT_CTOR_INHERITED(VRControllerGVR, VRControllerHost);
|
|
|
|
VRControlerState& state = mControllerInfo.mControllerState;
|
|
strncpy(state.mControllerName, "Daydream Controller", kVRControllerNameMaxLen);
|
|
|
|
// The gvr_controller_button enum starts with GVR_CONTROLLER_BUTTON_NONE at index zero
|
|
// so the GVR controller has one less button than GVR_CONTROLLER_BUTTON_COUNT specifies.
|
|
state.mNumButtons = GVR_CONTROLLER_BUTTON_COUNT - 1; // Skip dummy none button
|
|
state.mNumAxes = 2;
|
|
state.mNumHaptics = 0;
|
|
}
|
|
|
|
VRControllerGVR::~VRControllerGVR()
|
|
{
|
|
MOZ_COUNT_DTOR_INHERITED(VRControllerGVR, VRControllerHost);
|
|
}
|
|
|
|
void
|
|
VRControllerGVR::Update(gvr_controller_state* aState, VRSystemManager* aManager)
|
|
{
|
|
mPose.Clear();
|
|
|
|
if (gvr_controller_state_get_connection_state(aState) != GVR_CONTROLLER_CONNECTED) {
|
|
return;
|
|
}
|
|
const uint64_t previousPressMask = GetButtonPressed();
|
|
const uint64_t previousTouchMask = GetButtonTouched();
|
|
uint64_t currentPressMask = 0;
|
|
uint64_t currentTouchMask = 0;
|
|
// Index 0 is the dummy button so skip it.
|
|
for (int ix = 1; ix < GVR_CONTROLLER_BUTTON_COUNT; ix++) {
|
|
const uint64_t buttonMask = 0x01 << (ix - 1);
|
|
bool pressed = gvr_controller_state_get_button_state(aState, ix);
|
|
bool touched = pressed;
|
|
if (ix == GVR_CONTROLLER_BUTTON_CLICK) {
|
|
touched = gvr_controller_state_is_touching(aState);
|
|
double xAxis = 0.0;
|
|
double yAxis = 0.0;
|
|
if (touched) {
|
|
gvr_vec2f axes = gvr_controller_state_get_touch_pos(aState);
|
|
xAxis = (axes.x * 2.0) - 1.0;
|
|
yAxis = (axes.y * 2.0) - 1.0;
|
|
}
|
|
aManager->NewAxisMove(0, 0, xAxis);
|
|
aManager->NewAxisMove(0, 1, yAxis);
|
|
}
|
|
if (pressed) {
|
|
currentPressMask |= buttonMask;
|
|
}
|
|
if (touched) {
|
|
currentTouchMask |= buttonMask;
|
|
}
|
|
if (((currentPressMask & buttonMask) ^ (previousPressMask & buttonMask)) ||
|
|
((currentTouchMask & buttonMask) ^ (previousTouchMask & buttonMask))) {
|
|
aManager->NewButtonEvent(0, ix - 1, pressed, touched, pressed ? 1.0 : 0.0);
|
|
}
|
|
}
|
|
SetButtonPressed(currentPressMask);
|
|
SetButtonTouched(currentTouchMask);
|
|
|
|
mPose.flags = dom::GamepadCapabilityFlags::Cap_Orientation | dom::GamepadCapabilityFlags::Cap_Position | dom::GamepadCapabilityFlags::Cap_LinearAcceleration;
|
|
|
|
gvr_quatf ori = gvr_controller_state_get_orientation(aState);
|
|
mPose.orientation[0] = ori.qx;
|
|
mPose.orientation[1] = ori.qy;
|
|
mPose.orientation[2] = ori.qz;
|
|
mPose.orientation[3] = ori.qw;
|
|
mPose.isOrientationValid = true;
|
|
|
|
gvr_vec3f acc = gvr_controller_state_get_accel(aState);
|
|
mPose.linearAcceleration[0] = acc.x;
|
|
mPose.linearAcceleration[1] = acc.y;
|
|
mPose.linearAcceleration[2] = acc.z;
|
|
|
|
gvr_vec3f vel = gvr_controller_state_get_gyro(aState);
|
|
mPose.angularVelocity[0] = vel.x;
|
|
mPose.angularVelocity[1] = vel.y;
|
|
mPose.angularVelocity[2] = vel.z;
|
|
|
|
gvr_vec3f pos = gvr_controller_state_get_position(aState);
|
|
mPose.position[0] = pos.x;
|
|
mPose.position[1] = pos.y;
|
|
mPose.position[2] = pos.z;
|
|
|
|
aManager->NewPoseState(0, mPose);
|
|
}
|
|
|
|
/*static*/ already_AddRefed<VRSystemManagerGVR>
|
|
VRSystemManagerGVR::Create()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (!gfxPrefs::VREnabled()) {
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<VRSystemManagerGVR> manager = new VRSystemManagerGVR();
|
|
return manager.forget();
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::Destroy()
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::Shutdown()
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::Enumerate()
|
|
{
|
|
if (!GeckoVRManager::IsGVRPresent()) {
|
|
return;
|
|
}
|
|
|
|
if (!mGVRHMD) {
|
|
mGVRHMD = new VRDisplayGVR();
|
|
}
|
|
}
|
|
|
|
bool
|
|
VRSystemManagerGVR::ShouldInhibitEnumeration()
|
|
{
|
|
if (VRSystemManager::ShouldInhibitEnumeration()) {
|
|
return true;
|
|
}
|
|
if (mGVRHMD) {
|
|
// When we find an a VR device, don't
|
|
// allow any further enumeration as it
|
|
// may get picked up redundantly by other
|
|
// API's.
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::GetHMDs(nsTArray<RefPtr<VRDisplayHost>>& aHMDResult)
|
|
{
|
|
if (mGVRHMD) {
|
|
aHMDResult.AppendElement(mGVRHMD);
|
|
}
|
|
}
|
|
|
|
bool
|
|
VRSystemManagerGVR::GetIsPresenting()
|
|
{
|
|
if (!mGVRHMD) {
|
|
return false;
|
|
}
|
|
|
|
VRDisplayInfo displayInfo(mGVRHMD->GetDisplayInfo());
|
|
return displayInfo.GetPresentingGroups() != kVRGroupNone;
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::HandleInput()
|
|
{
|
|
if (mGVRHMD) {
|
|
mGVRHMD->UpdateControllers(this);
|
|
}
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::GetControllers(nsTArray<RefPtr<VRControllerHost>>& aControllerResult)
|
|
{
|
|
if (mGVRHMD) {
|
|
mGVRHMD->GetControllers(aControllerResult);
|
|
}
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::ScanForControllers()
|
|
{
|
|
if (mGVRHMD) {
|
|
mGVRHMD->EnableControllers(true, this);
|
|
}
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::RemoveControllers()
|
|
{
|
|
if (mGVRHMD) {
|
|
mGVRHMD->EnableControllers(false, this);
|
|
}
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::VibrateHaptic(uint32_t aControllerIdx,
|
|
uint32_t aHapticIndex,
|
|
double aIntensity,
|
|
double aDuration,
|
|
const VRManagerPromise& aPromise)
|
|
{
|
|
|
|
}
|
|
|
|
void
|
|
VRSystemManagerGVR::StopVibrateHaptic(uint32_t aControllerIdx)
|
|
{
|
|
|
|
}
|
|
|
|
VRSystemManagerGVR::VRSystemManagerGVR()
|
|
: mGVRHMD(nullptr)
|
|
{
|
|
|
|
}
|
|
|
|
VRSystemManagerGVR::~VRSystemManagerGVR()
|
|
{
|
|
|
|
}
|
|
|