Add framework for graphics-pipeline tests in spirv_assembly.
authorDejan Mircevski <deki@google.com>
Thu, 19 Nov 2015 18:35:33 +0000 (13:35 -0500)
committerAndrew Woloszyn <awoloszyn@google.com>
Wed, 2 Dec 2015 20:02:01 +0000 (15:02 -0500)
This allows single test code to be exercised in all shader kinds (except
for compute shader).

Implement assembly boilerplate as a StringTemplate with parameters in
certain sections that can be specialized.

Extend StringTemplate to accept optional parameter expansions, where a
missing parameter isn't an error.

Make SpirVAsmSource::operator<<() work on any parameter type.

external/vulkancts/framework/vulkan/vkSpirVProgram.hpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
framework/common/tcuStringTemplate.cpp

index f6717c4..863a89c 100644 (file)
@@ -34,6 +34,7 @@
  * \brief SPIR-V program and binary info.
  *//*--------------------------------------------------------------------*/
 
  * \brief SPIR-V program and binary info.
  *//*--------------------------------------------------------------------*/
 
+#include <string>
 #include <sstream>
 
 #include "vkDefs.hpp"
 #include <sstream>
 
 #include "vkDefs.hpp"
@@ -44,7 +45,8 @@ namespace vk
 
 struct SpirVAsmSource
 {
 
 struct SpirVAsmSource
 {
-       SpirVAsmSource& operator<<(const char* val)
+       template<typename T>
+       SpirVAsmSource& operator<<(const T& val)
        {
                program << val;
                return *this;
        {
                program << val;
                return *this;
index 463b049..d4e6c67 100644 (file)
 
 #include "vktSpvAsmInstructionTests.hpp"
 
 
 #include "vktSpvAsmInstructionTests.hpp"
 
+#include "tcuCommandLine.hpp"
+#include "tcuFormatUtil.hpp"
+#include "tcuRGBA.hpp"
+#include "tcuStringTemplate.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkDeviceUtil.hpp"
+#include "vkMemUtil.hpp"
+#include "vkPlatform.hpp"
+#include "vkPrograms.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkRef.hpp"
+#include "vkRefUtil.hpp"
+#include "vkStrUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deClock.h"
 #include "deRandom.hpp"
 #include "deRandom.hpp"
+#include "deStringUtil.hpp"
 #include "deUniquePtr.hpp"
 #include "tcuStringTemplate.hpp"
 
 #include "vktSpvAsmComputeShaderCase.hpp"
 #include "vktSpvAsmComputeShaderTestUtil.hpp"
 #include "deUniquePtr.hpp"
 #include "tcuStringTemplate.hpp"
 
 #include "vktSpvAsmComputeShaderCase.hpp"
 #include "vktSpvAsmComputeShaderTestUtil.hpp"
+#include "vktTestCaseUtil.hpp"
+
+#include <map>
+#include <string>
+#include <sstream>
 
 namespace vkt
 {
 
 namespace vkt
 {
@@ -49,12 +74,24 @@ namespace SpirVAssembly
 namespace
 {
 
 namespace
 {
 
+using namespace vk;
 using std::map;
 using std::string;
 using std::vector;
 using tcu::IVec3;
 using std::map;
 using std::string;
 using std::vector;
 using tcu::IVec3;
+using tcu::IVec4;
+using tcu::RGBA;
+using tcu::TestLog;
+using tcu::TestStatus;
+using tcu::Vec4;
+using de::UniquePtr;
 using tcu::StringTemplate;
 
 using tcu::StringTemplate;
 
+typedef Unique<VkShaderModule>                 ModuleHandleUp;
+typedef de::SharedPtr<ModuleHandleUp>  ModuleHandleSp;
+typedef Unique<VkShader>                               VkShaderUp;
+typedef de::SharedPtr<VkShaderUp>              VkShaderSp;
+
 template<typename T>   T                       randomScalar    (de::Random& rnd, T minValue, T maxValue);
 template<> inline              float           randomScalar    (de::Random& rnd, float minValue, float maxValue)               { return rnd.getFloat(minValue, maxValue);      }
 template<> inline              deInt32         randomScalar    (de::Random& rnd, deInt32 minValue, deInt32 maxValue)   { return rnd.getInt(minValue, maxValue);        }
 template<typename T>   T                       randomScalar    (de::Random& rnd, T minValue, T maxValue);
 template<> inline              float           randomScalar    (de::Random& rnd, float minValue, float maxValue)               { return rnd.getFloat(minValue, maxValue);      }
 template<> inline              deInt32         randomScalar    (de::Random& rnd, deInt32 minValue, deInt32 maxValue)   { return rnd.getInt(minValue, maxValue);        }
@@ -90,9 +127,9 @@ static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* ds
 // }
 
 static const char* const s_ShaderPreamble =
 // }
 
 static const char* const s_ShaderPreamble =
-       "OpCapability Shader\n"
-       "OpMemoryModel Logical GLSL450\n"
-       "OpEntryPoint GLCompute %main \"main\" %id\n"
+               "OpCapability Shader\n"
+               "OpMemoryModel Logical GLSL450\n"
+               "OpEntryPoint GLCompute %main \"main\" %id\n"
        "OpExecutionMode %main LocalSize 1 1 1\n";
 
 static const char* const s_CommonTypes =
        "OpExecutionMode %main LocalSize 1 1 1\n";
 
 static const char* const s_CommonTypes =
@@ -119,14 +156,14 @@ static const char* const s_InputOutputBuffer =
 // Declares buffer type and layout for uniform variables indata and outdata. Both of them are SSBO bounded to descriptor set 0.
 // indata is at binding point 0, while outdata is at 1.
 static const char* const s_InputOutputBufferTraits =
 // Declares buffer type and layout for uniform variables indata and outdata. Both of them are SSBO bounded to descriptor set 0.
 // indata is at binding point 0, while outdata is at 1.
 static const char* const s_InputOutputBufferTraits =
-       "OpDecorate %inbuf BufferBlock\n"
-       "OpDecorate %indata DescriptorSet 0\n"
-       "OpDecorate %indata Binding 0\n"
-       "OpDecorate %outbuf BufferBlock\n"
-       "OpDecorate %outdata DescriptorSet 0\n"
-       "OpDecorate %outdata Binding 1\n"
-       "OpDecorate %f32arr ArrayStride 4\n"
-       "OpMemberDecorate %inbuf 0 Offset 0\n"
+               "OpDecorate %inbuf BufferBlock\n"
+               "OpDecorate %indata DescriptorSet 0\n"
+               "OpDecorate %indata Binding 0\n"
+               "OpDecorate %outbuf BufferBlock\n"
+               "OpDecorate %outdata DescriptorSet 0\n"
+               "OpDecorate %outdata Binding 1\n"
+               "OpDecorate %f32arr ArrayStride 4\n"
+               "OpMemberDecorate %inbuf 0 Offset 0\n"
        "OpMemberDecorate %outbuf 0 Offset 0\n";
 
 tcu::TestCaseGroup* createOpNopGroup (tcu::TestContext& testCtx)
        "OpMemberDecorate %outbuf 0 Offset 0\n";
 
 tcu::TestCaseGroup* createOpNopGroup (tcu::TestContext& testCtx)
@@ -1642,16 +1679,1318 @@ tcu::TestCaseGroup* createOpUndefGroup (tcu::TestContext& testCtx)
 
                specializations["TYPE"] = cases[caseNdx].param;
                spec.assembly = shaderTemplate.specialize(specializations);
 
                specializations["TYPE"] = cases[caseNdx].param;
                spec.assembly = shaderTemplate.specialize(specializations);
-               spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
-               spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
+       spec.inputs.push_back(BufferSp(new Float32Buffer(positiveFloats)));
+       spec.outputs.push_back(BufferSp(new Float32Buffer(negativeFloats)));
                spec.numWorkGroups = IVec3(numElements, 1, 1);
 
                group->addChild(new SpvAsmComputeShaderCase(testCtx, cases[caseNdx].name, cases[caseNdx].name, spec));
        }
 
                spec.numWorkGroups = IVec3(numElements, 1, 1);
 
                group->addChild(new SpvAsmComputeShaderCase(testCtx, cases[caseNdx].name, cases[caseNdx].name, spec));
        }
 
-       return group.release();
+               return group.release();
+}
+
+
+typedef std::pair<std::string, VkShaderStage>  EntryToStage;
+typedef map<string, vector<EntryToStage> >             ModuleMap;
+
+// Context for a specific test instantiation. For example, an instantiation
+// may test colors yellow/magenta/cyan/mauve in a tesselation shader
+// with an entry point named 'main_to_the_main'
+struct InstanceContext
+{
+       // Map of modules to what entry_points we care to use from those modules.
+       ModuleMap       moduleMap;
+       RGBA            inputColors[4];
+       RGBA            outputColors[4];
+       // Concrete SPIR-V code to test via boilerplate specialization.
+       map<string, string> testCodeFragments;
+
+       InstanceContext (const RGBA (&inputs)[4], const RGBA (&outputs)[4], const map<string, string>& testCodeFragments_)
+               : testCodeFragments(testCodeFragments_)
+       {
+               memcpy(inputColors, inputs, sizeof(inputs));
+               memcpy(outputColors, outputs, sizeof(outputs));
+       }
+
+       InstanceContext (const InstanceContext& other)
+                       : moduleMap(other.moduleMap)
+                       , testCodeFragments(other.testCodeFragments)
+       {
+               memcpy(inputColors, other.inputColors, sizeof(inputColors));
+               memcpy(outputColors, other.outputColors, sizeof(outputColors));
+       }
+};
+
+// A description of a shader to be used for a single stage of the graphics pipeline.
+struct ShaderElement
+{
+       // The module that contains this shader entrypoint.
+       const char*             moduleName;
+
+       // The name of the entrypoint.
+       const char*             entryName;
+
+       // Which shader stage this entry point represents.
+       VkShaderStage   stage;
+
+       ShaderElement (const char* moduleName_, const char* entryPoint_, VkShaderStage shaderStage_)
+               : moduleName(moduleName_)
+               , entryName(entryPoint_)
+               , stage(shaderStage_)
+       {
+       }
+};
+
+void getDefaultColors(RGBA colors[4]) {
+       colors[0] = RGBA::white();
+       colors[1] = RGBA::red();
+       colors[2] = RGBA::blue();
+       colors[3] = RGBA::green();
+}
+// 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], const RGBA (&inputColors)[4], const RGBA (&outputColors)[4], const map<string, string>& testCodeFragments)
+{
+       InstanceContext ctx(inputColors, outputColors, testCodeFragments);
+       for (size_t i = 0; i < N; ++i)
+       {
+               ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage));
+       }
+       return ctx;
+}
+
+// The same as createInstanceContext above, but with default colors.
+template<size_t N>
+InstanceContext createInstanceContext (const ShaderElement (&elements)[N], const map<string, string>& testCodeFragments)
+{
+       RGBA defaultColors[4];
+       getDefaultColors(defaultColors);
+       return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments);
+}
+
+// For the current InstanceContext, constructs the required modules and shaders.
+// Fills in the modules vector with all of the used modules, and stage_shaders with
+// all stages and shaders that are to be used.
+void createShaders (const DeviceInterface& vk, const VkDevice vkDevice, InstanceContext& instance, Context& context, vector<ModuleHandleSp>& modules, map<VkShaderStage, VkShaderSp>& stage_shaders)
+{
+       for (ModuleMap::const_iterator moduleNdx = instance.moduleMap.begin(); moduleNdx != instance.moduleMap.end(); ++moduleNdx)
+       {
+               const ModuleHandleSp mod(new Unique<VkShaderModule>(createShaderModule(vk, vkDevice, context.getBinaryCollection().get(moduleNdx->first), 0)));
+               modules.push_back(ModuleHandleSp(mod));
+               for (vector<EntryToStage>::const_iterator shaderNdx = moduleNdx->second.begin(); shaderNdx != moduleNdx->second.end(); ++shaderNdx)
+               {
+                       const EntryToStage&                     stage                   = *shaderNdx;
+                       const VkShaderCreateInfo        shaderParam             =
+                       {
+                               VK_STRUCTURE_TYPE_SHADER_CREATE_INFO,                   //      VkStructureType         sType;
+                               DE_NULL,                                                                                //      const void*                     pNext;
+                               **modules.back(),                                                               //      VkShaderModule          module;
+                               stage.first.c_str(),                                                    //      const char*                     pName;
+                               0u,                                                                                             //      VkShaderCreateFlags     flags;
+                               stage.second,                                                                   //      VkShaderStage           stage;
+                       };
+                       stage_shaders[stage.second] = VkShaderSp(new Unique<VkShader>(createShader(vk, vkDevice, &shaderParam)));
+               }
+       }
+}
+
+#define TYPES                                                                                                                          \
+       "%void = OpTypeVoid\n"                                                                                                  \
+       "%bool = OpTypeBool\n"                                                                                                  \
+                                                                                                                                                       \
+       "%i32 = OpTypeInt 32 1\n"                                                                                               \
+       "%u32 = OpTypeInt 32 0\n"                                                                                               \
+                                                                                                                                                       \
+       "%f32 = OpTypeFloat 32\n"                                                                                               \
+       "%v3f32 = OpTypeVector %f32 3\n"                                                                                \
+       "%v4f32 = OpTypeVector %f32 4\n"                                                                                \
+                                                                                                                                                       \
+       "%v4f32_function = OpTypeFunction %v4f32 %v4f32\n"                                              \
+       "%fun = OpTypeFunction %void\n"                                                                                 \
+                                                                                                                                                       \
+       "%ip_f32 = OpTypePointer Input %f32\n"                                                                  \
+       "%ip_i32 = OpTypePointer Input %i32\n"                                                                  \
+       "%ip_v3f32 = OpTypePointer Input %v3f32\n"                                                              \
+       "%ip_v4f32 = OpTypePointer Input %v4f32\n"                                                              \
+                                                                                                                                                       \
+       "%op_f32 = OpTypePointer Output %f32\n"                                                                 \
+       "%op_v4f32 = OpTypePointer Output %v4f32\n"
+
+#define CONSTANTS                                                                                                                      \
+       "%c_f32_1 = OpConstant %f32 1\n"                                                                                \
+       "%c_i32_0 = OpConstant %i32 0\n"                                                                                \
+       "%c_i32_1 = OpConstant %i32 1\n"                                                                                \
+       "%c_i32_2 = OpConstant %i32 2\n"                                                                                \
+       "%c_u32_0 = OpConstant %u32 0\n"                                                                                \
+       "%c_u32_1 = OpConstant %u32 1\n"                                                                                \
+       "%c_u32_2 = OpConstant %u32 2\n"                                                                                \
+       "%c_u32_3 = OpConstant %u32 3\n"                                                                                \
+       "%c_u32_32 = OpConstant %u32 32\n"                                                                              \
+       "%c_u32_4 = OpConstant %u32 4\n"
+
+#define ARRAYS                                                                                                                         \
+       "%a1f32 = OpTypeArray %f32 %c_u32_1\n"                                                                  \
+       "%a2f32 = OpTypeArray %f32 %c_u32_2\n"                                                                  \
+       "%a3v4f32 = OpTypeArray %v4f32 %c_u32_3\n"                                                              \
+       "%a4f32 = OpTypeArray %f32 %c_u32_4\n"                                                                  \
+       "%a32v4f32 = OpTypeArray %v4f32 %c_u32_32\n"                                                    \
+       "%ip_a3v4f32 = OpTypePointer Input %a3v4f32\n"                                                  \
+       "%ip_a32v4f32 = OpTypePointer Input %a32v4f32\n"                                                \
+       "%op_a2f32 = OpTypePointer Output %a2f32\n"                                                             \
+       "%op_a3v4f32 = OpTypePointer Output %a3v4f32\n"                                                 \
+       "%op_a4f32 = OpTypePointer Output %a4f32\n"                                                             \
+       "%op_f32 = OpTypePointer Output %f32\n"
+
+// Boilerplate vertex-shader assembly.  Specialize with "testfun" mapping to an
+// OpFunction definition for %test_code that takes and returns a %v4f32.
+// Prefixes local IDs with "BP_" to avoid collisions with the rest of assembly
+// code.
+//
+// It corresponds roughly to this GLSL:
+//
+// layout(location = 0) in vec4 position;
+// layout(location = 1) in vec4 color;
+// layout(location = 1) out highp vec4 vtxColor;
+// void main (void) { gl_Position = position; vtxColor = test_func(color); }
+tcu::StringTemplate vertexShaderBoilerplate(
+       "OpCapability Shader\n"
+       "OpMemoryModel Logical GLSL450\n"
+       "OpEntryPoint Vertex %4 \"main\" %BP_Position %BP_vtxColor %BP_color %BP_vtxPosition %BP_vertex_id %BP_instance_id\n"
+       "${debug:opt}\n"
+       "OpName %main \"main\"\n"
+       "OpName %BP_vtxPosition \"vtxPosition\"\n"
+       "OpName %BP_Position \"position\"\n"
+       "OpName %BP_vtxColor \"vtxColor\"\n"
+       "OpName %BP_color \"color\"\n"
+       "OpName %vertex_id \"gl_VertexID\"\n"
+       "OpName %instance_id \"gl_InstanceID\"\n"
+       "OpName %test_code \"testfun(vf4;\"\n"
+       "OpDecorate %BP_vtxPosition Smooth\n"
+       "OpDecorate %BP_vtxPosition Location 2\n"
+       "OpDecorate %BP_Position Location 0\n"
+       "OpDecorate %BP_vtxColor Smooth\n"
+       "OpDecorate %BP_vtxColor Location 1\n"
+       "OpDecorate %BP_color Location 1\n"
+       "OpDecorate %BP_vertex_id BuiltIn VertexId\n"
+       "OpDecorate %BP_instance_id BuiltIn InstanceId\n"
+       TYPES
+       CONSTANTS
+       ARRAYS
+       "%BP_vtxPosition = OpVariable %op_v4f32 Output\n"
+       "%BP_Position = OpVariable %ip_v4f32 Input\n"
+       "%BP_vtxColor = OpVariable %op_v4f32 Output\n"
+       "%BP_color = OpVariable %ip_v4f32 Input\n"
+       "%BP_vertex_id = OpVariable %ip_i32 Input\n"
+       "%BP_instance_id = OpVariable %ip_i32 Input\n"
+       "%main = OpFunction %void None %fun\n"
+       "%BP_label = OpLabel\n"
+       "%BP_tmp_position = OpLoad %v4f32 %BP_Position\n"
+       "OpStore %BP_vtxPosition %BP_tmp_position\n"
+       "%BP_tmp_color = OpLoad %v4f32 %BP_color\n"
+       "%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_tmp_color\n"
+       "OpStore %BP_vtxColor %BP_clr_transformed\n"
+       "OpReturn\n"
+       "OpFunctionEnd\n"
+       "${testfun}\n");
+
+// Boilerplate tess-control shader assembly.  Specialize by mapping "testfun" to
+// an OpFunction definition for %test_code that takes and returns a %v4f32.
+// Prefixes local IDs with "BP_" to avoid collisions with the rest of assembly
+// code.
+//
+// It roughly corresponds to the following glsl.
+//
+// #version 450
+// layout(vertices = 3) out;
+// layout(location = 1) in vec4 in_color[];
+// layout(location = 2) in vec4 in_position[];
+// layout(location = 1) out vec4 out_color[];
+// layout(location = 2) out vec4 out_position[];
+//
+// void main() {
+//   out_color[gl_InvocationID] = testfun(in_color[gl_InvocationID]);
+//   out_position[gl_InvocationID] = in_position[gl_InvocationID];
+//   if (gl_InvocationID == 0) {
+//     gl_TessLevelOuter[0] = 1.0;
+//     gl_TessLevelOuter[1] = 1.0;
+//     gl_TessLevelOuter[2] = 1.0;
+//     gl_TessLevelInner[0] = 1.0;
+//   }
+// }
+tcu::StringTemplate tessControlShaderBoilerplate(
+       "OpCapability Tessellation\n"
+       "OpMemoryModel Logical GLSL450\n"
+       "OpEntryPoint TessellationControl %BP_main \"main\" %BP_out_color %BP_gl_InvocationID %BP_in_color %BP_out_position %BP_in_position %BP_gl_TessCoordOuter %BP_gl_TessCoordInner\n"
+       "OpExecutionMode %BP_main OutputVertices 3\n"
+       "${debug:opt}\n"
+       "OpName %BP_main \"main\"\n"
+       "OpName %BP_out_color \"out_color\"\n"
+       "OpName %BP_gl_InvocationID \"gl_InvocationID\"\n"
+       "OpName %BP_in_color \"in_color\"\n"
+       "OpName %BP_out_position \"out_position\"\n"
+       "OpName %BP_in_position \"in_position\"\n"
+       "OpName %BP_gl_TessCoordOuter \"gl_TessLevelOuter\"\n"
+       "OpName %BP_gl_TessCoordInner \"gl_TessLevelInner\"\n"
+       "OpName %test_code \"testfun(vf4;\"\n"
+       "OpDecorate %BP_out_color Location 1\n"
+       "OpDecorate %BP_gl_InvocationID BuiltIn InvocationId\n"
+       "OpDecorate %BP_in_color Location 1\n"
+       "OpDecorate %BP_out_position Location 2\n"
+       "OpDecorate %BP_in_position Location 2\n"
+       "OpDecorate %BP_gl_TessCoordOuter Patch\n"
+       "OpDecorate %BP_gl_TessCoordOuter BuiltIn TessLevelOuter\n"
+       "OpDecorate %BP_gl_TessCoordInner Patch\n"
+       "OpDecorate %BP_gl_TessCoordInner BuiltIn TessLevelInner\n"
+TYPES
+CONSTANTS
+ARRAYS
+       "%BP_out_color = OpVariable %op_a3v4f32 Output\n"
+       "%BP_gl_InvocationID = OpVariable %ip_i32 Input\n"
+       "%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
+       "%BP_out_position = OpVariable %op_a3v4f32 Output\n"
+       "%BP_in_position = OpVariable %ip_a32v4f32 Input\n"
+       "%BP_gl_TessCoordOuter = OpVariable %op_a4f32 Output\n"
+       "%BP_gl_TessCoordInner = OpVariable %op_a2f32 Output\n"
+
+       "%BP_main = OpFunction %void None %fun\n"
+       "%BP_label = OpLabel\n"
+
+       "%BP_invocation_id = OpLoad %i32 %BP_gl_InvocationID\n"
+
+       "%BP_in_color_ptr = OpAccessChain %ip_v4f32 %BP_in_color %BP_invocation_id\n"
+       "%BP_in_position_ptr = OpAccessChain %ip_v4f32 %BP_in_position %BP_invocation_id\n"
+
+       "%BP_in_color_val = OpLoad %v4f32 %BP_in_color_ptr\n"
+       "%BP_in_position_val = OpLoad %v4f32 %BP_in_position_ptr\n"
+
+       "%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_in_color_val\n"
+
+       "%BP_out_color_ptr = OpAccessChain %op_v4f32 %BP_out_color %BP_invocation_id\n"
+       "%BP_out_position_ptr = OpAccessChain %op_v4f32 %BP_out_position %BP_invocation_id\n"
+
+       "OpStore %BP_out_color_ptr %BP_clr_transformed\n"
+       "OpStore %BP_out_position_ptr %BP_in_position_val\n"
+
+       "%BP_is_first_invocation = OpIEqual %bool %BP_invocation_id %c_i32_0\n"
+       "OpSelectionMerge %BP_merge_label None\n"
+       "OpBranchConditional %BP_is_first_invocation %BP_first_invocation %BP_merge_label\n"
+
+       "%BP_first_invocation = OpLabel\n"
+       "%BP_tess_outer_0 = OpAccessChain %op_f32 %BP_gl_TessCoordOuter %c_i32_0\n"
+       "%BP_tess_outer_1 = OpAccessChain %op_f32 %BP_gl_TessCoordOuter %c_i32_1\n"
+       "%BP_tess_outer_2 = OpAccessChain %op_f32 %BP_gl_TessCoordOuter %c_i32_2\n"
+       "%BP_tess_inner = OpAccessChain %op_f32 %BP_gl_TessCoordInner %c_i32_0\n"
+
+       "OpStore %BP_tess_outer_0 %c_f32_1\n"
+       "OpStore %BP_tess_outer_1 %c_f32_1\n"
+       "OpStore %BP_tess_outer_2 %c_f32_1\n"
+       "OpStore %BP_tess_inner %c_f32_1\n"
+
+       "OpBranch %BP_merge_label\n"
+       "%BP_merge_label = OpLabel\n"
+       "OpReturn\n"
+       "OpFunctionEnd\n"
+       "${testfun}\n");
+
+// Boilerplate tess-eval shader assembly.  Specialize by mapping "testfun" to an
+// OpFunction definition for %test_code that takes and returns a %v4f32.
+// Prefixes local IDs with "BP_" to avoid collisions with the rest of assembly
+// code.
+//
+// It roughly corresponds to the following glsl.
+//
+// #version 450
+//
+// layout(triangles, equal_spacing, ccw) in;
+// layout(location = 1) in vec4 in_color[];
+// layout(location = 2) in vec4 in_position[];
+// layout(location = 1) out vec4 out_color;
+//
+// #define interpolate(val)
+//   vec4(gl_TessCoord.x) * val[0] + vec4(gl_TessCoord.y) * val[1] +
+//          vec4(gl_TessCoord.z) * val[2]
+//
+//
+// void main() {
+//   gl_Position = vec4(gl_TessCoord.x) * in_position[0] +
+//                  vec4(gl_TessCoord.y) * in_position[1] +
+//                  vec4(gl_TessCoord.z) * in_position[2];
+//   out_color = testfun(interpolate(in_color));
+// }
+
+tcu::StringTemplate tessEvalBoilerplate(
+       "OpCapability Tessellation\n"
+       "OpMemoryModel Logical GLSL450\n"
+       "OpEntryPoint TessellationEvaluation %BP_main \"main\" %BP_stream %BP_gl_tessCoord %BP_in_position %BP_out_color %BP_in_color \n"
+       "OpExecutionMode %BP_main InputTriangles\n"
+       "${debug:opt}\n"
+       "OpName %BP_main \"main\"\n"
+       "OpName %BP_per_vertex_out \"gl_PerVertex\"\n"
+       "OpMemberName %BP_per_vertex_out 0 \"gl_Position\"\n"
+       "OpMemberName %BP_per_vertex_out 1 \"gl_PointSize\"\n"
+       "OpMemberName %BP_per_vertex_out 2 \"gl_ClipDistance\"\n"
+       "OpMemberName %BP_per_vertex_out 3 \"gl_CullDistance\"\n"
+       "OpName %BP_stream \"\"\n"
+       "OpName %BP_gl_tessCoord \"gl_TessCoord\"\n"
+       "OpName %BP_in_position \"in_position\"\n"
+       "OpName %BP_out_color \"out_color\"\n"
+       "OpName %BP_in_color \"in_color\"\n"
+       "OpName %test_code \"testfun(vf4;\"\n"
+       "OpMemberDecorate %BP_per_vertex_out 0 BuiltIn Position\n"
+       "OpMemberDecorate %BP_per_vertex_out 1 BuiltIn PointSize\n"
+       "OpMemberDecorate %BP_per_vertex_out 2 BuiltIn ClipDistance\n"
+       "OpMemberDecorate %BP_per_vertex_out 3 BuiltIn CullDistance\n"
+       "OpDecorate %BP_per_vertex_out Block\n"
+       "OpDecorate %BP_gl_tessCoord BuiltIn TessCoord\n"
+       "OpDecorate %BP_in_position Location 2\n"
+       "OpDecorate %BP_out_color Location 1\n"
+       "OpDecorate %BP_in_color Location 1\n"
+       TYPES
+       CONSTANTS
+       ARRAYS
+       "%BP_per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+       "%BP_op_per_vertex_out = OpTypePointer Output %BP_per_vertex_out\n"
+       "%BP_stream = OpVariable %BP_op_per_vertex_out Output\n"
+       "%BP_gl_tessCoord = OpVariable %ip_v3f32 Input\n"
+       "%BP_in_position = OpVariable %ip_a32v4f32 Input\n"
+       "%BP_out_color = OpVariable %op_v4f32 Output\n"
+       "%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
+       "%BP_main = OpFunction %void None %fun\n"
+       "%BP_label = OpLabel\n"
+       "%BP_tc_0_ptr = OpAccessChain %ip_f32 %BP_gl_tessCoord %c_u32_0\n"
+       "%BP_tc_1_ptr = OpAccessChain %ip_f32 %BP_gl_tessCoord %c_u32_1\n"
+       "%BP_tc_2_ptr = OpAccessChain %ip_f32 %BP_gl_tessCoord %c_u32_2\n"
+
+       "%BP_tc_0 = OpLoad %f32 %BP_tc_0_ptr\n"
+       "%BP_tc_1 = OpLoad %f32 %BP_tc_1_ptr\n"
+       "%BP_tc_2 = OpLoad %f32 %BP_tc_2_ptr\n"
+
+       "%BP_in_pos_0_ptr = OpAccessChain %ip_v4f32 %BP_in_position %c_i32_0\n"
+       "%BP_in_pos_1_ptr = OpAccessChain %ip_v4f32 %BP_in_position %c_i32_1\n"
+       "%BP_in_pos_2_ptr = OpAccessChain %ip_v4f32 %BP_in_position %c_i32_2\n"
+
+       "%BP_in_pos_0 = OpLoad %v4f32 %BP_in_pos_0_ptr\n"
+       "%BP_in_pos_1 = OpLoad %v4f32 %BP_in_pos_1_ptr\n"
+       "%BP_in_pos_2 = OpLoad %v4f32 %BP_in_pos_2_ptr\n"
+
+       "%BP_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %BP_tc_0 %BP_in_pos_0\n"
+       "%BP_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %BP_tc_1 %BP_in_pos_1\n"
+       "%BP_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %BP_tc_2 %BP_in_pos_2\n"
+
+       "%BP_out_pos_ptr = OpAccessChain %op_v4f32 %BP_stream %c_i32_0\n"
+
+       "%BP_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %BP_in_pos_0_weighted %BP_in_pos_1_weighted\n"
+       "%BP_computed_out = OpFAdd %v4f32 %BP_in_pos_0_plus_pos_1 %BP_in_pos_2_weighted\n"
+       "OpStore %BP_out_pos_ptr %BP_computed_out\n"
+
+       "%BP_in_clr_0_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_0\n"
+       "%BP_in_clr_1_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_1\n"
+       "%BP_in_clr_2_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_2\n"
+
+       "%BP_in_clr_0 = OpLoad %v4f32 %BP_in_clr_0_ptr\n"
+       "%BP_in_clr_1 = OpLoad %v4f32 %BP_in_clr_1_ptr\n"
+       "%BP_in_clr_2 = OpLoad %v4f32 %BP_in_clr_2_ptr\n"
+
+       "%BP_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %BP_tc_0 %BP_in_clr_0\n"
+       "%BP_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %BP_tc_1 %BP_in_clr_1\n"
+       "%BP_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %BP_tc_2 %BP_in_clr_2\n"
+
+       "%BP_in_clr_0_plus_col_1 = OpFAdd %v4f32 %BP_in_clr_0_weighted %BP_in_clr_1_weighted\n"
+       "%BP_computed_clr = OpFAdd %v4f32 %BP_in_clr_0_plus_col_1 %BP_in_clr_2_weighted\n"
+       "%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_computed_clr\n"
+
+       "OpStore %BP_out_color %BP_clr_transformed\n"
+       "OpReturn\n"
+       "OpFunctionEnd\n"
+       "${testfun}\n");
+
+// Boilerplate geometry-shader assembly.  Specialize by mapping "testfun" to an
+// OpFunction definition for %test_code that takes and returns a %v4f32.
+// Prefixes local IDs with "BP_" to avoid collisions with the rest of assembly
+// code.
+//
+// Derived from this GLSL:
+//
+// #version 450
+// layout(triangles) in;
+// layout(triangle_strip, max_vertices = 3) out;
+//
+// layout(location = 1) in vec4 in_color[];
+// layout(location = 1) out vec4 out_color;
+//
+// void main() {
+//      gl_Position = gl_in[0].gl_Position;
+//      out_color = test_fun(in_color[0]);
+//      EmitVertex();
+//      gl_Position = gl_in[1].gl_Position;
+//      out_color = test_fun(in_color[1]);
+//      EmitVertex();
+//      gl_Position = gl_in[2].gl_Position;
+//      out_color = test_fun(in_color[2]);
+//      EmitVertex();
+//      EndPrimitive();
+// }
+tcu::StringTemplate geometryShaderBoilerplate(
+       "OpCapability Geometry\n"
+       "OpMemoryModel Logical GLSL450\n"
+       "OpEntryPoint Geometry %BP_main \"main\" %BP_stream %BP_gl_in %BP_out_color %BP_in_color\n"
+       "OpExecutionMode %BP_main InputTriangles\n"
+       "OpExecutionMode %BP_main Invocations 0\n"
+       "OpExecutionMode %BP_main OutputTriangleStrip\n"
+       "OpExecutionMode %BP_main OutputVertices 3\n"
+       "${debug:opt}\n"
+       "OpName %BP_main \"main\"\n"
+       "OpName %BP_per_vertex_out \"gl_PerVertex\"\n"
+       "OpMemberName %BP_per_vertex_out 0 \"gl_Position\"\n"
+       "OpMemberName %BP_per_vertex_out 1 \"gl_PointSize\"\n"
+       "OpMemberName %BP_per_vertex_out 2 \"gl_ClipDistance\"\n"
+       "OpMemberName %BP_per_vertex_out 3 \"gl_CullDistance\"\n"
+       "OpName %BP_stream \"\"\n"
+       "OpName %BP_per_vertex_in \"gl_PerVertex\"\n"
+       "OpMemberName %BP_per_vertex_in 0 \"gl_Position\"\n"
+       "OpMemberName %BP_per_vertex_in 1 \"gl_PointSize\"\n"
+       "OpMemberName %BP_per_vertex_in 2 \"gl_ClipDistance\"\n"
+       "OpMemberName %BP_per_vertex_in 3 \"gl_CullDistance\"\n"
+       "OpName %BP_gl_in \"gl_in\"\n"
+       "OpName %BP_out_color \"out_color\"\n"
+       "OpName %BP_in_color \"in_color\"\n"
+       "OpName %test_code \"testfun(vf4;\"\n"
+       "OpMemberDecorate %BP_per_vertex_out 0 BuiltIn Position\n"
+       "OpMemberDecorate %BP_per_vertex_out 1 BuiltIn PointSize\n"
+       "OpMemberDecorate %BP_per_vertex_out 2 BuiltIn ClipDistance\n"
+       "OpMemberDecorate %BP_per_vertex_out 3 BuiltIn CullDistance\n"
+       "OpDecorate %BP_per_vertex_out Block\n"
+       "OpDecorate %BP_per_vertex_out Stream 0\n"
+       "OpDecorate %BP_stream Stream 0\n"
+       "OpMemberDecorate %BP_per_vertex_in 0 BuiltIn Position\n"
+       "OpMemberDecorate %BP_per_vertex_in 1 BuiltIn PointSize\n"
+       "OpMemberDecorate %BP_per_vertex_in 2 BuiltIn ClipDistance\n"
+       "OpMemberDecorate %BP_per_vertex_in 3 BuiltIn CullDistance\n"
+       "OpDecorate %BP_per_vertex_in Block\n"
+       "OpDecorate %BP_out_color Location 1\n"
+       "OpDecorate %BP_out_color Stream 0\n"
+       "OpDecorate %BP_in_color Location 1\n"
+TYPES
+CONSTANTS
+ARRAYS
+       "%BP_per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+       "%BP_per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+       "%BP_a3_per_vertex_in = OpTypeArray %BP_per_vertex_in %c_u32_3\n"
+       "%BP_op_per_vertex_out = OpTypePointer Output %BP_per_vertex_out\n"
+       "%BP_ip_a3_per_vertex_in = OpTypePointer Input %BP_a3_per_vertex_in\n"
+
+       "%BP_stream = OpVariable %BP_op_per_vertex_out Output\n"
+       "%BP_gl_in = OpVariable %BP_ip_a3_per_vertex_in Input\n"
+       "%BP_out_color = OpVariable %op_v4f32 Output\n"
+       "%BP_in_color = OpVariable %ip_a3v4f32 Input\n"
+
+       "%BP_main = OpFunction %void None %fun\n"
+       "%BP_label = OpLabel\n"
+       "%BP_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_0 %c_i32_0\n"
+       "%BP_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_1 %c_i32_0\n"
+       "%BP_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_2 %c_i32_0\n"
+
+       "%BP_in_position_0 = OpLoad %v4f32 %BP_gl_in_0_gl_position\n"
+       "%BP_in_position_1 = OpLoad %v4f32 %BP_gl_in_1_gl_position\n"
+       "%BP_in_position_2 = OpLoad %v4f32 %BP_gl_in_2_gl_position \n"
+
+       "%BP_in_color_0_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_0\n"
+       "%BP_in_color_1_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_1\n"
+       "%BP_in_color_2_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_2\n"
+
+       "%BP_in_color_0 = OpLoad %v4f32 %BP_in_color_0_ptr\n"
+       "%BP_in_color_1 = OpLoad %v4f32 %BP_in_color_1_ptr\n"
+       "%BP_in_color_2 = OpLoad %v4f32 %BP_in_color_2_ptr\n"
+
+       "%BP_transformed_in_color_0 = OpFunctionCall %v4f32 %test_code %BP_in_color_0\n"
+       "%BP_transformed_in_color_1 = OpFunctionCall %v4f32 %test_code %BP_in_color_1\n"
+       "%BP_transformed_in_color_2 = OpFunctionCall %v4f32 %test_code %BP_in_color_2\n"
+
+       "%BP_out_gl_position = OpAccessChain %op_v4f32 %BP_stream %c_i32_0\n"
+
+       "OpStore %BP_out_gl_position %BP_in_position_0\n"
+       "OpStore %BP_out_color %BP_transformed_in_color_0\n"
+       "OpEmitVertex\n"
+
+       "OpStore %BP_out_gl_position %BP_in_position_1\n"
+       "OpStore %BP_out_color %BP_transformed_in_color_1\n"
+       "OpEmitVertex\n"
+
+       "OpStore %BP_out_gl_position %BP_in_position_2\n"
+       "OpStore %BP_out_color %BP_transformed_in_color_2\n"
+       "OpEmitVertex\n"
+
+       "OpEndPrimitive\n"
+       "OpReturn\n"
+       "OpFunctionEnd\n"
+       "${testfun}\n");
+
+// Boilerplate fragment-shader assembly.  Specialize by mapping "testfun" to an
+// OpFunction definition for %test_code that takes and returns a %v4f32.
+// Prefixes local IDs with "BP_" to avoid collisions with the rest of assembly
+// code.
+//
+// Derived from this GLSL:
+//
+// layout(location = 0) in highp vec4 vtxColor;
+// layout(location = 1) out highp vec4 fragColor;
+// highp vec4 testfun(highp vec4 x) { return x; }
+// void main(void) { fragColor = testfun(vtxColor); }
+//
+// with modifications including passing vtxColor by value and ripping out
+// testfun() definition.
+tcu::StringTemplate fragmentShaderBoilerplate(
+       "OpCapability Shader\n"
+       "OpMemoryModel Logical GLSL450\n"
+       "OpEntryPoint Fragment %BP_main \"main\" %BP_vtxColor %BP_fragColor\n"
+       "OpExecutionMode %BP_main OriginUpperLeft\n"
+       "${debug:opt}\n"
+       "OpName %BP_main \"main\"\n"
+       "OpName %BP_fragColor \"fragColor\"\n"
+       "OpName %BP_vtxColor \"vtxColor\"\n"
+       "OpName %test_code \"testfun(vf4;\"\n"
+       "OpDecorate %BP_fragColor Location 0\n"
+       "OpDecorate %BP_vtxColor Smooth\n"
+       "OpDecorate %BP_vtxColor Location 1\n"
+       TYPES
+       CONSTANTS
+       ARRAYS
+       "%BP_fragColor = OpVariable %op_v4f32 Output\n"
+       "%BP_vtxColor = OpVariable %ip_v4f32 Input\n"
+       "%BP_main = OpFunction %void None %fun\n"
+       "%BP_label_main = OpLabel\n"
+       "%BP_tmp1 = OpLoad %v4f32 %BP_vtxColor\n"
+       "%BP_tmp2 = OpFunctionCall %v4f32 %test_code %BP_tmp1\n"
+       "OpStore %BP_fragColor %BP_tmp2\n"
+       "OpReturn\n"
+       "OpFunctionEnd\n"
+       "${testfun}\n");
+
+// Creates fragments that specialize into a simple pass-through shader (of any kind).
+map<string, string> passthruFragments() {
+       map<string, string> fragments;
+       fragments["testfun"] =
+               // A %test_code function that returns its argument unchanged.
+               "%test_code = OpFunction %v4f32 None %v4f32_function\n"
+               "%param1 = OpFunctionParameter %v4f32\n"
+               "%label_testfun = OpLabel\n"
+               "OpReturnValue %param1\n"
+               "OpFunctionEnd\n";
+       return fragments;
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Vertex shader gets custom code from context, the rest are pass-through.
+void addShaderCodeCustomVertex(vk::SourceCollections& dst, InstanceContext context) {
+       map<string, string> passthru = passthruFragments();
+       dst.spirvAsmSources.add("vert") << vertexShaderBoilerplate.specialize(context.testCodeFragments);
+       dst.spirvAsmSources.add("tessc") << tessControlShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tesse") << tessEvalBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("geom") << geometryShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("frag") << fragmentShaderBoilerplate.specialize(passthru);
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Tessellation control shader gets custom code from context, the rest are
+// pass-through.
+void addShaderCodeCustomTessControl(vk::SourceCollections& dst, InstanceContext context) {
+       map<string, string> passthru = passthruFragments();
+       dst.spirvAsmSources.add("vert") << vertexShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tessc") << tessControlShaderBoilerplate.specialize(context.testCodeFragments);
+       dst.spirvAsmSources.add("tesse") << tessEvalBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("geom") << geometryShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("frag") << fragmentShaderBoilerplate.specialize(passthru);
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Tessellation evaluation shader gets custom code from context, the rest are
+// pass-through.
+void addShaderCodeCustomTessEval(vk::SourceCollections& dst, InstanceContext context) {
+       map<string, string> passthru = passthruFragments();
+       dst.spirvAsmSources.add("vert") << vertexShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tessc") << tessControlShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tesse") << tessEvalBoilerplate.specialize(context.testCodeFragments);
+       dst.spirvAsmSources.add("geom") << geometryShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("frag") << fragmentShaderBoilerplate.specialize(passthru);
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Geometry shader gets custom code from context, the rest are pass-through.
+void addShaderCodeCustomGeometry(vk::SourceCollections& dst, InstanceContext context) {
+       map<string, string> passthru = passthruFragments();
+       dst.spirvAsmSources.add("vert") << vertexShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tessc") << tessControlShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tesse") << tessEvalBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("geom") << geometryShaderBoilerplate.specialize(context.testCodeFragments);
+       dst.spirvAsmSources.add("frag") << fragmentShaderBoilerplate.specialize(passthru);
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Fragment shader gets custom code from context, the rest are pass-through.
+void addShaderCodeCustomFragment(vk::SourceCollections& dst, InstanceContext context) {
+       map<string, string> passthru = passthruFragments();
+       dst.spirvAsmSources.add("vert") << vertexShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tessc") << tessControlShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("tesse") << tessEvalBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("geom") << geometryShaderBoilerplate.specialize(passthru);
+       dst.spirvAsmSources.add("frag") << fragmentShaderBoilerplate.specialize(context.testCodeFragments);
+}
+
+// Sets up and runs a Vulkan pipeline, then spot-checks the resulting image.
+// Feeds the pipeline a set of colored triangles, which then must occur in the
+// rendered image.  The surface is cleared before executing the pipeline, so
+// whatever the shaders draw can be directly spot-checked.
+TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance)
+{
+       const VkDevice                                                  vkDevice                                = context.getDevice();
+       const DeviceInterface&                                  vk                                              = context.getDeviceInterface();
+       const VkQueue                                                   queue                                   = context.getUniversalQueue();
+       const deUint32                                                  queueFamilyIndex                = context.getUniversalQueueFamilyIndex();
+       const tcu::IVec2                                                renderSize                              (256, 256);
+       vector<ModuleHandleSp>                                  modules;
+       map<VkShaderStage, VkShaderSp>                  shaders;
+       const int                                                               testSpecificSeed                = 31354125;
+       const int                                                               seed                                    = context.getTestContext().getCommandLine().getBaseSeed() ^ testSpecificSeed;
+       de::Random(seed).shuffle(instance.inputColors, instance.inputColors+4);
+       de::Random(seed).shuffle(instance.outputColors, instance.outputColors+4);
+       const Vec4                                                              vertexData[]                    =
+       {
+               // Upper left corner:
+               Vec4(-1.0f, -1.0f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
+               Vec4(-0.5f, -1.0f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
+               Vec4(-1.0f, -0.5f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
+
+               // Upper right corner:
+               Vec4(+0.5f, -1.0f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
+               Vec4(+1.0f, -1.0f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
+               Vec4(+1.0f, -0.5f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
+
+               // Lower left corner:
+               Vec4(-1.0f, +0.5f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
+               Vec4(-0.5f, +1.0f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
+               Vec4(-1.0f, +1.0f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
+
+               // Lower right corner:
+               Vec4(+1.0f, +0.5f, 0.0f, 1.0f), instance.inputColors[3].toVec(),
+               Vec4(+1.0f, +1.0f, 0.0f, 1.0f), instance.inputColors[3].toVec(),
+               Vec4(+0.5f, +1.0f, 0.0f, 1.0f), instance.inputColors[3].toVec()
+       };
+       const size_t                                                    singleVertexDataSize    = 2 * sizeof(Vec4);
+       const size_t                                                    vertexCount                             = sizeof(vertexData) / singleVertexDataSize;
+
+       const VkBufferCreateInfo                                vertexBufferParams              =
+       {
+               VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,   //      VkStructureType         sType;
+               DE_NULL,                                                                //      const void*                     pNext;
+               (VkDeviceSize)sizeof(vertexData),               //      VkDeviceSize            size;
+               VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,              //      VkBufferUsageFlags      usage;
+               0u,                                                                             //      VkBufferCreateFlags     flags;
+               VK_SHARING_MODE_EXCLUSIVE,                              //      VkSharingMode           sharingMode;
+               1u,                                                                             //      deUint32                        queueFamilyCount;
+               &queueFamilyIndex,                                              //      const deUint32*         pQueueFamilyIndices;
+       };
+       const Unique<VkBuffer>                                  vertexBuffer                    (createBuffer(vk, vkDevice, &vertexBufferParams));
+       const UniquePtr<Allocation>                             vertexBufferMemory              (context.getDefaultAllocator().allocate(getBufferMemoryRequirements(vk, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible));
+
+       VK_CHECK(vk.bindBufferMemory(vkDevice, *vertexBuffer, vertexBufferMemory->getMemory(), vertexBufferMemory->getOffset()));
+
+       const VkDeviceSize                                              imageSizeBytes                  = (VkDeviceSize)(sizeof(deUint32)*renderSize.x()*renderSize.y());
+       const VkBufferCreateInfo                                readImageBufferParams   =
+       {
+               VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,           //      VkStructureType         sType;
+               DE_NULL,                                                                        //      const void*                     pNext;
+               imageSizeBytes,                                                         //      VkDeviceSize            size;
+               VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT,       //      VkBufferUsageFlags      usage;
+               0u,                                                                                     //      VkBufferCreateFlags     flags;
+               VK_SHARING_MODE_EXCLUSIVE,                                      //      VkSharingMode           sharingMode;
+               1u,                                                                                     //      deUint32                        queueFamilyCount;
+               &queueFamilyIndex,                                                      //      const deUint32*         pQueueFamilyIndices;
+       };
+       const Unique<VkBuffer>                                  readImageBuffer                 (createBuffer(vk, vkDevice, &readImageBufferParams));
+       const UniquePtr<Allocation>                             readImageBufferMemory   (context.getDefaultAllocator().allocate(getBufferMemoryRequirements(vk, vkDevice, *readImageBuffer), MemoryRequirement::HostVisible));
+
+       VK_CHECK(vk.bindBufferMemory(vkDevice, *readImageBuffer, readImageBufferMemory->getMemory(), readImageBufferMemory->getOffset()));
+
+       const VkImageCreateInfo                                 imageParams                             =
+       {
+               VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                                                                    //      VkStructureType         sType;
+               DE_NULL,                                                                                                                                //      const void*                     pNext;
+               VK_IMAGE_TYPE_2D,                                                                                                               //      VkImageType                     imageType;
+               VK_FORMAT_R8G8B8A8_UNORM,                                                                                               //      VkFormat                        format;
+               { renderSize.x(), renderSize.y(), 1 },                                                                  //      VkExtent3D                      extent;
+               1u,                                                                                                                                             //      deUint32                        mipLevels;
+               1u,                                                                                                                                             //      deUint32                        arraySize;
+               1u,                                                                                                                                             //      deUint32                        samples;
+               VK_IMAGE_TILING_OPTIMAL,                                                                                                //      VkImageTiling           tiling;
+               VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT, //      VkImageUsageFlags       usage;
+               0u,                                                                                                                                             //      VkImageCreateFlags      flags;
+               VK_SHARING_MODE_EXCLUSIVE,                                                                                              //      VkSharingMode           sharingMode;
+               1u,                                                                                                                                             //      deUint32                        queueFamilyCount;
+               &queueFamilyIndex,                                                                                                              //      const deUint32*         pQueueFamilyIndices;
+               VK_IMAGE_LAYOUT_UNDEFINED,                                                                                              //      VkImageLayout           initialLayout;
+       };
+
+       const Unique<VkImage>                                   image                                   (createImage(vk, vkDevice, &imageParams));
+       const UniquePtr<Allocation>                             imageMemory                             (context.getDefaultAllocator().allocate(getImageMemoryRequirements(vk, vkDevice, *image), MemoryRequirement::Any));
+
+       VK_CHECK(vk.bindImageMemory(vkDevice, *image, imageMemory->getMemory(), imageMemory->getOffset()));
+
+       const VkAttachmentDescription                   colorAttDesc                    =
+       {
+               VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION,               //      VkStructureType                                 sType;
+               DE_NULL,                                                                                //      const void*                                             pNext;
+               VK_FORMAT_R8G8B8A8_UNORM,                                               //      VkFormat                                                format;
+               1u,                                                                                             //      deUint32                                                samples;
+               VK_ATTACHMENT_LOAD_OP_CLEAR,                                    //      VkAttachmentLoadOp                              loadOp;
+               VK_ATTACHMENT_STORE_OP_STORE,                                   //      VkAttachmentStoreOp                             storeOp;
+               VK_ATTACHMENT_LOAD_OP_DONT_CARE,                                //      VkAttachmentLoadOp                              stencilLoadOp;
+               VK_ATTACHMENT_STORE_OP_DONT_CARE,                               //      VkAttachmentStoreOp                             stencilStoreOp;
+               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,               //      VkImageLayout                                   initialLayout;
+               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,               //      VkImageLayout                                   finalLayout;
+               0u,                                                                                             //      VkAttachmentDescriptionFlags    flags;
+       };
+       const VkAttachmentReference                             colorAttRef                             =
+       {
+               0u,                                                                                             //      deUint32                attachment;
+               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,               //      VkImageLayout   layout;
+       };
+       const VkSubpassDescription                              subpassDesc                             =
+       {
+               VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION,                  //      VkStructureType                                 sType;
+               DE_NULL,                                                                                //      const void*                                             pNext;
+               VK_PIPELINE_BIND_POINT_GRAPHICS,                                //      VkPipelineBindPoint                             pipelineBindPoint;
+               0u,                                                                                             //      VkSubpassDescriptionFlags               flags;
+               0u,                                                                                             //      deUint32                                                inputCount;
+               DE_NULL,                                                                                //      const VkAttachmentReference*    pInputAttachments;
+               1u,                                                                                             //      deUint32                                                colorCount;
+               &colorAttRef,                                                                   //      const VkAttachmentReference*    pColorAttachments;
+               DE_NULL,                                                                                //      const VkAttachmentReference*    pResolveAttachments;
+               { VK_NO_ATTACHMENT, VK_IMAGE_LAYOUT_GENERAL },  //      VkAttachmentReference                   depthStencilAttachment;
+               0u,                                                                                             //      deUint32                                                preserveCount;
+               DE_NULL,                                                                                //      const VkAttachmentReference*    pPreserveAttachments;
+
+       };
+       const VkRenderPassCreateInfo                    renderPassParams                =
+       {
+               VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,              //      VkStructureType                                 sType;
+               DE_NULL,                                                                                //      const void*                                             pNext;
+               1u,                                                                                             //      deUint32                                                attachmentCount;
+               &colorAttDesc,                                                                  //      const VkAttachmentDescription*  pAttachments;
+               1u,                                                                                             //      deUint32                                                subpassCount;
+               &subpassDesc,                                                                   //      const VkSubpassDescription*             pSubpasses;
+               0u,                                                                                             //      deUint32                                                dependencyCount;
+               DE_NULL,                                                                                //      const VkSubpassDependency*              pDependencies;
+       };
+       const Unique<VkRenderPass>                              renderPass                              (createRenderPass(vk, vkDevice, &renderPassParams));
+
+       const VkImageViewCreateInfo                             colorAttViewParams              =
+       {
+               VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,               //      VkStructureType                         sType;
+               DE_NULL,                                                                                //      const void*                                     pNext;
+               *image,                                                                                 //      VkImage                                         image;
+               VK_IMAGE_VIEW_TYPE_2D,                                                  //      VkImageViewType                         viewType;
+               VK_FORMAT_R8G8B8A8_UNORM,                                               //      VkFormat                                        format;
+               {
+                       VK_CHANNEL_SWIZZLE_R,
+                       VK_CHANNEL_SWIZZLE_G,
+                       VK_CHANNEL_SWIZZLE_B,
+                       VK_CHANNEL_SWIZZLE_A
+               },                                                                                              //      VkChannelMapping                        channels;
+               {
+                       VK_IMAGE_ASPECT_COLOR_BIT,                                              //      VkImageAspectFlags      aspectMask;
+                       0u,                                                                                             //      deUint32                        baseMipLevel;
+                       1u,                                                                                             //      deUint32                        mipLevels;
+                       0u,                                                                                             //      deUint32                        baseArrayLayer;
+                       1u,                                                                                             //      deUint32                        arraySize;
+               },                                                                                              //      VkImageSubresourceRange         subresourceRange;
+               0u,                                                                                             //      VkImageViewCreateFlags          flags;
+       };
+       const Unique<VkImageView>                               colorAttView                    (createImageView(vk, vkDevice, &colorAttViewParams));
+
+       createShaders(vk, vkDevice, instance, context, modules, shaders);
+
+       // Pipeline layout
+       const VkPipelineLayoutCreateInfo                pipelineLayoutParams    =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,                  //      VkStructureType                                 sType;
+               DE_NULL,                                                                                                //      const void*                                             pNext;
+               0u,                                                                                                             //      deUint32                                                descriptorSetCount;
+               DE_NULL,                                                                                                //      const VkDescriptorSetLayout*    pSetLayouts;
+               0u,                                                                                                             //      deUint32                                                pushConstantRangeCount;
+               DE_NULL,                                                                                                //      const VkPushConstantRange*              pPushConstantRanges;
+       };
+       const Unique<VkPipelineLayout>                  pipelineLayout                  (createPipelineLayout(vk, vkDevice, &pipelineLayoutParams));
+
+       // Pipeline
+       const VkSpecializationInfo                              emptyShaderSpecParams   =
+       {
+               0u,                                                                                                             //      deUint32                                                mapEntryCount;
+               DE_NULL,                                                                                                //      const VkSpecializationMapEntry* pMap;
+               0,                                                                                                              //      const deUintptr                                 dataSize;
+               DE_NULL,                                                                                                //      const void*                                             pData;
+       };
+       vector<VkPipelineShaderStageCreateInfo> shaderStageParams;
+       for(map<VkShaderStage, VkShaderSp>::const_iterator stage = shaders.begin(); stage != shaders.end(); ++stage) {
+               VkPipelineShaderStageCreateInfo info = {
+                       VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,    //      VkStructureType                         sType;
+                       DE_NULL,                                                                                                //      const void*                                     pNext;
+                       stage->first,                                                                                   //      VkShaderStage                           stage;
+                       **stage->second,                                                                                //      VkShader                                        shader;
+                       &emptyShaderSpecParams,                                                                 //      const VkSpecializationInfo*     pSpecializationInfo;
+               };
+               shaderStageParams.push_back(info);
+       }
+       const VkPipelineDepthStencilStateCreateInfo     depthStencilParams              =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,     //      VkStructureType         sType;
+               DE_NULL,                                                                                                        //      const void*                     pNext;
+               DE_FALSE,                                                                                                       //      deUint32                        depthTestEnable;
+               DE_FALSE,                                                                                                       //      deUint32                        depthWriteEnable;
+               VK_COMPARE_OP_ALWAYS,                                                                           //      VkCompareOp                     depthCompareOp;
+               DE_FALSE,                                                                                                       //      deUint32                        depthBoundsTestEnable;
+               DE_FALSE,                                                                                                       //      deUint32                        stencilTestEnable;
+               {
+                       VK_STENCIL_OP_KEEP,                                                                                     //      VkStencilOp     stencilFailOp;
+                       VK_STENCIL_OP_KEEP,                                                                                     //      VkStencilOp     stencilPassOp;
+                       VK_STENCIL_OP_KEEP,                                                                                     //      VkStencilOp     stencilDepthFailOp;
+                       VK_COMPARE_OP_ALWAYS,                                                                           //      VkCompareOp     stencilCompareOp;
+                       0u,                                                                                                                     //      deUint32        stencilCompareMask;
+                       0u,                                                                                                                     //      deUint32        stencilWriteMask;
+                       0u,                                                                                                                     //      deUint32        stencilReference;
+               },                                                                                                                      //      VkStencilOpState        front;
+               {
+                       VK_STENCIL_OP_KEEP,                                                                                     //      VkStencilOp     stencilFailOp;
+                       VK_STENCIL_OP_KEEP,                                                                                     //      VkStencilOp     stencilPassOp;
+                       VK_STENCIL_OP_KEEP,                                                                                     //      VkStencilOp     stencilDepthFailOp;
+                       VK_COMPARE_OP_ALWAYS,                                                                           //      VkCompareOp     stencilCompareOp;
+                       0u,                                                                                                                     //      deUint32        stencilCompareMask;
+                       0u,                                                                                                                     //      deUint32        stencilWriteMask;
+                       0u,                                                                                                                     //      deUint32        stencilReference;
+               },                                                                                                                      //      VkStencilOpState        back;
+               -1.0f,                                                                                                          //      float                           minDepthBounds;
+               +1.0f,                                                                                                          //      float                           maxDepthBounds;
+       };
+       const VkViewport                                                viewport0                               =
+       {
+               0.0f,                                                                                                           //      float   originX;
+               0.0f,                                                                                                           //      float   originY;
+               (float)renderSize.x(),                                                                          //      float   width;
+               (float)renderSize.y(),                                                                          //      float   height;
+               0.0f,                                                                                                           //      float   minDepth;
+               1.0f,                                                                                                           //      float   maxDepth;
+       };
+       const VkRect2D                                                  scissor0                                =
+       {
+               {
+                       0u,                                                                                                                     //      deInt32 x;
+                       0u,                                                                                                                     //      deInt32 y;
+               },                                                                                                                      //      VkOffset2D      offset;
+               {
+                       renderSize.x(),                                                                                         //      deInt32 width;
+                       renderSize.y(),                                                                                         //      deInt32 height;
+               },                                                                                                                      //      VkExtent2D      extent;
+       };
+       const VkPipelineViewportStateCreateInfo         viewportParams                  =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,          //      VkStructureType         sType;
+               DE_NULL,                                                                                                        //      const void*                     pNext;
+               1u,                                                                                                                     //      deUint32                        viewportCount;
+               &viewport0,
+               1u,
+               &scissor0
+       };
+       const VkSampleMask                                                      sampleMask                              = ~0u;
+       const VkPipelineMultisampleStateCreateInfo      multisampleParams               =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,       //      VkStructureType sType;
+               DE_NULL,                                                                                                        //      const void*             pNext;
+               1u,                                                                                                                     //      deUint32                rasterSamples;
+               DE_FALSE,                                                                                                       //      deUint32                sampleShadingEnable;
+               0.0f,                                                                                                           //      float                   minSampleShading;
+               &sampleMask,                                                                                            //      VkSampleMask    sampleMask;
+       };
+       const VkPipelineRasterStateCreateInfo           rasterParams                    =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_RASTER_STATE_CREATE_INFO,    //      VkStructureType sType;
+               DE_NULL,                                                                                                //      const void*             pNext;
+               DE_TRUE,                                                                                                //      deUint32                depthClipEnable;
+               DE_FALSE,                                                                                               //      deUint32                rasterizerDiscardEnable;
+               VK_FILL_MODE_SOLID,                                                                             //      VkFillMode              fillMode;
+               VK_CULL_MODE_NONE,                                                                              //      VkCullMode              cullMode;
+               VK_FRONT_FACE_CCW,                                                                              //      VkFrontFace             frontFace;
+               VK_FALSE,                                                                                               //      VkBool32                depthBiasEnable;
+               0.0f,                                                                                                   //      float                   depthBias;
+               0.0f,                                                                                                   //      float                   depthBiasClamp;
+               0.0f,                                                                                                   //      float                   slopeScaledDepthBias;
+               1.0f,                                                                                                   //      float                   lineWidth;
+       };
+       const VkPipelineInputAssemblyStateCreateInfo    inputAssemblyParams     =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,    //      VkStructureType         sType;
+               DE_NULL,                                                                                                                //      const void*                     pNext;
+               VK_PRIMITIVE_TOPOLOGY_PATCH,                                                                    //      VkPrimitiveTopology     topology;
+               DE_FALSE,                                                                                                               //      deUint32                        primitiveRestartEnable;
+       };
+       const VkVertexInputBindingDescription           vertexBinding0 =
+       {
+               0u,                                                                     // deUint32                                     binding;
+               deUint32(singleVertexDataSize),         // deUint32                                     strideInBytes;
+               VK_VERTEX_INPUT_STEP_RATE_VERTEX        // VkVertexInputStepRate        stepRate;
+       };
+       const VkVertexInputAttributeDescription         vertexAttrib0[2] =
+       {
+               {
+                       0u,                                                                     // deUint32     location;
+                       0u,                                                                     // deUint32     binding;
+                       VK_FORMAT_R32G32B32A32_SFLOAT,          // VkFormat     format;
+                       0u                                                                      // deUint32     offsetInBytes;
+               },
+               {
+                       1u,                                                                     // deUint32     location;
+                       0u,                                                                     // deUint32     binding;
+                       VK_FORMAT_R32G32B32A32_SFLOAT,          // VkFormat     format;
+                       sizeof(Vec4),                                           // deUint32     offsetInBytes;
+               }
+       };
+
+       const VkPipelineVertexInputStateCreateInfo      vertexInputStateParams  =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,      //      VkStructureType                                                         sType;
+               DE_NULL,                                                                                                        //      const void*                                                                     pNext;
+               1u,                                                                                                                     //      deUint32                                                                        bindingCount;
+               &vertexBinding0,                                                                                        //      const VkVertexInputBindingDescription*          pVertexBindingDescriptions;
+               2u,                                                                                                                     //      deUint32                                                                        attributeCount;
+               vertexAttrib0,                                                                                          //      const VkVertexInputAttributeDescription*        pVertexAttributeDescriptions;
+       };
+       const VkPipelineColorBlendAttachmentState       attBlendParams                  =
+       {
+               DE_FALSE,                                                                                                                               //      deUint32                blendEnable;
+               VK_BLEND_ONE,                                                                                                                   //      VkBlend                 srcBlendColor;
+               VK_BLEND_ZERO,                                                                                                                  //      VkBlend                 destBlendColor;
+               VK_BLEND_OP_ADD,                                                                                                                //      VkBlendOp               blendOpColor;
+               VK_BLEND_ONE,                                                                                                                   //      VkBlend                 srcBlendAlpha;
+               VK_BLEND_ZERO,                                                                                                                  //      VkBlend                 destBlendAlpha;
+               VK_BLEND_OP_ADD,                                                                                                                //      VkBlendOp               blendOpAlpha;
+               VK_CHANNEL_R_BIT|VK_CHANNEL_G_BIT|VK_CHANNEL_B_BIT|VK_CHANNEL_A_BIT,    //      VkChannelFlags  channelWriteMask;
+       };
+       const VkPipelineColorBlendStateCreateInfo       blendParams                             =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,       //      VkStructureType                                                         sType;
+               DE_NULL,                                                                                                        //      const void*                                                                     pNext;
+               DE_FALSE,                                                                                                       //      VkBool32                                                                        alphaToCoverageEnable;
+               DE_FALSE,                                                                                                       //      VkBool32                                                                        alphaToOneEnable;
+               DE_FALSE,                                                                                                       //      VkBool32                                                                        logicOpEnable;
+               VK_LOGIC_OP_COPY,                                                                                       //      VkLogicOp                                                                       logicOp;
+               1u,                                                                                                                     //      deUint32                                                                        attachmentCount;
+               &attBlendParams,                                                                                        //      const VkPipelineColorBlendAttachmentState*      pAttachments;
+               { 0.0f, 0.0f, 0.0f, 0.0f },                                                                     //      float                                                                           blendConst[4];
+       };
+       const VkPipelineDynamicStateCreateInfo  dynamicStateInfo                =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,   //      VkStructureType                 sType;
+               DE_NULL,                                                                                                //      const void*                             pNext;
+               0u,                                                                                                             //      deUint32                                dynamicStateCount;
+               DE_NULL                                                                                                 //      const VkDynamicState*   pDynamicStates;
+       };
+
+       const VkPipelineTessellationStateCreateInfo     tessellationState       =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
+               DE_NULL,
+               3u
+       };
+
+       const VkGraphicsPipelineCreateInfo              pipelineParams                  =
+       {
+               VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,                //      VkStructureType                                                                 sType;
+               DE_NULL,                                                                                                //      const void*                                                                             pNext;
+               (deUint32)shaderStageParams.size(),                                             //      deUint32                                                                                stageCount;
+               &shaderStageParams[0],                                                                  //      const VkPipelineShaderStageCreateInfo*                  pStages;
+               &vertexInputStateParams,                                                                //      const VkPipelineVertexInputStateCreateInfo*             pVertexInputState;
+               &inputAssemblyParams,                                                                   //      const VkPipelineInputAssemblyStateCreateInfo*   pInputAssemblyState;
+               &tessellationState,                                                                             //      const VkPipelineTessellationStateCreateInfo*    pTessellationState;
+               &viewportParams,                                                                                //      const VkPipelineViewportStateCreateInfo*                pViewportState;
+               &rasterParams,                                                                                  //      const VkPipelineRasterStateCreateInfo*                  pRasterState;
+               &multisampleParams,                                                                             //      const VkPipelineMultisampleStateCreateInfo*             pMultisampleState;
+               &depthStencilParams,                                                                    //      const VkPipelineDepthStencilStateCreateInfo*    pDepthStencilState;
+               &blendParams,                                                                                   //      const VkPipelineColorBlendStateCreateInfo*              pColorBlendState;
+               &dynamicStateInfo,                                                                              //      const VkPipelineDynamicStateCreateInfo*                 pDynamicState;
+               0u,                                                                                                             //      VkPipelineCreateFlags                                                   flags;
+               *pipelineLayout,                                                                                //      VkPipelineLayout                                                                layout;
+               *renderPass,                                                                                    //      VkRenderPass                                                                    renderPass;
+               0u,                                                                                                             //      deUint32                                                                                subpass;
+               DE_NULL,                                                                                                //      VkPipeline                                                                              basePipelineHandle;
+               0u,                                                                                                             //      deInt32                                                                                 basePipelineIndex;
+       };
+
+       const Unique<VkPipeline>                                pipeline                                (createGraphicsPipeline(vk, vkDevice, DE_NULL, &pipelineParams));
+
+       // Framebuffer
+       const VkFramebufferCreateInfo                   framebufferParams               =
+       {
+               VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,                              //      VkStructureType         sType;
+               DE_NULL,                                                                                                //      const void*                     pNext;
+               *renderPass,                                                                                    //      VkRenderPass            renderPass;
+               1u,                                                                                                             //      deUint32                        attachmentCount;
+               &*colorAttView,                                                                                 //      const VkImageView*      pAttachments;
+               (deUint32)renderSize.x(),                                                               //      deUint32                        width;
+               (deUint32)renderSize.y(),                                                               //      deUint32                        height;
+               1u,                                                                                                             //      deUint32                        layers;
+       };
+       const Unique<VkFramebuffer>                             framebuffer                             (createFramebuffer(vk, vkDevice, &framebufferParams));
+
+       const VkCmdPoolCreateInfo                               cmdPoolParams                   =
+       {
+               VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,                                         //      VkStructureType                 sType;
+               DE_NULL,                                                                                                        //      const void*                             pNext;
+               queueFamilyIndex,                                                                                       //      deUint32                                queueFamilyIndex;
+               VK_CMD_POOL_CREATE_RESET_COMMAND_BUFFER_BIT                                     //      VkCmdPoolCreateFlags    flags;
+       };
+       const Unique<VkCmdPool>                                 cmdPool                                 (createCommandPool(vk, vkDevice, &cmdPoolParams));
+
+       // Command buffer
+       const VkCmdBufferCreateInfo                             cmdBufParams                    =
+       {
+               VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,                               //      VkStructureType                 sType;
+               DE_NULL,                                                                                                //      const void*                             pNext;
+               *cmdPool,                                                                                               //      VkCmdPool                               pool;
+               VK_CMD_BUFFER_LEVEL_PRIMARY,                                                    //      VkCmdBufferLevel                level;
+               0u,                                                                                                             //      VkCmdBufferCreateFlags  flags;
+       };
+       const Unique<VkCmdBuffer>                               cmdBuf                                  (createCommandBuffer(vk, vkDevice, &cmdBufParams));
+
+       const VkCmdBufferBeginInfo                              cmdBufBeginParams               =
+       {
+               VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,                                //      VkStructureType                         sType;
+               DE_NULL,                                                                                                //      const void*                                     pNext;
+               0u,                                                                                                             //      VkCmdBufferOptimizeFlags        flags;
+               DE_NULL,                                                                                                //      VkRenderPass                            renderPass;
+               0u,                                                                                                             //      deUint32                                        subpass;
+               DE_NULL,                                                                                                //      VkFramebuffer                           framebuffer;
+       };
+
+       // Record commands
+       VK_CHECK(vk.beginCommandBuffer(*cmdBuf, &cmdBufBeginParams));
+
+       {
+               const VkMemoryBarrier           vertFlushBarrier        =
+               {
+                       VK_STRUCTURE_TYPE_MEMORY_BARRIER,                       //      VkStructureType         sType;
+                       DE_NULL,                                                                        //      const void*                     pNext;
+                       VK_MEMORY_OUTPUT_HOST_WRITE_BIT,                        //      VkMemoryOutputFlags     outputMask;
+                       VK_MEMORY_INPUT_VERTEX_ATTRIBUTE_FETCH_BIT,     //      VkMemoryInputFlags      inputMask;
+               };
+               const VkImageMemoryBarrier      colorAttBarrier         =
+               {
+                       VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,         //      VkStructureType                 sType;
+                       DE_NULL,                                                                        //      const void*                             pNext;
+                       0u,                                                                                     //      VkMemoryOutputFlags             outputMask;
+                       VK_MEMORY_INPUT_COLOR_ATTACHMENT_BIT,           //      VkMemoryInputFlags              inputMask;
+                       VK_IMAGE_LAYOUT_UNDEFINED,                                      //      VkImageLayout                   oldLayout;
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,       //      VkImageLayout                   newLayout;
+                       queueFamilyIndex,                                                       //      deUint32                                srcQueueFamilyIndex;
+                       queueFamilyIndex,                                                       //      deUint32                                destQueueFamilyIndex;
+                       *image,                                                                         //      VkImage                                 image;
+                       {
+                               VK_IMAGE_ASPECT_COLOR_BIT,                                      //      VkImageAspect   aspect;
+                               0u,                                                                                     //      deUint32                baseMipLevel;
+                               1u,                                                                                     //      deUint32                mipLevels;
+                               0u,                                                                                     //      deUint32                baseArraySlice;
+                               1u,                                                                                     //      deUint32                arraySize;
+                       }                                                                                       //      VkImageSubresourceRange subresourceRange;
+               };
+               const void*                             barriers[]                              = { &vertFlushBarrier, &colorAttBarrier };
+               vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GPU_COMMANDS, DE_FALSE, (deUint32)DE_LENGTH_OF_ARRAY(barriers), barriers);
+       }
+
+       {
+               const VkClearValue                      clearValue              = makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f);
+               const VkRenderPassBeginInfo     passBeginParams =
+               {
+                       VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,                       //      VkStructureType         sType;
+                       DE_NULL,                                                                                        //      const void*                     pNext;
+                       *renderPass,                                                                            //      VkRenderPass            renderPass;
+                       *framebuffer,                                                                           //      VkFramebuffer           framebuffer;
+                       { { 0, 0 }, { renderSize.x(), renderSize.y() } },       //      VkRect2D                        renderArea;
+                       1u,                                                                                                     //      deUint32                        clearValueCount;
+                       &clearValue,                                                                            //      const VkClearValue*     pClearValues;
+               };
+               vk.cmdBeginRenderPass(*cmdBuf, &passBeginParams, VK_RENDER_PASS_CONTENTS_INLINE);
+       }
+
+       vk.cmdBindPipeline(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+       {
+               const VkDeviceSize bindingOffset = 0;
+               vk.cmdBindVertexBuffers(*cmdBuf, 0u, 1u, &vertexBuffer.get(), &bindingOffset);
+       }
+       vk.cmdDraw(*cmdBuf, deUint32(vertexCount), 1u /*run pipeline once*/, 0u /*first vertex*/, 0u /*first instanceIndex*/);
+       vk.cmdEndRenderPass(*cmdBuf);
+
+       {
+               const VkImageMemoryBarrier      renderFinishBarrier     =
+               {
+                       VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,         //      VkStructureType                 sType;
+                       DE_NULL,                                                                        //      const void*                             pNext;
+                       VK_MEMORY_OUTPUT_COLOR_ATTACHMENT_BIT,          //      VkMemoryOutputFlags             outputMask;
+                       VK_MEMORY_INPUT_TRANSFER_BIT,                           //      VkMemoryInputFlags              inputMask;
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,       //      VkImageLayout                   oldLayout;
+                       VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,        //      VkImageLayout                   newLayout;
+                       queueFamilyIndex,                                                       //      deUint32                                srcQueueFamilyIndex;
+                       queueFamilyIndex,                                                       //      deUint32                                destQueueFamilyIndex;
+                       *image,                                                                         //      VkImage                                 image;
+                       {
+                               VK_IMAGE_ASPECT_COLOR_BIT,                                      //      VkImageAspectFlags      aspectMask;
+                               0u,                                                                                     //      deUint32                        baseMipLevel;
+                               1u,                                                                                     //      deUint32                        mipLevels;
+                               0u,                                                                                     //      deUint32                        baseArraySlice;
+                               1u,                                                                                     //      deUint32                        arraySize;
+                       }                                                                                       //      VkImageSubresourceRange subresourceRange;
+               };
+               const void*                             barriers[]                              = { &renderFinishBarrier };
+               vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS, VK_PIPELINE_STAGE_TRANSFER_BIT, DE_FALSE, (deUint32)DE_LENGTH_OF_ARRAY(barriers), barriers);
+       }
+
+       {
+               const VkBufferImageCopy copyParams      =
+               {
+                       (VkDeviceSize)0u,                                               //      VkDeviceSize                    bufferOffset;
+                       (deUint32)renderSize.x(),                               //      deUint32                                bufferRowLength;
+                       (deUint32)renderSize.y(),                               //      deUint32                                bufferImageHeight;
+                       {
+                               VK_IMAGE_ASPECT_COLOR,                                  //      VkImageAspect           aspect;
+                               0u,                                                                             //      deUint32                        mipLevel;
+                               0u,                                                                             //      deUint32                        arrayLayer;
+                               1u,                                                                             //      deUint32                        arraySize;
+                       },                                                                              //      VkImageSubresourceCopy  imageSubresource;
+                       { 0u, 0u, 0u },                                                 //      VkOffset3D                              imageOffset;
+                       { renderSize.x(), renderSize.y(), 1u }  //      VkExtent3D                              imageExtent;
+               };
+               vk.cmdCopyImageToBuffer(*cmdBuf, *image, VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, *readImageBuffer, 1u, &copyParams);
+       }
+
+       {
+               const VkBufferMemoryBarrier     copyFinishBarrier       =
+               {
+                       VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,        //      VkStructureType         sType;
+                       DE_NULL,                                                                        //      const void*                     pNext;
+                       VK_MEMORY_OUTPUT_TRANSFER_BIT,                          //      VkMemoryOutputFlags     outputMask;
+                       VK_MEMORY_INPUT_HOST_READ_BIT,                          //      VkMemoryInputFlags      inputMask;
+                       queueFamilyIndex,                                                       //      deUint32                        srcQueueFamilyIndex;
+                       queueFamilyIndex,                                                       //      deUint32                        destQueueFamilyIndex;
+                       *readImageBuffer,                                                       //      VkBuffer                        buffer;
+                       0u,                                                                                     //      VkDeviceSize            offset;
+                       imageSizeBytes                                                          //      VkDeviceSize            size;
+               };
+               const void*                             barriers[]                              = { &copyFinishBarrier };
+               vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, DE_FALSE, (deUint32)DE_LENGTH_OF_ARRAY(barriers), barriers);
+       }
+
+       VK_CHECK(vk.endCommandBuffer(*cmdBuf));
+
+       // Upload vertex data
+       {
+               const VkMappedMemoryRange       range                   =
+               {
+                       VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,  //      VkStructureType sType;
+                       DE_NULL,                                                                //      const void*             pNext;
+                       vertexBufferMemory->getMemory(),                //      VkDeviceMemory  mem;
+                       0,                                                                              //      VkDeviceSize    offset;
+                       (VkDeviceSize)sizeof(vertexData),               //      VkDeviceSize    size;
+               };
+               void*                                           vertexBufPtr    = vertexBufferMemory->getHostPtr();
+
+               deMemcpy(vertexBufPtr, &vertexData[0], sizeof(vertexData));
+               VK_CHECK(vk.flushMappedMemoryRanges(vkDevice, 1u, &range));
+       }
+
+       // Submit & wait for completion
+       {
+               const VkFenceCreateInfo fenceParams     =
+               {
+                       VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,    //      VkStructureType         sType;
+                       DE_NULL,                                                                //      const void*                     pNext;
+                       0u,                                                                             //      VkFenceCreateFlags      flags;
+               };
+               const Unique<VkFence>   fence           (createFence(vk, vkDevice, &fenceParams));
+
+               VK_CHECK(vk.queueSubmit(queue, 1u, &cmdBuf.get(), *fence));
+               VK_CHECK(vk.waitForFences(vkDevice, 1u, &fence.get(), DE_TRUE, ~0ull));
+       }
+
+       void* imagePtr  = readImageBufferMemory->getHostPtr();
+       tcu::ConstPixelBufferAccess pixelBuffer(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
+                                                                                       renderSize.x(), renderSize.y(), 1, imagePtr);
+       // Log image
+       {
+               const VkMappedMemoryRange       range           =
+               {
+                       VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,  //      VkStructureType sType;
+                       DE_NULL,                                                                //      const void*             pNext;
+                       readImageBufferMemory->getMemory(),             //      VkDeviceMemory  mem;
+                       0,                                                                              //      VkDeviceSize    offset;
+                       imageSizeBytes,                                                 //      VkDeviceSize    size;
+               };
+
+               VK_CHECK(vk.invalidateMappedMemoryRanges(vkDevice, 1u, &range));
+               context.getTestContext().getLog() << TestLog::Image("Result", "Result", pixelBuffer);
+       }
+
+       const RGBA threshold(1, 1, 1, 1);
+       const RGBA upperLeft(pixelBuffer.getPixel(1, 1));
+       if (!tcu::compareThreshold(upperLeft, instance.outputColors[0], threshold)) {
+               return TestStatus::fail("Upper left corner mismatch");
+       }
+
+       const RGBA upperRight(pixelBuffer.getPixel(pixelBuffer.getWidth() - 1, 1));
+       if (!tcu::compareThreshold(upperRight, instance.outputColors[1], threshold)) {
+               return TestStatus::fail("Upper right corner mismatch");
+       }
+
+       const RGBA lowerLeft(pixelBuffer.getPixel(1, pixelBuffer.getHeight() - 1));
+       if (!tcu::compareThreshold(lowerLeft, instance.outputColors[2], threshold)) {
+               return TestStatus::fail("Lower left corner mismatch");
+       }
+
+       const RGBA lowerRight(pixelBuffer.getPixel(pixelBuffer.getWidth() - 1, pixelBuffer.getHeight() - 1));
+       if (!tcu::compareThreshold(lowerRight, instance.outputColors[3], threshold)) {
+               return TestStatus::fail("Lower right corner mismatch");
+       }
+
+       return TestStatus::pass("Rendered output matches input");
 }
 
 }
 
