blob: 2281164263a28f51999db0dc8d1923bc5618a32b [file] [log] [blame]
// Copyright 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cc/thread_proxy.h"
#include "base/bind.h"
#include "base/debug/trace_event.h"
#include "cc/delay_based_time_source.h"
#include "cc/draw_quad.h"
#include "cc/frame_rate_controller.h"
#include "cc/graphics_context.h"
#include "cc/input_handler.h"
#include "cc/layer_tree_host.h"
#include "cc/scheduler.h"
#include "cc/scoped_thread_proxy.h"
#include "cc/thread.h"
#include <public/WebSharedGraphicsContext3D.h>
using WebKit::WebSharedGraphicsContext3D;
namespace {
// Measured in seconds.
const double contextRecreationTickRate = 0.03;
} // namespace
namespace cc {
scoped_ptr<Proxy> ThreadProxy::create(LayerTreeHost* layerTreeHost, scoped_ptr<Thread> implThread)
{
return make_scoped_ptr(new ThreadProxy(layerTreeHost, implThread.Pass())).PassAs<Proxy>();
}
ThreadProxy::ThreadProxy(LayerTreeHost* layerTreeHost, scoped_ptr<Thread> implThread)
: Proxy(implThread.Pass())
, m_animateRequested(false)
, m_commitRequested(false)
, m_commitRequestSentToImplThread(false)
, m_layerTreeHost(layerTreeHost)
, m_rendererInitialized(false)
, m_started(false)
, m_texturesAcquired(true)
, m_inCompositeAndReadback(false)
, m_manageTilesPending(false)
, m_mainThreadProxy(ScopedThreadProxy::create(Proxy::mainThread()))
, m_beginFrameCompletionEventOnImplThread(0)
, m_readbackRequestOnImplThread(0)
, m_commitCompletionEventOnImplThread(0)
, m_textureAcquisitionCompletionEventOnImplThread(0)
, m_nextFrameIsNewlyCommittedFrameOnImplThread(false)
, m_renderVSyncEnabled(layerTreeHost->settings().renderVSyncEnabled)
, m_totalCommitCount(0)
, m_deferCommits(false)
{
TRACE_EVENT0("cc", "ThreadProxy::ThreadProxy");
DCHECK(isMainThread());
}
ThreadProxy::~ThreadProxy()
{
TRACE_EVENT0("cc", "ThreadProxy::~ThreadProxy");
DCHECK(isMainThread());
DCHECK(!m_started);
}
bool ThreadProxy::compositeAndReadback(void *pixels, const gfx::Rect& rect)
{
TRACE_EVENT0("cc", "ThreadProxy::compositeAndReadback");
DCHECK(isMainThread());
DCHECK(m_layerTreeHost);
DCHECK(!m_deferCommits);
if (!m_layerTreeHost->initializeRendererIfNeeded()) {
TRACE_EVENT0("cc", "compositeAndReadback_EarlyOut_LR_Uninitialized");
return false;
}
// Perform a synchronous commit.
{
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent beginFrameCompletion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::forceBeginFrameOnImplThread, base::Unretained(this), &beginFrameCompletion));
beginFrameCompletion.wait();
}
m_inCompositeAndReadback = true;
beginFrame(scoped_ptr<BeginFrameAndCommitState>());
m_inCompositeAndReadback = false;
// Perform a synchronous readback.
ReadbackRequest request;
request.rect = rect;
request.pixels = pixels;
{
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::requestReadbackOnImplThread, base::Unretained(this), &request));
request.completion.wait();
}
return request.success;
}
void ThreadProxy::requestReadbackOnImplThread(ReadbackRequest* request)
{
DCHECK(Proxy::isImplThread());
DCHECK(!m_readbackRequestOnImplThread);
if (!m_layerTreeHostImpl.get()) {
request->success = false;
request->completion.signal();
return;
}
m_readbackRequestOnImplThread = request;
m_schedulerOnImplThread->setNeedsRedraw();
m_schedulerOnImplThread->setNeedsForcedRedraw();
}
void ThreadProxy::startPageScaleAnimation(gfx::Vector2d targetOffset, bool useAnchor, float scale, base::TimeDelta duration)
{
DCHECK(Proxy::isMainThread());
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::requestStartPageScaleAnimationOnImplThread, base::Unretained(this), targetOffset, useAnchor, scale, duration));
}
void ThreadProxy::requestStartPageScaleAnimationOnImplThread(gfx::Vector2d targetOffset, bool useAnchor, float scale, base::TimeDelta duration)
{
DCHECK(Proxy::isImplThread());
if (m_layerTreeHostImpl.get())
m_layerTreeHostImpl->startPageScaleAnimation(targetOffset, useAnchor, scale, base::TimeTicks::Now(), duration);
}
void ThreadProxy::finishAllRendering()
{
DCHECK(Proxy::isMainThread());
DCHECK(!m_deferCommits);
// Make sure all GL drawing is finished on the impl thread.
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::finishAllRenderingOnImplThread, base::Unretained(this), &completion));
completion.wait();
}
bool ThreadProxy::isStarted() const
{
DCHECK(Proxy::isMainThread());
return m_started;
}
bool ThreadProxy::initializeContext()
{
TRACE_EVENT0("cc", "ThreadProxy::initializeContext");
scoped_ptr<GraphicsContext> context = m_layerTreeHost->createContext();
if (!context.get())
return false;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::initializeContextOnImplThread, base::Unretained(this), base::Passed(context.Pass())));
return true;
}
void ThreadProxy::setSurfaceReady()
{
TRACE_EVENT0("cc", "ThreadProxy::setSurfaceReady");
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::setSurfaceReadyOnImplThread, base::Unretained(this)));
}
void ThreadProxy::setSurfaceReadyOnImplThread()
{
TRACE_EVENT0("cc", "ThreadProxy::setSurfaceReadyOnImplThread");
m_schedulerOnImplThread->setCanBeginFrame(true);
}
void ThreadProxy::setVisible(bool visible)
{
TRACE_EVENT0("cc", "ThreadProxy::setVisible");
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::setVisibleOnImplThread, base::Unretained(this), &completion, visible));
completion.wait();
}
void ThreadProxy::setVisibleOnImplThread(CompletionEvent* completion, bool visible)
{
TRACE_EVENT0("cc", "ThreadProxy::setVisibleOnImplThread");
m_layerTreeHostImpl->setVisible(visible);
m_schedulerOnImplThread->setVisible(visible);
completion->signal();
}
bool ThreadProxy::initializeRenderer()
{
TRACE_EVENT0("cc", "ThreadProxy::initializeRenderer");
// Make a blocking call to initializeRendererOnImplThread. The results of that call
// are pushed into the initializeSucceeded and capabilities local variables.
CompletionEvent completion;
bool initializeSucceeded = false;
RendererCapabilities capabilities;
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::initializeRendererOnImplThread,
base::Unretained(this),
&completion,
&initializeSucceeded,
&capabilities));
completion.wait();
if (initializeSucceeded) {
m_rendererInitialized = true;
m_RendererCapabilitiesMainThreadCopy = capabilities;
}
return initializeSucceeded;
}
bool ThreadProxy::recreateContext()
{
TRACE_EVENT0("cc", "ThreadProxy::recreateContext");
DCHECK(isMainThread());
// Try to create the context.
scoped_ptr<GraphicsContext> context = m_layerTreeHost->createContext();
if (!context.get())
return false;
if (m_layerTreeHost->needsSharedContext())
if (!WebSharedGraphicsContext3D::createCompositorThreadContext())
return false;
// Make a blocking call to recreateContextOnImplThread. The results of that
// call are pushed into the recreateSucceeded and capabilities local
// variables.
CompletionEvent completion;
bool recreateSucceeded = false;
RendererCapabilities capabilities;
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::recreateContextOnImplThread,
base::Unretained(this),
&completion,
base::Passed(context.Pass()),
&recreateSucceeded,
&capabilities));
completion.wait();
if (recreateSucceeded)
m_RendererCapabilitiesMainThreadCopy = capabilities;
return recreateSucceeded;
}
void ThreadProxy::renderingStats(RenderingStats* stats)
{
DCHECK(isMainThread());
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::renderingStatsOnImplThread,
base::Unretained(this), &completion, stats));
stats->totalCommitTimeInSeconds = m_totalCommitTime.InSecondsF();
stats->totalCommitCount = m_totalCommitCount;
completion.wait();
}
const RendererCapabilities& ThreadProxy::rendererCapabilities() const
{
DCHECK(m_rendererInitialized);
return m_RendererCapabilitiesMainThreadCopy;
}
void ThreadProxy::loseContext()
{
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::didLoseContextOnImplThread, base::Unretained(this)));
}
void ThreadProxy::setNeedsAnimate()
{
DCHECK(isMainThread());
if (m_animateRequested)
return;
TRACE_EVENT0("cc", "ThreadProxy::setNeedsAnimate");
m_animateRequested = true;
if (m_commitRequestSentToImplThread)
return;
m_commitRequestSentToImplThread = true;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::setNeedsCommitOnImplThread, base::Unretained(this)));
}
void ThreadProxy::setNeedsCommit()
{
DCHECK(isMainThread());
if (m_commitRequested)
return;
TRACE_EVENT0("cc", "ThreadProxy::setNeedsCommit");
m_commitRequested = true;
if (m_commitRequestSentToImplThread)
return;
m_commitRequestSentToImplThread = true;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::setNeedsCommitOnImplThread, base::Unretained(this)));
}
void ThreadProxy::didLoseContextOnImplThread()
{
DCHECK(isImplThread());
TRACE_EVENT0("cc", "ThreadProxy::didLoseContextOnImplThread");
m_schedulerOnImplThread->didLoseContext();
}
void ThreadProxy::onSwapBuffersCompleteOnImplThread()
{
DCHECK(isImplThread());
TRACE_EVENT0("cc", "ThreadProxy::onSwapBuffersCompleteOnImplThread");
m_schedulerOnImplThread->didSwapBuffersComplete();
m_mainThreadProxy->postTask(FROM_HERE, base::Bind(&ThreadProxy::didCompleteSwapBuffers, base::Unretained(this)));
}
void ThreadProxy::onVSyncParametersChanged(base::TimeTicks timebase, base::TimeDelta interval)
{
DCHECK(isImplThread());
TRACE_EVENT2("cc", "ThreadProxy::onVSyncParametersChanged", "timebase", (timebase - base::TimeTicks()).InMilliseconds(), "interval", interval.InMilliseconds());
m_schedulerOnImplThread->setTimebaseAndInterval(timebase, interval);
}
void ThreadProxy::onCanDrawStateChanged(bool canDraw)
{
DCHECK(isImplThread());
TRACE_EVENT1("cc", "ThreadProxy::onCanDrawStateChanged", "canDraw", canDraw);
m_schedulerOnImplThread->setCanDraw(canDraw);
}
void ThreadProxy::setNeedsCommitOnImplThread()
{
DCHECK(isImplThread());
TRACE_EVENT0("cc", "ThreadProxy::setNeedsCommitOnImplThread");
m_schedulerOnImplThread->setNeedsCommit();
}
void ThreadProxy::setNeedsManageTilesOnImplThread()
{
if (m_manageTilesPending)
return;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::manageTilesOnImplThread, base::Unretained(this)));
m_manageTilesPending = true;
}
void ThreadProxy::manageTilesOnImplThread()
{
// TODO(nduca): If needed, move this into CCSchedulerStateMachine.
m_manageTilesPending = false;
if (m_layerTreeHostImpl)
m_layerTreeHostImpl->manageTiles();
}
void ThreadProxy::setNeedsForcedCommitOnImplThread()
{
DCHECK(isImplThread());
TRACE_EVENT0("cc", "ThreadProxy::setNeedsForcedCommitOnImplThread");
m_schedulerOnImplThread->setNeedsForcedCommit();
}
void ThreadProxy::postAnimationEventsToMainThreadOnImplThread(scoped_ptr<AnimationEventsVector> events, base::Time wallClockTime)
{
DCHECK(isImplThread());
TRACE_EVENT0("cc", "ThreadProxy::postAnimationEventsToMainThreadOnImplThread");
m_mainThreadProxy->postTask(FROM_HERE, base::Bind(&ThreadProxy::setAnimationEvents, base::Unretained(this), base::Passed(events.Pass()), wallClockTime));
}
bool ThreadProxy::reduceContentsTextureMemoryOnImplThread(size_t limitBytes, int priorityCutoff)
{
DCHECK(isImplThread());
if (!m_layerTreeHost->contentsTextureManager())
return false;
bool reduceResult = m_layerTreeHost->contentsTextureManager()->reduceMemoryOnImplThread(limitBytes, priorityCutoff, m_layerTreeHostImpl->resourceProvider());
if (!reduceResult)
return false;
// The texture upload queue may reference textures that were just purged, clear
// them from the queue.
if (m_currentResourceUpdateControllerOnImplThread.get())
m_currentResourceUpdateControllerOnImplThread->discardUploadsToEvictedResources();
return true;
}
void ThreadProxy::sendManagedMemoryStats()
{
DCHECK(isImplThread());
if (!m_layerTreeHostImpl.get())
return;
if (!m_layerTreeHostImpl->renderer())
return;
if (!m_layerTreeHost->contentsTextureManager())
return;
m_layerTreeHostImpl->renderer()->sendManagedMemoryStats(
m_layerTreeHost->contentsTextureManager()->memoryVisibleBytes(),
m_layerTreeHost->contentsTextureManager()->memoryVisibleAndNearbyBytes(),
m_layerTreeHost->contentsTextureManager()->memoryUseBytes());
}
void ThreadProxy::setNeedsRedraw()
{
DCHECK(isMainThread());
TRACE_EVENT0("cc", "ThreadProxy::setNeedsRedraw");
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::setFullRootLayerDamageOnImplThread, base::Unretained(this)));
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::setNeedsRedrawOnImplThread, base::Unretained(this)));
}
void ThreadProxy::setDeferCommits(bool deferCommits)
{
DCHECK(isMainThread());
DCHECK_NE(m_deferCommits, deferCommits);
m_deferCommits = deferCommits;
if (m_deferCommits)
TRACE_EVENT_ASYNC_BEGIN0("cc", "ThreadProxy::setDeferCommits", this);
else
TRACE_EVENT_ASYNC_END0("cc", "ThreadProxy::setDeferCommits", this);
if (!m_deferCommits && m_pendingDeferredCommit)
m_mainThreadProxy->postTask(FROM_HERE, base::Bind(&ThreadProxy::beginFrame, base::Unretained(this), base::Passed(m_pendingDeferredCommit.Pass())));
}
bool ThreadProxy::commitRequested() const
{
DCHECK(isMainThread());
return m_commitRequested;
}
void ThreadProxy::setNeedsRedrawOnImplThread()
{
DCHECK(isImplThread());
TRACE_EVENT0("cc", "ThreadProxy::setNeedsRedrawOnImplThread");
m_schedulerOnImplThread->setNeedsRedraw();
}
void ThreadProxy::start()
{
DCHECK(isMainThread());
DCHECK(Proxy::implThread());
// Create LayerTreeHostImpl.
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
scoped_ptr<InputHandler> handler = m_layerTreeHost->createInputHandler();
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::initializeImplOnImplThread, base::Unretained(this), &completion, handler.release()));
completion.wait();
m_started = true;
}
void ThreadProxy::stop()
{
TRACE_EVENT0("cc", "ThreadProxy::stop");
DCHECK(isMainThread());
DCHECK(m_started);
// Synchronously deletes the impl.
{
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::layerTreeHostClosedOnImplThread, base::Unretained(this), &completion));
completion.wait();
}
m_mainThreadProxy->shutdown(); // Stop running tasks posted to us.
DCHECK(!m_layerTreeHostImpl.get()); // verify that the impl deleted.
m_layerTreeHost = 0;
m_started = false;
}
void ThreadProxy::forceSerializeOnSwapBuffers()
{
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::forceSerializeOnSwapBuffersOnImplThread, base::Unretained(this), &completion));
completion.wait();
}
void ThreadProxy::forceSerializeOnSwapBuffersOnImplThread(CompletionEvent* completion)
{
if (m_rendererInitialized)
m_layerTreeHostImpl->renderer()->doNoOp();
completion->signal();
}
void ThreadProxy::finishAllRenderingOnImplThread(CompletionEvent* completion)
{
TRACE_EVENT0("cc", "ThreadProxy::finishAllRenderingOnImplThread");
DCHECK(isImplThread());
m_layerTreeHostImpl->finishAllRendering();
completion->signal();
}
void ThreadProxy::forceBeginFrameOnImplThread(CompletionEvent* completion)
{
TRACE_EVENT0("cc", "ThreadProxy::forceBeginFrameOnImplThread");
DCHECK(!m_beginFrameCompletionEventOnImplThread);
setNeedsForcedCommitOnImplThread();
if (m_schedulerOnImplThread->commitPending()) {
completion->signal();
return;
}
m_beginFrameCompletionEventOnImplThread = completion;
}
void ThreadProxy::scheduledActionBeginFrame()
{
TRACE_EVENT0("cc", "ThreadProxy::scheduledActionBeginFrame");
scoped_ptr<BeginFrameAndCommitState> beginFrameState(new BeginFrameAndCommitState);
beginFrameState->monotonicFrameBeginTime = base::TimeTicks::Now();
beginFrameState->scrollInfo = m_layerTreeHostImpl->processScrollDeltas();
beginFrameState->implTransform = m_layerTreeHostImpl->implTransform();
DCHECK_GT(m_layerTreeHostImpl->memoryAllocationLimitBytes(), 0u);
beginFrameState->memoryAllocationLimitBytes = m_layerTreeHostImpl->memoryAllocationLimitBytes();
if (m_layerTreeHost->contentsTextureManager())
m_layerTreeHost->contentsTextureManager()->getEvictedBackings(beginFrameState->evictedContentsTexturesBackings);
m_mainThreadProxy->postTask(FROM_HERE, base::Bind(&ThreadProxy::beginFrame, base::Unretained(this), base::Passed(beginFrameState.Pass())));
if (m_beginFrameCompletionEventOnImplThread) {
m_beginFrameCompletionEventOnImplThread->signal();
m_beginFrameCompletionEventOnImplThread = 0;
}
}
void ThreadProxy::beginFrame(scoped_ptr<BeginFrameAndCommitState> beginFrameState)
{
TRACE_EVENT0("cc", "ThreadProxy::beginFrame");
DCHECK(isMainThread());
if (!m_layerTreeHost)
return;
// Be sure to never early-out of unlinking evicted textures whenever, so that
// the each evicted backing is sent to the main thread exactly once.
if (beginFrameState && !beginFrameState->evictedContentsTexturesBackings.empty()) {
DCHECK(m_layerTreeHost->contentsTextureManager());
m_layerTreeHost->contentsTextureManager()->unlinkEvictedBackings(beginFrameState->evictedContentsTexturesBackings);
}
if (m_deferCommits) {
m_pendingDeferredCommit = beginFrameState.Pass();
m_layerTreeHost->didDeferCommit();
TRACE_EVENT0("cc", "EarlyOut_DeferCommits");
return;
}
if (m_layerTreeHost->needsSharedContext() && !WebSharedGraphicsContext3D::haveCompositorThreadContext())
WebSharedGraphicsContext3D::createCompositorThreadContext();
// Do not notify the impl thread of commit requests that occur during
// the apply/animate/layout part of the beginFrameAndCommit process since
// those commit requests will get painted immediately. Once we have done
// the paint, m_commitRequested will be set to false to allow new commit
// requests to be scheduled.
m_commitRequested = true;
m_commitRequestSentToImplThread = true;
// On the other hand, the animationRequested flag needs to be cleared
// here so that any animation requests generated by the apply or animate
// callbacks will trigger another frame.
m_animateRequested = false;
if (beginFrameState) {
m_layerTreeHost->applyScrollAndScale(*beginFrameState->scrollInfo);
m_layerTreeHost->setImplTransform(beginFrameState->implTransform);
}
if (!m_inCompositeAndReadback && !m_layerTreeHost->visible()) {
m_commitRequested = false;
m_commitRequestSentToImplThread = false;
TRACE_EVENT0("cc", "EarlyOut_NotVisible");
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::beginFrameAbortedOnImplThread, base::Unretained(this)));
return;
}
m_layerTreeHost->willBeginFrame();
if (beginFrameState)
m_layerTreeHost->updateAnimations(beginFrameState->monotonicFrameBeginTime);
m_layerTreeHost->layout();
// Clear the commit flag after updating animations and layout here --- objects that only
// layout when painted will trigger another setNeedsCommit inside
// updateLayers.
m_commitRequested = false;
m_commitRequestSentToImplThread = false;
if (!m_layerTreeHost->initializeRendererIfNeeded()) {
TRACE_EVENT0("cc", "EarlyOut_InitializeFailed");
return;
}
scoped_ptr<ResourceUpdateQueue> queue = make_scoped_ptr(new ResourceUpdateQueue);
m_layerTreeHost->updateLayers(*(queue.get()), beginFrameState ? beginFrameState->memoryAllocationLimitBytes : 0);
// Once single buffered layers are committed, they cannot be modified until
// they are drawn by the impl thread.
m_texturesAcquired = false;
m_layerTreeHost->willCommit();
// Before applying scrolls and calling animate, we set m_animateRequested to
// false. If it is true now, it means setNeedAnimate was called again, but
// during a state when m_commitRequestSentToImplThread = true. We need to
// force that call to happen again now so that the commit request is sent to
// the impl thread.
if (m_animateRequested) {
// Forces setNeedsAnimate to consider posting a commit task.
m_animateRequested = false;
setNeedsAnimate();
}
// Notify the impl thread that the beginFrame has completed. This will
// begin the commit process, which is blocking from the main thread's
// point of view, but asynchronously performed on the impl thread,
// coordinated by the Scheduler.
{
TRACE_EVENT0("cc", "commit");
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
base::TimeTicks startTime = base::TimeTicks::HighResNow();
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::beginFrameCompleteOnImplThread, base::Unretained(this), &completion, queue.release()));
completion.wait();
base::TimeTicks endTime = base::TimeTicks::HighResNow();
m_totalCommitTime += endTime - startTime;
m_totalCommitCount++;
}
m_layerTreeHost->commitComplete();
m_layerTreeHost->didBeginFrame();
}
void ThreadProxy::beginFrameCompleteOnImplThread(CompletionEvent* completion, ResourceUpdateQueue* rawQueue)
{
scoped_ptr<ResourceUpdateQueue> queue(rawQueue);
TRACE_EVENT0("cc", "ThreadProxy::beginFrameCompleteOnImplThread");
DCHECK(!m_commitCompletionEventOnImplThread);
DCHECK(isImplThread() && isMainThreadBlocked());
DCHECK(m_schedulerOnImplThread);
DCHECK(m_schedulerOnImplThread->commitPending());
if (!m_layerTreeHostImpl.get()) {
TRACE_EVENT0("cc", "EarlyOut_NoLayerTree");
completion->signal();
return;
}
if (m_layerTreeHost->contentsTextureManager()->linkedEvictedBackingsExist()) {
// Clear any uploads we were making to textures linked to evicted
// resources
queue->clearUploadsToEvictedResources();
// Some textures in the layer tree are invalid. Kick off another commit
// to fill them again.
setNeedsCommitOnImplThread();
}
m_layerTreeHost->contentsTextureManager()->pushTexturePrioritiesToBackings();
m_currentResourceUpdateControllerOnImplThread = ResourceUpdateController::create(this, Proxy::implThread(), queue.Pass(), m_layerTreeHostImpl->resourceProvider(), hasImplThread());
m_currentResourceUpdateControllerOnImplThread->performMoreUpdates(
m_schedulerOnImplThread->anticipatedDrawTime());
m_commitCompletionEventOnImplThread = completion;
}
void ThreadProxy::beginFrameAbortedOnImplThread()
{
TRACE_EVENT0("cc", "ThreadProxy::beginFrameAbortedOnImplThread");
DCHECK(isImplThread());
DCHECK(m_schedulerOnImplThread);
DCHECK(m_schedulerOnImplThread->commitPending());
m_schedulerOnImplThread->beginFrameAborted();
}
void ThreadProxy::scheduledActionCommit()
{
TRACE_EVENT0("cc", "ThreadProxy::scheduledActionCommit");
DCHECK(isImplThread());
DCHECK(m_commitCompletionEventOnImplThread);
DCHECK(m_currentResourceUpdateControllerOnImplThread);
// Complete all remaining texture updates.
m_currentResourceUpdateControllerOnImplThread->finalize();
m_currentResourceUpdateControllerOnImplThread.reset();
// If there are linked evicted backings, these backings' resources may be put into the
// impl tree, so we can't draw yet. Determine this before clearing all evicted backings.
bool newImplTreeHasNoEvictedResources = !m_layerTreeHost->contentsTextureManager()->linkedEvictedBackingsExist();
m_layerTreeHostImpl->beginCommit();
m_layerTreeHost->beginCommitOnImplThread(m_layerTreeHostImpl.get());
m_layerTreeHost->finishCommitOnImplThread(m_layerTreeHostImpl.get());
if (newImplTreeHasNoEvictedResources) {
if (m_layerTreeHostImpl->contentsTexturesPurged())
m_layerTreeHostImpl->resetContentsTexturesPurged();
}
m_layerTreeHostImpl->commitComplete();
m_nextFrameIsNewlyCommittedFrameOnImplThread = true;
m_commitCompletionEventOnImplThread->signal();
m_commitCompletionEventOnImplThread = 0;
// SetVisible kicks off the next scheduler action, so this must be last.
m_schedulerOnImplThread->setVisible(m_layerTreeHostImpl->visible());
}
void ThreadProxy::scheduledActionBeginContextRecreation()
{
DCHECK(isImplThread());
m_mainThreadProxy->postTask(FROM_HERE, base::Bind(&ThreadProxy::beginContextRecreation, base::Unretained(this)));
}
ScheduledActionDrawAndSwapResult ThreadProxy::scheduledActionDrawAndSwapInternal(bool forcedDraw)
{
TRACE_EVENT0("cc", "ThreadProxy::scheduledActionDrawAndSwap");
ScheduledActionDrawAndSwapResult result;
result.didDraw = false;
result.didSwap = false;
DCHECK(isImplThread());
DCHECK(m_layerTreeHostImpl.get());
if (!m_layerTreeHostImpl.get())
return result;
DCHECK(m_layerTreeHostImpl->renderer());
if (!m_layerTreeHostImpl->renderer())
return result;
// FIXME: compute the frame display time more intelligently
base::TimeTicks monotonicTime = base::TimeTicks::Now();
base::Time wallClockTime = base::Time::Now();
if (m_inputHandlerOnImplThread.get())
m_inputHandlerOnImplThread->animate(monotonicTime);
m_layerTreeHostImpl->animate(monotonicTime, wallClockTime);
// This method is called on a forced draw, regardless of whether we are able to produce a frame,
// as the calling site on main thread is blocked until its request completes, and we signal
// completion here. If canDraw() is false, we will indicate success=false to the caller, but we
// must still signal completion to avoid deadlock.
// We guard prepareToDraw() with canDraw() because it always returns a valid frame, so can only
// be used when such a frame is possible. Since drawLayers() depends on the result of
// prepareToDraw(), it is guarded on canDraw() as well.
LayerTreeHostImpl::FrameData frame;
bool drawFrame = m_layerTreeHostImpl->canDraw() && (m_layerTreeHostImpl->prepareToDraw(frame) || forcedDraw);
if (drawFrame) {
m_layerTreeHostImpl->drawLayers(frame);
result.didDraw = true;
}
m_layerTreeHostImpl->didDrawAllLayers(frame);
// Check for a pending compositeAndReadback.
if (m_readbackRequestOnImplThread) {
m_readbackRequestOnImplThread->success = false;
if (drawFrame) {
m_layerTreeHostImpl->readback(m_readbackRequestOnImplThread->pixels, m_readbackRequestOnImplThread->rect);
m_readbackRequestOnImplThread->success = !m_layerTreeHostImpl->isContextLost();
}
m_readbackRequestOnImplThread->completion.signal();
m_readbackRequestOnImplThread = 0;
} else if (drawFrame)
result.didSwap = m_layerTreeHostImpl->swapBuffers();
// Tell the main thread that the the newly-commited frame was drawn.
if (m_nextFrameIsNewlyCommittedFrameOnImplThread) {
m_nextFrameIsNewlyCommittedFrameOnImplThread = false;
m_mainThreadProxy->postTask(FROM_HERE, base::Bind(&ThreadProxy::didCommitAndDrawFrame, base::Unretained(this)));
}
return result;
}
void ThreadProxy::acquireLayerTextures()
{
// Called when the main thread needs to modify a layer texture that is used
// directly by the compositor.
// This method will block until the next compositor draw if there is a
// previously committed frame that is still undrawn. This is necessary to
// ensure that the main thread does not monopolize access to the textures.
DCHECK(isMainThread());
if (m_texturesAcquired)
return;
TRACE_EVENT0("cc", "ThreadProxy::acquireLayerTextures");
DebugScopedSetMainThreadBlocked mainThreadBlocked(this);
CompletionEvent completion;
Proxy::implThread()->postTask(base::Bind(&ThreadProxy::acquireLayerTexturesForMainThreadOnImplThread, base::Unretained(this), &completion));
completion.wait(); // Block until it is safe to write to layer textures from the main thread.
m_texturesAcquired = true;
}
void ThreadProxy::acquireLayerTexturesForMainThreadOnImplThread(CompletionEvent* completion)
{
DCHECK(isImplThread());
DCHECK(!m_textureAcquisitionCompletionEventOnImplThread);
m_textureAcquisitionCompletionEventOnImplThread = completion;
m_schedulerOnImplThread->setMainThreadNeedsLayerTextures();
}
void ThreadProxy::scheduledActionAcquireLayerTexturesForMainThread()
{
DCHECK(m_textureAcquisitionCompletionEventOnImplThread);
m_textureAcquisitionCompletionEventOnImplThread->signal();
m_textureAcquisitionCompletionEventOnImplThread = 0;
}
ScheduledActionDrawAndSwapResult ThreadProxy::scheduledActionDrawAndSwapIfPossible()
{
return scheduledActionDrawAndSwapInternal(false);
}
ScheduledActionDrawAndSwapResult ThreadProxy::scheduledActionDrawAndSwapForced()
{
return scheduledActionDrawAndSwapInternal(true);
}
void ThreadProxy::didAnticipatedDrawTimeChange(base::TimeTicks time)
{
if (!m_currentResourceUpdateControllerOnImplThread)
return;
m_currentResourceUpdateControllerOnImplThread->performMoreUpdates(time);
}
void ThreadProxy::readyToFinalizeTextureUpdates()
{
DCHECK(isImplThread());
m_schedulerOnImplThread->beginFrameComplete();
}
void ThreadProxy::didCommitAndDrawFrame()
{
DCHECK(isMainThread());
if (!m_layerTreeHost)
return;
m_layerTreeHost->didCommitAndDrawFrame();
}
void ThreadProxy::didCompleteSwapBuffers()
{
DCHECK(isMainThread());
if (!m_layerTreeHost)
return;
m_layerTreeHost->didCompleteSwapBuffers();
}
void ThreadProxy::setAnimationEvents(scoped_ptr<AnimationEventsVector> events, base::Time wallClockTime)
{
TRACE_EVENT0("cc", "ThreadProxy::setAnimationEvents");
DCHECK(isMainThread());
if (!m_layerTreeHost)
return;
m_layerTreeHost->setAnimationEvents(events.Pass(), wallClockTime);
}
void ThreadProxy::beginContextRecreation()
{
TRACE_EVENT0("cc", "ThreadProxy::beginContextRecreation");
DCHECK(isMainThread());
m_layerTreeHost->didLoseContext();
m_contextRecreationCallback.Reset(base::Bind(&ThreadProxy::tryToRecreateContext, base::Unretained(this)));
Proxy::mainThread()->postTask(m_contextRecreationCallback.callback());
}
void ThreadProxy::tryToRecreateContext()
{
DCHECK(isMainThread());
DCHECK(m_layerTreeHost);
LayerTreeHost::RecreateResult result = m_layerTreeHost->recreateContext();
if (result == LayerTreeHost::RecreateFailedButTryAgain)
Proxy::mainThread()->postTask(m_contextRecreationCallback.callback());
else if (result == LayerTreeHost::RecreateSucceeded)
m_contextRecreationCallback.Cancel();
}
void ThreadProxy::initializeImplOnImplThread(CompletionEvent* completion, InputHandler* handler)
{
TRACE_EVENT0("cc", "ThreadProxy::initializeImplOnImplThread");
DCHECK(isImplThread());
m_layerTreeHostImpl = m_layerTreeHost->createLayerTreeHostImpl(this);
const base::TimeDelta displayRefreshInterval = base::TimeDelta::FromMicroseconds(base::Time::kMicrosecondsPerSecond / 60);
scoped_ptr<FrameRateController> frameRateController;
if (m_renderVSyncEnabled)
frameRateController.reset(new FrameRateController(DelayBasedTimeSource::create(displayRefreshInterval, Proxy::implThread())));
else
frameRateController.reset(new FrameRateController(Proxy::implThread()));
m_schedulerOnImplThread = Scheduler::create(this, frameRateController.Pass());
m_schedulerOnImplThread->setVisible(m_layerTreeHostImpl->visible());
m_inputHandlerOnImplThread = scoped_ptr<InputHandler>(handler);
if (m_inputHandlerOnImplThread.get())
m_inputHandlerOnImplThread->bindToClient(m_layerTreeHostImpl.get());
completion->signal();
}
void ThreadProxy::initializeContextOnImplThread(scoped_ptr<GraphicsContext> context)
{
TRACE_EVENT0("cc", "ThreadProxy::initializeContextOnImplThread");
DCHECK(isImplThread());
m_contextBeforeInitializationOnImplThread = context.Pass();
}
void ThreadProxy::initializeRendererOnImplThread(CompletionEvent* completion, bool* initializeSucceeded, RendererCapabilities* capabilities)
{
TRACE_EVENT0("cc", "ThreadProxy::initializeRendererOnImplThread");
DCHECK(isImplThread());
DCHECK(m_contextBeforeInitializationOnImplThread.get());
*initializeSucceeded = m_layerTreeHostImpl->initializeRenderer(m_contextBeforeInitializationOnImplThread.Pass());
if (*initializeSucceeded) {
*capabilities = m_layerTreeHostImpl->rendererCapabilities();
m_schedulerOnImplThread->setSwapBuffersCompleteSupported(
capabilities->usingSwapCompleteCallback);
}
completion->signal();
}
void ThreadProxy::layerTreeHostClosedOnImplThread(CompletionEvent* completion)
{
TRACE_EVENT0("cc", "ThreadProxy::layerTreeHostClosedOnImplThread");
DCHECK(isImplThread());
m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->resourceProvider());
m_inputHandlerOnImplThread.reset();
m_layerTreeHostImpl.reset();
m_schedulerOnImplThread.reset();
completion->signal();
}
void ThreadProxy::setFullRootLayerDamageOnImplThread()
{
DCHECK(isImplThread());
m_layerTreeHostImpl->setFullRootLayerDamage();
}
size_t ThreadProxy::maxPartialTextureUpdates() const
{
return ResourceUpdateController::maxPartialTextureUpdates();
}
void ThreadProxy::recreateContextOnImplThread(CompletionEvent* completion, scoped_ptr<GraphicsContext> context, bool* recreateSucceeded, RendererCapabilities* capabilities)
{
TRACE_EVENT0("cc", "ThreadProxy::recreateContextOnImplThread");
DCHECK(isImplThread());
m_layerTreeHost->deleteContentsTexturesOnImplThread(m_layerTreeHostImpl->resourceProvider());
*recreateSucceeded = m_layerTreeHostImpl->initializeRenderer(context.Pass());
if (*recreateSucceeded) {
*capabilities = m_layerTreeHostImpl->rendererCapabilities();
m_schedulerOnImplThread->didRecreateContext();
}
completion->signal();
}
void ThreadProxy::renderingStatsOnImplThread(CompletionEvent* completion, RenderingStats* stats)
{
DCHECK(isImplThread());
m_layerTreeHostImpl->renderingStats(stats);
completion->signal();
}
ThreadProxy::BeginFrameAndCommitState::BeginFrameAndCommitState()
: memoryAllocationLimitBytes(0)
{
}
ThreadProxy::BeginFrameAndCommitState::~BeginFrameAndCommitState()
{
}
} // namespace cc