Add SPIR-V unused variable tests
authorRicardo Garcia <rgarcia@igalia.com>
Thu, 9 May 2019 08:01:37 +0000 (10:01 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Thu, 23 May 2019 13:28:14 +0000 (09:28 -0400)
These tests check everything works when a shader contains references to
variables that have a binding which is not present in the pipeline
layout. For the test to be legal, the variable cannot be used from the
entry point. The tests have two variants: either the variable is simply
unused or the variable is used by a function that is not part of the
entry point call tree.

New tests:
dEQP-VK.spirv_assembly.instruction.*.unused_variables.*

Components: Vulkan
VK-GL-CTS issue: 1038

Change-Id: Ib54c79da4d164e494c0004f0cb1503783e38bc70

android/cts/master/vk-master.txt
external/vulkancts/modules/vulkan/shaderexecutor/vktShaderExecutor.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.hpp
external/vulkancts/mustpass/master/vk-default-no-waivers.txt
external/vulkancts/mustpass/master/vk-default.txt

index 617b515..555c923 100755 (executable)
@@ -229948,6 +229948,10 @@ dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_sgreaterthane
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_sgreaterthan
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_slessthan
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_slessthanequal
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.variable_set_0_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.function_set_0_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.variable_set_5_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.function_set_5_binding_5
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.flat
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.no_perspective
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.relaxedprecision
@@ -230275,6 +230279,26 @@ dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse1_fra
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse1_frag2
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse2_frag1
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse2_frag2
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_frag_unused_var
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_frag_unused_var
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_frag_unused_func
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_frag_unused_func
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_geom_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_geom_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_geom_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_geom_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_unused_var_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_unused_var_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_unused_func_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_unused_func_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_normal_tesse_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_normal_tesse_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_normal_tesse_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_normal_tesse_unused_func_frag_normal
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_vert
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_tessc
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_tesse
index 3dec5b0..e445c6f 100644 (file)
@@ -2144,7 +2144,7 @@ std::string generateSpirv(const ShaderSpec& spec, const bool are16Bit, const boo
                "%ip_v3u32 = OpTypePointer Input %v3u32\n"
                "%up_float   = OpTypePointer Uniform " << inputType1 << "\n"
                "\n"
-               "%fun     = OpTypeFunction %void\n"
+               "%voidf   = OpTypeFunction %void\n"
                "%fp_u32  = OpTypePointer Function %u32\n"
                "%fp_i32  = OpTypePointer Function " << outputType << "\n"
                "%fp_f32  = OpTypePointer Function " << inputType1 << "\n"
@@ -2181,7 +2181,7 @@ std::string generateSpirv(const ShaderSpec& spec, const bool are16Bit, const boo
                "%up_ssboOUT  = OpTypePointer Uniform %ssboOUT\n"
                "%ssbo_dst    = OpVariable %up_ssboOUT Uniform\n"
                "\n"
-               "%BP_main = OpFunction %void None %fun\n"
+               "%BP_main = OpFunction %void None %voidf\n"
                "%BP_label = OpLabel\n"
                "%invocationNdx = OpVariable  %fp_u32 Function\n";
 
index cd5ed2b..2d06d96 100644 (file)
@@ -60,15 +60,16 @@ bool verifyOutputWithEpsilon (const std::vector<AllocationSp>& outputAllocs, con
 }
 }
 
-std::string getComputeAsmShaderPreamble (const std::string& capabilities, const std::string& extensions, const std::string& exeModes)
+std::string getComputeAsmShaderPreamble (const std::string& capabilities, const std::string& extensions, const std::string& exeModes, const std::string& extraEntryPoints)
 {
        return
                std::string("OpCapability Shader\n") +
                capabilities +
                extensions +
                "OpMemoryModel Logical GLSL450\n"
-               "OpEntryPoint GLCompute %main \"main\" %id\n"
-               "OpExecutionMode %main LocalSize 1 1 1\n"+
+               "OpEntryPoint GLCompute %main \"main\" %id\n" +
+               extraEntryPoints +
+               "OpExecutionMode %main LocalSize 1 1 1\n" +
                exeModes;
 }
 
@@ -167,7 +168,7 @@ std::string makeComputeShaderAssembly(const std::map<std::string, std::string>&
 
                "${pre_main:opt}\n"
 
-               "%BP_main   = OpFunction %void None %fun\n"
+               "%BP_main   = OpFunction %void None %voidf\n"
                "%BP_label  = OpLabel\n"
                "%BP_id3ul  = OpLoad %v3u32 %BP_id3u\n"
                "%BP_id4u   = OpCompositeConstruct %v4u32 %BP_id3ul %c_u32_0\n"
index db93dbb..229413e 100644 (file)
@@ -372,7 +372,7 @@ struct ComputeShaderSpec
  * \brief Helper functions for SPIR-V assembly shared by various tests
  *//*--------------------------------------------------------------------*/
 
-std::string getComputeAsmShaderPreamble                                (const std::string& capabilities = "", const std::string& extensions = "", const std::string& exeModes = "");
+std::string getComputeAsmShaderPreamble                                (const std::string& capabilities = "", const std::string& extensions = "", const std::string& exeModes = "", const std::string& extraEntryPoints = "");
 const char* getComputeAsmShaderPreambleWithoutLocalSize         (void);
 std::string getComputeAsmCommonTypes                           (std::string blockStorageClass = "Uniform");
 const char*    getComputeAsmCommonInt64Types                   (void);
index 92c1edd..39212f3 100644 (file)
@@ -321,6 +321,80 @@ string InstanceContext::getSpecializedFailMessage (const string& failureReason)
        return StringTemplate(failMessageTemplate).specialize(parameters);
 }
 
+InstanceContext createInstanceContext (const std::vector<ShaderElement>&                       elements,
+                                                                          const tcu::RGBA                                                              (&inputColors)[4],
+                                                                          const tcu::RGBA                                                              (&outputColors)[4],
+                                                                          const std::map<std::string, std::string>&    testCodeFragments,
+                                                                          const StageToSpecConstantMap&                                specConstants,
+                                                                          const PushConstants&                                                 pushConstants,
+                                                                          const GraphicsResources&                                             resources,
+                                                                          const GraphicsInterfaces&                                    interfaces,
+                                                                          const std::vector<std::string>&                              extensions,
+                                                                          VulkanFeatures                                                               vulkanFeatures,
+                                                                          VkShaderStageFlags                                                   customizedStages,
+                                                                          const qpTestResult                                                   failResult,
+                                                                          const std::string&                                                   failMessageTemplate)
+{
+       InstanceContext ctx (inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources, interfaces, extensions, vulkanFeatures, customizedStages);
+       for (size_t i = 0; i < elements.size(); ++i)
+       {
+               ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage));
+               ctx.requiredStages = static_cast<VkShaderStageFlagBits>(ctx.requiredStages | elements[i].stage);
+       }
+       ctx.failResult                          = failResult;
+       if (!failMessageTemplate.empty())
+               ctx.failMessageTemplate = failMessageTemplate;
+       return ctx;
+}
+
+InstanceContext createInstanceContext (const std::vector<ShaderElement>&                       elements,
+                                                                          tcu::RGBA                                                                    (&inputColors)[4],
+                                                                          const tcu::RGBA                                                              (&outputColors)[4],
+                                                                          const std::map<std::string, std::string>&    testCodeFragments)
+{
+       return createInstanceContext(elements, inputColors, outputColors, testCodeFragments,
+                                                                StageToSpecConstantMap(), PushConstants(), GraphicsResources(),
+                                                                GraphicsInterfaces(), std::vector<std::string>(),
+                                                                VulkanFeatures(), vk::VK_SHADER_STAGE_ALL);
+}
+
+InstanceContext createInstanceContext (const std::vector<ShaderElement>&                       elements,
+                                                                          const std::map<std::string, std::string>&    testCodeFragments)
+{
+       tcu::RGBA defaultColors[4];
+       getDefaultColors(defaultColors);
+       return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments);
+}
+
+UnusedVariableContext createUnusedVariableContext(const ShaderTaskArray& shaderTasks, const VariableLocation& location)
+{
+       for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(shaderTasks); ++i)
+       {
+               DE_ASSERT(shaderTasks[i] >= 0 && shaderTasks[i] < SHADER_TASK_LAST);
+       }
+
+       std::vector<ShaderElement> elements;
+
+       DE_ASSERT(shaderTasks[SHADER_TASK_INDEX_VERTEX]         != SHADER_TASK_NONE);
+       DE_ASSERT(shaderTasks[SHADER_TASK_INDEX_FRAGMENT]       != SHADER_TASK_NONE);
+       elements.push_back(ShaderElement("vert", "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
+       elements.push_back(ShaderElement("frag", "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
+
+       if (shaderTasks[SHADER_TASK_INDEX_GEOMETRY] != SHADER_TASK_NONE)
+               elements.push_back(ShaderElement("geom", "main", vk::VK_SHADER_STAGE_GEOMETRY_BIT));
+
+       if (shaderTasks[SHADER_TASK_INDEX_TESS_CONTROL] != SHADER_TASK_NONE)
+               elements.push_back(ShaderElement("tessc", "main", vk::VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT));
+
+       if (shaderTasks[SHADER_TASK_INDEX_TESS_EVAL] != SHADER_TASK_NONE)
+               elements.push_back(ShaderElement("tesse", "main", vk::VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT));
+
+       return UnusedVariableContext(
+               createInstanceContext(elements, map<string, string>()),
+               shaderTasks,
+               location);
+}
+
 ShaderElement::ShaderElement (const string&                            moduleName_,
                                                          const string&                         entryPoint_,
                                                          VkShaderStageFlagBits         shaderStage_)
@@ -431,7 +505,7 @@ string makeVertexShaderAssembly (const map<string, string>& fragments)
                "%BP_gl_InstanceIndex = OpVariable %ip_i32 Input\n"
                "${pre_main:opt}\n"
                "${IF_variable:opt}\n"
-               "%BP_main = OpFunction %void None %fun\n"
+               "%BP_main = OpFunction %void None %voidf\n"
                "%BP_label = OpLabel\n"
                "${IF_carryforward:opt}\n"
                "${post_interface_op_vert:opt}\n"
@@ -531,7 +605,7 @@ string makeTessControlShaderAssembly (const map<string, string>& fragments)
                "${pre_main:opt}\n"
                "${IF_variable:opt}\n"
 
-               "%BP_main = OpFunction %void None %fun\n"
+               "%BP_main = OpFunction %void None %voidf\n"
                "%BP_label = OpLabel\n"
                "%BP_gl_Invoc = OpLoad %i32 %BP_gl_InvocationID\n"
                "${IF_carryforward:opt}\n"
@@ -648,7 +722,7 @@ string makeTessEvalShaderAssembly (const map<string, string>& fragments)
                "%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
                "${pre_main:opt}\n"
                "${IF_variable:opt}\n"
-               "%BP_main = OpFunction %void None %fun\n"
+               "%BP_main = OpFunction %void None %voidf\n"
                "%BP_label = OpLabel\n"
                "${IF_carryforward:opt}\n"
                "${post_interface_op_tesse:opt}\n"
@@ -794,7 +868,7 @@ string makeGeometryShaderAssembly (const map<string, string>& fragments)
                "${pre_main:opt}\n"
                "${IF_variable:opt}\n"
 
-               "%BP_main = OpFunction %void None %fun\n"
+               "%BP_main = OpFunction %void None %voidf\n"
                "%BP_label = OpLabel\n"
 
                "${IF_carryforward:opt}\n"
@@ -898,7 +972,7 @@ string makeFragmentShaderAssembly (const map<string, string>& fragments)
                "%BP_vtxColor = OpVariable %ip_v4f32 Input\n"
                "${pre_main:opt}\n"
                "${IF_variable:opt}\n"
-               "%BP_main = OpFunction %void None %fun\n"
+               "%BP_main = OpFunction %void None %voidf\n"
                "%BP_label_main = OpLabel\n"
                "${IF_carryforward:opt}\n"
                "${post_interface_op_frag:opt}\n"
@@ -1525,7 +1599,7 @@ void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx)
                                                "%frag_vtxColor = OpVariable %ip_v4f32 Input\n"
 
                                                "; Vertex Entry\n"
-                                               "%vert_main = OpFunction %void None %fun\n"
+                                               "%vert_main = OpFunction %void None %voidf\n"
                                                "%vert_label = OpLabel\n"
                                                "%vert_tmp_position = OpLoad %v4f32 %vert_Position\n";
 
@@ -1545,7 +1619,7 @@ void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx)
        if (useGeometry)
        {
                combinedModule <<       "; Geometry Entry\n"
-                                                       "%geom_main = OpFunction %void None %fun\n"
+                                                       "%geom_main = OpFunction %void None %voidf\n"
                                                        "%geom_label = OpLabel\n"
                                                        "%geom_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_0 %c_i32_0\n"
                                                        "%geom_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_1 %c_i32_0\n"
@@ -1576,7 +1650,7 @@ void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx)
        if (useTessellation)
        {
                combinedModule <<       "; Tessellation Control Entry\n"
-                                                       "%tessc_main = OpFunction %void None %fun\n"
+                                                       "%tessc_main = OpFunction %void None %voidf\n"
                                                        "%tessc_label = OpLabel\n"
                                                        "%tessc_invocation_id = OpLoad %i32 %tessc_gl_InvocationID\n"
                                                        "%tessc_in_color_ptr = OpAccessChain %ip_v4f32 %tessc_in_color %tessc_invocation_id\n"
@@ -1605,7 +1679,7 @@ void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx)
                                                        "OpFunctionEnd\n"
 
                                                        "; Tessellation Evaluation Entry\n"
-                                                       "%tesse_main = OpFunction %void None %fun\n"
+                                                       "%tesse_main = OpFunction %void None %voidf\n"
                                                        "%tesse_label = OpLabel\n"
                                                        "%tesse_tc_0_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_0\n"
                                                        "%tesse_tc_1_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_1\n"
@@ -1643,7 +1717,7 @@ void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx)
        }
 
        combinedModule  <<      "; Fragment Entry\n"
