Add support for builtin blocks
Problem of supporting builtin blocks boils down to how you refer to them
from the API side -- if the reference is ONLY a SPIRV id, then block
members are difficult to work with. Instead, represent a builtin
variable binding as (id, first component, num components). For a single
variable, first component == 0 always.
Bug: b/120799499
Change-Id: If9e99f4c10f73a008e2f5071a95785920c7fbed1
Reviewed-on: https://swiftshader-review.googlesource.com/c/23488
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index fffafdf..060a6ff 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -28,10 +28,7 @@
serialID{serialCounter++}, modes{}
{
// Simplifying assumptions (to be satisfied by earlier transformations)
- // - There is exactly one extrypoint in the module, and it's the one we want
- // - Builtin interface blocks have been split. [Splitting user-defined interface blocks
- // without changing layout is impossible in the general case because splitting an array
- // of structs produces a weirdly-strided array, which SPIRV can't represent.
+ // - There is exactly one entrypoint in the module, and it's the one we want
// - The only input/output OpVariables present are those used by the entrypoint
for (auto insn : *this)
@@ -116,6 +113,29 @@
object.kind = Object::Kind::Type;
object.definition = insn;
object.sizeInComponents = ComputeTypeSize(insn);
+
+ // A structure is a builtin block if it has a builtin
+ // member. All members of such a structure are builtins.
+ if (insn.opcode() == spv::OpTypeStruct)
+ {
+ auto d = memberDecorations.find(resultId);
+ if (d != memberDecorations.end())
+ {
+ for (auto &m : d->second)
+ {
+ if (m.HasBuiltIn)
+ {
+ object.isBuiltInBlock = true;
+ break;
+ }
+ }
+ }
+ }
+ else if (insn.opcode() == spv::OpTypePointer)
+ {
+ auto pointeeType = insn.word(3);
+ object.isBuiltInBlock = defs[pointeeType].isBuiltInBlock;
+ }
break;
}
@@ -131,31 +151,17 @@
object.kind = Object::Kind::Variable;
object.definition = insn;
object.storageClass = storageClass;
- object.sizeInComponents = defs[typeId].sizeInComponents;
+
+ auto &type = defs[typeId];
+
+ object.sizeInComponents = type.sizeInComponents;
+ object.isBuiltInBlock = type.isBuiltInBlock;
// Register builtins
- // TODO: detect the builtin block!
- auto &d = decorations[resultId];
- if (storageClass == spv::StorageClassInput)
+ if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput)
{
- if (d.HasBuiltIn)
- {
- inputBuiltins[d.BuiltIn] = resultId;
- } else
- {
- PopulateInterface(&inputs, resultId);
- }
- }
- if (storageClass == spv::StorageClassOutput)
- {
- if (d.HasBuiltIn)
- {
- outputBuiltins[d.BuiltIn] = resultId;
- } else
- {
- PopulateInterface(&outputs, resultId);
- }
+ ProcessInterfaceVariable(object);
}
break;
}
@@ -175,12 +181,65 @@
break;
}
+ case spv::OpCapability:
+ // Various capabilities will be declared, but none affect our code generation at this point.
+ case spv::OpMemoryModel:
+ // Memory model does not affect our code generation until we decide to do Vulkan Memory Model support.
+ case spv::OpEntryPoint:
+ // Due to preprocessing, the entrypoint provides no value.
+ break;
+
default:
break; // This is OK, these passes are intentionally partial
}
}
}
+ void SpirvShader::ProcessInterfaceVariable(Object const &object)
+ {
+ assert(object.storageClass == spv::StorageClassInput || object.storageClass == spv::StorageClassOutput);
+
+ auto &builtinInterface = (object.storageClass == spv::StorageClassInput) ? inputBuiltins : outputBuiltins;
+ auto &userDefinedInterface = (object.storageClass == spv::StorageClassInput) ? inputs : outputs;
+
+ auto resultId = object.definition.word(2);
+ if (object.isBuiltInBlock)
+ {
+ // walk the builtin block, registering each of its members separately.
+ auto ptrType = defs[object.definition.word(1)].definition;
+ assert(ptrType.opcode() == spv::OpTypePointer);
+ auto pointeeType = ptrType.word(3);
+ auto m = memberDecorations.find(pointeeType);
+ assert(m != memberDecorations.end()); // otherwise we wouldn't have marked the type chain
+ auto structType = defs[pointeeType].definition;
+ auto offset = 0u;
+ auto word = 2u;
+ for (auto &member : m->second)
+ {
+ auto &memberType = defs[structType.word(word)];
+
+ if (member.HasBuiltIn)
+ {
+ builtinInterface[member.BuiltIn] = {resultId, offset, memberType.sizeInComponents};
+ }
+
+ offset += memberType.sizeInComponents;
+ ++word;
+ }
+ return;
+ }
+
+ auto d = decorations.find(resultId);
+ if (d != decorations.end() && d->second.HasBuiltIn)
+ {
+ builtinInterface[d->second.BuiltIn] = {resultId, 0, object.sizeInComponents};
+ }
+ else
+ {
+ PopulateInterface(&userDefinedInterface, resultId);
+ }
+ }
+
void SpirvShader::ProcessExecutionMode(InsnIterator insn)
{
auto mode = static_cast<spv::ExecutionMode>(insn.word(2));