Merge "Enable wide color gamut rendering"
This commit is contained in:
committed by
Android (Google) Code Review
commit
0ecc0ff5e0
@@ -33,17 +33,24 @@
|
||||
#include <ui/DisplayInfo.h>
|
||||
#include <ui/PixelFormat.h>
|
||||
|
||||
#include <system/graphics.h>
|
||||
|
||||
// TODO: Fix Skia.
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#include <SkImageEncoder.h>
|
||||
#include <SkData.h>
|
||||
#include <SkColorSpace.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
using namespace android;
|
||||
|
||||
static uint32_t DEFAULT_DISPLAY_ID = ISurfaceComposer::eDisplayIdMain;
|
||||
|
||||
#define COLORSPACE_UNKNOWN 0
|
||||
#define COLORSPACE_SRGB 1
|
||||
#define COLORSPACE_DISPLAY_P3 2
|
||||
|
||||
static void usage(const char* pname)
|
||||
{
|
||||
fprintf(stderr,
|
||||
@@ -67,6 +74,31 @@ static SkColorType flinger2skia(PixelFormat f)
|
||||
}
|
||||
}
|
||||
|
||||
static sk_sp<SkColorSpace> dataSpaceToColorSpace(android_dataspace d)
|
||||
{
|
||||
switch (d) {
|
||||
case HAL_DATASPACE_V0_SRGB:
|
||||
return SkColorSpace::MakeSRGB();
|
||||
case HAL_DATASPACE_DISPLAY_P3:
|
||||
return SkColorSpace::MakeRGB(
|
||||
SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t dataSpaceToInt(android_dataspace d)
|
||||
{
|
||||
switch (d) {
|
||||
case HAL_DATASPACE_V0_SRGB:
|
||||
return COLORSPACE_SRGB;
|
||||
case HAL_DATASPACE_DISPLAY_P3:
|
||||
return COLORSPACE_DISPLAY_P3;
|
||||
default:
|
||||
return COLORSPACE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
static status_t notifyMediaScanner(const char* fileName) {
|
||||
String8 cmd("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
|
||||
String8 fileUrl("\"");
|
||||
@@ -139,6 +171,7 @@ int main(int argc, char** argv)
|
||||
|
||||
void const* base = NULL;
|
||||
uint32_t w, s, h, f;
|
||||
android_dataspace d;
|
||||
size_t size = 0;
|
||||
|
||||
// Maps orientations from DisplayInfo to ISurfaceComposer
|
||||
@@ -177,13 +210,15 @@ int main(int argc, char** argv)
|
||||
h = screenshot.getHeight();
|
||||
s = screenshot.getStride();
|
||||
f = screenshot.getFormat();
|
||||
d = screenshot.getDataSpace();
|
||||
size = screenshot.getSize();
|
||||
}
|
||||
|
||||
if (base != NULL) {
|
||||
if (png) {
|
||||
const SkImageInfo info =
|
||||
SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType);
|
||||
SkImageInfo::Make(w, h, flinger2skia(f), kPremul_SkAlphaType,
|
||||
dataSpaceToColorSpace(d));
|
||||
SkPixmap pixmap(info, base, s * bytesPerPixel(f));
|
||||
struct FDWStream final : public SkWStream {
|
||||
size_t fBytesWritten = 0;
|
||||
@@ -200,9 +235,11 @@ int main(int argc, char** argv)
|
||||
notifyMediaScanner(fn);
|
||||
}
|
||||
} else {
|
||||
uint32_t c = dataSpaceToInt(d);
|
||||
write(fd, &w, 4);
|
||||
write(fd, &h, 4);
|
||||
write(fd, &f, 4);
|
||||
write(fd, &c, 4);
|
||||
size_t Bpp = bytesPerPixel(f);
|
||||
for (size_t y=0 ; y<h ; y++) {
|
||||
write(fd, base, w*Bpp);
|
||||
|
||||
@@ -358,7 +358,6 @@ public final class ThreadedRenderer {
|
||||
private long mNativeProxy;
|
||||
private boolean mInitialized = false;
|
||||
private RenderNode mRootNode;
|
||||
private Choreographer mChoreographer;
|
||||
private boolean mRootNodeNeedsUpdate;
|
||||
|
||||
private boolean mEnabled;
|
||||
@@ -425,8 +424,6 @@ public final class ThreadedRenderer {
|
||||
/**
|
||||
* Indicates whether threaded rendering is currently requested but not
|
||||
* necessarily enabled yet.
|
||||
*
|
||||
* @return True to request threaded rendering, false otherwise.
|
||||
*/
|
||||
void setRequested(boolean requested) {
|
||||
mRequested = requested;
|
||||
@@ -596,6 +593,13 @@ public final class ThreadedRenderer {
|
||||
nSetOpaque(mNativeProxy, opaque && !mHasInsets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable wide gamut rendering on this renderer.
|
||||
*/
|
||||
void setWideGamut(boolean wideGamut) {
|
||||
nSetWideGamut(mNativeProxy, wideGamut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current width of the surface. This is the width that the surface
|
||||
* was last set to in a call to {@link #setup(int, int, View.AttachInfo, Rect)}.
|
||||
@@ -933,11 +937,11 @@ public final class ThreadedRenderer {
|
||||
if (mInitialized) return;
|
||||
mInitialized = true;
|
||||
mAppContext = context.getApplicationContext();
|
||||
initSched(context, renderProxy);
|
||||
initSched(renderProxy);
|
||||
initGraphicsStats();
|
||||
}
|
||||
|
||||
private void initSched(Context context, long renderProxy) {
|
||||
private void initSched(long renderProxy) {
|
||||
try {
|
||||
int tid = nGetRenderThreadTid(renderProxy);
|
||||
ActivityManager.getService().setRenderThread(tid);
|
||||
@@ -1007,6 +1011,7 @@ public final class ThreadedRenderer {
|
||||
private static native void nSetLightCenter(long nativeProxy,
|
||||
float lightX, float lightY, float lightZ);
|
||||
private static native void nSetOpaque(long nativeProxy, boolean opaque);
|
||||
private static native void nSetWideGamut(long nativeProxy, boolean wideGamut);
|
||||
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
|
||||
private static native void nDestroy(long nativeProxy, long rootRenderNode);
|
||||
private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.app.ResourcesManager;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.CompatibilityInfo;
|
||||
import android.content.res.Configuration;
|
||||
@@ -960,8 +961,12 @@ 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 =
|
||||
attrs.getColorMode() == ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT;
|
||||
|
||||
mAttachInfo.mThreadedRenderer = ThreadedRenderer.create(mContext, translucent,
|
||||
attrs.getTitle().toString());
|
||||
mAttachInfo.mThreadedRenderer.setWideGamut(wideGamut);
|
||||
if (mAttachInfo.mThreadedRenderer != null) {
|
||||
mAttachInfo.mHardwareAccelerated =
|
||||
mAttachInfo.mHardwareAccelerationRequested = true;
|
||||
|
||||
@@ -2090,6 +2090,7 @@ public interface WindowManager extends ViewManager {
|
||||
out.writeInt(needsMenuKey);
|
||||
out.writeInt(accessibilityIdOfAnchor);
|
||||
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
|
||||
out.writeInt(mColorMode);
|
||||
out.writeLong(hideTimeoutMilliseconds);
|
||||
}
|
||||
|
||||
@@ -2144,6 +2145,7 @@ public interface WindowManager extends ViewManager {
|
||||
needsMenuKey = in.readInt();
|
||||
accessibilityIdOfAnchor = in.readInt();
|
||||
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
|
||||
mColorMode = in.readInt();
|
||||
hideTimeoutMilliseconds = in.readLong();
|
||||
}
|
||||
|
||||
@@ -2189,6 +2191,8 @@ public interface WindowManager extends ViewManager {
|
||||
@TestApi
|
||||
public static final int ACCESSIBILITY_TITLE_CHANGED = 1 << 25;
|
||||
/** {@hide} */
|
||||
public static final int COLOR_MODE_CHANGED = 1 << 26;
|
||||
/** {@hide} */
|
||||
public static final int EVERYTHING_CHANGED = 0xffffffff;
|
||||
|
||||
// internal buffer to backup/restore parameters under compatibility mode.
|
||||
@@ -2366,6 +2370,11 @@ public interface WindowManager extends ViewManager {
|
||||
changes |= ACCESSIBILITY_TITLE_CHANGED;
|
||||
}
|
||||
|
||||
if (mColorMode != o.mColorMode) {
|
||||
mColorMode = o.mColorMode;
|
||||
changes |= COLOR_MODE_CHANGED;
|
||||
}
|
||||
|
||||
// This can't change, it's only set at window creation time.
|
||||
hideTimeoutMilliseconds = o.hideTimeoutMilliseconds;
|
||||
|
||||
|
||||
@@ -221,11 +221,20 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz,
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkColorSpace> colorSpace;
|
||||
if (screenshot->getDataSpace() == HAL_DATASPACE_DISPLAY_P3) {
|
||||
colorSpace = SkColorSpace::MakeRGB(
|
||||
SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kDCIP3_D65_Gamut);
|
||||
} else {
|
||||
colorSpace = SkColorSpace::MakeSRGB();
|
||||
}
|
||||
|
||||
SkImageInfo screenshotInfo = SkImageInfo::Make(screenshot->getWidth(),
|
||||
screenshot->getHeight(),
|
||||
colorType,
|
||||
alphaType,
|
||||
GraphicsJNI::defaultColorSpace());
|
||||
colorSpace);
|
||||
|
||||
const size_t rowBytes =
|
||||
screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
|
||||
|
||||
@@ -681,6 +681,12 @@ 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) {
|
||||
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
|
||||
proxy->setWideGamut(wideGamut);
|
||||
}
|
||||
|
||||
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
|
||||
jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
|
||||
LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
|
||||
@@ -982,6 +988,7 @@ static const JNINativeMethod gMethods[] = {
|
||||
{ "nSetup", "(JFII)V", (void*) android_view_ThreadedRenderer_setup },
|
||||
{ "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
|
||||
{ "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
|
||||
{ "nSetWideGamut", "(JZ)V", (void*) android_view_ThreadedRenderer_setWideGamut },
|
||||
{ "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
|
||||
{ "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
|
||||
{ "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
|
||||
|
||||
@@ -155,7 +155,8 @@ void SkiaOpenGLPipeline::onStop() {
|
||||
}
|
||||
}
|
||||
|
||||
bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
|
||||
ColorMode colorMode) {
|
||||
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
mEglManager.destroySurface(mEglSurface);
|
||||
@@ -163,7 +164,8 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior)
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
mEglSurface = mEglManager.createSurface(surface);
|
||||
const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
|
||||
mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
|
||||
}
|
||||
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
|
||||
@@ -40,7 +40,8 @@ public:
|
||||
FrameInfo* currentFrameInfo, bool* requireSwap) override;
|
||||
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
|
||||
DeferredLayerUpdater* createTextureLayer() override;
|
||||
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
|
||||
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
|
||||
renderthread::ColorMode colorMode) override;
|
||||
void onStop() override;
|
||||
bool isSurfaceReady() override;
|
||||
bool isContextReady() override;
|
||||
|
||||
@@ -131,13 +131,15 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() {
|
||||
void SkiaVulkanPipeline::onStop() {
|
||||
}
|
||||
|
||||
bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
bool SkiaVulkanPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior,
|
||||
ColorMode colorMode) {
|
||||
if (mVkSurface) {
|
||||
mVkManager.destroySurface(mVkSurface);
|
||||
mVkSurface = nullptr;
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
// TODO: handle color mode
|
||||
mVkSurface = mVkManager.createSurface(surface);
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
FrameInfo* currentFrameInfo, bool* requireSwap) override;
|
||||
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
|
||||
DeferredLayerUpdater* createTextureLayer() override;
|
||||
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior) override;
|
||||
bool setSurface(Surface* window, renderthread::SwapBehavior swapBehavior,
|
||||
renderthread::ColorMode colorMode) override;
|
||||
void onStop() override;
|
||||
bool isSurfaceReady() override;
|
||||
bool isContextReady() override;
|
||||
|
||||
@@ -186,7 +186,8 @@ void CanvasContext::setSurface(Surface* surface) {
|
||||
|
||||
mNativeSurface = surface;
|
||||
|
||||
bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior);
|
||||
ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb;
|
||||
bool hasSurface = mRenderPipeline->setSurface(surface, mSwapBehavior, colorMode);
|
||||
|
||||
mFrameNumber = -1;
|
||||
|
||||
@@ -241,6 +242,10 @@ void CanvasContext::setOpaque(bool opaque) {
|
||||
mOpaque = opaque;
|
||||
}
|
||||
|
||||
void CanvasContext::setWideGamut(bool wideGamut) {
|
||||
mWideColorGamut = wideGamut;
|
||||
}
|
||||
|
||||
bool CanvasContext::makeCurrent() {
|
||||
if (mStopped) return false;
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ public:
|
||||
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
|
||||
void setLightCenter(const Vector3& lightCenter);
|
||||
void setOpaque(bool opaque);
|
||||
void setWideGamut(bool wideGamut);
|
||||
bool makeCurrent();
|
||||
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
|
||||
int64_t syncQueued, RenderNode* target);
|
||||
@@ -240,6 +241,7 @@ private:
|
||||
nsecs_t mLastDropVsync = 0;
|
||||
|
||||
bool mOpaque;
|
||||
bool mWideColorGamut = false;
|
||||
BakedOpRenderer::LightInfo mLightInfo;
|
||||
FrameBuilder::LightGeometry mLightGeometry = { {0, 0, 0}, 0 };
|
||||
|
||||
|
||||
@@ -76,12 +76,16 @@ const char* EglManager::eglErrorString() {
|
||||
static struct {
|
||||
bool bufferAge = false;
|
||||
bool setDamage = false;
|
||||
bool noConfigContext = false;
|
||||
bool pixelFormatFloat = false;
|
||||
bool glColorSpace = false;
|
||||
} EglExtensions;
|
||||
|
||||
EglManager::EglManager(RenderThread& thread)
|
||||
: mRenderThread(thread)
|
||||
, mEglDisplay(EGL_NO_DISPLAY)
|
||||
, mEglConfig(nullptr)
|
||||
, mEglConfigWideGamut(nullptr)
|
||||
, mEglContext(EGL_NO_CONTEXT)
|
||||
, mPBufferSurface(EGL_NO_SURFACE)
|
||||
, mCurrentSurface(EGL_NO_SURFACE) {
|
||||
@@ -116,7 +120,7 @@ void EglManager::initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
loadConfig();
|
||||
loadConfigs();
|
||||
createContext();
|
||||
createPBufferSurface();
|
||||
makeCurrent(mPBufferSurface);
|
||||
@@ -143,6 +147,7 @@ void EglManager::initialize() {
|
||||
void EglManager::initExtensions() {
|
||||
auto extensions = StringUtils::split(
|
||||
eglQueryString(mEglDisplay, EGL_EXTENSIONS));
|
||||
|
||||
// For our purposes we don't care if EGL_BUFFER_AGE is a result of
|
||||
// EGL_EXT_buffer_age or EGL_KHR_partial_update as our usage is covered
|
||||
// under EGL_KHR_partial_update and we don't need the expanded scope
|
||||
@@ -152,13 +157,17 @@ void EglManager::initExtensions() {
|
||||
EglExtensions.setDamage = extensions.has("EGL_KHR_partial_update");
|
||||
LOG_ALWAYS_FATAL_IF(!extensions.has("EGL_KHR_swap_buffers_with_damage"),
|
||||
"Missing required extension EGL_KHR_swap_buffers_with_damage");
|
||||
|
||||
EglExtensions.glColorSpace = extensions.has("EGL_KHR_gl_colorspace");
|
||||
EglExtensions.noConfigContext = extensions.has("EGL_KHR_no_config_context");
|
||||
EglExtensions.pixelFormatFloat = extensions.has("EGL_EXT_pixel_format_float");
|
||||
}
|
||||
|
||||
bool EglManager::hasEglContext() {
|
||||
return mEglDisplay != EGL_NO_DISPLAY;
|
||||
}
|
||||
|
||||
void EglManager::loadConfig() {
|
||||
void EglManager::loadConfigs() {
|
||||
ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior));
|
||||
EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved)
|
||||
? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
|
||||
@@ -175,19 +184,44 @@ void EglManager::loadConfig() {
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint num_configs = 1;
|
||||
if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
|
||||
|| num_configs != 1) {
|
||||
EGLint numConfigs = 1;
|
||||
if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, numConfigs, &numConfigs)
|
||||
|| numConfigs != 1) {
|
||||
if (mSwapBehavior == SwapBehavior::Preserved) {
|
||||
// Try again without dirty regions enabled
|
||||
ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
|
||||
mSwapBehavior = SwapBehavior::Discard;
|
||||
loadConfig();
|
||||
loadConfigs();
|
||||
return; // the call to loadConfigs() we just made picks the wide gamut config
|
||||
} else {
|
||||
// Failed to get a valid config
|
||||
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", eglErrorString());
|
||||
}
|
||||
}
|
||||
|
||||
if (EglExtensions.pixelFormatFloat) {
|
||||
// If we reached this point, we have a valid swap behavior
|
||||
EGLint attribs16F[] = {
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_COLOR_COMPONENT_TYPE_EXT, EGL_COLOR_COMPONENT_TYPE_FLOAT_EXT,
|
||||
EGL_RED_SIZE, 16,
|
||||
EGL_GREEN_SIZE, 16,
|
||||
EGL_BLUE_SIZE, 16,
|
||||
EGL_ALPHA_SIZE, 16,
|
||||
EGL_DEPTH_SIZE, 0,
|
||||
EGL_STENCIL_SIZE, Stencil::getStencilSize(),
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
numConfigs = 1;
|
||||
if (!eglChooseConfig(mEglDisplay, attribs16F, &mEglConfigWideGamut, numConfigs, &numConfigs)
|
||||
|| numConfigs != 1) {
|
||||
LOG_ALWAYS_FATAL(
|
||||
"Device claims wide gamut support, cannot find matching config, error = %s",
|
||||
eglErrorString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EglManager::createContext() {
|
||||
@@ -195,7 +229,9 @@ void EglManager::createContext() {
|
||||
EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
|
||||
EGL_NONE
|
||||
};
|
||||
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
|
||||
mEglContext = eglCreateContext(mEglDisplay,
|
||||
EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
|
||||
EGL_NO_CONTEXT, attribs);
|
||||
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
|
||||
"Failed to create context, error = %s", eglErrorString());
|
||||
}
|
||||
@@ -210,18 +246,60 @@ void EglManager::createPBufferSurface() {
|
||||
}
|
||||
}
|
||||
|
||||
EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
|
||||
EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) {
|
||||
initialize();
|
||||
|
||||
wideColorGamut = wideColorGamut && EglExtensions.glColorSpace
|
||||
&& EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext;
|
||||
|
||||
// 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
|
||||
// color gamut rendering is off, the app simply renders in the display's native
|
||||
// color gamut.
|
||||
//
|
||||
// When wide gamut rendering is off:
|
||||
// - Blending is done by default in gamma space, which requires using a
|
||||
// linear EGL color space (the GPU uses the color values as is)
|
||||
// - If linear blending is on, we must use the sRGB EGL color space (the
|
||||
// GPU will perform sRGB to linear and linear to SRGB conversions before
|
||||
// and after blending)
|
||||
//
|
||||
// When wide gamut rendering is on we cannot rely on the GPU performing
|
||||
// linear blending for us. We use two different color spaces to tag the
|
||||
// surface appropriately for SurfaceFlinger:
|
||||
// - Gamma blending (default) requires the use of the scRGB-nl color space
|
||||
// - Linear blending requires the use of the scRGB color space
|
||||
|
||||
// Not all Android targets support the EGL_GL_COLOR_SPACE_KHR extension
|
||||
// We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value.
|
||||
// According to section 3.4.1 of the EGL specification, the attributes
|
||||
// list is considered empty if the first entry is EGL_NONE
|
||||
EGLint attribs[] = {
|
||||
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
|
||||
EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR,
|
||||
EGL_COLORSPACE, EGL_COLORSPACE_sRGB,
|
||||
#endif
|
||||
EGL_NONE, EGL_NONE,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, attribs);
|
||||
if (EglExtensions.glColorSpace) {
|
||||
attribs[0] = EGL_GL_COLORSPACE_KHR;
|
||||
#ifdef ANDROID_ENABLE_LINEAR_BLENDING
|
||||
if (wideColorGamut) {
|
||||
attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
|
||||
} else {
|
||||
attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR;
|
||||
}
|
||||
#else
|
||||
if (wideColorGamut) {
|
||||
// TODO: this should be using scRGB-nl, not scRGB, we need an extension for this
|
||||
// TODO: in the meantime SurfaceFlinger just assumes that scRGB is scRGB-nl
|
||||
attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT;
|
||||
} else {
|
||||
attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
EGLSurface surface = eglCreateWindowSurface(mEglDisplay,
|
||||
wideColorGamut ? mEglConfigWideGamut : mEglConfig, window, attribs);
|
||||
LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
|
||||
"Failed to create EGLSurface for window %p, eglErr = %s",
|
||||
(void*) window, eglErrorString());
|
||||
|
||||
@@ -39,7 +39,7 @@ public:
|
||||
|
||||
bool hasEglContext();
|
||||
|
||||
EGLSurface createSurface(EGLNativeWindowType window);
|
||||
EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut);
|
||||
void destroySurface(EGLSurface surface);
|
||||
|
||||
void destroy();
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
|
||||
void initExtensions();
|
||||
void createPBufferSurface();
|
||||
void loadConfig();
|
||||
void loadConfigs();
|
||||
void createContext();
|
||||
EGLint queryBufferAge(EGLSurface surface);
|
||||
|
||||
@@ -76,6 +76,7 @@ private:
|
||||
|
||||
EGLDisplay mEglDisplay;
|
||||
EGLConfig mEglConfig;
|
||||
EGLConfig mEglConfigWideGamut;
|
||||
EGLContext mEglContext;
|
||||
EGLSurface mPBufferSurface;
|
||||
|
||||
|
||||
@@ -44,6 +44,12 @@ enum class MakeCurrentResult {
|
||||
Succeeded
|
||||
};
|
||||
|
||||
enum class ColorMode {
|
||||
Srgb,
|
||||
WideColorGamut,
|
||||
// Hdr
|
||||
};
|
||||
|
||||
class Frame;
|
||||
|
||||
class IRenderPipeline {
|
||||
@@ -61,7 +67,7 @@ public:
|
||||
FrameInfo* currentFrameInfo, bool* requireSwap) = 0;
|
||||
virtual bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) = 0;
|
||||
virtual DeferredLayerUpdater* createTextureLayer() = 0;
|
||||
virtual bool setSurface(Surface* window, SwapBehavior swapBehavior) = 0;
|
||||
virtual bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) = 0;
|
||||
virtual void onStop() = 0;
|
||||
virtual bool isSurfaceReady() = 0;
|
||||
virtual bool isContextReady() = 0;
|
||||
|
||||
@@ -146,7 +146,7 @@ void OpenGLPipeline::onStop() {
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, ColorMode colorMode) {
|
||||
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
mEglManager.destroySurface(mEglSurface);
|
||||
@@ -154,7 +154,8 @@ bool OpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior) {
|
||||
}
|
||||
|
||||
if (surface) {
|
||||
mEglSurface = mEglManager.createSurface(surface);
|
||||
const bool wideColorGamut = colorMode == ColorMode::WideColorGamut;
|
||||
mEglSurface = mEglManager.createSurface(surface, wideColorGamut);
|
||||
}
|
||||
|
||||
if (mEglSurface != EGL_NO_SURFACE) {
|
||||
|
||||
@@ -44,7 +44,7 @@ public:
|
||||
FrameInfo* currentFrameInfo, bool* requireSwap) override;
|
||||
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) override;
|
||||
DeferredLayerUpdater* createTextureLayer() override;
|
||||
bool setSurface(Surface* window, SwapBehavior swapBehavior) override;
|
||||
bool setSurface(Surface* window, SwapBehavior swapBehavior, ColorMode colorMode) override;
|
||||
void onStop() override;
|
||||
bool isSurfaceReady() override;
|
||||
bool isContextReady() override;
|
||||
|
||||
@@ -227,6 +227,18 @@ void RenderProxy::setOpaque(bool opaque) {
|
||||
post(task);
|
||||
}
|
||||
|
||||
CREATE_BRIDGE2(setWideGamut, CanvasContext* context, bool wideGamut) {
|
||||
args->context->setWideGamut(args->wideGamut);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RenderProxy::setWideGamut(bool wideGamut) {
|
||||
SETUP_TASK(setWideGamut);
|
||||
args->context = mContext;
|
||||
args->wideGamut = wideGamut;
|
||||
post(task);
|
||||
}
|
||||
|
||||
int64_t* RenderProxy::frameInfo() {
|
||||
return mDrawFrameTask.frameInfo();
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace DumpFlags {
|
||||
*/
|
||||
class ANDROID_API RenderProxy {
|
||||
public:
|
||||
ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode, IContextFactory* contextFactory);
|
||||
ANDROID_API RenderProxy(bool opaque, RenderNode* rootNode, IContextFactory* contextFactory);
|
||||
ANDROID_API virtual ~RenderProxy();
|
||||
|
||||
// Won't take effect until next EGLSurface creation
|
||||
@@ -85,6 +85,7 @@ public:
|
||||
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
|
||||
ANDROID_API void setLightCenter(const Vector3& lightCenter);
|
||||
ANDROID_API void setOpaque(bool opaque);
|
||||
ANDROID_API void setWideGamut(bool wideGamut);
|
||||
ANDROID_API int64_t* frameInfo();
|
||||
ANDROID_API int syncAndDrawFrame();
|
||||
ANDROID_API void destroy();
|
||||
|
||||
@@ -100,7 +100,7 @@ final class Screenshooter {
|
||||
// Rotate the screenshot to the current orientation
|
||||
if (rotation != ROTATION_FREEZE_0) {
|
||||
Bitmap unrotatedScreenShot = Bitmap.createBitmap(displayWidth, displayHeight,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Bitmap.Config.ARGB_8888, screenShot.hasAlpha(), screenShot.getColorSpace());
|
||||
Canvas canvas = new Canvas(unrotatedScreenShot);
|
||||
canvas.translate(unrotatedScreenShot.getWidth() / 2,
|
||||
unrotatedScreenShot.getHeight() / 2);
|
||||
|
||||
@@ -578,7 +578,8 @@ class GlobalScreenshot {
|
||||
if (requiresRotation) {
|
||||
// Rotate the screenshot to the current orientation
|
||||
Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,
|
||||
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);
|
||||
mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888,
|
||||
mScreenBitmap.hasAlpha(), mScreenBitmap.getColorSpace());
|
||||
Canvas c = new Canvas(ss);
|
||||
c.translate(ss.getWidth() / 2, ss.getHeight() / 2);
|
||||
c.rotate(degrees);
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -48,6 +49,10 @@ public class DisplayTransformManager {
|
||||
*/
|
||||
public static final int LEVEL_COLOR_MATRIX_INVERT_COLOR = 300;
|
||||
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX = 1015;
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_DALTONIZER = 1014;
|
||||
private static final int SURFACE_FLINGER_TRANSACTION_SATURATION = 1022;
|
||||
|
||||
/**
|
||||
* Map of level -> color transformation matrix.
|
||||
*/
|
||||
@@ -66,6 +71,10 @@ public class DisplayTransformManager {
|
||||
@GuardedBy("mDaltonizerModeLock")
|
||||
private int mDaltonizerMode = -1;
|
||||
|
||||
private final Object mSaturationLock = new Object();
|
||||
@GuardedBy("mSaturationLock")
|
||||
private float mSaturation = 1.0f;
|
||||
|
||||
/* package */ DisplayTransformManager() {
|
||||
}
|
||||
|
||||
@@ -155,6 +164,30 @@ public class DisplayTransformManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current saturation.
|
||||
*/
|
||||
public float getSaturation() {
|
||||
synchronized (mSaturationLock) {
|
||||
return mSaturation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the saturation level of the display. The default value is 1.0.
|
||||
*
|
||||
* @param saturation A value between 0 (0% saturation, grayscale) and 2 (100% extra saturation)
|
||||
*/
|
||||
public void setSaturation(float saturation) {
|
||||
synchronized (mSaturationLock) {
|
||||
saturation = MathUtils.constrain(saturation, 0.0f, 2.0f);
|
||||
if (mSaturation != saturation) {
|
||||
mSaturation = saturation;
|
||||
applySaturation(saturation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates the provided color transformation matrix to the SurfaceFlinger.
|
||||
*/
|
||||
@@ -172,7 +205,7 @@ public class DisplayTransformManager {
|
||||
data.writeInt(0);
|
||||
}
|
||||
try {
|
||||
flinger.transact(1015, data, null, 0);
|
||||
flinger.transact(SURFACE_FLINGER_TRANSACTION_COLOR_MATRIX, data, null, 0);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "Failed to set color transform", ex);
|
||||
} finally {
|
||||
@@ -191,7 +224,7 @@ public class DisplayTransformManager {
|
||||
data.writeInterfaceToken("android.ui.ISurfaceComposer");
|
||||
data.writeInt(mode);
|
||||
try {
|
||||
flinger.transact(1014, data, null, 0);
|
||||
flinger.transact(SURFACE_FLINGER_TRANSACTION_DALTONIZER, data, null, 0);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "Failed to set Daltonizer mode", ex);
|
||||
} finally {
|
||||
@@ -199,4 +232,23 @@ public class DisplayTransformManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates the provided saturation to the SurfaceFlinger.
|
||||
*/
|
||||
private static void applySaturation(float saturation) {
|
||||
final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
|
||||
if (flinger != null) {
|
||||
final Parcel data = Parcel.obtain();
|
||||
data.writeInterfaceToken("android.ui.ISurfaceComposer");
|
||||
data.writeFloat(saturation);
|
||||
try {
|
||||
flinger.transact(SURFACE_FLINGER_TRANSACTION_SATURATION, data, null, 0);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "Failed to set saturation", ex);
|
||||
} finally {
|
||||
data.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user