-                                               "%frag_main = OpFunction %void None %fun\n"
+                                               "%frag_main = OpFunction %void None %voidf\n"
                                                "%frag_label_main = OpLabel\n"
                                                "%frag_tmp1 = OpLoad %v4f32 %frag_vtxColor\n"
                                                "OpStore %frag_fragColor %frag_tmp1\n"
@@ -1653,6 +1727,461 @@ void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx)
        dst.spirvAsmSources.add("module") << combinedModule.str();
 }
 
+void createUnusedVariableModules (vk::SourceCollections& dst, UnusedVariableContext ctx)
+{
+       if (ctx.shaderTasks[SHADER_TASK_INDEX_VERTEX] != SHADER_TASK_NONE)
+       {
+               std::ostringstream      shader;
+               bool                            tessellation = (ctx.shaderTasks[SHADER_TASK_INDEX_TESS_CONTROL] != SHADER_TASK_NONE
+                                                                                       || ctx.shaderTasks[SHADER_TASK_INDEX_TESS_EVAL] != SHADER_TASK_NONE);
+               const ShaderTask&       task = ctx.shaderTasks[SHADER_TASK_INDEX_VERTEX];
+
+               shader  << "OpCapability Shader\n"
+                               << "OpMemoryModel Logical GLSL450\n";
+
+               // Entry point depends on if tessellation is enabled or not to provide the vertex position.
+               shader  << "OpEntryPoint Vertex %main \"main\" %Position %vtxColor %color "
+                               << (tessellation ? "%vtxPosition" : "%vtx_glPerVertex")
+                               << " %vertex_id %instance_id\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader << getUnusedEntryPoint();
+               }
+
+               // Decorations.
+               shader  << "OpDecorate %Position Location 0\n"
+                               << "OpDecorate %vtxColor Location 1\n"
+                               << "OpDecorate %color Location 1\n"
+                               << "OpDecorate %vertex_id BuiltIn VertexIndex\n"
+                               << "OpDecorate %instance_id BuiltIn InstanceIndex\n";
+               if (tessellation)
+               {
+                       shader  << "OpDecorate %vtxPosition Location 2\n";
+               }
+               else
+               {
+                       shader  << "OpMemberDecorate %vert_per_vertex_out 0 BuiltIn Position\n"
+                                       << "OpMemberDecorate %vert_per_vertex_out 1 BuiltIn PointSize\n"
+                                       << "OpMemberDecorate %vert_per_vertex_out 2 BuiltIn ClipDistance\n"
+                                       << "OpMemberDecorate %vert_per_vertex_out 3 BuiltIn CullDistance\n"
+                                       << "OpDecorate %vert_per_vertex_out Block\n";
+               }
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedDecorations(ctx.variableLocation);
+               }
+
+               // Standard types, constants and arrays.
+               shader  << "; Start of standard types, constants and arrays\n"
+                               << SPIRV_ASSEMBLY_TYPES
+                               << SPIRV_ASSEMBLY_CONSTANTS
+                               << SPIRV_ASSEMBLY_ARRAYS
+                               << "; End of standard types, constants and arrays\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedTypesAndConstants();
+               }
+
+               // Variables.
+               if (tessellation)
+               {
+                       shader  << "%vtxPosition = OpVariable %op_v4f32 Output\n";
+               }
+               else
+               {
+                       shader  << "%vert_per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+                                       << "%vert_op_per_vertex_out = OpTypePointer Output %vert_per_vertex_out\n"
+                                       << "%vtx_glPerVertex = OpVariable %vert_op_per_vertex_out Output\n";
+               }
+               shader  << "%Position = OpVariable %ip_v4f32 Input\n"
+                               << "%vtxColor = OpVariable %op_v4f32 Output\n"
+                               << "%color = OpVariable %ip_v4f32 Input\n"
+                               << "%vertex_id = OpVariable %ip_i32 Input\n"
+                               << "%instance_id = OpVariable %ip_i32 Input\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedBuffer();
+               }
+
+               // Vertex main function.
+               shader  << "%main = OpFunction %void None %voidf\n"
+                               << "%label = OpLabel\n"
+                               << "%tmp_position = OpLoad %v4f32 %Position\n";
+               if (tessellation)
+               {
+                       shader  << "OpStore %vtxPosition %tmp_position\n";
+               }
+               else
+               {
+                       shader  << "%vert_out_pos_ptr = OpAccessChain %op_v4f32 %vtx_glPerVertex %c_i32_0\n"
+                                       << "OpStore %vert_out_pos_ptr %tmp_position\n";
+               }
+               shader  << "%tmp_color = OpLoad %v4f32 %color\n"
+                               << "OpStore %vtxColor %tmp_color\n"
+                               << "OpReturn\n"
+                               << "OpFunctionEnd\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedFunctionBody();
+               }
+
+               dst.spirvAsmSources.add("vert") << shader.str();
+       }
+
+       if (ctx.shaderTasks[SHADER_TASK_INDEX_GEOMETRY] != SHADER_TASK_NONE)
+       {
+               const ShaderTask&       task = ctx.shaderTasks[SHADER_TASK_INDEX_GEOMETRY];
+               std::ostringstream      shader;
+
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader << getOpCapabilityShader();
+               }
+               shader  << "OpCapability Geometry\n"
+                               << "OpMemoryModel Logical GLSL450\n";
+
+               // Entry points.
+               shader  << "OpEntryPoint Geometry %geom1_main \"main\" %out_gl_position %gl_in %out_color %in_color\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedEntryPoint();
+               }
+               shader  << "OpExecutionMode %geom1_main Triangles\n"
+                               << "OpExecutionMode %geom1_main OutputTriangleStrip\n"
+                               << "OpExecutionMode %geom1_main OutputVertices 3\n";
+
+               // Decorations.
+               shader  << "OpDecorate %out_gl_position BuiltIn Position\n"
+                               << "OpMemberDecorate %per_vertex_in 0 BuiltIn Position\n"
+                               << "OpMemberDecorate %per_vertex_in 1 BuiltIn PointSize\n"
+                               << "OpMemberDecorate %per_vertex_in 2 BuiltIn ClipDistance\n"
+                               << "OpMemberDecorate %per_vertex_in 3 BuiltIn CullDistance\n"
+                               << "OpDecorate %per_vertex_in Block\n"
+                               << "OpDecorate %out_color Location 1\n"
+                               << "OpDecorate %in_color Location 1\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedDecorations(ctx.variableLocation);
+               }
+
+               // Standard types, constants and arrays.
+               shader  << "; Start of standard types, constants and arrays\n"
+                               << SPIRV_ASSEMBLY_TYPES
+                               << SPIRV_ASSEMBLY_CONSTANTS
+                               << SPIRV_ASSEMBLY_ARRAYS
+                               << "; End of standard types, constants and arrays\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedTypesAndConstants();
+               }
+
+               // Variables.
+               shader  << "%per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+                               << "%a3_per_vertex_in = OpTypeArray %per_vertex_in %c_u32_3\n"
+                               << "%ip_a3_per_vertex_in = OpTypePointer Input %a3_per_vertex_in\n"
+                               << "%gl_in = OpVariable %ip_a3_per_vertex_in Input\n"
+                               << "%out_color = OpVariable %op_v4f32 Output\n"
+                               << "%in_color = OpVariable %ip_a3v4f32 Input\n"
+                               << "%out_gl_position = OpVariable %op_v4f32 Output\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader << getUnusedBuffer();
+               }
+
+               // Main function.
+               shader  << "%geom1_main = OpFunction %void None %voidf\n"
+                               << "%geom1_label = OpLabel\n"
+                               << "%geom1_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
+                               << "%geom1_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
+                               << "%geom1_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_2 %c_i32_0\n"
+                               << "%geom1_in_position_0 = OpLoad %v4f32 %geom1_gl_in_0_gl_position\n"
+                               << "%geom1_in_position_1 = OpLoad %v4f32 %geom1_gl_in_1_gl_position\n"
+                               << "%geom1_in_position_2 = OpLoad %v4f32 %geom1_gl_in_2_gl_position \n"
+                               << "%geom1_in_color_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
+                               << "%geom1_in_color_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
+                               << "%geom1_in_color_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
+                               << "%geom1_in_color_0 = OpLoad %v4f32 %geom1_in_color_0_ptr\n"
+                               << "%geom1_in_color_1 = OpLoad %v4f32 %geom1_in_color_1_ptr\n"
+                               << "%geom1_in_color_2 = OpLoad %v4f32 %geom1_in_color_2_ptr\n"
+                               << "OpStore %out_gl_position %geom1_in_position_0\n"
+                               << "OpStore %out_color %geom1_in_color_0\n"
+                               << "OpEmitVertex\n"
+                               << "OpStore %out_gl_position %geom1_in_position_1\n"
+                               << "OpStore %out_color %geom1_in_color_1\n"
+                               << "OpEmitVertex\n"
+                               << "OpStore %out_gl_position %geom1_in_position_2\n"
+                               << "OpStore %out_color %geom1_in_color_2\n"
+                               << "OpEmitVertex\n"
+                               << "OpEndPrimitive\n"
+                               << "OpReturn\n"
+                               << "OpFunctionEnd\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedFunctionBody();
+               }
+
+               dst.spirvAsmSources.add("geom") << shader.str();
+       }
+
+       if (ctx.shaderTasks[SHADER_TASK_INDEX_TESS_CONTROL]     != SHADER_TASK_NONE)
+       {
+               const ShaderTask&       task = ctx.shaderTasks[SHADER_TASK_INDEX_TESS_CONTROL];
+               std::ostringstream      shader;
+
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getOpCapabilityShader();
+               }
+               shader  << "OpCapability Tessellation\n"
+                               << "OpMemoryModel Logical GLSL450\n";
+
+               // Entry point.
+               shader  << "OpEntryPoint TessellationControl %tessc1_main \"main\" %out_color %gl_InvocationID %in_color %out_position %in_position %gl_TessLevelOuter %gl_TessLevelInner\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedEntryPoint();
+               }
+               shader  << "OpExecutionMode %tessc1_main OutputVertices 3\n";
+
+               // Decorations.
+               shader  << "OpDecorate %out_color Location 1\n"
+                               << "OpDecorate %gl_InvocationID BuiltIn InvocationId\n"
+                               << "OpDecorate %in_color Location 1\n"
+                               << "OpDecorate %out_position Location 2\n"
+                               << "OpDecorate %in_position Location 2\n"
+                               << "OpDecorate %gl_TessLevelOuter Patch\n"
+                               << "OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter\n"
+                               << "OpDecorate %gl_TessLevelInner Patch\n"
+                               << "OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedDecorations(ctx.variableLocation);
+               }
+
+               // Standard types, constants and arrays.
+               shader  << "; Start of standard types, constants and arrays\n"
+                               << SPIRV_ASSEMBLY_TYPES
+                               << SPIRV_ASSEMBLY_CONSTANTS
+                               << SPIRV_ASSEMBLY_ARRAYS
+                               << "; End of standard types, constants and arrays\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedTypesAndConstants();
+               }
+
+               // Variables.
+               shader  << "%out_color = OpVariable %op_a3v4f32 Output\n"
+                               << "%gl_InvocationID = OpVariable %ip_i32 Input\n"
+                               << "%in_color = OpVariable %ip_a32v4f32 Input\n"
+                               << "%out_position = OpVariable %op_a3v4f32 Output\n"
+                               << "%in_position = OpVariable %ip_a32v4f32 Input\n"
+                               << "%gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
+                               << "%gl_TessLevelInner = OpVariable %op_a2f32 Output\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader << getUnusedBuffer();
+               }
+
+               // Main entry point.
+               shader  << "%tessc1_main = OpFunction %void None %voidf\n"
+                               << "%tessc1_label = OpLabel\n"
+                               << "%tessc1_invocation_id = OpLoad %i32 %gl_InvocationID\n"
+                               << "%tessc1_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc1_invocation_id\n"
+                               << "%tessc1_in_position_ptr = OpAccessChain %ip_v4f32 %in_position %tessc1_invocation_id\n"
+                               << "%tessc1_in_color_val = OpLoad %v4f32 %tessc1_in_color_ptr\n"
+                               << "%tessc1_in_position_val = OpLoad %v4f32 %tessc1_in_position_ptr\n"
+                               << "%tessc1_out_color_ptr = OpAccessChain %op_v4f32 %out_color %tessc1_invocation_id\n"
+                               << "%tessc1_out_position_ptr = OpAccessChain %op_v4f32 %out_position %tessc1_invocation_id\n"
+                               << "OpStore %tessc1_out_color_ptr %tessc1_in_color_val\n"
+                               << "OpStore %tessc1_out_position_ptr %tessc1_in_position_val\n"
+                               << "%tessc1_is_first_invocation = OpIEqual %bool %tessc1_invocation_id %c_i32_0\n"
+                               << "OpSelectionMerge %tessc1_merge_label None\n"
+                               << "OpBranchConditional %tessc1_is_first_invocation %tessc1_first_invocation %tessc1_merge_label\n"
+                               << "%tessc1_first_invocation = OpLabel\n"
+                               << "%tessc1_tess_outer_0 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_0\n"
+                               << "%tessc1_tess_outer_1 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_1\n"
+                               << "%tessc1_tess_outer_2 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_2\n"
+                               << "%tessc1_tess_inner = OpAccessChain %op_f32 %gl_TessLevelInner %c_i32_0\n"
+                               << "OpStore %tessc1_tess_outer_0 %c_f32_1\n"
+                               << "OpStore %tessc1_tess_outer_1 %c_f32_1\n"
+                               << "OpStore %tessc1_tess_outer_2 %c_f32_1\n"
+                               << "OpStore %tessc1_tess_inner %c_f32_1\n"
+                               << "OpBranch %tessc1_merge_label\n"
+                               << "%tessc1_merge_label = OpLabel\n"
+                               << "OpReturn\n"
+                               << "OpFunctionEnd\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedFunctionBody();
+               }
+
+               dst.spirvAsmSources.add("tessc") << shader.str();
+       }
+
+       if (ctx.shaderTasks[SHADER_TASK_INDEX_TESS_EVAL] != SHADER_TASK_NONE)
+       {
+               const ShaderTask&       task = ctx.shaderTasks[SHADER_TASK_INDEX_TESS_EVAL];
+               std::ostringstream      shader;
+
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getOpCapabilityShader();
+               }
+               shader  << "OpCapability Tessellation\n"
+                               << "OpMemoryModel Logical GLSL450\n";
+
+               // Entry point.
+               shader  << "OpEntryPoint TessellationEvaluation %tesse1_main \"main\" %stream %gl_tessCoord %in_position %out_color %in_color \n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedEntryPoint();
+               }
+               shader  << "OpExecutionMode %tesse1_main Triangles\n"
+                               << "OpExecutionMode %tesse1_main SpacingEqual\n"
+                               << "OpExecutionMode %tesse1_main VertexOrderCcw\n";
+
+               // Decorations.
+               shader  << "OpMemberDecorate %per_vertex_out 0 BuiltIn Position\n"
+                               << "OpMemberDecorate %per_vertex_out 1 BuiltIn PointSize\n"
+                               << "OpMemberDecorate %per_vertex_out 2 BuiltIn ClipDistance\n"
+                               << "OpMemberDecorate %per_vertex_out 3 BuiltIn CullDistance\n"
+                               << "OpDecorate %per_vertex_out Block\n"
+                               << "OpDecorate %gl_tessCoord BuiltIn TessCoord\n"
+                               << "OpDecorate %in_position Location 2\n"
+                               << "OpDecorate %out_color Location 1\n"
+                               << "OpDecorate %in_color Location 1\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedDecorations(ctx.variableLocation);
+               }
+
+               // Standard types, constants and arrays.
+               shader  << "; Start of standard types, constants and arrays\n"
+                               << SPIRV_ASSEMBLY_TYPES
+                               << SPIRV_ASSEMBLY_CONSTANTS
+                               << SPIRV_ASSEMBLY_ARRAYS
+                               << "; End of standard types, constants and arrays\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedTypesAndConstants();
+               }
+
+               // Variables.
+               shader  << "%per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+                               << "%op_per_vertex_out = OpTypePointer Output %per_vertex_out\n"
+                               << "%stream = OpVariable %op_per_vertex_out Output\n"
+                               << "%gl_tessCoord = OpVariable %ip_v3f32 Input\n"
+                               << "%in_position = OpVariable %ip_a32v4f32 Input\n"
+                               << "%out_color = OpVariable %op_v4f32 Output\n"
+                               << "%in_color = OpVariable %ip_a32v4f32 Input\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader << getUnusedBuffer();
+               }
+
+               // Main entry point.
+               shader  << "%tesse1_main = OpFunction %void None %voidf\n"
+                               << "%tesse1_label = OpLabel\n"
+                               << "%tesse1_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
+                               << "%tesse1_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
+                               << "%tesse1_tc_2_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_2\n"
+                               << "%tesse1_tc_0 = OpLoad %f32 %tesse1_tc_0_ptr\n"
+                               << "%tesse1_tc_1 = OpLoad %f32 %tesse1_tc_1_ptr\n"
+                               << "%tesse1_tc_2 = OpLoad %f32 %tesse1_tc_2_ptr\n"
+                               << "%tesse1_in_pos_0_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_0\n"
+                               << "%tesse1_in_pos_1_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_1\n"
+                               << "%tesse1_in_pos_2_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_2\n"
+                               << "%tesse1_in_pos_0 = OpLoad %v4f32 %tesse1_in_pos_0_ptr\n"
+                               << "%tesse1_in_pos_1 = OpLoad %v4f32 %tesse1_in_pos_1_ptr\n"
+                               << "%tesse1_in_pos_2 = OpLoad %v4f32 %tesse1_in_pos_2_ptr\n"
+                               << "%tesse1_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_0 %tesse1_tc_0\n"
+                               << "%tesse1_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_1 %tesse1_tc_1\n"
+                               << "%tesse1_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_2 %tesse1_tc_2\n"
+                               << "%tesse1_out_pos_ptr = OpAccessChain %op_v4f32 %stream %c_i32_0\n"
+                               << "%tesse1_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse1_in_pos_0_weighted %tesse1_in_pos_1_weighted\n"
+                               << "%tesse1_computed_out = OpFAdd %v4f32 %tesse1_in_pos_0_plus_pos_1 %tesse1_in_pos_2_weighted\n"
+                               << "OpStore %tesse1_out_pos_ptr %tesse1_computed_out\n"
+                               << "%tesse1_in_clr_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
+                               << "%tesse1_in_clr_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
+                               << "%tesse1_in_clr_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
+                               << "%tesse1_in_clr_0 = OpLoad %v4f32 %tesse1_in_clr_0_ptr\n"
+                               << "%tesse1_in_clr_1 = OpLoad %v4f32 %tesse1_in_clr_1_ptr\n"
+                               << "%tesse1_in_clr_2 = OpLoad %v4f32 %tesse1_in_clr_2_ptr\n"
+                               << "%tesse1_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_0 %tesse1_tc_0\n"
+                               << "%tesse1_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_1 %tesse1_tc_1\n"
+                               << "%tesse1_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_2 %tesse1_tc_2\n"
+                               << "%tesse1_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse1_in_clr_0_weighted %tesse1_in_clr_1_weighted\n"
+                               << "%tesse1_computed_clr = OpFAdd %v4f32 %tesse1_in_clr_0_plus_col_1 %tesse1_in_clr_2_weighted\n"
+                               << "OpStore %out_color %tesse1_computed_clr\n"
+                               << "OpReturn\n"
+                               << "OpFunctionEnd\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedFunctionBody();
+               }
+
+               dst.spirvAsmSources.add("tesse") << shader.str();
+       }
+
+       if (ctx.shaderTasks[SHADER_TASK_INDEX_FRAGMENT] != SHADER_TASK_NONE)
+       {
+               const ShaderTask&       task = ctx.shaderTasks[SHADER_TASK_INDEX_FRAGMENT];
+               std::ostringstream      shader;
+
+               shader  << "OpCapability Shader\n"
+                               << "OpMemoryModel Logical GLSL450\n";
+
+               // Entry point.
+               shader  << "OpEntryPoint Fragment %main \"main\" %vtxColor %fragColor\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedEntryPoint();
+               }
+               shader  << "OpExecutionMode %main OriginUpperLeft\n";
+
+               // Decorations.
+               shader  << "OpDecorate %fragColor Location 0\n"
+                               << "OpDecorate %vtxColor Location 1\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedDecorations(ctx.variableLocation);
+               }
+
+               // Standard types, constants and arrays.
+               shader  << "; Start of standard types, constants and arrays\n"
+                               << SPIRV_ASSEMBLY_TYPES
+                               << SPIRV_ASSEMBLY_CONSTANTS
+                               << SPIRV_ASSEMBLY_ARRAYS
+                               << "; End of standard types, constants and arrays\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader  << getUnusedTypesAndConstants();
+               }
+
+               // Variables.
+               shader  << "%fragColor = OpVariable %op_v4f32 Output\n"
+                               << "%vtxColor = OpVariable %ip_v4f32 Input\n";
+               if (task != SHADER_TASK_NORMAL)
+               {
+                       shader << getUnusedBuffer();
+               }
+
+               // Main entry point.
+               shader  << "%main = OpFunction %void None %voidf\n"
+                               << "%label_main = OpLabel\n"
+                               << "%tmp1 = OpLoad %v4f32 %vtxColor\n"
+                               << "OpStore %fragColor %tmp1\n"
+                               << "OpReturn\n"
+                               << "OpFunctionEnd\n";
+               if (task == SHADER_TASK_UNUSED_FUNC)
+               {
+                       shader  << getUnusedFunctionBody();
+               }
+
+               dst.spirvAsmSources.add("frag") << shader.str();
+       }
+}
+
 void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
 {
        dst.spirvAsmSources.add("vert") <<
@@ -1680,7 +2209,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "%vertex_id = OpVariable %ip_i32 Input\n"
                "%instance_id = OpVariable %ip_i32 Input\n"
 
-               "%main = OpFunction %void None %fun\n"
+               "%main = OpFunction %void None %voidf\n"
                "%label = OpLabel\n"
                "%tmp_position = OpLoad %v4f32 %Position\n"
                "OpStore %vtxPosition %tmp_position\n"
@@ -1689,7 +2218,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "OpReturn\n"
                "OpFunctionEnd\n"
 
-               "%main2 = OpFunction %void None %fun\n"
+               "%main2 = OpFunction %void None %voidf\n"
                "%label2 = OpLabel\n"
                "%tmp_position2 = OpLoad %v4f32 %Position\n"
                "OpStore %vtxPosition %tmp_position2\n"
@@ -1720,14 +2249,14 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "%fragColor = OpVariable %op_v4f32 Output\n"
                "%vtxColor = OpVariable %ip_v4f32 Input\n"
 
-               "%main = OpFunction %void None %fun\n"
+               "%main = OpFunction %void None %voidf\n"
                "%label_main = OpLabel\n"
                "%tmp1 = OpLoad %v4f32 %vtxColor\n"
                "OpStore %fragColor %tmp1\n"
                "OpReturn\n"
                "OpFunctionEnd\n"
 
-               "%main2 = OpFunction %void None %fun\n"
+               "%main2 = OpFunction %void None %voidf\n"
                "%label_main2 = OpLabel\n"
                "%tmp2 = OpLoad %v4f32 %vtxColor\n"
                "%tmp3 = OpFSub %v4f32 %cval %tmp2\n"
@@ -1767,7 +2296,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "%in_color = OpVariable %ip_a3v4f32 Input\n"
                "%out_gl_position = OpVariable %op_v4f32 Output\n"
 
-               "%geom1_main = OpFunction %void None %fun\n"
+               "%geom1_main = OpFunction %void None %voidf\n"
                "%geom1_label = OpLabel\n"
                "%geom1_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
                "%geom1_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
@@ -1794,7 +2323,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "OpReturn\n"
                "OpFunctionEnd\n"
 
-               "%geom2_main = OpFunction %void None %fun\n"
+               "%geom2_main = OpFunction %void None %voidf\n"
                "%geom2_label = OpLabel\n"
                "%geom2_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
                "%geom2_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
@@ -1855,7 +2384,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "%gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
                "%gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
 
-               "%tessc1_main = OpFunction %void None %fun\n"
+               "%tessc1_main = OpFunction %void None %voidf\n"
                "%tessc1_label = OpLabel\n"
                "%tessc1_invocation_id = OpLoad %i32 %gl_InvocationID\n"
                "%tessc1_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc1_invocation_id\n"
@@ -1883,7 +2412,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "OpReturn\n"
                "OpFunctionEnd\n"
 
-               "%tessc2_main = OpFunction %void None %fun\n"
+               "%tessc2_main = OpFunction %void None %voidf\n"
                "%tessc2_label = OpLabel\n"
                "%tessc2_invocation_id = OpLoad %i32 %gl_InvocationID\n"
                "%tessc2_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc2_invocation_id\n"
@@ -1945,7 +2474,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "%out_color = OpVariable %op_v4f32 Output\n"
                "%in_color = OpVariable %ip_a32v4f32 Input\n"
 
-               "%tesse1_main = OpFunction %void None %fun\n"
+               "%tesse1_main = OpFunction %void None %voidf\n"
                "%tesse1_label = OpLabel\n"
                "%tesse1_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
                "%tesse1_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
@@ -1981,7 +2510,7 @@ void createMultipleEntries (vk::SourceCollections& dst, InstanceContext)
                "OpReturn\n"
                "OpFunctionEnd\n"
 
-               "%tesse2_main = OpFunction %void None %fun\n"
+               "%tesse2_main = OpFunction %void None %voidf\n"
                "%tesse2_label = OpLabel\n"
                "%tesse2_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
                "%tesse2_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
@@ -2454,6 +2983,11 @@ VkImageAspectFlags getImageAspectFlags (VkFormat format)
        return aspectFlags;
 };
 
