mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Backed out changeset 71b6060b6015 (bug 1863914) for causing Bug 1868934
This commit is contained in:
parent
20cc3bf2c1
commit
cc338968d0
@ -19,6 +19,26 @@ InlineTranslator::InlineTranslator(DrawTarget* aDT, void* aFontContext)
|
||||
: mBaseDT(aDT), mFontContext(aFontContext) {}
|
||||
|
||||
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);
|
||||
|
||||
uint32_t magicInt;
|
||||
|
@ -166,28 +166,6 @@ class InlineTranslator : public Translator {
|
||||
std::string GetError() { return mError; }
|
||||
|
||||
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;
|
||||
Matrix mBaseDTTransform;
|
||||
nsRefPtrHashtable<nsPtrHashKey<void>, DrawTarget> mDrawTargets;
|
||||
|
@ -25,6 +25,13 @@ bool RecordedEvent::DoWithEventFromStream(
|
||||
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) {
|
||||
switch (aType) {
|
||||
case DRAWTARGETCREATION:
|
||||
|
@ -214,7 +214,7 @@ struct SizeCollector {
|
||||
};
|
||||
|
||||
struct MemWriter {
|
||||
constexpr explicit MemWriter(char* aPtr) : mPtr(aPtr) {}
|
||||
explicit MemWriter(char* aPtr) : mPtr(aPtr) {}
|
||||
void write(const char* aData, size_t aSize) {
|
||||
memcpy(mPtr, aData, aSize);
|
||||
mPtr += aSize;
|
||||
@ -222,30 +222,13 @@ struct MemWriter {
|
||||
char* mPtr;
|
||||
};
|
||||
|
||||
class ContiguousBuffer {
|
||||
public:
|
||||
ContiguousBuffer(char* aStart, size_t aSize)
|
||||
: 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 {
|
||||
// This is a simple interface for an EventRingBuffer, so we can use it in the
|
||||
// RecordedEvent reading and writing machinery.
|
||||
class EventRingBuffer {
|
||||
public:
|
||||
/**
|
||||
* Templated RecordEvent function so that we can record into the buffer
|
||||
* quickly using MemWriter.
|
||||
* Templated RecordEvent function so that when we have enough contiguous
|
||||
* space we can record into the buffer quickly using MemWriter.
|
||||
*
|
||||
* @param aRecordedEvent the event to record
|
||||
*/
|
||||
@ -254,25 +237,56 @@ class ContiguousBufferStream {
|
||||
SizeCollector size;
|
||||
WriteElement(size, aRecordedEvent->GetType());
|
||||
aRecordedEvent->Record(size);
|
||||
auto& buffer = GetContiguousBuffer(size.mTotalSize);
|
||||
if (!buffer.IsValid()) {
|
||||
return;
|
||||
if (size.mTotalSize > mAvailable) {
|
||||
WaitForAndRecalculateAvailableSpace();
|
||||
}
|
||||
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:
|
||||
/**
|
||||
* 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 {
|
||||
@ -416,7 +430,7 @@ class RecordedEvent {
|
||||
|
||||
virtual void RecordToStream(std::ostream& 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 OutputSimpleEventInfo(std::stringstream& aStringStream) const {}
|
||||
@ -446,6 +460,9 @@ class RecordedEvent {
|
||||
static bool DoWithEventFromStream(
|
||||
EventStream& aStream, EventType aType,
|
||||
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; }
|
||||
|
||||
@ -478,7 +495,7 @@ class RecordedEventDerived : public RecordedEvent {
|
||||
WriteElement(aStream, this->mType);
|
||||
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));
|
||||
}
|
||||
void RecordToStream(MemStream& aStream) const override {
|
||||
|
@ -151,12 +151,6 @@ void CanvasManagerChild::EndCanvasTransaction() {
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasManagerChild::ClearCachedResources() {
|
||||
if (mCanvasChild) {
|
||||
mCanvasChild->ClearCachedResources();
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasManagerChild::DeactivateCanvas() {
|
||||
mActive = false;
|
||||
if (mCanvasChild) {
|
||||
|
@ -47,7 +47,6 @@ class CanvasManagerChild final : public PCanvasManagerChild {
|
||||
|
||||
bool IsCanvasActive() { return mActive; }
|
||||
void EndCanvasTransaction();
|
||||
void ClearCachedResources();
|
||||
void DeactivateCanvas();
|
||||
|
||||
RefPtr<layers::CanvasChild> GetCanvasChild();
|
||||
|
@ -9,268 +9,294 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "mozilla/layers/SharedSurfacesChild.h"
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
#include "RecordedCanvasEventImpl.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
struct ShmemAndHandle {
|
||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
||||
Handle handle;
|
||||
};
|
||||
static const uint32_t kMaxSpinCount = 200;
|
||||
|
||||
static Maybe<ShmemAndHandle> CreateAndMapShmem(size_t aSize) {
|
||||
auto shmem = MakeRefPtr<ipc::SharedMemoryBasic>();
|
||||
if (!shmem->Create(aSize) || !shmem->Map(aSize)) {
|
||||
return Nothing();
|
||||
}
|
||||
static const TimeDuration kTimeout = TimeDuration::FromMilliseconds(100);
|
||||
static const int32_t kTimeoutRetryCount = 50;
|
||||
|
||||
auto shmemHandle = shmem->TakeHandle();
|
||||
if (!shmemHandle) {
|
||||
return Nothing();
|
||||
}
|
||||
static const uint32_t kCacheLineSize = 64;
|
||||
static const uint32_t kSmallStreamSize = 64 * 1024;
|
||||
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() {
|
||||
mDefaultBufferSize = ipc::SharedMemory::PageAlignedSize(
|
||||
StaticPrefs::gfx_canvas_remote_default_buffer_size());
|
||||
mMaxSpinCount = StaticPrefs::gfx_canvas_remote_max_spin_count();
|
||||
mDropBufferLimit = StaticPrefs::gfx_canvas_remote_drop_buffer_limit();
|
||||
mDropBufferOnZero = mDropBufferLimit;
|
||||
}
|
||||
|
||||
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())) {
|
||||
bool CanvasEventRingBuffer::InitBuffer(
|
||||
base::ProcessId aOtherPid, ipc::SharedMemoryBasic::Handle* aReadHandle) {
|
||||
size_t shmemSize = StreamSize() + (2 * kCacheLineSize);
|
||||
mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
|
||||
if (NS_WARN_IF(!mSharedMemory->Create(shmemSize)) ||
|
||||
NS_WARN_IF(!mSharedMemory->Map(shmemSize))) {
|
||||
mGood = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
mHeader = static_cast<Header*>(header->shmem->memory());
|
||||
mHeader->eventCount = 0;
|
||||
mHeader->writerWaitCount = 0;
|
||||
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)) {
|
||||
*aReadHandle = mSharedMemory->TakeHandle();
|
||||
if (NS_WARN_IF(!*aReadHandle)) {
|
||||
mGood = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
mReaderSemaphore.reset(CrossProcessSemaphore::Create("CanvasTranslator", 0));
|
||||
auto readerSem = mReaderSemaphore->CloneHandle();
|
||||
mReaderSemaphore->CloseHandle();
|
||||
if (!IsHandleValid(readerSem)) {
|
||||
return false;
|
||||
}
|
||||
mBuf = static_cast<char*>(mSharedMemory->memory());
|
||||
mBufPos = mBuf;
|
||||
mAvailable = StreamSize();
|
||||
|
||||
if (!mHelpers->InitTranslator(aTextureType, std::move(header->handle),
|
||||
std::move(bufferHandles), mDefaultBufferSize,
|
||||
std::move(readerSem), std::move(writerSem),
|
||||
/* aUseIPDLThread */ false)) {
|
||||
return false;
|
||||
}
|
||||
static_assert(sizeof(ReadFooter) <= kCacheLineSize,
|
||||
"ReadFooter must fit in kCacheLineSize.");
|
||||
mRead = reinterpret_cast<ReadFooter*>(mBuf + StreamSize());
|
||||
mRead->count = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
void CanvasDrawEventRecorder::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
||||
aEvent.RecordToStream(*this);
|
||||
bool CanvasEventRingBuffer::InitWriter(
|
||||
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() {
|
||||
int64_t checkpoint = mHeader->eventCount;
|
||||
RecordEvent(RecordedCheckpoint());
|
||||
return checkpoint;
|
||||
bool CanvasEventRingBuffer::InitReader(
|
||||
ipc::SharedMemoryBasic::Handle aReadHandle,
|
||||
CrossProcessSemaphoreHandle aReaderSem,
|
||||
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) {
|
||||
uint32_t spinCount = mMaxSpinCount;
|
||||
bool CanvasEventRingBuffer::SetNewBuffer(
|
||||
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 {
|
||||
if (mHeader->processedCount >= aCheckpoint) {
|
||||
return true;
|
||||
memcpy(mBufPos, curDestPtr, mAvailable);
|
||||
IncrementWriteCountBy(mAvailable);
|
||||
curDestPtr += mAvailable;
|
||||
remainingToWrite -= mAvailable;
|
||||
if (!WaitForAndRecalculateAvailableSpace()) {
|
||||
return;
|
||||
}
|
||||
} while (--spinCount != 0);
|
||||
} while (remainingToWrite > mAvailable);
|
||||
|
||||
mHeader->writerState = State::AboutToWait;
|
||||
if (mHeader->processedCount >= aCheckpoint) {
|
||||
mHeader->writerState = State::Processing;
|
||||
return true;
|
||||
memcpy(mBufPos, curDestPtr, remainingToWrite);
|
||||
UpdateWriteTotalsBy(remainingToWrite);
|
||||
}
|
||||
|
||||
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;
|
||||
mHeader->writerState = State::Waiting;
|
||||
uint32_t bufPos = mOurCount % StreamSize();
|
||||
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.
|
||||
while (!mHelpers->ReaderClosed() && mHeader->readerState != State::Failed) {
|
||||
if (mWriterSemaphore->Wait(Some(TimeDuration::FromMilliseconds(100)))) {
|
||||
MOZ_ASSERT(mHeader->processedCount >= aCheckpoint);
|
||||
return true;
|
||||
mBufPos = mBuf + bufPos;
|
||||
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
|
||||
// reason (e.g. shutdown), so mark us as failed so the reader is aware.
|
||||
mHeader->writerState = State::Failed;
|
||||
return false;
|
||||
}
|
||||
|
||||
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 (remainingToRead <= mAvailable) {
|
||||
memcpy(curSrcPtr, mBufPos, remainingToRead);
|
||||
UpdateReadTotalsBy(remainingToRead);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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::Paused:
|
||||
case State::Failed:
|
||||
return;
|
||||
case State::AboutToWait:
|
||||
// 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
|
||||
// closed to avoid hangs.
|
||||
if (mHelpers->ReaderClosed()) {
|
||||
if (mWriterServices->ReaderClosed()) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
case State::Waiting:
|
||||
if (mHeader->processedCount < mHeader->eventCount) {
|
||||
if (mRead->count != mOurCount) {
|
||||
// We have to use compareExchange here because the reader can change
|
||||
// from Waiting to Stopped.
|
||||
if (mHeader->readerState.compareExchange(State::Waiting,
|
||||
State::Processing)) {
|
||||
if (mRead->state.compareExchange(State::Waiting, State::Processing)) {
|
||||
mReaderSemaphore->Signal();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mHeader->readerState == State::Stopped);
|
||||
MOZ_ASSERT(mRead->state == State::Stopped);
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
case State::Stopped:
|
||||
if (mHeader->processedCount < mHeader->eventCount) {
|
||||
mHeader->readerState = State::Processing;
|
||||
if (!mHelpers->RestartReader()) {
|
||||
mHeader->writerState = State::Failed;
|
||||
}
|
||||
if (mRead->count != mOurCount) {
|
||||
mRead->state = State::Processing;
|
||||
mWriterServices->ResumeReader();
|
||||
}
|
||||
return;
|
||||
default:
|
||||
@ -280,6 +306,267 @@ void CanvasDrawEventRecorder::CheckAndSignalReader() {
|
||||
} 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(
|
||||
gfx::SourceSurface* aSurface, const char* aReason) {
|
||||
wr::ExternalImageId extId{};
|
||||
|
@ -7,32 +7,195 @@
|
||||
#ifndef mozilla_layers_CanvasDrawEventRecorder_h
|
||||
#define mozilla_layers_CanvasDrawEventRecorder_h
|
||||
|
||||
#include <queue>
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/gfx/DrawEventRecorder.h"
|
||||
#include "mozilla/ipc/CrossProcessSemaphore.h"
|
||||
#include "mozilla/ipc/SharedMemoryBasic.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using EventType = gfx::RecordedEvent::EventType;
|
||||
|
||||
namespace layers {
|
||||
|
||||
typedef mozilla::ipc::SharedMemoryBasic::Handle Handle;
|
||||
typedef mozilla::CrossProcessSemaphoreHandle CrossProcessSemaphoreHandle;
|
||||
static const uint8_t kCheckpointEventType = -1;
|
||||
static const uint8_t kDropBufferEventType = -2;
|
||||
|
||||
class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
|
||||
public gfx::ContiguousBufferStream {
|
||||
class CanvasEventRingBuffer final : public gfx::EventRingBuffer {
|
||||
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 {
|
||||
Processing,
|
||||
|
||||
@ -49,61 +212,89 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
|
||||
*/
|
||||
AboutToWait,
|
||||
Waiting,
|
||||
Paused,
|
||||
Stopped,
|
||||
Failed,
|
||||
};
|
||||
|
||||
struct Header {
|
||||
Atomic<int64_t> eventCount;
|
||||
Atomic<int64_t> writerWaitCount;
|
||||
Atomic<State> writerState;
|
||||
uint8_t padding1[44];
|
||||
Atomic<int64_t> processedCount;
|
||||
Atomic<State> readerState;
|
||||
struct ReadFooter {
|
||||
Atomic<uint32_t> count;
|
||||
Atomic<uint32_t> returnCount;
|
||||
Atomic<State> state;
|
||||
};
|
||||
|
||||
class Helpers {
|
||||
public:
|
||||
virtual ~Helpers() = default;
|
||||
|
||||
virtual bool InitTranslator(const TextureType& aTextureType,
|
||||
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;
|
||||
struct WriteFooter {
|
||||
Atomic<uint32_t> count;
|
||||
Atomic<uint32_t> returnCount;
|
||||
Atomic<uint32_t> requiredDifference;
|
||||
Atomic<State> state;
|
||||
};
|
||||
|
||||
bool Init(TextureType aTextureType, UniquePtr<Helpers> aHelpers);
|
||||
CanvasEventRingBuffer(const CanvasEventRingBuffer&) = delete;
|
||||
void operator=(const CanvasEventRingBuffer&) = delete;
|
||||
|
||||
/**
|
||||
* Record an event for processing by the CanvasParent's CanvasTranslator.
|
||||
* @param aEvent the event to record
|
||||
*/
|
||||
void RecordEvent(const gfx::RecordedEvent& aEvent) final;
|
||||
void IncrementWriteCountBy(uint32_t aCount);
|
||||
|
||||
bool WaitForReadCount(uint32_t aReadCount, TimeDuration aTimeout);
|
||||
|
||||
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,
|
||||
const char* aReason) 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.
|
||||
@ -112,60 +303,19 @@ class CanvasDrawEventRecorder final : public gfx::DrawEventRecorderPrivate,
|
||||
* @returns true if the checkpoint was reached, false if the reader is closed
|
||||
* 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();
|
||||
|
||||
protected:
|
||||
gfx::ContiguousBuffer& GetContiguousBuffer(size_t aSize) final;
|
||||
|
||||
void IncrementEventCount() final;
|
||||
bool SwitchBuffer(base::ProcessId aOtherPid,
|
||||
ipc::SharedMemoryBasic::Handle* aHandle) {
|
||||
return mOutputStream.SwitchBuffer(aOtherPid, aHandle);
|
||||
}
|
||||
|
||||
private:
|
||||
void WriteInternalEvent(EventType aEventType);
|
||||
|
||||
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;
|
||||
CanvasEventRingBuffer mOutputStream;
|
||||
};
|
||||
|
||||
} // 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 NEXT_TEXTURE_ID = EventType(EventType::LAST + 11);
|
||||
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
|
||||
: public RecordedEventDerived<RecordedCanvasBeginTransaction> {
|
||||
@ -349,7 +344,31 @@ class RecordedGetDataForSurface final
|
||||
|
||||
inline bool RecordedGetDataForSurface::PlayCanvasEvent(
|
||||
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;
|
||||
}
|
||||
|
||||
@ -557,87 +576,6 @@ RecordedTextureDestruction::RecordedTextureDestruction(S& aStream)
|
||||
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) \
|
||||
f(CANVAS_BEGIN_TRANSACTION, RecordedCanvasBeginTransaction); \
|
||||
f(CANVAS_END_TRANSACTION, RecordedCanvasEndTransaction); \
|
||||
@ -651,11 +589,7 @@ class RecordedDropBuffer final
|
||||
f(REMOVE_SURFACE_ALIAS, RecordedRemoveSurfaceAlias); \
|
||||
f(DEVICE_CHANGE_ACKNOWLEDGED, RecordedDeviceChangeAcknowledged); \
|
||||
f(NEXT_TEXTURE_ID, RecordedNextTextureId); \
|
||||
f(TEXTURE_DESTRUCTION, RecordedTextureDestruction); \
|
||||
f(CHECKPOINT, RecordedCheckpoint); \
|
||||
f(PAUSE_TRANSLATION, RecordedPauseTranslation); \
|
||||
f(RECYCLE_BUFFER, RecordedRecycleBuffer); \
|
||||
f(DROP_BUFFER, RecordedDropBuffer);
|
||||
f(TEXTURE_DESTRUCTION, RecordedTextureDestruction);
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -23,7 +23,7 @@ RecordedTextureData::RecordedTextureData(
|
||||
already_AddRefed<CanvasChild> aCanvasChild, gfx::IntSize aSize,
|
||||
gfx::SurfaceFormat aFormat, TextureType aTextureType)
|
||||
: mCanvasChild(aCanvasChild), mSize(aSize), mFormat(aFormat) {
|
||||
mCanvasChild->EnsureRecorder(aSize, aFormat, aTextureType);
|
||||
mCanvasChild->EnsureRecorder(aTextureType);
|
||||
}
|
||||
|
||||
RecordedTextureData::~RecordedTextureData() {
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "mozilla/ipc/FileDescriptor.h"
|
||||
#include "mozilla/layers/CompositorTypes.h" // for TextureFlags, etc
|
||||
#include "mozilla/layers/LayersTypes.h" // for LayerRenderState, etc
|
||||
#include "mozilla/layers/LayersMessages.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/layers/TextureSourceProvider.h"
|
||||
#include "mozilla/mozalloc.h" // for operator delete
|
||||
|
@ -15,42 +15,21 @@
|
||||
#include "mozilla/ipc/Endpoint.h"
|
||||
#include "mozilla/ipc/ProcessChild.h"
|
||||
#include "mozilla/layers/CanvasDrawEventRecorder.h"
|
||||
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "RecordedCanvasEventImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
|
||||
/* static */ bool CanvasChild::mInForeground = true;
|
||||
|
||||
class RingBufferWriterServices final
|
||||
: public CanvasEventRingBuffer::WriterServices {
|
||||
public:
|
||||
explicit RecorderHelpers(const RefPtr<CanvasChild>& aCanvasChild)
|
||||
explicit RingBufferWriterServices(RefPtr<CanvasChild> aCanvasChild)
|
||||
: mCanvasChild(aCanvasChild) {}
|
||||
|
||||
~RecorderHelpers() 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);
|
||||
}
|
||||
~RingBufferWriterServices() override = default;
|
||||
|
||||
bool ReaderClosed() override {
|
||||
if (!mCanvasChild) {
|
||||
@ -59,11 +38,11 @@ class RecorderHelpers final : public CanvasDrawEventRecorder::Helpers {
|
||||
return !mCanvasChild->CanSend() || ipc::ProcessChild::ExpectingShutdown();
|
||||
}
|
||||
|
||||
bool RestartReader() override {
|
||||
void ResumeReader() override {
|
||||
if (!mCanvasChild) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
return mCanvasChild->SendRestartTranslation();
|
||||
mCanvasChild->ResumeTranslation();
|
||||
}
|
||||
|
||||
private:
|
||||
@ -176,24 +155,45 @@ ipc::IPCResult CanvasChild::RecvDeactivate() {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void CanvasChild::EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
TextureType aTextureType) {
|
||||
void CanvasChild::EnsureRecorder(TextureType aTextureType) {
|
||||
if (!mRecorder) {
|
||||
auto recorder = MakeRefPtr<CanvasDrawEventRecorder>();
|
||||
if (!recorder->Init(aTextureType, MakeUnique<RecorderHelpers>(this))) {
|
||||
MOZ_ASSERT(mTextureType == TextureType::Unknown);
|
||||
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;
|
||||
}
|
||||
|
||||
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.");
|
||||
|
||||
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() {
|
||||
if (CanSend()) {
|
||||
@ -202,11 +202,24 @@ void CanvasChild::Destroy() {
|
||||
}
|
||||
|
||||
void CanvasChild::OnTextureWriteLock() {
|
||||
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||
if (!mRecorder) {
|
||||
return;
|
||||
}
|
||||
|
||||
mHasOutstandingWriteLock = true;
|
||||
mLastWriteLockCheckpoint = mRecorder->CreateCheckpoint();
|
||||
}
|
||||
|
||||
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) {
|
||||
mRecorder->RecordEvent(RecordedCanvasFlush());
|
||||
if (!mRecorder->WaitForCheckpoint(mLastWriteLockCheckpoint)) {
|
||||
@ -225,8 +238,24 @@ void CanvasChild::OnTextureForwarded() {
|
||||
}
|
||||
|
||||
bool CanvasChild::EnsureBeginTransaction() {
|
||||
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||
if (!mRecorder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -234,33 +263,25 @@ bool CanvasChild::EnsureBeginTransaction() {
|
||||
}
|
||||
|
||||
void CanvasChild::EndTransaction() {
|
||||
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||
if (!mRecorder) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsInTransaction) {
|
||||
RecordEvent(RecordedCanvasEndTransaction());
|
||||
mRecorder->RecordEvent(RecordedCanvasEndTransaction());
|
||||
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;
|
||||
}
|
||||
|
||||
void CanvasChild::DropFreeBuffersWhenDormant() {
|
||||
// Drop any free buffers if we have not had any non-empty transactions.
|
||||
if (mDormant) {
|
||||
mRecorder->DropFreeBuffers();
|
||||
}
|
||||
/* static */
|
||||
void CanvasChild::ClearCachedResources() {
|
||||
// We use this as a proxy for the tab being in the backgound.
|
||||
mInForeground = false;
|
||||
}
|
||||
|
||||
void CanvasChild::ClearCachedResources() { mRecorder->DropFreeBuffers(); }
|
||||
|
||||
bool CanvasChild::ShouldBeCleanedUp() const {
|
||||
// Always return true if we've been deactivated.
|
||||
if (Deactivated()) {
|
||||
@ -268,11 +289,16 @@ bool CanvasChild::ShouldBeCleanedUp() const {
|
||||
}
|
||||
|
||||
// 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(
|
||||
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(
|
||||
gfx::BackendType::SKIA, gfx::IntSize(1, 1), aFormat);
|
||||
RefPtr<gfx::DrawTarget> dt = MakeAndAddRef<gfx::DrawTargetRecording>(
|
||||
@ -280,36 +306,6 @@ already_AddRefed<gfx::DrawTarget> CanvasChild::CreateDrawTarget(
|
||||
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 (!mDataSurfaceShmemAvailable || 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();
|
||||
mDataSurfaceShmemAvailable = true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mDataSurfaceShmemAvailable);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
||||
// We drop mRecorder in ActorDestroy to break the reference cycle.
|
||||
if (!mRecorder) {
|
||||
@ -319,15 +315,16 @@ void CanvasChild::RecordEvent(const gfx::RecordedEvent& aEvent) {
|
||||
mRecorder->RecordEvent(aEvent);
|
||||
}
|
||||
|
||||
int64_t CanvasChild::CreateCheckpoint() {
|
||||
return mRecorder->CreateCheckpoint();
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
|
||||
const gfx::SourceSurface* aSurface) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
|
||||
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
|
||||
// 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
|
||||
@ -340,59 +337,43 @@ already_AddRefed<gfx::DataSourceSurface> CanvasChild::GetDataSurface(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RecordEvent(RecordedPrepareDataForSurface(aSurface));
|
||||
mRecorder->RecordEvent(RecordedPrepareDataForSurface(aSurface));
|
||||
uint32_t checkpoint = mRecorder->CreateCheckpoint();
|
||||
|
||||
gfx::IntSize ssSize = aSurface->GetSize();
|
||||
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;
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
mDataSurfaceShmemAvailable = false;
|
||||
RecordEvent(RecordedGetDataForSurface(aSurface));
|
||||
auto checkpoint = CreateCheckpoint();
|
||||
struct DataShmemHolder {
|
||||
RefPtr<ipc::SharedMemoryBasic> shmem;
|
||||
RefPtr<CanvasChild> canvasChild;
|
||||
};
|
||||
mRecorder->RecordEvent(RecordedGetDataForSurface(aSurface));
|
||||
mRecorder->ReturnRead(dest, ssSize.height * dataFormatWidth);
|
||||
|
||||
auto* data = static_cast<uint8_t*>(mDataSurfaceShmem->memory());
|
||||
auto* closure = new DataShmemHolder{do_AddRef(mDataSurfaceShmem), 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();
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::SourceSurface> CanvasChild::WrapSurface(
|
||||
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 MakeAndAddRef<SourceSurfaceCanvasRecording>(aSurface, this, mRecorder);
|
||||
}
|
||||
|
||||
void CanvasChild::ReturnDataSurfaceShmem(
|
||||
already_AddRefed<ipc::SharedMemoryBasic> aDataSurfaceShmem) {
|
||||
RefPtr<ipc::SharedMemoryBasic> data = aDataSurfaceShmem;
|
||||
// We can only reuse the latest data surface shmem.
|
||||
if (data == mDataSurfaceShmem) {
|
||||
MOZ_ASSERT(!mDataSurfaceShmemAvailable);
|
||||
mDataSurfaceShmemAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "mozilla/layers/PCanvasChild.h"
|
||||
#include "mozilla/layers/SourceSurfaceSharedData.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -36,7 +38,7 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||
/**
|
||||
* Release resources until they are next required.
|
||||
*/
|
||||
void ClearCachedResources();
|
||||
static void ClearCachedResources();
|
||||
|
||||
ipc::IPCResult RecvNotifyDeviceChanged();
|
||||
|
||||
@ -47,8 +49,12 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||
*
|
||||
* @params aTextureType the TextureType to create in the CanvasTranslator.
|
||||
*/
|
||||
void EnsureRecorder(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
|
||||
TextureType aTextureType);
|
||||
void EnsureRecorder(TextureType aTextureType);
|
||||
|
||||
/**
|
||||
* Send a messsage to our CanvasParent to resume translation.
|
||||
*/
|
||||
void ResumeTranslation();
|
||||
|
||||
/**
|
||||
* Clean up IPDL actor.
|
||||
@ -105,8 +111,6 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||
*/
|
||||
void RecordEvent(const gfx::RecordedEvent& aEvent);
|
||||
|
||||
int64_t CreateCheckpoint();
|
||||
|
||||
/**
|
||||
* Wrap the given surface, so that we can provide a DataSourceSurface if
|
||||
* required.
|
||||
@ -135,27 +139,18 @@ class CanvasChild final : public PCanvasChild, public SupportsWeakPtr {
|
||||
|
||||
~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 bool mDeactivated;
|
||||
static bool mInForeground;
|
||||
|
||||
RefPtr<CanvasDrawEventRecorder> mRecorder;
|
||||
|
||||
RefPtr<ipc::SharedMemoryBasic> mDataSurfaceShmem;
|
||||
bool mDataSurfaceShmemAvailable = false;
|
||||
int64_t mLastWriteLockCheckpoint = 0;
|
||||
TextureType mTextureType = TextureType::Unknown;
|
||||
uint32_t mLastWriteLockCheckpoint = 0;
|
||||
uint32_t mTransactionsSinceGetDataSurface = kCacheDataSurfaceThreshold;
|
||||
std::vector<RefPtr<gfx::SourceSurface>> mLastTransactionExternalSurfaces;
|
||||
bool mIsInTransaction = false;
|
||||
bool mHasOutstandingWriteLock = false;
|
||||
bool mDormant = false;
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
@ -14,10 +14,8 @@
|
||||
#include "mozilla/gfx/GPUParent.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/ipc/Endpoint.h"
|
||||
#include "mozilla/layers/CanvasTranslator.h"
|
||||
#include "mozilla/layers/SharedSurfacesParent.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
@ -31,6 +29,25 @@
|
||||
namespace mozilla {
|
||||
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,
|
||||
const gfx::IntSize& aSize,
|
||||
gfx::SurfaceFormat aFormat) {
|
||||
@ -51,10 +68,6 @@ TextureData* CanvasTranslator::CreateTextureData(TextureType aTextureType,
|
||||
}
|
||||
|
||||
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.
|
||||
Telemetry::ScalarAdd(Telemetry::ScalarID::GFX_CANVAS_REMOTE_ACTIVATED, 1);
|
||||
}
|
||||
@ -82,49 +95,31 @@ bool CanvasTranslator::IsInTaskQueue() const {
|
||||
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(
|
||||
const TextureType& aTextureType, Handle&& aReadHandle,
|
||||
nsTArray<Handle>&& aBufferHandles, uint64_t aBufferSize,
|
||||
const TextureType& aTextureType,
|
||||
ipc::SharedMemoryBasic::Handle&& aReadHandle,
|
||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
||||
CrossProcessSemaphoreHandle&& aWriterSem, bool aUseIPDLThread) {
|
||||
if (mHeaderShmem) {
|
||||
CrossProcessSemaphoreHandle&& aWriterSem, const bool& aUseIPDLThread) {
|
||||
if (mStream) {
|
||||
return IPC_FAIL(this, "RecvInitTranslator called twice.");
|
||||
}
|
||||
|
||||
mTextureType = aTextureType;
|
||||
|
||||
mHeaderShmem = MakeAndAddRef<ipc::SharedMemoryBasic>();
|
||||
if (!CreateAndMapShmem(mHeaderShmem, std::move(aReadHandle),
|
||||
ipc::SharedMemory::RightsReadWrite, sizeof(Header))) {
|
||||
return IPC_FAIL(this, "Failed.");
|
||||
// We need to initialize the stream first, because it might be used to
|
||||
// communicate other failures back to the writer.
|
||||
mStream = MakeUnique<CanvasEventRingBuffer>();
|
||||
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 (!CheckForFreshCanvasDevice(__LINE__)) {
|
||||
gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
|
||||
mStream = nullptr;
|
||||
return IPC_OK();
|
||||
}
|
||||
#endif
|
||||
@ -132,157 +127,56 @@ mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
|
||||
if (!aUseIPDLThread) {
|
||||
mTranslationTaskQueue = gfx::CanvasRenderThread::CreateWorkerTaskQueue();
|
||||
}
|
||||
|
||||
// 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();
|
||||
return RecvResumeTranslation();
|
||||
}
|
||||
|
||||
ipc::IPCResult CanvasTranslator::RecvRestartTranslation() {
|
||||
if (mDeactivated) {
|
||||
// The other side might have sent a message before we deactivated.
|
||||
return IPC_OK();
|
||||
ipc::IPCResult CanvasTranslator::RecvNewBuffer(
|
||||
ipc::SharedMemoryBasic::Handle&& aReadHandle) {
|
||||
if (!mStream) {
|
||||
return IPC_FAIL(this, "RecvNewBuffer before RecvInitTranslator.");
|
||||
}
|
||||
|
||||
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::TranslateRecording",
|
||||
this,
|
||||
&CanvasTranslator::TranslateRecording));
|
||||
|
||||
return IPC_OK();
|
||||
// We need to set the new buffer on the translation queue to be sure that the
|
||||
// drop buffer event has been processed.
|
||||
DispatchToTaskQueue(NS_NewRunnableFunction(
|
||||
"CanvasTranslator SetNewBuffer",
|
||||
[self = RefPtr(this), readHandle = std::move(aReadHandle)]() mutable {
|
||||
self->mStream->SetNewBuffer(std::move(readHandle));
|
||||
}));
|
||||
return RecvResumeTranslation();
|
||||
}
|
||||
|
||||
ipc::IPCResult CanvasTranslator::RecvAddBuffer(
|
||||
ipc::SharedMemoryBasic::Handle&& aBufferHandle, uint64_t aBufferSize) {
|
||||
if (mDeactivated) {
|
||||
ipc::IPCResult CanvasTranslator::RecvResumeTranslation() {
|
||||
if (!mStream) {
|
||||
return IPC_FAIL(this, "RecvResumeTranslation before RecvInitTranslator.");
|
||||
}
|
||||
if (CheckDeactivated()) {
|
||||
// The other side might have sent a resume message before we deactivated.
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
DispatchToTaskQueue(
|
||||
NewRunnableMethod<ipc::SharedMemoryBasic::Handle&&, size_t>(
|
||||
"CanvasTranslator::AddBuffer", this, &CanvasTranslator::AddBuffer,
|
||||
std::move(aBufferHandle), aBufferSize));
|
||||
|
||||
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::StartTranslation",
|
||||
this,
|
||||
&CanvasTranslator::StartTranslation));
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
void CanvasTranslator::AddBuffer(ipc::SharedMemoryBasic::Handle&& aBufferHandle,
|
||||
size_t aBufferSize) {
|
||||
MOZ_ASSERT(IsInTaskQueue());
|
||||
MOZ_RELEASE_ASSERT(mHeader->readerState == State::Paused);
|
||||
void CanvasTranslator::StartTranslation() {
|
||||
MOZ_RELEASE_ASSERT(mStream->IsValid(),
|
||||
"StartTranslation called before buffer has been set.");
|
||||
|
||||
// Default sized buffers will have been queued for recycling.
|
||||
if (mCurrentShmem.Size() == mDefaultBufferSize) {
|
||||
mCanvasShmems.emplace(std::move(mCurrentShmem));
|
||||
if (!TranslateRecording() && CanSend()) {
|
||||
DispatchToTaskQueue(NewRunnableMethod("CanvasTranslator::StartTranslation",
|
||||
this,
|
||||
&CanvasTranslator::StartTranslation));
|
||||
}
|
||||
|
||||
CanvasShmem newShmem;
|
||||
if (!CreateAndMapShmem(newShmem.shmem, std::move(aBufferHandle),
|
||||
ipc::SharedMemory::RightsReadOnly, aBufferSize)) {
|
||||
return;
|
||||
// If the stream has been marked as bad and the Writer hasn't failed,
|
||||
// deactivate remote canvas.
|
||||
if (!mStream->good() && !mStream->WriterFailed()) {
|
||||
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) {
|
||||
@ -302,6 +196,12 @@ void CanvasTranslator::ActorDestroy(ActorDestroyReason why) {
|
||||
|
||||
void CanvasTranslator::FinishShutdown() {
|
||||
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() {
|
||||
@ -321,10 +221,10 @@ void CanvasTranslator::Deactivate() {
|
||||
return;
|
||||
}
|
||||
mDeactivated = true;
|
||||
mHeader->readerState = State::Failed;
|
||||
|
||||
// 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.
|
||||
mStream->SetIsBad();
|
||||
gfx::CanvasRenderThread::Dispatch(
|
||||
NewRunnableMethod("CanvasTranslator::SendDeactivate", this,
|
||||
&CanvasTranslator::SendDeactivate));
|
||||
@ -338,101 +238,20 @@ void CanvasTranslator::Deactivate() {
|
||||
gfx::CanvasManagerParent::DisableRemoteCanvas();
|
||||
}
|
||||
|
||||
void CanvasTranslator::CheckAndSignalWriter() {
|
||||
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() {
|
||||
bool CanvasTranslator::TranslateRecording() {
|
||||
MOZ_ASSERT(IsInTaskQueue());
|
||||
|
||||
mHeader->readerState = State::Processing;
|
||||
EventType eventType;
|
||||
while (ReadNextEvent(eventType)) {
|
||||
bool success = RecordedEvent::DoWithEvent(
|
||||
mCurrentMemReader, static_cast<RecordedEvent::EventType>(eventType),
|
||||
if (!mStream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t eventType = mStream->ReadNextEvent();
|
||||
while (mStream->good() && eventType != kDropBufferEventType) {
|
||||
bool success = RecordedEvent::DoWithEventFromStream(
|
||||
*mStream, static_cast<RecordedEvent::EventType>(eventType),
|
||||
[&](RecordedEvent* recordedEvent) -> bool {
|
||||
// Make sure that the whole event was read from the stream.
|
||||
if (!mCurrentMemReader.good()) {
|
||||
if (!mStream->good()) {
|
||||
if (!CanSend()) {
|
||||
// The other side has closed only warn about read failure.
|
||||
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.
|
||||
if (!mCurrentMemReader.good()) {
|
||||
return;
|
||||
if (!mStream->good()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!success && !HandleExtensionEvent(eventType)) {
|
||||
@ -460,16 +279,34 @@ void CanvasTranslator::TranslateRecording() {
|
||||
} else {
|
||||
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) \
|
||||
case _typeenum: { \
|
||||
auto e = _class(mCurrentMemReader); \
|
||||
if (!mCurrentMemReader.good()) { \
|
||||
auto e = _class(*mStream); \
|
||||
if (!mStream->good()) { \
|
||||
if (!CanSend()) { \
|
||||
/* The other side has closed only warn about read failure. */ \
|
||||
gfxWarning() << "Failed to read event type: " << _typeenum; \
|
||||
@ -638,12 +475,6 @@ already_AddRefed<gfx::SourceSurface> CanvasTranslator::LookupExternalSurface(
|
||||
return SharedSurfacesParent::Get(wr::ToExternalImageId(aKey));
|
||||
}
|
||||
|
||||
void CanvasTranslator::CheckpointReached() { CheckAndSignalWriter(); }
|
||||
|
||||
void CanvasTranslator::PauseTranslation() {
|
||||
mHeader->readerState = State::Paused;
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::GradientStops> CanvasTranslator::GetOrCreateGradientStops(
|
||||
gfx::GradientStop* aRawStops, uint32_t aNumStops,
|
||||
gfx::ExtendMode aExtendMode) {
|
||||
|
@ -11,8 +11,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "mozilla/gfx/InlineTranslator.h"
|
||||
#include "mozilla/gfx/RecordedEvent.h"
|
||||
#include "CanvasChild.h"
|
||||
#include "mozilla/layers/CanvasDrawEventRecorder.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/layers/PCanvasParent.h"
|
||||
@ -21,8 +19,6 @@
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using EventType = gfx::RecordedEvent::EventType;
|
||||
class TaskQueue;
|
||||
|
||||
namespace layers {
|
||||
@ -56,49 +52,40 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||
* CanvasEventRingBuffer.
|
||||
*
|
||||
* @param aTextureType the TextureType the translator will create
|
||||
* @param aHeaderHandle handle for the control header
|
||||
* @param aBufferHandles handles for the initial buffers for translation
|
||||
* @param aBufferSize size of buffers and the default size
|
||||
* @param aReadHandle handle to the shared memory for the
|
||||
* CanvasEventRingBuffer
|
||||
* @param aReaderSem reading 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
|
||||
* pool for translation requests
|
||||
*/
|
||||
ipc::IPCResult RecvInitTranslator(const TextureType& aTextureType,
|
||||
Handle&& aReadHandle,
|
||||
nsTArray<Handle>&& aBufferHandles,
|
||||
uint64_t aBufferSize,
|
||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
||||
CrossProcessSemaphoreHandle&& aWriterSem,
|
||||
bool aUseIPDLThread);
|
||||
ipc::IPCResult RecvInitTranslator(
|
||||
const TextureType& aTextureType,
|
||||
ipc::SharedMemoryBasic::Handle&& aReadHandle,
|
||||
CrossProcessSemaphoreHandle&& aReaderSem,
|
||||
CrossProcessSemaphoreHandle&& aWriterSem, const 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
|
||||
* it is of the default size. The translation will then be restarted.
|
||||
* Used to tell the CanvasTranslator to start translating again after it has
|
||||
* stopped due to a timeout waiting for events.
|
||||
*/
|
||||
ipc::IPCResult RecvAddBuffer(Handle&& aBufferHandle, uint64_t aBufferSize);
|
||||
|
||||
/**
|
||||
* Sets the shared memory to be used for readback.
|
||||
*/
|
||||
ipc::IPCResult RecvSetDataSurfaceBuffer(Handle&& aBufferHandle,
|
||||
uint64_t aBufferSize);
|
||||
ipc::IPCResult RecvResumeTranslation();
|
||||
|
||||
void ActorDestroy(ActorDestroyReason why) final;
|
||||
|
||||
void CheckAndSignalWriter();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
@ -123,6 +110,18 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||
*/
|
||||
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
|
||||
* the next CreateDrawTarget.
|
||||
@ -157,10 +156,6 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||
*/
|
||||
TextureData* LookupTextureData(int64_t aTextureId);
|
||||
|
||||
void CheckpointReached();
|
||||
|
||||
void PauseTranslation();
|
||||
|
||||
/**
|
||||
* 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(
|
||||
gfx::ReferencePtr aSurface);
|
||||
|
||||
void RecycleBuffer();
|
||||
|
||||
void NextBuffer();
|
||||
|
||||
void GetDataSurface(uint64_t aSurfaceRef);
|
||||
|
||||
private:
|
||||
~CanvasTranslator();
|
||||
|
||||
void AddBuffer(Handle&& aBufferHandle, size_t aBufferSize);
|
||||
void Bind(Endpoint<PCanvasParent>&& aEndpoint);
|
||||
|
||||
void SetDataSurfaceBuffer(Handle&& aBufferHandle, size_t aBufferSize);
|
||||
|
||||
bool ReadNextEvent(EventType& aEventType);
|
||||
|
||||
bool HasPendingEvent();
|
||||
|
||||
bool ReadPendingEvent(EventType& aEventType);
|
||||
void StartTranslation();
|
||||
|
||||
void FinishShutdown();
|
||||
|
||||
@ -290,30 +273,9 @@ class CanvasTranslator final : public gfx::InlineTranslator,
|
||||
#if defined(XP_WIN)
|
||||
RefPtr<ID3D11Device> mDevice;
|
||||
#endif
|
||||
|
||||
size_t mDefaultBufferSize;
|
||||
uint32_t mMaxSpinCount;
|
||||
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;
|
||||
// We hold the ring buffer as a UniquePtr so we can drop it once
|
||||
// mTranslationTaskQueue has shutdown to break a RefPtr cycle.
|
||||
UniquePtr<CanvasEventRingBuffer> mStream;
|
||||
TextureType mTextureType = TextureType::Unknown;
|
||||
UniquePtr<TextureData> mReferenceTextureData;
|
||||
// Sometimes during device reset our reference DrawTarget can be null, so we
|
||||
|
@ -531,9 +531,7 @@ void CompositorBridgeChild::EndCanvasTransaction() {
|
||||
}
|
||||
|
||||
void CompositorBridgeChild::ClearCachedResources() {
|
||||
if (auto* cm = gfx::CanvasManagerChild::Get()) {
|
||||
cm->ClearCachedResources();
|
||||
}
|
||||
CanvasChild::ClearCachedResources();
|
||||
}
|
||||
|
||||
bool CompositorBridgeChild::AllocUnsafeShmem(size_t aSize, ipc::Shmem* aShmem) {
|
||||
|
@ -25,35 +25,25 @@ async protocol PCanvas {
|
||||
parent:
|
||||
/**
|
||||
* Initialize a CanvasTranslator for a particular TextureType, which
|
||||
* translates events from shared memory buffers. aHeaderHandle is a shared
|
||||
* memory handle for the control header. aBufferHandles are shared memory
|
||||
* handles for the initial buffers for translation. aBufferSize is the size of
|
||||
* 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
|
||||
* translates events from a CanvasEventRingBuffer. aReadHandle is the shared
|
||||
* memory handle for the ring buffer. aReaderSem and aWriterSem are handles
|
||||
* for the semaphores to handle waiting on either side.
|
||||
*/
|
||||
async InitTranslator(TextureType aTextureType, Handle aHeaderHandle,
|
||||
Handle[] aBufferHandles, uint64_t aBufferSize,
|
||||
async InitTranslator(TextureType aTextureType, Handle aReadHandle,
|
||||
CrossProcessSemaphoreHandle aReaderSem,
|
||||
CrossProcessSemaphoreHandle aWriterSem,
|
||||
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
|
||||
* it is of the default size. The translation will then be restarted.
|
||||
* Used to tell the CanvasTranslator to start translating again after it has
|
||||
* stopped due to a timeout waiting for events.
|
||||
*/
|
||||
async AddBuffer(Handle aBufferHandle, uint64_t aBufferSize);
|
||||
|
||||
/**
|
||||
* Sets the shared memory to be used for readback.
|
||||
*/
|
||||
async SetDataSurfaceBuffer(Handle aBufferHandle, uint64_t aBufferSize);
|
||||
async ResumeTranslation();
|
||||
|
||||
async __delete__();
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "RenderCompositorD3D11SWGL.h"
|
||||
#include "ScopedGLHelpers.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/gfx/CanvasManagerParent.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/layers/GpuProcessD3D11TextureMap.h"
|
||||
#include "mozilla/layers/TextureD3D11.h"
|
||||
|
@ -5744,36 +5744,6 @@
|
||||
#endif
|
||||
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
|
||||
type: bool
|
||||
#if defined(XP_WIN)
|
||||
|
Loading…
Reference in New Issue
Block a user