blob: 7eea21f32cbad68013ff6491b3599e6dff2083cb [file] [log] [blame]
/*
* Copyright 2023 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/BlendFormula.h"
#include "include/core/SkBlendMode.h"
namespace {
using skgpu::BlendFormula;
/**
* When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard
* Porter Duff formula.
*/
constexpr BlendFormula MakeCoeffFormula(skgpu::BlendCoeff srcCoeff, skgpu::BlendCoeff dstCoeff) {
// When the coeffs are (Zero, Zero) or (Zero, One) we set the primary output to none.
return (skgpu::BlendCoeff::kZero == srcCoeff &&
(skgpu::BlendCoeff::kZero == dstCoeff || skgpu::BlendCoeff::kOne == dstCoeff))
? BlendFormula(BlendFormula::kNone_OutputType, BlendFormula::kNone_OutputType,
skgpu::BlendEquation::kAdd, skgpu::BlendCoeff::kZero, dstCoeff)
: BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kNone_OutputType,
skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff);
}
/**
* Basic coeff formula similar to MakeCoeffFormula but we will make the src f*Sa. This is used in
* LCD dst-out.
*/
constexpr BlendFormula MakeSAModulateFormula(skgpu::BlendCoeff srcCoeff,
skgpu::BlendCoeff dstCoeff) {
return BlendFormula(BlendFormula::kSAModulate_OutputType, BlendFormula::kNone_OutputType,
skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff);
}
/**
* When there is coverage, the equation with f=coverage is:
*
* D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D
*
* This can be rewritten as:
*
* D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)])
*
* To implement this formula, we output [f * (1 - dstCoeff)] for the secondary color and replace the
* HW dst coeff with IS2C.
*
* Xfer modes: dst-atop (Sa!=1)
*/
constexpr BlendFormula MakeCoverageFormula(BlendFormula::OutputType oneMinusDstCoeffModulateOutput,
skgpu::BlendCoeff srcCoeff) {
return BlendFormula(BlendFormula::kModulate_OutputType, oneMinusDstCoeffModulateOutput,
skgpu::BlendEquation::kAdd, srcCoeff, skgpu::BlendCoeff::kIS2C);
}
/**
* When there is coverage and the src coeff is Zero, the equation with f=coverage becomes:
*
* D' = f * D * dstCoeff + (1-f) * D
*
* This can be rewritten as:
*
* D' = D - D * [f * (1 - dstCoeff)]
*
* To implement this formula, we output [f * (1 - dstCoeff)] for the primary color and use a reverse
* subtract HW blend equation with coeffs of (DC, One).
*
* Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1)
*/
constexpr BlendFormula MakeCoverageSrcCoeffZeroFormula(
BlendFormula::OutputType oneMinusDstCoeffModulateOutput) {
return BlendFormula(oneMinusDstCoeffModulateOutput, BlendFormula::kNone_OutputType,
skgpu::BlendEquation::kReverseSubtract, skgpu::BlendCoeff::kDC,
skgpu::BlendCoeff::kOne);
}
/**
* When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes:
*
* D' = f * S * srcCoeff + (1-f) * D
*
* To implement this formula, we output [f] for the secondary color and replace the HW dst coeff
* with IS2A. (Note that we can avoid dual source blending when Sa=1 by using ISA.)
*
* Xfer modes (Sa!=1): src, src-in, src-out
*/
constexpr BlendFormula MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff srcCoeff) {
return BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kCoverage_OutputType,
skgpu::BlendEquation::kAdd, srcCoeff, skgpu::BlendCoeff::kIS2A);
}
/**
* This table outlines the blend formulas we will use with each xfermode, with and without coverage,
* with and without an opaque input color. Optimization properties are deduced at compile time so we
* can make runtime decisions quickly. RGB coverage is not supported.
*/
constexpr BlendFormula gBlendTable[2][2][(int)SkBlendMode::kLastCoeffMode + 1] = {
/*>> No coverage, input color unknown <<*/ {{
/* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
/* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
/* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
/* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
/* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSA),
/* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
/* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
/* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
/* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kSA),
/* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
/* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
/* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
/* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
}, /*>> Has coverage, input color unknown <<*/ {
/* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
/* src */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kOne),
/* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
/* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* src-in */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kDA),
/* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
/* src-out */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kIDA),
/* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
/* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
/* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType,
skgpu::BlendCoeff::kIDA),
/* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
/* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
/* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
/* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
}}, /*>> No coverage, input color opaque <<*/ {{
/* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
/* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
/* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
skgpu::BlendCoeff::kISA), // see comment below
/* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
/* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
/* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
/* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
/* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
/* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
/* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
/* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
}, /*>> Has coverage, input color opaque <<*/ {
/* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
/* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
/* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
/* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
/* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
/* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
/* dst-out */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
/* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
/* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
/* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
/* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
/* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
/* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
}}};
// In the above table src-over is not optimized to src mode when the color is opaque because we
// found no advantage to doing so. Also, we are using a global src-over blend in most cases which is
// not specialized for opaque input. For GPUs where dropping to src (and thus able to disable
// blending) is an advantage we change the blend mode to src before getting the blend formula from
// this table.
constexpr BlendFormula gLCDBlendTable[(int)SkBlendMode::kLastCoeffMode + 1] = {
/* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
/* src */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
skgpu::BlendCoeff::kOne),
/* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero,
skgpu::BlendCoeff::kOne),
/* src-over */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
skgpu::BlendCoeff::kOne),
/* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA,
skgpu::BlendCoeff::kOne),
/* src-in */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
skgpu::BlendCoeff::kDA),
/* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
/* src-out */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
skgpu::BlendCoeff::kIDA),
/* dst-out */ MakeSAModulateFormula(skgpu::BlendCoeff::kZero,
skgpu::BlendCoeff::kISC),
/* src-atop */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
skgpu::BlendCoeff::kDA),
/* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType,
skgpu::BlendCoeff::kIDA),
/* xor */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
skgpu::BlendCoeff::kIDA),
/* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
skgpu::BlendCoeff::kOne),
/* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
/* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
skgpu::BlendCoeff::kISC),
};
} // anonymous namespace
namespace skgpu {
BlendFormula GetBlendFormula(bool isOpaque, bool hasCoverage, SkBlendMode xfermode) {
SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
return gBlendTable[isOpaque][hasCoverage][(int)xfermode];
}
BlendFormula GetLCDBlendFormula(SkBlendMode xfermode) {
SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
return gLCDBlendTable[(int)xfermode];
}
} // namespace skgpu