Wire-up colorMode="hdr"

Fow now it uses a fixed white point of 150nits
TBD if this is disabled or adjusted

Test: Demo app
Change-Id: Iac13597b3d7633fdef3feaf7ec1da0c27c87904c
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index ecf9774..8cb8e1d 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -22,6 +22,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.pm.ActivityInfo;
 import android.content.res.CompatibilityInfo.Translator;
 import android.graphics.Canvas;
 import android.graphics.ColorSpace;
@@ -1001,7 +1002,10 @@
             mHardwareRenderer = new HardwareRenderer();
             mHardwareRenderer.setContentRoot(mRenderNode);
             mHardwareRenderer.setSurface(Surface.this, true);
-            mHardwareRenderer.setWideGamut(isWideColorGamut);
+            mHardwareRenderer.setColorMode(
+                    isWideColorGamut
+                            ? ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
+                            : ActivityInfo.COLOR_MODE_DEFAULT);
             mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f);
             mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f);
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 6552e30..2a2d1e6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1329,13 +1329,9 @@
                 final boolean hasSurfaceInsets = insets.left != 0 || insets.right != 0
                         || insets.top != 0 || insets.bottom != 0;
                 final boolean translucent = attrs.format != PixelFormat.OPAQUE || hasSurfaceInsets;
-                final boolean wideGamut =
-                        mContext.getResources().getConfiguration().isScreenWideColorGamut()
-                        && attrs.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT;
-
                 mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
                         attrs.getTitle().toString());
-                mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut);
+                updateColorModeIfNeeded(attrs.getColorMode());
                 updateForceDarkMode();
                 if (mAttachInfo.mThreadedRenderer != null) {
                     mAttachInfo.mHardwareAccelerated =
@@ -2648,7 +2644,7 @@
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_RESIZED) != 0;
                 final boolean alwaysConsumeSystemBarsChanged =
                         mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
-                final boolean colorModeChanged = hasColorModeChanged(lp.getColorMode());
+                updateColorModeIfNeeded(lp.getColorMode());
                 surfaceCreated = !hadSurface && mSurface.isValid();
                 surfaceDestroyed = hadSurface && !mSurface.isValid();
                 surfaceReplaced = (surfaceGenerationId != mSurface.getGenerationId())
@@ -2677,10 +2673,6 @@
                     // hierarchy is measured below.
                     dispatchApplyInsets = true;
                 }
-                if (colorModeChanged && mAttachInfo.mThreadedRenderer != null) {
-                    mAttachInfo.mThreadedRenderer.setWideGamut(
-                            lp.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
-                }
 
                 if (surfaceCreated) {
                     // If we are creating a new surface, then we need to
@@ -2725,7 +2717,7 @@
                         mAttachInfo.mThreadedRenderer.destroy();
                     }
                 } else if ((surfaceReplaced
-                        || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged)
+                        || surfaceSizeChanged || windowRelayoutWasForced)
                         && mSurfaceHolder == null
                         && mAttachInfo.mThreadedRenderer != null
                         && mSurface.isValid()) {
@@ -4557,18 +4549,16 @@
         }
     }
 
