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

Commit 3b748a44 authored by Romain Guy's avatar Romain Guy
Browse files

Pack preloaded framework assets in a texture atlas

When the Android runtime starts, the system preloads a series of assets
in the Zygote process. These assets are shared across all processes.
Unfortunately, each one of these assets is later uploaded in its own
OpenGL texture, once per process. This wastes memory and generates
unnecessary OpenGL state changes.

This CL introduces an asset server that provides an atlas to all processes.

Note: bitmaps used by skia shaders are *not* sampled from the atlas.
It's an uncommon use case and would require extra texture transforms
in the GL shaders.

WHAT IS THE ASSETS ATLAS

The "assets atlas" is a single, shareable graphic buffer that contains
all the system's preloaded bitmap drawables (this includes 9-patches.)
The atlas is made of two distinct objects: the graphic buffer that
contains the actual pixels and the map which indicates where each
preloaded bitmap can be found in the atlas (essentially a pair of
x and y coordinates.)

HOW IS THE ASSETS ATLAS GENERATED

Because we need to support a wide variety of devices and because it
is easy to change the list of preloaded drawables, the atlas is
generated at runtime, during the startup phase of the system process.

There are several steps that lead to the atlas generation:

1. If the device is booting for the first time, or if the device was
updated, we need to find the best atlas configuration. To do so,
the atlas service tries a number of width, height and algorithm
variations that allows us to pack as many assets as possible while
using as little memory as possible. Once a best configuration is found,
it gets written to disk in /data/system/framework_atlas

2. Given a best configuration (algorithm variant, dimensions and
number of bitmaps that can be packed in the atlas), the atlas service
packs all the preloaded bitmaps into a single graphic buffer object.

3. The packing is done using Skia in a temporary native bitmap. The
Skia bitmap is then copied into the graphic buffer using OpenGL ES
to benefit from texture swizzling.

HOW PROCESSES USE THE ATLAS

Whenever a process' hardware renderer initializes its EGL context,
it queries the atlas service for the graphic buffer and the map.

It is important to remember that both the context and the map will
be valid for the lifetime of the hardware renderer (if the system
process goes down, all apps get killed as well.)

Every time the hardware renderer needs to render a bitmap, it first
checks whether the bitmap can be found in the assets atlas. When
the bitmap is part of the atlas, texture coordinates are remapped
appropriately before rendering.

Change-Id: I8eaecf53e7f6a33d90da3d0047c5ceec89ea3af0
parent dd424cf0
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -161,6 +161,7 @@ LOCAL_SRC_FILES += \
	core/java/android/view/accessibility/IAccessibilityManager.aidl \
	core/java/android/view/accessibility/IAccessibilityManager.aidl \
	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
	core/java/android/view/IApplicationToken.aidl \
	core/java/android/view/IApplicationToken.aidl \
	core/java/android/view/IAssetAtlas.aidl \
	core/java/android/view/IMagnificationCallbacks.aidl \
	core/java/android/view/IMagnificationCallbacks.aidl \
	core/java/android/view/IInputFilter.aidl \
	core/java/android/view/IInputFilter.aidl \
	core/java/android/view/IInputFilterHost.aidl \
	core/java/android/view/IInputFilterHost.aidl \
+8 −2
Original line number Original line Diff line number Diff line
@@ -16,8 +16,6 @@


package android.content.res;
package android.content.res;


import android.os.Trace;
import android.view.View;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.XmlUtils;


