Commit 2d202265 authored by Chris Lord's avatar Chris Lord
Browse files

Bug 724928 - Tell Gecko not to draw areas we aren't interested in. r=kats

Alter GeckoSoftwareLayerClient.beginDrawing so that it can return a rectangle
of the buffer that it's interested in. Gecko then uses this to clip the dirty
region, which often saves on unnecessary drawing during flings.
parent 356d8337
Loading
Loading
Loading
Loading
+57 −20
Original line number Diff line number Diff line
@@ -189,7 +189,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
        return true;
    }

    public boolean beginDrawing(int width, int height, int tileWidth, int tileHeight, String metadata, boolean hasDirectTexture) {
    public Rect beginDrawing(int width, int height, int tileWidth, int tileHeight,
                             String metadata, boolean hasDirectTexture) {
        setHasDirectTexture(hasDirectTexture);

        // Make sure the tile-size matches. If it doesn't, we could crash trying
@@ -197,15 +198,17 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
        if (mHasDirectTexture) {
            if (tileWidth != 0 || tileHeight != 0) {
                Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + tileHeight);
                return false;
                return null;
            }
        } else {
            if (tileWidth != TILE_SIZE.width || tileHeight != TILE_SIZE.height) {
                Log.e(LOGTAG, "Aborting draw, incorrect tile size of " + tileWidth + "x" + tileHeight);
                return false;
                return null;
            }
        }

        LayerController controller = getLayerController();

        try {
            JSONObject viewportObject = new JSONObject(metadata);
            mNewGeckoViewport = new ViewportMetrics(viewportObject);
@@ -213,12 +216,49 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
            // Update the background color, if it's present.
            String backgroundColorString = viewportObject.optString("backgroundColor");
            if (backgroundColorString != null) {
                LayerController controller = getLayerController();
                controller.setCheckerboardColor(parseColorFromGecko(backgroundColorString));
            }
        } catch (JSONException e) {
            Log.e(LOGTAG, "Aborting draw, bad viewport description: " + metadata);
            return false;
            return null;
        }

        // Make sure we don't spend time painting areas we aren't interested in.
        // Only do this if the Gecko viewport isn't going to override our viewport.
        Rect bufferRect = new Rect(0, 0, width, height);

        if (!mUpdateViewportOnEndDraw) {
            // First, find out our ideal displayport. We do this by taking the
            // clamped viewport origin and taking away the optimum viewport offset.
            // This would be what we would send to Gecko if adjustViewport were
            // called now.
            ViewportMetrics currentMetrics = controller.getViewportMetrics();
            PointF currentBestOrigin = RectUtils.getOrigin(currentMetrics.getClampedViewport());
            PointF viewportOffset = currentMetrics.getOptimumViewportOffset(new IntSize(width, height));
            currentBestOrigin.offset(-viewportOffset.x, -viewportOffset.y);

            Rect currentRect = RectUtils.round(new RectF(currentBestOrigin.x, currentBestOrigin.y,
                                                         currentBestOrigin.x + width, currentBestOrigin.y + height));

            // Second, store Gecko's displayport.
            PointF currentOrigin = mNewGeckoViewport.getDisplayportOrigin();
            bufferRect = RectUtils.round(new RectF(currentOrigin.x, currentOrigin.y,
                                                   currentOrigin.x + width, currentOrigin.y + height));


            // Take the intersection of the two as the area we're interested in rendering.
            if (!bufferRect.intersect(currentRect)) {
                // If there's no intersection, we have no need to render anything,
                // but make sure to update the viewport size.
                beginTransaction(mTileLayer);
                try {
                    updateViewport(true);
                } finally {
                    endTransaction(mTileLayer);
                }
                return null;
            }
            bufferRect.offset(Math.round(-currentOrigin.x), Math.round(-currentOrigin.y));
        }

        beginTransaction(mTileLayer);
@@ -227,12 +267,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
        if (mBufferSize.width != width || mBufferSize.height != height) {
            mBufferSize = new IntSize(width, height);

            // We only need to allocate buffer memory if we're using MultiTileLayer.
            if (!(mTileLayer instanceof MultiTileLayer)) {
                return true;
            }

            // Reallocate the buffer if necessary
            if (mTileLayer instanceof MultiTileLayer) {
                int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8;
                int size = mBufferSize.getArea() * bpp;
                if (mBuffer == null || mBuffer.capacity() != size) {
@@ -245,8 +281,9 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL
                    mBuffer = GeckoAppShell.allocateDirectBuffer(size);
                }
            }
        }

        return true;
        return bufferRect;
    }

    private void updateViewport(final boolean onlyUpdatePageSize) {
+10 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@
package org.mozilla.gecko.gfx;

import org.mozilla.gecko.FloatUtils;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import org.json.JSONException;
@@ -109,6 +111,14 @@ public final class RectUtils {
        return new IntSize(rect.width(), rect.height());
    }

    public static Point getOrigin(Rect rect) {
        return new Point(rect.left, rect.top);
    }

    public static PointF getOrigin(RectF rect) {
        return new PointF(rect.left, rect.top);
    }

    /*
     * Returns the rect that represents a linear transition between `from` and `to` at time `t`,
     * which is on the scale [0, 1).
+18 −5
Original line number Diff line number Diff line
@@ -331,7 +331,7 @@ AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv)

    jLockBufferMethod = getMethod("lockBuffer", "()Ljava/nio/ByteBuffer;");
    jUnlockBufferMethod = getMethod("unlockBuffer", "()V");
    jBeginDrawingMethod = getMethod("beginDrawing", "(IIIILjava/lang/String;Z)Z");
    jBeginDrawingMethod = getMethod("beginDrawing", "(IIIILjava/lang/String;Z)Landroid/graphics/Rect;");
    jEndDrawingMethod = getMethod("endDrawing", "(IIII)V");
#endif
}
@@ -392,8 +392,8 @@ AndroidGeckoEvent::ReadRectField(JNIEnv *jenv)
    if (!r.isNull()) {
        mRect.SetRect(r.Left(),
                      r.Top(),
                      r.Right() - r.Left(),
                      r.Bottom() - r.Top());
                      r.Width(),
                      r.Height());
    } else {
        mRect.SetEmpty();
    }
@@ -690,7 +690,7 @@ AndroidGeckoSoftwareLayerClient::UnlockBuffer()
}

bool
AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, const nsAString &aMetadata, bool aHasDirectTexture)
AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, nsIntRect &aDirtyRect, const nsAString &aMetadata, bool aHasDirectTexture)
{
    NS_ASSERTION(!isNull(), "BeginDrawing() called on null software layer client!");
    JNIEnv *env = AndroidBridge::GetJNIEnv();
@@ -699,7 +699,20 @@ AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTile

    AndroidBridge::AutoLocalJNIFrame(env, 1);
    jstring jMetadata = env->NewString(nsPromiseFlatString(aMetadata).get(), aMetadata.Length());
    return env->CallBooleanMethod(wrapped_obj, jBeginDrawingMethod, aWidth, aHeight, aTileWidth, aTileHeight, jMetadata, aHasDirectTexture);

    jobject rectObject = env->CallObjectMethod(wrapped_obj, jBeginDrawingMethod,
                                               aWidth, aHeight, aTileWidth, aTileHeight,
                                               jMetadata, aHasDirectTexture);

    if (rectObject == nsnull)
        return false;

    AndroidRect rect(env, rectObject);
    nsIntRect newDirtyRect = aDirtyRect.Intersect(nsIntRect(rect.Top(), rect.Left(),
                                                            rect.Width(), rect.Height()));
    aDirtyRect.SetRect(newDirtyRect.x, newDirtyRect.y, newDirtyRect.width, newDirtyRect.height);

    return true;
}

void
+3 −1
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ public:
    int Left() { return mLeft; }
    int Right() { return mRight; }
    int Top() { return mTop; }
    int Width() { return mRight - mLeft; }
    int Height() { return mBottom - mTop; }

protected:
    int mBottom;
@@ -161,7 +163,7 @@ public:
    jobject LockBuffer();
    unsigned char *LockBufferBits();
    void UnlockBuffer();
    bool BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, const nsAString &aMetadata, bool aHasDirectTexture);
    bool BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, nsIntRect &aDirtyRect, const nsAString &aMetadata, bool aHasDirectTexture);
    void EndDrawing(const nsIntRect &aRect);

private:
+3 −3
Original line number Diff line number Diff line
@@ -1208,16 +1208,16 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
        metadataProvider->GetDrawMetadata(metadata);
    }

    nsIntRect dirtyRect = ae->Rect().Intersect(nsIntRect(0, 0, gAndroidBounds.width, gAndroidBounds.height));

    AndroidGeckoSoftwareLayerClient &client =
        AndroidBridge::Bridge()->GetSoftwareLayerClient();
    if (!client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height,
                             gAndroidTileSize.width, gAndroidTileSize.height,
                             metadata, HasDirectTexture())) {
                             dirtyRect, metadata, HasDirectTexture())) {
        return;
    }

    nsIntRect dirtyRect = ae->Rect().Intersect(nsIntRect(0, 0, gAndroidBounds.width, gAndroidBounds.height));

    unsigned char *bits = NULL;
    if (HasDirectTexture()) {
      if (sDirectTexture->Width() != gAndroidBounds.width ||