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 "mozilla/StaticPrefs_gfx.h"
8 #include "gfxUtils.h"
9 #include "mozilla/Mutex.h"
10 #include "mozilla/Range.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/gfx/RectAbsolute.h"
13 #include "mozilla/gfx/Logging.h"
14 #include "mozilla/gfx/RecordedEvent.h"
15 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
16 #include "WebRenderTypes.h"
17 #include "webrender_ffi.h"
18 #include "GeckoProfiler.h"
19 
20 #include <unordered_map>
21 
22 #ifdef XP_MACOSX
23 #  include "mozilla/gfx/UnscaledFontMac.h"
24 #elif defined(XP_WIN)
25 #  include "mozilla/gfx/UnscaledFontDWrite.h"
26 #else
27 #  include "mozilla/gfx/UnscaledFontFreeType.h"
28 #endif
29 
30 namespace std {
31 template <>
32 struct hash<mozilla::wr::FontKey> {
operator ()std::hash33   size_t operator()(const mozilla::wr::FontKey& key) const {
34     return hash<size_t>()(mozilla::wr::AsUint64(key));
35   }
36 };
37 
38 template <>
39 struct hash<mozilla::wr::FontInstanceKey> {
operator ()std::hash40   size_t operator()(const mozilla::wr::FontInstanceKey& key) const {
41     return hash<size_t>()(mozilla::wr::AsUint64(key));
42   }
43 };
44 };  // namespace std
45 
46 namespace mozilla {
47 
48 using namespace gfx;
49 
50 namespace wr {
51 
52 struct FontTemplate {
53   const uint8_t* mData;
54   size_t mSize;
55   uint32_t mIndex;
56   const VecU8* mVec;
57   RefPtr<UnscaledFont> mUnscaledFont;
58 
FontTemplatemozilla::wr::FontTemplate59   FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {}
60 
~FontTemplatemozilla::wr::FontTemplate61   ~FontTemplate() {
62     if (mVec) {
63       wr_dec_ref_arc(mVec);
64     }
65   }
66 };
67 
68 struct FontInstanceData {
69   WrFontKey mFontKey;
70   float mSize;
71   Maybe<FontInstanceOptions> mOptions;
72   Maybe<FontInstancePlatformOptions> mPlatformOptions;
73   UniquePtr<gfx::FontVariation[]> mVariations;
74   size_t mNumVariations;
75   RefPtr<ScaledFont> mScaledFont;
76 
FontInstanceDatamozilla::wr::FontInstanceData77   FontInstanceData() : mSize(0), mNumVariations(0) {}
78 };
79 
80 StaticMutex sFontDataTableLock;
81 std::unordered_map<WrFontKey, FontTemplate> sFontDataTable;
82 std::unordered_map<WrFontInstanceKey, FontInstanceData> sBlobFontTable;
83 
84 // Fixed-size ring buffer logging font deletion events to aid debugging.
85 static struct FontDeleteLog {
86   static const size_t MAX_ENTRIES = 256;
87 
88   uint64_t mEntries[MAX_ENTRIES] = {0};
89   size_t mNextEntry = 0;
90 
AddEntrymozilla::wr::FontDeleteLog91   void AddEntry(uint64_t aEntry) {
92     mEntries[mNextEntry] = aEntry;
93     mNextEntry = (mNextEntry + 1) % MAX_ENTRIES;
94   }
95 
Addmozilla::wr::FontDeleteLog96   void Add(WrFontKey aKey) { AddEntry(AsUint64(aKey)); }
97 
98   // Store namespace clears as font id 0, since this will never be allocated.
Addmozilla::wr::FontDeleteLog99   void Add(WrIdNamespace aNamespace) {
100     AddEntry(AsUint64(WrFontKey{aNamespace, 0}));
101   }
102 
AddAllmozilla::wr::FontDeleteLog103   void AddAll() { AddEntry(~0); }
104 
105   // Find a matching entry in the log, searching backwards starting at the
106   // newest entry and finishing with the oldest entry. Returns a brief
107   // description of why the font was deleted, if known.
Findmozilla::wr::FontDeleteLog108   const char* Find(WrFontKey aKey) {
109     uint64_t keyEntry = AsUint64(aKey);
110     uint64_t namespaceEntry = AsUint64(WrFontKey{aKey.mNamespace, 0});
111     size_t offset = mNextEntry;
112     do {
113       offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES;
114       if (mEntries[offset] == keyEntry) {
115         return "deleted font";
116       } else if (mEntries[offset] == namespaceEntry) {
117         return "cleared namespace";
118       } else if (mEntries[offset] == (uint64_t)~0) {
119         return "cleared all";
120       }
121     } while (offset != mNextEntry);
122     return "unknown font";
123   }
124 } sFontDeleteLog;
125 
ClearAllBlobImageResources()126 void ClearAllBlobImageResources() {
127   StaticMutexAutoLock lock(sFontDataTableLock);
128   sFontDeleteLog.AddAll();
129   sBlobFontTable.clear();
130   sFontDataTable.clear();
131 }
132 
133 extern "C" {
ClearBlobImageResources(WrIdNamespace aNamespace)134 void ClearBlobImageResources(WrIdNamespace aNamespace) {
135   StaticMutexAutoLock lock(sFontDataTableLock);
136   sFontDeleteLog.Add(aNamespace);
137   for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) {
138     if (i->first.mNamespace == aNamespace) {
139       i = sBlobFontTable.erase(i);
140     } else {
141       i++;
142     }
143   }
144   for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
145     if (i->first.mNamespace == aNamespace) {
146       i = sFontDataTable.erase(i);
147     } else {
148       i++;
149     }
150   }
151 }
152 
HasFontData(WrFontKey aKey)153 bool HasFontData(WrFontKey aKey) {
154   StaticMutexAutoLock lock(sFontDataTableLock);
155   return sFontDataTable.find(aKey) != sFontDataTable.end();
156 }
157 
AddFontData(WrFontKey aKey,const uint8_t * aData,size_t aSize,uint32_t aIndex,const ArcVecU8 * aVec)158 void AddFontData(WrFontKey aKey, const uint8_t* aData, size_t aSize,
159                  uint32_t aIndex, const ArcVecU8* aVec) {
160   StaticMutexAutoLock lock(sFontDataTableLock);
161   auto i = sFontDataTable.find(aKey);
162   if (i == sFontDataTable.end()) {
163     FontTemplate& font = sFontDataTable[aKey];
164     font.mData = aData;
165     font.mSize = aSize;
166     font.mIndex = aIndex;
167     font.mVec = wr_add_ref_arc(aVec);
168   }
169 }
170 
AddNativeFontHandle(WrFontKey aKey,void * aHandle,uint32_t aIndex)171 void AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) {
172   StaticMutexAutoLock lock(sFontDataTableLock);
173   auto i = sFontDataTable.find(aKey);
174   if (i == sFontDataTable.end()) {
175     FontTemplate& font = sFontDataTable[aKey];
176 #ifdef XP_MACOSX
177     font.mUnscaledFont =
178         new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false);
179 #elif defined(XP_WIN)
180     font.mUnscaledFont = new UnscaledFontDWrite(
181         reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr);
182 #elif defined(ANDROID)
183     font.mUnscaledFont = new UnscaledFontFreeType(
184         reinterpret_cast<const char*>(aHandle), aIndex);
185 #else
186     font.mUnscaledFont = new UnscaledFontFontconfig(
187         reinterpret_cast<const char*>(aHandle), aIndex);
188 #endif
189   }
190 }
191 
DeleteFontData(WrFontKey aKey)192 void DeleteFontData(WrFontKey aKey) {
193   StaticMutexAutoLock lock(sFontDataTableLock);
194   sFontDeleteLog.Add(aKey);
195   auto i = sFontDataTable.find(aKey);
196   if (i != sFontDataTable.end()) {
197     sFontDataTable.erase(i);
198   }
199 }
200 
AddBlobFont(WrFontInstanceKey aInstanceKey,WrFontKey aFontKey,float aSize,const FontInstanceOptions * aOptions,const FontInstancePlatformOptions * aPlatformOptions,const FontVariation * aVariations,size_t aNumVariations)201 void AddBlobFont(WrFontInstanceKey aInstanceKey, WrFontKey aFontKey,
202                  float aSize, const FontInstanceOptions* aOptions,
203                  const FontInstancePlatformOptions* aPlatformOptions,
204                  const FontVariation* aVariations, size_t aNumVariations) {
205   StaticMutexAutoLock lock(sFontDataTableLock);
206   auto i = sBlobFontTable.find(aInstanceKey);
207   if (i == sBlobFontTable.end()) {
208     FontInstanceData& font = sBlobFontTable[aInstanceKey];
209     font.mFontKey = aFontKey;
210     font.mSize = aSize;
211     if (aOptions) {
212       font.mOptions = Some(*aOptions);
213     }
214     if (aPlatformOptions) {
215       font.mPlatformOptions = Some(*aPlatformOptions);
216     }
217     if (aNumVariations) {
218       font.mNumVariations = aNumVariations;
219       font.mVariations.reset(new gfx::FontVariation[aNumVariations]);
220       PodCopy(font.mVariations.get(),
221               reinterpret_cast<const gfx::FontVariation*>(aVariations),
222               aNumVariations);
223     }
224   }
225 }
226 
DeleteBlobFont(WrFontInstanceKey aKey)227 void DeleteBlobFont(WrFontInstanceKey aKey) {
228   StaticMutexAutoLock lock(sFontDataTableLock);
229   auto i = sBlobFontTable.find(aKey);
230   if (i != sBlobFontTable.end()) {
231     sBlobFontTable.erase(i);
232   }
233 }
234 
235 }  // extern
236 
GetUnscaledFont(Translator * aTranslator,WrFontKey aKey)237 static RefPtr<UnscaledFont> GetUnscaledFont(Translator* aTranslator,
238                                             WrFontKey aKey) {
239   auto i = sFontDataTable.find(aKey);
240   if (i == sFontDataTable.end()) {
241     gfxDevCrash(LogReason::UnscaledFontNotFound)
242         << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle
243         << " because " << sFontDeleteLog.Find(aKey);
244     return nullptr;
245   }
246   FontTemplate& data = i->second;
247   if (data.mUnscaledFont) {
248     return data.mUnscaledFont;
249   }
250   MOZ_ASSERT(data.mData);
251   FontType type =
252 #ifdef XP_MACOSX
253       FontType::MAC;
254 #elif defined(XP_WIN)
255       FontType::DWRITE;
256 #elif defined(ANDROID)
257       FontType::FREETYPE;
258 #else
259       FontType::FONTCONFIG;
260 #endif
261   // makes a copy of the data
262   RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource(
263       (uint8_t*)data.mData, data.mSize, type, aTranslator->GetFontContext());
264   RefPtr<UnscaledFont> unscaledFont;
265   if (!fontResource) {
266     gfxDevCrash(LogReason::NativeFontResourceNotFound)
267         << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
268   } else {
269     // Instance data is only needed for GDI fonts which webrender does not
270     // support.
271     unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
272     if (!unscaledFont) {
273       gfxDevCrash(LogReason::UnscaledFontNotFound)
274           << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
275     }
276   }
277   data.mUnscaledFont = unscaledFont;
278   return unscaledFont;
279 }
280 
GetScaledFont(Translator * aTranslator,WrFontInstanceKey aKey)281 static RefPtr<ScaledFont> GetScaledFont(Translator* aTranslator,
282                                         WrFontInstanceKey aKey) {
283   StaticMutexAutoLock lock(sFontDataTableLock);
284   auto i = sBlobFontTable.find(aKey);
285   if (i == sBlobFontTable.end()) {
286     gfxDevCrash(LogReason::ScaledFontNotFound)
287         << "Failed to get ScaledFont entry for FontInstanceKey "
288         << aKey.mHandle;
289     return nullptr;
290   }
291   FontInstanceData& data = i->second;
292   if (data.mScaledFont) {
293     return data.mScaledFont;
294   }
295   RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
296   if (!unscaled) {
297     return nullptr;
298   }
299   RefPtr<ScaledFont> scaled = unscaled->CreateScaledFontFromWRFont(
300       data.mSize, data.mOptions.ptrOr(nullptr),
301       data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(),
302       data.mNumVariations);
303   if (!scaled) {
304     gfxDevCrash(LogReason::ScaledFontNotFound)
305         << "Failed to create ScaledFont for FontKey " << aKey.mHandle;
306   }
307   data.mScaledFont = scaled;
308   return data.mScaledFont;
309 }
310 
311 template <typename T>
ConvertFromBytes(const uint8_t * bytes)312 T ConvertFromBytes(const uint8_t* bytes) {
313   T t;
314   memcpy(&t, bytes, sizeof(T));
315   return t;
316 }
317 
318 struct Reader {
319   const uint8_t* buf;
320   size_t len;
321   size_t pos;
322 
Readermozilla::wr::Reader323   Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {}
324 
325   template <typename T>
Readmozilla::wr::Reader326   T Read() {
327     MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len);
328     T ret = ConvertFromBytes<T>(buf + pos);
329     pos += sizeof(T);
330     return ret;
331   }
332 
ReadSizemozilla::wr::Reader333   size_t ReadSize() { return Read<size_t>(); }
ReadIntmozilla::wr::Reader334   int ReadInt() { return Read<int>(); }
335 
ReadBoundsmozilla::wr::Reader336   IntRectAbsolute ReadBounds() { return Read<IntRectAbsolute>(); }
337 
ReadBlobFontmozilla::wr::Reader338   layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); }
339 };
340 
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)341 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
342                                 gfx::SurfaceFormat aFormat,
343                                 const mozilla::wr::DeviceIntRect* aVisibleRect,
344                                 const mozilla::wr::LayoutIntRect* aRenderRect,
345                                 const uint16_t aTileSize,
346                                 const mozilla::wr::TileOffset* aTileOffset,
347                                 const mozilla::wr::LayoutIntRect* aDirtyRect,
348                                 Range<uint8_t> aOutput) {
349   IntSize size(aRenderRect->width(), aRenderRect->height());
350   AUTO_PROFILER_TRACING_MARKER("WebRender", "RasterizeSingleBlob", GRAPHICS);
351   MOZ_RELEASE_ASSERT(size.width > 0 && size.height > 0);
352   if (size.width <= 0 || size.height <= 0) {
353     return false;
354   }
355 
356   auto stride = size.width * gfx::BytesPerPixel(aFormat);
357 
358   if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
359     return false;
360   }
361 
362   // In bindings.rs we allocate a buffer filled with opaque white.
363   bool uninitialized = false;
364 
365   RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData(
366       gfx::BackendType::SKIA, aOutput.begin().get(), size, stride, aFormat,
367       uninitialized);
368 
369   if (!dt) {
370     return false;
371   }
372 
373   // We try hard to not have empty blobs but we can end up with
374   // them because of CompositorHitTestInfo and merging.
375   size_t footerSize = sizeof(size_t);
376   MOZ_RELEASE_ASSERT(aBlob.length() >= footerSize);
377   size_t indexOffset = ConvertFromBytes<size_t>(aBlob.end().get() - footerSize);
378 
379   // aRenderRect is the part of the blob that we are currently rendering
380   // (for example a tile) in the same coordinate space as aVisibleRect.
381   IntPoint origin = gfx::IntPoint(aRenderRect->min.x, aRenderRect->min.y);
382 
383   MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - footerSize);
384   Reader reader(aBlob.begin().get() + indexOffset,
385                 aBlob.length() - footerSize - indexOffset);
386 
387   dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin);
388 
389   auto bounds = gfx::IntRect(origin, size);
390 
391   if (aDirtyRect) {
392     gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(),
393                     aDirtyRect->height());
394     dt->PushClipRect(dirty);
395     bounds =
396         bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y,
397                                  aDirtyRect->width(), aDirtyRect->height()));
398   }
399 
400   bool ret = true;
401   size_t offset = 0;
402   auto absBounds = IntRectAbsolute::FromRect(bounds);
403   while (reader.pos < reader.len) {
404     size_t end = reader.ReadSize();
405     size_t extra_end = reader.ReadSize();
406     MOZ_RELEASE_ASSERT(extra_end >= end);
407     MOZ_RELEASE_ASSERT(extra_end < aBlob.length());
408 
409     auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
410     if (combinedBounds.IsEmpty()) {
411       offset = extra_end;
412       continue;
413     }
414 
415     layers::WebRenderTranslator translator(dt);
416     Reader fontReader(aBlob.begin().get() + end, extra_end - end);
417     size_t count = fontReader.ReadSize();
418     for (size_t i = 0; i < count; i++) {
419       layers::BlobFont blobFont = fontReader.ReadBlobFont();
420       RefPtr<ScaledFont> scaledFont =
421           GetScaledFont(&translator, blobFont.mFontInstanceKey);
422       translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
423     }
424 
425     Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
426     ret =
427         translator.TranslateRecording((char*)blob.begin().get(), blob.length());
428     if (!ret) {
429       gfxCriticalNote << "Replay failure: " << translator.GetError();
430       MOZ_RELEASE_ASSERT(false);
431     }
432     offset = extra_end;
433   }
434 
435   if (StaticPrefs::gfx_webrender_blob_paint_flashing()) {
436     dt->SetTransform(gfx::Matrix());
437     float r = float(rand()) / float(RAND_MAX);
438     float g = float(rand()) / float(RAND_MAX);
439     float b = float(rand()) / float(RAND_MAX);
440     dt->FillRect(gfx::Rect(origin.x, origin.y, size.width, size.height),
441                  gfx::ColorPattern(gfx::DeviceColor(r, g, b, 0.5)));
442   }
443 
444   if (aDirtyRect) {
445     dt->PopClip();
446   }
447 
448 #if 0
449   static int i = 0;
450   char filename[40];
451   sprintf(filename, "out%d.png", i++);
452   gfxUtils::WriteAsPNG(dt, filename);
453 #endif
454 
455   return ret;
456 }
457 
458 }  // namespace wr
459 }  // namespace mozilla
460 
461 extern "C" {
462 
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)463 bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob,
464                         mozilla::wr::ImageFormat aFormat,
465                         const mozilla::wr::LayoutIntRect* aRenderRect,
466                         const mozilla::wr::DeviceIntRect* aVisibleRect,
467                         const uint16_t aTileSize,
468                         const mozilla::wr::TileOffset* aTileOffset,
469                         const mozilla::wr::LayoutIntRect* aDirtyRect,
470                         mozilla::wr::MutByteSlice output) {
471   return mozilla::wr::Moz2DRenderCallback(
472       mozilla::wr::ByteSliceToRange(blob),
473       mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect,
474       aRenderRect, aTileSize, aTileOffset, aDirtyRect,
475       mozilla::wr::MutByteSliceToRange(output));
476 }
477 
478 }  // extern
479