diff options
Diffstat (limited to 'gfx/layers/TextureDIB.cpp')
-rw-r--r-- | gfx/layers/TextureDIB.cpp | 505 |
1 files changed, 505 insertions, 0 deletions
diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp new file mode 100644 index 000000000..79a9469bf --- /dev/null +++ b/gfx/layers/TextureDIB.cpp @@ -0,0 +1,505 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * 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/layers/ISurfaceAllocator.h" +#include "mozilla/ipc/ProtocolUtils.h" + +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::GetPlatform()->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->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::SetCompositor(Compositor* aCompositor) +{ + mCompositor = aCompositor; +} + +void +TextureHostDirectUpload::DeallocateDeviceData() +{ + if (mTextureSource) { + mTextureSource->DeallocateDeviceData(); + } +} + +bool +TextureHostDirectUpload::BindTextureSource(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 (!mCompositor) { + // This can happen if we send textures to a compositable that isn't yet + // attached to a layer. + return; + } + + if (!mTextureSource) { + mTextureSource = mCompositor->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 (!mCompositor) { + // This can happen if we send textures to a compositable that isn't yet + // attached to a layer. + return; + } + + if (!mTextureSource) { + mTextureSource = mCompositor->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(); +} + +} +} |