/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SKSL_SPIRVCODEGENERATOR #define SKSL_SPIRVCODEGENERATOR #include <stack> #include <tuple> #include <unordered_map> #include "SkSLCodeGenerator.h" #include "SkSLMemoryLayout.h" #include "ir/SkSLBinaryExpression.h" #include "ir/SkSLBoolLiteral.h" #include "ir/SkSLConstructor.h" #include "ir/SkSLDoStatement.h" #include "ir/SkSLFloatLiteral.h" #include "ir/SkSLIfStatement.h" #include "ir/SkSLIndexExpression.h" #include "ir/SkSLInterfaceBlock.h" #include "ir/SkSLIntLiteral.h" #include "ir/SkSLFieldAccess.h" #include "ir/SkSLForStatement.h" #include "ir/SkSLFunctionCall.h" #include "ir/SkSLFunctionDeclaration.h" #include "ir/SkSLFunctionDefinition.h" #include "ir/SkSLPrefixExpression.h" #include "ir/SkSLPostfixExpression.h" #include "ir/SkSLProgramElement.h" #include "ir/SkSLReturnStatement.h" #include "ir/SkSLStatement.h" #include "ir/SkSLSwitchStatement.h" #include "ir/SkSLSwizzle.h" #include "ir/SkSLTernaryExpression.h" #include "ir/SkSLVarDeclarations.h" #include "ir/SkSLVarDeclarationsStatement.h" #include "ir/SkSLVariableReference.h" #include "ir/SkSLWhileStatement.h" #include "spirv.h" namespace SkSL { #define kLast_Capability SpvCapabilityMultiViewport /** * Converts a Program into a SPIR-V binary. */ class SPIRVCodeGenerator : public CodeGenerator { public: class LValue { public: virtual ~LValue() {} // returns a pointer to the lvalue, if possible. If the lvalue cannot be directly referenced // by a pointer (e.g. vector swizzles), returns 0. virtual SpvId getPointer() = 0; virtual SpvId load(OutputStream& out) = 0; virtual void store(SpvId value, OutputStream& out) = 0; }; SPIRVCodeGenerator(const Context* context, const Program* program, ErrorReporter* errors, OutputStream* out) : INHERITED(program, errors, out) , fContext(*context) , fDefaultLayout(MemoryLayout::k140_Standard) , fCapabilities(1 << SpvCapabilityShader) , fIdCount(1) , fBoolTrue(0) , fBoolFalse(0) , fSetupFragPosition(false) , fCurrentBlock(0) , fSynthetics(nullptr, errors) { this->setupIntrinsics(); } bool generateCode() override; private: enum IntrinsicKind { kGLSL_STD_450_IntrinsicKind, kSPIRV_IntrinsicKind, kSpecial_IntrinsicKind }; enum SpecialIntrinsic { kAtan_SpecialIntrinsic, kClamp_SpecialIntrinsic, kMax_SpecialIntrinsic, kMin_SpecialIntrinsic, kMix_SpecialIntrinsic, kMod_SpecialIntrinsic, kSubpassLoad_SpecialIntrinsic, kTexelFetch_SpecialIntrinsic, kTexture_SpecialIntrinsic, }; void setupIntrinsics(); SpvId nextId(); Type getActualType(const Type& type); SpvId getType(const Type& type); SpvId getType(const Type& type, const MemoryLayout& layout); SpvId getImageType(const Type& type); SpvId getFunctionType(const FunctionDeclaration& function); SpvId getPointerType(const Type& type, SpvStorageClass_ storageClass); SpvId getPointerType(const Type& type, const MemoryLayout& layout, SpvStorageClass_ storageClass); void writePrecisionModifier(const Modifiers& modifiers, SpvId id); std::vector<SpvId> getAccessChain(const Expression& expr, OutputStream& out); void writeLayout(const Layout& layout, SpvId target); void writeLayout(const Layout& layout, SpvId target, int member); void writeStruct(const Type& type, const MemoryLayout& layout, SpvId resultId); void writeProgramElement(const ProgramElement& pe, OutputStream& out); SpvId writeInterfaceBlock(const InterfaceBlock& intf); SpvId writeFunctionStart(const FunctionDeclaration& f, OutputStream& out); SpvId writeFunctionDeclaration(const FunctionDeclaration& f, OutputStream& out); SpvId writeFunction(const FunctionDefinition& f, OutputStream& out); void writeGlobalVars(Program::Kind kind, const VarDeclarations& v, OutputStream& out); void writeVarDeclarations(const VarDeclarations& decl, OutputStream& out); SpvId writeVariableReference(const VariableReference& ref, OutputStream& out); std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out); SpvId writeExpression(const Expression& expr, OutputStream& out); SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out); SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out); void writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst, SpvId signedInst, SpvId unsignedInst, const std::vector<SpvId>& args, OutputStream& out); /** * Given a list of potentially mixed scalars and vectors, promotes the scalars to match the * size of the vectors and returns the ids of the written expressions. e.g. given (float, vec2), * returns (vec2(float), vec2). It is an error to use mismatched vector sizes, e.g. (float, * vec2, vec3). */ std::vector<SpvId> vectorize(const std::vector<std::unique_ptr<Expression>>& args, OutputStream& out); SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, OutputStream& out); SpvId writeConstantVector(const Constructor& c); SpvId writeFloatConstructor(const Constructor& c, OutputStream& out); SpvId writeIntConstructor(const Constructor& c, OutputStream& out); SpvId writeUIntConstructor(const Constructor& c, OutputStream& out); /** * Writes a matrix with the diagonal entries all equal to the provided expression, and all other * entries equal to zero. */ void writeUniformScaleMatrix(SpvId id, SpvId diagonal, const Type& type, OutputStream& out); /** * Writes a potentially-different-sized copy of a matrix. Entries which do not exist in the * source matrix are filled with zero; entries which do not exist in the destination matrix are * ignored. */ void writeMatrixCopy(SpvId id, SpvId src, const Type& srcType, const Type& dstType, OutputStream& out); SpvId writeMatrixConstructor(const Constructor& c, OutputStream& out); SpvId writeVectorConstructor(const Constructor& c, OutputStream& out); SpvId writeArrayConstructor(const Constructor& c, OutputStream& out); SpvId writeConstructor(const Constructor& c, OutputStream& out); SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out); SpvId writeSwizzle(const Swizzle& swizzle, OutputStream& out); /** * Folds the potentially-vector result of a logical operation down to a single bool. If * operandType is a vector type, assumes that the intermediate result in id is a bvec of the * same dimensions, and applys all() to it to fold it down to a single bool value. Otherwise, * returns the original id value. */ SpvId foldToBool(SpvId id, const Type& operandType, OutputStream& out); SpvId writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator, SpvOp_ intOperator, OutputStream& out); SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, SpvOp_ ifBool, OutputStream& out); SpvId writeBinaryOperation(const BinaryExpression& expr, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt, OutputStream& out); SpvId writeBinaryExpression(const BinaryExpression& b, OutputStream& out); SpvId writeTernaryExpression(const TernaryExpression& t, OutputStream& out); SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out); SpvId writeLogicalAnd(const BinaryExpression& b, OutputStream& out); SpvId writeLogicalOr(const BinaryExpression& o, OutputStream& out); SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out); SpvId writePostfixExpression(const PostfixExpression& p, OutputStream& out); SpvId writeBoolLiteral(const BoolLiteral& b); SpvId writeIntLiteral(const IntLiteral& i); SpvId writeFloatLiteral(const FloatLiteral& f); void writeStatement(const Statement& s, OutputStream& out); void writeBlock(const Block& b, OutputStream& out); void writeIfStatement(const IfStatement& stmt, OutputStream& out); void writeForStatement(const ForStatement& f, OutputStream& out); void writeWhileStatement(const WhileStatement& w, OutputStream& out); void writeDoStatement(const DoStatement& d, OutputStream& out); void writeSwitchStatement(const SwitchStatement& s, OutputStream& out); void writeReturnStatement(const ReturnStatement& r, OutputStream& out); void writeCapabilities(OutputStream& out); void writeInstructions(const Program& program, OutputStream& out); void writeOpCode(SpvOp_ opCode, int length, OutputStream& out); void writeWord(int32_t word, OutputStream& out); void writeString(const char* string, size_t length, OutputStream& out); void writeLabel(SpvId id, OutputStream& out); void writeInstruction(SpvOp_ opCode, OutputStream& out); void writeInstruction(SpvOp_ opCode, StringFragment string, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, StringFragment string, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, StringFragment string, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, int32_t word6, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, int32_t word6, int32_t word7, OutputStream& out); void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4, int32_t word5, int32_t word6, int32_t word7, int32_t word8, OutputStream& out); void writeGeometryShaderExecutionMode(SpvId entryPoint, OutputStream& out); const Context& fContext; const MemoryLayout fDefaultLayout; uint64_t fCapabilities; SpvId fIdCount; SpvId fGLSLExtendedInstructions; typedef std::tuple<IntrinsicKind, int32_t, int32_t, int32_t, int32_t> Intrinsic; std::unordered_map<String, Intrinsic> fIntrinsicMap; std::unordered_map<const FunctionDeclaration*, SpvId> fFunctionMap; std::unordered_map<const Variable*, SpvId> fVariableMap; std::unordered_map<const Variable*, int32_t> fInterfaceBlockMap; std::unordered_map<String, SpvId> fImageTypeMap; std::unordered_map<String, SpvId> fTypeMap; StringStream fCapabilitiesBuffer; StringStream fGlobalInitializersBuffer; StringStream fConstantBuffer; StringStream fExtraGlobalsBuffer; StringStream fExternalFunctionsBuffer; StringStream fVariableBuffer; StringStream fNameBuffer; StringStream fDecorationBuffer; SpvId fBoolTrue; SpvId fBoolFalse; std::unordered_map<int64_t, SpvId> fIntConstants; std::unordered_map<uint64_t, SpvId> fUIntConstants; std::unordered_map<float, SpvId> fFloatConstants; std::unordered_map<double, SpvId> fDoubleConstants; bool fSetupFragPosition; // label of the current block, or 0 if we are not in a block SpvId fCurrentBlock; std::stack<SpvId> fBreakTarget; std::stack<SpvId> fContinueTarget; SpvId fRTHeightStructId = (SpvId) -1; SpvId fRTHeightFieldIndex = (SpvId) -1; // holds variables synthesized during output, for lifetime purposes SymbolTable fSynthetics; friend class PointerLValue; friend class SwizzleLValue; typedef CodeGenerator INHERITED; }; } #endif