+TestStatus runAndVerifyUnusedVariablePipeline (Context &context, UnusedVariableContext unusedVariableContext)
+{
+       return runAndVerifyDefaultPipeline(context, unusedVariableContext.instanceContext);
+}
+
 TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance)
 {
        if (getMinRequiredVulkanVersion(instance.resources.spirvVersion) > context.getUsedApiVersion())
index e6d51a3..57f5bc1 100644 (file)
@@ -302,6 +302,40 @@ struct InstanceContext
        std::string getSpecializedFailMessage (const std::string& failureReason);
 };
 
+enum ShaderTask
+{
+       SHADER_TASK_NONE = 0,
+       SHADER_TASK_NORMAL,
+       SHADER_TASK_UNUSED_VAR,
+       SHADER_TASK_UNUSED_FUNC,
+       SHADER_TASK_LAST
+};
+
+enum ShaderTaskIndex
+{
+       SHADER_TASK_INDEX_VERTEX                = 0,
+       SHADER_TASK_INDEX_GEOMETRY              = 1,
+       SHADER_TASK_INDEX_TESS_CONTROL  = 2,
+       SHADER_TASK_INDEX_TESS_EVAL             = 3,
+       SHADER_TASK_INDEX_FRAGMENT              = 4,
+       SHADER_TASK_INDEX_LAST                  = 5
+};
+
+typedef ShaderTask ShaderTaskArray[SHADER_TASK_INDEX_LAST];
+
+struct UnusedVariableContext
+{
+       InstanceContext         instanceContext;
+       ShaderTaskArray         shaderTasks;
+       VariableLocation        variableLocation;
+
+       UnusedVariableContext(const InstanceContext& ctx, const ShaderTaskArray& tasks, const VariableLocation& location)
+               : instanceContext(ctx), shaderTasks(), variableLocation(location)
+       {
+               deMemcpy(shaderTasks, tasks, sizeof(tasks));
+       }
+};
+
 // A description of a shader to be used for a single stage of the graphics pipeline.
 struct ShaderElement
 {
@@ -334,18 +368,20 @@ void getInvertedDefaultColors (tcu::RGBA (&colors)[4]);
 // Creates fragments that specialize into a simple pass-through shader (of any kind).
 std::map<std::string, std::string> passthruFragments (void);
 
-// Creates a combined shader module based on VkShaderStageFlagBits defined in InstanceContext
+// Creates a combined shader module based on VkShaderStageFlagBits defined in InstanceContext.
 void createCombinedModule (vk::SourceCollections& dst, InstanceContext ctx);
 
+// Creates shaders with unused variables based on the UnusedVariableContext.
+void createUnusedVariableModules (vk::SourceCollections& dst, UnusedVariableContext ctx);
+
 // This has two shaders of each stage. The first
 // is a passthrough, the second inverts the color.
 void createMultipleEntries (vk::SourceCollections& dst, InstanceContext);
 
-// Turns a statically sized array of ShaderElements into an instance-context
-// by setting up the mapping of modules to their contained shaders and stages.
-// The inputs and expected outputs are given by inputColors and outputColors
-template<size_t N>
-InstanceContext createInstanceContext (const ShaderElement                                                     (&elements)[N],
+// Turns a vector of ShaderElements into an instance-context by setting up the mapping of modules
+// to their contained shaders and stages. The inputs and expected outputs are given by inputColors
+// and outputColors
+InstanceContext createInstanceContext (const std::vector<ShaderElement>&                       elements,
                                                                           const tcu::RGBA                                                              (&inputColors)[4],
                                                                           const tcu::RGBA                                                              (&outputColors)[4],
                                                                           const std::map<std::string, std::string>&    testCodeFragments,
@@ -357,43 +393,63 @@ InstanceContext createInstanceContext (const ShaderElement                                                        (&elements)[N],
                                                                           VulkanFeatures                                                               vulkanFeatures,
                                                                           VkShaderStageFlags                                                   customizedStages,
                                                                           const qpTestResult                                                   failResult                      = QP_TEST_RESULT_FAIL,
-                                                                          const std::string&                                                   failMessageTemplate     = std::string())
+                                                                          const std::string&                                                   failMessageTemplate     = std::string());
+
+// Same as above but using a statically sized array.
+template <size_t N>
+inline InstanceContext createInstanceContext (const ShaderElement                                                      (&elements)[N],
+                                                                                         const tcu::RGBA                                                               (&inputColors)[4],
+                                                                                         const tcu::RGBA                                                               (&outputColors)[4],
+                                                                                         const std::map<std::string, std::string>&             testCodeFragments,
+                                                                                         const StageToSpecConstantMap&                                 specConstants,
+                                                                                         const PushConstants&                                                  pushConstants,
+                                                                                         const GraphicsResources&                                              resources,
+                                                                                         const GraphicsInterfaces&                                             interfaces,
+                                                                                         const std::vector<std::string>&                               extensions,
+                                                                                         VulkanFeatures                                                                vulkanFeatures,
+                                                                                         VkShaderStageFlags                                                    customizedStages,
+                                                                                         const qpTestResult                                                    failResult                      = QP_TEST_RESULT_FAIL,
+                                                                                         const std::string&                                                    failMessageTemplate     = std::string())
 {
-       InstanceContext ctx (inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources, interfaces, extensions, vulkanFeatures, customizedStages);
-       for (size_t i = 0; i < N; ++i)
-       {
-               ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage));
-               ctx.requiredStages = static_cast<VkShaderStageFlagBits>(ctx.requiredStages | elements[i].stage);
-       }
-       ctx.failResult                          = failResult;
-       if (!failMessageTemplate.empty())
-               ctx.failMessageTemplate = failMessageTemplate;
-       return ctx;
+       std::vector<ShaderElement> elementsVector(elements, elements + N);
+       return createInstanceContext(elementsVector, inputColors, outputColors, testCodeFragments, specConstants, pushConstants,
+                                                                resources, interfaces, extensions, vulkanFeatures, customizedStages, failResult, failMessageTemplate);
 }
 
 // The same as createInstanceContext above, without extensions, spec constants, and resources.