+void createTestsForAllStages(const std::string& name,
+                                                        const RGBA (&inputColors)[4],
+                                                        const RGBA (&outputColors)[4],
+                                                        const map<string, string>& testCodeFragments,
+                                                        tcu::TestCaseGroup* tests)
+{
+       const ShaderElement             pipelineStages[]                                =
+       {
+               ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX),
+               ShaderElement("tessc", "main", VK_SHADER_STAGE_TESS_CONTROL),
+               ShaderElement("tesse", "main", VK_SHADER_STAGE_TESS_EVALUATION),
+               ShaderElement("geom", "main", VK_SHADER_STAGE_GEOMETRY),
+               ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT),
+       };
+
+       addFunctionCaseWithPrograms<InstanceContext>(tests, name + "-vert", "", addShaderCodeCustomVertex, runAndVerifyDefaultPipeline,
+                                                                                                createInstanceContext(pipelineStages, inputColors, outputColors, testCodeFragments));
+
+       addFunctionCaseWithPrograms<InstanceContext>(tests, name + "-tessc", "", addShaderCodeCustomTessControl, runAndVerifyDefaultPipeline,
+                                                                                                createInstanceContext(pipelineStages, inputColors, outputColors, testCodeFragments));
+
+       addFunctionCaseWithPrograms<InstanceContext>(tests, name + "-tesse", "", addShaderCodeCustomTessEval, runAndVerifyDefaultPipeline,
+                                                                                                createInstanceContext(pipelineStages, inputColors, outputColors, testCodeFragments));
+
+       addFunctionCaseWithPrograms<InstanceContext>(tests, name + "-geom", "", addShaderCodeCustomGeometry, runAndVerifyDefaultPipeline,
+                                                                                                createInstanceContext(pipelineStages, inputColors, outputColors, testCodeFragments));
+
+       addFunctionCaseWithPrograms<InstanceContext>(tests, name + "-frag", "", addShaderCodeCustomFragment, runAndVerifyDefaultPipeline,
+                                                                                                createInstanceContext(pipelineStages, inputColors, outputColors, testCodeFragments));
+}
 } // anonymous
 
 tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
 } // anonymous
 
 tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
