Bug 1574745 - Maintain a full window render target when rendering to native layers with CompositorOGL. r=mattwoodrow
authorMarkus Stange <mstange@themasta.com>
Fri, 30 Aug 2019 19:52:40 +0000
changeset 551514 ea690c643c8cba21a81cc2ee279e21ba03b2a8b1
parent 551513 29be5f4832cabc9a1a94268138b47b1af988e1e4
child 551515 1db2a705c75c28805a054c2023401860fad803df
push id11865
push userbtara@mozilla.com
push dateMon, 02 Sep 2019 08:54:37 +0000
treeherdermozilla-beta@37f59c4671b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1574745
milestone70.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 1574745 - Maintain a full window render target when rendering to native layers with CompositorOGL. r=mattwoodrow This makes profiler screenshots and frame recording work again, when native layers are used. The copying is a bit unfortunate. Maybe we can combine this copy with the first downscaling step in the future. Or for frame recording, which doesn't use downscaling, we could readback the layers individually and reassemble them in CPU memory. But both of those solutions are more complex than this. Differential Revision: https://phabricator.services.mozilla.com/D43877
gfx/layers/opengl/CompositorOGL.cpp
gfx/layers/opengl/CompositorOGL.h
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -300,26 +300,28 @@ void CompositorOGL::CleanupResources() {
     // Leak resources!
     mQuadVBO = 0;
     mTriangleVBO = 0;
     mPreviousFrameDoneSync = nullptr;
     mThisFrameDoneSync = nullptr;
     mGLContext = nullptr;
     mPrograms.clear();
     mNativeLayersReferenceRT = nullptr;
+    mFullWindowRenderTarget = nullptr;
     return;
   }
 
   for (std::map<ShaderConfigOGL, ShaderProgramOGL*>::iterator iter =
            mPrograms.begin();
        iter != mPrograms.end(); iter++) {
     delete iter->second;
   }
   mPrograms.clear();
   mNativeLayersReferenceRT = nullptr;
+  mFullWindowRenderTarget = nullptr;
 
 #ifdef MOZ_WIDGET_GTK
   // TextureSources might hold RefPtr<gl::GLContext>.
   // All of them needs to be released to destroy GLContext.
   // GLContextGLX has to be destroyed before related gtk window is destroyed.
   for (auto textureSource : mRegisteredTextureSources) {
     textureSource->DeallocateDeviceData();
   }
@@ -901,39 +903,45 @@ void CompositorOGL::BeginFrameForNativeL
   mPixelsFilled = 0;
 
   // Default blend function implements "OVER"
   mGLContext->fBlendFuncSeparate(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA,
                                  LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
   mGLContext->fEnable(LOCAL_GL_BLEND);
 
   mFrameInProgress = true;
+  mShouldInvalidateWindow = NeedToRecreateFullWindowRenderTarget();
 
   // Make a 1x1 dummy render target so that GetCurrentRenderTarget() returns
   // something non-null even outside of calls to
   // Begin/EndRenderingToNativeLayer.
   if (!mNativeLayersReferenceRT) {
     mNativeLayersReferenceRT =
         CreateRenderTarget(IntRect(0, 0, 1, 1), INIT_MODE_CLEAR);
   }
   SetRenderTarget(mNativeLayersReferenceRT);
+  mWindowRenderTarget = mFullWindowRenderTarget;
 }
 
 Maybe<gfx::IntRect> CompositorOGL::BeginRenderingToNativeLayer(
     const nsIntRegion& aInvalidRegion, const Maybe<gfx::IntRect>& aClipRect,
     const nsIntRegion& aOpaqueRegion, NativeLayer* aNativeLayer) {
   MOZ_RELEASE_ASSERT(aNativeLayer);
   MOZ_RELEASE_ASSERT(mCurrentRenderTarget == mNativeLayersReferenceRT,
                      "Please restore the current render target to the one that "
                      "was in place after the call to BeginFrameForNativeLayers "
                      "before calling BeginRenderingToNativeLayer.");
 
   IntRect rect = aNativeLayer->GetRect();
   IntRegion layerInvalid;
-  layerInvalid.And(aInvalidRegion, rect);
+  if (mShouldInvalidateWindow) {
+    layerInvalid = rect;
+  } else {
+    layerInvalid.And(aInvalidRegion, rect);
+  }
 
   RefPtr<CompositingRenderTarget> rt =
       RenderTargetForNativeLayer(aNativeLayer, layerInvalid);
   if (!rt) {
     return Nothing();
   }
   SetRenderTarget(rt);
   mCurrentNativeLayer = aNativeLayer;
@@ -960,16 +968,65 @@ Maybe<gfx::IntRect> CompositorOGL::Begin
     }
   } else {
     mGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
   }
 
   return Some(rect);
 }
 