-    private boolean hasColorModeChanged(int colorMode) {
+    private void updateColorModeIfNeeded(int colorMode) {
         if (mAttachInfo.mThreadedRenderer == null) {
-            return false;
+            return;
         }
-        final boolean isWideGamut = colorMode == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT;
-        if (mAttachInfo.mThreadedRenderer.isWideGamut() == isWideGamut) {
-            return false;
+        // TODO: Centralize this sanitization? Why do we let setting bad modes?
+        // Alternatively, can we just let HWUI figure it out? Do we need to care here?
+        if (!mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
+            colorMode = ActivityInfo.COLOR_MODE_DEFAULT;
         }
-        if (isWideGamut && !mContext.getResources().getConfiguration().isScreenWideColorGamut()) {
-            return false;
-        }
-        return true;
+        mAttachInfo.mThreadedRenderer.setColorMode(colorMode);
     }
 
     @Override
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 1da4344..b3103fd5 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -23,6 +23,7 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
@@ -157,7 +158,7 @@
     protected RenderNode mRootNode;
     private boolean mOpaque = true;
     private boolean mForceDark = false;
-    private boolean mIsWideGamut = false;
+    private @ActivityInfo.ColorMode int mColorMode = ActivityInfo.COLOR_MODE_DEFAULT;
 
     /**
      * Creates a new instance of a HardwareRenderer. The HardwareRenderer will default
@@ -167,7 +168,7 @@
         ProcessInitializer.sInstance.initDisplayInfo();
         mRootNode = RenderNode.adopt(nCreateRootRenderNode());
         mRootNode.setClipToBounds(false);
-        mNativeProxy = nCreateProxy(!mOpaque, mIsWideGamut, mRootNode.mNativeRenderNode);
+        mNativeProxy = nCreateProxy(!mOpaque, mRootNode.mNativeRenderNode);
         if (mNativeProxy == 0) {
             throw new OutOfMemoryError("Unable to create hardware renderer");
         }
@@ -619,17 +620,17 @@
     }
 
     /**
-     * Enable/disable wide gamut rendering on this renderer. Whether or not the actual rendering
-     * will be wide gamut depends on the hardware support for such rendering.
+     * Sets the desired color mode on this renderer. Whether or not the actual rendering
+     * will use the requested colorMode depends on the hardware support for such rendering.
      *
-     * @param wideGamut true if this renderer should render in wide gamut, false if it should
-     *                  render in sRGB
-     *                  TODO: Figure out color...
+     * @param colorMode The @{@link ActivityInfo.ColorMode} to request
      * @hide
      */
-    public void setWideGamut(boolean wideGamut) {
-        mIsWideGamut = wideGamut;
-        nSetWideGamut(mNativeProxy, wideGamut);
+    public void setColorMode(@ActivityInfo.ColorMode int colorMode) {
+        if (mColorMode != colorMode) {
+            mColorMode = colorMode;
+            nSetColorMode(mNativeProxy, colorMode);
+        }
     }
 
     /**
@@ -804,11 +805,6 @@
         nSetPictureCaptureCallback(mNativeProxy, callback);
     }
 
-    /** @hide */
-    public boolean isWideGamut() {
-        return mIsWideGamut;
-    }
-
     /** called by native */
     static void invokePictureCapturedCallback(long picturePtr, PictureCapturedCallback callback) {
         Picture picture = new Picture(picturePtr);
@@ -1207,8 +1203,7 @@
 
     private static native long nCreateRootRenderNode();
 
-    private static native long nCreateProxy(boolean translucent, boolean isWideGamut,
-            long rootRenderNode);
+    private static native long nCreateProxy(boolean translucent, long rootRenderNode);
 
     private static native void nDeleteProxy(long nativeProxy);
 
@@ -1230,7 +1225,7 @@
 
     private static native void nSetOpaque(long nativeProxy, boolean opaque);
 
-    private static native void nSetWideGamut(long nativeProxy, boolean wideGamut);
+    private static native void nSetColorMode(long nativeProxy, int colorMode);
 
     private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
 
diff --git a/libs/hwui/ColorMode.h b/libs/hwui/ColorMode.h
new file mode 100644
index 0000000..6d387f9
--- /dev/null
+++ b/libs/hwui/ColorMode.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::uirenderer {
+
+// Must match the constants in ActivityInfo.java
+enum class ColorMode {
+    // SRGB means HWUI will produce buffer in SRGB color space.
+    Default = 0,
+    // WideColorGamut selects the most optimal colorspace & format for the device's display
+    // Most commonly DisplayP3 + RGBA_8888 currently.
+    WideColorGamut = 1,
+    // HDR Rec2020 + F16
+    Hdr = 2,
+    // HDR Rec2020 + 1010102
+    Hdr10 = 3,
+};
+
+} // namespace android::uirenderer
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 42743db..7d6875f 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -143,11 +143,10 @@
 }
 
 static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
-        jboolean translucent, jboolean isWideGamut, jlong rootRenderNodePtr) {
+        jboolean translucent, jlong rootRenderNodePtr) {
     RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootRenderNodePtr);
     ContextFactoryImpl factory(rootRenderNode);
     RenderProxy* proxy = new RenderProxy(translucent, rootRenderNode, &factory);
-    proxy->setWideGamut(isWideGamut);
     return (jlong) proxy;
 }
 
@@ -218,10 +217,10 @@
     proxy->setOpaque(opaque);
 }
 
