diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index 56e6df1bbf..1a1c9bd59a 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -448,7 +448,7 @@ public void onFrameAvailable(SurfaceTexture surfaceTexture) { @Keep @SuppressWarnings("unused") - void dispatchCreateWidgetLayer(final int aHandle, final Surface aSurface, final int aWidth, final int aHeight) { + void dispatchCreateWidgetLayer(final int aHandle, final Surface aSurface, final int aWidth, final int aHeight, final long aNativeCallback) { runOnUiThread(() -> { final Widget widget = mWidgets.get(aHandle); if (widget == null) { @@ -456,13 +456,16 @@ void dispatchCreateWidgetLayer(final int aHandle, final Surface aSurface, final return; } - Runnable aFirstDrawCallback = null; - if (aSurface != null && !widget.getFirstDraw()) { - aFirstDrawCallback = () -> { + Runnable aFirstDrawCallback = () -> { + if (aNativeCallback != 0) { + queueRunnable(() -> runCallbackNative(aNativeCallback)); + } + if (aSurface != null && !widget.getFirstDraw()) { widget.setFirstDraw(true); updateWidget(widget); - }; - } + } + }; + widget.setSurface(aSurface, aWidth, aHeight, aFirstDrawCallback); @@ -630,7 +633,7 @@ void resumeGeckoViewCompositor() { @Keep @SuppressWarnings("unused") - void renderPointerLayer(final Surface aSurface) { + void renderPointerLayer(final Surface aSurface, final long aNativeCallback) { runOnUiThread(() -> { try { Canvas canvas = aSurface.lockHardwareCanvas(); @@ -653,6 +656,9 @@ void renderPointerLayer(final Surface aSurface) { catch (Exception ex) { ex.printStackTrace(); } + if (aNativeCallback != 0) { + queueRunnable(() -> runCallbackNative(aNativeCallback)); + } }); } @@ -977,4 +983,5 @@ public void resetUIYaw() { private native void hideVRVideoNative(); private native void resetUIYawNative(); private native void setControllersVisibleNative(boolean aVisible); + private native void runCallbackNative(long aCallback); } diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp index 7ba69e9970..2d0ba41a1c 100644 --- a/app/src/main/cpp/BrowserWorld.cpp +++ b/app/src/main/cpp/BrowserWorld.cpp @@ -1250,5 +1250,14 @@ JNI_METHOD(void, resetUIYawNative) crow::BrowserWorld::Instance().ResetUIYaw(); } +JNI_METHOD(void, runCallbackNative) +(JNIEnv* aEnv, jobject, jlong aCallback) { + if (aCallback) { + auto func = reinterpret_cast *>((uintptr_t)aCallback); + (*func)(); + delete func; + } +} + } // extern "C" diff --git a/app/src/main/cpp/Pointer.cpp b/app/src/main/cpp/Pointer.cpp index 50a3eae2ef..4c11c5518a 100644 --- a/app/src/main/cpp/Pointer.cpp +++ b/app/src/main/cpp/Pointer.cpp @@ -127,10 +127,10 @@ Pointer::Load(const DeviceDelegatePtr& aDevice) { m.layer = layer; const float size = kOuterRadius * 2.0f; layer->SetWorldSize(size, size); - layer->SetInitializeDelegate([](const VRLayer& aLayer) { + layer->SetSurfaceChangedDelegate([](const VRLayer& aLayer, VRLayer::SurfaceChange aChange, const std::function& aCallback) { const VRLayerQuad& quad = static_cast(aLayer); - if (quad.IsInitialized()) { - VRBrowser::RenderPointerLayer(quad.GetSurface()); + if (aChange == VRLayer::SurfaceChange::Create) { + VRBrowser::RenderPointerLayer(quad.GetSurface(), aCallback); } }); vrb::CreationContextPtr create = m.context.lock(); diff --git a/app/src/main/cpp/Skybox.cpp b/app/src/main/cpp/Skybox.cpp index 3d27f5bc97..4b1bc080bd 100644 --- a/app/src/main/cpp/Skybox.cpp +++ b/app/src/main/cpp/Skybox.cpp @@ -59,9 +59,12 @@ struct Skybox::State { transform = vrb::Transform::Create(create); if (layer) { root->AddNode(VRLayerNode::Create(create, layer)); - layer->SetInitializeDelegate([=](const VRLayer& aLayer) { + layer->SetSurfaceChangedDelegate([=](const VRLayer& aLayer, VRLayer::SurfaceChange aChange, const std::function& aCallback) { this->layerTextureHandle = layer->GetTextureHandle(); LoadLayer(); + if (aCallback) { + aCallback(); + } }); } else { root->AddNode(transform); diff --git a/app/src/main/cpp/SplashAnimation.cpp b/app/src/main/cpp/SplashAnimation.cpp index 03f435a6ac..5c898a83bd 100644 --- a/app/src/main/cpp/SplashAnimation.cpp +++ b/app/src/main/cpp/SplashAnimation.cpp @@ -97,10 +97,13 @@ SplashAnimation::Load(vrb::RenderContextPtr& aContext, const DeviceDelegatePtr& if (!m.read->IsValid()) { VRB_WARN("Splash FBO is not valid"); } - m.layer->SetInitializeDelegate([=](const VRLayer& aLayer){ - if (aLayer.IsInitialized()) { + m.layer->SetSurfaceChangedDelegate([=](const VRLayer& aLayer, VRLayer::SurfaceChange aChange, const std::function& aCallback){ + if (aChange == VRLayer::SurfaceChange::Create) { m.HandleLayerInitialized(texture); } + if (aCallback) { + aCallback(); + } }); } m.logo = Quad::Create(create, w, w / aspect, m.layer); diff --git a/app/src/main/cpp/VRBrowser.cpp b/app/src/main/cpp/VRBrowser.cpp index e9fd5d6655..bb48e5d74d 100644 --- a/app/src/main/cpp/VRBrowser.cpp +++ b/app/src/main/cpp/VRBrowser.cpp @@ -13,7 +13,7 @@ namespace { static const char* kDispatchCreateWidgetName = "dispatchCreateWidget"; static const char* kDispatchCreateWidgetSignature = "(ILandroid/graphics/SurfaceTexture;II)V"; static const char* kDispatchCreateWidgetLayerName = "dispatchCreateWidgetLayer"; -static const char* kDispatchCreateWidgetLayerSignature = "(ILandroid/view/Surface;II)V"; +static const char* kDispatchCreateWidgetLayerSignature = "(ILandroid/view/Surface;IIJ)V"; static const char* kHandleMotionEventName = "handleMotionEvent"; static const char* kHandleMotionEventSignature = "(IIZFF)V"; static const char* kHandleScrollEventName = "handleScrollEvent"; @@ -33,7 +33,7 @@ static const char* kPauseCompositorSignature = "()V"; static const char* kResumeCompositorName = "resumeGeckoViewCompositor"; static const char* kResumeCompositorSignature = "()V"; static const char* kRenderPointerLayerName = "renderPointerLayer"; -static const char* kRenderPointerLayerSignature = "(Landroid/view/Surface;)V"; +static const char* kRenderPointerLayerSignature = "(Landroid/view/Surface;J)V"; static const char* kGetStorageAbsolutePathName = "getStorageAbsolutePath"; static const char* kGetStorageAbsolutePathSignature = "()Ljava/lang/String;"; static const char* kIsOverrideEnvPathEnabledName = "isOverrideEnvPathEnabled"; @@ -136,9 +136,13 @@ VRBrowser::DispatchCreateWidget(jint aWidgetHandle, jobject aSurface, jint aWidt void -VRBrowser::DispatchCreateWidgetLayer(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight) { +VRBrowser::DispatchCreateWidgetLayer(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight, const std::function& aFirstCompositeCallback) { if (!ValidateMethodID(sEnv, sActivity, sDispatchCreateWidgetLayer, __FUNCTION__)) { return; } - sEnv->CallVoidMethod(sActivity, sDispatchCreateWidgetLayer, aWidgetHandle, aSurface, aWidth, aHeight); + jlong callback = 0; + if (aFirstCompositeCallback) { + callback = reinterpret_cast(new std::function(aFirstCompositeCallback)); + } + sEnv->CallVoidMethod(sActivity, sDispatchCreateWidgetLayer, aWidgetHandle, aSurface, aWidth, aHeight, callback); CheckJNIException(sEnv, __FUNCTION__); } @@ -207,9 +211,13 @@ VRBrowser::ResumeCompositor() { } void -VRBrowser::RenderPointerLayer(jobject aSurface) { +VRBrowser::RenderPointerLayer(jobject aSurface, const std::function& aFirstCompositeCallback) { if (!ValidateMethodID(sEnv, sActivity, sRenderPointerLayer, __FUNCTION__)) { return; } - sEnv->CallVoidMethod(sActivity, sRenderPointerLayer, aSurface); + jlong callback = 0; + if (aFirstCompositeCallback) { + callback = reinterpret_cast(new std::function(aFirstCompositeCallback)); + } + sEnv->CallVoidMethod(sActivity, sRenderPointerLayer, aSurface, callback); CheckJNIException(sEnv, __FUNCTION__); } diff --git a/app/src/main/cpp/VRBrowser.h b/app/src/main/cpp/VRBrowser.h index 575b97ed78..d1216b6a53 100644 --- a/app/src/main/cpp/VRBrowser.h +++ b/app/src/main/cpp/VRBrowser.h @@ -18,7 +18,7 @@ namespace VRBrowser { void InitializeJava(JNIEnv* aEnv, jobject aActivity); void ShutdownJava(); void DispatchCreateWidget(jint aWidgetHandle, jobject aSurfaceTexture, jint aWidth, jint aHeight); -void DispatchCreateWidgetLayer(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight); +void DispatchCreateWidgetLayer(jint aWidgetHandle, jobject aSurface, jint aWidth, jint aHeight, const std::function& aFirstCompositeCallback); void HandleMotionEvent(jint aWidgetHandle, jint aController, jboolean aPressed, jfloat aX, jfloat aY); void HandleScrollEvent(jint aWidgetHandle, jint aController, jfloat aX, jfloat aY); void HandleAudioPose(jfloat qx, jfloat qy, jfloat qz, jfloat qw, jfloat px, jfloat py, jfloat pz); @@ -28,7 +28,7 @@ void HandleBack(); void RegisterExternalContext(jlong aContext); void PauseCompositor(); void ResumeCompositor(); -void RenderPointerLayer(jobject aSurface); +void RenderPointerLayer(jobject aSurface, const std::function& aFirstCompositeCallback); std::string GetStorageAbsolutePath(const std::string& aRelativePath); bool isOverrideEnvPathEnabled(); std::string GetActiveEnvironment(); diff --git a/app/src/main/cpp/VRLayer.cpp b/app/src/main/cpp/VRLayer.cpp index a33efec61a..d95d887f48 100644 --- a/app/src/main/cpp/VRLayer.cpp +++ b/app/src/main/cpp/VRLayer.cpp @@ -24,7 +24,8 @@ struct VRLayer::State { device::Eye currentEye; vrb::Color tintColor; device::EyeRect textureRect[2]; - InitializeDelegate initDelegate; + SurfaceChangedDelegate surfaceChangedDelegate; + std::function pendingEvent; State(): initialized(false), priority(0), @@ -112,13 +113,7 @@ VRLayer::ShouldDrawBefore(const VRLayer& aLayer) { void VRLayer::SetInitialized(bool aInitialized) { - if (m.initialized != aInitialized) { - m.initialized = aInitialized; - if (m.initDelegate) { - m.initDelegate(*this); - } - } - + m.initialized = aInitialized; } void @@ -169,10 +164,11 @@ VRLayer::SetTextureRect(device::Eye aEye, const crow::device::EyeRect &aTextureR } void -VRLayer::SetInitializeDelegate(const VRLayer::InitializeDelegate& aDelegate) { - m.initDelegate = aDelegate; - if (m.initialized && m.initDelegate) { - m.initDelegate(*this); +VRLayer::SetSurfaceChangedDelegate(const crow::VRLayer::SurfaceChangedDelegate &aDelegate){ + m.surfaceChangedDelegate = aDelegate; + if (m.pendingEvent && m.surfaceChangedDelegate) { + m.pendingEvent(); + m.pendingEvent = nullptr; } } @@ -181,6 +177,16 @@ VRLayer::SetDrawInFront(bool aDrawInFront) { m.drawInFront = aDrawInFront; } +void VRLayer::NotifySurfaceChanged(SurfaceChange aChange, const std::function& aFirstCompositeCallback) { + if (m.surfaceChangedDelegate) { + m.surfaceChangedDelegate(*this, aChange, aFirstCompositeCallback); + } else { + m.pendingEvent = [=](){ + NotifySurfaceChanged(aChange, aFirstCompositeCallback); + }; + } +} + // Layer Quad struct VRLayerQuad::State: public VRLayer::State { diff --git a/app/src/main/cpp/VRLayer.h b/app/src/main/cpp/VRLayer.h index b77562fca3..e78c65ffc0 100644 --- a/app/src/main/cpp/VRLayer.h +++ b/app/src/main/cpp/VRLayer.h @@ -29,7 +29,13 @@ class VRLayer { EQUIRECTANGULAR }; - typedef std::function InitializeDelegate; + enum class SurfaceChange { + Create, + Destroy + }; + typedef std::function& aFirstCompositeCallback)> SurfaceChangedDelegate; VRLayer::LayerType GetLayerType() const; bool IsInitialized() const; @@ -52,8 +58,9 @@ class VRLayer { void SetPriority(int32_t aPriority); void SetTintColor(const vrb::Color& aTintColor); void SetTextureRect(device::Eye aEye, const device::EyeRect& aTextureRect); - void SetInitializeDelegate(const InitializeDelegate& aDelegate); + void SetSurfaceChangedDelegate(const SurfaceChangedDelegate& aDelegate); void SetDrawInFront(bool aDrawInFront); + void NotifySurfaceChanged(SurfaceChange aChange, const std::function& aFirstCompositeCallback); protected: struct State; VRLayer(State& aState, LayerType aLayerType); @@ -117,7 +124,6 @@ class VRLayerCube: public VRLayer { GLuint GetTextureHandle() const; bool IsLoaded() const; - void SetTextureHandle(uint32_t aTextureHandle); void SetLoaded(bool aReady); protected: diff --git a/app/src/main/cpp/Widget.cpp b/app/src/main/cpp/Widget.cpp index dff4ed9562..6f871dfea9 100644 --- a/app/src/main/cpp/Widget.cpp +++ b/app/src/main/cpp/Widget.cpp @@ -55,9 +55,9 @@ struct Widget::State { } layer = aLayer; if (layer) { - layer->SetInitializeDelegate([=](const VRLayer& aLayer) { + layer->SetSurfaceChangedDelegate([=](const VRLayer& aLayer, VRLayer::SurfaceChange aChange, const std::function& aCallback) { const VRLayerQuad& layerQuad = static_cast(aLayer); - VRBrowser::DispatchCreateWidgetLayer((jint)aHandle, layerQuad.GetSurface(), layerQuad.GetWidth(), layerQuad.GetHeight()); + VRBrowser::DispatchCreateWidgetLayer((jint)aHandle, layerQuad.GetSurface(), layerQuad.GetWidth(), layerQuad.GetHeight(), aCallback); }); } else { surface = vrb::TextureSurface::Create(render, name); diff --git a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp index 789dbb6f2a..23cdd1255e 100644 --- a/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp +++ b/app/src/oculusvr/cpp/DeviceDelegateOculusVR.cpp @@ -10,7 +10,6 @@ #include "VRLayer.h" #include -#include #include #include "vrb/CameraEye.h" #include "vrb/Color.h" @@ -119,11 +118,15 @@ template class OculusLayer { public: ovrTextureSwapChain * swapChain = nullptr; + bool composited = false; T layer; U ovrLayer; void Init() { layer->SetInitialized(true); + layer->NotifySurfaceChanged(VRLayer::SurfaceChange::Create, [=]() { + composited = true; + }); } virtual void Update(const ovrTracking2& aTracking) { @@ -141,7 +144,7 @@ class OculusLayer { } bool IsDrawRequested() const { - return swapChain && layer->IsDrawRequested(); + return swapChain && composited && layer->IsDrawRequested(); } bool GetDrawInFront() const { @@ -167,6 +170,8 @@ class OculusLayer { swapChain = nullptr; } layer->SetInitialized(false); + composited = false; + layer->NotifySurfaceChanged(VRLayer::SurfaceChange::Destroy, nullptr); } virtual ~OculusLayer() { @@ -180,9 +185,10 @@ typedef std::shared_ptr OculusLayerQuadPtr; class OculusLayerQuad: public OculusLayer { public: - ANativeWindow * nativeWindow = nullptr; jobject surface = nullptr; vrb::FBOPtr fbo; + vrb::RenderContextWeak contextWeak; + JNIEnv * jniEnv = nullptr; static OculusLayerQuadPtr Create(const VRLayerQuadPtr& aLayer) { auto result = std::make_shared(); @@ -195,40 +201,14 @@ class OculusLayerQuad: public OculusLayer { return; } + jniEnv = aEnv; + contextWeak = aContext; + ovrLayer = vrapi_DefaultLayerProjection2(); ovrLayer.Header.SrcBlend = VRAPI_FRAME_LAYER_BLEND_ONE; ovrLayer.Header.DstBlend = VRAPI_FRAME_LAYER_BLEND_ONE_MINUS_SRC_ALPHA; - if (layer->GetSurfaceType() == VRLayerQuad::SurfaceType::AndroidSurface) { - swapChain = vrapi_CreateAndroidSurfaceSwapChain(layer->GetWidth(), layer->GetHeight()); - surface = vrapi_GetTextureSwapChainAndroidSurface(swapChain); - surface = aEnv->NewGlobalRef(surface); - nativeWindow = ANativeWindow_fromSurface(aEnv, surface); - layer->SetSurface(surface); - } else { - swapChain = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_2D, VRAPI_TEXTURE_FORMAT_8888, - layer->GetWidth(), layer->GetHeight(), 1, false); - fbo = vrb::FBO::Create(aContext); - GLuint texture = vrapi_GetTextureSwapChainHandle(swapChain, 0); - VRB_GL_CHECK(glBindTexture(GL_TEXTURE_2D, texture)); - VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); - VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); - vrb::FBO::Attributes attributes; - attributes.depth = false; - attributes.samples = 0; - VRB_GL_CHECK(fbo->SetTextureHandle(texture, layer->GetWidth(), layer->GetHeight(), attributes)); - if (fbo->IsValid()) { - fbo->Bind(); - VRB_GL_CHECK(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); - VRB_GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); - fbo->Unbind(); - } else { - VRB_WARN("FAILED to make valid FBO for OculusLayerQuad"); - } - } - + InitSwapChain(swapChain, surface, fbo); layer->SetResizeDelegate([=]{ Resize(); }); @@ -278,14 +258,67 @@ class OculusLayerQuad: public OculusLayer { } void Resize() { - if (nativeWindow && swapChain) { - ANativeWindow_setBuffersGeometry(nativeWindow, layer->GetWidth(), layer->GetHeight(), 0 /* Format unchanged */); + if (!swapChain) { + return; } + // Delay the destruction of the current swapChain until the new one is composited. + // This is required to prevent a black flicker when resizing. + ovrTextureSwapChain * newSwapChain = nullptr; + jobject newSurface = nullptr; + vrb::FBOPtr newFBO; + InitSwapChain(newSwapChain, newSurface, newFBO); + layer->SetSurface(newSurface); + layer->NotifySurfaceChanged(VRLayer::SurfaceChange::Create, [=]() { + if (swapChain) { + vrapi_DestroyTextureSwapChain(swapChain); + } + if (surface) { + jniEnv->DeleteGlobalRef(surface); + } + swapChain = newSwapChain; + surface = newSurface; + fbo = newFBO; + composited = true; + }); } const ovrLayerHeader2 * Header() const override { return &ovrLayer.Header; } + +private: + void InitSwapChain(ovrTextureSwapChain*& swapChainOut, jobject & surfaceOut, vrb::FBOPtr fboOut) { + if (layer->GetSurfaceType() == VRLayerQuad::SurfaceType::AndroidSurface) { + swapChainOut = vrapi_CreateAndroidSurfaceSwapChain(layer->GetWidth(), layer->GetHeight()); + surfaceOut = vrapi_GetTextureSwapChainAndroidSurface(swapChainOut); + surfaceOut = jniEnv->NewGlobalRef(surfaceOut); + layer->SetSurface(surface); + } else { + swapChainOut = vrapi_CreateTextureSwapChain(VRAPI_TEXTURE_TYPE_2D, VRAPI_TEXTURE_FORMAT_8888, + layer->GetWidth(), layer->GetHeight(), 1, false); + vrb::RenderContextPtr ctx = contextWeak.lock(); + fboOut = vrb::FBO::Create(ctx); + GLuint texture = vrapi_GetTextureSwapChainHandle(swapChainOut, 0); + VRB_GL_CHECK(glBindTexture(GL_TEXTURE_2D, texture)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + VRB_GL_CHECK(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + vrb::FBO::Attributes attributes; + attributes.depth = false; + attributes.samples = 0; + VRB_GL_CHECK(fboOut->SetTextureHandle(texture, layer->GetWidth(), layer->GetHeight(), attributes)); + if (fboOut->IsValid()) { + fboOut->Bind(); + VRB_GL_CHECK(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + VRB_GL_CHECK(glClear(GL_COLOR_BUFFER_BIT)); + fboOut->Unbind(); + } else { + VRB_WARN("FAILED to make valid FBO for OculusLayerQuad"); + } + } + } + }; class OculusLayerCube;