blob: f150446b7989de75e79b90ff4bb2487294ffa6a1 [file] [log] [blame]
/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "bench/Benchmark.h"
#include "bench/ResultsWriter.h"
#include "bench/SkSLBench.h"
#include "include/core/SkCanvas.h"
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/mock/GrMockCaps.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLDSLParser.h"
class SkSLCompilerStartupBench : public Benchmark {
protected:
const char* onGetName() override {
return "sksl_compiler_startup";
}
bool isSuitableFor(Backend backend) override {
return backend == kNonRendering_Backend;
}
void onDraw(int loops, SkCanvas*) override {
GrShaderCaps caps;
for (int i = 0; i < loops; i++) {
SkSL::Compiler compiler(&caps);
}
}
};
DEF_BENCH(return new SkSLCompilerStartupBench();)
enum class Output {
kNone,
kGLSL,
kMetal,
kSPIRV
};
class SkSLCompileBench : public Benchmark {
public:
static const char* output_string(Output output) {
switch (output) {
case Output::kNone: return "";
case Output::kGLSL: return "glsl_";
case Output::kMetal: return "metal_";
case Output::kSPIRV: return "spirv_";
}
SkUNREACHABLE;
}
SkSLCompileBench(std::string name, const char* src, bool optimize, Output output)
: fName(std::string("sksl_") + (optimize ? "" : "unoptimized_") + output_string(output) +
name)
, fSrc(src)
, fCaps(GrContextOptions(), GrMockOptions())
, fCompiler(fCaps.shaderCaps())
, fOutput(output) {
fSettings.fOptimize = optimize;
fSettings.fDSLMangling = false;
// The test programs we compile don't follow Vulkan rules and thus produce invalid
// SPIR-V. This is harmless, so long as we don't try to validate them.
fSettings.fValidateSPIRV = false;
}
protected:
const char* onGetName() override {
return fName.c_str();
}
bool isSuitableFor(Backend backend) override {
return backend == kNonRendering_Backend;
}
void onDraw(int loops, SkCanvas* canvas) override {
for (int i = 0; i < loops; i++) {
std::unique_ptr<SkSL::Program> program = SkSL::DSLParser(&fCompiler,
fSettings,
SkSL::ProgramKind::kFragment,
fSrc).program();
if (fCompiler.errorCount()) {
SK_ABORT("shader compilation failed: %s\n", fCompiler.errorText().c_str());
}
std::string result;
switch (fOutput) {
case Output::kNone: break;
case Output::kGLSL: SkAssertResult(fCompiler.toGLSL(*program, &result)); break;
case Output::kMetal: SkAssertResult(fCompiler.toMetal(*program, &result)); break;
case Output::kSPIRV: SkAssertResult(fCompiler.toSPIRV(*program, &result)); break;
}
}
}
private:
std::string fName;
std::string fSrc;
GrMockCaps fCaps;
SkSL::Compiler fCompiler;
SkSL::Program::Settings fSettings;
Output fOutput;
using INHERITED = Benchmark;
};
///////////////////////////////////////////////////////////////////////////////
#define COMPILER_BENCH(name, text) \
static constexpr char name ## _SRC[] = text; \
DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/false, Output::kNone);) \
DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true, Output::kNone);) \
DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true, Output::kGLSL);) \
DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true, Output::kMetal);) \
DEF_BENCH(return new SkSLCompileBench(#name, name ## _SRC, /*optimize=*/true, Output::kSPIRV);)
// This fragment shader is from the third tile on the top row of GM_gradients_2pt_conical_outside.
COMPILER_BENCH(large, R"(
uniform float3x3 umatrix_S1_c0;
uniform half4 uthresholds_S1_c1_c0_c0[1];
uniform float4 uscale_S1_c1_c0_c0[4];
uniform float4 ubias_S1_c1_c0_c0[4];
uniform half uinvR1_S1_c1_c0_c1_c0;
uniform half ufx_S1_c1_c0_c1_c0;
uniform float3x3 umatrix_S1_c1_c0_c1;
uniform half4 uleftBorderColor_S1_c1_c0;
uniform half4 urightBorderColor_S1_c1_c0;
uniform half urange_S1;
uniform sampler2D uTextureSampler_0_S1;
flat in half4 vcolor_S0;
noperspective in float2 vTransformedCoords_8_S0;
out half4 sk_FragColor;
half4 TextureEffect_S1_c0_c0(half4 _input, float2 _coords)
{
return sample(uTextureSampler_0_S1, _coords).000r;
}
half4 MatrixEffect_S1_c0(half4 _input, float2 _coords)
{
return TextureEffect_S1_c0_c0(_input, float3x2(umatrix_S1_c0) * _coords.xy1);
}
half4 LoopingBinaryColorizer_S1_c1_c0_c0(half4 _input, float2 _coords)
{
half4 _tmp_0_inColor = _input;
float2 _tmp_1_coords = _coords;
half t = half(_tmp_1_coords.x);
;
;
int chunk = 0;
;
int pos;
if (t < uthresholds_S1_c1_c0_c0[chunk].y)
{
pos = int(t < uthresholds_S1_c1_c0_c0[chunk].x ? 0 : 1);
}
else
{
pos = int(t < uthresholds_S1_c1_c0_c0[chunk].z ? 2 : 3);
}
;
return half4(half4(float(t) * uscale_S1_c1_c0_c0[pos] + ubias_S1_c1_c0_c0[pos]));
}
half4 TwoPointConicalFocalLayout_S1_c1_c0_c1_c0(half4 _input)
{
half4 _tmp_2_inColor = _input;
float2 _tmp_3_coords = vTransformedCoords_8_S0;
float t = -1.0;
half v = 1.0;
float x_t = -1.0;
if (bool(int(0)))
{
x_t = dot(_tmp_3_coords, _tmp_3_coords) / _tmp_3_coords.x;
}
else if (bool(int(0)))
{
x_t = length(_tmp_3_coords) - _tmp_3_coords.x * float(uinvR1_S1_c1_c0_c1_c0);
}
else
{
float temp = _tmp_3_coords.x * _tmp_3_coords.x - _tmp_3_coords.y * _tmp_3_coords.y;
if (temp >= 0.0)
{
if (bool(int(0)) || !bool(int(1)))
{
x_t = -sqrt(temp) - _tmp_3_coords.x * float(uinvR1_S1_c1_c0_c1_c0);
}
else
{
x_t = sqrt(temp) - _tmp_3_coords.x * float(uinvR1_S1_c1_c0_c1_c0);
}
}
}
if (!bool(int(0)))
{
if (x_t <= 0.0)
{
v = -1.0;
}
}
if (bool(int(1)))
{
if (bool(int(0)))
{
t = x_t;
}
else
{
t = x_t + float(ufx_S1_c1_c0_c1_c0);
}
}
else
{
if (bool(int(0)))
{
t = -x_t;
}
else
{
t = -x_t + float(ufx_S1_c1_c0_c1_c0);
}
}
if (bool(int(0)))
{
t = 1.0 - t;
}
return half4(half4(half(t), v, 0.0, 0.0));
}
half4 MatrixEffect_S1_c1_c0_c1(half4 _input)
{
return TwoPointConicalFocalLayout_S1_c1_c0_c1_c0(_input);
}
half4 ClampedGradient_S1_c1_c0(half4 _input)
{
half4 _tmp_4_inColor = _input;
half4 t = MatrixEffect_S1_c1_c0_c1(_tmp_4_inColor);
half4 outColor;
if (!bool(int(0)) && t.y < 0.0)
{
outColor = half4(0.0);
}
else if (t.x < 0.0)
{
outColor = uleftBorderColor_S1_c1_c0;
}
else if (t.x > 1.0)
{
outColor = urightBorderColor_S1_c1_c0;
}
else
{
outColor = LoopingBinaryColorizer_S1_c1_c0_c0(_tmp_4_inColor, float2(half2(t.x, 0.0)));
}
if (bool(int(0)))
{
outColor.xyz *= outColor.w;
}
return half4(outColor);
}
half4 DisableCoverageAsAlpha_S1_c1(half4 _input)
{
_input = ClampedGradient_S1_c1_c0(_input);
half4 _tmp_5_inColor = _input;
return half4(_input);
}
half4 Dither_S1(half4 _input)
{
_input = DisableCoverageAsAlpha_S1_c1(_input);
half4 _tmp_6_inColor = _input;
half value = MatrixEffect_S1_c0(_tmp_6_inColor, sk_FragCoord.xy).w - 0.5;
return half4(half4(clamp(_input.xyz + value * urange_S1, 0.0, _input.w), _input.w));
}
void main()
{
// Stage 0, QuadPerEdgeAAGeometryProcessor
half4 outputColor_S0;
outputColor_S0 = vcolor_S0;
const half4 outputCoverage_S0 = half4(1);
half4 output_S1;
output_S1 = Dither_S1(outputColor_S0);
{
// Xfer Processor: Porter Duff
sk_FragColor = output_S1 * outputCoverage_S0;
}
}
)");
// This fragment shader is taken from GM_BlurDrawImage.
COMPILER_BENCH(medium, R"(
uniform float3x3 umatrix_S1_c0;
uniform float3x3 umatrix_S2_c0_c0;
uniform float4 urect_S2_c0;
uniform sampler2D uTextureSampler_0_S1;
uniform sampler2D uTextureSampler_0_S2;
flat in half4 vcolor_S0;
noperspective in float2 vTransformedCoords_3_S0;
out half4 sk_FragColor;
half4 TextureEffect_S1_c0_c0(half4 _input)
{
return sample(uTextureSampler_0_S1, vTransformedCoords_3_S0);
}
half4 MatrixEffect_S1_c0(half4 _input)
{
return TextureEffect_S1_c0_c0(_input);
}
half4 DisableCoverageAsAlpha_S1(half4 _input)
{
_input = MatrixEffect_S1_c0(_input);
half4 _tmp_0_inColor = _input;
return half4(_input);
}
half4 TextureEffect_S2_c0_c0_c0(half4 _input, float2 _coords)
{
return sample(uTextureSampler_0_S2, _coords).000r;
}
half4 MatrixEffect_S2_c0_c0(half4 _input, float2 _coords)
{
return TextureEffect_S2_c0_c0_c0(_input, float3x2(umatrix_S2_c0_c0) * _coords.xy1);
}
half4 RectBlur_S2_c0(half4 _input, float2 _coords)
{
half4 _tmp_1_inColor = _input;
float2 _tmp_2_coords = _coords;
half xCoverage;
half yCoverage;
if (bool(int(1)))
{
half2 xy = max(half2(urect_S2_c0.xy - _tmp_2_coords), half2(_tmp_2_coords - urect_S2_c0.zw));
xCoverage = MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(xy.x, 0.5))).w;
yCoverage = MatrixEffect_S2_c0_c0(_tmp_1_inColor, float2(half2(xy.y, 0.5))).w;
}
else
{
half4 rect = half4(half2(urect_S2_c0.xy - _tmp_2_coords), half2(_tmp_2_coords - urect_S2_c0.zw));
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;
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;
}
return half4((_input * xCoverage) * yCoverage);
}
half4 DeviceSpace_S2(half4 _input)
{
return RectBlur_S2_c0(_input, sk_FragCoord.xy);
}
void main()
{
// Stage 0, QuadPerEdgeAAGeometryProcessor
half4 outputColor_S0;
outputColor_S0 = vcolor_S0;
const half4 outputCoverage_S0 = half4(1);
half4 output_S1;
output_S1 = DisableCoverageAsAlpha_S1(outputColor_S0);
half4 output_S2;
output_S2 = DeviceSpace_S2(outputCoverage_S0);
{
// Xfer Processor: Porter Duff
sk_FragColor = output_S1 * output_S2;
}
}
)");
// This is the fragment shader used to blit the Viewer window when running the software rasterizer.
COMPILER_BENCH(small, R"(
uniform float3x3 umatrix_S1_c0;
uniform sampler2D uTextureSampler_0_S1;
flat in half4 vcolor_S0;
noperspective in float2 vTransformedCoords_3_S0;
out half4 sk_FragColor;
half4 TextureEffect_S1_c0_c0(half4 _input)
{
return sample(uTextureSampler_0_S1, vTransformedCoords_3_S0);
}
half4 MatrixEffect_S1_c0(half4 _input)
{
return TextureEffect_S1_c0_c0(_input);
}
half4 DisableCoverageAsAlpha_S1(half4 _input)
{
_input = MatrixEffect_S1_c0(_input);
half4 _tmp_0_inColor = _input;
return half4(_input);
}
void main()
{
// Stage 0, QuadPerEdgeAAGeometryProcessor
half4 outputColor_S0;
outputColor_S0 = vcolor_S0;
const half4 outputCoverage_S0 = half4(1);
half4 output_S1;
output_S1 = DisableCoverageAsAlpha_S1(outputColor_S0);
{
// Xfer Processor: Porter Duff
sk_FragColor = output_S1 * outputCoverage_S0;
}
}
)");
COMPILER_BENCH(tiny, "void main() { sk_FragColor = half4(1); }");
#if defined(SK_BUILD_FOR_UNIX)
#include <malloc.h>
// These benchmarks aren't timed, they produce memory usage statistics. They run standalone, and
// directly add their results to the nanobench log.
void RunSkSLMemoryBenchmarks(NanoJSONResultsWriter* log) {
auto heap_bytes_used = []() { return mallinfo().uordblks; };
auto bench = [log](const char* name, int bytes) {
log->beginObject(name); // test
log->beginObject("meta"); // config
log->appendS32("bytes", bytes); // sub_result
log->endObject(); // config
log->endObject(); // test
};
// Heap used by a default compiler (with no modules loaded)
{
int before = heap_bytes_used();
GrShaderCaps caps;
SkSL::Compiler compiler(&caps);
int after = heap_bytes_used();
bench("sksl_compiler_baseline", after - before);
}
// Heap used by a compiler with the two main GPU modules (fragment + vertex) loaded
{
int before = heap_bytes_used();
GrShaderCaps caps;
SkSL::Compiler compiler(&caps);
compiler.moduleForProgramKind(SkSL::ProgramKind::kVertex);
compiler.moduleForProgramKind(SkSL::ProgramKind::kFragment);
int after = heap_bytes_used();
bench("sksl_compiler_gpu", after - before);
}
// Heap used by a compiler with the runtime shader, color filter and blending modules loaded
{
int before = heap_bytes_used();
GrShaderCaps caps;
SkSL::Compiler compiler(&caps);
compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeColorFilter);
compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeShader);
compiler.moduleForProgramKind(SkSL::ProgramKind::kRuntimeBlender);
int after = heap_bytes_used();
bench("sksl_compiler_runtimeeffect", after - before);
}
}
#else
void RunSkSLMemoryBenchmarks(NanoJSONResultsWriter*) {}
#endif