Revert "Revert "Complete rewrite of the SkSL interpreter""

This reverts commit 99c54f0290.
This commit is contained in:
Ethan Nicholas 2020-01-22 10:31:55 -05:00
parent 4ca0dacbce
commit 7deb1c26ba
22 changed files with 3280 additions and 3776 deletions

View File

@ -9,6 +9,7 @@
#include "include/utils/SkRandom.h"
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLInterpreter.h"
// Without this build flag, this bench isn't runnable.
#if defined(SK_ENABLE_SKSL_INTERPRETER)
@ -22,6 +23,8 @@ public:
, fCount(pixels) {}
protected:
static constexpr int VecWidth = 16;
const char* onGetName() override {
return fName.c_str();
}
@ -35,9 +38,10 @@ protected:
SkSL::Program::Settings settings;
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, fSrc, settings);
SkASSERT(compiler.errorCount() == 0);
fByteCode = compiler.toByteCode(*program);
std::unique_ptr<SkSL::ByteCode> byteCode = compiler.toByteCode(*program);
fMain = byteCode->getFunction("main");
fInterpreter.reset(new SkSL::Interpreter<VecWidth>(std::move(byteCode)));
SkASSERT(compiler.errorCount() == 0);
fMain = fByteCode->getFunction("main");
SkRandom rnd;
fPixels.resize(fCount * 4);
@ -55,14 +59,14 @@ protected:
fPixels.data() + 3 * fCount,
};
SkAssertResult(fByteCode->runStriped(fMain, fCount, args, 4, nullptr, 0, nullptr, 0));
fInterpreter->runStriped(fMain, fCount, (float**) args);
}
}
private:
SkString fName;
SkSL::String fSrc;
std::unique_ptr<SkSL::ByteCode> fByteCode;
std::unique_ptr<SkSL::Interpreter<VecWidth>> fInterpreter;
const SkSL::ByteCodeFunction* fMain;
int fCount;

View File