+void CompositorOGL::NormalDrawingDone() {
+  // Now is a good time to update mFullWindowRenderTarget.
+  if (!mCurrentNativeLayer) {
+    return;
+  }
+
+  if (!mGLContext->IsSupported(GLFeature::framebuffer_blit)) {
+    return;
+  }
+
+  if (!ShouldRecordFrames()) {
+    // If we are no longer recording a profile, we can drop the render target if
+    // it exists.
+    mWindowRenderTarget = nullptr;
+    mFullWindowRenderTarget = nullptr;
+    return;
+  }
+
+  if (NeedToRecreateFullWindowRenderTarget()) {
+    // We have either (1) just started recording and not yet allocated a
+    // buffer or (2) are already recording and have resized the window. In
+    // either case, we need a new render target.
+    IntRect windowRect(IntPoint(0, 0),
+                       mWidget->GetClientSize().ToUnknownSize());
+    RefPtr<CompositingRenderTarget> rt =
+        CreateRenderTarget(windowRect, INIT_MODE_NONE);
+    mFullWindowRenderTarget =
+        static_cast<CompositingRenderTargetOGL*>(rt.get());
+    mWindowRenderTarget = mFullWindowRenderTarget;
+
+    // Initialize the render target by binding it.
+    RefPtr<CompositingRenderTarget> previousTarget = mCurrentRenderTarget;
+    SetRenderTarget(mFullWindowRenderTarget);
+    SetRenderTarget(previousTarget);
+  }
+
+  // Copy the appropriate rectangle from the layer to mFullWindowRenderTarget.
+  RefPtr<CompositingRenderTargetOGL> layerRT = mCurrentRenderTarget;
+  IntRect copyRect = layerRT->GetClipRect().valueOr(layerRT->GetRect());
+  IntRect sourceRect = copyRect - layerRT->GetOrigin();
+  sourceRect.y = layerRT->GetSize().height - sourceRect.YMost();
+  IntRect destRect = copyRect;
+  destRect.y = mFullWindowRenderTarget->GetSize().height - destRect.YMost();
+  GLuint sourceFBO = layerRT->GetFBO();
+  GLuint destFBO = mFullWindowRenderTarget->GetFBO();
+  mGLContext->BlitHelper()->BlitFramebufferToFramebuffer(
+      sourceFBO, destFBO, sourceRect, destRect, LOCAL_GL_NEAREST);
+}
+
 void CompositorOGL::EndRenderingToNativeLayer() {
   MOZ_RELEASE_ASSERT(mCurrentNativeLayer,
                      "EndRenderingToNativeLayer not paired with a call to "
                      "BeginRenderingToNativeLayer?");
 
   if (StaticPrefs::nglayout_debug_widget_update_flashing()) {
     float r = float(rand()) / RAND_MAX;
     float g = float(rand()) / RAND_MAX;
@@ -1976,16 +2033,17 @@ void CompositorOGL::EndFrame() {
     if (target) {
       CopyToTarget(target, nsIntPoint(), Matrix());
       WriteSnapshotToDumpFile(this, target);
     }
   }
 #endif
 
   mFrameInProgress = false;
+  mShouldInvalidateWindow = false;
 
   if (mTarget) {
     CopyToTarget(mTarget, mTargetBounds.TopLeft(), Matrix());
     mGLContext->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, 0);
     mTarget = nullptr;
     mWindowRenderTarget = nullptr;
     mCurrentRenderTarget = nullptr;
     Compositor::EndFrame();
@@ -2037,16 +2095,27 @@ void CompositorOGL::WaitForGPU() {
                                 LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT,
                                 LOCAL_GL_TIMEOUT_IGNORED);
     mGLContext->fDeleteSync(mPreviousFrameDoneSync);
   }
   mPreviousFrameDoneSync = mThisFrameDoneSync;
   mThisFrameDoneSync = nullptr;
 }
 
