Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ce4a7dfc authored by Romain Guy's avatar Romain Guy
Browse files

Don't crash when making a layer larger than supported dimensions

Bug #8437401

A misplaced ref count decrement was causing a crash when attempting to
resize a layer to dimensions larger than the max texture size supported
by the GPU.

This change fixes the crash and clarifies the warnings to make it more
obvious what's happening.

Change-Id: I632dc1b90aaa2605969e10523491a81c4922d3dc
parent 4500a8d5
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -248,9 +248,7 @@ status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList,
}

status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) {
    mLayers.add(layer);
    mCaches.resourceCache.incrementRefcount(layer);

    layer = refLayer(layer);
    addDrawOp(new (alloc()) DrawLayerOp(layer, x, y));
    return DrawGlInfo::kStatusDone;
}
+6 −0
Original line number Diff line number Diff line
@@ -271,6 +271,12 @@ private:
        return copy;
    }

    inline Layer* refLayer(Layer* layer) {
        mLayers.add(layer);
        mCaches.resourceCache.incrementRefcount(layer);
        return layer;
    }

    inline SkBitmap* refBitmap(SkBitmap* bitmap) {
        // Note that this assumes the bitmap is immutable. There are cases this won't handle
        // correctly, such as creating the bitmap from scratch, drawing with it, changing its
+7 −0
Original line number Diff line number Diff line
@@ -75,6 +75,13 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
        return true;
    }

    const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
    if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
        ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
                desiredWidth, desiredHeight, maxTextureSize, maxTextureSize);
        return false;
    }

    uint32_t oldWidth = getWidth();
    uint32_t oldHeight = getHeight();

+18 −7
Original line number Diff line number Diff line
@@ -222,6 +222,21 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
        return NULL;
    }

    // We first obtain a layer before comparing against the max texture size
    // because layers are not allocated at the exact desired size. They are
    // always created slighly larger to improve recycling
    const uint32_t maxTextureSize = caches.maxTextureSize;
    if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) {
        ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
                width, height, maxTextureSize, maxTextureSize);

        // Creating a new layer always increment its refcount by 1, this allows
        // us to destroy the layer object if one was created for us
        Caches::getInstance().resourceCache.decrementRefcount(layer);

        return NULL;
    }

    layer->setFbo(fbo);
    layer->layer.set(0.0f, 0.0f, width, height);
    layer->texCoords.set(0.0f, height / float(layer->getHeight()),
@@ -243,14 +258,11 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque
        layer->setEmpty(false);
        layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);

        // This should only happen if we run out of memory
        if (glGetError() != GL_NO_ERROR) {
            ALOGD("Could not allocate texture for layer (fbo=%d %dx%d)",
                    fbo, width, height);

            ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);

            Caches::getInstance().resourceCache.decrementRefcount(layer);

            caches.resourceCache.decrementRefcount(layer);
            return NULL;
        }
    }
@@ -272,7 +284,6 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
            layer->texCoords.set(0.0f, height / float(layer->getHeight()),
                    width / float(layer->getWidth()), 0.0f);
        } else {
            Caches::getInstance().resourceCache.decrementRefcount(layer);
            return false;
        }
    }