summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RenderCompositorSWGL.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/webrender_bindings/RenderCompositorSWGL.cpp')
-rw-r--r--gfx/webrender_bindings/RenderCompositorSWGL.cpp315
1 files changed, 315 insertions, 0 deletions
diff --git a/gfx/webrender_bindings/RenderCompositorSWGL.cpp b/gfx/webrender_bindings/RenderCompositorSWGL.cpp
new file mode 100644
index 0000000000..1eec99bb3a
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorSWGL.cpp
@@ -0,0 +1,315 @@
+/* -*- 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/gfx/Logging.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+#endif
+
+namespace mozilla {
+using namespace gfx;
+
+namespace wr {
+
+extern LazyLogModule gRenderThreadLog;
+#define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
+
+/* static */
+UniquePtr<RenderCompositor> RenderCompositorSWGL::Create(
+ const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
+ void* ctx = wr_swgl_create_context();
+ if (!ctx) {
+ gfxCriticalNote << "Failed SWGL context creation for WebRender";
+ return nullptr;
+ }
+ return MakeUnique<RenderCompositorSWGL>(aWidget, ctx);
+}
+
+RenderCompositorSWGL::RenderCompositorSWGL(
+ const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
+ : RenderCompositor(aWidget), mContext(aContext) {
+ MOZ_ASSERT(mContext);
+ LOG("RenderCompositorSWGL::RenderCompositorSWGL()");
+}
+
+RenderCompositorSWGL::~RenderCompositorSWGL() {
+ LOG("RenderCompositorSWGL::~RenderCompositorSWGL()");
+
+ wr_swgl_destroy_context(mContext);
+}
+
+void RenderCompositorSWGL::ClearMappedBuffer() {
+ mMappedData = nullptr;
+ mMappedStride = 0;
+ mDT = nullptr;
+}
+
+bool RenderCompositorSWGL::MakeCurrent() {
+ wr_swgl_make_current(mContext);
+ return true;
+}
+
+bool RenderCompositorSWGL::BeginFrame() {
+ mRenderWidgetSize = Some(mWidget->GetClientSize());
+#ifdef MOZ_WAYLAND
+ if (mLastRenderWidgetSize != mRenderWidgetSize.value()) {
+ mLastRenderWidgetSize = mRenderWidgetSize.value();
+ mRequestFullRender = true;
+ }
+#endif
+ // Set up a temporary region representing the entire window surface in case a
+ // dirty region is not supplied.
+ ClearMappedBuffer();
+ mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
+ wr_swgl_make_current(mContext);
+ return true;
+}
+
+bool RenderCompositorSWGL::AllocateMappedBuffer(
+ const wr::DeviceIntRect* aOpaqueRects, size_t aNumOpaqueRects) {
+ // Request a new draw target to use from the widget...
+ MOZ_ASSERT(!mDT);
+ layers::BufferMode bufferMode = layers::BufferMode::BUFFERED;
+ mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion, &bufferMode);
+ if (!mDT) {
+ gfxCriticalNoteOnce
+ << "RenderCompositorSWGL failed mapping default framebuffer, no dt";
+ 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;
+ gfx::IntSize size;
+ int32_t stride = 0;
+ gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
+ if (bufferMode != layers::BufferMode::BUFFERED && !mSurface &&
+ mDT->LockBits(&data, &size, &stride, &format) &&
+ (format != gfx::SurfaceFormat::B8G8R8A8 &&
+ format != gfx::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;
+ }
+ LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds();
+ // If locking succeeded above, just use that.
+ if (data) {
+ mMappedData = data;
+ mMappedStride = stride;
+ // Disambiguate whether the widget's draw target has its origin at zero or
+ // if it is offset to the dirty region origin. The DT might either enclose
+ // only the region itself, the region including the origin, or the entire
+ // widget. Thus, if the DT doesn't only enclose the region, we assume it
+ // contains the origin.
+ if (size != bounds.Size().ToUnknownSize()) {
+ // Update the bounds to include zero if the origin is at zero.
+ bounds.ExpandToEnclose(LayoutDeviceIntPoint(0, 0));
+ }
+ // Sometimes we end up racing on the widget size, and it can shrink between
+ // BeginFrame and StartCompositing. We calculated our dirty region based on
+ // the previous widget size, so we need to clamp the bounds here to ensure
+ // we remain within the buffer.
+ bounds.IntersectRect(
+ bounds,
+ LayoutDeviceIntRect(bounds.TopLeft(),
+ LayoutDeviceIntSize(size.width, size.height)));
+ } 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 = gfx::Factory::CreateDataSourceSurface(
+ size, gfx::SurfaceFormat::B8G8R8A8);
+ }
+ gfx::DataSourceSurface::MappedSurface map = {nullptr, 0};
+ if (!mSurface || !mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
+ // We failed mapping the data surface, so need to cancel the frame.
+ mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion);
+ ClearMappedBuffer();
+ gfxCriticalNoteOnce
+ << "RenderCompositorSWGL failed mapping default framebuffer, no surf";
+ return false;
+ }
+ mMappedData = map.mData;
+ mMappedStride = map.mStride;
+ }
+ MOZ_ASSERT(mMappedData != nullptr && mMappedStride > 0);
+ wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
+ bounds.height, mMappedStride, mMappedData);
+
+ LayoutDeviceIntRegion opaque;
+ for (size_t i = 0; i < aNumOpaqueRects; i++) {
+ const auto& rect = aOpaqueRects[i];
+ opaque.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y, rect.width(),
+ rect.height()));
+ }
+
+ LayoutDeviceIntRegion clear = mWidget->GetTransparentRegion();
+ clear.AndWith(mDirtyRegion);
+ clear.SubOut(opaque);
+ for (auto iter = clear.RectIter(); !iter.Done(); iter.Next()) {
+ const auto& rect = iter.Get();
+ wr_swgl_clear_color_rect(mContext, 0, rect.x, rect.y, rect.width,
+ rect.height, 0, 0, 0, 0);
+ }
+
+ return true;
+}
+
+void RenderCompositorSWGL::StartCompositing(
+ wr::ColorF aClearColor, const wr::DeviceIntRect* aDirtyRects,
+ size_t aNumDirtyRects, const wr::DeviceIntRect* aOpaqueRects,
+ size_t aNumOpaqueRects) {
+ if (mDT) {
+ // Cancel any existing buffers that might accidentally be left from updates
+ CommitMappedBuffer(false);
+ // Reset the region to the widget bounds
+ mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
+ }
+ if (aNumDirtyRects) {
+ // Install the dirty rects into the bounds of the existing region
+ auto bounds = mDirtyRegion.GetBounds();
+ mDirtyRegion.SetEmpty();
+ for (size_t i = 0; i < aNumDirtyRects; i++) {
+ const auto& rect = aDirtyRects[i];
+ mDirtyRegion.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y,
+ rect.width(), rect.height()));
+ }
+ // Ensure the region lies within the widget bounds
+ mDirtyRegion.AndWith(bounds);
+ }
+ // Now that the dirty rects have been supplied and the composition region
+ // is known, allocate and install a framebuffer encompassing the composition
+ // region.
+ if (mDirtyRegion.IsEmpty() ||
+ !AllocateMappedBuffer(aOpaqueRects, aNumOpaqueRects)) {
+ // If allocation of the mapped default framebuffer failed, then just install
+ // a temporary framebuffer (with a minimum size of 2x2) so compositing can
+ // still proceed.
+ auto bounds = mDirtyRegion.GetBounds();
+ bounds.width = std::max(bounds.width, 2);
+ bounds.height = std::max(bounds.height, 2);
+ wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
+ bounds.height, 0, nullptr);
+ }
+}
+
+void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) {
+ if (!mDT) {
+ mDirtyRegion.SetEmpty();
+ return;
+ }
+ // Force any delayed clears to resolve.
+ if (aDirty) {
+ wr_swgl_resolve_framebuffer(mContext, 0);
+ }
+ // Clear out the old framebuffer in case something tries to access it after
+ // the frame.
+ wr_swgl_init_default_framebuffer(mContext, 0, 0, 0, 0, 0, nullptr);
+ // If we have a draw target at this point, mapping must have succeeded.
+ MOZ_ASSERT(mMappedData != 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 (aDirty) {
+ // The temporary source surface is always a partial region of the widget
+ // that is offset from the origin to the actual bounds of the dirty
+ // region. The destination DT may also be an offset partial region, but we
+ // must check to see if its size matches the region bounds to verify this.
+ LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds();
+ gfx::IntPoint srcOffset = bounds.TopLeft().ToUnknownPoint();
+ gfx::IntPoint dstOffset = mDT->GetSize() == bounds.Size().ToUnknownSize()
+ ? srcOffset
+ : gfx::IntPoint(0, 0);
+ for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
+ gfx::IntRect dirtyRect = iter.Get().ToUnknownRect();
+ mDT->CopySurface(mSurface, dirtyRect - srcOffset,
+ dirtyRect.TopLeft() - dstOffset);
+ }
+ }
+ } else {
+ // Otherwise, we had locked the DT directly. Just release the data.
+ mDT->ReleaseBits(mMappedData);
+ }
+ mDT->Flush();
+
+ // Done with the DT. Hand it back to the widget and clear out any trace of it.
+ mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion);
+ mDirtyRegion.SetEmpty();
+ ClearMappedBuffer();
+}
+
+void RenderCompositorSWGL::CancelFrame() {
+ CommitMappedBuffer(false);
+ mRenderWidgetSize = Nothing();
+}
+
+RenderedFrameId RenderCompositorSWGL::EndFrame(
+ const nsTArray<DeviceIntRect>& aDirtyRects) {
+ // Dirty rects have already been set inside StartCompositing. We need to keep
+ // those dirty rects exactly the same here so we supply the same exact region
+ // to EndRemoteDrawingInRegion as for StartRemoteDrawingInRegion.
+ RenderedFrameId frameId = GetNextRenderFrameId();
+ CommitMappedBuffer();
+ mRenderWidgetSize = Nothing();
+ return frameId;
+}
+
+bool RenderCompositorSWGL::RequestFullRender() {
+#ifdef MOZ_WIDGET_ANDROID
+ // XXX Add partial present support.
+ return true;
+#endif
+#ifdef MOZ_WAYLAND
+ // We're requested to do full render after Resume() on Wayland.
+ if (mRequestFullRender) {
+ mRequestFullRender = false;
+ return true;
+ }
+#endif
+ return false;
+}
+
+void RenderCompositorSWGL::Pause() {}
+
+bool RenderCompositorSWGL::Resume() {
+#ifdef MOZ_WAYLAND
+ mRequestFullRender = true;
+#endif
+ return true;
+}
+
+LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() {
+ // If we're between BeginFrame() and EndFrame()/CancelFrame() calls
+ // return recent rendering size instead of actual underlying widget
+ // size. It prevents possible rendering artifacts if widget size was changed.
+ return mRenderWidgetSize ? mRenderWidgetSize.value()
+ : mWidget->GetClientSize();
+}
+
+void RenderCompositorSWGL::GetCompositorCapabilities(
+ CompositorCapabilities* aCaps) {
+ // Always support a single update rect for SwCompositor
+ aCaps->max_update_rects = 1;
+
+ // On uncomposited desktops such as X11 without compositor or Window 7 with
+ // Aero disabled we need to force a full redraw when the window contents may
+ // be damaged.
+#ifdef MOZ_WIDGET_GTK
+ aCaps->redraw_on_invalidation = widget::GdkIsX11Display();
+#else
+ aCaps->redraw_on_invalidation = true;
+#endif
+}
+
+} // namespace wr
+} // namespace mozilla