/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gfxPrefs.h" #include "gfxUtils.h" #include "mozilla/Mutex.h" #include "mozilla/Range.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/RectAbsolute.h" #include "mozilla/gfx/Logging.h" #include "mozilla/gfx/RecordedEvent.h" #include "mozilla/layers/WebRenderDrawEventRecorder.h" #include "WebRenderTypes.h" #include "webrender_ffi.h" #include "GeckoProfiler.h" #include #ifdef XP_MACOSX #include "mozilla/gfx/UnscaledFontMac.h" #elif defined(XP_WIN) #include "mozilla/gfx/UnscaledFontDWrite.h" #else #include "mozilla/gfx/UnscaledFontFreeType.h" #endif namespace std { template <> struct hash { size_t operator()(const mozilla::wr::FontKey& key) const { return hash()(mozilla::wr::AsUint64(key)); } }; template <> struct hash { size_t operator()(const mozilla::wr::FontInstanceKey& key) const { return hash()(mozilla::wr::AsUint64(key)); } }; }; // namespace std namespace mozilla { using namespace gfx; namespace wr { struct FontTemplate { const uint8_t* mData; size_t mSize; uint32_t mIndex; const VecU8* mVec; RefPtr mUnscaledFont; FontTemplate() : mData(nullptr), mSize(0), mIndex(0), mVec(nullptr) {} ~FontTemplate() { if (mVec) { wr_dec_ref_arc(mVec); } } }; struct FontInstanceData { WrFontKey mFontKey; float mSize; Maybe mOptions; Maybe mPlatformOptions; UniquePtr mVariations; size_t mNumVariations; RefPtr mScaledFont; FontInstanceData() : mSize(0), mNumVariations(0) {} }; StaticMutex sFontDataTableLock; std::unordered_map sFontDataTable; std::unordered_map sBlobFontTable; // Fixed-size ring buffer logging font deletion events to aid debugging. static struct FontDeleteLog { static const size_t MAX_ENTRIES = 256; uint64_t mEntries[MAX_ENTRIES] = {0}; size_t mNextEntry = 0; void AddEntry(uint64_t aEntry) { mEntries[mNextEntry] = aEntry; mNextEntry = (mNextEntry + 1) % MAX_ENTRIES; } void Add(WrFontKey aKey) { AddEntry(AsUint64(aKey)); } // Store namespace clears as font id 0, since this will never be allocated. void Add(WrIdNamespace aNamespace) { AddEntry(AsUint64(WrFontKey{aNamespace, 0})); } void AddAll() { AddEntry(~0); } // Find a matching entry in the log, searching backwards starting at the // newest entry and finishing with the oldest entry. Returns a brief // description of why the font was deleted, if known. const char* Find(WrFontKey aKey) { uint64_t keyEntry = AsUint64(aKey); uint64_t namespaceEntry = AsUint64(WrFontKey{aKey.mNamespace, 0}); size_t offset = mNextEntry; do { offset = (offset + MAX_ENTRIES - 1) % MAX_ENTRIES; if (mEntries[offset] == keyEntry) { return "deleted font"; } else if (mEntries[offset] == namespaceEntry) { return "cleared namespace"; } else if (mEntries[offset] == (uint64_t)~0) { return "cleared all"; } } while (offset != mNextEntry); return "unknown font"; } } sFontDeleteLog; void ClearAllBlobImageResources() { StaticMutexAutoLock lock(sFontDataTableLock); sFontDeleteLog.AddAll(); sBlobFontTable.clear(); sFontDataTable.clear(); } extern "C" { void ClearBlobImageResources(WrIdNamespace aNamespace) { StaticMutexAutoLock lock(sFontDataTableLock); sFontDeleteLog.Add(aNamespace); for (auto i = sBlobFontTable.begin(); i != sBlobFontTable.end();) { if (i->first.mNamespace == aNamespace) { i = sBlobFontTable.erase(i); } else { i++; } } for (auto i = sFontDataTable.begin(); i != sFontDataTable.end();) { if (i->first.mNamespace == aNamespace) { i = sFontDataTable.erase(i); } else { i++; } } } void AddFontData(WrFontKey aKey, const uint8_t* aData, size_t aSize, uint32_t aIndex, const ArcVecU8* aVec) { StaticMutexAutoLock lock(sFontDataTableLock); auto i = sFontDataTable.find(aKey); if (i == sFontDataTable.end()) { FontTemplate& font = sFontDataTable[aKey]; font.mData = aData; font.mSize = aSize; font.mIndex = aIndex; font.mVec = wr_add_ref_arc(aVec); } } void AddNativeFontHandle(WrFontKey aKey, void* aHandle, uint32_t aIndex) { StaticMutexAutoLock lock(sFontDataTableLock); auto i = sFontDataTable.find(aKey); if (i == sFontDataTable.end()) { FontTemplate& font = sFontDataTable[aKey]; #ifdef XP_MACOSX font.mUnscaledFont = new UnscaledFontMac(reinterpret_cast(aHandle), false); #elif defined(XP_WIN) font.mUnscaledFont = new UnscaledFontDWrite( reinterpret_cast(aHandle), nullptr); #elif defined(ANDROID) font.mUnscaledFont = new UnscaledFontFreeType( reinterpret_cast(aHandle), aIndex); #else font.mUnscaledFont = new UnscaledFontFontconfig( reinterpret_cast(aHandle), aIndex); #endif } } void DeleteFontData(WrFontKey aKey) { StaticMutexAutoLock lock(sFontDataTableLock); sFontDeleteLog.Add(aKey); auto i = sFontDataTable.find(aKey); if (i != sFontDataTable.end()) { sFontDataTable.erase(i); } } void AddBlobFont(WrFontInstanceKey aInstanceKey, WrFontKey aFontKey, float aSize, const FontInstanceOptions* aOptions, const FontInstancePlatformOptions* aPlatformOptions, const FontVariation* aVariations, size_t aNumVariations) { StaticMutexAutoLock lock(sFontDataTableLock); auto i = sBlobFontTable.find(aInstanceKey); if (i == sBlobFontTable.end()) { FontInstanceData& font = sBlobFontTable[aInstanceKey]; font.mFontKey = aFontKey; font.mSize = aSize; if (aOptions) { font.mOptions = Some(*aOptions); } if (aPlatformOptions) { font.mPlatformOptions = Some(*aPlatformOptions); } if (aNumVariations) { font.mNumVariations = aNumVariations; font.mVariations.reset(new gfx::FontVariation[aNumVariations]); PodCopy(font.mVariations.get(), reinterpret_cast(aVariations), aNumVariations); } } } void DeleteBlobFont(WrFontInstanceKey aKey) { StaticMutexAutoLock lock(sFontDataTableLock); auto i = sBlobFontTable.find(aKey); if (i != sBlobFontTable.end()) { sBlobFontTable.erase(i); } } } // extern static RefPtr GetUnscaledFont(Translator* aTranslator, WrFontKey aKey) { auto i = sFontDataTable.find(aKey); if (i == sFontDataTable.end()) { gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to get UnscaledFont entry for FontKey " << aKey.mHandle << " because " << sFontDeleteLog.Find(aKey); return nullptr; } FontTemplate& data = i->second; if (data.mUnscaledFont) { return data.mUnscaledFont; } MOZ_ASSERT(data.mData); FontType type = #ifdef XP_MACOSX FontType::MAC; #elif defined(XP_WIN) FontType::DWRITE; #elif defined(ANDROID) FontType::FREETYPE; #else FontType::FONTCONFIG; #endif // makes a copy of the data RefPtr fontResource = Factory::CreateNativeFontResource( (uint8_t*)data.mData, data.mSize, aTranslator->GetReferenceDrawTarget()->GetBackendType(), type, aTranslator->GetFontContext()); RefPtr unscaledFont; if (!fontResource) { gfxDevCrash(LogReason::NativeFontResourceNotFound) << "Failed to create NativeFontResource for FontKey " << aKey.mHandle; } else { // Instance data is only needed for GDI fonts which webrender does not // support. unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0); if (!unscaledFont) { gfxDevCrash(LogReason::UnscaledFontNotFound) << "Failed to create UnscaledFont for FontKey " << aKey.mHandle; } } data.mUnscaledFont = unscaledFont; return unscaledFont; } static RefPtr GetScaledFont(Translator* aTranslator, WrFontInstanceKey aKey) { StaticMutexAutoLock lock(sFontDataTableLock); auto i = sBlobFontTable.find(aKey); if (i == sBlobFontTable.end()) { gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to get ScaledFont entry for FontInstanceKey " << aKey.mHandle; return nullptr; } FontInstanceData& data = i->second; if (data.mScaledFont) { return data.mScaledFont; } RefPtr unscaled = GetUnscaledFont(aTranslator, data.mFontKey); if (!unscaled) { return nullptr; } RefPtr scaled = unscaled->CreateScaledFontFromWRFont( data.mSize, data.mOptions.ptrOr(nullptr), data.mPlatformOptions.ptrOr(nullptr), data.mVariations.get(), data.mNumVariations); if (!scaled) { gfxDevCrash(LogReason::ScaledFontNotFound) << "Failed to create ScaledFont for FontKey " << aKey.mHandle; } data.mScaledFont = scaled; return data.mScaledFont; } static bool Moz2DRenderCallback(const Range aBlob, gfx::IntSize aSize, gfx::SurfaceFormat aFormat, const uint16_t* aTileSize, const mozilla::wr::TileOffset* aTileOffset, const mozilla::wr::LayoutIntRect* aDirtyRect, Range aOutput) { AUTO_PROFILER_TRACING("WebRender", "RasterizeSingleBlob"); MOZ_ASSERT(aSize.width > 0 && aSize.height > 0); if (aSize.width <= 0 || aSize.height <= 0) { return false; } auto stride = aSize.width * gfx::BytesPerPixel(aFormat); if (aOutput.length() < static_cast(aSize.height * stride)) { return false; } // In bindings.rs we allocate a buffer filled with opaque white. bool uninitialized = false; RefPtr dt = gfx::Factory::CreateDrawTargetForData( gfx::BackendType::SKIA, aOutput.begin().get(), aSize, stride, aFormat, uninitialized); if (!dt) { return false; } auto origin = gfx::IntPoint(0, 0); if (aTileOffset) { origin = gfx::IntPoint(aTileOffset->x * *aTileSize, aTileOffset->y * *aTileSize); dt = gfx::Factory::CreateOffsetDrawTarget(dt, origin); } auto bounds = gfx::IntRect(origin, aSize); if (aDirtyRect) { Rect dirty(aDirtyRect->origin.x, aDirtyRect->origin.y, aDirtyRect->size.width, aDirtyRect->size.height); dt->PushClipRect(dirty); bounds = bounds.Intersect( IntRect(aDirtyRect->origin.x, aDirtyRect->origin.y, aDirtyRect->size.width, aDirtyRect->size.height)); } struct Reader { const uint8_t* buf; size_t len; size_t pos; Reader(const uint8_t* buf, size_t len) : buf(buf), len(len), pos(0) {} size_t ReadSize() { size_t ret; MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len); memcpy(&ret, buf + pos, sizeof(ret)); pos += sizeof(ret); return ret; } int ReadInt() { int ret; MOZ_RELEASE_ASSERT(pos + sizeof(ret) <= len); memcpy(&ret, buf + pos, sizeof(ret)); pos += sizeof(ret); return ret; } IntRectAbsolute ReadBounds() { MOZ_RELEASE_ASSERT(pos + sizeof(int32_t) * 4 <= len); int32_t x1, y1, x2, y2; memcpy(&x1, buf + pos + 0 * sizeof(int32_t), sizeof(x1)); memcpy(&y1, buf + pos + 1 * sizeof(int32_t), sizeof(y1)); memcpy(&x2, buf + pos + 2 * sizeof(int32_t), sizeof(x2)); memcpy(&y2, buf + pos + 3 * sizeof(int32_t), sizeof(y2)); pos += sizeof(int32_t) * 4; return IntRectAbsolute(x1, y1, x2, y2); } layers::BlobFont ReadBlobFont() { MOZ_RELEASE_ASSERT(pos + sizeof(layers::BlobFont) <= len); layers::BlobFont ret; memcpy(&ret, buf + pos, sizeof(ret)); pos += sizeof(ret); return ret; } }; // We try hard to not have empty blobs but we can end up with // them because of CompositorHitTestInfo and merging. MOZ_RELEASE_ASSERT(aBlob.length() >= sizeof(size_t)); size_t indexOffset = *(size_t*)(aBlob.end().get() - sizeof(size_t)); MOZ_RELEASE_ASSERT(indexOffset <= aBlob.length() - sizeof(size_t)); Reader reader(aBlob.begin().get() + indexOffset, aBlob.length() - sizeof(size_t) - indexOffset); bool ret = true; size_t offset = 0; auto absBounds = IntRectAbsolute::FromRect(bounds); while (reader.pos < reader.len) { size_t end = reader.ReadSize(); size_t extra_end = reader.ReadSize(); MOZ_RELEASE_ASSERT(extra_end >= end); MOZ_RELEASE_ASSERT(extra_end < aBlob.length()); auto combinedBounds = absBounds.Intersect(reader.ReadBounds()); if (combinedBounds.IsEmpty()) { offset = extra_end; continue; } layers::WebRenderTranslator translator(dt); Reader fontReader(aBlob.begin().get() + end, extra_end - end); size_t count = fontReader.ReadSize(); for (size_t i = 0; i < count; i++) { layers::BlobFont blobFont = fontReader.ReadBlobFont(); RefPtr scaledFont = GetScaledFont(&translator, blobFont.mFontInstanceKey); translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont); } Range blob(aBlob.begin() + offset, aBlob.begin() + end); ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length()); if (!ret) { gfxCriticalNote << "Replay failure: " << translator.GetError(); MOZ_RELEASE_ASSERT(false); } offset = extra_end; } if (gfxPrefs::WebRenderBlobPaintFlashing()) { dt->SetTransform(gfx::Matrix()); float r = float(rand()) / RAND_MAX; float g = float(rand()) / RAND_MAX; float b = float(rand()) / RAND_MAX; dt->FillRect(gfx::Rect(origin.x, origin.y, aSize.width, aSize.height), gfx::ColorPattern(gfx::Color(r, g, b, 0.5))); } if (aDirtyRect) { dt->PopClip(); } #if 0 static int i = 0; char filename[40]; sprintf(filename, "out%d.png", i++); gfxUtils::WriteAsPNG(dt, filename); #endif return ret; } } // namespace wr } // namespace mozilla extern "C" { bool wr_moz2d_render_cb(const mozilla::wr::ByteSlice blob, int32_t width, int32_t height, mozilla::wr::ImageFormat aFormat, const uint16_t* aTileSize, const mozilla::wr::TileOffset* aTileOffset, const mozilla::wr::LayoutIntRect* aDirtyRect, mozilla::wr::MutByteSlice output) { return mozilla::wr::Moz2DRenderCallback( mozilla::wr::ByteSliceToRange(blob), mozilla::gfx::IntSize(width, height), mozilla::wr::ImageFormatToSurfaceFormat(aFormat), aTileSize, aTileOffset, aDirtyRect, mozilla::wr::MutByteSliceToRange(output)); } } // extern