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 "mozilla/StaticPrefs_gfx.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"
20 #include <unordered_map>
23 # include "mozilla/gfx/UnscaledFontMac.h"
25 # include "mozilla/gfx/UnscaledFontDWrite.h"
27 # include "mozilla/gfx/UnscaledFontFreeType.h"
32 struct hash
<mozilla::wr::FontKey
> {
33 size_t operator()(const mozilla::wr::FontKey
& key
) const {
34 return hash
<size_t>()(mozilla::wr::AsUint64(key
));
39 struct hash
<mozilla::wr::FontInstanceKey
> {
40 size_t operator()(const mozilla::wr::FontInstanceKey
& key
) const {
41 return hash
<size_t>()(mozilla::wr::AsUint64(key
));
57 RefPtr
<UnscaledFont
> mUnscaledFont
;
59 FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {}
68 struct FontInstanceData
{
71 Maybe
<FontInstanceOptions
> mOptions
;
72 Maybe
<FontInstancePlatformOptions
> mPlatformOptions
;
73 UniquePtr
<gfx::FontVariation
[]> mVariations
;
74 size_t mNumVariations
;
75 RefPtr
<ScaledFont
> mScaledFont
;
77 FontInstanceData() : mSize(0), mNumVariations(0) {}
80 StaticMutex sFontDataTableLock
;
81 MOZ_RUNINIT
std::unordered_map
<WrFontKey
, FontTemplate
> sFontDataTable
;
82 MOZ_RUNINIT
std::unordered_map
<WrFontInstanceKey
, FontInstanceData
>
85 // Fixed-size ring buffer logging font deletion events to aid debugging.
86 static struct FontDeleteLog
{
87 static const size_t MAX_ENTRIES
= 256;
89 uint64_t mEntries
[MAX_ENTRIES
] = {0};
90 size_t mNextEntry
= 0;
92 void AddEntry(uint64_t aEntry
) {
93 mEntries
[mNextEntry
] = aEntry
;
94 mNextEntry
= (mNextEntry
+ 1) % MAX_ENTRIES
;
97 void Add(WrFontKey aKey
) { AddEntry(AsUint64(aKey
)); }
99 // Store namespace clears as font id 0, since this will never be allocated.
100 void Add(WrIdNamespace aNamespace
) {
101 AddEntry(AsUint64(WrFontKey
{aNamespace
, 0}));
104 void AddAll() { AddEntry(~0); }
106 // Find a matching entry in the log, searching backwards starting at the
107 // newest entry and finishing with the oldest entry. Returns a brief
108 // description of why the font was deleted, if known.
109 const char* Find(WrFontKey aKey
) {
110 uint64_t keyEntry
= AsUint64(aKey
);
111 uint64_t namespaceEntry
= AsUint64(WrFontKey
{aKey
.mNamespace
, 0});
112 size_t offset
= mNextEntry
;
114 offset
= (offset
+ MAX_ENTRIES
- 1) % MAX_ENTRIES
;
115 if (mEntries
[offset
] == keyEntry
) {
116 return "deleted font";
117 } else if (mEntries
[offset
] == namespaceEntry
) {
118 return "cleared namespace";
119 } else if (mEntries
[offset
] == (uint64_t)~0) {
120 return "cleared all";
122 } while (offset
!= mNextEntry
);
123 return "unknown font";
127 void ClearAllBlobImageResources() {
128 StaticMutexAutoLock
lock(sFontDataTableLock
);
129 sFontDeleteLog
.AddAll();
130 sBlobFontTable
.clear();
131 sFontDataTable
.clear();
135 void ClearBlobImageResources(WrIdNamespace aNamespace
) {
136 StaticMutexAutoLock
lock(sFontDataTableLock
);
137 sFontDeleteLog
.Add(aNamespace
);
138 for (auto i
= sBlobFontTable
.begin(); i
!= sBlobFontTable
.end();) {
139 if (i
->first
.mNamespace
== aNamespace
) {
140 i
= sBlobFontTable
.erase(i
);
145 for (auto i
= sFontDataTable
.begin(); i
!= sFontDataTable
.end();) {
146 if (i
->first
.mNamespace
== aNamespace
) {
147 i
= sFontDataTable
.erase(i
);
154 bool HasFontData(WrFontKey aKey
) {
155 StaticMutexAutoLock
lock(sFontDataTableLock
);
156 return sFontDataTable
.find(aKey
) != sFontDataTable
.end();
159 void AddFontData(WrFontKey aKey
, const uint8_t* aData
, size_t aSize
,
160 uint32_t aIndex
, const ArcVecU8
* aVec
) {
161 StaticMutexAutoLock
lock(sFontDataTableLock
);
162 auto i
= sFontDataTable
.find(aKey
);
163 if (i
== sFontDataTable
.end()) {
164 FontTemplate
& font
= sFontDataTable
[aKey
];
167 font
.mIndex
= aIndex
;
168 font
.mVec
= wr_add_ref_arc(aVec
);
172 void AddNativeFontHandle(WrFontKey aKey
, void* aHandle
, uint32_t aIndex
) {
173 StaticMutexAutoLock
lock(sFontDataTableLock
);
174 auto i
= sFontDataTable
.find(aKey
);
175 if (i
== sFontDataTable
.end()) {
176 FontTemplate
& font
= sFontDataTable
[aKey
];
179 new UnscaledFontMac(reinterpret_cast<CGFontRef
>(aHandle
), false);
180 #elif defined(XP_WIN)
181 font
.mUnscaledFont
= new UnscaledFontDWrite(
182 reinterpret_cast<IDWriteFontFace
*>(aHandle
), nullptr);
183 #elif defined(ANDROID)
184 font
.mUnscaledFont
= new UnscaledFontFreeType(
185 reinterpret_cast<const char*>(aHandle
), aIndex
);
187 font
.mUnscaledFont
= new UnscaledFontFontconfig(
188 reinterpret_cast<const char*>(aHandle
), aIndex
);
193 void DeleteFontData(WrFontKey aKey
) {
194 StaticMutexAutoLock
lock(sFontDataTableLock
);
195 sFontDeleteLog
.Add(aKey
);
196 auto i
= sFontDataTable
.find(aKey
);
197 if (i
!= sFontDataTable
.end()) {
198 sFontDataTable
.erase(i
);
202 void AddBlobFont(WrFontInstanceKey aInstanceKey
, WrFontKey aFontKey
,
203 float aSize
, const FontInstanceOptions
* aOptions
,
204 const FontInstancePlatformOptions
* aPlatformOptions
,
205 const FontVariation
* aVariations
, size_t aNumVariations
) {
206 StaticMutexAutoLock
lock(sFontDataTableLock
);
207 auto i
= sBlobFontTable
.find(aInstanceKey
);
208 if (i
== sBlobFontTable
.end()) {
209 FontInstanceData
& font
= sBlobFontTable
[aInstanceKey
];
210 font
.mFontKey
= aFontKey
;
213 font
.mOptions
= Some(*aOptions
);
215 if (aPlatformOptions
) {
216 font
.mPlatformOptions
= Some(*aPlatformOptions
);
218 if (aNumVariations
) {
219 font
.mNumVariations
= aNumVariations
;
220 font
.mVariations
.reset(new gfx::FontVariation
[aNumVariations
]);
221 PodCopy(font
.mVariations
.get(),
222 reinterpret_cast<const gfx::FontVariation
*>(aVariations
),
228 void DeleteBlobFont(WrFontInstanceKey aKey
) {
229 StaticMutexAutoLock
lock(sFontDataTableLock
);
230 auto i
= sBlobFontTable
.find(aKey
);
231 if (i
!= sBlobFontTable
.end()) {
232 sBlobFontTable
.erase(i
);
238 static RefPtr
<UnscaledFont
> GetUnscaledFont(Translator
* aTranslator
,
240 auto i
= sFontDataTable
.find(aKey
);
241 if (i
== sFontDataTable
.end()) {
242 gfxDevCrash(LogReason::UnscaledFontNotFound
)
243 << "Failed to get UnscaledFont entry for FontKey " << aKey
.mHandle
244 << " because " << sFontDeleteLog
.Find(aKey
);
247 FontTemplate
& data
= i
->second
;
248 if (data
.mUnscaledFont
) {
249 return data
.mUnscaledFont
;
251 MOZ_ASSERT(data
.mData
);
255 #elif defined(XP_WIN)
257 #elif defined(ANDROID)
260 FontType::FONTCONFIG
;
262 // makes a copy of the data
263 RefPtr
<NativeFontResource
> fontResource
= Factory::CreateNativeFontResource(
264 (uint8_t*)data
.mData
, data
.mSize
, type
, aTranslator
->GetFontContext());
265 RefPtr
<UnscaledFont
> unscaledFont
;
267 gfxDevCrash(LogReason::NativeFontResourceNotFound
)
268 << "Failed to create NativeFontResource for FontKey " << aKey
.mHandle
;
270 // Instance data is only needed for GDI fonts which webrender does not
272 unscaledFont
= fontResource
->CreateUnscaledFont(data
.mIndex
, nullptr, 0);
274 gfxDevCrash(LogReason::UnscaledFontNotFound
)
275 << "Failed to create UnscaledFont for FontKey " << aKey
.mHandle
;
278 data
.mUnscaledFont
= unscaledFont
;
282 static RefPtr
<ScaledFont
> GetScaledFont(Translator
* aTranslator
,
283 WrFontInstanceKey aKey
) {
284 StaticMutexAutoLock
lock(sFontDataTableLock
);
285 auto i
= sBlobFontTable
.find(aKey
);
286 if (i
== sBlobFontTable
.end()) {
287 gfxDevCrash(LogReason::ScaledFontNotFound
)
288 << "Failed to get ScaledFont entry for FontInstanceKey "
292 FontInstanceData
& data
= i
->second
;
293 if (data
.mScaledFont
) {
294 return data
.mScaledFont
;
296 RefPtr
<UnscaledFont
> unscaled
= GetUnscaledFont(aTranslator
, data
.mFontKey
);
300 RefPtr
<ScaledFont
> scaled
= unscaled
->CreateScaledFontFromWRFont(
301 data
.mSize
, data
.mOptions
.ptrOr(nullptr),
302 data
.mPlatformOptions
.ptrOr(nullptr), data
.mVariations
.get(),
303 data
.mNumVariations
);
305 gfxDevCrash(LogReason::ScaledFontNotFound
)
306 << "Failed to create ScaledFont for FontKey " << aKey
.mHandle
;
308 data
.mScaledFont
= scaled
;
309 return data
.mScaledFont
;
312 template <typename T
>
313 T
ConvertFromBytes(const uint8_t* bytes
) {
315 memcpy(&t
, bytes
, sizeof(T
));
324 Reader(const uint8_t* buf
, size_t len
) : buf(buf
), len(len
), pos(0) {}
326 template <typename T
>
328 MOZ_RELEASE_ASSERT(pos
+ sizeof(T
) <= len
);
329 T ret
= ConvertFromBytes
<T
>(buf
+ pos
);
334 size_t ReadSize() { return Read
<size_t>(); }
335 int ReadInt() { return Read
<int>(); }
337 IntRectAbsolute
ReadBounds() { return Read
<IntRectAbsolute
>(); }
339 layers::BlobFont
ReadBlobFont() { return Read
<layers::BlobFont
>(); }
342 static bool Moz2DRenderCallback(const Range
<const uint8_t> aBlob
,
343 gfx::SurfaceFormat aFormat
,
344 const mozilla::wr::DeviceIntRect
* aVisibleRect
,
345 const mozilla::wr::LayoutIntRect
* aRenderRect
,
346 const uint16_t aTileSize
,
347 const mozilla::wr::TileOffset
* aTileOffset
,
348 const mozilla::wr::LayoutIntRect
* aDirtyRect
,
349 Range
<uint8_t> aOutput
) {
350 IntSize
size(aRenderRect
->width(), aRenderRect
->height());
351 AUTO_PROFILER_TRACING_MARKER("WebRender", "RasterizeSingleBlob", GRAPHICS
);
352 MOZ_RELEASE_ASSERT(size
.width
> 0 && size
.height
> 0);
353 if (size
.width
<= 0 || size
.height
<= 0) {
357 auto stride
= size
.width
* gfx::BytesPerPixel(aFormat
);
359 if (aOutput
.length() < static_cast<size_t>(size
.height
* stride
)) {
363 // In bindings.rs we allocate a buffer filled with opaque white.
364 bool uninitialized
= false;
366 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForData(
367 gfx::BackendType::SKIA
, aOutput
.begin().get(), size
, stride
, aFormat
,
374 // aRenderRect is the part of the blob that we are currently rendering
375 // (for example a tile) in the same coordinate space as aVisibleRect.
376 IntPoint origin
= gfx::IntPoint(aRenderRect
->min
.x
, aRenderRect
->min
.y
);
378 dt
= gfx::Factory::CreateOffsetDrawTarget(dt
, origin
);
383 // We try hard to not have empty blobs but we can end up with
384 // them because of CompositorHitTestInfo and merging.
385 size_t footerSize
= sizeof(size_t);
386 MOZ_RELEASE_ASSERT(aBlob
.length() >= footerSize
);
387 size_t indexOffset
= ConvertFromBytes
<size_t>(aBlob
.end().get() - footerSize
);
389 MOZ_RELEASE_ASSERT(indexOffset
<= aBlob
.length() - footerSize
);
390 Reader
reader(aBlob
.begin().get() + indexOffset
,
391 aBlob
.length() - footerSize
- indexOffset
);
393 auto bounds
= gfx::IntRect(origin
, size
);
396 gfx::Rect
dirty(aDirtyRect
->min
.x
, aDirtyRect
->min
.y
, aDirtyRect
->width(),
397 aDirtyRect
->height());
398 dt
->PushClipRect(dirty
);
400 bounds
.Intersect(IntRect(aDirtyRect
->min
.x
, aDirtyRect
->min
.y
,
401 aDirtyRect
->width(), aDirtyRect
->height()));
406 auto absBounds
= IntRectAbsolute::FromRect(bounds
);
407 while (reader
.pos
< reader
.len
) {
408 size_t end
= reader
.ReadSize();
409 size_t extra_end
= reader
.ReadSize();
410 MOZ_RELEASE_ASSERT(offset
<= end
);
411 MOZ_RELEASE_ASSERT(extra_end
>= end
);
412 MOZ_RELEASE_ASSERT(extra_end
< aBlob
.length());
414 auto combinedBounds
= absBounds
.Intersect(reader
.ReadBounds());
415 if (combinedBounds
.IsEmpty()) {
420 layers::WebRenderTranslator
translator(dt
);
421 Reader
fontReader(aBlob
.begin().get() + end
, extra_end
- end
);
422 size_t count
= fontReader
.ReadSize();
423 for (size_t i
= 0; i
< count
; i
++) {
424 layers::BlobFont blobFont
= fontReader
.ReadBlobFont();
425 RefPtr
<ScaledFont
> scaledFont
=
426 GetScaledFont(&translator
, blobFont
.mFontInstanceKey
);
427 translator
.AddScaledFont(blobFont
.mScaledFontPtr
, scaledFont
);
430 Range
<const uint8_t> blob(aBlob
.begin() + offset
, aBlob
.begin() + end
);
432 translator
.TranslateRecording((char*)blob
.begin().get(), blob
.length());
434 gfxCriticalNote
<< "Replay failure: " << translator
.GetError();
435 MOZ_RELEASE_ASSERT(false);
440 if (StaticPrefs::gfx_webrender_debug_blob_paint_flashing()) {
441 dt
->SetTransform(gfx::Matrix());
442 float r
= float(rand()) / float(RAND_MAX
);
443 float g
= float(rand()) / float(RAND_MAX
);
444 float b
= float(rand()) / float(RAND_MAX
);
445 dt
->FillRect(gfx::Rect(origin
.x
, origin
.y
, size
.width
, size
.height
),
446 gfx::ColorPattern(gfx::DeviceColor(r
, g
, b
, 0.5)));
456 sprintf(filename
, "out%d.png", i
++);
457 gfxUtils::WriteAsPNG(dt
, filename
);
464 } // namespace mozilla
468 bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob
,
469 mozilla::wr::ImageFormat aFormat
,
470 const mozilla::wr::LayoutIntRect
* aRenderRect
,
471 const mozilla::wr::DeviceIntRect
* aVisibleRect
,
472 const uint16_t aTileSize
,
473 const mozilla::wr::TileOffset
* aTileOffset
,
474 const mozilla::wr::LayoutIntRect
* aDirtyRect
,
475 mozilla::wr::MutByteSlice output
) {
476 return mozilla::wr::Moz2DRenderCallback(
477 mozilla::wr::ByteSliceToRange(blob
),
478 mozilla::wr::ImageFormatToSurfaceFormat(aFormat
), aVisibleRect
,
479 aRenderRect
, aTileSize
, aTileOffset
, aDirtyRect
,
480 mozilla::wr::MutByteSliceToRange(output
));