diff options
| author | 2020-07-23 13:47:49 -0700 | |
|---|---|---|
| committer | 2020-07-30 08:54:03 -0700 | |
| commit | b36bfddb1e72076c43f849f1366b44086b530a9a (patch) | |
| tree | efccb52cce63310b8c2af544f86bdd475e2bf1fd | |
| parent | 088b06b649d2f7bd15842b26d5702d83e03986c8 (diff) | |
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
| -rw-r--r-- | core/java/android/view/Surface.java | 6 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 30 | ||||
| -rw-r--r-- | graphics/java/android/graphics/HardwareRenderer.java | 31 | ||||
| -rw-r--r-- | libs/hwui/ColorMode.h | 34 | ||||
| -rw-r--r-- | libs/hwui/jni/android_graphics_HardwareRenderer.cpp | 13 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.cpp | 26 | ||||
| -rw-r--r-- | libs/hwui/pipeline/skia/SkiaPipeline.h | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 11 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.h | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/EglManager.cpp | 105 | ||||
| -rw-r--r-- | libs/hwui/renderthread/EglManager.h | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/IRenderPipeline.h | 11 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.cpp | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/RenderProxy.h | 3 | ||||
| -rw-r--r-- | libs/hwui/utils/Color.cpp | 22 | ||||
| -rw-r--r-- | libs/hwui/utils/Color.h | 1 |
17 files changed, 211 insertions, 100 deletions
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index ecf97749d349..8cb8e1d12d8a 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -22,6 +22,7 @@ import android.annotation.FloatRange; 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 @@ public class Surface implements Parcelable { 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 6552e30dde69..2a2d1e6c4c77 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1329,13 +1329,9 @@ public final class ViewRootImpl implements ViewParent, 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 @@ public final class ViewRootImpl implements ViewParent, & 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 @@ public final class ViewRootImpl implements ViewParent, // 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 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mThreadedRenderer.destroy(); } } else if ((surfaceReplaced - || surfaceSizeChanged || windowRelayoutWasForced || colorModeChanged) + || surfaceSizeChanged || windowRelayoutWasForced) && mSurfaceHolder == null && mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) { @@ -4557,18 +4549,16 @@ public final class ViewRootImpl implements ViewParent, } } - private boolean hasColorModeChanged(int colorMode) { + private void updateColorModeIfNeeded(int colorMode) { if (mAttachInfo.mThreadedRenderer == null) { - return false; - } - final boolean isWideGamut = colorMode == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT; - if (mAttachInfo.mThreadedRenderer.isWideGamut() == isWideGamut) { - return false; + return; } - if (isWideGamut && !mContext.getResources().getConfiguration().isScreenWideColorGamut()) { - 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; } - return true; + mAttachInfo.mThreadedRenderer.setColorMode(colorMode); } @Override diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index 1da434496297..b3103fd516dd 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; 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 @@ public class HardwareRenderer { 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 @@ public class HardwareRenderer { 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 @@ public class HardwareRenderer { } /** - * 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 @@ public class HardwareRenderer { 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 @@ public class HardwareRenderer { 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 @@ public class HardwareRenderer { 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 000000000000..6d387f9ef43d --- /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 42743db3061c..7d6875f59d17 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_createRootRenderNode(JNIEnv* env, job } 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 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, 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 @@ static const JNINativeMethod gMethods[] = { (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 @@ static const JNINativeMethod gMethods[] = { {"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 24a6228242a5..389fe7eed7c7 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -87,6 +87,8 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con // 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 89a1c713ef62..6dd36981e8aa 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::dumpResourceCacheUsage() const { 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 8341164edc19..100bfb6b159a 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -50,7 +50,7 @@ public: 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 @@ protected: 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 a362bd220936..13d544c68e95 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -174,7 +174,10 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { } else { mNativeSurface = nullptr; } + setupPipelineSurface(); +} +void CanvasContext::setupPipelineSurface() { bool hasSurface = mRenderPipeline->setSurface( mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior); @@ -184,7 +187,7 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { 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 @@ void CanvasContext::setOpaque(bool opaque) { 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 0306eeccc5d4..cba710f01063 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 @@ public: 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 @@ private: 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 c7013531c07f..2a8aa8c3e5b7 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -76,6 +76,7 @@ static struct { 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 @@ static struct { 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 @@ void EglManager::initialize() { } 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 @@ EGLConfig EglManager::load8BitsConfig(EGLDisplay display, EglManager::SwapBehavi 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 @@ void EglManager::initExtensions() { 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 @@ void EglManager::loadConfigs() { 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 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, 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 @@ Result<EGLSurface, EGLint> EglManager::createSurface(EGLNativeWindowType window, // 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 f67fb31db951..69f3ed014c53 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -88,6 +88,7 @@ private: 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 @@ private: 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 c3c22869a42f..a04738d6a6f0 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 @@ namespace renderthread { 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 b764f74bf116..aad0cca80cdc 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -109,8 +109,8 @@ void RenderProxy::setOpaque(bool opaque) { 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 16eabadc064c..33dabc9895b1 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 @@ public: 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 ca1bf690cfd0..eff34a83af1b 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -344,5 +344,27 @@ SkColor LabToSRGB(const Lab& lab, SkAlpha alpha) { 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 71ed68371a9a..1654072fd264 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -126,6 +126,7 @@ struct Lab { Lab sRGBToLab(SkColor color); SkColor LabToSRGB(const Lab& lab, SkAlpha alpha); +skcms_TransferFunction GetPQSkTransferFunction(float sdr_white_level = 0.f); } /* namespace uirenderer */ } /* namespace android */ |