OSDN Git Service

Collect input/output interfaces
authorChris Forbes <chrisforbes@google.com>
Tue, 11 Dec 2018 03:02:58 +0000 (19:02 -0800)
committerChris Forbes <chrisforbes@google.com>
Thu, 17 Jan 2019 20:44:33 +0000 (20:44 +0000)
I'm still on the fence about leaving complex input/output definitions
intact. However, this provides enough support to walk such structures
all the way down to the leaves and determine exactly which components
are in use, what their types are, and what the interpolation qualifiers
etc should be.

Bug: b/120799499

Change-Id: Ia1e46a571126088e29bfdab6bc5ece0343e294e4
Reviewed-on: https://swiftshader-review.googlesource.com/c/23176
Tested-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
src/Device/Config.hpp
src/Pipeline/SpirvShader.cpp
src/Pipeline/SpirvShader.hpp

index 5d99047..d3b4889 100644 (file)
@@ -97,6 +97,7 @@ namespace sw
                MAX_TEXTURE_LOD = MIPMAP_LEVELS - 2,   // Trilinear accesses lod+1
                RENDERTARGETS = 8,
                NUM_TEMPORARY_REGISTERS = 4096,
+               MAX_INTERFACE_COMPONENTS = 32 * 4,
        };
 }
 
index 8ae2d8d..641ac81 100644 (file)
@@ -22,11 +22,16 @@ namespace sw
 {
        volatile int SpirvShader::serialCounter = 1;    // Start at 1, 0 is invalid shader.
 
-       SpirvShader::SpirvShader(InsnStore const &insns) : insns{insns}, serialID{serialCounter++}, modes{}
+       SpirvShader::SpirvShader(InsnStore const &insns)
+                       : insns{insns}, inputs{MAX_INTERFACE_COMPONENTS},
+                         outputs{MAX_INTERFACE_COMPONENTS},
+                         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
-               // - Input / Output interface blocks, builtin or otherwise, have been split.
+               // - 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
 
                for (auto insn : *this)
@@ -129,14 +134,28 @@ namespace sw
                                object.sizeInComponents = defs[typeId].sizeInComponents;
 
                                // Register builtins
+
+                               // TODO: detect the builtin block!
                                auto &d = decorations[resultId];
-                               if (d.HasBuiltIn && storageClass == spv::StorageClassInput)
+                               if (storageClass == spv::StorageClassInput)
                                {
-                                       inputBuiltins[d.BuiltIn] = resultId;
+                                       if (d.HasBuiltIn)
+                                       {
+                                               inputBuiltins[d.BuiltIn] = resultId;
+                                       } else
+                                       {
+                                               PopulateInterface(&inputs, resultId);
+                                       }
                                }
-                               if (d.HasBuiltIn && storageClass == spv::StorageClassOutput)
+                               if (storageClass == spv::StorageClassOutput)
                                {
-                                       outputBuiltins[d.BuiltIn] = resultId;
+                                       if (d.HasBuiltIn)
+                                       {
+                                               outputBuiltins[d.BuiltIn] = resultId;
+                                       } else
+                                       {
+                                               PopulateInterface(&outputs, resultId);
+                                       }
                                }
                                break;
                        }
@@ -236,6 +255,96 @@ namespace sw
                }
        }
 
