SkSL: Allow invoking children (shaders, etc) like functions
Previously, you would declare child objects (shaders, colorFilters, etc.) and "sample" them like this: uniform shader input; uniform colorFilter filter; half4 main(float2 coord) { half4 inColor = sample(input, coord); return sample(filter, inColor); } With the new syntax, those child objects become directly callable, reflecting the way that Skia assembles all parts of the paint (as functions) in the overall fragment shader: uniform shader input; uniform colorFilter filter; half4 main(float2 coord) { half4 inColor = input(coord); return filter(inColor); } Bug: skia:12302 Change-Id: Ia12351964dc5d2300660187933188e738671cd83 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/436517 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com> Reviewed-by: John Stiles <johnstiles@google.com>
This commit is contained in:
parent
c18ee4e55a
commit
eb0f29dba2
@ -17,6 +17,13 @@ Milestone 94
|
|||||||
* Removed SK_SUPPORT_DEPRECATED_CLIPOPS build flag. Clips can only be intersect and difference.
|
* Removed SK_SUPPORT_DEPRECATED_CLIPOPS build flag. Clips can only be intersect and difference.
|
||||||
https://review.skia.org/436565
|
https://review.skia.org/436565
|
||||||
|
|
||||||
|
* There is a new syntax for invoking (sampling) child effects in SkSL. Previously, children
|
||||||
|
(shaders, colorFilters, blenders) were invoked using different overloads of `sample`. That
|
||||||
|
syntax is deprecated (but still supported). Now, the child object can be invoked directly,
|
||||||
|
like a function. The arguments to these invocations are the same as the arguments that followed
|
||||||
|
the child in calls to `sample`.
|
||||||
|
https://review.skia.org/436517
|
||||||
|
|
||||||
* * *
|
* * *
|
||||||
|
|
||||||
Milestone 93
|
Milestone 93
|
||||||
|
@ -103,6 +103,8 @@ skia_sksl_sources = [
|
|||||||
"$_src/sksl/ir/SkSLBlock.h",
|
"$_src/sksl/ir/SkSLBlock.h",
|
||||||
"$_src/sksl/ir/SkSLBoolLiteral.h",
|
"$_src/sksl/ir/SkSLBoolLiteral.h",
|
||||||
"$_src/sksl/ir/SkSLBreakStatement.h",
|
"$_src/sksl/ir/SkSLBreakStatement.h",
|
||||||
|
"$_src/sksl/ir/SkSLChildCall.cpp",
|
||||||
|
"$_src/sksl/ir/SkSLChildCall.h",
|
||||||
"$_src/sksl/ir/SkSLConstructor.cpp",
|
"$_src/sksl/ir/SkSLConstructor.cpp",
|
||||||
"$_src/sksl/ir/SkSLConstructor.h",
|
"$_src/sksl/ir/SkSLConstructor.h",
|
||||||
"$_src/sksl/ir/SkSLConstructorArray.cpp",
|
"$_src/sksl/ir/SkSLConstructorArray.cpp",
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
// Expressions
|
// Expressions
|
||||||
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
||||||
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
||||||
|
#include "src/sksl/ir/SkSLChildCall.h"
|
||||||
#include "src/sksl/ir/SkSLConstructor.h"
|
#include "src/sksl/ir/SkSLConstructor.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
|
#include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorMatrixResize.h"
|
#include "src/sksl/ir/SkSLConstructorMatrixResize.h"
|
||||||
@ -62,18 +63,13 @@ namespace SkSL {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
static bool is_sample_call_to_fp(const FunctionCall& fc, const Variable& fp) {
|
// Visitor that determines the merged SampleUsage for a given child in the program.
|
||||||
const FunctionDeclaration& f = fc.function();
|
|
||||||
return f.intrinsicKind() == k_sample_IntrinsicKind && fc.arguments().size() >= 1 &&
|
|
||||||
fc.arguments()[0]->is<VariableReference>() &&
|
|
||||||
fc.arguments()[0]->as<VariableReference>().variable() == &fp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visitor that determines the merged SampleUsage for a given child 'fp' in the program.
|
|
||||||
class MergeSampleUsageVisitor : public ProgramVisitor {
|
class MergeSampleUsageVisitor : public ProgramVisitor {
|
||||||
public:
|
public:
|
||||||
MergeSampleUsageVisitor(const Context& context, const Variable& fp, bool writesToSampleCoords)
|
MergeSampleUsageVisitor(const Context& context,
|
||||||
: fContext(context), fFP(fp), fWritesToSampleCoords(writesToSampleCoords) {}
|
const Variable& child,
|
||||||
|
bool writesToSampleCoords)
|
||||||
|
: fContext(context), fChild(child), fWritesToSampleCoords(writesToSampleCoords) {}
|
||||||
|
|
||||||
SampleUsage visit(const Program& program) {
|
SampleUsage visit(const Program& program) {
|
||||||
fUsage = SampleUsage(); // reset to none
|
fUsage = SampleUsage(); // reset to none
|
||||||
@ -85,43 +81,34 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
const Context& fContext;
|
const Context& fContext;
|
||||||
const Variable& fFP;
|
const Variable& fChild;
|
||||||
const bool fWritesToSampleCoords;
|
const bool fWritesToSampleCoords;
|
||||||
SampleUsage fUsage;
|
SampleUsage fUsage;
|
||||||
int fElidedSampleCoordCount = 0;
|
int fElidedSampleCoordCount = 0;
|
||||||
|
|
||||||
bool visitExpression(const Expression& e) override {
|
bool visitExpression(const Expression& e) override {
|
||||||
// Looking for sample(fp, ...)
|
// Looking for child(...)
|
||||||
if (e.is<FunctionCall>()) {
|
if (e.is<ChildCall>() && &e.as<ChildCall>().child() == &fChild) {
|
||||||
const FunctionCall& fc = e.as<FunctionCall>();
|
// Determine the type of call at this site, and merge it with the accumulated state
|
||||||
if (is_sample_call_to_fp(fc, fFP)) {
|
const ExpressionArray& arguments = e.as<ChildCall>().arguments();
|
||||||
// Determine the type of call at this site, and merge it with the accumulated state
|
SkASSERT(arguments.size() >= 1);
|
||||||
if (fc.arguments().size() >= 2) {
|
|
||||||
const Expression* coords = fc.arguments()[1].get();
|
const Expression* maybeCoords = arguments[0].get();
|
||||||
if (coords->type() == *fContext.fTypes.fFloat2) {
|
if (maybeCoords->type() == *fContext.fTypes.fFloat2) {
|
||||||
// If the coords are a direct reference to the program's sample-coords,
|
// If the coords are a direct reference to the program's sample-coords, and those
|
||||||
// and those coords are never modified, we can conservatively turn this
|
// coords are never modified, we can conservatively turn this into PassThrough
|
||||||
// into PassThrough sampling. In all other cases, we consider it Explicit.
|
// sampling. In all other cases, we consider it Explicit.
|
||||||
if (!fWritesToSampleCoords && coords->is<VariableReference>() &&
|
if (!fWritesToSampleCoords && maybeCoords->is<VariableReference>() &&
|
||||||
coords->as<VariableReference>()
|
maybeCoords->as<VariableReference>().variable()->modifiers().fLayout.fBuiltin ==
|
||||||
.variable()
|
SK_MAIN_COORDS_BUILTIN) {
|
||||||
->modifiers()
|
|
||||||
.fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN) {
|
|
||||||
fUsage.merge(SampleUsage::PassThrough());
|
|
||||||
++fElidedSampleCoordCount;
|
|
||||||
} else {
|
|
||||||
fUsage.merge(SampleUsage::Explicit());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// sample(fp, half4 inputColor) -> PassThrough
|
|
||||||
fUsage.merge(SampleUsage::PassThrough());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// sample(fp) -> PassThrough
|
|
||||||
fUsage.merge(SampleUsage::PassThrough());
|
fUsage.merge(SampleUsage::PassThrough());
|
||||||
|
++fElidedSampleCoordCount;
|
||||||
|
} else {
|
||||||
|
fUsage.merge(SampleUsage::Explicit());
|
||||||
}
|
}
|
||||||
// NOTE: we don't return true here just because we found a sample call. We need to
|
} else {
|
||||||
// process the entire program and merge across all encountered calls.
|
// child(inputColor) or child(srcColor, dstColor) -> PassThrough
|
||||||
|
fUsage.merge(SampleUsage::PassThrough());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,17 +136,14 @@ public:
|
|||||||
using INHERITED = ProgramVisitor;
|
using INHERITED = ProgramVisitor;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Visitor that searches for calls to sample() from a function other than main()
|
// Visitor that searches for child calls from a function other than main()
|
||||||
class SampleOutsideMainVisitor : public ProgramVisitor {
|
class SampleOutsideMainVisitor : public ProgramVisitor {
|
||||||
public:
|
public:
|
||||||
SampleOutsideMainVisitor() {}
|
SampleOutsideMainVisitor() {}
|
||||||
|
|
||||||
bool visitExpression(const Expression& e) override {
|
bool visitExpression(const Expression& e) override {
|
||||||
if (e.is<FunctionCall>()) {
|
if (e.is<ChildCall>()) {
|
||||||
const FunctionDeclaration& f = e.as<FunctionCall>().function();
|
return true;
|
||||||
if (f.intrinsicKind() == k_sample_IntrinsicKind) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return INHERITED::visitExpression(e);
|
return INHERITED::visitExpression(e);
|
||||||
}
|
}
|
||||||
@ -1199,6 +1183,7 @@ public:
|
|||||||
// These are completely disallowed in SkSL constant-(index)-expressions. GLSL allows
|
// These are completely disallowed in SkSL constant-(index)-expressions. GLSL allows
|
||||||
// calls to built-in functions where the arguments are all constant-expressions, but
|
// calls to built-in functions where the arguments are all constant-expressions, but
|
||||||
// we don't guarantee that behavior. (skbug.com/10835)
|
// we don't guarantee that behavior. (skbug.com/10835)
|
||||||
|
case Expression::Kind::kChildCall:
|
||||||
case Expression::Kind::kExternalFunctionCall:
|
case Expression::Kind::kExternalFunctionCall:
|
||||||
case Expression::Kind::kFunctionCall:
|
case Expression::Kind::kFunctionCall:
|
||||||
return true;
|
return true;
|
||||||
@ -1311,6 +1296,14 @@ template <typename T> bool TProgramVisitor<T>::visitExpression(typename T::Expre
|
|||||||
return (b.left() && this->visitExpressionPtr(b.left())) ||
|
return (b.left() && this->visitExpressionPtr(b.left())) ||
|
||||||
(b.right() && this->visitExpressionPtr(b.right()));
|
(b.right() && this->visitExpressionPtr(b.right()));
|
||||||
}
|
}
|
||||||
|
case Expression::Kind::kChildCall: {
|
||||||
|
// We don't visit the child variable itself, just the arguments
|
||||||
|
auto& c = e.template as<ChildCall>();
|
||||||
|
for (auto& arg : c.arguments()) {
|
||||||
|
if (arg && this->visitExpressionPtr(arg)) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case Expression::Kind::kConstructorArray:
|
case Expression::Kind::kConstructorArray:
|
||||||
case Expression::Kind::kConstructorArrayCast:
|
case Expression::Kind::kConstructorArrayCast:
|
||||||
case Expression::Kind::kConstructorCompound:
|
case Expression::Kind::kConstructorCompound:
|
||||||
|
@ -277,6 +277,11 @@ void Dehydrator::write(const Expression* e) {
|
|||||||
this->writeU8(b.value());
|
this->writeU8(b.value());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case Expression::Kind::kChildCall:
|
||||||
|
SkDEBUGFAIL("unimplemented--not expected to be used from within an include file");
|
||||||
|
break;
|
||||||
|
|
||||||
case Expression::Kind::kCodeString:
|
case Expression::Kind::kCodeString:
|
||||||
SkDEBUGFAIL("shouldn't be able to receive kCodeString here");
|
SkDEBUGFAIL("shouldn't be able to receive kCodeString here");
|
||||||
break;
|
break;
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
||||||
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
||||||
#include "src/sksl/ir/SkSLBreakStatement.h"
|
#include "src/sksl/ir/SkSLBreakStatement.h"
|
||||||
|
#include "src/sksl/ir/SkSLChildCall.h"
|
||||||
#include "src/sksl/ir/SkSLConstructor.h"
|
#include "src/sksl/ir/SkSLConstructor.h"
|
||||||
#include "src/sksl/ir/SkSLContinueStatement.h"
|
#include "src/sksl/ir/SkSLContinueStatement.h"
|
||||||
#include "src/sksl/ir/SkSLDiscardStatement.h"
|
#include "src/sksl/ir/SkSLDiscardStatement.h"
|
||||||
@ -1314,6 +1315,18 @@ void IRGenerator::copyIntrinsicIfNeeded(const FunctionDeclaration& function) {
|
|||||||
std::unique_ptr<Expression> IRGenerator::call(int offset,
|
std::unique_ptr<Expression> IRGenerator::call(int offset,
|
||||||
const FunctionDeclaration& function,
|
const FunctionDeclaration& function,
|
||||||
ExpressionArray arguments) {
|
ExpressionArray arguments) {
|
||||||
|
if (function.intrinsicKind() == k_sample_IntrinsicKind && arguments.size() >= 1 &&
|
||||||
|
arguments[0]->type().isEffectChild()) {
|
||||||
|
// Translate old-style sample(child, ...) calls into new-style child(...) IR
|
||||||
|
SkASSERT(arguments[0]->is<VariableReference>());
|
||||||
|
const Variable& child = *arguments[0]->as<VariableReference>().variable();
|
||||||
|
ExpressionArray argumentsWithoutChild;
|
||||||
|
for (size_t i = 1; i < arguments.size(); i++) {
|
||||||
|
argumentsWithoutChild.push_back(std::move(arguments[i]));
|
||||||
|
}
|
||||||
|
return ChildCall::Convert(fContext, offset, child, std::move(argumentsWithoutChild));
|
||||||
|
}
|
||||||
|
|
||||||
if (function.isBuiltin()) {
|
if (function.isBuiltin()) {
|
||||||
if (function.intrinsicKind() == k_dFdy_IntrinsicKind) {
|
if (function.intrinsicKind() == k_dFdy_IntrinsicKind) {
|
||||||
fInputs.fUseFlipRTUniform = true;
|
fInputs.fUseFlipRTUniform = true;
|
||||||
@ -1413,6 +1426,16 @@ std::unique_ptr<Expression> IRGenerator::call(int offset,
|
|||||||
}
|
}
|
||||||
return this->call(offset, *functions[0], std::move(arguments));
|
return this->call(offset, *functions[0], std::move(arguments));
|
||||||
}
|
}
|
||||||
|
case Expression::Kind::kVariableReference: {
|
||||||
|
if (!functionValue->type().isEffectChild()) {
|
||||||
|
this->errorReporter().error(offset, "not a function");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return ChildCall::Convert(fContext,
|
||||||
|
offset,
|
||||||
|
*functionValue->as<VariableReference>().variable(),
|
||||||
|
std::move(arguments));
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
this->errorReporter().error(offset, "not a function");
|
this->errorReporter().error(offset, "not a function");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
||||||
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
||||||
#include "src/sksl/ir/SkSLBreakStatement.h"
|
#include "src/sksl/ir/SkSLBreakStatement.h"
|
||||||
|
#include "src/sksl/ir/SkSLChildCall.h"
|
||||||
#include "src/sksl/ir/SkSLConstructor.h"
|
#include "src/sksl/ir/SkSLConstructor.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorArray.h"
|
#include "src/sksl/ir/SkSLConstructorArray.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
|
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
|
||||||
@ -311,6 +312,14 @@ std::unique_ptr<Expression> Inliner::inlineExpression(int offset,
|
|||||||
case Expression::Kind::kIntLiteral:
|
case Expression::Kind::kIntLiteral:
|
||||||
case Expression::Kind::kFloatLiteral:
|
case Expression::Kind::kFloatLiteral:
|
||||||
return expression.clone();
|
return expression.clone();
|
||||||
|
case Expression::Kind::kChildCall: {
|
||||||
|
const ChildCall& childCall = expression.as<ChildCall>();
|
||||||
|
return ChildCall::Make(*fContext,
|
||||||
|
offset,
|
||||||
|
childCall.type().clone(symbolTableForExpression),
|
||||||
|
childCall.child(),
|
||||||
|
argList(childCall.arguments()));
|
||||||
|
}
|
||||||
case Expression::Kind::kConstructorArray: {
|
case Expression::Kind::kConstructorArray: {
|
||||||
const ConstructorArray& ctor = expression.as<ConstructorArray>();
|
const ConstructorArray& ctor = expression.as<ConstructorArray>();
|
||||||
return ConstructorArray::Make(*fContext, offset,
|
return ConstructorArray::Make(*fContext, offset,
|
||||||
@ -958,6 +967,13 @@ public:
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Expression::Kind::kChildCall: {
|
||||||
|
ChildCall& childCallExpr = (*expr)->as<ChildCall>();
|
||||||
|
for (std::unique_ptr<Expression>& arg : childCallExpr.arguments()) {
|
||||||
|
this->visitExpression(&arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Expression::Kind::kConstructorArray:
|
case Expression::Kind::kConstructorArray:
|
||||||
case Expression::Kind::kConstructorArrayCast:
|
case Expression::Kind::kConstructorArrayCast:
|
||||||
case Expression::Kind::kConstructorCompound:
|
case Expression::Kind::kConstructorCompound:
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include "src/sksl/SkSLOperators.h"
|
#include "src/sksl/SkSLOperators.h"
|
||||||
#include "src/sksl/SkSLStringStream.h"
|
#include "src/sksl/SkSLStringStream.h"
|
||||||
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
#include "src/sksl/ir/SkSLBinaryExpression.h"
|
||||||
|
#include "src/sksl/ir/SkSLChildCall.h"
|
||||||
#include "src/sksl/ir/SkSLConstructor.h"
|
#include "src/sksl/ir/SkSLConstructor.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
|
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
|
||||||
#include "src/sksl/ir/SkSLDoStatement.h"
|
#include "src/sksl/ir/SkSLDoStatement.h"
|
||||||
@ -79,6 +80,7 @@ private:
|
|||||||
void writeStructDefinition(const StructDefinition& s);
|
void writeStructDefinition(const StructDefinition& s);
|
||||||
|
|
||||||
void writeExpression(const Expression& expr, Precedence parentPrecedence);
|
void writeExpression(const Expression& expr, Precedence parentPrecedence);
|
||||||
|
void writeChildCall(const ChildCall& c);
|
||||||
void writeFunctionCall(const FunctionCall& c);
|
void writeFunctionCall(const FunctionCall& c);
|
||||||
void writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence);
|
void writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence);
|
||||||
void writeFieldAccess(const FieldAccess& f);
|
void writeFieldAccess(const FieldAccess& f);
|
||||||
@ -138,76 +140,74 @@ void PipelineStageCodeGenerator::writeLine(skstd::string_view s) {
|
|||||||
fBuffer->writeText("\n");
|
fBuffer->writeText("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
|
void PipelineStageCodeGenerator::writeChildCall(const ChildCall& c) {
|
||||||
const FunctionDeclaration& function = c.function();
|
|
||||||
const ExpressionArray& arguments = c.arguments();
|
const ExpressionArray& arguments = c.arguments();
|
||||||
if (function.isBuiltin() && function.name() == "sample") {
|
SkASSERT(arguments.size() >= 1);
|
||||||
SkASSERT(arguments.size() >= 2);
|
int index = 0;
|
||||||
const Expression* child = arguments[0].get();
|
bool found = false;
|
||||||
SkASSERT(child->type().isEffectChild());
|
for (const ProgramElement* p : fProgram.elements()) {
|
||||||
SkASSERT(child->is<VariableReference>());
|
if (p->is<GlobalVarDeclaration>()) {
|
||||||
int index = 0;
|
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
|
||||||
bool found = false;
|
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
|
||||||
for (const ProgramElement* p : fProgram.elements()) {
|
if (&decl.var() == &c.child()) {
|
||||||
if (p->is<GlobalVarDeclaration>()) {
|
found = true;
|
||||||
const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
|
} else if (decl.var().type().isEffectChild()) {
|
||||||
const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
|
++index;
|
||||||
if (&decl.var() == child->as<VariableReference>().variable()) {
|
|
||||||
found = true;
|
|
||||||
} else if (decl.var().type().isEffectChild()) {
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (found) {
|
}
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SkASSERT(found);
|
||||||
|
|
||||||
|
// Shaders require a coordinate argument. Color filters require a color argument.
|
||||||
|
// Blenders require two color arguments.
|
||||||
|
String sampleOutput;
|
||||||
|
{
|
||||||
|
AutoOutputBuffer exprBuffer(this);
|
||||||
|
this->writeExpression(*arguments[0], Precedence::kSequence);
|
||||||
|
|
||||||
|
switch (c.child().type().typeKind()) {
|
||||||
|
case Type::TypeKind::kShader: {
|
||||||
|
SkASSERT(arguments.size() == 1);
|
||||||
|
SkASSERT(arguments[0]->type() == *fProgram.fContext->fTypes.fFloat2);
|
||||||
|
sampleOutput = fCallbacks->sampleShader(index, exprBuffer.fBuffer.str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
case Type::TypeKind::kColorFilter: {
|
||||||
SkASSERT(found);
|
SkASSERT(arguments.size() == 1);
|
||||||
|
SkASSERT(arguments[0]->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
||||||
|
arguments[0]->type() == *fProgram.fContext->fTypes.fFloat4);
|
||||||
|
sampleOutput = fCallbacks->sampleColorFilter(index, exprBuffer.fBuffer.str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::TypeKind::kBlender: {
|
||||||
|
SkASSERT(arguments.size() == 2);
|
||||||
|
SkASSERT(arguments[0]->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
||||||
|
arguments[0]->type() == *fProgram.fContext->fTypes.fFloat4);
|
||||||
|
SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
||||||
|
arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
|
||||||
|
|
||||||
// Shaders require a coordinate argument. Color filters require a color argument.
|
AutoOutputBuffer exprBuffer2(this);
|
||||||
// Blenders require two color arguments.
|
this->writeExpression(*arguments[1], Precedence::kSequence);
|
||||||
String sampleOutput;
|
|
||||||
{
|
|
||||||
AutoOutputBuffer exprBuffer(this);
|
|
||||||
this->writeExpression(*arguments[1], Precedence::kSequence);
|
|
||||||
|
|
||||||
switch (child->type().typeKind()) {
|
sampleOutput = fCallbacks->sampleBlender(index, exprBuffer.fBuffer.str(),
|
||||||
case Type::TypeKind::kShader: {
|
exprBuffer2.fBuffer.str());
|
||||||
SkASSERT(arguments.size() == 2);
|
break;
|
||||||
SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fFloat2);
|
}
|
||||||
sampleOutput = fCallbacks->sampleShader(index, exprBuffer.fBuffer.str());
|
default: {
|
||||||
break;
|
SkDEBUGFAILF("cannot sample from type '%s'",
|
||||||
}
|
c.child().type().description().c_str());
|
||||||
case Type::TypeKind::kColorFilter: {
|
|
||||||
SkASSERT(arguments.size() == 2);
|
|
||||||
SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
|
||||||
arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
|
|
||||||
sampleOutput = fCallbacks->sampleColorFilter(index, exprBuffer.fBuffer.str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Type::TypeKind::kBlender: {
|
|
||||||
SkASSERT(arguments.size() == 3);
|
|
||||||
SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
|
||||||
arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);
|
|
||||||
SkASSERT(arguments[2]->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
|
||||||
arguments[2]->type() == *fProgram.fContext->fTypes.fFloat4);
|
|
||||||
|
|
||||||
AutoOutputBuffer exprBuffer2(this);
|
|
||||||
this->writeExpression(*arguments[2], Precedence::kSequence);
|
|
||||||
|
|
||||||
sampleOutput = fCallbacks->sampleBlender(index, exprBuffer.fBuffer.str(),
|
|
||||||
exprBuffer2.fBuffer.str());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
SkDEBUGFAILF("cannot sample from type '%s'",
|
|
||||||
child->type().description().c_str());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this->write(sampleOutput);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
this->write(sampleOutput);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
|
||||||
|
const FunctionDeclaration& function = c.function();
|
||||||
|
|
||||||
if (function.isBuiltin()) {
|
if (function.isBuiltin()) {
|
||||||
this->write(function.name());
|
this->write(function.name());
|
||||||
@ -217,7 +217,7 @@ void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
|
|||||||
|
|
||||||
this->write("(");
|
this->write("(");
|
||||||
const char* separator = "";
|
const char* separator = "";
|
||||||
for (const auto& arg : arguments) {
|
for (const auto& arg : c.arguments()) {
|
||||||
this->write(separator);
|
this->write(separator);
|
||||||
separator = ", ";
|
separator = ", ";
|
||||||
this->writeExpression(*arg, Precedence::kSequence);
|
this->writeExpression(*arg, Precedence::kSequence);
|
||||||
@ -435,6 +435,9 @@ void PipelineStageCodeGenerator::writeExpression(const Expression& expr,
|
|||||||
case Expression::Kind::kIntLiteral:
|
case Expression::Kind::kIntLiteral:
|
||||||
this->write(expr.description());
|
this->write(expr.description());
|
||||||
break;
|
break;
|
||||||
|
case Expression::Kind::kChildCall:
|
||||||
|
this->writeChildCall(expr.as<ChildCall>());
|
||||||
|
break;
|
||||||
case Expression::Kind::kConstructorArray:
|
case Expression::Kind::kConstructorArray:
|
||||||
case Expression::Kind::kConstructorArrayCast:
|
case Expression::Kind::kConstructorArrayCast:
|
||||||
case Expression::Kind::kConstructorCompound:
|
case Expression::Kind::kConstructorCompound:
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "src/sksl/ir/SkSLBlock.h"
|
#include "src/sksl/ir/SkSLBlock.h"
|
||||||
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
#include "src/sksl/ir/SkSLBoolLiteral.h"
|
||||||
#include "src/sksl/ir/SkSLBreakStatement.h"
|
#include "src/sksl/ir/SkSLBreakStatement.h"
|
||||||
|
#include "src/sksl/ir/SkSLChildCall.h"
|
||||||
#include "src/sksl/ir/SkSLConstructor.h"
|
#include "src/sksl/ir/SkSLConstructor.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorArray.h"
|
#include "src/sksl/ir/SkSLConstructorArray.h"
|
||||||
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
|
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
|
||||||
@ -181,6 +182,7 @@ private:
|
|||||||
Value writeExpression(const Expression& expr);
|
Value writeExpression(const Expression& expr);
|
||||||
Value writeBinaryExpression(const BinaryExpression& b);
|
Value writeBinaryExpression(const BinaryExpression& b);
|
||||||
Value writeAggregationConstructor(const AnyConstructor& c);
|
Value writeAggregationConstructor(const AnyConstructor& c);
|
||||||
|
Value writeChildCall(const ChildCall& c);
|
||||||
Value writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
|
Value writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
|
||||||
Value writeConstructorMatrixResize(const ConstructorMatrixResize& c);
|
Value writeConstructorMatrixResize(const ConstructorMatrixResize& c);
|
||||||
Value writeConstructorCast(const AnyConstructor& c);
|
Value writeConstructorCast(const AnyConstructor& c);
|
||||||
@ -865,76 +867,63 @@ Value SkVMGenerator::writeMatrixInverse4x4(const Value& m) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value SkVMGenerator::writeChildCall(const ChildCall& c) {
|
||||||
|
auto child_it = fVariableMap.find(&c.child());
|
||||||
|
SkASSERT(child_it != fVariableMap.end());
|
||||||
|
|
||||||
|
const Expression* arg = c.arguments()[0].get();
|
||||||
|
Value argVal = this->writeExpression(*arg);
|
||||||
|
skvm::Color color;
|
||||||
|
|
||||||
|
switch (c.child().type().typeKind()) {
|
||||||
|
case Type::TypeKind::kShader: {
|
||||||
|
SkASSERT(c.arguments().size() == 1);
|
||||||
|
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fFloat2);
|
||||||
|
skvm::Coord coord = {f32(argVal[0]), f32(argVal[1])};
|
||||||
|
color = fSampleShader(child_it->second, coord);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::TypeKind::kColorFilter: {
|
||||||
|
SkASSERT(c.arguments().size() == 1);
|
||||||
|
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
||||||
|
arg->type() == *fProgram.fContext->fTypes.fFloat4);
|
||||||
|
skvm::Color inColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
|
||||||
|
color = fSampleColorFilter(child_it->second, inColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Type::TypeKind::kBlender: {
|
||||||
|
SkASSERT(c.arguments().size() == 2);
|
||||||
|
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
||||||
|
arg->type() == *fProgram.fContext->fTypes.fFloat4);
|
||||||
|
skvm::Color srcColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
|
||||||
|
|
||||||
|
arg = c.arguments()[1].get();
|
||||||
|
argVal = this->writeExpression(*arg);
|
||||||
|
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
||||||
|
arg->type() == *fProgram.fContext->fTypes.fFloat4);
|
||||||
|
skvm::Color dstColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
|
||||||
|
|
||||||
|
color = fSampleBlender(child_it->second, srcColor, dstColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
SkDEBUGFAILF("cannot sample from type '%s'", c.child().type().description().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value result(4);
|
||||||
|
result[0] = color.r;
|
||||||
|
result[1] = color.g;
|
||||||
|
result[2] = color.b;
|
||||||
|
result[3] = color.a;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
Value SkVMGenerator::writeIntrinsicCall(const FunctionCall& c) {
|
Value SkVMGenerator::writeIntrinsicCall(const FunctionCall& c) {
|
||||||
IntrinsicKind intrinsicKind = c.function().intrinsicKind();
|
IntrinsicKind intrinsicKind = c.function().intrinsicKind();
|
||||||
SkASSERT(intrinsicKind != kNotIntrinsic);
|
SkASSERT(intrinsicKind != kNotIntrinsic);
|
||||||
|
|
||||||
const size_t nargs = c.arguments().size();
|
const size_t nargs = c.arguments().size();
|
||||||
|
|
||||||
if (intrinsicKind == k_sample_IntrinsicKind) {
|
|
||||||
// Sample is very special. The first argument is a child (shader/colorFilter/blender),
|
|
||||||
// which is opaque and can't be evaluated.
|
|
||||||
SkASSERT(nargs >= 2);
|
|
||||||
const Expression* child = c.arguments()[0].get();
|
|
||||||
SkASSERT(child->type().isEffectChild());
|
|
||||||
SkASSERT(child->is<VariableReference>());
|
|
||||||
|
|
||||||
auto fp_it = fVariableMap.find(child->as<VariableReference>().variable());
|
|
||||||
SkASSERT(fp_it != fVariableMap.end());
|
|
||||||
|
|
||||||
// Shaders require a coordinate argument. Color filters require a color argument.
|
|
||||||
// When we call sampleChild, the other value remains the incoming default.
|
|
||||||
const Expression* arg = c.arguments()[1].get();
|
|
||||||
Value argVal = this->writeExpression(*arg);
|
|
||||||
skvm::Color color;
|
|
||||||
|
|
||||||
switch (child->type().typeKind()) {
|
|
||||||
case Type::TypeKind::kShader: {
|
|
||||||
SkASSERT(nargs == 2);
|
|
||||||
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fFloat2);
|
|
||||||
skvm::Coord coord = {f32(argVal[0]), f32(argVal[1])};
|
|
||||||
color = fSampleShader(fp_it->second, coord);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Type::TypeKind::kColorFilter: {
|
|
||||||
SkASSERT(nargs == 2);
|
|
||||||
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
|
||||||
arg->type() == *fProgram.fContext->fTypes.fFloat4);
|
|
||||||
skvm::Color inColor = {f32(argVal[0]), f32(argVal[1]),
|
|
||||||
f32(argVal[2]), f32(argVal[3])};
|
|
||||||
color = fSampleColorFilter(fp_it->second, inColor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Type::TypeKind::kBlender: {
|
|
||||||
SkASSERT(nargs == 3);
|
|
||||||
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
|
||||||
arg->type() == *fProgram.fContext->fTypes.fFloat4);
|
|
||||||
skvm::Color srcColor = {f32(argVal[0]), f32(argVal[1]),
|
|
||||||
f32(argVal[2]), f32(argVal[3])};
|
|
||||||
|
|
||||||
arg = c.arguments()[2].get();
|
|
||||||
argVal = this->writeExpression(*arg);
|
|
||||||
SkASSERT(arg->type() == *fProgram.fContext->fTypes.fHalf4 ||
|
|
||||||
arg->type() == *fProgram.fContext->fTypes.fFloat4);
|
|
||||||
skvm::Color dstColor = {f32(argVal[0]), f32(argVal[1]),
|
|
||||||
f32(argVal[2]), f32(argVal[3])};
|
|
||||||
|
|
||||||
color = fSampleBlender(fp_it->second, srcColor, dstColor);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
SkDEBUGFAILF("cannot sample from type '%s'", child->type().description().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Value result(4);
|
|
||||||
result[0] = color.r;
|
|
||||||
result[1] = color.g;
|
|
||||||
result[2] = color.b;
|
|
||||||
result[3] = color.a;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t kMaxArgs = 3; // eg: clamp, mix, smoothstep
|
const size_t kMaxArgs = 3; // eg: clamp, mix, smoothstep
|
||||||
Value args[kMaxArgs];
|
Value args[kMaxArgs];
|
||||||
SkASSERT(nargs >= 1 && nargs <= SK_ARRAY_COUNT(args));
|
SkASSERT(nargs >= 1 && nargs <= SK_ARRAY_COUNT(args));
|
||||||
@ -1336,6 +1325,8 @@ Value SkVMGenerator::writeExpression(const Expression& e) {
|
|||||||
return this->writeBinaryExpression(e.as<BinaryExpression>());
|
return this->writeBinaryExpression(e.as<BinaryExpression>());
|
||||||
case Expression::Kind::kBoolLiteral:
|
case Expression::Kind::kBoolLiteral:
|
||||||
return fBuilder->splat(e.as<BoolLiteral>().value() ? ~0 : 0);
|
return fBuilder->splat(e.as<BoolLiteral>().value() ? ~0 : 0);
|
||||||
|
case Expression::Kind::kChildCall:
|
||||||
|
return this->writeChildCall(e.as<ChildCall>());
|
||||||
case Expression::Kind::kConstructorArray:
|
case Expression::Kind::kConstructorArray:
|
||||||
case Expression::Kind::kConstructorCompound:
|
case Expression::Kind::kConstructorCompound:
|
||||||
case Expression::Kind::kConstructorStruct:
|
case Expression::Kind::kConstructorStruct:
|
||||||
|
107
src/sksl/ir/SkSLChildCall.cpp
Normal file
107
src/sksl/ir/SkSLChildCall.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Google LLC
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "src/sksl/SkSLContext.h"
|
||||||
|
#include "src/sksl/ir/SkSLChildCall.h"
|
||||||
|
|
||||||
|
namespace SkSL {
|
||||||
|
|
||||||
|
bool ChildCall::hasProperty(Property property) const {
|
||||||
|
for (const auto& arg : this->arguments()) {
|
||||||
|
if (arg->hasProperty(property)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Expression> ChildCall::clone() const {
|
||||||
|
ExpressionArray cloned;
|
||||||
|
cloned.reserve_back(this->arguments().size());
|
||||||
|
for (const std::unique_ptr<Expression>& arg : this->arguments()) {
|
||||||
|
cloned.push_back(arg->clone());
|
||||||
|
}
|
||||||
|
return std::make_unique<ChildCall>(fOffset, &this->type(), &this->child(), std::move(cloned));
|
||||||
|
}
|
||||||
|
|
||||||
|
String ChildCall::description() const {
|
||||||
|
String result = String(this->child().name()) + "(";
|
||||||
|
String separator;
|
||||||
|
for (const std::unique_ptr<Expression>& arg : this->arguments()) {
|
||||||
|
result += separator;
|
||||||
|
result += arg->description();
|
||||||
|
separator = ", ";
|
||||||
|
}
|
||||||
|
result += ")";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ChildCallSignature {
|
||||||
|
const Type* fReturnType = nullptr;
|
||||||
|
SkSTArray<2, const Type*> fParamTypes;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ChildCallSignature child_call_signature(const Context& context, const Variable& child) {
|
||||||
|
const Type* half4 = context.fTypes.fHalf4.get();
|
||||||
|
const Type* float2 = context.fTypes.fFloat2.get();
|
||||||
|
|
||||||
|
switch (child.type().typeKind()) {
|
||||||
|
case Type::TypeKind::kBlender: return { half4, { half4, half4 } };
|
||||||
|
case Type::TypeKind::kColorFilter: return { half4, { half4 } };
|
||||||
|
case Type::TypeKind::kShader: return { half4, { float2 } };
|
||||||
|
default:
|
||||||
|
SkUNREACHABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Expression> ChildCall::Convert(const Context& context,
|
||||||
|
int offset,
|
||||||
|
const Variable& child,
|
||||||
|
ExpressionArray arguments) {
|
||||||
|
ChildCallSignature signature = child_call_signature(context, child);
|
||||||
|
skstd::string_view typeName = child.type().name();
|
||||||
|
|
||||||
|
// Reject function calls with the wrong number of arguments.
|
||||||
|
if (signature.fParamTypes.size() != arguments.size()) {
|
||||||
|
String msg = "call to '" + typeName + "' expected " +
|
||||||
|
to_string((int)signature.fParamTypes.size()) + " argument";
|
||||||
|
if (signature.fParamTypes.size() != 1) {
|
||||||
|
msg += "s";
|
||||||
|
}
|
||||||
|
msg += ", but found " + to_string(arguments.count());
|
||||||
|
context.errors().error(offset, msg);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arguments.size(); i++) {
|
||||||
|
// Coerce each argument to the proper type.
|
||||||
|
arguments[i] = signature.fParamTypes[i]->coerceExpression(std::move(arguments[i]), context);
|
||||||
|
if (!arguments[i]) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Make(context, offset, signature.fReturnType, child, std::move(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Expression> ChildCall::Make(const Context& context,
|
||||||
|
int offset,
|
||||||
|
const Type* returnType,
|
||||||
|
const Variable& child,
|
||||||
|
ExpressionArray arguments) {
|
||||||
|
#ifdef SK_DEBUG
|
||||||
|
ChildCallSignature signature = child_call_signature(context, child);
|
||||||
|
SkASSERT(signature.fParamTypes.size() == arguments.size());
|
||||||
|
for (size_t i = 0; i < arguments.size(); i++) {
|
||||||
|
SkASSERT(arguments[i]->type() == *signature.fParamTypes[i]);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return std::make_unique<ChildCall>(offset, returnType, &child, std::move(arguments));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SkSL
|
70
src/sksl/ir/SkSLChildCall.h
Normal file
70
src/sksl/ir/SkSLChildCall.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2021 Google LLC
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SKSL_CHILDCALL
|
||||||
|
#define SKSL_CHILDCALL
|
||||||
|
|
||||||
|
#include "include/private/SkTArray.h"
|
||||||
|
#include "src/sksl/ir/SkSLExpression.h"
|
||||||
|
#include "src/sksl/ir/SkSLVariable.h"
|
||||||
|
|
||||||
|
namespace SkSL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call to a child effect object (shader, color filter, or blender).
|
||||||
|
*/
|
||||||
|
class ChildCall final : public Expression {
|
||||||
|
public:
|
||||||
|
static constexpr Kind kExpressionKind = Kind::kChildCall;
|
||||||
|
|
||||||
|
ChildCall(int offset, const Type* type, const Variable* child, ExpressionArray arguments)
|
||||||
|
: INHERITED(offset, kExpressionKind, type)
|
||||||
|
, fChild(*child)
|
||||||
|
, fArguments(std::move(arguments)) {}
|
||||||
|
|
||||||
|
// Performs type conversion on arguments, determines return type, and reports errors via the
|
||||||
|
// ErrorReporter.
|
||||||
|
static std::unique_ptr<Expression> Convert(const Context& context,
|
||||||
|
int offset,
|
||||||
|
const Variable& child,
|
||||||
|
ExpressionArray arguments);
|
||||||
|
|
||||||
|
// Creates the child call; reports errors via ASSERT.
|
||||||
|
static std::unique_ptr<Expression> Make(const Context& context,
|
||||||
|
int offset,
|
||||||
|
const Type* returnType,
|
||||||
|
const Variable& child,
|
||||||
|
ExpressionArray arguments);
|
||||||
|
|
||||||
|
const Variable& child() const {
|
||||||
|
return fChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpressionArray& arguments() {
|
||||||
|
return fArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExpressionArray& arguments() const {
|
||||||
|
return fArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasProperty(Property property) const override;
|
||||||
|
|
||||||
|
std::unique_ptr<Expression> clone() const override;
|
||||||
|
|
||||||
|
String description() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Variable& fChild;
|
||||||
|
ExpressionArray fArguments;
|
||||||
|
|
||||||
|
using INHERITED = Expression;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace SkSL
|
||||||
|
|
||||||
|
#endif
|
@ -29,6 +29,7 @@ public:
|
|||||||
enum class Kind {
|
enum class Kind {
|
||||||
kBinary = (int) Statement::Kind::kLast + 1,
|
kBinary = (int) Statement::Kind::kLast + 1,
|
||||||
kBoolLiteral,
|
kBoolLiteral,
|
||||||
|
kChildCall,
|
||||||
kCodeString,
|
kCodeString,
|
||||||
kConstructorArray,
|
kConstructorArray,
|
||||||
kConstructorArrayCast,
|
kConstructorArrayCast,
|
||||||
|
@ -1948,7 +1948,7 @@ DEF_GPUTEST_FOR_MOCK_CONTEXT(DSLSampleShader, r, ctxInfo) {
|
|||||||
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), default_settings(),
|
AutoDSLContext context(ctxInfo.directContext()->priv().getGpu(), default_settings(),
|
||||||
SkSL::ProgramKind::kRuntimeShader);
|
SkSL::ProgramKind::kRuntimeShader);
|
||||||
DSLGlobalVar shader(kUniform_Modifier, kShader_Type, "shader");
|
DSLGlobalVar shader(kUniform_Modifier, kShader_Type, "shader");
|
||||||
EXPECT_EQUAL(Sample(shader, Float2(0, 0)), "sample(shader, float2(0.0, 0.0))");
|
EXPECT_EQUAL(Sample(shader, Float2(0, 0)), "shader(float2(0.0, 0.0))");
|
||||||
|
|
||||||
{
|
{
|
||||||
ExpectError error(r, "no match for sample(shader, half4)");
|
ExpectError error(r, "no match for sample(shader, half4)");
|
||||||
|
Loading…
Reference in New Issue
Block a user