summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/IpcResourceUpdateQueue.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/wr/IpcResourceUpdateQueue.cpp')
-rw-r--r--gfx/layers/wr/IpcResourceUpdateQueue.cpp484
1 files changed, 484 insertions, 0 deletions
diff --git a/gfx/layers/wr/IpcResourceUpdateQueue.cpp b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
new file mode 100644
index 0000000000..d19dc7f2f9
--- /dev/null
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -0,0 +1,484 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "IpcResourceUpdateQueue.h"
+#include <string.h>
+#include <algorithm>
+#include "mozilla/Maybe.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/layers/PTextureChild.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+
+namespace mozilla {
+namespace wr {
+
+using namespace mozilla::layers;
+
+ShmSegmentsWriter::ShmSegmentsWriter(layers::WebRenderBridgeChild* aAllocator,
+ size_t aChunkSize)
+ : mShmAllocator(aAllocator), mCursor(0), mChunkSize(aChunkSize) {
+ MOZ_ASSERT(mShmAllocator);
+}
+
+ShmSegmentsWriter::~ShmSegmentsWriter() { Clear(); }
+
+ShmSegmentsWriter::ShmSegmentsWriter(ShmSegmentsWriter&& aOther) noexcept
+ : mSmallAllocs(std::move(aOther.mSmallAllocs)),
+ mLargeAllocs(std::move(aOther.mLargeAllocs)),
+ mShmAllocator(aOther.mShmAllocator),
+ mCursor(aOther.mCursor),
+ mChunkSize(aOther.mChunkSize) {
+ aOther.mCursor = 0;
+}
+
+ShmSegmentsWriter& ShmSegmentsWriter::operator=(
+ ShmSegmentsWriter&& aOther) noexcept {
+ MOZ_ASSERT(IsEmpty(), "Will forget existing updates!");
+ Clear();
+ mSmallAllocs = std::move(aOther.mSmallAllocs);
+ mLargeAllocs = std::move(aOther.mLargeAllocs);
+ mShmAllocator = aOther.mShmAllocator;
+ mCursor = aOther.mCursor;
+ mChunkSize = aOther.mChunkSize;
+ aOther.mCursor = 0;
+ return *this;
+}
+
+layers::OffsetRange ShmSegmentsWriter::Write(Range<uint8_t> aBytes) {
+ const size_t start = mCursor;
+ const size_t length = aBytes.length();
+
+ if (length >= mChunkSize * 4) {
+ auto range = AllocLargeChunk(length);
+ if (range.length()) {
+ // Allocation was successful
+ uint8_t* dstPtr = mLargeAllocs.LastElement().get<uint8_t>();
+ memcpy(dstPtr, aBytes.begin().get(), length);
+ }
+ return range;
+ }
+
+ int remainingBytesToCopy = length;
+
+ size_t srcCursor = 0;
+ size_t dstCursor = mCursor;
+ size_t currAllocLen = mSmallAllocs.Length();
+
+ while (remainingBytesToCopy > 0) {
+ if (dstCursor >= mSmallAllocs.Length() * mChunkSize) {
+ if (!AllocChunk()) {
+ // Allocation failed, so roll back to the state at the start of this
+ // Write() call and abort.
+ while (mSmallAllocs.Length() > currAllocLen) {
+ RefCountedShmem shm = mSmallAllocs.PopLastElement();
+ RefCountedShm::Dealloc(mShmAllocator, shm);
+ }
+ MOZ_ASSERT(mSmallAllocs.Length() == currAllocLen);
+ return layers::OffsetRange(0, start, 0);
+ }
+ // Allocation succeeded, so dstCursor should now be pointing to
+ // something inside the allocation buffer
+ MOZ_ASSERT(dstCursor < (mSmallAllocs.Length() * mChunkSize));
+ }
+
+ const size_t dstMaxOffset = mChunkSize * mSmallAllocs.Length();
+ const size_t dstBaseOffset = mChunkSize * (mSmallAllocs.Length() - 1);
+
+ MOZ_ASSERT(dstCursor >= dstBaseOffset);
+ MOZ_ASSERT(dstCursor <= dstMaxOffset);
+
+ size_t availableRange = dstMaxOffset - dstCursor;
+ size_t copyRange = std::min<int>(availableRange, remainingBytesToCopy);
+
+ uint8_t* srcPtr = &aBytes[srcCursor];
+ uint8_t* dstPtr = RefCountedShm::GetBytes(mSmallAllocs.LastElement()) +
+ (dstCursor - dstBaseOffset);
+
+ memcpy(dstPtr, srcPtr, copyRange);
+
+ srcCursor += copyRange;
+ dstCursor += copyRange;
+ remainingBytesToCopy -= copyRange;
+
+ // sanity check
+ MOZ_ASSERT(remainingBytesToCopy >= 0);
+ }
+
+ mCursor += length;
+
+ return layers::OffsetRange(0, start, length);
+}
+
+bool ShmSegmentsWriter::AllocChunk() {
+ RefCountedShmem shm;
+ if (!mShmAllocator->AllocResourceShmem(mChunkSize, shm)) {
+ gfxCriticalNote << "ShmSegmentsWriter failed to allocate chunk #"
+ << mSmallAllocs.Length();
+ MOZ_ASSERT(false, "ShmSegmentsWriter fails to allocate chunk");
+ return false;
+ }
+ RefCountedShm::AddRef(shm);
+ mSmallAllocs.AppendElement(shm);
+ return true;
+}
+
+layers::OffsetRange ShmSegmentsWriter::AllocLargeChunk(size_t aSize) {
+ ipc::Shmem shm;
+ if (!mShmAllocator->AllocShmem(aSize, &shm)) {
+ gfxCriticalNote
+ << "ShmSegmentsWriter failed to allocate large chunk of size " << aSize;
+ MOZ_ASSERT(false, "ShmSegmentsWriter fails to allocate large chunk");
+ return layers::OffsetRange(0, 0, 0);
+ }
+ mLargeAllocs.AppendElement(shm);
+
+ return layers::OffsetRange(mLargeAllocs.Length(), 0, aSize);
+}
+
+void ShmSegmentsWriter::Flush(nsTArray<RefCountedShmem>& aSmallAllocs,
+ nsTArray<ipc::Shmem>& aLargeAllocs) {
+ MOZ_ASSERT(aSmallAllocs.IsEmpty());
+ MOZ_ASSERT(aLargeAllocs.IsEmpty());
+ aSmallAllocs = std::move(mSmallAllocs);
+ aLargeAllocs = std::move(mLargeAllocs);
+ mCursor = 0;
+}
+
+bool ShmSegmentsWriter::IsEmpty() const { return mCursor == 0; }
+
+void ShmSegmentsWriter::Clear() {
+ if (mShmAllocator) {
+ IpcResourceUpdateQueue::ReleaseShmems(mShmAllocator, mSmallAllocs);
+ IpcResourceUpdateQueue::ReleaseShmems(mShmAllocator, mLargeAllocs);
+ }
+ mCursor = 0;
+}
+
+ShmSegmentsReader::ShmSegmentsReader(
+ const nsTArray<RefCountedShmem>& aSmallShmems,
+ const nsTArray<ipc::Shmem>& aLargeShmems)
+ : mSmallAllocs(aSmallShmems), mLargeAllocs(aLargeShmems), mChunkSize(0) {
+ if (mSmallAllocs.IsEmpty()) {
+ return;
+ }
+
+ mChunkSize = RefCountedShm::GetSize(mSmallAllocs[0]);
+
+ // Check that all shmems are readable and have the same size. If anything
+ // isn't right, set mChunkSize to zero which signifies that the reader is
+ // in an invalid state and Read calls will return false;
+ for (const auto& shm : mSmallAllocs) {
+ if (!RefCountedShm::IsValid(shm) ||
+ RefCountedShm::GetSize(shm) != mChunkSize ||
+ RefCountedShm::GetBytes(shm) == nullptr) {
+ mChunkSize = 0;
+ return;
+ }
+ }
+
+ for (const auto& shm : mLargeAllocs) {
+ if (!shm.IsReadable() || shm.get<uint8_t>() == nullptr) {
+ mChunkSize = 0;
+ return;
+ }
+ }
+}
+
+bool ShmSegmentsReader::ReadLarge(const layers::OffsetRange& aRange,
+ wr::Vec<uint8_t>& aInto) {
+ // source = zero is for small allocs.
+ MOZ_RELEASE_ASSERT(aRange.source() != 0);
+ if (aRange.source() > mLargeAllocs.Length()) {
+ return false;
+ }
+ size_t id = aRange.source() - 1;
+ const ipc::Shmem& shm = mLargeAllocs[id];
+ if (shm.Size<uint8_t>() < aRange.length()) {
+ return false;
+ }
+
+ uint8_t* srcPtr = shm.get<uint8_t>();
+ aInto.PushBytes(Range<uint8_t>(srcPtr, aRange.length()));
+
+ return true;
+}
+
+bool ShmSegmentsReader::Read(const layers::OffsetRange& aRange,
+ wr::Vec<uint8_t>& aInto) {
+ if (aRange.length() == 0) {
+ return true;
+ }
+
+ if (aRange.source() != 0) {
+ return ReadLarge(aRange, aInto);
+ }
+
+ if (mChunkSize == 0) {
+ return false;
+ }
+
+ if (aRange.start() + aRange.length() > mChunkSize * mSmallAllocs.Length()) {
+ return false;
+ }
+
+ size_t initialLength = aInto.Length();
+
+ size_t srcCursor = aRange.start();
+ size_t remainingBytesToCopy = aRange.length();
+ while (remainingBytesToCopy > 0) {
+ const size_t shm_idx = srcCursor / mChunkSize;
+ const size_t ptrOffset = srcCursor % mChunkSize;
+ const size_t copyRange =
+ std::min(remainingBytesToCopy, mChunkSize - ptrOffset);
+ uint8_t* srcPtr =
+ RefCountedShm::GetBytes(mSmallAllocs[shm_idx]) + ptrOffset;
+
+ aInto.PushBytes(Range<uint8_t>(srcPtr, copyRange));
+
+ srcCursor += copyRange;
+ remainingBytesToCopy -= copyRange;
+ }
+
+ return aInto.Length() - initialLength == aRange.length();
+}
+
+Maybe<Range<uint8_t>> ShmSegmentsReader::GetReadPointerLarge(
+ const layers::OffsetRange& aRange) {
+ // source = zero is for small allocs.
+ MOZ_RELEASE_ASSERT(aRange.source() != 0);
+ if (aRange.source() > mLargeAllocs.Length()) {
+ return Nothing();
+ }
+ size_t id = aRange.source() - 1;
+ const ipc::Shmem& shm = mLargeAllocs[id];
+ if (shm.Size<uint8_t>() < aRange.length()) {
+ return Nothing();
+ }
+
+ uint8_t* srcPtr = shm.get<uint8_t>();
+ return Some(Range<uint8_t>(srcPtr, aRange.length()));
+}
+
+Maybe<Range<uint8_t>> ShmSegmentsReader::GetReadPointer(
+ const layers::OffsetRange& aRange) {
+ if (aRange.length() == 0) {
+ return Some(Range<uint8_t>());
+ }
+
+ if (aRange.source() != 0) {
+ return GetReadPointerLarge(aRange);
+ }
+
+ if (mChunkSize == 0 ||
+ aRange.start() + aRange.length() > mChunkSize * mSmallAllocs.Length()) {
+ return Nothing();
+ }
+
+ size_t srcCursor = aRange.start();
+ size_t remainingBytesToCopy = aRange.length();
+ const size_t shm_idx = srcCursor / mChunkSize;
+ const size_t ptrOffset = srcCursor % mChunkSize;
+ // Return nothing if we can't return a pointer to the full range
+ if (mChunkSize - ptrOffset < remainingBytesToCopy) {
+ return Nothing();
+ }
+ uint8_t* srcPtr = RefCountedShm::GetBytes(mSmallAllocs[shm_idx]) + ptrOffset;
+ return Some(Range<uint8_t>(srcPtr, remainingBytesToCopy));
+}
+
+IpcResourceUpdateQueue::IpcResourceUpdateQueue(
+ layers::WebRenderBridgeChild* aAllocator, size_t aChunkSize)
+ : mWriter(aAllocator, aChunkSize) {}
+
+IpcResourceUpdateQueue::IpcResourceUpdateQueue(
+ IpcResourceUpdateQueue&& aOther) noexcept
+ : mWriter(std::move(aOther.mWriter)),
+ mUpdates(std::move(aOther.mUpdates)) {}
+
+IpcResourceUpdateQueue& IpcResourceUpdateQueue::operator=(
+ IpcResourceUpdateQueue&& aOther) noexcept {
+ MOZ_ASSERT(IsEmpty(), "Will forget existing updates!");
+ mWriter = std::move(aOther.mWriter);
+ mUpdates = std::move(aOther.mUpdates);
+ return *this;
+}
+
+void IpcResourceUpdateQueue::ReplaceResources(IpcResourceUpdateQueue&& aOther) {
+ MOZ_ASSERT(IsEmpty(), "Will forget existing updates!");
+ mWriter = std::move(aOther.mWriter);
+ mUpdates = std::move(aOther.mUpdates);
+}
+
+bool IpcResourceUpdateQueue::AddImage(ImageKey key,
+ const ImageDescriptor& aDescriptor,
+ Range<uint8_t> aBytes) {
+ auto bytes = mWriter.Write(aBytes);
+ if (!bytes.length()) {
+ return false;
+ }
+ mUpdates.AppendElement(layers::OpAddImage(aDescriptor, bytes, 0, key));
+ return true;
+}
+
+bool IpcResourceUpdateQueue::AddBlobImage(BlobImageKey key,
+ const ImageDescriptor& aDescriptor,
+ Range<uint8_t> aBytes,
+ ImageIntRect aVisibleRect) {
+ MOZ_RELEASE_ASSERT(aDescriptor.width > 0 && aDescriptor.height > 0);
+ auto bytes = mWriter.Write(aBytes);
+ if (!bytes.length()) {
+ return false;
+ }
+ mUpdates.AppendElement(
+ layers::OpAddBlobImage(aDescriptor, bytes, aVisibleRect, 0, key));
+ return true;
+}
+
+void IpcResourceUpdateQueue::AddSharedExternalImage(wr::ExternalImageId aExtId,
+ wr::ImageKey aKey) {
+ mUpdates.AppendElement(layers::OpAddSharedExternalImage(aExtId, aKey));
+}
+
+void IpcResourceUpdateQueue::PushExternalImageForTexture(
+ wr::ExternalImageId aExtId, wr::ImageKey aKey,
+ layers::TextureClient* aTexture, bool aIsUpdate) {
+ MOZ_ASSERT(aTexture);
+ MOZ_ASSERT(aTexture->GetIPDLActor());
+ MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() ==
+ mWriter.WrBridge()->GetIPCChannel());
+ mUpdates.AppendElement(layers::OpPushExternalImageForTexture(
+ aExtId, aKey, WrapNotNull(aTexture->GetIPDLActor()), aIsUpdate));
+}
+
+bool IpcResourceUpdateQueue::UpdateImageBuffer(
+ ImageKey aKey, const ImageDescriptor& aDescriptor, Range<uint8_t> aBytes) {
+ auto bytes = mWriter.Write(aBytes);
+ if (!bytes.length()) {
+ return false;
+ }
+ mUpdates.AppendElement(layers::OpUpdateImage(aDescriptor, bytes, aKey));
+ return true;
+}
+
+bool IpcResourceUpdateQueue::UpdateBlobImage(BlobImageKey aKey,
+ const ImageDescriptor& aDescriptor,
+ Range<uint8_t> aBytes,
+ ImageIntRect aVisibleRect,
+ ImageIntRect aDirtyRect) {
+ MOZ_ASSERT(aVisibleRect.width > 0 && aVisibleRect.height > 0);
+
+ auto bytes = mWriter.Write(aBytes);
+ if (!bytes.length()) {
+ return false;
+ }
+ mUpdates.AppendElement(layers::OpUpdateBlobImage(aDescriptor, bytes, aKey,
+ aVisibleRect, aDirtyRect));
+ return true;
+}
+
+void IpcResourceUpdateQueue::UpdateSharedExternalImage(
+ wr::ExternalImageId aExtId, wr::ImageKey aKey, ImageIntRect aDirtyRect) {
+ mUpdates.AppendElement(
+ layers::OpUpdateSharedExternalImage(aExtId, aKey, aDirtyRect));
+}
+
+void IpcResourceUpdateQueue::SetBlobImageVisibleArea(
+ wr::BlobImageKey aKey, const ImageIntRect& aArea) {
+ mUpdates.AppendElement(layers::OpSetBlobImageVisibleArea(aArea, aKey));
+}
+
+void IpcResourceUpdateQueue::DeleteImage(ImageKey aKey) {
+ mUpdates.AppendElement(layers::OpDeleteImage(aKey));
+}
+
+void IpcResourceUpdateQueue::DeleteBlobImage(BlobImageKey aKey) {
+ mUpdates.AppendElement(layers::OpDeleteBlobImage(aKey));
+}
+
+bool IpcResourceUpdateQueue::AddRawFont(wr::FontKey aKey, Range<uint8_t> aBytes,
+ uint32_t aIndex) {
+ auto bytes = mWriter.Write(aBytes);
+ if (!bytes.length()) {
+ return false;
+ }
+ mUpdates.AppendElement(layers::OpAddRawFont(bytes, aIndex, aKey));
+ return true;
+}
+
+bool IpcResourceUpdateQueue::AddFontDescriptor(wr::FontKey aKey,
+ Range<uint8_t> aBytes,
+ uint32_t aIndex) {
+ auto bytes = mWriter.Write(aBytes);
+ if (!bytes.length()) {
+ return false;
+ }
+ mUpdates.AppendElement(layers::OpAddFontDescriptor(bytes, aIndex, aKey));
+ return true;
+}
+
+void IpcResourceUpdateQueue::DeleteFont(wr::FontKey aKey) {
+ mUpdates.AppendElement(layers::OpDeleteFont(aKey));
+}
+
+void IpcResourceUpdateQueue::AddFontInstance(
+ wr::FontInstanceKey aKey, wr::FontKey aFontKey, float aGlyphSize,
+ const wr::FontInstanceOptions* aOptions,
+ const wr::FontInstancePlatformOptions* aPlatformOptions,
+ Range<const gfx::FontVariation> aVariations) {
+ auto bytes = mWriter.WriteAsBytes(aVariations);
+ mUpdates.AppendElement(layers::OpAddFontInstance(
+ aOptions ? Some(*aOptions) : Nothing(),
+ aPlatformOptions ? Some(*aPlatformOptions) : Nothing(), bytes, aKey,
+ aFontKey, aGlyphSize));
+}
+
+void IpcResourceUpdateQueue::DeleteFontInstance(wr::FontInstanceKey aKey) {
+ mUpdates.AppendElement(layers::OpDeleteFontInstance(aKey));
+}
+
+void IpcResourceUpdateQueue::Flush(
+ nsTArray<layers::OpUpdateResource>& aUpdates,
+ nsTArray<layers::RefCountedShmem>& aSmallAllocs,
+ nsTArray<ipc::Shmem>& aLargeAllocs) {
+ aUpdates = std::move(mUpdates);
+ mWriter.Flush(aSmallAllocs, aLargeAllocs);
+}
+
+bool IpcResourceUpdateQueue::IsEmpty() const {
+ if (mUpdates.Length() == 0) {
+ MOZ_ASSERT(mWriter.IsEmpty());
+ return true;
+ }
+ return false;
+}
+
+void IpcResourceUpdateQueue::Clear() {
+ mWriter.Clear();
+ mUpdates.Clear();
+}
+
+// static
+void IpcResourceUpdateQueue::ReleaseShmems(
+ ipc::IProtocol* aShmAllocator, nsTArray<layers::RefCountedShmem>& aShms) {
+ for (auto& shm : aShms) {
+ if (RefCountedShm::IsValid(shm) && RefCountedShm::Release(shm) == 0) {
+ RefCountedShm::Dealloc(aShmAllocator, shm);
+ }
+ }
+ aShms.Clear();
+}
+
+// static
+void IpcResourceUpdateQueue::ReleaseShmems(ipc::IProtocol* aShmAllocator,
+ nsTArray<ipc::Shmem>& aShms) {
+ for (auto& shm : aShms) {
+ aShmAllocator->DeallocShmem(shm);
+ }
+ aShms.Clear();
+}
+
+} // namespace wr
+} // namespace mozilla