import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParser;
@@ -30,6 +28,7 @@ import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.graphics.drawable.Drawable.ConstantState;
import android.os.Build;
import android.os.Build;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Trace;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Log;
@@ -1985,6 +1984,13 @@ public class Resources {
        }
        }
    }
    }


    /**
     * @hide
     */
    public LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
        return sPreloadedDrawables[0];
    }

    private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
    private boolean verifyPreloadConfig(int changingConfigurations, int allowVarying,
            int resourceId, String name) {
            int resourceId, String name) {
        // We allow preloading of resources even if they vary by font scale (which
        // We allow preloading of resources even if they vary by font scale (which
+34 −29
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ColorFilter;
import android.graphics.DrawFilter;
import android.graphics.DrawFilter;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Path;
@@ -314,21 +315,21 @@ class GLES20Canvas extends HardwareCanvas {
     * 
     * 
     * @see #flushCaches(int) 
     * @see #flushCaches(int) 
     */
     */
    public static final int FLUSH_CACHES_LAYERS = 0;
    static final int FLUSH_CACHES_LAYERS = 0;
    
    
    /**
    /**
     * Must match Caches::FlushMode values
     * Must match Caches::FlushMode values
     * 
     * 
     * @see #flushCaches(int) 
     * @see #flushCaches(int) 
     */
     */
    public static final int FLUSH_CACHES_MODERATE = 1;
    static final int FLUSH_CACHES_MODERATE = 1;


    /**
    /**
     * Must match Caches::FlushMode values
     * Must match Caches::FlushMode values
     * 
     * 
     * @see #flushCaches(int) 
     * @see #flushCaches(int) 
     */
     */
    public static final int FLUSH_CACHES_FULL = 2;
    static final int FLUSH_CACHES_FULL = 2;


    /**
    /**
     * Flush caches to reclaim as much memory as possible. The amount of memory
     * Flush caches to reclaim as much memory as possible. The amount of memory
@@ -338,10 +339,8 @@ class GLES20Canvas extends HardwareCanvas {
     * {@link #FLUSH_CACHES_FULL}.
     * {@link #FLUSH_CACHES_FULL}.
     * 
     * 
     * @param level Hint about the amount of memory to reclaim
     * @param level Hint about the amount of memory to reclaim
     * 
     * @hide
     */
     */
    public static void flushCaches(int level) {
    static void flushCaches(int level) {
        nFlushCaches(level);
        nFlushCaches(level);
    }
    }


@@ -353,20 +352,27 @@ class GLES20Canvas extends HardwareCanvas {
     * 
     * 
     * @hide
     * @hide
     */
     */
    public static void terminateCaches() {
    static void terminateCaches() {
        nTerminateCaches();
        nTerminateCaches();
    }
    }


    private static native void nTerminateCaches();
    private static native void nTerminateCaches();


    /**
    static boolean initCaches() {
     * @hide
        return nInitCaches();
     */
    }
    public static void initCaches() {

        nInitCaches();
    private static native boolean nInitCaches();

    ///////////////////////////////////////////////////////////////////////////
    // Atlas
    ///////////////////////////////////////////////////////////////////////////

    static void initAtlas(GraphicBuffer buffer, int[] map) {
        nInitAtlas(buffer, map, map.length);
    }
    }


    private static native void nInitCaches();
    private static native void nInitAtlas(GraphicBuffer buffer, int[] map, int count);


    ///////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////
    // Display list
    // Display list
@@ -718,20 +724,21 @@ class GLES20Canvas extends HardwareCanvas {
    }
    }


    @Override
    @Override
    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
    public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
        Bitmap bitmap = patch.getBitmap();
        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
        if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
        // Shaders are ignored when drawing patches
        // Shaders are ignored when drawing patches
        int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
        int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
        try {
        try {
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
            nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks,
            nDrawPatch(mRenderer, bitmap.mNativeBitmap, patch.mChunk,
                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
        } finally {
        } finally {
            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
            if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier);
        }
        }
    }
    }


    private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks,
    private static native void nDrawPatch(int renderer, int bitmap, byte[] chunks,
            float left, float top, float right, float bottom, int paint);
            float left, float top, float right, float bottom, int paint);


    @Override
    @Override
@@ -741,14 +748,14 @@ class GLES20Canvas extends HardwareCanvas {
        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
        try {
        try {
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint);
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, nativePaint);
        } finally {
        } finally {
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
        }
        }
    }
    }


    private static native void nDrawBitmap(
    private static native void nDrawBitmap(int renderer, int bitmap,
            int renderer, int bitmap, byte[] buffer, float left, float top, int paint);
            float left, float top, int paint);


    @Override
    @Override
    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
    public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
@@ -757,15 +764,13 @@ class GLES20Canvas extends HardwareCanvas {
        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
        try {
        try {
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer,
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap,  matrix.native_instance, nativePaint);
                    matrix.native_instance, nativePaint);
        } finally {
        } finally {
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
        }
        }
    }
    }


    private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff,
    private static native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint);
            int matrix, int paint);


    @Override
    @Override
    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
    public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
