sksl programs can now directly query GLSL caps
authorEthan Nicholas <ethannicholas@google.com>
Mon, 21 Nov 2016 20:59:48 +0000 (15:59 -0500)
committerSkia Commit-Bot <skia-commit-bot@chromium.org>
Tue, 22 Nov 2016 14:20:27 +0000 (14:20 +0000)
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>
src/sksl/README
src/sksl/SkSLCompiler.cpp
src/sksl/SkSLCompiler.h
src/sksl/SkSLContext.h
src/sksl/SkSLIRGenerator.cpp
src/sksl/SkSLIRGenerator.h
src/sksl/SkSLUtil.h
src/sksl/ir/SkSLModifiers.h
tests/SkSLErrorTest.cpp
tests/SkSLGLSLTest.cpp

index 6302075507edfdb97ee9151abe0bcfaf2b5f2596..0ab8a0500a10a29138475f276b42a4356690e376 100644 (file)
@@ -18,6 +18,19 @@ differences (for instance, you always use "in" and "out", and skslc will handle
 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 
index 905eff4e2869500de5ae0e79aacb94b21cc5670d..62e5fa72e5955f2ab8f74746c2691bb19ee9cae6 100644 (file)
@@ -135,6 +135,11 @@ Compiler::Compiler()
     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);
@@ -383,10 +388,11 @@ void Compiler::internalConvertProgram(SkString text,
     }
 }
 
-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) {
@@ -402,7 +408,7 @@ std::unique_ptr<Program> Compiler::convertProgram(Program::Kind kind, SkString t
     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;
 }
@@ -428,7 +434,8 @@ void Compiler::writeErrorCount() {
 }
 
 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);
@@ -446,9 +453,32 @@ bool Compiler::toSPIRV(Program::Kind kind, const SkString& text, SkString* 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);
index e1bc052fef3eaf0a88666e4a545aeefc15351bdd..5a06bfa264d4ab3f9fece9f965aee9c9202c4861 100644 (file)
@@ -15,6 +15,7 @@
 #include "SkSLCFGGenerator.h"
 #include "SkSLContext.h"
 #include "SkSLErrorReporter.h"
+#include "SkSLIRGenerator.h"
 #include "SkSLGLSLCodeGenerator.h"
 
 #define SK_FRAGCOLOR_BUILTIN 10001
@@ -37,7 +38,8 @@ public:
 
     ~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);
     
index 05e08f24cbec523d83a725f2609894184ccc6660..9289edeee5f0183147d8b3a952c6b3522b4f960b 100644 (file)
@@ -133,6 +133,7 @@ public:
                                                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) {
@@ -247,6 +248,8 @@ public:
 
     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)
index 245c72f5db2bc71016a4b36bbdaf6c1083926a31..aec4aab81f53da1f81095434199f06f4205c1673 100644 (file)
@@ -84,6 +84,7 @@ IRGenerator::IRGenerator(const Context* context, std::shared_ptr<SymbolTable> sy
                          ErrorReporter& errorReporter)
 : fContext(*context)
 , fCurrentFunction(nullptr)
+, fCapsMap(nullptr)
 , fSymbolTable(std::move(symbolTable))
 , fLoopLevel(0)
 , fErrors(errorReporter) {}
@@ -96,6 +97,16 @@ void IRGenerator::popSymbolTable() {
     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));
 }
@@ -1331,6 +1342,25 @@ std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expressi
     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);
@@ -1368,6 +1398,10 @@ std::unique_ptr<Expression> IRGenerator::convertSuffixExpression(
             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), 
index d7bd59ad028ffa5744edef34d0afffd9e792d44e..5b56689517323afd17e039b79d3a52c7222c77a1 100644 (file)
 
 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).
@@ -67,6 +89,17 @@ public:
                                                                   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();
 
@@ -105,6 +138,7 @@ private:
     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);
@@ -120,6 +154,7 @@ private:
 
     const Context& fContext;
     const FunctionDeclaration* fCurrentFunction;
+    const std::unordered_map<SkString, CapValue>* fCapsMap;
     std::shared_ptr<SymbolTable> fSymbolTable;
     int fLoopLevel;
     ErrorReporter& fErrors;
index ad8287e39fbcbf6473b4d6fca14074feb892ac24..fae1c8560631be9abc5a4b2dfcf09d412930d05c 100644 (file)
@@ -71,6 +71,17 @@ public:
         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);
index 2c9b3b39c911b46bb355e037e97c1b1c970156c1..fb26677c5217e65657cb7b0a66f82b1928fcad7e 100644 (file)
@@ -30,6 +30,10 @@ struct Modifiers {
         kNoPerspective_Flag = ASTModifiers::kNoPerspective_Flag
     };
 
+    Modifiers()
+    : fLayout(Layout())
+    , fFlags(0) {}
+
     Modifiers(const ASTModifiers& modifiers)
     : fLayout(modifiers.fLayout)
     , fFlags(modifiers.fFlags) {}
index c9e342c6d57abc7ff5ee495ff412847fab87a167..896b32ccb8912f1d53a5da1cf19a03eaa5814c67 100644 (file)
@@ -372,4 +372,10 @@ DEF_TEST(SkSLStaticIfError, r) {
                  "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
index 7af32bef6f86af85514f8102774e5c684e0a7dc8..4a9338113f2aa1145f3b599419e82ca6e1d4845c 100644 (file)
@@ -524,4 +524,27 @@ DEF_TEST(SkSLStaticIf, r) {
          "}\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