forked from mirrors/gecko-dev
Backed out changeset 2b56c2b2837a (bug 1863914) for causing bustage on CanvasDrawEventRecorder.h. CLOSED TREE
This commit is contained in:
parent
17befdb121
commit
00e5d5a924
19 changed files with 1125 additions and 1012 deletions
|
|
@ -19,6 +19,26 @@ InlineTranslator::InlineTranslator(DrawTarget* aDT, void* aFontContext)
|
||||||
: mBaseDT(aDT), mFontContext(aFontContext) {}
|
: mBaseDT(aDT), mFontContext(aFontContext) {}
|
||||||
|
|
||||||
bool InlineTranslator::TranslateRecording(char* aData, size_t aLen) {
|
bool InlineTranslator::TranslateRecording(char* aData, size_t aLen) {
|
||||||
|
// an istream like class for reading from memory
|
||||||
|
struct MemReader {
|
||||||
|
MemReader(char* aData, size_t aLen) : mData(aData), mEnd(aData + aLen) {}
|
||||||
|
void read(char* s, std::streamsize n) {
|
||||||
|
if (n <= (mEnd - mData)) {
|
||||||
|
memcpy(s, mData, n);
|
||||||
|
mData += n;
|
||||||
|
} else {
|
||||||
|
// We've requested more data than is available
|
||||||
|
// set the Reader into an eof state
|
||||||
|
SetIsBad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool eof() { return mData > mEnd; }
|
||||||
|
bool good() { return !eof(); }
|
||||||
|
void SetIsBad() { mData = mEnd + 1; }
|
||||||
|
|
||||||
|
char* mData;
|
||||||
|
char* mEnd;
|
||||||
|
};
|
||||||
MemReader reader(aData, aLen);
|
MemReader reader(aData, aLen);
|
||||||
|
|
||||||
uint32_t magicInt;
|
uint32_t magicInt;
|
||||||
|
|
|
||||||
|
|
@ -166,28 +166,6 @@ class InlineTranslator : public Translator {
|
||||||
std::string GetError() { return mError; }
|
std::string GetError() { return mError; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// an istream like class for reading from memory
|
|
||||||
struct MemReader {
|
|
||||||
constexpr MemReader(char* aData, size_t aLen)
|
|
||||||
: mData(aData), mEnd(aData + aLen) {}
|
|
||||||
void read(char* s, std::streamsize n) {
|
|
||||||
if (n <= (mEnd - mData)) {
|
|
||||||
memcpy(s, mData, n);
|
|
||||||
mData += n;
|
|
||||||
} else {
|
|
||||||
// We've requested more data than is available
|
|
||||||
// set the Reader into an eof state
|
|
||||||
SetIsBad();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool eof() { return mData > mEnd; }
|
|
||||||
bool good() { return !eof(); }
|
|
||||||
void SetIsBad() { mData = mEnd + 1; }
|
|
||||||
|
|
||||||
char* mData;
|
|
||||||
char* mEnd;
|
|
||||||
};
|
|
||||||
|
|
||||||
RefPtr<DrawTarget> mBaseDT;
|
RefPtr<DrawTarget> mBaseDT;
|
||||||
Matrix mBaseDTTransform;
|
Matrix mBaseDTTransform;
|
||||||
nsRefPtrHashtable<nsPtrHashKey<void>, DrawTarget> mDrawTargets;
|
nsRefPtrHashtable<nsPtrHashKey<void>, DrawTarget> mDrawTargets;
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,13 @@ bool RecordedEvent::DoWithEventFromStream(
|
||||||
return DoWithEvent(aStream, aType, aAction);
|
return DoWithEvent(aStream, aType, aAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
bool RecordedEvent::DoWithEventFromStream(
|
||||||
|
EventRingBuffer& aStream, EventType aType,
|
||||||
|
const std::function<bool(RecordedEvent*)>& aAction) {
|
||||||
|
return DoWithEvent(aStream, aType, aAction);
|
||||||
|
}
|
||||||
|
|
||||||
std::string RecordedEvent::GetEventName(EventType aType) {
|
std::string RecordedEvent::GetEventName(EventType aType) {
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
case DRAWTARGETCREATION:
|
case DRAWTARGETCREATION:
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ struct SizeCollector {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MemWriter {
|
struct MemWriter {
|
||||||
constexpr explicit MemWriter(char* aPtr) : mPtr(aPtr) {}
|
explicit MemWriter(char* aPtr) : mPtr(aPtr) {}
|
||||||
void write(const char* aData, size_t aSize) {
|
void write(const char* aData, size_t aSize) {
|
||||||
memcpy(mPtr, aData, aSize);
|
memcpy(mPtr, aData, aSize);
|
||||||
mPtr += aSize;
|
mPtr += aSize;
|
||||||
|
|
@ -222,30 +222,13 @@ struct MemWriter {
|
||||||
char* mPtr;
|
char* mPtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ContiguousBuffer {
|
// This is a simple interface for an EventRingBuffer, so we can use it in the
|
||||||
public:
|
// RecordedEvent reading and writing machinery.
|
||||||
ContiguousBuffer(char* aStart, size_t aSize)
|
class EventRingBuffer {
|
||||||
: mWriter(aStart), mEnd(aStart + aSize) {}
|
|
||||||
|
|
||||||
constexpr MOZ_IMPLICIT ContiguousBuffer(std::nullptr_t) : mWriter(nullptr) {}
|
|
||||||
|
|
||||||
MemWriter& Writer() { return mWriter; }
|
|
||||||
|
|
||||||
size_t SizeRemaining() { return mWriter.mPtr ? mEnd - mWriter.mPtr : 0; }
|
|
||||||
|
|
||||||
bool IsValid() { return !!mWriter.mPtr; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
MemWriter mWriter;
|
|
||||||
char* mEnd = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allows a derived class to provide guaranteed contiguous buffer.
|
|
||||||
class ContiguousBufferStream {
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Templated RecordEvent function so that we can record into the buffer
|
* Templated RecordEvent function so that when we have enough contiguous
|
||||||
* quickly using MemWriter.
|
* space we can record into the buffer quickly using MemWriter.
|
||||||
*
|
*
|
||||||
* @param aRecordedEvent the event to record
|
* @param aRecordedEvent the event to record
|
||||||
*/
|
*/
|
||||||
|
|
@ -254,25 +237,56 @@ class ContiguousBufferStream {
|
||||||
SizeCollector size;
|
SizeCollector size;
|
||||||
WriteElement(size, aRecordedEvent->GetType());
|
WriteElement(size, aRecordedEvent->GetType());
|
||||||
aRecordedEvent->Record(size);
|
aRecordedEvent->Record(size);
|
||||||
auto& buffer = GetContiguousBuffer(size.mTotalSize);
|
if (size.mTotalSize > mAvailable) {
|
||||||
if (!buffer.IsValid()) {
|
WaitForAndRecalculateAvailableSpace();
|
||||||
return;
|
}
|
||||||
|
if (size.mTotalSize <= mAvailable) {
|
||||||
|
MemWriter writer(mBufPos);
|
||||||
|
WriteElement(writer, aRecordedEvent->GetType());
|
||||||
|
aRecordedEvent->Record(writer);
|
||||||
|
UpdateWriteTotalsBy(size.mTotalSize);
|
||||||
|
} else {
|
||||||
|
WriteElement(*this, aRecordedEvent->GetType());
|
||||||
|
aRecordedEvent->Record(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(size.mTotalSize <= buffer.SizeRemaining());
|
|
||||||
|
|
||||||
WriteElement(buffer.Writer(), aRecordedEvent->GetType());
|
|
||||||
aRecordedEvent->Record(buffer.Writer());
|
|
||||||
IncrementEventCount();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple write function required by WriteElement.
|
||||||
|
*
|
||||||
|
* @param aData the data to be written to the buffer
|
||||||
|
* @param aSize the number of chars to write
|
||||||
|
*/
|
||||||
|
virtual void write(const char* const aData, const size_t aSize) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple read function required by ReadElement.
|
||||||
|
*
|
||||||
|
* @param aOut the pointer to read into
|
||||||
|
* @param aSize the number of chars to read
|
||||||
|
*/
|
||||||
|
virtual void read(char* const aOut, const size_t aSize) = 0;
|
||||||
|
|
||||||
|
virtual bool good() const = 0;
|
||||||
|
|
||||||
|
virtual void SetIsBad() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Provide a contiguous buffer with at least aSize remaining.
|
* Wait until space is available for writing and then set mBufPos and
|
||||||
|
* mAvailable.
|
||||||
*/
|
*/
|
||||||
virtual ContiguousBuffer& GetContiguousBuffer(size_t aSize) = 0;
|
virtual bool WaitForAndRecalculateAvailableSpace() = 0;
|
||||||
|
|
||||||
virtual void IncrementEventCount() = 0;
|
/**
|
||||||
|
* Update write count, mBufPos and mAvailable.
|
||||||
|
*
|
||||||
|
* @param aCount number of bytes written
|
||||||
|
*/
|
||||||
|
virtual void UpdateWriteTotalsBy(uint32_t aCount) = 0;
|
||||||
|
|
||||||
|
char* mBufPos = nullptr;
|
||||||
|
uint32_t mAvailable = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MemStream {
|
struct MemStream {
|
||||||
|
|
@ -416,7 +430,7 @@ class RecordedEvent {
|
||||||
|
|
||||||
virtual void RecordToStream(std::ostream& aStream) const = 0;
|
virtual void RecordToStream(std::ostream& aStream) const = 0;
|
||||||
virtual void RecordToStream(EventStream& aStream) const = 0;
|
virtual void RecordToStream(EventStream& aStream) const = 0;
|
||||||
virtual void RecordToStream(ContiguousBufferStream& aStream) const = 0;
|
virtual void RecordToStream(EventRingBuffer& aStream) const = 0;
|
||||||
virtual void RecordToStream(MemStream& aStream) const = 0;
|
virtual void RecordToStream(MemStream& aStream) const = 0;
|
||||||
|
|
||||||
virtual void OutputSimpleEventInfo(std::stringstream& aStringStream) const {}
|
virtual void OutputSimpleEventInfo(std::stringstream& aStringStream) const {}
|
||||||
|
|
@ -446,6 +460,9 @@ class RecordedEvent {
|
||||||
static bool DoWithEventFromStream(
|
static bool DoWithEventFromStream(
|
||||||
EventStream& aStream, EventType aType,
|
EventStream& aStream, EventType aType,
|
||||||
const std::function<bool(RecordedEvent*)>& aAction);
|
const std::function<bool(RecordedEvent*)>& aAction);
|
||||||
|
static bool DoWithEventFromStream(
|
||||||
|
EventRingBuffer& aStream, EventType aType,
|
||||||
|
const std::function<bool(RecordedEvent*)>& aAction);
|
||||||
|
|
||||||
EventType GetType() const { return (EventType)mType; }
|
EventType GetType() const { return (EventType)mType; }
|
||||||
|
|
||||||
|
|
@ -478,7 +495,7 @@ class RecordedEventDerived : public RecordedEvent {
|
||||||
WriteElement(aStream, this->mType);
|
WriteElement(aStream, this->mType);
|
||||||
static_cast<const Derived*>(this)->Record(aStream);
|
static_cast<const Derived*>(this)->Record(aStream);
|
||||||
}
|
}
|
||||||
void RecordToStream(ContiguousBufferStream& aStream) const final {
|
void RecordToStream(EventRingBuffer& aStream) const final {
|
||||||
aStream.RecordEvent(static_cast<const Derived*>(this));
|
aStream.RecordEvent(static_cast<const Derived*>(this));
|
||||||
}
|
}
|
||||||
void RecordToStream(MemStream& aStream) const override {
|
void RecordToStream(MemStream& aStream) const override {
|
||||||
|
|
|
||||||
|
|
@ -151,12 +151,6 @@ void CanvasManagerChild::EndCanvasTransaction() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasManagerChild::ClearCachedResources() {
|
|
||||||
if (mCanvasChild) {
|
|
||||||
mCanvasChild->ClearCachedResources();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasManagerChild::DeactivateCanvas() {
|
void CanvasManagerChild::DeactivateCanvas() {
|
||||||
mActive = false;
|
mActive = false;
|
||||||
if (mCanvasChild) {
|
if (mCanvasChild) {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
||||||
|
|
||||||
bool IsCanvasActive() { return mActive; }
|
bool IsCanvasActive() { return mActive; }
|
||||||
void EndCanvasTransaction();
|
void EndCanvasTransaction();
|
||||||
void ClearCachedResources();
|
|
||||||
void DeactivateCanvas();
|
void DeactivateCanvas();
|
||||||
|
|
||||||
RefPtr<layers::CanvasChild> GetCanvasChild();
|
RefPtr<layers::CanvasChild> GetCanvasChild();
|
||||||
|
|
|
||||||
|
|
@ -9,268 +9,294 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "mozilla/layers/SharedSurfacesChild.h"
|
#include "mozilla/layers/SharedSurfacesChild.h"
|
||||||
#include "mozilla/StaticPrefs_gfx.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "RecordedCanvasEventImpl.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
||||||
struct ShmemAndHandle {
|
static const uint32_t kMaxSpinCount = 200;
|
||||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
|
||||||
Handle handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Maybe<ShmemAndHandle> CreateAndMapShmem(size_t aSize) {
|
static const TimeDuration kTimeout = TimeDuration::FromMilliseconds(100);
|
||||||
auto shmem = MakeRefPtr<ipc::SharedMemoryBasic>();
|
static const int32_t kTimeoutRetryCount = 50;
|
||||||
if (!shmem->Create(aSize) || !shmem->Map(aSize)) {
|
|
||||||
return Nothing();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shmemHandle = shmem->TakeHandle();
|
static const uint32_t kCacheLineSize = 64;
|
||||||
if (!shmemHandle) {
|
static const uint32_t kSmallStreamSize = 64 * 1024;
|
||||||
return Nothing();
|
static const uint32_t kLargeStreamSize = 2048 * 1024;
|
||||||
}
|
|
||||||
|
|
||||||
return Some(ShmemAndHandle{shmem.forget(), std::move(shmemHandle)});
|
static_assert((static_cast<uint64_t>(UINT32_MAX) + 1) % kSmallStreamSize == 0,
|
||||||
|
"kSmallStreamSize must be a power of two.");
|
||||||
|
static_assert((static_cast<uint64_t>(UINT32_MAX) + 1) % kLargeStreamSize == 0,
|
||||||
|
"kLargeStreamSize must be a power of two.");
|
||||||
|
|
||||||
|
uint32_t CanvasEventRingBuffer::StreamSize() {
|
||||||
|
return mLargeStream ? kLargeStreamSize : kSmallStreamSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasDrawEventRecorder::CanvasDrawEventRecorder() {
|
bool CanvasEventRingBuffer::InitBuffer(
|
||||||
mDefaultBufferSize = ipc::SharedMemory::PageAlignedSize(
|
base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aReadHandle) {
|
||||||
StaticPrefs::gfx_canvas_remote_default_buffer_size());
|
size_t shmemSize = StreamSize() + (2 * kCacheLineSize);
|
||||||
mMaxSpinCount = StaticPrefs::gfx_canvas_remote_max_spin_count();
|
mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
|
||||||
mDropBufferLimit = StaticPrefs::gfx_canvas_remote_drop_buffer_limit();
|
if (NS_WARN_IF(!mSharedMemory->Create(shmemSize)) ||
|
||||||
mDropBufferOnZero = mDropBufferLimit;
|
NS_WARN_IF(!mSharedMemory->Map(shmemSize))) {
|
||||||
}
|
mGood = false;
|
||||||
|
|
||||||
bool CanvasDrawEventRecorder::Init(TextureType aTextureType,
|
|
||||||
UniquePtr<Helpers> aHelpers) {
|
|
||||||
mHelpers = std::move(aHelpers);
|
|
||||||
|
|
||||||
MOZ_ASSERT(mTextureType == TextureType::Unknown);
|
|
||||||
auto header = CreateAndMapShmem(sizeof(Header));
|
|
||||||
if (NS_WARN_IF(header.isNothing())) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mHeader = static_cast<Header*>(header->shmem->memory());
|
*aReadHandle = mSharedMemory->TakeHandle();
|
||||||
mHeader->eventCount = 0;
|
if (NS_WARN_IF(!*aReadHandle)) {
|
||||||
mHeader->writerWaitCount = 0;
|
mGood = false;
|
||||||
mHeader->writerState = State::Processing;
|
|
||||||
mHeader->processedCount = 0;
|
|
||||||
mHeader->readerState = State::Processing;
|
|
||||||
|
|
||||||
// We always keep at least two buffers. This means that when we
|
|
||||||
// have to add a new buffer, there is at least a full buffer that requires
|
|
||||||
// translating while the handle is sent over.
|
|
||||||
AutoTArray<Handle, 2> bufferHandles;
|
|
||||||
auto buffer = CreateAndMapShmem(mDefaultBufferSize);
|
|
||||||
if (NS_WARN_IF(buffer.isNothing())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mCurrentBuffer = CanvasBuffer(std::move(buffer->shmem));
|
|
||||||
bufferHandles.AppendElement(std::move(buffer->handle));
|
|
||||||
|
|
||||||
buffer = CreateAndMapShmem(mDefaultBufferSize);
|
|
||||||
if (NS_WARN_IF(buffer.isNothing())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mRecycledBuffers.emplace(buffer->shmem.forget(), 0);
|
|
||||||
bufferHandles.AppendElement(std::move(buffer->handle));
|
|
||||||
|
|
||||||
mWriterSemaphore.reset(CrossProcessSemaphore::Create("CanvasRecorder", 0));
|
|
||||||
auto writerSem = mWriterSemaphore->CloneHandle();
|
|
||||||
mWriterSemaphore->CloseHandle();
|
|
||||||
if (!IsHandleValid(writerSem)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mReaderSemaphore.reset(CrossProcessSemaphore::Create("CanvasTranslator", 0));
|
mBuf = static_cast<char*>(mSharedMemory->memory());
|
||||||
auto readerSem = mReaderSemaphore->CloneHandle();
|
mBufPos = mBuf;
|
||||||
mReaderSemaphore->CloseHandle();
|
mAvailable = StreamSize();
|
||||||
if (!IsHandleValid(readerSem)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mHelpers->InitTranslator(aTextureType, std::move(header->handle),
|
static_assert(sizeof(ReadFooter) <= kCacheLineSize,
|
||||||
std::move(bufferHandles), mDefaultBufferSize,
|
"ReadFooter must fit in kCacheLineSize.");
|
||||||
std::move(readerSem), std::move(writerSem),
|
mRead = reinterpret_cast<ReadFooter*>(mBuf + StreamSize());
|
||||||
/* aUseIPDLThread */ false)) {
|
mRead->count = 0;
|
||||||
return false;
|
mRead->returnCount = 0;
|
||||||
}
|
mRead->state = State::Processing;
|
||||||
|
|
||||||
|
static_assert(sizeof(WriteFooter) <= kCacheLineSize,
|
||||||
|
"WriteFooter must fit in kCacheLineSize.");
|
||||||
|
mWrite = reinterpret_cast<WriteFooter*>(mBuf + StreamSize() + kCacheLineSize);
|
||||||
|
mWrite->count = 0;
|
||||||
|
mWrite->returnCount = 0;
|
||||||
|
mWrite->requiredDifference = 0;
|
||||||
|
mWrite->state = State::Processing;
|
||||||
|
mOurCount = 0;
|
||||||
|
|
||||||
mTextureType = aTextureType;
|
|
||||||
mHeaderShmem = header->shmem;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasDrawEventRecorder::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
bool CanvasEventRingBuffer::InitWriter(
|
||||||
aEvent.RecordToStream(*this);
|
base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aReadHandle,
|
||||||
|
CrossProcessSemaphoreHandle* aReaderSem,
|
||||||
|
CrossProcessSemaphoreHandle* aWriterSem,
|
||||||
|
UniquePtr<WriterServices> aWriterServices) {
|
||||||
|
if (!InitBuffer(aOtherPid, aReadHandle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mReaderSemaphore.reset(
|
||||||
|
CrossProcessSemaphore::Create("SharedMemoryStreamParent", 0));
|
||||||
|
*aReaderSem = mReaderSemaphore->CloneHandle();
|
||||||
|
mReaderSemaphore->CloseHandle();
|
||||||
|
if (!IsHandleValid(*aReaderSem)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mWriterSemaphore.reset(
|
||||||
|
CrossProcessSemaphore::Create("SharedMemoryStreamChild", 0));
|
||||||
|
*aWriterSem = mWriterSemaphore->CloneHandle();
|
||||||
|
mWriterSemaphore->CloseHandle();
|
||||||
|
if (!IsHandleValid(*aWriterSem)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWriterServices = std::move(aWriterServices);
|
||||||
|
|
||||||
|
mGood = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CanvasDrawEventRecorder::CreateCheckpoint() {
|
bool CanvasEventRingBuffer::InitReader(
|
||||||
int64_t checkpoint = mHeader->eventCount;
|
ipc::SharedMemoryBasic::Handle aReadHandle,
|
||||||
RecordEvent(RecordedCheckpoint());
|
CrossProcessSemaphoreHandle aReaderSem,
|
||||||
return checkpoint;
|
CrossProcessSemaphoreHandle aWriterSem,
|
||||||
|
UniquePtr<ReaderServices> aReaderServices) {
|
||||||
|
if (!SetNewBuffer(std::move(aReadHandle))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem)));
|
||||||
|
mReaderSemaphore->CloseHandle();
|
||||||
|
mWriterSemaphore.reset(CrossProcessSemaphore::Create(std::move(aWriterSem)));
|
||||||
|
mWriterSemaphore->CloseHandle();
|
||||||
|
|
||||||
|
mReaderServices = std::move(aReaderServices);
|
||||||
|
|
||||||
|
mGood = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanvasDrawEventRecorder::WaitForCheckpoint(int64_t aCheckpoint) {
|
bool CanvasEventRingBuffer::SetNewBuffer(
|
||||||
uint32_t spinCount = mMaxSpinCount;
|
ipc::SharedMemoryBasic::Handle aReadHandle) {
|
||||||
|
MOZ_RELEASE_ASSERT(
|
||||||
|
!mSharedMemory,
|
||||||
|
"Shared memory should have been dropped before new buffer is sent.");
|
||||||
|
|
||||||
|
size_t shmemSize = StreamSize() + (2 * kCacheLineSize);
|
||||||
|
mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
|
||||||
|
if (NS_WARN_IF(!mSharedMemory->SetHandle(
|
||||||
|
std::move(aReadHandle), ipc::SharedMemory::RightsReadWrite)) ||
|
||||||
|
NS_WARN_IF(!mSharedMemory->Map(shmemSize))) {
|
||||||
|
mGood = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mSharedMemory->CloseHandle();
|
||||||
|
|
||||||
|
mBuf = static_cast<char*>(mSharedMemory->memory());
|
||||||
|
mRead = reinterpret_cast<ReadFooter*>(mBuf + StreamSize());
|
||||||
|
mWrite = reinterpret_cast<WriteFooter*>(mBuf + StreamSize() + kCacheLineSize);
|
||||||
|
mOurCount = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableSpace() {
|
||||||
|
if (!good()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bufPos = mOurCount % StreamSize();
|
||||||
|
uint32_t maxToWrite = StreamSize() - bufPos;
|
||||||
|
mAvailable = std::min(maxToWrite, WaitForBytesToWrite());
|
||||||
|
if (!mAvailable) {
|
||||||
|
mBufPos = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBufPos = mBuf + bufPos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::write(const char* const aData, const size_t aSize) {
|
||||||
|
const char* curDestPtr = aData;
|
||||||
|
size_t remainingToWrite = aSize;
|
||||||
|
if (remainingToWrite > mAvailable) {
|
||||||
|
if (!WaitForAndRecalculateAvailableSpace()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingToWrite <= mAvailable) {
|
||||||
|
memcpy(mBufPos, curDestPtr, remainingToWrite);
|
||||||
|
UpdateWriteTotalsBy(remainingToWrite);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (mHeader->processedCount >= aCheckpoint) {
|
memcpy(mBufPos, curDestPtr, mAvailable);
|
||||||
return true;
|
IncrementWriteCountBy(mAvailable);
|
||||||
|
curDestPtr += mAvailable;
|
||||||
|
remainingToWrite -= mAvailable;
|
||||||
|
if (!WaitForAndRecalculateAvailableSpace()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} while (--spinCount != 0);
|
} while (remainingToWrite > mAvailable);
|
||||||
|
|
||||||
mHeader->writerState = State::AboutToWait;
|
memcpy(mBufPos, curDestPtr, remainingToWrite);
|
||||||
if (mHeader->processedCount >= aCheckpoint) {
|
UpdateWriteTotalsBy(remainingToWrite);
|
||||||
mHeader->writerState = State::Processing;
|
}
|
||||||
return true;
|
|
||||||
|
void CanvasEventRingBuffer::IncrementWriteCountBy(uint32_t aCount) {
|
||||||
|
mOurCount += aCount;
|
||||||
|
mWrite->count = mOurCount;
|
||||||
|
if (mRead->state != State::Processing) {
|
||||||
|
CheckAndSignalReader();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::UpdateWriteTotalsBy(uint32_t aCount) {
|
||||||
|
IncrementWriteCountBy(aCount);
|
||||||
|
mBufPos += aCount;
|
||||||
|
mAvailable -= aCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::WaitForAndRecalculateAvailableData() {
|
||||||
|
if (!good()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mHeader->writerWaitCount = aCheckpoint;
|
uint32_t bufPos = mOurCount % StreamSize();
|
||||||
mHeader->writerState = State::Waiting;
|
uint32_t maxToRead = StreamSize() - bufPos;
|
||||||
|
mAvailable = std::min(maxToRead, WaitForBytesToRead());
|
||||||
|
if (!mAvailable) {
|
||||||
|
SetIsBad();
|
||||||
|
mBufPos = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Wait unless we detect the reading side has closed.
|
mBufPos = mBuf + bufPos;
|
||||||
while (!mHelpers->ReaderClosed() && mHeader->readerState != State::Failed) {
|
return true;
|
||||||
if (mWriterSemaphore->Wait(Some(TimeDuration::FromMilliseconds(100)))) {
|
}
|
||||||
MOZ_ASSERT(mHeader->processedCount >= aCheckpoint);
|
|
||||||
return true;
|
void CanvasEventRingBuffer::read(char* const aOut, const size_t aSize) {
|
||||||
|
char* curSrcPtr = aOut;
|
||||||
|
size_t remainingToRead = aSize;
|
||||||
|
if (remainingToRead > mAvailable) {
|
||||||
|
if (!WaitForAndRecalculateAvailableData()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either the reader has failed or we're stopping writing for some other
|
if (remainingToRead <= mAvailable) {
|
||||||
// reason (e.g. shutdown), so mark us as failed so the reader is aware.
|
memcpy(curSrcPtr, mBufPos, remainingToRead);
|
||||||
mHeader->writerState = State::Failed;
|
UpdateReadTotalsBy(remainingToRead);
|
||||||
return false;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasDrawEventRecorder::WriteInternalEvent(EventType aEventType) {
|
|
||||||
MOZ_ASSERT(mCurrentBuffer.SizeRemaining() > 0);
|
|
||||||
|
|
||||||
WriteElement(mCurrentBuffer.Writer(), aEventType);
|
|
||||||
IncrementEventCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::ContiguousBuffer& CanvasDrawEventRecorder::GetContiguousBuffer(
|
|
||||||
size_t aSize) {
|
|
||||||
// We make sure that our buffer can hold aSize + 1 to ensure we always have
|
|
||||||
// room for the end of buffer event.
|
|
||||||
|
|
||||||
// Check if there is enough room is our current buffer.
|
|
||||||
if (mCurrentBuffer.SizeRemaining() > aSize) {
|
|
||||||
return mCurrentBuffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the next recycled buffer is big enough and free use that.
|
|
||||||
if (mRecycledBuffers.front().Capacity() > aSize &&
|
|
||||||
mRecycledBuffers.front().eventCount <= mHeader->processedCount) {
|
|
||||||
// Only queue default size buffers for recycling.
|
|
||||||
if (mCurrentBuffer.Capacity() == mDefaultBufferSize) {
|
|
||||||
WriteInternalEvent(RECYCLE_BUFFER);
|
|
||||||
mRecycledBuffers.emplace(std::move(mCurrentBuffer.shmem),
|
|
||||||
mHeader->eventCount);
|
|
||||||
} else {
|
|
||||||
WriteInternalEvent(DROP_BUFFER);
|
|
||||||
}
|
|
||||||
|
|
||||||
mCurrentBuffer = CanvasBuffer(std::move(mRecycledBuffers.front().shmem));
|
|
||||||
mRecycledBuffers.pop();
|
|
||||||
|
|
||||||
// If we have more than one recycled buffers free a configured number of
|
|
||||||
// times in a row then drop one.
|
|
||||||
if (mRecycledBuffers.size() > 1 &&
|
|
||||||
mRecycledBuffers.front().eventCount < mHeader->processedCount) {
|
|
||||||
if (--mDropBufferOnZero == 0) {
|
|
||||||
WriteInternalEvent(DROP_BUFFER);
|
|
||||||
mCurrentBuffer =
|
|
||||||
CanvasBuffer(std::move(mRecycledBuffers.front().shmem));
|
|
||||||
mRecycledBuffers.pop();
|
|
||||||
mDropBufferOnZero = 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mDropBufferOnZero = mDropBufferLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mCurrentBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't have a buffer free or it is not big enough, so create a new one.
|
|
||||||
WriteInternalEvent(PAUSE_TRANSLATION);
|
|
||||||
|
|
||||||
// Only queue default size buffers for recycling.
|
|
||||||
if (mCurrentBuffer.Capacity() == mDefaultBufferSize) {
|
|
||||||
mRecycledBuffers.emplace(std::move(mCurrentBuffer.shmem),
|
|
||||||
mHeader->eventCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t bufferSize = std::max(mDefaultBufferSize,
|
|
||||||
ipc::SharedMemory::PageAlignedSize(aSize + 1));
|
|
||||||
auto newBuffer = CreateAndMapShmem(bufferSize);
|
|
||||||
if (NS_WARN_IF(newBuffer.isNothing())) {
|
|
||||||
mHeader->writerState = State::Failed;
|
|
||||||
mCurrentBuffer = CanvasBuffer(nullptr);
|
|
||||||
return mCurrentBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mHelpers->AddBuffer(std::move(newBuffer->handle), bufferSize)) {
|
|
||||||
mHeader->writerState = State::Failed;
|
|
||||||
mCurrentBuffer = CanvasBuffer(nullptr);
|
|
||||||
return mCurrentBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
mCurrentBuffer = CanvasBuffer(std::move(newBuffer->shmem));
|
|
||||||
return mCurrentBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasDrawEventRecorder::DropFreeBuffers() {
|
|
||||||
while (mRecycledBuffers.size() > 1 &&
|
|
||||||
mRecycledBuffers.front().eventCount < mHeader->processedCount) {
|
|
||||||
WriteInternalEvent(DROP_BUFFER);
|
|
||||||
mCurrentBuffer = CanvasBuffer(std::move(mRecycledBuffers.front().shmem));
|
|
||||||
mRecycledBuffers.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasDrawEventRecorder::IncrementEventCount() {
|
|
||||||
mHeader->eventCount++;
|
|
||||||
CheckAndSignalReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasDrawEventRecorder::CheckAndSignalReader() {
|
|
||||||
do {
|
do {
|
||||||
switch (mHeader->readerState) {
|
memcpy(curSrcPtr, mBufPos, mAvailable);
|
||||||
|
IncrementReadCountBy(mAvailable);
|
||||||
|
curSrcPtr += mAvailable;
|
||||||
|
remainingToRead -= mAvailable;
|
||||||
|
if (!WaitForAndRecalculateAvailableData()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (remainingToRead > mAvailable);
|
||||||
|
|
||||||
|
memcpy(curSrcPtr, mBufPos, remainingToRead);
|
||||||
|
UpdateReadTotalsBy(remainingToRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::IncrementReadCountBy(uint32_t aCount) {
|
||||||
|
mOurCount += aCount;
|
||||||
|
mRead->count = mOurCount;
|
||||||
|
if (mWrite->state != State::Processing) {
|
||||||
|
CheckAndSignalWriter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::UpdateReadTotalsBy(uint32_t aCount) {
|
||||||
|
IncrementReadCountBy(aCount);
|
||||||
|
mBufPos += aCount;
|
||||||
|
mAvailable -= aCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::CheckAndSignalReader() {
|
||||||
|
do {
|
||||||
|
switch (mRead->state) {
|
||||||
case State::Processing:
|
case State::Processing:
|
||||||
case State::Paused:
|
|
||||||
case State::Failed:
|
case State::Failed:
|
||||||
return;
|
return;
|
||||||
case State::AboutToWait:
|
case State::AboutToWait:
|
||||||
// The reader is making a decision about whether to wait. So, we must
|
// The reader is making a decision about whether to wait. So, we must
|
||||||
// wait until it has decided to avoid races. Check if the reader is
|
// wait until it has decided to avoid races. Check if the reader is
|
||||||
// closed to avoid hangs.
|
// closed to avoid hangs.
|
||||||
if (mHelpers->ReaderClosed()) {
|
if (mWriterServices->ReaderClosed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
case State::Waiting:
|
case State::Waiting:
|
||||||
if (mHeader->processedCount < mHeader->eventCount) {
|
if (mRead->count != mOurCount) {
|
||||||
// We have to use compareExchange here because the reader can change
|
// We have to use compareExchange here because the reader can change
|
||||||
// from Waiting to Stopped.
|
// from Waiting to Stopped.
|
||||||
if (mHeader->readerState.compareExchange(State::Waiting,
|
if (mRead->state.compareExchange(State::Waiting, State::Processing)) {
|
||||||
State::Processing)) {
|
|
||||||
mReaderSemaphore->Signal();
|
mReaderSemaphore->Signal();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(mHeader->readerState == State::Stopped);
|
MOZ_ASSERT(mRead->state == State::Stopped);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case State::Stopped:
|
case State::Stopped:
|
||||||
if (mHeader->processedCount < mHeader->eventCount) {
|
if (mRead->count != mOurCount) {
|
||||||
mHeader->readerState = State::Processing;
|
mRead->state = State::Processing;
|
||||||
if (!mHelpers->RestartReader()) {
|
mWriterServices->ResumeReader();
|
||||||
mHeader->writerState = State::Failed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
|
@ -280,6 +306,267 @@ void CanvasDrawEventRecorder::CheckAndSignalReader() {
|
||||||
} while (true);
|
} while (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::HasDataToRead() {
|
||||||
|
return (mWrite->count != mOurCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::StopIfEmpty() {
|
||||||
|
// Double-check that the writer isn't waiting.
|
||||||
|
CheckAndSignalWriter();
|
||||||
|
mRead->state = State::AboutToWait;
|
||||||
|
if (HasDataToRead()) {
|
||||||
|
mRead->state = State::Processing;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mRead->state = State::Stopped;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::WaitForDataToRead(TimeDuration aTimeout,
|
||||||
|
int32_t aRetryCount) {
|
||||||
|
uint32_t spinCount = kMaxSpinCount;
|
||||||
|
do {
|
||||||
|
if (HasDataToRead()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} while (--spinCount != 0);
|
||||||
|
|
||||||
|
// Double-check that the writer isn't waiting.
|
||||||
|
CheckAndSignalWriter();
|
||||||
|
mRead->state = State::AboutToWait;
|
||||||
|
if (HasDataToRead()) {
|
||||||
|
mRead->state = State::Processing;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mRead->state = State::Waiting;
|
||||||
|
do {
|
||||||
|
if (mReaderSemaphore->Wait(Some(aTimeout))) {
|
||||||
|
MOZ_RELEASE_ASSERT(HasDataToRead());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mReaderServices->WriterClosed()) {
|
||||||
|
// Something has gone wrong on the writing side, just return false so
|
||||||
|
// that we can hopefully recover.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} while (aRetryCount-- > 0);
|
||||||
|
|
||||||
|
// We have to use compareExchange here because the writer can change our
|
||||||
|
// state if we are waiting. signaled
|
||||||
|
if (!mRead->state.compareExchange(State::Waiting, State::Stopped)) {
|
||||||
|
MOZ_RELEASE_ASSERT(HasDataToRead());
|
||||||
|
MOZ_RELEASE_ASSERT(mRead->state == State::Processing);
|
||||||
|
// The writer has just signaled us, so consume it before returning
|
||||||
|
MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t CanvasEventRingBuffer::ReadNextEvent() {
|
||||||
|
uint8_t nextEvent;
|
||||||
|
ReadElement(*this, nextEvent);
|
||||||
|
while (nextEvent == kCheckpointEventType && good()) {
|
||||||
|
ReadElement(*this, nextEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextEvent == kDropBufferEventType) {
|
||||||
|
// Writer is switching to a different sized buffer.
|
||||||
|
mBuf = nullptr;
|
||||||
|
mBufPos = nullptr;
|
||||||
|
mRead = nullptr;
|
||||||
|
mWrite = nullptr;
|
||||||
|
mAvailable = 0;
|
||||||
|
mSharedMemory = nullptr;
|
||||||
|
// We always toggle between smaller and larger stream sizes.
|
||||||
|
mLargeStream = !mLargeStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CanvasEventRingBuffer::CreateCheckpoint() {
|
||||||
|
WriteElement(*this, kCheckpointEventType);
|
||||||
|
return mOurCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::WaitForCheckpoint(uint32_t aCheckpoint) {
|
||||||
|
return WaitForReadCount(aCheckpoint, kTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::SwitchBuffer(
|
||||||
|
base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aReadHandle) {
|
||||||
|
WriteElement(*this, kDropBufferEventType);
|
||||||
|
|
||||||
|
// Make sure the drop buffer event has been read before continuing. We can't
|
||||||
|
// write an actual checkpoint because there will be no buffer to read from.
|
||||||
|
if (!WaitForCheckpoint(mOurCount)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mBuf = nullptr;
|
||||||
|
mBufPos = nullptr;
|
||||||
|
mRead = nullptr;
|
||||||
|
mWrite = nullptr;
|
||||||
|
mAvailable = 0;
|
||||||
|
mSharedMemory = nullptr;
|
||||||
|
// We always toggle between smaller and larger stream sizes.
|
||||||
|
mLargeStream = !mLargeStream;
|
||||||
|
return InitBuffer(aOtherPid, aReadHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::CheckAndSignalWriter() {
|
||||||
|
do {
|
||||||
|
switch (mWrite->state) {
|
||||||
|
case State::Processing:
|
||||||
|
return;
|
||||||
|
case State::AboutToWait:
|
||||||
|
// The writer is making a decision about whether to wait. So, we must
|
||||||
|
// wait until it has decided to avoid races. Check if the writer is
|
||||||
|
// closed to avoid hangs.
|
||||||
|
if (mReaderServices->WriterClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case State::Waiting:
|
||||||
|
if (mWrite->count - mOurCount <= mWrite->requiredDifference) {
|
||||||
|
mWrite->state = State::Processing;
|
||||||
|
mWriterSemaphore->Signal();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanvasEventRingBuffer::WaitForReadCount(uint32_t aReadCount,
|
||||||
|
TimeDuration aTimeout) {
|
||||||
|
uint32_t requiredDifference = mOurCount - aReadCount;
|
||||||
|
uint32_t spinCount = kMaxSpinCount;
|
||||||
|
do {
|
||||||
|
if (mOurCount - mRead->count <= requiredDifference) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} while (--spinCount != 0);
|
||||||
|
|
||||||
|
// Double-check that the reader isn't waiting.
|
||||||
|
CheckAndSignalReader();
|
||||||
|
mWrite->state = State::AboutToWait;
|
||||||
|
if (mOurCount - mRead->count <= requiredDifference) {
|
||||||
|
mWrite->state = State::Processing;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWrite->requiredDifference = requiredDifference;
|
||||||
|
mWrite->state = State::Waiting;
|
||||||
|
|
||||||
|
// Wait unless we detect the reading side has closed.
|
||||||
|
while (!mWriterServices->ReaderClosed() && mRead->state != State::Failed) {
|
||||||
|
if (mWriterSemaphore->Wait(Some(aTimeout))) {
|
||||||
|
MOZ_ASSERT(mOurCount - mRead->count <= requiredDifference);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either the reader has failed or we're stopping writing for some other
|
||||||
|
// reason (e.g. shutdown), so mark us as failed so the reader is aware.
|
||||||
|
mWrite->state = State::Failed;
|
||||||
|
mGood = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CanvasEventRingBuffer::WaitForBytesToWrite() {
|
||||||
|
uint32_t streamFullReadCount = mOurCount - StreamSize();
|
||||||
|
if (!WaitForReadCount(streamFullReadCount + 1, kTimeout)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mRead->count - streamFullReadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CanvasEventRingBuffer::WaitForBytesToRead() {
|
||||||
|
if (!WaitForDataToRead(kTimeout, kTimeoutRetryCount)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mWrite->count - mOurCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::ReturnWrite(const char* aData, size_t aSize) {
|
||||||
|
uint32_t writeCount = mRead->returnCount;
|
||||||
|
uint32_t bufPos = writeCount % StreamSize();
|
||||||
|
uint32_t bufRemaining = StreamSize() - bufPos;
|
||||||
|
uint32_t availableToWrite =
|
||||||
|
std::min(bufRemaining, (mWrite->returnCount + StreamSize() - writeCount));
|
||||||
|
while (availableToWrite < aSize) {
|
||||||
|
if (availableToWrite) {
|
||||||
|
memcpy(mBuf + bufPos, aData, availableToWrite);
|
||||||
|
writeCount += availableToWrite;
|
||||||
|
mRead->returnCount = writeCount;
|
||||||
|
bufPos = writeCount % StreamSize();
|
||||||
|
bufRemaining = StreamSize() - bufPos;
|
||||||
|
aData += availableToWrite;
|
||||||
|
aSize -= availableToWrite;
|
||||||
|
} else if (mReaderServices->WriterClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
availableToWrite = std::min(
|
||||||
|
bufRemaining, (mWrite->returnCount + StreamSize() - writeCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(mBuf + bufPos, aData, aSize);
|
||||||
|
writeCount += aSize;
|
||||||
|
mRead->returnCount = writeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasEventRingBuffer::ReturnRead(char* aOut, size_t aSize) {
|
||||||
|
// First wait for the event returning the data to be read.
|
||||||
|
WaitForCheckpoint(mOurCount);
|
||||||
|
uint32_t readCount = mWrite->returnCount;
|
||||||
|
|
||||||
|
// If the event sending back data fails to play then it will ReturnWrite
|
||||||
|
// nothing. So, wait until something has been written or the reader has
|
||||||
|
// stopped processing.
|
||||||
|
while (readCount == mRead->returnCount) {
|
||||||
|
// We recheck the count, because the other side can write all the data and
|
||||||
|
// started waiting in between these two lines.
|
||||||
|
if (mRead->state != State::Processing && readCount == mRead->returnCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t bufPos = readCount % StreamSize();
|
||||||
|
uint32_t bufRemaining = StreamSize() - bufPos;
|
||||||
|
uint32_t availableToRead =
|
||||||
|
std::min(bufRemaining, (mRead->returnCount - readCount));
|
||||||
|
while (availableToRead < aSize) {
|
||||||
|
if (availableToRead) {
|
||||||
|
memcpy(aOut, mBuf + bufPos, availableToRead);
|
||||||
|
readCount += availableToRead;
|
||||||
|
mWrite->returnCount = readCount;
|
||||||
|
bufPos = readCount % StreamSize();
|
||||||
|
bufRemaining = StreamSize() - bufPos;
|
||||||
|
aOut += availableToRead;
|
||||||
|
aSize -= availableToRead;
|
||||||
|
} else if (mWriterServices->ReaderClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
availableToRead = std::min(bufRemaining, (mRead->returnCount - readCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(aOut, mBuf + bufPos, aSize);
|
||||||
|
readCount += aSize;
|
||||||
|
mWrite->returnCount = readCount;
|
||||||
|
}
|
||||||
|
|
||||||
void CanvasDrawEventRecorder::StoreSourceSurfaceRecording(
|
void CanvasDrawEventRecorder::StoreSourceSurfaceRecording(
|
||||||
gfx::SourceSurface* aSurface, const char* aReason) {
|
gfx::SourceSurface* aSurface, const char* aReason) {
|
||||||
wr::ExternalImageId extId{};
|
wr::ExternalImageId extId{};
|
||||||
|
|
|
||||||
|
|
@ -7,32 +7,195 @@
|
||||||
#ifndef mozilla_layers_CanvasDrawEventRecorder_h
|
#ifndef mozilla_layers_CanvasDrawEventRecorder_h
|
||||||
#define mozilla_layers_CanvasDrawEventRecorder_h
|
#define mozilla_layers_CanvasDrawEventRecorder_h
|
||||||
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#include "mozilla/Atomics.h"
|
|
||||||
#include "mozilla/gfx/DrawEventRecorder.h"
|
#include "mozilla/gfx/DrawEventRecorder.h"
|
||||||
#include "mozilla/ipc/CrossProcessSemaphore.h"
|
#include "mozilla/ipc/CrossProcessSemaphore.h"
|
||||||
#include "mozilla/ipc/SharedMemoryBasic.h"
|
#include "mozilla/ipc/SharedMemoryBasic.h"
|
||||||
#include "mozilla/layers/LayersTypes.h"
|
|
||||||
#include "mozilla/RefPtr.h"
|
|
||||||
#include "mozilla/UniquePtr.h"
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
using EventType = gfx::RecordedEvent::EventType;
|
|
||||||
|
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
||||||
typedef mozilla::ipc::SharedMemoryBasic::Handle Handle;
|
static const uint8_t kCheckpointEventType = -1;
|
||||||
typedef mozilla::CrossProcessSemaphoreHandle CrossProcessSemaphoreHandle;
|
static const uint8_t kDropBufferEventType = -2;
|
||||||
|
|
||||||
class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
|
class CanvasEventRingBuffer final : public gfx::EventRingBuffer {
|
||||||
public gfx::ContiguousBufferStream {
|
|
||||||
public:
|
public:
|
||||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CanvasDrawEventRecorder, final)
|
/**
|
||||||
|
* WriterServices allows consumers of CanvasEventRingBuffer to provide
|
||||||
|
* functions required by the write side of a CanvasEventRingBuffer without
|
||||||
|
* introducing unnecessary dependencies on IPC code.
|
||||||
|
*/
|
||||||
|
class WriterServices {
|
||||||
|
public:
|
||||||
|
virtual ~WriterServices() = default;
|
||||||
|
|
||||||
CanvasDrawEventRecorder();
|
/**
|
||||||
|
* @returns true if the reader of the CanvasEventRingBuffer has permanently
|
||||||
|
* stopped processing, otherwise returns false.
|
||||||
|
*/
|
||||||
|
virtual bool ReaderClosed() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the reader to resume processing when it is in a stopped state.
|
||||||
|
*/
|
||||||
|
virtual void ResumeReader() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReaderServices allows consumers of CanvasEventRingBuffer to provide
|
||||||
|
* functions required by the read side of a CanvasEventRingBuffer without
|
||||||
|
* introducing unnecessary dependencies on IPC code.
|
||||||
|
*/
|
||||||
|
class ReaderServices {
|
||||||
|
public:
|
||||||
|
virtual ~ReaderServices() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns true if the writer of the CanvasEventRingBuffer has permanently
|
||||||
|
* stopped processing, otherwise returns false.
|
||||||
|
*/
|
||||||
|
virtual bool WriterClosed() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
CanvasEventRingBuffer() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the shared memory used for the ringbuffer and footers.
|
||||||
|
* @param aOtherPid process ID to share the handles to
|
||||||
|
* @param aReadHandle handle to the shared memory for the buffer
|
||||||
|
*/
|
||||||
|
bool InitBuffer(base::ProcessId aOtherPid,
|
||||||
|
ipc::SharedMemoryBasic::Handle* aReadHandle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the write side of a CanvasEventRingBuffer returning handles to
|
||||||
|
* the shared memory for the buffer and the two semaphores for waiting in the
|
||||||
|
* reader and the writer.
|
||||||
|
*
|
||||||
|
* @param aOtherPid process ID to share the handles to
|
||||||
|
* @param aReadHandle handle to the shared memory for the buffer
|
||||||
|
* @param aReaderSem reading blocked semaphore
|
||||||
|
* @param aWriterSem writing blocked semaphore
|
||||||
|
* @param aWriterServices provides functions required by the writer
|
||||||
|
* @returns true if initialization succeeds
|
||||||
|
*/
|
||||||
|
bool InitWriter(base::ProcessId aOtherPid,
|
||||||
|
ipc::SharedMemoryBasic::Handle* aReadHandle,
|
||||||
|
CrossProcessSemaphoreHandle* aReaderSem,
|
||||||
|
CrossProcessSemaphoreHandle* aWriterSem,
|
||||||
|
UniquePtr<WriterServices> aWriterServices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the read side of a CanvasEventRingBuffer.
|
||||||
|
*
|
||||||
|
* @param aReadHandle handle to the shared memory for the buffer
|
||||||
|
* @param aReaderSem reading blocked semaphore
|
||||||
|
* @param aWriterSem writing blocked semaphore
|
||||||
|
* @param aReaderServices provides functions required by the reader
|
||||||
|
* @returns true if initialization succeeds
|
||||||
|
*/
|
||||||
|
bool InitReader(ipc::SharedMemoryBasic::Handle aReadHandle,
|
||||||
|
CrossProcessSemaphoreHandle aReaderSem,
|
||||||
|
CrossProcessSemaphoreHandle aWriterSem,
|
||||||
|
UniquePtr<ReaderServices> aReaderServices);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a new buffer to resume after we have been stopped by the writer.
|
||||||
|
*
|
||||||
|
* @param aReadHandle handle to the shared memory for the buffer
|
||||||
|
* @returns true if initialization succeeds
|
||||||
|
*/
|
||||||
|
bool SetNewBuffer(ipc::SharedMemoryBasic::Handle aReadHandle);
|
||||||
|
|
||||||
|
bool IsValid() const { return mSharedMemory; }
|
||||||
|
|
||||||
|
bool good() const final { return mGood; }
|
||||||
|
|
||||||
|
bool WriterFailed() const { return mWrite && mWrite->state == State::Failed; }
|
||||||
|
|
||||||
|
void SetIsBad() final {
|
||||||
|
mGood = false;
|
||||||
|
mRead->state = State::Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const char* const aData, const size_t aSize) final;
|
||||||
|
|
||||||
|
bool HasDataToRead();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will put the reader into a stopped state if there is no more data to
|
||||||
|
* read. If this returns false the caller is responsible for continuing
|
||||||
|
* translation at a later point. If it returns false the writer will start the
|
||||||
|
* translation again when more data is written.
|
||||||
|
*
|
||||||
|
* @returns true if stopped
|
||||||
|
*/
|
||||||
|
bool StopIfEmpty();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Waits for data to become available. This will wait for aTimeout duration
|
||||||
|
* aRetryCount number of times, checking to see if the other side is closed in
|
||||||
|
* between each one.
|
||||||
|
*
|
||||||
|
* @param aTimeout duration to wait
|
||||||
|
* @param aRetryCount number of times to retry
|
||||||
|
* @returns true if data is available to read.
|
||||||
|
*/
|
||||||
|
bool WaitForDataToRead(TimeDuration aTimeout, int32_t aRetryCount);
|
||||||
|
|
||||||
|
uint8_t ReadNextEvent();
|
||||||
|
|
||||||
|
void read(char* const aOut, const size_t aSize) final;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a checkpoint event to the buffer.
|
||||||
|
*
|
||||||
|
* @returns the write count after the checkpoint has been written
|
||||||
|
*/
|
||||||
|
uint32_t CreateCheckpoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits until the given checkpoint has been read from the buffer.
|
||||||
|
*
|
||||||
|
* @params aCheckpoint the checkpoint to wait for
|
||||||
|
* @params aTimeout duration to wait while reader is not active
|
||||||
|
* @returns true if the checkpoint was reached, false if the reader is closed
|
||||||
|
* or we timeout.
|
||||||
|
*/
|
||||||
|
bool WaitForCheckpoint(uint32_t aCheckpoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch to a different sized buffer.
|
||||||
|
*/
|
||||||
|
bool SwitchBuffer(base::ProcessId aOtherPid,
|
||||||
|
ipc::SharedMemoryBasic::Handle* aHandle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to send data back to the writer. This is done through the same shared
|
||||||
|
* memory so the writer must wait and read the response after it has submitted
|
||||||
|
* the event that uses this.
|
||||||
|
*
|
||||||
|
* @param aData the data to be written back to the writer
|
||||||
|
* @param aSize the number of chars to write
|
||||||
|
*/
|
||||||
|
void ReturnWrite(const char* aData, size_t aSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to read data sent back from the reader via ReturnWrite. This is done
|
||||||
|
* through the same shared memory so the writer must wait until all expected
|
||||||
|
* data is read before writing new events to the buffer.
|
||||||
|
*
|
||||||
|
* @param aOut the pointer to read into
|
||||||
|
* @param aSize the number of chars to read
|
||||||
|
*/
|
||||||
|
void ReturnRead(char* aOut, size_t aSize);
|
||||||
|
|
||||||
|
bool UsingLargeStream() { return mLargeStream; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool WaitForAndRecalculateAvailableSpace() final;
|
||||||
|
void UpdateWriteTotalsBy(uint32_t aCount) final;
|
||||||
|
|
||||||
|
private:
|
||||||
enum class State : uint32_t {
|
enum class State : uint32_t {
|
||||||
Processing,
|
Processing,
|
||||||
|
|
||||||
|
|
@ -49,65 +212,89 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
|
||||||
*/
|
*/
|
||||||
AboutToWait,
|
AboutToWait,
|
||||||
Waiting,
|
Waiting,
|
||||||
Paused,
|
|
||||||
Stopped,
|
Stopped,
|
||||||
Failed,
|
Failed,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Header {
|
struct ReadFooter {
|
||||||
union {
|
Atomic<uint32_t> count;
|
||||||
struct {
|
Atomic<uint32_t> returnCount;
|
||||||
Atomic<int64_t> eventCount;
|
Atomic<State> state;
|
||||||
Atomic<int64_t> writerWaitCount;
|
|
||||||
Atomic<State> writerState;
|
|
||||||
};
|
|
||||||
uint8_t padding1[64];
|
|
||||||
};
|
|
||||||
Atomic<int64_t> processedCount;
|
|
||||||
Atomic<State> readerState;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Helpers {
|
struct WriteFooter {
|
||||||
public:
|
Atomic<uint32_t> count;
|
||||||
virtual ~Helpers() = default;
|
Atomic<uint32_t> returnCount;
|
||||||
|
Atomic<uint32_t> requiredDifference;
|
||||||
virtual bool InitTranslator(const TextureType& aTextureType,
|
Atomic<State> state;
|
||||||
Handle&& aReadHandle,
|
|
||||||
nsTArray<Handle>&& aBufferHandles,
|
|
||||||
const uint64_t& aBufferSize,
|
|
||||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
|
||||||
CrossProcessSemaphoreHandle&& aWriterSem,
|
|
||||||
const bool& aUseIPDLThread) = 0;
|
|
||||||
|
|
||||||
virtual bool AddBuffer(Handle&& aBufferHandle,
|
|
||||||
const uint64_t& aBufferSize) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns true if the reader of the CanvasEventRingBuffer has permanently
|
|
||||||
* stopped processing, otherwise returns false.
|
|
||||||
*/
|
|
||||||
virtual bool ReaderClosed() = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Causes the reader to resume processing when it is in a stopped state.
|
|
||||||
*/
|
|
||||||
virtual bool RestartReader() = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool Init(TextureType aTextureType, UniquePtr<Helpers> aHelpers);
|
CanvasEventRingBuffer(const CanvasEventRingBuffer&) = delete;
|
||||||
|
void operator=(const CanvasEventRingBuffer&) = delete;
|
||||||
|
|
||||||
/**
|
void IncrementWriteCountBy(uint32_t aCount);
|
||||||
* Record an event for processing by the CanvasParent's CanvasTranslator.
|
|
||||||
* @param aEvent the event to record
|
bool WaitForReadCount(uint32_t aReadCount, TimeDuration aTimeout);
|
||||||
*/
|
|
||||||
void RecordEvent(const gfx::RecordedEvent& aEvent) final;
|
bool WaitForAndRecalculateAvailableData();
|
||||||
|
|
||||||
|
void UpdateReadTotalsBy(uint32_t aCount);
|
||||||
|
void IncrementReadCountBy(uint32_t aCount);
|
||||||
|
|
||||||
|
void CheckAndSignalReader();
|
||||||
|
|
||||||
|
void CheckAndSignalWriter();
|
||||||
|
|
||||||
|
uint32_t WaitForBytesToWrite();
|
||||||
|
|
||||||
|
uint32_t WaitForBytesToRead();
|
||||||
|
|
||||||
|
uint32_t StreamSize();
|
||||||
|
|
||||||
|
RefPtr<ipc::SharedMemoryBasic> mSharedMemory;
|
||||||
|
UniquePtr<CrossProcessSemaphore> mReaderSemaphore;
|
||||||
|
UniquePtr<CrossProcessSemaphore> mWriterSemaphore;
|
||||||
|
UniquePtr<WriterServices> mWriterServices;
|
||||||
|
UniquePtr<ReaderServices> mReaderServices;
|
||||||
|
char* mBuf = nullptr;
|
||||||
|
uint32_t mOurCount = 0;
|
||||||
|
WriteFooter* mWrite = nullptr;
|
||||||
|
ReadFooter* mRead = nullptr;
|
||||||
|
bool mGood = false;
|
||||||
|
bool mLargeStream = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate {
|
||||||
|
public:
|
||||||
|
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(CanvasDrawEventRecorder, final)
|
||||||
|
explicit CanvasDrawEventRecorder(){};
|
||||||
|
|
||||||
|
bool Init(base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aHandle,
|
||||||
|
CrossProcessSemaphoreHandle* aReaderSem,
|
||||||
|
CrossProcessSemaphoreHandle* aWriterSem,
|
||||||
|
UniquePtr<CanvasEventRingBuffer::WriterServices> aWriterServices) {
|
||||||
|
return mOutputStream.InitWriter(aOtherPid, aHandle, aReaderSem, aWriterSem,
|
||||||
|
std::move(aWriterServices));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordEvent(const gfx::RecordedEvent& aEvent) final {
|
||||||
|
if (!mOutputStream.good()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aEvent.RecordToStream(mOutputStream);
|
||||||
|
}
|
||||||
|
|
||||||
void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface,
|
void StoreSourceSurfaceRecording(gfx::SourceSurface* aSurface,
|
||||||
const char* aReason) final;
|
const char* aReason) final;
|
||||||
|
|
||||||
void Flush() final {}
|
void Flush() final {}
|
||||||
|
|
||||||
int64_t CreateCheckpoint();
|
void ReturnRead(char* aOut, size_t aSize) {
|
||||||
|
mOutputStream.ReturnRead(aOut, aSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CreateCheckpoint() { return mOutputStream.CreateCheckpoint(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until the given checkpoint has been read by the translator.
|
* Waits until the given checkpoint has been read by the translator.
|
||||||
|
|
@ -116,60 +303,19 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
|
||||||
* @returns true if the checkpoint was reached, false if the reader is closed
|
* @returns true if the checkpoint was reached, false if the reader is closed
|
||||||
* or we timeout.
|
* or we timeout.
|
||||||
*/
|
*/
|
||||||
bool WaitForCheckpoint(int64_t aCheckpoint);
|
bool WaitForCheckpoint(uint32_t aCheckpoint) {
|
||||||
|
return mOutputStream.WaitForCheckpoint(aCheckpoint);
|
||||||
|
}
|
||||||
|
|
||||||
TextureType GetTextureType() { return mTextureType; }
|
bool UsingLargeStream() { return mOutputStream.UsingLargeStream(); }
|
||||||
|
|
||||||
void DropFreeBuffers();
|
bool SwitchBuffer(base::ProcessId aOtherPid,
|
||||||
|
ipc::SharedMemoryBasic::Handle* aHandle) {
|
||||||
protected:
|
return mOutputStream.SwitchBuffer(aOtherPid, aHandle);
|
||||||
gfx::ContiguousBuffer& GetContiguousBuffer(size_t aSize) final;
|
}
|
||||||
|
|
||||||
void IncrementEventCount() final;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void WriteInternalEvent(EventType aEventType);
|
CanvasEventRingBuffer mOutputStream;
|
||||||
|
|
||||||
void CheckAndSignalReader();
|
|
||||||
|
|
||||||
size_t mDefaultBufferSize;
|
|
||||||
uint32_t mMaxSpinCount;
|
|
||||||
uint32_t mDropBufferLimit;
|
|
||||||
uint32_t mDropBufferOnZero;
|
|
||||||
|
|
||||||
UniquePtr<Helpers> mHelpers;
|
|
||||||
|
|
||||||
TextureType mTextureType = TextureType::Unknown;
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> mHeaderShmem;
|
|
||||||
Header* mHeader = nullptr;
|
|
||||||
|
|
||||||
struct CanvasBuffer : public gfx::ContiguousBuffer {
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
|
||||||
|
|
||||||
CanvasBuffer() : ContiguousBuffer(nullptr) {}
|
|
||||||
|
|
||||||
explicit CanvasBuffer(RefPtr<ipc::SharedMemoryBasic>&& aShmem)
|
|
||||||
: ContiguousBuffer(static_cast<char*>(aShmem->memory()),
|
|
||||||
aShmem->Size()),
|
|
||||||
shmem(std::move(aShmem)) {}
|
|
||||||
|
|
||||||
size_t Capacity() { return shmem->Size(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RecycledBuffer {
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
|
||||||
int64_t eventCount = 0;
|
|
||||||
explicit RecycledBuffer(RefPtr<ipc::SharedMemoryBasic>&& aShmem,
|
|
||||||
int64_t aEventCount)
|
|
||||||
: shmem(std::move(aShmem)), eventCount(aEventCount) {}
|
|
||||||
size_t Capacity() { return shmem->Size(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
CanvasBuffer mCurrentBuffer;
|
|
||||||
std::queue<RecycledBuffer> mRecycledBuffers;
|
|
||||||
|
|
||||||
UniquePtr<CrossProcessSemaphore> mWriterSemaphore;
|
|
||||||
UniquePtr<CrossProcessSemaphore> mReaderSemaphore;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
|
|
|
||||||
|
|
@ -40,11 +40,6 @@ const EventType REMOVE_SURFACE_ALIAS = EventType(EventType::LAST + 9);
|
||||||
const EventType DEVICE_CHANGE_ACKNOWLEDGED = EventType(EventType::LAST + 10);
|
const EventType DEVICE_CHANGE_ACKNOWLEDGED = EventType(EventType::LAST + 10);
|
||||||
const EventType NEXT_TEXTURE_ID = EventType(EventType::LAST + 11);
|
const EventType NEXT_TEXTURE_ID = EventType(EventType::LAST + 11);
|
||||||
const EventType TEXTURE_DESTRUCTION = EventType(EventType::LAST + 12);
|
const EventType TEXTURE_DESTRUCTION = EventType(EventType::LAST + 12);
|
||||||
const EventType CHECKPOINT = EventType(EventType::LAST + 13);
|
|
||||||
const EventType PAUSE_TRANSLATION = EventType(EventType::LAST + 14);
|
|
||||||
const EventType RECYCLE_BUFFER = EventType(EventType::LAST + 15);
|
|
||||||
const EventType DROP_BUFFER = EventType(EventType::LAST + 16);
|
|
||||||
const EventType LAST_CANVAS_EVENT_TYPE = DROP_BUFFER;
|
|
||||||
|
|
||||||
class RecordedCanvasBeginTransaction final
|
class RecordedCanvasBeginTransaction final
|
||||||
: public RecordedEventDerived<RecordedCanvasBeginTransaction> {
|
: public RecordedEventDerived<RecordedCanvasBeginTransaction> {
|
||||||
|
|
@ -349,7 +344,31 @@ class RecordedGetDataForSurface final
|
||||||
|
|
||||||
inline bool RecordedGetDataForSurface::PlayCanvasEvent(
|
inline bool RecordedGetDataForSurface::PlayCanvasEvent(
|
||||||
CanvasTranslator* aTranslator) const {
|
CanvasTranslator* aTranslator) const {
|
||||||
aTranslator->GetDataSurface(mSurface.mLongPtr);
|
gfx::SourceSurface* surface = aTranslator->LookupSourceSurface(mSurface);
|
||||||
|
if (!surface) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniquePtr<gfx::DataSourceSurface::ScopedMap> map =
|
||||||
|
aTranslator->GetPreparedMap(mSurface);
|
||||||
|
if (!map) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t dataFormatWidth =
|
||||||
|
surface->GetSize().width * BytesPerPixel(surface->GetFormat());
|
||||||
|
int32_t srcStride = map->GetStride();
|
||||||
|
if (dataFormatWidth > srcStride) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* src = reinterpret_cast<char*>(map->GetData());
|
||||||
|
char* endSrc = src + (map->GetSurface()->GetSize().height * srcStride);
|
||||||
|
while (src < endSrc) {
|
||||||
|
aTranslator->ReturnWrite(src, dataFormatWidth);
|
||||||
|
src += srcStride;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -557,87 +576,6 @@ RecordedTextureDestruction::RecordedTextureDestruction(S& aStream)
|
||||||
ReadElement(aStream, mTextureId);
|
ReadElement(aStream, mTextureId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class RecordedCheckpoint final
|
|
||||||
: public RecordedEventDerived<RecordedCheckpoint> {
|
|
||||||
public:
|
|
||||||
RecordedCheckpoint() : RecordedEventDerived(CHECKPOINT) {}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
MOZ_IMPLICIT RecordedCheckpoint(S& aStream)
|
|
||||||
: RecordedEventDerived(CHECKPOINT) {}
|
|
||||||
|
|
||||||
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const {
|
|
||||||
aTranslator->CheckpointReached();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
void Record(S& aStream) const {}
|
|
||||||
|
|
||||||
std::string GetName() const final { return "RecordedCheckpoint"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class RecordedPauseTranslation final
|
|
||||||
: public RecordedEventDerived<RecordedPauseTranslation> {
|
|
||||||
public:
|
|
||||||
RecordedPauseTranslation() : RecordedEventDerived(PAUSE_TRANSLATION) {}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
MOZ_IMPLICIT RecordedPauseTranslation(S& aStream)
|
|
||||||
: RecordedEventDerived(PAUSE_TRANSLATION) {}
|
|
||||||
|
|
||||||
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const {
|
|
||||||
aTranslator->PauseTranslation();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
void Record(S& aStream) const {}
|
|
||||||
|
|
||||||
std::string GetName() const final { return "RecordedPauseTranslation"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class RecordedRecycleBuffer final
|
|
||||||
: public RecordedEventDerived<RecordedRecycleBuffer> {
|
|
||||||
public:
|
|
||||||
RecordedRecycleBuffer() : RecordedEventDerived(RECYCLE_BUFFER) {}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
MOZ_IMPLICIT RecordedRecycleBuffer(S& aStream)
|
|
||||||
: RecordedEventDerived(RECYCLE_BUFFER) {}
|
|
||||||
|
|
||||||
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const {
|
|
||||||
aTranslator->RecycleBuffer();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
void Record(S& aStream) const {}
|
|
||||||
|
|
||||||
std::string GetName() const final { return "RecordedNextBuffer"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class RecordedDropBuffer final
|
|
||||||
: public RecordedEventDerived<RecordedDropBuffer> {
|
|
||||||
public:
|
|
||||||
RecordedDropBuffer() : RecordedEventDerived(DROP_BUFFER) {}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
MOZ_IMPLICIT RecordedDropBuffer(S& aStream)
|
|
||||||
: RecordedEventDerived(DROP_BUFFER) {}
|
|
||||||
|
|
||||||
bool PlayCanvasEvent(CanvasTranslator* aTranslator) const {
|
|
||||||
// Use the next buffer without recycling which drops the current buffer.
|
|
||||||
aTranslator->NextBuffer();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class S>
|
|
||||||
void Record(S& aStream) const {}
|
|
||||||
|
|
||||||
std::string GetName() const final { return "RecordedDropAndMoveNextBuffer"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FOR_EACH_CANVAS_EVENT(f) \
|
#define FOR_EACH_CANVAS_EVENT(f) \
|
||||||
f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \
|
f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \
|
||||||
f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction); \
|
f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction); \
|
||||||
|
|
@ -651,11 +589,7 @@ class RecordedDropBuffer final
|
||||||
f(REMOVE_SURFACE_ALIAS, RecordedRemoveSurfaceAlias); \
|
f(REMOVE_SURFACE_ALIAS, RecordedRemoveSurfaceAlias); \
|
||||||
f(DEVICE_CHANGE_ACKNOWLEDGED, RecordedDeviceChangeAcknowledged); \
|
f(DEVICE_CHANGE_ACKNOWLEDGED, RecordedDeviceChangeAcknowledged); \
|
||||||
f(NEXT_TEXTURE_ID, RecordedNextTextureId); \
|
f(NEXT_TEXTURE_ID, RecordedNextTextureId); \
|
||||||
f(TEXTURE_DESTRUCTION, RecordedTextureDestruction); \
|
f(TEXTURE_DESTRUCTION, RecordedTextureDestruction);
|
||||||
f(CHECKPOINT, RecordedCheckpoint); \
|
|
||||||
f(PAUSE_TRANSLATION, RecordedPauseTranslation); \
|
|
||||||
f(RECYCLE_BUFFER, RecordedRecycleBuffer); \
|
|
||||||
f(DROP_BUFFER, RecordedDropBuffer);
|
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ RecordedTextureData::RecordedTextureData(
|
||||||
already_AddRefed<CanvasChild> aCanvasChild, gfx::IntSize aSize,
|
already_AddRefed<CanvasChild> aCanvasChild, gfx::IntSize aSize,
|
||||||
gfx::SurfaceFormat aFormat, TextureType aTextureType)
|
gfx::SurfaceFormat aFormat, TextureType aTextureType)
|
||||||
: mCanvasChild(aCanvasChild), mSize(aSize), mFormat(aFormat) {
|
: mCanvasChild(aCanvasChild), mSize(aSize), mFormat(aFormat) {
|
||||||
mCanvasChild->EnsureRecorder(aSize, aFormat, aTextureType);
|
mCanvasChild->EnsureRecorder(aTextureType);
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordedTextureData::~RecordedTextureData() {
|
RecordedTextureData::~RecordedTextureData() {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
#include "mozilla/ipc/FileDescriptor.h"
|
#include "mozilla/ipc/FileDescriptor.h"
|
||||||
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
|
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
|
||||||
#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
|
#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
|
||||||
#include "mozilla/layers/LayersMessages.h"
|
|
||||||
#include "mozilla/layers/LayersSurfaces.h"
|
#include "mozilla/layers/LayersSurfaces.h"
|
||||||
#include "mozilla/layers/TextureSourceProvider.h"
|
#include "mozilla/layers/TextureSourceProvider.h"
|
||||||
#include "mozilla/mozalloc.h" // for operator delete
|
#include "mozilla/mozalloc.h" // for operator delete
|
||||||
|
|
|
||||||
|
|
@ -15,42 +15,21 @@
|
||||||
#include "mozilla/ipc/Endpoint.h"
|
#include "mozilla/ipc/Endpoint.h"
|
||||||
#include "mozilla/ipc/ProcessChild.h"
|
#include "mozilla/ipc/ProcessChild.h"
|
||||||
#include "mozilla/layers/CanvasDrawEventRecorder.h"
|
#include "mozilla/layers/CanvasDrawEventRecorder.h"
|
||||||
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
|
||||||
#include "mozilla/Maybe.h"
|
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
#include "RecordedCanvasEventImpl.h"
|
#include "RecordedCanvasEventImpl.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
||||||
class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
|
/* static */ bool CanvasChild::mInForeground = true;
|
||||||
|
|
||||||
|
class RingBufferWriterServices final
|
||||||
|
: public CanvasEventRingBuffer::WriterServices {
|
||||||
public:
|
public:
|
||||||
explicit RecorderHelpers(const RefPtr<CanvasChild>& aCanvasChild)
|
explicit RingBufferWriterServices(RefPtr<CanvasChild> aCanvasChild)
|
||||||
: mCanvasChild(aCanvasChild) {}
|
: mCanvasChild(aCanvasChild) {}
|
||||||
|
|
||||||
~RecorderHelpers() override = default;
|
~RingBufferWriterServices() override = default;
|
||||||
|
|
||||||
bool InitTranslator(const TextureType& aTextureType, Handle&& aReadHandle,
|
|
||||||
nsTArray<Handle>&& aBufferHandles,
|
|
||||||
const uint64_t& aBufferSize,
|
|
||||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
|
||||||
CrossProcessSemaphoreHandle&& aWriterSem,
|
|
||||||
const bool& aUseIPDLThread) override {
|
|
||||||
if (!mCanvasChild) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mCanvasChild->SendInitTranslator(
|
|
||||||
aTextureType, std::move(aReadHandle), std::move(aBufferHandles),
|
|
||||||
aBufferSize, std::move(aReaderSem), std::move(aWriterSem),
|
|
||||||
aUseIPDLThread);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddBuffer(Handle&& aBufferHandle, const uint64_t& aBufferSize) override {
|
|
||||||
if (!mCanvasChild) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mCanvasChild->SendAddBuffer(std::move(aBufferHandle), aBufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReaderClosed() override {
|
bool ReaderClosed() override {
|
||||||
if (!mCanvasChild) {
|
if (!mCanvasChild) {
|
||||||
|
|
@ -59,11 +38,11 @@ class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
|
||||||
return !mCanvasChild->CanSend() || ipc::ProcessChild::ExpectingShutdown();
|
return !mCanvasChild->CanSend() || ipc::ProcessChild::ExpectingShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestartReader() override {
|
void ResumeReader() override {
|
||||||
if (!mCanvasChild) {
|
if (!mCanvasChild) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
return mCanvasChild->SendRestartTranslation();
|
mCanvasChild->ResumeTranslation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -176,24 +155,45 @@ ipc::IPCResult CanvasChild::RecvDeactivate() {
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
void CanvasChild::EnsureRecorder(TextureType aTextureType) {
|
||||||
TextureType aTextureType) {
|
|
||||||
if (!mRecorder) {
|
if (!mRecorder) {
|
||||||
auto recorder = MakeRefPtr<CanvasDrawEventRecorder>();
|
MOZ_ASSERT(mTextureType == TextureType::Unknown);
|
||||||
if (!recorder->Init(aTextureType, MakeUnique<RecorderHelpers>(this))) {
|
mTextureType = aTextureType;
|
||||||
|
mRecorder = MakeAndAddRef<CanvasDrawEventRecorder>();
|
||||||
|
SharedMemoryBasic::Handle handle;
|
||||||
|
CrossProcessSemaphoreHandle readerSem;
|
||||||
|
CrossProcessSemaphoreHandle writerSem;
|
||||||
|
if (!mRecorder->Init(OtherPid(), &handle, &readerSem, &writerSem,
|
||||||
|
MakeUnique<RingBufferWriterServices>(this))) {
|
||||||
|
mRecorder = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRecorder = recorder.forget();
|
if (CanSend()) {
|
||||||
|
Unused << SendInitTranslator(mTextureType, std::move(handle),
|
||||||
|
std::move(readerSem), std::move(writerSem),
|
||||||
|
/* aUseIPDLThread */ false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_RELEASE_ASSERT(mRecorder->GetTextureType() == aTextureType,
|
MOZ_RELEASE_ASSERT(mTextureType == aTextureType,
|
||||||
"We only support one remote TextureType currently.");
|
"We only support one remote TextureType currently.");
|
||||||
|
|
||||||
EnsureDataSurfaceShmem(aSize, aFormat);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {}
|
void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
|
||||||
|
// Explicitly drop our reference to the recorder, because it holds a reference
|
||||||
|
// to us via the ResumeTranslation callback.
|
||||||
|
if (mRecorder) {
|
||||||
|
mRecorder->DetachResources();
|
||||||
|
mRecorder = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanvasChild::ResumeTranslation() {
|
||||||
|
if (CanSend()) {
|
||||||
|
SendResumeTranslation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CanvasChild::Destroy() {
|
void CanvasChild::Destroy() {
|
||||||
if (CanSend()) {
|
if (CanSend()) {
|
||||||
|
|
@ -202,11 +202,24 @@ void CanvasChild::Destroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::OnTextureWriteLock() {
|
void CanvasChild::OnTextureWriteLock() {
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mHasOutstandingWriteLock = true;
|
mHasOutstandingWriteLock = true;
|
||||||
mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint();
|
mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::OnTextureForwarded() {
|
void CanvasChild::OnTextureForwarded() {
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're forwarding textures, so we must be in the foreground.
|
||||||
|
mInForeground = true;
|
||||||
|
|
||||||
if (mHasOutstandingWriteLock) {
|
if (mHasOutstandingWriteLock) {
|
||||||
mRecorder->RecordEvent(RecordedCanvasFlush());
|
mRecorder->RecordEvent(RecordedCanvasFlush());
|
||||||
if (!mRecorder->WaitForCheckpoint(mLastWriteLockCheckpoint)) {
|
if (!mRecorder->WaitForCheckpoint(mLastWriteLockCheckpoint)) {
|
||||||
|
|
@ -225,8 +238,24 @@ void CanvasChild::OnTextureForwarded() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanvasChild::EnsureBeginTransaction() {
|
bool CanvasChild::EnsureBeginTransaction() {
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mIsInTransaction) {
|
if (!mIsInTransaction) {
|
||||||
RecordEvent(RecordedCanvasBeginTransaction());
|
// Ensure we are using a large buffer when in the foreground and small one
|
||||||
|
// in the background.
|
||||||
|
if (mInForeground != mRecorder->UsingLargeStream()) {
|
||||||
|
SharedMemoryBasic::Handle handle;
|
||||||
|
if (!mRecorder->SwitchBuffer(OtherPid(), &handle) ||
|
||||||
|
!SendNewBuffer(std::move(handle))) {
|
||||||
|
mRecorder = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mRecorder->RecordEvent(RecordedCanvasBeginTransaction());
|
||||||
mIsInTransaction = true;
|
mIsInTransaction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,33 +263,25 @@ bool CanvasChild::EnsureBeginTransaction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::EndTransaction() {
|
void CanvasChild::EndTransaction() {
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mIsInTransaction) {
|
if (mIsInTransaction) {
|
||||||
RecordEvent(RecordedCanvasEndTransaction());
|
mRecorder->RecordEvent(RecordedCanvasEndTransaction());
|
||||||
mIsInTransaction = false;
|
mIsInTransaction = false;
|
||||||
mDormant = false;
|
|
||||||
} else {
|
|
||||||
// Schedule to drop free buffers if we have no non-empty transactions.
|
|
||||||
if (!mDormant) {
|
|
||||||
mDormant = true;
|
|
||||||
NS_DelayedDispatchToCurrentThread(
|
|
||||||
NewRunnableMethod("CanvasChild::DropFreeBuffersWhenDormant", this,
|
|
||||||
&CanvasChild::DropFreeBuffersWhenDormant),
|
|
||||||
StaticPrefs::gfx_canvas_remote_drop_buffer_milliseconds());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
++mTransactionsSinceGetDataSurface;
|
++mTransactionsSinceGetDataSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::DropFreeBuffersWhenDormant() {
|
/* static */
|
||||||
// Drop any free buffers if we have not had any non-empty transactions.
|
void CanvasChild::ClearCachedResources() {
|
||||||
if (mDormant) {
|
// We use this as a proxy for the tab being in the backgound.
|
||||||
mRecorder->DropFreeBuffers();
|
mInForeground = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::ClearCachedResources() { mRecorder->DropFreeBuffers(); }
|
|
||||||
|
|
||||||
bool CanvasChild::ShouldBeCleanedUp() const {
|
bool CanvasChild::ShouldBeCleanedUp() const {
|
||||||
// Always return true if we've been deactivated.
|
// Always return true if we've been deactivated.
|
||||||
if (Deactivated()) {
|
if (Deactivated()) {
|
||||||
|
|
@ -268,11 +289,16 @@ bool CanvasChild::ShouldBeCleanedUp() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can only be cleaned up if nothing else references our recorder.
|
// We can only be cleaned up if nothing else references our recorder.
|
||||||
return mRecorder->hasOneRef();
|
return !mRecorder || mRecorder->hasOneRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
|
already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
|
||||||
gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
|
gfx::IntSize aSize, gfx::SurfaceFormat aFormat) {
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
|
RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
|
||||||
gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
|
gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
|
||||||
RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
|
RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
|
||||||
|
|
@ -280,34 +306,6 @@ already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
|
||||||
return dt.forget();
|
return dt.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanvasChild::EnsureDataSurfaceShmem(gfx::IntSize aSize,
|
|
||||||
gfx::SurfaceFormat aFormat) {
|
|
||||||
size_t dataFormatWidth = aSize.width * BytesPerPixel(aFormat);
|
|
||||||
size_t sizeRequired =
|
|
||||||
ipc::SharedMemory::PageAlignedSize(dataFormatWidth * aSize.height);
|
|
||||||
if (!mDataSurfaceShmem || mDataSurfaceShmem->Size() < sizeRequired) {
|
|
||||||
RecordEvent(RecordedPauseTranslation());
|
|
||||||
auto dataSurfaceShmem = MakeRefPtr<ipc::SharedMemoryBasic>();
|
|
||||||
if (!dataSurfaceShmem->Create(sizeRequired) ||
|
|
||||||
!dataSurfaceShmem->Map(sizeRequired)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto shmemHandle = dataSurfaceShmem->TakeHandle();
|
|
||||||
if (!shmemHandle) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SendSetDataSurfaceBuffer(std::move(shmemHandle), sizeRequired)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDataSurfaceShmem = dataSurfaceShmem.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
||||||
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
if (!mRecorder) {
|
if (!mRecorder) {
|
||||||
|
|
@ -317,15 +315,16 @@ void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
||||||
mRecorder->RecordEvent(aEvent);
|
mRecorder->RecordEvent(aEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t CanvasChild::CreateCheckpoint() {
|
|
||||||
return mRecorder->CreateCheckpoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
|
already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
|
||||||
const gfx::SourceSurface* aSurface) {
|
const gfx::SourceSurface* aSurface) {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||||
MOZ_ASSERT(aSurface);
|
MOZ_ASSERT(aSurface);
|
||||||
|
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// mTransactionsSinceGetDataSurface is used to determine if we want to prepare
|
// mTransactionsSinceGetDataSurface is used to determine if we want to prepare
|
||||||
// a DataSourceSurface in the GPU process up front at the end of the
|
// a DataSourceSurface in the GPU process up front at the end of the
|
||||||
// transaction, but that only makes sense if the canvas JS is requesting data
|
// transaction, but that only makes sense if the canvas JS is requesting data
|
||||||
|
|
@ -338,56 +337,43 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RecordEvent(RecordedPrepareDataForSurface(aSurface));
|
mRecorder->RecordEvent(RecordedPrepareDataForSurface(aSurface));
|
||||||
|
uint32_t checkpoint = mRecorder->CreateCheckpoint();
|
||||||
|
|
||||||
gfx::IntSize ssSize = aSurface->GetSize();
|
gfx::IntSize ssSize = aSurface->GetSize();
|
||||||
gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
|
gfx::SurfaceFormat ssFormat = aSurface->GetFormat();
|
||||||
if (!EnsureDataSurfaceShmem(ssSize, ssFormat)) {
|
size_t dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat);
|
||||||
|
RefPtr<gfx::DataSourceSurface> dataSurface =
|
||||||
|
gfx::Factory::CreateDataSourceSurfaceWithStride(ssSize, ssFormat,
|
||||||
|
dataFormatWidth);
|
||||||
|
if (!dataSurface) {
|
||||||
|
gfxWarning() << "Failed to create DataSourceSurface.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
gfx::DataSourceSurface::ScopedMap map(dataSurface,
|
||||||
|
gfx::DataSourceSurface::READ_WRITE);
|
||||||
|
char* dest = reinterpret_cast<char*>(map.GetData());
|
||||||
|
if (!mRecorder->WaitForCheckpoint(checkpoint)) {
|
||||||
|
gfxWarning() << "Timed out preparing data for DataSourceSurface.";
|
||||||
|
return dataSurface.forget();
|
||||||
|
}
|
||||||
|
|
||||||
RecordEvent(RecordedGetDataForSurface(aSurface));
|
mRecorder->RecordEvent(RecordedGetDataForSurface(aSurface));
|
||||||
auto checkpoint = CreateCheckpoint();
|
mRecorder->ReturnRead(dest, ssSize.height * dataFormatWidth);
|
||||||
struct DataShmemHolder {
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
|
||||||
RefPtr<CanvasChild> canvasChild;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto* data = static_cast<uint8_t*>(mDataSurfaceShmem->memory());
|
|
||||||
auto* closure = new DataShmemHolder{mDataSurfaceShmem.forget(), this};
|
|
||||||
auto dataFormatWidth = ssSize.width * BytesPerPixel(ssFormat);
|
|
||||||
|
|
||||||
RefPtr<gfx::DataSourceSurface> dataSurface =
|
|
||||||
gfx::Factory::CreateWrappingDataSourceSurface(
|
|
||||||
data, dataFormatWidth, ssSize, ssFormat,
|
|
||||||
[](void* aClosure) {
|
|
||||||
auto* shmemHolder = static_cast<DataShmemHolder*>(aClosure);
|
|
||||||
shmemHolder->canvasChild->ReturnDataSurfaceShmem(
|
|
||||||
shmemHolder->shmem.forget());
|
|
||||||
delete shmemHolder;
|
|
||||||
},
|
|
||||||
closure);
|
|
||||||
|
|
||||||
mRecorder->WaitForCheckpoint(checkpoint);
|
|
||||||
return dataSurface.forget();
|
return dataSurface.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
|
already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
|
||||||
const RefPtr<gfx::SourceSurface>& aSurface) {
|
const RefPtr<gfx::SourceSurface>& aSurface) {
|
||||||
if (!aSurface) {
|
MOZ_ASSERT(aSurface);
|
||||||
|
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||||
|
if (!mRecorder) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
|
return MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasChild::ReturnDataSurfaceShmem(
|
|
||||||
already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem) {
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> data = aDataSurfaceShmem;
|
|
||||||
if (!mDataSurfaceShmem || data->Size() > mDataSurfaceShmem->Size()) {
|
|
||||||
mDataSurfaceShmem = data.forget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@
|
||||||
#include "mozilla/layers/PCanvasChild.h"
|
#include "mozilla/layers/PCanvasChild.h"
|
||||||
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
|
#include "nsRefPtrHashtable.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
|
@ -36,7 +38,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||||
/**
|
/**
|
||||||
* Release resources until they are next required.
|
* Release resources until they are next required.
|
||||||
*/
|
*/
|
||||||
void ClearCachedResources();
|
static void ClearCachedResources();
|
||||||
|
|
||||||
ipc::IPCResult RecvNotifyDeviceChanged();
|
ipc::IPCResult RecvNotifyDeviceChanged();
|
||||||
|
|
||||||
|
|
@ -47,8 +49,12 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||||
*
|
*
|
||||||
* @params aTextureType the TextureType to create in the CanvasTranslator.
|
* @params aTextureType the TextureType to create in the CanvasTranslator.
|
||||||
*/
|
*/
|
||||||
void EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
void EnsureRecorder(TextureType aTextureType);
|
||||||
TextureType aTextureType);
|
|
||||||
|
/**
|
||||||
|
* Send a messsage to our CanvasParent to resume translation.
|
||||||
|
*/
|
||||||
|
void ResumeTranslation();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up IPDL actor.
|
* Clean up IPDL actor.
|
||||||
|
|
@ -105,8 +111,6 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||||
*/
|
*/
|
||||||
void RecordEvent(const gfx::RecordedEvent& aEvent);
|
void RecordEvent(const gfx::RecordedEvent& aEvent);
|
||||||
|
|
||||||
int64_t CreateCheckpoint();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrap the given surface, so that we can provide a DataSourceSurface if
|
* Wrap the given surface, so that we can provide a DataSourceSurface if
|
||||||
* required.
|
* required.
|
||||||
|
|
@ -135,26 +139,18 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||||
|
|
||||||
~CanvasChild() final;
|
~CanvasChild() final;
|
||||||
|
|
||||||
bool EnsureDataSurfaceShmem(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
|
|
||||||
|
|
||||||
void ReturnDataSurfaceShmem(
|
|
||||||
already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem);
|
|
||||||
|
|
||||||
void DropFreeBuffersWhenDormant();
|
|
||||||
|
|
||||||
static const uint32_t kCacheDataSurfaceThreshold = 10;
|
static const uint32_t kCacheDataSurfaceThreshold = 10;
|
||||||
|
|
||||||
static bool mDeactivated;
|
static bool mDeactivated;
|
||||||
|
static bool mInForeground;
|
||||||
|
|
||||||
RefPtr<CanvasDrawEventRecorder> mRecorder;
|
RefPtr<CanvasDrawEventRecorder> mRecorder;
|
||||||
|
TextureType mTextureType = TextureType::Unknown;
|
||||||
RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;
|
uint32_t mLastWriteLockCheckpoint = 0;
|
||||||
int64_t mLastWriteLockCheckpoint = 0;
|
|
||||||
uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold;
|
uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold;
|
||||||
std::vector<RefPtr<gfx::SourceSurface>> mLastTransactionExternalSurfaces;
|
std::vector<RefPtr<gfx::SourceSurface>> mLastTransactionExternalSurfaces;
|
||||||
bool mIsInTransaction = false;
|
bool mIsInTransaction = false;
|
||||||
bool mHasOutstandingWriteLock = false;
|
bool mHasOutstandingWriteLock = false;
|
||||||
bool mDormant = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace layers
|
} // namespace layers
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,8 @@
|
||||||
#include "mozilla/gfx/GPUParent.h"
|
#include "mozilla/gfx/GPUParent.h"
|
||||||
#include "mozilla/gfx/Logging.h"
|
#include "mozilla/gfx/Logging.h"
|
||||||
#include "mozilla/ipc/Endpoint.h"
|
#include "mozilla/ipc/Endpoint.h"
|
||||||
#include "mozilla/layers/CanvasTranslator.h"
|
|
||||||
#include "mozilla/layers/SharedSurfacesParent.h"
|
#include "mozilla/layers/SharedSurfacesParent.h"
|
||||||
#include "mozilla/layers/TextureClient.h"
|
#include "mozilla/layers/TextureClient.h"
|
||||||
#include "mozilla/StaticPrefs_gfx.h"
|
|
||||||
#include "mozilla/SyncRunnable.h"
|
#include "mozilla/SyncRunnable.h"
|
||||||
#include "mozilla/TaskQueue.h"
|
#include "mozilla/TaskQueue.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
|
@ -31,6 +29,25 @@
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
||||||
|
// When in a transaction we wait for a short time because we're expecting more
|
||||||
|
// events from the content process. We don't want to wait for too long in case
|
||||||
|
// other content processes are waiting for events to process.
|
||||||
|
static const TimeDuration kReadEventTimeout = TimeDuration::FromMilliseconds(5);
|
||||||
|
|
||||||
|
class RingBufferReaderServices final
|
||||||
|
: public CanvasEventRingBuffer::ReaderServices {
|
||||||
|
public:
|
||||||
|
explicit RingBufferReaderServices(RefPtr<CanvasTranslator> aCanvasTranslator)
|
||||||
|
: mCanvasTranslator(std::move(aCanvasTranslator)) {}
|
||||||
|
|
||||||
|
~RingBufferReaderServices() final = default;
|
||||||
|
|
||||||
|
bool WriterClosed() final { return !mCanvasTranslator->CanSend(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<CanvasTranslator> mCanvasTranslator;
|
||||||
|
};
|
||||||
|
|
||||||
TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
|
TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
|
||||||
const gfx::IntSize& aSize,
|
const gfx::IntSize& aSize,
|
||||||
gfx::SurfaceFormat aFormat) {
|
gfx::SurfaceFormat aFormat) {
|
||||||
|
|
@ -51,10 +68,6 @@ TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasTranslator::CanvasTranslator() {
|
CanvasTranslator::CanvasTranslator() {
|
||||||
mMaxSpinCount = StaticPrefs::gfx_canvas_remote_max_spin_count();
|
|
||||||
mNextEventTimeout = TimeDuration::FromMilliseconds(
|
|
||||||
StaticPrefs::gfx_canvas_remote_event_timeout_ms());
|
|
||||||
|
|
||||||
// Track when remote canvas has been activated.
|
// Track when remote canvas has been activated.
|
||||||
Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1);
|
Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -82,49 +95,31 @@ bool CanvasTranslator::IsInTaskQueue() const {
|
||||||
return gfx::CanvasRenderThread::IsInCanvasRenderThread();
|
return gfx::CanvasRenderThread::IsInCanvasRenderThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool CreateAndMapShmem(RefPtr<ipc::SharedMemoryBasic>& aShmem,
|
|
||||||
Handle&& aHandle,
|
|
||||||
ipc::SharedMemory::OpenRights aOpenRights,
|
|
||||||
size_t aSize) {
|
|
||||||
auto shmem = MakeRefPtr<ipc::SharedMemoryBasic>();
|
|
||||||
if (!shmem->SetHandle(std::move(aHandle), aOpenRights) ||
|
|
||||||
!shmem->Map(aSize)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
shmem->CloseHandle();
|
|
||||||
aShmem = shmem.forget();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
|
mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
|
||||||
const TextureType& aTextureType, Handle&& aReadHandle,
|
const TextureType& aTextureType,
|
||||||
nsTArray<Handle>&& aBufferHandles, uint64_t aBufferSize,
|
ipc::SharedMemoryBasic::Handle&& aReadHandle,
|
||||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
CrossProcessSemaphoreHandle&& aReaderSem,
|
||||||
CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread) {
|
CrossProcessSemaphoreHandle&& aWriterSem, const bool& aUseIPDLThread) {
|
||||||
if (mHeaderShmem) {
|
if (mStream) {
|
||||||
return IPC_FAIL(this, "RecvInitTranslator called twice.");
|
return IPC_FAIL(this, "RecvInitTranslator called twice.");
|
||||||
}
|
}
|
||||||
|
|
||||||
mTextureType = aTextureType;
|
mTextureType = aTextureType;
|
||||||
|
|
||||||
mHeaderShmem = MakeAndAddRef<ipc::SharedMemoryBasic>();
|
// We need to initialize the stream first, because it might be used to
|
||||||
if (!CreateAndMapShmem(mHeaderShmem, std::move(aReadHandle),
|
// communicate other failures back to the writer.
|
||||||
ipc::SharedMemory::RightsReadWrite, sizeof(Header))) {
|
mStream = MakeUnique<CanvasEventRingBuffer>();
|
||||||
return IPC_FAIL(this, "Failed.");
|
if (!mStream->InitReader(std::move(aReadHandle), std::move(aReaderSem),
|
||||||
|
std::move(aWriterSem),
|
||||||
|
MakeUnique<RingBufferReaderServices>(this))) {
|
||||||
|
mStream = nullptr;
|
||||||
|
return IPC_FAIL(this, "Failed to initialize ring buffer reader.");
|
||||||
}
|
}
|
||||||
|
|
||||||
mHeader = static_cast<Header*>(mHeaderShmem->memory());
|
|
||||||
|
|
||||||
mWriterSemaphore.reset(CrossProcessSemaphore::Create(std::move(aWriterSem)));
|
|
||||||
mWriterSemaphore->CloseHandle();
|
|
||||||
|
|
||||||
mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem)));
|
|
||||||
mReaderSemaphore->CloseHandle();
|
|
||||||
|
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
if (!CheckForFreshCanvasDevice(__LINE__)) {
|
if (!CheckForFreshCanvasDevice(__LINE__)) {
|
||||||
gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
|
gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
|
||||||
|
mStream = nullptr;
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -132,157 +127,56 @@ mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
|
||||||
if (!aUseIPDLThread) {
|
if (!aUseIPDLThread) {
|
||||||
mTranslationTaskQueue = gfx::CanvasRenderThread::CreateWorkerTaskQueue();
|
mTranslationTaskQueue = gfx::CanvasRenderThread::CreateWorkerTaskQueue();
|
||||||
}
|
}
|
||||||
|
return RecvResumeTranslation();
|
||||||
// Use the first buffer as our current buffer.
|
|
||||||
mDefaultBufferSize = aBufferSize;
|
|
||||||
auto handleIter = aBufferHandles.begin();
|
|
||||||
if (!CreateAndMapShmem(mCurrentShmem.shmem, std::move(*handleIter),
|
|
||||||
ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
|
|
||||||
return IPC_FAIL(this, "Failed.");
|
|
||||||
}
|
|
||||||
mCurrentMemReader = mCurrentShmem.CreateMemReader();
|
|
||||||
|
|
||||||
// Add all other buffers to our recycled CanvasShmems.
|
|
||||||
for (handleIter++; handleIter < aBufferHandles.end(); handleIter++) {
|
|
||||||
CanvasShmem newShmem;
|
|
||||||
if (!CreateAndMapShmem(newShmem.shmem, std::move(*handleIter),
|
|
||||||
ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
|
|
||||||
return IPC_FAIL(this, "Failed.");
|
|
||||||
}
|
|
||||||
mCanvasShmems.emplace(std::move(newShmem));
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::TranslateRecording",
|
|
||||||
this,
|
|
||||||
&CanvasTranslator::TranslateRecording));
|
|
||||||
return IPC_OK();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc::IPCResult CanvasTranslator::RecvRestartTranslation() {
|
ipc::IPCResult CanvasTranslator::RecvNewBuffer(
|
||||||
if (mDeactivated) {
|
ipc::SharedMemoryBasic::Handle&& aReadHandle) {
|
||||||
// The other side might have sent a message before we deactivated.
|
if (!mStream) {
|
||||||
return IPC_OK();
|
return IPC_FAIL(this, "RecvNewBuffer before RecvInitTranslator.");
|
||||||
}
|
}
|
||||||
|
// We need to set the new buffer on the translation queue to be sure that the
|
||||||
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::TranslateRecording",
|
// drop buffer event has been processed.
|
||||||
this,
|
DispatchToTaskQueue(NS_NewRunnableFunction(
|
||||||
&CanvasTranslator::TranslateRecording));
|
"CanvasTranslator SetNewBuffer",
|
||||||
|
[self = RefPtr(this), readHandle = std::move(aReadHandle)]() mutable {
|
||||||
return IPC_OK();
|
self->mStream->SetNewBuffer(std::move(readHandle));
|
||||||
|
}));
|
||||||
|
return RecvResumeTranslation();
|
||||||
}
|
}
|
||||||
|
|
||||||
ipc::IPCResult CanvasTranslator::RecvAddBuffer(
|
ipc::IPCResult CanvasTranslator::RecvResumeTranslation() {
|
||||||
ipc::SharedMemoryBasic::Handle&& aBufferHandle, uint64_t aBufferSize) {
|
if (!mStream) {
|
||||||
if (mDeactivated) {
|
return IPC_FAIL(this, "RecvResumeTranslation before RecvInitTranslator.");
|
||||||
|
}
|
||||||
|
if (CheckDeactivated()) {
|
||||||
// The other side might have sent a resume message before we deactivated.
|
// The other side might have sent a resume message before we deactivated.
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchToTaskQueue(
|
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::StartTranslation",
|
||||||
NewRunnableMethod<ipc::SharedMemoryBasic::Handle&&, size_t>(
|
this,
|
||||||
"CanvasTranslator::AddBuffer", this, &CanvasTranslator::AddBuffer,
|
&CanvasTranslator::StartTranslation));
|
||||||
std::move(aBufferHandle), aBufferSize));
|
|
||||||
|
|
||||||
return IPC_OK();
|
return IPC_OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasTranslator::AddBuffer(ipc::SharedMemoryBasic::Handle&& aBufferHandle,
|
void CanvasTranslator::StartTranslation() {
|
||||||
size_t aBufferSize) {
|
MOZ_RELEASE_ASSERT(mStream->IsValid(),
|
||||||
MOZ_ASSERT(IsInTaskQueue());
|
"StartTranslation called before buffer has been set.");
|
||||||
MOZ_RELEASE_ASSERT(mHeader->readerState == State::Paused);
|
|
||||||
|
|
||||||
// Default sized buffers will have been queued for recycling.
|
if (!TranslateRecording() && CanSend()) {
|
||||||
if (mCurrentShmem.Size() == mDefaultBufferSize) {
|
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::StartTranslation",
|
||||||
mCanvasShmems.emplace(std::move(mCurrentShmem));
|
this,
|
||||||
|
&CanvasTranslator::StartTranslation));
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasShmem newShmem;
|
// If the stream has been marked as bad and the Writer hasn't failed,
|
||||||
if (!CreateAndMapShmem(newShmem.shmem, std::move(aBufferHandle),
|
// deactivate remote canvas.
|
||||||
ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
|
if (!mStream->good() && !mStream->WriterFailed()) {
|
||||||
return;
|
Telemetry::ScalarAdd(
|
||||||
|
Telemetry::ScalarID::GFX_CANVAS_REMOTE_DEACTIVATED_BAD_STREAM, 1);
|
||||||
|
Deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
mCurrentShmem = std::move(newShmem);
|
|
||||||
mCurrentMemReader = mCurrentShmem.CreateMemReader();
|
|
||||||
|
|
||||||
TranslateRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
ipc::IPCResult CanvasTranslator::RecvSetDataSurfaceBuffer(
|
|
||||||
ipc::SharedMemoryBasic::Handle&& aBufferHandle, uint64_t aBufferSize) {
|
|
||||||
if (mDeactivated) {
|
|
||||||
// The other side might have sent a resume message before we deactivated.
|
|
||||||
return IPC_OK();
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchToTaskQueue(
|
|
||||||
NewRunnableMethod<ipc::SharedMemoryBasic::Handle&&, size_t>(
|
|
||||||
"CanvasTranslator::SetDataSurfaceBuffer", this,
|
|
||||||
&CanvasTranslator::SetDataSurfaceBuffer, std::move(aBufferHandle),
|
|
||||||
aBufferSize));
|
|
||||||
|
|
||||||
return IPC_OK();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasTranslator::SetDataSurfaceBuffer(
|
|
||||||
ipc::SharedMemoryBasic::Handle&& aBufferHandle, size_t aBufferSize) {
|
|
||||||
MOZ_ASSERT(IsInTaskQueue());
|
|
||||||
MOZ_RELEASE_ASSERT(mHeader->readerState == State::Paused);
|
|
||||||
|
|
||||||
if (!CreateAndMapShmem(mDataSurfaceShmem, std::move(aBufferHandle),
|
|
||||||
ipc::SharedMemory::RightsReadWrite, aBufferSize)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TranslateRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasTranslator::GetDataSurface(uint64_t aSurfaceRef) {
|
|
||||||
MOZ_ASSERT(IsInTaskQueue());
|
|
||||||
|
|
||||||
ReferencePtr surfaceRef = reinterpret_cast<void*>(aSurfaceRef);
|
|
||||||
gfx::SourceSurface* surface = LookupSourceSurface(surfaceRef);
|
|
||||||
if (!surface) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UniquePtr<gfx::DataSourceSurface::ScopedMap> map = GetPreparedMap(surfaceRef);
|
|
||||||
if (!map) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto dstSize = surface->GetSize();
|
|
||||||
auto srcSize = map->GetSurface()->GetSize();
|
|
||||||
int32_t dataFormatWidth = dstSize.width * BytesPerPixel(surface->GetFormat());
|
|
||||||
int32_t srcStride = map->GetStride();
|
|
||||||
if (dataFormatWidth > srcStride || srcSize != dstSize) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto requiredSize = dataFormatWidth * dstSize.height;
|
|
||||||
if (requiredSize <= 0 || size_t(requiredSize) > mDataSurfaceShmem->Size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* dst = static_cast<char*>(mDataSurfaceShmem->memory());
|
|
||||||
const char* src = reinterpret_cast<char*>(map->GetData());
|
|
||||||
const char* endSrc = src + (srcSize.height * srcStride);
|
|
||||||
while (src < endSrc) {
|
|
||||||
memcpy(dst, src, dataFormatWidth);
|
|
||||||
src += srcStride;
|
|
||||||
dst += dataFormatWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasTranslator::RecycleBuffer() {
|
|
||||||
mCanvasShmems.emplace(std::move(mCurrentShmem));
|
|
||||||
NextBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasTranslator::NextBuffer() {
|
|
||||||
mCurrentShmem = std::move(mCanvasShmems.front());
|
|
||||||
mCanvasShmems.pop();
|
|
||||||
mCurrentMemReader = mCurrentShmem.CreateMemReader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
|
void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
|
||||||
|
|
@ -302,6 +196,12 @@ void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
|
||||||
|
|
||||||
void CanvasTranslator::FinishShutdown() {
|
void CanvasTranslator::FinishShutdown() {
|
||||||
MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread());
|
MOZ_ASSERT(gfx::CanvasRenderThread::IsInCanvasRenderThread());
|
||||||
|
|
||||||
|
// mTranslationTaskQueue has shutdown we can safely drop the ring buffer to
|
||||||
|
// break the cycle caused by RingBufferReaderServices.
|
||||||
|
mStream = nullptr;
|
||||||
|
|
||||||
|
gfx::CanvasManagerParent::RemoveReplayTextures(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanvasTranslator::CheckDeactivated() {
|
bool CanvasTranslator::CheckDeactivated() {
|
||||||
|
|
@ -321,10 +221,10 @@ void CanvasTranslator::Deactivate() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mDeactivated = true;
|
mDeactivated = true;
|
||||||
mHeader->readerState = State::Failed;
|
|
||||||
|
|
||||||
// We need to tell the other side to deactivate. Make sure the stream is
|
// We need to tell the other side to deactivate. Make sure the stream is
|
||||||
// marked as bad so that the writing side won't wait for space to write.
|
// marked as bad so that the writing side won't wait for space to write.
|
||||||
|
mStream->SetIsBad();
|
||||||
gfx::CanvasRenderThread::Dispatch(
|
gfx::CanvasRenderThread::Dispatch(
|
||||||
NewRunnableMethod("CanvasTranslator::SendDeactivate", this,
|
NewRunnableMethod("CanvasTranslator::SendDeactivate", this,
|
||||||
&CanvasTranslator::SendDeactivate));
|
&CanvasTranslator::SendDeactivate));
|
||||||
|
|
@ -338,101 +238,20 @@ void CanvasTranslator::Deactivate() {
|
||||||
gfx::CanvasManagerParent::DisableRemoteCanvas();
|
gfx::CanvasManagerParent::DisableRemoteCanvas();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasTranslator::CheckAndSignalWriter() {
|
bool CanvasTranslator::TranslateRecording() {
|
||||||
do {
|
|
||||||
switch (mHeader->writerState) {
|
|
||||||
case State::Processing:
|
|
||||||
return;
|
|
||||||
case State::AboutToWait:
|
|
||||||
// The writer is making a decision about whether to wait. So, we must
|
|
||||||
// wait until it has decided to avoid races. Check if the writer is
|
|
||||||
// closed to avoid hangs.
|
|
||||||
if (!CanSend()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
case State::Waiting:
|
|
||||||
if (mHeader->processedCount >= mHeader->writerWaitCount) {
|
|
||||||
mHeader->writerState = State::Processing;
|
|
||||||
mWriterSemaphore->Signal();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
MOZ_ASSERT_UNREACHABLE("Invalid waiting state.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanvasTranslator::HasPendingEvent() {
|
|
||||||
return mHeader->processedCount < mHeader->eventCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanvasTranslator::ReadPendingEvent(EventType& aEventType) {
|
|
||||||
ReadElementConstrained(mCurrentMemReader, aEventType,
|
|
||||||
EventType::DRAWTARGETCREATION, LAST_CANVAS_EVENT_TYPE);
|
|
||||||
return mCurrentMemReader.good();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CanvasTranslator::ReadNextEvent(EventType& aEventType) {
|
|
||||||
if (mHeader->readerState == State::Paused) {
|
|
||||||
Flush();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t spinCount = mMaxSpinCount;
|
|
||||||
do {
|
|
||||||
if (HasPendingEvent()) {
|
|
||||||
return ReadPendingEvent(aEventType);
|
|
||||||
}
|
|
||||||
} while (--spinCount != 0);
|
|
||||||
|
|
||||||
Flush();
|
|
||||||
mHeader->readerState = State::AboutToWait;
|
|
||||||
if (HasPendingEvent()) {
|
|
||||||
mHeader->readerState = State::Processing;
|
|
||||||
return ReadPendingEvent(aEventType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mIsInTransaction) {
|
|
||||||
mHeader->readerState = State::Stopped;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When in a transaction we wait for a short time because we're expecting more
|
|
||||||
// events from the content process. We don't want to wait for too long in case
|
|
||||||
// other content processes are waiting for events to process.
|
|
||||||
mHeader->readerState = State::Waiting;
|
|
||||||
if (mReaderSemaphore->Wait(Some(mNextEventTimeout))) {
|
|
||||||
MOZ_RELEASE_ASSERT(HasPendingEvent());
|
|
||||||
MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing);
|
|
||||||
return ReadPendingEvent(aEventType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to use compareExchange here because the writer can change our
|
|
||||||
// state if we are waiting.
|
|
||||||
if (!mHeader->readerState.compareExchange(State::Waiting, State::Stopped)) {
|
|
||||||
MOZ_RELEASE_ASSERT(HasPendingEvent());
|
|
||||||
MOZ_RELEASE_ASSERT(mHeader->readerState == State::Processing);
|
|
||||||
// The writer has just signaled us, so consume it before returning
|
|
||||||
MOZ_ALWAYS_TRUE(mReaderSemaphore->Wait());
|
|
||||||
return ReadPendingEvent(aEventType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CanvasTranslator::TranslateRecording() {
|
|
||||||
MOZ_ASSERT(IsInTaskQueue());
|
MOZ_ASSERT(IsInTaskQueue());
|
||||||
|
|
||||||
mHeader->readerState = State::Processing;
|
if (!mStream) {
|
||||||
EventType eventType;
|
return false;
|
||||||
while (ReadNextEvent(eventType)) {
|
}
|
||||||
bool success = RecordedEvent::DoWithEvent(
|
|
||||||
mCurrentMemReader, static_cast<RecordedEvent::EventType>(eventType),
|
uint8_t eventType = mStream->ReadNextEvent();
|
||||||
|
while (mStream->good() && eventType != kDropBufferEventType) {
|
||||||
|
bool success = RecordedEvent::DoWithEventFromStream(
|
||||||
|
*mStream, static_cast<RecordedEvent::EventType>(eventType),
|
||||||
[&](RecordedEvent* recordedEvent) -> bool {
|
[&](RecordedEvent* recordedEvent) -> bool {
|
||||||
// Make sure that the whole event was read from the stream.
|
// Make sure that the whole event was read from the stream.
|
||||||
if (!mCurrentMemReader.good()) {
|
if (!mStream->good()) {
|
||||||
if (!CanSend()) {
|
if (!CanSend()) {
|
||||||
// The other side has closed only warn about read failure.
|
// The other side has closed only warn about read failure.
|
||||||
gfxWarning() << "Failed to read event type: "
|
gfxWarning() << "Failed to read event type: "
|
||||||
|
|
@ -448,8 +267,8 @@ void CanvasTranslator::TranslateRecording() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check the stream is good here or we will log the issue twice.
|
// Check the stream is good here or we will log the issue twice.
|
||||||
if (!mCurrentMemReader.good()) {
|
if (!mStream->good()) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success && !HandleExtensionEvent(eventType)) {
|
if (!success && !HandleExtensionEvent(eventType)) {
|
||||||
|
|
@ -460,16 +279,34 @@ void CanvasTranslator::TranslateRecording() {
|
||||||
} else {
|
} else {
|
||||||
gfxCriticalNote << "Failed to play canvas event type: " << eventType;
|
gfxCriticalNote << "Failed to play canvas event type: " << eventType;
|
||||||
}
|
}
|
||||||
|
if (!mStream->good()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mHeader->processedCount++;
|
if (!mIsInTransaction) {
|
||||||
|
return mStream->StopIfEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mStream->HasDataToRead()) {
|
||||||
|
// We're going to wait for the next event, so take the opportunity to
|
||||||
|
// flush the rendering.
|
||||||
|
Flush();
|
||||||
|
if (!mStream->WaitForDataToRead(kReadEventTimeout, 0)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eventType = mStream->ReadNextEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \
|
#define READ_AND_PLAY_CANVAS_EVENT_TYPE(_typeenum, _class) \
|
||||||
case _typeenum: { \
|
case _typeenum: { \
|
||||||
auto e = _class(mCurrentMemReader); \
|
auto e = _class(*mStream); \
|
||||||
if (!mCurrentMemReader.good()) { \
|
if (!mStream->good()) { \
|
||||||
if (!CanSend()) { \
|
if (!CanSend()) { \
|
||||||
/* The other side has closed only warn about read failure. */ \
|
/* The other side has closed only warn about read failure. */ \
|
||||||
gfxWarning() << "Failed to read event type: " << _typeenum; \
|
gfxWarning() << "Failed to read event type: " << _typeenum; \
|
||||||
|
|
@ -638,12 +475,6 @@ already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(
|
||||||
return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey));
|
return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); }
|
|
||||||
|
|
||||||
void CanvasTranslator::PauseTranslation() {
|
|
||||||
mHeader->readerState = State::Paused;
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops(
|
already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops(
|
||||||
gfx::GradientStop* aRawStops, uint32_t aNumStops,
|
gfx::GradientStop* aRawStops, uint32_t aNumStops,
|
||||||
gfx::ExtendMode aExtendMode) {
|
gfx::ExtendMode aExtendMode) {
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "mozilla/gfx/InlineTranslator.h"
|
#include "mozilla/gfx/InlineTranslator.h"
|
||||||
#include "mozilla/gfx/RecordedEvent.h"
|
|
||||||
#include "CanvasChild.h"
|
|
||||||
#include "mozilla/layers/CanvasDrawEventRecorder.h"
|
#include "mozilla/layers/CanvasDrawEventRecorder.h"
|
||||||
#include "mozilla/layers/LayersSurfaces.h"
|
#include "mozilla/layers/LayersSurfaces.h"
|
||||||
#include "mozilla/layers/PCanvasParent.h"
|
#include "mozilla/layers/PCanvasParent.h"
|
||||||
|
|
@ -21,8 +19,6 @@
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
using EventType = gfx::RecordedEvent::EventType;
|
|
||||||
class TaskQueue;
|
class TaskQueue;
|
||||||
|
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
@ -56,49 +52,40 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||||
* CanvasEventRingBuffer.
|
* CanvasEventRingBuffer.
|
||||||
*
|
*
|
||||||
* @param aTextureType the TextureType the translator will create
|
* @param aTextureType the TextureType the translator will create
|
||||||
* @param aHeaderHandle handle for the control header
|
* @param aReadHandle handle to the shared memory for the
|
||||||
* @param aBufferHandles handles for the initial buffers for translation
|
* CanvasEventRingBuffer
|
||||||
* @param aBufferSize size of buffers and the default size
|
|
||||||
* @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer
|
* @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer
|
||||||
* @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer
|
* @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer
|
||||||
* @param aUseIPDLThread if true, use the IPDL thread instead of the worker
|
* @param aUseIPDLThread if true, use the IPDL thread instead of the worker
|
||||||
* pool for translation requests
|
* pool for translation requests
|
||||||
*/
|
*/
|
||||||
ipc::IPCResult RecvInitTranslator(const TextureType& aTextureType,
|
ipc::IPCResult RecvInitTranslator(
|
||||||
Handle&& aReadHandle,
|
const TextureType& aTextureType,
|
||||||
nsTArray<Handle>&& aBufferHandles,
|
ipc::SharedMemoryBasic::Handle&& aReadHandle,
|
||||||
uint64_t aBufferSize,
|
CrossProcessSemaphoreHandle&& aReaderSem,
|
||||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
CrossProcessSemaphoreHandle&& aWriterSem, const bool& aUseIPDLThread);
|
||||||
CrossProcessSemaphoreHandle&& aWriterSem,
|
|
||||||
bool aUseIPDLThread);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restart the translation from a Stopped state.
|
* New buffer to resume translation after it has been stopped by writer.
|
||||||
*/
|
*/
|
||||||
ipc::IPCResult RecvRestartTranslation();
|
ipc::IPCResult RecvNewBuffer(ipc::SharedMemoryBasic::Handle&& aReadHandle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new buffer to be translated. The current buffer will be recycled if
|
* Used to tell the CanvasTranslator to start translating again after it has
|
||||||
* it is of the default size. The translation will then be restarted.
|
* stopped due to a timeout waiting for events.
|
||||||
*/
|
*/
|
||||||
ipc::IPCResult RecvAddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize);
|
ipc::IPCResult RecvResumeTranslation();
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the shared memory to be used for readback.
|
|
||||||
*/
|
|
||||||
ipc::IPCResult RecvSetDataSurfaceBuffer(Handle&& aBufferHandle,
|
|
||||||
uint64_t aBufferSize);
|
|
||||||
|
|
||||||
void ActorDestroy(ActorDestroyReason why) final;
|
void ActorDestroy(ActorDestroyReason why) final;
|
||||||
|
|
||||||
void CheckAndSignalWriter();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Translates events until no more are available or the end of a transaction
|
* Translates events until no more are available or the end of a transaction
|
||||||
* If this returns false the caller of this is responsible for re-calling
|
* If this returns false the caller of this is responsible for re-calling
|
||||||
* this function.
|
* this function.
|
||||||
|
*
|
||||||
|
* @returns true if all events are processed and false otherwise.
|
||||||
*/
|
*/
|
||||||
void TranslateRecording();
|
bool TranslateRecording();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the beginning of rendering for a transaction. While in a transaction
|
* Marks the beginning of rendering for a transaction. While in a transaction
|
||||||
|
|
@ -123,6 +110,18 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||||
*/
|
*/
|
||||||
void DeviceChangeAcknowledged();
|
void DeviceChangeAcknowledged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to send data back to the writer. This is done through the same shared
|
||||||
|
* memory so the writer must wait and read the response after it has submitted
|
||||||
|
* the event that uses this.
|
||||||
|
*
|
||||||
|
* @param aData the data to be written back to the writer
|
||||||
|
* @param aSize the number of chars to write
|
||||||
|
*/
|
||||||
|
void ReturnWrite(const char* aData, size_t aSize) {
|
||||||
|
mStream->ReturnWrite(aData, aSize);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the texture ID that will be used as a lookup for the texture created by
|
* Set the texture ID that will be used as a lookup for the texture created by
|
||||||
* the next CreateDrawTarget.
|
* the next CreateDrawTarget.
|
||||||
|
|
@ -157,10 +156,6 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||||
*/
|
*/
|
||||||
TextureData* LookupTextureData(int64_t aTextureId);
|
TextureData* LookupTextureData(int64_t aTextureId);
|
||||||
|
|
||||||
void CheckpointReached();
|
|
||||||
|
|
||||||
void PauseTranslation();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the texture and other objects associated with a texture ID.
|
* Removes the texture and other objects associated with a texture ID.
|
||||||
*
|
*
|
||||||
|
|
@ -249,24 +244,12 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||||
UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap(
|
UniquePtr<gfx::DataSourceSurface::ScopedMap> GetPreparedMap(
|
||||||
gfx::ReferencePtr aSurface);
|
gfx::ReferencePtr aSurface);
|
||||||
|
|
||||||
void RecycleBuffer();
|
|
||||||
|
|
||||||
void NextBuffer();
|
|
||||||
|
|
||||||
void GetDataSurface(uint64_t aSurfaceRef);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~CanvasTranslator();
|
~CanvasTranslator();
|
||||||
|
|
||||||
void AddBuffer(Handle&& aBufferHandle, size_t aBufferSize);
|
void Bind(Endpoint<PCanvasParent>&& aEndpoint);
|
||||||
|
|
||||||
void SetDataSurfaceBuffer(Handle&& aBufferHandle, size_t aBufferSize);
|
void StartTranslation();
|
||||||
|
|
||||||
bool ReadNextEvent(EventType& aEventType);
|
|
||||||
|
|
||||||
bool HasPendingEvent();
|
|
||||||
|
|
||||||
bool ReadPendingEvent(EventType& aEventType);
|
|
||||||
|
|
||||||
void FinishShutdown();
|
void FinishShutdown();
|
||||||
|
|
||||||
|
|
@ -290,30 +273,9 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
RefPtr<ID3D11Device> mDevice;
|
RefPtr<ID3D11Device> mDevice;
|
||||||
#endif
|
#endif
|
||||||
|
// We hold the ring buffer as a UniquePtr so we can drop it once
|
||||||
size_t mDefaultBufferSize;
|
// mTranslationTaskQueue has shutdown to break a RefPtr cycle.
|
||||||
uint32_t mMaxSpinCount;
|
UniquePtr<CanvasEventRingBuffer> mStream;
|
||||||
TimeDuration mNextEventTimeout;
|
|
||||||
|
|
||||||
using State = CanvasDrawEventRecorder::State;
|
|
||||||
using Header = CanvasDrawEventRecorder::Header;
|
|
||||||
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> mHeaderShmem;
|
|
||||||
Header* mHeader = nullptr;
|
|
||||||
|
|
||||||
struct CanvasShmem {
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
|
||||||
auto Size() { return shmem->Size(); }
|
|
||||||
MemReader CreateMemReader() {
|
|
||||||
return {static_cast<char*>(shmem->memory()), Size()};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::queue<CanvasShmem> mCanvasShmems;
|
|
||||||
CanvasShmem mCurrentShmem;
|
|
||||||
MemReader mCurrentMemReader{0, 0};
|
|
||||||
RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;
|
|
||||||
UniquePtr<CrossProcessSemaphore> mWriterSemaphore;
|
|
||||||
UniquePtr<CrossProcessSemaphore> mReaderSemaphore;
|
|
||||||
TextureType mTextureType = TextureType::Unknown;
|
TextureType mTextureType = TextureType::Unknown;
|
||||||
UniquePtr<TextureData> mReferenceTextureData;
|
UniquePtr<TextureData> mReferenceTextureData;
|
||||||
// Sometimes during device reset our reference DrawTarget can be null, so we
|
// Sometimes during device reset our reference DrawTarget can be null, so we
|
||||||
|
|
|
||||||
|
|
@ -530,9 +530,7 @@ void CompositorBridgeChild::EndCanvasTransaction() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompositorBridgeChild::ClearCachedResources() {
|
void CompositorBridgeChild::ClearCachedResources() {
|
||||||
if (auto* cm = gfx::CanvasManagerChild::Get()) {
|
CanvasChild::ClearCachedResources();
|
||||||
cm->ClearCachedResources();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompositorBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
|
bool CompositorBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
|
||||||
|
|
|
||||||
|
|
@ -25,35 +25,25 @@ async protocol PCanvas {
|
||||||
parent:
|
parent:
|
||||||
/**
|
/**
|
||||||
* Initialize a CanvasTranslator for a particular TextureType, which
|
* Initialize a CanvasTranslator for a particular TextureType, which
|
||||||
* translates events from shared memory buffers. aHeaderHandle is a shared
|
* translates events from a CanvasEventRingBuffer. aReadHandle is the shared
|
||||||
* memory handle for the control header. aBufferHandles are shared memory
|
* memory handle for the ring buffer. aReaderSem and aWriterSem are handles
|
||||||
* handles for the initial buffers for translation. aBufferSize is the size of
|
* for the semaphores to handle waiting on either side.
|
||||||
* each aBufferHandles' memory and the default size. aReaderSem and aWriterSem
|
|
||||||
* are handles for the semaphores to handle waiting on either side.
|
|
||||||
* aUseIPDLThread if true, use the IPDL thread instead of the worker pool for
|
|
||||||
* translation requests
|
|
||||||
*/
|
*/
|
||||||
async InitTranslator(TextureType aTextureType, Handle aHeaderHandle,
|
async InitTranslator(TextureType aTextureType, Handle aReadHandle,
|
||||||
Handle[] aBufferHandles, uint64_t aBufferSize,
|
|
||||||
CrossProcessSemaphoreHandle aReaderSem,
|
CrossProcessSemaphoreHandle aReaderSem,
|
||||||
CrossProcessSemaphoreHandle aWriterSem,
|
CrossProcessSemaphoreHandle aWriterSem,
|
||||||
bool aUseIPDLThread);
|
bool aUseIPDLThread);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restart the translation from a Stopped state.
|
* Send a new buffer to resume translation after it's been stopped by writer.
|
||||||
*/
|
*/
|
||||||
async RestartTranslation();
|
async NewBuffer(Handle aReadHandle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new buffer to be translated. The current buffer will be recycled if
|
* Used to tell the CanvasTranslator to start translating again after it has
|
||||||
* it is of the default size. The translation will then be restarted.
|
* stopped due to a timeout waiting for events.
|
||||||
*/
|
*/
|
||||||
async AddBuffer(Handle aBufferHandle, uint64_t aBufferSize);
|
async ResumeTranslation();
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the shared memory to be used for readback.
|
|
||||||
*/
|
|
||||||
async SetDataSurfaceBuffer(Handle aBufferHandle, uint64_t aBufferSize);
|
|
||||||
|
|
||||||
async __delete__();
|
async __delete__();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@
|
||||||
#include "RenderCompositorD3D11SWGL.h"
|
#include "RenderCompositorD3D11SWGL.h"
|
||||||
#include "ScopedGLHelpers.h"
|
#include "ScopedGLHelpers.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/gfx/CanvasManagerParent.h"
|
|
||||||
#include "mozilla/gfx/Logging.h"
|
#include "mozilla/gfx/Logging.h"
|
||||||
#include "mozilla/layers/GpuProcessD3D11TextureMap.h"
|
#include "mozilla/layers/GpuProcessD3D11TextureMap.h"
|
||||||
#include "mozilla/layers/TextureD3D11.h"
|
#include "mozilla/layers/TextureD3D11.h"
|
||||||
|
|
|
||||||
|
|
@ -5734,36 +5734,6 @@
|
||||||
#endif
|
#endif
|
||||||
mirror: once
|
mirror: once
|
||||||
|
|
||||||
# Default size of the shmem buffers used for recording
|
|
||||||
- name: gfx.canvas.remote.default-buffer-size
|
|
||||||
type: RelaxedAtomicUint32
|
|
||||||
value: 32 * 1024
|
|
||||||
mirror: always
|
|
||||||
|
|
||||||
# How many times to spin before waiting in remote canvas
|
|
||||||
- name: gfx.canvas.remote.max-spin-count
|
|
||||||
type: RelaxedAtomicUint32
|
|
||||||
value: 500
|
|
||||||
mirror: always
|
|
||||||
|
|
||||||
# How long to wait in milliseconds for the next event while in a transaction
|
|
||||||
- name: gfx.canvas.remote.event-timeout-ms
|
|
||||||
type: RelaxedAtomicUint32
|
|
||||||
value: 2
|
|
||||||
mirror: always
|
|
||||||
|
|
||||||
# How many times we have a spare buffer before we drop one
|
|
||||||
- name: gfx.canvas.remote.drop-buffer-limit
|
|
||||||
type: RelaxedAtomicUint32
|
|
||||||
value: 100
|
|
||||||
mirror: always
|
|
||||||
|
|
||||||
# Delay in milliseconds to drop buffers when there have been no non-empty transactions
|
|
||||||
- name: gfx.canvas.remote.drop-buffer-milliseconds
|
|
||||||
type: RelaxedAtomicUint32
|
|
||||||
value: 10000
|
|
||||||
mirror: always
|
|
||||||
|
|
||||||
- name: gfx.canvas.willreadfrequently.enabled
|
- name: gfx.canvas.willreadfrequently.enabled
|
||||||
type: bool
|
type: bool
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue