Backed out changeset 8d9053f1c203 (bug 1942129) Backed out changeset 393e3c507c27 (bug 1942129) Backed out changeset 8240d353d224 (bug 1942129) Backed out changeset 8c4cd026b720 (bug 1942129) Backed out changeset 742634b0d6e9 (bug 1942129) Backed out changeset d16857f9812f (bug 1942129) Backed out changeset 7ff7af041ee7 (bug 1942129) Backed out changeset ef41d9e4c7de (bug 1942129)
279 lines
7.6 KiB
C++
279 lines
7.6 KiB
C++
/* -*- 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 "SourceSurfaceSharedData.h"
|
|
|
|
#include "mozilla/Likely.h"
|
|
#include "mozilla/StaticPrefs_image.h"
|
|
#include "mozilla/Types.h" // for decltype
|
|
#include "mozilla/layers/SharedSurfacesChild.h"
|
|
#include "mozilla/layers/SharedSurfacesParent.h"
|
|
#include "nsDebug.h" // for NS_ABORT_OOM
|
|
|
|
#include "base/process_util.h"
|
|
|
|
#ifdef DEBUG
|
|
/**
|
|
* If defined, this makes SourceSurfaceSharedData::Finalize memory protect the
|
|
* underlying shared buffer in the producing process (the content or UI
|
|
* process). Given flushing the page table is expensive, and its utility is
|
|
* predominantly diagnostic (in case of overrun), turn it off by default.
|
|
*/
|
|
# define SHARED_SURFACE_PROTECT_FINALIZED
|
|
#endif
|
|
|
|
using namespace mozilla::layers;
|
|
|
|
namespace mozilla {
|
|
namespace gfx {
|
|
|
|
void SourceSurfaceSharedDataWrapper::Init(const IntSize& aSize, int32_t aStride,
|
|
SurfaceFormat aFormat,
|
|
SharedMemory::Handle aHandle,
|
|
base::ProcessId aCreatorPid) {
|
|
MOZ_ASSERT(!mBuf);
|
|
mSize = aSize;
|
|
mStride = aStride;
|
|
mFormat = aFormat;
|
|
mCreatorPid = aCreatorPid;
|
|
|
|
size_t len = GetAlignedDataLength();
|
|
mBuf = MakeAndAddRef<SharedMemory>();
|
|
if (!mBuf->SetHandle(std::move(aHandle), ipc::SharedMemory::RightsReadOnly)) {
|
|
MOZ_CRASH("Invalid shared memory handle!");
|
|
}
|
|
|
|
bool mapped = EnsureMapped(len);
|
|
if ((sizeof(uintptr_t) <= 4 ||
|
|
StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) &&
|
|
len / 1024 >
|
|
StaticPrefs::image_mem_shared_unmap_min_threshold_kb_AtStartup()) {
|
|
mHandleLock.emplace("SourceSurfaceSharedDataWrapper::mHandleLock");
|
|
|
|
if (mapped) {
|
|
// Tracking at the initial mapping, and not just after the first use of
|
|
// the surface means we might get unmapped again before the next frame
|
|
// gets rendered if a low virtual memory condition persists.
|
|
SharedSurfacesParent::AddTracking(this);
|
|
}
|
|
} else if (!mapped) {
|
|
// We don't support unmapping for this surface, and we failed to map it.
|
|
NS_ABORT_OOM(len);
|
|
} else {
|
|
mBuf->CloseHandle();
|
|
}
|
|
}
|
|
|
|
void SourceSurfaceSharedDataWrapper::Init(SourceSurfaceSharedData* aSurface) {
|
|
MOZ_ASSERT(!mBuf);
|
|
MOZ_ASSERT(aSurface);
|
|
mSize = aSurface->mSize;
|
|
mStride = aSurface->mStride;
|
|
mFormat = aSurface->mFormat;
|
|
mCreatorPid = base::GetCurrentProcId();
|
|
mBuf = aSurface->mBuf;
|
|
}
|
|
|
|
bool SourceSurfaceSharedDataWrapper::EnsureMapped(size_t aLength) {
|
|
MOZ_ASSERT(!GetData());
|
|
|
|
while (!mBuf->Map(aLength)) {
|
|
nsTArray<RefPtr<SourceSurfaceSharedDataWrapper>> expired;
|
|
if (!SharedSurfacesParent::AgeOneGeneration(expired)) {
|
|
return false;
|
|
}
|
|
MOZ_ASSERT(!expired.Contains(this));
|
|
SharedSurfacesParent::ExpireMap(expired);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SourceSurfaceSharedDataWrapper::Map(MapType aMapType,
|
|
MappedSurface* aMappedSurface) {
|
|
uint8_t* dataPtr;
|
|
|
|
if (aMapType != MapType::READ) {
|
|
// The data may be write-protected
|
|
return false;
|
|
}
|
|
|
|
if (mHandleLock) {
|
|
MutexAutoLock lock(*mHandleLock);
|
|
dataPtr = GetData();
|
|
if (mMapCount == 0) {
|
|
if (mConsumers > 0) {
|
|
SharedSurfacesParent::RemoveTracking(this);
|
|
}
|
|
if (!dataPtr) {
|
|
size_t len = GetAlignedDataLength();
|
|
if (!EnsureMapped(len)) {
|
|
NS_ABORT_OOM(len);
|
|
}
|
|
dataPtr = GetData();
|
|
}
|
|
}
|
|
++mMapCount;
|
|
} else {
|
|
dataPtr = GetData();
|
|
++mMapCount;
|
|
}
|
|
|
|
MOZ_ASSERT(dataPtr);
|
|
aMappedSurface->mData = dataPtr;
|
|
aMappedSurface->mStride = mStride;
|
|
return true;
|
|
}
|
|
|
|
void SourceSurfaceSharedDataWrapper::Unmap() {
|
|
if (mHandleLock) {
|
|
MutexAutoLock lock(*mHandleLock);
|
|
if (--mMapCount == 0 && mConsumers > 0) {
|
|
SharedSurfacesParent::AddTracking(this);
|
|
}
|
|
} else {
|
|
--mMapCount;
|
|
}
|
|
MOZ_ASSERT(mMapCount >= 0);
|
|
}
|
|
|
|
void SourceSurfaceSharedDataWrapper::ExpireMap() {
|
|
MutexAutoLock lock(*mHandleLock);
|
|
if (mMapCount == 0) {
|
|
mBuf->Unmap();
|
|
}
|
|
}
|
|
|
|
bool SourceSurfaceSharedData::Init(const IntSize& aSize, int32_t aStride,
|
|
SurfaceFormat aFormat,
|
|
bool aShare /* = true */) {
|
|
mSize = aSize;
|
|
mStride = aStride;
|
|
mFormat = aFormat;
|
|
|
|
size_t len = GetAlignedDataLength();
|
|
mBuf = new SharedMemory();
|
|
if (NS_WARN_IF(!mBuf->Create(len)) || NS_WARN_IF(!mBuf->Map(len))) {
|
|
mBuf = nullptr;
|
|
return false;
|
|
}
|
|
|
|
if (aShare) {
|
|
layers::SharedSurfacesChild::Share(this);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void SourceSurfaceSharedData::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
|
|
SizeOfInfo& aInfo) const {
|
|
MutexAutoLock lock(mMutex);
|
|
aInfo.AddType(SurfaceType::DATA_SHARED);
|
|
if (mBuf) {
|
|
aInfo.mNonHeapBytes = GetAlignedDataLength();
|
|
}
|
|
if (!mClosed) {
|
|
aInfo.mExternalHandles = 1;
|
|
}
|
|
Maybe<wr::ExternalImageId> extId = SharedSurfacesChild::GetExternalId(this);
|
|
if (extId) {
|
|
aInfo.mExternalId = wr::AsUint64(extId.ref());
|
|
}
|
|
}
|
|
|
|
uint8_t* SourceSurfaceSharedData::GetDataInternal() const {
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
// If we have an old buffer lingering, it is because we get reallocated to
|
|
// get a new handle to share, but there were still active mappings.
|
|
if (MOZ_UNLIKELY(mOldBuf)) {
|
|
MOZ_ASSERT(mMapCount > 0);
|
|
MOZ_ASSERT(mFinalized);
|
|
return static_cast<uint8_t*>(mOldBuf->Memory());
|
|
}
|
|
return static_cast<uint8_t*>(mBuf->Memory());
|
|
}
|
|
|
|
nsresult SourceSurfaceSharedData::CloneHandle(SharedMemory::Handle& aHandle) {
|
|
MutexAutoLock lock(mMutex);
|
|
MOZ_ASSERT(mHandleCount > 0);
|
|
|
|
if (mClosed) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
aHandle = mBuf->CloneHandle();
|
|
if (MOZ_UNLIKELY(!aHandle)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void SourceSurfaceSharedData::CloseHandleInternal() {
|
|
mMutex.AssertCurrentThreadOwns();
|
|
|
|
if (mClosed) {
|
|
MOZ_ASSERT(mHandleCount == 0);
|
|
MOZ_ASSERT(mShared);
|
|
return;
|
|
}
|
|
|
|
if (mShared) {
|
|
mBuf->CloseHandle();
|
|
mClosed = true;
|
|
}
|
|
}
|
|
|
|
bool SourceSurfaceSharedData::ReallocHandle() {
|
|
MutexAutoLock lock(mMutex);
|
|
MOZ_ASSERT(mHandleCount > 0);
|
|
MOZ_ASSERT(mClosed);
|
|
|
|
if (NS_WARN_IF(!mFinalized)) {
|
|
// We haven't finished populating the surface data yet, which means we are
|
|
// out of luck, as we have no means of synchronizing with the producer to
|
|
// write new data to a new buffer. This should be fairly rare, caused by a
|
|
// crash in the GPU process, while we were decoding an image.
|
|
return false;
|
|
}
|
|
|
|
size_t len = GetAlignedDataLength();
|
|
RefPtr<SharedMemory> buf = new SharedMemory();
|
|
if (NS_WARN_IF(!buf->Create(len)) || NS_WARN_IF(!buf->Map(len))) {
|
|
return false;
|
|
}
|
|
|
|
size_t copyLen = GetDataLength();
|
|
memcpy(buf->Memory(), mBuf->Memory(), copyLen);
|
|
#ifdef SHARED_SURFACE_PROTECT_FINALIZED
|
|
buf->Protect(static_cast<char*>(buf->Memory()), len, RightsRead);
|
|
#endif
|
|
|
|
if (mMapCount > 0 && !mOldBuf) {
|
|
mOldBuf = std::move(mBuf);
|
|
}
|
|
mBuf = std::move(buf);
|
|
mClosed = false;
|
|
mShared = false;
|
|
return true;
|
|
}
|
|
|
|
void SourceSurfaceSharedData::Finalize() {
|
|
MutexAutoLock lock(mMutex);
|
|
MOZ_ASSERT(!mFinalized);
|
|
|
|
#ifdef SHARED_SURFACE_PROTECT_FINALIZED
|
|
size_t len = GetAlignedDataLength();
|
|
mBuf->Protect(static_cast<char*>(mBuf->Memory()), len, RightsRead);
|
|
#endif
|
|
|
|
mFinalized = true;
|
|
}
|
|
|
|
} // namespace gfx
|
|
} // namespace mozilla
|