Hardware renderer initialization from the ViewRootImpl class setView(…) Method calls enableHardwareAcceleration (…). Start.

HardwareRenderer is an abstract class that represents an interface to a hardware-accelerated rendering view hierarchy.

First we find the location of the mHardwareRenderer assignment, so we can determine what subclass it is. The method setView ViewRootImpl class invokes the enableHardwareAcceleration (…). Method to enable hardware acceleration to start.

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent.View.AttachInfo.Callbacks.HardwareRenderer.HardwareDrawCallbacks {...public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) { mView = view; .// Do not enable hardware acceleration if the application has Surface
                if (mSurfaceHolder == null) { enableHardwareAcceleration(attrs); }... }... }... }... }Copy the code
  1. Create a hardware renderer class instance
  2. Set the name for the hardware renderer

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent.View.AttachInfo.Callbacks.HardwareRenderer.HardwareDrawCallbacks {...private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
        mAttachInfo.mHardwareAccelerated = false;
        mAttachInfo.mHardwareAccelerationRequested = false;

        // Do not enable hardware acceleration when the application is in compatibility mode
        if(mTranslator ! =null) return;

        // Try enabling hardware acceleration if necessary
        final booleanhardwareAccelerated = (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) ! =0;

        if (hardwareAccelerated) {
            if(! HardwareRenderer.isAvailable()) {return;
            }

            // Persistence processes (including systems) should not speed up rendering on low-end devices.
            // In this case, sRendererDisabled is set.
            // Also, the system process itself should never be accelerated.
            // In this case, both sRendererDisabled and sSystemRendererDisabled are set.
            // Set sSystemRendererDisabled,
            // Code in a system process can enable hardware-accelerated drawing using PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED.
            // this is basically used to lock the screen.
            final booleanfakeHwAccelerated = (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED) ! =0;
            final booleanforceHwAccelerated = (attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED) ! =0;

            if (fakeHwAccelerated) {
                // This is only used for the preview window that the window manager displays to launch the application,
                // So they look more like applications being launched.
                mAttachInfo.mHardwareAccelerationRequested = true;
            } else if(! HardwareRenderer.sRendererDisabled || (HardwareRenderer.sSystemRendererDisabled && forceHwAccelerated)) {if(mAttachInfo.mHardwareRenderer ! =null) {
                    mAttachInfo.mHardwareRenderer.destroy();
                }

                final Rect insets = attrs.surfaceInsets;
                final booleanhasSurfaceInsets = insets.left ! =0|| insets.right ! =0|| insets.top ! =0|| insets.bottom ! =0;
                final booleantranslucent = attrs.format ! = PixelFormat.OPAQUE || hasSurfaceInsets;// 1. Create a hardware renderer class instance
                mAttachInfo.mHardwareRenderer = HardwareRenderer.create(mContext, translucent);
                if(mAttachInfo.mHardwareRenderer ! =null) {
                    // 2. Set the name of the hardware renderer
                    mAttachInfo.mHardwareRenderer.setName(attrs.getTitle().toString());
                    mAttachInfo.mHardwareAccelerated =
                            mAttachInfo.mHardwareAccelerationRequested = true;
                }
            }
        }
    }
    ......
}
Copy the code

Create a hardware renderer using OpenGL. HardwareRenderer is actually the ThreadedRenderer class.

frameworks/base/core/java/android/view/HardwareRenderer.java

public abstract class HardwareRenderer {.../ * * *@param3. Always True if the Surface is translucent, otherwise false *@returnA hardware renderer supported by OpenGL. * /
    static HardwareRenderer create(Context context, boolean translucent) {
        HardwareRenderer renderer = null;
        if (DisplayListCanvas.isAvailable()) {
            renderer = new ThreadedRenderer(context, translucent);
        }
        returnrenderer; }... }Copy the code

Hardware renderer, which will render proxy to render thread. Most calls are currently synchronous. The UI thread can block on the renderer thread, but the renderer thread must not block on the UI thread. ThreadedRenderer creates a RenderProxy instance. RenderProxy, in turn, creates and manages CanvasContext on RenderThread. CanvasContext is fully managed by the lifecycle of RenderProxy.

Note that while the EGL context and Surface are currently created and managed by the render thread, the goal is to move it into a shared structure that can be managed by two threads. EGLSurface creation and removal should be done on the UI thread, not on the render thread, to avoid Surface buffer allocation blocking the render thread.