@@ -787,7 +792,7 @@ class GLES20Canvas extends HardwareCanvas {
                bottom = src.bottom;
                bottom = src.bottom;
            }
            }


            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
        } finally {
        } finally {
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
@@ -814,14 +819,14 @@ class GLES20Canvas extends HardwareCanvas {
                bottom = src.bottom;
                bottom = src.bottom;
            }
            }
    
    
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom,
            nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom,
                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
                    dst.left, dst.top, dst.right, dst.bottom, nativePaint);
        } finally {
        } finally {
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
        }
        }
    }
    }


    private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer,
    private static native void nDrawBitmap(int renderer, int bitmap,
            float srcLeft, float srcTop, float srcRight, float srcBottom,
            float srcLeft, float srcTop, float srcRight, float srcBottom,
            float left, float top, float right, float bottom, int paint);
            float left, float top, float right, float bottom, int paint);


@@ -891,14 +896,14 @@ class GLES20Canvas extends HardwareCanvas {
        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
        int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
        try {
        try {
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;        
            final int nativePaint = paint == null ? 0 : paint.mNativePaint;        
            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight,
            nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, meshWidth, meshHeight,
                    verts, vertOffset, colors, colorOffset, nativePaint);
                    verts, vertOffset, colors, colorOffset, nativePaint);
        } finally {
        } finally {
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
            if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
        }
        }
    }
    }


    private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer,
    private static native void nDrawBitmapMesh(int renderer, int bitmap,
            int meshWidth, int meshHeight, float[] verts, int vertOffset,
            int meshWidth, int meshHeight, float[] verts, int vertOffset,
            int[] colors, int colorOffset, int paint);
            int[] colors, int colorOffset, int paint);


+8 −1
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view;


import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.NinePatch;


import java.util.ArrayList;
import java.util.ArrayList;


@@ -29,7 +30,8 @@ class GLES20DisplayList extends DisplayList {
    // alive as long as the DisplayList is alive.  The Bitmap and DisplayList lists
    // alive as long as the DisplayList is alive.  The Bitmap and DisplayList lists
    // are populated by the GLES20RecordingCanvas during appropriate drawing calls and are
    // are populated by the GLES20RecordingCanvas during appropriate drawing calls and are
    // cleared at the start of a new drawing frame or when the view is detached from the window.
    // cleared at the start of a new drawing frame or when the view is detached from the window.
    final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
    final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(10);
    final ArrayList<NinePatch> mNinePatches = new ArrayList<NinePatch>(10);
    final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();
    final ArrayList<DisplayList> mChildDisplayLists = new ArrayList<DisplayList>();


    private GLES20RecordingCanvas mCanvas;
    private GLES20RecordingCanvas mCanvas;
@@ -83,7 +85,12 @@ class GLES20DisplayList extends DisplayList {
        }
        }
        mValid = false;
        mValid = false;


        clearReferences();
    }

    void clearReferences() {
        mBitmaps.clear();
        mBitmaps.clear();
        mNinePatches.clear();
        mChildDisplayLists.clear();
        mChildDisplayLists.clear();
    }
    }


+6 −5
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package android.view;
import android.graphics.Bitmap;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.BitmapShader;
import android.graphics.Matrix;
import android.graphics.Matrix;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Rect;
@@ -62,8 +63,7 @@ class GLES20RecordingCanvas extends GLES20Canvas {
    }
    }


    void start() {
    void start() {
        mDisplayList.mBitmaps.clear();
        mDisplayList.clearReferences();
        mDisplayList.mChildDisplayLists.clear();
    }
    }


    int end(int nativeDisplayList) {
    int end(int nativeDisplayList) {
@@ -80,9 +80,10 @@ class GLES20RecordingCanvas extends GLES20Canvas {
    }
    }


    @Override
    @Override
    public void drawPatch(Bitmap bitmap, byte[] chunks, RectF dst, Paint paint) {
    public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
        super.drawPatch(bitmap, chunks, dst, paint);
        super.drawPatch(patch, dst, paint);
        mDisplayList.mBitmaps.add(bitmap);
        mDisplayList.mBitmaps.add(patch.getBitmap());
        mDisplayList.mNinePatches.add(patch);
        // Shaders in the Paint are ignored when drawing a Bitmap
        // Shaders in the Paint are ignored when drawing a Bitmap
    }
    }


Loading