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. 12 layout(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 17 layout(ctype=SkPMColor4f) in uniform float4 scale0_1; 18 layout(ctype=SkPMColor4f, when=intervalCount > 1) in uniform float4 scale2_3; 19 layout(ctype=SkPMColor4f, when=intervalCount > 2) in uniform float4 scale4_5; 20 layout(ctype=SkPMColor4f, when=intervalCount > 3) in uniform float4 scale6_7; 21 layout(ctype=SkPMColor4f, when=intervalCount > 4) in uniform float4 scale8_9; 22 layout(ctype=SkPMColor4f, when=intervalCount > 5) in uniform float4 scale10_11; 23 layout(ctype=SkPMColor4f, when=intervalCount > 6) in uniform float4 scale12_13; 24 layout(ctype=SkPMColor4f, when=intervalCount > 7) in uniform float4 scale14_15; 25 26 layout(ctype=SkPMColor4f) in uniform float4 bias0_1; 27 layout(ctype=SkPMColor4f, when=intervalCount > 1) in uniform float4 bias2_3; 28 layout(ctype=SkPMColor4f, when=intervalCount > 2) in uniform float4 bias4_5; 29 layout(ctype=SkPMColor4f, when=intervalCount > 3) in uniform float4 bias6_7; 30 layout(ctype=SkPMColor4f, when=intervalCount > 4) in uniform float4 bias8_9; 31 layout(ctype=SkPMColor4f, when=intervalCount > 5) in uniform float4 bias10_11; 32 layout(ctype=SkPMColor4f, when=intervalCount > 6) in uniform float4 bias12_13; 33 layout(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 47 in uniform half4 thresholds1_7; 48 in uniform half4 thresholds9_13; 49 50 void 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 = half4(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