Add global struct definitions to SkSL.
Previously, GLSL and Metal code generators would emit a struct wherever
the type was first used in the code, regardless of where it was
originally defined or what scope the type needs to live in. This CL adds
a ProgramElement for struct definitions, so that structs will now appear
at the top-level as they were originally defined. In the case of Metal,
some special handling is also needed to handle the Globals struct
properly.
Not yet fully supported:
- No special handling for structs declared inside functions yet
- No support for structs in separate scopes with overlapping names
The severity of the remaining issues depends mostly on whether we want
to support structs inside functions in Runtime Effects.
Change-Id: Ia95d4529506cb3fa6da63f5cb548199a93e1c0c5
Bug: skia:10922, skia:10923, skia:10925, skia:10926
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/338600
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index 95ccb80..3324fe6 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -14,6 +14,7 @@
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLModifiersDeclaration.h"
#include "src/sksl/ir/SkSLNop.h"
+#include "src/sksl/ir/SkSLStructDefinition.h"
#include "src/sksl/ir/SkSLVariableReference.h"
#include <algorithm>
@@ -111,21 +112,27 @@
}
}
+bool MetalCodeGenerator::writeStructDefinition(const Type& type) {
+ for (const Type* search : fWrittenStructs) {
+ if (*search == type) {
+ // already written
+ return false;
+ }
+ }
+ fWrittenStructs.push_back(&type);
+ this->writeLine("struct " + type.name() + " {");
+ fIndentation++;
+ this->writeFields(type.fields(), type.fOffset);
+ fIndentation--;
+ this->write("}");
+ return true;
+}
+
void MetalCodeGenerator::writeType(const Type& type) {
if (type.typeKind() == Type::TypeKind::kStruct) {
- for (const Type* search : fWrittenStructs) {
- if (*search == type) {
- // already written
- this->write(type.name());
- return;
- }
+ if (!this->writeStructDefinition(type)) {
+ this->write(type.name());
}
- fWrittenStructs.push_back(&type);
- this->writeLine("struct " + type.name() + " {");
- fIndentation++;
- this->writeFields(type.fields(), type.fOffset);
- fIndentation--;
- this->write("}");
} else {
this->write(this->typeName(type));
}
@@ -1587,6 +1594,26 @@
}
}
+void MetalCodeGenerator::writeStructDefinitions() {
+ for (const ProgramElement* e : fProgram.elements()) {
+ if (e->is<StructDefinition>()) {
+ if (this->writeStructDefinition(e->as<StructDefinition>().type())) {
+ this->writeLine(";");
+ }
+ } else if (e->is<GlobalVarDeclaration>()) {
+ // If a global var declaration introduces a struct type, we need to write that type
+ // here, since globals are all embedded in a sub-struct.
+ const Type* type = &e->as<GlobalVarDeclaration>().declaration()
+ ->as<VarDeclaration>().baseType();
+ if (type->typeKind() == Type::TypeKind::kStruct) {
+ if (this->writeStructDefinition(*type)) {
+ this->writeLine(";");
+ }
+ }
+ }
+ }
+}
+
void MetalCodeGenerator::visitGlobalStruct(GlobalStructVisitor* visitor) {
// Visit the interface blocks.
for (const auto& [interfaceType, interfaceName] : fInterfaceBlockNameMap) {
@@ -1737,6 +1764,9 @@
case ProgramElement::Kind::kInterfaceBlock:
// handled in writeInterfaceBlocks, do nothing
break;
+ case ProgramElement::Kind::kStructDefinition:
+ // Handled in writeStructDefinitions. Do nothing.
+ break;
case ProgramElement::Kind::kFunction:
this->writeFunction(e.as<FunctionDefinition>());
break;
@@ -1919,6 +1949,7 @@
fOut = &fHeader;
fProgramKind = fProgram.fKind;
this->writeHeader();
+ this->writeStructDefinitions();
this->writeUniformStruct();
this->writeInputStruct();
this->writeOutputStruct();