Convert interpreter "compound types" test to SkVM

This is the first test that used uniform data, so fix up how uniforms
work in the generic SkSL-to-SkVM function.

Bug: skia:11094
Bug: skia:11096
Change-Id: Ie391c1a6b8b68f0f4f014d7e767d7b5101341fab
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/352739
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2021-01-11 13:16:28 -05:00 committed by Skia Commit-Bot
parent a55af2311a
commit c92df39de0
3 changed files with 83 additions and 54 deletions

View File

@ -1445,13 +1445,11 @@ skvm::Color ProgramToSkVM(const Program& program,
bool ProgramToSkVM(const Program& program,
const FunctionDefinition& function,
skvm::Builder* b,
SkSpan<skvm::Val> uniforms,
SkVMSignature* outSignature) {
SkVMSignature ignored,
*signature = outSignature ? outSignature : &ignored;
skvm::Ptr uniforms = b->uniform();
(void)uniforms;
std::vector<skvm::Ptr> argPtrs;
std::vector<skvm::Val> argVals;
@ -1474,7 +1472,7 @@ bool ProgramToSkVM(const Program& program,
}
skvm::Coord zeroCoord = {b->splat(0.0f), b->splat(0.0f)};
SkVMGenerator generator(program, b, /*uniforms=*/{}, /*device=*/zeroCoord, /*local=*/zeroCoord,
SkVMGenerator generator(program, b, uniforms, /*device=*/zeroCoord, /*local=*/zeroCoord,
/*sampleChild=*/{});
generator.writeFunction(function, argVals, returnVals);

View File

@ -38,21 +38,23 @@ struct SkVMSignature {
};
/*
* Converts 'function' to skvm instructions in 'builder'. Always adds one arg for uniforms, then
* one per value in the parameter list, and finally one per value in the return type. For example:
* Converts 'function' to skvm instructions in 'builder'. Always adds one arg per value in the
* parameter list, then one per value in the return type. For example:
*
* uniform float u; uniform float v;
* float2 fn(float2 a, float b) { ... }
*
* ... is mapped so that it can be called as:
*
* p.eval(N, uniforms, &a.x, &a.y, &b, &return.x, &return.y);
* p.eval(N, &a.x, &a.y, &b, &return.x, &return.y);
*
* The number of parameter and return slots (pointers) is placed in 'outSignature', if provided.
* If the program declares any uniforms, 'uniforms' should contain the IDs of each individual value
* (eg, one ID per component of a vector).
*/
bool ProgramToSkVM(const Program& program,
const FunctionDefinition& function,
skvm::Builder* b,
SkSpan<skvm::Val> uniforms,
SkVMSignature* outSignature = nullptr);
const FunctionDefinition* Program_GetFunction(const Program& program, const char* function);

View File

@ -109,19 +109,18 @@ void test_skvm(skiatest::Reporter* r, const char* src, float* in, const float* e
skvm::Builder b;
SkSL::SkVMSignature sig;
SkSL::ProgramToSkVM(*program, *main, &b, &sig);
SkSL::ProgramToSkVM(*program, *main, &b, /*uniforms=*/{}, &sig);
skvm::Program p = b.done();
REPORTER_ASSERT(r, p.nargs() == (int)(1 /*uniforms*/ + sig.fParameterSlots + sig.fReturnSlots));
REPORTER_ASSERT(r, p.nargs() == (int)(sig.fParameterSlots + sig.fReturnSlots));
auto out = std::make_unique<float[]>(sig.fReturnSlots);
auto args = std::make_unique<void*[]>(1 + sig.fParameterSlots + sig.fReturnSlots);
args[0] = nullptr; // uniforms
auto args = std::make_unique<void*[]>(sig.fParameterSlots + sig.fReturnSlots);
for (size_t i = 0; i < sig.fParameterSlots; ++i) {
args[1+ i] = in + i;
args[i] = in + i;
}
for (size_t i = 0; i < sig.fReturnSlots; ++i) {
args[1 + sig.fParameterSlots + i] = out.get() + i;
args[sig.fParameterSlots + i] = out.get() + i;
}
// TODO: Test with and without JIT?
@ -207,12 +206,11 @@ void test_skvm(skiatest::Reporter* r, const char* src,
REPORTER_ASSERT(r, main);
skvm::Builder b;
SkSL::ProgramToSkVM(*program, *main, &b);
SkSL::ProgramToSkVM(*program, *main, &b, /*uniforms=*/{});
skvm::Program p = b.done();
// TODO: Test with and without JIT?
const void* uniforms = nullptr;
p.eval(1, uniforms, &inR, &inG, &inB, &inA);
p.eval(1, &inR, &inG, &inB, &inA);
float actual[4] = { inR, inG, inB, inA };
float expected[4] = { exR, exG, exB, exA };
@ -597,9 +595,9 @@ DEF_TEST(SkSLInterpreterCompound, r) {
" return tempFloats[7];\n"
"}\n"
// Uniforms, array-of-structs, dynamic indices
// Uniforms, array-of-structs
"uniform Rect gRects[4];\n"
"Rect get_rect(int i) { return gRects[i]; }\n"
"Rect get_rect_2() { return gRects[2]; }\n"
// Kitchen sink (swizzles, inout, SoAoS)
"struct ManyRects { int numRects; RectAndColor rects[4]; };\n"
@ -611,38 +609,57 @@ DEF_TEST(SkSLInterpreterCompound, r) {
" }\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);
ProgramBuilder program(r, src);
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");
auto rect_height = SkSL::Program_GetFunction(*program, "rect_height"),
make_blue_rect = SkSL::Program_GetFunction(*program, "make_blue_rect"),
median = SkSL::Program_GetFunction(*program, "median"),
sums = SkSL::Program_GetFunction(*program, "sums"),
get_rect_2 = SkSL::Program_GetFunction(*program, "get_rect_2"),
fill_rects = SkSL::Program_GetFunction(*program, "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;
auto build = [&](const SkSL::FunctionDefinition* fn) {
skvm::Builder b;
skvm::Ptr uniformPtr = b.uniform();
skvm::Val uniforms[16];
for (int i = 0; i < 16; ++i) {
uniforms[i] = b.uniform32(uniformPtr, i * sizeof(int)).id;
}
SkSL::ProgramToSkVM(*program, *fn, &b, uniforms);
return b.done();
};
struct Args {
Args(void* uniformData) { fArgs.push_back(uniformData); }
void add(void* base, int n) {
for (int i = 0; i < n; ++i) {
fArgs.push_back(SkTAddOffset<void>(base, i * sizeof(float)));
}
}
std::vector<void*> fArgs;
};
{
SkIRect in = SkIRect::MakeXYWH(10, 10, 20, 30);
int out = 0;
SkAssertResult(byteCode->run(rect_height, (float*)&in, 4, (float*)&out, 1, fRects, 16));
skvm::Program p = build(rect_height);
Args args(gRects);
args.add(&in, 4);
args.add(&out, 1);
p.eval(1, args.fArgs.data());
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));
skvm::Program p = build(make_blue_rect);
Args args(gRects);
args.add(&in, 2);
args.add(&out, 8);
p.eval(1, args.fArgs.data());
REPORTER_ASSERT(r, out.fRect.width() == 15);
REPORTER_ASSERT(r, out.fRect.height() == 25);
SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
@ -652,29 +669,44 @@ DEF_TEST(SkSLInterpreterCompound, r) {
{
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));
skvm::Program p = build(median);
Args args(gRects);
args.add(&in, 15);
args.add(&out, 1);
p.eval(1, args.fArgs.data());
REPORTER_ASSERT(r, out == 8);
}
{
// TODO: Doesn't work until SkVM generator supports loops
if (false) {
float in[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
float out = 0;
SkAssertResult(byteCode->run(sums, in, 8, &out, 1, fRects, 16));
skvm::Program p = build(sums);
Args args(gRects);
args.add(&in, 8);
args.add(&out, 1);
p.eval(1, args.fArgs.data());
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));
skvm::Program p = build(get_rect_2);
Args args(gRects);
args.add(&out, 4);
p.eval(1, args.fArgs.data());
REPORTER_ASSERT(r, out == gRects[2]);
}
{
// TODO: Doesn't work until SkVM generator supports loops
if (false) {
ManyRects in;
memset(&in, 0, sizeof(in));
in.fNumRects = 2;
SkAssertResult(byteCode->run(fill_rects, (float*)&in, 33, nullptr, 0, fRects, 16));
skvm::Program p = build(fill_rects);
Args args(gRects);
args.add(&in, 33);
p.eval(1, args.fArgs.data());
ManyRects expected;
memset(&expected, 0, sizeof(expected));
expected.fNumRects = 2;
@ -740,12 +772,11 @@ DEF_TEST(SkSLInterpreterReturnThenCall, r) {
REPORTER_ASSERT(r, main);
skvm::Builder b;
SkSL::ProgramToSkVM(*program, *main, &b);
SkSL::ProgramToSkVM(*program, *main, &b, /*uniforms=*/{});
skvm::Program p = b.done();
float xs[] = { -2.0f, 0.0f, 3.0f, -1.0f };
const void* uniforms = nullptr;
p.eval(4, uniforms, xs);
p.eval(4, xs);
REPORTER_ASSERT(r, xs[0] == -2.0f);
REPORTER_ASSERT(r, xs[1] == 1.0f);
@ -763,14 +794,13 @@ DEF_TEST(SkSLInterpreterEarlyReturn, r) {
REPORTER_ASSERT(r, main);
skvm::Builder b;
SkSL::ProgramToSkVM(*program, *main, &b);
SkSL::ProgramToSkVM(*program, *main, &b, /*uniforms=*/{});
skvm::Program p = b.done();
float xs[] = { 1.0f, 3.0f },
ys[] = { 2.0f, 2.0f };
float rets[2];
const void* uniforms = nullptr;
p.eval(2, uniforms, xs, ys, rets);
p.eval(2, xs, ys, rets);
REPORTER_ASSERT(r, rets[0] == 1.0f);
REPORTER_ASSERT(r, rets[1] == 2.0f);
@ -820,12 +850,11 @@ DEF_TEST(SkSLInterpreterFunctions, r) {
auto test_fn = [&](const SkSL::FunctionDefinition* fn, float in, float expected) {
skvm::Builder b;
SkSL::ProgramToSkVM(*program, *fn, &b);
SkSL::ProgramToSkVM(*program, *fn, &b, /*uniforms=*/{});
skvm::Program p = b.done();
float out = 0.0f;
const void* uniforms = nullptr;
p.eval(1, uniforms, &in, &out);
p.eval(1, &in, &out);
REPORTER_ASSERT(r, out == expected);
};