-template<size_t N>
-inline InstanceContext createInstanceContext (const ShaderElement                                              (&elements)[N],
-                                                                                         tcu::RGBA                                                                     (&inputColors)[4],
-                                                                                         const tcu::RGBA                                                       (&outputColors)[4],
-                                                                                         const std::map<std::string, std::string>&     testCodeFragments)
+InstanceContext createInstanceContext (const std::vector<ShaderElement>&                       elements,
+                                                                          tcu::RGBA                                                                    (&inputColors)[4],
+                                                                          const tcu::RGBA                                                              (&outputColors)[4],
+                                                                          const std::map<std::string, std::string>&    testCodeFragments);
+
+// Same as above, but using a statically sized array.
+template <size_t N>
+inline InstanceContext createInstanceContext (const ShaderElement                                                      (&elements)[N],
+                                                                                         tcu::RGBA                                                                             (&inputColors)[4],
+                                                                                         const tcu::RGBA                                                               (&outputColors)[4],
+                                                                                         const std::map<std::string, std::string>&             testCodeFragments)
 {
-       return createInstanceContext(elements, inputColors, outputColors, testCodeFragments,
-                                                                StageToSpecConstantMap(), PushConstants(), GraphicsResources(),
-                                                                GraphicsInterfaces(), std::vector<std::string>(),
-                                                                VulkanFeatures(), vk::VK_SHADER_STAGE_ALL);
+       std::vector<ShaderElement> elementsVector(elements, elements + N);
+       return createInstanceContext(elementsVector, inputColors, outputColors, testCodeFragments);
 }
 
 // The same as createInstanceContext above, but with default colors.