@ -8,7 +8,6 @@ _src = get_path_info("../src", "abspath")
skia_sksl_sources = [
"$_src/sksl/SkSLASTNode.cpp",
"$_src/sksl/SkSLByteCode.cpp",
"$_src/sksl/SkSLByteCodeGenerator.cpp",
"$_src/sksl/SkSLCFGGenerator.cpp",
"$_src/sksl/SkSLCompiler.cpp",

View File

@ -16,6 +16,7 @@
#include "include/private/SkTemplates.h"
#include "include/utils/SkRandom.h"
#include "modules/particles/include/SkParticleData.h"
#include "src/sksl/SkSLInterpreter.h"
#include <memory>
@ -25,6 +26,8 @@ class SkParticleBinding;
class SkParticleDrawable;
class SkParticleExternalValue;
static constexpr int INTERPRETER_WIDTH = 8;
namespace skresources {
class ResourceProvider;
}
@ -122,13 +125,16 @@ private:
friend class SkParticleEffect;
// Cached
template<int width>
struct Program {
std::unique_ptr<SkSL::ByteCode> fByteCode;
std::unique_ptr<SkSL::Interpreter<width>> fInterpreter;
SkTArray<std::unique_ptr<SkParticleExternalValue>> fExternalValues;
};
Program fEffectProgram;
Program fParticleProgram;
// for performance it would be better to run this with a Program<1>, but for code-size reasons
// we stick to INTERPRETER_WIDTH
Program<INTERPRETER_WIDTH> fEffectProgram;
Program<INTERPRETER_WIDTH> fParticleProgram;
};
class SkParticleEffect : public SkRefCnt {
@ -183,8 +189,17 @@ public:
void setFrame (float f) { fState.fFrame = f; }
void setFlags (uint32_t f) { fState.fFlags = f; }
const SkSL::ByteCode* effectCode() const { return fParams->fEffectProgram.fByteCode.get(); }
const SkSL::ByteCode* particleCode() const { return fParams->fParticleProgram.fByteCode.get(); }
const SkSL::ByteCode* effectCode() const {
return fParams->fEffectProgram.fInterpreter ?
&fParams->fEffectProgram.fInterpreter->getCode() :
nullptr;
}
const SkSL::ByteCode* particleCode() const {
return fParams->fParticleProgram.fInterpreter ?
&fParams->fParticleProgram.fInterpreter->getCode() :
nullptr;
}
float* effectUniforms() { return fEffectUniforms.data(); }
float* particleUniforms() { return fParticleUniforms.data(); }

View File

@ -119,7 +119,9 @@ void SkParticleEffectParams::prepare(const skresources::ResourceProvider* resour
fDrawable->prepare(resourceProvider);
}
auto buildProgram = [this](const SkSL::String& code, Program* p) {
auto buildProgram = [this](const SkSL::String& code) ->
std::pair<std::unique_ptr<SkSL::ByteCode>,
SkTArray<std::unique_ptr<SkParticleExternalValue>>> {
SkSL::Compiler compiler;
SkSL::Program::Settings settings;
@ -140,17 +142,15 @@ void SkParticleEffectParams::prepare(const skresources::ResourceProvider* resour
auto program = compiler.convertProgram(SkSL::Program::kGeneric_Kind, code, settings);
if (!program) {
SkDebugf("%s\n", compiler.errorText().c_str());
return;
return std::make_pair(nullptr, std::move(externalValues));
}
auto byteCode = compiler.toByteCode(*program);
if (!byteCode) {
SkDebugf("%s\n", compiler.errorText().c_str());
return;
return std::make_pair(nullptr, std::move(externalValues));
}
p->fByteCode = std::move(byteCode);
p->fExternalValues.swap(externalValues);
return std::make_pair(std::move(byteCode), std::move(externalValues));
};
SkSL::String effectCode(kCommonHeader);
@ -160,8 +160,15 @@ void SkParticleEffectParams::prepare(const skresources::ResourceProvider* resour
particleCode.append(kParticleHeader);
particleCode.append(fParticleCode.c_str());
buildProgram(effectCode, &fEffectProgram);
buildProgram(particleCode, &fParticleProgram);
auto effectProgram = buildProgram(effectCode);
fEffectProgram.fInterpreter.reset(new SkSL::Interpreter<INTERPRETER_WIDTH>(
std::move(effectProgram.first)));
fEffectProgram.fExternalValues.swap(effectProgram.second);
auto particleProgram = buildProgram(particleCode);
fParticleProgram.fInterpreter.reset(new SkSL::Interpreter<INTERPRETER_WIDTH>(
std::move(particleProgram.first)));
fParticleProgram.fExternalValues.swap(particleProgram.second);
}
SkParticleEffect::SkParticleEffect(sk_sp<SkParticleEffectParams> params, const SkRandom& random)
@ -222,15 +229,22 @@ void SkParticleEffect::processEffectSpawnRequests(double now) {
}
void SkParticleEffect::runEffectScript(double now, const char* entry) {
if (const auto& byteCode = fParams->fEffectProgram.fByteCode) {
if (auto fun = byteCode->getFunction(entry)) {
SkSL::Interpreter<INTERPRETER_WIDTH>* interpreter = fParams->fEffectProgram.fInterpreter.get();
if (interpreter) {
const auto& byteCode = interpreter->getCode();
if (auto fun = byteCode.getFunction(entry)) {
for (const auto& value : fParams->fEffectProgram.fExternalValues) {
value->setRandom(&fRandom);
value->setEffect(this);
}
SkAssertResult(byteCode->run(fun, &fState.fAge, sizeof(EffectState) / sizeof(float),
nullptr, 0,
fEffectUniforms.data(), fEffectUniforms.count()));
interpreter->setUniforms(fEffectUniforms.data());
static constexpr int numChannels = sizeof(EffectState) / sizeof(float);
SkASSERT(numChannels == fun->getParameterSlotCount());
float* args[numChannels];
for (int i = 0; i < numChannels; ++i) {
args[i] = &fState.fAge + i;
}
SkAssertResult(interpreter->runStriped(fun, 1, args));
this->processEffectSpawnRequests(now);
}
}
@ -263,8 +277,11 @@ void SkParticleEffect::processParticleSpawnRequests(double now, int start) {
}
void SkParticleEffect::runParticleScript(double now, const char* entry, int start, int count) {
if (const auto& byteCode = fParams->fParticleProgram.fByteCode) {
if (auto fun = byteCode->getFunction(entry)) {
SkSL::Interpreter<INTERPRETER_WIDTH>* interpreter =
fParams->fParticleProgram.fInterpreter.get();
if (interpreter) {
const auto& byteCode = interpreter->getCode();
if (auto fun = byteCode.getFunction(entry)) {
float* args[SkParticles::kNumChannels];
for (int i = 0; i < SkParticles::kNumChannels; ++i) {
args[i] = fParticles.fData[i].get() + start;
@ -275,10 +292,8 @@ void SkParticleEffect::runParticleScript(double now, const char* entry, int star
value->setEffect(this);
}
memcpy(&fParticleUniforms[1], &fState.fAge, sizeof(EffectState));
SkAssertResult(byteCode->runStriped(fun, count, args, SkParticles::kNumChannels,
nullptr, 0,
fParticleUniforms.data(),
fParticleUniforms.count()));
interpreter->setUniforms(fParticleUniforms.data());
SkAssertResult(interpreter->runStriped(fun, count, (float**) args));
this->processParticleSpawnRequests(now, start);
}
}

View File

@ -19,6 +19,7 @@
#include "src/core/SkReadBuffer.h"
#include "src/core/SkVM.h"
#include "src/core/SkWriteBuffer.h"
#include "src/sksl/SkSLInterpreter.h"
#if SK_SUPPORT_GPU
#include "src/gpu/GrFragmentProcessor.h"
@ -420,17 +421,20 @@ public:
ctx->ninputs = fEffect->uniformSize() / 4;
ctx->shaderConvention = false;
SkAutoMutexExclusive ama(fByteCodeMutex);
if (!fByteCode) {
SkAutoMutexExclusive ama(fInterpreterMutex);
if (!fInterpreter) {
auto [byteCode, errorText] = fEffect->toByteCode(fInputs->data());
if (!byteCode) {
SkDebugf("%s\n", errorText.c_str());
return false;
}
fByteCode = std::move(byteCode);
fMain = byteCode->getFunction("main");
fInterpreter.reset(
new SkSL::Interpreter<SkRasterPipeline_InterpreterCtx::VECTOR_WIDTH>(
std::move(byteCode)));
}
ctx->byteCode = fByteCode.get();
ctx->fn = ctx->byteCode->getFunction("main");
ctx->fn = fMain;
ctx->interpreter = fInterpreter.get();
rec.fPipeline->append(SkRasterPipeline::interpreter, ctx);
return true;
}
@ -453,8 +457,10 @@ private:
sk_sp<SkRuntimeEffect> fEffect;
sk_sp<SkData> fInputs;
mutable SkMutex fByteCodeMutex;
mutable std::unique_ptr<SkSL::ByteCode> fByteCode;
mutable SkMutex fInterpreterMutex;
mutable std::unique_ptr<SkSL::Interpreter<SkRasterPipeline_InterpreterCtx::VECTOR_WIDTH>>
fInterpreter;
mutable const SkSL::ByteCodeFunction* fMain;
friend class SkColorFilter;

View File

@ -161,12 +161,15 @@ struct SkRasterPipeline_CallbackCtx {
};
namespace SkSL {
class ByteCode;
class ByteCodeFunction;
template<int width>
class Interpreter;
}
struct SkRasterPipeline_InterpreterCtx {
const SkSL::ByteCode* byteCode;
static constexpr int VECTOR_WIDTH = 8;
SkSL::Interpreter<VECTOR_WIDTH>* interpreter;
const SkSL::ByteCodeFunction* fn;
SkColor4f paintColor;

View File

@ -10,7 +10,7 @@
#include "include/core/SkTypes.h"
#include "src/core/SkUtils.h" // unaligned_{load,store}
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLInterpreter.h"
// Every function in this file should be marked static and inline using SI.
#if defined(__clang__)
@ -2711,7 +2711,6 @@ STAGE(interpreter, SkRasterPipeline_InterpreterCtx* c) {
float* args[] = { xx, yy, rr, gg, bb, aa };
float** in_args = args;
int in_count = 6;
if (c->shaderConvention) {
// our caller must have called seed_shader to set these
@ -2723,15 +2722,14 @@ STAGE(interpreter, SkRasterPipeline_InterpreterCtx* c) {
sk_unaligned_store(aa, F(c->paintColor.fA));
} else {
in_args += 2; // skip x,y
in_count = 4;
sk_unaligned_store(rr, r);
sk_unaligned_store(gg, g);
sk_unaligned_store(bb, b);
sk_unaligned_store(aa, a);
}
SkAssertResult(c->byteCode->runStriped(c->fn, tail ? tail : N, in_args, in_count,
nullptr, 0, (const float*)c->inputs, c->ninputs));
c->interpreter->setUniforms((float*) c->inputs);
SkAssertResult(c->interpreter->runStriped(c->fn, tail ? tail : N, (float**) in_args));
r = sk_unaligned_load<F>(rr);
g = sk_unaligned_load<F>(gg);

View File

@ -14,6 +14,8 @@
#include "src/shaders/SkRTShader.h"
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLInterpreter.h"
#if SK_SUPPORT_GPU
#include "src/gpu/GrColorInfo.h"
@ -45,17 +47,19 @@ bool SkRTShader::onAppendStages(const SkStageRec& rec) const {
ctx->ninputs = fEffect->uniformSize() / 4;
ctx->shaderConvention = true;
SkAutoMutexExclusive ama(fByteCodeMutex);
if (!fByteCode) {
SkAutoMutexExclusive ama(fInterpreterMutex);
if (!fInterpreter) {
auto [byteCode, errorText] = fEffect->toByteCode(fInputs->data());
if (!byteCode) {
SkDebugf("%s\n", errorText.c_str());
return false;
}
fByteCode = std::move(byteCode);
fMain = byteCode->getFunction("main");
fInterpreter.reset(new SkSL::Interpreter<SkRasterPipeline_InterpreterCtx::VECTOR_WIDTH>(
std::move(byteCode)));
}
ctx->byteCode = fByteCode.get();
ctx->fn = ctx->byteCode->getFunction("main");
ctx->fn = fMain;
ctx->interpreter = fInterpreter.get();
rec.fPipeline->append(SkRasterPipeline::seed_shader);
rec.fPipeline->append_matrix(rec.fAlloc, inverse);

View File

@ -18,7 +18,12 @@ class SkData;
class SkMatrix;
class SkRuntimeEffect;
namespace SkSL { class ByteCode; }
namespace SkSL {
class ByteCodeFunction;
template<int width>
class Interpreter;
}
class SkRTShader : public SkShaderBase {
public:
@ -37,6 +42,8 @@ protected:
bool onAppendStages(const SkStageRec& rec) const override;
private:
static constexpr int VECTOR_WIDTH = 8;
SK_FLATTENABLE_HOOKS(SkRTShader)
sk_sp<SkRuntimeEffect> fEffect;
@ -45,8 +52,9 @@ private:
sk_sp<SkData> fInputs;
std::vector<sk_sp<SkShader>> fChildren;
mutable SkMutex fByteCodeMutex;
mutable std::unique_ptr<SkSL::ByteCode> fByteCode;
mutable SkMutex fInterpreterMutex;
mutable std::unique_ptr<SkSL::Interpreter<VECTOR_WIDTH>> fInterpreter;
mutable const SkSL::ByteCodeFunction* fMain;
typedef SkShaderBase INHERITED;
};

File diff suppressed because it is too large Load Diff

View File

@ -9,206 +9,59 @@
#define SKSL_BYTECODE
#include "include/private/SkOnce.h"
#include "include/private/SkVx.h"
#include "src/sksl/SkSLString.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include <memory>
#include <vector>
namespace SkSL {
class ExternalValue;
struct FunctionDeclaration;
// GCC and Clang support the "labels as values" extension which we need to implement the interpreter
// using threaded code. Otherwise, we fall back to using a switch statement in a for loop.
#if defined(__GNUC__) || defined(__clang__)
#define SKSLC_THREADED_CODE
using instruction = void*;
#else
using instruction = uint16_t;
#endif
#define VECTOR(name) name ## 4, name ## 3, name ## 2, name
#define VECTOR_MATRIX(name) name ## 4, name ## 3, name ## 2, name, name ## N
enum class ByteCodeInstruction : uint16_t {
// B = bool, F = float, I = int, S = signed, U = unsigned
// All binary VECTOR instructions (kAddF, KSubtractI, kCompareIEQ, etc.) are followed by a byte
// indicating the count, even though it is redundant due to the count appearing in the opcode.
// This is because the original opcodes are lost after we preprocess it into threaded code, and
// we need to still be able to access the count so as to permit the implementation to use opcode
// fallthrough.
VECTOR_MATRIX(kAddF),
VECTOR(kAddI),
kAndB,
kBranch,
// Followed by a byte indicating the index of the function to call
kCall,
// Followed by three bytes indicating: the number of argument slots, the number of return slots,
// and the index of the external value to call
kCallExternal,
// For dynamic array access: Followed by byte indicating length of array
kClampIndex,
VECTOR(kCompareIEQ),
VECTOR(kCompareINEQ),
VECTOR_MATRIX(kCompareFEQ),
VECTOR_MATRIX(kCompareFNEQ),
VECTOR(kCompareFGT),
VECTOR(kCompareFGTEQ),
VECTOR(kCompareFLT),
VECTOR(kCompareFLTEQ),
VECTOR(kCompareSGT),
VECTOR(kCompareSGTEQ),
VECTOR(kCompareSLT),
VECTOR(kCompareSLTEQ),
VECTOR(kCompareUGT),
VECTOR(kCompareUGTEQ),
VECTOR(kCompareULT),
VECTOR(kCompareULTEQ),
VECTOR(kConvertFtoI),
VECTOR(kConvertStoF),
VECTOR(kConvertUtoF),
// Followed by a (redundant) byte indicating the count
VECTOR(kCos),
VECTOR_MATRIX(kDivideF),
VECTOR(kDivideS),
VECTOR(kDivideU),
// Duplicates the top stack value. Followed by a (redundant) byte indicating the count.
VECTOR_MATRIX(kDup),
kInverse2x2,
kInverse3x3,
kInverse4x4,
// kLoad/kLoadGlobal are followed by a byte indicating the count, and a byte indicating the
// local/global slot to load
VECTOR(kLoad),
VECTOR(kLoadGlobal),
VECTOR(kLoadUniform),
// As kLoad/kLoadGlobal, then a count byte (1-4), and then one byte per swizzle component (0-3).
kLoadSwizzle,
kLoadSwizzleGlobal,
kLoadSwizzleUniform,
// kLoadExtended* are fallback load ops when we lack a specialization. They are followed by a
// count byte, and get the slot to load from the top of the stack.
kLoadExtended,
kLoadExtendedGlobal,
kLoadExtendedUniform,
// Followed by four bytes: srcCols, srcRows, dstCols, dstRows. Consumes the src matrix from the
// stack, and replaces it with the dst matrix. Per GLSL rules, there are no restrictions on
// dimensions. Any overlapping values are copied, and any other values are filled in with the
// identity matrix.
kMatrixToMatrix,
// Followed by three bytes: leftCols (== rightRows), leftRows, rightCols
kMatrixMultiply,
VECTOR_MATRIX(kNegateF),
VECTOR(kNegateI),
VECTOR_MATRIX(kMultiplyF),
VECTOR(kMultiplyI),
kNotB,
kOrB,
VECTOR_MATRIX(kPop),
// Followed by a 32 bit value containing the value to push
kPushImmediate,
// Followed by a byte indicating external value to read
VECTOR(kReadExternal),
VECTOR(kRemainderF),
VECTOR(kRemainderS),
VECTOR(kRemainderU),
// Followed by a byte indicating the number of slots to reserve on the stack (for later return)
kReserve,
// Followed by a byte indicating the number of slots being returned
kReturn,
// Followed by two bytes indicating columns and rows of matrix (2, 3, or 4 each).
// Takes a single value from the top of the stack, and converts to a CxR matrix with that value
// replicated along the diagonal (and zero elsewhere), per the GLSL matrix construction rules.
kScalarToMatrix,
// Followed by a byte indicating the number of bits to shift
kShiftLeft,
kShiftRightS,
kShiftRightU,
// Followed by a (redundant) byte indicating the count
VECTOR(kSin),
VECTOR(kSqrt),
// kStore/kStoreGlobal are followed by a byte indicating the local/global slot to store
VECTOR(kStore),
VECTOR(kStoreGlobal),
// Fallback stores. Followed by count byte, and get the slot to store from the top of the stack
kStoreExtended,
kStoreExtendedGlobal,
// As kStore/kStoreGlobal, then a count byte (1-4), then one byte per swizzle component (0-3).
// Expects the stack to look like: ... v1 v2 v3 v4, where the number of 'v's is equal to the
// number of swizzle components. After the store, all v's are popped from the stack.
kStoreSwizzle,
kStoreSwizzleGlobal,
// As above, but gets the store slot from the top of the stack (before values to be stored)
kStoreSwizzleIndirect,
kStoreSwizzleIndirectGlobal,
// Followed by two count bytes (1-4), and then one byte per swizzle component (0-3). The first
// count byte provides the current vector size (the vector is the top n stack elements), and the
// second count byte provides the swizzle component count.
kSwizzle,
VECTOR_MATRIX(kSubtractF),
VECTOR(kSubtractI),
// Followed by a (redundant) byte indicating the count
VECTOR(kTan),
// Followed by a byte indicating external value to write
VECTOR(kWriteExternal),
kXorB,
kMaskPush,
kMaskPop,
kMaskNegate,
// Followed by count byte
kMaskBlend,
// Followed by address
kBranchIfAllFalse,
kLoopBegin,
kLoopNext,
kLoopMask,
kLoopEnd,
kLoopBreak,
kLoopContinue,
};
#undef VECTOR
class ByteCode;
class ExternalValue;
class ByteCodeFunction {
public:
int getParameterCount() const { return fParameterCount; }
int getReturnCount() const { return fReturnCount; }
/**
* Print bytecode disassembly to stdout.
*/
void disassemble() const;
private:
ByteCodeFunction(const FunctionDeclaration* declaration);
friend class ByteCode;
friend class ByteCodeGenerator;
friend struct Interpreter;
// all counts are of 32-bit values, so a float4 counts as 4 parameter or return slots
struct Parameter {
int fSlotCount;
bool fIsOutParameter;
};
SkSL::String fName;
std::vector<Parameter> fParameters;
int fParameterCount;
int fReturnCount = 0;
/**
* Note that this is the actual number of parameters, not the number of parameter slots.
*/
int getParameterCount() const { return fParameters.size(); }
Parameter getParameter(int idx) const { return fParameters[idx]; }
int getParameterSlotCount() const { return fParameterSlotCount; }
int getReturnSlotCount() const { return fReturnSlotCount; }
void disassemble() const { }
private:
ByteCodeFunction(const FunctionDeclaration* declaration)
: fName(declaration->fName) {}
String fName;
std::vector<Parameter> fParameters;
int fParameterSlotCount;
int fReturnSlotCount;
int fStackSlotCount;
int fLocalCount = 0;
int fStackCount = 0;
int fConditionCount = 0;
int fLoopCount = 0;
mutable SkOnce fPreprocessOnce;
std::vector<uint8_t> fCode;
/**
* Replace each opcode with the corresponding entry from the labels array.
*/
void preprocess(const void* labels[]);
friend class ByteCode;
friend class ByteCodeGenerator;
template<int width>
friend class Interpreter;
};
enum class TypeCategory {
@ -220,9 +73,260 @@ enum class TypeCategory {
class SK_API ByteCode {
public:
static constexpr int kVecWidth = 8;
template<int width>
union Vector {
skvx::Vec<width, float> fFloat;
skvx::Vec<width, int32_t> fInt;
skvx::Vec<width, uint32_t> fUInt;
ByteCode() = default;
Vector() = default;
Vector(skvx::Vec<width, float> f)
: fFloat(f) {}
Vector(skvx::Vec<width, int32_t> i)
: fInt(i) {}
Vector(skvx::Vec<width, uint32_t> u)
: fUInt(u) {}
};
enum class Instruction : uint8_t {
// no parameters
kNop,
// no parameters
kAbort,
// Register target, Register src1, Register src2
kAddF,
// Register target, Register src1, Register src2
kAddI,
// Register target, Register src1, Register src2
kAnd,
// Register index, int arrayLength
kBoundsCheck,
// Pointer target
kBranch,
// Pointer target
kBranchIfAllFalse,
// no parameters
kBreak,
// Register target, uint8_t functionIndex, Register parameters
kCall,
// Register target, uint8_t externalValueIndex, uint8_t targetSize, Register arguments,
// uint8_t argumentSize
kCallExternal,
// Register target, Register src1, Register src2
kCompareEQF,
// Register target, Register src1, Register src2
kCompareEQI,
// Register target, Register src1, Register src2
kCompareNEQF,
// Register target, Register src1, Register src2
kCompareNEQI,
// Register target, Register src1, Register src2
kCompareGTF,
// Register target, Register src1, Register src2
kCompareGTS,
// Register target, Register src1, Register src2
kCompareGTU,
// Register target, Register src1, Register src2
kCompareGTEQF,
// Register target, Register src1, Register src2
kCompareGTEQS,
// Register target, Register src1, Register src2
kCompareGTEQU,
// Register target, Register src1, Register src2
kCompareLTF,
// Register target, Register src1, Register src2
kCompareLTS,
// Register target, Register src1, Register src2
kCompareLTU,
// Register target, Register src1, Register src2
kCompareLTEQF,
// Register target, Register src1, Register src2
kCompareLTEQS,
// Register target, Register src1, Register src2
kCompareLTEQU,
// no parameters
kContinue,
// Register target, Register src
kCopy,
// Register target, Register src,
kCos,
// Register target, Register src1, Register src2
kDivideF,
// Register target, Register src1, Register src2
kDivideS,
// Register target, Register src1, Register src2
kDivideU,
// Register target, Register src
kFloatToSigned,
// Register target, Register src
kFloatToUnsigned,
// Load a constant into a register
// Register target, Immediate value
kImmediate,
// Register target, Register src
kInverse2x2,
// Register target, Register src
kInverse3x3,
// Register target, Register src
kInverse4x4,
// Load the memory cell pointed to by srcPtr into a register
// Register target, Register srcPtr
kLoad,
// Load the memory cell pointed to by src into a register
// Register target, Pointer src
kLoadDirect,
// Load the parameter slot pointed to by srcPtr into a register
// Register target, Register srcPtr
kLoadParameter,
// Load the parameter slot pointed to by src into a register
// Register target, Pointer src
kLoadParameterDirect,
// Load the stack cell pointed to by srcPtr + sp into a register
// Register target, Register srcPtr
kLoadStack,
// Load the stack cell pointed to by src + sp into a register
// Register target, Pointer src
kLoadStackDirect,
// Pushes a new loop onto the loop and continue stacks
// no parameters
kLoopBegin,
// Pops the loop and continue stacks
// no parameters
kLoopEnd,
// Register mask
kLoopMask,
// no parameters
kLoopNext,
// no parameters
kMaskNegate,
// no parameters
kMaskPop,
// Register mask
kMaskPush,
// Register target, Register left, Register right, uint8_t leftColsAndRightRows,
// uint8_t leftRows, uint8_t rightCols
kMatrixMultiply,
// Register target, Register src, uint8_t srcColumns, uint8_t srcRows, uint8_t dstColumns,
// uint8_t dstRows
kMatrixToMatrix,
// Register target, Register src1, Register src2
kMultiplyF,
// Register target, Register src1, Register src2
kMultiplyI,
// Register target, Register src
kNegateF,
// Register target, Register src
kNegateS,
// Register target, Register src
kNot,
// Register target, Register src1, Register src2
kOr,
// Register src
kPrint,
// Register target, uint8_t count, uint8_t index
kReadExternal,
// Register target, Register src1, Register src2
kRemainderF,
// Register target, Register src1, Register src2
kRemainderS,
// Register target, Register src1, Register src2
kRemainderU,
// no parameters
kReturn,
// Register value
kReturnValue,
// Register target, Register src, uint8_t columns, uint8_t rows
kScalarToMatrix,
// Register target, Register test, Register ifTrue, Register ifFalse
kSelect,
// Register target, Register src, uint8_t count
kShiftLeft,
// Register target, Register src, uint8_t count
kShiftRightS,
// Register target, Register src, uint8_t count
kShiftRightU,
// Register target, Register src
kSignedToFloat,
// Register target, Register src,
kSin,
// Register target, Register src,
kSqrt,
// Store to the memory cell pointed to by dstPtr
// Register dstPtr, Register src
kStore,
// Store to the memory cell pointed to by dst
// Pointer dst, Register src
kStoreDirect,
// Store to the parameter slot pointed to by dstPtr
// Register dstPtr, Register src
kStoreParameter,
// Store to the parameter slot pointed to by dst
// Pointer dst, Register src
kStoreParameterDirect,
// Stores a register into the stack cell pointed to by dst + sp
// Register dst, Register src
kStoreStack,
// Stores a register into the stack cell pointed to by dstPtr + sp
// Pointer dst, Register src
kStoreStackDirect,
// Register target, Register src1, Register src2
kSubtractF,
// Register target, Register src1, Register src2
kSubtractI,
// Register target, Register src,
kTan,
// Register target, Register src,
kUnsignedToFloat,
// uint8_t index, uint8_t count, Register src
kWriteExternal,
// Register target, Register src1, Register src2
kXor,
};
// Compound values like vectors span multiple Registers or Pointer addresses. We always refer to
// them by the address of their first slot, so for instance if you add two float4's together,
// the resulting Register contains the first channel of the result, with the other three
// channels following in the next three Registers.
struct Register {
uint16_t fIndex;
Register operator+(uint16_t offset) const {
return Register{(uint16_t) (fIndex + offset)};
}
};
struct Pointer {
uint16_t fAddress;
Pointer operator+(uint16_t offset) const {
return Pointer{(uint16_t) (fAddress + offset)};
}
};
union Immediate {
float fFloat;
int32_t fInt;
uint32_t fUInt;
Immediate() {}
Immediate(float f)
: fFloat(f) {}
Immediate(int32_t i)
: fInt(i) {}
Immediate(uint32_t u)
: fUInt(u) {}
};
static constexpr int kPointerMax = 65535;
static constexpr int kRegisterMax = 65535;
const ByteCodeFunction* getFunction(const char* name) const {
for (const auto& f : fFunctions) {
@ -233,36 +337,9 @@ public:
return nullptr;
}
/**
* Invokes the specified function once, with the given arguments.
* 'args', 'outReturn', and 'uniforms' are collections of 32-bit values (typically floats,
* but possibly int32_t or uint32_t, depending on the types used in the SkSL).
* Any 'out' or 'inout' parameters will result in the 'args' array being modified.
* The return value is stored in 'outReturn' (may be null, to discard the return value).
* 'uniforms' are mapped to 'uniform' globals, in order.
*/
bool SKSL_WARN_UNUSED_RESULT run(const ByteCodeFunction*,
float* args, int argCount,
float* outReturn, int returnCount,
const float* uniforms, int uniformCount) const;
/**
* Invokes the specified function with the given arguments, 'N' times. 'args' and 'outReturn'
* are accepted and returned in structure-of-arrays form:
* args[0] points to an array of N values, the first argument for each invocation
* ...
* args[argCount - 1] points to an array of N values, the last argument for each invocation
*
* All values in 'args', 'outReturn', and 'uniforms' are 32-bit values (typically floats,
* but possibly int32_t or uint32_t, depending on the types used in the SkSL).
* Any 'out' or 'inout' parameters will result in the 'args' array being modified.
* The return value is stored in 'outReturn' (may be null, to discard the return value).
* 'uniforms' are mapped to 'uniform' globals, in order.
*/
bool SKSL_WARN_UNUSED_RESULT runStriped(const ByteCodeFunction*, int N,
float* args[], int argCount,
float* outReturn[], int returnCount,
const float* uniforms, int uniformCount) const;
int getGlobalSlotCount() const {
return fGlobalSlotCount;
}
struct Uniform {
SkSL::String fName;
@ -285,20 +362,19 @@ public:
const Uniform& getUniform(int i) const { return fUniforms[i]; }
private:
ByteCode(const ByteCode&) = delete;
ByteCode& operator=(const ByteCode&) = delete;
std::vector<std::unique_ptr<ByteCodeFunction>> fFunctions;
std::vector<ExternalValue*> fExternalValues;
friend class ByteCodeGenerator;
friend struct Interpreter;
int fGlobalSlotCount;
int fGlobalSlotCount = 0;
int fUniformSlotCount = 0;
std::vector<Uniform> fUniforms;
std::vector<std::unique_ptr<ByteCodeFunction>> fFunctions;
std::vector<ExternalValue*> fExternalValues;
friend class ByteCodeGenerator;
template<int width>
friend class Interpreter;
};
}
} // namespace
#endif

File diff suppressed because it is too large Load Diff

View File

@ -54,95 +54,19 @@ namespace SkSL {
class ByteCodeGenerator : public CodeGenerator {
public:
class LValue {
public:
LValue(ByteCodeGenerator& generator)
: fGenerator(generator) {}
virtual ~LValue() {}
/**
* Stack before call: ... lvalue
* Stack after call: ... lvalue load
*/
virtual void load() = 0;
/**
* Stack before call: ... lvalue value
* Stack after call: ...
*/
virtual void store(bool discard) = 0;
protected:
ByteCodeGenerator& fGenerator;
};
ByteCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors,
ByteCode* output);
ByteCodeGenerator(const Program* program, ErrorReporter* errors, ByteCode* output);
bool generateCode() override;
void write8(uint8_t b);
void write16(uint16_t b);
void write32(uint32_t b);
void write(ByteCodeInstruction inst, int count = kUnusedStackCount);
/**
* Based on 'type', writes the s (signed), u (unsigned), or f (float) instruction.
*/
void writeTypedInstruction(const Type& type, ByteCodeInstruction s, ByteCodeInstruction u,
ByteCodeInstruction f, int count, bool writeCount = true);
static int SlotCount(const Type& type);
private:
static constexpr int kUnusedStackCount = INT32_MAX;
static int StackUsage(ByteCodeInstruction, int count);
// reserves 16 bits in the output code, to be filled in later with an address once we determine
// it
class DeferredLocation {
public:
DeferredLocation(ByteCodeGenerator* generator)
: fGenerator(*generator)
, fOffset(generator->fCode->size()) {
generator->write16(0);
}
#ifdef SK_DEBUG
~DeferredLocation() {
SkASSERT(fSet);
}
#endif
void set() {
int target = fGenerator.fCode->size();
SkASSERT(target <= 65535);
(*fGenerator.fCode)[fOffset] = target;
(*fGenerator.fCode)[fOffset + 1] = target >> 8;
#ifdef SK_DEBUG
fSet = true;
#endif
}
private:
ByteCodeGenerator& fGenerator;
size_t fOffset;
#ifdef SK_DEBUG
bool fSet = false;
#endif
};
// Intrinsics which do not simply map to a single opcode
enum class SpecialIntrinsic {
kDot,
kInverse,
};
struct Intrinsic {
Intrinsic(ByteCodeInstruction instruction)
Intrinsic(ByteCode::Instruction instruction)
: fIsSpecial(false)
, fValue(instruction) {}
@ -153,201 +77,250 @@ private:
bool fIsSpecial;
union Value {
Value(ByteCodeInstruction instruction)
Value(ByteCode::Instruction instruction)
: fInstruction(instruction) {}
Value(SpecialIntrinsic special)
: fSpecial(special) {}
ByteCodeInstruction fInstruction;
ByteCode::Instruction fInstruction;
SpecialIntrinsic fSpecial;
} fValue;
};
class LValue {
public:
LValue(ByteCodeGenerator& generator)
: fGenerator(generator) {}
// Similar to Variable::Storage, but locals and parameters are grouped together, and globals
// are further subidivided into uniforms and other (writable) globals.
enum class Storage {
kLocal, // include parameters
kGlobal, // non-uniform globals
kUniform, // uniform globals
virtual ~LValue() {}
virtual void load(ByteCode::Register result) = 0;
virtual void store(ByteCode::Register src) = 0;
protected:
ByteCodeGenerator& fGenerator;
};
struct Location {
int fSlot;
Storage fStorage;
enum {
kPointer_Kind,
kRegister_Kind
} fKind;
// Not really invalid, but a "safe" placeholder to be more explicit at call-sites
static Location MakeInvalid() { return { 0, Storage::kLocal }; }
union {
ByteCode::Pointer fPointer;
ByteCode::Register fRegister;
};
Location makeOnStack() { return { -1, fStorage }; }
bool isOnStack() const { return fSlot < 0; }
Location(ByteCode::Pointer p)
: fKind(kPointer_Kind)
, fPointer(p) {}
Location operator+(int offset) {
SkASSERT(fSlot >= 0);
return { fSlot + offset, fStorage };
Location(ByteCode::Register r)
: fKind(kRegister_Kind)
, fRegister(r) {}
/**
* Returns this location offset by 'offset' bytes. For pointers, this is a compile-time
* operation, while for registers there will be CPU instructions output to handle the
* runtime calculation of the address.
*/
Location offset(ByteCodeGenerator& generator, int offset) {
if (!offset) {
return *this;
}
if (fKind == kPointer_Kind) {
return Location(fPointer + offset);
}
ByteCode::Register a = generator.next(1);
generator.write(ByteCode::Instruction::kImmediate);
generator.write(a);
generator.write(ByteCode::Immediate{offset});
ByteCode::Register result = generator.next(1);
generator.write(ByteCode::Instruction::kAddI);
generator.write(result);
generator.write(fRegister);
generator.write(a);
return result;
}
ByteCodeInstruction selectLoad(ByteCodeInstruction local,
ByteCodeInstruction global,
ByteCodeInstruction uniform) const {
switch (fStorage) {
case Storage::kLocal: return local;
case Storage::kGlobal: return global;
case Storage::kUniform: return uniform;
/**
* Returns this location offset by the number of bytes stored in the 'offset' register. This
* will output the necessary CPU instructions to perform the math and return a new register
* location.
*/
Location offset(ByteCodeGenerator& generator, ByteCode::Register offset) {
ByteCode::Register current;
switch (fKind) {
case kPointer_Kind:
current = generator.next(1);
generator.write(ByteCode::Instruction::kImmediate);
generator.write(current);
generator.write(ByteCode::Immediate{fPointer.fAddress});
break;
case kRegister_Kind:
current = fRegister;
}
SkUNREACHABLE;
}
ByteCodeInstruction selectStore(ByteCodeInstruction local,
ByteCodeInstruction global) const {
switch (fStorage) {
case Storage::kLocal: return local;
case Storage::kGlobal: return global;
case Storage::kUniform: ABORT("Trying to store to a uniform"); break;
}
return local;
ByteCode::Register result = generator.next(1);
generator.write(ByteCode::Instruction::kAddI);
generator.write(result);
generator.write(current);
generator.write(offset);
return result;
}
};
// reserves 16 bits in the output code, to be filled in later with an address once we determine
// it
class DeferredLocation {
public:
explicit DeferredLocation(ByteCodeGenerator* generator)
: fGenerator(*generator)
, fOffset(generator->fCode->size()) {
generator->write(ByteCode::Pointer{65535});
}
void set() {
SkASSERT(fGenerator.fCode->size() <= ByteCode::kPointerMax);
static_assert(sizeof(ByteCode::Pointer) == 2,
"failed assumption that ByteCode::Pointer is uint16_t");
void* dst = &(*fGenerator.fCode)[fOffset];
// ensure that the placeholder value 65535 hasn't been modified yet
SkASSERT(((uint8_t*) dst)[0] == 255 && ((uint8_t*) dst)[1] == 255);
ByteCode::Pointer target{(uint16_t) fGenerator.fCode->size()};
memcpy(dst, &target, sizeof(target));
}
private:
ByteCodeGenerator& fGenerator;
size_t fOffset;
};
template<typename T>
void write(T value) {
size_t n = fCode->size();
fCode->resize(n + sizeof(value));
memcpy(fCode->data() + n, &value, sizeof(value));
}
ByteCode::Register next(int slotCount);
/**
* Returns the local slot into which var should be stored, allocating a new slot if it has not
* already been assigned one. Compound variables (e.g. vectors) will consume more than one local
* slot, with the getLocation return value indicating where the first element should be stored.
* Based on 'type', writes the s (signed), u (unsigned), or f (float) instruction.
*/
void writeTypedInstruction(const Type& type, ByteCode::Instruction s, ByteCode::Instruction u,
ByteCode::Instruction f);
ByteCode::Instruction getLoadInstruction(Location location, Variable::Storage storage);
ByteCode::Instruction getStoreInstruction(Location location, Variable::Storage storage);
static int SlotCount(const Type& type);
Location getLocation(const Variable& var);
/**
* As above, but computes the (possibly dynamic) address of an expression involving indexing &
* field access. If the address is known, it's returned. If not, -1 is returned, and the
* location will be left on the top of the stack.
*/
Location getLocation(const Expression& expr);
void gatherUniforms(const Type& type, const String& name);
Variable::Storage getStorage(const Expression& expr);
std::unique_ptr<ByteCodeFunction> writeFunction(const FunctionDefinition& f);
void writeVarDeclarations(const VarDeclarations& decl);
void writeVariableExpression(const Expression& expr);
void writeExpression(const Expression& expr, bool discard = false);
/**
* Pushes whatever values are required by the lvalue onto the stack, and returns an LValue
* permitting loads and stores to it.
*/
std::unique_ptr<LValue> getLValue(const Expression& expr);
void writeIntrinsicCall(const FunctionCall& c);
void writeFunction(const FunctionDefinition& f);
void writeFunctionCall(const FunctionCall& c);
// For compound values, the result argument specifies the first component. Subsequent components
// will be in subsequent registers.
void writeConstructor(const Constructor& c);
void writeBinaryInstruction(const Type& operandType, ByteCode::Register left,
ByteCode::Register right, ByteCode::Instruction s,
ByteCode::Instruction u, ByteCode::Instruction f,
ByteCode::Register result);
void writeExternalFunctionCall(const ExternalFunctionCall& c);
void writeBinaryExpression(const BinaryExpression& expr, ByteCode::Register result);
void writeExternalValue(const ExternalValueReference& r);
void writeConstructor(const Constructor& c, ByteCode::Register result);
void writeSwizzle(const Swizzle& swizzle);
void writeExternalFunctionCall(const ExternalFunctionCall& f, ByteCode::Register result);
bool writeBinaryExpression(const BinaryExpression& b, bool discard);
void writeExternalValue(const ExternalValueReference& e, ByteCode::Register result);
void writeTernaryExpression(const TernaryExpression& t);
void writeIntrinsicCall(const FunctionCall& c, Intrinsic intrinsic, ByteCode::Register result);
void writeNullLiteral(const NullLiteral& n);
void writeFunctionCall(const FunctionCall& c, ByteCode::Register result);
bool writePrefixExpression(const PrefixExpression& p, bool discard);
void incOrDec(Token::Kind op, Expression& operand, bool prefix, ByteCode::Register result);
bool writePostfixExpression(const PostfixExpression& p, bool discard);
void writePostfixExpression(const PostfixExpression& p, ByteCode::Register result);
void writeBoolLiteral(const BoolLiteral& b);
void writePrefixExpression(const PrefixExpression& p, ByteCode::Register result);
void writeIntLiteral(const IntLiteral& i);
void writeSwizzle(const Swizzle& s, ByteCode::Register result);
void writeFloatLiteral(const FloatLiteral& f);
void writeTernaryExpression(const TernaryExpression& t, ByteCode::Register result);
void writeStatement(const Statement& s);
void writeVariableExpression(const Expression& e, ByteCode::Register result);
void writeExpression(const Expression& expr, ByteCode::Register result);
ByteCode::Register writeExpression(const Expression& expr);
void writeBlock(const Block& b);
void writeBreakStatement(const BreakStatement& b);
void writeContinueStatement(const ContinueStatement& c);
void writeIfStatement(const IfStatement& stmt);
void writeDoStatement(const DoStatement& d);
void writeForStatement(const ForStatement& f);
void writeIfStatement(const IfStatement& i);
void writeReturn(const ReturnStatement& r);
void writeVarDeclarations(const VarDeclarations& v);
void writeWhileStatement(const WhileStatement& w);
void writeDoStatement(const DoStatement& d);
void writeStatement(const Statement& s);
void writeSwitchStatement(const SwitchStatement& s);
void writeReturnStatement(const ReturnStatement& r);
// updates the current set of breaks to branch to the current location
void setBreakTargets();
// updates the current set of continues to branch to the current location
void setContinueTargets();
void enterLoop() {
fLoopCount++;
fMaxLoopCount = std::max(fMaxLoopCount, fLoopCount);
}
void exitLoop() {
SkASSERT(fLoopCount > 0);
fLoopCount--;
}
void enterCondition() {
fConditionCount++;
fMaxConditionCount = std::max(fMaxConditionCount, fConditionCount);
}
void exitCondition() {
SkASSERT(fConditionCount > 0);
fConditionCount--;
}
const Context& fContext;
void gatherUniforms(const Type& type, const String& name);
ByteCode* fOutput;
int fNextRegister = 0;
const FunctionDefinition* fFunction;
std::vector<const FunctionDefinition*> fFunctions;
std::vector<uint8_t>* fCode;
std::vector<const Variable*> fLocals;
std::stack<std::vector<DeferredLocation>> fContinueTargets;
std::stack<std::vector<DeferredLocation>> fBreakTargets;
std::vector<const FunctionDefinition*> fFunctions;
int fParameterCount;
int fStackCount;
int fMaxStackCount;
int fLoopCount;
int fMaxLoopCount;
int fConditionCount;
int fMaxConditionCount;
const std::unordered_map<String, Intrinsic> fIntrinsics;
friend class DeferredLocation;
friend class ByteCodeExpressionLValue;
friend class ByteCodeExternalValueLValue;
friend class ByteCodeSimpleLValue;
friend class ByteCodeSwizzleLValue;
typedef CodeGenerator INHERITED;
};
template<>
inline void ByteCodeGenerator::write(ByteCodeGenerator::Location loc) {
switch (loc.fKind) {
case ByteCodeGenerator::Location::kPointer_Kind:
this->write(loc.fPointer);
break;
case ByteCodeGenerator::Location::kRegister_Kind:
this->write(loc.fRegister);
break;
}
}
}
#endif

View File

@ -77,14 +77,17 @@ static const char* SKSL_PIPELINE_INCLUDE =
namespace SkSL {
static void grab_intrinsics(std::vector<std::unique_ptr<ProgramElement>>* src,
std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>>* target) {
for (auto& element : *src) {
std::map<String, std::pair<std::unique_ptr<ProgramElement>, bool>>* target) {
for (auto iter = src->begin(); iter != src->end(); ) {
std::unique_ptr<ProgramElement>& element = *iter;
switch (element->fKind) {
case ProgramElement::kFunction_Kind: {
FunctionDefinition& f = (FunctionDefinition&) *element;
StringFragment name = f.fDeclaration.fName;
SkASSERT(target->find(name) == target->end());
(*target)[name] = std::make_pair(std::move(element), false);
SkASSERT(f.fDeclaration.fBuiltin);
String key = f.fDeclaration.declaration();
SkASSERT(target->find(key) == target->end());
(*target)[key] = std::make_pair(std::move(element), false);
iter = src->erase(iter);
break;
}
case ProgramElement::kEnum_Kind: {
@ -92,6 +95,7 @@ static void grab_intrinsics(std::vector<std::unique_ptr<ProgramElement>>* src,
StringFragment name = e.fTypeName;
SkASSERT(target->find(name) == target->end());
(*target)[name] = std::make_pair(std::move(element), false);
iter = src->erase(iter);
break;
}
default:
@ -278,11 +282,13 @@ Compiler::Compiler(Flags flags)
this->processIncludeFile(Program::kPipelineStage_Kind, SKSL_PIPELINE_INCLUDE,
strlen(SKSL_PIPELINE_INCLUDE), fGpuSymbolTable, &fPipelineInclude,
&fPipelineSymbolTable);
std::vector<std::unique_ptr<ProgramElement>> interpIntrinsics;
this->processIncludeFile(Program::kGeneric_Kind, SKSL_INTERP_INCLUDE,
strlen(SKSL_INTERP_INCLUDE), symbols, &fInterpreterInclude,
&fInterpreterSymbolTable);
grab_intrinsics(&interpIntrinsics, &fInterpreterIntrinsics);
grab_intrinsics(&fInterpreterInclude, &fInterpreterIntrinsics);
// need to hang on to the source so that FunctionDefinition.fSource pointers in this file
// remain valid
fInterpreterIncludeSource = std::move(fIRGenerator->fFile);
}
Compiler::~Compiler() {
@ -1624,7 +1630,7 @@ std::unique_ptr<ByteCode> Compiler::toByteCode(Program& program) {
}
fSource = program.fSource.get();
std::unique_ptr<ByteCode> result(new ByteCode());
ByteCodeGenerator cg(fContext.get(), &program, this, result.get());
ByteCodeGenerator cg(&program, this, result.get());
bool success = cg.generateCode();
fSource = nullptr;
if (success) {

View File

@ -215,8 +215,8 @@ private:
Position position(int offset);
std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>> fGPUIntrinsics;
std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>> fInterpreterIntrinsics;
std::map<String, std::pair<std::unique_ptr<ProgramElement>, bool>> fGPUIntrinsics;
std::map<String, std::pair<std::unique_ptr<ProgramElement>, bool>> fInterpreterIntrinsics;
std::unique_ptr<ASTFile> fGpuIncludeSource;
std::shared_ptr<SymbolTable> fGpuSymbolTable;
std::vector<std::unique_ptr<ProgramElement>> fVertexInclude;
@ -227,6 +227,7 @@ private:
std::shared_ptr<SymbolTable> fGeometrySymbolTable;
std::vector<std::unique_ptr<ProgramElement>> fPipelineInclude;
std::shared_ptr<SymbolTable> fPipelineSymbolTable;
std::unique_ptr<ASTFile> fInterpreterIncludeSource;
std::vector<std::unique_ptr<ProgramElement>> fInterpreterInclude;
std::shared_ptr<SymbolTable> fInterpreterSymbolTable;

View File

@ -1776,7 +1776,7 @@ std::unique_ptr<Expression> IRGenerator::call(int offset,
const FunctionDeclaration& function,
std::vector<std::unique_ptr<Expression>> arguments) {
if (function.fBuiltin) {
auto found = fIntrinsics->find(function.fName);
auto found = fIntrinsics->find(function.declaration());
if (found != fIntrinsics->end() && !found->second.second) {
found->second.second = true;
const FunctionDeclaration* old = fCurrentFunction;
@ -2186,7 +2186,7 @@ std::unique_ptr<Expression> IRGenerator::convertField(std::unique_ptr<Expression
}
}
fErrors.error(base->fOffset, "type '" + base->fType.displayName() + "' does not have a "
"field named '" + field + "");
"field named '" + field + "'");
return nullptr;
}

View File

@ -159,7 +159,7 @@ private:
std::shared_ptr<SymbolTable> fSymbolTable;
// Symbols which have definitions in the include files. The bool tells us whether this
// intrinsic has been included already.
std::map<StringFragment, std::pair<std::unique_ptr<ProgramElement>, bool>>* fIntrinsics = nullptr;
std::map<String, std::pair<std::unique_ptr<ProgramElement>, bool>>* fIntrinsics = nullptr;
// holds extra temp variable declarations needed for the current function
std::vector<std::unique_ptr<Statement>> fExtraVars;
int fLoopLevel;

1353
src/sksl/SkSLInterpreter.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,7 @@ struct FunctionDeclaration : public Symbol {
for (auto p : fParameters) {
result += separator;
separator = ", ";
result += p->fName;
result += p->fType.displayName();
}
result += ")";
return result;

View File

@ -114,9 +114,7 @@ void SymbolTable::markAllFunctionsBuiltin() {
break;
case Symbol::kUnresolvedFunction_Kind:
for (auto& f : ((UnresolvedFunction&) *pair.second).fFunctions) {
if (!((FunctionDeclaration*)f)->fDefined) {
((FunctionDeclaration*)f)->fBuiltin = true;
}
((FunctionDeclaration*)f)->fBuiltin = true;
}
break;
default:

View File

@ -1,5 +1,7 @@
STRINGIFY(
sk_has_side_effects void print(float f);
$genType cos($genType y);
$genHType cos($genHType y);
float dot($genType x, $genType y);

View File

@ -9,19 +9,11 @@
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLExternalValue.h"
#include "src/sksl/SkSLInterpreter.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;
@ -39,30 +31,11 @@ void test(skiatest::Reporter* r, const char* src, float* in, float* expected,
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);
SkSL::Interpreter<1> interpreter(std::move(byteCode));
SkSL::ByteCode::Vector<1>* result;
bool success = interpreter.run(main, (SkSL::ByteCode::Vector<1>*) in, &result);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, result->fFloat[0] == expected[0]);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
@ -83,7 +56,8 @@ void vec_test(skiatest::Reporter* r, const char* src) {
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
const SkSL::ByteCodeFunction* main1 = byteCode->getFunction("main");
SkSL::Interpreter<1> interpreter1(std::move(byteCode));
// Test on four different vectors (with varying orderings to get divergent control flow)
const float input[16] = { 1, 2, 3, 4,
@ -97,9 +71,16 @@ void vec_test(skiatest::Reporter* r, const char* src) {
// 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));
SkAssertResult(interpreter1.run(main1, (SkSL::ByteCode::Vector<1>*) (out_s + i * 4),
nullptr));
}
byteCode = compiler.toByteCode(*program);
SkASSERT(compiler.errorCount() == 0);
const SkSL::ByteCodeFunction* main4 = byteCode->getFunction("main");
SkSL::Interpreter<4> interpreter4(std::move(byteCode));
// Need to transpose input vectors for striped execution
auto transpose = [](float* v) {
for (int r = 0; r < 4; ++r)
@ -112,7 +93,7 @@ void vec_test(skiatest::Reporter* r, const char* src) {
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));
SkAssertResult(interpreter4.runStriped(main4, 4, (float**) args));
// Transpose striped outputs back
transpose(out_v);
@ -125,7 +106,7 @@ void vec_test(skiatest::Reporter* r, const char* src) {
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();
main4->disassemble();
REPORT_FAILURE(r, "VecInterpreter mismatch", SkString());
}
}
@ -147,20 +128,26 @@ void test(skiatest::Reporter* r, const char* src, float inR, float inG, float in
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) {
SkSL::ByteCode::Vector<1> inoutColor[4];
inoutColor[0].fFloat[0] = inR;
inoutColor[1].fFloat[0] = inG;
inoutColor[2].fFloat[0] = inB;
inoutColor[3].fFloat[0] = inA;
SkSL::Interpreter<1> interpreter(std::move(byteCode));
bool success = interpreter.run(main, inoutColor, nullptr);
REPORTER_ASSERT(r, success);
if (inoutColor[0].fFloat[0] != expectedR || inoutColor[1].fFloat[0] != expectedG ||
inoutColor[2].fFloat[0] != expectedB || inoutColor[3].fFloat[0] != 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]);
expectedG, expectedB, expectedA, inoutColor[0].fFloat[0],
inoutColor[1].fFloat[0], inoutColor[2].fFloat[0], inoutColor[3].fFloat[0]);
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);
REPORTER_ASSERT(r, inoutColor[0].fFloat[0] == expectedR);
REPORTER_ASSERT(r, inoutColor[1].fFloat[0] == expectedG);
REPORTER_ASSERT(r, inoutColor[2].fFloat[0] == expectedB);
REPORTER_ASSERT(r, inoutColor[3].fFloat[0] == expectedA);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
@ -177,6 +164,10 @@ DEF_TEST(SkSLInterpreterAdd, r) {
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);
test(r, "void main(inout half4 color) { color.rg = color.r + color.gb; }", 1, 2, 3, 4,
3, 4, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.rg + color.b; }", 1, 2, 3, 4,
4, 5, 3, 4);
}
DEF_TEST(SkSLInterpreterSubtract, r) {
@ -189,6 +180,10 @@ DEF_TEST(SkSLInterpreterSubtract, r) {
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);
test(r, "void main(inout half4 color) { color.rg = color.r - color.gb; }", 1, 2, 3, 4,
-1, -2, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.rg - color.b; }", 1, 2, 3, 4,
-2, -1, 3, 4);
}
DEF_TEST(SkSLInterpreterMultiply, r) {
@ -200,6 +195,10 @@ DEF_TEST(SkSLInterpreterMultiply, r) {
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);
test(r, "void main(inout half4 color) { color.rg = color.r * color.gb; }", 5, 2, 3, 4,
10, 15, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.rg * color.b; }", 1, 2, 3, 4,
3, 6, 3, 4);
}
DEF_TEST(SkSLInterpreterDivide, r) {
@ -211,6 +210,10 @@ DEF_TEST(SkSLInterpreterDivide, r) {
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);
test(r, "void main(inout half4 color) { color.rg = color.r / color.gb; }", 12, 2, 3, 4,
6, 4, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.rg / color.b; }", 6, 3, 3, 4,
2, 1, 3, 4);
}
DEF_TEST(SkSLInterpreterRemainder, r) {
@ -222,6 +225,14 @@ DEF_TEST(SkSLInterpreterRemainder, r) {
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);
test(r, "void main(inout half4 color) { color.rg = color.r + color.gb; }", 1, 2, 3, 4,
3, 4, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.rg + color.b; }", 1, 2, 3, 4,
4, 5, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.r % color.gb; }", 10, 2, 3, 4,
0, 1, 3, 4);
test(r, "void main(inout half4 color) { color.rg = color.rg % color.b; }", 6, 3, 4, 4,
2, 3, 4, 4);
}
DEF_TEST(SkSLInterpreterAnd, r) {
@ -278,7 +289,7 @@ DEF_TEST(SkSLInterpreterBitwise, r) {
unsigned out;
out = 0x00000088;
test(r, "int main(int x) { return x << 3; }", (float*)&in, (float*)&out);
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);
@ -473,12 +484,16 @@ DEF_TEST(SkSLInterpreterIf, r) {
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, 1, 3, 1, 2, 1, 3);
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);
test(r, "void main(inout half4 color) { if (color.rg != color.ba) color.a = 1; }",
1, 2, 1, 3, 1, 2, 1, 1);
}
DEF_TEST(SkSLInterpreterWhile, r) {
@ -641,51 +656,67 @@ DEF_TEST(SkSLInterpreterCompound, r) {
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;
SkSL::Interpreter<1> interpreter(std::move(byteCode));
auto geti = [](SkSL::Interpreter<1>::Vector* v) { return v->fInt[0]; };
auto getf = [](SkSL::Interpreter<1>::Vector* v) { return v->fFloat[0]; };
{
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);
SkSL::Interpreter<1>::Vector* out;
bool success = interpreter.run(rect_height, (SkSL::Interpreter<1>::Vector*) &in, &out);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, geti(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);
SkSL::Interpreter<1>::Vector* out;
bool success = interpreter.run(make_blue_rect, (SkSL::Interpreter<1>::Vector*) in, &out);
REPORTER_ASSERT(r, success);
RectAndColor result{ { geti(out), geti(out + 1), geti(out + 2), geti(out + 3) },
{ getf(out + 4), getf(out + 5), getf(out + 6), getf(out + 7) } };
REPORTER_ASSERT(r, result.fRect.width() == 15);
REPORTER_ASSERT(r, result.fRect.height() == 25);
SkColor4f blue = { 0.0f, 1.0f, 0.0f, 1.0f };
REPORTER_ASSERT(r, out.fColor == blue);
REPORTER_ASSERT(r, result.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);
SkSL::Interpreter<1>::Vector* out;
bool success = interpreter.run(median, (SkSL::Interpreter<1>::Vector*) in, &out);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, geti(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));
SkSL::Interpreter<1>::Vector* out;
bool success = interpreter.run(sums, (SkSL::Interpreter<1>::Vector*) in, &out);
REPORTER_ASSERT(r, success);
for (int i = 0; i < 8; ++i) {
REPORTER_ASSERT(r, out[i] == static_cast<float>((i + 1) * (i + 2) / 2));
REPORTER_ASSERT(r, getf(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]);
interpreter.setUniforms(fRects);
SkSL::Interpreter<1>::Vector* out;
bool success = interpreter.run(get_rect, (SkSL::Interpreter<1>::Vector*) &in, &out);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, geti(out) == gRects[2].fLeft);
REPORTER_ASSERT(r, geti(out + 1) == gRects[2].fTop);
REPORTER_ASSERT(r, geti(out + 2) == gRects[2].fRight);
REPORTER_ASSERT(r, geti(out + 3) == gRects[2].fBottom);
}
{
ManyRects in;
memset(&in, 0, sizeof(in));
in.fNumRects = 2;
SkAssertResult(byteCode->run(fill_rects, (float*)&in, 33, nullptr, 0, fRects, 16));
bool success = interpreter.run(fill_rects, (SkSL::Interpreter<1>::Vector*) &in, nullptr);
REPORTER_ASSERT(r, success);
ManyRects expected;
memset(&expected, 0, sizeof(expected));
expected.fNumRects = 2;
@ -718,9 +749,11 @@ static void expect_run_failure(skiatest::Reporter* r, const char* src, float* in
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);
auto main = byteCode->getFunction("main");
SkSL::Interpreter<1> interpreter(std::move(byteCode));
SkSL::ByteCode::Vector<1>* result;
bool success = interpreter.run(main, (SkSL::ByteCode::Vector<1>*) in, &result);
REPORTER_ASSERT(r, !success);
}
DEF_TEST(SkSLInterpreterRestrictFunctionCalls, r) {
@ -786,16 +819,21 @@ DEF_TEST(SkSLInterpreterFunctions, r) {
REPORTER_ASSERT(r, dot3);
REPORTER_ASSERT(r, dot2);
float out = 0.0f;
SkSL::Interpreter<1> interpreter(std::move(byteCode));
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);
SkSL::Interpreter<1>::Vector* out;
bool success = interpreter.run(main, (SkSL::Interpreter<1>::Vector*) &in, &out);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, out->fFloat[0] = 6.0f);
SkAssertResult(byteCode->run(dot2, &in, 1, &out, 1, nullptr, 0));
REPORTER_ASSERT(r, out = -1.0f);
success = interpreter.run(dot3, (SkSL::Interpreter<1>::Vector*) &in, &out);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, out->fFloat[0] = 9.0f);
success = interpreter.run(dot2, (SkSL::Interpreter<1>::Vector*) &in, &out);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, out->fFloat[0] = -1.0f);
}
DEF_TEST(SkSLInterpreterOutParams, r) {
@ -804,15 +842,18 @@ DEF_TEST(SkSLInterpreterOutParams, r) {
"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) {"
"half2 tricky(half x, half y, inout half2 color, half z, out half w) {"
" color.xy = color.yx;"
" w = 47;"
" return half2(x + y, z);"
"}"
"void main(inout half4 color) {"
" half2 t = tricky(1, 2, color.rb, 5);"
" half w;"
" half2 t = tricky(1, 2, color.rb, 5, w);"
" color.r += w;"
" color.ga = t;"
"}",
1, 2, 3, 4, 3, 3, 1, 5);
1, 2, 3, 4, 50, 3, 1, 5);
}
DEF_TEST(SkSLInterpreterMathFunctions, r) {
@ -1029,9 +1070,11 @@ DEF_TEST(SkSLInterpreterExternalValues, r) {
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);
SkSL::Interpreter<1> interpreter(std::move(byteCode));
SkSL::ByteCode::Vector<1>* result;
bool success = interpreter.run(main, nullptr, &result);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, result->fFloat[0] == 66.0);
REPORTER_ASSERT(r, outValue == 152);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
@ -1062,7 +1105,9 @@ DEF_TEST(SkSLInterpreterExternalValuesVector, r) {
return;
}
const SkSL::ByteCodeFunction* main = byteCode->getFunction("main");
SkAssertResult(byteCode->run(main, nullptr, 0, nullptr, 0, nullptr, 0));
SkSL::Interpreter<1> interpreter(std::move(byteCode));
bool success = interpreter.run(main, nullptr, nullptr);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, value[0] == 2);
REPORTER_ASSERT(r, value[1] == 4);
REPORTER_ASSERT(r, value[2] == 6);
@ -1127,9 +1172,11 @@ DEF_TEST(SkSLInterpreterExternalValuesCall, r) {
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);
SkSL::Interpreter<1> interpreter(std::move(byteCode));
SkSL::ByteCode::Vector<1>* result;
bool success = interpreter.run(main, nullptr, &result);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, result->fFloat[0] == 5.0);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}
@ -1142,32 +1189,23 @@ public:
: 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) override {
fFunction(arguments, outReturn);
}
private:
SkSL::Compiler& fCompiler;
void (*fFunction)(float[4], float[4]);
typedef SkSL::ExternalValue INHERITED;
};
DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
SkSL::Compiler compiler;
SkSL::Program::Settings settings;
@ -1195,12 +1233,14 @@ DEF_TEST(SkSLInterpreterExternalValuesVectorCall, r) {
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);
SkSL::Interpreter<1> interpreter(std::move(byteCode));
SkSL::ByteCode::Vector<1>* result;
bool success = interpreter.run(main, nullptr, &result);
REPORTER_ASSERT(r, success);
REPORTER_ASSERT(r, result[0].fFloat[0] == 1.0);
REPORTER_ASSERT(r, result[1].fFloat[0] == 2.0);
REPORTER_ASSERT(r, result[2].fFloat[0] == 3.0);
REPORTER_ASSERT(r, result[3].fFloat[0] == 4.0);
} else {
printf("%s\n%s", src, compiler.errorText().c_str());
}