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;
protected:
// Intended for use by AutoOutputStream.
OutputStream* outputStream() { return fOut; }
void setOutputStream(OutputStream* output) { fOut = output; }
protected:
const Program& fProgram;
ErrorReporter& fErrors;
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
#endif

View File

@ -1216,27 +1216,26 @@ void MetalCodeGenerator::writeFunction(const FunctionDefinition& f) {
}
fFunctionHeader = "";
OutputStream* oldOut = fOut;
StringStream buffer;
fOut = &buffer;
fIndentation++;
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
if (!stmt->isEmpty()) {
this->writeStatement(*stmt);
this->writeLine();
{
AutoOutputStream outputToBuffer(this, &buffer);
fIndentation++;
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
if (!stmt->isEmpty()) {
this->writeStatement(*stmt);
this->writeLine();
}
}
}
if (f.declaration().name() == "main") {
// 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())) {
this->writeReturnStatementFromMain();
this->writeLine("");
if (f.declaration().name() == "main") {
// 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())) {
this->writeReturnStatementFromMain();
this->writeLine("");
}
}
fIndentation--;
this->writeLine("}");
}
fIndentation--;
this->writeLine("}");
fOut = oldOut;
this->write(fFunctionHeader);
this->write(buffer.str());
}
@ -2019,26 +2018,29 @@ MetalCodeGenerator::Requirements MetalCodeGenerator::requirements(const Function
}
bool MetalCodeGenerator::generateCode() {
OutputStream* rawOut = fOut;
fOut = &fHeader;
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);
write_stringstream(fExtraFunctions, *rawOut);
write_stringstream(body, *rawOut);
StringStream header;
{
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();
}

View File

@ -67,11 +67,9 @@ void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
: Compiler::FormatArg::Kind::kChildProcessor,
index));
if (arguments.size() > 1) {
OutputStream* oldOut = fOut;
StringStream buffer;
fOut = &buffer;
AutoOutputStream outputToBuffer(this, &buffer);
this->writeExpression(*arguments[1], kSequence_Precedence);
fOut = oldOut;
fArgs->fFormatArgs[childCallIndex].fCoords = buffer.str();
}
return;
@ -185,48 +183,50 @@ void PipelineStageCodeGenerator::writeSwitchStatement(const SwitchStatement& s)
void PipelineStageCodeGenerator::writeFunction(const FunctionDefinition& f) {
fFunctionHeader = "";
OutputStream* oldOut = fOut;
StringStream buffer;
fOut = &buffer;
Compiler::GLSLFunction result;
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
// explicitly cast any returns (from main) to half4. This is only strictly necessary
// if the return type is float4 - injecting it unconditionally reduces the risk of an
// obscure bug.
fCastReturnsToHalf = true;
for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
this->writeStatement(*stmt);
this->writeLine();
{
AutoOutputStream streamToBuffer(this, &buffer);
// 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
// explicitly cast any returns (from main) to half4. This is only strictly necessary
// if the return type is float4 - injecting it unconditionally reduces the risk of an
// obscure bug.
fCastReturnsToHalf = true;
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(buffer.str());
} else {
const FunctionDeclaration& decl = f.declaration();
Compiler::GLSLFunction result;
if (!type_to_grsltype(fContext, decl.returnType(), &result.fReturnType)) {
fErrors.error(f.fOffset, "unsupported return type");
return;
}
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");
{
AutoOutputStream streamToBuffer(this, &buffer);
const FunctionDeclaration& decl = f.declaration();
if (!type_to_grsltype(fContext, decl.returnType(), &result.fReturnType)) {
fErrors.error(f.fOffset, "unsupported return type");
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.fFormatArgs = std::move(fArgs->fFormatArgs);
fArgs->fFunctions.push_back(result);
fArgs->fFunctions.push_back(std::move(result));
}
}