Surface.cpp revision 25c25a826222bf0fa1df56f6cfd8582296357b49
1/*
2 * Copyright 2012, The Android Open Source Project
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 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * 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 THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "Surface"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "Surface.h"
31
32#include "AndroidLog.h"
33#include "BaseLayerAndroid.h"
34#include "ClassTracker.h"
35#include "LayerAndroid.h"
36#include "LayerContent.h"
37#include "GLWebViewState.h"
38#include "PrerenderedInval.h"
39#include "SkCanvas.h"
40#include "SurfaceBacking.h"
41#include "Tile.h"
42#include "TileTexture.h"
43#include "TilesManager.h"
44
45#include <wtf/text/CString.h>
46
47// Surfaces with an area larger than 2048*2048 should never be unclipped
48#define MAX_FULL_CONTENT_AREA 4194304
49
50namespace WebCore {
51
52Surface::Surface()
53    : m_surfaceBacking(0)
54    , m_needsTexture(false)
55    , m_hasText(false)
56{
57#ifdef DEBUG_COUNT
58    ClassTracker::instance()->increment("Surface");
59#endif
60}
61
62Surface::~Surface()
63{
64    for (unsigned int i = 0; i < m_layers.size(); i++)
65        SkSafeUnref(m_layers[i]);
66    if (m_surfaceBacking)
67        SkSafeUnref(m_surfaceBacking);
68#ifdef DEBUG_COUNT
69    ClassTracker::instance()->decrement("Surface");
70#endif
71}
72
73bool Surface::tryUpdateSurface(Surface* oldSurface)
74{
75    if (!needsTexture() || !oldSurface->needsTexture())
76        return false;
77
78    // merge surfaces based on first layer ID
79    if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId())
80        return false;
81
82    m_surfaceBacking = oldSurface->m_surfaceBacking;
83    SkSafeRef(m_surfaceBacking);
84
85    ALOGV("%p taking old SurfBack %p from surface %p, nt %d",
86          this, m_surfaceBacking, oldSurface, oldSurface->needsTexture());
87
88    if (!m_surfaceBacking) {
89        // no SurfBack to inval, so don't worry about it.
90        return true;
91    }
92
93    if (singleLayer() && oldSurface->singleLayer()) {
94        // both are single matching layers, simply apply inval
95        SkRegion* layerInval = getFirstLayer()->getInvalRegion();
96        m_surfaceBacking->markAsDirty(*layerInval);
97    } else {
98        SkRegion invalRegion;
99        bool fullInval = m_layers.size() != oldSurface->m_layers.size();
100        if (!fullInval) {
101            for (unsigned int i = 0; i < m_layers.size(); i++) {
102                if ((m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId())
103                    || (m_layers[i]->fullContentAreaMapped() != oldSurface->m_layers[i]->fullContentAreaMapped())) {
104                    // layer list has changed, fully invalidate
105                    // TODO: partially invalidate based on layer size/position
106                    fullInval = true;
107                    break;
108                } else if (!m_layers[i]->getInvalRegion()->isEmpty()) {
109                    // merge layer inval - translate the layer's inval region into surface coordinates
110                    // TODO: handle scale/3d transform mapping
111                    FloatRect layerPos = m_layers[i]->fullContentAreaMapped();
112                    m_layers[i]->getInvalRegion()->translate(layerPos.x(), layerPos.y());
113                    invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op);
114                    break;
115                }
116            }
117        }
118
119        if (fullInval)
120            invalRegion.setRect(-1e8, -1e8, 2e8, 2e8);
121
122        m_surfaceBacking->markAsDirty(invalRegion);
123    }
124    return true;
125}
126
127void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform)
128{
129    m_layers.append(layer);
130    SkSafeRef(layer);
131
132    m_needsTexture |= layer->needsTexture();
133    m_hasText |= layer->hasText();
134
135    // add this layer's size to the surface's area
136    // TODO: handle scale/3d transform mapping
137    IntRect rect = enclosingIntRect(layer->fullContentAreaMapped());
138
139    if (layer->needsTexture()) {
140        if (m_fullContentArea.isEmpty()) {
141            m_drawTransform = transform;
142            m_drawTransform.translate3d(-rect.x(), -rect.y(), 0);
143            m_fullContentArea = rect;
144        } else
145            m_fullContentArea.unite(rect);
146        ALOGV("Surf %p adding LA %p, size " INT_RECT_FORMAT
147              " now fullContentArea " INT_RECT_FORMAT,
148              this, layer, INT_RECT_ARGS(rect), INT_RECT_ARGS(m_fullContentArea));
149    }
150
151    if (isBase())
152        m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor();
153}
154
155IntRect Surface::visibleContentArea(bool force3dContentVisible) const
156{
157    if (singleLayer())
158        return getFirstLayer()->visibleContentArea(force3dContentVisible);
159
160    IntRect rect = m_fullContentArea;
161
162    // clip with the viewport in content coordinate
163    IntRect contentViewport(TilesManager::instance()->shader()->contentViewport());
164    rect.intersect(contentViewport);
165
166    // TODO: handle recursive layer clip
167
168    return rect;
169}
170
171IntRect Surface::fullContentArea()
172{
173    if (singleLayer())
174        return getFirstLayer()->fullContentArea();
175    return m_fullContentArea;
176}
177
178bool Surface::useAggressiveRendering()
179{
180    // When the background is semi-opaque, 0 < alpha < 255, we had to turn off
181    // low res to avoid artifacts from double drawing.
182    // TODO: avoid double drawing for low res tiles.
183    return isBase()
184           && (!m_background.alpha()
185           || !m_background.hasAlpha());
186}
187
188void Surface::prepareGL(bool layerTilesDisabled, bool updateWithBlit)
189{
190    bool tilesDisabled = layerTilesDisabled && !isBase();
191    if (!m_surfaceBacking) {
192        ALOGV("prepareGL on Surf %p, no SurfBack, needsTexture? %d",
193              this, m_surfaceBacking, needsTexture());
194
195        if (needsTexture() || (isBase() && layerTilesDisabled))
196            m_surfaceBacking = new SurfaceBacking(isBase());
197        else
198            return;
199    }
200
201    if (tilesDisabled) {
202        m_surfaceBacking->discardTextures();
203    } else {
204        bool allowZoom = hasText(); // only allow for scale > 1 if painting vectors
205        IntRect prepareArea = computePrepareArea();
206        IntRect fullArea = fullContentArea();
207
208        ALOGV("prepareGL on Surf %p with SurfBack %p, %d layers, first layer %s (%d) "
209              "prepareArea(%d, %d - %d x %d) fullArea(%d, %d - %d x %d)",
210              this, m_surfaceBacking, m_layers.size(),
211              getFirstLayer()->subclassName().ascii().data(),
212              getFirstLayer()->uniqueId(),
213              prepareArea.x(), prepareArea.y(), prepareArea.width(), prepareArea.height(),
214              fullArea.x(), fullArea.y(), fullArea.width(), fullArea.height());
215
216        m_surfaceBacking->prepareGL(getFirstLayer()->state(), allowZoom,
217                                    prepareArea, fullArea,
218                                    this, useAggressiveRendering(), updateWithBlit);
219    }
220    for (size_t i = 0; i < m_layers.size(); i++) {
221        LayerContent* content = m_layers[i]->content();
222        if (content)
223            content->clearPrerenders();
224    }
225}
226
227bool Surface::drawGL(bool layerTilesDisabled)
228{
229    bool tilesDisabled = layerTilesDisabled && !isBase();
230    if (singleLayer() && !getFirstLayer()->visible())
231        return false;
232
233    if (!isBase()) {
234        FloatRect drawClip = getFirstLayer()->drawClip();
235        if (!singleLayer()) {
236            for (unsigned int i = 1; i < m_layers.size(); i++)
237                drawClip.unite(m_layers[i]->drawClip());
238        }
239        FloatRect clippingRect = TilesManager::instance()->shader()->rectInInvViewCoord(drawClip);
240        TilesManager::instance()->shader()->clip(clippingRect);
241    }
242
243    bool askRedraw = false;
244    if (m_surfaceBacking && !tilesDisabled) {
245        ALOGV("drawGL on Surf %p with SurfBack %p, first layer %s (%d)", this, m_surfaceBacking,
246              getFirstLayer()->subclassName().ascii().data(), getFirstLayer()->uniqueId());
247
248        bool force3dContentVisible = true;
249        IntRect drawArea = visibleContentArea(force3dContentVisible);
250        m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(),
251                                 useAggressiveRendering(), background());
252    }
253
254    // draw member layers (draws image textures, glextras)
255    for (unsigned int i = 0; i < m_layers.size(); i++) {
256        if (m_layers[i]->drawGL(tilesDisabled)) {
257           m_layers[i]->addDirtyArea();
258           askRedraw = true;
259        }
260    }
261
262    return askRedraw;
263}
264
265void Surface::swapTiles(bool calculateFrameworkInvals)
266{
267    if (!m_surfaceBacking)
268        return;
269
270    if (m_surfaceBacking->swapTiles() && calculateFrameworkInvals)
271        addFrameworkInvals();
272}
273
274void Surface::addFrameworkInvals()
275{
276    // Let's return an inval area to framework that will
277    // contain all of our layers' areas
278    for (unsigned int i = 0; i < m_layers.size(); i++)
279        m_layers[i]->addDirtyArea();
280}
281
282bool Surface::isReady()
283{
284    if (!m_surfaceBacking)
285        return true;
286
287    return m_surfaceBacking->isReady();
288}
289
290bool Surface::isMissingContent()
291{
292    if (!m_surfaceBacking)
293        return true;
294
295    return m_surfaceBacking->isMissingContent();
296}
297
298bool Surface::canUpdateWithBlit()
299{
300    // If we don't have a texture, we have nothing to update and thus can take
301    // the fast path
302    if (!needsTexture())
303        return true;
304    // If we have a surface backing that isn't ready, we can't update with a blit
305    // If it is ready, then check to see if it is dirty. We can only call isDirty()
306    // if isReady() returns true
307    if (!m_surfaceBacking)
308        return false;
309    if (!m_surfaceBacking->isReady())
310        return false;
311    if (!m_surfaceBacking->isDirty())
312        return true;
313    if (!singleLayer())
314        return false;
315    return getFirstLayer()->canUpdateWithBlit();
316}
317
318IntRect Surface::computePrepareArea()
319{
320    IntRect area;
321
322    if (!getFirstLayer()->contentIsScrollable()
323        && !isBase()
324        && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) {
325
326        area = fullContentArea();
327
328        double total = ((double) area.width()) * ((double) area.height());
329        if (total > MAX_FULL_CONTENT_AREA)
330            area = visibleContentArea();
331    } else
332        area = visibleContentArea();
333
334    return area;
335}
336
337void Surface::computeTexturesAmount(TexturesResult* result)
338{
339    if (!m_surfaceBacking || isBase())
340        return;
341
342
343    LayerAndroid* layer = 0;
344    if (singleLayer())
345        layer = getFirstLayer();
346
347    m_surfaceBacking->computeTexturesAmount(result, visibleContentArea(),
348                                            fullContentArea(), layer);
349}
350
351bool Surface::isBase()
352{
353    // base layer surface
354    // - doesn't use layer tiles (disables blending, doesn't compute textures amount)
355    // - ignores clip rects
356    // - only prepares clippedArea
357    return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer;
358}
359
360bool Surface::paint(SkCanvas* canvas)
361{
362    if (singleLayer()) {
363        getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers);
364
365        // TODO: double buffer by disabling SurfaceCollection swaps and position
366        // updates until painting complete
367
368        // In single surface mode, draw layer content onto the base layer
369        if (isBase()
370            && getFirstLayer()->countChildren()
371            && getFirstLayer()->state()->isSingleSurfaceRenderingMode()) {
372            for (int i = 0; i < getFirstLayer()->countChildren(); i++)
373                getFirstLayer()->getChild(i)->drawCanvas(canvas, true, Layer::FlattenedLayers);
374        }
375    } else {
376        SkAutoCanvasRestore acr(canvas, true);
377        SkMatrix matrix;
378        GLUtils::toSkMatrix(matrix, m_drawTransform);
379
380        SkMatrix inverse;
381        inverse.reset();
382        matrix.invert(&inverse);
383
384        SkMatrix canvasMatrix = canvas->getTotalMatrix();
385        inverse.postConcat(canvasMatrix);
386        canvas->setMatrix(inverse);
387
388        for (unsigned int i=0; i<m_layers.size(); i++)
389            m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers);
390    }
391    return true;
392}
393
394float Surface::opacity()
395{
396    if (singleLayer())
397        return getFirstLayer()->drawOpacity();
398    return 1.0;
399}
400
401Color* Surface::background()
402{
403    if (!isBase() || !m_background.isValid())
404        return 0;
405    return &m_background;
406}
407
408bool Surface::blitFromContents(Tile* tile)
409{
410    if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content())
411        return false;
412
413    LayerContent* content = getFirstLayer()->content();
414    // Extract the dirty rect from the region. Note that this is *NOT* constrained
415    // to this tile
416    IntRect dirtyRect = tile->dirtyArea().getBounds();
417    IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(),
418                               tile->y() * TilesManager::tileHeight(),
419                               TilesManager::tileWidth(),
420                               TilesManager::tileHeight());
421    FloatRect tileRectInDoc = tileRect;
422    tileRectInDoc.scale(1 / tile->scale());
423    dirtyRect.intersect(enclosingIntRect(tileRectInDoc));
424    PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect);
425    if (!prerenderedInval || prerenderedInval->bitmap.isNull())
426        return false;
427    SkBitmap sourceBitmap = prerenderedInval->bitmap;
428    // Calculate the screen rect that is dirty, then intersect it with the
429    // tile's screen rect so that we end up with the pixels we need to blit
430    FloatRect screenDirty = dirtyRect;
431    screenDirty.scale(tile->scale());
432    IntRect enclosingScreenDirty = enclosingIntRect(screenDirty);
433    enclosingScreenDirty.intersect(tileRect);
434    if (enclosingScreenDirty.isEmpty())
435        return false;
436    // Make sure the screen area we want to blit is contained by the
437    // prerendered screen area
438    if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) {
439        ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain "
440                "enclosingScreenDirty " INT_RECT_FORMAT,
441                INT_RECT_ARGS(prerenderedInval->screenArea),
442                INT_RECT_ARGS(enclosingScreenDirty));
443        return false;
444    }
445    IntPoint origin = prerenderedInval->screenArea.location();
446    SkBitmap subset;
447    subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(),
448            enclosingScreenDirty.height());
449    subset.allocPixels();
450
451    int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y();
452    int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x();
453    if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset))
454        return false;
455    // Now upload
456    SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(),
457                                             enclosingScreenDirty.y() - tileRect.y(),
458                                             enclosingScreenDirty.width(),
459                                             enclosingScreenDirty.height());
460    GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId,
461                                     subset, textureInval);
462    tile->onBlitUpdate();
463    return true;
464}
465
466const TransformationMatrix* Surface::drawTransform()
467{
468    // single layer surfaces query the layer's draw transform, while multi-layer
469    // surfaces copy the draw transform once, during initialization
470    // TODO: support fixed multi-layer surfaces by querying the changing drawTransform
471    if (singleLayer())
472        return getFirstLayer()->drawTransform();
473
474    return &m_drawTransform;
475}
476
477} // namespace WebCore
478