+InstanceContext createInstanceContext (const std::vector<ShaderElement>&                       elements,
+                                                                          const std::map<std::string, std::string>&    testCodeFragments);
+
+// Same as above, but using a statically sized array.
 template<size_t N>
-InstanceContext createInstanceContext (const ShaderElement                                                     (&elements)[N],
-                                                                          const std::map<std::string, std::string>&    testCodeFragments)
+inline InstanceContext createInstanceContext (const ShaderElement                                              (&elements)[N],
+                                                                                         const std::map<std::string, std::string>&     testCodeFragments)
 {
-       tcu::RGBA defaultColors[4];
-       getDefaultColors(defaultColors);
-       return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments);
+       std::vector<ShaderElement> elementsVector(elements, elements + N);
+       return createInstanceContext(elementsVector, testCodeFragments);
 }
 
+
+// Create an unused variable context for the given combination.
+UnusedVariableContext createUnusedVariableContext(const ShaderTaskArray& shaderTasks, const VariableLocation& location);
+
 void addShaderCodeCustomVertex (vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
 void addShaderCodeCustomTessControl (vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
 void addShaderCodeCustomTessEval (vk::SourceCollections& dst, InstanceContext& context, const SpirVAsmBuildOptions* spirVAsmBuildOptions);
@@ -539,6 +595,9 @@ inline void createTestsForAllStages (const std::string& name,
 // whatever the shaders draw can be directly spot-checked.
 tcu::TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance);
 
+// Use the instance context in the UnusedVariableContext to run the function above.
+tcu::TestStatus runAndVerifyUnusedVariablePipeline (Context &context, UnusedVariableContext unusedVariableContext);
+
 // Adds a new test to group using custom fragments for the tessellation-control
 // stage and passthrough fragments for all other stages.  Uses default colors
 // for input and expected output.
index 623f4c1..f93f583 100644 (file)
@@ -379,6 +379,128 @@ tcu::TestCaseGroup* createOpNopGroup (tcu::TestContext& testCtx)
        return group.release();
 }
 
+tcu::TestCaseGroup* createUnusedVariableComputeTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group                   (new tcu::TestCaseGroup(testCtx, "unused_variables", "Compute shaders with unused variables"));
+       de::Random                                              rnd                             (deStringHash(group->getName()));
+       const int                                               numElements             = 100;
+       vector<float>                                   positiveFloats  (numElements, 0);
+       vector<float>                                   negativeFloats  (numElements, 0);
+
+       fillRandomScalars(rnd, 1.f, 100.f, &positiveFloats[0], numElements);
+
+       for (size_t ndx = 0; ndx < numElements; ++ndx)
+               negativeFloats[ndx] = -positiveFloats[ndx];
+
+       const VariableLocation                  testLocations[] =
+       {
+               // Set          Binding
+               { 0,            5                       },
+               { 5,            5                       },
+       };
+
+       for (size_t locationNdx = 0; locationNdx < DE_LENGTH_OF_ARRAY(testLocations); ++locationNdx)
+       {
+               const VariableLocation& location = testLocations[locationNdx];
+
+               // Unused variable.
+               {
+                       ComputeShaderSpec                               spec;
+
+                       spec.assembly =
+                               string(getComputeAsmShaderPreamble()) +
+
+                               "OpDecorate %id BuiltIn GlobalInvocationId\n"
+
+                               + getUnusedDecorations(location)
+
+                               + string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes())
+
+                               + getUnusedTypesAndConstants()
+
+                               + string(getComputeAsmInputOutputBuffer())
+
+                               + getUnusedBuffer() +
+
+                               "%id        = OpVariable %uvec3ptr Input\n"
+                               "%zero      = OpConstant %i32 0\n"
+
+                               "%main      = OpFunction %void None %voidf\n"
+                               "%label     = OpLabel\n"
+                               "%idval     = OpLoad %uvec3 %id\n"
+                               "%x         = OpCompositeExtract %u32 %idval 0\n"
+
+                               "%inloc     = OpAccessChain %f32ptr %indata %zero %x\n"
+                               "%inval     = OpLoad %f32 %inloc\n"
+                               "%neg       = OpFNegate %f32 %inval\n"
+                               "%outloc    = OpAccessChain %f32ptr %outdata %zero %x\n"
+                               "             OpStore %outloc %neg\n"
+                               "             OpReturn\n"
+                               "             OpFunctionEnd\n";
+                       spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
+                       spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
+                       spec.numWorkGroups = IVec3(numElements, 1, 1);
+
+                       std::string testName            = "variable_" + location.toString();
+                       std::string testDescription     = "Unused variable test with " + location.toDescription();
+
+                       group->addChild(new SpvAsmComputeShaderCase(testCtx, testName.c_str(), testDescription.c_str(), spec));
+               }
+
+               // Unused function.
+               {
+                       ComputeShaderSpec                               spec;
+
+                       spec.assembly =
+                               string(getComputeAsmShaderPreamble("", "", "", getUnusedEntryPoint())) +
+
+                               "OpDecorate %id BuiltIn GlobalInvocationId\n"
+
+                               + getUnusedDecorations(location)
+
+                               + string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes())
+
+                               + getUnusedTypesAndConstants() +
+
+                               "%c_i32_0 = OpConstant %i32 0\n"
+                               "%c_i32_1 = OpConstant %i32 1\n"
+
+                               + string(getComputeAsmInputOutputBuffer())
+
+                               + getUnusedBuffer() +
+
+                               "%id        = OpVariable %uvec3ptr Input\n"
+                               "%zero      = OpConstant %i32 0\n"
+
+                               "%main      = OpFunction %void None %voidf\n"
+                               "%label     = OpLabel\n"
+                               "%idval     = OpLoad %uvec3 %id\n"
+                               "%x         = OpCompositeExtract %u32 %idval 0\n"
+
+                               "%inloc     = OpAccessChain %f32ptr %indata %zero %x\n"
+                               "%inval     = OpLoad %f32 %inloc\n"
+                               "%neg       = OpFNegate %f32 %inval\n"
+                               "%outloc    = OpAccessChain %f32ptr %outdata %zero %x\n"
+                               "             OpStore %outloc %neg\n"
+                               "             OpReturn\n"
+                               "             OpFunctionEnd\n"
+
+                               + getUnusedFunctionBody();
+
+                       spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
+                       spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
+                       spec.numWorkGroups = IVec3(numElements, 1, 1);
+
+                       std::string testName            = "function_" + location.toString();
+                       std::string testDescription     = "Unused function test with " + location.toDescription();
+
+                       group->addChild(new SpvAsmComputeShaderCase(testCtx, testName.c_str(), testDescription.c_str(), spec));
+               }
+       }
+
+       return group.release();
+}
+
 template<bool nanSupported>
 bool compareFUnord (const std::vector<Resource>& inputs, const vector<AllocationSp>& outputAllocs, const std::vector<Resource>& expectedOutputs, TestLog& log)
 {
@@ -8197,6 +8319,93 @@ tcu::TestCaseGroup* createModuleTests(tcu::TestContext& testCtx)
        return moduleTests.release();
 }
 
