summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/RenderCompositorSWGL.cpp
blob: ec5be61f4d100c37bb4111d86f6216a2c5ef8663 (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
/* -*- 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"

namespace mozilla {
using namespace gfx;

namespace wr {

/* static */
UniquePtr<RenderCompositor> RenderCompositorSWGL::Create(
    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>(std::move(aWidget), ctx);
}

RenderCompositorSWGL::RenderCompositorSWGL(
    RefPtr<widget::CompositorWidget>&& aWidget, void* aContext)
    : RenderCompositor(std::move(aWidget)), mContext(aContext) {
  MOZ_ASSERT(mContext);
}

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() {
  // Set up a temporary region representing the entire window surface in case a
  // dirty region is not supplied.
  ClearMappedBuffer();
  mRegion = 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(mRegion, &bufferMode);
  if (!mDT) {
    return false;
  }
  mWidget->ClearBeforePaint(mDT, mRegion);
  // 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 = mRegion.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));
    }
  } 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, mRegion);
      ClearMappedBuffer();
      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.origin.x, rect.origin.y,
                                      rect.size.width, rect.size.height));
  }

  RefPtr<DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
      BackendType::SKIA, mMappedData, bounds.Size().ToUnknownSize(),
      mMappedStride, SurfaceFormat::B8G8R8A8, false);

  LayoutDeviceIntRegion clear;
  clear.Sub(mRegion, opaque);
  for (auto iter = clear.RectIter(); !iter.Done(); iter.Next()) {
    dt->ClearRect(
        IntRectToRect((iter.Get() - bounds.TopLeft()).ToUnknownRect()));
  }

  return true;
}

void RenderCompositorSWGL::StartCompositing(
    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
    mRegion = LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetBufferSize());
  }
  if (aNumDirtyRects) {
    // Install the dirty rects into the bounds of the existing region
    auto bounds = mRegion.GetBounds();
    mRegion.SetEmpty();
    for (size_t i = 0; i < aNumDirtyRects; i++) {
      const auto& rect = aDirtyRects[i];
      mRegion.OrWith(LayoutDeviceIntRect(rect.origin.x, rect.origin.y,
                                         rect.size.width, rect.size.height));
    }
    // Ensure the region lies within the widget bounds
    mRegion.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 (!AllocateMappedBuffer(aOpaqueRects, aNumOpaqueRects)) {
    gfxCriticalNote
        << "RenderCompositorSWGL failed mapping default framebuffer";
    // If allocation of the mapped default framebuffer failed, then just install
    // a small temporary framebuffer so compositing can still proceed.
    wr_swgl_init_default_framebuffer(mContext, 0, 0, 2, 2, 0, nullptr);
  }
}

void RenderCompositorSWGL::CommitMappedBuffer(bool aDirty) {
  if (!mDT) {
    return;
  }
  // 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 = mRegion.GetBounds();
      gfx::IntPoint srcOffset = bounds.TopLeft().ToUnknownPoint();
      gfx::IntPoint dstOffset = mDT->GetSize() == bounds.Size().ToUnknownSize()
                                    ? srcOffset
                                    : gfx::IntPoint(0, 0);
      for (auto iter = mRegion.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);
  }
  // Done with the DT. Hand it back to the widget and clear out any trace of it.
  mWidget->EndRemoteDrawingInRegion(mDT, mRegion);
  ClearMappedBuffer();
}

void RenderCompositorSWGL::CancelFrame() { CommitMappedBuffer(false); }

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();
  return frameId;
}

void RenderCompositorSWGL::Pause() {}

bool RenderCompositorSWGL::Resume() { return true; }

LayoutDeviceIntSize RenderCompositorSWGL::GetBufferSize() {
  return mWidget->GetClientSize();
}

CompositorCapabilities RenderCompositorSWGL::GetCompositorCapabilities() {
  CompositorCapabilities caps;

  // don't use virtual surfaces
  caps.virtual_surface_size = 0;

  return caps;
}

}  // namespace wr
}  // namespace mozilla