1 /*
2 * Copyright 2021 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 "experimental/graphite/src/ContextUtils.h"
9
10 #include <string>
11 #include "experimental/graphite/src/DrawTypes.h"
12 #include "experimental/graphite/src/Uniform.h"
13 #include "experimental/graphite/src/UniformCache.h"
14 #include "experimental/graphite/src/UniformManager.h"
15 #include "include/core/SkPaint.h"
16
17 namespace skgpu {
18
19 namespace {
20
21 // TODO: For the sprint we only support 4 stops in the gradients
22 static constexpr int kMaxStops = 4;
23 // TODO: For the sprint we unify all the gradient uniforms into a standard set of 6:
24 // kMaxStops colors
25 // kMaxStops offsets
26 // 2 points
27 // 2 radii
28 static constexpr int kNumGradientUniforms = 6;
29 static constexpr Uniform kGradientUniforms[kNumGradientUniforms] {
30 {"colors", SLType::kHalf4 , kMaxStops },
31 {"offsets", SLType::kFloat, kMaxStops },
32 {"point0", SLType::kFloat2 },
33 {"point1", SLType::kFloat2 },
34 {"radius0", SLType::kFloat },
35 {"radius1", SLType::kFloat },
36 };
37
38 static constexpr int kNumSolidUniforms = 1;
39 static constexpr Uniform kSolidUniforms[kNumSolidUniforms] {
40 {"color", SLType::kFloat4 }
41 };
42
make_gradient_uniform_data_common(void * srcs[kNumGradientUniforms])43 sk_sp<UniformData> make_gradient_uniform_data_common(void* srcs[kNumGradientUniforms]) {
44 UniformManager mgr(Layout::kMetal);
45
46 // TODO: Given that, for the sprint, we always know the uniforms we could cache 'dataSize'
47 // for each layout and skip the first call.
48 size_t dataSize = mgr.writeUniforms(SkSpan<const Uniform>(kGradientUniforms,
49 kNumGradientUniforms),
50 nullptr, nullptr, nullptr);
51
52 sk_sp<UniformData> result = UniformData::Make(kNumGradientUniforms,
53 kGradientUniforms,
54 dataSize);
55
56 mgr.writeUniforms(SkSpan<const Uniform>(kGradientUniforms, kNumGradientUniforms),
57 srcs, result->offsets(), result->data());
58 return result;
59 }
60
make_linear_gradient_uniform_data(SkPoint startPoint,SkPoint endPoint,SkColor4f colors[kMaxStops],float offsets[kMaxStops])61 sk_sp<UniformData> make_linear_gradient_uniform_data(SkPoint startPoint,
62 SkPoint endPoint,
63 SkColor4f colors[kMaxStops],
64 float offsets[kMaxStops]) {
65 float unusedRadii[2] = { 0.0f, 0.0f };
66 void* srcs[kNumGradientUniforms] = {
67 colors,
68 offsets,
69 &startPoint,
70 &endPoint,
71 &unusedRadii[0],
72 &unusedRadii[1],
73 };
74
75 return make_gradient_uniform_data_common(srcs);
76 };
77
make_radial_gradient_uniform_data(SkPoint point,float radius,SkColor4f colors[kMaxStops],float offsets[kMaxStops])78 sk_sp<UniformData> make_radial_gradient_uniform_data(SkPoint point,
79 float radius,
80 SkColor4f colors[kMaxStops],
81 float offsets[kMaxStops]) {
82 SkPoint unusedPoint = {0.0f, 0.0f};
83 float unusedRadius = 0.0f;
84
85 void* srcs[kNumGradientUniforms] = {
86 colors,
87 offsets,
88 &point,
89 &unusedPoint,
90 &radius,
91 &unusedRadius,
92 };
93
94 return make_gradient_uniform_data_common(srcs);
95 };
96
make_sweep_gradient_uniform_data(SkPoint point,SkColor4f colors[kMaxStops],float offsets[kMaxStops])97 sk_sp<UniformData> make_sweep_gradient_uniform_data(SkPoint point,
98 SkColor4f colors[kMaxStops],
99 float offsets[kMaxStops]) {
100 SkPoint unusedPoint = {0.0f, 0.0f};
101 float unusedRadii[2] = {0.0f, 0.0f};
102
103 void* srcs[kNumGradientUniforms] = {
104 colors,
105 offsets,
106 &point,
107 &unusedPoint,
108 &unusedRadii[0],
109 &unusedRadii[1],
110 };
111
112 return make_gradient_uniform_data_common(srcs);
113 };
114
make_conical_gradient_uniform_data(SkPoint point0,SkPoint point1,float radius0,float radius1,SkColor4f colors[kMaxStops],float offsets[kMaxStops])115 sk_sp<UniformData> make_conical_gradient_uniform_data(SkPoint point0,
116 SkPoint point1,
117 float radius0,
118 float radius1,
119 SkColor4f colors[kMaxStops],
120 float offsets[kMaxStops]) {
121
122 void* srcs[kNumGradientUniforms] = {
123 colors,
124 offsets,
125 &point0,
126 &point1,
127 &radius0,
128 &radius1,
129 };
130
131 return make_gradient_uniform_data_common(srcs);
132 };
133
to_color4fs(int numColors,SkColor colors[kMaxStops],SkColor4f color4fs[kMaxStops])134 void to_color4fs(int numColors, SkColor colors[kMaxStops], SkColor4f color4fs[kMaxStops]) {
135 SkASSERT(numColors >= 2 && numColors <= kMaxStops);
136
137 int i;
138 for (i = 0; i < numColors; ++i) {
139 color4fs[i] = SkColor4f::FromColor(colors[i]);
140 }
141 for ( ; i < kMaxStops; ++i) {
142 color4fs[i] = color4fs[numColors-1];
143 }
144 }
145
expand_stops(int numStops,float offsets[kMaxStops])146 void expand_stops(int numStops, float offsets[kMaxStops]) {
147 SkASSERT(numStops >= 2 && numStops <= kMaxStops);
148
149 for (int i = numStops ; i < kMaxStops; ++i) {
150 offsets[i] = offsets[numStops-1];
151 }
152 }
153
make_solid_uniform_data(SkColor4f color)154 sk_sp<UniformData> make_solid_uniform_data(SkColor4f color) {
155 UniformManager mgr(Layout::kMetal);
156
157 size_t dataSize = mgr.writeUniforms(SkSpan<const Uniform>(kSolidUniforms, kNumSolidUniforms),
158 nullptr, nullptr, nullptr);
159
160 sk_sp<UniformData> result = UniformData::Make(kNumSolidUniforms, kSolidUniforms, dataSize);
161
162 void* srcs[kNumSolidUniforms] = { &color };
163
164 mgr.writeUniforms(SkSpan<const Uniform>(kSolidUniforms, kNumSolidUniforms),
165 srcs, result->offsets(), result->data());
166 return result;
167 }
168
169 } // anonymous namespace
170
Make(int count,const Uniform * uniforms,size_t dataSize)171 sk_sp<UniformData> UniformData::Make(int count,
172 const Uniform* uniforms,
173 size_t dataSize) {
174 // TODO: the offsets and data should just be allocated right after UniformData in an arena
175 uint32_t* offsets = new uint32_t[count];
176 char* data = new char[dataSize];
177
178 return sk_sp<UniformData>(new UniformData(count, uniforms, offsets, data, dataSize));
179 }
180
ExtractCombo(UniformCache * cache,const SkPaint & p)181 std::tuple<Combination, sk_sp<UniformData>> ExtractCombo(UniformCache* cache, const SkPaint& p) {
182 Combination result;
183 sk_sp<UniformData> uniforms;
184
185 if (auto s = p.getShader()) {
186 SkColor colors[kMaxStops];
187 SkColor4f color4fs[kMaxStops];
188 float offsets[kMaxStops];
189 SkShader::GradientInfo gradInfo;
190
191 gradInfo.fColorCount = kMaxStops;
192 gradInfo.fColors = colors;
193 gradInfo.fColorOffsets = offsets;
194
195 SkShader::GradientType type = s->asAGradient(&gradInfo);
196 if (gradInfo.fColorCount > kMaxStops) {
197 type = SkShader::GradientType::kNone_GradientType;
198 }
199
200 switch (type) {
201 case SkShader::kLinear_GradientType: {
202 to_color4fs(gradInfo.fColorCount, colors, color4fs);
203 expand_stops(gradInfo.fColorCount, offsets);
204
205 result.fShaderType = ShaderCombo::ShaderType::kLinearGradient;
206 result.fTileMode = gradInfo.fTileMode;
207
208 uniforms = make_linear_gradient_uniform_data(gradInfo.fPoint[0],
209 gradInfo.fPoint[1],
210 color4fs,
211 offsets);
212 } break;
213 case SkShader::kRadial_GradientType: {
214 to_color4fs(gradInfo.fColorCount, colors, color4fs);
215 expand_stops(gradInfo.fColorCount, offsets);
216
217 result.fShaderType = ShaderCombo::ShaderType::kRadialGradient;
218 result.fTileMode = gradInfo.fTileMode;
219
220 uniforms = make_radial_gradient_uniform_data(gradInfo.fPoint[0],
221 gradInfo.fRadius[0],
222 color4fs,
223 offsets);
224 } break;
225 case SkShader::kSweep_GradientType:
226 to_color4fs(gradInfo.fColorCount, colors, color4fs);
227 expand_stops(gradInfo.fColorCount, offsets);
228
229 result.fShaderType = ShaderCombo::ShaderType::kSweepGradient;
230 result.fTileMode = gradInfo.fTileMode;
231
232 uniforms = make_sweep_gradient_uniform_data(gradInfo.fPoint[0],
233 color4fs,
234 offsets);
235 break;
236 case SkShader::GradientType::kConical_GradientType:
237 to_color4fs(gradInfo.fColorCount, colors, color4fs);
238 expand_stops(gradInfo.fColorCount, offsets);
239
240 result.fShaderType = ShaderCombo::ShaderType::kConicalGradient;
241 result.fTileMode = gradInfo.fTileMode;
242
243 uniforms = make_conical_gradient_uniform_data(gradInfo.fPoint[0],
244 gradInfo.fPoint[1],
245 gradInfo.fRadius[0],
246 gradInfo.fRadius[1],
247 color4fs,
248 offsets);
249 break;
250 case SkShader::GradientType::kColor_GradientType:
251 case SkShader::GradientType::kNone_GradientType:
252 default:
253 result.fShaderType = ShaderCombo::ShaderType::kNone;
254 result.fTileMode = SkTileMode::kRepeat;
255
256 uniforms = make_solid_uniform_data(p.getColor4f());
257 break;
258 }
259 } else {
260 // Solid colored paint
261 result.fShaderType = ShaderCombo::ShaderType::kNone;
262 result.fTileMode = SkTileMode::kRepeat;
263
264 uniforms = make_solid_uniform_data(p.getColor4f());
265 }
266
267 result.fBlendMode = p.getBlendMode_or(SkBlendMode::kSrcOver);
268
269 sk_sp<UniformData> trueUD = cache->findOrCreate(std::move(uniforms));
270 return { result, std::move(trueUD) };
271 }
272
273 namespace {
274
275 // TODO: use a SkSpan for the parameters
emit_MSL_uniform_struct(const Uniform * uniforms,int numUniforms)276 std::string emit_MSL_uniform_struct(const Uniform *uniforms, int numUniforms) {
277 std::string result;
278
279 result.append("struct FragmentUniforms {\n");
280 for (int i = 0; i < numUniforms; ++i) {
281 // TODO: this is sufficient for the sprint but should be changed to use SkSL's
282 // machinery
283 switch (uniforms[i].type()) {
284 case SLType::kFloat4:
285 result.append("vector_float4");
286 break;
287 case SLType::kFloat2:
288 result.append("vector_float2");
289 break;
290 case SLType::kFloat:
291 result.append("float");
292 break;
293 case SLType::kHalf4:
294 result.append("vector_half4");
295 break;
296 default:
297 SkASSERT(0);
298 }
299
300 result.append(" ");
301 result.append(uniforms[i].name());
302 if (uniforms[i].count()) {
303 result.append("[");
304 result.append(std::to_string(uniforms[i].count()));
305 result.append("]");
306 }
307 result.append(";\n");
308 }
309 result.append("};\n");
310 return result;
311 }
312
313 } // anonymous namespace
314
GetMSLUniformStruct(ShaderCombo::ShaderType shaderType)315 std::string GetMSLUniformStruct(ShaderCombo::ShaderType shaderType) {
316 switch (shaderType) {
317 case ShaderCombo::ShaderType::kLinearGradient:
318 case ShaderCombo::ShaderType::kRadialGradient:
319 case ShaderCombo::ShaderType::kSweepGradient:
320 case ShaderCombo::ShaderType::kConicalGradient:
321 return emit_MSL_uniform_struct(kGradientUniforms, kNumGradientUniforms);
322 case ShaderCombo::ShaderType::kNone:
323 default:
324 return emit_MSL_uniform_struct(kSolidUniforms, kNumSolidUniforms);
325 }
326 }
327
328 } // namespace skgpu
329