+std::string getUnusedVarTestNamePiece(const std::string& prefix, ShaderTask task)
+{
+       switch (task)
+       {
+               case SHADER_TASK_NONE:                  return "";
+               case SHADER_TASK_NORMAL:                return prefix + "_normal";
+               case SHADER_TASK_UNUSED_VAR:    return prefix + "_unused_var";
+               case SHADER_TASK_UNUSED_FUNC:   return prefix + "_unused_func";
+               default:                                                DE_ASSERT(DE_FALSE);
+       }
+       // unreachable
+       return "";
+}
+
+std::string getShaderTaskIndexName(ShaderTaskIndex index)
+{
+       switch (index)
+       {
+       case SHADER_TASK_INDEX_VERTEX:                  return "vertex";
+       case SHADER_TASK_INDEX_GEOMETRY:                return "geom";
+       case SHADER_TASK_INDEX_TESS_CONTROL:    return "tessc";
+       case SHADER_TASK_INDEX_TESS_EVAL:               return "tesse";
+       case SHADER_TASK_INDEX_FRAGMENT:                return "frag";
+       default:                                                                DE_ASSERT(DE_FALSE);
+       }
+       // unreachable
+       return "";
+}
+
+std::string getUnusedVarTestName(const ShaderTaskArray& shaderTasks, const VariableLocation& location)
+{
+       std::string testName = location.toString();
+
+       for (size_t i = 0; i < DE_LENGTH_OF_ARRAY(shaderTasks); ++i)
+       {
+               if (shaderTasks[i] != SHADER_TASK_NONE)
+               {
+                       testName += "_" + getUnusedVarTestNamePiece(getShaderTaskIndexName((ShaderTaskIndex)i), shaderTasks[i]);
+               }
+       }
+
+       return testName;
+}
+
+tcu::TestCaseGroup* createUnusedVariableTests(tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup>         moduleTests                             (new tcu::TestCaseGroup(testCtx, "unused_variables", "Graphics shaders with unused variables"));
+
+       ShaderTaskArray                                         shaderCombinations[]    =
+       {
+               // Vertex                                       Geometry                                        Tess. Control                           Tess. Evaluation                        Fragment
+               { SHADER_TASK_UNUSED_VAR,       SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NORMAL      },
+               { SHADER_TASK_UNUSED_FUNC,      SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NORMAL      },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_UNUSED_VAR  },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_UNUSED_FUNC },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_UNUSED_VAR,         SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NORMAL      },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_UNUSED_FUNC,        SHADER_TASK_NONE,                       SHADER_TASK_NONE,                       SHADER_TASK_NORMAL      },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_NONE,                       SHADER_TASK_UNUSED_VAR,         SHADER_TASK_NORMAL,                     SHADER_TASK_NORMAL      },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_NONE,                       SHADER_TASK_UNUSED_FUNC,        SHADER_TASK_NORMAL,                     SHADER_TASK_NORMAL      },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_NONE,                       SHADER_TASK_NORMAL,                     SHADER_TASK_UNUSED_VAR,         SHADER_TASK_NORMAL      },
+               { SHADER_TASK_NORMAL,           SHADER_TASK_NONE,                       SHADER_TASK_NORMAL,                     SHADER_TASK_UNUSED_FUNC,        SHADER_TASK_NORMAL      }
+       };
+
+       const VariableLocation                          testLocations[] =
+       {
+               // Set          Binding
+               { 0,            5                       },
+               { 5,            5                       },
+       };
+
+       for (size_t combNdx = 0; combNdx < DE_LENGTH_OF_ARRAY(shaderCombinations); ++combNdx)
+       {
+               for (size_t locationNdx = 0; locationNdx < DE_LENGTH_OF_ARRAY(testLocations); ++locationNdx)
+               {
+                       const ShaderTaskArray&  shaderTasks             = shaderCombinations[combNdx];
+                       const VariableLocation& location                = testLocations[locationNdx];
+                       std::string                             testName                = getUnusedVarTestName(shaderTasks, location);
+
+                       addFunctionCaseWithPrograms<UnusedVariableContext>(
+                               moduleTests.get(), testName, "", createUnusedVariableModules, runAndVerifyUnusedVariablePipeline,
+                               createUnusedVariableContext(shaderTasks, location));
+               }
+       }
+
+       return moduleTests.release();
+}
+
 tcu::TestCaseGroup* createLoopTests(tcu::TestContext& testCtx)
 {
        de::MovePtr<tcu::TestCaseGroup> testGroup(new tcu::TestCaseGroup(testCtx, "loop", "Looping control flow"));
@@ -18449,6 +18658,7 @@ tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
        computeTests->addChild(createWorkgroupMemoryComputeGroup(testCtx));
        computeTests->addChild(createSpirvIdsAbuseGroup(testCtx));
        computeTests->addChild(createSignedIntCompareGroup(testCtx));
+       computeTests->addChild(createUnusedVariableComputeTests(testCtx));
 
        graphicsTests->addChild(createCrossStageInterfaceTests(testCtx));
        graphicsTests->addChild(createSpivVersionCheckTests(testCtx, !testComputePipeline));
@@ -18464,6 +18674,7 @@ tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
        graphicsTests->addChild(createOpUndefTests(testCtx));
        graphicsTests->addChild(createSelectionBlockOrderTests(testCtx));
        graphicsTests->addChild(createModuleTests(testCtx));
+       graphicsTests->addChild(createUnusedVariableTests(testCtx));
        graphicsTests->addChild(createSwitchBlockOrderTests(testCtx));
        graphicsTests->addChild(createOpPhiTests(testCtx));
        graphicsTests->addChild(createNoContractionTests(testCtx));
index dd880a3..5e15141 100644 (file)
@@ -38,6 +38,16 @@ namespace SpirVAssembly
 
 using namespace vk;
 
+std::string VariableLocation::toString() const
+{
+       return "set_" + de::toString(set) + "_binding_" + de::toString(binding);
+}
+
+std::string VariableLocation::toDescription() const
+{
+       return "Set " + de::toString(set) + " and Binding " + de::toString(binding);
+}
+
 bool is8BitStorageFeaturesSupported (const Context& context, Extension8BitStorageFeatures toCheck)
 {
        VkPhysicalDevice8BitStorageFeaturesKHR extensionFeatures = context.get8BitStorageFeatures();
@@ -599,5 +609,51 @@ std::vector<deFloat16> getFloat16s (de::Random& rnd, deUint32 count)
        return float16;
 }
 
+std::string getOpCapabilityShader()
+{
+       return  "OpCapability Shader\n";
+}
+
+std::string getUnusedEntryPoint()
+{
+       return  "OpEntryPoint Vertex %unused_func \"unused_func\"\n";
+}
+
+std::string getUnusedDecorations(const VariableLocation& location)
+{
+       return  "OpMemberDecorate %UnusedBufferType 0 Offset 0\n"
+            "OpMemberDecorate %UnusedBufferType 1 Offset 4\n"
+            "OpDecorate %UnusedBufferType BufferBlock\n"
+            "OpDecorate %unused_buffer DescriptorSet " + de::toString(location.set) + "\n"
+            "OpDecorate %unused_buffer Binding " + de::toString(location.binding) + "\n";
+}
+
+std::string getUnusedTypesAndConstants()
+{
+       return  "%c_f32_101 = OpConstant %f32 101\n"
+                       "%c_i32_201 = OpConstant %i32 201\n"
+                       "%UnusedBufferType = OpTypeStruct %f32 %i32\n"
+                       "%unused_ptr_Uniform_UnusedBufferType = OpTypePointer Uniform %UnusedBufferType\n"
+                       "%unused_ptr_Uniform_float = OpTypePointer Uniform %f32\n"
+                       "%unused_ptr_Uniform_int = OpTypePointer Uniform %i32\n";
+}
+
+std::string getUnusedBuffer()
+{
+       return  "%unused_buffer = OpVariable %unused_ptr_Uniform_UnusedBufferType Uniform\n";
+}
+
+std::string getUnusedFunctionBody()
+{
+       return  "%unused_func = OpFunction %void None %voidf\n"
+                       "%unused_func_label = OpLabel\n"
+                       "%unused_out_float_ptr = OpAccessChain %unused_ptr_Uniform_float %unused_buffer %c_i32_0\n"
+            "OpStore %unused_out_float_ptr %c_f32_101\n"
+                       "%unused_out_int_ptr = OpAccessChain %unused_ptr_Uniform_int %unused_buffer %c_i32_1\n"
+            "OpStore %unused_out_int_ptr %c_i32_201\n"
+            "OpReturn\n"
+            "OpFunctionEnd\n";
+}
+
 } // SpirVAssembly
 } // vkt
