added support for interpreting generic SkSL functions
Bug: skia: Change-Id: I575b91c654393cc6cfedea617852598e8595d5ed Reviewed-on: https://skia-review.googlesource.com/c/skia/+/209809 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
parent
aca8830244
commit
746035afeb
@ -71,6 +71,8 @@ enum class ByteCodeInstruction : uint8_t {
|
|||||||
kPushImmediate,
|
kPushImmediate,
|
||||||
kRemainderS,
|
kRemainderS,
|
||||||
kRemainderU,
|
kRemainderU,
|
||||||
|
// Followed by a byte indicating the number of slots being returned
|
||||||
|
kReturn,
|
||||||
kStore,
|
kStore,
|
||||||
kStoreGlobal,
|
kStoreGlobal,
|
||||||
// Followed by a count byte (1-4), and then one byte per swizzle component (0-3). Expects the
|
// Followed by a count byte (1-4), and then one byte per swizzle component (0-3). Expects the
|
||||||
|
@ -690,8 +690,9 @@ void ByteCodeGenerator::writeIfStatement(const IfStatement& i) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ByteCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
|
void ByteCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
|
||||||
// not yet implemented
|
this->writeExpression(*r.fExpression);
|
||||||
abort();
|
this->write(ByteCodeInstruction::kReturn);
|
||||||
|
this->write8(r.fExpression->fType.columns() * r.fExpression->fType.rows());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ByteCodeGenerator::writeSwitchStatement(const SwitchStatement& r) {
|
void ByteCodeGenerator::writeSwitchStatement(const SwitchStatement& r) {
|
||||||
|
@ -57,12 +57,8 @@ static const char* SKSL_FP_INCLUDE =
|
|||||||
#include "sksl_fp.inc"
|
#include "sksl_fp.inc"
|
||||||
;
|
;
|
||||||
|
|
||||||
static const char* SKSL_PIPELINE_STAGE_INCLUDE =
|
static const char* SKSL_GENERIC_INCLUDE =
|
||||||
#include "sksl_pipeline.inc"
|
#include "sksl_generic.inc"
|
||||||
;
|
|
||||||
|
|
||||||
static const char* SKSL_MIXER_INCLUDE =
|
|
||||||
#include "sksl_mixer.inc"
|
|
||||||
;
|
;
|
||||||
|
|
||||||
namespace SkSL {
|
namespace SkSL {
|
||||||
@ -1265,18 +1261,12 @@ std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, String tex
|
|||||||
&elements);
|
&elements);
|
||||||
fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
|
fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
|
||||||
break;
|
break;
|
||||||
case Program::kPipelineStage_Kind:
|
case Program::kPipelineStage_Kind: // fall through
|
||||||
|
case Program::kGeneric_Kind:
|
||||||
inherited = nullptr;
|
inherited = nullptr;
|
||||||
fIRGenerator->start(&settings, nullptr);
|
fIRGenerator->start(&settings, nullptr);
|
||||||
fIRGenerator->convertProgram(kind, SKSL_PIPELINE_STAGE_INCLUDE,
|
fIRGenerator->convertProgram(kind, SKSL_GENERIC_INCLUDE,
|
||||||
strlen(SKSL_PIPELINE_STAGE_INCLUDE), *fTypes, &elements);
|
strlen(SKSL_GENERIC_INCLUDE), *fTypes, &elements);
|
||||||
fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
|
|
||||||
break;
|
|
||||||
case Program::kMixer_Kind:
|
|
||||||
inherited = nullptr;
|
|
||||||
fIRGenerator->start(&settings, nullptr);
|
|
||||||
fIRGenerator->convertProgram(kind, SKSL_MIXER_INCLUDE, strlen(SKSL_MIXER_INCLUDE),
|
|
||||||
*fTypes, &elements);
|
|
||||||
fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
|
fIRGenerator->fSymbolTable->markAllFunctionsBuiltin();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -685,11 +685,6 @@ std::unique_ptr<Statement> IRGenerator::getNormalizeSkPositionCode() {
|
|||||||
return std::unique_ptr<Statement>(new ExpressionStatement(std::move(result)));
|
return std::unique_ptr<Statement>(new ExpressionStatement(std::move(result)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if the modifiers are (explicitly or implicitly) nothing but 'in'
|
|
||||||
static bool is_in(const Modifiers& modifiers) {
|
|
||||||
return (modifiers.fFlags & ~Modifiers::kIn_Flag) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IRGenerator::convertFunction(const ASTFunction& f) {
|
void IRGenerator::convertFunction(const ASTFunction& f) {
|
||||||
const Type* returnType = this->convertType(*f.fReturnType);
|
const Type* returnType = this->convertType(*f.fReturnType);
|
||||||
if (!returnType) {
|
if (!returnType) {
|
||||||
@ -744,19 +739,8 @@ void IRGenerator::convertFunction(const ASTFunction& f) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Program::kMixer_Kind: {
|
case Program::kGeneric_Kind:
|
||||||
if (*returnType != *fContext.fVoid_Type ||
|
|
||||||
parameters.size() != 2 ||
|
|
||||||
parameters[0]->fType != *fContext.fHalf4_Type ||
|
|
||||||
!is_in(parameters[0]->fModifiers) ||
|
|
||||||
parameters[1]->fType != *fContext.fHalf4_Type ||
|
|
||||||
!is_in(parameters[1]->fModifiers)) {
|
|
||||||
fErrors.error(f.fOffset, "mixer stage 'main' must be declared void main("
|
|
||||||
"half4, half4)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
if (parameters.size()) {
|
if (parameters.size()) {
|
||||||
fErrors.error(f.fOffset, "shader 'main' must have zero parameters");
|
fErrors.error(f.fOffset, "shader 'main' must have zero parameters");
|
||||||
|
@ -29,7 +29,7 @@ namespace SkSL {
|
|||||||
|
|
||||||
static constexpr int UNINITIALIZED = 0xDEADBEEF;
|
static constexpr int UNINITIALIZED = 0xDEADBEEF;
|
||||||
|
|
||||||
Interpreter::Value Interpreter::run(const ByteCodeFunction& f, Interpreter::Value args[],
|
Interpreter::Value* Interpreter::run(const ByteCodeFunction& f, Interpreter::Value args[],
|
||||||
Interpreter::Value inputs[]) {
|
Interpreter::Value inputs[]) {
|
||||||
fIP = 0;
|
fIP = 0;
|
||||||
fCurrentFunction = &f;
|
fCurrentFunction = &f;
|
||||||
@ -62,7 +62,7 @@ Interpreter::Value Interpreter::run(const ByteCodeFunction& f, Interpreter::Valu
|
|||||||
offset += p->fType.columns() * p->fType.rows();
|
offset += p->fType.columns() * p->fType.rows();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fReturnValue;
|
return fStack.data();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CallbackCtx : public SkRasterPipeline_CallbackCtx {
|
struct CallbackCtx : public SkRasterPipeline_CallbackCtx {
|
||||||
@ -142,7 +142,7 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
|||||||
case ByteCodeInstruction::kDupDown: printf("dupdown %d", this->read8()); break;
|
case ByteCodeInstruction::kDupDown: printf("dupdown %d", this->read8()); break;
|
||||||
case ByteCodeInstruction::kFloatToInt: printf("floattoint"); break;
|
case ByteCodeInstruction::kFloatToInt: printf("floattoint"); break;
|
||||||
case ByteCodeInstruction::kLoad: printf("load"); break;
|
case ByteCodeInstruction::kLoad: printf("load"); break;
|
||||||
case ByteCodeInstruction::kLoadGlobal: printf("loadglobal"); break;
|
case ByteCodeInstruction::kLoadGlobal: printf("loadglobal %d", this->read8()); break;
|
||||||
case ByteCodeInstruction::kLoadSwizzle: {
|
case ByteCodeInstruction::kLoadSwizzle: {
|
||||||
int count = this->read8();
|
int count = this->read8();
|
||||||
printf("loadswizzle %d", count);
|
printf("loadswizzle %d", count);
|
||||||
@ -166,8 +166,10 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
|||||||
break;
|
break;
|
||||||
case ByteCodeInstruction::kRemainderS: printf("remainders"); break;
|
case ByteCodeInstruction::kRemainderS: printf("remainders"); break;
|
||||||
case ByteCodeInstruction::kRemainderU: printf("remainderu"); break;
|
case ByteCodeInstruction::kRemainderU: printf("remainderu"); break;
|
||||||
|
case ByteCodeInstruction::kReturn: printf("return %d", this->read8()); break;
|
||||||
case ByteCodeInstruction::kSignedToFloat: printf("signedtofloat"); break;
|
case ByteCodeInstruction::kSignedToFloat: printf("signedtofloat"); break;
|
||||||
case ByteCodeInstruction::kStore: printf("store"); break;
|
case ByteCodeInstruction::kStore: printf("store"); break;
|
||||||
|
case ByteCodeInstruction::kStoreGlobal: printf("storeglobal"); break;
|
||||||
case ByteCodeInstruction::kStoreSwizzle: {
|
case ByteCodeInstruction::kStoreSwizzle: {
|
||||||
int count = this->read8();
|
int count = this->read8();
|
||||||
printf("storeswizzle %d", count);
|
printf("storeswizzle %d", count);
|
||||||
@ -189,7 +191,8 @@ void Interpreter::disassemble(const ByteCodeFunction& f) {
|
|||||||
}
|
}
|
||||||
case ByteCodeInstruction::kUnsignedToFloat: printf("unsignedtofloat"); break;
|
case ByteCodeInstruction::kUnsignedToFloat: printf("unsignedtofloat"); break;
|
||||||
case ByteCodeInstruction::kVector: printf("vector%d", this->read8()); break;
|
case ByteCodeInstruction::kVector: printf("vector%d", this->read8()); break;
|
||||||
default: SkASSERT(false);
|
default: printf("%d\n", fCurrentFunction->fCode[fIP - 1]);
|
||||||
|
SkASSERT(false);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
@ -323,6 +326,14 @@ void Interpreter::next() {
|
|||||||
break;
|
break;
|
||||||
BINARY_OP(kRemainderS, int32_t, fSigned, %)
|
BINARY_OP(kRemainderS, int32_t, fSigned, %)
|
||||||
BINARY_OP(kRemainderU, uint32_t, fUnsigned, %)
|
BINARY_OP(kRemainderU, uint32_t, fUnsigned, %)
|
||||||
|
case ByteCodeInstruction::kReturn: {
|
||||||
|
int count = this->read8();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
fStack[i] = fStack[fStack.size() - count + i];
|
||||||
|
}
|
||||||
|
fIP = (int) fCurrentFunction->fCode.size();
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ByteCodeInstruction::kStore: {
|
case ByteCodeInstruction::kStore: {
|
||||||
Value value = this->pop();
|
Value value = this->pop();
|
||||||
int target = this->pop().fSigned;
|
int target = this->pop().fSigned;
|
||||||
|
@ -53,14 +53,13 @@ public:
|
|||||||
|
|
||||||
Interpreter(std::unique_ptr<Program> program, std::unique_ptr<ByteCode> byteCode)
|
Interpreter(std::unique_ptr<Program> program, std::unique_ptr<ByteCode> byteCode)
|
||||||
: fProgram(std::move(program))
|
: fProgram(std::move(program))
|
||||||
, fByteCode(std::move(byteCode))
|
, fByteCode(std::move(byteCode)) {}
|
||||||
, fReturnValue(0) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invokes the specified function with the given arguments, returning its return value. 'out'
|
* Invokes the specified function with the given arguments, returning its return value. 'out'
|
||||||
* and 'inout' parameters will result in the 'args' array being modified.
|
* and 'inout' parameters will result in the 'args' array being modified.
|
||||||
*/
|
*/
|
||||||
Value run(const ByteCodeFunction& f, Value args[], Value inputs[]);
|
Value* run(const ByteCodeFunction& f, Value args[], Value inputs[]);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StackIndex stackAlloc(int count);
|
StackIndex stackAlloc(int count);
|
||||||
@ -93,7 +92,6 @@ private:
|
|||||||
const ByteCodeFunction* fCurrentFunction;
|
const ByteCodeFunction* fCurrentFunction;
|
||||||
std::vector<Value> fGlobals;
|
std::vector<Value> fGlobals;
|
||||||
std::vector<Value> fStack;
|
std::vector<Value> fStack;
|
||||||
Value fReturnValue;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -156,19 +156,6 @@ void PipelineStageCodeGenerator::writeVariableReference(const VariableReference&
|
|||||||
SkASSERT(found);
|
SkASSERT(found);
|
||||||
fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kUniform,
|
fFormatArgs->push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kUniform,
|
||||||
index));
|
index));
|
||||||
} else if (fProgramKind == Program::kMixer_Kind &&
|
|
||||||
ref.fVariable.fStorage == Variable::kParameter_Storage &&
|
|
||||||
fCurrentFunction->fName == "main") {
|
|
||||||
this->write("%s");
|
|
||||||
for (size_t i = 0; i < fCurrentFunction->fParameters.size(); ++i) {
|
|
||||||
if (fCurrentFunction->fParameters[i] == &ref.fVariable) {
|
|
||||||
fFormatArgs->push_back(Compiler::FormatArg(
|
|
||||||
Compiler::FormatArg::Kind::kChildProcessor,
|
|
||||||
i));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SkASSERT(false);
|
|
||||||
} else {
|
} else {
|
||||||
this->write(ref.fVariable.fName);
|
this->write(ref.fVariable.fName);
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,8 @@ struct Program {
|
|||||||
kGeometry_Kind,
|
kGeometry_Kind,
|
||||||
kFragmentProcessor_Kind,
|
kFragmentProcessor_Kind,
|
||||||
kPipelineStage_Kind,
|
kPipelineStage_Kind,
|
||||||
kMixer_Kind
|
kGeneric_Kind,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Program(Kind kind,
|
Program(Kind kind,
|
||||||
|
@ -10,12 +10,53 @@
|
|||||||
|
|
||||||
#include "Test.h"
|
#include "Test.h"
|
||||||
|
|
||||||
|
void test(skiatest::Reporter* r, const char* src, SkSL::Interpreter::Value* in, int expectedCount,
|
||||||
|
SkSL::Interpreter::Value* expected) {
|
||||||
|
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);
|
||||||
|
REPORTER_ASSERT(r, !compiler.errorCount());
|
||||||
|
if (compiler.errorCount() > 0) {
|
||||||
|
printf("%s\n%s", src, compiler.errorText().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkSL::ByteCodeFunction* main = byteCode->fFunctions[0].get();
|
||||||
|
SkSL::Interpreter interpreter(std::move(program), std::move(byteCode));
|
||||||
|
SkSL::Interpreter::Value* out = interpreter.run(*main, in, nullptr);
|
||||||
|
bool valid = !memcmp(out, expected, sizeof(SkSL::Interpreter::Value) * expectedCount);
|
||||||
|
if (!valid) {
|
||||||
|
printf("for program: %s\n", src);
|
||||||
|
printf(" expected (");
|
||||||
|
const char* separator = "";
|
||||||
|
for (int i = 0; i < expectedCount; ++i) {
|
||||||
|
printf("%s%f", separator, expected[i].fFloat);
|
||||||
|
separator = ", ";
|
||||||
|
}
|
||||||
|
printf("), but received (");
|
||||||
|
separator = "";
|
||||||
|
for (int i = 0; i < expectedCount; ++i) {
|
||||||
|
printf("%s%f", separator, out[i].fFloat);
|
||||||
|
separator = ", ";
|
||||||
|
}
|
||||||
|
printf(")\n");
|
||||||
|
}
|
||||||
|
REPORTER_ASSERT(r, valid);
|
||||||
|
} else {
|
||||||
|
printf("%s\n%s", src, compiler.errorText().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void test(skiatest::Reporter* r, const char* src, float inR, float inG, float inB, float inA,
|
void test(skiatest::Reporter* r, const char* src, float inR, float inG, float inB, float inA,
|
||||||
float expectedR, float expectedG, float expectedB, float expectedA) {
|
float expectedR, float expectedG, float expectedB, float expectedA) {
|
||||||
SkSL::Compiler compiler;
|
SkSL::Compiler compiler;
|
||||||
SkSL::Program::Settings settings;
|
SkSL::Program::Settings settings;
|
||||||
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
|
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(
|
||||||
SkSL::Program::kPipelineStage_Kind,
|
SkSL::Program::kGeneric_Kind,
|
||||||
SkSL::String(src), settings);
|
SkSL::String(src), settings);
|
||||||
REPORTER_ASSERT(r, program);
|
REPORTER_ASSERT(r, program);
|
||||||
if (program) {
|
if (program) {
|
||||||
@ -45,11 +86,6 @@ void test(skiatest::Reporter* r, const char* src, float inR, float inG, float in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEF_TEST(SkSLInterpreterTEMP_TEST, r) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
DEF_TEST(SkSLInterpreterAdd, r) {
|
DEF_TEST(SkSLInterpreterAdd, r) {
|
||||||
test(r, "void main(inout half4 color) { color.r = color.r + color.g; }", 0.25, 0.75, 0, 0, 1,
|
test(r, "void main(inout half4 color) { color.r = color.r + color.g; }", 0.25, 0.75, 0, 0, 1,
|
||||||
0.75, 0, 0);
|
0.75, 0, 0);
|
||||||
@ -198,3 +234,15 @@ 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,
|
test(r, "int x; void main(inout half4 color) { x = 10; color.b = x; }", 1, 2, 3, 4, 1, 2, 10,
|
||||||
4);
|
4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEF_TEST(SkSLInterpreterGeneric, r) {
|
||||||
|
float value1 = 5;
|
||||||
|
float expected1 = 25;
|
||||||
|
test(r, "float main(float x) { return x * x; }", (SkSL::Interpreter::Value*) &value1, 1,
|
||||||
|
(SkSL::Interpreter::Value*) &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); }",
|
||||||
|
(SkSL::Interpreter::Value*) &value2, 2,
|
||||||
|
(SkSL::Interpreter::Value*) expected2);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user