Start RenderThread earlier to preload Vulkan/EGL drivers

This CL should fix application startup regression for Vulkan
detected by "Hermetic Startup: EmptyActivity" test.
EGL drivers are loaded in a temp thread to leave more time
in RenderThread for other work. Loading EGL drivers
on the RenderThread may cause a perf regression.

Test: Ran cold-dropcache-test test.
Bug: 122659224
Bug: 123361175
Change-Id: I8ca818e98fac196a41d079be15594caca5cb1bab
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cc419b8..7908637 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3229,8 +3229,9 @@
             TAG, "Handling launch of " + r);
 
         // Initialize before creating the activity
-        if (!ThreadedRenderer.sRendererDisabled) {
-            GraphicsEnvironment.earlyInitEGL();
+        if (!ThreadedRenderer.sRendererDisabled
+                && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
+            HardwareRenderer.preload();
         }
         WindowManagerGlobal.initialize();
 
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index ec6da24..4a14ece 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -25,7 +25,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
-import android.opengl.EGL14;
 import android.provider.Settings;
 import android.util.Log;
 import android.widget.Toast;
@@ -677,25 +676,6 @@
         return true;
     }
 
-    /**
-     * Start a background thread to initialize EGL.
-     *
-     * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
-     * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
-     * its first frame adds directly to user-visible app launch latency. By starting it earlier
-     * on a separate thread, it can usually be finished well before the UI is ready to be drawn.
-     *
-     * Should only be called after chooseDriver().
-     */
-    public static void earlyInitEGL() {
-        final Thread eglInitThread = new Thread(
-                () -> {
-                    EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
-                },
-                "EGL Init");
-        eglInitThread.start();
-    }
-
     private static String chooseAbi(ApplicationInfo ai) {
         final String isa = VMRuntime.getCurrentInstructionSet();
         if (ai.primaryCpuAbi != null &&
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 2aa5cb4..ecc2dd0 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -1029,6 +1029,10 @@
     proxy->setForceDark(enable);
 }
 
+static void android_view_ThreadedRenderer_preload(JNIEnv*, jclass) {
+    RenderProxy::preload();
+}
+
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
 // ----------------------------------------------------------------------------
@@ -1144,6 +1148,7 @@
     { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
     { "nAllocateBuffers", "(J)V", (void*)android_view_ThreadedRenderer_allocateBuffers },
     { "nSetForceDark", "(JZ)V", (void*)android_view_ThreadedRenderer_setForceDark },
+    { "preload", "()V", (void*)android_view_ThreadedRenderer_preload },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 99d8c1b..b020556 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -1027,6 +1027,18 @@
      */
     public static native void disableVsync();
 
+    /**
+     * Start render thread and initialize EGL or Vulkan.
+     *
+     * Initializing EGL involves loading and initializing the graphics driver. Some drivers take
+     * several 10s of milliseconds to do this, so doing it on-demand when an app tries to render
+     * its first frame adds directly to user-visible app launch latency.
+     *
+     * Should only be called after GraphicsEnvironment.chooseDriver().
+     * @hide
+     */
+    public static native void preload();
+
     /** @hide */
     protected static native void setupShadersDiskCache(String cacheFile, String skiaCacheFile);
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 720c603..34f76d9 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -381,6 +381,14 @@
     });
 }
 
+void RenderProxy::preload() {
+    // Create RenderThread object and start the thread. Then preload Vulkan/EGL driver.
+    auto& thread = RenderThread::getInstance();
+    thread.queue().post([&thread]() {
+        thread.preload();
+    });
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6e1bfd7..a1a5551 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -131,6 +131,8 @@
 
     ANDROID_API static void disableVsync();
 
+    ANDROID_API static void preload();
+
     static void repackVectorDrawableAtlas();
 
     static void releaseVDAtlasEntries();
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index bfae80f..08edd20 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -41,6 +41,7 @@
 #include <utils/Condition.h>
 #include <utils/Log.h>
 #include <utils/Mutex.h>
+#include <thread>
 
 namespace android {
 namespace uirenderer {
@@ -175,9 +176,6 @@
     mRenderState = new RenderState(*this);
     mVkManager = new VulkanManager();
     mCacheManager = new CacheManager(mDisplayInfo);
-    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
-        requireVkContext();
-    }
 }
 
 void RenderThread::requireGlContext() {
@@ -409,6 +407,17 @@
     return gettid() == getInstance().getTid();
 }
 
+void RenderThread::preload() {
+    std::thread eglInitThread([]() {
+        //TODO: don't load EGL drivers for Vulkan, when HW bitmap uploader is refactored.
+        eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    });
+    eglInitThread.detach();
+    if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
+        requireVkContext();
+    }
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 419e7c7..329b4b9 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -115,6 +115,8 @@
     void requireVkContext();
     void destroyRenderingContext();
 
+    void preload();
+
     /**
      * isCurrent provides a way to query, if the caller is running on
      * the render thread.
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 1e685ab..3b43f12 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -360,7 +360,7 @@
     }
 }
 
-sk_sp<GrContext> VulkanManager::createContext(GrContextOptions options) {
+sk_sp<GrContext> VulkanManager::createContext(const GrContextOptions& options) {
     auto getProc = [] (const char* proc_name, VkInstance instance, VkDevice device) {
         if (device != VK_NULL_HANDLE) {
             return vkGetDeviceProcAddr(device, proc_name);
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9763686..95c9630 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -150,7 +150,7 @@
     // Returned pointers are owned by VulkanManager.
     VkFunctorInitParams getVkFunctorInitParams() const;
 
-    sk_sp<GrContext> createContext(GrContextOptions options);
+    sk_sp<GrContext> createContext(const GrContextOptions& options);
 
 private:
     // Sets up the VkInstance and VkDevice objects. Also fills out the passed in