+bool CompositorOGL::NeedToRecreateFullWindowRenderTarget() const {
+  if (!ShouldRecordFrames()) {
+    return false;
+  }
+  if (!mFullWindowRenderTarget) {
+    return true;
+  }
+  IntSize windowSize = mWidget->GetClientSize().ToUnknownSize();
+  return mFullWindowRenderTarget->GetSize() != windowSize;
+}
+
 void CompositorOGL::SetDestinationSurfaceSize(const IntSize& aSize) {
   mSurfaceSize.width = aSize.width;
   mSurfaceSize.height = aSize.height;
 }
 
 void CompositorOGL::CopyToTarget(DrawTarget* aTarget,
                                  const nsIntPoint& aTopLeft,
                                  const gfx::Matrix& aTransform) {
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -190,16 +190,18 @@ class CompositorOGL final : public Compo
   void DrawTriangles(const nsTArray<gfx::TexturedTriangle>& aTriangles,
                      const gfx::Rect& aRect, const gfx::IntRect& aClipRect,
                      const EffectChain& aEffectChain, gfx::Float aOpacity,
                      const gfx::Matrix4x4& aTransform,
                      const gfx::Rect& aVisibleRect) override;
 
   bool SupportsLayerGeometry() const override;
 
+  void NormalDrawingDone() override;
+
   void EndFrame() override;
 
   void WaitForGPU() override;
 
   bool SupportsPartialTextureUpdate() override;
 
   bool CanUseCanvasLayerForSize(const gfx::IntSize& aSize) override {
     if (!mGLContext) return false;
@@ -296,16 +298,18 @@ class CompositorOGL final : public Compo
                     const gfx::Rect& aVisibleRect);
 
   void PrepareViewport(CompositingRenderTargetOGL* aRenderTarget);
 
   bool SupportsTextureDirectMapping();
 
   void InsertFrameDoneSync();
 
+  bool NeedToRecreateFullWindowRenderTarget() const;
+
   /** Widget associated with this compositor */
   LayoutDeviceIntSize mWidgetSize;
   RefPtr<GLContext> mGLContext;
   UniquePtr<GLBlitTextureImageHelper> mBlitTextureImageHelper;
   gfx::Matrix4x4 mProjMatrix;
   bool mCanRenderToDefaultFramebuffer = true;
 
 #ifdef XP_DARWIN
@@ -328,17 +332,25 @@ class CompositorOGL final : public Compo
   /** Currently bound render target */
   RefPtr<CompositingRenderTargetOGL> mCurrentRenderTarget;
 
   // The 1x1 dummy render target that's the "current" render target between
   // BeginFrameForNativeLayers and EndFrame but outside pairs of
   // Begin/EndRenderingToNativeLayer. Created on demand.
   RefPtr<CompositingRenderTarget> mNativeLayersReferenceRT;
 
-  CompositingRenderTargetOGL* mWindowRenderTarget;
+  // The render target that profiler screenshots / frame recording read from.
+  // This will be the actual window framebuffer when rendering to a window, and
+  // it will be mFullWindowRenderTarget when rendering to native layers.
+  RefPtr<CompositingRenderTargetOGL> mWindowRenderTarget;
+
+  // Non-null when using native layers and frame recording is requested.
+  // EndNormalDrawing() maintains a copy of the entire window contents in this
+  // render target, by copying from the native layer render targets.
+  RefPtr<CompositingRenderTargetOGL> mFullWindowRenderTarget;
 
   /**
    * VBO that has some basics in it for a textured quad, including vertex
    * coords and texcoords.
    */
   GLuint mQuadVBO;
 
   /**
@@ -359,16 +371,20 @@ class CompositorOGL final : public Compo
    */
   bool mUseExternalSurfaceSize;
 
   /**
    * Have we had DrawQuad calls since the last frame was rendered?
    */
   bool mFrameInProgress;
 
+  // Only true between BeginFromeForNativeLayers and EndFrame, and only if the
+  // full window render target needed to be recreated in the current frame.
+  bool mShouldInvalidateWindow = false;
+
   /*
    * Clear aRect on current render target.
    */
   void ClearRect(const gfx::Rect& aRect) override;
 
   /* Start a new frame.
    */
   Maybe<gfx::IntRect> BeginFrameForWindow(