1/*
2 * Copyright 2018 Google Inc.
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// Unrolled gradient code supporting up to 8 intervals that produces code
9// targeting a specific interval count.
10
11// Assumed to be between 1 and 8.
12layout(key) in int intervalCount;
13
14// With the current hardstop detection threshold of 0.00024, the maximum scale and bias values
15// will be on the order of 4k (since they divide by dt). That is well outside the precision
16// capabilities of half floats, which can lead to inaccurate gradient calculations
17layout(ctype=SkPMColor4f) in uniform float4 scale0_1;
18layout(ctype=SkPMColor4f, when=intervalCount > 1) in uniform float4 scale2_3;
19layout(ctype=SkPMColor4f, when=intervalCount > 2) in uniform float4 scale4_5;
20layout(ctype=SkPMColor4f, when=intervalCount > 3) in uniform float4 scale6_7;
21layout(ctype=SkPMColor4f, when=intervalCount > 4) in uniform float4 scale8_9;
22layout(ctype=SkPMColor4f, when=intervalCount > 5) in uniform float4 scale10_11;
23layout(ctype=SkPMColor4f, when=intervalCount > 6) in uniform float4 scale12_13;
24layout(ctype=SkPMColor4f, when=intervalCount > 7) in uniform float4 scale14_15;
25
26layout(ctype=SkPMColor4f) in uniform float4 bias0_1;
27layout(ctype=SkPMColor4f, when=intervalCount > 1) in uniform float4 bias2_3;
28layout(ctype=SkPMColor4f, when=intervalCount > 2) in uniform float4 bias4_5;
29layout(ctype=SkPMColor4f, when=intervalCount > 3) in uniform float4 bias6_7;
30layout(ctype=SkPMColor4f, when=intervalCount > 4) in uniform float4 bias8_9;
31layout(ctype=SkPMColor4f, when=intervalCount > 5) in uniform float4 bias10_11;
32layout(ctype=SkPMColor4f, when=intervalCount > 6) in uniform float4 bias12_13;
33layout(ctype=SkPMColor4f, when=intervalCount > 7) in uniform float4 bias14_15;
34
35// The 7 threshold positions that define the boundaries of the 8 intervals (excluding t = 0, and t =
36// 1) are packed into two half4's instead of having up to 7 separate scalar uniforms. For low
37// interval counts, the extra components are ignored in the shader, but the uniform simplification
38// is worth it. It is assumed thresholds are provided in increasing value, mapped as:
39//  - thresholds1_7.x = boundary between (0,1) and (2,3) -> 1_2
40//  -              .y = boundary between (2,3) and (4,5) -> 3_4
41//  -              .z = boundary between (4,5) and (6,7) -> 5_6
42//  -              .w = boundary between (6,7) and (8,9) -> 7_8
43//  - thresholds9_13.x = boundary between (8,9) and (10,11) -> 9_10
44//  -               .y = boundary between (10,11) and (12,13) -> 11_12
45//  -               .z = boundary between (12,13) and (14,15) -> 13_14
46//  -               .w = unused
47in uniform half4 thresholds1_7;
48in uniform half4 thresholds9_13;
49
50void main() {
51    half t = sk_InColor.x;
52
53    float4 scale, bias;
54    // Explicit binary search for the proper interval that t falls within. The interval count
55    // checks are converted into constant expressions in the C++ generated SkSL, which are then
56    // optimized to the minimal number of branches for the specific interval count.
57
58    // thresholds1_7.w is mid point for intervals (0,7) and (8,15)
59    if (intervalCount <= 4 || t < thresholds1_7.w) {
60        // thresholds1_7.y is mid point for intervals (0,3) and (4,7)
61        if (intervalCount <= 2 || t < thresholds1_7.y) {
62            // thresholds1_7.x is mid point for intervals (0,1) and (2,3)
63            if (intervalCount <= 1 || t < thresholds1_7.x) {
64                scale = scale0_1;
65                bias = bias0_1;
66            } else {
67                scale = scale2_3;
68                bias = bias2_3;
69            }
70        } else {
71            // thresholds1_7.z is mid point for intervals (4,5) and (6,7)
72            if (intervalCount <= 3 || t < thresholds1_7.z) {
73                scale = scale4_5;
74                bias = bias4_5;
75            } else {
76                scale = scale6_7;
77                bias = bias6_7;
78            }
79        }
80    } else {
81        // thresholds9_13.y is mid point for intervals (8,11) and (12,15)
82        if (intervalCount <= 6 || t < thresholds9_13.y) {
83            // thresholds9_13.x is mid point for intervals (8,9) and (10,11)
84            if (intervalCount <= 5 || t < thresholds9_13.x) {
85                // interval 8-9
86                scale = scale8_9;
87                bias = bias8_9;
88            } else {
89                // interval 10-11
90                scale = scale10_11;
91                bias = bias10_11;
92            }
93        } else {
94            // thresholds9_13.z is mid point for intervals (12,13) and (14,15)
95            if (intervalCount <= 7 || t < thresholds9_13.z) {
96                // interval 12-13
97                scale = scale12_13;
98                bias = bias12_13;
99            } else {
100                // interval 14-15
101                scale = scale14_15;
102                bias = bias14_15;
103            }
104        }
105    }
106
107    sk_OutColor = t * scale + bias;
108}
109
110//////////////////////////////////////////////////////////////////////////////
111
112@class {
113    static const int kMaxColorCount = 16;
114}
115
116@make {
117    static std::unique_ptr<GrFragmentProcessor> Make(const SkPMColor4f* colors,
118                                                     const SkScalar* positions,
119                                                     int count);
120}
121
122@cppEnd {
123    static const int kMaxIntervals = 8;
124    std::unique_ptr<GrFragmentProcessor> GrUnrolledBinaryGradientColorizer::Make(
125            const SkPMColor4f* colors, const SkScalar* positions, int count) {
126        // Depending on how the positions resolve into hard stops or regular stops, the number of
127        // intervals specified by the number of colors/positions can change. For instance, a plain
128        // 3 color gradient is two intervals, but a 4 color gradient with a hard stop is also
129        // two intervals. At the most extreme end, an 8 interval gradient made entirely of hard
130        // stops has 16 colors.
131
132        if (count > kMaxColorCount) {
133            // Definitely cannot represent this gradient configuration
134            return nullptr;
135        }
136
137        // The raster implementation also uses scales and biases, but since they must be calculated
138        // after the dst color space is applied, it limits our ability to cache their values.
139        SkPMColor4f scales[kMaxIntervals];
140        SkPMColor4f biases[kMaxIntervals];
141        SkScalar thresholds[kMaxIntervals];
142
143        int intervalCount = 0;
144
145        for (int i = 0; i < count - 1; i++) {
146            if (intervalCount >= kMaxIntervals) {
147                // Already reached kMaxIntervals, and haven't run out of color stops so this
148                // gradient cannot be represented by this shader.
149                return nullptr;
150            }
151
152            SkScalar t0 = positions[i];
153            SkScalar t1 = positions[i + 1];
154            SkScalar dt = t1 - t0;
155            // If the interval is empty, skip to the next interval. This will automatically create
156            // distinct hard stop intervals as needed. It also protects against malformed gradients
157            // that have repeated hard stops at the very beginning that are effectively unreachable.
158            if (SkScalarNearlyZero(dt)) {
159                continue;
160            }
161
162            auto c0 = Sk4f::Load(colors[i].vec());
163            auto c1 = Sk4f::Load(colors[i + 1].vec());
164
165            auto scale = (c1 - c0) / dt;
166            auto bias = c0 - t0 * scale;
167
168            scale.store(scales + intervalCount);
169            bias.store(biases + intervalCount);
170            thresholds[intervalCount] = t1;
171            intervalCount++;
172        }
173
174        // For isEqual to make sense, set the unused values to something consistent
175        for (int i = intervalCount; i < kMaxIntervals; i++) {
176            scales[i] = SK_PMColor4fTRANSPARENT;
177            biases[i] = SK_PMColor4fTRANSPARENT;
178            thresholds[i] = 0.0;
179        }
180
181        return std::unique_ptr<GrFragmentProcessor>(new GrUnrolledBinaryGradientColorizer(
182                intervalCount, scales[0], scales[1], scales[2], scales[3], scales[4], scales[5],
183                scales[6], scales[7], biases[0], biases[1], biases[2], biases[3], biases[4],
184                biases[5], biases[6], biases[7],
185                SkRect::MakeLTRB(thresholds[0], thresholds[1], thresholds[2], thresholds[3]),
186                SkRect::MakeLTRB(thresholds[4], thresholds[5], thresholds[6], 0.0)));
187    }
188}
189