-static void android_view_ThreadedRenderer_setWideGamut(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jboolean wideGamut) {
+static void android_view_ThreadedRenderer_setColorMode(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint colorMode) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->setWideGamut(wideGamut);
+    proxy->setColorMode(static_cast<ColorMode>(colorMode));
 }
 
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
@@ -659,7 +658,7 @@
          (void*)android_view_ThreadedRenderer_setProcessStatsBuffer},
         {"nGetRenderThreadTid", "(J)I", (void*)android_view_ThreadedRenderer_getRenderThreadTid},
         {"nCreateRootRenderNode", "()J", (void*)android_view_ThreadedRenderer_createRootRenderNode},
-        {"nCreateProxy", "(ZZJ)J", (void*)android_view_ThreadedRenderer_createProxy},
+        {"nCreateProxy", "(ZJ)J", (void*)android_view_ThreadedRenderer_createProxy},
         {"nDeleteProxy", "(J)V", (void*)android_view_ThreadedRenderer_deleteProxy},
         {"nLoadSystemProperties", "(J)Z",
          (void*)android_view_ThreadedRenderer_loadSystemProperties},
@@ -671,7 +670,7 @@
         {"nSetLightAlpha", "(JFF)V", (void*)android_view_ThreadedRenderer_setLightAlpha},
         {"nSetLightGeometry", "(JFFFF)V", (void*)android_view_ThreadedRenderer_setLightGeometry},
         {"nSetOpaque", "(JZ)V", (void*)android_view_ThreadedRenderer_setOpaque},
