layers: Add minimal actual analysis of interface blocks to DrawState

There's definitely still room for improvement here (SPIRV-Tools might have some stuff that will make some of these more complex module walks a little clearer); however, this adds enough support for interface blocks for us to generate all the same kinds of mismatch errors we were previously generating for loose inputs and outputs.

Signed-off-by: Chris Forbes <chrisforbes@google.com>
diff --git a/layers/draw_state.cpp b/layers/draw_state.cpp
index b9d1388..a9c809b 100644
--- a/layers/draw_state.cpp
+++ b/layers/draw_state.cpp
@@ -545,6 +545,91 @@
     /* TODO: collect the name, too? Isn't required to be present. */
 };
 
+
+static void
+collect_interface_block_members(layer_data *my_data, VkDevice dev,
+                                shader_module const *src,
+                                std::map<uint32_t, interface_var> &out,
+                                std::map<uint32_t, interface_var> &builtins_out,
+                                std::unordered_map<unsigned, unsigned> const &blocks,
+                                bool is_array_of_verts,
+                                uint32_t id,
+                                uint32_t type_id)
+{
+    /* Walk down the type_id presented, trying to determine whether it's actually an interface block. */
+    std::unordered_map<unsigned, unsigned>::const_iterator type_def_it;
+    while (true) {
+
+        type_def_it = src->type_def_index.find(type_id);
+        if (type_def_it == src->type_def_index.end()) {
+            return; /* this is actually broken SPIR-V */
+        }
+
+        unsigned int const *code = (unsigned int const *)&src->words[type_def_it->second];
+        unsigned opcode = code[0] & 0x0ffffu;
+
+        if (opcode == spv::OpTypePointer) {
+            type_id = code[3];
+        }
+        else if (opcode == spv::OpTypeArray && is_array_of_verts) {
+            type_id = code[2];
+            is_array_of_verts = false;
+        }
+        else if (opcode == spv::OpTypeStruct) {
+            if (blocks.find(type_id) == blocks.end()) {
+                /* This isn't an interface block. */
+                return;
+            }
+            else {
+                /* We have found the correct type. Walk its members. */
+                break;
+            }
+        }
+        else {
+            /* not an interface block */
+            return;
+        }
+    }
+
+    /* Walk OpMemberDecorate for type_id. */
+    unsigned int const *code = (unsigned int const *)&src->words[0];
+    size_t size = src->words.size();
+    unsigned word = 5;
+    while (word < size) {
+
+        unsigned opcode = code[word] & 0x0ffffu;
+        unsigned oplen = (code[word] & 0xffff0000u) >> 16;
+
+        if (opcode == spv::OpMemberDecorate && code[word+1] == type_id) {
+            unsigned member_index = code[word+2];
+            unsigned member_type_id = code[type_def_it->second + 2 + member_index];
+
+            if (code[word+3] == spv::DecorationLocation) {
+                unsigned location = code[word+4];
+                unsigned num_locations = get_locations_consumed_by_type(src, member_type_id, false);
+                for (unsigned int offset = 0; offset < num_locations; offset++) {
+                    interface_var v;
+                    v.id = id;
+                    /* TODO: member index in interface_var too? */
+                    v.type_id = member_type_id;
+                    v.offset = offset;
+                    out[location + offset] = v;
+                }
+            }
+            else if (code[word+3] == spv::DecorationBuiltIn) {
+                unsigned builtin = code[word+4];
+                interface_var v;
+                v.id = id;
+                v.type_id = member_type_id;
+                v.offset = 0;
+                builtins_out[builtin] = v;
+            }
+        }
+
+        word += oplen;
+    }
+}
+
 static void
 collect_interface_by_location(layer_data *my_data, VkDevice dev,
                               shader_module const *src, spv::StorageClass sinterface,
@@ -557,6 +642,7 @@
 
     std::unordered_map<unsigned, unsigned> var_locations;
     std::unordered_map<unsigned, unsigned> var_builtins;
+    std::unordered_map<unsigned, unsigned> blocks;
 
     unsigned word = 5;
     while (word < size) {
@@ -575,6 +661,10 @@
             if (code[word+2] == spv::DecorationBuiltIn) {
                 var_builtins[code[word+1]] = code[word+3];
             }
+
+            if (code[word+2] == spv::DecorationBlock) {
+                blocks[code[word+1]] = 1;
+            }
         }
 
         /* TODO: handle grouped decorations */
@@ -620,6 +710,11 @@
                 v.offset = 0;
                 builtins_out[builtin] = v;
             }
