1/*
2 * Copyright (C) 2015 Igalia S.L.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "TextureMapperPlatformLayerProxy.h"
28
29#if USE(COORDINATED_GRAPHICS)
30
31#include "BitmapTextureGL.h"
32#include "TextureMapperGL.h"
33#include "TextureMapperLayer.h"
34#include "TextureMapperPlatformLayerBuffer.h"
35
36#if USE(GLIB_EVENT_LOOP)
37#include <wtf/glib/RunLoopSourcePriority.h>
38#endif
39#include <wtf/Scope.h>
40
41static const Seconds releaseUnusedSecondsTolerance { 1_s };
42static const Seconds releaseUnusedBuffersTimerInterval = { 500_ms };
43
44namespace WebCore {
45
46TextureMapperPlatformLayerProxy::TextureMapperPlatformLayerProxy()
47 : m_compositor(nullptr)
48 , m_targetLayer(nullptr)
49{
50}
51
52TextureMapperPlatformLayerProxy::~TextureMapperPlatformLayerProxy()
53{
54 LockHolder locker(m_lock);
55 if (m_targetLayer)
56 m_targetLayer->setContentsLayer(nullptr);
57}
58
59void TextureMapperPlatformLayerProxy::activateOnCompositingThread(Compositor* compositor, TextureMapperLayer* targetLayer)
60{
61#ifndef NDEBUG
62 if (!m_compositorThread)
63 m_compositorThread = &Thread::current();
64#endif
65 ASSERT(m_compositorThread == &Thread::current());
66 ASSERT(compositor);
67 ASSERT(targetLayer);
68 LockHolder locker(m_lock);
69 m_compositor = compositor;
70 m_targetLayer = targetLayer;
71 if (m_targetLayer && m_currentBuffer)
72 m_targetLayer->setContentsLayer(m_currentBuffer.get());
73
74 m_releaseUnusedBuffersTimer = std::make_unique<RunLoop::Timer<TextureMapperPlatformLayerProxy>>(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired);
75 m_compositorThreadUpdateTimer = std::make_unique<RunLoop::Timer<TextureMapperPlatformLayerProxy>>(RunLoop::current(), this, &TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired);
76
77#if USE(GLIB_EVENT_LOOP)
78 m_compositorThreadUpdateTimer->setPriority(RunLoopSourcePriority::CompositingThreadUpdateTimer);
79 m_releaseUnusedBuffersTimer->setPriority(RunLoopSourcePriority::ReleaseUnusedResourcesTimer);
80#endif
81}
82
83void TextureMapperPlatformLayerProxy::invalidate()
84{
85 ASSERT(m_compositorThread == &Thread::current());
86 Function<void()> updateFunction;
87 {
88 LockHolder locker(m_lock);
89 m_compositor = nullptr;
90 m_targetLayer = nullptr;
91
92 m_currentBuffer = nullptr;
93 m_pendingBuffer = nullptr;
94 m_releaseUnusedBuffersTimer = nullptr;
95 m_usedBuffers.clear();
96
97 // Clear the timer and dispatch the update function manually now.
98 m_compositorThreadUpdateTimer = nullptr;
99 if (!m_compositorThreadUpdateFunction)
100 return;
101 updateFunction = WTFMove(m_compositorThreadUpdateFunction);
102 }
103
104 updateFunction();
105}
106
107bool TextureMapperPlatformLayerProxy::isActive()
108{
109 ASSERT(m_lock.isHeld());
110 return !!m_targetLayer && !!m_compositor;
111}
112
113void TextureMapperPlatformLayerProxy::pushNextBuffer(std::unique_ptr<TextureMapperPlatformLayerBuffer> newBuffer)
114{
115 ASSERT(m_lock.isHeld());
116 m_pendingBuffer = WTFMove(newBuffer);
117 m_wasBufferDropped = false;
118
119 if (m_compositor)
120 m_compositor->onNewBufferAvailable();
121}
122
123std::unique_ptr<TextureMapperPlatformLayerBuffer> TextureMapperPlatformLayerProxy::getAvailableBuffer(const IntSize& size, GLint internalFormat)
124{
125 ASSERT(m_lock.isHeld());
126 ASSERT(m_compositorThread == &Thread::current());
127 std::unique_ptr<TextureMapperPlatformLayerBuffer> availableBuffer;
128
129 auto buffers = WTFMove(m_usedBuffers);
130 for (auto& buffer : buffers) {
131 if (!buffer)
132 continue;
133
134 if (!availableBuffer && buffer->canReuseWithoutReset(size, internalFormat)) {
135 availableBuffer = WTFMove(buffer);
136 availableBuffer->markUsed();
137 continue;
138 }
139 m_usedBuffers.append(WTFMove(buffer));
140 }
141
142 if (!m_usedBuffers.isEmpty())
143 scheduleReleaseUnusedBuffers();
144 return availableBuffer;
145}
146
147void TextureMapperPlatformLayerProxy::appendToUnusedBuffers(std::unique_ptr<TextureMapperPlatformLayerBuffer> buffer)
148{
149 ASSERT(m_lock.isHeld());
150 ASSERT(m_compositorThread == &Thread::current());
151 m_usedBuffers.append(WTFMove(buffer));
152 scheduleReleaseUnusedBuffers();
153}
154
155void TextureMapperPlatformLayerProxy::scheduleReleaseUnusedBuffers()
156{
157 if (!m_releaseUnusedBuffersTimer->isActive())
158 m_releaseUnusedBuffersTimer->startOneShot(releaseUnusedBuffersTimerInterval);
159}
160
161void TextureMapperPlatformLayerProxy::releaseUnusedBuffersTimerFired()
162{
163 LockHolder locker(m_lock);
164 if (m_usedBuffers.isEmpty())
165 return;
166
167 auto buffers = WTFMove(m_usedBuffers);
168 MonotonicTime minUsedTime = MonotonicTime::now() - releaseUnusedSecondsTolerance;
169
170 for (auto& buffer : buffers) {
171 if (buffer && buffer->lastUsedTime() >= minUsedTime)
172 m_usedBuffers.append(WTFMove(buffer));
173 }
174}
175
176void TextureMapperPlatformLayerProxy::swapBuffer()
177{
178 ASSERT(m_compositorThread == &Thread::current());
179 LockHolder locker(m_lock);
180 if (!m_targetLayer || !m_pendingBuffer)
181 return;
182
183 auto prevBuffer = WTFMove(m_currentBuffer);
184
185 m_currentBuffer = WTFMove(m_pendingBuffer);
186 m_targetLayer->setContentsLayer(m_currentBuffer.get());
187
188 if (prevBuffer && prevBuffer->hasManagedTexture())
189 appendToUnusedBuffers(WTFMove(prevBuffer));
190}
191
192void TextureMapperPlatformLayerProxy::dropCurrentBufferWhilePreservingTexture(bool shouldWait)
193{
194 if (!shouldWait)
195 ASSERT(m_lock.isHeld());
196
197 if (m_pendingBuffer && m_pendingBuffer->hasManagedTexture()) {
198 m_usedBuffers.append(WTFMove(m_pendingBuffer));
199 scheduleReleaseUnusedBuffers();
200 }
201
202 if (!m_compositorThreadUpdateTimer)
203 return;
204
205 m_compositorThreadUpdateFunction =
206 [this, shouldWait] {
207 LockHolder locker(m_lock);
208
209 auto maybeNotifySynchronousOperation = WTF::makeScopeExit([this, shouldWait]() {
210 if (shouldWait) {
211 LockHolder holder(m_wasBufferDroppedLock);
212 m_wasBufferDropped = true;
213 m_wasBufferDroppedCondition.notifyAll();
214 }
215 });
216
217 if (!m_compositor || !m_targetLayer || !m_currentBuffer)
218 return;
219
220 m_pendingBuffer = m_currentBuffer->clone();
221 auto prevBuffer = WTFMove(m_currentBuffer);
222 m_currentBuffer = WTFMove(m_pendingBuffer);
223 m_targetLayer->setContentsLayer(m_currentBuffer.get());
224
225 if (prevBuffer->hasManagedTexture())
226 appendToUnusedBuffers(WTFMove(prevBuffer));
227 };
228
229 if (shouldWait) {
230 LockHolder holder(m_wasBufferDroppedLock);
231 m_wasBufferDropped = false;
232 }
233
234 m_compositorThreadUpdateTimer->startOneShot(0_s);
235 if (shouldWait) {
236 LockHolder holder(m_wasBufferDroppedLock);
237 m_wasBufferDroppedCondition.wait(m_wasBufferDroppedLock, [this] {
238 return m_wasBufferDropped;
239 });
240 }
241}
242
243bool TextureMapperPlatformLayerProxy::scheduleUpdateOnCompositorThread(Function<void()>&& updateFunction)
244{
245 LockHolder locker(m_lock);
246 if (!m_compositorThreadUpdateTimer)
247 return false;
248
249 m_compositorThreadUpdateFunction = WTFMove(updateFunction);
250 m_compositorThreadUpdateTimer->startOneShot(0_s);
251 return true;
252}
253
254void TextureMapperPlatformLayerProxy::compositorThreadUpdateTimerFired()
255{
256 Function<void()> updateFunction;
257 {
258 LockHolder locker(m_lock);
259 if (!m_compositorThreadUpdateFunction)
260 return;
261 updateFunction = WTFMove(m_compositorThreadUpdateFunction);
262 }
263
264 updateFunction();
265}
266
267} // namespace WebCore
268
269#endif // USE(COORDINATED_GRAPHICS)
270