+       void SpirvShader::PopulateInterfaceSlot(std::vector<InterfaceComponent> *iface, Decorations const &d, AttribType type)
+       {
+               // Populate a single scalar slot in the interface from a collection of decorations and the intended component type.
+               auto scalarSlot = (d.Location << 2) | d.Component;
+
+               auto &slot = (*iface)[scalarSlot];
+               slot.Type = type;
+               slot.Flat = d.Flat;
+               slot.NoPerspective = d.NoPerspective;
+               slot.Centroid = d.Centroid;
+       }
+
+       int SpirvShader::PopulateInterfaceInner(std::vector<InterfaceComponent> *iface, uint32_t id, Decorations d)
+       {
+               // Recursively walks variable definition and its type tree, taking into account
+               // any explicit Location or Component decorations encountered; where explicit
+               // Locations or Components are not specified, assigns them sequentially.
+               // Collected decorations are carried down toward the leaves and across
+               // siblings; Effect of decorations intentionally does not flow back up the tree.
+               //
+               // Returns the next available location.
+
+               // This covers the rules in Vulkan 1.1 spec, 14.1.4 Location Assignment.
+
+               auto const it = decorations.find(id);
+               if (it != decorations.end())
+               {
+                       d.Apply(it->second);
+               }
+
+               auto const &obj = defs[id];
+               switch (obj.definition.opcode())
+               {
+               case spv::OpVariable:
+                       return PopulateInterfaceInner(iface, obj.definition.word(1), d);
+               case spv::OpTypePointer:
+                       return PopulateInterfaceInner(iface, obj.definition.word(3), d);
+               case spv::OpTypeMatrix:
+                       for (auto i = 0u; i < obj.definition.word(3); i++, d.Location++)
+                       {
+                               // consumes same components of N consecutive locations
+                               PopulateInterfaceInner(iface, obj.definition.word(2), d);
+                       }
+                       return d.Location;
+               case spv::OpTypeVector:
+                       for (auto i = 0u; i < obj.definition.word(3); i++, d.Component++)
+                       {
+                               // consumes N consecutive components in the same location
+                               PopulateInterfaceInner(iface, obj.definition.word(2), d);
+                       }
+                       return d.Location + 1;
+               case spv::OpTypeFloat:
+                       PopulateInterfaceSlot(iface, d, ATTRIBTYPE_FLOAT);
+                       return d.Location + 1;
+               case spv::OpTypeInt:
+                       PopulateInterfaceSlot(iface, d, obj.definition.word(3) ? ATTRIBTYPE_INT : ATTRIBTYPE_UINT);
+                       return d.Location + 1;
+               case spv::OpTypeBool:
+                       PopulateInterfaceSlot(iface, d, ATTRIBTYPE_UINT);
+                       return d.Location + 1;
+               case spv::OpTypeStruct:
+               {
+                       auto const memberDecorationsIt = memberDecorations.find(id);
+                       // iterate over members, which may themselves have Location/Component decorations
+                       for (auto i = 0u; i < obj.definition.wordCount() - 2; i++)
+                       {
+                               // Apply any member decorations for this member to the carried state.
+                               if (memberDecorationsIt != memberDecorations.end() && i < memberDecorationsIt->second.size())
+                               {
+                                       d.Apply(memberDecorationsIt->second[i]);
+                               }
+                               d.Location = PopulateInterfaceInner(iface, obj.definition.word(i + 2), d);
+                               d.Component = 0;    // Implicit locations always have component=0
+                       }
+                       return d.Location;
+               }
+                       // TODO: array
+               default:
+                       // Intentionally partial; most opcodes do not participate in type hierarchies
+                       return 0;
+               }
+       }
+
+       void SpirvShader::PopulateInterface(std::vector<InterfaceComponent> *iface, uint32_t id)
+       {
+               // Walk a variable definition and populate the interface from it.
+               Decorations d{};
+               PopulateInterfaceInner(iface, id, d);
+       }
+
        void SpirvShader::Decorations::Apply(spv::Decoration decoration, uint32_t arg)
        {
                switch (decoration)
@@ -256,7 +365,7 @@ namespace sw
                        Flat = true;
                        break;
                case spv::DecorationNoPerspective:
-                       Noperspective = true;
+                       NoPerspective = true;
                        break;
                case spv::DecorationCentroid:
                        Centroid = true;
@@ -295,7 +404,7 @@ namespace sw
                }
 
                Flat |= src.Flat;
-               Noperspective |= src.Noperspective;
+               NoPerspective |= src.NoPerspective;
                Centroid |= src.Centroid;
                Block |= src.Block;
                BufferBlock |= src.BufferBlock;
index ac53739..4d5e453 100644 (file)
@@ -163,14 +163,14 @@ namespace sw
                        bool HasBuiltIn : 1;
                        bool Flat : 1;
                        bool Centroid : 1;
-                       bool Noperspective : 1;
+                       bool NoPerspective : 1;
                        bool Block : 1;
                        bool BufferBlock : 1;
 
                        Decorations()
                                        : Location{-1}, Component{0}, BuiltIn{}, HasLocation{false}, HasComponent{false}, HasBuiltIn{false},
                                          Flat{false},
-                                         Centroid{false}, Noperspective{false}, Block{false},
+                                         Centroid{false}, NoPerspective{false}, Block{false},
                                          BufferBlock{false}
                        {
                        }
@@ -185,6 +185,22 @@ namespace sw
                std::unordered_map<uint32_t, Decorations> decorations;
                std::unordered_map<uint32_t, std::vector<Decorations>> memberDecorations;
 
+               struct InterfaceComponent
+               {
+                       AttribType Type;
+                       bool Flat : 1;
+                       bool Centroid : 1;
+                       bool NoPerspective : 1;
+
+                       InterfaceComponent()
+                                       : Type{ATTRIBTYPE_UNUSED}, Flat{false}, Centroid{false}, NoPerspective{false}
+                       {
+                       }
+               };
+
+               std::vector<InterfaceComponent> inputs;
+               std::vector<InterfaceComponent> outputs;
+
        private:
                const int serialID;
                static volatile int serialCounter;
@@ -196,6 +212,12 @@ namespace sw
                void ProcessExecutionMode(InsnIterator it);
 
                uint32_t ComputeTypeSize(InsnIterator insn);
+
+               void PopulateInterfaceSlot(std::vector<InterfaceComponent> *iface, Decorations const &d, AttribType type);
+
+               int PopulateInterfaceInner(std::vector<InterfaceComponent> *iface, uint32_t id, Decorations d);
+
+               void PopulateInterface(std::vector<InterfaceComponent> *iface, uint32_t id);
        };
 }