ThreadedCompositor.cpp [plain text]
#include "config.h"
#if USE(COORDINATED_GRAPHICS_THREADED)
#include "ThreadedCompositor.h"
#include <WebCore/TransformationMatrix.h>
#include <wtf/CurrentTime.h>
#include <wtf/RunLoop.h>
#include <wtf/StdLibExtras.h>
#if USE(OPENGL_ES_2)
#include <GLES2/gl2.h>
#else
#include <GL/gl.h>
#endif
using namespace WebCore;
namespace WebKit {
class CompositingRunLoop {
WTF_MAKE_NONCOPYABLE(CompositingRunLoop);
WTF_MAKE_FAST_ALLOCATED;
public:
enum UpdateTiming {
Immediate,
WaitUntilNextFrame,
};
CompositingRunLoop(std::function<void()> updateFunction)
: m_runLoop(RunLoop::current())
, m_updateTimer(m_runLoop, this, &CompositingRunLoop::updateTimerFired)
, m_updateFunction(WTF::move(updateFunction))
, m_lastUpdateTime(0)
{
}
void callOnCompositingRunLoop(std::function<void()> function)
{
if (&m_runLoop == &RunLoop::current()) {
function();
return;
}
m_runLoop.dispatch(WTF::move(function));
}
void setUpdateTimer(UpdateTiming timing = Immediate)
{
if (m_updateTimer.isActive())
return;
const static double targetFPS = 60;
double nextUpdateTime = 0;
if (timing == WaitUntilNextFrame)
nextUpdateTime = std::max((1 / targetFPS) - (monotonicallyIncreasingTime() - m_lastUpdateTime), 0.0);
m_updateTimer.startOneShot(nextUpdateTime);
}
void stopUpdateTimer()
{
if (m_updateTimer.isActive())
m_updateTimer.stop();
}
RunLoop& runLoop()
{
return m_runLoop;
}
private:
void updateTimerFired()
{
m_updateFunction();
m_lastUpdateTime = monotonicallyIncreasingTime();
}
RunLoop& m_runLoop;
RunLoop::Timer<CompositingRunLoop> m_updateTimer;
std::function<void()> m_updateFunction;
double m_lastUpdateTime;
};
Ref<ThreadedCompositor> ThreadedCompositor::create(Client* client)
{
return adoptRef(*new ThreadedCompositor(client));
}
ThreadedCompositor::ThreadedCompositor(Client* client)
: m_client(client)
, m_threadIdentifier(0)
{
createCompositingThread();
}
ThreadedCompositor::~ThreadedCompositor()
{
terminateCompositingThread();
}
void ThreadedCompositor::setNeedsDisplay()
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->scheduleDisplayImmediately();
});
}
void ThreadedCompositor::setNativeSurfaceHandleForCompositing(uint64_t handle)
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->m_nativeSurfaceHandle = handle;
protector->m_scene->setActive(true);
});
}
void ThreadedCompositor::didChangeViewportSize(const IntSize& newSize)
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->viewportController()->didChangeViewportSize(newSize);
});
}
void ThreadedCompositor::didChangeViewportAttribute(const ViewportAttributes& attr)
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->viewportController()->didChangeViewportAttribute(attr);
});
}
void ThreadedCompositor::didChangeContentsSize(const IntSize& size)
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->viewportController()->didChangeContentsSize(size);
});
}
void ThreadedCompositor::scrollTo(const IntPoint& position)
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->viewportController()->scrollTo(position);
});
}
void ThreadedCompositor::scrollBy(const IntSize& delta)
{
RefPtr<ThreadedCompositor> protector(this);
callOnCompositingThread([=] {
protector->viewportController()->scrollBy(delta);
});
}
void ThreadedCompositor::purgeBackingStores()
{
m_client->purgeBackingStores();
}
void ThreadedCompositor::renderNextFrame()
{
m_client->renderNextFrame();
}
void ThreadedCompositor::updateViewport()
{
m_compositingRunLoop->setUpdateTimer(CompositingRunLoop::WaitUntilNextFrame);
}
void ThreadedCompositor::commitScrollOffset(uint32_t layerID, const IntSize& offset)
{
m_client->commitScrollOffset(layerID, offset);
}
bool ThreadedCompositor::ensureGLContext()
{
if (!glContext())
return false;
glContext()->makeContextCurrent();
IntSize contextSize = glContext()->defaultFrameBufferSize();
if (m_viewportSize != contextSize) {
glViewport(0, 0, contextSize.width(), contextSize.height());
m_viewportSize = contextSize;
}
return true;
}
GLContext* ThreadedCompositor::glContext()
{
if (m_context)
return m_context.get();
if (!m_nativeSurfaceHandle)
return 0;
m_context = GLContext::createContextForWindow(m_nativeSurfaceHandle, GLContext::sharingContext());
return m_context.get();
}
void ThreadedCompositor::scheduleDisplayImmediately()
{
m_compositingRunLoop->setUpdateTimer(CompositingRunLoop::Immediate);
}
void ThreadedCompositor::didChangeVisibleRect()
{
FloatRect visibleRect = viewportController()->visibleContentsRect();
float scale = viewportController()->pageScaleFactor();
callOnMainThread([=] {
m_client->setVisibleContentsRect(visibleRect, FloatPoint::zero(), scale);
});
scheduleDisplayImmediately();
}
void ThreadedCompositor::renderLayerTree()
{
if (!m_scene)
return;
if (!ensureGLContext())
return;
FloatRect clipRect(0, 0, m_viewportSize.width(), m_viewportSize.height());
TransformationMatrix viewportTransform;
FloatPoint scrollPostion = viewportController()->visibleContentsRect().location();
viewportTransform.scale(viewportController()->pageScaleFactor());
viewportTransform.translate(-scrollPostion.x(), -scrollPostion.y());
m_scene->paintToCurrentGLContext(viewportTransform, 1, clipRect, Color::white, false, scrollPostion);
glContext()->swapBuffers();
}
void ThreadedCompositor::updateSceneState(const CoordinatedGraphicsState& state)
{
RefPtr<CoordinatedGraphicsScene> scene = m_scene;
m_scene->appendUpdate([scene, state] {
scene->commitSceneState(state);
});
setNeedsDisplay();
}
void ThreadedCompositor::callOnCompositingThread(std::function<void()> function)
{
m_compositingRunLoop->callOnCompositingRunLoop(WTF::move(function));
}
void ThreadedCompositor::compositingThreadEntry(void* coordinatedCompositor)
{
static_cast<ThreadedCompositor*>(coordinatedCompositor)->runCompositingThread();
}
void ThreadedCompositor::createCompositingThread()
{
if (m_threadIdentifier)
return;
MutexLocker locker(m_initializeRunLoopConditionMutex);
m_threadIdentifier = createThread(compositingThreadEntry, this, "WebCore: ThreadedCompositor");
m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex);
}
void ThreadedCompositor::runCompositingThread()
{
{
MutexLocker locker(m_initializeRunLoopConditionMutex);
m_compositingRunLoop = std::make_unique<CompositingRunLoop>([&] {
renderLayerTree();
});
m_scene = adoptRef(new CoordinatedGraphicsScene(this));
m_viewportController = std::make_unique<SimpleViewportController>(this);
m_initializeRunLoopCondition.signal();
}
m_compositingRunLoop->runLoop().run();
m_compositingRunLoop->stopUpdateTimer();
m_scene->purgeGLResources();
{
MutexLocker locker(m_terminateRunLoopConditionMutex);
m_compositingRunLoop = nullptr;
m_context = nullptr;
m_terminateRunLoopCondition.signal();
}
detachThread(m_threadIdentifier);
}
void ThreadedCompositor::terminateCompositingThread()
{
MutexLocker locker(m_terminateRunLoopConditionMutex);
m_scene->detach();
m_compositingRunLoop->runLoop().stop();
m_terminateRunLoopCondition.wait(m_terminateRunLoopConditionMutex);
}
}
#endif // USE(COORDINATED_GRAPHICS_THREADED)