@@ -1674,7 +3013,12 @@ tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
        instructionTests->addChild(createBlockOrderGroup(testCtx));
        instructionTests->addChild(createOpUndefGroup(testCtx));
        instructionTests->addChild(createOpUnreachableGroup(testCtx));
        instructionTests->addChild(createBlockOrderGroup(testCtx));
        instructionTests->addChild(createOpUndefGroup(testCtx));
        instructionTests->addChild(createOpUnreachableGroup(testCtx));
-
+    
+    RGBA defaultColors[4];
+       getDefaultColors(defaultColors);
+       de::MovePtr<tcu::TestCaseGroup> group   (new tcu::TestCaseGroup(testCtx, "graphics-assembly", "Test the graphics pipeline"));
+       createTestsForAllStages("passthru", defaultColors, defaultColors, passthruFragments(), group.get());
+       instructionTests->addChild(group.release());
        return instructionTests.release();
 }
 
        return instructionTests.release();
 }
 
index 15e0f36..0fc703c 100644 (file)
@@ -72,14 +72,16 @@ string StringTemplate::specialize (const map<string, string>& params) const
                        // Parse parameter contents.
                        string  paramStr                = m_template.substr(paramNdx+2, paramEndNdx-2-paramNdx);
                        bool    paramSingleLine = false;
                        // Parse parameter contents.
                        string  paramStr                = m_template.substr(paramNdx+2, paramEndNdx-2-paramNdx);
                        bool    paramSingleLine = false;
