1 /*
2 * Copyright 2023 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/BlendFormula.h"
9
10 #include "include/core/SkBlendMode.h"
11
12 namespace {
13
14 using skgpu::BlendFormula;
15
16 /**
17 * When there is no coverage, or the blend mode can tweak alpha for coverage, we use the standard
18 * Porter Duff formula.
19 */
MakeCoeffFormula(skgpu::BlendCoeff srcCoeff,skgpu::BlendCoeff dstCoeff)20 constexpr BlendFormula MakeCoeffFormula(skgpu::BlendCoeff srcCoeff, skgpu::BlendCoeff dstCoeff) {
21 // When the coeffs are (Zero, Zero) or (Zero, One) we set the primary output to none.
22 return (skgpu::BlendCoeff::kZero == srcCoeff &&
23 (skgpu::BlendCoeff::kZero == dstCoeff || skgpu::BlendCoeff::kOne == dstCoeff))
24 ? BlendFormula(BlendFormula::kNone_OutputType, BlendFormula::kNone_OutputType,
25 skgpu::BlendEquation::kAdd, skgpu::BlendCoeff::kZero, dstCoeff)
26 : BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kNone_OutputType,
27 skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff);
28 }
29
30 /**
31 * Basic coeff formula similar to MakeCoeffFormula but we will make the src f*Sa. This is used in
32 * LCD dst-out.
33 */
MakeSAModulateFormula(skgpu::BlendCoeff srcCoeff,skgpu::BlendCoeff dstCoeff)34 constexpr BlendFormula MakeSAModulateFormula(skgpu::BlendCoeff srcCoeff,
35 skgpu::BlendCoeff dstCoeff) {
36 return BlendFormula(BlendFormula::kSAModulate_OutputType, BlendFormula::kNone_OutputType,
37 skgpu::BlendEquation::kAdd, srcCoeff, dstCoeff);
38 }
39
40 /**
41 * When there is coverage, the equation with f=coverage is:
42 *
43 * D' = f * (S * srcCoeff + D * dstCoeff) + (1-f) * D
44 *
45 * This can be rewritten as:
46 *
47 * D' = f * S * srcCoeff + D * (1 - [f * (1 - dstCoeff)])
48 *
49 * To implement this formula, we output [f * (1 - dstCoeff)] for the secondary color and replace the
50 * HW dst coeff with IS2C.
51 *
52 * Xfer modes: dst-atop (Sa!=1)
53 */
MakeCoverageFormula(BlendFormula::OutputType oneMinusDstCoeffModulateOutput,skgpu::BlendCoeff srcCoeff)54 constexpr BlendFormula MakeCoverageFormula(BlendFormula::OutputType oneMinusDstCoeffModulateOutput,
55 skgpu::BlendCoeff srcCoeff) {
56 return BlendFormula(BlendFormula::kModulate_OutputType, oneMinusDstCoeffModulateOutput,
57 skgpu::BlendEquation::kAdd, srcCoeff, skgpu::BlendCoeff::kIS2C);
58 }
59
60 /**
61 * When there is coverage and the src coeff is Zero, the equation with f=coverage becomes:
62 *
63 * D' = f * D * dstCoeff + (1-f) * D
64 *
65 * This can be rewritten as:
66 *
67 * D' = D - D * [f * (1 - dstCoeff)]
68 *
69 * To implement this formula, we output [f * (1 - dstCoeff)] for the primary color and use a reverse
70 * subtract HW blend equation with coeffs of (DC, One).
71 *
72 * Xfer modes: clear, dst-out (Sa=1), dst-in (Sa!=1), modulate (Sc!=1)
73 */
MakeCoverageSrcCoeffZeroFormula(BlendFormula::OutputType oneMinusDstCoeffModulateOutput)74 constexpr BlendFormula MakeCoverageSrcCoeffZeroFormula(
75 BlendFormula::OutputType oneMinusDstCoeffModulateOutput) {
76 return BlendFormula(oneMinusDstCoeffModulateOutput, BlendFormula::kNone_OutputType,
77 skgpu::BlendEquation::kReverseSubtract, skgpu::BlendCoeff::kDC,
78 skgpu::BlendCoeff::kOne);
79 }
80
81 /**
82 * When there is coverage and the dst coeff is Zero, the equation with f=coverage becomes:
83 *
84 * D' = f * S * srcCoeff + (1-f) * D
85 *
86 * To implement this formula, we output [f] for the secondary color and replace the HW dst coeff
87 * with IS2A. (Note that we can avoid dual source blending when Sa=1 by using ISA.)
88 *
89 * Xfer modes (Sa!=1): src, src-in, src-out
90 */
MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff srcCoeff)91 constexpr BlendFormula MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff srcCoeff) {
92 return BlendFormula(BlendFormula::kModulate_OutputType, BlendFormula::kCoverage_OutputType,
93 skgpu::BlendEquation::kAdd, srcCoeff, skgpu::BlendCoeff::kIS2A);
94 }
95
96 /**
97 * This table outlines the blend formulas we will use with each xfermode, with and without coverage,
98 * with and without an opaque input color. Optimization properties are deduced at compile time so we
99 * can make runtime decisions quickly. RGB coverage is not supported.
100 */
101 constexpr BlendFormula gBlendTable[2][2][(int)SkBlendMode::kLastCoeffMode + 1] = {
102 /*>> No coverage, input color unknown <<*/ {{
103
104 /* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
105 /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
106 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
107 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
108 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
109 /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
110 /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSA),
111 /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
112 /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
113 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
114 /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kSA),
115 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
116 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
117 /* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
118 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
119
120 }, /*>> Has coverage, input color unknown <<*/ {
121
122 /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
123 /* src */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kOne),
124 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
125 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
126 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
127 /* src-in */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kDA),
128 /* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
129 /* src-out */ MakeCoverageDstCoeffZeroFormula(skgpu::BlendCoeff::kIDA),
130 /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kISA),
131 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
132 /* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType,
133 skgpu::BlendCoeff::kIDA),
134 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
135 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
136 /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
137 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
138
139 }}, /*>> No coverage, input color opaque <<*/ {{
140
141 /* clear */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
142 /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kZero),
143 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
144 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
145 skgpu::BlendCoeff::kISA), // see comment below
146 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
147 /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
148 /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
149 /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
150 /* dst-out */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kZero),
151 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kZero),
152 /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
153 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kZero),
154 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
155 /* modulate */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kSC),
156 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
157
158 }, /*>> Has coverage, input color opaque <<*/ {
159
160 /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
161 /* src */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
162 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
163 /* src-over */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISA),
164 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
165 /* src-in */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
166 /* dst-in */ MakeCoeffFormula(skgpu::BlendCoeff::kZero, skgpu::BlendCoeff::kOne),
167 /* src-out */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
168 /* dst-out */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
169 /* src-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kDA, skgpu::BlendCoeff::kISA),
170 /* dst-atop */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kOne),
171 /* xor */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA, skgpu::BlendCoeff::kISA),
172 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kOne),
173 /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
174 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne, skgpu::BlendCoeff::kISC),
175 }}};
176 // In the above table src-over is not optimized to src mode when the color is opaque because we
177 // found no advantage to doing so. Also, we are using a global src-over blend in most cases which is
178 // not specialized for opaque input. For GPUs where dropping to src (and thus able to disable
179 // blending) is an advantage we change the blend mode to src before getting the blend formula from
180 // this table.
181
182 constexpr BlendFormula gLCDBlendTable[(int)SkBlendMode::kLastCoeffMode + 1] = {
183 /* clear */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kCoverage_OutputType),
184 /* src */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
185 skgpu::BlendCoeff::kOne),
186 /* dst */ MakeCoeffFormula(skgpu::BlendCoeff::kZero,
187 skgpu::BlendCoeff::kOne),
188 /* src-over */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
189 skgpu::BlendCoeff::kOne),
190 /* dst-over */ MakeCoeffFormula(skgpu::BlendCoeff::kIDA,
191 skgpu::BlendCoeff::kOne),
192 /* src-in */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
193 skgpu::BlendCoeff::kDA),
194 /* dst-in */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISAModulate_OutputType),
195 /* src-out */ MakeCoverageFormula(BlendFormula::kCoverage_OutputType,
196 skgpu::BlendCoeff::kIDA),
197 /* dst-out */ MakeSAModulateFormula(skgpu::BlendCoeff::kZero,
198 skgpu::BlendCoeff::kISC),
199 /* src-atop */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
200 skgpu::BlendCoeff::kDA),
201 /* dst-atop */ MakeCoverageFormula(BlendFormula::kISAModulate_OutputType,
202 skgpu::BlendCoeff::kIDA),
203 /* xor */ MakeCoverageFormula(BlendFormula::kSAModulate_OutputType,
204 skgpu::BlendCoeff::kIDA),
205 /* plus */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
206 skgpu::BlendCoeff::kOne),
207 /* modulate */ MakeCoverageSrcCoeffZeroFormula(BlendFormula::kISCModulate_OutputType),
208 /* screen */ MakeCoeffFormula(skgpu::BlendCoeff::kOne,
209 skgpu::BlendCoeff::kISC),
210 };
211
212 } // anonymous namespace
213
214 namespace skgpu {
215
GetBlendFormula(bool isOpaque,bool hasCoverage,SkBlendMode xfermode)216 BlendFormula GetBlendFormula(bool isOpaque, bool hasCoverage, SkBlendMode xfermode) {
217 SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
218 return gBlendTable[isOpaque][hasCoverage][(int)xfermode];
219 }
220
GetLCDBlendFormula(SkBlendMode xfermode)221 BlendFormula GetLCDBlendFormula(SkBlendMode xfermode) {
222 SkASSERT((unsigned)xfermode <= (unsigned)SkBlendMode::kLastCoeffMode);
223 return gLCDBlendTable[(int)xfermode];
224 }
225
226 } // namespace skgpu
227