Use a scoped helper class to push and pop CodeGenerator::fOut changes.

This mirrors other "AutoXxxxxxx" classes used in SkSL to push and pop
temporary changes into member variables, such as AutoSymbolTable or
AutoLoopLevel.

Metal and Pipeline code generators were updated to use this class.
SkSL was left as-is for now; it modifies fOut more extensively than the
others and will need special care.

Change-Id: Icf505b9b55e3458de349e35e3b812dd005f9afed
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/341457
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
John Stiles 2020-12-07 12:33:55 -05:00 committed by Skia Commit-Bot
parent 1b27c3d7a3
commit 4453237e52
3 changed files with 106 additions and 70 deletions

View File

@ -28,13 +28,47 @@ public:
virtual bool generateCode() = 0; virtual bool generateCode() = 0;
protected: // Intended for use by AutoOutputStream.
OutputStream* outputStream() { return fOut; }
void setOutputStream(OutputStream* output) { fOut = output; }
protected:
const Program& fProgram; const Program& fProgram;
ErrorReporter& fErrors; ErrorReporter& fErrors;
OutputStream* fOut; OutputStream* fOut;
}; };
class AutoOutputStream {
public:
// Maintains the current indentation level while writing to the new output stream.
AutoOutputStream(CodeGenerator* codeGen, OutputStream* newOutput)
: fCodeGen(codeGen)
, fOldOutput(codeGen->outputStream()) {
fCodeGen->setOutputStream(newOutput);
}
// Resets the indentation when entering the scope, and restores it when leaving.
AutoOutputStream(CodeGenerator* codeGen, OutputStream* newOutput, int *indentationPtr)
: fCodeGen(codeGen)
, fOldOutput(codeGen->outputStream())
, fIndentationPtr(indentationPtr)
, fOldIndentation(indentationPtr ? *indentationPtr : 0) {
fCodeGen->setOutputStream(newOutput);
*fIndentationPtr = 0;
}
~AutoOutputStream() {
fCodeGen->setOutputStream(fOldOutput);
if (fIndentationPtr) {
*fIndentationPtr = fOldIndentation;
}
}
private:
CodeGenerator* fCodeGen = nullptr;
OutputStream* fOldOutput = nullptr;
int *fIndentationPtr = nullptr;
int fOldIndentation = 0;
};
} // namespace SkSL } // namespace SkSL
#endif #endif

View File

@ -1216,27 +1216,26 @@ void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
} }
fFunctionHeader = ""; fFunctionHeader = "";
OutputStream* oldOut = fOut;
StringStream buffer; StringStream buffer;
fOut = &buffer; {
fIndentation++; AutoOutputStream outputToBuffer(this, &buffer);
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) { fIndentation++;
if (!stmt->isEmpty()) { for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
this->writeStatement(*stmt); if (!stmt->isEmpty()) {
this->writeLine(); this->writeStatement(*stmt);
this->writeLine();
}
} }
} if (f.declaration().name() == "main") {
if (f.declaration().name() == "main") { // If the main function doesn't end with a return, we need to synthesize one here.
// If the main function doesn't end with a return, we need to synthesize one here. if (!is_block_ending_with_return(f.body().get())) {
if (!is_block_ending_with_return(f.body().get())) { this->writeReturnStatementFromMain();
this->writeReturnStatementFromMain(); this->writeLine("");
this->writeLine(""); }
} }
fIndentation--;
this->writeLine("}");
} }
fIndentation--;
this->writeLine("}");
fOut = oldOut;
this->write(fFunctionHeader); this->write(fFunctionHeader);
this->write(buffer.str()); this->write(buffer.str());
} }
@ -2019,26 +2018,29 @@ MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Function
} }
bool MetalCodeGenerator::generateCode() { bool MetalCodeGenerator::generateCode() {
OutputStream* rawOut = fOut;
fOut = &fHeader;
fProgramKind = fProgram.fKind; fProgramKind = fProgram.fKind;
this->writeHeader();
this->writeStructDefinitions();
this->writeUniformStruct();
this->writeInputStruct();
this->writeOutputStruct();
this->writeInterfaceBlocks();
this->writeGlobalStruct();
StringStream body;
fOut = &body;
for (const ProgramElement* e : fProgram.elements()) {
this->writeProgramElement(*e);
}
fOut = rawOut;
write_stringstream(fHeader, *rawOut); StringStream header;
write_stringstream(fExtraFunctions, *rawOut); {
write_stringstream(body, *rawOut); AutoOutputStream outputToHeader(this, &header, &fIndentation);
this->writeHeader();
this->writeStructDefinitions();
this->writeUniformStruct();
this->writeInputStruct();
this->writeOutputStruct();
this->writeInterfaceBlocks();
this->writeGlobalStruct();
}
StringStream body;
{
AutoOutputStream outputToBody(this, &body, &fIndentation);
for (const ProgramElement* e : fProgram.elements()) {
this->writeProgramElement(*e);
}
}
write_stringstream(header, *fOut);
write_stringstream(fExtraFunctions, *fOut);
write_stringstream(body, *fOut);
return 0 == fErrors.errorCount(); return 0 == fErrors.errorCount();
} }