+            else {
+                /* An interface block instance */
+                collect_interface_block_members(my_data, dev, src, out, builtins_out,
+                                                blocks, is_array_of_verts, id, type);
+            }
         }
 
         word += oplen;
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 40f1b4b..05ce78c 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -4704,6 +4704,56 @@
     }
 }
 
+TEST_F(VkLayerTest, CreatePipelineFragmentInputNotProvidedInBlock)
+{
+    m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
+        "not written by vertex shader");
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
+
+    char const *vsSource =
+        "#version 400\n"
+        "#extension GL_ARB_separate_shader_objects: require\n"
+        "#extension GL_ARB_shading_language_420pack: require\n"
+        "\n"
+        "out gl_PerVertex {\n"
+        "    vec4 gl_Position;\n"
+        "};\n"
+        "void main(){\n"
+        "   gl_Position = vec4(1);\n"
+        "}\n";
+    char const *fsSource =
+        "#version 450\n"
+        "#extension GL_ARB_separate_shader_objects: require\n"
+        "#extension GL_ARB_shading_language_420pack: require\n"
+        "\n"
+        "in block { layout(location=0) float x; } ins;\n"
+        "layout(location=0) out vec4 color;\n"
+        "void main(){\n"
+        "   color = vec4(ins.x);\n"
+        "}\n";
+
+    VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
+    VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
+
+    VkPipelineObj pipe(m_device);
+    pipe.AddColorAttachment();
+    pipe.AddShader(&vs);
+    pipe.AddShader(&fs);
+
+    VkDescriptorSetObj descriptorSet(m_device);
+    descriptorSet.AppendDummy();
+    descriptorSet.CreateVKDescriptorSet(m_commandBuffer);
+
+    pipe.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderPass());
+
+    if (!m_errorMonitor->DesiredMsgFound()) {
+        FAIL() << "Did not receive Error 'not written by vertex shader'";
+        m_errorMonitor->DumpFailureMsgs();
+    }
+}
+
 TEST_F(VkLayerTest, CreatePipelineVsFsTypeMismatch)
 {
     m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
@@ -4756,6 +4806,59 @@
     }
 }
 
+TEST_F(VkLayerTest, CreatePipelineVsFsTypeMismatchInBlock)
+{
+    m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT,
+        "Type mismatch on location 0");
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
+
+    char const *vsSource =
+        "#version 450\n"
+        "#extension GL_ARB_separate_shader_objects: require\n"
+        "#extension GL_ARB_shading_language_420pack: require\n"
+        "\n"
+        "out block { layout(location=0) int x; } outs;\n"
+        "out gl_PerVertex {\n"
+        "    vec4 gl_Position;\n"
+        "};\n"
+        "void main(){\n"
+        "   outs.x = 0;\n"
+        "   gl_Position = vec4(1);\n"
+        "}\n";
+    char const *fsSource =
+        "#version 450\n"
+        "#extension GL_ARB_separate_shader_objects: require\n"
+        "#extension GL_ARB_shading_language_420pack: require\n"
+        "\n"
+        "in block { layout(location=0) float x; } ins;\n"  /* VS writes int */
+        "layout(location=0) out vec4 color;\n"
+        "void main(){\n"
+        "   color = vec4(ins.x);\n"
+        "}\n";
+
+    VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
+    VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
+
+    VkPipelineObj pipe(m_device);
+    pipe.AddColorAttachment();
+    pipe.AddShader(&vs);
+    pipe.AddShader(&fs);
+
+    VkDescriptorSetObj descriptorSet(m_device);
+    descriptorSet.AppendDummy();
+    descriptorSet.CreateVKDescriptorSet(m_commandBuffer);
+
+    pipe.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderPass());
+
+    if (!m_errorMonitor->DesiredMsgFound()) {
+        FAIL() << "Did not receive Error 'Type mismatch on location 0'";
+        m_errorMonitor->DumpFailureMsgs();
+    }
+}
+
+
 TEST_F(VkLayerTest, CreatePipelineAttribNotConsumed)
 {
     m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_PERF_WARN_BIT_EXT,
@@ -4815,6 +4918,7 @@
     }
 }
 
+
 TEST_F(VkLayerTest, CreatePipelineAttribLocationMismatch)
 {
     m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_PERF_WARN_BIT_EXT,