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:
parent
1b27c3d7a3
commit
4453237e52
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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(), ¶mSLType)) {
|
|
||||||
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(), ¶mSLType)) {
|
||||||
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user