-        {"nSetWideGamut", "(JZ)V", (void*)android_view_ThreadedRenderer_setWideGamut},
+        {"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
         {"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
         {"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
         {"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 24a6228..389fe7e 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -87,6 +87,8 @@
         // Note: The default preference of pixel format is RGBA_8888, when other
         // pixel format is available, we should branch out and do more check.
         fboInfo.fFormat = GL_RGBA8;
+    } else if (colorType == kRGBA_1010102_SkColorType) {
+        fboInfo.fFormat = GL_RGB10_A2;
     } else {
         LOG_ALWAYS_FATAL("Unsupported color type.");
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 89a1c71..6dd3698 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -35,6 +35,7 @@
 #include "VectorDrawable.h"
 #include "thread/CommonPool.h"
 #include "tools/SkSharingProc.h"
+#include "utils/Color.h"
 #include "utils/String8.h"
 #include "utils/TraceUtils.h"
 
@@ -587,14 +588,23 @@
 
 void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) {
     mColorMode = colorMode;
-    if (colorMode == ColorMode::SRGB) {
-        mSurfaceColorType = SkColorType::kN32_SkColorType;
-        mSurfaceColorSpace = SkColorSpace::MakeSRGB();
-    } else if (colorMode == ColorMode::WideColorGamut) {
-        mSurfaceColorType = DeviceInfo::get()->getWideColorType();
-        mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
-    } else {
-        LOG_ALWAYS_FATAL("Unreachable: unsupported color mode.");
+    switch (colorMode) {
+        case ColorMode::Default:
+            mSurfaceColorType = SkColorType::kN32_SkColorType;
+            mSurfaceColorSpace = SkColorSpace::MakeSRGB();
+            break;
+        case ColorMode::WideColorGamut:
+            mSurfaceColorType = DeviceInfo::get()->getWideColorType();
+            mSurfaceColorSpace = DeviceInfo::get()->getWideColorSpace();
+            break;
+        case ColorMode::Hdr:
+            mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+            mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
+            break;
+        case ColorMode::Hdr10:
+            mSurfaceColorType = SkColorType::kRGBA_1010102_SkColorType;
+            mSurfaceColorSpace = SkColorSpace::MakeRGB(GetPQSkTransferFunction(), SkNamedGamut::kRec2020);
+            break;
     }
 }
 
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 8341164..100bfb6 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -50,7 +50,7 @@
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
                              ErrorHandler* errorHandler) override;
 
-    void setSurfaceColorProperties(renderthread::ColorMode colorMode) override;
+    void setSurfaceColorProperties(ColorMode colorMode) override;
     SkColorType getSurfaceColorType() const override { return mSurfaceColorType; }
     sk_sp<SkColorSpace> getSurfaceColorSpace() override { return mSurfaceColorSpace; }
 
@@ -76,7 +76,7 @@
 
     renderthread::RenderThread& mRenderThread;
 
-    renderthread::ColorMode mColorMode = renderthread::ColorMode::SRGB;
+    ColorMode mColorMode = ColorMode::Default;
     SkColorType mSurfaceColorType;
     sk_sp<SkColorSpace> mSurfaceColorSpace;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index a362bd2..13d544c 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -174,7 +174,10 @@
     } else {
         mNativeSurface = nullptr;
     }
+    setupPipelineSurface();
+}
 
+void CanvasContext::setupPipelineSurface() {
     bool hasSurface = mRenderPipeline->setSurface(
             mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior);
 
@@ -184,7 +187,7 @@
 
     mFrameNumber = -1;
 
-    if (window != nullptr && hasSurface) {
+    if (mNativeSurface != nullptr && hasSurface) {
         mHaveNewSurface = true;
         mSwapHistory.clear();
         // Enable frame stats after the surface has been bound to the appropriate graphics API.
@@ -239,9 +242,9 @@
     mOpaque = opaque;
 }
 
-void CanvasContext::setWideGamut(bool wideGamut) {
-    ColorMode colorMode = wideGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
-    mRenderPipeline->setSurfaceColorProperties(colorMode);
+void CanvasContext::setColorMode(ColorMode mode) {
+    mRenderPipeline->setSurfaceColorProperties(mode);
+    setupPipelineSurface();
 }
 
 bool CanvasContext::makeCurrent() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 0306eec..cba710f 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -30,6 +30,7 @@
 #include "renderthread/RenderTask.h"
 #include "renderthread/RenderThread.h"
 #include "utils/RingBuffer.h"
+#include "ColorMode.h"
 
 #include <SkBitmap.h>
 #include <SkRect.h>
@@ -119,7 +120,7 @@
     void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightGeometry(const Vector3& lightCenter, float lightRadius);
     void setOpaque(bool opaque);
-    void setWideGamut(bool wideGamut);
+    void setColorMode(ColorMode mode);
     bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
     void draw();
@@ -211,6 +212,7 @@
     bool isSwapChainStuffed();
     bool surfaceRequiresRedraw();
     void setPresentTime();
+    void setupPipelineSurface();
 
     SkRect computeDirtyRect(const Frame& frame, SkRect* dirty);
 
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c701353..2a8aa8c 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -76,6 +76,7 @@
     bool glColorSpace = false;
     bool scRGB = false;
     bool displayP3 = false;
+    bool hdr = false;
     bool contextPriority = false;
     bool surfacelessContext = false;
     bool nativeFenceSync = false;
@@ -86,7 +87,8 @@
 EglManager::EglManager()
         : mEglDisplay(EGL_NO_DISPLAY)
         , mEglConfig(nullptr)
-        , mEglConfigWideGamut(nullptr)
+        , mEglConfigF16(nullptr)
+        , mEglConfig1010102(nullptr)
         , mEglContext(EGL_NO_CONTEXT)
         , mPBufferSurface(EGL_NO_SURFACE)
         , mCurrentSurface(EGL_NO_SURFACE)
@@ -143,8 +145,7 @@
     } else {
         LOG_ALWAYS_FATAL("Unsupported wide color space.");
     }
-    mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension &&
-                                mEglConfigWideGamut != EGL_NO_CONFIG_KHR;
+    mHasWideColorGamutSupport = EglExtensions.glColorSpace && hasWideColorSpaceExtension;
 }
 
 EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavior swapBehavior) {
@@ -177,6 +178,35 @@
     return config;
 }
 
+EGLConfig EglManager::load1010102Config(EGLDisplay display, SwapBehavior swapBehavior) {
+    EGLint eglSwapBehavior =
+            (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    // If we reached this point, we have a valid swap behavior
+    EGLint attribs[] = {EGL_RENDERABLE_TYPE,
+                        EGL_OPENGL_ES2_BIT,
+                        EGL_RED_SIZE,
+                        10,
+                        EGL_GREEN_SIZE,
+                        10,
+                        EGL_BLUE_SIZE,
+                        10,
+                        EGL_ALPHA_SIZE,
+                        2,
+                        EGL_DEPTH_SIZE,
+                        0,
+                        EGL_STENCIL_SIZE,
+                        STENCIL_BUFFER_SIZE,
+                        EGL_SURFACE_TYPE,
+                        EGL_WINDOW_BIT | eglSwapBehavior,
+                        EGL_NONE};
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EGLint numConfigs = 1;
+    if (!eglChooseConfig(display, attribs, &config, numConfigs, &numConfigs) || numConfigs != 1) {
+        return EGL_NO_CONFIG_KHR;
+    }
+    return config;
+}
+
 EGLConfig EglManager::loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior) {
     EGLint eglSwapBehavior =
             (swapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
@@ -230,6 +260,7 @@
     EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float");
     EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
     EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
+    EglExtensions.hdr = extensions.has("EGL_EXT_gl_colorspace_bt2020_pq");
     EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
     EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
     EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
@@ -260,18 +291,20 @@
             LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
         }
     }
-    SkColorType wideColorType = DeviceInfo::get()->getWideColorType();
 
     // When we reach this point, we have a valid swap behavior
-    if (wideColorType == SkColorType::kRGBA_F16_SkColorType && EglExtensions.pixelFormatFloat) {
-        mEglConfigWideGamut = loadFP16Config(mEglDisplay, mSwapBehavior);
-        if (mEglConfigWideGamut == EGL_NO_CONFIG_KHR) {
+    if (EglExtensions.pixelFormatFloat) {
+        mEglConfigF16 = loadFP16Config(mEglDisplay, mSwapBehavior);
+        if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
             ALOGE("Device claims wide gamut support, cannot find matching config, error = %s",
                   eglErrorString());
             EglExtensions.pixelFormatFloat = false;
         }
-    } else if (wideColorType == SkColorType::kN32_SkColorType) {
-        mEglConfigWideGamut = load8BitsConfig(mEglDisplay, mSwapBehavior);
+    }
+    mEglConfig1010102 = load1010102Config(mEglDisplay, mSwapBehavior);
+    if (mEglConfig1010102 == EGL_NO_CONFIG_KHR) {
+        ALOGW("Failed to initialize 101010-2 format, error = %s",
+              eglErrorString());
     }
 }
 
@@ -311,8 +344,9 @@
                                                      sk_sp<SkColorSpace> colorSpace) {
     LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized");
 
-    bool wideColorGamut = colorMode == ColorMode::WideColorGamut && mHasWideColorGamutSupport &&
-                          EglExtensions.noConfigContext;
+    if (!mHasWideColorGamutSupport || !EglExtensions.noConfigContext) {
+        colorMode = ColorMode::Default;
+    }
 
     // The color space we want to use depends on whether linear blending is turned
     // on and whether the app has requested wide color gamut rendering. When wide
@@ -338,26 +372,47 @@
     // list is considered empty if the first entry is EGL_NONE
     EGLint attribs[] = {EGL_NONE, EGL_NONE, EGL_NONE};
 
+    EGLConfig config = mEglConfig;
+    if (DeviceInfo::get()->getWideColorType() == kRGBA_F16_SkColorType) {
+        if (mEglConfigF16 == EGL_NO_CONFIG_KHR) {
+            colorMode = ColorMode::Default;
+        } else {
+            config = mEglConfigF16;
+        }
+    }
     if (EglExtensions.glColorSpace) {
         attribs[0] = EGL_GL_COLORSPACE_KHR;
-        if (wideColorGamut) {
-            skcms_Matrix3x3 colorGamut;
-            LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
-                                "Could not get gamut matrix from color space");
-            if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
-                attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
-            } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
-                attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
-            } else {
-                LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+        switch (colorMode) {
+            case ColorMode::Default:
+                attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+                break;
+            case ColorMode::WideColorGamut: {
+                skcms_Matrix3x3 colorGamut;
+                LOG_ALWAYS_FATAL_IF(!colorSpace->toXYZD50(&colorGamut),
+                                    "Could not get gamut matrix from color space");
+                if (memcmp(&colorGamut, &SkNamedGamut::kDisplayP3, sizeof(colorGamut)) == 0) {
+                    attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
+                } else if (memcmp(&colorGamut, &SkNamedGamut::kSRGB, sizeof(colorGamut)) == 0) {
+                    attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT;
+                } else if (memcmp(&colorGamut, &SkNamedGamut::kRec2020, sizeof(colorGamut)) == 0) {
+                    attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                } else {
+                    LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
+                }
+                break;
             }
-        } else {
-            attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
+            case ColorMode::Hdr:
+                config = mEglConfigF16;
+                attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                break;
+            case ColorMode::Hdr10:
+                config = mEglConfig1010102;
+                attribs[1] = EGL_GL_COLORSPACE_BT2020_PQ_EXT;
+                break;
         }
     }
 
