Bug 1918529 - fix some subpixel misalignment issues with gfx.webrender.svg-filter...
[gecko.git] / gfx / webrender_bindings / Moz2DImageRenderer.cpp
blob9d455fbe86b1a840934ff4fdfa014d46fe4e6efa
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"
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"
20 #include <unordered_map>
22 #ifdef XP_DARWIN
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
30 namespace std {
31 template <>
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));
38 template <>
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));
44 }; // namespace std
46 namespace mozilla {
48 using namespace gfx;
50 namespace wr {
52 struct FontTemplate {
53 const uint8_t* mData;
54 size_t mSize;
55 uint32_t mIndex;
56 const VecU8* mVec;
57 RefPtr<UnscaledFont> mUnscaledFont;
59 FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {}
61 ~FontTemplate() {
62 if (mVec) {
63 wr_dec_ref_arc(mVec);
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;
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>
83 sBlobFontTable;
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;
113 do {
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";
125 } sFontDeleteLog;
127 void ClearAllBlobImageResources() {
128 StaticMutexAutoLock lock(sFontDataTableLock);
129 sFontDeleteLog.AddAll();
130 sBlobFontTable.clear();
131 sFontDataTable.clear();
134 extern "C" {
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);
141 } else {
142 i++;
145 for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
146 if (i->first.mNamespace == aNamespace) {
147 i = sFontDataTable.erase(i);
148 } else {
149 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];
165 font.mData = aData;
166 font.mSize = aSize;
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];
177 #ifdef XP_DARWIN
178 font.mUnscaledFont =
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);
186 #else
187 font.mUnscaledFont = new UnscaledFontFontconfig(
188 reinterpret_cast<const char*>(aHandle), aIndex);
189 #endif
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;
211 font.mSize = aSize;
212 if (aOptions) {
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),
223 aNumVariations);
228 void DeleteBlobFont(WrFontInstanceKey aKey) {
229 StaticMutexAutoLock lock(sFontDataTableLock);
230 auto i = sBlobFontTable.find(aKey);
231 if (i != sBlobFontTable.end()) {
232 sBlobFontTable.erase(i);
236 } // extern
238 static RefPtr<UnscaledFont> GetUnscaledFont(Translator* aTranslator,
239 WrFontKey aKey) {
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);
245 return nullptr;
247 FontTemplate& data = i->second;
248 if (data.mUnscaledFont) {
249 return data.mUnscaledFont;
251 MOZ_ASSERT(data.mData);
252 FontType type =
253 #ifdef XP_DARWIN
254 FontType::MAC;
255 #elif defined(XP_WIN)
256 FontType::DWRITE;
257 #elif defined(ANDROID)
258 FontType::FREETYPE;
259 #else
260 FontType::FONTCONFIG;
261 #endif
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;
266 if (!fontResource) {
267 gfxDevCrash(LogReason::NativeFontResourceNotFound)
268 << "Failed to create NativeFontResource for FontKey " << aKey.mHandle;
269 } else {
270 // Instance data is only needed for GDI fonts which webrender does not
271 // support.
272 unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
273 if (!unscaledFont) {
274 gfxDevCrash(LogReason::UnscaledFontNotFound)
275 << "Failed to create UnscaledFont for FontKey " << aKey.mHandle;
278 data.mUnscaledFont = unscaledFont;
279 return 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 "
289 << aKey.mHandle;
290 return nullptr;
292 FontInstanceData& data = i->second;
293 if (data.mScaledFont) {
294 return data.mScaledFont;
296 RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
297 if (!unscaled) {
298 return nullptr;
300 RefPtr<ScaledFont> scaled = unscaled->CreateScaledFontFromWRFont(
301 data.mSize, data.mOptions.ptrOr(nullptr),
302 data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(),
303 data.mNumVariations);
304 if (!scaled) {
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) {
314 T t;
315 memcpy(&t, bytes, sizeof(T));
316 return t;
319 struct Reader {
320 const uint8_t* buf;
321 size_t len;
322 size_t pos;
324 Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {}
326 template <typename T>
327 T Read() {
328 MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len);
329 T ret = ConvertFromBytes<T>(buf + pos);
330 pos += sizeof(T);
331 return ret;
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) {
354 return false;
357 auto stride = size.width * gfx::BytesPerPixel(aFormat);
359 if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
360 return false;
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,
368 uninitialized);
370 if (!dt) {
371 return false;
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);
379 if (!dt) {
380 return false;
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);
395 if (aDirtyRect) {
396 gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(),
397 aDirtyRect->height());
398 dt->PushClipRect(dirty);
399 bounds =
400 bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y,
401 aDirtyRect->width(), aDirtyRect->height()));
404 bool ret = true;
405 size_t offset = 0;
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()) {
416 offset = extra_end;
417 continue;
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);
431 ret =
432 translator.TranslateRecording((char*)blob.begin().get(), blob.length());
433 if (!ret) {
434 gfxCriticalNote << "Replay failure: " << translator.GetError();
435 MOZ_RELEASE_ASSERT(false);
437 offset = extra_end;
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)));
449 if (aDirtyRect) {
450 dt->PopClip();
453 #if 0
454 static int i = 0;
455 char filename[40];
456 sprintf(filename, "out%d.png", i++);
457 gfxUtils::WriteAsPNG(dt, filename);
458 #endif
460 return ret;
463 } // namespace wr
464 } // namespace mozilla
466 extern "C" {
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));
483 } // extern