[BlackBerry] fix confusing destruction sequence in LayerCompositingThread
[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 void LayerCompositingThread::destroyOnCompositingThread()
75 {
76     if (!isCompositingThread()) {
77         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
78             &LayerCompositingThread::destroyOnCompositingThread,
79             this));
80         return;
81     }
82
83     delete this;
84 }
85
86 LayerCompositingThread::~LayerCompositingThread()
87 {
88     ASSERT(isCompositingThread());
89
90     m_tiler->layerCompositingThreadDestroyed();
91
92     ASSERT(!superlayer());
93
94     // Remove the superlayer reference from all sublayers.
95     while (m_sublayers.size())
96         m_sublayers[0]->removeFromSuperlayer();
97
98     // Delete all allocated textures
99     deleteTextures();
100
101     // We just deleted all our textures, no need for the
102     // layer renderer to track us anymore
103     if (m_layerRenderer)
104         m_layerRenderer->removeLayer(this);
105 }
106
107 void LayerCompositingThread::setLayerRenderer(LayerRenderer* renderer)
108 {
109     // It's not expected that layers will ever switch renderers.
110     ASSERT(!renderer || !m_layerRenderer || renderer == m_layerRenderer);
111
112     m_layerRenderer = renderer;
113     if (m_layerRenderer)
114         m_layerRenderer->addLayer(this);
115 }
116
117 void LayerCompositingThread::deleteTextures()
118 {
119     releaseTextureResources();
120
121     m_tiler->deleteTextures();
122 }
123
124 void LayerCompositingThread::setDrawTransform(const TransformationMatrix& matrix)
125 {
126     m_drawTransform = matrix;
127
128     float bx = m_bounds.width() / 2.0;
129     float by = m_bounds.height() / 2.0;
130     m_transformedBounds.setP1(matrix.mapPoint(FloatPoint(-bx, -by)));
131     m_transformedBounds.setP2(matrix.mapPoint(FloatPoint(-bx, by)));
132     m_transformedBounds.setP3(matrix.mapPoint(FloatPoint(bx, by)));
133     m_transformedBounds.setP4(matrix.mapPoint(FloatPoint(bx, -by)));
134
135     m_drawRect = m_transformedBounds.boundingBox();
136 }
137
138 static FloatQuad getTransformedRect(const IntSize& bounds, const IntRect& rect, const TransformationMatrix& drawTransform)
139 {
140     float x = -bounds.width() / 2.0 + rect.x();
141     float y = -bounds.height() / 2.0 + rect.y();
142     float w = rect.width();
143     float h = rect.height();
144     FloatQuad result;
145     result.setP1(drawTransform.mapPoint(FloatPoint(x, y)));
146     result.setP2(drawTransform.mapPoint(FloatPoint(x, y + h)));
147     result.setP3(drawTransform.mapPoint(FloatPoint(x + w, y + h)));
148     result.setP4(drawTransform.mapPoint(FloatPoint(x + w, y)));
149
150     return result;
151 }
152
153
154 FloatQuad LayerCompositingThread::getTransformedHolePunchRect() const
155 {
156     // FIXME: the following line disables clipping a video in an iframe i.e. the fix associated with PR 99638.
157     // Some revised test case (e.g. video-iframe.html) show that the original fix works correctly when scrolling
158     // the contents of the frame, but fails to clip correctly if the page (main frame) is scrolled.
159     static bool enableVideoClipping = false;
160
161     if (!mediaPlayer() || !enableVideoClipping) {
162         // m_holePunchClipRect is valid only when there's a media player.
163         return getTransformedRect(m_bounds, m_holePunchRect, m_drawTransform);
164     }
165
166     // The hole punch rectangle may need to be clipped,
167     // e.g. if the <video> is on a layer that's included and clipped by an <iframe>.
168
169     // In order to clip we need to determine the current position of this layer, which
170     // is encoded in the m_drawTransform value, which was used to initialize m_drawRect.
171     IntRect drawRect = m_layerRenderer->toWebKitDocumentCoordinates(m_drawRect);
172
173     // Assert that in this case, where the hole punch rectangle equals the size of the layer,
174     // the drawRect has the same size as the hole punch.
175     // ASSERT(drawRect.size() == m_holePunchRect.size());
176     // Don't assert it programtically though because there may be off-by-one error due to rounding when there's zooming.
177
178     // The difference between drawRect and m_holePunchRect is that drawRect has an accurate position
179     // in WebKit document coordinates, whereas the m_holePunchRect location is (0,0) i.e. it's relative to this layer.
180
181     // Clip the drawRect.
182     // Both drawRect and m_holePunchClipRect already have correct locations, in WebKit document coordinates.
183     IntPoint location = drawRect.location();
184     drawRect.intersect(m_holePunchClipRect);
185
186     // Shift the clipped drawRect to have the same kind of located-at-zero position as the original holePunchRect.
187     drawRect.move(-location.x(), -location.y());
188
189 #if DEBUG_VIDEO_CLIPPING
190      IntRect drawRectInWebKitDocumentCoordination = m_layerRenderer->toWebKitDocumentCoordinates(m_drawRect);
191      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).",
192         drawRectInWebKitDocumentCoordination.x(), drawRectInWebKitDocumentCoordination.y(), drawRectInWebKitDocumentCoordination.width(), drawRectInWebKitDocumentCoordination.height(),
193         m_holePunchClipRect.x(), m_holePunchClipRect.y(), m_holePunchClipRect.width(), m_holePunchClipRect.height(),
194         drawRect.x(), drawRect.y(), drawRect.width(), drawRect.height());
195 #endif
196
197     return getTransformedRect(m_bounds, drawRect, m_drawTransform);
198 }
199
200 void LayerCompositingThread::drawTextures(int positionLocation, int texCoordLocation, const FloatRect& visibleRect)
201 {
202     float texcoords[4 * 2] = { 0, 0,  0, 1,  1, 1,  1, 0 };
203
204     if (m_pluginView) {
205         if (m_isVisible) {
206             // The layer contains Flash, video, or other plugin contents.
207             m_pluginBuffer = m_pluginView->lockFrontBufferForRead();
208
209             if (!m_pluginBuffer)
210                 return;
211
212             if (!BlackBerry::Platform::Graphics::lockAndBindBufferGLTexture(m_pluginBuffer, GL_TEXTURE_2D)) {
213                 m_pluginView->unlockFrontBuffer();
214                 return;
215             }
216
217             m_layerRenderer->addLayerToReleaseTextureResourcesList(this);
218
219             glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &m_transformedBounds);
220             glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
221             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
222         }
223         return;
224     }
225 #if ENABLE(VIDEO)
226     if (m_mediaPlayer) {
227         if (m_isVisible) {
228             // We need to specify the media player location in contents coordinates. The 'visibleRect'
229             // specifies the content region covered by our viewport. So we transform from our
230             // normalized device coordinates [-1, 1] to the 'visibleRect'.
231             float vrw2 = visibleRect.width() / 2.0;
232             float vrh2 = visibleRect.height() / 2.0;
233             float x = m_transformedBounds.p1().x() * vrw2 + vrw2 + visibleRect.x();
234             float y = -m_transformedBounds.p1().y() * vrh2 + vrh2 + visibleRect.y();
235             m_mediaPlayer->paint(0, IntRect((int)(x + 0.5), (int)(y + 0.5), m_bounds.width(), m_bounds.height()));
236             MediaPlayerPrivate* mpp = static_cast<MediaPlayerPrivate*>(m_mediaPlayer->platformMedia().media.qnxMediaPlayer);
237             mpp->drawBufferingAnimation(m_drawTransform, positionLocation, texCoordLocation);
238         }
239         return;
240     }
241 #endif
242 #if ENABLE(WEBGL)
243     if (layerType() == LayerData::WebGLLayer) {
244         pthread_mutex_lock(m_frontBufferLock);
245         glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &m_transformedBounds);
246         float canvasWidthRatio = 1.0f;
247         float canvasHeightRatio = 1.0f;
248         float upsideDown[4 * 2] = { 0, 1,  0, 1 - canvasHeightRatio,  canvasWidthRatio, 1 - canvasHeightRatio,  canvasWidthRatio, 1 };
249         // Flip the texture Y axis because OpenGL and Skia have different origins
250         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, upsideDown);
251         glBindTexture(GL_TEXTURE_2D, m_texID);
252         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
253         pthread_mutex_unlock(m_frontBufferLock);
254         // FIXME: If the canvas/texture is larger than 2048x2048, then we'll die here
255         return;
256     }
257 #endif
258     if (m_texID) {
259         m_layerRenderer->addLayerToReleaseTextureResourcesList(this);
260         pthread_mutex_lock(m_frontBufferLock);
261
262         glDisable(GL_SCISSOR_TEST);
263         glBindTexture(GL_TEXTURE_2D, m_texID);
264         glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &m_transformedBounds);
265         float upsideDown[4 * 2] = { 0, 1,  0, 0,  1, 0,  1, 1 };
266         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, upsideDown);
267         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
268         return;
269     }
270
271     m_tiler->drawTextures(this, positionLocation, texCoordLocation);
272 }
273
274 void LayerCompositingThread::drawSurface(const TransformationMatrix& drawTransform, LayerCompositingThread* mask, int positionLocation, int texCoordLocation)
275 {
276     if (m_layerRenderer->layerAlreadyOnSurface(this)) {
277         unsigned texID = layerRendererSurface()->texture()->textureId();
278         if (!texID) {
279             ASSERT_NOT_REACHED();
280             return;
281         }
282         textureCacheCompositingThread()->textureAccessed(layerRendererSurface()->texture());
283         glBindTexture(GL_TEXTURE_2D, texID);
284
285         if (mask) {
286             glActiveTexture(GL_TEXTURE1);
287             mask->bindContentsTexture();
288             glActiveTexture(GL_TEXTURE0);
289         }
290
291         FloatQuad surfaceQuad = getTransformedRect(m_bounds, IntRect(IntPoint::zero(), m_bounds), drawTransform);
292         glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, &surfaceQuad);
293
294         float texcoords[4 * 2] = { 0, 0,  0, 1,  1, 1,  1, 0 };
295         glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
296         glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
297     }
298 }
299
300 void LayerCompositingThread::drawMissingTextures(int positionLocation, int texCoordLocation, const FloatRect& visibleRect)
301 {
302     if (m_pluginView || m_texID)
303         return;
304
305 #if ENABLE(VIDEO)
306     if (m_mediaPlayer)
307         return;
308 #endif
309
310     m_tiler->drawMissingTextures(this, positionLocation, texCoordLocation);
311 }
312
313 void LayerCompositingThread::releaseTextureResources()
314 {
315     if (m_pluginView && m_pluginBuffer) {
316         BlackBerry::Platform::Graphics::releaseBufferGLTexture(m_pluginBuffer);
317         m_pluginBuffer = 0;
318         m_pluginView->unlockFrontBuffer();
319     }
320     if (m_texID && m_frontBufferLock)
321         pthread_mutex_unlock(m_frontBufferLock);
322 }
323
324 void LayerCompositingThread::setPluginView(PluginView* pluginView)
325 {
326     if (!isCompositingThread()) {
327         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
328             &LayerCompositingThread::setPluginView,
329             this,
330             pluginView));
331         return;
332     }
333
334     m_pluginView = pluginView;
335 }
336
337 #if ENABLE(VIDEO)
338 void LayerCompositingThread::setMediaPlayer(MediaPlayer* mediaPlayer)
339 {
340     if (!isCompositingThread()) {
341         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
342             &LayerCompositingThread::setMediaPlayer,
343             this,
344             mediaPlayer));
345         return;
346     }
347
348     m_mediaPlayer = mediaPlayer;
349 }
350 #endif
351
352 void LayerCompositingThread::clearAnimations()
353 {
354     // Animations don't use thread safe refcounting, and must only be
355     // touched when the two threads are in sync.
356     if (!isCompositingThread()) {
357         dispatchSyncCompositingMessage(BlackBerry::Platform::createMethodCallMessage(
358             &LayerCompositingThread::clearAnimations,
359             this));
360         return;
361     }
362
363     m_runningAnimations.clear();
364     m_suspendedAnimations.clear();
365 }
366
367 void LayerCompositingThread::removeSublayer(LayerCompositingThread* sublayer)
368 {
369     ASSERT(isCompositingThread());
370
371     int foundIndex = indexOfSublayer(sublayer);
372     if (foundIndex == -1)
373         return;
374
375     sublayer->setSuperlayer(0);
376     m_sublayers.remove(foundIndex);
377 }
378
379 int LayerCompositingThread::indexOfSublayer(const LayerCompositingThread* reference)
380 {
381     for (size_t i = 0; i < m_sublayers.size(); i++) {
382         if (m_sublayers[i] == reference)
383             return i;
384     }
385     return -1;
386 }
387
388 const LayerCompositingThread* LayerCompositingThread::rootLayer() const
389 {
390     const LayerCompositingThread* layer = this;
391     for (LayerCompositingThread* superlayer = layer->superlayer(); superlayer; layer = superlayer, superlayer = superlayer->superlayer()) { }
392     return layer;
393 }
394
395 void LayerCompositingThread::removeFromSuperlayer()
396 {
397     if (m_superlayer)
398         m_superlayer->removeSublayer(this);
399 }
400
401 void LayerCompositingThread::setSublayers(const Vector<RefPtr<LayerCompositingThread> >& sublayers)
402 {
403     if (sublayers == m_sublayers)
404         return;
405
406     while (m_sublayers.size()) {
407         RefPtr<LayerCompositingThread> layer = m_sublayers[0].get();
408         ASSERT(layer->superlayer());
409         layer->removeFromSuperlayer();
410     }
411
412     m_sublayers.clear();
413
414     size_t listSize = sublayers.size();
415     for (size_t i = 0; i < listSize; i++) {
416         RefPtr<LayerCompositingThread> sublayer = sublayers[i];
417         sublayer->removeFromSuperlayer();
418         sublayer->setSuperlayer(this);
419         m_sublayers.insert(i, sublayer);
420     }
421 }
422
423 void LayerCompositingThread::updateTextureContentsIfNeeded()
424 {
425     if (m_texID || pluginView())
426         return;
427
428 #if ENABLE(VIDEO)
429     if (mediaPlayer())
430         return;
431 #endif
432
433     m_tiler->uploadTexturesIfNeeded();
434 }
435
436 void LayerCompositingThread::setVisible(bool visible)
437 {
438     if (visible == m_visible)
439         return;
440
441     m_visible = visible;
442
443     if (m_texID || pluginView())
444         return;
445
446 #if ENABLE(VIDEO)
447     if (mediaPlayer())
448         return;
449 #endif
450
451     m_tiler->layerVisibilityChanged(visible);
452 }
453
454 void LayerCompositingThread::setNeedsCommit()
455 {
456     if (m_layerRenderer)
457         m_layerRenderer->setNeedsCommit();
458 }
459
460 void LayerCompositingThread::scheduleCommit()
461 {
462     if (!isWebKitThread()) {
463         if (m_commitScheduled)
464             return;
465
466         m_commitScheduled = true;
467
468         dispatchWebKitMessage(BlackBerry::Platform::createMethodCallMessage(&LayerCompositingThread::scheduleCommit, this));
469         return;
470     }
471
472     m_commitScheduled = false;
473
474     // FIXME: The only way to get at our LayerWebKitThread is to go through the tiler.
475     if (LayerWebKitThread* layer = m_tiler->layer())
476         layer->setNeedsCommit();
477 }
478
479 bool LayerCompositingThread::updateAnimations(double currentTime)
480 {
481     // The commit mechanism always overwrites our state with state from the
482     // WebKit thread. This means we have to restore the last animated value for
483     // suspended animations.
484     for (size_t i = 0; i < m_suspendedAnimations.size(); ++i) {
485         LayerAnimation* animation = m_suspendedAnimations[i].get();
486         // From looking at the WebCore code, it appears that when the animation
487         // is paused, the timeOffset is modified so it will be an appropriate
488         // elapsedTime.
489         double elapsedTime = animation->timeOffset();
490         animation->apply(this, elapsedTime);
491     }
492
493     for (size_t i = 0; i < m_runningAnimations.size(); ++i) {
494         LayerAnimation* animation = m_runningAnimations[i].get();
495         double elapsedTime = (m_suspendTime ? m_suspendTime : currentTime) - animation->startTime() + animation->timeOffset();
496         animation->apply(this, elapsedTime);
497     }
498
499     return !m_runningAnimations.isEmpty();
500 }
501
502 bool LayerCompositingThread::hasVisibleHolePunchRect() const
503 {
504     if (m_pluginView && !m_isVisible)
505         return false;
506
507 #if ENABLE(VIDEO)
508     if (m_mediaPlayer && !m_isVisible)
509         return false;
510 #endif
511
512     return hasHolePunchRect();
513 }
514
515 void LayerCompositingThread::createLayerRendererSurface()
516 {
517     ASSERT(!m_layerRendererSurface);
518     m_layerRendererSurface = adoptPtr(new LayerRendererSurface(m_layerRenderer, this));
519 }
520
521 }
522
523 #endif // USE(ACCELERATED_COMPOSITING)