51dc28505f
For posterity, here's my initial, wrong thinking: If we squint at "return foo" and read it as "result = foo; goto end_of_program", and then remember we can always skip forward jumps (and where's further forward than end of program?), early returns turn out to work just like a store. The reason this is wrong is that by the time we reach a final return, the entire mask stack has been popped back down to its original default ffffffff (active) state. But that return shouldn't override any prior returns. So that scheme isn't quite right. Instead we accumulate the result by disabling updates to lanes that have already returned. By the time we're done, all lanes should have hit _some_ active return, now asserted. Bug: skia:10852 Change-Id: I27b05f04a60ff4a5f2fe5f59bf398c3f7224a41b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/327457 Commit-Queue: Mike Klein <mtklein@google.com> Reviewed-by: Brian Osman <brianosman@google.com>
1247 lines
50 KiB
C++
1247 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"
|
|
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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
|
|
"float tempFloats[8];\n"
|
|
"int median(int a[15]) { return a[7]; }\n"
|
|
"float[8] sums(float a[8]) {\n"
|
|
" float tempFloats[8];\n"
|
|
" tempFloats[0] = a[0];\n"
|
|
" for (int i = 1; i < 8; ++i) { tempFloats[i] = tempFloats[i - 1] + a[i]; }\n"
|
|
" return tempFloats;\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";
|
|
|
|
SkSL::Compiler compiler;
|
|
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[8] = { 0 };
|
|
SkAssertResult(byteCode->run(sums, in, 8, out, 8, fRects, 16));
|
|
for (int i = 0; i < 8; ++i) {
|
|
REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 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) {
|
|
SkSL::Compiler compiler;
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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; }";
|
|
|
|
SkSL::Compiler compiler;
|
|
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
|
|
expect_failure(r, "float main(float x[4]) { return x[-1]; }");
|
|
expect_failure(r, "float2 main(float2 x[2]) { return x[2]; }");
|
|
|
|
// 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";
|
|
|
|
SkSL::Compiler compiler;
|
|
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));
|
|
SkSL::Compiler compiler;
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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) {
|
|
SkSL::Compiler compiler;
|
|
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());
|
|
}
|
|
}
|