gecko-dev/gfx/layers/TextureDIB.cpp
Andrew Osmond d639d2c5ac Bug 1435586 - Use static gfxPlatform methods directly instead of requiring initialization. r=kats
gfxPlatform::GetSourceSurfaceForSurface and CreateDrawTargetForSurface
are both static methods that we sometimes use via the pattern
gfxPlatform::GetPlatform()->... This is problematic because this forces
gfxPlatform to be initialized in the process, and in the GPU process, we
don't support this. It should be safe to call these methods without
initializing gfxPlatform, so this patch removes the GetPlatform() call.

GetSourceSurfaceForSurface may end up initializing gfxPlatform anyways,
depending on whether or not a DrawTarget was given. This should not be a
concern for the crashes observed in bug 1435586.

Differential Revision: https://phabricator.services.mozilla.com/D33785
2019-06-05 12:40:26 -04:00

467 lines
14 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 "TextureDIB.h"
#include "gfx2DGlue.h"
#include "mozilla/gfx/DataSurfaceHelpers.h" // For BufferSizeFromDimensions
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/layers/ISurfaceAllocator.h"
#include "mozilla/layers/TextureForwarder.h" // For LayersIPCChannel
namespace mozilla {
using namespace gfx;
namespace layers {
/**
* Can only be drawn into through Cairo.
* The coresponding TextureHost depends on the compositor
*/
class MemoryDIBTextureData : public DIBTextureData {
public:
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual TextureData* CreateSimilar(
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
static DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat);
virtual void Deallocate(LayersIPCChannel* aAllocator) override {
mSurface = nullptr;
}
MemoryDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfxWindowsSurface* aSurface)
: DIBTextureData(aSize, aFormat, aSurface) {
MOZ_COUNT_CTOR(MemoryDIBTextureData);
}
virtual ~MemoryDIBTextureData() { MOZ_COUNT_DTOR(MemoryDIBTextureData); }
};
/**
* Can only be drawn into through Cairo.
* The coresponding TextureHost depends on the compositor
*/
class ShmemDIBTextureData : public DIBTextureData {
public:
virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
virtual TextureData* CreateSimilar(
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
TextureFlags aFlags = TextureFlags::DEFAULT,
TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
static DIBTextureData* Create(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
LayersIPCChannel* aAllocator);
void DeallocateData() {
if (mSurface) {
::DeleteObject(mBitmap);
::DeleteDC(mDC);
::CloseHandle(mFileMapping);
mBitmap = NULL;
mDC = NULL;
mFileMapping = NULL;
mSurface = nullptr;
}
}
virtual void Deallocate(LayersIPCChannel* aAllocator) override {
DeallocateData();
}
ShmemDIBTextureData(gfx::IntSize aSize, gfx::SurfaceFormat aFormat,
gfxWindowsSurface* aSurface, HANDLE aFileMapping,
HANDLE aHostHandle, HDC aDC, HBITMAP aBitmap)
: DIBTextureData(aSize, aFormat, aSurface),
mFileMapping(aFileMapping),
mHostHandle(aHostHandle),
mDC(aDC),
mBitmap(aBitmap) {
MOZ_COUNT_CTOR(ShmemDIBTextureData);
}
virtual ~ShmemDIBTextureData() {
MOZ_COUNT_DTOR(ShmemDIBTextureData);
// The host side has its own references and handles to this data, we can
// safely clear ours.
DeallocateData();
}
HANDLE mFileMapping;
HANDLE mHostHandle;
HDC mDC;
HBITMAP mBitmap;
};
void DIBTextureData::FillInfo(TextureData::Info& aInfo) const {
aInfo.size = mSize;
aInfo.format = mFormat;
aInfo.hasIntermediateBuffer = true;
aInfo.hasSynchronization = false;
aInfo.supportsMoz2D = true;
aInfo.canExposeMappedData = false;
}
already_AddRefed<gfx::DrawTarget> DIBTextureData::BorrowDrawTarget() {
return gfxPlatform::CreateDrawTargetForSurface(mSurface, mSize);
}
DIBTextureData* DIBTextureData::Create(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
LayersIPCChannel* aAllocator) {
if (!aAllocator) {
return nullptr;
}
if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
return nullptr;
}
if (aAllocator->IsSameProcess()) {
return MemoryDIBTextureData::Create(aSize, aFormat);
} else {
return ShmemDIBTextureData::Create(aSize, aFormat, aAllocator);
}
}
TextureData* MemoryDIBTextureData::CreateSimilar(
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
if (!aAllocator) {
return nullptr;
}
return MemoryDIBTextureData::Create(mSize, mFormat);
}
bool MemoryDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
MOZ_ASSERT(mSurface);
// The host will release this ref when it receives the surface descriptor.
// We AddRef in case we die before the host receives the pointer.
aOutDescriptor =
SurfaceDescriptorDIB(reinterpret_cast<uintptr_t>(mSurface.get()));
mSurface.get()->AddRef();
return true;
}
DIBTextureData* MemoryDIBTextureData::Create(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat) {
RefPtr<gfxWindowsSurface> surface =
new gfxWindowsSurface(aSize, SurfaceFormatToImageFormat(aFormat));
if (!surface || surface->CairoStatus()) {
NS_WARNING("Could not create DIB surface");
return nullptr;
}
return new MemoryDIBTextureData(aSize, aFormat, surface);
}
bool MemoryDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
if (!srcSurf) {
gfxCriticalError()
<< "Failed to GetDataSurface in UpdateFromSurface (DIB).";
return false;
}
DataSourceSurface::MappedSurface sourceMap;
if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
return false;
}
for (int y = 0; y < srcSurf->GetSize().height; y++) {
memcpy(imgSurf->Data() + imgSurf->Stride() * y,
sourceMap.mData + sourceMap.mStride * y,
srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
}
srcSurf->Unmap();
return true;
}
TextureData* ShmemDIBTextureData::CreateSimilar(
LayersIPCChannel* aAllocator, LayersBackend aLayersBackend,
TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const {
if (!aAllocator) {
return nullptr;
}
return ShmemDIBTextureData::Create(mSize, mFormat, aAllocator);
}
bool ShmemDIBTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface) {
RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
if (!srcSurf) {
gfxCriticalError()
<< "Failed to GetDataSurface in UpdateFromSurface (DTD).";
return false;
}
DataSourceSurface::MappedSurface sourceMap;
if (!srcSurf->Map(gfx::DataSourceSurface::READ, &sourceMap)) {
gfxCriticalError() << "Failed to map source surface for UpdateFromSurface.";
return false;
}
GdiFlush();
uint32_t stride = mSize.width * BytesPerPixel(mFormat);
uint8_t* data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_WRITE, 0, 0,
stride * mSize.height);
if (!data) {
gfxCriticalError() << "Failed to map view of file for UpdateFromSurface.";
srcSurf->Unmap();
return false;
}
for (int y = 0; y < srcSurf->GetSize().height; y++) {
memcpy(data + stride * y, sourceMap.mData + sourceMap.mStride * y,
srcSurf->GetSize().width * BytesPerPixel(srcSurf->GetFormat()));
}
::UnmapViewOfFile(data);
srcSurf->Unmap();
return true;
}
bool ShmemDIBTextureData::Serialize(SurfaceDescriptor& aOutDescriptor) {
if (mFormat == gfx::SurfaceFormat::UNKNOWN) {
return false;
}
::GdiFlush();
aOutDescriptor =
SurfaceDescriptorFileMapping((WindowsHandle)mHostHandle, mFormat, mSize);
return true;
}
DIBTextureData* ShmemDIBTextureData::Create(gfx::IntSize aSize,
gfx::SurfaceFormat aFormat,
LayersIPCChannel* aAllocator) {
MOZ_ASSERT(aAllocator->GetParentPid() != base::ProcessId());
DWORD mapSize = aSize.width * aSize.height * BytesPerPixel(aFormat);
HANDLE fileMapping = ::CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, mapSize, NULL);
if (!fileMapping) {
gfxCriticalError() << "Failed to create memory file mapping for " << mapSize
<< " bytes.";
return nullptr;
}
BITMAPV4HEADER header;
memset(&header, 0, sizeof(BITMAPV4HEADER));
header.bV4Size = sizeof(BITMAPV4HEADER);
header.bV4Width = aSize.width;
header.bV4Height = -LONG(aSize.height); // top-to-buttom DIB
header.bV4Planes = 1;
header.bV4BitCount = 32;
header.bV4V4Compression = BI_BITFIELDS;
header.bV4RedMask = 0x00FF0000;
header.bV4GreenMask = 0x0000FF00;
header.bV4BlueMask = 0x000000FF;
HDC nulldc = ::GetDC(NULL);
HDC dc = ::CreateCompatibleDC(nulldc);
::ReleaseDC(nullptr, nulldc);
if (!dc) {
::CloseHandle(fileMapping);
gfxCriticalError() << "Failed to create DC for bitmap.";
return nullptr;
}
void* bits;
HBITMAP bitmap = ::CreateDIBSection(dc, (BITMAPINFO*)&header, DIB_RGB_COLORS,
&bits, fileMapping, 0);
if (!bitmap) {
gfxCriticalError() << "Failed to create DIB section for a bitmap of size "
<< aSize << " and mapSize " << mapSize;
::CloseHandle(fileMapping);
::DeleteDC(dc);
return nullptr;
}
::SelectObject(dc, bitmap);
RefPtr<gfxWindowsSurface> surface = new gfxWindowsSurface(dc, 0);
if (surface->CairoStatus()) {
::DeleteObject(bitmap);
::DeleteDC(dc);
::CloseHandle(fileMapping);
gfxCriticalError() << "Could not create surface, status: "
<< surface->CairoStatus();
return nullptr;
}
HANDLE hostHandle = NULL;
if (!ipc::DuplicateHandle(fileMapping, aAllocator->GetParentPid(),
&hostHandle, 0, DUPLICATE_SAME_ACCESS)) {
gfxCriticalError()
<< "Failed to duplicate handle to parent process for surface.";
::DeleteObject(bitmap);
::DeleteDC(dc);
::CloseHandle(fileMapping);
return nullptr;
}
return new ShmemDIBTextureData(aSize, aFormat, surface, fileMapping,
hostHandle, dc, bitmap);
}
bool TextureHostDirectUpload::Lock() {
MOZ_ASSERT(!mIsLocked);
mIsLocked = true;
return true;
}
void TextureHostDirectUpload::Unlock() {
MOZ_ASSERT(mIsLocked);
mIsLocked = false;
}
void TextureHostDirectUpload::SetTextureSourceProvider(
TextureSourceProvider* aProvider) {
mProvider = aProvider;
}
void TextureHostDirectUpload::DeallocateDeviceData() {
if (mTextureSource) {
mTextureSource->DeallocateDeviceData();
}
}
bool TextureHostDirectUpload::BindTextureSource(
CompositableTextureSourceRef& aTexture) {
return AcquireTextureSource(aTexture);
}
bool TextureHostDirectUpload::AcquireTextureSource(
CompositableTextureSourceRef& aTexture) {
if (!mTextureSource) {
Updated();
}
aTexture = mTextureSource;
return !!aTexture;
}
DIBTextureHost::DIBTextureHost(TextureFlags aFlags,
const SurfaceDescriptorDIB& aDescriptor)
: TextureHostDirectUpload(aFlags, SurfaceFormat::B8G8R8X8, IntSize()) {
// We added an extra ref for transport, so we shouldn't AddRef now.
mSurface =
dont_AddRef(reinterpret_cast<gfxWindowsSurface*>(aDescriptor.surface()));
MOZ_ASSERT(mSurface);
mSize = mSurface->GetSize();
mFormat = mSurface->GetSurfaceFormat();
}
void DIBTextureHost::UpdatedInternal(const nsIntRegion* aRegion) {
if (!mProvider) {
// This can happen if we send textures to a compositable that isn't yet
// attached to a layer.
return;
}
if (!mTextureSource) {
mTextureSource = mProvider->CreateDataTextureSource(mFlags);
}
if (mSurface->CairoStatus()) {
gfxWarning() << "Bad Cairo surface internal update "
<< mSurface->CairoStatus();
mTextureSource = nullptr;
return;
}
RefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(
imgSurf->Data(), imgSurf->Stride(), mSize, mFormat);
if (!surf ||
!mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
mTextureSource = nullptr;
}
ReadUnlock();
}
TextureHostFileMapping::TextureHostFileMapping(
TextureFlags aFlags, const SurfaceDescriptorFileMapping& aDescriptor)
: TextureHostDirectUpload(aFlags, aDescriptor.format(), aDescriptor.size()),
mFileMapping((HANDLE)aDescriptor.handle()) {}
TextureHostFileMapping::~TextureHostFileMapping() {
::CloseHandle(mFileMapping);
}
UserDataKey kFileMappingKey;
static void UnmapFileData(void* aData) {
MOZ_ASSERT(aData);
::UnmapViewOfFile(aData);
}
void TextureHostFileMapping::UpdatedInternal(const nsIntRegion* aRegion) {
if (!mProvider) {
// This can happen if we send textures to a compositable that isn't yet
// attached to a layer.
return;
}
if (!mTextureSource) {
mTextureSource = mProvider->CreateDataTextureSource(mFlags);
}
uint8_t* data = nullptr;
int32_t totalBytes = BufferSizeFromDimensions(mSize.width, mSize.height,
BytesPerPixel(mFormat));
if (totalBytes > 0) {
data = (uint8_t*)::MapViewOfFile(mFileMapping, FILE_MAP_READ, 0, 0,
totalBytes);
}
if (data) {
RefPtr<DataSourceSurface> surf = Factory::CreateWrappingDataSourceSurface(
data, mSize.width * BytesPerPixel(mFormat), mSize, mFormat);
if (surf) {
surf->AddUserData(&kFileMappingKey, data, UnmapFileData);
if (!mTextureSource->Update(surf, const_cast<nsIntRegion*>(aRegion))) {
mTextureSource = nullptr;
}
} else {
mTextureSource = nullptr;
}
} else {
mTextureSource = nullptr;
}
ReadUnlock();
}
} // namespace layers
} // namespace mozilla