The ThreadedRenderer constructor does the following:

  1. Gets the light and shadow properties specified by the theme
  2. Call nCreateRootRenderNode to create RenderNode
  3. Call nCreateProxy to create RenderProxy

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {...// Light attributes specified by the theme
    private final float mLightY;
    private final float mLightZ;
    private final float mLightRadius;
    private final int mAmbientShadowAlpha;
    private final intmSpotShadowAlpha; .private longmNativeProxy; .privateRenderNode mRootNode; . ThreadedRenderer(Context context,boolean translucent) {
        final TypedArray a = context.obtainStyledAttributes(null, R.styleable.Lighting, 0.0);
        mLightY = a.getDimension(R.styleable.Lighting_lightY, 0);
        mLightZ = a.getDimension(R.styleable.Lighting_lightZ, 0);
        mLightRadius = a.getDimension(R.styleable.Lighting_lightRadius, 0);
        mAmbientShadowAlpha =
                (int) (255 * a.getFloat(R.styleable.Lighting_ambientShadowAlpha, 0) + 0.5 f);
        mSpotShadowAlpha = (int) (255 * a.getFloat(R.styleable.Lighting_spotShadowAlpha, 0) + 0.5 f);
        a.recycle();

        long rootNodePtr = nCreateRootRenderNode();
        // Use the existing local render node.
        mRootNode = RenderNode.adopt(rootNodePtr);
        // Sets whether the render node should cut itself to its boundaries. This property is controlled by the superview of the view.
        mRootNode.setClipToBounds(false); mNativeProxy = nCreateProxy(translucent, rootNodePtr); ProcessInitializer.sInstance.init(context, mNativeProxy); loadSystemProperties(); }... }Copy the code

NCreateRootRenderNode is a JNI method whose Native counterpart is android_view_ThreadedRenderer_createRootRenderNode(…). Methods.

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {...private static native long nCreateRootRenderNode(a); . }Copy the code

New out the Native RootRenderNode object, then set the name to “RootRenderNode”, and finally force the RootRenderNode object pointer to jLong back to the Java layer.

frameworks/base/core/jni/android_view_ThreadedRenderer.cpp

namespace android {
......
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
    RootRenderNode* node = new RootRenderNode(env);
    node->incStrong(0);
    node->setName("RootRenderNode");
    return reinterpret_cast<jlong>(node); }...const char* const kClassPathName = "android/view/ThreadedRenderer";

static JNINativeMethod gMethods[] = {
    ......
    { "nCreateRootRenderNode"."()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
    ......
};

int register_android_view_ThreadedRenderer(JNIEnv* env) {
    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); }};Copy the code

What is a render node? Used to store recorded canvas commands and display properties for each View/ViewGroup. The recording of the canvas command is somewhat similar to that of SkPicture, except that the canvas recording function is in DisplayListCanvas (for managing records), DisplayListData, which is used to hold the actual data, and DisplayList, which is used to hold properties and perform playback on the renderer. Notice that when the canvas action flow of the view record is refreshed, the DisplayListData is swapped out below the single DisplayList. DisplayList (and its properties) remains attached.

NCreateProxy (…) Methods. It is a JNI function with a Native implementation android_view_ThreadedRenderer_createProxy(…). Methods.

frameworks/base/core/java/android/view/ThreadedRenderer.java

public class ThreadedRenderer extends HardwareRenderer {...private static native long nCreateProxy(boolean translucent, long rootRenderNode); . }Copy the code
  1. Force the rootRenderNodePtr jLong back to the RootRenderNode* pointer
  2. Creates a ContextFactoryImpl object that holds RootRenderNode inside
  3. Create RenderProxy to render the proxy object

frameworks/base/core/jni/android_view_ThreadedRenderer.cpp

namespace android {
......
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz, jboolean translucent, jlong rootRenderNodePtr) {
    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
    ContextFactoryImpl factory(rootRenderNode);
    return (jlong) new RenderProxy(translucent, rootRenderNode, &factory); }...const char* const kClassPathName = "android/view/ThreadedRenderer";

static JNINativeMethod gMethods[] = {
    ......
    { "nCreateProxy"."(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
    ......
};

int register_android_view_ThreadedRenderer(JNIEnv* env) {
    return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); }};Copy the code

RenderProxy is strictly single-threaded. All methods must be invoked on the owning thread.

  1. Call the RenderThread class getInstance() to get the RenderThread singleton and assign it to the RenderProxy member variable mRenderThread
  2. Expand the SETUP_TASK macro definition to get a pointer args to the createContextArgs structure that was defined at compile time after the CREATE_BRIDGE4 macro definition was expanded. The function Bridge_createContext is also implemented with the return statement as its body. This function is passed to the MethodInvokeRenderTask constructor as an input when the SETUP_TASK macro definition is expanded
  3. Assigns values to createContextArgs members always, rootRenderNode, Thread, and contextFactory
  4. MContext is a pointer member to CanvasContext* and postAndWait(…) is called. The function takes an argument to MethodInvokeRenderTask and returns a value assigned to mContext, which is actually a reference to the CanvasContext object newly generated at the end of the CREATE_BRIDGE4 macro. This function adds the render task to the render thread
  5. MDrawFrameTask is a DrawFrameTask member that represents the DrawFrameTask and calls its setContext(…). The RenderThread and CanvasContext* method sets the context.

frameworks/base/libs/hwui/renderthread/RenderProxy.cpp

