| /* |
| * Copyright 2020 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/SkSLDehydrator.h" |
| |
| #include "include/core/SkSpan.h" |
| #include "include/private/SkSLDefines.h" |
| #include "include/private/SkSLLayout.h" |
| #include "include/private/SkSLModifiers.h" |
| #include "include/private/SkSLProgramElement.h" |
| #include "include/private/SkSLStatement.h" |
| #include "include/private/SkSLSymbol.h" |
| #include "include/private/SkTArray.h" |
| #include "include/sksl/SkSLOperator.h" |
| #include "src/sksl/SkSLOutputStream.h" |
| #include "src/sksl/SkSLProgramSettings.h" |
| #include "src/sksl/SkSLRehydrator.h" |
| #include "src/sksl/ir/SkSLBinaryExpression.h" |
| #include "src/sksl/ir/SkSLBlock.h" |
| #include "src/sksl/ir/SkSLConstructorArray.h" |
| #include "src/sksl/ir/SkSLConstructorArrayCast.h" |
| #include "src/sksl/ir/SkSLConstructorCompound.h" |
| #include "src/sksl/ir/SkSLConstructorCompoundCast.h" |
| #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h" |
| #include "src/sksl/ir/SkSLConstructorMatrixResize.h" |
| #include "src/sksl/ir/SkSLConstructorScalarCast.h" |
| #include "src/sksl/ir/SkSLConstructorSplat.h" |
| #include "src/sksl/ir/SkSLConstructorStruct.h" |
| #include "src/sksl/ir/SkSLDoStatement.h" |
| #include "src/sksl/ir/SkSLExpression.h" |
| #include "src/sksl/ir/SkSLExpressionStatement.h" |
| #include "src/sksl/ir/SkSLField.h" |
| #include "src/sksl/ir/SkSLFieldAccess.h" |
| #include "src/sksl/ir/SkSLForStatement.h" |
| #include "src/sksl/ir/SkSLFunctionCall.h" |
| #include "src/sksl/ir/SkSLFunctionDeclaration.h" |
| #include "src/sksl/ir/SkSLFunctionDefinition.h" |
| #include "src/sksl/ir/SkSLFunctionPrototype.h" |
| #include "src/sksl/ir/SkSLIfStatement.h" |
| #include "src/sksl/ir/SkSLIndexExpression.h" |
| #include "src/sksl/ir/SkSLInterfaceBlock.h" |
| #include "src/sksl/ir/SkSLLiteral.h" |
| #include "src/sksl/ir/SkSLPostfixExpression.h" |
| #include "src/sksl/ir/SkSLPrefixExpression.h" |
| #include "src/sksl/ir/SkSLProgram.h" |
| #include "src/sksl/ir/SkSLReturnStatement.h" |
| #include "src/sksl/ir/SkSLSetting.h" |
| #include "src/sksl/ir/SkSLStructDefinition.h" |
| #include "src/sksl/ir/SkSLSwitchCase.h" |
| #include "src/sksl/ir/SkSLSwitchStatement.h" |
| #include "src/sksl/ir/SkSLSwizzle.h" |
| #include "src/sksl/ir/SkSLSymbolTable.h" |
| #include "src/sksl/ir/SkSLTernaryExpression.h" |
| #include "src/sksl/ir/SkSLType.h" |
| #include "src/sksl/ir/SkSLUnresolvedFunction.h" |
| #include "src/sksl/ir/SkSLVarDeclarations.h" |
| #include "src/sksl/ir/SkSLVariable.h" |
| #include "src/sksl/ir/SkSLVariableReference.h" |
| |
| #include <map> |
| |
| namespace SkSL { |
| |
| static constexpr int HEADER_SIZE = 2; |
| |
| class AutoDehydratorSymbolTable { |
| public: |
| AutoDehydratorSymbolTable(Dehydrator* dehydrator, const std::shared_ptr<SymbolTable>& symbols) |
| : fDehydrator(dehydrator) { |
| dehydrator->fSymbolMap.emplace_back(); |
| if (symbols) { |
| dehydrator->write(*symbols); |
| } else { |
| dehydrator->writeCommand(Rehydrator::kVoid_Command); |
| } |
| } |
| |
| ~AutoDehydratorSymbolTable() { |
| fDehydrator->fSymbolMap.pop_back(); |
| } |
| |
| private: |
| Dehydrator* fDehydrator; |
| }; |
| |
| void Dehydrator::writeId(const Symbol* s) { |
| uint16_t id = this->symbolId(s); |
| if (id) { |
| this->writeU16(id); |
| } else { |
| this->writeU16(Rehydrator::kBuiltin_Symbol); |
| this->write(s->name()); |
| } |
| } |
| |
| void Dehydrator::write(Layout l) { |
| if (l == Layout()) { |
| this->writeCommand(Rehydrator::kDefaultLayout_Command); |
| } else if (l == Layout::builtin(l.fBuiltin)) { |
| this->writeCommand(Rehydrator::kBuiltinLayout_Command); |
| this->writeS16(l.fBuiltin); |
| } else { |
| this->writeCommand(Rehydrator::kLayout_Command); |
| fBody.write32(l.fFlags); |
| this->writeS8(l.fLocation); |
| this->writeS16(l.fOffset); |
| this->writeS16(l.fBinding); |
| this->writeS8(l.fIndex); |
| this->writeS8(l.fSet); |
| this->writeS16(l.fBuiltin); |
| this->writeS8(l.fInputAttachmentIndex); |
| } |
| } |
| |
| void Dehydrator::write(Modifiers m) { |
| if (m == Modifiers()) { |
| this->writeCommand(Rehydrator::kDefaultModifiers_Command); |
| } else { |
| if (m.fFlags <= 255) { |
| this->writeCommand(Rehydrator::kModifiers8Bit_Command); |
| this->write(m.fLayout); |
| this->writeU8(m.fFlags); |
| } else { |
| this->writeCommand(Rehydrator::kModifiers_Command); |
| this->write(m.fLayout); |
| this->writeS32(m.fFlags); |
| } |
| } |
| } |
| |
| void Dehydrator::write(std::string_view s) { |
| this->write(std::string(s)); |
| } |
| |
| void Dehydrator::write(std::string s) { |
| auto found = fStrings.find(s); |
| int offset; |
| if (found == fStrings.end()) { |
| offset = fStringBuffer.bytesWritten() + HEADER_SIZE; |
| fStrings.insert({ s, offset }); |
| SkASSERT(s.length() <= 255); |
| fStringBreaks.add(fStringBuffer.bytesWritten()); |
| fStringBuffer.write8(s.length()); |
| fStringBuffer.writeString(s); |
| } else { |
| offset = found->second; |
| } |
| this->writeU16(offset); |
| } |
| |
| void Dehydrator::write(const Symbol& s) { |
| uint16_t id = this->symbolId(&s); |
| if (id) { |
| this->writeCommand(Rehydrator::kSymbolRef_Command); |
| this->writeU16(id); |
| return; |
| } |
| switch (s.kind()) { |
| case Symbol::Kind::kFunctionDeclaration: { |
| this->allocSymbolId(&s); |
| const FunctionDeclaration& f = s.as<FunctionDeclaration>(); |
| this->writeCommand(Rehydrator::kFunctionDeclaration_Command); |
| this->writeId(&f); |
| this->write(f.modifiers()); |
| this->write(f.name()); |
| this->writeU8(f.parameters().size()); |
| for (const Variable* p : f.parameters()) { |
| this->writeU16(this->symbolId(p)); |
| } |
| this->write(f.returnType()); |
| break; |
| } |
| case Symbol::Kind::kUnresolvedFunction: { |
| this->allocSymbolId(&s); |
| const UnresolvedFunction& f = s.as<UnresolvedFunction>(); |
| this->writeCommand(Rehydrator::kUnresolvedFunction_Command); |
| this->writeId(&f); |
| this->writeU8(f.functions().size()); |
| for (const FunctionDeclaration* funcDecl : f.functions()) { |
| this->write(*funcDecl); |
| } |
| break; |
| } |
| case Symbol::Kind::kType: { |
| const Type& t = s.as<Type>(); |
| switch (t.typeKind()) { |
| case Type::TypeKind::kArray: |
| this->allocSymbolId(&s); |
| this->writeCommand(Rehydrator::kArrayType_Command); |
| this->writeId(&t); |
| this->write(t.componentType()); |
| this->writeS8(t.columns()); |
| break; |
| case Type::TypeKind::kStruct: |
| this->allocSymbolId(&s); |
| this->writeCommand(Rehydrator::kStructType_Command); |
| this->writeId(&t); |
| this->write(t.name()); |
| this->writeU8(t.fields().size()); |
| for (const Type::Field& f : t.fields()) { |
| this->write(f.fModifiers); |
| this->write(f.fName); |
| this->write(*f.fType); |
| } |
| this->writeU8(t.isInterfaceBlock()); |
| break; |
| default: |
| this->writeCommand(Rehydrator::kSymbolRef_Command); |
| this->writeU16(Rehydrator::kBuiltin_Symbol); |
| this->write(t.name()); |
| break; |
| } |
| break; |
| } |
| case Symbol::Kind::kVariable: { |
| this->allocSymbolId(&s); |
| const Variable& v = s.as<Variable>(); |
| this->writeCommand(Rehydrator::kVariable_Command); |
| this->writeId(&v); |
| this->write(v.modifiers()); |
| this->write(v.name()); |
| this->write(v.type()); |
| this->writeU8((int8_t) v.storage()); |
| break; |
| } |
| case Symbol::Kind::kField: { |
| const Field& f = s.as<Field>(); |
| this->writeCommand(Rehydrator::kField_Command); |
| this->writeU16(this->symbolId(&f.owner())); |
| this->writeU8(f.fieldIndex()); |
| break; |
| } |
| case Symbol::Kind::kExternal: |
| SkASSERT(false); |
| break; |
| } |
| } |
| |
| void Dehydrator::write(const SymbolTable& symbols) { |
| this->writeCommand(Rehydrator::kSymbolTable_Command); |
| this->writeU8(symbols.isBuiltin()); |
| this->writeU16(symbols.fOwnedSymbols.size()); |
| |
| // write owned symbols |
| for (const std::unique_ptr<const Symbol>& s : symbols.fOwnedSymbols) { |
| this->write(*s); |
| } |
| |
| // write symbols |
| this->writeU16(symbols.fSymbols.count()); |
| std::map<std::string_view, const Symbol*> ordered; |
| symbols.foreach([&](std::string_view name, const Symbol* symbol) { |
| ordered.insert({name, symbol}); |
| }); |
| for (std::pair<std::string_view, const Symbol*> p : ordered) { |
| bool found = false; |
| for (size_t i = 0; i < symbols.fOwnedSymbols.size(); ++i) { |
| if (symbols.fOwnedSymbols[i].get() == p.second) { |
| fCommandBreaks.add(fBody.bytesWritten()); |
| this->writeU16(i); |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| // we should only fail to find builtin types |
| SkASSERT(p.second->is<Type>() && p.second->as<Type>().isInBuiltinTypes()); |
| this->writeU16(Rehydrator::kBuiltin_Symbol); |
| this->write(p.second->name()); |
| } |
| } |
| } |
| |
| |
| void Dehydrator::writeExpressionSpan(const SkSpan<const std::unique_ptr<Expression>>& span) { |
| this->writeU8(span.size()); |
| for (const auto& expr : span) { |
| this->write(expr.get()); |
| } |
| } |
| |
| void Dehydrator::write(const Expression* e) { |
| if (e) { |
| switch (e->kind()) { |
| case Expression::Kind::kBinary: { |
| const BinaryExpression& b = e->as<BinaryExpression>(); |
| this->writeCommand(Rehydrator::kBinary_Command); |
| this->write(b.left().get()); |
| this->writeU8((int) b.getOperator().kind()); |
| this->write(b.right().get()); |
| break; |
| } |
| case Expression::Kind::kChildCall: |
| SkDEBUGFAIL("unimplemented--not expected to be used from within an include file"); |
| break; |
| |
| case Expression::Kind::kConstructorArray: |
| this->writeCommand(Rehydrator::kConstructorArray_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorArray>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorArrayCast: |
| this->writeCommand(Rehydrator::kConstructorArrayCast_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorArrayCast>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorCompound: |
| this->writeCommand(Rehydrator::kConstructorCompound_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorCompound>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorCompoundCast: |
| this->writeCommand(Rehydrator::kConstructorCompoundCast_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorCompoundCast>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorDiagonalMatrix: |
| this->writeCommand(Rehydrator::kConstructorDiagonalMatrix_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorDiagonalMatrix>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorMatrixResize: |
| this->writeCommand(Rehydrator::kConstructorMatrixResize_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorMatrixResize>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorScalarCast: |
| this->writeCommand(Rehydrator::kConstructorScalarCast_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorScalarCast>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorSplat: |
| this->writeCommand(Rehydrator::kConstructorSplat_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorSplat>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kConstructorStruct: |
| this->writeCommand(Rehydrator::kConstructorStruct_Command); |
| this->write(e->type()); |
| this->writeExpressionSpan(e->as<ConstructorStruct>().argumentSpan()); |
| break; |
| |
| case Expression::Kind::kExternalFunctionCall: |
| case Expression::Kind::kExternalFunctionReference: |
| SkDEBUGFAIL("unimplemented--not expected to be used from within an include file"); |
| break; |
| |
| case Expression::Kind::kFieldAccess: { |
| const FieldAccess& f = e->as<FieldAccess>(); |
| this->writeCommand(Rehydrator::kFieldAccess_Command); |
| this->write(f.base().get()); |
| this->writeU8(f.fieldIndex()); |
| this->writeU8((int8_t) f.ownerKind()); |
| break; |
| } |
| case Expression::Kind::kFunctionCall: { |
| const FunctionCall& f = e->as<FunctionCall>(); |
| this->writeCommand(Rehydrator::kFunctionCall_Command); |
| this->write(f.type()); |
| this->writeId(&f.function()); |
| this->writeU8(f.arguments().size()); |
| for (const auto& a : f.arguments()) { |
| this->write(a.get()); |
| } |
| break; |
| } |
| case Expression::Kind::kIndex: { |
| const IndexExpression& i = e->as<IndexExpression>(); |
| this->writeCommand(Rehydrator::kIndex_Command); |
| this->write(i.base().get()); |
| this->write(i.index().get()); |
| break; |
| } |
| case Expression::Kind::kLiteral: { |
| const Literal& l = e->as<Literal>(); |
| if (l.type().isFloat()) { |
| float value = l.floatValue(); |
| int32_t floatBits; |
| memcpy(&floatBits, &value, sizeof(floatBits)); |
| this->writeCommand(Rehydrator::kFloatLiteral_Command); |
| this->write(l.type()); |
| this->writeS32(floatBits); |
| } else if (l.type().isBoolean()) { |
| this->writeCommand(Rehydrator::kBoolLiteral_Command); |
| this->writeU8(l.boolValue()); |
| } else { |
| SkASSERT(l.type().isInteger()); |
| this->writeCommand(Rehydrator::kIntLiteral_Command); |
| this->write(l.type()); |
| if (l.type().isUnsigned()) { |
| this->writeU32(l.intValue()); |
| } else { |
| this->writeS32(l.intValue()); |
| } |
| } |
| break; |
| } |
| case Expression::Kind::kPostfix: { |
| const PostfixExpression& p = e->as<PostfixExpression>(); |
| this->writeCommand(Rehydrator::kPostfix_Command); |
| this->writeU8((int) p.getOperator().kind()); |
| this->write(p.operand().get()); |
| break; |
| } |
| case Expression::Kind::kPrefix: { |
| const PrefixExpression& p = e->as<PrefixExpression>(); |
| this->writeCommand(Rehydrator::kPrefix_Command); |
| this->writeU8((int) p.getOperator().kind()); |
| this->write(p.operand().get()); |
| break; |
| } |
| case Expression::Kind::kSetting: { |
| const Setting& s = e->as<Setting>(); |
| this->writeCommand(Rehydrator::kSetting_Command); |
| this->write(s.name()); |
| break; |
| } |
| case Expression::Kind::kSwizzle: { |
| const Swizzle& s = e->as<Swizzle>(); |
| this->writeCommand(Rehydrator::kSwizzle_Command); |
| this->write(s.base().get()); |
| this->writeU8(s.components().size()); |
| for (int c : s.components()) { |
| this->writeU8(c); |
| } |
| break; |
| } |
| case Expression::Kind::kTernary: { |
| const TernaryExpression& t = e->as<TernaryExpression>(); |
| this->writeCommand(Rehydrator::kTernary_Command); |
| this->write(t.test().get()); |
| this->write(t.ifTrue().get()); |
| this->write(t.ifFalse().get()); |
| break; |
| } |
| case Expression::Kind::kVariableReference: { |
| const VariableReference& v = e->as<VariableReference>(); |
| this->writeCommand(Rehydrator::kVariableReference_Command); |
| this->writeId(v.variable()); |
| this->writeU8((int8_t) v.refKind()); |
| break; |
| } |
| case Expression::Kind::kFunctionReference: |
| case Expression::Kind::kMethodReference: |
| case Expression::Kind::kPoison: |
| case Expression::Kind::kTypeReference: |
| SkDEBUGFAIL("this expression shouldn't appear in finished code"); |
| break; |
| } |
| } else { |
| this->writeCommand(Rehydrator::kVoid_Command); |
| } |
| } |
| |
| void Dehydrator::write(const Statement* s) { |
| if (s) { |
| switch (s->kind()) { |
| case Statement::Kind::kBlock: { |
| const Block& b = s->as<Block>(); |
| this->writeCommand(Rehydrator::kBlock_Command); |
| AutoDehydratorSymbolTable symbols(this, b.symbolTable()); |
| this->writeU8(b.children().size()); |
| for (const std::unique_ptr<Statement>& blockStmt : b.children()) { |
| this->write(blockStmt.get()); |
| } |
| this->writeU8((int8_t)b.blockKind()); |
| break; |
| } |
| case Statement::Kind::kBreak: |
| this->writeCommand(Rehydrator::kBreak_Command); |
| break; |
| case Statement::Kind::kContinue: |
| this->writeCommand(Rehydrator::kContinue_Command); |
| break; |
| case Statement::Kind::kDiscard: |
| this->writeCommand(Rehydrator::kDiscard_Command); |
| break; |
| case Statement::Kind::kDo: { |
| const DoStatement& d = s->as<DoStatement>(); |
| this->writeCommand(Rehydrator::kDo_Command); |
| this->write(d.statement().get()); |
| this->write(d.test().get()); |
| break; |
| } |
| case Statement::Kind::kExpression: { |
| const ExpressionStatement& e = s->as<ExpressionStatement>(); |
| this->writeCommand(Rehydrator::kExpressionStatement_Command); |
| this->write(e.expression().get()); |
| break; |
| } |
| case Statement::Kind::kFor: { |
| const ForStatement& f = s->as<ForStatement>(); |
| this->writeCommand(Rehydrator::kFor_Command); |
| AutoDehydratorSymbolTable symbols(this, f.symbols()); |
| this->write(f.initializer().get()); |
| this->write(f.test().get()); |
| this->write(f.next().get()); |
| this->write(f.statement().get()); |
| break; |
| } |
| case Statement::Kind::kIf: { |
| const IfStatement& i = s->as<IfStatement>(); |
| this->writeCommand(Rehydrator::kIf_Command); |
| this->writeU8(i.isStatic()); |
| this->write(i.test().get()); |
| this->write(i.ifTrue().get()); |
| this->write(i.ifFalse().get()); |
| break; |
| } |
| case Statement::Kind::kNop: |
| this->writeCommand(Rehydrator::kNop_Command); |
| break; |
| case Statement::Kind::kReturn: { |
| const ReturnStatement& r = s->as<ReturnStatement>(); |
| this->writeCommand(Rehydrator::kReturn_Command); |
| this->write(r.expression().get()); |
| break; |
| } |
| case Statement::Kind::kSwitch: { |
| const SwitchStatement& ss = s->as<SwitchStatement>(); |
| this->writeCommand(Rehydrator::kSwitch_Command); |
| this->writeU8(ss.isStatic()); |
| AutoDehydratorSymbolTable symbols(this, ss.symbols()); |
| this->write(ss.value().get()); |
| this->writeU8(ss.cases().size()); |
| for (const std::unique_ptr<Statement>& stmt : ss.cases()) { |
| const SwitchCase& sc = stmt->as<SwitchCase>(); |
| if (sc.isDefault()) { |
| this->writeU8(1); |
| } else { |
| this->writeU8(0); |
| this->writeS32(sc.value()); |
| } |
| this->write(sc.statement().get()); |
| } |
| break; |
| } |
| case Statement::Kind::kSwitchCase: |
| SkDEBUGFAIL("SwitchCase statements shouldn't appear here"); |
| break; |
| case Statement::Kind::kVarDeclaration: { |
| const VarDeclaration& v = s->as<VarDeclaration>(); |
| this->writeCommand(Rehydrator::kVarDeclaration_Command); |
| this->writeU16(this->symbolId(&v.var())); |
| this->write(v.baseType()); |
| this->writeU8(v.arraySize()); |
| this->write(v.value().get()); |
| break; |
| } |
| } |
| } else { |
| this->writeCommand(Rehydrator::kVoid_Command); |
| } |
| } |
| |
| void Dehydrator::write(const ProgramElement& e) { |
| switch (e.kind()) { |
| case ProgramElement::Kind::kExtension: |
| SkASSERT(false); |
| break; |
| case ProgramElement::Kind::kFunction: { |
| const FunctionDefinition& f = e.as<FunctionDefinition>(); |
| this->writeCommand(Rehydrator::kFunctionDefinition_Command); |
| this->writeU16(this->symbolId(&f.declaration())); |
| this->write(f.body().get()); |
| break; |
| } |
| case ProgramElement::Kind::kFunctionPrototype: { |
| const FunctionPrototype& f = e.as<FunctionPrototype>(); |
| if (!f.isBuiltin()) { |
| this->writeCommand(Rehydrator::kFunctionPrototype_Command); |
| this->writeU16(this->symbolId(&f.declaration())); |
| } |
| break; |
| } |
| case ProgramElement::Kind::kInterfaceBlock: { |
| const InterfaceBlock& i = e.as<InterfaceBlock>(); |
| this->writeCommand(Rehydrator::kInterfaceBlock_Command); |
| this->write(i.variable()); |
| this->write(i.typeName()); |
| this->write(i.instanceName()); |
| this->writeU8(i.arraySize()); |
| break; |
| } |
| case ProgramElement::Kind::kModifiers: |
| SkASSERT(false); |
| break; |
| case ProgramElement::Kind::kStructDefinition: { |
| const StructDefinition& structDef = e.as<StructDefinition>(); |
| this->writeCommand(Rehydrator::kStructDefinition_Command); |
| this->write(structDef.type()); |
| break; |
| } |
| case ProgramElement::Kind::kGlobalVar: { |
| const GlobalVarDeclaration& v = e.as<GlobalVarDeclaration>(); |
| this->writeCommand(Rehydrator::kGlobalVar_Command); |
| this->write(v.declaration().get()); |
| break; |
| } |
| } |
| } |
| |
| void Dehydrator::write(const std::vector<std::unique_ptr<ProgramElement>>& elements) { |
| this->writeCommand(Rehydrator::kElements_Command); |
| for (const auto& e : elements) { |
| this->write(*e); |
| } |
| this->writeCommand(Rehydrator::kElementsComplete_Command); |
| } |
| |
| void Dehydrator::write(const Program& program) { |
| this->writeCommand(Rehydrator::kProgram_Command); |
| this->writeU8((int)program.fConfig->fKind); |
| this->writeU8((int)program.fConfig->fRequiredSkSLVersion); |
| this->write(*program.fSymbols); |
| |
| // Write the elements |
| this->writeCommand(Rehydrator::kElements_Command); |
| for (const auto& e : program.fSharedElements) { |
| this->writeCommand(Rehydrator::kSharedFunction_Command); |
| const FunctionDefinition& f = e->as<FunctionDefinition>(); |
| const FunctionDeclaration& decl = f.declaration(); |
| this->writeU8(decl.parameters().size()); |
| for (const Variable* param : decl.parameters()) { |
| this->write(*param); |
| } |
| this->write(f.declaration()); |
| this->write(*e); |
| } |
| for (const auto& e : program.fOwnedElements) { |
| this->write(*e); |
| } |
| this->writeCommand(Rehydrator::kElementsComplete_Command); |
| |
| // Write the inputs |
| struct KnownSkSLProgramInputs { bool useRTFlipUniform; }; |
| // Since it would be easy to forget to update this code in the face of Inputs changes and any |
| // resulting bugs could be very subtle, assert that the struct hasn't changed: |
| static_assert(sizeof(SkSL::Program::Inputs) == sizeof(KnownSkSLProgramInputs)); |
| this->writeU8(program.fInputs.fUseFlipRTUniform); |
| } |
| |
| void Dehydrator::finish(OutputStream& out) { |
| out.write16(Rehydrator::kVersion); |
| std::string stringBuffer = fStringBuffer.str(); |
| std::string commandBuffer = fBody.str(); |
| out.write16(fStringBuffer.str().size()); |
| fStringBufferStart = 4; |
| out.writeString(stringBuffer); |
| fCommandStart = fStringBufferStart + stringBuffer.size(); |
| out.writeString(commandBuffer); |
| } |
| |
| const char* Dehydrator::prefixAtOffset(size_t byte) { |
| if (byte >= fCommandStart) { |
| return fCommandBreaks.contains(byte - fCommandStart) ? "\n" : ""; |
| } |
| if (byte >= fStringBufferStart) { |
| return fStringBreaks.contains(byte - fStringBufferStart) ? "\n" : ""; |
| } |
| return ""; |
| } |
| |
| } // namespace SkSL |