michael@0: /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */ 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 "SurfaceStream.h" michael@0: michael@0: #include "gfxPoint.h" michael@0: #include "SharedSurface.h" michael@0: #include "SharedSurfaceGL.h" michael@0: #include "SurfaceFactory.h" michael@0: #include "GeckoProfiler.h" michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: SurfaceStreamType michael@0: SurfaceStream::ChooseGLStreamType(SurfaceStream::OMTC omtc, michael@0: bool preserveBuffer) michael@0: { michael@0: if (omtc == SurfaceStream::OffMainThread) { michael@0: if (preserveBuffer) michael@0: return SurfaceStreamType::TripleBuffer_Copy; michael@0: else michael@0: return SurfaceStreamType::TripleBuffer_Async; michael@0: } else { michael@0: if (preserveBuffer) michael@0: return SurfaceStreamType::SingleBuffer; michael@0: else michael@0: return SurfaceStreamType::TripleBuffer; michael@0: } michael@0: } michael@0: michael@0: SurfaceStream* michael@0: SurfaceStream::CreateForType(SurfaceStreamType type, mozilla::gl::GLContext* glContext, SurfaceStream* prevStream) michael@0: { michael@0: SurfaceStream* result = nullptr; michael@0: michael@0: switch (type) { michael@0: case SurfaceStreamType::SingleBuffer: michael@0: result = new SurfaceStream_SingleBuffer(prevStream); michael@0: break; michael@0: case SurfaceStreamType::TripleBuffer_Copy: michael@0: result = new SurfaceStream_TripleBuffer_Copy(prevStream); michael@0: break; michael@0: case SurfaceStreamType::TripleBuffer_Async: michael@0: result = new SurfaceStream_TripleBuffer_Async(prevStream); michael@0: break; michael@0: case SurfaceStreamType::TripleBuffer: michael@0: result = new SurfaceStream_TripleBuffer(prevStream); michael@0: break; michael@0: default: michael@0: MOZ_CRASH("Invalid Type."); michael@0: } michael@0: michael@0: result->mGLContext = glContext; michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: SurfaceStream_TripleBuffer::CopySurfaceToProducer(SharedSurface* src, SurfaceFactory* factory) michael@0: { michael@0: if (!mProducer) { michael@0: New(factory, src->Size(), mProducer); michael@0: if (!mProducer) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSERT(src->Size() == mProducer->Size(), "Size mismatch"); michael@0: michael@0: SharedSurface::Copy(src, mProducer, factory); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: SurfaceStream::New(SurfaceFactory* factory, const gfx::IntSize& size, michael@0: SharedSurface*& surf) michael@0: { michael@0: MOZ_ASSERT(!surf); michael@0: surf = factory->NewSharedSurface(size); michael@0: michael@0: if (surf) michael@0: mSurfaces.insert(surf); michael@0: } michael@0: michael@0: void michael@0: SurfaceStream::Recycle(SurfaceFactory* factory, SharedSurface*& surf) michael@0: { michael@0: if (surf) { michael@0: mSurfaces.erase(surf); michael@0: factory->Recycle(surf); michael@0: } michael@0: MOZ_ASSERT(!surf); michael@0: } michael@0: michael@0: void michael@0: SurfaceStream::Delete(SharedSurface*& surf) michael@0: { michael@0: if (surf) { michael@0: mSurfaces.erase(surf); michael@0: delete surf; michael@0: surf = nullptr; michael@0: } michael@0: MOZ_ASSERT(!surf); michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream::Surrender(SharedSurface*& surf) michael@0: { michael@0: SharedSurface* ret = surf; michael@0: michael@0: if (surf) { michael@0: mSurfaces.erase(surf); michael@0: surf = nullptr; michael@0: } michael@0: MOZ_ASSERT(!surf); michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream::Absorb(SharedSurface*& surf) michael@0: { michael@0: SharedSurface* ret = surf; michael@0: michael@0: if (surf) { michael@0: mSurfaces.insert(surf); michael@0: surf = nullptr; michael@0: } michael@0: MOZ_ASSERT(!surf); michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: void michael@0: SurfaceStream::Scrap(SharedSurface*& scrap) michael@0: { michael@0: if (scrap) { michael@0: mScraps.push(scrap); michael@0: scrap = nullptr; michael@0: } michael@0: MOZ_ASSERT(!scrap); michael@0: } michael@0: michael@0: void michael@0: SurfaceStream::RecycleScraps(SurfaceFactory* factory) michael@0: { michael@0: while (!mScraps.empty()) { michael@0: SharedSurface* cur = mScraps.top(); michael@0: mScraps.pop(); michael@0: michael@0: Recycle(factory, cur); michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: SurfaceStream::~SurfaceStream() michael@0: { michael@0: Delete(mProducer); michael@0: michael@0: while (!mScraps.empty()) { michael@0: SharedSurface* cur = mScraps.top(); michael@0: mScraps.pop(); michael@0: michael@0: Delete(cur); michael@0: } michael@0: michael@0: MOZ_ASSERT(mSurfaces.empty()); michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream::SwapConsumer() michael@0: { michael@0: MOZ_ASSERT(mIsAlive); michael@0: michael@0: SharedSurface* ret = SwapConsumer_NoWait(); michael@0: if (!ret) michael@0: return nullptr; michael@0: michael@0: if (!ret->WaitSync()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream::Resize(SurfaceFactory* factory, const gfx::IntSize& size) michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: michael@0: if (mProducer) { michael@0: Scrap(mProducer); michael@0: } michael@0: michael@0: New(factory, size, mProducer); michael@0: return mProducer; michael@0: } michael@0: michael@0: SurfaceStream_SingleBuffer::SurfaceStream_SingleBuffer(SurfaceStream* prevStream) michael@0: : SurfaceStream(SurfaceStreamType::SingleBuffer, prevStream) michael@0: , mConsumer(nullptr) michael@0: { michael@0: if (!prevStream) michael@0: return; michael@0: michael@0: SharedSurface* prevProducer = nullptr; michael@0: SharedSurface* prevConsumer = nullptr; michael@0: prevStream->SurrenderSurfaces(prevProducer, prevConsumer); michael@0: michael@0: if (prevConsumer == prevProducer) michael@0: prevConsumer = nullptr; michael@0: michael@0: mProducer = Absorb(prevProducer); michael@0: mConsumer = Absorb(prevConsumer); michael@0: } michael@0: michael@0: SurfaceStream_SingleBuffer::~SurfaceStream_SingleBuffer() michael@0: { michael@0: Delete(mConsumer); michael@0: } michael@0: michael@0: void michael@0: SurfaceStream_SingleBuffer::SurrenderSurfaces(SharedSurface*& producer, michael@0: SharedSurface*& consumer) michael@0: { michael@0: mIsAlive = false; michael@0: michael@0: producer = Surrender(mProducer); michael@0: consumer = Surrender(mConsumer); michael@0: michael@0: if (!consumer) michael@0: consumer = producer; michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream_SingleBuffer::SwapProducer(SurfaceFactory* factory, michael@0: const gfx::IntSize& size) michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: if (mConsumer) { michael@0: Recycle(factory, mConsumer); michael@0: } michael@0: michael@0: if (mProducer) { michael@0: // Fence now, before we start (maybe) juggling Prod around. michael@0: mProducer->Fence(); michael@0: michael@0: // Size mismatch means we need to squirrel the current Prod michael@0: // into Cons, and leave Prod empty, so it gets a new surface below. michael@0: bool needsNewBuffer = mProducer->Size() != size; michael@0: michael@0: // Even if we're the right size, if the type has changed, and we don't michael@0: // need to preserve, we should switch out for (presumedly) better perf. michael@0: if (mProducer->Type() != factory->Type() && michael@0: !factory->Caps().preserve) michael@0: { michael@0: needsNewBuffer = true; michael@0: } michael@0: michael@0: if (needsNewBuffer) { michael@0: Move(mProducer, mConsumer); michael@0: } michael@0: } michael@0: michael@0: // The old Prod (if there every was one) was invalid, michael@0: // so we need a new one. michael@0: if (!mProducer) { michael@0: New(factory, size, mProducer); michael@0: } michael@0: michael@0: return mProducer; michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream_SingleBuffer::SwapConsumer_NoWait() michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: michael@0: // Use Cons, if present. michael@0: // Otherwise, just use Prod directly. michael@0: SharedSurface* toConsume = mConsumer; michael@0: if (!toConsume) michael@0: toConsume = mProducer; michael@0: michael@0: return toConsume; michael@0: } michael@0: michael@0: michael@0: michael@0: SurfaceStream_TripleBuffer_Copy::SurfaceStream_TripleBuffer_Copy(SurfaceStream* prevStream) michael@0: : SurfaceStream(SurfaceStreamType::TripleBuffer_Copy, prevStream) michael@0: , mStaging(nullptr) michael@0: , mConsumer(nullptr) michael@0: { michael@0: if (!prevStream) michael@0: return; michael@0: michael@0: SharedSurface* prevProducer = nullptr; michael@0: SharedSurface* prevConsumer = nullptr; michael@0: prevStream->SurrenderSurfaces(prevProducer, prevConsumer); michael@0: michael@0: if (prevConsumer == prevProducer) michael@0: prevConsumer = nullptr; michael@0: michael@0: mProducer = Absorb(prevProducer); michael@0: mConsumer = Absorb(prevConsumer); michael@0: } michael@0: michael@0: SurfaceStream_TripleBuffer_Copy::~SurfaceStream_TripleBuffer_Copy() michael@0: { michael@0: Delete(mStaging); michael@0: Delete(mConsumer); michael@0: } michael@0: michael@0: void michael@0: SurfaceStream_TripleBuffer_Copy::SurrenderSurfaces(SharedSurface*& producer, michael@0: SharedSurface*& consumer) michael@0: { michael@0: mIsAlive = false; michael@0: michael@0: producer = Surrender(mProducer); michael@0: consumer = Surrender(mConsumer); michael@0: michael@0: if (!consumer) michael@0: consumer = Surrender(mStaging); michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream_TripleBuffer_Copy::SwapProducer(SurfaceFactory* factory, michael@0: const gfx::IntSize& size) michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: michael@0: RecycleScraps(factory); michael@0: if (mProducer) { michael@0: if (mStaging) { michael@0: // We'll re-use this for a new mProducer later on if michael@0: // the size remains the same michael@0: Recycle(factory, mStaging); michael@0: } michael@0: michael@0: Move(mProducer, mStaging); michael@0: mStaging->Fence(); michael@0: michael@0: New(factory, size, mProducer); michael@0: michael@0: if (mProducer && mStaging->Size() == mProducer->Size()) michael@0: SharedSurface::Copy(mStaging, mProducer, factory); michael@0: } else { michael@0: New(factory, size, mProducer); michael@0: } michael@0: michael@0: return mProducer; michael@0: } michael@0: michael@0: michael@0: SharedSurface* michael@0: SurfaceStream_TripleBuffer_Copy::SwapConsumer_NoWait() michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: michael@0: if (mStaging) { michael@0: Scrap(mConsumer); michael@0: Move(mStaging, mConsumer); michael@0: } michael@0: michael@0: return mConsumer; michael@0: } michael@0: michael@0: void SurfaceStream_TripleBuffer::Init(SurfaceStream* prevStream) michael@0: { michael@0: if (!prevStream) michael@0: return; michael@0: michael@0: SharedSurface* prevProducer = nullptr; michael@0: SharedSurface* prevConsumer = nullptr; michael@0: prevStream->SurrenderSurfaces(prevProducer, prevConsumer); michael@0: michael@0: if (prevConsumer == prevProducer) michael@0: prevConsumer = nullptr; michael@0: michael@0: mProducer = Absorb(prevProducer); michael@0: mConsumer = Absorb(prevConsumer); michael@0: } michael@0: michael@0: michael@0: SurfaceStream_TripleBuffer::SurfaceStream_TripleBuffer(SurfaceStreamType type, SurfaceStream* prevStream) michael@0: : SurfaceStream(type, prevStream) michael@0: , mStaging(nullptr) michael@0: , mConsumer(nullptr) michael@0: { michael@0: SurfaceStream_TripleBuffer::Init(prevStream); michael@0: } michael@0: michael@0: SurfaceStream_TripleBuffer::SurfaceStream_TripleBuffer(SurfaceStream* prevStream) michael@0: : SurfaceStream(SurfaceStreamType::TripleBuffer, prevStream) michael@0: , mStaging(nullptr) michael@0: , mConsumer(nullptr) michael@0: { michael@0: SurfaceStream_TripleBuffer::Init(prevStream); michael@0: } michael@0: michael@0: SurfaceStream_TripleBuffer::~SurfaceStream_TripleBuffer() michael@0: { michael@0: Delete(mStaging); michael@0: Delete(mConsumer); michael@0: } michael@0: michael@0: void michael@0: SurfaceStream_TripleBuffer::SurrenderSurfaces(SharedSurface*& producer, michael@0: SharedSurface*& consumer) michael@0: { michael@0: mIsAlive = false; michael@0: michael@0: producer = Surrender(mProducer); michael@0: consumer = Surrender(mConsumer); michael@0: michael@0: if (!consumer) michael@0: consumer = Surrender(mStaging); michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream_TripleBuffer::SwapProducer(SurfaceFactory* factory, michael@0: const gfx::IntSize& size) michael@0: { michael@0: PROFILER_LABEL("SurfaceStream_TripleBuffer", "SwapProducer"); michael@0: michael@0: MonitorAutoLock lock(mMonitor); michael@0: if (mProducer) { michael@0: RecycleScraps(factory); michael@0: michael@0: // If WaitForCompositor succeeds, mStaging has moved to mConsumer. michael@0: // If it failed, we might have to scrap it. michael@0: if (mStaging && !WaitForCompositor()) michael@0: Scrap(mStaging); michael@0: michael@0: MOZ_ASSERT(!mStaging); michael@0: Move(mProducer, mStaging); michael@0: mStaging->Fence(); michael@0: } michael@0: michael@0: MOZ_ASSERT(!mProducer); michael@0: New(factory, size, mProducer); michael@0: michael@0: return mProducer; michael@0: } michael@0: michael@0: SharedSurface* michael@0: SurfaceStream_TripleBuffer::SwapConsumer_NoWait() michael@0: { michael@0: MonitorAutoLock lock(mMonitor); michael@0: if (mStaging) { michael@0: Scrap(mConsumer); michael@0: Move(mStaging, mConsumer); michael@0: mMonitor.NotifyAll(); michael@0: } michael@0: michael@0: return mConsumer; michael@0: } michael@0: michael@0: SurfaceStream_TripleBuffer_Async::SurfaceStream_TripleBuffer_Async(SurfaceStream* prevStream) michael@0: : SurfaceStream_TripleBuffer(SurfaceStreamType::TripleBuffer_Async, prevStream) michael@0: { michael@0: } michael@0: michael@0: SurfaceStream_TripleBuffer_Async::~SurfaceStream_TripleBuffer_Async() michael@0: { michael@0: } michael@0: michael@0: bool michael@0: SurfaceStream_TripleBuffer_Async::WaitForCompositor() michael@0: { michael@0: PROFILER_LABEL("SurfaceStream_TripleBuffer_Async", "WaitForCompositor"); michael@0: michael@0: // We are assumed to be locked michael@0: while (mStaging) { michael@0: if (!NS_SUCCEEDED(mMonitor.Wait(PR_MillisecondsToInterval(100)))) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } /* namespace gfx */ michael@0: } /* namespace mozilla */