89a9cf7716a4c40f90c17b9cfd38b9814e3bc629
[WebKit-https.git] / Source / WebCore / platform / graphics / blackberry / LayerCompositingThread.cpp
1 /*
2  * Copyright (C) 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
3  * Copyright (C) 2010 Google Inc. All rights reserved.
4  * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34
35 #if USE(ACCELERATED_COMPOSITING)
36
37 #include "LayerCompositingThread.h"
38
39 #include "LayerMessage.h"
40 #include "LayerRenderer.h"
41 #include "LayerWebKitThread.h"
42 #if ENABLE(VIDEO)
43 #include "MediaPlayer.h"
44 #include "MediaPlayerPrivateBlackBerry.h"
45 #endif
46 #include "PluginView.h"
47 #include "TextureCacheCompositingThread.h"
48
49 #include <BlackBerryPlatformGraphics.h>
50 #include <BlackBerryPlatformLog.h>
51 #include <wtf/Assertions.h>
52
53 #define DEBUG_VIDEO_CLIPPING 0
54
55 namespace WebCore {
56
57 PassRefPtr<LayerCompositingThread> LayerCompositingThread::create(LayerType type, PassRefPtr<LayerTiler> tiler)
58 {
59     return adoptRef(new LayerCompositingThread(type, tiler));
60 }
61
62 LayerCompositingThread::LayerCompositingThread(LayerType type, PassRefPtr<LayerTiler> tiler)
63     : LayerData(type)
64     , m_layerRenderer(0)
65     , m_superlayer(0)
66     , m_pluginBuffer(0)
67     , m_drawOpacity(0)
68     , m_visible(false)
69     , m_commitScheduled(false)
70     , m_tiler(tiler)
71 {
72 }
73
74 LayerCompositingThread::~LayerCompositingThread()
75 {
76     // Unfortunately, ThreadSafeShared<T> is hardwired to call T::~T().
77     // To switch threads in case the last reference is released on the
78     // WebKit thread, we send a sync message to the compositing thread.
79     destroyOnCompositingThread();
80 }
81
82 void LayerCompositingThread::destroyOnCompositingThread()
83 {
84     if (!isCompositingThread()) {
85         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
86             &LayerCompositingThread::destroyOnCompositingThread,
87             this));
88         return;
89     }
90
91     m_tiler->layerCompositingThreadDestroyed();
92
93     ASSERT(!superlayer());
94
95     // Remove the superlayer reference from all sublayers.
96     while (m_sublayers.size())
97         m_sublayers[0]->removeFromSuperlayer();
98
99     // Delete all allocated textures
100     deleteTextures();
101
102     // We just deleted all our textures, no need for the
103     // layer renderer to track us anymore
104     if (m_layerRenderer)
105         m_layerRenderer->removeLayer(this);
106 }
107
108 void LayerCompositingThread::setLayerRenderer(LayerRenderer* renderer)
109 {
110     // It's not expected that layers will ever switch renderers.
111     ASSERT(!renderer || !m_layerRenderer || renderer == m_layerRenderer);
112
113     m_layerRenderer = renderer;
114     if (m_layerRenderer)
115         m_layerRenderer->addLayer(this);
116 }
117
118 void LayerCompositingThread::deleteTextures()
119 {
120     releaseTextureResources();
121
122     m_tiler->deleteTextures();
123 }
124
125 void LayerCompositingThread::setDrawTransform(const TransformationMatrix& matrix)
126 {
127     m_drawTransform = matrix;
128
129     float bx = m_bounds.width() / 2.0;
130     float by = m_bounds.height() / 2.0;
131     m_transformedBounds.setP1(matrix.mapPoint(FloatPoint(-bx, -by)));
132     m_transformedBounds.setP2(matrix.mapPoint(FloatPoint(-bx, by)));
133     m_transformedBounds.setP3(matrix.mapPoint(FloatPoint(bx, by)));
134     m_transformedBounds.setP4(matrix.mapPoint(FloatPoint(bx, -by)));
135
136     m_drawRect = m_transformedBounds.boundingBox();
137 }
138
139 static FloatQuad getTransformedRect(const IntSize& bounds, const IntRect& rect, const TransformationMatrix& drawTransform)
140 {
141     float x = -bounds.width() / 2.0 + rect.x();
142     float y = -bounds.height() / 2.0 + rect.y();
143     float w = rect.width();
144     float h = rect.height();
145     FloatQuad result;
146     result.setP1(drawTransform.mapPoint(FloatPoint(x, y)));
147     result.setP2(drawTransform.mapPoint(FloatPoint(x, y + h)));
148     result.setP3(drawTransform.mapPoint(FloatPoint(x + w, y + h)));
149     result.setP4(drawTransform.mapPoint(FloatPoint(x + w, y)));
150
151     return result;
152 }
153
154
155 FloatQuad LayerCompositingThread::getTransformedHolePunchRect() const
156 {
157     // FIXME: the following line disables clipping a video in an iframe i.e. the fix associated with PR 99638.
158     // Some revised test case (e.g. video-iframe.html) show that the original fix works correctly when scrolling
159     // the contents of the frame, but fails to clip correctly if the page (main frame) is scrolled.
160     static bool enableVideoClipping = false;
161
162     if (!mediaPlayer() || !enableVideoClipping) {
163         // m_holePunchClipRect is valid only when there's a media player.
164         return getTransformedRect(m_bounds, m_holePunchRect, m_drawTransform);
165     }
166
167     // The hole punch rectangle may need to be clipped,
168     // e.g. if the <video> is on a layer that's included and clipped by an <iframe>.
169
170     // In order to clip we need to determine the current position of this layer, which
171     // is encoded in the m_drawTransform value, which was used to initialize m_drawRect.
172     IntRect drawRect = m_layerRenderer->toWebKitDocumentCoordinates(m_drawRect);
173
174     // Assert that in this case, where the hole punch rectangle equals the size of the layer,
175     // the drawRect has the same size as the hole punch.
176     // ASSERT(drawRect.size() == m_holePunchRect.size());
177     // Don't assert it programtically though because there may be off-by-one error due to rounding when there's zooming.
178
179     // The difference between drawRect and m_holePunchRect is that drawRect has an accurate position
180     // in WebKit document coordinates, whereas the m_holePunchRect location is (0,0) i.e. it's relative to this layer.
181
182     // Clip the drawRect.
183     // Both drawRect and m_holePunchClipRect already have correct locations, in WebKit document coordinates.
184     IntPoint location = drawRect.location();
185     drawRect.intersect(m_holePunchClipRect);
186
187     // Shift the clipped drawRect to have the same kind of located-at-zero position as the original holePunchRect.
188     drawRect.move(-location.x(), -location.y());
189
190 #if DEBUG_VIDEO_CLIPPING
191      IntRect drawRectInWebKitDocumentCoordination = m_layerRenderer->toWebKitDocumentCoordinates(m_drawRect);
192      BlackBerry::Platform::log(BlackBerry::Platform::LogLevelInfo, "LayerCompositingThread::getTransformedHolePunchRect() - drawRect=(x=%d,y=%d,width=%d,height=%d) clipRect=(x=%d,y=%d,width=%d,height=%d) clippedRect=(x=%d,y=%d,width=%d,height=%d).",
193         drawRectInWebKitDocumentCoordination.x(), drawRectInWebKitDocumentCoordination.y(), drawRectInWebKitDocumentCoordination.width(), drawRectInWebKitDocumentCoordination.height(),
194         m_holePunchClipRect.x(), m_holePunchClipRect.y(), m_holePunchClipRect.width(), m_holePunchClipRect.height(),
195         drawRect.x(), drawRect.y(), drawRect.width(), drawRect.height());
196 #endif
197
198     return getTransformedRect(m_bounds, drawRect, m_drawTransform);
199 }
200
201 void LayerCompositingThread::drawTextures(int positionLocation, int texCoordLocation, const FloatRect& visibleRect)
202 {
203     float texcoords[4 * 2] = { 0, 0,  0, 1,  1, 1,  1, 0 };
204
205     if (m_pluginView) {
206         if (m_isVisible) {
207             // The layer contains Flash, video, or other plugin contents.
208             m_pluginBuffer = m_pluginView->lockFrontBufferForRead();
209
210             if (!m_pluginBuffer)
211                 return;
212
213             if (!BlackBerry::Platform::Graphics::lockAndBindBufferGLTexture(m_pluginBuffer, GL_TEXTURE_2D)) {
214                 m_pluginView->unlockFrontBuffer();
215                 return;
216             }
217
218             m_layerRenderer->addLayerToReleaseTextureResourcesList(this);
219
220             glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &m_transformedBounds);
221             glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
222             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
223         }
224         return;
225     }
226 #if ENABLE(VIDEO)
227     if (m_mediaPlayer) {
228         if (m_isVisible) {
229             // We need to specify the media player location in contents coordinates. The 'visibleRect'
230             // specifies the content region covered by our viewport. So we transform from our
231             // normalized device coordinates [-1, 1] to the 'visibleRect'.
232             float vrw2 = visibleRect.width() / 2.0;
233             float vrh2 = visibleRect.height() / 2.0;
234             float x = m_transformedBounds.p1().x() * vrw2 + vrw2 + visibleRect.x();
235             float y = -m_transformedBounds.p1().y() * vrh2 + vrh2 + visibleRect.y();
236             m_mediaPlayer->paint(0, IntRect((int)(x + 0.5), (int)(y + 0.5), m_bounds.width(), m_bounds.height()));
237             MediaPlayerPrivate* mpp = static_cast<MediaPlayerPrivate*>(m_mediaPlayer->platformMedia().media.qnxMediaPlayer);
238             mpp->drawBufferingAnimation(m_drawTransform, positionLocation, texCoordLocation);
239         }
240         return;
241     }
242 #endif
243 #if ENABLE(WEBGL)
244     if (layerType() == LayerData::WebGLLayer) {
245         pthread_mutex_lock(m_frontBufferLock);
246         glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &m_transformedBounds);
247         float canvasWidthRatio = 1.0f;
248         float canvasHeightRatio = 1.0f;
249         float upsideDown[4 * 2] = { 0, 1,  0, 1 - canvasHeightRatio,  canvasWidthRatio, 1 - canvasHeightRatio,  canvasWidthRatio, 1 };
250         // Flip the texture Y axis because OpenGL and Skia have different origins
251         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, upsideDown);
252         glBindTexture(GL_TEXTURE_2D, m_texID);
253         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
254         pthread_mutex_unlock(m_frontBufferLock);
255         // FIXME: If the canvas/texture is larger than 2048x2048, then we'll die here
256         return;
257     }
258 #endif
259     if (m_texID) {
260         m_layerRenderer->addLayerToReleaseTextureResourcesList(this);
261         pthread_mutex_lock(m_frontBufferLock);
262
263         glDisable(GL_SCISSOR_TEST);
264         glBindTexture(GL_TEXTURE_2D, m_texID);
265         glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &m_transformedBounds);
266         float upsideDown[4 * 2] = { 0, 1,  0, 0,  1, 0,  1, 1 };
267         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, upsideDown);
268         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
269         return;
270     }
271
272     m_tiler->drawTextures(this, positionLocation, texCoordLocation);
273 }
274
275 void LayerCompositingThread::drawSurface(const TransformationMatrix& drawTransform, LayerCompositingThread* mask, int positionLocation, int texCoordLocation)
276 {
277     if (m_layerRenderer->layerAlreadyOnSurface(this)) {
278         unsigned texID = layerRendererSurface()->texture()->textureId();
279         if (!texID) {
280             ASSERT_NOT_REACHED();
281             return;
282         }
283         textureCacheCompositingThread()->textureAccessed(layerRendererSurface()->texture());
284         glBindTexture(GL_TEXTURE_2D, texID);
285
286         if (mask) {
287             glActiveTexture(GL_TEXTURE1);
288             mask->bindContentsTexture();
289             glActiveTexture(GL_TEXTURE0);
290         }
291
292         FloatQuad surfaceQuad = getTransformedRect(m_bounds, IntRect(IntPoint::zero(), m_bounds), drawTransform);
293         glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &surfaceQuad);
294
295         float texcoords[4 * 2] = { 0, 0,  0, 1,  1, 1,  1, 0 };
296         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
297         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
298     }
299 }
300
301 void LayerCompositingThread::drawMissingTextures(int positionLocation, int texCoordLocation, const FloatRect& visibleRect)
302 {
303     if (m_pluginView || m_texID)
304         return;
305
306 #if ENABLE(VIDEO)
307     if (m_mediaPlayer)
308         return;
309 #endif
310
311     m_tiler->drawMissingTextures(this, positionLocation, texCoordLocation);
312 }
313
314 void LayerCompositingThread::releaseTextureResources()
315 {
316     if (m_pluginView && m_pluginBuffer) {
317         BlackBerry::Platform::Graphics::releaseBufferGLTexture(m_pluginBuffer);
318         m_pluginBuffer = 0;
319         m_pluginView->unlockFrontBuffer();
320     }
321     if (m_texID && m_frontBufferLock)
322         pthread_mutex_unlock(m_frontBufferLock);
323 }
324
325 void LayerCompositingThread::setPluginView(PluginView* pluginView)
326 {
327     if (!isCompositingThread()) {
328         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
329             &LayerCompositingThread::setPluginView,
330             this,
331             pluginView));
332         return;
333     }
334
335     m_pluginView = pluginView;
336 }
337
338 #if ENABLE(VIDEO)
339 void LayerCompositingThread::setMediaPlayer(MediaPlayer* mediaPlayer)
340 {
341     if (!isCompositingThread()) {
342         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
343             &LayerCompositingThread::setMediaPlayer,
344             this,
345             mediaPlayer));
346         return;
347     }
348
349     m_mediaPlayer = mediaPlayer;
350 }
351 #endif
352
353 void LayerCompositingThread::clearAnimations()
354 {
355     // Animations don't use thread safe refcounting, and must only be
356     // touched when the two threads are in sync.
357     if (!isCompositingThread()) {
358         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
359             &LayerCompositingThread::clearAnimations,
360             this));
361         return;
362     }
363
364     m_runningAnimations.clear();
365     m_suspendedAnimations.clear();
366 }
367
368 void LayerCompositingThread::removeSublayer(LayerCompositingThread* sublayer)
369 {
370     ASSERT(isCompositingThread());
371
372     int foundIndex = indexOfSublayer(sublayer);
373     if (foundIndex == -1)
374         return;
375
376     sublayer->setSuperlayer(0);
377     m_sublayers.remove(foundIndex);
378 }
379
380 int LayerCompositingThread::indexOfSublayer(const LayerCompositingThread* reference)
381 {
382     for (size_t i = 0; i < m_sublayers.size(); i++) {
383         if (m_sublayers[i] == reference)
384             return i;
385     }
386     return -1;
387 }
388
389 const LayerCompositingThread* LayerCompositingThread::rootLayer() const
390 {
391     const LayerCompositingThread* layer = this;
392     for (LayerCompositingThread* superlayer = layer->superlayer(); superlayer; layer = superlayer, superlayer = superlayer->superlayer()) { }
393     return layer;
394 }
395
396 void LayerCompositingThread::removeFromSuperlayer()
397 {
398     if (m_superlayer)
399         m_superlayer->removeSublayer(this);
400 }
401
402 void LayerCompositingThread::setSublayers(const Vector<RefPtr<LayerCompositingThread> >& sublayers)
403 {
404     if (sublayers == m_sublayers)
405         return;
406
407     while (m_sublayers.size()) {
408         RefPtr<LayerCompositingThread> layer = m_sublayers[0].get();
409         ASSERT(layer->superlayer());
410         layer->removeFromSuperlayer();
411     }
412
413     m_sublayers.clear();
414
415     size_t listSize = sublayers.size();
416     for (size_t i = 0; i < listSize; i++) {
417         RefPtr<LayerCompositingThread> sublayer = sublayers[i];
418         sublayer->removeFromSuperlayer();
419         sublayer->setSuperlayer(this);
420         m_sublayers.insert(i, sublayer);
421     }
422 }
423
424 void LayerCompositingThread::updateTextureContentsIfNeeded()
425 {
426     if (m_texID || pluginView())
427         return;
428
429 #if ENABLE(VIDEO)
430     if (mediaPlayer())
431         return;
432 #endif
433
434     m_tiler->uploadTexturesIfNeeded();
435 }
436
437 void LayerCompositingThread::setVisible(bool visible)
438 {
439     if (visible == m_visible)
440         return;
441
442     m_visible = visible;
443
444     if (m_texID || pluginView())
445         return;
446
447 #if ENABLE(VIDEO)
448     if (mediaPlayer())
449         return;
450 #endif
451
452     m_tiler->layerVisibilityChanged(visible);
453 }
454
455 void LayerCompositingThread::setNeedsCommit()
456 {
457     if (m_layerRenderer)
458         m_layerRenderer->setNeedsCommit();
459 }
460
461 void LayerCompositingThread::scheduleCommit()
462 {
463     if (!isWebKitThread()) {
464         if (m_commitScheduled)
465             return;
466
467         m_commitScheduled = true;
468
469         dispatchWebKitMessage(BlackBerry::Platform::createMethodCallMessage(&LayerCompositingThread::scheduleCommit, this));
470         return;
471     }
472
473     m_commitScheduled = false;
474
475     // FIXME: The only way to get at our LayerWebKitThread is to go through the tiler.
476     if (LayerWebKitThread* layer = m_tiler->layer())
477         layer->setNeedsCommit();
478 }
479
480 bool LayerCompositingThread::updateAnimations(double currentTime)
481 {
482     // The commit mechanism always overwrites our state with state from the
483     // WebKit thread. This means we have to restore the last animated value for
484     // suspended animations.
485     for (size_t i = 0; i < m_suspendedAnimations.size(); ++i) {
486         LayerAnimation* animation = m_suspendedAnimations[i].get();
487         // From looking at the WebCore code, it appears that when the animation
488         // is paused, the timeOffset is modified so it will be an appropriate
489         // elapsedTime.
490         double elapsedTime = animation->timeOffset();
491         animation->apply(this, elapsedTime);
492     }
493
494     for (size_t i = 0; i < m_runningAnimations.size(); ++i) {
495         LayerAnimation* animation = m_runningAnimations[i].get();
496         double elapsedTime = (m_suspendTime ? m_suspendTime : currentTime) - animation->startTime() + animation->timeOffset();
497         animation->apply(this, elapsedTime);
498     }
499
500     return !m_runningAnimations.isEmpty();
501 }
502
503 bool LayerCompositingThread::hasVisibleHolePunchRect() const
504 {
505     if (m_pluginView && !m_isVisible)
506         return false;
507
508 #if ENABLE(VIDEO)
509     if (m_mediaPlayer && !m_isVisible)
510         return false;
511 #endif
512
513     return hasHolePunchRect();
514 }
515
516 void LayerCompositingThread::createLayerRendererSurface()
517 {
518     ASSERT(!m_layerRendererSurface);
519     m_layerRendererSurface = adoptPtr(new LayerRendererSurface(m_layerRenderer, this));
520 }
521
522 }
523
524 #endif // USE(ACCELERATED_COMPOSITING)