Bug 1646835 - implement basic SWGL compositor. r?jrmuizel draft
authorLee Salzman <lsalzman@mozilla.com>
Tue, 23 Jun 2020 23:38:32 +0000
changeset 2999954 76d569db7f3407620eff3854a12747074402d090
parent 2999953 41579184dcbfbc1324bfd311a0d2b997b284cb6a
child 2999955 6bb30a09b0cacb5b4972e2c3c80e847ee691be88
push id558589
push userreviewbot
push dateTue, 23 Jun 2020 23:39:01 +0000
treeherdertry@725326fb9858 [default view] [failures only]
reviewersjrmuizel
bugs1646835
milestone79.0a1
Bug 1646835 - implement basic SWGL compositor. r?jrmuizel Summary: This is a first implementation of a software-only SWGL RenderCompositor that relies on the existing widget hooks for Basic compositing. It attempts to lock the widget and get a DT whose underlying data it can supply directly to SWGL to composite to. Critically, it does not rely on OpenGL or any other form of HW accel to function. The interface between the RenderCompositor and SWGL will be further refined in follow-up patches. Differential Revision: https://phabricator.services.mozilla.com/D80268 Depends on D80267 Test Plan: Reviewers: jrmuizel Subscribers: Bug #: 1646835 Differential Diff: PHID-DIFF-jq2ikgwpolprjs4jzygd
gfx/webrender_bindings/RenderCompositor.cpp
gfx/webrender_bindings/RenderCompositor.h
gfx/webrender_bindings/RenderCompositorSWGL.cpp
gfx/webrender_bindings/RenderCompositorSWGL.h
gfx/webrender_bindings/RendererOGL.cpp
gfx/webrender_bindings/moz.build
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/src/swgl_bindings.rs
--- a/gfx/webrender_bindings/RenderCompositor.cpp
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -117,17 +117,20 @@ UniquePtr<RenderCompositor> RenderCompos
 #endif
 }
 
 RenderCompositor::RenderCompositor(RefPtr<widget::CompositorWidget>&& aWidget)
     : mWidget(aWidget) {}
 
 RenderCompositor::~RenderCompositor() = default;
 
-bool RenderCompositor::MakeCurrent() { return gl()->MakeCurrent(); }
+bool RenderCompositor::MakeCurrent() {
+  gl::GLContext* context = gl();
+  return !context || context->MakeCurrent();
+}
 
 bool RenderCompositor::IsContextLost() {
   // XXX Add glGetGraphicsResetStatus handling for checking rendering context
   // has not been lost
   return false;
 }
 
 }  // namespace wr
