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
Merged-In: Ic0446a0d861e86a5a9d0382346b57fcc45c8a61b
Change-Id: Ic0446a0d861e86a5a9d0382346b57fcc45c8a61b
This commit is contained in:
Derek Sollenberger
2018-04-20 16:13:31 -04:00
parent 9ea13ca0b6
commit 551d08e2d5
16 changed files with 147 additions and 32 deletions

View File

@@ -40,7 +40,6 @@ DeferredLayerUpdater::DeferredLayerUpdater(RenderState& renderState, CreateLayer
}
DeferredLayerUpdater::~DeferredLayerUpdater() {
SkSafeUnref(mColorFilter);
setTransform(nullptr);
mRenderState.unregisterDeferredLayerUpdater(this);
destroyLayer();
@@ -67,8 +66,11 @@ void DeferredLayerUpdater::destroyLayer() {
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 @@ void DeferredLayerUpdater::doUpdateTexImage() {
#endif
mSurfaceTexture->getTransformMatrix(transform);
updateLayer(forceFilter, transform);
updateLayer(forceFilter, transform, mSurfaceTexture->getCurrentDataSpace());
}
}
@@ -153,17 +155,19 @@ void DeferredLayerUpdater::doUpdateVkTexImage() {
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() {

View File

@@ -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 @@ public:
// 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 @@ public:
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 @@ private:
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;

View File

@@ -32,7 +32,7 @@ namespace android {
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) {

View File

@@ -32,7 +32,7 @@ class Caches;
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; }

View File

@@ -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() {

View File

@@ -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 @@ public:
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 @@ public:
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.

View File

@@ -28,7 +28,7 @@ namespace uirenderer {
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)

View File

@@ -51,8 +51,13 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
GrGLTextureInfo externalTexture;
externalTexture.fTarget = glLayer->getRenderTarget();
externalTexture.fID = glLayer->getTextureId();
GrBackendTexture backendTexture(layerWidth, layerHeight, kRGBA_8888_GrPixelConfig,
externalTexture);
// 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,
kPremul_SkAlphaType, nullptr);
} else {
@@ -82,7 +87,7 @@ bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer
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) {

View File

@@ -152,7 +152,7 @@ bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBi
}
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();

View File

@@ -147,6 +147,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
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 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque,
}
if (cachedContext.get()) {
ATRACE_NAME("flush layers");
cachedContext->flush();
}
}

View File

@@ -115,7 +115,8 @@ bool SkiaVulkanPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bi
}
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);
}

View File

@@ -123,7 +123,8 @@ bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap
}
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);
Caches::getInstance().textureState().activateTexture(0);

View File

@@ -75,7 +75,7 @@ sp<DeferredLayerUpdater> TestUtils::createTextureLayerUpdater(
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);

View File

@@ -44,7 +44,7 @@ RENDERTHREAD_TEST(DeferredLayerUpdater, updateLayer) {
// 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);

View File

@@ -16,6 +16,8 @@
#include "Color.h"
#include <utils/Log.h>
#include <cmath>
namespace android {
@@ -53,5 +55,57 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) {
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

View File

@@ -17,6 +17,7 @@
#define COLOR_H
#include <math.h>
#include <system/graphics.h>
#include <SkColor.h>
#include <SkColorSpace.h>
@@ -111,6 +112,8 @@ static constexpr float EOCF(float srgb) {
// 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 */