skia2/tests/SkSLInterpreterTest.cpp
John Stiles 1b27c3d7a3 Check array bounds when a constant array index is used.
This sort of error would be detected by most backend compilers. This
case was also detected by the bytecode generator. It's easy for us to do
a similar check during SkSL IR generation and report the error sooner.

Also, `convertIndex` had migrated a few hundred lines away from
`convertIndexExpression`, so I moved it back to live next to its parent.

Change-Id: I715d3abf42581782b55ba60df30d0296355667d4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/341377
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
2020-12-07 18:17:30 +00:00

1260 lines
50 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 "include/core/SkM44.h"
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLExternalValue.h"
#include "src/utils/SkJSON.h"
#include "tests/Test.h"
#if defined(SK_ENABLE_SKSL_INTERPRETER)
static bool nearly_equal(const float a[], const float b[], int count) {
for (int i = 0; i < count; ++i) {
if (!SkScalarNearlyEqual(a[i], b[i])) {
return false;
}
}
return true;
}
void test(skiatest::Reporter* r, const char* src, float* in, float* expected,
bool exactCompare = true) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
if (program) {
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
program.reset();
REPORTER_ASSERT(r, !compiler.errorCount());
if (compiler.errorCount() > 0) {
printf("%s\n%s", src, compiler.errorText().c_str());
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
int returnCount = main->getReturnCount();
std::unique_ptr<float[]> out = std::unique_ptr<float[]>(new float[returnCount]);
SkAssertResult(byteCode->run(main, in, main->getParameterCount(), out.get(), returnCount,
nullptr, 0));
bool valid = exactCompare ? !memcmp(out.get(), expected, sizeof(float) * returnCount)
: nearly_equal(out.get(), expected, returnCount);
if (!valid) {
printf("for program: %s\n", src);
printf(" expected (");
const char* separator = "";
for (int i = 0; i < returnCount; ++i) {
printf("%s%f", separator, expected[i]);
separator = ", ";
}
printf("), but received (");
separator = "";
for (int i = 0; i < returnCount; ++i) {
printf("%s%f", separator, out.get()[i]);
separator = ", ";
}
printf(")\n");
main->disassemble();
}
REPORTER_ASSERT(r, valid);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
}
void vec_test(skiatest::Reporter* r, const char* src) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
if (!program) {
REPORT_FAILURE(r, "!program", SkString(compiler.errorText().c_str()));
return;
}
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
if (compiler.errorCount() > 0) {
REPORT_FAILURE(r, "!toByteCode", SkString(compiler.errorText().c_str()));
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
// Test on four different vectors (with varying orderings to get divergent control flow)
const float input[16] = { 1, 2, 3, 4,
4, 3, 2, 1,
7, 5, 8, 6,
6, 8, 5, 7 };
float out_s[16], out_v[16];
memcpy(out_s, input, sizeof(out_s));
memcpy(out_v, input, sizeof(out_v));
// First run in scalar mode to determine the expected output
for (int i = 0; i < 4; ++i) {
SkAssertResult(byteCode->run(main, out_s + i * 4, 4, nullptr, 0, nullptr, 0));
}
// Need to transpose input vectors for striped execution
auto transpose = [](float* v) {
for (int r = 0; r < 4; ++r)
for (int c = 0; c < r; ++c)
std::swap(v[r*4 + c], v[c*4 + r]);
};
// Need to transpose input vectors for striped execution
transpose(out_v);
float* args[] = { out_v, out_v + 4, out_v + 8, out_v + 12 };
// Now run in parallel and compare results
SkAssertResult(byteCode->runStriped(main, 4, args, 4, nullptr, 0, nullptr, 0));
// Transpose striped outputs back
transpose(out_v);
if (0 != memcmp(out_s, out_v, sizeof(out_s))) {
printf("for program: %s\n", src);
for (int i = 0; i < 4; ++i) {
printf("(%g %g %g %g) -> (%g %g %g %g), expected (%g %g %g %g)\n",
input[4*i + 0], input[4*i + 1], input[4*i + 2], input[4*i + 3],
out_v[4*i + 0], out_v[4*i + 1], out_v[4*i + 2], out_v[4*i + 3],
out_s[4*i + 0], out_s[4*i + 1], out_s[4*i + 2], out_s[4*i + 3]);
}
main->disassemble();
REPORT_FAILURE(r, "VecInterpreter mismatch", SkString());
}
}
void test(skiatest::Reporter* r, const char* src, float inR, float inG, float inB, float inA,
float expectedR, float expectedG, float expectedB, float expectedA) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
if (program) {
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
program.reset();
REPORTER_ASSERT(r, !compiler.errorCount());
if (compiler.errorCount() > 0) {
printf("%s\n%s", src, compiler.errorText().c_str());
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
float inoutColor[4] = { inR, inG, inB, inA };
SkAssertResult(byteCode->run(main, inoutColor, 4, nullptr, 0, nullptr, 0));
if (inoutColor[0] != expectedR || inoutColor[1] != expectedG ||
inoutColor[2] != expectedB || inoutColor[3] != expectedA) {
printf("for program: %s\n", src);
printf(" expected (%f, %f, %f, %f), but received (%f, %f, %f, %f)\n", expectedR,
expectedG, expectedB, expectedA, inoutColor[0], inoutColor[1], inoutColor[2],
inoutColor[3]);
main->disassemble();
}
REPORTER_ASSERT(r, inoutColor[0] == expectedR);
REPORTER_ASSERT(r, inoutColor[1] == expectedG);
REPORTER_ASSERT(r, inoutColor[2] == expectedB);
REPORTER_ASSERT(r, inoutColor[3] == expectedA);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
// Do additional testing of 4x1 vs 1x4 to stress divergent control flow, etc.
vec_test(r, src);
}
DEF_TEST(SkSLInterpreterAdd, r) {
test(r, "void main(inout half4 color) { color.r = color.r + color.g; }", 0.25, 0.75, 0, 0, 1,
0.75, 0, 0);
test(r, "void main(inout half4 color) { color += half4(1, 2, 3, 4); }", 4, 3, 2, 1, 5, 5, 5, 5);
test(r, "void main(inout half4 color) { half4 c = color; color += c; }", 0.25, 0.5, 0.75, 1,
0.5, 1, 1.5, 2);
test(r, "void main(inout half4 color) { color.r = int(color.r) + int(color.g); }", 1, 3, 0, 0,
4, 3, 0, 0);
}
DEF_TEST(SkSLInterpreterSubtract, r) {
test(r, "void main(inout half4 color) { color.r = color.r - color.g; }", 1, 0.75, 0, 0, 0.25,
0.75, 0, 0);
test(r, "void main(inout half4 color) { color -= half4(1, 2, 3, 4); }", 5, 5, 5, 5, 4, 3, 2, 1);
test(r, "void main(inout half4 color) { half4 c = color; color -= c; }", 4, 3, 2, 1,
0, 0, 0, 0);
test(r, "void main(inout half4 color) { color.x = -color.x; }", 4, 3, 2, 1, -4, 3, 2, 1);
test(r, "void main(inout half4 color) { color = -color; }", 4, 3, 2, 1, -4, -3, -2, -1);
test(r, "void main(inout half4 color) { color.r = int(color.r) - int(color.g); }", 3, 1, 0, 0,
2, 1, 0, 0);
}
DEF_TEST(SkSLInterpreterMultiply, r) {
test(r, "void main(inout half4 color) { color.r = color.r * color.g; }", 2, 3, 0, 0, 6, 3, 0,
0);
test(r, "void main(inout half4 color) { color *= half4(1, 2, 3, 4); }", 2, 3, 4, 5, 2, 6, 12,
20);
test(r, "void main(inout half4 color) { half4 c = color; color *= c; }", 4, 3, 2, 1,
16, 9, 4, 1);
test(r, "void main(inout half4 color) { color.r = int(color.r) * int(color.g); }", 3, -2, 0, 0,
-6, -2, 0, 0);
}
DEF_TEST(SkSLInterpreterDivide, r) {
test(r, "void main(inout half4 color) { color.r = color.r / color.g; }", 1, 2, 0, 0, 0.5, 2, 0,
0);
test(r, "void main(inout half4 color) { color /= half4(1, 2, 3, 4); }", 12, 12, 12, 12, 12, 6,
4, 3);
test(r, "void main(inout half4 color) { half4 c = color; color /= c; }", 4, 3, 2, 1,
1, 1, 1, 1);
test(r, "void main(inout half4 color) { color.r = int(color.r) / int(color.g); }", 8, -2, 0, 0,
-4, -2, 0, 0);
}
DEF_TEST(SkSLInterpreterRemainder, r) {
test(r, "void main(inout half4 color) { color.r = color.r % color.g; }", 3.125, 2, 0, 0,
1.125, 2, 0, 0);
test(r, "void main(inout half4 color) { color %= half4(1, 2, 3, 4); }", 9.5, 9.5, 9.5, 9.5,
0.5, 1.5, 0.5, 1.5);
test(r, "void main(inout half4 color) { color.r = int(color.r) % int(color.g); }", 8, 3, 0, 0,
2, 3, 0, 0);
test(r, "void main(inout half4 color) { color.rg = half2(int2(int(color.r), int(color.g)) % "
"int(color.b)); }", 8, 10, 6, 0, 2, 4, 6, 0);
}
DEF_TEST(SkSLInterpreterAnd, r) {
test(r, "void main(inout half4 color) { if (color.r > color.g && color.g > color.b) "
"color = half4(color.a); }", 2, 1, 0, 3, 3, 3, 3, 3);
test(r, "void main(inout half4 color) { if (color.r > color.g && color.g > color.b) "
"color = half4(color.a); }", 1, 1, 0, 3, 1, 1, 0, 3);
test(r, "void main(inout half4 color) { if (color.r > color.g && color.g > color.b) "
"color = half4(color.a); }", 2, 1, 1, 3, 2, 1, 1, 3);
test(r, "int global; bool update() { global = 123; return true; }"
"void main(inout half4 color) { global = 0; if (color.r > color.g && update()) "
"color = half4(color.a); color.a = global; }", 2, 1, 1, 3, 3, 3, 3, 123);
test(r, "int global; bool update() { global = 123; return true; }"
"void main(inout half4 color) { global = 0; if (color.r > color.g && update()) "
"color = half4(color.a); color.a = global; }", 1, 1, 1, 3, 1, 1, 1, 0);
}
DEF_TEST(SkSLInterpreterOr, r) {
test(r, "void main(inout half4 color) { if (color.r > color.g || color.g > color.b) "
"color = half4(color.a); }", 2, 1, 0, 3, 3, 3, 3, 3);
test(r, "void main(inout half4 color) { if (color.r > color.g || color.g > color.b) "
"color = half4(color.a); }", 1, 1, 0, 3, 3, 3, 3, 3);
test(r, "void main(inout half4 color) { if (color.r > color.g || color.g > color.b) "
"color = half4(color.a); }", 1, 1, 1, 3, 1, 1, 1, 3);
test(r, "int global; bool update() { global = 123; return true; }"
"void main(inout half4 color) { global = 0; if (color.r > color.g || update()) "
"color = half4(color.a); color.a = global; }", 1, 1, 1, 3, 3, 3, 3, 123);
test(r, "int global; bool update() { global = 123; return true; }"
"void main(inout half4 color) { global = 0; if (color.r > color.g || update()) "
"color = half4(color.a); color.a = global; }", 2, 1, 1, 3, 3, 3, 3, 0);
}
DEF_TEST(SkSLInterpreterBitwise, r) {
test(r, "void main(inout half4 color) { color.r = half(int(color.r) | 3); }",
5, 0, 0, 0, 7, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(int(color.r) & 3); }",
6, 0, 0, 0, 2, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(int(color.r) ^ 3); }",
5, 0, 0, 0, 6, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(~int(color.r) & 3); }",
6, 0, 0, 0, 1, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(uint(color.r) | 3); }",
5, 0, 0, 0, 7, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(uint(color.r) & 3); }",
6, 0, 0, 0, 2, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(uint(color.r) ^ 3); }",
5, 0, 0, 0, 6, 0, 0, 0);
test(r, "void main(inout half4 color) { color.r = half(~uint(color.r) & 3); }",
6, 0, 0, 0, 1, 0, 0, 0);
// Shift operators
unsigned in = 0x80000011;
unsigned out;
out = 0x00000088;
test(r, "int main(int x) { return x << 3; }", (float*)&in, (float*)&out);
out = 0xF0000002;
test(r, "int main(int x) { return x >> 3; }", (float*)&in, (float*)&out);
out = 0x10000002;
test(r, "uint main(uint x) { return x >> 3; }", (float*)&in, (float*)&out);
}
DEF_TEST(SkSLInterpreterMatrix, r) {
float in[16];
float expected[16];
// Constructing matrix from scalar produces a diagonal matrix
in[0] = 1.0f;
expected[0] = 2.0f;
test(r, "float main(float x) { float4x4 m = float4x4(x); return m[1][1] + m[1][2] + m[2][2]; }",
in, expected);
// With non-square matrix
test(r, "float main(float x) { float3x2 m = float3x2(x); return m[0][0] + m[1][1] + m[2][1]; }",
in, expected);
// Constructing from a different-sized matrix fills the remaining space with the identity matrix
test(r, "float main(float x) {"
"float3x2 m = float3x2(x);"
"float4x4 m2 = float4x4(m);"
"return m2[0][0] + m2[3][3]; }",
in, expected);
// Constructing a matrix from vectors or scalars fills in values in column-major order
in[0] = 1.0f;
in[1] = 2.0f;
in[2] = 4.0f;
in[3] = 8.0f;
expected[0] = 6.0f;
test(r, "float main(float4 v) { float2x2 m = float2x2(v); return m[0][1] + m[1][0]; }",
in, expected);
expected[0] = 10.0f;
test(r, "float main(float4 v) {"
"float2x2 m = float2x2(v.x, v.y, v.w, v.z);"
"return m[0][1] + m[1][0]; }",
in, expected);
// Initialize 16 values to be used as inputs to matrix tests
for (int i = 0; i < 16; ++i) { in[i] = (float)i; }
// M+M, M-S, S-M
for (int i = 0; i < 16; ++i) { expected[i] = (float)(2 * i); }
test(r, "float4x4 main(float4x4 m) { return m + m; }", in, expected);
for (int i = 0; i < 16; ++i) { expected[i] = (float)(i + 3); }
test(r, "float4x4 main(float4x4 m) { return m + 3.0; }", in, expected);
test(r, "float4x4 main(float4x4 m) { return 3.0 + m; }", in, expected);
// M-M, M-S, S-M
for (int i = 0; i < 8; ++i) { expected[i] = 8.0f; }
test(r, "float4x2 main(float4x2 m1, float4x2 m2) { return m2 - m1; }", in, expected);
for (int i = 0; i < 16; ++i) { expected[i] = (float)(i - 3); }
test(r, "float4x4 main(float4x4 m) { return m - 3.0; }", in, expected);
for (int i = 0; i < 16; ++i) { expected[i] = (float)(3 - i); }
test(r, "float4x4 main(float4x4 m) { return 3.0 - m; }", in, expected);
// M*S, S*M, M/S, S/M
for (int i = 0; i < 16; ++i) { expected[i] = (float)(i * 3); }
test(r, "float4x4 main(float4x4 m) { return m * 3.0; }", in, expected);
test(r, "float4x4 main(float4x4 m) { return 3.0 * m; }", in, expected);
for (int i = 0; i < 16; ++i) { expected[i] = (float)(i) / 2.0f; }
test(r, "float4x4 main(float4x4 m) { return m / 2.0; }", in, expected);
for (int i = 0; i < 16; ++i) { expected[i] = 1.0f / (float)(i + 1); }
test(r, "float4x4 main(float4x4 m) { return 1.0 / (m + 1); }", in, expected);
#if 0
// Matrix negation - legal in GLSL, not in SkSL?
for (int i = 0; i < 16; ++i) { expected[i] = (float)(-i); }
test(r, "float4x4 main(float4x4 m) { return -m; }", in, 16, expected);
#endif
// M*V, V*M
for (int i = 0; i < 4; ++i) {
expected[i] = 12.0f*i + 13.0f*(i+4) + 14.0f*(i+8);
}
test(r, "float4 main(float3x4 m, float3 v) { return m * v; }", in, expected);
for (int i = 0; i < 4; ++i) {
expected[i] = 12.0f*(3*i) + 13.0f*(3*i+1) + 14.0f*(3*i+2);
}
test(r, "float4 main(float4x3 m, float3 v) { return v * m; }", in, expected);
// M*M
{
SkM44 m = SkM44::ColMajor(in);
SkM44 m2;
float in2[16];
for (int i = 0; i < 16; ++i) {
in2[i] = (i + 4) % 16;
}
m2 = SkM44::ColMajor(in2);
m.setConcat(m, m2);
// Rearrange the columns on the RHS so we detect left-hand/right-hand errors
test(r, "float4x4 main(float4x4 m) { return m * float4x4(m[1], m[2], m[3], m[0]); }",
in, (float*)&m);
}
}
DEF_TEST(SkSLInterpreterTernary, r) {
test(r, "void main(inout half4 color) { color.r = color.g > color.b ? color.g : color.b; }",
0, 1, 2, 0, 2, 1, 2, 0);
test(r, "void main(inout half4 color) { color.r = color.g > color.b ? color.g : color.b; }",
0, 3, 2, 0, 3, 3, 2, 0);
}
DEF_TEST(SkSLInterpreterCast, r) {
union Val {
float f;
uint32_t u;
int32_t s;
};
Val input[2];
Val expected[2];
input[0].s = 3;
input[1].s = -5;
expected[0].f = 3.0f;
expected[1].f = -5.0f;
test(r, "float main(int x) { return float (x); }", (float*)input, (float*)expected);
test(r, "float2 main(int2 x) { return float2(x); }", (float*)input, (float*)expected);
input[0].u = 3;
input[1].u = 5;
expected[0].f = 3.0f;
expected[1].f = 5.0f;
test(r, "float main(uint x) { return float (x); }", (float*)input, (float*)expected);
test(r, "float2 main(uint2 x) { return float2(x); }", (float*)input, (float*)expected);
input[0].f = 3.0f;
input[1].f = -5.0f;
expected[0].s = 3;
expected[1].s = -5;
test(r, "int main(float x) { return int (x); }", (float*)input, (float*)expected);
test(r, "int2 main(float2 x) { return int2(x); }", (float*)input, (float*)expected);
input[0].s = 3;
expected[0].f = 3.0f;
expected[1].f = 3.0f;
test(r, "float2 main(int x) { return float2(x); }", (float*)input, (float*)expected);
}
DEF_TEST(SkSLInterpreterIf, r) {
test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 3, 0, 0,
5, 3, 0, 1);
test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 5, 0, 0,
5, 5, 0, 0);
test(r, "void main(inout half4 color) { if (color.r > color.g) color.a = 1; }", 5, 6, 0, 0,
5, 6, 0, 0);
test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 3, 5, 0, 0,
3, 5, 0, 1);
test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 5, 5, 0, 0,
5, 5, 0, 0);
test(r, "void main(inout half4 color) { if (color.r < color.g) color.a = 1; }", 6, 5, 0, 0,
6, 5, 0, 0);
test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 3, 0, 0,
5, 3, 0, 1);
test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 5, 0, 0,
5, 5, 0, 1);
test(r, "void main(inout half4 color) { if (color.r >= color.g) color.a = 1; }", 5, 6, 0, 0,
5, 6, 0, 0);
test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 3, 5, 0, 0,
3, 5, 0, 1);
test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 5, 5, 0, 0,
5, 5, 0, 1);
test(r, "void main(inout half4 color) { if (color.r <= color.g) color.a = 1; }", 6, 5, 0, 0,
6, 5, 0, 0);
test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; }", 2, 2, 0, 0,
2, 2, 0, 1);
test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; }", 2, -2, 0, 0,
2, -2, 0, 0);
test(r, "void main(inout half4 color) { if (color.r != color.g) color.a = 1; }", 2, 2, 0, 0,
2, 2, 0, 0);
test(r, "void main(inout half4 color) { if (color.r != color.g) color.a = 1; }", 2, -2, 0, 0,
2, -2, 0, 1);
test(r, "void main(inout half4 color) { if (!(color.r == color.g)) color.a = 1; }", 2, 2, 0, 0,
2, 2, 0, 0);
test(r, "void main(inout half4 color) { if (!(color.r == color.g)) color.a = 1; }", 2, -2, 0, 0,
2, -2, 0, 1);
test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; else "
"color.a = 2; }", 1, 1, 0, 0, 1, 1, 0, 1);
test(r, "void main(inout half4 color) { if (color.r == color.g) color.a = 1; else "
"color.a = 2; }", 2, -2, 0, 0, 2, -2, 0, 2);
}
DEF_TEST(SkSLInterpreterIfVector, r) {
test(r, "void main(inout half4 color) { if (color.rg == color.ba) color.a = 1; }",
1, 2, 1, 2, 1, 2, 1, 1);
test(r, "void main(inout half4 color) { if (color.rg == color.ba) color.a = 1; }",
1, 2, 3, 2, 1, 2, 3, 2);
test(r, "void main(inout half4 color) { if (color.rg != color.ba) color.a = 1; }",
1, 2, 1, 2, 1, 2, 1, 2);
test(r, "void main(inout half4 color) { if (color.rg != color.ba) color.a = 1; }",
1, 2, 3, 2, 1, 2, 3, 1);
}
DEF_TEST(SkSLInterpreterWhile, r) {
test(r, "void main(inout half4 color) { while (color.r < 8) { color.r++; } }",
1, 2, 3, 4, 8, 2, 3, 4);
test(r, "void main(inout half4 color) { while (color.r < 1) color.r += 0.25; }", 0, 0, 0, 0, 1,
0, 0, 0);
test(r, "void main(inout half4 color) { while (color.r > 1) color.r -= 0.25; }", 0, 0, 0, 0, 0,
0, 0, 0);
test(r, "void main(inout half4 color) { while (true) { color.r += 0.5; "
"if (color.r > 5) break; } }", 0, 0, 0, 0, 5.5, 0, 0, 0);
test(r, "void main(inout half4 color) { while (color.r < 10) { color.r += 0.5; "
"if (color.r < 5) continue; break; } }", 0, 0, 0, 0, 5, 0, 0, 0);
test(r,
"void main(inout half4 color) {"
" while (true) {"
" if (color.r > 4) { break; }"
" while (true) { color.a = 1; break; }"
" break;"
" }"
"}",
6, 5, 4, 3, 6, 5, 4, 3);
}
DEF_TEST(SkSLInterpreterDo, r) {
test(r, "void main(inout half4 color) { do color.r += 0.25; while (color.r < 1); }", 0, 0, 0, 0,
1, 0, 0, 0);
test(r, "void main(inout half4 color) { do color.r -= 0.25; while (color.r > 1); }", 0, 0, 0, 0,
-0.25, 0, 0, 0);
test(r, "void main(inout half4 color) { do { color.r += 0.5; if (color.r > 1) break; } while "
"(true); }", 0, 0, 0, 0, 1.5, 0, 0, 0);
test(r, "void main(inout half4 color) {do { color.r += 0.5; if (color.r < 5) "
"continue; if (color.r >= 5) break; } while (true); }", 0, 0, 0, 0, 5, 0, 0, 0);
test(r, "void main(inout half4 color) { do { color.r += 0.5; } while (false); }",
0, 0, 0, 0, 0.5, 0, 0, 0);
}
DEF_TEST(SkSLInterpreterFor, r) {
test(r, "void main(inout half4 color) { for (int i = 1; i <= 10; ++i) color.r += i; }", 0, 0, 0,
0, 55, 0, 0, 0);
test(r,
"void main(inout half4 color) {"
" for (int i = 1; i <= 10; ++i)"
" for (int j = i; j <= 10; ++j)"
" color.r += j;"
"}",
0, 0, 0, 0,
385, 0, 0, 0);
test(r,
"void main(inout half4 color) {"
" for (int i = 1; i <= 10; ++i)"
" for (int j = 1; ; ++j) {"
" if (i == j) continue;"
" if (j > 10) break;"
" color.r += j;"
" }"
"}",
0, 0, 0, 0,
495, 0, 0, 0);
}
DEF_TEST(SkSLInterpreterPrefixPostfix, r) {
test(r, "void main(inout half4 color) { color.r = ++color.g; }", 1, 2, 3, 4, 3, 3, 3, 4);
test(r, "void main(inout half4 color) { color.r = color.g++; }", 1, 2, 3, 4, 2, 3, 3, 4);
}
DEF_TEST(SkSLInterpreterSwizzle, r) {
test(r, "void main(inout half4 color) { color = color.abgr; }", 1, 2, 3, 4, 4, 3, 2, 1);
test(r, "void main(inout half4 color) { color.rgb = half4(5, 6, 7, 8).bbg; }", 1, 2, 3, 4, 7, 7,
6, 4);
test(r, "void main(inout half4 color) { color.bgr = int3(5, 6, 7); }", 1, 2, 3, 4, 7, 6,
5, 4);
}
DEF_TEST(SkSLInterpreterGlobal, r) {
test(r, "int x; void main(inout half4 color) { x = 10; color.b = x; }", 1, 2, 3, 4, 1, 2, 10,
4);
test(r, "float4 x; void main(inout float4 color) { x = color * 2; color = x; }",
1, 2, 3, 4, 2, 4, 6, 8);
test(r, "float4 x; void main(inout float4 color) { x = float4(5, 6, 7, 8); color = x.wzyx; }",
1, 2, 3, 4, 8, 7, 6, 5);
test(r, "float4 x; void main(inout float4 color) { x.wzyx = float4(5, 6, 7, 8); color = x; }",
1, 2, 3, 4, 8, 7, 6, 5);
}
DEF_TEST(SkSLInterpreterGeneric, r) {
float value1 = 5;
float expected1 = 25;
test(r, "float main(float x) { return x * x; }", &value1, &expected1);
float value2[2] = { 5, 25 };
float expected2[2] = { 25, 625 };
test(r, "float2 main(float x, float y) { return float2(x * x, y * y); }", value2, expected2);
}
DEF_TEST(SkSLInterpreterCompound, r) {
struct RectAndColor { SkIRect fRect; SkColor4f fColor; };
struct ManyRects { int fNumRects; RectAndColor fRects[4]; };
const char* src =
// Some struct definitions
"struct Point { int x; int y; };\n"
"struct Rect { Point p0; Point p1; };\n"
"struct RectAndColor { Rect r; float4 color; };\n"
// Structs as globals, parameters, return values
"RectAndColor temp;\n"
"int rect_height(Rect r) { return r.p1.y - r.p0.y; }\n"
"RectAndColor make_blue_rect(int w, int h) {\n"
" temp.r.p0.x = temp.r.p0.y = 0;\n"
" temp.r.p1.x = w; temp.r.p1.y = h;\n"
" temp.color = float4(0, 1, 0, 1);\n"
" return temp;\n"
"}\n"
// Initialization and assignment of types larger than 4 slots
"RectAndColor init_big(RectAndColor r) { RectAndColor s = r; return s; }\n"
"RectAndColor copy_big(RectAndColor r) { RectAndColor s; s = r; return s; }\n"
// Same for arrays, including some non-constant indexing
"int median(int a[15]) { return a[7]; }\n"
"float tempFloats[8];\n"
"float sums(float a[8]) {\n"
" tempFloats[0] = a[0];\n"
" for (int i = 1; i < 8; ++i) { tempFloats[i] = tempFloats[i - 1] + a[i]; }\n"
" return tempFloats[7];\n"
"}\n"
// Uniforms, array-of-structs, dynamic indices
"uniform Rect gRects[4];\n"
"Rect get_rect(int i) { return gRects[i]; }\n"
// Kitchen sink (swizzles, inout, SoAoS)
"struct ManyRects { int numRects; RectAndColor rects[4]; };\n"
"void fill_rects(inout ManyRects mr) {\n"
" for (int i = 0; i < mr.numRects; ++i) {\n"
" mr.rects[i].r = gRects[i];\n"
" float b = mr.rects[i].r.p1.y;\n"
" mr.rects[i].color = float4(b, b, b, b);\n"
" }\n"
"}\n";
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
settings.fRemoveDeadFunctions = false;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, !compiler.errorCount());
auto rect_height = byteCode->getFunction("rect_height"),
make_blue_rect = byteCode->getFunction("make_blue_rect"),
median = byteCode->getFunction("median"),
sums = byteCode->getFunction("sums"),
get_rect = byteCode->getFunction("get_rect"),
fill_rects = byteCode->getFunction("fill_rects");
SkIRect gRects[4] = { { 1,2,3,4 }, { 5,6,7,8 }, { 9,10,11,12 }, { 13,14,15,16 } };
const float* fRects = (const float*)gRects;
{
SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
int out = 0;
SkAssertResult(byteCode->run(rect_height, (float*)&in, 4, (float*)&out, 1, fRects, 16));
REPORTER_ASSERT(r, out == 30);
}
{
int in[2] = { 15, 25 };
RectAndColor out;
SkAssertResult(byteCode->run(make_blue_rect, (float*)in, 2, (float*)&out, 8, fRects, 16));
REPORTER_ASSERT(r, out.fRect.width() == 15);
REPORTER_ASSERT(r, out.fRect.height() == 25);
SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
REPORTER_ASSERT(r, out.fColor == blue);
}
{
int in[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
int out = 0;
SkAssertResult(byteCode->run(median, (float*)in, 15, (float*)&out, 1, fRects, 16));
REPORTER_ASSERT(r, out == 8);
}
{
float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
float out = 0;
SkAssertResult(byteCode->run(sums, in, 8, &out, 1, fRects, 16));
REPORTER_ASSERT(r, out == static_cast<float>((7 + 1) * (7 + 2) / 2));
}
{
int in = 2;
SkIRect out = SkIRect::MakeEmpty();
SkAssertResult(byteCode->run(get_rect, (float*)&in, 1, (float*)&out, 4, fRects, 16));
REPORTER_ASSERT(r, out == gRects[2]);
}
{
ManyRects in;
memset(&in, 0, sizeof(in));
in.fNumRects = 2;
SkAssertResult(byteCode->run(fill_rects, (float*)&in, 33, nullptr, 0, fRects, 16));
ManyRects expected;
memset(&expected, 0, sizeof(expected));
expected.fNumRects = 2;
for (int i = 0; i < 2; ++i) {
expected.fRects[i].fRect = gRects[i];
float c = gRects[i].fBottom;
expected.fRects[i].fColor = { c, c, c, c };
}
REPORTER_ASSERT(r, memcmp(&in, &expected, sizeof(in)) == 0);
}
}
static void expect_failure(skiatest::Reporter* r, const char* src) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
auto byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, compiler.errorCount() > 0);
REPORTER_ASSERT(r, !byteCode);
}
static void expect_run_failure(skiatest::Reporter* r, const char* src, float* in) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
auto byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, byteCode);
auto fun = byteCode->getFunction("main");
bool result = byteCode->run(fun, in, fun->getParameterCount(), nullptr, 0, nullptr, 0);
REPORTER_ASSERT(r, !result);
}
DEF_TEST(SkSLInterpreterRestrictFunctionCalls, r) {
// Ensure that simple recursion is not allowed
expect_failure(r, "float main() { return main() + 1; }");
// Ensure that calls to undefined functions are not allowed (to prevent mutual recursion)
expect_failure(r, "float foo(); float bar() { return foo(); } float foo() { return bar(); }");
// returns are not allowed inside loops
expect_failure(r, "float main(float x) { while (x > 1) { return x; } return 0; }");
}
DEF_TEST(SkSLInterpreterEarlyReturn, r) {
// Unlike returns in loops, returns in conditionals should work.
const char* src = "float main(float x, float y) { if (x < y) { return x; } return y; }";
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src),
SkSL::Program::Settings{});
REPORTER_ASSERT(r, program);
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, byteCode);
// Our function should work fine via run().
const SkSL::ByteCodeFunction* fun = byteCode->getFunction("main");
float in[] = { 1.0f, 2.0f };
float ret;
REPORTER_ASSERT(r, byteCode->run(fun, in,2, &ret,1, nullptr,0));
REPORTER_ASSERT(r, ret == 1.0f);
in[0] = 3.0f;
REPORTER_ASSERT(r, byteCode->run(fun, in,2, &ret,1, nullptr,0));
REPORTER_ASSERT(r, ret == 2.0f);
// Now same again via runStriped(), won't quite work yet.
float xs[] = { 1.0f, 3.0f },
ys[] = { 2.0f, 2.0f };
float rets[2];
float* ins[] = { xs, ys };
float* outs[] = { rets+0, rets+1 } ;
REPORTER_ASSERT(r, byteCode->runStriped(fun,2, ins,2, outs,1, nullptr,0));
REPORTER_ASSERT(r, rets[0] == 1.0f);
//REPORTER_ASSERT(r, rets[1] == 2.0f); // TODO: skia:10852, make striped early returns work.
}
DEF_TEST(SkSLInterpreterArrayBounds, r) {
// Out of bounds array access at compile time prevents a program from being generated at all
// (tested in ArrayIndexOutOfRange.sksl).
// Out of bounds array access at runtime is pinned, and we don't update any inout data.
float in[3] = { -1.0f, 1.0f, 2.0f };
expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
REPORTER_ASSERT(r, in[0] == -1.0f && in[1] == 1.0f && in[2] == 2.0f);
in[0] = 3.0f;
expect_run_failure(r, "void main(inout float data[3]) { data[int(data[0])] = 0; }", in);
REPORTER_ASSERT(r, in[0] == 3.0f && in[1] == 1.0f && in[2] == 2.0f);
}
DEF_TEST(SkSLInterpreterFunctions, r) {
const char* src =
"float sqr(float x) { return x * x; }\n"
"float sub(float x, float y) { return x - y; }\n"
"float main(float x) { return sub(sqr(x), x); }\n"
// Different signatures
"float dot(float2 a, float2 b) { return a.x*b.x + a.y*b.y; }\n"
"float dot(float3 a, float3 b) { return a.x*b.x + a.y*b.y + a.z*b.z; }\n"
"float dot3_test(float x) { return dot(float3(x, x + 1, x + 2), float3(1, -1, 2)); }\n"
"float dot2_test(float x) { return dot(float2(x, x + 1), float2(1, -1)); }\n";
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
settings.fRemoveDeadFunctions = false;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(SkSL::Program::kGeneric_Kind,
SkSL::String(src), settings);
REPORTER_ASSERT(r, program);
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, !compiler.errorCount());
auto sub = byteCode->getFunction("sub");
auto sqr = byteCode->getFunction("sqr");
auto main = byteCode->getFunction("main");
auto tan = byteCode->getFunction("tan");
auto dot3 = byteCode->getFunction("dot3_test");
auto dot2 = byteCode->getFunction("dot2_test");
REPORTER_ASSERT(r, sub);
REPORTER_ASSERT(r, sqr);
REPORTER_ASSERT(r, main);
REPORTER_ASSERT(r, !tan);
REPORTER_ASSERT(r, dot3);
REPORTER_ASSERT(r, dot2);
float out = 0.0f;
float in = 3.0f;
SkAssertResult(byteCode->run(main, &in, 1, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = 6.0f);
SkAssertResult(byteCode->run(dot3, &in, 1, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = 9.0f);
SkAssertResult(byteCode->run(dot2, &in, 1, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = -1.0f);
}
DEF_TEST(SkSLInterpreterOutParams, r) {
test(r,
"void oneAlpha(inout half4 color) { color.a = 1; }"
"void main(inout half4 color) { oneAlpha(color); }",
0, 0, 0, 0, 0, 0, 0, 1);
test(r,
"half2 tricky(half x, half y, inout half2 color, half z) {"
" color.xy = color.yx;"
" return half2(x + y, z);"
"}"
"void main(inout half4 color) {"
" half2 t = tricky(1, 2, color.rb, 5);"
" color.ga = t;"
"}",
1, 2, 3, 4, 3, 3, 1, 5);
}
DEF_TEST(SkSLInterpreterSwizzleSingleLvalue, r) {
// Add in your SkSL here.
test(r,
"void main(inout half4 color) { color.xywz = half4(1,2,3,4); }",
0, 0, 0, 0, 1, 2, 4, 3);
}
DEF_TEST(SkSLInterpreterSwizzleDoubleLvalue, r) {
// Add in your SkSL here.
test(r,
"void main(inout half4 color) { color.xywz.yxzw = half4(1,2,3,4); }",
0, 0, 0, 0, 2, 1, 4, 3);
}
DEF_TEST(SkSLInterpreterMathFunctions, r) {
float value[4], expected[4];
value[0] = 0.0f; expected[0] = 0.0f;
test(r, "float main(float x) { return sin(x); }", value, expected);
test(r, "float main(float x) { return tan(x); }", value, expected);
value[0] = 0.0f; expected[0] = 1.0f;
test(r, "float main(float x) { return cos(x); }", value, expected);
value[0] = 25.0f; expected[0] = 5.0f;
test(r, "float main(float x) { return sqrt(x); }", value, expected);
value[0] = 90.0f; expected[0] = sk_float_degrees_to_radians(value[0]);
test(r, "float main(float x) { return radians(x); }", value, expected);
value[0] = 1.0f; value[1] = -1.0f;
expected[0] = 1.0f / SK_FloatSqrt2; expected[1] = -1.0f / SK_FloatSqrt2;
test(r, "float2 main(float2 x) { return normalize(x); }", value, expected);
}
DEF_TEST(SkSLInterpreterVoidFunction, r) {
test(r,
"half x; void foo() { x = 1.0; }"
"void main(inout half4 color) { foo(); color.r = x; }",
0, 0, 0, 0, 1, 0, 0, 0);
}
DEF_TEST(SkSLInterpreterMix, r) {
float value, expected;
value = 0.5f; expected = 0.0f;
test(r, "float main(float x) { return mix(-10, 10, x); }", &value, &expected);
value = 0.75f; expected = 5.0f;
test(r, "float main(float x) { return mix(-10, 10, x); }", &value, &expected);
value = 2.0f; expected = 30.0f;
test(r, "float main(float x) { return mix(-10, 10, x); }", &value, &expected);
float valueVectors[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f },
expectedVector[] = { 3.0f, 4.0f, 5.0f, 6.0f };
test(r, "float4 main(float4 x, float4 y) { return mix(x, y, 0.5); }", valueVectors,
expectedVector);
}
DEF_TEST(SkSLInterpreterCross, r) {
float args[] = { 1.0f, 4.0f, -6.0f, -2.0f, 7.0f, -3.0f };
SkV3 cross = SkV3::Cross({args[0], args[1], args[2]},
{args[3], args[4], args[5]});
float expected[] = { cross.x, cross.y, cross.z };
test(r, "float3 main(float3 x, float3 y) { return cross(x, y); }", args, expected);
}
DEF_TEST(SkSLInterpreterInverse, r) {
{
SkMatrix m;
m.setRotate(30).postScale(1, 2);
float args[4] = { m[0], m[3], m[1], m[4] };
SkAssertResult(m.invert(&m));
float expt[4] = { m[0], m[3], m[1], m[4] };
test(r, "float2x2 main(float2x2 m) { return inverse(m); }", args, expt, false);
}
{
SkMatrix m;
m.setRotate(30).postScale(1, 2).postTranslate(1, 2);
float args[9] = { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] };
SkAssertResult(m.invert(&m));
float expt[9] = { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] };
test(r, "float3x3 main(float3x3 m) { return inverse(m); }", args, expt, false);
}
{
float args[16], expt[16];
// just some crazy thing that is invertible
SkM44 m = {1, 2, 3, 4, 1, 2, 0, 3, 1, 0, 1, 4, 1, 3, 2, 0};
m.getColMajor(args);
SkAssertResult(m.invert(&m));
m.getColMajor(expt);
test(r, "float4x4 main(float4x4 m) { return inverse(m); }", args, expt, false);
}
}
DEF_TEST(SkSLInterpreterDot, r) {
float args[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
float expected = args[0] * args[2] +
args[1] * args[3];
test(r, "float main(float2 x, float2 y) { return dot(x, y); }", args, &expected);
expected = args[0] * args[3] +
args[1] * args[4] +
args[2] * args[5];
test(r, "float main(float3 x, float3 y) { return dot(x, y); }", args, &expected);
expected = args[0] * args[4] +
args[1] * args[5] +
args[2] * args[6] +
args[3] * args[7];
test(r, "float main(float4 x, float4 y) { return dot(x, y); }", args, &expected);
}
static const SkSL::Type& type_of(const skjson::Value* value, SkSL::Compiler* compiler) {
switch (value->getType()) {
case skjson::Value::Type::kNumber: {
float f = *value->as<skjson::NumberValue>();
if (f == (float) (int) f) {
return *compiler->context().fInt_Type;
}
return *compiler->context().fFloat_Type;
}
case skjson::Value::Type::kBool:
return *compiler->context().fBool_Type;
default:
return *compiler->context().fVoid_Type;
}
}
class JSONExternalValue : public SkSL::ExternalValue {
public:
JSONExternalValue(const char* name, const skjson::Value* value, SkSL::Compiler* compiler)
: INHERITED(name, type_of(value, compiler))
, fValue(*value)
, fCompiler(*compiler) {}
bool canRead() const override {
return type() != *fCompiler.context().fVoid_Type;
}
void read(int /*unusedIndex*/, float* target) const override {
if (type() == *fCompiler.context().fInt_Type) {
*(int*) target = *fValue.as<skjson::NumberValue>();
} else if (type() == *fCompiler.context().fFloat_Type) {
*(float*) target = *fValue.as<skjson::NumberValue>();
} else if (type() == *fCompiler.context().fBool_Type) {
// ByteCode "booleans" are actually bit-masks
*(int*) target = *fValue.as<skjson::BoolValue>() ? ~0 : 0;
} else {
SkASSERT(false);
}
}
SkSL::ExternalValue* getChild(const char* name) const override {
if (fValue.getType() == skjson::Value::Type::kObject) {
const skjson::Value& v = fValue.as<skjson::ObjectValue>()[name];
fOwned.push_back(std::make_unique<JSONExternalValue>(name, &v, &fCompiler));
return fOwned.back().get();
}
return nullptr;
}
private:
const skjson::Value& fValue;
SkSL::Compiler& fCompiler;
mutable std::vector<std::unique_ptr<SkSL::ExternalValue>> fOwned;
using INHERITED = SkSL::ExternalValue;
};
class PointerExternalValue : public SkSL::ExternalValue {
public:
PointerExternalValue(const char* name, const SkSL::Type& type, void* data, size_t size)
: INHERITED(name, type)
, fBytes(data)
, fSize(size) {}
bool canRead() const override {
return true;
}
bool canWrite() const override {
return true;
}
void read(int /*unusedIndex*/, float* target) const override {
memcpy(target, fBytes, fSize);
}
void write(int /*unusedIndex*/, float* src) const override {
memcpy(fBytes, src, fSize);
}
private:
void* fBytes;
size_t fSize;
using INHERITED = SkSL::ExternalValue;
};
DEF_TEST(SkSLInterpreterExternalValues, r) {
const char* json = "{ \"value1\": 12, \"child\": { \"value2\": true, \"value3\": 5.5 } }";
skjson::DOM dom(json, strlen(json));
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
const char* src = "float main() {"
" outValue = 152;"
" return root.child.value2 ? root.value1 * root.child.value3 : -1;"
"}";
int32_t outValue = -1;
std::vector<std::unique_ptr<SkSL::ExternalValue>> externalValues;
externalValues.push_back(std::make_unique<JSONExternalValue>("root", &dom.root(), &compiler));
externalValues.push_back(std::make_unique<PointerExternalValue>(
"outValue", *compiler.context().fInt_Type, &outValue, sizeof(outValue)));
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
SkSL::Program::kGeneric_Kind, SkSL::String(src), settings, &externalValues);
REPORTER_ASSERT(r, program);
if (program) {
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, !compiler.errorCount());
if (compiler.errorCount() > 0) {
printf("%s\n%s", src, compiler.errorText().c_str());
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
float out;
SkAssertResult(byteCode->run(main, nullptr, 0, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out == 66.0);
REPORTER_ASSERT(r, outValue == 152);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
}
DEF_TEST(SkSLInterpreterExternalValuesVector, r) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
const char* src = "void main() {"
" value *= 2;"
"}";
int32_t value[4] = { 1, 2, 3, 4 };
std::vector<std::unique_ptr<SkSL::ExternalValue>> externalValues;
externalValues.push_back(std::make_unique<PointerExternalValue>(
"value", *compiler.context().fInt4_Type, value, sizeof(value)));
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
SkSL::Program::kGeneric_Kind, SkSL::String(src), settings, &externalValues);
REPORTER_ASSERT(r, program);
if (program) {
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, !compiler.errorCount());
if (compiler.errorCount() > 0) {
printf("%s\n%s", src, compiler.errorText().c_str());
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
SkAssertResult(byteCode->run(main, nullptr, 0, nullptr, 0, nullptr, 0));
REPORTER_ASSERT(r, value[0] == 2);
REPORTER_ASSERT(r, value[1] == 4);
REPORTER_ASSERT(r, value[2] == 6);
REPORTER_ASSERT(r, value[3] == 8);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
}
class FunctionExternalValue : public SkSL::ExternalValue {
public:
FunctionExternalValue(const char* name, float(*function)(float), SkSL::Compiler& compiler)
: INHERITED(name, *compiler.context().fFloat_Type)
, fCompiler(compiler)
, fFunction(function) {}
bool canCall() const override {
return true;
}
int callParameterCount() const override {
return 1;
}
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fFloat_Type.get();
}
void call(int /*unusedIndex*/, float* arguments, float* outReturn) const override {
outReturn[0] = fFunction(arguments[0]);
}
private:
SkSL::Compiler& fCompiler;
float (*fFunction)(float);
using INHERITED = SkSL::ExternalValue;
};
DEF_TEST(SkSLInterpreterExternalValuesCall, r) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
const char* src = "float main() {"
" return external(25);"
"}";
std::vector<std::unique_ptr<SkSL::ExternalValue>> externalValues;
externalValues.push_back(std::make_unique<FunctionExternalValue>(
"external", [](float x) { return (float)sqrt(x); }, compiler));
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
SkSL::Program::kGeneric_Kind, SkSL::String(src), settings, &externalValues);
REPORTER_ASSERT(r, program);
if (program) {
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, !compiler.errorCount());
if (compiler.errorCount() > 0) {
printf("%s\n%s", src, compiler.errorText().c_str());
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
float out;
SkAssertResult(byteCode->run(main, nullptr, 0, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out == 5.0);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
}
class VectorFunctionExternalValue : public SkSL::ExternalValue {
public:
VectorFunctionExternalValue(const char* name, void(*function)(float[4], float[4]),
SkSL::Compiler& compiler)
: INHERITED(name, *compiler.context().fFloat4_Type)
, fCompiler(compiler)
, fFunction(function) {}
bool canCall() const override {
return true;
}
int callParameterCount() const override {
return 1;
}
void getCallParameterTypes(const SkSL::Type** outTypes) const override {
outTypes[0] = fCompiler.context().fFloat4_Type.get();
}
void call(int /*unusedIndex*/, float* arguments, float* outReturn) const override {
fFunction(arguments, outReturn);
}
private:
SkSL::Compiler& fCompiler;
void (*fFunction)(float[4], float[4]);
using INHERITED = SkSL::ExternalValue;
};
DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
GrShaderCaps caps(GrContextOptions{});
SkSL::Compiler compiler(&caps);
SkSL::Program::Settings settings;
const char* src =
"float4 main() {"
" return external(float4(1, 4, 9, 16));"
"}";
std::vector<std::unique_ptr<SkSL::ExternalValue>> externalValues;
externalValues.push_back(std::make_unique<VectorFunctionExternalValue>(
"external",
[](float in[4], float out[4]) {
out[0] = sqrt(in[0]);
out[1] = sqrt(in[1]);
out[2] = sqrt(in[2]);
out[3] = sqrt(in[3]);
},
compiler));
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
SkSL::Program::kGeneric_Kind, SkSL::String(src), settings, &externalValues);
REPORTER_ASSERT(r, program);
if (program) {
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
REPORTER_ASSERT(r, !compiler.errorCount());
if (compiler.errorCount() > 0) {
printf("%s\n%s", src, compiler.errorText().c_str());
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
float out[4];
SkAssertResult(byteCode->run(main, nullptr, 0, out, 4, nullptr, 0));
REPORTER_ASSERT(r, out[0] == 1.0);
REPORTER_ASSERT(r, out[1] == 2.0);
REPORTER_ASSERT(r, out[2] == 3.0);
REPORTER_ASSERT(r, out[3] == 4.0);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
}
#endif // SK_ENABLE_SKSL_INTERPRETER