--- a/gfx/webrender_bindings/RenderCompositor.h
+++ b/gfx/webrender_bindings/RenderCompositor.h
@@ -33,16 +33,20 @@ class RenderCompositor {
   static UniquePtr<RenderCompositor> Create(
       RefPtr<widget::CompositorWidget>&& aWidget);
 
   RenderCompositor(RefPtr<widget::CompositorWidget>&& aWidget);
   virtual ~RenderCompositor();
 
   virtual bool BeginFrame() = 0;
 
+  // Optional handler in case the frame was aborted allowing the compositor
+  // to clean up relevant resources if required.
+  virtual void CancelFrame() {}
+
   // Called to notify the RenderCompositor that all of the commands for a frame
   // have been pushed to the queue.
   // @return a RenderedFrameId for the frame
   virtual RenderedFrameId EndFrame(
       const nsTArray<DeviceIntRect>& aDirtyRects) = 0;
   // Returns false when waiting gpu tasks is failed.
   // It might happen when rendering context is lost.
   virtual bool WaitForGPU() { return true; }
@@ -51,16 +55,21 @@ class RenderCompositor {
   // @return the last (highest) completed RenderedFrameId
   virtual RenderedFrameId GetLastCompletedFrameId() {
     return mLatestRenderFrameId.Prev();
   }
 
   // Update FrameId when WR rendering does not happen.
   virtual RenderedFrameId UpdateFrameId() { return GetNextRenderFrameId(); }
 
+  // Return a data mapping of the underlying framebuffer if possible.
+  virtual bool GetMappedBuffer(uint8_t** aData, int32_t* aStride) {
+    return false;
+  }
+
   virtual void Pause() = 0;
   virtual bool Resume() = 0;
   // Called when WR rendering is skipped
   virtual void Update() {}
 
   virtual gl::GLContext* gl() const { return nullptr; }
 
   virtual bool MakeCurrent();
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorSWGL.cpp
@@ -0,0 +1,146 @@
+/* -*- 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 "RenderCompositorSWGL.h"
+
+#include "mozilla/widget/CompositorWidget.h"
+
+namespace mozilla {
+namespace wr {
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorSWGL::Create(
+    RefPtr<widget::CompositorWidget>&& aWidget) {
+  return MakeUnique<RenderCompositorSWGL>(std::move(aWidget));
+}
+
+RenderCompositorSWGL::RenderCompositorSWGL(
+    RefPtr<widget::CompositorWidget>&& aWidget)
+    : RenderCompositor(std::move(aWidget)) {
+  ClearMappedBuffer();
+}
+
+RenderCompositorSWGL::~RenderCompositorSWGL() {}
+
+void RenderCompositorSWGL::ClearMappedBuffer() {
+  mMap.mData = nullptr;
+  mMap.mStride = 0;
+  mDT = nullptr;
+}
+
+bool RenderCompositorSWGL::BeginFrame() {
+  // Request a new draw target to use from the widget...
+  ClearMappedBuffer();
+  LayoutDeviceIntRect bounds(LayoutDeviceIntPoint(), GetBufferSize());
+  mRegion = LayoutDeviceIntRegion(bounds);
+  layers::BufferMode bufferMode = layers::BufferMode::BUFFERED;
+  mDT = mWidget->StartRemoteDrawingInRegion(mRegion, &bufferMode);
+  if (!mDT) {
+    return false;
+  }
+  // Attempt to lock the underlying buffer directly from the draw target.
+  // Verify that the size at least matches what the widget claims and that
+  // the format is BGRA8 as SWGL requires.
+  uint8_t* data = nullptr;
+  IntSize size;
+  int32_t stride = 0;
+  SurfaceFormat format = SurfaceFormat::UNKNOWN;
+  if (!mSurface && mDT->LockBits(&data, &size, &stride, &format) &&
+      (size != bounds.Size().ToUnknownSize() ||
+       (format != SurfaceFormat::B8G8R8A8 &&
+        format != SurfaceFormat::B8G8R8X8))) {
+    // We tried to lock the DT and it succeeded, but the size or format
+    // of the data is not compatible, so just release it and fall back below...
+    mDT->ReleaseBits(data);
+    data = nullptr;
+  }
+  // If locking succeeded above, just use that.
+  if (data) {
+    mMap.mData = data;
+    mMap.mStride = stride;
+  } else {
+    // If we couldn't lock the DT above, then allocate a data surface and map
+    // that for usage with SWGL.
+    size = bounds.Size().ToUnknownSize();
+    if (!mSurface || mSurface->GetSize() != size) {
+      mSurface =
+          Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
+    }
+    if (!mSurface || !mSurface->Map(DataSourceSurface::READ_WRITE, &mMap)) {
+      // We failed mapping the data surface, so need to cancel the frame.
+      mWidget->EndRemoteDrawingInRegion(mDT, mRegion);
+      ClearMappedBuffer();
+      return false;
+    }
+  }
+  return true;
+}
+
+void RenderCompositorSWGL::CommitMappedBuffer(
+    const nsTArray<DeviceIntRect>* aDirtyRects) {
+  if (!mDT) {
+    return;
+  }
+  // If we have a draw target at this point, mapping must have succeeded.
+  MOZ_ASSERT(mMap.mData != nullptr);
+  if (mSurface) {
+    // If we're using a data surface, unmap it and draw it to the DT if there
+    // are any supplied dirty rects.
+    mSurface->Unmap();
+    if (aDirtyRects) {
+      if (aDirtyRects->IsEmpty()) {
+        gfx::Rect bounds(mSurface->GetRect());
+        mDT->DrawSurface(mSurface, bounds, bounds,
+                         gfx::DrawSurfaceOptions(gfx::SamplingFilter::POINT),
+                         gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
+      } else {
+        for (const DeviceIntRect& dirtyRect : *aDirtyRects) {
+          gfx::Rect bounds(dirtyRect.origin.x, dirtyRect.origin.y,
+                           dirtyRect.size.width, dirtyRect.size.height);
+          mDT->DrawSurface(
+              mSurface, bounds, bounds,
+              gfx::DrawSurfaceOptions(gfx::SamplingFilter::POINT),
+              gfx::DrawOptions(1.0f, gfx::CompositionOp::OP_SOURCE));
+        }
+      }
+    }
+  } else {
+    // Otherwise, we had locked the DT directly. Just release the data.
+    mDT->ReleaseBits(mMap.mData);
+  }
+  // Done with the DT. Hand it back to the widget and clear out any trace of it.
+  mWidget->EndRemoteDrawingInRegion(mDT, mRegion);
+  ClearMappedBuffer();
+}
+
+void RenderCompositorSWGL::CancelFrame() { CommitMappedBuffer(); }
+
+RenderedFrameId RenderCompositorSWGL::EndFrame(
+    const nsTArray<DeviceIntRect>& aDirtyRects) {
+  RenderedFrameId frameId = GetNextRenderFrameId();
+  CommitMappedBuffer(&aDirtyRects);
+  return frameId;
+}
+
+void RenderCompositorSWGL::Pause() {}
+
+bool RenderCompositorSWGL::Resume() { return true; }
+
+LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() {
+  return mWidget->GetClientSize();
+}
+
+CompositorCapabilities RenderCompositorSWGL::GetCompositorCapabilities() {
+  CompositorCapabilities caps;
+
+  // don't use virtual surfaces
+  caps.virtual_surface_size = 0;
+
+  return caps;
+}
+
+}  // namespace wr
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorSWGL.h
@@ -0,0 +1,59 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_RENDERCOMPOSITOR_SWGL_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_SWGL_H
+
+#include "mozilla/webrender/RenderCompositor.h"
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderCompositorSWGL : public RenderCompositor {
+ public:
+  static UniquePtr<RenderCompositor> Create(
+      RefPtr<widget::CompositorWidget>&& aWidget);
+
+  RenderCompositorSWGL(RefPtr<widget::CompositorWidget>&& aWidget);
+  virtual ~RenderCompositorSWGL();
+
+  bool BeginFrame() override;
+  void CancelFrame() override;
+  RenderedFrameId EndFrame(const nsTArray<DeviceIntRect>& aDirtyRects) final;
+
+  bool GetMappedBuffer(uint8_t** aData, int32_t* aStride) override {
+    if (mMap.mData) {
+      *aData = mMap.mData;
+      *aStride = mMap.mStride;
+      return true;
+    }
+    return false;
+  }
+
+  void Pause() override;
+  bool Resume() override;
+
+  LayoutDeviceIntSize GetBufferSize() override;
+
+  // Interface for wr::Compositor
+  CompositorCapabilities GetCompositorCapabilities() override;
+
+ private:
+  RefPtr<DrawTarget> mDT;
+  LayoutDeviceIntRegion mRegion;
+  RefPtr<DataSourceSurface> mSurface;
+  gfx::DataSourceSurface::MappedSurface mMap;
+
+  void ClearMappedBuffer();
+
+  void CommitMappedBuffer(const nsTArray<DeviceIntRect>* aDirtyRects = nullptr);
+};
+
+}  // namespace wr
+}  // namespace mozilla
+
+#endif
--- a/gfx/webrender_bindings/RendererOGL.cpp
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -131,17 +131,21 @@ RenderedFrameId RendererOGL::UpdateAndRe
     mCompositor->GetWidget()->PostRender(&widgetContext);
     return RenderedFrameId();
   }
 
   auto size = mCompositor->GetBufferSize();
 
   if (mSoftwareContext) {
     wr_swgl_make_current(mSoftwareContext);
-    wr_swgl_init_default_framebuffer(mSoftwareContext, size.width, size.height);
+    uint8_t* data = nullptr;
+    int32_t stride = 0;
+    mCompositor->GetMappedBuffer(&data, &stride);
+    wr_swgl_init_default_framebuffer(mSoftwareContext, size.width, size.height,
+                                     stride, data);
   }
 
   wr_renderer_update(mRenderer);
 
   bool fullRender = mCompositor->RequestFullRender();
   // When we're rendering to an external target, we want to render everything.
   if (mCompositor->UsePartialPresent() &&
       (aReadbackBuffer.isSome() || layers::ProfilerScreenshots::IsEnabled())) {
@@ -149,16 +153,17 @@ RenderedFrameId RendererOGL::UpdateAndRe
   }
   if (fullRender) {
     wr_renderer_force_redraw(mRenderer);
   }
 
   nsTArray<DeviceIntRect> dirtyRects;
   if (!wr_renderer_render(mRenderer, size.width, size.height, aOutStats,
                           &dirtyRects)) {
+    mCompositor->CancelFrame();
     RenderThread::Get()->HandleWebRenderError(WebRenderError::RENDER);
     mCompositor->GetWidget()->PostRender(&widgetContext);
     return RenderedFrameId();
   }
 
   if (aReadbackBuffer.isSome()) {
     MOZ_ASSERT(aReadbackSize.isSome());
     MOZ_ASSERT(aReadbackFormat.isSome());
--- a/gfx/webrender_bindings/moz.build
+++ b/gfx/webrender_bindings/moz.build
@@ -6,16 +6,17 @@
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Graphics: WebRender')
 
 EXPORTS.mozilla.webrender += [
     'RenderBufferTextureHost.h',
     'RenderCompositor.h',
     'RenderCompositorOGL.h',
+    'RenderCompositorSWGL.h',
     'RenderEGLImageTextureHost.h',
     'RendererOGL.h',
     'RendererScreenshotGrabber.h',
     'RenderSharedSurfaceTextureHost.h',
     'RenderTextureHost.h',
     'RenderTextureHostOGL.h',
     'RenderTextureHostWrapper.h',
     'RenderThread.h',
@@ -24,16 +25,17 @@ EXPORTS.mozilla.webrender += [
     'WebRenderTypes.h',
 ]
 
 UNIFIED_SOURCES += [
     'Moz2DImageRenderer.cpp',
     'RenderBufferTextureHost.cpp',
     'RenderCompositor.cpp',
     'RenderCompositorOGL.cpp',
+    'RenderCompositorSWGL.cpp',
     'RenderEGLImageTextureHost.cpp',
     'RendererOGL.cpp',
     'RendererScreenshotGrabber.cpp',
     'RenderSharedSurfaceTextureHost.cpp',
     'RenderTextureHost.cpp',
     'RenderTextureHostOGL.cpp',
     'RenderTextureHostWrapper.cpp',
     'RenderThread.cpp',
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1344,45 +1344,48 @@ pub extern "C" fn wr_window_new(
     out_handle: &mut *mut DocumentHandle,
     out_renderer: &mut *mut Renderer,
     out_max_texture_size: *mut i32,
     enable_gpu_markers: bool,
     panic_on_gl_error: bool,
 ) -> bool {
     assert!(unsafe { is_in_render_thread() });
 
-    let native_gl = if unsafe { is_glcontext_gles(gl_context) } {
-        unsafe { gl::GlesFns::load_with(|symbol| get_proc_address(gl_context, symbol)) }
+    let native_gl = if gl_context == ptr::null_mut() {
+        None
+    } else if unsafe { is_glcontext_gles(gl_context) } {
+        unsafe { Some(gl::GlesFns::load_with(|symbol| get_proc_address(gl_context, symbol))) }
     } else {
-        unsafe { gl::GlFns::load_with(|symbol| get_proc_address(gl_context, symbol)) }
+        unsafe { Some(gl::GlFns::load_with(|symbol| get_proc_address(gl_context, symbol))) }
     };
 
     let software = swgl_context != ptr::null_mut();
     let (gl, sw_gl) = if software {
         let ctx = swgl::Context::from(swgl_context);
         ctx.make_current();
         (Rc::new(ctx.clone()) as Rc<dyn gl::Gl>, Some(ctx))
     } else {
-        (native_gl.clone(), None)
+        (native_gl.as_ref().expect("Native GL context required when not using SWGL!").clone(), None)
     };
 
     let version = gl.get_string(gl::VERSION);
 
     info!("WebRender - OpenGL version new {}", version);
 
     let workers = unsafe { Arc::clone(&(*thread_pool).0) };
     let workers_low_priority = unsafe {
         if support_low_priority_threadpool {
             Arc::clone(&(*thread_pool_low_priority).0)
         } else {
             Arc::clone(&(*thread_pool).0)
         }
     };
 
-    let upload_method = if unsafe { is_glcontext_angle(gl_context) } {
+    let upload_method = if gl_context != ptr::null_mut() &&
+                           unsafe { is_glcontext_angle(gl_context) } {
         UploadMethod::Immediate
     } else {
         UploadMethod::PixelBuffer(ONE_TIME_USAGE_HINT)
     };
 
     let precache_flags = if env_var_to_bool("MOZ_WR_PRECACHE_SHADERS") {
         ShaderPrecacheFlags::FULL_COMPILE
     } else {
@@ -1404,17 +1407,17 @@ pub extern "C" fn wr_window_new(
     let compositor_config = if software {
         let wr_compositor: Option<Box<dyn Compositor>> = if compositor != ptr::null_mut() {
             Some(Box::new(WrCompositor(compositor)))
         } else {
             None
         };
         CompositorConfig::Native {
             max_update_rects: 1,
-            compositor: Box::new(SwCompositor::new(sw_gl.unwrap(), Some(native_gl), wr_compositor)),
+            compositor: Box::new(SwCompositor::new(sw_gl.unwrap(), native_gl, wr_compositor)),
         }
     } else if compositor != ptr::null_mut() {
         CompositorConfig::Native {
             max_update_rects,
             compositor: Box::new(WrCompositor(compositor)),
         }
     } else {
         CompositorConfig::Draw {
--- a/gfx/webrender_bindings/src/swgl_bindings.rs
+++ b/gfx/webrender_bindings/src/swgl_bindings.rs
@@ -584,17 +584,17 @@ impl Compositor for SwCompositor {
                                 tile.color_id,
                                 rect.min_x() - valid.origin.x,
                                 rect.min_y() - valid.origin.y,
                                 rect.size.width,
                                 rect.size.height,
                                 rect.min_x(),
                                 rect.min_y(),
                                 surface.is_opaque,
-                                true,
+                                false,
                             );
                         }
                     }
                 }
             }
         }
     }