blob: b04f9c6b97a69a68ef3a8d8a21ac76509e2d10c1 [file] [log] [blame]
/*
* Copyright (C) 2015 Igalia S.L.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "TextureMapperPlatformLayerProxy.h"
#if USE(COORDINATED_GRAPHICS)
#include "BitmapTextureGL.h"
#include "TextureMapperGL.h"
#include "TextureMapperLayer.h"
#include "TextureMapperPlatformLayerBuffer.h"
#if USE(GLIB_EVENT_LOOP)
#include <wtf/glib/RunLoopSourcePriority.h>
#endif
#include <wtf/Scope.h>
static const Seconds releaseUnusedSecondsTolerance { 1_s };
static const Seconds releaseUnusedBuffersTimerInterval = { 500_ms };
namespace WebCore {
TextureMapperPlatformLayerProxy::TextureMapperPlatformLayerProxy()
: m_compositor(nullptr)
, m_targetLayer(nullptr)
{
}
TextureMapperPlatformLayerProxy::~TextureMapperPlatformLayerProxy()
{
Locker locker { m_lock };
if (m_targetLayer)
m_targetLayer->setContentsLayer(nullptr);
}
void TextureMapperPlatformLayerProxy::activateOnCompositingThread(Compositor* compositor, TextureMapperLayer* targetLayer)
{
#ifndef NDEBUG
if (!m_compositorThread)
m_compositorThread = &Thread::current();
#endif
ASSERT(m_compositorThread == &Thread::current());
ASSERT(compositor);
ASSERT(targetLayer);
Function<void()> updateFunction;
{
Locker locker { m_lock };
m_compositor = compositor;
m_targetLayer = targetLayer;
if (m_targetLayer && m_currentBuffer)
m_targetLayer->setContentsLayer(m_currentBuffer.get());
m_releaseUnusedBuffersTimer = makeUnique<RunLoop::Timer<TextureMapperPlatformLayerProxy>>(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired);
m_compositorThreadUpdateTimer = makeUnique<RunLoop::Timer<TextureMapperPlatformLayerProxy>>(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired);
#if USE(GLIB_EVENT_LOOP)
m_compositorThreadUpdateTimer->setPriority(RunLoopSourcePriority::CompositingThreadUpdateTimer);
m_releaseUnusedBuffersTimer->setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
#endif
if (!m_compositorThreadUpdateFunction)
return;
updateFunction = WTFMove(m_compositorThreadUpdateFunction);
}
updateFunction();
}
void TextureMapperPlatformLayerProxy::invalidate()
{
ASSERT(m_compositorThread == &Thread::current());
Function<void()> updateFunction;
{
Locker locker { m_lock };
m_compositor = nullptr;
m_targetLayer = nullptr;
m_currentBuffer = nullptr;
m_pendingBuffer = nullptr;
m_releaseUnusedBuffersTimer = nullptr;
m_usedBuffers.clear();
// Clear the timer and dispatch the update function manually now.
m_compositorThreadUpdateTimer = nullptr;
if (!m_compositorThreadUpdateFunction)
return;
updateFunction = WTFMove(m_compositorThreadUpdateFunction);
}
updateFunction();
}
bool TextureMapperPlatformLayerProxy::isActive()
{
ASSERT(m_lock.isHeld());
return !!m_targetLayer && !!m_compositor;
}
void TextureMapperPlatformLayerProxy::pushNextBuffer(std::unique_ptr<TextureMapperPlatformLayerBuffer>&& newBuffer)
{
ASSERT(m_lock.isHeld());
#if USE(ANGLE)
// When the newBuffer overwrites m_pendingBuffer that is not swapped yet,
// the layer related to m_pendingBuffer is flickering since its texture is destroyed.
if (m_pendingBuffer && m_pendingBuffer->hasManagedTexture())
return;
#endif
m_pendingBuffer = WTFMove(newBuffer);
m_wasBufferDropped = false;
if (m_compositor)
m_compositor->onNewBufferAvailable();
}
std::unique_ptr<TextureMapperPlatformLayerBuffer> TextureMapperPlatformLayerProxy::getAvailableBuffer(const IntSize& size, GLint internalFormat)
{
ASSERT(m_lock.isHeld());
ASSERT(m_compositorThread == &Thread::current());
std::unique_ptr<TextureMapperPlatformLayerBuffer> availableBuffer;
auto buffers = WTFMove(m_usedBuffers);
for (auto& buffer : buffers) {
if (!buffer)
continue;
if (!availableBuffer && buffer->canReuseWithoutReset(size, internalFormat)) {
availableBuffer = WTFMove(buffer);
availableBuffer->markUsed();
continue;
}
m_usedBuffers.append(WTFMove(buffer));
}
if (!m_usedBuffers.isEmpty())
scheduleReleaseUnusedBuffers();
return availableBuffer;
}
void TextureMapperPlatformLayerProxy::appendToUnusedBuffers(std::unique_ptr<TextureMapperPlatformLayerBuffer> buffer)
{
ASSERT(m_lock.isHeld());
ASSERT(m_compositorThread == &Thread::current());
m_usedBuffers.append(WTFMove(buffer));
scheduleReleaseUnusedBuffers();
}
void TextureMapperPlatformLayerProxy::scheduleReleaseUnusedBuffers()
{
if (!m_releaseUnusedBuffersTimer->isActive())
m_releaseUnusedBuffersTimer->startOneShot(releaseUnusedBuffersTimerInterval);
}
void TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired()
{
Locker locker { m_lock };
if (m_usedBuffers.isEmpty())
return;
auto buffers = WTFMove(m_usedBuffers);
MonotonicTime minUsedTime = MonotonicTime::now() - releaseUnusedSecondsTolerance;
for (auto& buffer : buffers) {
if (buffer && buffer->lastUsedTime() >= minUsedTime)
m_usedBuffers.append(WTFMove(buffer));
}
}
void TextureMapperPlatformLayerProxy::swapBuffer()
{
ASSERT(m_compositorThread == &Thread::current());
Locker locker { m_lock };
if (!m_targetLayer || !m_pendingBuffer)
return;
auto prevBuffer = WTFMove(m_currentBuffer);
m_currentBuffer = WTFMove(m_pendingBuffer);
m_targetLayer->setContentsLayer(m_currentBuffer.get());
if (prevBuffer && prevBuffer->hasManagedTexture())
appendToUnusedBuffers(WTFMove(prevBuffer));
}
void TextureMapperPlatformLayerProxy::dropCurrentBufferWhilePreservingTexture(bool shouldWait)
{
if (!shouldWait)
ASSERT(m_lock.isHeld());
if (m_pendingBuffer && m_pendingBuffer->hasManagedTexture()) {
m_usedBuffers.append(WTFMove(m_pendingBuffer));
scheduleReleaseUnusedBuffers();
}
if (!m_compositorThreadUpdateTimer)
return;
m_compositorThreadUpdateFunction =
[this, shouldWait] {
Locker locker { m_lock };
auto maybeNotifySynchronousOperation = makeScopeExit([this, shouldWait]() {
if (shouldWait) {
Locker locker { m_wasBufferDroppedLock };
m_wasBufferDropped = true;
m_wasBufferDroppedCondition.notifyAll();
}
});
if (!m_compositor || !m_targetLayer || !m_currentBuffer)
return;
m_pendingBuffer = m_currentBuffer->clone();
auto prevBuffer = WTFMove(m_currentBuffer);
m_currentBuffer = WTFMove(m_pendingBuffer);
m_targetLayer->setContentsLayer(m_currentBuffer.get());
if (prevBuffer->hasManagedTexture())
appendToUnusedBuffers(WTFMove(prevBuffer));
};
if (shouldWait) {
Locker locker { m_wasBufferDroppedLock };
m_wasBufferDropped = false;
}
m_compositorThreadUpdateTimer->startOneShot(0_s);
if (shouldWait) {
Locker locker { m_wasBufferDroppedLock };
m_wasBufferDroppedCondition.wait(m_wasBufferDroppedLock, [this] {
assertIsHeld(m_wasBufferDroppedLock);
return m_wasBufferDropped;
});
}
}
bool TextureMapperPlatformLayerProxy::scheduleUpdateOnCompositorThread(Function<void()>&& updateFunction)
{
Locker locker { m_lock };
m_compositorThreadUpdateFunction = WTFMove(updateFunction);
if (!m_compositorThreadUpdateTimer)
return false;
m_compositorThreadUpdateTimer->startOneShot(0_s);
return true;
}
void TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired()
{
Function<void()> updateFunction;
{
Locker locker { m_lock };
if (!m_compositorThreadUpdateFunction)
return;
updateFunction = WTFMove(m_compositorThreadUpdateFunction);
}
updateFunction();
}
} // namespace WebCore
#endif // USE(COORDINATED_GRAPHICS)