xref: /aosp_15_r20/external/skia/bench/SkSLBench.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2019 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 #include "bench/Benchmark.h"
8 #include "bench/ResultsWriter.h"
9 #include "bench/SkSLBench.h"
10 #include "include/core/SkCanvas.h"
11 #include "src/base/SkArenaAlloc.h"
12 #include "src/core/SkRasterPipeline.h"
13 #include "src/gpu/ganesh/GrCaps.h"
14 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
15 #include "src/gpu/ganesh/mock/GrMockCaps.h"
16 #include "src/sksl/SkSLCompiler.h"
17 #include "src/sksl/SkSLModuleLoader.h"
18 #include "src/sksl/SkSLParser.h"
19 #include "src/sksl/codegen/SkSLGLSLCodeGenerator.h"
20 #include "src/sksl/codegen/SkSLMetalCodeGenerator.h"
21 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
22 #include "src/sksl/codegen/SkSLRasterPipelineCodeGenerator.h"
23 #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
24 #include "src/sksl/codegen/SkSLWGSLCodeGenerator.h"
25 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
26 #include "src/sksl/ir/SkSLProgram.h"
27 
28 #include <regex>
29 
30 #include "src/sksl/generated/sksl_shared.minified.sksl"
31 #include "src/sksl/generated/sksl_compute.minified.sksl"
32 #include "src/sksl/generated/sksl_frag.minified.sksl"
33 #include "src/sksl/generated/sksl_gpu.minified.sksl"
34 #include "src/sksl/generated/sksl_public.minified.sksl"
35 #include "src/sksl/generated/sksl_rt_shader.minified.sksl"
36 #include "src/sksl/generated/sksl_vert.minified.sksl"
37 #include "src/sksl/generated/sksl_graphite_frag.minified.sksl"
38 #include "src/sksl/generated/sksl_graphite_vert.minified.sksl"
39 #include "src/sksl/generated/sksl_graphite_frag_es2.minified.sksl"
40 #include "src/sksl/generated/sksl_graphite_vert_es2.minified.sksl"
41 
42 class SkSLCompilerStartupBench : public Benchmark {
43 protected:
onGetName()44     const char* onGetName() override {
45         return "sksl_compiler_startup";
46     }
47 
isSuitableFor(Backend backend)48     bool isSuitableFor(Backend backend) override {
49         return backend == Backend::kNonRendering;
50     }
51 
onDraw(int loops,SkCanvas *)52     void onDraw(int loops, SkCanvas*) override {
53         for (int i = 0; i < loops; i++) {
54             SkSL::Compiler compiler;
55         }
56     }
57 };
58 
59 DEF_BENCH(return new SkSLCompilerStartupBench();)
60 
61 enum class Output {
62     kNone,
63     kGLSL,
64     kMetal,
65     kSPIRV,
66     kSkRP,
67     kGrMtl,
68     kGrWGSL,
69 };
70 
71 class SkSLCompileBench : public Benchmark {
72 public:
output_string(Output output)73     static const char* output_string(Output output) {
74         switch (output) {
75             case Output::kNone:    return "";
76             case Output::kGLSL:    return "glsl_";
77             case Output::kMetal:   return "metal_";
78             case Output::kSPIRV:   return "spirv_";
79             case Output::kGrMtl:   return "grmtl_";
80             case Output::kGrWGSL:  return "grwgsl_";
81             case Output::kSkRP:    return "skrp_";
82         }
83         SkUNREACHABLE;
84     }
85 
SkSLCompileBench(std::string name,const char * src,bool optimize,Output output)86     SkSLCompileBench(std::string name, const char* src, bool optimize, Output output)
87             : fName(std::string("sksl_") + (optimize ? "" : "unoptimized_") +
88                     output_string(output) + name)
89             , fSrc(src)
90             , fCaps(GrContextOptions(), GrMockOptions())
91             , fOutput(output) {
92         fSettings.fOptimize = optimize;
93         // The test programs we compile don't follow Vulkan rules and thus produce invalid SPIR-V.
94         // This is harmless, so long as we don't try to validate them.
95         fSettings.fValidateSPIRV = false;
96 
97         this->fixUpSource();
98     }
99 
100 protected:
onGetName()101     const char* onGetName() override {
102         return fName.c_str();
103     }
104 
isSuitableFor(Backend backend)105     bool isSuitableFor(Backend backend) override {
106 #if !defined(SK_GRAPHITE)
107         if (this->usesGraphite()) {
108             return false;
109         }
110 #endif
111         return backend == Backend::kNonRendering;
112     }
113 
usesRuntimeShader() const114     bool usesRuntimeShader() const {
115         return fOutput == Output::kSkRP;
116     }
117 
usesGraphite() const118     bool usesGraphite() const {
119         return fOutput == Output::kGrMtl || fOutput == Output::kGrWGSL;
120     }
121 
fixUpSource()122     void fixUpSource() {
123         auto fixup = [this](const char* input, const char* replacement) {
124             fSrc = std::regex_replace(fSrc, std::regex(input), replacement);
125         };
126 
127         // Runtime shaders have slightly different conventions than fragment shaders.
128         // Perform a handful of fixups to compensate. These are hand-tuned for our current set of
129         // test shaders and will probably need to be updated if we add more.
130         if (this->usesRuntimeShader()) {
131             fixup(R"(void main\(\))",                              "half4 main(float2 xy)");
132             fixup(R"(sk_FragColor =)",                             "return");
133             fixup(R"(sk_FragCoord)",                               "_FragCoord");
134             fixup(R"(sampler2D )",                                 "uniform shader ");
135             fixup(R"((flat |noperspective |)in )",                 "uniform ");
136             fixup(R"(sample\(([A-Za-z0-9_]+), ([A-Za-z0-9_]+)\))", "$01.eval($02)");
137             fSrc = "#version 300\nuniform float4 _FragCoord;\n" + fSrc;
138         }
139     }
140 
onDraw(int loops,SkCanvas * canvas)141     void onDraw(int loops, SkCanvas* canvas) override {
142         SkSL::ProgramKind kind;
143         if (this->usesRuntimeShader()) {
144             kind = SkSL::ProgramKind::kRuntimeShader;
145         } else if (this->usesGraphite()) {
146             kind = SkSL::ProgramKind::kGraphiteFragment;
147         } else {
148             kind = SkSL::ProgramKind::kFragment;
149         }
150         for (int i = 0; i < loops; i++) {
151             std::unique_ptr<SkSL::Program> program = fCompiler.convertProgram(kind, fSrc,
152                                                                               fSettings);
153             if (fCompiler.errorCount()) {
154                 SK_ABORT("shader compilation failed: %s\n", fCompiler.errorText().c_str());
155             }
156             std::string result;
157             switch (fOutput) {
158                 case Output::kNone:
159                     break;
160 
161                 case Output::kGLSL:
162                     SkAssertResult(SkSL::ToGLSL(*program, fCaps.shaderCaps(), &result));
163                     break;
164 
165                 case Output::kMetal:
166                 case Output::kGrMtl:
167                     SkAssertResult(SkSL::ToMetal(*program, fCaps.shaderCaps(), &result));
168                     break;
169 
170                 case Output::kSPIRV:
171                     SkAssertResult(SkSL::ToSPIRV(*program, fCaps.shaderCaps(), &result));
172                     break;
173 
174                 case Output::kGrWGSL:
175                     SkAssertResult(SkSL::ToWGSL(*program, fCaps.shaderCaps(), &result));
176                     break;
177 
178                 case Output::kSkRP:
179                     SkAssertResult(CompileToSkRP(*program));
180                     break;
181             }
182         }
183     }
184 
CompileToSkRP(const SkSL::Program & program)185     static bool CompileToSkRP(const SkSL::Program& program) {
186         const SkSL::FunctionDeclaration* main = program.getFunction("main");
187         if (!main) {
188             return false;
189         }
190 
191         // Compile our program.
192         std::unique_ptr<SkSL::RP::Program> rasterProg = SkSL::MakeRasterPipelineProgram(
193                 program, *main->definition(), /*debugTrace=*/nullptr, /*writeTraceOps=*/false);
194         if (!rasterProg) {
195             return false;
196         }
197 
198         // We need to supply a valid uniform range, but the uniform values inside don't actually
199         // matter, since we aren't going to run the shader.
200         float uniformBuffer[1024];
201         if (rasterProg->numUniforms() > (int)std::size(uniformBuffer)) {
202             return false;
203         }
204 
205         // Append the program to a raster pipeline.
206         SkSTArenaAlloc<2048> alloc;
207         SkRasterPipeline pipeline(&alloc);
208         rasterProg->appendStages(&pipeline,
209                                  &alloc,
210                                  /*callbacks=*/nullptr,
211                                  /*uniforms=*/SkSpan{uniformBuffer, rasterProg->numUniforms()});
212         return true;
213     }
214 
215 private:
216     std::string fName;
217     std::string fSrc;
218     GrMockCaps fCaps;
219     SkSL::Compiler fCompiler;
220     SkSL::ProgramSettings fSettings;
221     Output fOutput;
222 
223     using INHERITED = Benchmark;
224 };
225 
226 ///////////////////////////////////////////////////////////////////////////////
227 
228 #define COMPILER_BENCH(name, text)                                                               \
229   static constexpr char name ## _SRC[] = text;                                                   \
230   DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/false, Output::kNone);)  \
231   DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true,  Output::kNone);)  \
232   DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true,  Output::kGLSL);)  \
233   DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true,  Output::kMetal);) \
234   DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true,  Output::kSPIRV);) \
235   DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true,  Output::kSkRP);)
236 
237 // This fragment shader is from the third tile on the top row of GM_gradients_2pt_conical_outside.
238 // To get an ES2 compatible shader, nonconstantArrayIndexSupport in GrShaderCaps is forced off.
239 COMPILER_BENCH(large, R"(
240 uniform half4 uthresholds1_7_S1_c0_c0_c0;
241 uniform half4 uthresholds9_13_S1_c0_c0_c0;
242 uniform float4 uscale_S1_c0_c0_c0[4];
243 uniform float4 ubias_S1_c0_c0_c0[4];
244 uniform half uinvR1_S1_c0_c0_c1_c0;
245 uniform half ufx_S1_c0_c0_c1_c0;
246 uniform float3x3 umatrix_S1_c0_c0_c1;
247 uniform half4 uleftBorderColor_S1_c0_c0;
248 uniform half4 urightBorderColor_S1_c0_c0;
249 uniform float3x3 umatrix_S1_c1;
250 uniform half urange_S1;
251 sampler2D uTextureSampler_0_S1;
252 flat in half4 vcolor_S0;
253 noperspective in float2 vTransformedCoords_6_S0;
254 half4 UnrolledBinaryColorizer_S1_c0_c0_c0(half4 _input, float2 _coords)
255 {
256 	half4 _tmp_0_inColor = _input;
257 	float2 _tmp_1_coords = _coords;
258 	half t = half(_tmp_1_coords.x);
259 	float4 s;
260 	float4 b;
261 	{
262 		if (t < uthresholds1_7_S1_c0_c0_c0.y)
263 		{
264 			if (t < uthresholds1_7_S1_c0_c0_c0.x)
265 			{
266 				s = uscale_S1_c0_c0_c0[0];
267 				b = ubias_S1_c0_c0_c0[0];
268 			}
269 			else
270 			{
271 				s = uscale_S1_c0_c0_c0[1];
272 				b = ubias_S1_c0_c0_c0[1];
273 			}
274 		}
275 		else
276 		{
277 			if (t < uthresholds1_7_S1_c0_c0_c0.z)
278 			{
279 				s = uscale_S1_c0_c0_c0[2];
280 				b = ubias_S1_c0_c0_c0[2];
281 			}
282 			else
283 			{
284 				s = uscale_S1_c0_c0_c0[3];
285 				b = ubias_S1_c0_c0_c0[3];
286 			}
287 		}
288 	}
289 	return half4(half4(float(t) * s + b));
290 }
291 half4 TwoPointConicalFocalLayout_S1_c0_c0_c1_c0(half4 _input)
292 {
293 	half4 _tmp_2_inColor = _input;
294 	float2 _tmp_3_coords = vTransformedCoords_6_S0;
295 	float t = -1.0;
296 	half v = 1.0;
297 	float x_t = -1.0;
298 	if (bool(int(0)))
299 	{
300 		x_t = dot(_tmp_3_coords, _tmp_3_coords) / _tmp_3_coords.x;
301 	}
302 	else if (bool(int(0)))
303 	{
304 		x_t = length(_tmp_3_coords) - _tmp_3_coords.x * float(uinvR1_S1_c0_c0_c1_c0);
305 	}
306 	else
307 	{
308 		float temp = _tmp_3_coords.x * _tmp_3_coords.x - _tmp_3_coords.y * _tmp_3_coords.y;
309 		if (temp >= 0.0)
310 		{
311 			if (bool(int(0)) || !bool(int(1)))
312 			{
313 				x_t = -sqrt(temp) - _tmp_3_coords.x * float(uinvR1_S1_c0_c0_c1_c0);
314 			}
315 			else
316 			{
317 				x_t = sqrt(temp) - _tmp_3_coords.x * float(uinvR1_S1_c0_c0_c1_c0);
318 			}
319 		}
320 	}
321 	if (!bool(int(0)))
322 	{
323 		if (x_t <= 0.0)
324 		{
325 			v = -1.0;
326 		}
327 	}
328 	if (bool(int(1)))
329 	{
330 		if (bool(int(0)))
331 		{
332 			t = x_t;
333 		}
334 		else
335 		{
336 			t = x_t + float(ufx_S1_c0_c0_c1_c0);
337 		}
338 	}
339 	else
340 	{
341 		if (bool(int(0)))
342 		{
343 			t = -x_t;
344 		}
345 		else
346 		{
347 			t = -x_t + float(ufx_S1_c0_c0_c1_c0);
348 		}
349 	}
350 	if (bool(int(0)))
351 	{
352 		t = 1.0 - t;
353 	}
354 	return half4(half4(half(t), v, 0.0, 0.0));
355 }
356 half4 MatrixEffect_S1_c0_c0_c1(half4 _input)
357 {
358 	return TwoPointConicalFocalLayout_S1_c0_c0_c1_c0(_input);
359 }
360 half4 ClampedGradient_S1_c0_c0(half4 _input)
361 {
362 	half4 _tmp_4_inColor = _input;
363 	half4 t = MatrixEffect_S1_c0_c0_c1(_tmp_4_inColor);
364 	half4 outColor;
365 	if (!bool(int(0)) && t.y < 0.0)
366 	{
367 		outColor = half4(0.0);
368 	}
369 	else if (t.x < 0.0)
370 	{
371 		outColor = uleftBorderColor_S1_c0_c0;
372 	}
373 	else if (t.x > 1.0)
374 	{
375 		outColor = urightBorderColor_S1_c0_c0;
376 	}
377 	else
378 	{
379 		outColor = UnrolledBinaryColorizer_S1_c0_c0_c0(_tmp_4_inColor, float2(half2(t.x, 0.0)));
380 	}
381 	return half4(outColor);
382 }
383 half4 DisableCoverageAsAlpha_S1_c0(half4 _input)
384 {
385 	_input = ClampedGradient_S1_c0_c0(_input);
386 	half4 _tmp_5_inColor = _input;
387 	return half4(_input);
388 }
389 half4 TextureEffect_S1_c1_c0(half4 _input, float2 _coords)
390 {
391 	return sample(uTextureSampler_0_S1, _coords).000r;
392 }
393 half4 MatrixEffect_S1_c1(half4 _input, float2 _coords)
394 {
395 	return TextureEffect_S1_c1_c0(_input, float3x2(umatrix_S1_c1) * _coords.xy1);
396 }
397 half4 Dither_S1(half4 _input)
398 {
399 	half4 _tmp_6_inColor = _input;
400 	half4 color = DisableCoverageAsAlpha_S1_c0(_tmp_6_inColor);
401 	half value = MatrixEffect_S1_c1(_tmp_6_inColor, sk_FragCoord.xy).w - 0.5;
402 	return half4(half4(clamp(color.xyz + value * urange_S1, 0.0, color.w), color.w));
403 }
404 void main()
405 {
406 	// Stage 0, QuadPerEdgeAAGeometryProcessor
407 	half4 outputColor_S0;
408 	outputColor_S0 = vcolor_S0;
409 	const half4 outputCoverage_S0 = half4(1);
410 	half4 output_S1;
411 	output_S1 = Dither_S1(outputColor_S0);
412 	{
413 		// Xfer Processor: Porter Duff
414 		sk_FragColor = output_S1 * outputCoverage_S0;
415 	}
416 }
417 )");
418 
419 // This fragment shader is taken from GM_BlurDrawImage.
420 COMPILER_BENCH(medium, R"(
421 uniform float3x3 umatrix_S1_c0;
422 uniform float3x3 umatrix_S2_c0_c0;
423 uniform float4 urect_S2_c0;
424 sampler2D uTextureSampler_0_S1;
425 sampler2D uTextureSampler_0_S2;
426 flat in half4 vcolor_S0;
427 noperspective in float2 vTransformedCoords_3_S0;
428 half4 TextureEffect_S1_c0_c0(half4 _input)
429 {
430 	return sample(uTextureSampler_0_S1, vTransformedCoords_3_S0);
431 }
432 half4 MatrixEffect_S1_c0(half4 _input)
433 {
434 	return TextureEffect_S1_c0_c0(_input);
435 }
436 half4 DisableCoverageAsAlpha_S1(half4 _input)
437 {
438 	_input = MatrixEffect_S1_c0(_input);
439 	half4 _tmp_0_inColor = _input;
440 	return half4(_input);
441 }
442 half4 TextureEffect_S2_c0_c0_c0(half4 _input, float2 _coords)
443 {
444 	return sample(uTextureSampler_0_S2, _coords).000r;
445 }
446 half4 MatrixEffect_S2_c0_c0(half4 _input, float2 _coords)
447 {
448 	return TextureEffect_S2_c0_c0_c0(_input, float3x2(umatrix_S2_c0_c0) * _coords.xy1);
449 }
450 half4 RectBlur_S2_c0(half4 _input, float2 _coords)
451 {
452 	half4 _tmp_1_inColor = _input;
453 	float2 _tmp_2_coords = _coords;
454 	half xCoverage;
455 	half yCoverage;
456 	if (bool(int(1)))
457 	{
458 		half2 xy = max(half2(urect_S2_c0.xy - _tmp_2_coords), half2(_tmp_2_coords - urect_S2_c0.zw));
459 		xCoverage = MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(xy.x, 0.5))).w;
460 		yCoverage = MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(xy.y, 0.5))).w;
461 	}
462 	else
463 	{
464 		half4 rect = half4(half2(urect_S2_c0.xy - _tmp_2_coords), half2(_tmp_2_coords - urect_S2_c0.zw));
465 		xCoverage = (1.0 - MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(rect.x, 0.5))).w) - MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(rect.z, 0.5))).w;
466 		yCoverage = (1.0 - MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(rect.y, 0.5))).w) - MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(rect.w, 0.5))).w;
467 	}
468 	return half4((_input * xCoverage) * yCoverage);
469 }
470 half4 DeviceSpace_S2(half4 _input)
471 {
472 	return RectBlur_S2_c0(_input, sk_FragCoord.xy);
473 }
474 void main()
475 {
476 	// Stage 0, QuadPerEdgeAAGeometryProcessor
477 	half4 outputColor_S0;
478 	outputColor_S0 = vcolor_S0;
479 	const half4 outputCoverage_S0 = half4(1);
480 	half4 output_S1;
481 	output_S1 = DisableCoverageAsAlpha_S1(outputColor_S0);
482 	half4 output_S2;
483 	output_S2 = DeviceSpace_S2(outputCoverage_S0);
484 	{
485 		// Xfer Processor: Porter Duff
486 		sk_FragColor = output_S1 * output_S2;
487 	}
488 }
489 )");
490 
491 // This fragment shader is taken from GM_lcdtext.
492 COMPILER_BENCH(small, R"(
493 sampler2D uTextureSampler_0_S0;
494 noperspective in float2 vTextureCoords_S0;
495 flat in float vTexIndex_S0;
496 noperspective in half4 vinColor_S0;
497 void main()
498 {
499 	// Stage 0, BitmapText
500 	half4 outputColor_S0;
501 	outputColor_S0 = vinColor_S0;
502 	half4 texColor;
503 	{
504 		texColor = sample(uTextureSampler_0_S0, vTextureCoords_S0).rrrr;
505 	}
506 	half4 outputCoverage_S0 = texColor;
507 	{
508 		// Xfer Processor: Porter Duff
509 		sk_FragColor = outputColor_S0 * outputCoverage_S0;
510 	}
511 }
512 )");
513 
514 COMPILER_BENCH(tiny, "void main() { sk_FragColor = half4(1); }");
515 
516 #define GRAPHITE_BENCH(name, text)                                                                \
517     static constexpr char name##_SRC[] = text;                                                    \
518     DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true, Output::kGrMtl);) \
519     DEF_BENCH(return new SkSLCompileBench(#name, name##_SRC, /*optimize=*/true, Output::kGrWGSL);)
520 
521 // This fragment shader is from the third tile on the top row of GM_gradients_2pt_conical_outside.
522 GRAPHITE_BENCH(graphite_large, R"(
523 layout(location=0) in flat int shadingSsboIndexVar;
524 layout(location=1) in float2 localCoordsVar;
525 layout(location=2) in float4 jacobian;
526 layout(location=3) in float4 edgeDistances;
527 layout(location=4) in float4 xRadii;
528 layout(location=5) in float4 yRadii;
529 layout(location=6) in float2 strokeParams;
530 layout(location=7) in float2 perPixelControl;
531 struct FSUniformData
532 {
533 	// 0 - SolidColor uniforms
534 	float4 color_0;
535 	// 2 - ConicalGradient8 uniforms
536 	float4 colors_2[8];
537 	float4 offsets_2[2];
538 	float2 point0_2;
539 	float2 point1_2;
540 	float radius0_2;
541 	float radius1_2;
542 	int tilemode_2;
543 	int colorSpace_2;
544 	int doUnPremul_2;
545 	// 3 - ColorSpaceTransform uniforms
546 	int flags_3;
547 	int srcKind_3;
548 	half3x3 gamutTransform_3;
549 	int dstKind_3;
550 	half4x4 csXformCoeffs_3;
551 	// 4 - DitherShader uniforms
552 	half range_4;
553 }
554 ;
555 layout (binding=2) buffer FSUniforms
556 {
557 	FSUniformData fsUniformData[];
558 }
559 ;
560 // 4 - DitherShader samplers
561 layout(binding=0) sampler2D sampler_4;
562 // [1]   1: ColorFilterShader
563 half4 ColorFilterShader_1(half4 inColor, half4 destColor, float2 coords)
564 {
565 	return sk_color_space_transform(sk_conical_grad_8_shader(coords, fsUniformData[shadingSsboIndexVar].colors_2, fsUniformData[shadingSsboIndexVar].offsets_2, fsUniformData[shadingSsboIndexVar].point0_2, fsUniformData[shadingSsboIndexVar].point1_2, fsUniformData[shadingSsboIndexVar].radius0_2, fsUniformData[shadingSsboIndexVar].radius1_2, fsUniformData[shadingSsboIndexVar].tilemode_2, fsUniformData[shadingSsboIndexVar].colorSpace_2, fsUniformData[shadingSsboIndexVar].doUnPremul_2), fsUniformData[shadingSsboIndexVar].flags_3, fsUniformData[shadingSsboIndexVar].srcKind_3, fsUniformData[shadingSsboIndexVar].gamutTransform_3, fsUniformData[shadingSsboIndexVar].dstKind_3, fsUniformData[shadingSsboIndexVar].csXformCoeffs_3);
566 }
567 void main()
568 {
569 	half4 initialColor = half4(0);
570 	// [0] SolidColor
571 	half4 outColor_0 = sk_solid_shader(fsUniformData[shadingSsboIndexVar].color_0);
572 	// [1] ColorFilterShader
573 	half4 outColor_1 = ColorFilterShader_1(outColor_0, half4(1), localCoordsVar);
574 	// [4] DitherShader
575 	half4 outColor_4 = sk_dither_shader(outColor_1, localCoordsVar, fsUniformData[shadingSsboIndexVar].range_4, sampler_4);
576 	// [5] SrcOver
577 	half4 outColor_5 = outColor_4;
578 	half4 outputCoverage;
579 	outputCoverage = analytic_rrect_coverage_fn(sk_FragCoord, jacobian, edgeDistances, xRadii, yRadii, strokeParams, perPixelControl);
580 	sk_FragColor = outColor_5 * outputCoverage;
581 }
582 )");
583 
584 // This fragment shader is taken from GM_lcdtext.
585 GRAPHITE_BENCH(graphite_small, R"(
586 layout(location=0) in flat int shadingSsboIndexVar;
587 layout(location=1) in float2 textureCoords;
588 layout(location=2) in half texIndex;
589 layout(location=3) in half maskFormat;
590 layout (binding=1) uniform StepUniforms
591 {
592 	layout(offset=0) float4x4 subRunDeviceMatrix;
593 	layout(offset=64) float4x4 deviceToLocal;
594 	layout(offset=128) float2 atlasSizeInv;
595 }
596 ;
597 struct FSUniformData
598 {
599 	// 0 - SolidColor uniforms
600 	float4 color_0;
601 }
602 ;
603 layout (binding=2) buffer FSUniforms
604 {
605 	FSUniformData fsUniformData[];
606 }
607 ;
608 layout(binding=0) sampler2D text_atlas_0;
609 layout(binding=1) sampler2D text_atlas_1;
610 layout(binding=2) sampler2D text_atlas_2;
611 layout(binding=3) sampler2D text_atlas_3;
612 void main()
613 {
614 	half4 initialColor = half4(0);
615 	// [0] SolidColor
616 	half4 outColor_0 = sk_solid_shader(fsUniformData[shadingSsboIndexVar].color_0);
617 	// [1] SrcOver
618 	half4 outColor_1 = outColor_0;
619 	half4 outputCoverage;
620 	outputCoverage = bitmap_text_coverage_fn(sample_indexed_atlas(textureCoords, int(texIndex), text_atlas_0, text_atlas_1, text_atlas_2, text_atlas_3), int(maskFormat));
621 	sk_FragColor = outColor_1 * outputCoverage;
622 }
623 )");
624 
625 #if defined(SK_BUILD_FOR_UNIX)
626 
627 #include <malloc.h>
heap_bytes_used()628 static int64_t heap_bytes_used() {
629     return (int64_t)mallinfo().uordblks;
630 }
631 
632 #elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
633 
634 #include <malloc/malloc.h>
heap_bytes_used()635 static int64_t heap_bytes_used() {
636     malloc_statistics_t stats;
637     malloc_zone_pressure_relief(malloc_default_zone(), 0);
638     malloc_zone_statistics(malloc_default_zone(), &stats);
639     return (int64_t)stats.size_in_use;
640 }
641 
642 #else
643 
heap_bytes_used()644 static int64_t heap_bytes_used() {
645     return -1;
646 }
647 
648 #endif
649 
bench(NanoJSONResultsWriter * log,const char * name,int bytes)650 static void bench(NanoJSONResultsWriter* log, const char* name, int bytes) {
651     SkDEBUGCODE(SkDebugf("%s: %d bytes\n", name, bytes);)
652     log->beginObject(name);          // test
653     log->beginObject("meta");        //   config
654     log->appendS32("bytes", bytes);  //     sub_result
655     log->endObject();                //   config
656     log->endObject();                // test
657 }
658 
659 // These benchmarks aren't timed, they produce memory usage statistics. They run standalone, and
660 // directly add their results to the nanobench log.
RunSkSLModuleBenchmarks(NanoJSONResultsWriter * log)661 void RunSkSLModuleBenchmarks(NanoJSONResultsWriter* log) {
662     // Heap used by a default compiler (with no modules loaded)
663     int64_t before = heap_bytes_used();
664     SkSL::Compiler compiler;
665     int baselineBytes = heap_bytes_used();
666     if (baselineBytes >= 0) {
667         baselineBytes = (baselineBytes - before);
668         bench(log, "sksl_compiler_baseline", baselineBytes);
669     }
670 
671     // Heap used by a compiler with the two main GPU modules (fragment + vertex) and runtime effects
672     // (shader + color filter + blender) loaded. Ganesh will load all of these in regular usage.
673     before = heap_bytes_used();
674     compiler.moduleForProgramKind(SkSL::ProgramKind::kVertex);
675     compiler.moduleForProgramKind(SkSL::ProgramKind::kFragment);
676     compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeColorFilter);
677     compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeShader);
678     compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeBlender);
679     compiler.moduleForProgramKind(SkSL::ProgramKind::kPrivateRuntimeColorFilter);
680     compiler.moduleForProgramKind(SkSL::ProgramKind::kPrivateRuntimeShader);
681     compiler.moduleForProgramKind(SkSL::ProgramKind::kPrivateRuntimeBlender);
682     int64_t gpuBytes = heap_bytes_used();
683     if (gpuBytes >= 0) {
684         gpuBytes = (gpuBytes - before) + baselineBytes;
685         bench(log, "sksl_compiler_gpu", gpuBytes);
686     }
687 
688 #if defined(SK_GRAPHITE)
689     // Heap used by a compiler with the Graphite modules loaded.
690     before = heap_bytes_used();
691     compiler.moduleForProgramKind(SkSL::ProgramKind::kGraphiteVertex);
692     compiler.moduleForProgramKind(SkSL::ProgramKind::kGraphiteFragment);
693     int64_t graphiteBytes = heap_bytes_used();
694     if (graphiteBytes >= 0) {
695         graphiteBytes = (graphiteBytes - before) + gpuBytes;
696         bench(log, "sksl_compiler_graphite", graphiteBytes);
697     }
698 
699     // Heap used by a compiler with compute-shader support loaded.
700     before = heap_bytes_used();
701     compiler.moduleForProgramKind(SkSL::ProgramKind::kCompute);
702     int64_t computeBytes = heap_bytes_used();
703     if (computeBytes >= 0) {
704         computeBytes = (computeBytes - before) + baselineBytes;
705         bench(log, "sksl_compiler_compute", computeBytes);
706     }
707 #endif
708 
709     // Report the minified module sizes.
710     int compilerGPUBinarySize = std::size(SKSL_MINIFIED_sksl_shared) +
711                                 std::size(SKSL_MINIFIED_sksl_gpu) +
712                                 std::size(SKSL_MINIFIED_sksl_vert) +
713                                 std::size(SKSL_MINIFIED_sksl_frag) +
714                                 std::size(SKSL_MINIFIED_sksl_public) +
715                                 std::size(SKSL_MINIFIED_sksl_rt_shader);
716     bench(log, "sksl_binary_size_gpu", compilerGPUBinarySize);
717 
718     int compilerGraphiteBinarySize = std::size(SKSL_MINIFIED_sksl_graphite_frag) +
719                                      std::size(SKSL_MINIFIED_sksl_graphite_vert);
720     bench(log, "sksl_binary_size_graphite", compilerGraphiteBinarySize);
721 
722     int compilerGraphiteES2BinarySize = std::size(SKSL_MINIFIED_sksl_graphite_frag_es2) +
723                                         std::size(SKSL_MINIFIED_sksl_graphite_vert_es2);
724     bench(log, "sksl_binary_size_graphite_es2", compilerGraphiteES2BinarySize);
725 
726     int compilerComputeBinarySize = std::size(SKSL_MINIFIED_sksl_compute);
727     bench(log, "sksl_binary_size_compute", compilerComputeBinarySize);
728 }
729 
730 class SkSLModuleLoaderBench : public Benchmark {
731 public:
SkSLModuleLoaderBench(const char * name,std::vector<SkSL::ProgramKind> moduleList)732     SkSLModuleLoaderBench(const char* name, std::vector<SkSL::ProgramKind> moduleList)
733             : fName(name), fModuleList(std::move(moduleList)) {}
734 
onGetName()735     const char* onGetName() override {
736         return fName;
737     }
738 
isSuitableFor(Backend backend)739     bool isSuitableFor(Backend backend) override {
740         return backend == Backend::kNonRendering;
741     }
742 
shouldLoop() const743     bool shouldLoop() const override {
744         return false;
745     }
746 
onPreDraw(SkCanvas *)747     void onPreDraw(SkCanvas*) override {
748         SkSL::ModuleLoader::Get().unloadModules();
749     }
750 
onDraw(int loops,SkCanvas *)751     void onDraw(int loops, SkCanvas*) override {
752         SkASSERT(loops == 1);
753         SkSL::Compiler compiler;
754         for (SkSL::ProgramKind kind : fModuleList) {
755             compiler.moduleForProgramKind(kind);
756         }
757     }
758 
759     const char* fName;
760     std::vector<SkSL::ProgramKind> fModuleList;
761 };
762 
763 DEF_BENCH(return new SkSLModuleLoaderBench("sksl_module_loader_ganesh",
764                                            {
765                                                    SkSL::ProgramKind::kVertex,
766                                                    SkSL::ProgramKind::kFragment,
767                                                    SkSL::ProgramKind::kRuntimeColorFilter,
768                                                    SkSL::ProgramKind::kRuntimeShader,
769                                                    SkSL::ProgramKind::kRuntimeBlender,
770                                                    SkSL::ProgramKind::kPrivateRuntimeColorFilter,
771                                                    SkSL::ProgramKind::kPrivateRuntimeShader,
772                                                    SkSL::ProgramKind::kPrivateRuntimeBlender,
773                                                    SkSL::ProgramKind::kCompute,
774                                            });)
775 
776 DEF_BENCH(return new SkSLModuleLoaderBench("sksl_module_loader_graphite",
777                                            {
778                                                    SkSL::ProgramKind::kVertex,
779                                                    SkSL::ProgramKind::kFragment,
780                                                    SkSL::ProgramKind::kRuntimeColorFilter,
781                                                    SkSL::ProgramKind::kRuntimeShader,
782                                                    SkSL::ProgramKind::kRuntimeBlender,
783                                                    SkSL::ProgramKind::kPrivateRuntimeColorFilter,
784                                                    SkSL::ProgramKind::kPrivateRuntimeShader,
785                                                    SkSL::ProgramKind::kPrivateRuntimeBlender,
786                                                    SkSL::ProgramKind::kCompute,
787                                                    SkSL::ProgramKind::kGraphiteVertex,
788                                                    SkSL::ProgramKind::kGraphiteFragment,
789                                            });)
790