index 2efca10..b0007b0 100644 (file)
@@ -64,7 +64,7 @@ namespace SpirVAssembly
                                                                                                                                                                                                                        \
        "%v4f32_v4f32_function = OpTypeFunction %v4f32 %v4f32\n"                                                                        \
        "%bool_function = OpTypeFunction %bool\n"                                                                                                                               \
-       "%fun = OpTypeFunction %void\n"                                                                                                                                                 \
+       "%voidf = OpTypeFunction %void\n"                                                                                                                                                       \
                                                                                                                                                                                                                        \
        "%ip_f32 = OpTypePointer Input %f32\n"                                                                                                                                  \
        "%ip_i32 = OpTypePointer Input %i32\n"                                                                                                                                  \
@@ -271,6 +271,18 @@ struct VulkanFeatures
        }
 };
 
+struct VariableLocation
+{
+       deUint32 set;
+       deUint32 binding;
+
+       // Returns a string representation of the structure suitable for test names.
+       std::string toString() const ;
+
+       // Returns a string representation of the structure suitable for test descriptions.
+       std::string toDescription() const;
+};
+
 // Returns true if the given 8bit storage extension features in `toCheck` are all supported.
 bool is8BitStorageFeaturesSupported (const Context&                                            context,
                                                                          Extension8BitStorageFeatures          toCheck);
