mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
be6d95b423
Differential Revision: https://phabricator.services.mozilla.com/D178634
316 lines
12 KiB
C++
316 lines
12 KiB
C++
/* -*- 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_WIDGET_GTK
|
|
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_WIDGET_GTK
|
|
// 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_WIDGET_GTK
|
|
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
|