gecko-dev/gfx/layers/TextureDIB.cpp

467 строки
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