View File

@ -67,11 +67,9 @@ void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
: Compiler::FormatArg::Kind::kChildProcessor, : Compiler::FormatArg::Kind::kChildProcessor,
index)); index));
if (arguments.size() > 1) { if (arguments.size() > 1) {
OutputStream* oldOut = fOut;
StringStream buffer; StringStream buffer;
fOut = &buffer; AutoOutputStream outputToBuffer(this, &buffer);
this->writeExpression(*arguments[1], kSequence_Precedence); this->writeExpression(*arguments[1], kSequence_Precedence);
fOut = oldOut;
fArgs->fFormatArgs[childCallIndex].fCoords = buffer.str(); fArgs->fFormatArgs[childCallIndex].fCoords = buffer.str();
} }
return; return;
@ -185,48 +183,50 @@ void PipelineStageCodeGenerator::writeSwitchStatement(const SwitchStatement& s)
void PipelineStageCodeGenerator::writeFunction(const FunctionDefinition& f) { void PipelineStageCodeGenerator::writeFunction(const FunctionDefinition& f) {
fFunctionHeader = ""; fFunctionHeader = "";
OutputStream* oldOut = fOut;
StringStream buffer; StringStream buffer;
fOut = &buffer; Compiler::GLSLFunction result;
if (f.declaration().name() == "main") { if (f.declaration().name() == "main") {
// We allow public SkSL's main() to return half4 -or- float4 (ie vec4). When we emit {
// our code in the processor, the surrounding code is going to expect half4, so we AutoOutputStream streamToBuffer(this, &buffer);
// explicitly cast any returns (from main) to half4. This is only strictly necessary // We allow public SkSL's main() to return half4 -or- float4 (ie vec4). When we emit
// if the return type is float4 - injecting it unconditionally reduces the risk of an // our code in the processor, the surrounding code is going to expect half4, so we
// obscure bug. // explicitly cast any returns (from main) to half4. This is only strictly necessary
fCastReturnsToHalf = true; // if the return type is float4 - injecting it unconditionally reduces the risk of an
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) { // obscure bug.
this->writeStatement(*stmt); fCastReturnsToHalf = true;
this->writeLine(); for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
this->writeStatement(*stmt);
this->writeLine();
}
fCastReturnsToHalf = false;
} }
fCastReturnsToHalf = false;
fOut = oldOut;
this->write(fFunctionHeader); this->write(fFunctionHeader);
this->write(buffer.str()); this->write(buffer.str());
} else { } else {
const FunctionDeclaration& decl = f.declaration(); {
Compiler::GLSLFunction result; AutoOutputStream streamToBuffer(this, &buffer);
if (!type_to_grsltype(fContext, decl.returnType(), &result.fReturnType)) { const FunctionDeclaration& decl = f.declaration();
fErrors.error(f.fOffset, "unsupported return type"); if (!type_to_grsltype(fContext, decl.returnType(), &result.fReturnType)) {
return; fErrors.error(f.fOffset, "unsupported return type");
}
result.fName = decl.name();
for (const Variable* v : decl.parameters()) {
GrSLType paramSLType;
if (!type_to_grsltype(fContext, v->type(), &paramSLType)) {
fErrors.error(v->fOffset, "unsupported parameter type");
return; return;
} }
result.fParameters.emplace_back(v->name(), paramSLType); result.fName = decl.name();
for (const Variable* v : decl.parameters()) {
GrSLType paramSLType;
if (!type_to_grsltype(fContext, v->type(), &paramSLType)) {
fErrors.error(v->fOffset, "unsupported parameter type");
return;
}
result.fParameters.emplace_back(v->name(), paramSLType);
}
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
this->writeStatement(*stmt);
this->writeLine();
}
} }
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
this->writeStatement(*stmt);
this->writeLine();
}
fOut = oldOut;
result.fBody = buffer.str(); result.fBody = buffer.str();
result.fFormatArgs = std::move(fArgs->fFormatArgs); result.fFormatArgs = std::move(fArgs->fFormatArgs);
fArgs->fFunctions.push_back(result); fArgs->fFunctions.push_back(std::move(result));
} }
} }