Commit bde34083 by Chris Forbes

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/23488Tested-by: 's avatarChris Forbes <chrisforbes@google.com> Reviewed-by: 's avatarAlexis Hétu <sugoi@google.com>
parent 5be4d706
...@@ -28,10 +28,7 @@ namespace sw ...@@ -28,10 +28,7 @@ namespace sw
serialID{serialCounter++}, modes{} serialID{serialCounter++}, modes{}
{ {
// Simplifying assumptions (to be satisfied by earlier transformations) // Simplifying assumptions (to be satisfied by earlier transformations)
// - There is exactly one extrypoint in the module, and it's the one we want // - There is exactly one entrypoint 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.
// - The only input/output OpVariables present are those used by the entrypoint // - The only input/output OpVariables present are those used by the entrypoint
for (auto insn : *this) for (auto insn : *this)
...@@ -116,6 +113,29 @@ namespace sw ...@@ -116,6 +113,29 @@ namespace sw
object.kind = Object::Kind::Type; object.kind = Object::Kind::Type;
object.definition = insn; object.definition = insn;
object.sizeInComponents = ComputeTypeSize(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; break;
} }
...@@ -131,31 +151,17 @@ namespace sw ...@@ -131,31 +151,17 @@ namespace sw
object.kind = Object::Kind::Variable; object.kind = Object::Kind::Variable;
object.definition = insn; object.definition = insn;
object.storageClass = storageClass; object.storageClass = storageClass;
object.sizeInComponents = defs[typeId].sizeInComponents;
auto &type = defs[typeId];
object.sizeInComponents = type.sizeInComponents;
object.isBuiltInBlock = type.isBuiltInBlock;
// Register builtins // Register builtins
// TODO: detect the builtin block! if (storageClass == spv::StorageClassInput || storageClass == spv::StorageClassOutput)
auto &d = decorations[resultId];
if (storageClass == spv::StorageClassInput)
{ {
if (d.HasBuiltIn) ProcessInterfaceVariable(object);
{
inputBuiltins[d.BuiltIn] = resultId;
} else
{
PopulateInterface(&inputs, resultId);
}
}
if (storageClass == spv::StorageClassOutput)
{
if (d.HasBuiltIn)
{
outputBuiltins[d.BuiltIn] = resultId;
} else
{
PopulateInterface(&outputs, resultId);
}
} }
break; break;
} }
...@@ -175,12 +181,65 @@ namespace sw ...@@ -175,12 +181,65 @@ namespace sw
break; 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: default:
break; // This is OK, these passes are intentionally partial 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) void SpirvShader::ProcessExecutionMode(InsnIterator insn)
{ {
auto mode = static_cast<spv::ExecutionMode>(insn.word(2)); auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
......
...@@ -103,6 +103,7 @@ namespace sw ...@@ -103,6 +103,7 @@ namespace sw
InsnIterator definition; InsnIterator definition;
spv::StorageClass storageClass; spv::StorageClass storageClass;
uint32_t sizeInComponents = 0; uint32_t sizeInComponents = 0;
bool isBuiltInBlock = false;
enum class Kind enum class Kind
{ {
...@@ -199,6 +200,13 @@ namespace sw ...@@ -199,6 +200,13 @@ namespace sw
} }
}; };
struct BuiltinMapping
{
uint32_t Id;
uint32_t FirstComponent;
uint32_t SizeInComponents;
};
std::vector<InterfaceComponent> inputs; std::vector<InterfaceComponent> inputs;
std::vector<InterfaceComponent> outputs; std::vector<InterfaceComponent> outputs;
...@@ -207,8 +215,8 @@ namespace sw ...@@ -207,8 +215,8 @@ namespace sw
static volatile int serialCounter; static volatile int serialCounter;
Modes modes; Modes modes;
std::unordered_map<uint32_t, Object> defs; std::unordered_map<uint32_t, Object> defs;
std::unordered_map<spv::BuiltIn, uint32_t> inputBuiltins; std::unordered_map<spv::BuiltIn, BuiltinMapping> inputBuiltins;
std::unordered_map<spv::BuiltIn, uint32_t> outputBuiltins; std::unordered_map<spv::BuiltIn, BuiltinMapping> outputBuiltins;
void ProcessExecutionMode(InsnIterator it); void ProcessExecutionMode(InsnIterator it);
...@@ -221,6 +229,8 @@ namespace sw ...@@ -221,6 +229,8 @@ namespace sw
void PopulateInterface(std::vector<InterfaceComponent> *iface, uint32_t id); void PopulateInterface(std::vector<InterfaceComponent> *iface, uint32_t id);
uint32_t GetConstantInt(uint32_t id); uint32_t GetConstantInt(uint32_t id);
void ProcessInterfaceVariable(Object const &object);
}; };
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment