summaryrefslogtreecommitdiffstats
path: root/gfx/webrender_bindings/Moz2DImageRenderer.cpp
blob: 500e7bf1c3a73d1318010931841eb97cb9b2c7dc (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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
/* -*- 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 "mozilla/StaticPrefs_gfx.h"
#include "gfxUtils.h"
#include "mozilla/Mutex.h"
#include "mozilla/Range.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/RectAbsolute.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/RecordedEvent.h"
#include "mozilla/layers/WebRenderDrawEventRecorder.h"
#include "WebRenderTypes.h"
#include "webrender_ffi.h"
#include "GeckoProfiler.h"

#include <unordered_map>

#ifdef XP_DARWIN
#  include "mozilla/gfx/UnscaledFontMac.h"
#elif defined(XP_WIN)
#  include "mozilla/gfx/UnscaledFontDWrite.h"
#else
#  include "mozilla/gfx/UnscaledFontFreeType.h"
#endif

namespace std {
template <>
struct hash<mozilla::wr::FontKey> {
  size_t operator()(const mozilla::wr::FontKey& key) const {
    return hash<size_t>()(mozilla::wr::AsUint64(key));
  }
};

template <>
struct hash<mozilla::wr::FontInstanceKey> {
  size_t operator()(const mozilla::wr::FontInstanceKey& key) const {
    return hash<size_t>()(mozilla::wr::AsUint64(key));
  }
};
};  // namespace std

namespace mozilla {

using namespace gfx;

namespace wr {

struct FontTemplate {
  const uint8_t* mData;
  size_t mSize;
  uint32_t mIndex;
  const VecU8* mVec;
  RefPtr<UnscaledFont> mUnscaledFont;

  FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {}

  ~FontTemplate() {
    if (mVec) {
      wr_dec_ref_arc(mVec);
    }
  }
};

struct FontInstanceData {
  WrFontKey mFontKey;
  float mSize;
  Maybe<FontInstanceOptions> mOptions;
  Maybe<FontInstancePlatformOptions> mPlatformOptions;
  UniquePtr<gfx::FontVariation[]> mVariations;
  size_t mNumVariations;
  RefPtr<ScaledFont> mScaledFont;

  FontInstanceData() : mSize(0), mNumVariations(0) {}
};

StaticMutex sFontDataTableLock;
std::unordered_map<WrFontKey, FontTemplate> sFontDataTable;
std::unordered_map<WrFontInstanceKey, FontInstanceData> sBlobFontTable;

// Fixed-size ring buffer logging font deletion events to aid debugging.
static struct FontDeleteLog {
  static const size_t MAX_ENTRIES = 256;

  uint64_t mEntries[MAX_ENTRIES] = {0};
  size_t mNextEntry = 0;

  void AddEntry(uint64_t aEntry) {
    mEntries[mNextEntry] = aEntry;
    mNextEntry = (mNextEntry + 1) % MAX_ENTRIES;
  }

  void Add(WrFontKey aKey) { AddEntry(AsUint64(aKey)); }

  // Store namespace clears as font id 0, since this will never be allocated.
  void Add(WrIdNamespace aNamespace) {
    AddEntry(AsUint64(WrFontKey{aNamespace, 0}));
  }

  void AddAll() { AddEntry(~0); }

  // Find a matching entry in the log, searching backwards starting at the
  // newest entry and finishing with the oldest entry. Returns a brief
  // description of why the font was deleted, if known.
  const char* Find(WrFontKey aKey) {
    uint64_t keyEntry = AsUint64(aKey);
    uint64_t namespaceEntry = AsUint64(WrFontKey{aKey.mNamespace, 0});
    size_t offset = mNextEntry;
    do {
      offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES;
      if (mEntries[offset] == keyEntry) {
        return "deleted font";
      } else if (mEntries[offset] == namespaceEntry) {
        return "cleared namespace";
      } else if (mEntries[offset] == (uint64_t)~0) {
        return "cleared all";
      }
    } while (offset != mNextEntry);
    return "unknown font";
  }
} sFontDeleteLog;

void ClearAllBlobImageResources() {
  StaticMutexAutoLock lock(sFontDataTableLock);
  sFontDeleteLog.AddAll();
  sBlobFontTable.clear();
  sFontDataTable.clear();
}

extern "C" {
void ClearBlobImageResources(WrIdNamespace aNamespace) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  sFontDeleteLog.Add(aNamespace);
  for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) {
    if (i->first.mNamespace == aNamespace) {
      i = sBlobFontTable.erase(i);
    } else {
      i++;
    }
  }
  for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
    if (i->first.mNamespace == aNamespace) {
      i = sFontDataTable.erase(i);
    } else {
      i++;
    }
  }
}

bool HasFontData(WrFontKey aKey) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  return sFontDataTable.find(aKey) != sFontDataTable.end();
}

void AddFontData(WrFontKey aKey, const uint8_t* aData, size_t aSize,
                 uint32_t aIndex, const ArcVecU8* aVec) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  auto i = sFontDataTable.find(aKey);
  if (i == sFontDataTable.end()) {
    FontTemplate& font = sFontDataTable[aKey];
    font.mData = aData;
    font.mSize = aSize;
    font.mIndex = aIndex;
    font.mVec = wr_add_ref_arc(aVec);
  }
}

void AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  auto i = sFontDataTable.find(aKey);
  if (i == sFontDataTable.end()) {
    FontTemplate& font = sFontDataTable[aKey];
#ifdef XP_DARWIN
    font.mUnscaledFont =
        new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false);
#elif defined(XP_WIN)
    font.mUnscaledFont = new UnscaledFontDWrite(
        reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr);
#elif defined(ANDROID)
    font.mUnscaledFont = new UnscaledFontFreeType(
        reinterpret_cast<const char*>(aHandle), aIndex);
#else
    font.mUnscaledFont = new UnscaledFontFontconfig(
        reinterpret_cast<const char*>(aHandle), aIndex);
#endif
  }
}

void DeleteFontData(WrFontKey aKey) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  sFontDeleteLog.Add(aKey);
  auto i = sFontDataTable.find(aKey);
  if (i != sFontDataTable.end()) {
    sFontDataTable.erase(i);
  }
}

void AddBlobFont(WrFontInstanceKey aInstanceKey, WrFontKey aFontKey,
                 float aSize, const FontInstanceOptions* aOptions,
                 const FontInstancePlatformOptions* aPlatformOptions,
                 const FontVariation* aVariations, size_t aNumVariations) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  auto i = sBlobFontTable.find(aInstanceKey);
  if (i == sBlobFontTable.end()) {
    FontInstanceData& font = sBlobFontTable[aInstanceKey];
    font.mFontKey = aFontKey;
    font.mSize = aSize;
    if (aOptions) {
      font.mOptions = Some(*aOptions);
    }
    if (aPlatformOptions) {
      font.mPlatformOptions = Some(*aPlatformOptions);
    }
    if (aNumVariations) {
      font.mNumVariations = aNumVariations;
      font.mVariations.reset(new gfx::FontVariation[aNumVariations]);
      PodCopy(font.mVariations.get(),
              reinterpret_cast<const gfx::FontVariation*>(aVariations),
              aNumVariations);
    }
  }
}

void DeleteBlobFont(WrFontInstanceKey aKey) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  auto i = sBlobFontTable.find(aKey);
  if (i != sBlobFontTable.end()) {
    sBlobFontTable.erase(i);
  }
}

}  // extern

static RefPtr<UnscaledFont> GetUnscaledFont(Translator* aTranslator,
                                            WrFontKey aKey) {
  auto i = sFontDataTable.find(aKey);
  if (i == sFontDataTable.end()) {
    gfxDevCrash(LogReason::UnscaledFontNotFound)
        << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle
        << " because " << sFontDeleteLog.Find(aKey);
    return nullptr;
  }
  FontTemplate& data = i->second;
  if (data.mUnscaledFont) {
    return data.mUnscaledFont;
  }
  MOZ_ASSERT(data.mData);
  FontType type =
#ifdef XP_DARWIN
      FontType::MAC;
#elif defined(XP_WIN)
      FontType::DWRITE;
#elif defined(ANDROID)
      FontType::FREETYPE;
#else
      FontType::FONTCONFIG;
#endif
  // makes a copy of the data
  RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource(
      (uint8_t*)data.mData, data.mSize, type, aTranslator->GetFontContext());
  RefPtr<UnscaledFont> unscaledFont;
  if (!fontResource) {
    gfxDevCrash(LogReason::NativeFontResourceNotFound)
        << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
  } else {
    // Instance data is only needed for GDI fonts which webrender does not
    // support.
    unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
    if (!unscaledFont) {
      gfxDevCrash(LogReason::UnscaledFontNotFound)
          << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
    }
  }
  data.mUnscaledFont = unscaledFont;
  return unscaledFont;
}

static RefPtr<ScaledFont> GetScaledFont(Translator* aTranslator,
                                        WrFontInstanceKey aKey) {
  StaticMutexAutoLock lock(sFontDataTableLock);
  auto i = sBlobFontTable.find(aKey);
  if (i == sBlobFontTable.end()) {
    gfxDevCrash(LogReason::ScaledFontNotFound)
        << "Failed to get ScaledFont entry for FontInstanceKey "
        << aKey.mHandle;
    return nullptr;
  }
  FontInstanceData& data = i->second;
  if (data.mScaledFont) {
    return data.mScaledFont;
  }
  RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
  if (!unscaled) {
    return nullptr;
  }
  RefPtr<ScaledFont> scaled = unscaled->CreateScaledFontFromWRFont(
      data.mSize, data.mOptions.ptrOr(nullptr),
      data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(),
      data.mNumVariations);
  if (!scaled) {
    gfxDevCrash(LogReason::ScaledFontNotFound)
        << "Failed to create ScaledFont for FontKey " << aKey.mHandle;
  }
  data.mScaledFont = scaled;
  return data.mScaledFont;
}

template <typename T>
T ConvertFromBytes(const uint8_t* bytes) {
  T t;
  memcpy(&t, bytes, sizeof(T));
  return t;
}

struct Reader {
  const uint8_t* buf;
  size_t len;
  size_t pos;

  Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {}

  template <typename T>
  T Read() {
    MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len);
    T ret = ConvertFromBytes<T>(buf + pos);
    pos += sizeof(T);
    return ret;
  }

  size_t ReadSize() { return Read<size_t>(); }
  int ReadInt() { return Read<int>(); }

  IntRectAbsolute ReadBounds() { return Read<IntRectAbsolute>(); }

  layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); }
};

static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
                                gfx::SurfaceFormat aFormat,
                                const mozilla::wr::DeviceIntRect* aVisibleRect,
                                const mozilla::wr::LayoutIntRect* aRenderRect,
                                const uint16_t aTileSize,
                                const mozilla::wr::TileOffset* aTileOffset,
                                const mozilla::wr::LayoutIntRect* aDirtyRect,
                                Range<uint8_t> aOutput) {
  IntSize size(aRenderRect->width(), aRenderRect->height());
  AUTO_PROFILER_TRACING_MARKER("WebRender", "RasterizeSingleBlob", GRAPHICS);
  MOZ_RELEASE_ASSERT(size.width > 0 && size.height > 0);
  if (size.width <= 0 || size.height <= 0) {
    return false;
  }

  auto stride = size.width * gfx::BytesPerPixel(aFormat);

  if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
    return false;
  }

  // In bindings.rs we allocate a buffer filled with opaque white.
  bool uninitialized = false;

  RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
      gfx::BackendType::SKIA, aOutput.begin().get(), size, stride, aFormat,
      uninitialized);

  if (!dt) {
    return false;
  }

  // We try hard to not have empty blobs but we can end up with
  // them because of CompositorHitTestInfo and merging.
  size_t footerSize = sizeof(size_t);
  MOZ_RELEASE_ASSERT(aBlob.length() >= footerSize);
  size_t indexOffset = ConvertFromBytes<size_t>(aBlob.end().get() - footerSize);

  // aRenderRect is the part of the blob that we are currently rendering
  // (for example a tile) in the same coordinate space as aVisibleRect.
  IntPoint origin = gfx::IntPoint(aRenderRect->min.x, aRenderRect->min.y);

  MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - footerSize);
  Reader reader(aBlob.begin().get() + indexOffset,
                aBlob.length() - footerSize - indexOffset);

  dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin);

  auto bounds = gfx::IntRect(origin, size);

  if (aDirtyRect) {
    gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(),
                    aDirtyRect->height());
    dt->PushClipRect(dirty);
    bounds =
        bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y,
                                 aDirtyRect->width(), aDirtyRect->height()));
  }

  bool ret = true;
  size_t offset = 0;
  auto absBounds = IntRectAbsolute::FromRect(bounds);
  while (reader.pos < reader.len) {
    size_t end = reader.ReadSize();
    size_t extra_end = reader.ReadSize();
    MOZ_RELEASE_ASSERT(extra_end >= end);
    MOZ_RELEASE_ASSERT(extra_end < aBlob.length());

    auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
    if (combinedBounds.IsEmpty()) {
      offset = extra_end;
      continue;
    }

    layers::WebRenderTranslator translator(dt);
    Reader fontReader(aBlob.begin().get() + end, extra_end - end);
    size_t count = fontReader.ReadSize();
    for (size_t i = 0; i < count; i++) {
      layers::BlobFont blobFont = fontReader.ReadBlobFont();
      RefPtr<ScaledFont> scaledFont =
          GetScaledFont(&translator, blobFont.mFontInstanceKey);
      translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
    }

    Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
    ret =
        translator.TranslateRecording((char*)blob.begin().get(), blob.length());
    if (!ret) {
      gfxCriticalNote << "Replay failure: " << translator.GetError();
      MOZ_RELEASE_ASSERT(false);
    }
    offset = extra_end;
  }

  if (StaticPrefs::gfx_webrender_debug_blob_paint_flashing()) {
    dt->SetTransform(gfx::Matrix());
    float r = float(rand()) / float(RAND_MAX);
    float g = float(rand()) / float(RAND_MAX);
    float b = float(rand()) / float(RAND_MAX);
    dt->FillRect(gfx::Rect(origin.x, origin.y, size.width, size.height),
                 gfx::ColorPattern(gfx::DeviceColor(r, g, b, 0.5)));
  }

  if (aDirtyRect) {
    dt->PopClip();
  }

#if 0
  static int i = 0;
  char filename[40];
  sprintf(filename, "out%d.png", i++);
  gfxUtils::WriteAsPNG(dt, filename);
#endif

  return ret;
}

}  // namespace wr
}  // namespace mozilla

extern "C" {

bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
                        mozilla::wr::ImageFormat aFormat,
                        const mozilla::wr::LayoutIntRect* aRenderRect,
                        const mozilla::wr::DeviceIntRect* aVisibleRect,
                        const uint16_t aTileSize,
                        const mozilla::wr::TileOffset* aTileOffset,
                        const mozilla::wr::LayoutIntRect* aDirtyRect,
                        mozilla::wr::MutByteSlice output) {
  return mozilla::wr::Moz2DRenderCallback(
      mozilla::wr::ByteSliceToRange(blob),
      mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect,
      aRenderRect, aTileSize, aTileOffset, aDirtyRect,
      mozilla::wr::MutByteSliceToRange(output));
}

}  // extern