• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/gpu/GrCaps.h"
12 #include "src/gpu/GrRecordingContextPriv.h"
13 #include "src/gpu/mock/GrMockCaps.h"
14 #include "src/sksl/SkSLCompiler.h"
15 #include "src/sksl/SkSLDSLParser.h"
16 
17 class SkSLCompilerStartupBench : public Benchmark {
18 protected:
onGetName()19     const char* onGetName() override {
20         return "sksl_compiler_startup";
21     }
22 
isSuitableFor(Backend backend)23     bool isSuitableFor(Backend backend) override {
24         return backend == kNonRendering_Backend;
25     }
26 
onDraw(int loops,SkCanvas *)27     void onDraw(int loops, SkCanvas*) override {
28         GrShaderCaps caps;
29         for (int i = 0; i < loops; i++) {
30             SkSL::Compiler compiler(&caps);
31         }
32     }
33 };
34 
35 DEF_BENCH(return new SkSLCompilerStartupBench();)
36 
37 enum class Output {
38     kNone,
39     kGLSL,
40     kMetal,
41     kSPIRV
42 };
43 
44 class SkSLCompileBench : public Benchmark {
45 public:
output_string(Output output)46     static const char* output_string(Output output) {
47         switch (output) {
48             case Output::kNone: return "";
49             case Output::kGLSL: return "glsl_";
50             case Output::kMetal: return "metal_";
51             case Output::kSPIRV: return "spirv_";
52         }
53         SkUNREACHABLE;
54     }
55 
SkSLCompileBench(std::string name,const char * src,bool optimize,Output output)56     SkSLCompileBench(std::string name, const char* src, bool optimize, Output output)
57         : fName(std::string("sksl_") + (optimize ? "" : "unoptimized_") + output_string(output) +
58                 name)
59         , fSrc(src)
60         , fCaps(GrContextOptions(), GrMockOptions())
61         , fCompiler(fCaps.shaderCaps())
62         , fOutput(output) {
63             fSettings.fOptimize = optimize;
64             fSettings.fDSLMangling = false;
65             // The test programs we compile don't follow Vulkan rules and thus produce invalid
66             // SPIR-V. This is harmless, so long as we don't try to validate them.
67             fSettings.fValidateSPIRV = false;
68         }
69 
70 protected:
onGetName()71     const char* onGetName() override {
72         return fName.c_str();
73     }
74 
isSuitableFor(Backend backend)75     bool isSuitableFor(Backend backend) override {
76         return backend == kNonRendering_Backend;
77     }
78 
onDraw(int loops,SkCanvas * canvas)79     void onDraw(int loops, SkCanvas* canvas) override {
80         for (int i = 0; i < loops; i++) {
81             std::unique_ptr<SkSL::Program> program = SkSL::DSLParser(&fCompiler,
82                                                                      fSettings,
83                                                                      SkSL::ProgramKind::kFragment,
84                                                                      fSrc).program();
85             if (fCompiler.errorCount()) {
86                 SK_ABORT("shader compilation failed: %s\n", fCompiler.errorText().c_str());
87             }
88             std::string result;
89             switch (fOutput) {
90                 case Output::kNone:  break;
91                 case Output::kGLSL:  SkAssertResult(fCompiler.toGLSL(*program,  &result)); break;
92                 case Output::kMetal: SkAssertResult(fCompiler.toMetal(*program, &result)); break;
93                 case Output::kSPIRV: SkAssertResult(fCompiler.toSPIRV(*program, &result)); break;
94             }
95         }
96     }
97 
98 private:
99     std::string fName;
100     std::string fSrc;
101     GrMockCaps fCaps;
102     SkSL::Compiler fCompiler;
103     SkSL::Program::Settings fSettings;
104     Output fOutput;
105 
106     using INHERITED = Benchmark;
107 };
108 
109 ///////////////////////////////////////////////////////////////////////////////
110 
111 #define COMPILER_BENCH(name, text)                                                               \
112 static constexpr char name ## _SRC[] = text;                                                     \
113 DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/false, Output::kNone);)  \
114 DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true,  Output::kNone);)  \
115 DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true,  Output::kGLSL);)  \
116 DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true,  Output::kMetal);) \
117 DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true,  Output::kSPIRV);)
118 
119 // This fragment shader is from the third tile on the top row of GM_gradients_2pt_conical_outside.
120 COMPILER_BENCH(large, R"(
121 uniform float3x3 umatrix_S1_c0;
122 uniform half4 uthresholds_S1_c1_c0_c0[1];
123 uniform float4 uscale_S1_c1_c0_c0[4];
124 uniform float4 ubias_S1_c1_c0_c0[4];
125 uniform half uinvR1_S1_c1_c0_c1_c0;
126 uniform half ufx_S1_c1_c0_c1_c0;
127 uniform float3x3 umatrix_S1_c1_c0_c1;
128 uniform half4 uleftBorderColor_S1_c1_c0;
129 uniform half4 urightBorderColor_S1_c1_c0;
130 uniform half urange_S1;
131 uniform sampler2D uTextureSampler_0_S1;
132 flat in half4 vcolor_S0;
133 noperspective in float2 vTransformedCoords_8_S0;
134 out half4 sk_FragColor;
135 half4 TextureEffect_S1_c0_c0(half4 _input, float2 _coords)
136 {
137 	return sample(uTextureSampler_0_S1, _coords).000r;
138 }
139 half4 MatrixEffect_S1_c0(half4 _input, float2 _coords)
140 {
141 	return TextureEffect_S1_c0_c0(_input, float3x2(umatrix_S1_c0) * _coords.xy1);
142 }
143 half4 LoopingBinaryColorizer_S1_c1_c0_c0(half4 _input, float2 _coords)
144 {
145 	half4 _tmp_0_inColor = _input;
146 	float2 _tmp_1_coords = _coords;
147 	half t = half(_tmp_1_coords.x);
148 	;
149 	;
150 	int chunk = 0;
151 	;
152 	int pos;
153 	if (t < uthresholds_S1_c1_c0_c0[chunk].y)
154 	{
155 		pos = int(t < uthresholds_S1_c1_c0_c0[chunk].x ? 0 : 1);
156 	}
157 	else
158 	{
159 		pos = int(t < uthresholds_S1_c1_c0_c0[chunk].z ? 2 : 3);
160 	}
161 	;
162 	return half4(half4(float(t) * uscale_S1_c1_c0_c0[pos] + ubias_S1_c1_c0_c0[pos]));
163 }
164 half4 TwoPointConicalFocalLayout_S1_c1_c0_c1_c0(half4 _input)
165 {
166 	half4 _tmp_2_inColor = _input;
167 	float2 _tmp_3_coords = vTransformedCoords_8_S0;
168 	float t = -1.0;
169 	half v = 1.0;
170 	float x_t = -1.0;
171 	if (bool(int(0)))
172 	{
173 		x_t = dot(_tmp_3_coords, _tmp_3_coords) / _tmp_3_coords.x;
174 	}
175 	else if (bool(int(0)))
176 	{
177 		x_t = length(_tmp_3_coords) - _tmp_3_coords.x * float(uinvR1_S1_c1_c0_c1_c0);
178 	}
179 	else
180 	{
181 		float temp = _tmp_3_coords.x * _tmp_3_coords.x - _tmp_3_coords.y * _tmp_3_coords.y;
182 		if (temp >= 0.0)
183 		{
184 			if (bool(int(0)) || !bool(int(1)))
185 			{
186 				x_t = -sqrt(temp) - _tmp_3_coords.x * float(uinvR1_S1_c1_c0_c1_c0);
187 			}
188 			else
189 			{
190 				x_t = sqrt(temp) - _tmp_3_coords.x * float(uinvR1_S1_c1_c0_c1_c0);
191 			}
192 		}
193 	}
194 	if (!bool(int(0)))
195 	{
196 		if (x_t <= 0.0)
197 		{
198 			v = -1.0;
199 		}
200 	}
201 	if (bool(int(1)))
202 	{
203 		if (bool(int(0)))
204 		{
205 			t = x_t;
206 		}
207 		else
208 		{
209 			t = x_t + float(ufx_S1_c1_c0_c1_c0);
210 		}
211 	}
212 	else
213 	{
214 		if (bool(int(0)))
215 		{
216 			t = -x_t;
217 		}
218 		else
219 		{
220 			t = -x_t + float(ufx_S1_c1_c0_c1_c0);
221 		}
222 	}
223 	if (bool(int(0)))
224 	{
225 		t = 1.0 - t;
226 	}
227 	return half4(half4(half(t), v, 0.0, 0.0));
228 }
229 half4 MatrixEffect_S1_c1_c0_c1(half4 _input)
230 {
231 	return TwoPointConicalFocalLayout_S1_c1_c0_c1_c0(_input);
232 }
233 half4 ClampedGradient_S1_c1_c0(half4 _input)
234 {
235 	half4 _tmp_4_inColor = _input;
236 	half4 t = MatrixEffect_S1_c1_c0_c1(_tmp_4_inColor);
237 	half4 outColor;
238 	if (!bool(int(0)) && t.y < 0.0)
239 	{
240 		outColor = half4(0.0);
241 	}
242 	else if (t.x < 0.0)
243 	{
244 		outColor = uleftBorderColor_S1_c1_c0;
245 	}
246 	else if (t.x > 1.0)
247 	{
248 		outColor = urightBorderColor_S1_c1_c0;
249 	}
250 	else
251 	{
252 		outColor = LoopingBinaryColorizer_S1_c1_c0_c0(_tmp_4_inColor, float2(half2(t.x, 0.0)));
253 	}
254 	if (bool(int(0)))
255 	{
256 		outColor.xyz *= outColor.w;
257 	}
258 	return half4(outColor);
259 }
260 half4 DisableCoverageAsAlpha_S1_c1(half4 _input)
261 {
262 	_input = ClampedGradient_S1_c1_c0(_input);
263 	half4 _tmp_5_inColor = _input;
264 	return half4(_input);
265 }
266 half4 Dither_S1(half4 _input)
267 {
268 	_input = DisableCoverageAsAlpha_S1_c1(_input);
269 	half4 _tmp_6_inColor = _input;
270 	half value = MatrixEffect_S1_c0(_tmp_6_inColor, sk_FragCoord.xy).w - 0.5;
271 	return half4(half4(clamp(_input.xyz + value * urange_S1, 0.0, _input.w), _input.w));
272 }
273 void main()
274 {
275 	// Stage 0, QuadPerEdgeAAGeometryProcessor
276 	half4 outputColor_S0;
277 	outputColor_S0 = vcolor_S0;
278 	const half4 outputCoverage_S0 = half4(1);
279 	half4 output_S1;
280 	output_S1 = Dither_S1(outputColor_S0);
281 	{
282 		// Xfer Processor: Porter Duff
283 		sk_FragColor = output_S1 * outputCoverage_S0;
284 	}
285 }
286 )");
287 
288 // This fragment shader is taken from GM_BlurDrawImage.
289 COMPILER_BENCH(medium, R"(
290 uniform float3x3 umatrix_S1_c0;
291 uniform float3x3 umatrix_S2_c0_c0;
292 uniform float4 urect_S2_c0;
293 uniform sampler2D uTextureSampler_0_S1;
294 uniform sampler2D uTextureSampler_0_S2;
295 flat in half4 vcolor_S0;
296 noperspective in float2 vTransformedCoords_3_S0;
297 out half4 sk_FragColor;
298 half4 TextureEffect_S1_c0_c0(half4 _input)
299 {
300 	return sample(uTextureSampler_0_S1, vTransformedCoords_3_S0);
301 }
302 half4 MatrixEffect_S1_c0(half4 _input)
303 {
304 	return TextureEffect_S1_c0_c0(_input);
305 }
306 half4 DisableCoverageAsAlpha_S1(half4 _input)
307 {
308 	_input = MatrixEffect_S1_c0(_input);
309 	half4 _tmp_0_inColor = _input;
310 	return half4(_input);
311 }
312 half4 TextureEffect_S2_c0_c0_c0(half4 _input, float2 _coords)
313 {
314 	return sample(uTextureSampler_0_S2, _coords).000r;
315 }
316 half4 MatrixEffect_S2_c0_c0(half4 _input, float2 _coords)
317 {
318 	return TextureEffect_S2_c0_c0_c0(_input, float3x2(umatrix_S2_c0_c0) * _coords.xy1);
319 }
320 half4 RectBlur_S2_c0(half4 _input, float2 _coords)
321 {
322 	half4 _tmp_1_inColor = _input;
323 	float2 _tmp_2_coords = _coords;
324 	half xCoverage;
325 	half yCoverage;
326 	if (bool(int(1)))
327 	{
328 		half2 xy = max(half2(urect_S2_c0.xy - _tmp_2_coords), half2(_tmp_2_coords - urect_S2_c0.zw));
329 		xCoverage = MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(xy.x, 0.5))).w;
330 		yCoverage = MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(xy.y, 0.5))).w;
331 	}
332 	else
333 	{
334 		half4 rect = half4(half2(urect_S2_c0.xy - _tmp_2_coords), half2(_tmp_2_coords - urect_S2_c0.zw));
335 		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;
336 		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;
337 	}
338 	return half4((_input * xCoverage) * yCoverage);
339 }
340 half4 DeviceSpace_S2(half4 _input)
341 {
342 	return RectBlur_S2_c0(_input, sk_FragCoord.xy);
343 }
344 void main()
345 {
346 	// Stage 0, QuadPerEdgeAAGeometryProcessor
347 	half4 outputColor_S0;
348 	outputColor_S0 = vcolor_S0;
349 	const half4 outputCoverage_S0 = half4(1);
350 	half4 output_S1;
351 	output_S1 = DisableCoverageAsAlpha_S1(outputColor_S0);
352 	half4 output_S2;
353 	output_S2 = DeviceSpace_S2(outputCoverage_S0);
354 	{
355 		// Xfer Processor: Porter Duff
356 		sk_FragColor = output_S1 * output_S2;
357 	}
358 }
359 )");
360 
361 // This is the fragment shader used to blit the Viewer window when running the software rasterizer.
362 COMPILER_BENCH(small, R"(
363 uniform float3x3 umatrix_S1_c0;
364 uniform sampler2D uTextureSampler_0_S1;
365 flat in half4 vcolor_S0;
366 noperspective in float2 vTransformedCoords_3_S0;
367 out half4 sk_FragColor;
368 half4 TextureEffect_S1_c0_c0(half4 _input)
369 {
370 	return sample(uTextureSampler_0_S1, vTransformedCoords_3_S0);
371 }
372 half4 MatrixEffect_S1_c0(half4 _input)
373 {
374 	return TextureEffect_S1_c0_c0(_input);
375 }
376 half4 DisableCoverageAsAlpha_S1(half4 _input)
377 {
378 	_input = MatrixEffect_S1_c0(_input);
379 	half4 _tmp_0_inColor = _input;
380 	return half4(_input);
381 }
382 void main()
383 {
384 	// Stage 0, QuadPerEdgeAAGeometryProcessor
385 	half4 outputColor_S0;
386 	outputColor_S0 = vcolor_S0;
387 	const half4 outputCoverage_S0 = half4(1);
388 	half4 output_S1;
389 	output_S1 = DisableCoverageAsAlpha_S1(outputColor_S0);
390 	{
391 		// Xfer Processor: Porter Duff
392 		sk_FragColor = output_S1 * outputCoverage_S0;
393 	}
394 }
395 )");
396 
397 COMPILER_BENCH(tiny, "void main() { sk_FragColor = half4(1); }");
398 
399 #if defined(SK_BUILD_FOR_UNIX)
400 
401 #include <malloc.h>
402 
403 // These benchmarks aren't timed, they produce memory usage statistics. They run standalone, and
404 // directly add their results to the nanobench log.
RunSkSLMemoryBenchmarks(NanoJSONResultsWriter * log)405 void RunSkSLMemoryBenchmarks(NanoJSONResultsWriter* log) {
406     auto heap_bytes_used = []() { return mallinfo().uordblks; };
407     auto bench = [log](const char* name, int bytes) {
408         log->beginObject(name);          // test
409         log->beginObject("meta");        //   config
410         log->appendS32("bytes", bytes);  //     sub_result
411         log->endObject();                //   config
412         log->endObject();                // test
413     };
414 
415     // Heap used by a default compiler (with no modules loaded)
416     {
417         int before = heap_bytes_used();
418         GrShaderCaps caps;
419         SkSL::Compiler compiler(&caps);
420         int after = heap_bytes_used();
421         bench("sksl_compiler_baseline", after - before);
422     }
423 
424     // Heap used by a compiler with the two main GPU modules (fragment + vertex) loaded
425     {
426         int before = heap_bytes_used();
427         GrShaderCaps caps;
428         SkSL::Compiler compiler(&caps);
429         compiler.moduleForProgramKind(SkSL::ProgramKind::kVertex);
430         compiler.moduleForProgramKind(SkSL::ProgramKind::kFragment);
431         int after = heap_bytes_used();
432         bench("sksl_compiler_gpu", after - before);
433     }
434 
435     // Heap used by a compiler with the runtime shader, color filter and blending modules loaded
436     {
437         int before = heap_bytes_used();
438         GrShaderCaps caps;
439         SkSL::Compiler compiler(&caps);
440         compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeColorFilter);
441         compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeShader);
442         compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeBlender);
443         int after = heap_bytes_used();
444         bench("sksl_compiler_runtimeeffect", after - before);
445     }
446 }
447 
448 #else
449 
RunSkSLMemoryBenchmarks(NanoJSONResultsWriter *)450 void RunSkSLMemoryBenchmarks(NanoJSONResultsWriter*) {}
451 
452 #endif
453