Revert "Revert "Complete rewrite of the SkSL interpreter""
This reverts commit 99c54f0290
.
This commit is contained in:
parent
4ca0dacbce
commit
7deb1c26ba
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -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(); }
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
@ -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
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
1353
src/sksl/SkSLInterpreter.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user