Support premul/unpremul of F16 during read/writePixels
Added PremulOutput and UnpremulOutput FP helpers. These are used (rather than GrConfigConversionEffect) when working with FP16 textures (and will also be used for other configs that can't be round-tripped via rounding). BUG=skia:5853 Change-Id: I101592c26c4f0b379d5e5a8678ef7b2f08e6ad56 Reviewed-on: https://skia-review.googlesource.com/9980 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
parent
ca0913ceb9
commit
de1a605346
@ -259,6 +259,15 @@ struct GrColor4f {
|
||||
float a = fRGBA[3];
|
||||
return GrColor4f(fRGBA[0] * a, fRGBA[1] * a, fRGBA[2] * a, a);
|
||||
}
|
||||
|
||||
GrColor4f unpremul() const {
|
||||
float a = fRGBA[3];
|
||||
if (a <= 0.0f) {
|
||||
return GrColor4f(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
float invAlpha = 1.0f / a;
|
||||
return GrColor4f(fRGBA[0] * invAlpha, fRGBA[1] * invAlpha, fRGBA[2] * invAlpha, a);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -432,9 +432,9 @@ private:
|
||||
/** Called before either of the above two functions to determine the appropriate fragment
|
||||
processors for conversions. */
|
||||
void testPMConversionsIfNecessary(uint32_t flags);
|
||||
/** Returns true if we've already determined that createPMtoUPMEffect and createUPMToPMEffect
|
||||
will fail. In such cases fall back to SW conversion. */
|
||||
bool didFailPMUPMConversionTest() const;
|
||||
/** Returns true if we've determined that createPMtoUPMEffect and createUPMToPMEffect will
|
||||
succeed for the passed in config. Otherwise we fall back to SW conversion. */
|
||||
bool validPMUPMConversionExists(GrPixelConfig) const;
|
||||
|
||||
/**
|
||||
* A callback similar to the above for use by the TextBlobCache
|
||||
|
@ -55,6 +55,18 @@ public:
|
||||
*/
|
||||
static sk_sp<GrFragmentProcessor> PremulInput(sk_sp<GrFragmentProcessor>);
|
||||
|
||||
/**
|
||||
* Returns a fragment processor that calls the passed in fragment processor, and then premuls
|
||||
* the output.
|
||||
*/
|
||||
static sk_sp<GrFragmentProcessor> PremulOutput(sk_sp<GrFragmentProcessor>);
|
||||
|
||||
/**
|
||||
* Returns a fragment processor that calls the passed in fragment processor, and then unpremuls
|
||||
* the output.
|
||||
*/
|
||||
static sk_sp<GrFragmentProcessor> UnpremulOutput(sk_sp<GrFragmentProcessor>);
|
||||
|
||||
/**
|
||||
* Returns a fragment processor that runs the passed in array of fragment processors in a
|
||||
* series. The original input is passed to the first, the first's output is passed to the
|
||||
|
@ -266,7 +266,7 @@ bool GrContext::writeSurfacePixels(GrSurface* surface, SkColorSpace* dstColorSpa
|
||||
|
||||
bool applyPremulToSrc = false;
|
||||
if (kUnpremul_PixelOpsFlag & pixelOpsFlags) {
|
||||
if (!GrPixelConfigIs8888Unorm(srcConfig)) {
|
||||
if (!GrPixelConfigIs8888Unorm(srcConfig) && kRGBA_half_GrPixelConfig != srcConfig) {
|
||||
return false;
|
||||
}
|
||||
applyPremulToSrc = true;
|
||||
@ -279,7 +279,7 @@ bool GrContext::writeSurfacePixels(GrSurface* surface, SkColorSpace* dstColorSpa
|
||||
GrGpu::DrawPreference drawPreference = GrGpu::kNoDraw_DrawPreference;
|
||||
// Don't prefer to draw for the conversion (and thereby access a texture from the cache) when
|
||||
// we've already determined that there isn't a roundtrip preserving conversion processor pair.
|
||||
if (applyPremulToSrc && !this->didFailPMUPMConversionTest()) {
|
||||
if (applyPremulToSrc && this->validPMUPMConversionExists(srcConfig)) {
|
||||
drawPreference = GrGpu::kCallerPrefersDraw_DrawPreference;
|
||||
}
|
||||
|
||||
@ -422,8 +422,8 @@ bool GrContext::readSurfacePixels(GrSurface* src, SkColorSpace* srcColorSpace,
|
||||
}
|
||||
|
||||
bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
|
||||
if (unpremul && !GrPixelConfigIs8888Unorm(dstConfig)) {
|
||||
// The unpremul flag is only allowed for 8888 configs.
|
||||
if (unpremul && !GrPixelConfigIs8888Unorm(dstConfig) && kRGBA_half_GrPixelConfig != dstConfig) {
|
||||
// The unpremul flag is only allowed for 8888 and F16 configs.
|
||||
return false;
|
||||
}
|
||||
// We don't allow conversion between integer configs and float/fixed configs.
|
||||
@ -434,7 +434,7 @@ bool GrContext::readSurfacePixels(GrSurface* src, SkColorSpace* srcColorSpace,
|
||||
GrGpu::DrawPreference drawPreference = GrGpu::kNoDraw_DrawPreference;
|
||||
// Don't prefer to draw for the conversion (and thereby access a texture from the cache) when
|
||||
// we've already determined that there isn't a roundtrip preserving conversion processor pair.
|
||||
if (unpremul && !this->didFailPMUPMConversionTest()) {
|
||||
if (unpremul && this->validPMUPMConversionExists(src->config())) {
|
||||
drawPreference = GrGpu::kCallerPrefersDraw_DrawPreference;
|
||||
}
|
||||
|
||||
@ -865,6 +865,11 @@ sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(GrTexture* texture,
|
||||
ASSERT_SINGLE_OWNER
|
||||
// We should have already called this->testPMConversionsIfNecessary().
|
||||
SkASSERT(fDidTestPMConversions);
|
||||
if (kRGBA_half_GrPixelConfig == texture->config()) {
|
||||
SkASSERT(swizzle == GrSwizzle::RGBA());
|
||||
return GrFragmentProcessor::UnpremulOutput(
|
||||
GrSimpleTextureEffect::Make(texture, nullptr, matrix));
|
||||
} else {
|
||||
GrConfigConversionEffect::PMConversion pmToUPM =
|
||||
static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
|
||||
if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
|
||||
@ -872,6 +877,7 @@ sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(GrTexture* texture,
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrTextureProxy> proxy,
|
||||
@ -880,14 +886,21 @@ sk_sp<GrFragmentProcessor> GrContext::createPMToUPMEffect(sk_sp<GrTextureProxy>
|
||||
ASSERT_SINGLE_OWNER
|
||||
// We should have already called this->testPMConversionsIfNecessary().
|
||||
SkASSERT(fDidTestPMConversions);
|
||||
if (kRGBA_half_GrPixelConfig == proxy->config()) {
|
||||
SkASSERT(swizzle == GrSwizzle::RGBA());
|
||||
return GrFragmentProcessor::UnpremulOutput(
|
||||
GrSimpleTextureEffect::Make(this->resourceProvider(), std::move(proxy),
|
||||
nullptr, matrix));
|
||||
} else {
|
||||
GrConfigConversionEffect::PMConversion pmToUPM =
|
||||
static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
|
||||
if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
|
||||
return GrConfigConversionEffect::Make(this->resourceProvider(),
|
||||
proxy, swizzle, pmToUPM, matrix);
|
||||
return GrConfigConversionEffect::Make(this->resourceProvider(), std::move(proxy),
|
||||
swizzle, pmToUPM, matrix);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrTextureProxy> proxy,
|
||||
@ -896,22 +909,31 @@ sk_sp<GrFragmentProcessor> GrContext::createUPMToPMEffect(sk_sp<GrTextureProxy>
|
||||
ASSERT_SINGLE_OWNER
|
||||
// We should have already called this->testPMConversionsIfNecessary().
|
||||
SkASSERT(fDidTestPMConversions);
|
||||
if (kRGBA_half_GrPixelConfig == proxy->config()) {
|
||||
SkASSERT(swizzle == GrSwizzle::RGBA());
|
||||
return GrFragmentProcessor::PremulOutput(
|
||||
GrSimpleTextureEffect::Make(this->resourceProvider(), std::move(proxy),
|
||||
nullptr, matrix));
|
||||
} else {
|
||||
GrConfigConversionEffect::PMConversion upmToPM =
|
||||
static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
|
||||
if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
|
||||
return GrConfigConversionEffect::Make(this->resourceProvider(),
|
||||
std::move(proxy), swizzle, upmToPM, matrix);
|
||||
return GrConfigConversionEffect::Make(this->resourceProvider(), std::move(proxy),
|
||||
swizzle, upmToPM, matrix);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GrContext::didFailPMUPMConversionTest() const {
|
||||
bool GrContext::validPMUPMConversionExists(GrPixelConfig config) const {
|
||||
ASSERT_SINGLE_OWNER
|
||||
// We should have already called this->testPMConversionsIfNecessary().
|
||||
SkASSERT(fDidTestPMConversions);
|
||||
// The PM<->UPM tests fail or succeed together so we only need to check one.
|
||||
return GrConfigConversionEffect::kNone_PMConversion == fPMToUPMConversion;
|
||||
// For F16, we always allow PM/UPM conversion on the GPU, even if it doesn't round-trip.
|
||||
return GrConfigConversionEffect::kNone_PMConversion != fPMToUPMConversion ||
|
||||
kRGBA_half_GrPixelConfig == config;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -106,10 +106,10 @@ sk_sp<GrFragmentProcessor> GrFragmentProcessor::MulOutputByInputAlpha(
|
||||
return GrXfermodeFragmentProcessor::MakeFromDstProcessor(std::move(fp), SkBlendMode::kDstIn);
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulInput(sk_sp<GrFragmentProcessor> fp) {
|
||||
namespace {
|
||||
|
||||
class PremulInputFragmentProcessor : public GrFragmentProcessor {
|
||||
public:
|
||||
class PremulInputFragmentProcessor : public GrFragmentProcessor {
|
||||
public:
|
||||
PremulInputFragmentProcessor()
|
||||
: INHERITED(kPreservesOpaqueInput_OptimizationFlag |
|
||||
kConstantOutputForConstantInput_OptimizationFlag) {
|
||||
@ -117,7 +117,8 @@ sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulInput(sk_sp<GrFragmentProc
|
||||
}
|
||||
|
||||
const char* name() const override { return "PremultiplyInput"; }
|
||||
private:
|
||||
|
||||
private:
|
||||
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
||||
class GLFP : public GrGLSLFragmentProcessor {
|
||||
public:
|
||||
@ -141,7 +142,48 @@ sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulInput(sk_sp<GrFragmentProc
|
||||
}
|
||||
|
||||
typedef GrFragmentProcessor INHERITED;
|
||||
};
|
||||
|
||||
class UnpremulInputFragmentProcessor : public GrFragmentProcessor {
|
||||
public:
|
||||
UnpremulInputFragmentProcessor()
|
||||
: INHERITED(kPreservesOpaqueInput_OptimizationFlag |
|
||||
kConstantOutputForConstantInput_OptimizationFlag) {
|
||||
this->initClassID<UnpremulInputFragmentProcessor>();
|
||||
}
|
||||
|
||||
const char* name() const override { return "UnpremultiplyInput"; }
|
||||
|
||||
private:
|
||||
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
||||
class GLFP : public GrGLSLFragmentProcessor {
|
||||
public:
|
||||
void emitCode(EmitArgs& args) override {
|
||||
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||
|
||||
fragBuilder->codeAppendf("%s = %s;", args.fOutputColor, args.fInputColor);
|
||||
fragBuilder->codeAppendf("float invAlpha = %s.a <= 0.0 ? 0.0 : 1.0 / %s.a;",
|
||||
args.fInputColor, args.fInputColor);
|
||||
fragBuilder->codeAppendf("%s.rgb *= invAlpha;", args.fOutputColor);
|
||||
}
|
||||
};
|
||||
return new GLFP;
|
||||
}
|
||||
|
||||
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
|
||||
|
||||
bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
|
||||
|
||||
GrColor4f constantOutputForConstantInput(GrColor4f input) const override {
|
||||
return input.unpremul();
|
||||
}
|
||||
|
||||
typedef GrFragmentProcessor INHERITED;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulInput(sk_sp<GrFragmentProcessor> fp) {
|
||||
if (!fp) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -149,6 +191,22 @@ sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulInput(sk_sp<GrFragmentProc
|
||||
return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrFragmentProcessor::PremulOutput(sk_sp<GrFragmentProcessor> fp) {
|
||||
if (!fp) {
|
||||
return nullptr;
|
||||
}
|
||||
sk_sp<GrFragmentProcessor> fpPipeline[] = { fp, sk_make_sp<PremulInputFragmentProcessor>() };
|
||||
return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrFragmentProcessor::UnpremulOutput(sk_sp<GrFragmentProcessor> fp) {
|
||||
if (!fp) {
|
||||
return nullptr;
|
||||
}
|
||||
sk_sp<GrFragmentProcessor> fpPipeline[] = { fp, sk_make_sp<UnpremulInputFragmentProcessor>() };
|
||||
return GrFragmentProcessor::RunInSeries(fpPipeline, 2);
|
||||
}
|
||||
|
||||
sk_sp<GrFragmentProcessor> GrFragmentProcessor::MakeInputPremulAndMulByOutput(
|
||||
sk_sp<GrFragmentProcessor> fp) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user