tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

RenderCompositorSWGL.cpp (11716B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "RenderCompositorSWGL.h"
      8 
      9 #include "mozilla/gfx/Logging.h"
     10 #include "mozilla/widget/CompositorWidget.h"
     11 
     12 #ifdef MOZ_WIDGET_GTK
     13 #  include "mozilla/WidgetUtilsGtk.h"
     14 #endif
     15 
     16 namespace mozilla {
     17 using namespace gfx;
     18 
     19 namespace wr {
     20 
     21 extern LazyLogModule gRenderThreadLog;
     22 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
     23 
     24 /* static */
     25 UniquePtr<RenderCompositor> RenderCompositorSWGL::Create(
     26    const RefPtr<widget::CompositorWidget>& aWidget, nsACString& aError) {
     27  void* ctx = wr_swgl_create_context();
     28  if (!ctx) {
     29    gfxCriticalNote << "Failed SWGL context creation for WebRender";
     30    return nullptr;
     31  }
     32  return MakeUnique<RenderCompositorSWGL>(aWidget, ctx);
     33 }
     34 
     35 RenderCompositorSWGL::RenderCompositorSWGL(
     36    const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
     37    : RenderCompositor(aWidget), mContext(aContext) {
     38  MOZ_ASSERT(mContext);
     39  LOG("RenderCompositorSWGL::RenderCompositorSWGL()");
     40 }
     41 
     42 RenderCompositorSWGL::~RenderCompositorSWGL() {
     43  LOG("RenderCompositorSWGL::~RenderCompositorSWGL()");
     44 
     45  wr_swgl_destroy_context(mContext);
     46 }
     47 
     48 void RenderCompositorSWGL::ClearMappedBuffer() {
     49  mMappedData = nullptr;
     50  mMappedStride = 0;
     51  mDT = nullptr;
     52 }
     53 
     54 bool RenderCompositorSWGL::MakeCurrent() {
     55  wr_swgl_make_current(mContext);
     56  return true;
     57 }
     58 
     59 bool RenderCompositorSWGL::BeginFrame() {
     60  mRenderWidgetSize = Some(mWidget->GetClientSize());
     61 #ifdef MOZ_WIDGET_GTK
     62  if (mLastRenderWidgetSize != mRenderWidgetSize.value()) {
     63    mLastRenderWidgetSize = mRenderWidgetSize.value();
     64    mRequestFullRender = true;
     65  }
     66 #endif
     67  // Set up a temporary region representing the entire window surface in case a
     68  // dirty region is not supplied.
     69  ClearMappedBuffer();
     70  mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
     71  wr_swgl_make_current(mContext);
     72  return true;
     73 }
     74 
     75 bool RenderCompositorSWGL::AllocateMappedBuffer(
     76    const wr::DeviceIntRect* aOpaqueRects, size_t aNumOpaqueRects) {
     77  // Request a new draw target to use from the widget...
     78  MOZ_ASSERT(!mDT);
     79  mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion);
     80  if (!mDT) {
     81 #if !defined(MOZ_WAYLAND)
     82    gfxCriticalNoteOnce
     83        << "RenderCompositorSWGL failed mapping default framebuffer, no dt";
     84 #endif
     85    return false;
     86  }
     87  // Attempt to lock the underlying buffer directly from the draw target.
     88  // Verify that the size at least matches what the widget claims and that
     89  // the format is BGRA8 as SWGL requires.
     90  uint8_t* data = nullptr;
     91  gfx::IntSize size;
     92  int32_t stride = 0;
     93  gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
     94  if (!mSurface && mDT->LockBits(&data, &size, &stride, &format) &&
     95      (format != gfx::SurfaceFormat::B8G8R8A8 &&
     96       format != gfx::SurfaceFormat::B8G8R8X8)) {
     97    // We tried to lock the DT and it succeeded, but the size or format
     98    // of the data is not compatible, so just release it and fall back below...
     99    mDT->ReleaseBits(data);
    100    data = nullptr;
    101  }
    102  LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds();
    103  // If locking succeeded above, just use that.
    104  if (data) {
    105    mMappedData = data;
    106    mMappedStride = stride;
    107    // Disambiguate whether the widget's draw target has its origin at zero or
    108    // if it is offset to the dirty region origin. The DT might either enclose
    109    // only the region itself, the region including the origin, or the entire
    110    // widget. Thus, if the DT doesn't only enclose the region, we assume it
    111    // contains the origin.
    112    if (size != bounds.Size().ToUnknownSize()) {
    113      // Update the bounds to include zero if the origin is at zero.
    114      bounds.ExpandToEnclose(LayoutDeviceIntPoint(0, 0));
    115    }
    116    // Sometimes we end up racing on the widget size, and it can shrink between
    117    // BeginFrame and StartCompositing. We calculated our dirty region based on
    118    // the previous widget size, so we need to clamp the bounds here to ensure
    119    // we remain within the buffer.
    120    bounds.IntersectRect(
    121        bounds,
    122        LayoutDeviceIntRect(bounds.TopLeft(),
    123                            LayoutDeviceIntSize(size.width, size.height)));
    124  } else {
    125    // If we couldn't lock the DT above, then allocate a data surface and map
    126    // that for usage with SWGL.
    127    size = bounds.Size().ToUnknownSize();
    128    if (!mSurface || mSurface->GetSize() != size) {
    129      mSurface = gfx::Factory::CreateDataSourceSurface(
    130          size, gfx::SurfaceFormat::B8G8R8A8);
    131    }
    132    gfx::DataSourceSurface::MappedSurface map = {nullptr, 0};
    133    if (!mSurface || !mSurface->Map(gfx::DataSourceSurface::READ_WRITE, &map)) {
    134      // We failed mapping the data surface, so need to cancel the frame.
    135      mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion);
    136      ClearMappedBuffer();
    137      gfxCriticalNoteOnce
    138          << "RenderCompositorSWGL failed mapping default framebuffer, no surf";
    139      return false;
    140    }
    141    mMappedData = map.mData;
    142    mMappedStride = map.mStride;
    143  }
    144  MOZ_ASSERT(mMappedData != nullptr && mMappedStride > 0);
    145  wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
    146                                   bounds.height, mMappedStride, mMappedData);
    147 
    148  LayoutDeviceIntRegion opaque;
    149  for (size_t i = 0; i < aNumOpaqueRects; i++) {
    150    const auto& rect = aOpaqueRects[i];
    151    opaque.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y, rect.width(),
    152                                      rect.height()));
    153  }
    154 
    155  LayoutDeviceIntRegion clear = mWidget->GetTransparentRegion();
    156  clear.AndWith(mDirtyRegion);
    157  clear.SubOut(opaque);
    158  for (auto iter = clear.RectIter(); !iter.Done(); iter.Next()) {
    159    const auto& rect = iter.Get();
    160    wr_swgl_clear_color_rect(mContext, 0, rect.x, rect.y, rect.width,
    161                             rect.height, 0, 0, 0, 0);
    162  }
    163 
    164  return true;
    165 }
    166 
    167 void RenderCompositorSWGL::StartCompositing(
    168    wr::ColorF aClearColor, const wr::DeviceIntRect* aDirtyRects,
    169    size_t aNumDirtyRects, const wr::DeviceIntRect* aOpaqueRects,
    170    size_t aNumOpaqueRects) {
    171  if (mDT) {
    172    // Cancel any existing buffers that might accidentally be left from updates
    173    CommitMappedBuffer(false);
    174    // Reset the region to the widget bounds
    175    mDirtyRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
    176  }
    177  if (aNumDirtyRects) {
    178    // Install the dirty rects into the bounds of the existing region
    179    auto bounds = mDirtyRegion.GetBounds();
    180    mDirtyRegion.SetEmpty();
    181    for (size_t i = 0; i < aNumDirtyRects; i++) {
    182      const auto& rect = aDirtyRects[i];
    183      mDirtyRegion.OrWith(LayoutDeviceIntRect(rect.min.x, rect.min.y,
    184                                              rect.width(), rect.height()));
    185    }
    186    // Ensure the region lies within the widget bounds
    187    mDirtyRegion.AndWith(bounds);
    188  }
    189  // Now that the dirty rects have been supplied and the composition region
    190  // is known, allocate and install a framebuffer encompassing the composition
    191  // region.
    192  if (mDirtyRegion.IsEmpty() ||
    193      !AllocateMappedBuffer(aOpaqueRects, aNumOpaqueRects)) {
    194    // If allocation of the mapped default framebuffer failed, then just install
    195    // a temporary framebuffer (with a minimum size of 2x2) so compositing can
    196    // still proceed.
    197    auto bounds = mDirtyRegion.GetBounds();
    198    bounds.width = std::max(bounds.width, 2);
    199    bounds.height = std::max(bounds.height, 2);
    200    wr_swgl_init_default_framebuffer(mContext, bounds.x, bounds.y, bounds.width,
    201                                     bounds.height, 0, nullptr);
    202  }
    203 }
    204 
    205 void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) {
    206  if (!mDT) {
    207    mDirtyRegion.SetEmpty();
    208    return;
    209  }
    210  // Force any delayed clears to resolve.
    211  if (aDirty) {
    212    wr_swgl_resolve_framebuffer(mContext, 0);
    213  }
    214  // Clear out the old framebuffer in case something tries to access it after
    215  // the frame.
    216  wr_swgl_init_default_framebuffer(mContext, 0, 0, 0, 0, 0, nullptr);
    217  // If we have a draw target at this point, mapping must have succeeded.
    218  MOZ_ASSERT(mMappedData != nullptr);
    219  if (mSurface) {
    220    // If we're using a data surface, unmap it and draw it to the DT if there
    221    // are any supplied dirty rects.
    222    mSurface->Unmap();
    223    if (aDirty) {
    224      // The temporary source surface is always a partial region of the widget
    225      // that is offset from the origin to the actual bounds of the dirty
    226      // region. The destination DT may also be an offset partial region, but we
    227      // must check to see if its size matches the region bounds to verify this.
    228      LayoutDeviceIntRect bounds = mDirtyRegion.GetBounds();
    229      gfx::IntPoint srcOffset = bounds.TopLeft().ToUnknownPoint();
    230      gfx::IntPoint dstOffset = mDT->GetSize() == bounds.Size().ToUnknownSize()
    231                                    ? srcOffset
    232                                    : gfx::IntPoint(0, 0);
    233      for (auto iter = mDirtyRegion.RectIter(); !iter.Done(); iter.Next()) {
    234        gfx::IntRect dirtyRect = iter.Get().ToUnknownRect();
    235        mDT->CopySurface(mSurface, dirtyRect - srcOffset,
    236                         dirtyRect.TopLeft() - dstOffset);
    237      }
    238    }
    239  } else {
    240    // Otherwise, we had locked the DT directly. Just release the data.
    241    mDT->ReleaseBits(mMappedData);
    242  }
    243  mDT->Flush();
    244 
    245  // Done with the DT. Hand it back to the widget and clear out any trace of it.
    246  mWidget->EndRemoteDrawingInRegion(mDT, mDirtyRegion);
    247  mDirtyRegion.SetEmpty();
    248  ClearMappedBuffer();
    249 }
    250 
    251 void RenderCompositorSWGL::CancelFrame() {
    252  CommitMappedBuffer(false);
    253  mRenderWidgetSize = Nothing();
    254 }
    255 
    256 RenderedFrameId RenderCompositorSWGL::EndFrame(
    257    const nsTArray<DeviceIntRect>& aDirtyRects) {
    258  // Dirty rects have already been set inside StartCompositing. We need to keep
    259  // those dirty rects exactly the same here so we supply the same exact region
    260  // to EndRemoteDrawingInRegion as for StartRemoteDrawingInRegion.
    261  RenderedFrameId frameId = GetNextRenderFrameId();
    262  CommitMappedBuffer();
    263  mRenderWidgetSize = Nothing();
    264  return frameId;
    265 }
    266 
    267 bool RenderCompositorSWGL::RequestFullRender() {
    268 #if defined(MOZ_WIDGET_ANDROID)
    269  // XXX Add partial present support.
    270  return true;
    271 #elif defined(MOZ_WIDGET_GTK)
    272  // We're requested to do full render after Resume() on Wayland.
    273  if (mRequestFullRender) {
    274    mRequestFullRender = false;
    275    return true;
    276  }
    277  return false;
    278 #else
    279  return false;
    280 #endif
    281 }
    282 
    283 void RenderCompositorSWGL::Pause() {}
    284 
    285 bool RenderCompositorSWGL::Resume() {
    286 #ifdef MOZ_WIDGET_GTK
    287  mRequestFullRender = true;
    288 #endif
    289  return true;
    290 }
    291 
    292 LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() {
    293  // If we're between BeginFrame() and EndFrame()/CancelFrame() calls
    294  // return recent rendering size instead of actual underlying widget
    295  // size. It prevents possible rendering artifacts if widget size was changed.
    296  return mRenderWidgetSize ? mRenderWidgetSize.value()
    297                           : mWidget->GetClientSize();
    298 }
    299 
    300 void RenderCompositorSWGL::GetCompositorCapabilities(
    301    CompositorCapabilities* aCaps) {
    302  // Always support a single update rect for SwCompositor
    303  aCaps->max_update_rects = 1;
    304 
    305  // On uncomposited desktops such as X11 without compositor or Window 7 with
    306  // Aero disabled we need to force a full redraw when the window contents may
    307  // be damaged.
    308 #ifdef MOZ_WIDGET_GTK
    309  aCaps->redraw_on_invalidation = widget::GdkIsX11Display();
    310 #else
    311  aCaps->redraw_on_invalidation = true;
    312 #endif
    313 }
    314 
    315 }  // namespace wr
    316 }  // namespace mozilla