This adds support for querying "sk_Caps.<cap>" directly from within an SkSL program.
Combined with the existing support for collapsing 'if' statements with constant tests,
this means we can query caps using ordinary 'if' statements and the tests will
collapse out at compile time.
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4795
Change-Id: I24d716a7fe6abf1489760bf08189164264269076
Reviewed-on: https://skia-review.googlesource.com/4795
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Reviewed-by: Ben Wagner <benjaminwagner@google.com>
translating them to "varying" and "attribute" as appropriate). Be aware of the
following differences between SkSL and GLSL:
+* GLSL caps can be referenced via the syntax 'sk_Caps.<name>', e.g.
+ sk_Caps.sampleVariablesSupport. The value will be a constant boolean or int,
+ as appropriate. As SkSL supports constant folding and branch elimination, this
+ means that an 'if' statement which statically queries a cap will collapse down
+ to the chosen branch, meaning that:
+
+ if (sk_Caps.externalTextureSupport)
+ do_something();
+ else
+ do_something_else();
+
+ will compile as if you had written either 'do_something();' or
+ 'do_something_else();', depending on whether that cap is enabled or not.
* no #version statement is required, and will be ignored if present
* the output color is sk_FragColor (do not declare it)
* lowp, mediump, and highp are always permitted (but will only be respected if
ADD_TYPE(GSampler2DArrayShadow);
ADD_TYPE(GSamplerCubeArrayShadow);
+ SkString skCapsName("sk_Caps");
+ Variable* skCaps = new Variable(Position(), Modifiers(), skCapsName,
+ *fContext.fSkCaps_Type, Variable::kGlobal_Storage);
+ fIRGenerator->fSymbolTable->add(skCapsName, std::unique_ptr<Symbol>(skCaps));
+
Modifiers::Flag ignored1;
std::vector<std::unique_ptr<ProgramElement>> ignored2;
this->internalConvertProgram(SkString(SKSL_INCLUDE), &ignored1, &ignored2);
}
}
-std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, SkString text) {
+std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, SkString text,
+ std::unordered_map<SkString, CapValue> caps) {
fErrorText = "";
fErrorCount = 0;
- fIRGenerator->pushSymbolTable();
+ fIRGenerator->start(&caps);
std::vector<std::unique_ptr<ProgramElement>> elements;
Modifiers::Flag ignored;
switch (kind) {
this->internalConvertProgram(text, &defaultPrecision, &elements);
auto result = std::unique_ptr<Program>(new Program(kind, defaultPrecision, std::move(elements),
fIRGenerator->fSymbolTable));
- fIRGenerator->popSymbolTable();
+ fIRGenerator->finish();
this->writeErrorCount();
return result;
}
}
bool Compiler::toSPIRV(Program::Kind kind, const SkString& text, SkWStream& out) {
- auto program = this->convertProgram(kind, text);
+ std::unordered_map<SkString, CapValue> capsMap;
+ auto program = this->convertProgram(kind, text, capsMap);
if (fErrorCount == 0) {
SkSL::SPIRVCodeGenerator cg(&fContext);
cg.generateCode(*program.get(), out);
return result;
}
+static void fill_caps(const GrGLSLCaps& caps, std::unordered_map<SkString, CapValue>* capsMap) {
+#define CAP(name) capsMap->insert(std::make_pair(SkString(#name), CapValue(caps.name())));
+ CAP(fbFetchSupport);
+ CAP(fbFetchNeedsCustomOutput);
+ CAP(bindlessTextureSupport);
+ CAP(dropsTileOnZeroDivide);
+ CAP(flatInterpolationSupport);
+ CAP(noperspectiveInterpolationSupport);
+ CAP(multisampleInterpolationSupport);
+ CAP(sampleVariablesSupport);
+ CAP(sampleMaskOverrideCoverageSupport);
+ CAP(externalTextureSupport);
+ CAP(texelFetchSupport);
+ CAP(imageLoadStoreSupport);
+ CAP(mustEnableAdvBlendEqs);
+ CAP(mustEnableSpecificAdvBlendEqs);
+ CAP(mustDeclareFragmentShaderOutput);
+ CAP(canUseAnyFunctionInShader);
+#undef CAP
+}
+
bool Compiler::toGLSL(Program::Kind kind, const SkString& text, const GrGLSLCaps& caps,
SkWStream& out) {
- auto program = this->convertProgram(kind, text);
+ std::unordered_map<SkString, CapValue> capsMap;
+ fill_caps(caps, &capsMap);
+ auto program = this->convertProgram(kind, text, capsMap);
if (fErrorCount == 0) {
SkSL::GLSLCodeGenerator cg(&fContext, &caps);
cg.generateCode(*program.get(), out);
#include "SkSLCFGGenerator.h"
#include "SkSLContext.h"
#include "SkSLErrorReporter.h"
+#include "SkSLIRGenerator.h"
#include "SkSLGLSLCodeGenerator.h"
#define SK_FRAGCOLOR_BUILTIN 10001
~Compiler();
- std::unique_ptr<Program> convertProgram(Program::Kind kind, SkString text);
+ std::unique_ptr<Program> convertProgram(Program::Kind kind, SkString text,
+ std::unordered_map<SkString, CapValue> caps);
bool toSPIRV(Program::Kind kind, const SkString& text, SkWStream& out);
fUVec3_Type.get(), fUVec4_Type.get() }))
, fBVec_Type(new Type(SkString("$bvec"), { fInvalid_Type.get(), fBVec2_Type.get(),
fBVec3_Type.get(), fBVec4_Type.get() }))
+ , fSkCaps_Type(new Type(SkString("$sk_Caps")))
, fDefined_Expression(new Defined(*fInvalid_Type)) {}
static std::vector<const Type*> static_type(const Type& t) {
const std::unique_ptr<Type> fBVec_Type;
+ const std::unique_ptr<Type> fSkCaps_Type;
+
// dummy expression used to mark that a variable has a value during dataflow analysis (when it
// could have several different values, or the analyzer is otherwise unable to assign it a
// specific expression)
ErrorReporter& errorReporter)
: fContext(*context)
, fCurrentFunction(nullptr)
+, fCapsMap(nullptr)
, fSymbolTable(std::move(symbolTable))
, fLoopLevel(0)
, fErrors(errorReporter) {}
fSymbolTable = fSymbolTable->fParent;
}
+void IRGenerator::start(std::unordered_map<SkString, CapValue>* caps) {
+ this->fCapsMap = caps;
+ this->pushSymbolTable();
+}
+
+void IRGenerator::finish() {
+ this->popSymbolTable();
+ this->fCapsMap = nullptr;
+}
+
std::unique_ptr<Extension> IRGenerator::convertExtension(const ASTExtension& extension) {
return std::unique_ptr<Extension>(new Extension(extension.fPosition, extension.fName));
}
return std::unique_ptr<Expression>(new Swizzle(fContext, std::move(base), swizzleComponents));
}
+std::unique_ptr<Expression> IRGenerator::getCap(Position position, SkString name) {
+ ASSERT(fCapsMap);
+ auto found = fCapsMap->find(name);
+ if (found == fCapsMap->end()) {
+ fErrors.error(position, "unknown capability flag '" + name + "'");
+ return nullptr;
+ }
+ switch (found->second.fKind) {
+ case CapValue::kBool_Kind:
+ return std::unique_ptr<Expression>(new BoolLiteral(fContext, position,
+ (bool) found->second.fValue));
+ case CapValue::kInt_Kind:
+ return std::unique_ptr<Expression>(new IntLiteral(fContext, position,
+ found->second.fValue));
+ }
+ ASSERT(false);
+ return nullptr;
+}
+
std::unique_ptr<Expression> IRGenerator::convertSuffixExpression(
const ASTSuffixExpression& expression) {
std::unique_ptr<Expression> base = this->convertExpression(*expression.fBase);
return this->call(expression.fPosition, std::move(base), std::move(arguments));
}
case ASTSuffix::kField_Kind: {
+ if (base->fType == *fContext.fSkCaps_Type) {
+ return this->getCap(expression.fPosition,
+ ((ASTFieldSuffix&) *expression.fSuffix).fField);
+ }
switch (base->fType.kind()) {
case Type::kVector_Kind:
return this->convertSwizzle(std::move(base),
namespace SkSL {
+struct CapValue {
+ CapValue()
+ : fKind(kInt_Kind)
+ , fValue(-1) {
+ ASSERT(false);
+ }
+
+ CapValue(bool b)
+ : fKind(kBool_Kind)
+ , fValue(b) {}
+
+ CapValue(int i)
+ : fKind(kInt_Kind)
+ , fValue(i) {}
+
+ enum {
+ kBool_Kind,
+ kInt_Kind,
+ } fKind;
+ int fValue;
+};
+
/**
* Performs semantic analysis on an abstract syntax tree (AST) and produces the corresponding
* (unoptimized) intermediate representation (IR).
const ASTModifiersDeclaration& m);
private:
+ /**
+ * Prepare to compile a program. Pushes a new symbol table and installs the caps so that
+ * references to sk_Caps.<cap> can be resolved.
+ */
+ void start(std::unordered_map<SkString, CapValue>* caps);
+
+ /**
+ * Performs cleanup after compilation is complete.
+ */
+ void finish();
+
void pushSymbolTable();
void popSymbolTable();
Modifiers convertModifiers(const ASTModifiers& m);
std::unique_ptr<Expression> convertPrefixExpression(const ASTPrefixExpression& expression);
std::unique_ptr<Statement> convertReturn(const ASTReturnStatement& r);
+ std::unique_ptr<Expression> getCap(Position position, SkString name);
std::unique_ptr<Expression> convertSuffixExpression(const ASTSuffixExpression& expression);
std::unique_ptr<Expression> convertField(std::unique_ptr<Expression> base,
const SkString& field);
const Context& fContext;
const FunctionDeclaration* fCurrentFunction;
+ const std::unordered_map<SkString, CapValue>* fCapsMap;
std::shared_ptr<SymbolTable> fSymbolTable;
int fLoopLevel;
ErrorReporter& fErrors;
result->fShaderDerivativeExtensionString = "GL_OES_standard_derivatives";
return result;
}
+
+ static sk_sp<GrGLSLCaps> VariousCaps() {
+ sk_sp<GrGLSLCaps> result = sk_make_sp<GrGLSLCaps>(GrContextOptions());
+ result->fVersionDeclString = "#version 400";
+ result->fExternalTextureSupport = true;
+ result->fFBFetchSupport = false;
+ result->fDropsTileOnZeroDivide = true;
+ result->fTexelFetchSupport = true;
+ result->fCanUseAnyFunctionInShader = false;
+ return result;
+ }
};
void write_data(const SkData& d, SkWStream& out);
kNoPerspective_Flag = ASTModifiers::kNoPerspective_Flag
};
+ Modifiers()
+ : fLayout(Layout())
+ , fFlags(0) {}
+
Modifiers(const ASTModifiers& modifiers)
: fLayout(modifiers.fLayout)
, fFlags(modifiers.fFlags) {}
"error: 1: unknown identifier 'x'\n1 error\n");
}
+DEF_TEST(SkSLBadCap, r) {
+ test_failure(r,
+ "bool b = sk_Caps.bugFreeDriver;",
+ "error: 1: unknown capability flag 'bugFreeDriver'\n1 error\n");
+}
+
#endif
"}\n");
}
+DEF_TEST(SkSLCaps, r) {
+ test(r,
+ "void main() {"
+ "int x;"
+ "if (sk_Caps.externalTextureSupport) x = 1;"
+ "if (sk_Caps.fbFetchSupport) x = 2;"
+ "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.texelFetchSupport) x = 3;"
+ "if (sk_Caps.dropsTileOnZeroDivide && sk_Caps.canUseAnyFunctionInShader) x = 4;"
+ "}",
+ *SkSL::GLSLCapsFactory::VariousCaps(),
+ "#version 400\n"
+ "out vec4 sk_FragColor;\n"
+ "void main() {\n"
+ " int x;\n"
+ " x = 1;\n"
+ " {\n"
+ " }\n"
+ " x = 3;\n"
+ " {\n"
+ " }\n"
+ "}\n");
+}
+
#endif