gecko-dev/gfx/gl/SurfaceStream.cpp

495 lines
12 KiB
C++

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