Bug 1331938 - Part 2. Add SourceSurfaceSharedData, a shared data backed DataSourceSurface. r=nical

This commit is contained in:
Andrew Osmond 2017-01-18 10:12:32 -05:00
Родитель 608d601120
Коммит cb555a4ef6
12 изменённых файлов: 327 добавлений и 6 удалений

Просмотреть файл

@ -916,7 +916,7 @@ ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLCanvasElement& aCanvas
aCanvasEl.GetCurrentContextType() == CanvasContextType::WebGL2) &&
aCropRect.isSome()) {
// The _surface_ must be a DataSourceSurface.
MOZ_ASSERT(surface->GetType() == SurfaceType::DATA,
MOZ_ASSERT(surface->IsDataSourceSurface(),
"The snapshot SourceSurface from WebGL rendering contest is not \
DataSourceSurface.");
RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();

Просмотреть файл

@ -352,6 +352,16 @@ public:
*/
virtual bool IsValid() const { return true; }
/**
* This function will return true if the surface type matches that of a
* DataSourceSurface and if GetDataSurface will return the same object.
*/
bool IsDataSourceSurface() const {
SurfaceType type = GetType();
return type == SurfaceType::DATA ||
type == SurfaceType::DATA_SHARED;
}
/**
* This function will get a DataSourceSurface for this surface, a
* DataSourceSurface's data can be accessed directly.

Просмотреть файл

@ -13,7 +13,7 @@ already_AddRefed<DataSourceSurface>
DataSourceSurface::GetDataSurface()
{
RefPtr<DataSourceSurface> surface =
(GetType() == SurfaceType::DATA) ? this : new DataSourceSurfaceWrapper(this);
IsDataSourceSurface() ? this : new DataSourceSurfaceWrapper(this);
return surface.forget();
}

Просмотреть файл

@ -465,6 +465,9 @@ public:
case SurfaceType::TILED:
mMessage << "SurfaceType::TILED";
break;
case SurfaceType::DATA_SHARED:
mMessage << "SurfaceType::DATA_SHARED";
break;
default:
mMessage << "Invalid SurfaceType (" << (int)aType << ")";
break;

Просмотреть файл

@ -29,7 +29,8 @@ enum class SurfaceType : int8_t {
DUAL_DT, /* Snapshot of a dual drawtarget */
D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
RECORDING, /* Surface used for recording */
TILED /* Surface from a tiled DrawTarget */
TILED, /* Surface from a tiled DrawTarget */
DATA_SHARED, /* Data surface using shared memory */
};
enum class SurfaceFormat : int8_t {

Просмотреть файл

@ -348,6 +348,8 @@ AppendToString(std::stringstream& aStream, gfx::SurfaceType aType,
aStream << "SurfaceType::RECORDING"; break;
case SurfaceType::TILED:
aStream << "SurfaceType::TILED"; break;
case SurfaceType::DATA_SHARED:
aStream << "SurfaceType::DATA_SHARED"; break;
default:
NS_ERROR("unknown surface type");
aStream << "???";

Просмотреть файл

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/Types.h" // for decltype
namespace mozilla {
namespace gfx {
bool
SourceSurfaceSharedData::Init(const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat)
{
mSize = aSize;
mStride = aStride;
mFormat = aFormat;
size_t len = GetAlignedDataLength();
mBuf = new SharedMemoryBasic();
if (NS_WARN_IF(!mBuf->Create(len)) ||
NS_WARN_IF(!mBuf->Map(len))) {
mBuf = nullptr;
return false;
}
return true;
}
void
SourceSurfaceSharedData::GuaranteePersistance()
{
// Shared memory is not unmapped until we release SourceSurfaceSharedData.
}
void
SourceSurfaceSharedData::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
size_t& aHeapSizeOut,
size_t& aNonHeapSizeOut) const
{
if (mBuf) {
aNonHeapSizeOut += GetAlignedDataLength();
}
}
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::ShareToProcess(base::ProcessId aPid,
SharedMemoryBasic::Handle& aHandle)
{
MutexAutoLock lock(mMutex);
if (mClosed) {
return NS_ERROR_NOT_AVAILABLE;
}
bool shared = mBuf->ShareToProcess(aPid, &aHandle);
if (MOZ_UNLIKELY(!shared)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
void
SourceSurfaceSharedData::CloseHandleInternal()
{
mMutex.AssertCurrentThreadOwns();
if (mClosed) {
return;
}
if (mFinalized && mShared) {
mBuf->CloseHandle();
mClosed = true;
}
}
bool
SourceSurfaceSharedData::ReallocHandle()
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mClosed);
MOZ_ASSERT(mFinalized);
size_t len = GetAlignedDataLength();
RefPtr<SharedMemoryBasic> buf = new SharedMemoryBasic();
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);
buf->Protect(static_cast<char*>(buf->memory()), len, RightsRead);
if (mMapCount > 0 && !mOldBuf) {
mOldBuf = Move(mBuf);
}
mBuf = Move(buf);
mClosed = false;
mShared = false;
return true;
}
void
SourceSurfaceSharedData::Finalize()
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(!mClosed);
MOZ_ASSERT(!mFinalized);
size_t len = GetAlignedDataLength();
mBuf->Protect(static_cast<char*>(mBuf->memory()), len, RightsRead);
mFinalized = true;
CloseHandleInternal();
}
} // namespace gfx
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,163 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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/. */
#ifndef MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_
#define MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_
#include "mozilla/gfx/2D.h"
#include "mozilla/Mutex.h"
#include "mozilla/ipc/SharedMemoryBasic.h"
namespace mozilla {
namespace gfx {
/**
* This class is used to wrap shared (as in process) data buffers used by a
* source surface.
*/
class SourceSurfaceSharedData final : public DataSourceSurface
{
typedef mozilla::ipc::SharedMemoryBasic SharedMemoryBasic;
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceSharedData, override)
SourceSurfaceSharedData()
: mMutex("SourceSurfaceSharedData")
, mStride(0)
, mMapCount(0)
, mFormat(SurfaceFormat::UNKNOWN)
, mClosed(false)
, mFinalized(false)
, mShared(false)
{
}
bool Init(const IntSize &aSize,
int32_t aStride,
SurfaceFormat aFormat);
uint8_t* GetData() override
{
MutexAutoLock lock(mMutex);
return GetDataInternal();
}
int32_t Stride() override { return mStride; }
SurfaceType GetType() const override { return SurfaceType::DATA_SHARED; }
IntSize GetSize() const override { return mSize; }
SurfaceFormat GetFormat() const override { return mFormat; }
void GuaranteePersistance() override;
void AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
size_t& aHeapSizeOut,
size_t& aNonHeapSizeOut) const override;
/**
* Although Map (and Moz2D in general) isn't normally threadsafe,
* we want to allow it for SourceSurfaceSharedData since it should
* always be fine (for reading at least).
*
* This is the same as the base class implementation except using
* mMapCount instead of mIsMapped since that breaks for multithread.
*
* Additionally if a reallocation happened while there were active
* mappings, then we guarantee that GetData will continue to return
* the same data pointer by retaining the old shared buffer until
* the last mapping is freed via Unmap.
*/
bool Map(MapType, MappedSurface *aMappedSurface) override
{
MutexAutoLock lock(mMutex);
++mMapCount;
aMappedSurface->mData = GetDataInternal();
aMappedSurface->mStride = mStride;
return true;
}
void Unmap() override
{
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mMapCount > 0);
if (--mMapCount == 0) {
mOldBuf = nullptr;
}
}
/**
* Get a handle to share to another process for this buffer. Returns:
* NS_OK -- success, aHandle is valid.
* NS_ERROR_NOT_AVAILABLE -- handle was closed, need to reallocate.
* NS_ERROR_FAILURE -- failed to create a handle to share.
*/
nsresult ShareToProcess(base::ProcessId aPid,
SharedMemoryBasic::Handle& aHandle);
/**
* Indicates the buffer is not expected to be shared with any more processes.
* May release the handle if possible (see CloseHandleInternal). */
void FinishedSharing()
{
MutexAutoLock lock(mMutex);
mShared = true;
CloseHandleInternal();
}
/**
* Allocate a new shared memory buffer so that we can get a new handle for
* sharing to new processes. ShareToProcess must have failed with
* NS_ERROR_NOT_AVAILABLE in order for this to be safe to call. Returns true
* if the operation succeeds. If it fails, there is no state change.
*/
bool ReallocHandle();
/**
* Indicates we have finished writing to the buffer and it may be marked as
* read only. May release the handle if possible (see CloseHandleInternal).
*/
void Finalize();
private:
~SourceSurfaceSharedData() override
{
MOZ_ASSERT(mMapCount == 0);
}
uint8_t* GetDataInternal() const;
size_t GetDataLength() const
{
return static_cast<size_t>(mStride) * mSize.height;
}
size_t GetAlignedDataLength() const
{
return mozilla::ipc::SharedMemory::PageAlignedSize(GetDataLength());
}
/**
* Attempt to close the handle. Only if the buffer has been both finalized
* and we have completed sharing will it be released.
*/
void CloseHandleInternal();
Mutex mMutex;
int32_t mStride;
int32_t mMapCount;
IntSize mSize;
RefPtr<SharedMemoryBasic> mBuf;
RefPtr<SharedMemoryBasic> mOldBuf;
SurfaceFormat mFormat;
bool mClosed : 1;
bool mFinalized : 1;
bool mShared : 1;
};
} // namespace gfx
} // namespace mozilla
#endif /* MOZILLA_GFX_SOURCESURFACESHAREDDATA_H_ */