-    EGLSurface surface = eglCreateWindowSurface(
-            mEglDisplay, wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);
+    EGLSurface surface = eglCreateWindowSurface(mEglDisplay, config, window, attribs);
     if (surface == EGL_NO_SURFACE) {
         return Error<EGLint>{eglGetError()};
     }
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index f67fb31..69f3ed0 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -88,6 +88,7 @@
 
     static EGLConfig load8BitsConfig(EGLDisplay display, SwapBehavior swapBehavior);
     static EGLConfig loadFP16Config(EGLDisplay display, SwapBehavior swapBehavior);
+    static EGLConfig load1010102Config(EGLDisplay display, SwapBehavior swapBehavior);
 
     void initExtensions();
     void createPBufferSurface();
@@ -97,7 +98,8 @@
 
     EGLDisplay mEglDisplay;
     EGLConfig mEglConfig;
-    EGLConfig mEglConfigWideGamut;
+    EGLConfig mEglConfigF16;
+    EGLConfig mEglConfig1010102;
     EGLContext mEglContext;
     EGLSurface mPBufferSurface;
     EGLSurface mCurrentSurface;
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index c3c2286..a04738d 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -22,6 +22,7 @@
 #include "Lighting.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
+#include "ColorMode.h"
 
 #include <SkRect.h>
 #include <utils/RefBase.h>
