985403a274
Things have evolved since these were last updated. In particular, we no longer emit binding and set decorations for each individual uniform. Backends that rely on that (SPIRV, Metal), generate SkSL that has all the uniforms grouped in a buffer. GLSL emits the uniforms with no layout qualifiers. SPIRV supported this already (and automatically applied the default binding/set to the implicitly created uniform buffer). Metal *almost* supported this - it handled the uniform buffer correctly, but didn't apply the default binding to any un-decorated samplers. Bug: skia:12670 Change-Id: I7b193b89f86441344cf7ad5b7ed5e07451a1cc6d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/475157 Reviewed-by: Ethan Nicholas <ethannicholas@google.com> Commit-Queue: Brian Osman <brianosman@google.com>
453 lines
13 KiB
C++
453 lines
13 KiB
C++
/*
|
|
* 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(SkSL::String name, const char* src, bool optimize, Output output)
|
|
: fName(SkSL::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());
|
|
}
|
|
SkSL::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:
|
|
SkSL::String fName;
|
|
SkSL::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
|