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