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,