+                       bool    paramOptional   = false;
                        string  paramName;
                        size_t colonNdx = paramStr.find(":");
                        if (colonNdx != string::npos)
                        {
                                paramName = paramStr.substr(0, colonNdx);
                                string flagsStr = paramStr.substr(colonNdx+1);
                        string  paramName;
                        size_t colonNdx = paramStr.find(":");
                        if (colonNdx != string::npos)
                        {
                                paramName = paramStr.substr(0, colonNdx);
                                string flagsStr = paramStr.substr(colonNdx+1);
-                               TCU_CHECK(flagsStr == "single-line");
-                               paramSingleLine = true;
+                               if (flagsStr == "single-line") { paramSingleLine = true; }
+                               else if (flagsStr == "opt") { paramOptional = true; }
+                               else { throw tcu::InternalError("Unrecognized flag", paramStr.c_str(), __FILE__, __LINE__); }
                        }
                        else
                                paramName = paramStr;
                        }
                        else
                                paramName = paramStr;
@@ -98,7 +100,7 @@ string StringTemplate::specialize (const map<string, string>& params) const
                                else
                                        res << val;
                        }
                                else
                                        res << val;
                        }
-                       else
+                       else if (!paramOptional)
                                throw tcu::InternalError((string("Value for parameter '") + paramName + "' not found in map").c_str(), "", __FILE__, __LINE__);
 
                        // Skip over template.
                                throw tcu::InternalError((string("Value for parameter '") + paramName + "' not found in map").c_str(), "", __FILE__, __LINE__);
 
                        // Skip over template.