Bug 1380014. Share fonts with WebRender. r=lsalzman

This changes the serialization format a little bit.
We now have an index at the end of the blob. This
is currently used to store a list of the used font keys.
In the future we'll add rects and can use it for invalidation.
This commit is contained in:
Jeff Muizelaar 2017-10-27 18:21:27 -04:00
parent f1e1608327
commit ed62b1d4a1
11 changed files with 302 additions and 8 deletions

View File

@ -79,11 +79,42 @@ DrawEventRecorderMemory::DrawEventRecorderMemory()
WriteHeader(mOutputStream);
}
DrawEventRecorderMemory::DrawEventRecorderMemory(const SerializeResourcesFn &aFn) :
mSerializeCallback(aFn)
{
mExternalFonts = true;
WriteHeader(mOutputStream);
}
void
DrawEventRecorderMemory::Flush()
{
}
void
DrawEventRecorderMemory::FlushItem(IntRect aRect)
{
DetatchResources();
WriteElement(mIndex, mOutputStream.mLength);
mSerializeCallback(mOutputStream, mUnscaledFonts);
WriteElement(mIndex, mOutputStream.mLength);
ClearResources();
}
void
DrawEventRecorderMemory::Finish()
{
size_t indexOffset = mOutputStream.mLength;
// write out the index
mOutputStream.write(mIndex.mData, mIndex.mLength);
mIndex = MemStream();
// write out the offset of the Index to the end of the output stream
WriteElement(mOutputStream, indexOffset);
ClearResources();
}
size_t
DrawEventRecorderMemory::RecordingSize()
{
@ -94,6 +125,7 @@ void
DrawEventRecorderMemory::WipeRecording()
{
mOutputStream = MemStream();
mIndex = MemStream();
WriteHeader(mOutputStream);
}

View File

@ -14,6 +14,7 @@
#include <unordered_set>
#include <unordered_map>
#include <functional>
namespace mozilla {
namespace gfx {
@ -26,7 +27,9 @@ public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderPrivate)
DrawEventRecorderPrivate();
virtual ~DrawEventRecorderPrivate() { }
virtual void Finish() {
virtual void Finish() { ClearResources(); }
virtual void FlushItem(IntRect) { }
void DetatchResources() {
// The iteration is a bit awkward here because our iterator will
// be invalidated by the removal
for (auto font = mStoredFonts.begin(); font != mStoredFonts.end(); ) {
@ -37,7 +40,15 @@ public:
auto oldSurface = surface++;
(*oldSurface)->RemoveUserData(reinterpret_cast<UserDataKey*>(this));
}
mStoredFonts.clear();
mStoredSurfaces.clear();
}
void ClearResources() {
mUnscaledFonts.clear();
mStoredObjects.clear();
mStoredFontData.clear();
mUnscaledFontMap.clear();
}
template<class S>
@ -148,6 +159,8 @@ private:
std::ofstream mOutputStream;
};
typedef std::function<void(MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts)> SerializeResourcesFn;
// WARNING: This should not be used in its existing state because
// it is likely to OOM because of large continguous allocations.
class DrawEventRecorderMemory final : public DrawEventRecorderPrivate
@ -159,6 +172,7 @@ public:
* Constructs a DrawEventRecorder that stores the recording in memory.
*/
DrawEventRecorderMemory();
explicit DrawEventRecorderMemory(const SerializeResourcesFn &aSerialize);
void RecordEvent(const RecordedEvent &aEvent) override;
@ -173,9 +187,19 @@ public:
* and processed in chunks, releasing memory as it goes.
*/
void WipeRecording();
void Finish() override;
void FlushItem(IntRect) override;
MemStream mOutputStream;
/* The index stream is of the form:
* ItemIndex { size_t dataEnd; size_t extraDataEnd; }
* It gets concatenated to the end of mOutputStream in Finish()
* The last size_t in the stream is offset of the begining of the
* index.
*/
MemStream mIndex;
private:
SerializeResourcesFn mSerializeCallback;
~DrawEventRecorderMemory() {};
void Flush() override;

View File

@ -52,6 +52,7 @@ InlineTranslator::TranslateRecording(char *aData, size_t aLen)
};
MemReader reader(aData, aLen);
uint32_t magicInt;
ReadElement(reader, magicInt);
if (magicInt != mozilla::gfx::kMagicInt) {

View File

@ -128,6 +128,7 @@ public:
void AddUnscaledFont(ReferencePtr aRefPtr, UnscaledFont *aUnscaledFont) final
{
mUnscaledFontTable.push_back(aUnscaledFont);
mUnscaledFonts.Put(aRefPtr, aUnscaledFont);
}

View File

@ -117,6 +117,40 @@ RecordedEvent::GetEventName(EventType aType)
}
}
template<class S>
void RecordedEvent::RecordUnscaledFontImpl(UnscaledFont *aUnscaledFont, S& aOutput) {
RecordedFontData fontData(aUnscaledFont);
RecordedFontDetails fontDetails;
if (fontData.GetFontDetails(fontDetails)) {
// Try to serialise the whole font, just in case this is a web font that
// is not present on the system.
WriteElement(aOutput, fontData.mType);
fontData.RecordToStream(aOutput);
auto r = RecordedUnscaledFontCreation(aUnscaledFont, fontDetails);
WriteElement(aOutput, r.mType);
r.RecordToStream(aOutput);
} else {
// If that fails, record just the font description and try to load it from
// the system on the other side.
RecordedFontDescriptor fontDesc(aUnscaledFont);
if (fontDesc.IsValid()) {
WriteElement(aOutput, fontDesc.RecordedEvent::mType);
fontDesc.RecordToStream(aOutput);
} else {
gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise UnscaledFont";
}
}
}
void RecordedEvent::RecordUnscaledFont(UnscaledFont *aUnscaledFont, std::ostream *aOutput) {
RecordUnscaledFontImpl(aUnscaledFont, *aOutput);
}
void RecordedEvent::RecordUnscaledFont(UnscaledFont *aUnscaledFont, MemStream &aOutput) {
RecordUnscaledFontImpl(aUnscaledFont, aOutput);
}
already_AddRefed<DrawTarget>
Translator::CreateDrawTarget(ReferencePtr aRefPtr, const IntSize &aSize,
SurfaceFormat aFormat)

