3dad399d65f98bb123d0c8ec4ef530b1760a7b95
[WebKit-https.git] / Source / WebCore / platform / graphics / blackberry / LayerTiler.cpp
1 /*
2  * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20
21 #if USE(ACCELERATED_COMPOSITING)
22
23 #include "LayerTiler.h"
24
25 #include "BitmapImage.h"
26 #include "LayerCompositingThread.h"
27 #include "LayerMessage.h"
28 #include "LayerWebKitThread.h"
29 #include "NativeImageSkia.h"
30 #include "TextureCacheCompositingThread.h"
31
32 #include <BlackBerryPlatformScreen.h>
33 #include <GLES2/gl2.h>
34
35 using namespace std;
36
37 namespace WebCore {
38
39 // This is used to make the viewport as used in texture visibility calculations
40 // slightly larger so textures are uploaded before becoming really visible.
41 const float viewportInflationFactor = 1.1f;
42
43 // The tileMultiplier indicates how many tiles will fit in the largest dimension
44 // of the screen, if drawn using identity transform.
45 // We use half the screen size as tile size, to reduce the texture upload time
46 // for small repaint rects. Compared to using screen size directly, this should
47 // make most small invalidations 16x faster, unless they're unfortunate enough
48 // to intersect two or more tiles, where it would be 8x-1x faster.
49 const int tileMultiplier = 4;
50
51 static void transformPoint(float x, float y, const TransformationMatrix& m, float* result)
52 {
53     // Squash the Z coordinate so that layers aren't clipped against the near and
54     // far plane. Note that the perspective is maintained as we're still passing
55     // down the W coordinate.
56     result[0] = x * m.m11() + y * m.m21() + m.m41();
57     result[1] = x * m.m12() + y * m.m22() + m.m42();
58     result[2] = 0;
59     result[3] = x * m.m14() + y * m.m24() + m.m44();
60 }
61
62 static IntSize defaultTileSize()
63 {
64     static IntSize screenSize = BlackBerry::Platform::Graphics::Screen::primaryScreen()->nativeSize();
65     static int dim = max(screenSize.width(), screenSize.height()) / tileMultiplier;
66     return IntSize(dim, dim);
67 }
68
69 LayerTiler::LayerTiler(LayerWebKitThread* layer)
70     : m_layer(layer)
71     , m_tilingDisabled(false)
72     , m_contentsDirty(false)
73     , m_tileSize(defaultTileSize())
74     , m_clearTextureJobs(false)
75     , m_hasMissingTextures(false)
76     , m_contentsScale(0.0)
77 {
78 }
79
80 LayerTiler::~LayerTiler()
81 {
82     // Someone should have called LayerTiler::deleteTextures()
83     // before now. We can't call it here because we have no
84     // OpenGL context.
85     ASSERT(m_tilesCompositingThread.isEmpty());
86 }
87
88 void LayerTiler::layerWebKitThreadDestroyed()
89 {
90     m_layer = 0;
91 }
92
93 void LayerTiler::layerCompositingThreadDestroyed()
94 {
95     ASSERT(isCompositingThread());
96 }
97
98 void LayerTiler::setNeedsDisplay(const FloatRect& dirtyRect)
99 {
100     m_dirtyRect.unite(dirtyRect);
101     m_contentsDirty = true;
102 }
103
104 void LayerTiler::setNeedsDisplay()
105 {
106     m_dirtyRect.setLocation(FloatPoint::zero());
107     m_dirtyRect.setSize(m_layer->bounds());
108     m_contentsDirty = true;
109 }
110
111 void LayerTiler::updateTextureContentsIfNeeded(double scale)
112 {
113     updateTileSize();
114
115     HashSet<TileIndex> renderJobs;
116     {
117         MutexLocker locker(m_renderJobsMutex);
118         if (!m_contentsDirty && m_renderJobs.isEmpty())
119             return;
120         renderJobs = m_renderJobs;
121     }
122
123     bool isZoomJob = false;
124     if (scale != m_contentsScale) {
125         // The first time around, it does not count as a zoom job.
126         if (m_contentsScale)
127             isZoomJob = true;
128         m_contentsScale = scale;
129     }
130
131     IntRect dirtyRect = enclosingIntRect(m_dirtyRect);
132     IntSize requiredTextureSize;
133
134     if (m_layer->drawsContent()) {
135         // Layer contents must be drawn into a canvas.
136         IntRect untransformedDirtyRect(dirtyRect);
137         IntRect boundsRect(IntPoint::zero(), m_layer->bounds());
138         IntRect untransformedBoundsRect(boundsRect);
139         requiredTextureSize = boundsRect.size();
140
141         if (scale != 1.0) {
142             TransformationMatrix matrix;
143             matrix.scale(scale);
144
145             dirtyRect = matrix.mapRect(untransformedDirtyRect);
146             requiredTextureSize = matrix.mapRect(IntRect(IntPoint::zero(), requiredTextureSize)).size();
147             boundsRect = matrix.mapRect(untransformedBoundsRect);
148         }
149
150         if (requiredTextureSize != m_pendingTextureSize)
151             dirtyRect = boundsRect;
152         else {
153             // Clip the dirtyRect to the size of the layer to avoid drawing
154             // outside the bounds of the backing texture.
155             dirtyRect.intersect(boundsRect);
156         }
157     } else if (m_layer->contents()) {
158         // Layer is a container, and it contains an Image.
159         requiredTextureSize = m_layer->contents()->size();
160         dirtyRect = IntRect(IntPoint::zero(), requiredTextureSize);
161     }
162
163     // If we need display because we no longer need to be displayed, due to texture size becoming 0 x 0,
164     // or if we're re-rendering the whole thing anyway, clear old texture jobs.
165     HashSet<TileIndex> finishedJobs;
166     if (requiredTextureSize.isEmpty() || dirtyRect == IntRect(IntPoint::zero(), requiredTextureSize)) {
167         {
168             MutexLocker locker(m_renderJobsMutex);
169             m_renderJobs.clear();
170         }
171         clearTextureJobs();
172     } else if (!renderJobs.isEmpty()) {
173         if (Image* image = m_layer->contents()) {
174             bool isOpaque = false;
175             if (image->isBitmapImage())
176                 isOpaque = !static_cast<BitmapImage*>(image)->currentFrameHasAlpha();
177             if (NativeImagePtr nativeImage = image->nativeImageForCurrentFrame()) {
178                 SkBitmap bitmap = SkBitmap(*nativeImage);
179                 addTextureJob(TextureJob::setContents(bitmap, isOpaque));
180             }
181         } else {
182             // There might still be some pending render jobs due to visibility changes.
183             for (HashSet<TileIndex>::iterator it = renderJobs.begin(); it != renderJobs.end(); ++it) {
184                 {
185                     // Check if the job has been cancelled.
186                     MutexLocker locker(m_renderJobsMutex);
187                     if (!m_renderJobs.contains(*it))
188                         continue;
189                     m_renderJobs.remove(*it);
190                 }
191
192                 IntRect tileRect = rectForTile(*it, requiredTextureSize);
193                 if (tileRect.isEmpty())
194                     continue;
195
196                 bool isSolidColor = false;
197                 Color color;
198                 SkBitmap bitmap = m_layer->paintContents(tileRect, scale, &isSolidColor, &color);
199                 // bitmap can be null here. Make requiredTextureSize empty so that we
200                 // will not try to update and draw the layer.
201                 if (!bitmap.isNull()) {
202                     if (isSolidColor)
203                         addTextureJob(TextureJob::setContentsToColor(color, *it));
204                     else
205                         addTextureJob(TextureJob::updateContents(bitmap, tileRect));
206                 }
207
208                 finishedJobs.add(*it);
209             }
210         }
211     }
212
213     bool didResize = false;
214     if (m_pendingTextureSize != requiredTextureSize) {
215         didResize = true;
216         m_pendingTextureSize = requiredTextureSize;
217         addTextureJob(TextureJob::resizeContents(m_pendingTextureSize));
218     }
219     m_contentsDirty = false;
220     m_dirtyRect = FloatRect();
221
222     if (dirtyRect.isEmpty() || requiredTextureSize.isEmpty())
223         return;
224
225     if (Image* image = m_layer->contents()) {
226         bool isOpaque = false;
227         if (image->isBitmapImage())
228             isOpaque = !static_cast<BitmapImage*>(image)->currentFrameHasAlpha();
229         // No point in tiling an image layer, the image is already stored as an SkBitmap
230         NativeImagePtr nativeImage = m_layer->contents()->nativeImageForCurrentFrame();
231         if (nativeImage) {
232             SkBitmap bitmap = SkBitmap(*nativeImage);
233             addTextureJob(TextureJob::setContents(bitmap, isOpaque));
234         }
235         return;
236     }
237
238     IntPoint topLeft = dirtyRect.minXMinYCorner();
239     IntPoint bottomRight = dirtyRect.maxXMaxYCorner(); // This is actually a pixel below and to the right of the dirtyRect.
240
241     IntSize tileMaximumSize = tileSize();
242     bool wasOneTile = m_tilesWebKitThread.size() == 1;
243     bool isOneTile = m_pendingTextureSize.width() <= tileMaximumSize.width() && m_pendingTextureSize.height() <= tileMaximumSize.height();
244     IntPoint origin = originOfTile(indexOfTile(topLeft));
245     IntRect tileRect;
246     for (tileRect.setX(origin.x()); tileRect.x() < bottomRight.x(); tileRect.setX(tileRect.x() + tileMaximumSize.width())) {
247         for (tileRect.setY(origin.y()); tileRect.y() < bottomRight.y(); tileRect.setY(tileRect.y() + tileMaximumSize.height())) {
248             tileRect.setWidth(min(requiredTextureSize.width() - tileRect.x(), tileMaximumSize.width()));
249             tileRect.setHeight(min(requiredTextureSize.height() - tileRect.y(), tileMaximumSize.height()));
250
251             IntRect localDirtyRect(dirtyRect);
252             localDirtyRect.intersect(tileRect);
253             if (localDirtyRect.isEmpty())
254                 continue;
255
256             TileIndex index = indexOfTile(tileRect.location());
257
258             // If we already did this as part of one of the render jobs due to
259             // visibility changes, don't render that tile again.
260             if (finishedJobs.contains(index))
261                 continue;
262
263             if (!shouldPerformRenderJob(index, !isZoomJob)) {
264                 // Avoid checkerboarding unless the layer is resized. When
265                 // resized, the contents are likely to change appearance, for
266                 // example due to aspect ratio change. However, if it is a
267                 // resize due to zooming, the aspect ratio and content will
268                 // stay the same, and we can keep the old texture content as a
269                 // preview.
270                 // FIXME: the zoom preview only works if we don't re-tile the
271                 // layer. We need to store texture coordinates in
272                 // WebCore::Texture to be able to fix that.
273                 if (didResize && !(isZoomJob && wasOneTile && isOneTile))
274                     addTextureJob(TextureJob::discardContents(tileRect));
275                 else
276                     addTextureJob(TextureJob::dirtyContents(tileRect));
277                 continue;
278             }
279
280             // Just in case a new job for this tile has just been inserted by compositing thread.
281             removeRenderJob(index);
282
283             // FIXME: We are always drawing whole tiles at the moment, because
284             // we currently can't keep track of which part of a tile is
285             // rendered and which is not. Sending only a subrectangle of a tile
286             // to the compositing thread might cause it to be uploaded using
287             // glTexImage, if the texture was previously evicted from the cache.
288             // The result would be that a subrectangle of the tile was stretched
289             // to fit the tile geometry, appearing as a glaring misrendering of
290             // the web page.
291             bool isSolidColor = false;
292             Color color;
293             SkBitmap bitmap = m_layer->paintContents(tileRect, scale, &isSolidColor, &color);
294             // bitmap can be null here. Make requiredTextureSize empty so that we
295             // will not try to update and draw the layer.
296             if (!bitmap.isNull()) {
297                 if (isSolidColor)
298                     addTextureJob(TextureJob::setContentsToColor(color, index));
299                 else
300                     addTextureJob(TextureJob::updateContents(bitmap, tileRect));
301             }
302         }
303     }
304 }
305
306 bool LayerTiler::shouldPerformRenderJob(const TileIndex& index, bool allowPrefill)
307 {
308     // If the visibility information was propagated from the compositing
309     // thread, use that information.
310     // However, we are about to commit new layer properties that may make
311     // currently hidden layers visible. To avoid false negatives, we only allow
312     // the current state to be used to accept render jobs, not to reject them.
313     if (m_tilesWebKitThread.get(index).isVisible())
314         return true;
315
316     // Tiles that are not currently visible on the compositing thread may still
317     // deserve to be rendered if they should be prefilled...
318     if (allowPrefill && !m_tilesWebKitThread.contains(index) && shouldPrefillTile(index))
319         return true;
320
321     // Or if they are visible according to the state that's about to be
322     // committed. We do a visibility test using the current transform state.
323     IntRect contentRect = rectForTile(index, m_pendingTextureSize);
324     return m_layer->contentsVisible(contentRect);
325 }
326
327 void LayerTiler::addTextureJob(const TextureJob& job)
328 {
329     m_pendingTextureJobs.append(job);
330 }
331
332 void LayerTiler::clearTextureJobs()
333 {
334     // Clear any committed texture jobs on next invocation of LayerTiler::commitPendingTextureUploads().
335     m_clearTextureJobs = true;
336
337     removeUpdateContentsJobs(m_pendingTextureJobs);
338 }
339
340 void LayerTiler::commitPendingTextureUploads()
341 {
342     if (m_clearTextureJobs) {
343         removeUpdateContentsJobs(m_textureJobs);
344         m_clearTextureJobs = false;
345     }
346
347     for (Vector<TextureJob>::iterator it = m_pendingTextureJobs.begin(); it != m_pendingTextureJobs.end(); ++it)
348         m_textureJobs.append(*it);
349     m_pendingTextureJobs.clear();
350 }
351
352 void LayerTiler::layerVisibilityChanged(bool visible)
353 {
354     // For visible layers, we handle the tile-level visibility
355     // in the draw loop, see LayerTiler::drawTextures().
356     if (visible)
357         return;
358
359     {
360         // All tiles are invisible now.
361         MutexLocker locker(m_renderJobsMutex);
362         m_renderJobs.clear();
363     }
364
365     for (TileMap::iterator it = m_tilesCompositingThread.begin(); it != m_tilesCompositingThread.end(); ++it) {
366         TileIndex index = (*it).first;
367         LayerTile* tile = (*it).second;
368         tile->setVisible(false);
369     }
370 }
371
372 void LayerTiler::uploadTexturesIfNeeded()
373 {
374     TileJobsMap tileJobsMap;
375     Deque<TextureJob>::const_iterator textureJobIterEnd = m_textureJobs.end();
376     for (Deque<TextureJob>::const_iterator textureJobIter = m_textureJobs.begin(); textureJobIter != textureJobIterEnd; ++textureJobIter)
377         processTextureJob(*textureJobIter, tileJobsMap);
378
379     TileJobsMap::const_iterator tileJobsIterEnd = tileJobsMap.end();
380     for (TileJobsMap::const_iterator tileJobsIter = tileJobsMap.begin(); tileJobsIter != tileJobsIterEnd; ++tileJobsIter) {
381         IntPoint origin = originOfTile(tileJobsIter->first);
382
383         LayerTile* tile = m_tilesCompositingThread.get(tileJobsIter->first);
384         if (!tile) {
385             if (origin.x() >= m_requiredTextureSize.width() || origin.y() >= m_requiredTextureSize.height())
386                 continue;
387             tile = new LayerTile();
388             m_tilesCompositingThread.add(tileJobsIter->first, tile);
389         }
390
391         IntRect tileRect(origin, tileSize());
392         tileRect.setWidth(min(m_requiredTextureSize.width() - tileRect.x(), tileRect.width()));
393         tileRect.setHeight(min(m_requiredTextureSize.height() - tileRect.y(), tileRect.height()));
394
395         performTileJob(tile, *tileJobsIter->second, tileRect);
396     }
397
398     m_textureJobs.clear();
399 }
400
401 void LayerTiler::processTextureJob(const TextureJob& job, TileJobsMap& tileJobsMap)
402 {
403     if (job.m_type == TextureJob::ResizeContents) {
404         IntSize pendingTextureSize = job.m_dirtyRect.size();
405         if (pendingTextureSize.width() < m_requiredTextureSize.width() || pendingTextureSize.height() < m_requiredTextureSize.height())
406             pruneTextures();
407
408         m_requiredTextureSize = pendingTextureSize;
409         return;
410     }
411
412     if (job.m_type == TextureJob::SetContentsToColor) {
413         addTileJob(job.m_index, job, tileJobsMap);
414         return;
415      }
416
417     IntSize tileMaximumSize = tileSize();
418     IntPoint topLeft = job.m_dirtyRect.minXMinYCorner();
419     IntPoint bottomRight = job.m_dirtyRect.maxXMaxYCorner(); // This is actually a pixel below and to the right of the dirtyRect.
420     IntPoint origin = originOfTile(indexOfTile(topLeft));
421     IntRect tileRect;
422     for (tileRect.setX(origin.x()); tileRect.x() < bottomRight.x(); tileRect.setX(tileRect.x() + tileMaximumSize.width())) {
423         for (tileRect.setY(origin.y()); tileRect.y() < bottomRight.y(); tileRect.setY(tileRect.y() + tileMaximumSize.height()))
424             addTileJob(indexOfTile(tileRect.location()), job, tileJobsMap);
425     }
426 }
427
428 void LayerTiler::addTileJob(const TileIndex& index, const TextureJob& job, TileJobsMap& tileJobsMap)
429 {
430     // HashMap::add always returns a valid iterator even the key already exists.
431     pair<TileJobsMap::iterator, bool> result = tileJobsMap.add(index, &job);
432
433     // Successfully added the new job.
434     if (result.second)
435         return;
436
437     // In this case we leave the previous job.
438     if (job.m_type == TextureJob::DirtyContents && result.first->second->m_type == TextureJob::DiscardContents)
439         return;
440
441     // Override the previous job.
442     result.first->second = &job;
443 }
444
445 void LayerTiler::performTileJob(LayerTile* tile, const TextureJob& job, const IntRect& tileRect)
446 {
447     switch (job.m_type) {
448     case TextureJob::SetContentsToColor:
449         tile->setContentsToColor(job.m_color);
450         return;
451     case TextureJob::SetContents:
452         tile->setContents(job.m_contents, tileRect, indexOfTile(tileRect.location()), job.m_isOpaque);
453         return;
454     case TextureJob::UpdateContents:
455         tile->updateContents(job.m_contents, job.m_dirtyRect, tileRect);
456         return;
457     case TextureJob::DiscardContents:
458         tile->discardContents();
459         return;
460     case TextureJob::DirtyContents:
461         tile->setContentsDirty();
462         return;
463     case TextureJob::Unknown:
464     case TextureJob::ResizeContents:
465         ASSERT_NOT_REACHED();
466         return;
467     }
468     ASSERT_NOT_REACHED();
469 }
470
471 void LayerTiler::drawTextures(LayerCompositingThread* layer, int pos, int texCoord)
472 {
473     drawTexturesInternal(layer, pos, texCoord, false /* drawMissing */);
474 }
475
476 void LayerTiler::drawMissingTextures(LayerCompositingThread* layer, int pos, int texCoord)
477 {
478     drawTexturesInternal(layer, pos, texCoord, true /* drawMissing */);
479 }
480
481 void LayerTiler::drawTexturesInternal(LayerCompositingThread* layer, int positionLocation, int texCoordLocation, bool drawMissing)
482 {
483     const TransformationMatrix& drawTransform = layer->drawTransform();
484     IntSize bounds = layer->bounds();
485
486     float texcoords[4 * 2] = { 0, 0,  0, 1,  1, 1,  1, 0 };
487     float vertices[4 * 4];
488
489     glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, vertices);
490     glVertexAttribPointer(texCoordLocation, 2, GL_FLOAT, GL_FALSE, 0, texcoords);
491
492     m_hasMissingTextures = false;
493
494     int maxw = tileSize().width();
495     int maxh = tileSize().height();
496     float sx = static_cast<float>(bounds.width()) / m_requiredTextureSize.width();
497     float sy = static_cast<float>(bounds.height()) / m_requiredTextureSize.height();
498
499     bool needsDisplay = false;
500
501     bool blending = !drawMissing;
502
503     IntRect tileRect;
504     for (tileRect.setX(0); tileRect.x() < m_requiredTextureSize.width(); tileRect.setX(tileRect.x() + maxw)) {
505         for (tileRect.setY(0); tileRect.y() < m_requiredTextureSize.height(); tileRect.setY(tileRect.y() + maxh)) {
506             TileIndex index = indexOfTile(tileRect.location());
507             LayerTile* tile = m_tilesCompositingThread.get(index);
508             if (!tile) {
509                 tile = new LayerTile();
510                 m_tilesCompositingThread.add(index, tile);
511             }
512
513             float x = index.i() * maxw * sx;
514             float y = index.j() * maxh * sy;
515             float w = min(bounds.width() - x, maxw * sx);
516             float h = min(bounds.height() - y, maxh * sy);
517             float ox = x - bounds.width() / 2.0;
518             float oy = y - bounds.height() / 2.0;
519
520             // We apply the transformation by hand, since we need the z coordinate
521             // as well (to do perspective correct texturing) and we don't need
522             // to divide by w by hand, the GPU will do that for us
523             transformPoint(ox, oy, drawTransform, &vertices[0]);
524             transformPoint(ox, oy + h, drawTransform, &vertices[4]);
525             transformPoint(ox + w, oy + h, drawTransform, &vertices[8]);
526             transformPoint(ox + w, oy, drawTransform, &vertices[12]);
527
528             // Inflate the rect somewhat to attempt to make textures render before they show
529             // up on screen.
530             float d = viewportInflationFactor;
531             FloatRect rect(-d, -d, 2 * d, 2 * d);
532             FloatQuad quad(FloatPoint(vertices[0] / vertices[3], vertices[1] / vertices[3]),
533                            FloatPoint(vertices[4] / vertices[7], vertices[5] / vertices[7]),
534                            FloatPoint(vertices[8] / vertices[11], vertices[9] / vertices[11]),
535                            FloatPoint(vertices[12] / vertices[15], vertices[13] / vertices[15]));
536             bool visible = quad.boundingBox().intersects(rect);
537
538             bool wasVisible = tile->isVisible();
539             tile->setVisible(visible);
540
541             // This method is called in two passes. The first pass draws all
542             // visible tiles with textures.
543             // If a visible tile has no texture, set the m_hasMissingTextures
544             // flag, to indicate that we need a second pass.
545             // The second "drawMissing" pass draws all visible tiles without
546             // textures as checkerboard.
547             // However, don't draw brand new tiles as checkerboard. The checker-
548             // board indicates that a tile has dirty contents, but that's not
549             // the case if it's brand new.
550             if (visible) {
551                 bool hasTexture = tile->hasTexture();
552                 if (!hasTexture)
553                     m_hasMissingTextures = true;
554
555                 if (hasTexture && !drawMissing) {
556                     Texture* texture = tile->texture();
557                     if (texture->isOpaque() && layer->drawOpacity() == 1.0f && !layer->maskLayer()) {
558                         if (blending) {
559                             blending = false;
560                             glDisable(GL_BLEND);
561                         }
562                     } else if (!blending) {
563                         blending = true;
564                         glEnable(GL_BLEND);
565                         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
566                     }
567
568                     textureCacheCompositingThread()->textureAccessed(texture);
569                     glBindTexture(GL_TEXTURE_2D, texture->textureId());
570                 }
571
572                 if (hasTexture != drawMissing)
573                     glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
574
575                 if (tile->isDirty()) {
576                     addRenderJob(index);
577                     needsDisplay = true;
578                 }
579             } else if (wasVisible)
580                 removeRenderJob(index);
581         }
582     }
583
584     // Return early for the drawMissing case, don't flag us as needing commit.
585     if (drawMissing)
586         return;
587
588     // Switch on blending again (we know that drawMissing == false).
589     if (!blending) {
590         glEnable(GL_BLEND);
591         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
592     }
593
594     // If we schedule a commit, visibility will be updated, and display will
595     // happen if there are any visible and dirty textures.
596     if (needsDisplay)
597         layer->setNeedsCommit();
598 }
599
600 void LayerTiler::addRenderJob(const TileIndex& index)
601 {
602     ASSERT(isCompositingThread());
603
604     MutexLocker locker(m_renderJobsMutex);
605     m_renderJobs.add(index);
606 }
607
608 void LayerTiler::removeRenderJob(const TileIndex& index)
609 {
610     MutexLocker locker(m_renderJobsMutex);
611     m_renderJobs.remove(index);
612 }
613
614 void LayerTiler::deleteTextures()
615 {
616     // Since textures are deleted by a synchronous message
617     // from WebKit thread to compositing thread, we don't need
618     // any synchronization mechanism here, even though we are
619     // touching some WebKit thread state.
620     m_tilesWebKitThread.clear();
621
622     if (m_tilesCompositingThread.size()) {
623         for (TileMap::iterator it = m_tilesCompositingThread.begin(); it != m_tilesCompositingThread.end(); ++it)
624             (*it).second->discardContents();
625         m_tilesCompositingThread.clear();
626
627         m_contentsDirty = true;
628     }
629
630     // For various reasons, e.g. page cache, someone may try
631     // to render us after the textures were deleted.
632     m_pendingTextureSize = IntSize();
633     m_requiredTextureSize = IntSize();
634 }
635
636 void LayerTiler::pruneTextures()
637 {
638     // Prune tiles that are no longer needed.
639     Vector<TileIndex> tilesToDelete;
640     for (TileMap::iterator it = m_tilesCompositingThread.begin(); it != m_tilesCompositingThread.end(); ++it) {
641         TileIndex index = (*it).first;
642
643         IntPoint origin = originOfTile(index);
644         if (origin.x() >= m_requiredTextureSize.width() || origin.y() >= m_requiredTextureSize.height())
645             tilesToDelete.append(index);
646     }
647
648     for (Vector<TileIndex>::iterator it = tilesToDelete.begin(); it != tilesToDelete.end(); ++it) {
649         LayerTile* tile = m_tilesCompositingThread.take(*it);
650         tile->discardContents();
651         delete tile;
652     }
653 }
654
655 void LayerTiler::updateTileSize()
656 {
657     IntSize size = m_tilingDisabled ? m_layer->bounds() : defaultTileSize();
658     const IntSize maxTextureSize(2048, 2048);
659     size = size.shrunkTo(maxTextureSize);
660
661     if (m_tileSize == size || size.isEmpty())
662         return;
663
664     // Invalidate the whole layer if tile size changes.
665     setNeedsDisplay();
666     m_tileSize = size;
667 }
668
669 void LayerTiler::disableTiling(bool disable)
670 {
671     if (m_tilingDisabled == disable)
672         return;
673
674     m_tilingDisabled = disable;
675     updateTileSize();
676 }
677
678 bool LayerTiler::shouldPrefillTile(const TileIndex& index)
679 {
680     // For now, we use the heuristic of prefilling the first screenful of tiles.
681     // This gives the correct result only for layers with identity transform,
682     // which is why it's called a heuristic here. This is needed for the case
683     // where the developer actually designed their web page around the use of
684     // accelerated compositing, and expects even offscreen layers to have content.
685     // We oblige them to some degree by prefilling a screenful of tiles.
686     // This is redundant in some other scenarios, i.e. when an offscreen layer
687     // is composited only because of overlapping a flash ad or something like
688     // that, but we're willing to make this tradeoff.
689
690     // Since the tileMultiplier indicates how many tiles fit on the screen,
691     // the following formula can be used:
692     return index.i() < static_cast<unsigned>(tileMultiplier) && index.j() < static_cast<unsigned>(tileMultiplier);
693 }
694
695 TileIndex LayerTiler::indexOfTile(const WebCore::IntPoint& origin)
696 {
697     int offsetX = origin.x();
698     int offsetY = origin.y();
699     if (offsetX)
700         offsetX = offsetX / tileSize().width();
701     if (offsetY)
702         offsetY = offsetY / tileSize().height();
703     return TileIndex(offsetX, offsetY);
704 }
705
706 IntPoint LayerTiler::originOfTile(const TileIndex& index)
707 {
708     return IntPoint(index.i() * tileSize().width(), index.j() * tileSize().height());
709 }
710
711 IntRect LayerTiler::rectForTile(const TileIndex& index, const IntSize& bounds)
712 {
713     IntPoint origin = originOfTile(index);
714     IntSize offset(origin.x(), origin.y());
715     IntSize size = tileSize().shrunkTo(bounds - offset);
716     return IntRect(origin, size);
717 }
718
719 bool LayerTiler::hasDirtyTiles() const
720 {
721     for (TileMap::const_iterator it = m_tilesCompositingThread.begin(); it != m_tilesCompositingThread.end(); ++it) {
722         const LayerTile* tile = (*it).second;
723         if (tile->isDirty())
724             return true;
725     }
726
727     return false;
728 }
729
730 void LayerTiler::bindContentsTexture()
731 {
732     ASSERT(m_tilesCompositingThread.size() == 1);
733     if (m_tilesCompositingThread.size() != 1)
734         return;
735
736     const LayerTile* tile = m_tilesCompositingThread.begin()->second;
737
738     ASSERT(tile->hasTexture());
739     if (!tile->hasTexture())
740         return;
741
742     glBindTexture(GL_TEXTURE_2D, tile->texture()->textureId());
743 }
744
745 } // namespace WebCore
746
747 #endif