Bug 1795082 - Part 2/2: Drop post-processing from getURL() r=zombie
[gecko.git] / gfx / webrender_bindings / Moz2DImageRenderer.cpp
blob66f61d719e78552a23e962a57191d8838c5260d1
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 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;
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";
121 } while (offset != mNextEntry);
122 return "unknown font";
124 } sFontDeleteLog;
126 void ClearAllBlobImageResources() {
127 StaticMutexAutoLock lock(sFontDataTableLock);
128 sFontDeleteLog.AddAll();
129 sBlobFontTable.clear();
130 sFontDataTable.clear();
133 extern "C" {
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++;
144 for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) {
145 if (i->first.mNamespace == aNamespace) {
146 i = sFontDataTable.erase(i);
147 } else {
148 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];
164 font.mData = aData;
165 font.mSize = aSize;
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];
176 #ifdef XP_DARWIN
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
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;
210 font.mSize = aSize;
211 if (aOptions) {
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),
222 aNumVariations);
227 void DeleteBlobFont(WrFontInstanceKey aKey) {
228 StaticMutexAutoLock lock(sFontDataTableLock);
229 auto i = sBlobFontTable.find(aKey);
230 if (i != sBlobFontTable.end()) {
231 sBlobFontTable.erase(i);
235 } // extern
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;
246 FontTemplate& data = i->second;
247 if (data.mUnscaledFont) {
248 return data.mUnscaledFont;
250 MOZ_ASSERT(data.mData);
251 FontType type =
252 #ifdef XP_DARWIN
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;
277 data.mUnscaledFont = unscaledFont;
278 return 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 "
288 << aKey.mHandle;
289 return nullptr;
291 FontInstanceData& data = i->second;
292 if (data.mScaledFont) {
293 return data.mScaledFont;
295 RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey);
296 if (!unscaled) {
297 return nullptr;
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;
307 data.mScaledFont = scaled;
308 return data.mScaledFont;
311 template <typename T>
312 T ConvertFromBytes(const uint8_t* bytes) {
313 T t;
314 memcpy(&t, bytes, sizeof(T));
315 return t;
318 struct Reader {
319 const uint8_t* buf;
320 size_t len;
321 size_t pos;
323 Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {}
325 template <typename T>
326 T Read() {
327 MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len);
328 T ret = ConvertFromBytes<T>(buf + pos);
329 pos += sizeof(T);
330 return ret;
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) {
353 return false;
356 auto stride = size.width * gfx::BytesPerPixel(aFormat);
358 if (aOutput.length() < static_cast<size_t>(size.height * stride)) {
359 return false;
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,
367 uninitialized);
369 if (!dt) {
370 return false;
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);
378 if (!dt) {
379 return false;
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);
394 if (aDirtyRect) {
395 gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(),
396 aDirtyRect->height());
397 dt->PushClipRect(dirty);
398 bounds =
399 bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y,
400 aDirtyRect->width(), aDirtyRect->height()));
403 bool ret = true;
404 size_t offset = 0;
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()) {
414 offset = extra_end;
415 continue;
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);
429 ret =
430 translator.TranslateRecording((char*)blob.begin().get(), blob.length());
431 if (!ret) {
432 gfxCriticalNote << "Replay failure: " << translator.GetError();
433 MOZ_RELEASE_ASSERT(false);
435 offset = extra_end;
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)));
447 if (aDirtyRect) {
448 dt->PopClip();
451 #if 0
452 static int i = 0;
453 char filename[40];
454 sprintf(filename, "out%d.png", i++);
455 gfxUtils::WriteAsPNG(dt, filename);
456 #endif
458 return ret;
461 } // namespace wr
462 } // namespace mozilla
464 extern "C" {
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));
481 } // extern