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
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 @@ namespace sw
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 @@ namespace sw
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 @@ namespace sw
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));
......
......@@ -103,6 +103,7 @@ namespace sw
InsnIterator definition;
spv::StorageClass storageClass;
uint32_t sizeInComponents = 0;
bool isBuiltInBlock = false;
enum class Kind
{
......@@ -199,6 +200,13 @@ namespace sw
}
};
struct BuiltinMapping
{
uint32_t Id;
uint32_t FirstComponent;
uint32_t SizeInComponents;
};
std::vector<InterfaceComponent> inputs;
std::vector<InterfaceComponent> outputs;
......@@ -207,8 +215,8 @@ namespace sw
static volatile int serialCounter;
Modes modes;
std::unordered_map<uint32_t, Object> defs;
std::unordered_map<spv::BuiltIn, uint32_t> inputBuiltins;
std::unordered_map<spv::BuiltIn, uint32_t> outputBuiltins;
std::unordered_map<spv::BuiltIn, BuiltinMapping> inputBuiltins;
std::unordered_map<spv::BuiltIn, BuiltinMapping> outputBuiltins;
void ProcessExecutionMode(InsnIterator it);
......@@ -221,6 +229,8 @@ namespace sw
void PopulateInterface(std::vector<InterfaceComponent> *iface, 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