namespace android {
namespace uirenderer {
namespace renderthread {
......
#define ARGS(method) method ## Args.#define CREATE_BRIDGE4(name, a1, a2, a3, a4) CREATE_BRIDGE(name, a1,a2,a3,a4,,,,).#define CREATE_BRIDGE(name, a1, a2, a3, a4, a5, a6, a7, a8) \
    typedef struct { \
        a1; a2; a3; a4; a5; a6; a7; a8; \
    } ARGS(name); \
    static void* Bridge_ ## name(ARGS(name)* args)
    
#define SETUP_TASK(method) \
    LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
        "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \
                METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
    MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
    ARGS(method) *args = (ARGS(method) *) task->payload().CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
        RenderNode* rootRenderNode, IContextFactory* contextFactory) {
    return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode, args->contextFactory); }... RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory)
        : mRenderThread(RenderThread::getInstance()),mContext(nullptr) {
    SETUP_TASK(createContext);
    args->translucent = translucent;
    args->rootRenderNode = rootRenderNode;
    args->thread = &mRenderThread;
    args->contextFactory = contextFactory;
    mContext = (CanvasContext*) postAndWait(task);
    mDrawFrameTask.setContext(&mRenderThread, mContext); }... }/* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
Copy the code

A singleton RenderThread object is obtained by calling the RenderThread class getInstance() method. In Singleton.h, the Singleton class is a template Singleton class, and the getInstance() method returns the corresponding TYPE* pointer. The RenderThread class inherits from Singleton so it can return a RenderThread* pointer.

system/core/include/utils/Singleton.h

template <typename TYPE>
class ANDROID_API Singleton
{
public:
    static TYPE& getInstance(a) {
        Mutex::Autolock _l(sLock);
        TYPE* instance = sInstance;
        if (instance == 0) {
            instance = new TYPE(a); sInstance = instance; }return *instance;
    }

    static bool hasInstance(a) {
        Mutex::Autolock _l(sLock);
        returnsInstance ! =0;
    }

protected:
    ~Singleton() {};Singleton() {};private:
    Singleton(const Singleton&);
    Singleton& operator = (const Singleton&);
    static Mutex sLock;
    static TYPE* sInstance;
};
Copy the code

GetInstance () calls the RenderThread class’s no-argument constructor.

  1. Initializes a series of member variables
  2. MFrameCallbackTask is assigned a DispatchFrameCallbacks object
  3. MLooper is assigned to the Looper object
  4. Start the RenderThread thread

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
        , mNextWakeup(LLONG_MAX)
        , mDisplayEventReceiver(nullptr),mVsyncRequested(false),mFrameCallbackTaskPending(false),mFrameCallbackTask(nullptr),mRenderState(nullptr),mEglManager(nullptr) {
    Properties::load(a); mFrameCallbackTask =new DispatchFrameCallbacks(this);
    mLooper = new Looper(false);
    run("RenderThread");
}
Copy the code

Once the thread is started, focus on the return value of the threadLoop() function.

Thread objects can be used in two ways:

Loop: If threadLoop() returns true, it will be called again if requestExit() has not been called. Once: If threadLoop() returns false, the thread will exit on return.

Set to false to run only once, but there is an “infinite” for loop inside the threadLoop() body, used in conjunction with Looper. This is where all the RenderTasks are handled.

frameworks/base/libs/hwui/renderthread/RenderThread.cpp

bool RenderThread::threadLoop(a) {
    setpriority(PRIO_PROCESS, 0, PRIORITY_DISPLAY);
    initThreadLocals(a);int timeoutMillis = - 1;
    for (;;) {
        int result = mLooper->pollOnce(timeoutMillis);
        LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
                "RenderThread Looper POLL_ERROR!");

        nsecs_t nextWakeup;
        // If we have a task, process our queue
        while (RenderTask* task = nextTask(&nextWakeup)) {
            Call the task run() method
            task->run(a);// The task itself may have been deleted, do not reference it again
        }
        if (nextWakeup == LLONG_MAX) {
            timeoutMillis = - 1;
        } else {
            // Calculate the next wake-up time
            nsecs_t timeoutNanos = nextWakeup - systemTime(SYSTEM_TIME_MONOTONIC);
            timeoutMillis = nanoseconds_to_milliseconds(timeoutNanos);
            if (timeoutMillis < 0) {
                timeoutMillis = 0; }}// Handle frame callback registration
        if (mPendingRegistrationFrameCallbacks.size() && !mFrameCallbackTaskPending) {
            drainDisplayEventQueue(a); mFrameCallbacks.insert(
                    mPendingRegistrationFrameCallbacks.begin(), mPendingRegistrationFrameCallbacks.end());
            mPendingRegistrationFrameCallbacks.clear(a);// Request vsync signal
            requestVsync(a); }if(! mFrameCallbackTaskPending && ! mVsyncRequested && mFrameCallbacks.size()) {
            // TODO: Clean this up. This is working around an issue where a combination
            // of bad timing and slow drawing can result in dropping a stale vsync
            // on the floor (correct!) but fails to schedule to listen for the
            // next vsync (oops), so none of the callbacks are run.
            // Request vsync signal
            requestVsync();
        }
    }

    return false;
}
Copy the code

Finally, summarize their class diagram relationship.