Bug 1492554 - Make not composited WebGL canvas work with WebVR r=kip,jgilbert
authorImanol Fernandez <mortimergoro@gmail.com>
Fri, 28 Sep 2018 19:25:21 +0000
changeset 438736 9dbf3ababbfe6293e7f046f0a0d0e8e38aa157cf
parent 438735 2d322a75865c81bf97ec7bf58725ba3c0a9c95e6
child 438737 4c34c45253ef2eb49b8f5a726d9c6c4e8eb56de5
push id70102
push userrbarker@mozilla.com
push dateFri, 28 Sep 2018 19:46:16 +0000
treeherderautoland@9dbf3ababbfe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskip, jgilbert
bugs1492554
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1492554 - Make not composited WebGL canvas work with WebVR r=kip,jgilbert WebGL canvases not composited (e.g. not added to the DOM) don't work in WebVR. WebGLContext::InitializeCanvasRenderer is only called when 2D compositor renders a WebGL canvas for the first time. I tried to mimic what InitializeCanvasRenderer does internally as a workaround. Differential Revision: https://phabricator.services.mozilla.com/D6921
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -45,16 +45,17 @@
 #include "nsIVariant.h"
 #include "nsIWidget.h"
 #include "nsIXPConnect.h"
 #include "nsServiceManagerUtils.h"
 #include "SVGObserverUtils.h"
 #include "prenv.h"
 #include "ScopedGLHelpers.h"
 #include "VRManagerChild.h"
+#include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 #include "mozilla/layers/WebRenderUserData.h"
 #include "mozilla/layers/WebRenderCanvasRenderer.h"
 
 // Local
 #include "CanvasUtils.h"
 #include "WebGL1Context.h"
 #include "WebGLActiveInfo.h"
@@ -77,20 +78,16 @@
 #ifdef MOZ_WIDGET_COCOA
 #include "nsCocoaFeatures.h"
 #endif
 
 #ifdef XP_WIN
 #include "WGLLibrary.h"
 #endif
 
-#if defined(MOZ_WIDGET_ANDROID)
-#include "mozilla/layers/ImageBridgeChild.h"
-#endif
-
 // Generated
 #include "mozilla/dom/WebGLRenderingContextBinding.h"
 
 
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
@@ -140,16 +137,17 @@ WebGLContext::WebGLContext()
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
     mDisableExtensions = false;
     mIsMesa = false;
     mEmitContextLostErrorOnce = false;
     mWebGLError = 0;
     mUnderlyingGLError = 0;
+    mVRReady = false;
 
     mContextLostErrorSet = false;
 
     mViewportX = 0;
     mViewportY = 0;
     mViewportWidth = 0;
     mViewportHeight = 0;
 
@@ -1264,16 +1262,17 @@ WebGLContext::InitializeCanvasRenderer(n
 
     data.mGLContext = gl;
     data.mSize = DrawingBufferSize();
     data.mHasAlpha = mOptions.alpha;
     data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
 
     aRenderer->Initialize(data);
     aRenderer->SetDirty();
+    mVRReady = true;
     return true;
 }
 
 layers::LayersBackend
 WebGLContext::GetCompositorBackendType() const
 {
     if (mCanvasElement) {
         return mCanvasElement->GetCompositorBackendType();
@@ -2302,16 +2301,18 @@ WebGLContext::GetUnpackSize(bool isFunc3
 
 #if defined(MOZ_WIDGET_ANDROID)
 already_AddRefed<layers::SharedSurfaceTextureClient>
 WebGLContext::GetVRFrame()
 {
     if (!gl)
         return nullptr;
 
+    EnsureVRReady();
+
     // Create a custom GLScreenBuffer for VR.
     if (!mVRScreen) {
         auto caps = gl->Screen()->mCaps;
         mVRScreen = GLScreenBuffer::Create(gl, gfx::IntSize(1, 1), caps);
 
         RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
         if (imageBridge) {
             TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
@@ -2338,16 +2339,17 @@ WebGLContext::GetVRFrame()
     sharedSurface->Surf()->ProducerRelease();
 
     return sharedSurface.forget();
 }
 #else
 already_AddRefed<layers::SharedSurfaceTextureClient>
 WebGLContext::GetVRFrame()
 {
+  EnsureVRReady();
   /**
    * Swap buffers as though composition has occurred.
    * We will then share the resulting front buffer to be submitted to the VR
    * compositor.
    */
   BeginComposition();
   EndComposition();
 
@@ -2362,16 +2364,45 @@ WebGLContext::GetVRFrame()
   if (!sharedSurface)
       return nullptr;
 
   return sharedSurface.forget();
 }
 
 #endif  // ifdefined(MOZ_WIDGET_ANDROID)
 
+void
+WebGLContext::EnsureVRReady()
+{
+    if (mVRReady) {
+        return;
+    }
+
+    // Make not composited canvases work with WebVR. See bug #1492554
+    // WebGLContext::InitializeCanvasRenderer is only called when the 2D compositor renders a WebGL canvas
+    // for the first time. This causes canvases not added to the DOM not to work properly with WebVR.
+    // Here we mimic what InitializeCanvasRenderer does internally as a workaround.
+    const auto imageBridge = ImageBridgeChild::GetSingleton();
+    if (imageBridge) {
+        const auto caps = gl->Screen()->mCaps;
+        auto flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
+        if (!IsPremultAlpha() && mOptions.alpha) {
+            flags |= TextureFlags::NON_PREMULTIPLIED;
+        }
+        auto factory = gl::GLScreenBuffer::CreateFactory(gl, caps, imageBridge.get(), flags);
+        gl->Screen()->Morph(std::move(factory));
+#if defined(MOZ_WIDGET_ANDROID)
+        // On Android we are using a different GLScreenBuffer for WebVR, so we need a resize here because 
+        // PresentScreenBuffer() may not be called for the gl->Screen() after we set the new factory.
+        gl->Screen()->Resize(DrawingBufferSize());
+#endif
+        mVRReady = true;
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static inline size_t
 SizeOfViewElem(const dom::ArrayBufferView& view)
 {
     const auto& elemType = view.Type();
     if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
         return 1;
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -692,16 +692,17 @@ public:
     bool IsVertexArray(const WebGLVertexArray* obj);
 
     void LineWidth(GLfloat width);
     void LinkProgram(WebGLProgram& prog);
     void PixelStorei(GLenum pname, GLint param);
     void PolygonOffset(GLfloat factor, GLfloat units);
 
     already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame();
+    void EnsureVRReady();
 
     ////
 
     webgl::PackingInfo
     ValidImplementationColorReadPI(const webgl::FormatUsageInfo* usage) const;
 
 protected:
     bool ReadPixels_SharedPrecheck(dom::CallerType aCallerType,
@@ -1483,16 +1484,17 @@ protected:
     bool mOptionsFrozen;
     bool mDisableExtensions;
     bool mIsMesa;
     bool mLoseContextOnMemoryPressure;
     bool mCanLoseContextInForeground;
     bool mRestoreWhenVisible;
     bool mShouldPresent;
     bool mDisableFragHighP;
+    bool mVRReady;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture = 0;
     GLenum mDefaultFB_DrawBuffer0 = 0;
     GLenum mDefaultFB_ReadBuffer = 0;