@@ -42,16 +43,6 @@
 
 enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded };
 
-enum class ColorMode {
-    // SRGB means HWUI will produce buffer in SRGB color space.
-    SRGB,
-    // WideColorGamut means HWUI would support rendering scRGB non-linear into
-    // a signed buffer with enough range to support the wide color gamut of the
-    // display.
-    WideColorGamut,
-    // Hdr
-};
-
 class Frame;
 
 class IRenderPipeline {
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index b764f74b..aad0cca 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -109,8 +109,8 @@
     mRenderThread.queue().post([=]() { mContext->setOpaque(opaque); });
 }
 
-void RenderProxy::setWideGamut(bool wideGamut) {
-    mRenderThread.queue().post([=]() { mContext->setWideGamut(wideGamut); });
+void RenderProxy::setColorMode(ColorMode mode) {
+    mRenderThread.queue().post([=]() { mContext->setColorMode(mode); });
 }
 
 int64_t* RenderProxy::frameInfo() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 16eabad..33dabc9 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -24,6 +24,7 @@
 
 #include "../FrameMetricsObserver.h"
 #include "../IContextFactory.h"
+#include "ColorMode.h"
 #include "DrawFrameTask.h"
 #include "SwapBehavior.h"
 #include "hwui/Bitmap.h"
@@ -77,7 +78,7 @@
     void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightGeometry(const Vector3& lightCenter, float lightRadius);
     void setOpaque(bool opaque);
-    void setWideGamut(bool wideGamut);
+    void setColorMode(ColorMode mode);
     int64_t* frameInfo();
     int syncAndDrawFrame();
     void destroy();
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index ca1bf69..eff34a8 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -344,5 +344,27 @@
             static_cast<uint8_t>(rgb.b * 255));
 }
 
+// Note that SkColorSpace doesn't have the notion of an unspecified SDR white
+// level.
+static constexpr float kDefaultSDRWhiteLevel = 150.f;
+
+skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level) {
+    if (sdr_white_level <= 0.f) {
+        sdr_white_level = kDefaultSDRWhiteLevel;
+    }
+    // The generic PQ transfer function produces normalized luminance values i.e.
+    // the range 0-1 represents 0-10000 nits for the reference display, but we
+    // want to map 1.0 to |sdr_white_level| nits so we need to scale accordingly.
+    const double w = 10000. / sdr_white_level;
+    // Distribute scaling factor W by scaling A and B with X ^ (1/F):
+    // ((A + Bx^C) / (D + Ex^C))^F * W = ((A + Bx^C) / (D + Ex^C) * W^(1/F))^F
+    // See https://crbug.com/1058580#c32 for discussion.
+    skcms_TransferFunction fn = SkNamedTransferFn::kPQ;
+    const double ws = pow(w, 1. / fn.f);
+    fn.a = ws * fn.a;
+    fn.b = ws * fn.b;
+    return fn;
+}
+
 }  // namespace uirenderer
 }  // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 71ed683..1654072 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -126,6 +126,7 @@
 
 Lab sRGBToLab(SkColor color);
 SkColor LabToSRGB(const Lab& lab, SkAlpha alpha);
+skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level = 0.f);
 
 } /* namespace uirenderer */
 } /* namespace android */