fixed sksl static ifs to work for CircleEffect

static ifs (and switches) in .fp files are a bit tricky, because they
aren't necessarily static when the CPP file is being produced. They
become static when the CPP file produces the final SkSL; at this point
the final values of the 'in' variables are known.

This change permits 'deferred' static ifs and switches. The initial
compilation (.fp -> .cpp) passes the @if / @switch through, and then
the final compilation (.cpp's generated SkSL -> GLSL or whatever)
enforces the static test.

Bug: skia:
Change-Id: I0087dfe1725c8fd350507ac77f64db1d82659cdf
Reviewed-on: https://skia-review.googlesource.com/23403
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
This commit is contained in:
Ethan Nicholas 2017-07-14 10:12:15 -04:00 committed by Skia Commit-Bot
parent 9a03642ad2
commit 6e1cbc012b
8 changed files with 47 additions and 17 deletions

View File

@ -25,7 +25,7 @@ public:
(void) _outer;
prevRadius = -1.0;
fCircleVar = args.fUniformHandler->addUniform(kFragment_GrShaderFlag, kVec4f_GrSLType, kDefault_GrSLPrecision, "circle");
fragBuilder->codeAppendf("vec2 prevCenter;\nfloat prevRadius = %f;\nfloat d;\nif (%d == 2 || %d == 3) {\n d = (length((%s.xy - sk_FragCoord.xy) * %s.w) - 1.0) * %s.z;\n} else {\n d = (1.0 - length((%s.xy - sk_FragCoord.xy) * %s.w)) * %s.z;\n}\nif ((%d == 1 || %d == 3) || %d == 4) {\n d = clamp(d, 0.0, 1.0);\n} else {\n d = d > 0.5 ? 1.0 : 0.0;\n}\n%s = %s * d;\n", prevRadius, _outer.edgeType(), _outer.edgeType(), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), _outer.edgeType(), _outer.edgeType(), _outer.edgeType(), args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)");
fragBuilder->codeAppendf("vec2 prevCenter;\nfloat prevRadius = %f;\nfloat d;\n@if (%d == 2 || %d == 3) {\n d = (length((%s.xy - sk_FragCoord.xy) * %s.w) - 1.0) * %s.z;\n} else {\n d = (1.0 - length((%s.xy - sk_FragCoord.xy) * %s.w)) * %s.z;\n}\n@if ((%d == 1 || %d == 3) || %d == 4) {\n d = clamp(d, 0.0, 1.0);\n} else {\n d = d > 0.5 ? 1.0 : 0.0;\n}\n%s = %s * d;\n", prevRadius, _outer.edgeType(), _outer.edgeType(), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), args.fUniformHandler->getUniformCStr(fCircleVar), _outer.edgeType(), _outer.edgeType(), _outer.edgeType(), args.fOutputColor, args.fInputColor ? args.fInputColor : "vec4(1)");
}
private:
void onSetData(const GrGLSLProgramDataManager& pdman, const GrFragmentProcessor& _proc) override {

View File

@ -37,15 +37,15 @@ void main() {
// radius and then denormalized. This is to prevent overflow on devices that have a "real"
// mediump. It'd be nice to only do this on mediump devices.
float d;
if (edgeType == 2 /* kInverseFillBW_GrProcessorEdgeType */ ||
edgeType == 3 /* kInverseFillAA_GrProcessorEdgeType */) {
@if (edgeType == 2 /* kInverseFillBW_GrProcessorEdgeType */ ||
edgeType == 3 /* kInverseFillAA_GrProcessorEdgeType */) {
d = (length((circle.xy - sk_FragCoord.xy) * circle.w) - 1.0) * circle.z;
} else {
d = (1.0 - length((circle.xy - sk_FragCoord.xy) * circle.w)) * circle.z;
}
if (edgeType == 1 /* kFillAA_GrProcessorEdgeType */ ||
edgeType == 3 /* kInverseFillAA_GrProcessorEdgeType */ ||
edgeType == 4 /* kHairlineAA_GrProcessorEdgeType */) {
@if (edgeType == 1 /* kFillAA_GrProcessorEdgeType */ ||
edgeType == 3 /* kInverseFillAA_GrProcessorEdgeType */ ||
edgeType == 4 /* kHairlineAA_GrProcessorEdgeType */) {
d = clamp(d, 0.0, 1.0);
} else {
d = d > 0.5 ? 1.0 : 0.0;

View File

@ -225,6 +225,20 @@ void CPPCodeGenerator::writeVariableReference(const VariableReference& ref) {
}
}
void CPPCodeGenerator::writeIfStatement(const IfStatement& s) {
if (s.fIsStatic) {
this->write("@");
}
INHERITED::writeIfStatement(s);
}
void CPPCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
if (s.fIsStatic) {
this->write("@");
}
INHERITED::writeSwitchStatement(s);
}
void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) {
if (c.fFunction.fBuiltin && c.fFunction.fName == "COLORSPACE") {
String tmpVar = "_tmpVar" + to_string(++fVarCount);

View File

@ -41,6 +41,10 @@ private:
String getSamplerHandle(const Variable& var);
void writeIfStatement(const IfStatement& s) override;
void writeSwitchStatement(const SwitchStatement& s) override;
void writeFunctionCall(const FunctionCall& c) override;
void writeFunction(const FunctionDefinition& f) override;

View File

@ -54,8 +54,9 @@ static const char* SKSL_FP_INCLUDE =
namespace SkSL {
Compiler::Compiler()
: fErrorCount(0) {
Compiler::Compiler(Flags flags)
: fFlags(flags)
, fErrorCount(0) {
auto types = std::shared_ptr<SymbolTable>(new SymbolTable(this));
auto symbols = std::shared_ptr<SymbolTable>(new SymbolTable(types, this));
fIRGenerator = new IRGenerator(&fContext, symbols, *this);
@ -931,7 +932,7 @@ void Compiler::simplifyStatement(DefinitionMap& definitions,
(*iter)->setStatement(std::move(newBlock));
break;
} else {
if (s.fIsStatic) {
if (s.fIsStatic && !(fFlags & kPermitInvalidStaticTests_Flag)) {
this->error(s.fPosition,
"static switch contains non-static conditional break");
s.fIsStatic = false;
@ -947,7 +948,7 @@ void Compiler::simplifyStatement(DefinitionMap& definitions,
if (newBlock) {
(*iter)->setStatement(std::move(newBlock));
} else {
if (s.fIsStatic) {
if (s.fIsStatic && !(fFlags & kPermitInvalidStaticTests_Flag)) {
this->error(s.fPosition,
"static switch contains non-static conditional break");
s.fIsStatic = false;
@ -1047,13 +1048,15 @@ void Compiler::scanCFG(FunctionDefinition& f) {
const Statement& s = **iter->statement();
switch (s.fKind) {
case Statement::kIf_Kind:
if (((const IfStatement&) s).fIsStatic) {
if (((const IfStatement&) s).fIsStatic &&
!(fFlags & kPermitInvalidStaticTests_Flag)) {
this->error(s.fPosition, "static if has non-static test");
}
++iter;
break;
case Statement::kSwitch_Kind:
if (((const SwitchStatement&) s).fIsStatic) {
if (((const SwitchStatement&) s).fIsStatic &&
!(fFlags & kPermitInvalidStaticTests_Flag)) {
this->error(s.fPosition, "static switch has non-static test");
}
++iter;

View File

@ -43,7 +43,15 @@ class IRGenerator;
*/
class Compiler : public ErrorReporter {
public:
Compiler();
enum Flags {
kNone_Flags = 0,
// permits static if/switch statements to be used with non-constant tests. This is used when
// producing H and CPP code; the static tests don't have to have constant values *yet*, but
// the generated code will contain a static test which then does have to be a constant.
kPermitInvalidStaticTests_Flag = 1,
};
Compiler(Flags flags = kNone_Flags);
~Compiler() override;
@ -109,6 +117,7 @@ private:
std::shared_ptr<SymbolTable> fTypes;
IRGenerator* fIRGenerator;
String fSkiaVertText; // FIXME store parsed version instead
int fFlags;
Context fContext;
int fErrorCount;

View File

@ -162,7 +162,7 @@ protected:
void writeBlock(const Block& b);
void writeIfStatement(const IfStatement& stmt);
virtual void writeIfStatement(const IfStatement& stmt);
void writeForStatement(const ForStatement& f);
@ -170,7 +170,7 @@ protected:
void writeDoStatement(const DoStatement& d);
void writeSwitchStatement(const SwitchStatement& s);
virtual void writeSwitchStatement(const SwitchStatement& s);
void writeReturnStatement(const ReturnStatement& r);

View File

@ -95,7 +95,7 @@ int main(int argc, const char** argv) {
}
} else if (name.endsWith(".h")) {
SkSL::FileOutputStream out(argv[2]);
SkSL::Compiler compiler;
SkSL::Compiler compiler(SkSL::Compiler::kPermitInvalidStaticTests_Flag);
if (!out.isValid()) {
printf("error writing '%s'\n", argv[2]);
exit(4);
@ -112,7 +112,7 @@ int main(int argc, const char** argv) {
}
} else if (name.endsWith(".cpp")) {
SkSL::FileOutputStream out(argv[2]);
SkSL::Compiler compiler;
SkSL::Compiler compiler(SkSL::Compiler::kPermitInvalidStaticTests_Flag);
if (!out.isValid()) {
printf("error writing '%s'\n", argv[2]);
exit(4);