michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=8 et : michael@0: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ISurfaceAllocator.h" michael@0: #include // for int32_t michael@0: #include "gfx2DGlue.h" // for IntSize michael@0: #include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat michael@0: #include "gfxSharedImageSurface.h" // for gfxSharedImageSurface michael@0: #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc michael@0: #include "mozilla/Atomics.h" // for PrimitiveIntrinsics michael@0: #include "mozilla/ipc/SharedMemory.h" // for SharedMemory, etc michael@0: #include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc michael@0: #include "ShadowLayerUtils.h" michael@0: #include "mozilla/mozalloc.h" // for operator delete[], etc michael@0: #include "nsAutoPtr.h" // for nsRefPtr, getter_AddRefs, etc michael@0: #include "nsDebug.h" // for NS_RUNTIMEABORT michael@0: #include "nsXULAppAPI.h" // for XRE_GetProcessType, etc michael@0: #include "mozilla/ipc/Shmem.h" michael@0: #include "mozilla/layers/ImageDataSerializer.h" michael@0: #ifdef DEBUG michael@0: #include "prenv.h" michael@0: #endif michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter) michael@0: michael@0: mozilla::Atomic GfxMemoryImageReporter::sAmount(0); michael@0: michael@0: mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType() michael@0: { michael@0: return mozilla::ipc::SharedMemory::TYPE_BASIC; michael@0: } michael@0: michael@0: bool michael@0: IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface) michael@0: { michael@0: return aSurface.type() != SurfaceDescriptor::T__None && michael@0: aSurface.type() != SurfaceDescriptor::Tnull_t; michael@0: } michael@0: michael@0: ISurfaceAllocator::~ISurfaceAllocator() michael@0: { michael@0: // Check if we're not leaking.. michael@0: MOZ_ASSERT(mUsedShmems.empty()); michael@0: } michael@0: michael@0: void michael@0: ISurfaceAllocator::Finalize() michael@0: { michael@0: ShrinkShmemSectionHeap(); michael@0: } michael@0: michael@0: static inline uint8_t* michael@0: GetAddressFromDescriptor(const SurfaceDescriptor& aDescriptor, size_t& aSize) michael@0: { michael@0: MOZ_ASSERT(IsSurfaceDescriptorValid(aDescriptor)); michael@0: MOZ_ASSERT(aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorShmem || michael@0: aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorMemory); michael@0: if (aDescriptor.type() == SurfaceDescriptor::TSurfaceDescriptorShmem) { michael@0: Shmem shmem(aDescriptor.get_SurfaceDescriptorShmem().data()); michael@0: aSize = shmem.Size(); michael@0: return shmem.get(); michael@0: } else { michael@0: const SurfaceDescriptorMemory& image = aDescriptor.get_SurfaceDescriptorMemory(); michael@0: aSize = std::numeric_limits::max(); michael@0: return reinterpret_cast(image.data()); michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: GetDrawTargetForDescriptor(const SurfaceDescriptor& aDescriptor, gfx::BackendType aBackend) michael@0: { michael@0: size_t size; michael@0: uint8_t* data = GetAddressFromDescriptor(aDescriptor, size); michael@0: ImageDataDeserializer image(data, size); michael@0: return image.GetAsDrawTarget(aBackend); michael@0: } michael@0: michael@0: TemporaryRef michael@0: GetSurfaceForDescriptor(const SurfaceDescriptor& aDescriptor) michael@0: { michael@0: size_t size; michael@0: uint8_t* data = GetAddressFromDescriptor(aDescriptor, size); michael@0: ImageDataDeserializer image(data, size); michael@0: return image.GetAsSurface(); michael@0: } michael@0: michael@0: bool michael@0: ISurfaceAllocator::AllocSurfaceDescriptor(const gfx::IntSize& aSize, michael@0: gfxContentType aContent, michael@0: SurfaceDescriptor* aBuffer) michael@0: { michael@0: return AllocSurfaceDescriptorWithCaps(aSize, aContent, DEFAULT_BUFFER_CAPS, aBuffer); michael@0: } michael@0: michael@0: bool michael@0: ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize, michael@0: gfxContentType aContent, michael@0: uint32_t aCaps, michael@0: SurfaceDescriptor* aBuffer) michael@0: { michael@0: gfx::SurfaceFormat format = michael@0: gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent); michael@0: size_t size = ImageDataSerializer::ComputeMinBufferSize(aSize, format); michael@0: if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) { michael@0: uint8_t *data = new (std::nothrow) uint8_t[size]; michael@0: if (!data) { michael@0: return false; michael@0: } michael@0: GfxMemoryImageReporter::DidAlloc(data); michael@0: #ifdef XP_MACOSX michael@0: // Workaround a bug in Quartz where drawing an a8 surface to another a8 michael@0: // surface with OPERATOR_SOURCE still requires the destination to be clear. michael@0: if (format == gfx::SurfaceFormat::A8) { michael@0: memset(data, 0, size); michael@0: } michael@0: #endif michael@0: *aBuffer = SurfaceDescriptorMemory((uintptr_t)data, format); michael@0: } else { michael@0: michael@0: mozilla::ipc::SharedMemory::SharedMemoryType shmemType = OptimalShmemType(); michael@0: mozilla::ipc::Shmem shmem; michael@0: if (!AllocUnsafeShmem(size, shmemType, &shmem)) { michael@0: return false; michael@0: } michael@0: michael@0: *aBuffer = SurfaceDescriptorShmem(shmem, format); michael@0: } michael@0: michael@0: uint8_t* data = GetAddressFromDescriptor(*aBuffer, size); michael@0: ImageDataSerializer serializer(data, size); michael@0: serializer.InitializeBufferInfo(aSize, format); michael@0: return true; michael@0: } michael@0: michael@0: /* static */ bool michael@0: ISurfaceAllocator::IsShmem(SurfaceDescriptor* aSurface) michael@0: { michael@0: return aSurface && (aSurface->type() == SurfaceDescriptor::TSurfaceDescriptorShmem); michael@0: } michael@0: michael@0: void michael@0: ISurfaceAllocator::DestroySharedSurface(SurfaceDescriptor* aSurface) michael@0: { michael@0: MOZ_ASSERT(aSurface); michael@0: if (!aSurface) { michael@0: return; michael@0: } michael@0: if (!IPCOpen()) { michael@0: return; michael@0: } michael@0: switch (aSurface->type()) { michael@0: case SurfaceDescriptor::TSurfaceDescriptorShmem: michael@0: DeallocShmem(aSurface->get_SurfaceDescriptorShmem().data()); michael@0: break; michael@0: case SurfaceDescriptor::TSurfaceDescriptorMemory: michael@0: GfxMemoryImageReporter::WillFree((uint8_t*)aSurface->get_SurfaceDescriptorMemory().data()); michael@0: delete [] (uint8_t*)aSurface->get_SurfaceDescriptorMemory().data(); michael@0: break; michael@0: case SurfaceDescriptor::Tnull_t: michael@0: case SurfaceDescriptor::T__None: michael@0: break; michael@0: default: michael@0: NS_RUNTIMEABORT("surface type not implemented!"); michael@0: } michael@0: *aSurface = SurfaceDescriptor(); michael@0: } michael@0: michael@0: // XXX - We should actually figure out the minimum shmem allocation size on michael@0: // a certain platform and use that. michael@0: const uint32_t sShmemPageSize = 4096; michael@0: const uint32_t sSupportedBlockSize = 4; michael@0: michael@0: enum AllocationStatus michael@0: { michael@0: STATUS_ALLOCATED, michael@0: STATUS_FREED michael@0: }; michael@0: michael@0: struct ShmemSectionHeapHeader michael@0: { michael@0: Atomic mTotalBlocks; michael@0: Atomic mAllocatedBlocks; michael@0: }; michael@0: michael@0: struct ShmemSectionHeapAllocation michael@0: { michael@0: Atomic mStatus; michael@0: uint32_t mSize; michael@0: }; michael@0: michael@0: bool michael@0: ISurfaceAllocator::AllocShmemSection(size_t aSize, mozilla::layers::ShmemSection* aShmemSection) michael@0: { michael@0: // For now we only support sizes of 4. If we want to support different sizes michael@0: // some more complicated bookkeeping should be added. michael@0: MOZ_ASSERT(aSize == sSupportedBlockSize); michael@0: MOZ_ASSERT(aShmemSection); michael@0: michael@0: uint32_t allocationSize = (aSize + sizeof(ShmemSectionHeapAllocation)); michael@0: michael@0: for (size_t i = 0; i < mUsedShmems.size(); i++) { michael@0: ShmemSectionHeapHeader* header = mUsedShmems[i].get(); michael@0: if ((header->mAllocatedBlocks + 1) * allocationSize + sizeof(ShmemSectionHeapHeader) < sShmemPageSize) { michael@0: aShmemSection->shmem() = mUsedShmems[i]; michael@0: MOZ_ASSERT(mUsedShmems[i].IsWritable()); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (!aShmemSection->shmem().IsWritable()) { michael@0: ipc::Shmem tmp; michael@0: if (!AllocUnsafeShmem(sShmemPageSize, ipc::SharedMemory::TYPE_BASIC, &tmp)) { michael@0: return false; michael@0: } michael@0: michael@0: ShmemSectionHeapHeader* header = tmp.get(); michael@0: header->mTotalBlocks = 0; michael@0: header->mAllocatedBlocks = 0; michael@0: michael@0: mUsedShmems.push_back(tmp); michael@0: aShmemSection->shmem() = tmp; michael@0: } michael@0: michael@0: MOZ_ASSERT(aShmemSection->shmem().IsWritable()); michael@0: michael@0: ShmemSectionHeapHeader* header = aShmemSection->shmem().get(); michael@0: uint8_t* heap = aShmemSection->shmem().get() + sizeof(ShmemSectionHeapHeader); michael@0: michael@0: ShmemSectionHeapAllocation* allocHeader = nullptr; michael@0: michael@0: if (header->mTotalBlocks > header->mAllocatedBlocks) { michael@0: // Search for the first available block. michael@0: for (size_t i = 0; i < header->mTotalBlocks; i++) { michael@0: allocHeader = reinterpret_cast(heap); michael@0: michael@0: if (allocHeader->mStatus == STATUS_FREED) { michael@0: break; michael@0: } michael@0: heap += allocationSize; michael@0: } michael@0: MOZ_ASSERT(allocHeader && allocHeader->mStatus == STATUS_FREED); michael@0: MOZ_ASSERT(allocHeader->mSize == sSupportedBlockSize); michael@0: } else { michael@0: heap += header->mTotalBlocks * allocationSize; michael@0: michael@0: header->mTotalBlocks++; michael@0: allocHeader = reinterpret_cast(heap); michael@0: allocHeader->mSize = aSize; michael@0: } michael@0: michael@0: MOZ_ASSERT(allocHeader); michael@0: header->mAllocatedBlocks++; michael@0: allocHeader->mStatus = STATUS_ALLOCATED; michael@0: michael@0: aShmemSection->size() = aSize; michael@0: aShmemSection->offset() = (heap + sizeof(ShmemSectionHeapAllocation)) - aShmemSection->shmem().get(); michael@0: ShrinkShmemSectionHeap(); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: ISurfaceAllocator::FreeShmemSection(mozilla::layers::ShmemSection& aShmemSection) michael@0: { michael@0: MOZ_ASSERT(aShmemSection.size() == sSupportedBlockSize); michael@0: MOZ_ASSERT(aShmemSection.offset() < sShmemPageSize - sSupportedBlockSize); michael@0: michael@0: ShmemSectionHeapAllocation* allocHeader = michael@0: reinterpret_cast(aShmemSection.shmem().get() + michael@0: aShmemSection.offset() - michael@0: sizeof(ShmemSectionHeapAllocation)); michael@0: michael@0: MOZ_ASSERT(allocHeader->mSize == aShmemSection.size()); michael@0: michael@0: DebugOnly success = allocHeader->mStatus.compareExchange(STATUS_ALLOCATED, STATUS_FREED); michael@0: // If this fails something really weird is going on. michael@0: MOZ_ASSERT(success); michael@0: michael@0: ShmemSectionHeapHeader* header = aShmemSection.shmem().get(); michael@0: header->mAllocatedBlocks--; michael@0: michael@0: ShrinkShmemSectionHeap(); michael@0: } michael@0: michael@0: void michael@0: ISurfaceAllocator::ShrinkShmemSectionHeap() michael@0: { michael@0: for (size_t i = 0; i < mUsedShmems.size(); i++) { michael@0: ShmemSectionHeapHeader* header = mUsedShmems[i].get(); michael@0: if (header->mAllocatedBlocks == 0) { michael@0: DeallocShmem(mUsedShmems[i]); michael@0: michael@0: // We don't particularly care about order, move the last one in the array michael@0: // to this position. michael@0: mUsedShmems[i] = mUsedShmems[mUsedShmems.size() - 1]; michael@0: mUsedShmems.pop_back(); michael@0: i--; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: } // namespace michael@0: } // namespace