Moz2DImageRenderer.cpp (16771B)
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 #ifdef FUZZING 22 # include "prenv.h" 23 #endif 24 25 #ifdef XP_DARWIN 26 # include "mozilla/gfx/UnscaledFontMac.h" 27 #elif defined(XP_WIN) 28 # include "mozilla/gfx/UnscaledFontDWrite.h" 29 #else 30 # include "mozilla/gfx/UnscaledFontFreeType.h" 31 #endif 32 33 namespace std { 34 template <> 35 struct hash<mozilla::wr::FontKey> { 36 size_t operator()(const mozilla::wr::FontKey& key) const { 37 return hash<size_t>()(mozilla::wr::AsUint64(key)); 38 } 39 }; 40 41 template <> 42 struct hash<mozilla::wr::FontInstanceKey> { 43 size_t operator()(const mozilla::wr::FontInstanceKey& key) const { 44 return hash<size_t>()(mozilla::wr::AsUint64(key)); 45 } 46 }; 47 }; // namespace std 48 49 namespace mozilla { 50 51 using namespace gfx; 52 53 namespace wr { 54 55 struct FontTemplate { 56 const uint8_t* mData; 57 size_t mSize; 58 uint32_t mIndex; 59 const VecU8* mVec; 60 RefPtr<UnscaledFont> mUnscaledFont; 61 62 FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {} 63 64 ~FontTemplate() { 65 if (mVec) { 66 wr_dec_ref_arc(mVec); 67 } 68 } 69 }; 70 71 struct FontInstanceData { 72 WrFontKey mFontKey; 73 float mSize; 74 Maybe<FontInstanceOptions> mOptions; 75 Maybe<FontInstancePlatformOptions> mPlatformOptions; 76 UniquePtr<gfx::FontVariation[]> mVariations; 77 size_t mNumVariations; 78 RefPtr<ScaledFont> mScaledFont; 79 80 FontInstanceData() : mSize(0), mNumVariations(0) {} 81 }; 82 83 StaticMutex sFontDataTableLock; 84 MOZ_RUNINIT std::unordered_map<WrFontKey, FontTemplate> sFontDataTable; 85 MOZ_RUNINIT std::unordered_map<WrFontInstanceKey, FontInstanceData> 86 sBlobFontTable; 87 88 // Fixed-size ring buffer logging font deletion events to aid debugging. 89 static struct FontDeleteLog { 90 static const size_t MAX_ENTRIES = 256; 91 92 uint64_t mEntries[MAX_ENTRIES] = {0}; 93 size_t mNextEntry = 0; 94 95 void AddEntry(uint64_t aEntry) { 96 mEntries[mNextEntry] = aEntry; 97 mNextEntry = (mNextEntry + 1) % MAX_ENTRIES; 98 } 99 100 void Add(WrFontKey aKey) { AddEntry(AsUint64(aKey)); } 101 102 // Store namespace clears as font id 0, since this will never be allocated. 103 void Add(WrIdNamespace aNamespace) { 104 AddEntry(AsUint64(WrFontKey{aNamespace, 0})); 105 } 106 107 void AddAll() { AddEntry(~0); } 108 109 // Find a matching entry in the log, searching backwards starting at the 110 // newest entry and finishing with the oldest entry. Returns a brief 111 // description of why the font was deleted, if known. 112 const char* Find(WrFontKey aKey) { 113 uint64_t keyEntry = AsUint64(aKey); 114 uint64_t namespaceEntry = AsUint64(WrFontKey{aKey.mNamespace, 0}); 115 size_t offset = mNextEntry; 116 do { 117 offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES; 118 if (mEntries[offset] == keyEntry) { 119 return "deleted font"; 120 } else if (mEntries[offset] == namespaceEntry) { 121 return "cleared namespace"; 122 } else if (mEntries[offset] == (uint64_t)~0) { 123 return "cleared all"; 124 } 125 } while (offset != mNextEntry); 126 return "unknown font"; 127 } 128 } sFontDeleteLog; 129 130 void ClearAllBlobImageResources() { 131 StaticMutexAutoLock lock(sFontDataTableLock); 132 sFontDeleteLog.AddAll(); 133 sBlobFontTable.clear(); 134 sFontDataTable.clear(); 135 } 136 137 extern "C" { 138 void ClearBlobImageResources(WrIdNamespace aNamespace) { 139 StaticMutexAutoLock lock(sFontDataTableLock); 140 sFontDeleteLog.Add(aNamespace); 141 for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) { 142 if (i->first.mNamespace == aNamespace) { 143 i = sBlobFontTable.erase(i); 144 } else { 145 i++; 146 } 147 } 148 for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) { 149 if (i->first.mNamespace == aNamespace) { 150 i = sFontDataTable.erase(i); 151 } else { 152 i++; 153 } 154 } 155 } 156 157 bool HasFontData(WrFontKey aKey) { 158 StaticMutexAutoLock lock(sFontDataTableLock); 159 return sFontDataTable.find(aKey) != sFontDataTable.end(); 160 } 161 162 void AddFontData(WrFontKey aKey, const uint8_t* aData, size_t aSize, 163 uint32_t aIndex, const ArcVecU8* aVec) { 164 StaticMutexAutoLock lock(sFontDataTableLock); 165 auto i = sFontDataTable.find(aKey); 166 if (i == sFontDataTable.end()) { 167 FontTemplate& font = sFontDataTable[aKey]; 168 font.mData = aData; 169 font.mSize = aSize; 170 font.mIndex = aIndex; 171 font.mVec = wr_add_ref_arc(aVec); 172 } 173 } 174 175 void AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) { 176 StaticMutexAutoLock lock(sFontDataTableLock); 177 auto i = sFontDataTable.find(aKey); 178 if (i == sFontDataTable.end()) { 179 FontTemplate& font = sFontDataTable[aKey]; 180 #ifdef XP_DARWIN 181 font.mUnscaledFont = 182 new UnscaledFontMac(reinterpret_cast<CGFontRef>(aHandle), false); 183 #elif defined(XP_WIN) 184 font.mUnscaledFont = new UnscaledFontDWrite( 185 reinterpret_cast<IDWriteFontFace*>(aHandle), nullptr); 186 #elif defined(ANDROID) 187 font.mUnscaledFont = new UnscaledFontFreeType( 188 reinterpret_cast<const char*>(aHandle), aIndex); 189 #else 190 font.mUnscaledFont = new UnscaledFontFontconfig( 191 reinterpret_cast<const char*>(aHandle), aIndex); 192 #endif 193 } 194 } 195 196 void DeleteFontData(WrFontKey aKey) { 197 StaticMutexAutoLock lock(sFontDataTableLock); 198 sFontDeleteLog.Add(aKey); 199 auto i = sFontDataTable.find(aKey); 200 if (i != sFontDataTable.end()) { 201 sFontDataTable.erase(i); 202 } 203 } 204 205 void AddBlobFont(WrFontInstanceKey aInstanceKey, WrFontKey aFontKey, 206 float aSize, const FontInstanceOptions* aOptions, 207 const FontInstancePlatformOptions* aPlatformOptions, 208 const FontVariation* aVariations, size_t aNumVariations) { 209 StaticMutexAutoLock lock(sFontDataTableLock); 210 auto i = sBlobFontTable.find(aInstanceKey); 211 if (i == sBlobFontTable.end()) { 212 FontInstanceData& font = sBlobFontTable[aInstanceKey]; 213 font.mFontKey = aFontKey; 214 font.mSize = aSize; 215 if (aOptions) { 216 font.mOptions = Some(*aOptions); 217 } 218 if (aPlatformOptions) { 219 font.mPlatformOptions = Some(*aPlatformOptions); 220 } 221 if (aNumVariations) { 222 font.mNumVariations = aNumVariations; 223 font.mVariations.reset(new gfx::FontVariation[aNumVariations]); 224 PodCopy(font.mVariations.get(), 225 reinterpret_cast<const gfx::FontVariation*>(aVariations), 226 aNumVariations); 227 } 228 } 229 } 230 231 void DeleteBlobFont(WrFontInstanceKey aKey) { 232 StaticMutexAutoLock lock(sFontDataTableLock); 233 auto i = sBlobFontTable.find(aKey); 234 if (i != sBlobFontTable.end()) { 235 sBlobFontTable.erase(i); 236 } 237 } 238 239 } // extern 240 241 static RefPtr<UnscaledFont> GetUnscaledFont(Translator* aTranslator, 242 WrFontKey aKey) { 243 auto i = sFontDataTable.find(aKey); 244 if (i == sFontDataTable.end()) { 245 gfxDevCrash(LogReason::UnscaledFontNotFound) 246 << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle 247 << " because " << sFontDeleteLog.Find(aKey); 248 return nullptr; 249 } 250 FontTemplate& data = i->second; 251 if (data.mUnscaledFont) { 252 return data.mUnscaledFont; 253 } 254 MOZ_ASSERT(data.mData); 255 FontType type = 256 #ifdef XP_DARWIN 257 FontType::MAC; 258 #elif defined(XP_WIN) 259 FontType::DWRITE; 260 #elif defined(ANDROID) 261 FontType::FREETYPE; 262 #else 263 FontType::FONTCONFIG; 264 #endif 265 // makes a copy of the data 266 RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource( 267 (uint8_t*)data.mData, data.mSize, type, aTranslator->GetFontContext()); 268 RefPtr<UnscaledFont> unscaledFont; 269 if (!fontResource) { 270 gfxDevCrash(LogReason::NativeFontResourceNotFound) 271 << "Failed to create NativeFontResource for FontKey " << aKey.mHandle; 272 } else { 273 // Instance data is only needed for GDI fonts which webrender does not 274 // support. 275 unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0); 276 if (!unscaledFont) { 277 gfxDevCrash(LogReason::UnscaledFontNotFound) 278 << "Failed to create UnscaledFont for FontKey " << aKey.mHandle; 279 } 280 } 281 data.mUnscaledFont = unscaledFont; 282 return unscaledFont; 283 } 284 285 static RefPtr<ScaledFont> GetScaledFont(Translator* aTranslator, 286 WrFontInstanceKey aKey) { 287 StaticMutexAutoLock lock(sFontDataTableLock); 288 auto i = sBlobFontTable.find(aKey); 289 if (i == sBlobFontTable.end()) { 290 gfxDevCrash(LogReason::ScaledFontNotFound) 291 << "Failed to get ScaledFont entry for FontInstanceKey " 292 << aKey.mHandle; 293 return nullptr; 294 } 295 FontInstanceData& data = i->second; 296 if (data.mScaledFont) { 297 return data.mScaledFont; 298 } 299 RefPtr<UnscaledFont> unscaled = GetUnscaledFont(aTranslator, data.mFontKey); 300 if (!unscaled) { 301 return nullptr; 302 } 303 RefPtr<ScaledFont> scaled = unscaled->CreateScaledFontFromWRFont( 304 data.mSize, data.mOptions.ptrOr(nullptr), 305 data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(), 306 data.mNumVariations); 307 if (!scaled) { 308 gfxDevCrash(LogReason::ScaledFontNotFound) 309 << "Failed to create ScaledFont for FontKey " << aKey.mHandle; 310 } 311 data.mScaledFont = scaled; 312 return data.mScaledFont; 313 } 314 315 template <typename T> 316 T ConvertFromBytes(const uint8_t* bytes) { 317 T t; 318 memcpy(&t, bytes, sizeof(T)); 319 return t; 320 } 321 322 struct Reader { 323 const uint8_t* buf; 324 size_t len; 325 size_t pos; 326 327 Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {} 328 329 template <typename T> 330 T Read() { 331 MOZ_RELEASE_ASSERT(pos + sizeof(T) <= len); 332 T ret = ConvertFromBytes<T>(buf + pos); 333 pos += sizeof(T); 334 return ret; 335 } 336 337 size_t ReadSize() { return Read<size_t>(); } 338 int ReadInt() { return Read<int>(); } 339 340 IntRectAbsolute ReadBounds() { return Read<IntRectAbsolute>(); } 341 342 layers::BlobFont ReadBlobFont() { return Read<layers::BlobFont>(); } 343 }; 344 345 static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob, 346 gfx::SurfaceFormat aFormat, 347 const mozilla::wr::DeviceIntRect* aVisibleRect, 348 const mozilla::wr::LayoutIntRect* aRenderRect, 349 const uint16_t aTileSize, 350 const mozilla::wr::TileOffset* aTileOffset, 351 const mozilla::wr::LayoutIntRect* aDirtyRect, 352 Range<uint8_t> aOutput) { 353 IntSize size(aRenderRect->width(), aRenderRect->height()); 354 AUTO_PROFILER_MARKER("RasterizeSingleBlob", GRAPHICS); 355 #ifndef FUZZING 356 MOZ_RELEASE_ASSERT(size.width > 0 && size.height > 0); 357 #endif 358 if (size.width <= 0 || size.height <= 0) { 359 return false; 360 } 361 362 auto stride = size.width * gfx::BytesPerPixel(aFormat); 363 364 if (aOutput.length() < static_cast<size_t>(size.height * stride)) { 365 return false; 366 } 367 368 // In bindings.rs we allocate a buffer filled with opaque white. 369 bool uninitialized = false; 370 371 RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateDrawTargetForData( 372 gfx::BackendType::SKIA, aOutput.begin().get(), size, stride, aFormat, 373 uninitialized); 374 375 if (!dt) { 376 return false; 377 } 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 dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin); 384 if (!dt) { 385 return false; 386 } 387 388 // We try hard to not have empty blobs but we can end up with 389 // them because of CompositorHitTestInfo and merging. 390 size_t footerSize = sizeof(size_t); 391 MOZ_RELEASE_ASSERT(aBlob.length() >= footerSize); 392 size_t indexOffset = ConvertFromBytes<size_t>(aBlob.end().get() - footerSize); 393 394 MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - footerSize); 395 Reader reader(aBlob.begin().get() + indexOffset, 396 aBlob.length() - footerSize - indexOffset); 397 398 auto bounds = gfx::IntRect(origin, size); 399 400 if (aDirtyRect) { 401 gfx::Rect dirty(aDirtyRect->min.x, aDirtyRect->min.y, aDirtyRect->width(), 402 aDirtyRect->height()); 403 dt->PushClipRect(dirty); 404 bounds = 405 bounds.Intersect(IntRect(aDirtyRect->min.x, aDirtyRect->min.y, 406 aDirtyRect->width(), aDirtyRect->height())); 407 } 408 409 bool ret = true; 410 size_t offset = 0; 411 auto absBounds = IntRectAbsolute::FromRect(bounds); 412 while (reader.pos < reader.len) { 413 size_t end = reader.ReadSize(); 414 size_t extra_end = reader.ReadSize(); 415 MOZ_RELEASE_ASSERT(offset <= end); 416 MOZ_RELEASE_ASSERT(extra_end >= end); 417 MOZ_RELEASE_ASSERT(extra_end < aBlob.length()); 418 419 auto combinedBounds = absBounds.Intersect(reader.ReadBounds()); 420 if (combinedBounds.IsEmpty()) { 421 offset = extra_end; 422 continue; 423 } 424 425 layers::WebRenderTranslator translator(dt); 426 Reader fontReader(aBlob.begin().get() + end, extra_end - end); 427 size_t count = fontReader.ReadSize(); 428 for (size_t i = 0; i < count; i++) { 429 layers::BlobFont blobFont = fontReader.ReadBlobFont(); 430 RefPtr<ScaledFont> scaledFont = 431 GetScaledFont(&translator, blobFont.mFontInstanceKey); 432 translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont); 433 } 434 435 Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end); 436 ret = 437 translator.TranslateRecording((char*)blob.begin().get(), blob.length()); 438 if (!ret) { 439 gfxCriticalNote << "Replay failure: " << translator.GetError(); 440 MOZ_RELEASE_ASSERT(false); 441 } 442 offset = extra_end; 443 } 444 445 if (StaticPrefs::gfx_webrender_debug_blob_paint_flashing()) { 446 dt->SetTransform(gfx::Matrix()); 447 float r = float(rand()) / float(RAND_MAX); 448 float g = float(rand()) / float(RAND_MAX); 449 float b = float(rand()) / float(RAND_MAX); 450 dt->FillRect(gfx::Rect(origin.x, origin.y, size.width, size.height), 451 gfx::ColorPattern(gfx::DeviceColor(r, g, b, 0.5))); 452 } 453 454 if (aDirtyRect) { 455 dt->PopClip(); 456 } 457 458 #if 0 459 static int i = 0; 460 char filename[40]; 461 sprintf(filename, "out%d.png", i++); 462 gfxUtils::WriteAsPNG(dt, filename); 463 #endif 464 465 return ret; 466 } 467 468 } // namespace wr 469 } // namespace mozilla 470 471 #ifdef FUZZING 472 mozilla::StaticMutex sFuzzDumpLock; 473 static void writehex(const void* bytes, size_t len) { 474 const uint8_t* ptr = static_cast<const uint8_t*>(bytes); 475 while (len--) printf("%02X", *ptr++ & 0xFF); 476 } 477 #endif 478 479 extern "C" { 480 481 bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob, 482 mozilla::wr::ImageFormat aFormat, 483 const mozilla::wr::LayoutIntRect* aRenderRect, 484 const mozilla::wr::DeviceIntRect* aVisibleRect, 485 const uint16_t aTileSize, 486 const mozilla::wr::TileOffset* aTileOffset, 487 const mozilla::wr::LayoutIntRect* aDirtyRect, 488 mozilla::wr::MutByteSlice output) { 489 #ifdef FUZZING 490 if (!!PR_GetEnv("MOZ2D_RECORD")) { 491 sFuzzDumpLock.Lock(); 492 printf("<dump>"); 493 writehex(&aFormat, sizeof(uint8_t)); 494 writehex(&aRenderRect->min.x, sizeof(int32_t)); 495 writehex(&aRenderRect->min.y, sizeof(int32_t)); 496 writehex(&aRenderRect->max.x, sizeof(int32_t)); 497 writehex(&aRenderRect->max.y, sizeof(int32_t)); 498 writehex(&aVisibleRect->min.x, sizeof(int32_t)); 499 writehex(&aVisibleRect->min.y, sizeof(int32_t)); 500 writehex(&aVisibleRect->max.x, sizeof(int32_t)); 501 writehex(&aVisibleRect->max.y, sizeof(int32_t)); 502 writehex(&aTileSize, sizeof(uint16_t)); 503 if (aTileSize) { 504 writehex(&aTileOffset->x, sizeof(int32_t)); 505 writehex(&aTileOffset->y, sizeof(int32_t)); 506 } 507 uint8_t byte = aDirtyRect ? 1 : 0; 508 writehex(&byte, 1); 509 if (byte) { 510 writehex(&aDirtyRect->min.x, sizeof(int32_t)); 511 writehex(&aDirtyRect->min.y, sizeof(int32_t)); 512 writehex(&aDirtyRect->max.x, sizeof(int32_t)); 513 writehex(&aDirtyRect->max.y, sizeof(int32_t)); 514 } 515 writehex(&output.len, sizeof(uint32_t)); 516 writehex(blob.buffer, blob.len); 517 printf("</dump>\n"); 518 sFuzzDumpLock.Unlock(); 519 } 520 #endif 521 522 return mozilla::wr::Moz2DRenderCallback( 523 mozilla::wr::ByteSliceToRange(blob), 524 mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aVisibleRect, 525 aRenderRect, aTileSize, aTileOffset, aDirtyRect, 526 mozilla::wr::MutByteSliceToRange(output)); 527 } 528 529 } // extern