Respect a Layer's (e.g. SurfaceTexture) colorSpace when compositing
This CL extracts the android_dataspace from the GLConsumer and converts
it to a SkColorSpace. HWUI always expects to composite into an sRGB
destination so when we draw the layer we run the draw through a
colorFilter that converts the input colorSpace into that of the
destination.
Test: CtsViewTestCases
Bug: 78016220
Change-Id: Ic0446a0d861e86a5a9d0382346b57fcc45c8a61b
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index be7d663..569de76 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -40,7 +40,6 @@
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
- SkSafeUnref(mColorFilter);
setTransform(nullptr);
mRenderState.unregisterDeferredLayerUpdater(this);
destroyLayer();
@@ -67,8 +66,11 @@
void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
mAlpha = PaintUtils::getAlphaDirect(paint);
mMode = PaintUtils::getBlendModeDirect(paint);
- SkColorFilter* colorFilter = (paint) ? paint->getColorFilter() : nullptr;
- SkRefCnt_SafeAssign(mColorFilter, colorFilter);
+ if (paint) {
+ mColorFilter = paint->refColorFilter();
+ } else {
+ mColorFilter.reset();
+ }
}
void DeferredLayerUpdater::apply() {
@@ -143,7 +145,7 @@
#endif
mSurfaceTexture->getTransformMatrix(transform);
- updateLayer(forceFilter, transform);
+ updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
}
}
@@ -153,17 +155,19 @@
Layer::Api::OpenGL, Layer::Api::Vulkan);
static const mat4 identityMatrix;
- updateLayer(false, identityMatrix.data);
+ updateLayer(false, identityMatrix.data, HAL_DATASPACE_UNKNOWN);
VkLayer* vkLayer = static_cast<VkLayer*>(mLayer);
vkLayer->updateTexture();
}
-void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform) {
+void DeferredLayerUpdater::updateLayer(bool forceFilter, const float* textureTransform,
+ android_dataspace dataspace) {
mLayer->setBlend(mBlend);
mLayer->setForceFilter(forceFilter);
mLayer->setSize(mWidth, mHeight);
mLayer->getTexTransform().load(textureTransform);
+ mLayer->setDataSpace(dataspace);
}
void DeferredLayerUpdater::detachSurfaceTexture() {
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 9dc029f..fe3ee7a 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -20,6 +20,7 @@
#include <SkMatrix.h>
#include <cutils/compiler.h>
#include <gui/GLConsumer.h>
+#include <system/graphics.h>
#include <utils/StrongPointer.h>
#include <GLES2/gl2.h>
@@ -41,7 +42,7 @@
// Note that DeferredLayerUpdater assumes it is taking ownership of the layer
// and will not call incrementRef on it as a result.
typedef std::function<Layer*(RenderState& renderState, uint32_t layerWidth,
- uint32_t layerHeight, SkColorFilter* colorFilter, int alpha,
+ uint32_t layerHeight, sk_sp<SkColorFilter> colorFilter, int alpha,
SkBlendMode mode, bool blend)>
CreateLayerFn;
ANDROID_API explicit DeferredLayerUpdater(RenderState& renderState, CreateLayerFn createLayerFn,
@@ -96,7 +97,7 @@
void detachSurfaceTexture();
- void updateLayer(bool forceFilter, const float* textureTransform);
+ void updateLayer(bool forceFilter, const float* textureTransform, android_dataspace dataspace);
void destroyLayer();
@@ -109,7 +110,7 @@
int mWidth = 0;
int mHeight = 0;
bool mBlend = false;
- SkColorFilter* mColorFilter = nullptr;
+ sk_sp<SkColorFilter> mColorFilter;
int mAlpha = 255;
SkBlendMode mMode = SkBlendMode::kSrcOver;
sp<GLConsumer> mSurfaceTexture;
diff --git a/libs/hwui/GlLayer.cpp b/libs/hwui/GlLayer.cpp
index 32a3014..42ae29d 100644
--- a/libs/hwui/GlLayer.cpp
+++ b/libs/hwui/GlLayer.cpp
@@ -32,7 +32,7 @@
namespace uirenderer {
GlLayer::GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
: Layer(renderState, Api::OpenGL, colorFilter, alpha, mode)
, caches(Caches::getInstance())
, texture(caches) {
diff --git a/libs/hwui/GlLayer.h b/libs/hwui/GlLayer.h
index 1b09191..28749a0 100644
--- a/libs/hwui/GlLayer.h
+++ b/libs/hwui/GlLayer.h
@@ -32,7 +32,7 @@
class GlLayer : public Layer {
public:
GlLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend);
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend);
virtual ~GlLayer();
uint32_t getWidth() const override { return texture.mWidth; }
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 86950d5..fb8f033 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,34 +18,58 @@
#include "renderstate/RenderState.h"
-#include <SkColorFilter.h>
+#include <SkToSRGBColorFilter.h>
namespace android {
namespace uirenderer {
-Layer::Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
+Layer::Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter> colorFilter, int alpha,
SkBlendMode mode)
: GpuMemoryTracker(GpuObjectType::Layer)
, mRenderState(renderState)
, mApi(api)
- , colorFilter(nullptr)
+ , mColorFilter(colorFilter)
, alpha(alpha)
, mode(mode) {
// TODO: This is a violation of Android's typical ref counting, but it
// preserves the old inc/dec ref locations. This should be changed...
incStrong(nullptr);
-
+ buildColorSpaceWithFilter();
renderState.registerLayer(this);
}
Layer::~Layer() {
- SkSafeUnref(colorFilter);
-
mRenderState.unregisterLayer(this);
}
-void Layer::setColorFilter(SkColorFilter* filter) {
- SkRefCnt_SafeAssign(colorFilter, filter);
+void Layer::setColorFilter(sk_sp<SkColorFilter> filter) {
+ if (filter != mColorFilter) {
+ mColorFilter = filter;
+ buildColorSpaceWithFilter();
+ }
+}
+
+void Layer::setDataSpace(android_dataspace dataspace) {
+ if (dataspace != mCurrentDataspace) {
+ mCurrentDataspace = dataspace;
+ buildColorSpaceWithFilter();
+ }
+}
+
+void Layer::buildColorSpaceWithFilter() {
+ sk_sp<SkColorFilter> colorSpaceFilter;
+ sk_sp<SkColorSpace> colorSpace = DataSpaceToColorSpace(mCurrentDataspace);
+ if (colorSpace && !colorSpace->isSRGB()) {
+ colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
+ }
+
+ if (mColorFilter && colorSpaceFilter) {
+ mColorSpaceWithFilter = mColorFilter->makeComposed(colorSpaceFilter);
+ } else if (colorSpaceFilter) {
+ mColorSpaceWithFilter = colorSpaceFilter;
+ } else {
+ mColorSpaceWithFilter = mColorFilter;
+ }
}
void Layer::postDecStrong() {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 6921381..89bcddc 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -19,6 +19,8 @@
#include <GpuMemoryTracker.h>
#include <utils/RefBase.h>
+#include <SkColorFilter.h>
+#include <SkColorSpace.h>
#include <SkBlendMode.h>
#include <SkPaint.h>
@@ -72,9 +74,15 @@
inline SkBlendMode getMode() const { return mode; }
- inline SkColorFilter* getColorFilter() const { return colorFilter; }
+ inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); }
- void setColorFilter(SkColorFilter* filter);
+ void setColorFilter(sk_sp<SkColorFilter> filter);
+
+ void setDataSpace(android_dataspace dataspace);
+
+ void setColorSpace(sk_sp<SkColorSpace> colorSpace);
+
+ inline sk_sp<SkColorFilter> getColorSpaceWithFilter() const { return mColorSpaceWithFilter; }
inline mat4& getTexTransform() { return texTransform; }
@@ -87,18 +95,30 @@
void postDecStrong();
protected:
- Layer(RenderState& renderState, Api api, SkColorFilter* colorFilter, int alpha,
+ Layer(RenderState& renderState, Api api, sk_sp<SkColorFilter>, int alpha,
SkBlendMode mode);
RenderState& mRenderState;
private:
+ void buildColorSpaceWithFilter();
+
Api mApi;
/**
* Color filter used to draw this layer. Optional.
*/
- SkColorFilter* colorFilter;
+ sk_sp<SkColorFilter> mColorFilter;
+
+ /**
+ * Colorspace of the contents of the layer. Optional.
+ */
+ android_dataspace mCurrentDataspace = HAL_DATASPACE_UNKNOWN;
+
+ /**
+ * A color filter that is the combination of the mColorFilter and mColorSpace. Optional.
+ */
+ sk_sp<SkColorFilter> mColorSpaceWithFilter;
/**
* Indicates raster data backing the layer is scaled, requiring filtration.
diff --git a/libs/hwui/VkLayer.h b/libs/hwui/VkLayer.h
index f23f472..e9664d0 100644
--- a/libs/hwui/VkLayer.h
+++ b/libs/hwui/VkLayer.h
@@ -28,7 +28,7 @@
class VkLayer : public Layer {
public:
VkLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend)
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend)
: Layer(renderState, Api::Vulkan, colorFilter, alpha, mode)
, mWidth(layerWidth)
, mHeight(layerHeight)
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 896c01c..00ba7130 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -50,9 +50,13 @@
GlLayer* glLayer = static_cast<GlLayer*>(layer);
GrGLTextureInfo externalTexture;
externalTexture.fTarget = glLayer->getRenderTarget();
- SkASSERT(GL_RGBA == glLayer->getTexture().internalFormat());
- externalTexture.fFormat = GL_RGBA8;
externalTexture.fID = glLayer->getTextureId();
+ // The format may not be GL_RGBA8, but given the DeferredLayerUpdater and GLConsumer don't
+ // expose that info we use it as our default. Further, given that we only use this texture
+ // as a source this will not impact how Skia uses the texture. The only potential affect
+ // this is anticipated to have is that for some format types if we are not bound as an OES
+ // texture we may get invalid results for SKP capture if we read back the texture.
+ externalTexture.fFormat = GL_RGBA8;
GrBackendTexture backendTexture(layerWidth, layerHeight, GrMipMapped::kNo, externalTexture);
layerImage = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
@@ -83,7 +87,7 @@
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
- paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
+ paint.setColorFilter(layer->getColorSpaceWithFilter());
const bool nonIdentityMatrix = !matrix.isIdentity();
if (nonIdentityMatrix) {
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index b7aa78b..7dd441b 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -158,7 +158,7 @@
}
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode, bool blend) {
GlLayer* layer =
new GlLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
layer->generateTexture();
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 9e73046..d66cba1 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -147,6 +147,7 @@
GrContext* currentContext = layerNode->getLayerSurface()->getCanvas()->getGrContext();
if (cachedContext.get() != currentContext) {
if (cachedContext.get()) {
+ ATRACE_NAME("flush layers (context changed)");
cachedContext->flush();
}
cachedContext.reset(SkSafeRef(currentContext));
@@ -155,6 +156,7 @@
}
if (cachedContext.get()) {
+ ATRACE_NAME("flush layers");
cachedContext->flush();
}
}
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 6530074..5825060 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -115,7 +115,8 @@
}
static Layer* createLayer(RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight,
- SkColorFilter* colorFilter, int alpha, SkBlendMode mode, bool blend) {
+ sk_sp<SkColorFilter> colorFilter, int alpha, SkBlendMode mode,
+ bool blend) {
return new VkLayer(renderState, layerWidth, layerHeight, colorFilter, alpha, mode, blend);
}
diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp
index 16c5afd..1555c0a 100644
--- a/libs/hwui/tests/common/TestUtils.cpp
+++ b/libs/hwui/tests/common/TestUtils.cpp
@@ -72,7 +72,7 @@
layerUpdater->setTransform(&transform);
// updateLayer so it's ready to draw
- layerUpdater->updateLayer(true, Matrix4::identity().data);
+ layerUpdater->updateLayer(true, Matrix4::identity().data, HAL_DATASPACE_UNKNOWN);
if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
static_cast<GlLayer*>(layerUpdater->backingLayer())
->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
diff --git a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
index b8b5050..f29830f 100644
--- a/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
+++ b/libs/hwui/tests/unit/DeferredLayerUpdaterTests.cpp
@@ -44,7 +44,7 @@
// push the deferred updates to the layer
Matrix4 scaledMatrix;
scaledMatrix.loadScale(0.5, 0.5, 0.0);
- layerUpdater->updateLayer(true, scaledMatrix.data);
+ layerUpdater->updateLayer(true, scaledMatrix.data, HAL_DATASPACE_UNKNOWN);
if (layerUpdater->backingLayer()->getApi() == Layer::Api::OpenGL) {
GlLayer* glLayer = static_cast<GlLayer*>(layerUpdater->backingLayer());
glLayer->setRenderTarget(GL_TEXTURE_EXTERNAL_OES);
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index c2af867..75740e8 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -16,6 +16,8 @@
#include "Color.h"
+
+#include <utils/Log.h>
#include <cmath>
namespace android {
@@ -53,5 +55,57 @@
return false;
}
+sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) {
+
+ SkColorSpace::Gamut gamut;
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ gamut = SkColorSpace::kSRGB_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_BT2020:
+ gamut = SkColorSpace::kRec2020_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ gamut = SkColorSpace::kDCIP3_D65_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ gamut = SkColorSpace::kAdobeRGB_Gamut;
+ break;
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ return nullptr;
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ default:
+ ALOGW("Unsupported Gamut: %d", dataspace);
+ return nullptr;
+ }
+
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ return SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, gamut);
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ return SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
+ return nullptr;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ default:
+ ALOGW("Unsupported Gamma: %d", dataspace);
+ return nullptr;
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h
index 7ac0d96..2bec1f5 100644
--- a/libs/hwui/utils/Color.h
+++ b/libs/hwui/utils/Color.h
@@ -17,6 +17,7 @@
#define COLOR_H
#include <math.h>
+#include <system/graphics.h>
#include <SkColor.h>
#include <SkColorSpace.h>
@@ -111,6 +112,8 @@
// approximated with the native sRGB transfer function. This method
// returns true for sRGB, gamma 2.2 and Display P3 for instance
bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace);
+
+sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace);
} /* namespace uirenderer */
} /* namespace android */