summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RenderCompositorSWGL.cpp
blob: d767ff83f7718097e95883cbfdee31dea6cb198a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
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_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