no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / webrender_bindings / RenderCompositorSWGL.cpp
blobd767ff83f7718097e95883cbfdee31dea6cb198a
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/. */
7 #include "RenderCompositorSWGL.h"
9 #include "mozilla/gfx/Logging.h"
10 #include "mozilla/widget/CompositorWidget.h"
12 #ifdef MOZ_WIDGET_GTK
13 # include "mozilla/WidgetUtilsGtk.h"
14 #endif
16 namespace mozilla {
17 using namespace gfx;
19 namespace wr {
21 extern LazyLogModule gRenderThreadLog;
22 #define LOG(...) MOZ_LOG(gRenderThreadLog, LogLevel::Debug, (__VA_ARGS__))
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;
32 return MakeUnique<RenderCompositorSWGL>(aWidget, ctx);
35 RenderCompositorSWGL::RenderCompositorSWGL(
36 const RefPtr<widget::CompositorWidget>& aWidget, void* aContext)
37 : RenderCompositor(aWidget), mContext(aContext) {
38 MOZ_ASSERT(mContext);
39 LOG("RenderCompositorSWGL::RenderCompositorSWGL()");
42 RenderCompositorSWGL::~RenderCompositorSWGL() {
43 LOG("RenderCompositorSWGL::~RenderCompositorSWGL()");
45 wr_swgl_destroy_context(mContext);
48 void RenderCompositorSWGL::ClearMappedBuffer() {
49 mMappedData = nullptr;
50 mMappedStride = 0;
51 mDT = nullptr;
54 bool RenderCompositorSWGL::MakeCurrent() {
55 wr_swgl_make_current(mContext);
56 return true;
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;
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;
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 layers::BufferMode bufferMode = layers::BufferMode::BUFFERED;
80 mDT = mWidget->StartRemoteDrawingInRegion(mDirtyRegion, &bufferMode);
81 if (!mDT) {
82 gfxCriticalNoteOnce
83 << "RenderCompositorSWGL failed mapping default framebuffer, no dt";
84 return false;
86 // Attempt to lock the underlying buffer directly from the draw target.
87 // Verify that the size at least matches what the widget claims and that
88 // the format is BGRA8 as SWGL requires.
89 uint8_t* data = nullptr;
90 gfx::IntSize size;
91 int32_t stride = 0;
92 gfx::SurfaceFormat format = gfx::SurfaceFormat::UNKNOWN;
93 if (bufferMode != layers::BufferMode::BUFFERED && !mSurface &&
94 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;
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));
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);
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;
141 mMappedData = map.mData;
142 mMappedStride = map.mStride;
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);
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()));
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);
164 return true;
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());
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()));
186 // Ensure the region lies within the widget bounds
187 mDirtyRegion.AndWith(bounds);
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);
205 void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) {
206 if (!mDT) {
207 mDirtyRegion.SetEmpty();
208 return;
210 // Force any delayed clears to resolve.
211 if (aDirty) {
212 wr_swgl_resolve_framebuffer(mContext, 0);
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);
239 } else {
240 // Otherwise, we had locked the DT directly. Just release the data.
241 mDT->ReleaseBits(mMappedData);
243 mDT->Flush();
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();
251 void RenderCompositorSWGL::CancelFrame() {
252 CommitMappedBuffer(false);
253 mRenderWidgetSize = Nothing();
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;
267 bool RenderCompositorSWGL::RequestFullRender() {
268 #ifdef MOZ_WIDGET_ANDROID
269 // XXX Add partial present support.
270 return true;
271 #endif
272 #ifdef MOZ_WIDGET_GTK
273 // We're requested to do full render after Resume() on Wayland.
274 if (mRequestFullRender) {
275 mRequestFullRender = false;
276 return true;
278 #endif
279 return false;
282 void RenderCompositorSWGL::Pause() {}
284 bool RenderCompositorSWGL::Resume() {
285 #ifdef MOZ_WIDGET_GTK
286 mRequestFullRender = true;
287 #endif
288 return true;
291 LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() {
292 // If we're between BeginFrame() and EndFrame()/CancelFrame() calls
293 // return recent rendering size instead of actual underlying widget
294 // size. It prevents possible rendering artifacts if widget size was changed.
295 return mRenderWidgetSize ? mRenderWidgetSize.value()
296 : mWidget->GetClientSize();
299 void RenderCompositorSWGL::GetCompositorCapabilities(
300 CompositorCapabilities* aCaps) {
301 // Always support a single update rect for SwCompositor
302 aCaps->max_update_rects = 1;
304 // On uncomposited desktops such as X11 without compositor or Window 7 with
305 // Aero disabled we need to force a full redraw when the window contents may
306 // be damaged.
307 #ifdef MOZ_WIDGET_GTK
308 aCaps->redraw_on_invalidation = widget::GdkIsX11Display();
309 #else
310 aCaps->redraw_on_invalidation = true;
311 #endif
314 } // namespace wr
315 } // namespace mozilla