xref: /aosp_15_r20/external/skia/src/gpu/BlendFormula.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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