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 std::unordered_map
<WrFontKey
, FontTemplate
> sFontDataTable
;
82 std::unordered_map
<WrFontInstanceKey
, FontInstanceData
> sBlobFontTable
;
84 // Fixed-size ring buffer logging font deletion events to aid debugging.
85 static struct FontDeleteLog
{
86 static const size_t MAX_ENTRIES
= 256;
88 uint64_t mEntries
[MAX_ENTRIES
] = {0};
89 size_t mNextEntry
= 0;
91 void AddEntry(uint64_t aEntry
) {
92 mEntries
[mNextEntry
] = aEntry
;
93 mNextEntry
= (mNextEntry
+ 1) % MAX_ENTRIES
;
96 void Add(WrFontKey aKey
) { AddEntry(AsUint64(aKey
)); }
98 // Store namespace clears as font id 0, since this will never be allocated.
99 void Add(WrIdNamespace aNamespace
) {
100 AddEntry(AsUint64(WrFontKey
{aNamespace
, 0}));
103 void AddAll() { AddEntry(~0); }
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.
108 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
;
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";
121 } while (offset
!= mNextEntry
);
122 return "unknown font";
126 void ClearAllBlobImageResources() {
127 StaticMutexAutoLock
lock(sFontDataTableLock
);
128 sFontDeleteLog
.AddAll();
129 sBlobFontTable
.clear();
130 sFontDataTable
.clear();
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
);
144 for (auto i
= sFontDataTable
.begin(); i
!= sFontDataTable
.end();) {
145 if (i
->first
.mNamespace
== aNamespace
) {
146 i
= sFontDataTable
.erase(i
);
153 bool HasFontData(WrFontKey aKey
) {
154 StaticMutexAutoLock
lock(sFontDataTableLock
);
155 return sFontDataTable
.find(aKey
) != sFontDataTable
.end();
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
];
166 font
.mIndex
= aIndex
;
167 font
.mVec
= wr_add_ref_arc(aVec
);
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
];
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
);
186 font
.mUnscaledFont
= new UnscaledFontFontconfig(
187 reinterpret_cast<const char*>(aHandle
), aIndex
);
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
);
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
;
212 font
.mOptions
= Some(*aOptions
);
214 if (aPlatformOptions
) {
215 font
.mPlatformOptions
= Some(*aPlatformOptions
);
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
),
227 void DeleteBlobFont(WrFontInstanceKey aKey
) {
228 StaticMutexAutoLock
lock(sFontDataTableLock
);
229 auto i
= sBlobFontTable
.find(aKey
);
230 if (i
!= sBlobFontTable
.end()) {
231 sBlobFontTable
.erase(i
);
237 static RefPtr
<UnscaledFont
> GetUnscaledFont(Translator
* aTranslator
,
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
);
246 FontTemplate
& data
= i
->second
;
247 if (data
.mUnscaledFont
) {
248 return data
.mUnscaledFont
;
250 MOZ_ASSERT(data
.mData
);
254 #elif defined(XP_WIN)
256 #elif defined(ANDROID)
259 FontType::FONTCONFIG
;
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
;
266 gfxDevCrash(LogReason::NativeFontResourceNotFound
)
267 << "Failed to create NativeFontResource for FontKey " << aKey
.mHandle
;
269 // Instance data is only needed for GDI fonts which webrender does not
271 unscaledFont
= fontResource
->CreateUnscaledFont(data
.mIndex
, nullptr, 0);
273 gfxDevCrash(LogReason::UnscaledFontNotFound
)
274 << "Failed to create UnscaledFont for FontKey " << aKey
.mHandle
;
277 data
.mUnscaledFont
= unscaledFont
;
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 "
291 FontInstanceData
& data
= i
->second
;
292 if (data
.mScaledFont
) {
293 return data
.mScaledFont
;
295 RefPtr
<UnscaledFont
> unscaled
= GetUnscaledFont(aTranslator
, data
.mFontKey
);
299 RefPtr
<ScaledFont
> scaled
= unscaled
->CreateScaledFontFromWRFont(
300 data
.mSize
, data
.mOptions
.ptrOr(nullptr),
301 data
.mPlatformOptions
.ptrOr(nullptr), data
.mVariations
.get(),
302 data
.mNumVariations
);
304 gfxDevCrash(LogReason::ScaledFontNotFound
)
305 << "Failed to create ScaledFont for FontKey " << aKey
.mHandle
;
307 data
.mScaledFont
= scaled
;
308 return data
.mScaledFont
;
311 template <typename T
>
312 T
ConvertFromBytes(const uint8_t* bytes
) {
314 memcpy(&t
, bytes
, sizeof(T
));
323 Reader(const uint8_t* buf
, size_t len
) : buf(buf
), len(len
), pos(0) {}
325 template <typename T
>
327 MOZ_RELEASE_ASSERT(pos
+ sizeof(T
) <= len
);
328 T ret
= ConvertFromBytes
<T
>(buf
+ pos
);
333 size_t ReadSize() { return Read
<size_t>(); }
334 int ReadInt() { return Read
<int>(); }
336 IntRectAbsolute
ReadBounds() { return Read
<IntRectAbsolute
>(); }
338 layers::BlobFont
ReadBlobFont() { return Read
<layers::BlobFont
>(); }
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) {
356 auto stride
= size
.width
* gfx::BytesPerPixel(aFormat
);
358 if (aOutput
.length() < static_cast<size_t>(size
.height
* stride
)) {
362 // In bindings.rs we allocate a buffer filled with opaque white.
363 bool uninitialized
= false;
365 RefPtr
<gfx::DrawTarget
> dt
= gfx::Factory::CreateDrawTargetForData(
366 gfx::BackendType::SKIA
, aOutput
.begin().get(), size
, stride
, aFormat
,
373 // aRenderRect is the part of the blob that we are currently rendering
374 // (for example a tile) in the same coordinate space as aVisibleRect.
375 IntPoint origin
= gfx::IntPoint(aRenderRect
->min
.x
, aRenderRect
->min
.y
);
377 dt
= gfx::Factory::CreateOffsetDrawTarget(dt
, origin
);
382 // We try hard to not have empty blobs but we can end up with
383 // them because of CompositorHitTestInfo and merging.
384 size_t footerSize
= sizeof(size_t);
385 MOZ_RELEASE_ASSERT(aBlob
.length() >= footerSize
);
386 size_t indexOffset
= ConvertFromBytes
<size_t>(aBlob
.end().get() - footerSize
);
388 MOZ_RELEASE_ASSERT(indexOffset
<= aBlob
.length() - footerSize
);
389 Reader
reader(aBlob
.begin().get() + indexOffset
,
390 aBlob
.length() - footerSize
- indexOffset
);
392 auto bounds
= gfx::IntRect(origin
, size
);
395 gfx::Rect
dirty(aDirtyRect
->min
.x
, aDirtyRect
->min
.y
, aDirtyRect
->width(),
396 aDirtyRect
->height());
397 dt
->PushClipRect(dirty
);
399 bounds
.Intersect(IntRect(aDirtyRect
->min
.x
, aDirtyRect
->min
.y
,
400 aDirtyRect
->width(), aDirtyRect
->height()));
405 auto absBounds
= IntRectAbsolute::FromRect(bounds
);
406 while (reader
.pos
< reader
.len
) {
407 size_t end
= reader
.ReadSize();
408 size_t extra_end
= reader
.ReadSize();
409 MOZ_RELEASE_ASSERT(extra_end
>= end
);
410 MOZ_RELEASE_ASSERT(extra_end
< aBlob
.length());
412 auto combinedBounds
= absBounds
.Intersect(reader
.ReadBounds());
413 if (combinedBounds
.IsEmpty()) {
418 layers::WebRenderTranslator
translator(dt
);
419 Reader
fontReader(aBlob
.begin().get() + end
, extra_end
- end
);
420 size_t count
= fontReader
.ReadSize();
421 for (size_t i
= 0; i
< count
; i
++) {
422 layers::BlobFont blobFont
= fontReader
.ReadBlobFont();
423 RefPtr
<ScaledFont
> scaledFont
=
424 GetScaledFont(&translator
, blobFont
.mFontInstanceKey
);
425 translator
.AddScaledFont(blobFont
.mScaledFontPtr
, scaledFont
);
428 Range
<const uint8_t> blob(aBlob
.begin() + offset
, aBlob
.begin() + end
);
430 translator
.TranslateRecording((char*)blob
.begin().get(), blob
.length());
432 gfxCriticalNote
<< "Replay failure: " << translator
.GetError();
433 MOZ_RELEASE_ASSERT(false);
438 if (StaticPrefs::gfx_webrender_debug_blob_paint_flashing()) {
439 dt
->SetTransform(gfx::Matrix());
440 float r
= float(rand()) / float(RAND_MAX
);
441 float g
= float(rand()) / float(RAND_MAX
);
442 float b
= float(rand()) / float(RAND_MAX
);
443 dt
->FillRect(gfx::Rect(origin
.x
, origin
.y
, size
.width
, size
.height
),
444 gfx::ColorPattern(gfx::DeviceColor(r
, g
, b
, 0.5)));
454 sprintf(filename
, "out%d.png", i
++);
455 gfxUtils::WriteAsPNG(dt
, filename
);
462 } // namespace mozilla
466 bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob
,
467 mozilla::wr::ImageFormat aFormat
,
468 const mozilla::wr::LayoutIntRect
* aRenderRect
,
469 const mozilla::wr::DeviceIntRect
* aVisibleRect
,
470 const uint16_t aTileSize
,
471 const mozilla::wr::TileOffset
* aTileOffset
,
472 const mozilla::wr::LayoutIntRect
* aDirtyRect
,
473 mozilla::wr::MutByteSlice output
) {
474 return mozilla::wr::Moz2DRenderCallback(
475 mozilla::wr::ByteSliceToRange(blob
),
476 mozilla::wr::ImageFormatToSurfaceFormat(aFormat
), aVisibleRect
,
477 aRenderRect
, aTileSize
, aTileOffset
, aDirtyRect
,
478 mozilla::wr::MutByteSliceToRange(output
));