View File

@ -313,6 +313,10 @@ protected:
friend class DrawEventRecorderPrivate;
friend class DrawEventRecorderFile;
friend class DrawEventRecorderMemory;
static void RecordUnscaledFont(UnscaledFont *aUnscaledFont, std::ostream *aOutput);
static void RecordUnscaledFont(UnscaledFont *aUnscaledFont, MemStream &aOutput);
template<class S>
static void RecordUnscaledFontImpl(UnscaledFont *aUnscaledFont, S &aOutput);
MOZ_IMPLICIT RecordedEvent(int32_t aType) : mType(aType)
{}

View File

@ -22,6 +22,7 @@ namespace gfx {
template<class Derived>
class RecordedEventDerived : public RecordedEvent {
using RecordedEvent::RecordedEvent;
public:
void RecordToStream(std::ostream &aStream) const {
static_cast<const Derived*>(this)->Record(aStream);
}

View File

@ -25,6 +25,8 @@
namespace mozilla {
namespace layers {
using namespace gfx;
void WebRenderCommandBuilder::Destroy()
{
mLastCanvasDatas.Clear();
@ -500,12 +502,20 @@ WebRenderCommandBuilder::GenerateFallbackData(nsDisplayItem* aItem,
bool snapped;
bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(clippedBounds);
RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>();
RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
size_t count = aUnscaledFonts.size();
aStream.write((const char*)&count, sizeof(count));
for (auto unscaled : aUnscaledFonts) {
wr::FontKey key = mManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
aStream.write((const char*)&key, sizeof(key));
}
});
RefPtr<gfx::DrawTarget> dummyDt =
gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
RefPtr<gfx::DrawTarget> dt = gfx::Factory::CreateRecordingDrawTarget(recorder, dummyDt, paintSize.ToUnknownSize());
PaintItemByDrawTarget(aItem, dt, paintRect, offset, aDisplayListBuilder,
fallbackData->mBasicLayerManager, mManager, scale);
recorder->FlushItem(IntRect());
recorder->Finish();
Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData, recorder->mOutputStream.mLength);

View File

@ -13,18 +13,97 @@
#include "webrender_ffi.h"
#include <iostream>
#include <unordered_map>
#ifdef MOZ_ENABLE_FREETYPE
#include "mozilla/ThreadLocal.h"
#endif
namespace std {
template <>
struct hash<mozilla::wr::FontKey>{
public :
size_t operator()(const mozilla::wr::FontKey &key ) const
{
return hash<size_t>()(mozilla::wr::AsUint64(key));
}
};
};
namespace mozilla {
using namespace gfx;
namespace wr {
#ifdef MOZ_ENABLE_FREETYPE
static MOZ_THREAD_LOCAL(FT_Library) sFTLibrary;
#endif
struct FontTemplate {
void *mData;
size_t mSize;
int mIndex;
const VecU8 *mVec;
};
// we need to do special things for linux so that we have fonts per backend
std::unordered_map<FontKey, FontTemplate> sFontDataTable;
extern "C" {
void
AddFontData(wr::FontKey aKey, void *aData, size_t aSize, int aIndex, ArcVecU8 *aVec) {
auto i = sFontDataTable.find(aKey);
if (i == sFontDataTable.end()) {
FontTemplate font;
font.mData = aData;
font.mSize = aSize;
font.mIndex = aIndex;
font.mVec = wr_add_ref_arc(aVec);
sFontDataTable[aKey] = font;
}
}
void
DeleteFontData(wr::FontKey aKey) {
auto i = sFontDataTable.find(aKey);
if (i != sFontDataTable.end()) {
sFontDataTable.erase(i);
wr_dec_ref_arc(i->second.mVec);
}
}
}
RefPtr<UnscaledFont>
GetUnscaledFont(Translator *aTranslator, wr::FontKey key) {
MOZ_ASSERT(sFontDataTable.find(key) != sFontDataTable.end());
auto data = sFontDataTable[key];
FontType type =
#ifdef XP_MACOSX
FontType::MAC;
#elif XP_WIN
FontType::DWRITE;
#elif ANDROID
FontType::FREETYPE;
#else
FontType::FONTCONFIG;
#endif
// makes a copy of the data
RefPtr<NativeFontResource> fontResource = Factory::CreateNativeFontResource((uint8_t*)data.mData, data.mSize,
aTranslator->GetReferenceDrawTarget()->GetBackendType(),
type,
aTranslator->GetFontContext());
RefPtr<UnscaledFont> unscaledFont;
if (fontResource) {
// Instance data is only needed for GDI fonts which webrender does not
// support.
unscaledFont = fontResource->CreateUnscaledFont(data.mIndex, nullptr, 0);
}
return unscaledFont;
}
static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
@ -86,9 +165,43 @@ static bool Moz2DRenderCallback(const Range<const uint8_t> aBlob,
dt = gfx::Factory::CreateTiledDrawTarget(tileset);
}
gfx::InlineTranslator translator(dt, fontContext);
struct Reader {
const uint8_t *buf;
size_t len;
size_t pos;
auto ret = translator.TranslateRecording((char*)aBlob.begin().get(), aBlob.length());
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;
}
};
//XXX: Make safe
size_t indexOffset = *(size_t*)(aBlob.end().get()-sizeof(size_t));
Reader reader(aBlob.begin().get()+indexOffset, aBlob.length()-sizeof(size_t)-indexOffset);
bool ret;
size_t offset = 0;
while (reader.pos < reader.len) {
size_t end = reader.ReadSize();
size_t extra_end = reader.ReadSize();
gfx::InlineTranslator translator(dt, fontContext);
size_t count = *(size_t*)(aBlob.begin().get() + end);
for (size_t i = 0; i < count; i++) {
wr::FontKey key = *(wr::FontKey*)(aBlob.begin() + end + sizeof(count) + sizeof(wr::FontKey)*i).get();
RefPtr<UnscaledFont> font = GetUnscaledFont(&translator, key);
translator.AddUnscaledFont(0, font);
}
Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length());
offset = extra_end;
}
#if 0
static int i = 0;

View File

@ -38,7 +38,7 @@ type WrPipelineId = PipelineId;
/// cbindgen:field-names=[mNamespace, mHandle]
type WrImageKey = ImageKey;
/// cbindgen:field-names=[mNamespace, mHandle]
type WrFontKey = FontKey;
pub type WrFontKey = FontKey;
/// cbindgen:field-names=[mNamespace, mHandle]
type WrFontInstanceKey = FontInstanceKey;
/// cbindgen:field-names=[mNamespace, mHandle]

View File

@ -1,8 +1,10 @@
#![allow(improper_ctypes)] // this is needed so that rustc doesn't complain about passing the &Arc<Vec> to an extern function
use webrender_api::*;
use bindings::{ByteSlice, MutByteSlice, wr_moz2d_render_cb};
use rayon::ThreadPool;
use std::collections::hash_map::{HashMap, Entry};
use std::mem;
use std::ptr;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::sync::Arc;
@ -26,6 +28,48 @@ fn option_to_nullable<T>(option: &Option<T>) -> *const T {
}
}
fn to_usize(slice: &[u8]) -> usize {
convert_from_bytes(slice)
}
fn convert_from_bytes<T>(slice: &[u8]) -> T {
assert!(mem::size_of::<T>() <= slice.len());
let mut ret: T;
unsafe {
ret = mem::uninitialized();
ptr::copy_nonoverlapping(slice.as_ptr(),
&mut ret as *mut T as *mut u8,
mem::size_of::<T>());
}
ret
}
struct BufReader<'a>
{
buf: &'a[u8],
pos: usize,
}
impl<'a> BufReader<'a> {
fn new(buf: &'a[u8]) -> BufReader<'a> {
BufReader{ buf: buf, pos: 0 }
}
fn read<T>(&mut self) -> T {
let ret = convert_from_bytes(&self.buf[self.pos..]);
self.pos += mem::size_of::<T>();
ret
}
fn read_font_key(&mut self) -> FontKey {
self.read()
}
fn read_usize(&mut self) -> usize {
self.read()
}
}
impl BlobImageRenderer for Moz2dImageRenderer {
fn add(&mut self, key: ImageKey, data: BlobImageData, tiling: Option<TileSize>) {
self.blob_commands.insert(key, (Arc::new(data), tiling));
@ -41,7 +85,7 @@ impl BlobImageRenderer for Moz2dImageRenderer {
}
fn request(&mut self,
_resources: &BlobImageResources,
resources: &BlobImageResources,
request: BlobImageRequest,
descriptor: &BlobImageDescriptor,
_dirty_rect: Option<DeviceUintRect>) {
@ -61,6 +105,29 @@ impl BlobImageRenderer for Moz2dImageRenderer {
let commands = Arc::clone(&blob.0);
fn process_fonts(mut extra_data: BufReader, resources: &BlobImageResources) {
let font_count = extra_data.read_usize();
for _ in 0..font_count {
let key = extra_data.read_font_key();
let template = resources.get_font_data(key);
if let &FontTemplate::Raw(ref data, ref index) = template {
unsafe { AddFontData(key, data.as_ptr(), data.len(), *index, data); }
}
resources.get_font_data(key);
}
}
let index_offset_pos = commands.len()-mem::size_of::<usize>();
let index_offset = to_usize(&commands[index_offset_pos..]);
{
let mut index = BufReader::new(&commands[index_offset..index_offset_pos]);
while index.pos < index.buf.len() {
let end = index.read_usize();
let extra_end = index.read_usize();
process_fonts(BufReader::new(&commands[end..extra_end]), resources);
}
}
self.workers.spawn(move || {
let buf_size = (descriptor.width
* descriptor.height
@ -120,14 +187,21 @@ impl BlobImageRenderer for Moz2dImageRenderer {
// If we break out of the loop above it means the channel closed unexpectedly.
Err(BlobImageError::Other("Channel closed".into()))
}
fn delete_font(&mut self, _font: FontKey) {
fn delete_font(&mut self, font: FontKey) {
unsafe { DeleteFontData(font); }
}
fn delete_font_instance(&mut self, _key: FontInstanceKey) {
}
}
use bindings::WrFontKey;
extern "C" {
#[allow(improper_ctypes)]
fn AddFontData(key: WrFontKey, data: *const u8, size: usize, index: u32, vec: &Arc<Vec<u8>>);
fn DeleteFontData(key: WrFontKey);
}
impl Moz2dImageRenderer {
pub fn new(workers: Arc<ThreadPool>) -> Self {
let (tx, rx) = channel();