@@ -353,6 +365,24 @@ std::vector<float> getFloat32s (de::Random& rnd, deUint32 count);
 // Expected count to be at least 14 (numPicks).
 std::vector<deFloat16> getFloat16s (de::Random& rnd, deUint32 count);
 
+// Generate an OpCapability Shader line.
+std::string getOpCapabilityShader();
+
+// Generate an unused Vertex entry point.
+std::string getUnusedEntryPoint();
+
+// Generate unused decorations for an input/output buffer.
+std::string getUnusedDecorations(const VariableLocation& location);
+
+// Generate unused types and constants, including a buffer type.
+std::string getUnusedTypesAndConstants();
+
+// Generate the declaration of an unused buffer variable.
+std::string getUnusedBuffer();
+
+// Generate the body of an unused function that uses the previous buffer.
+std::string getUnusedFunctionBody();
+
 } // SpirVAssembly
 } // vkt
 
index 0d237a9..d0e23ef 100644 (file)
@@ -229962,6 +229962,10 @@ dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_sgreaterthane
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_sgreaterthan
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_slessthan
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_slessthanequal
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.variable_set_0_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.function_set_0_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.variable_set_5_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.function_set_5_binding_5
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.flat
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.no_perspective
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.relaxedprecision
@@ -230289,6 +230293,26 @@ dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse1_fra
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse1_frag2
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse2_frag1
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse2_frag2
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_frag_unused_var
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_frag_unused_var
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_frag_unused_func
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_frag_unused_func
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_geom_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_geom_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_geom_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_geom_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_unused_var_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_unused_var_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_unused_func_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_unused_func_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_normal_tesse_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_normal_tesse_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_normal_tesse_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_normal_tesse_unused_func_frag_normal
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_vert
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_tessc
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_tesse
index 2cd44f7..bca82fc 100644 (file)
@@ -229962,6 +229962,10 @@ dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_sgreaterthane
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_sgreaterthan
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_slessthan
 dEQP-VK.spirv_assembly.instruction.compute.signed_int_compare.uint_slessthanequal
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.variable_set_0_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.function_set_0_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.variable_set_5_binding_5
+dEQP-VK.spirv_assembly.instruction.compute.unused_variables.function_set_5_binding_5
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.flat
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.no_perspective
 dEQP-VK.spirv_assembly.instruction.graphics.cross_stage.basic_type.relaxedprecision
@@ -230289,6 +230293,26 @@ dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse1_fra
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse1_frag2
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse2_frag1
 dEQP-VK.spirv_assembly.instruction.graphics.module.vert2_geom2_tessc2_tesse2_frag2
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_frag_unused_var
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_frag_unused_var
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_frag_unused_func
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_frag_unused_func
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_geom_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_geom_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_geom_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_geom_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_unused_var_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_unused_var_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_unused_func_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_unused_func_tesse_normal_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_normal_tesse_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_normal_tesse_unused_var_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_0_binding_5_vertex_normal_tessc_normal_tesse_unused_func_frag_normal
+dEQP-VK.spirv_assembly.instruction.graphics.unused_variables.set_5_binding_5_vertex_normal_tessc_normal_tesse_unused_func_frag_normal
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_vert
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_tessc
 dEQP-VK.spirv_assembly.instruction.graphics.switch_block_order.out_of_order_tesse