Surface.cpp revision 418e065ccd82593c3f5d49942b0aaee6fac95615
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                    // layer list has changed, fully invalidate
104                    // TODO: partially invalidate based on layer size/position
105                    fullInval = true;
106                    break;
107                } else if (!m_layers[i]->getInvalRegion()->isEmpty()) {
108                    // merge layer inval - translate the layer's inval region into surface coordinates
109                    SkPoint pos = m_layers[i]->getPosition();
110                    m_layers[i]->getInvalRegion()->translate(pos.fX, pos.fY);
111                    invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op);
112                    break;
113                }
114            }
115        }
116
117        if (fullInval)
118            invalRegion.setRect(-1e8, -1e8, 2e8, 2e8);
119
120        m_surfaceBacking->markAsDirty(invalRegion);
121    }
122    return true;
123}
124
125void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform)
126{
127    m_layers.append(layer);
128    SkSafeRef(layer);
129
130    m_needsTexture |= layer->needsTexture();
131    m_hasText |= layer->hasText();
132
133    // calculate area size for comparison later
134    IntRect rect = layer->fullContentArea();
135    SkPoint pos = layer->getPosition();
136    rect.setLocation(IntPoint(pos.fX, pos.fY));
137
138    if (layer->needsTexture()) {
139        if (m_fullContentArea.isEmpty()) {
140            m_drawTransform = transform;
141            m_drawTransform.translate3d(-pos.fX, -pos.fY, 0);
142            m_fullContentArea = rect;
143        } else
144            m_fullContentArea.unite(rect);
145        ALOGV("Surf %p adding LA %p, size  %d, %d  %dx%d, now Surf size %d,%d  %dx%d",
146              this, layer, rect.x(), rect.y(), rect.width(), rect.height(),
147              m_fullContentArea.x(), m_fullContentArea.y(),
148              m_fullContentArea.width(), m_fullContentArea.height());
149    }
150
151    if (isBase())
152        m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor();
153}
154
155IntRect Surface::visibleContentArea(bool force3dContentVisible)
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 (!getFirstLayer()->visible())
231        return false;
232
233    bool isBaseLayer = isBase()
234        || getFirstLayer()->subclassType() == LayerAndroid::FixedBackgroundBaseLayer
235        || getFirstLayer()->subclassType() == LayerAndroid::ForegroundBaseLayer;
236
237    if (!isBaseLayer) {
238        // TODO: why are clipping regions wrong for base layer?
239        FloatRect drawClip = getFirstLayer()->drawClip();
240        FloatRect clippingRect = TilesManager::instance()->shader()->rectInInvViewCoord(drawClip);
241        TilesManager::instance()->shader()->clip(clippingRect);
242    }
243
244    bool askRedraw = false;
245    if (m_surfaceBacking && !tilesDisabled) {
246        ALOGV("drawGL on Surf %p with SurfBack %p, first layer %s (%d)", this, m_surfaceBacking,
247              getFirstLayer()->subclassName().ascii().data(), getFirstLayer()->uniqueId());
248
249        bool force3dContentVisible = true;
250        IntRect drawArea = visibleContentArea(force3dContentVisible);
251        m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(),
252                                 useAggressiveRendering(), background());
253    }
254
255    // draw member layers (draws image textures, glextras)
256    for (unsigned int i = 0; i < m_layers.size(); i++)
257        askRedraw |= m_layers[i]->drawGL(tilesDisabled);
258
259    return askRedraw;
260}
261
262void Surface::swapTiles()
263{
264    if (!m_surfaceBacking)
265        return;
266
267    m_surfaceBacking->swapTiles();
268}
269
270bool Surface::isReady()
271{
272    if (!m_surfaceBacking)
273        return true;
274
275    return m_surfaceBacking->isReady();
276}
277
278bool Surface::isMissingContent()
279{
280    if (!m_surfaceBacking)
281        return true;
282
283    return m_surfaceBacking->isMissingContent();
284}
285
286bool Surface::canUpdateWithBlit()
287{
288    // If we don't have a texture, we have nothing to update and thus can take
289    // the fast path
290    if (!needsTexture())
291        return true;
292    // If we have a surface backing that isn't ready, we can't update with a blit
293    // If it is ready, then check to see if it is dirty. We can only call isDirty()
294    // if isReady() returns true
295    if (!m_surfaceBacking)
296        return false;
297    if (!m_surfaceBacking->isReady())
298        return false;
299    if (!m_surfaceBacking->isDirty())
300        return true;
301    if (!singleLayer())
302        return false;
303    return getFirstLayer()->canUpdateWithBlit();
304}
305
306IntRect Surface::computePrepareArea()
307{
308    IntRect area;
309
310    if (!getFirstLayer()->contentIsScrollable()
311        && !isBase()
312        && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) {
313
314        area = fullContentArea();
315
316        double total = ((double) area.width()) * ((double) area.height());
317        if (total > MAX_FULL_CONTENT_AREA)
318            area = visibleContentArea();
319    } else
320        area = visibleContentArea();
321
322    return area;
323}
324
325void Surface::computeTexturesAmount(TexturesResult* result)
326{
327    if (!m_surfaceBacking || isBase())
328        return;
329
330    m_surfaceBacking->computeTexturesAmount(result, getFirstLayer());
331}
332
333bool Surface::isBase()
334{
335    // base layer surface
336    // - doesn't use layer tiles (disables blending, doesn't compute textures amount)
337    // - ignores clip rects
338    // - only prepares clippedArea
339    return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer;
340}
341
342bool Surface::paint(SkCanvas* canvas)
343{
344    if (singleLayer()) {
345        getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers);
346
347        // TODO: double buffer by disabling SurfaceCollection swaps and position
348        // updates until painting complete
349
350        // In single surface mode, draw layer content onto the base layer
351        if (isBase()
352            && getFirstLayer()->countChildren()
353            && getFirstLayer()->state()->layersRenderingMode() > GLWebViewState::kClippedTextures) {
354            for (unsigned int i = 0; i < getFirstLayer()->countChildren(); i++)
355                getFirstLayer()->getChild(i)->drawCanvas(canvas, true, Layer::FlattenedLayers);
356        }
357    } else {
358        SkAutoCanvasRestore acr(canvas, true);
359        SkMatrix matrix;
360        GLUtils::toSkMatrix(matrix, m_drawTransform);
361
362        SkMatrix inverse;
363        inverse.reset();
364        matrix.invert(&inverse);
365
366        SkMatrix canvasMatrix = canvas->getTotalMatrix();
367        inverse.postConcat(canvasMatrix);
368        canvas->setMatrix(inverse);
369
370        for (unsigned int i=0; i<m_layers.size(); i++)
371            m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers);
372    }
373    return true;
374}
375
376float Surface::opacity()
377{
378    if (singleLayer())
379        return getFirstLayer()->drawOpacity();
380    return 1.0;
381}
382
383Color* Surface::background()
384{
385    if (!isBase() || !m_background.isValid())
386        return 0;
387    return &m_background;
388}
389
390bool Surface::blitFromContents(Tile* tile)
391{
392    if (!singleLayer() || !tile || !getFirstLayer() || !getFirstLayer()->content())
393        return false;
394
395    LayerContent* content = getFirstLayer()->content();
396    // Extract the dirty rect from the region. Note that this is *NOT* constrained
397    // to this tile
398    IntRect dirtyRect = tile->dirtyArea().getBounds();
399    IntRect tileRect = IntRect(tile->x() * TilesManager::tileWidth(),
400                               tile->y() * TilesManager::tileHeight(),
401                               TilesManager::tileWidth(),
402                               TilesManager::tileHeight());
403    FloatRect tileRectInDoc = tileRect;
404    tileRectInDoc.scale(1 / tile->scale());
405    dirtyRect.intersect(enclosingIntRect(tileRectInDoc));
406    PrerenderedInval* prerenderedInval = content->prerenderForRect(dirtyRect);
407    if (!prerenderedInval || prerenderedInval->bitmap.isNull())
408        return false;
409    SkBitmap sourceBitmap = prerenderedInval->bitmap;
410    // Calculate the screen rect that is dirty, then intersect it with the
411    // tile's screen rect so that we end up with the pixels we need to blit
412    FloatRect screenDirty = dirtyRect;
413    screenDirty.scale(tile->scale());
414    IntRect enclosingScreenDirty = enclosingIntRect(screenDirty);
415    enclosingScreenDirty.intersect(tileRect);
416    if (enclosingScreenDirty.isEmpty())
417        return false;
418    // Make sure the screen area we want to blit is contained by the
419    // prerendered screen area
420    if (!prerenderedInval->screenArea.contains(enclosingScreenDirty)) {
421        ALOGD("prerendered->screenArea " INT_RECT_FORMAT " doesn't contain "
422                "enclosingScreenDirty " INT_RECT_FORMAT,
423                INT_RECT_ARGS(prerenderedInval->screenArea),
424                INT_RECT_ARGS(enclosingScreenDirty));
425        return false;
426    }
427    IntPoint origin = prerenderedInval->screenArea.location();
428    SkBitmap subset;
429    subset.setConfig(sourceBitmap.config(), enclosingScreenDirty.width(),
430            enclosingScreenDirty.height());
431    subset.allocPixels();
432
433    int topOffset = enclosingScreenDirty.y() - prerenderedInval->screenArea.y();
434    int leftOffset = enclosingScreenDirty.x() - prerenderedInval->screenArea.x();
435    if (!GLUtils::deepCopyBitmapSubset(sourceBitmap, subset, leftOffset, topOffset))
436        return false;
437    // Now upload
438    SkIRect textureInval = SkIRect::MakeXYWH(enclosingScreenDirty.x() - tileRect.x(),
439                                             enclosingScreenDirty.y() - tileRect.y(),
440                                             enclosingScreenDirty.width(),
441                                             enclosingScreenDirty.height());
442    GLUtils::updateTextureWithBitmap(tile->frontTexture()->m_ownTextureId,
443                                     subset, textureInval);
444    tile->onBlitUpdate();
445    return true;
446}
447
448const TransformationMatrix* Surface::drawTransform()
449{
450    // single layer surfaces query the layer's draw transform, while multi-layer
451    // surfaces copy the draw transform once, during initialization
452    // TODO: support fixed multi-layer surfaces by querying the changing drawTransform
453    if (singleLayer())
454        return getFirstLayer()->drawTransform();
455
456    return &m_drawTransform;
457}
458
459} // namespace WebCore
460