Просмотреть файл

@ -201,6 +201,7 @@ EXPORTS.mozilla.layers += [
'opengl/TextureHostOGL.h',
'PersistentBufferProvider.h',
'RenderTrace.h',
'SourceSurfaceSharedData.h',
'SourceSurfaceVolatileData.h',
'TextureWrapperImage.h',
'TransactionIdAllocator.h',
@ -374,6 +375,7 @@ UNIFIED_SOURCES += [
'RenderTrace.cpp',
'RotatedBuffer.cpp',
'ShareableCanvasLayer.cpp',
'SourceSurfaceSharedData.cpp',
'SourceSurfaceVolatileData.cpp',
'TextureWrapperImage.cpp',
]

Просмотреть файл

@ -885,7 +885,7 @@ gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height);
if (aSurface->GetType() != SurfaceType::DATA) {
if (!aSurface->IsDataSourceSurface()) {
// If the surface is NOT of type DATA then its data is not mapped into main
// memory. Format conversion is probably faster on the GPU, and by doing it
// there we can avoid any expensive uploads/readbacks except for (possibly)

Просмотреть файл

@ -47,7 +47,7 @@ public:
imgIContainer::DECODE_FLAGS_DEFAULT);
ASSERT_TRUE(mSurface != nullptr);
EXPECT_EQ(SurfaceType::DATA, mSurface->GetType());
EXPECT_TRUE(mSurface->IsDataSourceSurface());
EXPECT_TRUE(mSurface->GetFormat() == SurfaceFormat::B8G8R8X8 ||
mSurface->GetFormat() == SurfaceFormat::B8G8R8A8);
EXPECT_EQ(mTestCase.mSize, mSurface->GetSize());

Просмотреть файл

@ -64,7 +64,7 @@ CheckDecoderState(const ImageTestCase& aTestCase, Decoder* aDecoder)
RefPtr<SourceSurface> surface = currentFrame->GetSourceSurface();
// Verify that the resulting surfaces matches our expectations.
EXPECT_EQ(SurfaceType::DATA, surface->GetType());
EXPECT_TRUE(surface->IsDataSourceSurface());
EXPECT_TRUE(surface->GetFormat() == SurfaceFormat::B8G8R8X8 ||
surface->GetFormat() == SurfaceFormat::B8G8R8A8);
EXPECT_EQ(aTestCase.mOutputSize, surface->GetSize());