Add tessellation shader tests (ported from ES 3.1)
authorMaciej Jesionowski <maciej.jesionowski@mobica.com>
Fri, 18 Mar 2016 15:15:47 +0000 (16:15 +0100)
committerMaciej Jesionowski <maciej.jesionowski@mobica.com>
Tue, 10 May 2016 07:45:30 +0000 (09:45 +0200)
Changes with respect to original tests:
- Transform Feedback replaced with SSBO usage
  (impacted some failure conditions)
- Geometry shader had to be used in some situations
- GL queries replaced with Vulkan limits checks
- Negative tests not ported due to ShaderLibrary limitation
  (shaders that fail compilation aren't supported)

Mustpass exclusions:
- Tests in `user_defined_io` group

85 files changed:
external/fetch_sources.py
external/glslang/CMakeLists.txt
external/vulkancts/data/vulkan/data/tessellation/barrier_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/gl_position_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_0.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_1.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_2.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/patch_vertices_10_in_5_out_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/patch_vertices_5_in_10_out_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/primitive_id_tcs_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/primitive_id_tes_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/user_defined_io_isolines_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/user_defined_io_quads_ref.png [new file with mode: 0644]
external/vulkancts/data/vulkan/data/tessellation/user_defined_io_triangles_ref.png [new file with mode: 0644]
external/vulkancts/modules/vulkan/CMakeLists.txt
external/vulkancts/modules/vulkan/tessellation/CMakeLists.txt [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationCommonEdgeTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationCommonEdgeTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationCoordinatesTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationCoordinatesTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationFractionalSpacingTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationFractionalSpacingTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationInvarianceTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationInvarianceTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationLimitsTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationLimitsTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationMiscDrawTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationMiscDrawTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationPrimitiveDiscardTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationPrimitiveDiscardTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationShaderInputOutputTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationShaderInputOutputTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationUserDefinedIO.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationUserDefinedIO.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationWindingTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/tessellation/vktTessellationWindingTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/vktTestPackage.cpp
external/vulkancts/mustpass/1.0.0/src/test-issues.txt
external/vulkancts/mustpass/1.0.0/vk-default.txt

index 67024d5..1b814db 100644 (file)
@@ -174,7 +174,7 @@ PACKAGES = [
                "spirv-tools"),
        GitRepo(
                "https://github.com/KhronosGroup/glslang.git",
-               "5639f3aca5b75cbe5419a623eecf5e3794fab917",
+               "3357d870e455005a3781743e77403572eefb60d7",
                "glslang"),
 ]
 
index 7c040e3..9ad2973 100644 (file)
@@ -29,6 +29,7 @@ if (EXISTS ${GLSLANG_ABS_PATH}/glslang/GenericCodeGen/CodeGen.cpp)
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent
                ${GLSLANG_ABS_PATH}/glslang/GenericCodeGen
                ${GLSLANG_ABS_PATH}/glslang/OSDependent
+               ${GLSLANG_ABS_PATH}/hlsl
                ${GLSLANG_ABS_PATH}/OGLCompilersDLL
                ${GLSLANG_ABS_PATH}/SPIRV
                )
@@ -51,6 +52,7 @@ if (EXISTS ${GLSLANG_ABS_PATH}/glslang/GenericCodeGen/CodeGen.cpp)
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/limits.cpp
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/linkValidate.cpp
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/parseConst.cpp
+               ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/propagateNoContraction.cpp
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/reflection.cpp
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/preprocessor/Pp.cpp
                ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/preprocessor/PpAtom.cpp
@@ -63,10 +65,17 @@ if (EXISTS ${GLSLANG_ABS_PATH}/glslang/GenericCodeGen/CodeGen.cpp)
                ${GLSLANG_ABS_PATH}/glslang/GenericCodeGen/Link.cpp
                ${GLSLANG_ABS_PATH}/OGLCompilersDLL/InitializeDll.cpp
 
+               ${GLSLANG_ABS_PATH}/hlsl/hlslGrammar.cpp
+               ${GLSLANG_ABS_PATH}/hlsl/hlslOpMap.cpp
+               ${GLSLANG_ABS_PATH}/hlsl/hlslParseHelper.cpp
+               ${GLSLANG_ABS_PATH}/hlsl/hlslScanContext.cpp
+               ${GLSLANG_ABS_PATH}/hlsl/hlslTokenStream.cpp
+
                ${GLSLANG_ABS_PATH}/SPIRV/GlslangToSpv.cpp
                ${GLSLANG_ABS_PATH}/SPIRV/InReadableOrder.cpp
                ${GLSLANG_ABS_PATH}/SPIRV/SpvBuilder.cpp
                ${GLSLANG_ABS_PATH}/SPIRV/SPVRemapper.cpp
+               ${GLSLANG_ABS_PATH}/SPIRV/Logger.cpp
                ${GLSLANG_ABS_PATH}/SPIRV/doc.cpp
                ${GLSLANG_ABS_PATH}/SPIRV/disassemble.cpp
 
diff --git a/external/vulkancts/data/vulkan/data/tessellation/barrier_ref.png b/external/vulkancts/data/vulkan/data/tessellation/barrier_ref.png
new file mode 100644 (file)
index 0000000..1f68aae
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/barrier_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..8340b25
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..af13309
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..edcb04f
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_equal_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..e7752a4
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..bfc84fb
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..b205d3d
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_even_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..8340b25
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..8a2f165
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..f3faa84
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_quads_fractional_odd_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..d6b5428
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..1832754
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..d1c1c99
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_equal_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..9308abf
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..2fdc5df
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..29a85e0
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_even_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..d6b5428
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..853a31c
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..58f3352
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_cover_triangles_fractional_odd_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..d240932
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..87d5764
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..61c801d
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_equal_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..a9065f6
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..2b1f010
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..7f37cd1
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_even_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..d240932
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..f8ed000
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..e218794
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_quads_fractional_odd_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..89c3533
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..fd94847
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..a2e7044
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_equal_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..9336230
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..6a58a2a
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..342e109
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_even_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..89c3533
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..56d0fce
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..a2e7044
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/fill_overlap_triangles_fractional_odd_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/gl_position_ref.png b/external/vulkancts/data/vulkan/data/tessellation/gl_position_ref.png
new file mode 100644 (file)
index 0000000..e9a9c57
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/gl_position_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..c7b6e82
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..110b3cc
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..9744722
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_equal_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..70cb8ea
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..1659c51
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..9106ff5
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_even_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_0.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_0.png
new file mode 100644 (file)
index 0000000..c7b6e82
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_0.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_1.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_1.png
new file mode 100644 (file)
index 0000000..a90a547
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_1.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_2.png b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_2.png
new file mode 100644 (file)
index 0000000..62e87e3
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/isolines_fractional_odd_spacing_ref_2.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/patch_vertices_10_in_5_out_ref.png b/external/vulkancts/data/vulkan/data/tessellation/patch_vertices_10_in_5_out_ref.png
new file mode 100644 (file)
index 0000000..adf9cf7
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/patch_vertices_10_in_5_out_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/patch_vertices_5_in_10_out_ref.png b/external/vulkancts/data/vulkan/data/tessellation/patch_vertices_5_in_10_out_ref.png
new file mode 100644 (file)
index 0000000..5f296ee
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/patch_vertices_5_in_10_out_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/primitive_id_tcs_ref.png b/external/vulkancts/data/vulkan/data/tessellation/primitive_id_tcs_ref.png
new file mode 100644 (file)
index 0000000..011fc6c
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/primitive_id_tcs_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/primitive_id_tes_ref.png b/external/vulkancts/data/vulkan/data/tessellation/primitive_id_tes_ref.png
new file mode 100644 (file)
index 0000000..011fc6c
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/primitive_id_tes_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_isolines_ref.png b/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_isolines_ref.png
new file mode 100644 (file)
index 0000000..310c9d8
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_isolines_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_quads_ref.png b/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_quads_ref.png
new file mode 100644 (file)
index 0000000..02bf1d5
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_quads_ref.png differ
diff --git a/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_triangles_ref.png b/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_triangles_ref.png
new file mode 100644 (file)
index 0000000..ce98970
Binary files /dev/null and b/external/vulkancts/data/vulkan/data/tessellation/user_defined_io_triangles_ref.png differ
index 6ecf784..4a5b5db 100644 (file)
@@ -16,6 +16,7 @@ add_subdirectory(compute)
 add_subdirectory(image)
 add_subdirectory(wsi)
 add_subdirectory(sparse_resources)
+add_subdirectory(tessellation)
 
 include_directories(
        api
@@ -34,6 +35,7 @@ include_directories(
        image
        wsi
        sparse_resources
+       tessellation
        )
 
 set(DEQP_VK_COMMON_SRCS
@@ -75,6 +77,7 @@ set(DEQP_VK_COMMON_LIBS
        deqp-vk-image
        deqp-vk-wsi
        deqp-vk-sparse-resources
+       deqp-vk-tessellation
        )
 
 add_library(deqp-vk-common STATIC ${DEQP_VK_COMMON_SRCS})
diff --git a/external/vulkancts/modules/vulkan/tessellation/CMakeLists.txt b/external/vulkancts/modules/vulkan/tessellation/CMakeLists.txt
new file mode 100644 (file)
index 0000000..78b7f0e
--- /dev/null
@@ -0,0 +1,37 @@
+include_directories(..)
+
+set(DEQP_VK_TESSELLATION_SRCS
+       vktTessellationTests.cpp
+       vktTessellationTests.hpp
+       vktTessellationUtil.cpp
+       vktTessellationUtil.hpp
+       vktTessellationLimitsTests.hpp
+       vktTessellationLimitsTests.cpp
+       vktTessellationCoordinatesTests.hpp
+       vktTessellationCoordinatesTests.cpp
+       vktTessellationWindingTests.hpp
+       vktTessellationWindingTests.cpp
+       vktTessellationShaderInputOutputTests.hpp
+       vktTessellationShaderInputOutputTests.cpp
+       vktTessellationMiscDrawTests.hpp
+       vktTessellationMiscDrawTests.cpp
+       vktTessellationCommonEdgeTests.hpp
+       vktTessellationCommonEdgeTests.cpp
+       vktTessellationFractionalSpacingTests.hpp
+       vktTessellationFractionalSpacingTests.cpp
+       vktTessellationPrimitiveDiscardTests.hpp
+       vktTessellationPrimitiveDiscardTests.cpp
+       vktTessellationInvarianceTests.hpp
+       vktTessellationInvarianceTests.cpp
+       vktTessellationUserDefinedIO.hpp
+       vktTessellationUserDefinedIO.cpp
+       )
+
+set(DEQP_VK_TESSELLATION_LIBS
+       deqp-vk-common
+       tcutil
+       vkutil
+       )
+
+add_library(deqp-vk-tessellation STATIC ${DEQP_VK_TESSELLATION_SRCS})
+target_link_libraries(deqp-vk-tessellation ${DEQP_VK_TESSELLATION_LIBS})
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationCommonEdgeTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationCommonEdgeTests.cpp
new file mode 100644 (file)
index 0000000..db0b2d4
--- /dev/null
@@ -0,0 +1,515 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Common Edge Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationCommonEdgeTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuTexture.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum CaseType
+{
+       CASETYPE_BASIC = 0,             //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
+       CASETYPE_PRECISE,               //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
+
+       CASETYPE_LAST
+};
+
+struct CaseDefinition
+{
+       TessPrimitiveType       primitiveType;
+       SpacingMode                     spacingMode;
+       CaseType                        caseType;
+};
+
+//! Check that a certain rectangle in the image contains no black pixels.
+//! Returns true if an image successfully passess the verification.
+bool verifyResult (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image)
+{
+       const int startX = static_cast<int>(0.15f * image.getWidth());
+       const int endX   = static_cast<int>(0.85f * image.getWidth());
+       const int startY = static_cast<int>(0.15f * image.getHeight());
+       const int endY   = static_cast<int>(0.85f * image.getHeight());
+
+       for (int y = startY; y < endY; ++y)
+       for (int x = startX; x < endX; ++x)
+       {
+               const tcu::Vec4 pixel = image.getPixel(x, y);
+
+               if (pixel.x() == 0 && pixel.y() == 0 && pixel.z() == 0)
+               {
+                       log << tcu::TestLog::Message << "Failure: there seem to be cracks in the rendered result" << tcu::TestLog::EndMessage
+                               << tcu::TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << tcu::TestLog::EndMessage;
+
+                       return false;
+               }
+       }
+
+       log << tcu::TestLog::Message << "Success: there seem to be no cracks in the rendered result" << tcu::TestLog::EndMessage;
+
+       return true;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in highp vec2  in_v_position;\n"
+                       << "layout(location = 1) in highp float in_v_tessParam;\n"
+                       << "\n"
+                       << "layout(location = 0) out highp vec2  in_tc_position;\n"
+                       << "layout(location = 1) out highp float in_tc_tessParam;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_position = in_v_position;\n"
+                       << "    in_tc_tessParam = in_v_tessParam;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "")
+                       << "\n"
+                       << "layout(vertices = " << numVertices << ") out;\n"
+                       << "\n"
+                       << "layout(location = 0) in highp vec2  in_tc_position[];\n"
+                       << "layout(location = 1) in highp float in_tc_tessParam[];\n"
+                       << "\n"
+                       << "layout(location = 0) out highp vec2 in_te_position[];\n"
+                       << "\n"
+                       << (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "")
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = 5.0;\n"
+                       << "    gl_TessLevelInner[1] = 5.0;\n"
+                       << "\n"
+                       << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
+                               "    gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
+                               "    gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
+                               "    gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
+                               : caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
+                               "    gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
+                               "    gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
+                               "    gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
+                               "    gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
+                               : "")
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream primitiveSpecificCode;
+               if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                       primitiveSpecificCode
+                       << "    highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
+                       << "\n"
+                       << "    highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
+                       << "    in_f_color = vec4(gl_TessCoord*f, 1.0);\n";
+               else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+                       primitiveSpecificCode
+                       << (caseDef.caseType == CASETYPE_BASIC ?
+                               "    highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
+                               "                   + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
+                               "                   + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2]\n"
+                               "                   + (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
+                               : caseDef.caseType == CASETYPE_PRECISE ?
+                               "    highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
+                               "    highp vec2 b = (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
+                               "    highp vec2 c = (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[2];\n"
+                               "    highp vec2 d = (    gl_TessCoord.x)*(    gl_TessCoord.y)*in_te_position[3];\n"
+                               "    highp vec2 pos = a+b+c+d;\n"
+                               : "")
+                       << "\n"
+                       << "    highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n"
+                       << "    in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n";
+
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "")
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+                                                << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) in highp vec2 in_te_position[];\n"
+                       << "\n"
+                       << "layout(location = 0) out mediump vec4 in_f_color;\n"
+                       << "\n"
+                       << (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "")
+                       << "void main (void)\n"
+                       << "{\n"
+                       << primitiveSpecificCode.str()
+                       << "\n"
+                       << "    // Offset the position slightly, based on the parity of the bits in the float representation.\n"
+                       << "    // This is done to detect possible small differences in edge vertex positions between patches.\n"
+                       << "    uvec2 bits = floatBitsToUint(pos);\n"
+                       << "    uint numBits = 0u;\n"
+                       << "    for (uint i = 0u; i < 32u; i++)\n"
+                       << "        numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
+                       << "    pos += float(numBits&1u)*0.04;\n"
+                       << "\n"
+                       << "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in mediump vec4 in_f_color;\n"
+                       << "\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = in_f_color;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+//! Generic test code used by all test cases.
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+       DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+       DE_ASSERT(caseDef.caseType == CASETYPE_BASIC || caseDef.caseType == CASETYPE_PRECISE);
+
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+       const DeviceInterface&  vk                                      = context.getDeviceInterface();
+       const VkDevice                  device                          = context.getDevice();
+       const VkQueue                   queue                           = context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = context.getDefaultAllocator();
+
+       // Prepare test data
+
+       std::vector<float>              gridPosComps;
+       std::vector<float>              gridTessParams;
+       std::vector<deUint16>   gridIndices;
+
+       {
+               const int gridWidth                             = 4;
+               const int gridHeight                    = 4;
+               const int numVertices                   = (gridWidth+1)*(gridHeight+1);
+               const int numIndices                    = gridWidth*gridHeight * (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : 4);
+               const int numPosCompsPerVertex  = 2;
+               const int totalNumPosComps              = numPosCompsPerVertex*numVertices;
+
+               gridPosComps.reserve(totalNumPosComps);
+               gridTessParams.reserve(numVertices);
+               gridIndices.reserve(numIndices);
+
+               {
+                       for (int i = 0; i < gridHeight+1; ++i)
+                       for (int j = 0; j < gridWidth+1; ++j)
+                       {
+                               gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
+                               gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
+                               gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
+                       }
+               }
+
+               // Generate patch vertex indices.
+               // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
+               //               triangles/quads share a vertex, it's at the same index for everyone.
+
+               if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+               {
+                       for (int i = 0; i < gridHeight; i++)
+                       for (int j = 0; j < gridWidth; j++)
+                       {
+                               const deUint16 corners[4] =
+                               {
+                                       (deUint16)((i+0)*(gridWidth+1) + j+0),
+                                       (deUint16)((i+0)*(gridWidth+1) + j+1),
+                                       (deUint16)((i+1)*(gridWidth+1) + j+0),
+                                       (deUint16)((i+1)*(gridWidth+1) + j+1)
+                               };
+
+                               const int secondTriangleVertexIndexOffset = caseDef.caseType == CASETYPE_BASIC   ? 0 : 1;
+
+                               for (int k = 0; k < 3; k++)
+                                       gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
+                               for (int k = 0; k < 3; k++)
+                                       gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
+                       }
+               }
+               else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+               {
+                       for (int i = 0; i < gridHeight; ++i)
+                       for (int j = 0; j < gridWidth; ++j)
+                       {
+                               for (int m = 0; m < 2; m++)
+                               for (int n = 0; n < 2; n++)
+                                       gridIndices.push_back((deUint16)((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2));
+
+                               if (caseDef.caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
+                                       std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
+                                                                gridIndices.begin() + gridIndices.size());
+                       }
+               }
+               else
+                       DE_ASSERT(false);
+
+               DE_ASSERT(static_cast<int>(gridPosComps.size()) == totalNumPosComps);
+               DE_ASSERT(static_cast<int>(gridTessParams.size()) == numVertices);
+               DE_ASSERT(static_cast<int>(gridIndices.size()) == numIndices);
+       }
+
+       // Vertex input buffer: we put both attributes and indices in here.
+
+       const VkDeviceSize vertexDataSizeBytes    = sizeInBytes(gridPosComps) + sizeInBytes(gridTessParams) + sizeInBytes(gridIndices);
+       const std::size_t  vertexPositionsOffset  = 0;
+       const std::size_t  vertexTessParamsOffset = sizeInBytes(gridPosComps);
+       const std::size_t  vertexIndicesOffset    = vertexTessParamsOffset + sizeInBytes(gridTessParams);
+
+       const Buffer vertexBuffer(vk, device, allocator,
+               makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       {
+               const Allocation& alloc = vertexBuffer.getAllocation();
+               deUint8* const pData = static_cast<deUint8*>(alloc.getHostPtr());
+
+               deMemcpy(pData + vertexPositionsOffset,  &gridPosComps[0],   static_cast<std::size_t>(sizeInBytes(gridPosComps)));
+               deMemcpy(pData + vertexTessParamsOffset, &gridTessParams[0], static_cast<std::size_t>(sizeInBytes(gridTessParams)));
+               deMemcpy(pData + vertexIndicesOffset,    &gridIndices[0],    static_cast<std::size_t>(sizeInBytes(gridIndices)));
+
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+               // No barrier needed, flushed memory is automatically visible
+       }
+
+       // Color attachment
+
+       const tcu::IVec2                          renderSize                             = tcu::IVec2(256, 256);
+       const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
+       const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+       const Image                                       colorAttachmentImage           (vk, device, allocator,
+                                                                                                                        makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+                                                                                                                        MemoryRequirement::Any);
+
+       // Color output buffer: image will be copied here for verification
+
+       const VkDeviceSize      colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+       const Buffer            colorBuffer                      (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+       // Pipeline
+
+       const Unique<VkImageView>          colorAttachmentView  (makeImageView                                           (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+       const Unique<VkRenderPass>         renderPass                   (makeRenderPass                                          (vk, device, colorFormat));
+       const Unique<VkFramebuffer>        framebuffer                  (makeFramebuffer                                         (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+       const Unique<VkCommandPool>        cmdPool                              (makeCommandPool                                         (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>  cmdBuffer                        (makeCommandBuffer                                       (vk, device, *cmdPool));
+       const Unique<VkPipelineLayout> pipelineLayout           (makePipelineLayoutWithoutDescriptors(vk, device));
+
+       const int inPatchSize = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setRenderSize            (renderSize)
+               .setPatchControlPoints(inPatchSize)
+               .addVertexBinding         (makeVertexInputBindingDescription(0u, sizeof(tcu::Vec2), VK_VERTEX_INPUT_RATE_VERTEX))
+               .addVertexBinding         (makeVertexInputBindingDescription(1u, sizeof(float),     VK_VERTEX_INPUT_RATE_VERTEX))
+               .addVertexAttribute       (makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32_SFLOAT, 0u))
+               .addVertexAttribute       (makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32_SFLOAT,    0u))
+               .setShader                        (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                        (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                        (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader                        (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                            context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                            (vk, device, *pipelineLayout, *renderPass));
+
+       // Draw commands
+
+       beginCommandBuffer(vk, *cmdBuffer);
+
+       // Change color attachment image layout
+       {
+               const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+                       (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                       VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+       }
+
+       // Begin render pass
+       {
+               const VkRect2D renderArea = {
+                       makeOffset2D(0, 0),
+                       makeExtent2D(renderSize.x(), renderSize.y()),
+               };
+               const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+               beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+       }
+
+       vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+       {
+               const VkBuffer buffers[] = { *vertexBuffer, *vertexBuffer };
+               const VkDeviceSize offsets[] = { vertexPositionsOffset, vertexTessParamsOffset, };
+               vk.cmdBindVertexBuffers(*cmdBuffer, 0u, DE_LENGTH_OF_ARRAY(buffers), buffers, offsets);
+
+               vk.cmdBindIndexBuffer(*cmdBuffer, *vertexBuffer, vertexIndicesOffset, VK_INDEX_TYPE_UINT16);
+       }
+
+       vk.cmdDrawIndexed(*cmdBuffer, static_cast<deUint32>(gridIndices.size()), 1u, 0u, 0, 0u);
+       endRenderPass(vk, *cmdBuffer);
+
+       // Copy render result to a host-visible buffer
+       {
+               const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+       }
+       {
+               const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+               vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
+       }
+       {
+               const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+                       VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                       0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+       }
+
+       endCommandBuffer(vk, *cmdBuffer);
+       submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+       {
+               // Log the result image.
+
+               const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+               invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+               const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+
+               tcu::TestLog& log = context.getTestContext().getLog();
+               log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess)
+                       << tcu::TestLog::Message
+                       << "Note: coloring is done to clarify the positioning and orientation of the "
+                       << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" :
+                               caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS         ? "quads"         : "")
+                       << "; the color of a vertex corresponds to the index of that vertex in the patch"
+                       << tcu::TestLog::EndMessage;
+
+               if (caseDef.caseType == CASETYPE_BASIC)
+                       log << tcu::TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << tcu::TestLog::EndMessage;
+               else if (caseDef.caseType == CASETYPE_PRECISE)
+                       log << tcu::TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << tcu::TestLog::EndMessage;
+               else
+                       DE_ASSERT(false);
+
+               // Verify the result.
+
+               const bool ok = verifyResult(log, imagePixelAccess);
+               return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+       }
+}
+
+std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const CaseType caseType)
+{
+       std::ostringstream str;
+       str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode)
+               << (caseType == CASETYPE_PRECISE ? "_precise" : "");
+       return str.str();
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.common_edge.*
+tcu::TestCaseGroup* createCommonEdgeTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them"));
+
+       static const TessPrimitiveType primitiveTypes[] =
+       {
+               TESSPRIMITIVETYPE_TRIANGLES,
+               TESSPRIMITIVETYPE_QUADS,
+       };
+
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
+       for (int caseTypeNdx = 0; caseTypeNdx < CASETYPE_LAST; ++caseTypeNdx)
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+       {
+               const TessPrimitiveType primitiveType = primitiveTypes[primitiveTypeNdx];
+               const CaseType                  caseType          = static_cast<CaseType>(caseTypeNdx);
+               const SpacingMode               spacingMode   = static_cast<SpacingMode>(spacingModeNdx);
+               const CaseDefinition    caseDef           = { primitiveType, spacingMode, caseType };
+
+               addFunctionCaseWithPrograms(group.get(), getCaseName(primitiveType, spacingMode, caseType), "", initPrograms, test, caseDef);
+       }
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationCommonEdgeTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationCommonEdgeTests.hpp
new file mode 100644 (file)
index 0000000..35e9921
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONCOMMONEDGETESTS_HPP
+#define _VKTTESSELLATIONCOMMONEDGETESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Common Edge Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createCommonEdgeTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONCOMMONEDGETESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationCoordinatesTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationCoordinatesTests.cpp
new file mode 100644 (file)
index 0000000..67ce58e
--- /dev/null
@@ -0,0 +1,650 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Coordinates Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationCoordinatesTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuRGBA.hpp"
+#include "tcuSurface.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+template <typename T>
+class SizeLessThan
+{
+public:
+       bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
+};
+
+std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+       std::ostringstream str;
+       str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode);
+       return str.str();
+}
+
+std::vector<TessLevels> genTessLevelCases (const TessPrimitiveType     primitiveType,
+                                                                                  const SpacingMode            spacingMode)
+{
+       static const TessLevels rawTessLevelCases[] =
+       {
+               { { 1.0f,       1.0f    },      { 1.0f,         1.0f,   1.0f,   1.0f    } },
+               { { 63.0f,      24.0f   },      { 15.0f,        42.0f,  10.0f,  12.0f   } },
+               { { 3.0f,       2.0f    },      { 6.0f,         8.0f,   7.0f,   9.0f    } },
+               { { 4.0f,       6.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
+               { { 2.0f,       2.0f    },      { 6.0f,         8.0f,   7.0f,   9.0f    } },
+               { { 5.0f,       6.0f    },      { 1.0f,         1.0f,   1.0f,   1.0f    } },
+               { { 1.0f,       6.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
+               { { 5.0f,       1.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
+               { { 5.2f,       1.6f    },      { 2.9f,         3.4f,   1.5f,   4.1f    } }
+       };
+
+       if (spacingMode == SPACINGMODE_EQUAL)
+               return std::vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
+       else
+       {
+               std::vector<TessLevels> result;
+               result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
+
+               for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); ++tessLevelCaseNdx)
+               {
+                       TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
+
+                       float* const inner = &curTessLevelCase.inner[0];
+                       float* const outer = &curTessLevelCase.outer[0];
+
+                       for (int j = 0; j < 2; ++j) inner[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[j]));
+                       for (int j = 0; j < 4; ++j) outer[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, outer[j]));
+
+                       if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                       {
+                               if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
+                               {
+                                       if (inner[0] == 1.0f)
+                                               inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
+                               }
+                       }
+                       else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
+                       {
+                               if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
+                               {
+                                       if (inner[0] == 1.0f) inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
+                                       if (inner[1] == 1.0f) inner[1] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[1] + 0.1f));
+                               }
+                       }
+
+                       result.push_back(curTessLevelCase);
+               }
+
+               DE_ASSERT(static_cast<int>(result.size()) == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
+               return result;
+       }
+}
+
+std::vector<tcu::Vec3> generateReferenceTessCoords (const TessPrimitiveType    primitiveType,
+                                                                                                       const SpacingMode               spacingMode,
+                                                                                                       const float*                    innerLevels,
+                                                                                                       const float*                    outerLevels)
+{
+       if (isPatchDiscarded(primitiveType, outerLevels))
+               return std::vector<tcu::Vec3>();
+
+       switch (primitiveType)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:
+               {
+                       int inner;
+                       int outer[3];
+                       getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
+
+                       if (spacingMode != SPACINGMODE_EQUAL)
+                       {
+                               // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
+                               DE_ASSERT(de::abs(innerLevels[0] - static_cast<float>(inner)) < 0.001f);
+                               for (int i = 0; i < 3; ++i)
+                                       DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
+                               DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
+                       }
+
+                       return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
+               }
+
+               case TESSPRIMITIVETYPE_QUADS:
+               {
+                       int inner[2];
+                       int outer[4];
+                       getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
+
+                       if (spacingMode != SPACINGMODE_EQUAL)
+                       {
+                               // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
+                               for (int i = 0; i < 2; ++i)
+                                       DE_ASSERT(de::abs(innerLevels[i] - static_cast<float>(inner[i])) < 0.001f);
+                               for (int i = 0; i < 4; ++i)
+                                       DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
+
+                               DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
+                       }
+
+                       return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
+               }
+
+               case TESSPRIMITIVETYPE_ISOLINES:
+               {
+                       int outer[2];
+                       getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
+
+                       if (spacingMode != SPACINGMODE_EQUAL)
+                       {
+                               // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
+                               DE_ASSERT(de::abs(outerLevels[1] - static_cast<float>(outer[1])) < 0.001f);
+                       }
+
+                       return generateReferenceIsolineTessCoords(outer[0], outer[1]);
+               }
+
+               default:
+                       DE_ASSERT(false);
+                       return std::vector<tcu::Vec3>();
+       }
+}
+
+void drawPoint (tcu::Surface& dst, const int centerX, const int centerY, const tcu::RGBA& color, const int size)
+{
+       const int width         = dst.getWidth();
+       const int height        = dst.getHeight();
+       DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
+       DE_ASSERT(size > 0);
+
+       for (int yOff = -((size-1)/2); yOff <= size/2; ++yOff)
+       for (int xOff = -((size-1)/2); xOff <= size/2; ++xOff)
+       {
+               const int pixX = centerX + xOff;
+               const int pixY = centerY + yOff;
+               if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
+                       dst.setPixel(pixX, pixY, color);
+       }
+}
+
+void drawTessCoordPoint (tcu::Surface& dst, const TessPrimitiveType primitiveType, const tcu::Vec3& pt, const tcu::RGBA& color, const int size)
+{
+       // \note These coordinates should match the description in the log message in TessCoordTestInstance::iterate.
+
+       static const tcu::Vec2 triangleCorners[3] =
+       {
+               tcu::Vec2(0.95f, 0.95f),
+               tcu::Vec2(0.5f,  0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
+               tcu::Vec2(0.05f, 0.95f)
+       };
+
+       static const float quadIsolineLDRU[4] =
+       {
+               0.1f, 0.9f, 0.9f, 0.1f
+       };
+
+       const tcu::Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
+                                                                                                                                                 + pt.y()*triangleCorners[1]
+                                                                                                                                                 + pt.z()*triangleCorners[2]
+
+                                         : primitiveType == TESSPRIMITIVETYPE_QUADS ||
+                                               primitiveType == TESSPRIMITIVETYPE_ISOLINES ? tcu::Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
+                                                                                                                                                           (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
+
+                                         : tcu::Vec2(-1.0f);
+
+       drawPoint(dst, static_cast<int>(dstPos.x() * dst.getWidth()), static_cast<int>(dstPos.y() * dst.getHeight()), color, size);
+}
+
+void drawTessCoordVisualization (tcu::Surface& dst, const TessPrimitiveType primitiveType, const std::vector<tcu::Vec3>& coords)
+{
+       const int imageWidth  = 256;
+       const int imageHeight = 256;
+       dst.setSize(imageWidth, imageHeight);
+
+       tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
+
+       for (int i = 0; i < static_cast<int>(coords.size()); ++i)
+               drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
+}
+
+inline bool vec3XLessThan (const tcu::Vec3& a, const tcu::Vec3& b)
+{
+       return a.x() < b.x();
+}
+
+int binarySearchFirstVec3WithXAtLeast (const std::vector<tcu::Vec3>& sorted, float x)
+{
+       const tcu::Vec3 ref(x, 0.0f, 0.0f);
+       const std::vector<tcu::Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
+       if (first == sorted.end())
+               return -1;
+       return static_cast<int>(std::distance(sorted.begin(), first));
+}
+
+// Check that all points in subset are (approximately) present also in superset.
+bool oneWayComparePointSets (tcu::TestLog&                                     log,
+                                                        tcu::Surface&                                  errorDst,
+                                                        const TessPrimitiveType                primitiveType,
+                                                        const std::vector<tcu::Vec3>&  subset,
+                                                        const std::vector<tcu::Vec3>&  superset,
+                                                        const char*                                    subsetName,
+                                                        const char*                                    supersetName,
+                                                        const tcu::RGBA&                               errorColor)
+{
+       const std::vector<tcu::Vec3> supersetSorted              = sorted(superset, vec3XLessThan);
+       const float                                      epsilon                         = 0.01f;
+       const int                                        maxNumFailurePrints = 5;
+       int                                                      numFailuresDetected = 0;
+
+       for (int subNdx = 0; subNdx < static_cast<int>(subset.size()); ++subNdx)
+       {
+               const tcu::Vec3& subPt = subset[subNdx];
+
+               bool matchFound = false;
+
+               {
+                       // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
+                       const tcu::Vec3 matchMin                        = subPt - epsilon;
+                       const tcu::Vec3 matchMax                        = subPt + epsilon;
+                       const int               firstCandidateNdx       = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
+
+                       if (firstCandidateNdx >= 0)
+                       {
+                               // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
+                               for (int superNdx = firstCandidateNdx; superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x(); ++superNdx)
+                               {
+                                       const tcu::Vec3& superPt = supersetSorted[superNdx];
+
+                                       if (tcu::boolAll(tcu::greaterThanEqual  (superPt, matchMin)) &&
+                                               tcu::boolAll(tcu::lessThanEqual         (superPt, matchMax)))
+                                       {
+                                               matchFound = true;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               if (!matchFound)
+               {
+                       ++numFailuresDetected;
+                       if (numFailuresDetected < maxNumFailurePrints)
+                               log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << tcu::TestLog::EndMessage;
+                       else if (numFailuresDetected == maxNumFailurePrints)
+                               log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage;
+
+                       drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
+               }
+       }
+
+       return numFailuresDetected == 0;
+}
+
+//! Returns true on matching coordinate sets.
+bool compareTessCoords (tcu::TestLog&                                  log,
+                                               TessPrimitiveType                               primitiveType,
+                                               const std::vector<tcu::Vec3>&   refCoords,
+                                               const std::vector<tcu::Vec3>&   resCoords)
+{
+       tcu::Surface    refVisual;
+       tcu::Surface    resVisual;
+       bool                    success = true;
+
+       drawTessCoordVisualization(refVisual, primitiveType, refCoords);
+       drawTessCoordVisualization(resVisual, primitiveType, resCoords);
+
+       // Check that all points in reference also exist in result.
+       success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
+       // Check that all points in result also exist in reference.
+       success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
+
+       if (!success)
+       {
+               log << tcu::TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << tcu::TestLog::EndMessage
+                       << tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
+                       << tcu::TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << tcu::TestLog::EndMessage;
+       }
+
+       log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
+
+       return success;
+}
+
+class TessCoordTest : public TestCase
+{
+public:
+                                                               TessCoordTest   (tcu::TestContext&                      testCtx,
+                                                                                                const TessPrimitiveType        primitiveType,
+                                                                                                const SpacingMode                      spacingMode);
+
+       void                                            initPrograms    (SourceCollections&                     programCollection) const;
+       TestInstance*                           createInstance  (Context&                                       context) const;
+
+private:
+       const TessPrimitiveType         m_primitiveType;
+       const SpacingMode                       m_spacingMode;
+};
+
+TessCoordTest::TessCoordTest (tcu::TestContext&                        testCtx,
+                                                         const TessPrimitiveType       primitiveType,
+                                                         const SpacingMode                     spacingMode)
+       : TestCase                      (testCtx, getCaseName(primitiveType, spacingMode), "")
+       , m_primitiveType       (primitiveType)
+       , m_spacingMode         (spacingMode)
+{
+}
+
+void TessCoordTest::initPrograms (SourceCollections& programCollection) const
+{
+       // Vertex shader - no inputs
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 1) out;\n"
+                       << "\n"
+                       << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
+                       << "    float inner0;\n"
+                       << "    float inner1;\n"
+                       << "    float outer0;\n"
+                       << "    float outer1;\n"
+                       << "    float outer2;\n"
+                       << "    float outer3;\n"
+                       << "} sb_levels;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
+                       << "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
+                       << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
+                       << "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
+                       << "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
+                                                << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n"
+                       << "\n"
+                       << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
+                       << "    int  numInvocations;\n"
+                       << "    vec3 tessCoord[];\n"            // alignment is 16 bytes, same as vec4
+                       << "} sb_out;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
+                       << "    sb_out.tessCoord[index] = gl_TessCoord;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+}
+
+class TessCoordTestInstance : public TestInstance
+{
+public:
+                                                               TessCoordTestInstance (Context&                                 context,
+                                                                                                          const TessPrimitiveType      primitiveType,
+                                                                                                          const SpacingMode            spacingMode);
+
+       tcu::TestStatus                         iterate                           (void);
+
+private:
+       const TessPrimitiveType         m_primitiveType;
+       const SpacingMode                       m_spacingMode;
+};
+
+TessCoordTestInstance::TessCoordTestInstance (Context&                                 context,
+                                                                                         const TessPrimitiveType       primitiveType,
+                                                                                         const SpacingMode                     spacingMode)
+       : TestInstance          (context)
+       , m_primitiveType       (primitiveType)
+       , m_spacingMode         (spacingMode)
+{
+}
+
+tcu::TestStatus TessCoordTestInstance::iterate (void)
+{
+       const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
+       const VkDevice                  device                          = m_context.getDevice();
+       const VkQueue                   queue                           = m_context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = m_context.getDefaultAllocator();
+
+       // Test data
+
+       const std::vector<TessLevels>            tessLevelCases                 = genTessLevelCases(m_primitiveType, m_spacingMode);
+       std::vector<std::vector<tcu::Vec3> > allReferenceTessCoords     (tessLevelCases.size());
+
+       for (deUint32 i = 0; i < tessLevelCases.size(); ++i)
+               allReferenceTessCoords[i] = generateReferenceTessCoords(m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
+
+       const int maxNumVertices = static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(), SizeLessThan<std::vector<tcu::Vec3> >())->size());
+
+       // Input buffer: tessellation levels. Data is filled in later.
+
+       const Buffer tessLevelsBuffer(vk, device, allocator,
+               makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Output buffer: number of invocations + padding + tessellation coordinates. Initialized later.
+
+       const int          resultBufferTessCoordsOffset  = 4 * sizeof(deInt32);
+       const int          extraneousVertices                    = 16;  // allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary)
+       const VkDeviceSize resultBufferSizeBytes                 = resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices)*sizeof(tcu::Vec4);
+       const Buffer       resultBuffer                                  (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+
+       const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
+       const VkDescriptorBufferInfo resultBufferInfo     = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(vk, device);
+
+       // Pipeline: set up vertex processing without rasterization
+
+       const Unique<VkRenderPass>              renderPass    (makeRenderPassWithoutAttachments (vk, device));
+       const Unique<VkFramebuffer>             framebuffer   (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+       const Unique<VkPipelineLayout>  pipelineLayout(makePipelineLayout               (vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>             cmdPool       (makeCommandPool                  (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>   cmdBuffer     (makeCommandBuffer                (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      m_context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        m_context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+               .build    (vk, device, *pipelineLayout, *renderPass));
+
+       deUint32 numPassedCases = 0;
+
+       // Repeat the test for all tessellation coords cases
+       for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
+       {
+               m_context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType)
+                       << tcu::TestLog::EndMessage;
+
+               // Upload tessellation levels data to the input buffer
+               {
+                       const Allocation& alloc = tessLevelsBuffer.getAllocation();
+                       TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
+                       *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+               }
+
+               // Clear the results buffer
+               {
+                       const Allocation& alloc = resultBuffer.getAllocation();
+                       deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+               }
+
+               // Reset the command buffer and begin recording.
+               beginCommandBuffer(vk, *cmdBuffer);
+               beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
+
+               vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+               vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+
+               // Process a single abstract vertex.
+               vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
+               endRenderPass(vk, *cmdBuffer);
+
+               {
+                       const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+                               VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                               0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+               }
+
+               endCommandBuffer(vk, *cmdBuffer);
+               submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+               // Verify results
+               {
+                       const Allocation& resultAlloc = resultBuffer.getAllocation();
+                       invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+                       const deInt32                                   numResults                      = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+                       const std::vector<tcu::Vec3>    resultTessCoords    = readInterleavedData<tcu::Vec3>(numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
+                       const std::vector<tcu::Vec3>&   referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx];
+                       const int                                               numExpectedResults  = static_cast<int>(referenceTessCoords.size());
+                       tcu::TestLog&                                   log                                     = m_context.getTestContext().getLog();
+
+                       if (numResults < numExpectedResults)
+                       {
+                               log << tcu::TestLog::Message
+                                       << "Failure: generated " << numResults << " coordinates, but the expected reference value is " << numExpectedResults
+                                       << tcu::TestLog::EndMessage;
+                       }
+                       else if (numResults == numExpectedResults)
+                               log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates" << tcu::TestLog::EndMessage;
+                       else
+                       {
+                               log << tcu::TestLog::Message
+                                       << "Note: generated " << numResults << " coordinates (out of which " << numExpectedResults << " must be unique)"
+                                       << tcu::TestLog::EndMessage;
+                       }
+
+                       if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                               log << tcu::TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << tcu::TestLog::EndMessage;
+                       else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+                               log << tcu::TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << tcu::TestLog::EndMessage;
+                       else
+                               DE_ASSERT(false);
+
+                       if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) && (numResults >= numExpectedResults))
+                               ++numPassedCases;
+               }
+       }  // for tessLevelCaseNdx
+
+       return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Some cases have failed"));
+}
+
+TestInstance* TessCoordTest::createInstance (Context& context) const
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode);
+}
+
+} // anonymous
+
+//! Based on dEQP-GLES31.functional.tessellation.tesscoord.*
+//! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations.
+//! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data.
+tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "tesscoord", "Tessellation coordinates tests"));
+
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+               group->addChild(new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx));
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationCoordinatesTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationCoordinatesTests.hpp
new file mode 100644 (file)
index 0000000..3dc7feb
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONCOORDINATESTESTS_HPP
+#define _VKTTESSELLATIONCOORDINATESTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Coordinates Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONCOORDINATESTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationFractionalSpacingTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationFractionalSpacingTests.cpp
new file mode 100644 (file)
index 0000000..b3f9392
--- /dev/null
@@ -0,0 +1,582 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Fractional Spacing Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationFractionalSpacingTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+template <typename T, typename MembT>
+std::vector<MembT> members (const std::vector<T>& objs, MembT T::* membP)
+{
+       std::vector<MembT> result(objs.size());
+       for (int i = 0; i < static_cast<int>(objs.size()); ++i)
+               result[i] = objs[i].*membP;
+       return result;
+}
+
+//! Predicate functor for comparing structs by their members.
+template <typename Pred, typename T, typename MembT>
+class MemberPred
+{
+public:
+                               MemberPred      (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {}
+       bool            operator()      (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); }
+
+private:
+       MembT T::*      m_membP;
+       Pred            m_pred;
+};
+
+//! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments.
+template <template <typename> class Pred, typename T, typename MembT>
+inline MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); }
+
+struct Segment
+{
+       int             index; //!< Index of left coordinate in sortedXCoords.
+       float   length;
+
+                       Segment (void)                                          : index(-1),            length(-1.0f)   {}
+                       Segment (int index_, float length_)     : index(index_),        length(length_) {}
+};
+
+inline std::vector<float> lengths (const std::vector<Segment>& segments) { return members(segments, &Segment::length); }
+
+struct LineData
+{
+       float   tessLevel;
+       float   additionalSegmentLength;
+       int             additionalSegmentLocation;
+
+                       LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {}
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Verify fractional spacing conditions for a single line
+ *
+ * Verify that the splitting of an edge (resulting from e.g. an isoline
+ * with outer levels { 1.0, tessLevel }) with a given fractional spacing
+ * mode fulfills certain conditions given in the spec.
+ *
+ * Note that some conditions can't be checked from just one line
+ * (specifically, that the additional segment decreases monotonically
+ * length and the requirement that the additional segments be placed
+ * identically for identical values of clamped level).
+ *
+ * Therefore, the function stores some values to additionalSegmentLengthDst
+ * and additionalSegmentLocationDst that can later be given to
+ * verifyFractionalSpacingMultiple(). A negative value in length means that
+ * no additional segments are present, i.e. there's just one segment.
+ * A negative value in location means that the value wasn't determinable,
+ * i.e. all segments had same length.
+ * The values are not stored if false is returned.
+ *//*--------------------------------------------------------------------*/
+bool verifyFractionalSpacingSingle (tcu::TestLog&                              log,
+                                                                       const SpacingMode                       spacingMode,
+                                                                       const float                                     tessLevel,
+                                                                       const std::vector<float>&       coords,
+                                                                       float* const                            pOutAdditionalSegmentLength,
+                                                                       int* const                                      pOutAdditionalSegmentLocation)
+{
+       DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
+
+       const float                                     clampedLevel    = getClampedTessLevel(spacingMode, tessLevel);
+       const int                                       finalLevel              = getRoundedTessLevel(spacingMode, clampedLevel);
+       const std::vector<float>        sortedCoords    = sorted(coords);
+       std::string                                     failNote                = "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n    " + containerStr(sortedCoords);
+
+       if (static_cast<int>(coords.size()) != finalLevel + 1)
+       {
+               log << tcu::TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1
+                       << " (clamped tessellation level is " << clampedLevel << ")"
+                       << "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel
+                       << " and should equal the number of segments, i.e. number of vertices minus 1" << tcu::TestLog::EndMessage
+                       << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+               return false;
+       }
+
+       if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f)
+       {
+               log << tcu::TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << tcu::TestLog::EndMessage
+                       << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+               return false;
+       }
+
+       {
+               std::vector<Segment> segments(finalLevel);
+               for (int i = 0; i < finalLevel; ++i)
+                       segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]);
+
+               failNote += "\nNote: segment lengths are, from left to right:\n    " + containerStr(lengths(segments));
+
+               {
+                       // Divide segments to two different groups based on length.
+
+                       std::vector<Segment> segmentsA;
+                       std::vector<Segment> segmentsB;
+                       segmentsA.push_back(segments[0]);
+
+                       for (int segNdx = 1; segNdx < static_cast<int>(segments.size()); ++segNdx)
+                       {
+                               const float             epsilon         = 0.001f;
+                               const Segment&  seg                     = segments[segNdx];
+
+                               if (de::abs(seg.length - segmentsA[0].length) < epsilon)
+                                       segmentsA.push_back(seg);
+                               else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon)
+                                       segmentsB.push_back(seg);
+                               else
+                               {
+                                       log << tcu::TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; "
+                                                                                                << "e.g. segment of length " << seg.length << " isn't approximately equal to either "
+                                                                                                << segmentsA[0].length << " or " << segmentsB[0].length << tcu::TestLog::EndMessage
+                                                                                                << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+                                       return false;
+                               }
+                       }
+
+                       if (clampedLevel == static_cast<float>(finalLevel))
+                       {
+                               // All segments should be of equal length.
+                               if (!segmentsA.empty() && !segmentsB.empty())
+                               {
+                                       log << tcu::TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << tcu::TestLog::EndMessage
+                                               << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+                                       return false;
+                               }
+                       }
+
+                       if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok.
+                       {
+                               *pOutAdditionalSegmentLength   = (segments.size() == 1 ? -1.0f : segments[0].length);
+                               *pOutAdditionalSegmentLocation = -1;
+                               return true;
+                       }
+
+                       if (segmentsA.size() != 2 && segmentsB.size() != 2)
+                       {
+                               log << tcu::TestLog::Message << "Failure: when dividing the segments to 2 groups by length, neither of the two groups has exactly 2 or 0 segments in it" << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+                               return false;
+                       }
+
+                       // For convenience, arrange so that the 2-segment group is segmentsB.
+                       if (segmentsB.size() != 2)
+                               std::swap(segmentsA, segmentsB);
+
+                       // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each.
+                       //               Thus, we can't be sure which ones were meant as the additional segments.
+                       //               We give the benefit of the doubt by assuming that they're the shorter
+                       //               ones (as they should).
+
+                       if (segmentsA.size() != 2)
+                       {
+                               if (segmentsB[0].length > segmentsA[0].length + 0.001f)
+                               {
+                                       log << tcu::TestLog::Message << "Failure: the two additional segments are longer than the other segments" << tcu::TestLog::EndMessage
+                                               << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+                                       return false;
+                               }
+                       }
+                       else
+                       {
+                               // We have 2 segmentsA and 2 segmentsB, ensure segmentsB has the shorter lengths
+                               if (segmentsB[0].length > segmentsA[0].length)
+                                       std::swap(segmentsA, segmentsB);
+                       }
+
+                       // Check that the additional segments are placed symmetrically.
+                       if (segmentsB[0].index + segmentsB[1].index + 1 != static_cast<int>(segments.size()))
+                       {
+                               log << tcu::TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; "
+                                                                               << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index
+                                                                               << " (note: the two indexes should sum to " << static_cast<int>(segments.size())-1 << ", i.e. numberOfSegments-1)" << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage;
+                               return false;
+                       }
+
+                       *pOutAdditionalSegmentLength = segmentsB[0].length;
+                       if (segmentsA.size() != 2)
+                               *pOutAdditionalSegmentLocation = de::min(segmentsB[0].index, segmentsB[1].index);
+                       else
+                               *pOutAdditionalSegmentLocation = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index)
+                                                                                                : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b.
+
+                       return true;
+               }
+       }
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Verify fractional spacing conditions between multiple lines
+ *
+ * Verify the fractional spacing conditions that are not checked in
+ * verifyFractionalSpacingSingle(). Uses values given by said function
+ * as parameters, in addition to the spacing mode and tessellation level.
+ *//*--------------------------------------------------------------------*/
+static bool verifyFractionalSpacingMultiple (tcu::TestLog&                             log,
+                                                                                        const SpacingMode                      spacingMode,
+                                                                                        const std::vector<float>&      tessLevels,
+                                                                                        const std::vector<float>&      additionalSegmentLengths,
+                                                                                        const std::vector<int>&        additionalSegmentLocations)
+{
+       DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
+       DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() && tessLevels.size() == additionalSegmentLocations.size());
+
+       std::vector<LineData> lineDatas;
+
+       for (int i = 0; i < static_cast<int>(tessLevels.size()); ++i)
+               lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i]));
+
+       {
+               const std::vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel));
+
+               // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation.
+
+               for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx)
+               {
+                       const LineData& curData         = lineDatasSortedByLevel[lineNdx];
+                       const LineData& prevData        = lineDatasSortedByLevel[lineNdx-1];
+
+                       if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0)
+                               continue; // Unknown locations, skip.
+
+                       if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) &&
+                               curData.additionalSegmentLocation != prevData.additionalSegmentLocation)
+                       {
+                               log << tcu::TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel
+                                                                                        << " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")"
+                                                                                        << "; but first additional segments located at indices "
+                                                                                        << curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << tcu::TestLog::EndMessage;
+                               return false;
+                       }
+               }
+
+               // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction").
+
+               for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx)
+               {
+                       const LineData&         curData                         = lineDatasSortedByLevel[lineNdx];
+                       const LineData&         prevData                        = lineDatasSortedByLevel[lineNdx-1];
+
+                       if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f)
+                               continue; // Unknown segment lengths, skip.
+
+                       const float                     curClampedLevel         = getClampedTessLevel(spacingMode, curData.tessLevel);
+                       const float                     prevClampedLevel        = getClampedTessLevel(spacingMode, prevData.tessLevel);
+                       const int                       curFinalLevel           = getRoundedTessLevel(spacingMode, curClampedLevel);
+                       const int                       prevFinalLevel          = getRoundedTessLevel(spacingMode, prevClampedLevel);
+
+                       if (curFinalLevel != prevFinalLevel)
+                               continue;
+
+                       const float                     curFraction             = static_cast<float>(curFinalLevel) - curClampedLevel;
+                       const float                     prevFraction    = static_cast<float>(prevFinalLevel) - prevClampedLevel;
+
+                       if (curData.additionalSegmentLength < prevData.additionalSegmentLength ||
+                               (curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength))
+                       {
+                               log << tcu::TestLog::Message << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, among edges with same final tessellation level" << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively"
+                                                                                        << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel
+                                                                                        << "; fractions are " << prevFraction << " and " << curFraction
+                                                                                        << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << tcu::TestLog::EndMessage;
+                               return false;
+                       }
+               }
+       }
+
+       return true;
+}
+
+std::vector<float> genTessLevelCases (void)
+{
+       std::vector<float> result;
+
+       // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0)
+       {
+               static const float      rangeStarts[]           = { 7.0f, 8.0f, 9.0f };
+               const int                       numSamplesPerRange      = 10;
+
+               for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); ++rangeNdx)
+                       for (int i = 0; i < numSamplesPerRange; ++i)
+                               result.push_back(rangeStarts[rangeNdx] + static_cast<float>(i)/numSamplesPerRange);
+       }
+
+       // 0.3, 1.3, 2.3,  ... , 62.3
+       for (int i = 0; i <= 62; ++i)
+               result.push_back(static_cast<float>(i) + 0.3f);
+
+       return result;
+}
+
+//! Create a vector of floats from an array of floats. Offset is in bytes.
+std::vector<float> readFloatArray(const int count, const void* memory, const int offset)
+{
+       std::vector<float> results(count);
+
+       const float* pFloatData = reinterpret_cast<const float*>(static_cast<const deUint8*>(memory) + offset);
+       deMemcpy(&results[0], pFloatData, sizeof(float) * count);
+
+       return results;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const SpacingMode spacingMode)
+{
+       // Vertex shader: no inputs
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 1) out;\n"
+                       << "\n"
+                       << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
+                       << "    float outer1;\n"
+                       << "} sb_levels;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    gl_TessLevelOuter[0] = 1.0;\n"
+                       << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_ISOLINES) << ", "
+                                                << getSpacingModeShaderName(spacingMode) << ", point_mode) in;\n"
+                       << "\n"
+                       << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
+                       << "    int   numInvocations;\n"
+                       << "    float tessCoord[];\n"
+                       << "} sb_out;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
+                       << "    sb_out.tessCoord[index] = gl_TessCoord.x;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+}
+
+tcu::TestStatus test (Context& context, const SpacingMode spacingMode)
+{
+       DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
+
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       const DeviceInterface&  vk                                      = context.getDeviceInterface();
+       const VkDevice                  device                          = context.getDevice();
+       const VkQueue                   queue                           = context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = context.getDefaultAllocator();
+
+       const std::vector<float>        tessLevelCases = genTessLevelCases();
+       const int                                       maxNumVertices = 1 + getClampedRoundedTessLevel(spacingMode, *std::max_element(tessLevelCases.begin(), tessLevelCases.end()));
+
+       // Result buffer: generated tess coords go here.
+
+       const VkDeviceSize resultBufferSizeBytes = sizeof(int) + sizeof(float) * maxNumVertices;
+       const Buffer       resultBuffer                  (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Outer1 tessellation level constant buffer.
+
+       const VkDeviceSize tessLevelsBufferSizeBytes = sizeof(float);  // we pass only outer1
+       const Buffer       tessLevelsBuffer                      (vk, device, allocator, makeBufferCreateInfo(tessLevelsBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet                     (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+       const VkDescriptorBufferInfo  tessLevelsBufferInfo      = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, tessLevelsBufferSizeBytes);
+       const VkDescriptorBufferInfo  resultBufferInfo          = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(vk, device);
+
+       // Pipeline
+
+       const Unique<VkRenderPass>              renderPass        (makeRenderPassWithoutAttachments     (vk, device));
+       const Unique<VkFramebuffer>             framebuffer       (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+       const Unique<VkPipelineLayout>  pipelineLayout(makePipelineLayout                               (vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>             cmdPool           (makeCommandPool                                      (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>   cmdBuffer         (makeCommandBuffer                            (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
+               .build(vk, device, *pipelineLayout, *renderPass));
+
+       // Data that will be verified across all cases
+       std::vector<float> additionalSegmentLengths;
+       std::vector<int>   additionalSegmentLocations;
+
+       bool success = false;
+
+       // Repeat the test for all tessellation coords cases
+       for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
+       {
+               // Upload tessellation levels data to the input buffer
+               {
+                       const Allocation& alloc                   = tessLevelsBuffer.getAllocation();
+                       float* const      tessLevelOuter1 = static_cast<float*>(alloc.getHostPtr());
+
+                       *tessLevelOuter1 = tessLevelCases[tessLevelCaseNdx];
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), tessLevelsBufferSizeBytes);
+               }
+
+               // Clear the results buffer
+               {
+                       const Allocation& alloc = resultBuffer.getAllocation();
+                       deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+               }
+
+               beginCommandBuffer(vk, *cmdBuffer);
+
+               // Begin render pass
+               beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
+
+               vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+               vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+
+               vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
+               endRenderPass(vk, *cmdBuffer);
+
+               {
+                       const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+                               VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                               0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+               }
+
+               endCommandBuffer(vk, *cmdBuffer);
+               submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+               // Verify the result.
+               {
+                       tcu::TestLog& log = context.getTestContext().getLog();
+
+                       const Allocation& resultAlloc = resultBuffer.getAllocation();
+                       invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+                       const deInt32 numResults = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+                       const std::vector<float> resultTessCoords = readFloatArray(numResults, resultAlloc.getHostPtr(), sizeof(deInt32));
+
+                       // Outputs
+                       float additionalSegmentLength;
+                       int   additionalSegmentLocation;
+
+                       success = verifyFractionalSpacingSingle(log, spacingMode, tessLevelCases[tessLevelCaseNdx], resultTessCoords,
+                                                                                                       &additionalSegmentLength, &additionalSegmentLocation);
+
+                       if (!success)
+                               break;
+
+                       additionalSegmentLengths.push_back(additionalSegmentLength);
+                       additionalSegmentLocations.push_back(additionalSegmentLocation);
+               }
+       } // for tessLevelCaseNdx
+
+       if (success)
+               success = verifyFractionalSpacingMultiple(context.getTestContext().getLog(), spacingMode, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations);
+
+       return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.fractional_spacing.*
+//! Check validity of fractional spacing modes. Draws a single isoline, reads tess coords with SSBO.
+tcu::TestCaseGroup* createFractionalSpacingTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "fractional_spacing", "Test fractional spacing modes"));
+
+       addFunctionCaseWithPrograms(group.get(), "odd",  "", initPrograms, test, SPACINGMODE_FRACTIONAL_ODD);
+       addFunctionCaseWithPrograms(group.get(), "even", "", initPrograms, test, SPACINGMODE_FRACTIONAL_EVEN);
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationFractionalSpacingTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationFractionalSpacingTests.hpp
new file mode 100644 (file)
index 0000000..64ca5ab
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONFRACTIONALSPACINGTESTS_HPP
+#define _VKTTESSELLATIONFRACTIONALSPACINGTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Fractional Spacing Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createFractionalSpacingTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONFRACTIONALSPACINGTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationInvarianceTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationInvarianceTests.cpp
new file mode 100644 (file)
index 0000000..3c84792
--- /dev/null
@@ -0,0 +1,2204 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Invariance Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationInvarianceTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+#include "deRandom.hpp"
+
+#include <string>
+#include <vector>
+#include <set>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+       NUM_TESS_LEVELS = 6,  // two inner and four outer levels
+};
+
+enum WindingUsage
+{
+       WINDING_USAGE_CCW = 0,
+       WINDING_USAGE_CW,
+       WINDING_USAGE_VARY,
+
+       WINDING_USAGE_LAST,
+};
+
+inline WindingUsage getWindingUsage (const Winding winding)
+{
+       const WindingUsage usage = winding == WINDING_CCW ? WINDING_USAGE_CCW :
+                                                          winding == WINDING_CW  ? WINDING_USAGE_CW  : WINDING_USAGE_LAST;
+       DE_ASSERT(usage !=  WINDING_USAGE_LAST);
+       return usage;
+}
+
+std::vector<Winding> getWindingCases (const WindingUsage windingUsage)
+{
+       std::vector<Winding> cases;
+       switch (windingUsage)
+       {
+               case WINDING_USAGE_CCW:
+                       cases.push_back(WINDING_CCW);
+                       break;
+               case WINDING_USAGE_CW:
+                       cases.push_back(WINDING_CW);
+                       break;
+               case WINDING_USAGE_VARY:
+                       cases.push_back(WINDING_CCW);
+                       cases.push_back(WINDING_CW);
+                       break;
+               default:
+                       DE_ASSERT(false);
+                       break;
+       }
+       return cases;
+}
+
+enum PointModeUsage
+{
+       POINT_MODE_USAGE_DONT_USE = 0,
+       POINT_MODE_USAGE_USE,
+       POINT_MODE_USAGE_VARY,
+
+       POINT_MODE_USAGE_LAST,
+};
+
+inline PointModeUsage getPointModeUsage (const bool usePointMode)
+{
+       return usePointMode ? POINT_MODE_USAGE_USE : POINT_MODE_USAGE_DONT_USE;
+}
+
+std::vector<bool> getUsePointModeCases (const PointModeUsage pointModeUsage)
+{
+       std::vector<bool> cases;
+       switch (pointModeUsage)
+       {
+               case POINT_MODE_USAGE_DONT_USE:
+                       cases.push_back(false);
+                       break;
+               case POINT_MODE_USAGE_USE:
+                       cases.push_back(true);
+                       break;
+               case POINT_MODE_USAGE_VARY:
+                       cases.push_back(false);
+                       cases.push_back(true);
+                       break;
+               default:
+                       DE_ASSERT(false);
+                       break;
+       }
+       return cases;
+}
+
+//! Data captured in the shader per output primitive (in geometry stage).
+struct PerPrimitive
+{
+       deInt32         patchPrimitiveID;       //!< gl_PrimitiveID in tessellation evaluation shader
+       deInt32         primitiveID;            //!< ID of an output primitive in geometry shader (user-defined)
+
+       deInt32         unused_padding[2];
+
+       tcu::Vec4       tessCoord[3];           //!< 3 coords for triangles/quads, 2 for isolines, 1 for point mode. Vec4 due to alignment.
+};
+
+typedef std::vector<PerPrimitive> PerPrimitiveVec;
+
+inline bool byPatchPrimitiveID (const PerPrimitive& a, const PerPrimitive& b)
+{
+       return a.patchPrimitiveID < b.patchPrimitiveID;
+}
+
+inline std::string getProgramName (const std::string& baseName, const Winding winding, const bool usePointMode)
+{
+       std::ostringstream str;
+       str << baseName << "_" << getWindingShaderName(winding) << (usePointMode ? "_point_mode" : "");
+       return str.str();
+}
+
+inline std::string getProgramName (const std::string& baseName, const bool usePointMode)
+{
+       std::ostringstream str;
+       str << baseName << (usePointMode ? "_point_mode" : "");
+       return str.str();
+}
+
+inline std::string getProgramDescription (const Winding winding, const bool usePointMode)
+{
+       std::ostringstream str;
+       str << "winding mode " << getWindingShaderName(winding) << ", " << (usePointMode ? "" : "don't ") << "use point mode";
+       return str.str();
+};
+
+template <typename T, int N>
+std::vector<T> arrayToVector (const T (&arr)[N])
+{
+       return std::vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
+}
+
+template <typename T, int N>
+T arrayMax (const T (&arr)[N])
+{
+       return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
+}
+
+template <int Size>
+inline tcu::Vector<bool, Size> singleTrueMask (int index)
+{
+       DE_ASSERT(de::inBounds(index, 0, Size));
+       tcu::Vector<bool, Size> result;
+       result[index] = true;
+       return result;
+}
+
+template <typename ContainerT, typename T>
+inline bool contains (const ContainerT& c, const T& key)
+{
+       return c.find(key) != c.end();
+}
+
+template <typename SeqT, int Size, typename Pred>
+class LexCompare
+{
+public:
+       LexCompare (void) : m_pred(Pred()) {}
+
+       bool operator() (const SeqT& a, const SeqT& b) const
+       {
+               for (int i = 0; i < Size; ++i)
+               {
+                       if (m_pred(a[i], b[i]))
+                               return true;
+                       if (m_pred(b[i], a[i]))
+                               return false;
+               }
+               return false;
+       }
+
+private:
+       Pred m_pred;
+};
+
+template <int Size>
+class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> >
+{
+};
+
+//! Add default programs for invariance tests.
+//! Creates multiple shader programs for combinations of winding and point mode.
+//! mirrorCoords - special mode where some tessellation coordinates are mirrored in tessellation evaluation shader.
+//!                This is used by symmetric outer edge test.
+void addDefaultPrograms (vk::SourceCollections&  programCollection,
+                                                const TessPrimitiveType primitiveType,
+                                                const SpacingMode       spacingMode,
+                                                const WindingUsage      windingUsage,
+                                                const PointModeUsage    pointModeUsage,
+                                                const bool                              mirrorCoords = false)
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 1) out;\n"
+                       << "\n"
+                       << "layout(location = 0) in highp float in_tc_attr[];\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
+                       << "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+                       << "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+                       << "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+                       << "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       const std::string perVertexInterfaceBlock = \
+               "VertexData {\n"                                        // no in/out qualifier
+               "    vec4 in_gs_tessCoord;\n"           // w component is used by mirroring test
+               "    int  in_gs_primitiveID;\n"
+               "}";                                                            // no newline nor semicolon
+
+       // Alternative tess coordinates handling code
+       std::ostringstream tessEvalCoordSrc;
+       if (mirrorCoords)
+               switch (primitiveType)
+               {
+                       case TESSPRIMITIVETYPE_TRIANGLES:
+                               tessEvalCoordSrc << "    float x = gl_TessCoord.x;\n"
+                                                                << "    float y = gl_TessCoord.y;\n"
+                                                                << "    float z = gl_TessCoord.z;\n"
+                                                                << "\n"
+                                                                << "    // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
+                                                                << "    ib_out.in_gs_tessCoord   = z == 0.0 && x > 0.5 && x != 1.0 ? vec4(1.0-x,  1.0-y,    0.0, 1.0)\n"
+                                                                << "                             : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x,    0.0,  1.0-z, 1.0)\n"
+                                                                << "                             : x == 0.0 && y > 0.5 && y != 1.0 ? vec4(  0.0,  1.0-y,  1.0-z, 1.0)\n"
+                                                                << "                             : vec4(x, y, z, 0.0);\n";
+                               break;
+                       case TESSPRIMITIVETYPE_QUADS:
+                               tessEvalCoordSrc << "    float x = gl_TessCoord.x;\n"
+                                                                << "    float y = gl_TessCoord.y;\n"
+                                                                << "\n"
+                                                                << "    // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
+                                                                << "    ib_out.in_gs_tessCoord   = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4(    x, 1.0-y, 0.0, 1.0)\n"
+                                                                << "                             : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x,     y, 0.0, 1.0)\n"
+                                                                << "                             : vec4(x, y, 0.0, 0.0);\n";
+                               break;
+                       case TESSPRIMITIVETYPE_ISOLINES:
+                               tessEvalCoordSrc << "    float x = gl_TessCoord.x;\n"
+                                                                << "    float y = gl_TessCoord.y;\n"
+                                                                << "\n"
+                                                                << "    // Mirror one half of each outer edge onto the other half\n"
+                                                                << "    ib_out.in_gs_tessCoord   = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n"
+                                                                << "                             : vec4(x, y, 0.0, 0.0);\n";
+                               break;
+                       default:
+                               DE_ASSERT(false);
+                               return;
+               }
+       else
+               tessEvalCoordSrc << "    ib_out.in_gs_tessCoord   = vec4(gl_TessCoord, 0.0);\n";
+
+       const std::vector<Winding> windingCases      = getWindingCases(windingUsage);
+       const std::vector<bool>    usePointModeCases = getUsePointModeCases(pointModeUsage);
+
+       for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter)
+       for (std::vector<bool>::const_iterator usePointModeIter = usePointModeCases.begin(); usePointModeIter != usePointModeCases.end(); ++usePointModeIter)
+       {
+               // Tessellation evaluation shader
+               {
+                       std::ostringstream src;
+                       src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                               << "#extension GL_EXT_tessellation_shader : require\n"
+                               << "\n"
+                               << "layout(" << getTessPrimitiveTypeShaderName(primitiveType) << ", "
+                                                        << getSpacingModeShaderName(spacingMode) << ", "
+                                                        << getWindingShaderName(*windingIter)
+                                                        << (*usePointModeIter ? ", point_mode" : "") << ") in;\n"
+                               << "\n"
+                               << "layout(location = 0) out " << perVertexInterfaceBlock << " ib_out;\n"
+                               << "\n"
+                               << "void main (void)\n"
+                               << "{\n"
+                               << tessEvalCoordSrc.str()
+                               << "    ib_out.in_gs_primitiveID = gl_PrimitiveID;\n"
+                               << "}\n";
+
+                       programCollection.glslSources.add(getProgramName("tese", *windingIter, *usePointModeIter)) << glu::TessellationEvaluationSource(src.str());
+               }
+       }  // for windingNdx, usePointModeNdx
+
+       // Geometry shader: data is captured here.
+       {
+               for (std::vector<bool>::const_iterator usePointModeIter = usePointModeCases.begin(); usePointModeIter != usePointModeCases.end(); ++usePointModeIter)
+               {
+                       const int numVertices = numVerticesPerPrimitive(primitiveType, *usePointModeIter);  // Primitives that the tessellated patch comprises of.
+
+                       std::ostringstream src;
+                       src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                               << "#extension GL_EXT_geometry_shader : require\n"
+                               << "\n"
+                               << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, *usePointModeIter) << ") in;\n"
+                               << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, *usePointModeIter) << ", max_vertices = " << numVertices << ") out;\n"
+                               << "\n"
+                               << "layout(location = 0) in " << perVertexInterfaceBlock << " ib_in[];\n"
+                               << "\n"
+                               << "struct PerPrimitive {\n"
+                               << "    int  patchPrimitiveID;\n"
+                               << "    int  primitiveID;\n"
+                               << "    vec4 tessCoord[3];\n"
+                               << "};\n"
+                               << "\n"
+                               << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+                               << "    int          numPrimitives;\n"
+                               << "    PerPrimitive primitive[];\n"
+                               << "} sb_out;\n"
+                               << "\n"
+                               << "void main (void)\n"
+                               << "{\n"
+                               << "    int index = atomicAdd(sb_out.numPrimitives, 1);\n"
+                               << "    sb_out.primitive[index].patchPrimitiveID = ib_in[0].in_gs_primitiveID;\n"
+                               << "    sb_out.primitive[index].primitiveID      = index;\n";
+                       for (int i = 0; i < numVertices; ++i)
+                               src << "    sb_out.primitive[index].tessCoord[" << i << "]     = ib_in[" << i << "].in_gs_tessCoord;\n";
+                       for (int i = 0; i < numVertices; ++i)
+                               src << "\n"
+                                       << "    gl_Position = vec4(0.0);\n"
+                                       << "    EmitVertex();\n";
+                       src << "}\n";
+
+                       programCollection.glslSources.add(getProgramName("geom", *usePointModeIter)) << glu::GeometrySource(src.str());
+               }
+       }
+}
+
+//! A description of an outer edge of a triangle, quad or isolines.
+//! An outer edge can be described by the index of a u/v/w coordinate
+//! and the coordinate's value along that edge.
+struct OuterEdgeDescription
+{
+       int             constantCoordinateIndex;
+       float   constantCoordinateValueChoices[2];
+       int             numConstantCoordinateValueChoices;
+
+       OuterEdgeDescription (const int i, const float c0)                                      : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
+       OuterEdgeDescription (const int i, const float c0, const float c1)      : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
+
+       std::string description (void) const
+       {
+               static const char* const        coordinateNames[] = { "u", "v", "w" };
+               std::string                                     result;
+               for (int i = 0; i < numConstantCoordinateValueChoices; ++i)
+                       result += std::string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]);
+               return result;
+       }
+
+       bool contains (const tcu::Vec3& v) const
+       {
+               for (int i = 0; i < numConstantCoordinateValueChoices; ++i)
+                       if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i])
+                               return true;
+               return false;
+       }
+};
+
+std::vector<OuterEdgeDescription> outerEdgeDescriptions (const TessPrimitiveType primType)
+{
+       static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] =
+       {
+               OuterEdgeDescription(0, 0.0f),
+               OuterEdgeDescription(1, 0.0f),
+               OuterEdgeDescription(2, 0.0f)
+       };
+
+       static const OuterEdgeDescription quadOuterEdgeDescriptions[4] =
+       {
+               OuterEdgeDescription(0, 0.0f),
+               OuterEdgeDescription(1, 0.0f),
+               OuterEdgeDescription(0, 1.0f),
+               OuterEdgeDescription(1, 1.0f)
+       };
+
+       static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] =
+       {
+               OuterEdgeDescription(0, 0.0f, 1.0f),
+       };
+
+       switch (primType)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:       return arrayToVector(triangleOuterEdgeDescriptions);
+               case TESSPRIMITIVETYPE_QUADS:           return arrayToVector(quadOuterEdgeDescriptions);
+               case TESSPRIMITIVETYPE_ISOLINES:        return arrayToVector(isolinesOuterEdgeDescriptions);
+
+               default:
+                       DE_ASSERT(false);
+                       return std::vector<OuterEdgeDescription>();
+       }
+}
+
+namespace InvariantOuterEdge
+{
+
+struct CaseDefinition
+{
+       TessPrimitiveType       primitiveType;
+       SpacingMode                     spacingMode;
+       Winding                         winding;
+       bool                            usePointMode;
+};
+
+typedef std::set<tcu::Vec3, VecLexLessThan<3> > Vec3Set;
+
+std::vector<float> generateRandomPatchTessLevels (const int numPatches, const int constantOuterLevelIndex, const float constantOuterLevel, de::Random& rnd)
+{
+       std::vector<float> tessLevels(numPatches*NUM_TESS_LEVELS);
+
+       for (int patchNdx = 0; patchNdx < numPatches; ++patchNdx)
+       {
+               float* const inner = &tessLevels[patchNdx*NUM_TESS_LEVELS + 0];
+               float* const outer = &tessLevels[patchNdx*NUM_TESS_LEVELS + 2];
+
+               for (int j = 0; j < 2; ++j)
+                       inner[j] = rnd.getFloat(1.0f, 62.0f);
+               for (int j = 0; j < 4; ++j)
+                       outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f);
+       }
+
+       return tessLevels;
+}
+
+std::vector<float> generatePatchTessLevels (const int numPatches, const int constantOuterLevelIndex, const float constantOuterLevel)
+{
+       de::Random rnd(123);
+       return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
+}
+
+int multiplePatchReferencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* levels, int numPatches)
+{
+       int result = 0;
+       for (int patchNdx = 0; patchNdx < numPatches; ++patchNdx)
+               result += referencePrimitiveCount(primitiveType, spacingMode, usePointMode, &levels[NUM_TESS_LEVELS*patchNdx + 0], &levels[NUM_TESS_LEVELS*patchNdx + 2]);
+       return result;
+}
+
+template<std::size_t N>
+int computeMaxPrimitiveCount (const int numPatchesToDraw, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float (&singleOuterEdgeLevels)[N])
+{
+       const int                outerEdgeIndex  = 0; // outer-edge index doesn't affect vertex count
+       const std::vector<float> patchTessLevels = generatePatchTessLevels(numPatchesToDraw, outerEdgeIndex, arrayMax(singleOuterEdgeLevels));
+       return multiplePatchReferencePrimitiveCount(primitiveType, spacingMode, usePointMode, &patchTessLevels[0], numPatchesToDraw);
+}
+
+void logOuterTessellationLevel (tcu::TestLog& log, const float tessLevel, const OuterEdgeDescription& edgeDesc)
+{
+       log << tcu::TestLog::Message
+               << "Testing with outer tessellation level " << tessLevel << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs"
+               << tcu::TestLog::EndMessage;
+}
+
+void logPrimitiveCountError (tcu::TestLog& log, const int numPatchesToDraw, int numPrimitives, const int refNumPrimitives, const std::vector<float>& patchTessLevels)
+{
+       log << tcu::TestLog::Message
+               << "Failure: the number of generated primitives is " << numPrimitives << ", expected at least " << refNumPrimitives
+               << tcu::TestLog::EndMessage;
+
+       if (numPatchesToDraw == 1)
+               log << tcu::TestLog::Message
+                       << "Note: rendered one patch; tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
+                       << containerStr(patchTessLevels, NUM_TESS_LEVELS)
+                       << tcu::TestLog::EndMessage;
+       else
+               log << tcu::TestLog::Message
+                       << "Note: rendered " << numPatchesToDraw << " patches in one draw call; "
+                       << "tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
+                       << containerStr(patchTessLevels, NUM_TESS_LEVELS)
+                       << tcu::TestLog::EndMessage;
+}
+
+class BaseTestInstance : public TestInstance
+{
+public:
+       struct DrawResult
+       {
+               bool                    success;
+               int                             refNumPrimitives;
+               int                             numPrimitiveVertices;
+               deInt32                 numPrimitives;
+               PerPrimitiveVec primitives;
+       };
+
+                                                                                       BaseTestInstance                (Context& context, const CaseDefinition caseDef, const int numPatchesToDraw);
+       DrawResult                                                              draw                                    (const deUint32 vertexCount, const std::vector<float>& patchTessLevels, const Winding winding, const bool usePointMode);
+       void                                                                    uploadVertexAttributes  (const std::vector<float>& vertexData);
+
+protected:
+       static const float                                              m_singleOuterEdgeLevels[];
+
+       const CaseDefinition                                    m_caseDef;
+       const int                                                               m_numPatchesToDraw;
+       const VkFormat                                                  m_vertexFormat;
+       const deUint32                                                  m_vertexStride;
+       const std::vector<OuterEdgeDescription> m_edgeDescriptions;
+       const int                                                               m_maxNumPrimitivesInDrawCall;
+       const VkDeviceSize                                              m_vertexDataSizeBytes;
+       const Buffer                                                    m_vertexBuffer;
+       const int                                                               m_resultBufferPrimitiveDataOffset;
+       const VkDeviceSize                                              m_resultBufferSizeBytes;
+       const Buffer                                                    m_resultBuffer;
+       Unique<VkDescriptorSetLayout>                   m_descriptorSetLayout;
+       Unique<VkDescriptorPool>                                m_descriptorPool;
+       Unique<VkDescriptorSet>                                 m_descriptorSet;
+       Unique<VkRenderPass>                                    m_renderPass;
+       Unique<VkFramebuffer>                                   m_framebuffer;
+       Unique<VkPipelineLayout>                                m_pipelineLayout;
+       Unique<VkCommandPool>                                   m_cmdPool;
+       Unique<VkCommandBuffer>                                 m_cmdBuffer;
+};
+
+const float BaseTestInstance::m_singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
+
+BaseTestInstance::BaseTestInstance (Context& context, const CaseDefinition caseDef, const int numPatchesToDraw)
+       : TestInstance                                                  (context)
+       , m_caseDef                                                             (caseDef)
+       , m_numPatchesToDraw                                    (numPatchesToDraw)
+       , m_vertexFormat                                                (VK_FORMAT_R32_SFLOAT)
+       , m_vertexStride                                                (tcu::getPixelSize(mapVkFormat(m_vertexFormat)))
+       , m_edgeDescriptions                                    (outerEdgeDescriptions(m_caseDef.primitiveType))
+       , m_maxNumPrimitivesInDrawCall                  (computeMaxPrimitiveCount(m_numPatchesToDraw, caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode, m_singleOuterEdgeLevels))
+       , m_vertexDataSizeBytes                                 (NUM_TESS_LEVELS * m_numPatchesToDraw * m_vertexStride)
+       , m_vertexBuffer                                                (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(),
+                                                                                       makeBufferCreateInfo(m_vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible)
+       , m_resultBufferPrimitiveDataOffset             (sizeof(deInt32) * 4)
+       , m_resultBufferSizeBytes                               (m_resultBufferPrimitiveDataOffset + m_maxNumPrimitivesInDrawCall * sizeof(PerPrimitive))
+       , m_resultBuffer                                                (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(),
+                                                                                       makeBufferCreateInfo(m_resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible)
+       , m_descriptorSetLayout                                 (DescriptorSetLayoutBuilder()
+                                                                                       .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT)
+                                                                                       .build(m_context.getDeviceInterface(), m_context.getDevice()))
+       , m_descriptorPool                                              (DescriptorPoolBuilder()
+                                                                                       .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+                                                                                       .build(m_context.getDeviceInterface(), m_context.getDevice(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u))
+       , m_descriptorSet                                               (makeDescriptorSet(m_context.getDeviceInterface(), m_context.getDevice(), *m_descriptorPool, *m_descriptorSetLayout))
+       , m_renderPass                                                  (makeRenderPassWithoutAttachments (m_context.getDeviceInterface(), m_context.getDevice()))
+       , m_framebuffer                                                 (makeFramebufferWithoutAttachments(m_context.getDeviceInterface(), m_context.getDevice(), *m_renderPass))
+       , m_pipelineLayout                                              (makePipelineLayout               (m_context.getDeviceInterface(), m_context.getDevice(), *m_descriptorSetLayout))
+       , m_cmdPool                                                             (makeCommandPool                  (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getUniversalQueueFamilyIndex()))
+       , m_cmdBuffer                                                   (makeCommandBuffer                (m_context.getDeviceInterface(), m_context.getDevice(), *m_cmdPool))
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(),
+                                       FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(m_resultBuffer.get(), 0ull, m_resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(m_context.getDeviceInterface(), m_context.getDevice());
+}
+
+//! patchTessLevels are tessellation levels for all drawn patches.
+BaseTestInstance::DrawResult BaseTestInstance::draw (const deUint32 vertexCount, const std::vector<float>& patchTessLevels, const Winding winding, const bool usePointMode)
+{
+       const DeviceInterface&  vk              = m_context.getDeviceInterface();
+       const VkDevice                  device  = m_context.getDevice();
+       const VkQueue                   queue   = m_context.getUniversalQueue();
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setPatchControlPoints        (NUM_TESS_LEVELS)
+               .setVertexInputSingleAttribute(m_vertexFormat, m_vertexStride)
+               .setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(getProgramName("tese", winding, usePointMode)), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,                m_context.getBinaryCollection().get(getProgramName("geom", usePointMode)), DE_NULL)
+               .build                        (vk, device, *m_pipelineLayout, *m_renderPass));
+
+       {
+               const Allocation& alloc = m_resultBuffer.getAllocation();
+               deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(m_resultBufferSizeBytes));
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), m_resultBufferSizeBytes);
+       }
+
+       beginCommandBuffer(vk, *m_cmdBuffer);
+       beginRenderPassWithRasterizationDisabled(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer);
+
+       vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+       vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL);
+       {
+               const VkDeviceSize vertexBufferOffset = 0ull;
+               vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
+       }
+
+       vk.cmdDraw(*m_cmdBuffer, vertexCount, 1u, 0u, 0u);
+       endRenderPass(vk, *m_cmdBuffer);
+
+       {
+               const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+                       VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *m_resultBuffer, 0ull, m_resultBufferSizeBytes);
+
+               vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                       0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+       }
+
+       endCommandBuffer(vk, *m_cmdBuffer);
+       submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
+
+       // Read back and check results
+
+       const Allocation& resultAlloc = m_resultBuffer.getAllocation();
+       invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), m_resultBufferSizeBytes);
+
+       DrawResult result;
+       result.success                          = true;
+       result.refNumPrimitives     = multiplePatchReferencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, usePointMode, &patchTessLevels[0], m_numPatchesToDraw);
+       result.numPrimitiveVertices = numVerticesPerPrimitive(m_caseDef.primitiveType, usePointMode);
+       result.numPrimitives        = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+       result.primitives           = sorted(readInterleavedData<PerPrimitive>(result.numPrimitives, resultAlloc.getHostPtr(), m_resultBufferPrimitiveDataOffset, sizeof(PerPrimitive)),
+                                                                                byPatchPrimitiveID);
+
+       // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+       DE_ASSERT(result.numPrimitives <= m_maxNumPrimitivesInDrawCall);
+
+       tcu::TestLog& log = m_context.getTestContext().getLog();
+       if (result.numPrimitives != result.refNumPrimitives)
+       {
+               logPrimitiveCountError(log, m_numPatchesToDraw, result.numPrimitives, result.refNumPrimitives, patchTessLevels);
+               result.success = false;
+       }
+       return result;
+}
+
+void BaseTestInstance::uploadVertexAttributes (const std::vector<float>& vertexData)
+{
+       const DeviceInterface&  vk              = m_context.getDeviceInterface();
+       const VkDevice                  device  = m_context.getDevice();
+
+       const Allocation& alloc = m_vertexBuffer.getAllocation();
+       deMemcpy(alloc.getHostPtr(), &vertexData[0], sizeInBytes(vertexData));
+       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeInBytes(vertexData));
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #2
+ *
+ * Test that the set of vertices along an outer edge of a quad or triangle
+ * only depends on that edge's tessellation level, and spacing.
+ *
+ * For each (outer) edge in the quad or triangle, draw multiple patches
+ * with identical tessellation levels for that outer edge but with
+ * different values for the other outer edges; compare, among the
+ * primitives, the vertices generated for that outer edge. Repeat with
+ * different programs, using different winding etc. settings. Compare
+ * the edge's vertices between different programs.
+ *//*--------------------------------------------------------------------*/
+class OuterEdgeDivisionTestInstance : public BaseTestInstance
+{
+public:
+                                               OuterEdgeDivisionTestInstance   (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 10) {}
+       tcu::TestStatus         iterate                                                 (void);
+};
+
+tcu::TestStatus OuterEdgeDivisionTestInstance::iterate (void)
+{
+       for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex)
+       for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx)
+       {
+               const OuterEdgeDescription& edgeDesc        = m_edgeDescriptions[outerEdgeIndex];
+               const std::vector<float>    patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
+
+               Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches.
+
+               uploadVertexAttributes(patchTessLevels);
+               logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc);
+
+               for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
+               for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx)
+               {
+                       const Winding winding        = static_cast<Winding>(windingNdx);
+                       const bool        usePointMode   = (usePointModeNdx != 0);
+                       const bool    isFirstProgram = (windingNdx == 0 && usePointModeNdx == 0);
+
+                       const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, winding, usePointMode);
+
+                       if (!result.success)
+                               return tcu::TestStatus::fail("Invalid set of vertices");
+
+                       // Check the vertices of each patch.
+
+                       int primitiveNdx = 0;
+                       for (int patchNdx = 0; patchNdx < m_numPatchesToDraw; ++patchNdx)
+                       {
+                               DE_ASSERT(primitiveNdx < result.numPrimitives);
+
+                               const float* const      innerLevels     = &patchTessLevels[NUM_TESS_LEVELS*patchNdx + 0];
+                               const float* const      outerLevels     = &patchTessLevels[NUM_TESS_LEVELS*patchNdx + 2];
+
+                               Vec3Set outerEdgeVertices;
+
+                               // We're interested in just the vertices on the current outer edge.
+                               for (; primitiveNdx < result.numPrimitives && result.primitives[primitiveNdx].patchPrimitiveID == patchNdx; ++primitiveNdx)
+                               for (int i = 0; i < result.numPrimitiveVertices; ++i)
+                               {
+                                       const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2);
+                                       if (edgeDesc.contains(coord))
+                                               outerEdgeVertices.insert(coord);
+                               }
+
+                               // Compare the vertices to those of the first patch (unless this is the first patch).
+
+                               if (isFirstProgram && patchNdx == 0)
+                                       firstOuterEdgeVertices = outerEdgeVertices;
+                               else if (firstOuterEdgeVertices != outerEdgeVertices)
+                               {
+                                       tcu::TestLog& log = m_context.getTestContext().getLog();
+
+                                       log << tcu::TestLog::Message
+                                               << "Failure: vertices generated for the edge differ between the following cases:\n"
+                                               << "  - case A: " << getProgramDescription((Winding)0, (bool)0) << ", tessellation levels: "
+                                               << getTessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n"
+                                               << "  - case B: " << getProgramDescription(winding, usePointMode) << ", tessellation levels: "
+                                               << getTessellationLevelsString(innerLevels, outerLevels)
+                                               << tcu::TestLog::EndMessage;
+
+                                       log << tcu::TestLog::Message
+                                               << "Note: resulting vertices for the edge for the cases were:\n"
+                                               << "  - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n"
+                                               << "  - case B: " << containerStr(outerEdgeVertices, 5, 14)
+                                               << tcu::TestLog::EndMessage;
+
+                                       return tcu::TestStatus::fail("Invalid set of vertices");
+                               }
+                       }
+                       DE_ASSERT(primitiveNdx == result.numPrimitives);
+               } // for windingNdx, usePointModeNdx
+       } // for outerEdgeIndex, outerEdgeLevelCaseNdx
+
+       return tcu::TestStatus::pass("OK");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #4
+ *
+ * Test that the vertices on an outer edge don't depend on which of the
+ * edges it is, other than with respect to component order.
+ *//*--------------------------------------------------------------------*/
+class OuterEdgeIndexIndependenceTestInstance : public BaseTestInstance
+{
+public:
+                                               OuterEdgeIndexIndependenceTestInstance  (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {}
+       tcu::TestStatus         iterate                                                                 (void);
+};
+
+tcu::TestStatus OuterEdgeIndexIndependenceTestInstance::iterate (void)
+{
+       for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx)
+       {
+               Vec3Set firstEdgeVertices;
+
+               for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex)
+               {
+                       const OuterEdgeDescription& edgeDesc        = m_edgeDescriptions[outerEdgeIndex];
+                       const std::vector<float>    patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
+
+                       uploadVertexAttributes(patchTessLevels);
+                       logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc);
+                       const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, m_caseDef.winding, m_caseDef.usePointMode);
+
+                       // Verify case result
+
+                       if (!result.success)
+                               return tcu::TestStatus::fail("Invalid set of vertices");
+
+                       Vec3Set currentEdgeVertices;
+
+                       // Get the vertices on the current outer edge.
+                       for (int primitiveNdx = 0; primitiveNdx < result.numPrimitives; ++primitiveNdx)
+                       for (int i = 0; i < result.numPrimitiveVertices; ++i)
+                       {
+                               const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2);
+                               if (edgeDesc.contains(coord))
+                               {
+                                       // Swizzle components to match the order of the first edge.
+                                       if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                                               currentEdgeVertices.insert(outerEdgeIndex == 0 ? coord :
+                                                                                                  outerEdgeIndex == 1 ? coord.swizzle(1, 0, 2) :
+                                                                                                  outerEdgeIndex == 2 ? coord.swizzle(2, 1, 0) : tcu::Vec3(-1.0f));
+                                       else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+                                               currentEdgeVertices.insert(tcu::Vec3(outerEdgeIndex == 0 ? coord.y() :
+                                                                                                                        outerEdgeIndex == 1 ? coord.x() :
+                                                                                                                        outerEdgeIndex == 2 ? coord.y() :
+                                                                                                                        outerEdgeIndex == 3 ? coord.x() : -1.0f,
+                                                                                                                        0.0f, 0.0f));
+                                       else
+                                               DE_ASSERT(false);
+                               }
+                       }
+
+                       if (outerEdgeIndex == 0)
+                               firstEdgeVertices = currentEdgeVertices;
+                       else
+                       {
+                               // Compare vertices of this edge to those of the first edge.
+                               if (currentEdgeVertices != firstEdgeVertices)
+                               {
+                                       const char* const swizzleDesc =
+                                               m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)" :
+                                                                                                                                                                 outerEdgeIndex == 2 ? "(z, y, x)" : DE_NULL) :
+                                               m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)" :
+                                                                                                                                                         outerEdgeIndex == 2 ? "(y, 0)" :
+                                                                                                                                                         outerEdgeIndex == 3 ? "(x, 0)" : DE_NULL)
+                                               : DE_NULL;
+
+                                       tcu::TestLog& log = m_context.getTestContext().getLog();
+                                       log << tcu::TestLog::Message
+                                               << "Failure: the set of vertices on the " << edgeDesc.description() << " edge"
+                                               << " doesn't match the set of vertices on the " << m_edgeDescriptions[0].description() << " edge"
+                                               << tcu::TestLog::EndMessage;
+
+                                       log << tcu::TestLog::Message
+                                               << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc
+                                               << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5)
+                                               << "\non " << m_edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5)
+                                               << tcu::TestLog::EndMessage;
+
+                                       return tcu::TestStatus::fail("Invalid set of vertices");
+                               }
+                       }
+               }
+       }
+       return tcu::TestStatus::pass("OK");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #3
+ *
+ * Test that the vertices along an outer edge are placed symmetrically.
+ *
+ * Draw multiple patches with different tessellation levels and different
+ * point_mode, winding etc. Before outputting tesscoords from shader, mirror
+ * the vertices in the TES such that every vertex on an outer edge -
+ * except the possible middle vertex - should be duplicated in the output.
+ * Check that appropriate duplicates exist.
+ *//*--------------------------------------------------------------------*/
+class SymmetricOuterEdgeTestInstance : public BaseTestInstance
+{
+public:
+                                               SymmetricOuterEdgeTestInstance  (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {}
+       tcu::TestStatus         iterate                                                 (void);
+};
+
+tcu::TestStatus SymmetricOuterEdgeTestInstance::iterate (void)
+{
+       for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex)
+       for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx)
+       {
+               const OuterEdgeDescription& edgeDesc        = m_edgeDescriptions[outerEdgeIndex];
+               const std::vector<float>    patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
+
+               uploadVertexAttributes(patchTessLevels);
+               logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc);
+               const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, m_caseDef.winding, m_caseDef.usePointMode);
+
+               // Verify case result
+
+               if (!result.success)
+                       return tcu::TestStatus::fail("Invalid set of vertices");
+
+               Vec3Set nonMirroredEdgeVertices;
+               Vec3Set mirroredEdgeVertices;
+
+               // Get the vertices on the current outer edge.
+               for (int primitiveNdx = 0; primitiveNdx < result.numPrimitives; ++primitiveNdx)
+               for (int i = 0; i < result.numPrimitiveVertices; ++i)
+               {
+                       const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2);
+                       if (edgeDesc.contains(coord))
+                       {
+                               // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point;
+                               // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them.
+                               if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES &&
+                                       coord == tcu::select(tcu::Vec3(0.0f), tcu::Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex)))
+                                       continue;
+                               if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS &&
+                                       coord.swizzle(0,1) == tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(0.5f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex)))
+                                       continue;
+                               if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_ISOLINES &&
+                                       (coord == tcu::Vec3(0.0f, 0.5f, 0.0f) || coord == tcu::Vec3(1.0f, 0.5f, 0.0f) || coord == tcu::Vec3(0.0f, 0.0f, 0.0f) || coord == tcu::Vec3(1.0f, 0.0f, 0.0f)))
+                                       continue;
+
+                               const bool isMirrored = result.primitives[primitiveNdx].tessCoord[i].w() > 0.5f;
+                               if (isMirrored)
+                                       mirroredEdgeVertices.insert(coord);
+                               else
+                                       nonMirroredEdgeVertices.insert(coord);
+                       }
+               }
+
+               if (m_caseDef.primitiveType != TESSPRIMITIVETYPE_ISOLINES)
+               {
+                       // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge.
+
+                       tcu::Vec3 endpointA;
+                       tcu::Vec3 endpointB;
+
+                       if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                       {
+                               endpointA = tcu::select(tcu::Vec3(1.0f), tcu::Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3));
+                               endpointB = tcu::select(tcu::Vec3(1.0f), tcu::Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3));
+                       }
+                       else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+                       {
+                               endpointA.xy() = tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
+                               endpointB.xy() = tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
+                       }
+                       else
+                               DE_ASSERT(false);
+
+                       if (!contains(nonMirroredEdgeVertices, endpointA) ||
+                               !contains(nonMirroredEdgeVertices, endpointB))
+                       {
+                               m_context.getTestContext().getLog()
+                                       << tcu::TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
+                                                                                        << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << tcu::TestLog::EndMessage;
+
+                               return tcu::TestStatus::fail("Invalid set of vertices");
+                       }
+                       nonMirroredEdgeVertices.erase(endpointA);
+                       nonMirroredEdgeVertices.erase(endpointB);
+               }
+
+               if (nonMirroredEdgeVertices != mirroredEdgeVertices)
+               {
+                       m_context.getTestContext().getLog()
+                               << tcu::TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << tcu::TestLog::EndMessage
+                               << tcu::TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
+                                                                                << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << tcu::TestLog::EndMessage;
+
+                       return tcu::TestStatus::fail("Invalid set of vertices");
+               }
+       }
+       return tcu::TestStatus::pass("OK");
+}
+
+class OuterEdgeDivisionTest : public TestCase
+{
+public:
+       OuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+               : TestCase      (testCtx, name, description)
+               , m_caseDef     (caseDef)
+       {
+       }
+
+       void initPrograms (vk::SourceCollections& programCollection) const
+       {
+               addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, WINDING_USAGE_VARY, POINT_MODE_USAGE_VARY);
+       }
+
+       TestInstance* createInstance (Context& context) const
+       {
+               return new OuterEdgeDivisionTestInstance(context, m_caseDef);
+       };
+
+private:
+       const CaseDefinition m_caseDef;
+};
+
+class OuterEdgeIndexIndependenceTest : public TestCase
+{
+public:
+       OuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+               : TestCase      (testCtx, name, description)
+               , m_caseDef     (caseDef)
+       {
+               DE_ASSERT(m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+       }
+
+       void initPrograms (vk::SourceCollections& programCollection) const
+       {
+               addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode));
+       }
+
+       TestInstance* createInstance (Context& context) const
+       {
+               return new OuterEdgeIndexIndependenceTestInstance(context, m_caseDef);
+       };
+
+private:
+       const CaseDefinition m_caseDef;
+};
+
+class SymmetricOuterEdgeTest : public TestCase
+{
+public:
+       SymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+               : TestCase      (testCtx, name, description)
+               , m_caseDef     (caseDef)
+       {
+       }
+
+       void initPrograms (vk::SourceCollections& programCollection) const
+       {
+               const bool mirrorCoords = true;
+               addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode), mirrorCoords);
+       }
+
+       TestInstance* createInstance (Context& context) const
+       {
+               return new SymmetricOuterEdgeTestInstance(context, m_caseDef);
+       };
+
+private:
+       const CaseDefinition m_caseDef;
+};
+
+tcu::TestCase* makeOuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+       const CaseDefinition caseDef = { primitiveType, spacingMode, WINDING_LAST, false };  // winding is ignored by this test
+       return new OuterEdgeDivisionTest(testCtx, name, description, caseDef);
+}
+
+tcu::TestCase* makeOuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+       const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
+       return new OuterEdgeIndexIndependenceTest(testCtx, name, description, caseDef);
+}
+
+tcu::TestCase* makeSymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+       const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
+       return new SymmetricOuterEdgeTest(testCtx, name, description, caseDef);
+}
+
+} // InvariantOuterEdge ns
+
+namespace PrimitiveSetInvariance
+{
+
+enum CaseType
+{
+       CASETYPE_INVARIANT_PRIMITIVE_SET,
+       CASETYPE_INVARIANT_TRIANGLE_SET,
+       CASETYPE_INVARIANT_OUTER_TRIANGLE_SET,
+       CASETYPE_INVARIANT_INNER_TRIANGLE_SET,
+};
+
+struct CaseDefinition
+{
+       CaseType                                caseType;
+       TessPrimitiveType               primitiveType;
+       SpacingMode                             spacingMode;
+       WindingUsage                    windingUsage;
+       bool                                    usePointMode;
+};
+
+struct LevelCase
+{
+       std::vector<TessLevels> levels;
+       int                                             mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed.
+
+       LevelCase (const TessLevels& lev) : levels(std::vector<TessLevels>(1, lev)), mem(0) {}
+       LevelCase (void) : mem(0) {}
+};
+
+typedef tcu::Vector<tcu::Vec3, 3> Triangle;
+
+inline Triangle makeTriangle (const PerPrimitive& primitive)
+{
+       return Triangle(primitive.tessCoord[0].swizzle(0, 1, 2),
+                                       primitive.tessCoord[1].swizzle(0, 1, 2),
+                                       primitive.tessCoord[2].swizzle(0, 1, 2));
+}
+
+//! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too.
+template <typename IsTriangleRelevantT>
+bool compareTriangleSets (const PerPrimitiveVec&               primitivesA,
+                                                 const PerPrimitiveVec&                primitivesB,
+                                                 tcu::TestLog&                                 log,
+                                                 const IsTriangleRelevantT&    isTriangleRelevant,
+                                                 const char*                                   ignoredTriangleDescription = DE_NULL)
+{
+       typedef LexCompare<Triangle, 3, VecLexLessThan<3> >             TriangleLexLessThan;
+       typedef std::set<Triangle, TriangleLexLessThan>                 TriangleSet;
+
+       const int               numTrianglesA = static_cast<int>(primitivesA.size());
+       const int               numTrianglesB = static_cast<int>(primitivesB.size());
+       TriangleSet             trianglesA;
+       TriangleSet             trianglesB;
+
+       for (int aOrB = 0; aOrB < 2; ++aOrB)
+       {
+               const PerPrimitiveVec& primitives       = aOrB == 0 ? primitivesA       : primitivesB;
+               const int                          numTriangles = aOrB == 0 ? numTrianglesA     : numTrianglesB;
+               TriangleSet&               triangles    = aOrB == 0 ? trianglesA        : trianglesB;
+
+               for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
+               {
+                       Triangle triangle = makeTriangle(primitives[triNdx]);
+
+                       if (isTriangleRelevant(triangle.getPtr()))
+                       {
+                               std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>());
+                               triangles.insert(triangle);
+                       }
+               }
+       }
+       {
+               TriangleSet::const_iterator aIt = trianglesA.begin();
+               TriangleSet::const_iterator bIt = trianglesB.begin();
+
+               while (aIt != trianglesA.end() || bIt != trianglesB.end())
+               {
+                       const bool aEnd = aIt == trianglesA.end();
+                       const bool bEnd = bIt == trianglesB.end();
+
+                       if (aEnd || bEnd || *aIt != *bIt)
+                       {
+                               log << tcu::TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order"
+                                       << (ignoredTriangleDescription == DE_NULL ? "" : std::string() + ", and " + ignoredTriangleDescription) << ")" << tcu::TestLog::EndMessage;
+
+                               if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt)))
+                                       log << tcu::TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << tcu::TestLog::EndMessage;
+                               else
+                                       log << tcu::TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << tcu::TestLog::EndMessage;
+
+                               return false;
+                       }
+
+                       ++aIt;
+                       ++bIt;
+               }
+
+               return true;
+       }
+}
+
+template <typename ArgT, bool res>
+struct ConstantUnaryPredicate
+{
+       bool operator() (const ArgT&) const { return res; }
+};
+
+bool compareTriangleSets (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, tcu::TestLog& log)
+{
+       return compareTriangleSets(primitivesA, primitivesB, log, ConstantUnaryPredicate<const tcu::Vec3*, true>());
+}
+
+//! Compare two sets of primitives. Order of primitives in each set is undefined, but within each primitive
+//! vertex order and coordinates are expected to match exactly.
+bool comparePrimitivesExact (const PerPrimitive* const primitivesA, const PerPrimitive* const primitivesB, const int numPrimitivesPerPatch)
+{
+       int ndxB = 0;
+       for (int ndxA = 0; ndxA < numPrimitivesPerPatch; ++ndxA)
+       {
+               const tcu::Vec4 (&coordsA)[3] = primitivesA[ndxA].tessCoord;
+               bool match = false;
+
+               // Actually both sets are usually somewhat sorted, so don't reset ndxB after each match. Instead, continue from the next index.
+               for (int i = 0; i < numPrimitivesPerPatch; ++i)
+               {
+                       const tcu::Vec4 (&coordsB)[3] = primitivesB[ndxB].tessCoord;
+                       ndxB = (ndxB + 1) % numPrimitivesPerPatch;
+
+                       if (coordsA[0] == coordsB[0] && coordsA[1] == coordsB[1] && coordsA[2] == coordsB[2])
+                       {
+                               match = true;
+                               break;
+                       }
+               }
+
+               if (!match)
+                       return false;
+       }
+       return true;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Base class for testing invariance of entire primitive set
+ *
+ * Draws two patches with identical tessellation levels and compares the
+ * results. Repeats the same with other programs that are only different
+ * in irrelevant ways; compares the results between these two programs.
+ * Also potentially compares to results produced by different tessellation
+ * levels (see e.g. invariance rule #6).
+ * Furthermore, repeats the above with multiple different tessellation
+ * value sets.
+ *
+ * The manner of primitive set comparison is defined by subclass. E.g.
+ * case for invariance rule #1 tests that same vertices come out, in same
+ * order; rule #5 only requires that the same triangles are output, but
+ * not necessarily in the same order.
+ *//*--------------------------------------------------------------------*/
+class InvarianceTestCase : public TestCase
+{
+public:
+                                                                       InvarianceTestCase                      (tcu::TestContext& context, const std::string& name, const std::string& description, const CaseDefinition& caseDef)
+                                                                               : TestCase      (context, name, description)
+                                                                               , m_caseDef     (caseDef) {}
+
+       virtual                                                 ~InvarianceTestCase                     (void) {}
+
+       void                                                    initPrograms                            (SourceCollections& programCollection) const;
+       TestInstance*                                   createInstance                          (Context& context) const;
+
+private:
+       const CaseDefinition                    m_caseDef;
+};
+
+void InvarianceTestCase::initPrograms (SourceCollections& programCollection) const
+{
+       addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.windingUsage, getPointModeUsage(m_caseDef.usePointMode));
+}
+
+class InvarianceTestInstance : public TestInstance
+{
+public:
+                                                                       InvarianceTestInstance          (Context& context, const CaseDefinition& caseDef) : TestInstance(context), m_caseDef(caseDef) {}
+       virtual                                                 ~InvarianceTestInstance         (void) {}
+
+       tcu::TestStatus                                 iterate                                         (void);
+
+protected:
+       virtual std::vector<LevelCase>  genTessLevelCases                       (void) const;
+       virtual bool                                    compare                                         (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int levelCaseMem) const = 0;
+
+       const CaseDefinition                    m_caseDef;
+};
+
+std::vector<LevelCase> InvarianceTestInstance::genTessLevelCases (void) const
+{
+       static const TessLevels basicTessLevelCases[] =
+       {
+               { { 1.0f,       1.0f    },      { 1.0f,         1.0f,   1.0f,   1.0f    } },
+               { { 63.0f,      24.0f   },      { 15.0f,        42.0f,  10.0f,  12.0f   } },
+               { { 3.0f,       2.0f    },      { 6.0f,         8.0f,   7.0f,   9.0f    } },
+               { { 4.0f,       6.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
+               { { 2.0f,       2.0f    },      { 6.0f,         8.0f,   7.0f,   9.0f    } },
+               { { 5.0f,       6.0f    },      { 1.0f,         1.0f,   1.0f,   1.0f    } },
+               { { 1.0f,       6.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
+               { { 5.0f,       1.0f    },      { 2.0f,         3.0f,   1.0f,   4.0f    } },
+               { { 5.2f,       1.6f    },      { 2.9f,         3.4f,   1.5f,   4.1f    } }
+       };
+
+       std::vector<LevelCase> result;
+       for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); ++i)
+               result.push_back(LevelCase(basicTessLevelCases[i]));
+
+       {
+               de::Random rnd(123);
+               for (int i = 0; i < 10; ++i)
+               {
+                       TessLevels levels;
+                       for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); ++j)
+                               levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
+                       for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); ++j)
+                               levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
+                       result.push_back(LevelCase(levels));
+               }
+       }
+
+       return result;
+}
+
+tcu::TestStatus InvarianceTestInstance::iterate (void)
+{
+       requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(),
+                                       FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
+       const VkDevice                  device                          = m_context.getDevice();
+       const VkQueue                   queue                           = m_context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = m_context.getDefaultAllocator();
+
+       const std::vector<LevelCase>    tessLevelCases                          = genTessLevelCases();
+       const int                                               numPatchesPerDrawCall           = 2;
+       int                                                             maxNumPrimitivesPerPatch        = 0;  // computed below
+       std::vector<std::vector<int> >  primitiveCounts;
+
+       for (int caseNdx = 0; caseNdx < static_cast<int>(tessLevelCases.size()); ++caseNdx)
+       {
+               primitiveCounts.push_back(std::vector<int>());
+               for (int levelNdx = 0; levelNdx < static_cast<int>(tessLevelCases[caseNdx].levels.size()); ++levelNdx)
+               {
+                       const int primitiveCount = referencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.usePointMode,
+                                                                                                                          &tessLevelCases[caseNdx].levels[levelNdx].inner[0], &tessLevelCases[caseNdx].levels[levelNdx].outer[0]);
+                       primitiveCounts.back().push_back(primitiveCount);
+                       maxNumPrimitivesPerPatch = de::max(maxNumPrimitivesPerPatch, primitiveCount);
+               }
+       }
+
+       // Vertex input attributes buffer: to pass tessellation levels
+
+       const VkFormat     vertexFormat        = VK_FORMAT_R32_SFLOAT;
+       const deUint32     vertexStride        = tcu::getPixelSize(mapVkFormat(vertexFormat));
+       const VkDeviceSize vertexDataSizeBytes = NUM_TESS_LEVELS * numPatchesPerDrawCall * vertexStride;
+       const Buffer       vertexBuffer        (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Output buffer: number of primitives and an array of PerPrimitive structures
+
+       const int                  resultBufferMaxVertices              = numPatchesPerDrawCall * maxNumPrimitivesPerPatch * numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode);
+       const int                  resultBufferTessCoordsOffset = sizeof(deInt32) * 4;
+       const VkDeviceSize resultBufferSizeBytes        = resultBufferTessCoordsOffset + resultBufferMaxVertices * sizeof(PerPrimitive);
+       const Buffer       resultBuffer                 (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+       const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(vk, device);
+
+       const Unique<VkRenderPass>     renderPass    (makeRenderPassWithoutAttachments (vk, device));
+       const Unique<VkFramebuffer>    framebuffer   (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+       const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout               (vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>    cmdPool       (makeCommandPool                  (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>  cmdBuffer     (makeCommandBuffer                (vk, device, *cmdPool));
+
+       for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < static_cast<int>(tessLevelCases.size()); ++tessLevelCaseNdx)
+       {
+               const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx];
+               PerPrimitiveVec  firstPrim;
+
+               {
+                       tcu::TestLog& log = m_context.getTestContext().getLog();
+                       std::ostringstream tessLevelsStr;
+
+                       for (int i = 0; i < static_cast<int>(levelCase.levels.size()); ++i)
+                               tessLevelsStr << (levelCase.levels.size() > 1u ? "\n" : "") << getTessellationLevelsString(levelCase.levels[i], m_caseDef.primitiveType);
+
+                       log << tcu::TestLog::Message << "Tessellation level sets: " << tessLevelsStr.str() << tcu::TestLog::EndMessage;
+               }
+
+               for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < static_cast<int>(levelCase.levels.size()); ++subTessLevelCaseNdx)
+               {
+                       const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx];
+                       {
+                               TessLevels data[2];
+                               data[0] = tessLevels;
+                               data[1] = tessLevels;
+
+                               const Allocation& alloc = vertexBuffer.getAllocation();
+                               deMemcpy(alloc.getHostPtr(), data, sizeof(data));
+                               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(data));
+                       }
+
+                       int programNdx = 0;
+                       const std::vector<Winding> windingCases = getWindingCases(m_caseDef.windingUsage);
+                       for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter)
+                       {
+                               const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+                                       .setPatchControlPoints        (NUM_TESS_LEVELS)
+                                       .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+                                       .setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
+                                       .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
+                                       .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(getProgramName("tese", *windingIter, m_caseDef.usePointMode)), DE_NULL)
+                                       .setShader                    (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,                m_context.getBinaryCollection().get(getProgramName("geom", m_caseDef.usePointMode)), DE_NULL)
+                                       .build                        (vk, device, *pipelineLayout, *renderPass));
+
+                               {
+                                       const Allocation& alloc = resultBuffer.getAllocation();
+                                       deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+                                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+                               }
+
+                               beginCommandBuffer(vk, *cmdBuffer);
+                               beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
+
+                               vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+                               vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+                               {
+                                       const VkDeviceSize vertexBufferOffset = 0ull;
+                                       vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+                               }
+
+                               vk.cmdDraw(*cmdBuffer, numPatchesPerDrawCall * NUM_TESS_LEVELS, 1u, 0u, 0u);
+                               endRenderPass(vk, *cmdBuffer);
+
+                               {
+                                       const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+                                               VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
+
+                                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                                               0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+                               }
+
+                               endCommandBuffer(vk, *cmdBuffer);
+                               submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+                               // Verify case result
+                               {
+                                       const Allocation& resultAlloc = resultBuffer.getAllocation();
+                                       invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+                                       const int                               refNumPrimitives     = numPatchesPerDrawCall * primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx];
+                                       const int                               numPrimitiveVertices = numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode);
+                                       const deInt32                   numPrimitives        = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+                                       const PerPrimitiveVec   primitives           = sorted(readInterleavedData<PerPrimitive>(numPrimitives, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(PerPrimitive)),
+                                                                                                                                                 byPatchPrimitiveID);
+
+                                       // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+                                       DE_ASSERT(numPrimitiveVertices * numPrimitives <= resultBufferMaxVertices);
+
+                                       tcu::TestLog& log = m_context.getTestContext().getLog();
+
+                                       if (numPrimitives != refNumPrimitives)
+                                       {
+                                               log << tcu::TestLog::Message << "Failure: got " << numPrimitives << " primitives, but expected " << refNumPrimitives << tcu::TestLog::EndMessage;
+
+                                               return tcu::TestStatus::fail("Invalid set of primitives");
+                                       }
+
+                                       const int                                       half  = static_cast<int>(primitives.size() / 2);
+                                       const PerPrimitiveVec           prim0 = PerPrimitiveVec(primitives.begin(), primitives.begin() + half);
+                                       const PerPrimitive* const       prim1 = &primitives[half];
+
+                                       if (!comparePrimitivesExact(&prim0[0], prim1, half))
+                                       {
+                                                       log << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two primitives drawn in one draw call" << tcu::TestLog::EndMessage
+                                                               << tcu::TestLog::Message << "Note: tessellation levels for both primitives were: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType) << tcu::TestLog::EndMessage;
+
+                                                       return tcu::TestStatus::fail("Invalid set of primitives");
+                                       }
+
+                                       if (programNdx == 0 && subTessLevelCaseNdx == 0)
+                                               firstPrim = prim0;
+                                       else
+                                       {
+                                               const bool compareOk = compare(firstPrim, prim0, levelCase.mem);
+                                               if (!compareOk)
+                                               {
+                                                       log << tcu::TestLog::Message
+                                                               << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
+                                                               << "  - case A: program 0, tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx].levels[0], m_caseDef.primitiveType) << "\n"
+                                                               << "  - case B: program " << programNdx << ", tessellation levels: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType)
+                                                               << tcu::TestLog::EndMessage;
+
+                                                       return tcu::TestStatus::fail("Invalid set of primitives");
+                                               }
+                                       }
+                               }
+                               ++programNdx;
+                       }
+               }
+       }
+       return tcu::TestStatus::pass("OK");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #1
+ *
+ * Test that the sequence of primitives input to the TES only depends on
+ * the tessellation levels, tessellation mode, spacing mode, winding, and
+ * point mode.
+ *//*--------------------------------------------------------------------*/
+class InvariantPrimitiveSetTestInstance : public InvarianceTestInstance
+{
+public:
+       InvariantPrimitiveSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+       bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
+       {
+               if (!comparePrimitivesExact(&primitivesA[0], &primitivesB[0], static_cast<int>(primitivesA.size())))
+               {
+                       m_context.getTestContext().getLog()
+                               << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two programs" << tcu::TestLog::EndMessage;
+
+                       return false;
+               }
+               return true;
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #5
+ *
+ * Test that the set of triangles input to the TES only depends on the
+ * tessellation levels, tessellation mode and spacing mode. Specifically,
+ * winding doesn't change the set of triangles, though it can change the
+ * order in which they are input to TES, and can (and will) change the
+ * vertex order within a triangle.
+ *//*--------------------------------------------------------------------*/
+class InvariantTriangleSetTestInstance : public InvarianceTestInstance
+{
+public:
+       InvariantTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+       bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
+       {
+               return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog());
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #6
+ *
+ * Test that the set of inner triangles input to the TES only depends on
+ * the inner tessellation levels, tessellation mode and spacing mode.
+ *//*--------------------------------------------------------------------*/
+class InvariantInnerTriangleSetTestInstance : public InvarianceTestInstance
+{
+public:
+       InvariantInnerTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+       std::vector<LevelCase> genTessLevelCases (void) const
+       {
+               const int                                               numSubCases             = 4;
+               const std::vector<LevelCase>    baseResults             = InvarianceTestInstance::genTessLevelCases();
+               std::vector<LevelCase>                  result;
+               de::Random                                              rnd                             (123);
+
+               // Generate variants with different values for irrelevant levels.
+               for (int baseNdx = 0; baseNdx < static_cast<int>(baseResults.size()); ++baseNdx)
+               {
+                       const TessLevels&       base    = baseResults[baseNdx].levels[0];
+                       TessLevels                      levels  = base;
+                       LevelCase                       levelCase;
+
+                       for (int subNdx = 0; subNdx < numSubCases; ++subNdx)
+                       {
+                               levelCase.levels.push_back(levels);
+
+                               for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
+                                       levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
+                               if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                                       levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
+                       }
+
+                       result.push_back(levelCase);
+               }
+
+               return result;
+       }
+
+       struct IsInnerTriangleTriangle
+       {
+               bool operator() (const tcu::Vec3* vertices) const
+               {
+                       for (int v = 0; v < 3; ++v)
+                               for (int c = 0; c < 3; ++c)
+                                       if (vertices[v][c] == 0.0f)
+                                               return false;
+                       return true;
+               }
+       };
+
+       struct IsInnerQuadTriangle
+       {
+               bool operator() (const tcu::Vec3* vertices) const
+               {
+                       for (int v = 0; v < 3; ++v)
+                               for (int c = 0; c < 2; ++c)
+                                       if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
+                                               return false;
+                       return true;
+               }
+       };
+
+       bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
+       {
+               if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                       return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerTriangleTriangle(), "outer triangles");
+               else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+                       return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerQuadTriangle(), "outer triangles");
+               else
+               {
+                       DE_ASSERT(false);
+                       return false;
+               }
+       }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #7
+ *
+ * Test that the set of outer triangles input to the TES only depends on
+ * tessellation mode, spacing mode and the inner and outer tessellation
+ * levels corresponding to the inner and outer edges relevant to that
+ * triangle.
+ *//*--------------------------------------------------------------------*/
+class InvariantOuterTriangleSetTestInstance : public InvarianceTestInstance
+{
+public:
+       InvariantOuterTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+       std::vector<LevelCase> genTessLevelCases (void) const
+       {
+               const int                                               numSubCasesPerEdge      = 4;
+               const int                                               numEdges                        = m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES        ? 3
+                                                                                                                       : m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS            ? 4 : 0;
+               const std::vector<LevelCase>    baseResult                      = InvarianceTestInstance::genTessLevelCases();
+               std::vector<LevelCase>                  result;
+               de::Random                                              rnd                                     (123);
+
+               // Generate variants with different values for irrelevant levels.
+               for (int baseNdx = 0; baseNdx < static_cast<int>(baseResult.size()); ++baseNdx)
+               {
+                       const TessLevels& base = baseResult[baseNdx].levels[0];
+                       if (base.inner[0] == 1.0f || (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
+                               continue;
+
+                       for (int edgeNdx = 0; edgeNdx < numEdges; ++edgeNdx)
+                       {
+                               TessLevels      levels = base;
+                               LevelCase       levelCase;
+                               levelCase.mem = edgeNdx;
+
+                               for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; ++subCaseNdx)
+                               {
+                                       levelCase.levels.push_back(levels);
+
+                                       for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
+                                       {
+                                               if (i != edgeNdx)
+                                                       levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
+                                       }
+
+                                       if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+                                               levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
+                               }
+
+                               result.push_back(levelCase);
+                       }
+               }
+
+               return result;
+       }
+
+       class IsTriangleTriangleOnOuterEdge
+       {
+       public:
+               IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
+               bool operator() (const tcu::Vec3* vertices) const
+               {
+                       bool touchesAppropriateEdge = false;
+                       for (int v = 0; v < 3; ++v)
+                               if (vertices[v][m_edgeNdx] == 0.0f)
+                                       touchesAppropriateEdge = true;
+
+                       if (touchesAppropriateEdge)
+                       {
+                               const tcu::Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
+                               return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
+                                          avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
+                       }
+                       return false;
+               }
+
+       private:
+               const int m_edgeNdx;
+       };
+
+       class IsQuadTriangleOnOuterEdge
+       {
+       public:
+               IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
+
+               bool onEdge (const tcu::Vec3& v) const
+               {
+                       return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
+               }
+
+               static inline bool onAnyEdge (const tcu::Vec3& v)
+               {
+                       return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
+               }
+
+               bool operator() (const tcu::Vec3* vertices) const
+               {
+                       for (int v = 0; v < 3; ++v)
+                       {
+                               const tcu::Vec3& a = vertices[v];
+                               const tcu::Vec3& b = vertices[(v+1)%3];
+                               const tcu::Vec3& c = vertices[(v+2)%3];
+                               if (onEdge(a) && onEdge(b))
+                                       return true;
+                               if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
+                                       return true;
+                       }
+
+                       return false;
+               }
+
+       private:
+               const int m_edgeNdx;
+       };
+
+       bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int outerEdgeNdx) const
+       {
+               if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+               {
+                       return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
+                                                                          IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
+                                                                          ("inner triangles, and outer triangles corresponding to other edge than edge "
+                                                                               + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
+               }
+               else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+               {
+                       return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
+                                                                          IsQuadTriangleOnOuterEdge(outerEdgeNdx),
+                                                                          ("inner triangles, and outer triangles corresponding to other edge than edge "
+                                                                               + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
+               }
+               else
+                       DE_ASSERT(false);
+
+               return true;
+       }
+};
+
+TestInstance* InvarianceTestCase::createInstance (Context& context) const
+{
+       switch (m_caseDef.caseType)
+       {
+               case CASETYPE_INVARIANT_PRIMITIVE_SET:                  return new InvariantPrimitiveSetTestInstance    (context, m_caseDef);
+               case CASETYPE_INVARIANT_TRIANGLE_SET:                   return new InvariantTriangleSetTestInstance     (context, m_caseDef);
+               case CASETYPE_INVARIANT_OUTER_TRIANGLE_SET:             return new InvariantOuterTriangleSetTestInstance(context, m_caseDef);
+               case CASETYPE_INVARIANT_INNER_TRIANGLE_SET:             return new InvariantInnerTriangleSetTestInstance(context, m_caseDef);
+               default:
+                       DE_ASSERT(false);
+                       return DE_NULL;
+       }
+}
+
+TestCase* makeInvariantPrimitiveSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+       const CaseDefinition caseDef = { CASETYPE_INVARIANT_PRIMITIVE_SET, primitiveType, spacingMode, getWindingUsage(winding), usePointMode };
+       return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+TestCase* makeInvariantTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+       DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
+       const CaseDefinition caseDef = { CASETYPE_INVARIANT_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
+       return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+TestCase* makeInvariantInnerTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+       DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
+       const CaseDefinition caseDef = { CASETYPE_INVARIANT_INNER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
+       return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+TestCase* makeInvariantOuterTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+       DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
+       const CaseDefinition caseDef = { CASETYPE_INVARIANT_OUTER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
+       return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+} // PrimitiveSetInvariance ns
+
+namespace TessCoordComponent
+{
+
+enum CaseType
+{
+       CASETYPE_TESS_COORD_RANGE = 0,          //!< Test that all (relevant) components of tess coord are in [0,1].
+       CASETYPE_ONE_MINUS_TESS_COORD,          //!< Test that for every (relevant) component c of a tess coord, 1.0-c is exact.
+
+       CASETYPE_LAST
+};
+
+struct CaseDefinition
+{
+       CaseType                        caseType;
+       TessPrimitiveType       primitiveType;
+       SpacingMode                     spacingMode;
+       Winding                         winding;
+       bool                            usePointMode;
+};
+
+std::vector<TessLevels> genTessLevelCases (const int numCases)
+{
+       de::Random                              rnd(123);
+       std::vector<TessLevels> result;
+
+       for (int i = 0; i < numCases; ++i)
+       {
+               TessLevels levels;
+               levels.inner[0] = rnd.getFloat(1.0f, 63.0f);
+               levels.inner[1] = rnd.getFloat(1.0f, 63.0f);
+               levels.outer[0] = rnd.getFloat(1.0f, 63.0f);
+               levels.outer[1] = rnd.getFloat(1.0f, 63.0f);
+               levels.outer[2] = rnd.getFloat(1.0f, 63.0f);
+               levels.outer[3] = rnd.getFloat(1.0f, 63.0f);
+               result.push_back(levels);
+       }
+
+       return result;
+}
+
+typedef bool (*CompareFunc)(tcu::TestLog& log, const float value);
+
+bool compareTessCoordRange (tcu::TestLog& log, const float value)
+{
+       if (!de::inRange(value, 0.0f, 1.0f))
+       {
+               log << tcu::TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << tcu::TestLog::EndMessage;
+               return false;
+       }
+       return true;
+}
+
+bool compareOneMinusTessCoord (tcu::TestLog& log, const float value)
+{
+       if (value != 1.0f)
+       {
+               log << tcu::TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << tcu::TestLog::EndMessage;
+               return false;
+       }
+       return true;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 1) out;\n"
+                       << "\n"
+                       << "layout(location = 0) in highp float in_tc_attr[];\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
+                       << "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+                       << "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+                       << "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+                       << "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream tessCoordSrc;
+
+               if (caseDef.caseType == CASETYPE_TESS_COORD_RANGE)
+                       tessCoordSrc << "    sb_out.tessCoord[index] = gl_TessCoord;\n";
+               else if (caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD)
+               {
+                       const char* components[]  = { "x" , "y", "z" };
+                       const int   numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
+
+                       for (int i = 0; i < numComponents; ++i)
+                               tessCoordSrc << "    {\n"
+                                                        << "        float oneMinusComp        = 1.0 - gl_TessCoord." << components[i] << ";\n"
+                                                        << "        sb_out.tessCoord[index]." << components[i] << " = gl_TessCoord." << components[i] << " + oneMinusComp;\n"
+                                                        << "    }\n";
+               }
+               else
+               {
+                       DE_ASSERT(false);
+                       return;
+               }
+
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+                                                << getSpacingModeShaderName(caseDef.spacingMode) << ", "
+                                                << getWindingShaderName(caseDef.winding)
+                                                << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
+                       << "\n"
+                       << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+                       << "    int  numInvocations;\n"
+                       << "    vec3 tessCoord[];\n"
+                       << "} sb_out;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
+                       << tessCoordSrc.str()
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+}
+
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       const DeviceInterface&  vk                                      = context.getDeviceInterface();
+       const VkDevice                  device                          = context.getDevice();
+       const VkQueue                   queue                           = context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = context.getDefaultAllocator();
+
+       const int                                               numTessLevelCases       = 32;
+       const std::vector<TessLevels>   tessLevelCases          = genTessLevelCases(numTessLevelCases);
+
+       int maxNumVerticesInDrawCall = 0;
+       for (int i = 0; i < numTessLevelCases; ++i)
+               maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode,
+                                                                                  &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]));
+
+       // We may get more invocations than expected, so add some more space (arbitrary number).
+       maxNumVerticesInDrawCall += 4;
+
+       // Vertex input attributes buffer: to pass tessellation levels
+
+       const VkFormat          vertexFormat        = VK_FORMAT_R32_SFLOAT;
+       const deUint32          vertexStride        = tcu::getPixelSize(mapVkFormat(vertexFormat));
+       const VkDeviceSize      vertexDataSizeBytes = NUM_TESS_LEVELS * vertexStride;
+       const Buffer            vertexBuffer        (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       DE_ASSERT(vertexDataSizeBytes == sizeof(TessLevels));
+
+       // Output buffer: number of invocations and array of tess coords
+
+       const int                  resultBufferTessCoordsOffset = sizeof(deInt32) * 4;
+       const VkDeviceSize resultBufferSizeBytes        = resultBufferTessCoordsOffset + maxNumVerticesInDrawCall * sizeof(tcu::Vec4);
+       const Buffer       resultBuffer                 (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+       const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(vk, device);
+
+       const Unique<VkRenderPass>     renderPass    (makeRenderPassWithoutAttachments (vk, device));
+       const Unique<VkFramebuffer>    framebuffer   (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+       const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout               (vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>    cmdPool       (makeCommandPool                  (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>  cmdBuffer     (makeCommandBuffer                (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setPatchControlPoints        (NUM_TESS_LEVELS)
+               .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+               .setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
+               .build                        (vk, device, *pipelineLayout, *renderPass));
+
+       for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; ++tessLevelCaseNdx)
+       {
+               context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Testing with tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
+                       << tcu::TestLog::EndMessage;
+
+               {
+                       const Allocation& alloc = vertexBuffer.getAllocation();
+                       deMemcpy(alloc.getHostPtr(), &tessLevelCases[tessLevelCaseNdx], sizeof(TessLevels));
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+               }
+               {
+                       const Allocation& alloc = resultBuffer.getAllocation();
+                       deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+               }
+
+               beginCommandBuffer(vk, *cmdBuffer);
+               beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer);
+
+               vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+               vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+               {
+                       const VkDeviceSize vertexBufferOffset = 0ull;
+                       vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+               }
+
+               vk.cmdDraw(*cmdBuffer, NUM_TESS_LEVELS, 1u, 0u, 0u);
+               endRenderPass(vk, *cmdBuffer);
+
+               {
+                       const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+                               VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                               0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+               }
+
+               endCommandBuffer(vk, *cmdBuffer);
+               submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+               // Verify case result
+               {
+                       const Allocation& resultAlloc = resultBuffer.getAllocation();
+                       invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+                       const deInt32                            numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+                       const std::vector<tcu::Vec3> vertices    = readInterleavedData<tcu::Vec3>(numVertices, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
+
+                       // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+                       DE_ASSERT(numVertices <= maxNumVerticesInDrawCall);
+
+                       tcu::TestLog& log           = context.getTestContext().getLog();
+                       const int     numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
+
+                       CompareFunc compare = (caseDef.caseType == CASETYPE_TESS_COORD_RANGE     ? compareTessCoordRange :
+                                                                  caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD ? compareOneMinusTessCoord : DE_NULL);
+
+                       DE_ASSERT(compare != DE_NULL);
+
+                       for (std::vector<tcu::Vec3>::const_iterator vertexIter = vertices.begin(); vertexIter != vertices.end(); ++vertexIter)
+                       for (int i = 0; i < numComponents; ++i)
+                               if (!compare(log, (*vertexIter)[i]))
+                               {
+                                               log << tcu::TestLog::Message << "Note: got a wrong tessellation coordinate "
+                                                       << (numComponents == 3 ? de::toString(*vertexIter) : de::toString(vertexIter->swizzle(0,1))) << tcu::TestLog::EndMessage;
+
+                                               tcu::TestStatus::fail("Invalid tessellation coordinate component");
+                               }
+               }
+       }
+       return tcu::TestStatus::pass("OK");
+}
+
+tcu::TestCase* makeTessCoordRangeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+       const CaseDefinition caseDef = { CASETYPE_TESS_COORD_RANGE, primitiveType, spacingMode, winding, usePointMode };
+       return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
+}
+
+tcu::TestCase* makeOneMinusTessCoordTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+       const CaseDefinition caseDef = { CASETYPE_ONE_MINUS_TESS_COORD, primitiveType, spacingMode, winding, usePointMode };
+       return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
+}
+
+} // TessCoordComponent ns
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.invariance.*
+//! Original OpenGL ES tests used transform feedback to get vertices in primitive order. To emulate this behavior we have to use geometry shader,
+//! which allows us to intercept verticess of final output primitives. This can't be done with tessellation shaders alone as number and order of
+//! invocation is undefined.
+tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "invariance", "Test tessellation invariance rules"));
+
+       de::MovePtr<tcu::TestCaseGroup> invariantPrimitiveSetGroup              (new tcu::TestCaseGroup(testCtx, "primitive_set",                                       "Test invariance rule #1"));
+       de::MovePtr<tcu::TestCaseGroup> invariantOuterEdgeGroup                                 (new tcu::TestCaseGroup(testCtx, "outer_edge_division",                         "Test invariance rule #2"));
+       de::MovePtr<tcu::TestCaseGroup> symmetricOuterEdgeGroup                                 (new tcu::TestCaseGroup(testCtx, "outer_edge_symmetry",                         "Test invariance rule #3"));
+       de::MovePtr<tcu::TestCaseGroup> outerEdgeVertexSetIndexIndependenceGroup(new tcu::TestCaseGroup(testCtx, "outer_edge_index_independence",       "Test invariance rule #4"));
+       de::MovePtr<tcu::TestCaseGroup> invariantTriangleSetGroup                               (new tcu::TestCaseGroup(testCtx, "triangle_set",                                        "Test invariance rule #5"));
+       de::MovePtr<tcu::TestCaseGroup> invariantInnerTriangleSetGroup                  (new tcu::TestCaseGroup(testCtx, "inner_triangle_set",                          "Test invariance rule #6"));
+       de::MovePtr<tcu::TestCaseGroup> invariantOuterTriangleSetGroup                  (new tcu::TestCaseGroup(testCtx, "outer_triangle_set",                          "Test invariance rule #7"));
+       de::MovePtr<tcu::TestCaseGroup> tessCoordComponentRangeGroup                    (new tcu::TestCaseGroup(testCtx, "tess_coord_component_range",          "Test invariance rule #8, first part"));
+       de::MovePtr<tcu::TestCaseGroup> oneMinusTessCoordComponentGroup                 (new tcu::TestCaseGroup(testCtx, "one_minus_tess_coord_component",      "Test invariance rule #8, second part"));
+
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+       {
+               const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
+               const SpacingMode       spacingMode   = static_cast<SpacingMode>(spacingModeNdx);
+               const bool              triOrQuad     = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
+               const std::string       primName      = getTessPrimitiveTypeShaderName(primitiveType);
+               const std::string       primSpacName  = primName + "_" + getSpacingModeShaderName(spacingMode);
+
+               if (triOrQuad)
+               {
+                       invariantOuterEdgeGroup->addChild       (    InvariantOuterEdge::makeOuterEdgeDivisionTest        (testCtx, primSpacName, "", primitiveType, spacingMode));
+                       invariantTriangleSetGroup->addChild     (PrimitiveSetInvariance::makeInvariantTriangleSetTest     (testCtx, primSpacName, "", primitiveType, spacingMode));
+                       invariantInnerTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantInnerTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode));
+                       invariantOuterTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantOuterTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode));
+               }
+
+               for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
+               for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx)
+               {
+                       const Winding     winding               = static_cast<Winding>(windingNdx);
+                       const bool        usePointMode          = (usePointModeNdx != 0);
+                       const std::string primSpacWindPointName = primSpacName + "_" + getWindingShaderName(winding) + (usePointMode ? "_point_mode" : "");
+
+                       invariantPrimitiveSetGroup->addChild     (PrimitiveSetInvariance::makeInvariantPrimitiveSetTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+                       tessCoordComponentRangeGroup->addChild   (    TessCoordComponent::makeTessCoordRangeTest       (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+                       oneMinusTessCoordComponentGroup->addChild(    TessCoordComponent::makeOneMinusTessCoordTest    (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+                       symmetricOuterEdgeGroup->addChild        (    InvariantOuterEdge::makeSymmetricOuterEdgeTest   (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+
+                       if (triOrQuad)
+                               outerEdgeVertexSetIndexIndependenceGroup->addChild(InvariantOuterEdge::makeOuterEdgeIndexIndependenceTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+               }
+       }
+
+       group->addChild(invariantPrimitiveSetGroup.release());
+       group->addChild(invariantOuterEdgeGroup.release());
+       group->addChild(symmetricOuterEdgeGroup.release());
+       group->addChild(outerEdgeVertexSetIndexIndependenceGroup.release());
+       group->addChild(invariantTriangleSetGroup.release());
+       group->addChild(invariantInnerTriangleSetGroup.release());
+       group->addChild(invariantOuterTriangleSetGroup.release());
+       group->addChild(tessCoordComponentRangeGroup.release());
+       group->addChild(oneMinusTessCoordComponentGroup.release());
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationInvarianceTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationInvarianceTests.hpp
new file mode 100644 (file)
index 0000000..702e881
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONINVARIANCETESTS_HPP
+#define _VKTTESSELLATIONINVARIANCETESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Invariance Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONINVARIANCETESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationLimitsTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationLimitsTests.cpp
new file mode 100644 (file)
index 0000000..d0ce3bb
--- /dev/null
@@ -0,0 +1,140 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Limits Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationLimitsTests.hpp"
+#include "vktTestCaseUtil.hpp"
+
+#include "tcuTestLog.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum TessellationLimits
+{
+       LIMIT_MAX_TESSELLATION_GENERATION_LEVEL,
+       LIMIT_MAX_TESSELLATION_PATCH_SIZE,
+    LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_INPUT_COMPONENTS,
+    LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_OUTPUT_COMPONENTS,
+    LIMIT_MAX_TESSELLATION_CONTROL_PER_PATCH_OUTPUT_COMPONENTS,
+    LIMIT_MAX_TESSELLATION_CONTROL_TOTAL_OUTPUT_COMPONENTS,
+    LIMIT_MAX_TESSELLATION_EVALUATION_INPUT_COMPONENTS,
+    LIMIT_MAX_TESSELLATION_EVALUATION_OUTPUT_COMPONENTS,
+};
+
+struct LimitsCaseDefinition
+{
+       TessellationLimits      limitType;
+       deUint32                        minimum;                //!< Implementation must provide at least this value
+};
+
+tcu::TestStatus expectGreaterOrEqual(tcu::TestLog& log, const deUint32 expected, const deUint32 actual)
+{
+       log << tcu::TestLog::Message << "Expected: " << expected << ", got: " << actual << tcu::TestLog::EndMessage;
+
+       if (actual >= expected)
+               return tcu::TestStatus::pass("OK");
+       else
+               return tcu::TestStatus::fail("Value doesn't meet minimal spec requirements");
+}
+
+tcu::TestStatus deviceLimitsTestCase(Context& context, const LimitsCaseDefinition caseDef)
+{
+       const InstanceInterface&                vki                     = context.getInstanceInterface();
+       const VkPhysicalDevice                  physDevice      = context.getPhysicalDevice();
+       const VkPhysicalDeviceFeatures  features        = getPhysicalDeviceFeatures(vki, physDevice);
+
+       if (!features.tessellationShader)
+               throw tcu::NotSupportedError("Tessellation shader not supported");
+
+       const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice);
+       tcu::TestLog&                                    log            = context.getTestContext().getLog();
+
+       switch (caseDef.limitType)
+       {
+               case LIMIT_MAX_TESSELLATION_GENERATION_LEVEL:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationGenerationLevel);
+               case LIMIT_MAX_TESSELLATION_PATCH_SIZE:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationPatchSize);
+               case LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_INPUT_COMPONENTS:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlPerVertexInputComponents);
+               case LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_OUTPUT_COMPONENTS:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlPerVertexOutputComponents);
+               case LIMIT_MAX_TESSELLATION_CONTROL_PER_PATCH_OUTPUT_COMPONENTS:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlPerPatchOutputComponents);
+               case LIMIT_MAX_TESSELLATION_CONTROL_TOTAL_OUTPUT_COMPONENTS:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlTotalOutputComponents);
+               case LIMIT_MAX_TESSELLATION_EVALUATION_INPUT_COMPONENTS:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationEvaluationInputComponents);
+               case LIMIT_MAX_TESSELLATION_EVALUATION_OUTPUT_COMPONENTS:
+                       return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationEvaluationOutputComponents);
+       }
+
+       // Control should never get here.
+       DE_FATAL("Internal test error");
+       return tcu::TestStatus::fail("Test error");
+}
+
+} // anonymous
+
+//! These tests correspond roughly to dEQP-GLES31.functional.tessellation.state_query.*
+tcu::TestCaseGroup* createLimitsTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "limits", "Tessellation limits tests"));
+
+       static const struct
+       {
+               std::string                             caseName;
+               LimitsCaseDefinition    caseDef;
+       } cases[] =
+       {
+               { "max_tessellation_generation_level",                                          { LIMIT_MAX_TESSELLATION_GENERATION_LEVEL,                                              64   } },
+               { "max_tessellation_patch_size",                                                        { LIMIT_MAX_TESSELLATION_PATCH_SIZE,                                                    32   } },
+               { "max_tessellation_control_per_vertex_input_components",       { LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_INPUT_COMPONENTS,   64   } },
+               { "max_tessellation_control_per_vertex_output_components",      { LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_OUTPUT_COMPONENTS,  64   } },
+               { "max_tessellation_control_per_patch_output_components",       { LIMIT_MAX_TESSELLATION_CONTROL_PER_PATCH_OUTPUT_COMPONENTS,   120  } },
+               { "max_tessellation_control_total_output_components",           { LIMIT_MAX_TESSELLATION_CONTROL_TOTAL_OUTPUT_COMPONENTS,               2048 } },
+               { "max_tessellation_evaluation_input_components",                       { LIMIT_MAX_TESSELLATION_EVALUATION_INPUT_COMPONENTS,                   64   } },
+               { "max_tessellation_evaluation_output_components",                      { LIMIT_MAX_TESSELLATION_EVALUATION_OUTPUT_COMPONENTS,                  64   } },
+       };
+
+       for (int i = 0; i < DE_LENGTH_OF_ARRAY(cases); ++i)
+               addFunctionCase<LimitsCaseDefinition>(group.get(), cases[i].caseName, "", deviceLimitsTestCase, cases[i].caseDef);
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationLimitsTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationLimitsTests.hpp
new file mode 100644 (file)
index 0000000..0e69964
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONLIMITSTESTS_HPP
+#define _VKTTESSELLATIONLIMITSTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Limits Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createLimitsTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONLIMITSTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationMiscDrawTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationMiscDrawTests.cpp
new file mode 100644 (file)
index 0000000..a713122
--- /dev/null
@@ -0,0 +1,612 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Miscellaneous Draw Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationMiscDrawTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageIO.hpp"
+#include "tcuTexture.hpp"
+#include "tcuImageCompare.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+struct CaseDefinition
+{
+       TessPrimitiveType       primitiveType;
+       SpacingMode                     spacingMode;
+       std::string                     referenceImagePathPrefix;       //!< without case suffix and extension (e.g. "_1.png")
+};
+
+inline CaseDefinition makeCaseDefinition (const TessPrimitiveType      primitiveType,
+                                                                                 const SpacingMode                     spacingMode,
+                                                                                 const std::string&            referenceImagePathPrefix)
+{
+       CaseDefinition caseDef;
+       caseDef.primitiveType = primitiveType;
+       caseDef.spacingMode = spacingMode;
+       caseDef.referenceImagePathPrefix = referenceImagePathPrefix;
+       return caseDef;
+}
+
+std::vector<TessLevels> genTessLevelCases (const SpacingMode spacingMode)
+{
+       static const TessLevels tessLevelCases[] =
+       {
+               { { 9.0f,       9.0f    },      { 9.0f,         9.0f,   9.0f,   9.0f    } },
+               { { 8.0f,       11.0f   },      { 13.0f,        15.0f,  18.0f,  21.0f   } },
+               { { 17.0f,      14.0f   },      { 3.0f,         6.0f,   9.0f,   12.0f   } },
+       };
+
+       std::vector<TessLevels> resultTessLevels(DE_LENGTH_OF_ARRAY(tessLevelCases));
+
+       for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); ++tessLevelCaseNdx)
+       {
+               TessLevels& tessLevels = resultTessLevels[tessLevelCaseNdx];
+
+               for (int i = 0; i < 2; ++i)
+                       tessLevels.inner[i] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, tessLevelCases[tessLevelCaseNdx].inner[i]));
+
+               for (int i = 0; i < 4; ++i)
+                       tessLevels.outer[i] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, tessLevelCases[tessLevelCaseNdx].outer[i]));
+       }
+
+       return resultTessLevels;
+}
+
+std::vector<tcu::Vec2> genVertexPositions (const TessPrimitiveType primitiveType)
+{
+       std::vector<tcu::Vec2> positions;
+       positions.reserve(4);
+
+       if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+       {
+               positions.push_back(tcu::Vec2( 0.8f,    0.6f));
+               positions.push_back(tcu::Vec2( 0.0f, -0.786f));
+               positions.push_back(tcu::Vec2(-0.8f,    0.6f));
+       }
+       else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+       {
+               positions.push_back(tcu::Vec2(-0.8f, -0.8f));
+               positions.push_back(tcu::Vec2( 0.8f, -0.8f));
+               positions.push_back(tcu::Vec2(-0.8f,  0.8f));
+               positions.push_back(tcu::Vec2( 0.8f,  0.8f));
+       }
+       else
+               DE_ASSERT(false);
+
+       return positions;
+}
+
+//! Common test function used by all test cases.
+tcu::TestStatus runTest (Context& context, const CaseDefinition caseDef)
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+       const DeviceInterface&  vk                                      = context.getDeviceInterface();
+       const VkDevice                  device                          = context.getDevice();
+       const VkQueue                   queue                           = context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = context.getDefaultAllocator();
+
+       const std::vector<TessLevels> tessLevelCases = genTessLevelCases(caseDef.spacingMode);
+       const std::vector<tcu::Vec2>  vertexData         = genVertexPositions(caseDef.primitiveType);
+       const deUint32                            inPatchSize    = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+       // Vertex input: positions
+
+       const VkFormat     vertexFormat            = VK_FORMAT_R32G32_SFLOAT;
+       const deUint32     vertexStride            = tcu::getPixelSize(mapVkFormat(vertexFormat));
+       const VkDeviceSize vertexDataSizeBytes = sizeInBytes(vertexData);
+
+       const Buffer vertexBuffer(vk, device, allocator,
+               makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       DE_ASSERT(inPatchSize == vertexData.size());
+       DE_ASSERT(sizeof(vertexData[0]) == vertexStride);
+
+       {
+               const Allocation& alloc = vertexBuffer.getAllocation();
+               deMemcpy(alloc.getHostPtr(), &vertexData[0], static_cast<std::size_t>(vertexDataSizeBytes));
+
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+               // No barrier needed, flushed memory is automatically visible
+       }
+
+       // Color attachment
+
+       const tcu::IVec2                          renderSize                             = tcu::IVec2(256, 256);
+       const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
+       const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+       const Image                                       colorAttachmentImage           (vk, device, allocator,
+                                                                                                                        makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+                                                                                                                        MemoryRequirement::Any);
+
+       // Color output buffer: image will be copied here for verification
+
+       const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+       const Buffer       colorBuffer                  (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+       // Input buffer: tessellation levels. Data is filled in later.
+
+       const Buffer tessLevelsBuffer(vk, device, allocator,
+               makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+
+       const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
+               .update(vk, device);
+
+       // Pipeline
+
+       const Unique<VkImageView>               colorAttachmentView     (makeImageView          (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+       const Unique<VkRenderPass>              renderPass                      (makeRenderPass         (vk, device, colorFormat));
+       const Unique<VkFramebuffer>             framebuffer                     (makeFramebuffer        (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+       const Unique<VkPipelineLayout>  pipelineLayout          (makePipelineLayout     (vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>             cmdPool                         (makeCommandPool        (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>   cmdBuffer                       (makeCommandBuffer      (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setRenderSize                            (renderSize)
+               .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+               .setPatchControlPoints            (inPatchSize)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                            context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                                            (vk, device, *pipelineLayout, *renderPass));
+
+       // Draw commands
+
+       deUint32 numPassedCases = 0;
+
+       for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
+       {
+               context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
+                       << tcu::TestLog::EndMessage;
+
+               // Upload tessellation levels data to the input buffer
+               {
+                       const Allocation& alloc = tessLevelsBuffer.getAllocation();
+                       TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
+                       *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
+                       flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+               }
+
+               // Reset the command buffer and begin recording.
+               beginCommandBuffer(vk, *cmdBuffer);
+
+               // Change color attachment image layout
+               {
+                       // State is slightly different on the first iteration.
+                       const VkImageLayout currentLayout = (tessLevelCaseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+                       const VkAccessFlags srcFlags      = (tessLevelCaseNdx == 0 ? (VkAccessFlags)0 : VK_ACCESS_TRANSFER_READ_BIT);
+
+                       const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+                               srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                               currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                               *colorAttachmentImage, colorImageSubresourceRange);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+                               0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+               }
+
+               // Begin render pass
+               {
+                       const VkRect2D renderArea = {
+                               makeOffset2D(0, 0),
+                               makeExtent2D(renderSize.x(), renderSize.y()),
+                       };
+                       const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+                       beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+               }
+
+               vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+               vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+               {
+                       const VkDeviceSize vertexBufferOffset = 0ull;
+                       vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+               }
+
+               // Process enough vertices to make a patch.
+               vk.cmdDraw(*cmdBuffer, inPatchSize, 1u, 0u, 0u);
+               endRenderPass(vk, *cmdBuffer);
+
+               // Copy render result to a host-visible buffer
+               {
+                       const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+                               VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                               *colorAttachmentImage, colorImageSubresourceRange);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+                               0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+               }
+               {
+                       const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+                       vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
+               }
+               {
+                       const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+                               VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                               0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+               }
+
+               endCommandBuffer(vk, *cmdBuffer);
+               submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+               {
+                       const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+                       invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+                       // Verify case result
+                       const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+
+                       // Load reference image
+                       const std::string referenceImagePath = caseDef.referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png";
+                       tcu::TextureLevel referenceImage;
+                       tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), referenceImagePath.c_str());
+
+                       if (tcu::fuzzyCompare(context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
+                                                                 referenceImage.getAccess(), resultImageAccess, 0.002f, tcu::COMPARE_LOG_RESULT))
+                               ++numPassedCases;
+               }
+       } // tessLevelCaseNdx
+
+       return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+}
+
+inline const char* getTessLevelsSSBODeclaration (void)
+{
+       return  "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
+                       "    float inner0;\n"
+                       "    float inner1;\n"
+                       "    float outer0;\n"
+                       "    float outer1;\n"
+                       "    float outer2;\n"
+                       "    float outer3;\n"
+                       "} sb_levels;\n";
+}
+
+//! Add vertex, fragment, and tessellation control shaders.
+void initCommonPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       DE_ASSERT(!programCollection.glslSources.contains("vert"));
+       DE_ASSERT(!programCollection.glslSources.contains("tesc"));
+       DE_ASSERT(!programCollection.glslSources.contains("frag"));
+
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp vec2 in_v_position;\n"
+                       << "layout(location = 0) out highp vec2 in_tc_position;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_position = in_v_position;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = " << numVertices << ") out;\n"
+                       << "\n"
+                       << getTessLevelsSSBODeclaration()
+                       << "\n"
+                       << "layout(location = 0) in  highp vec2 in_tc_position[];\n"
+                       << "layout(location = 0) out highp vec2 in_te_position[];\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
+                       << "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
+                       << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
+                       << "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
+                       << "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp   vec4 in_f_color;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = in_f_color;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+void initProgramsFillCoverCase (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+
+       initCommonPrograms(programCollection, caseDef);
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+                                                << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp vec2 in_te_position[];\n"
+                       << "layout(location = 0) out highp vec4 in_f_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       <<      (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
+                                       "    highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
+                                       "    highp vec2 corner0 = in_te_position[0];\n"
+                                       "    highp vec2 corner1 = in_te_position[1];\n"
+                                       "    highp vec2 corner2 = in_te_position[2];\n"
+                                       "    highp vec2 pos =  corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
+                                       "    highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
+                                       "    highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
+                                       "    pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
+                                       "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                               : caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
+                                       "    highp vec2 corner0 = in_te_position[0];\n"
+                                       "    highp vec2 corner1 = in_te_position[1];\n"
+                                       "    highp vec2 corner2 = in_te_position[2];\n"
+                                       "    highp vec2 corner3 = in_te_position[3];\n"
+                                       "    highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
+                                       "                   + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
+                                       "                   + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*corner2\n"
+                                       "                   + (    gl_TessCoord.x)*(    gl_TessCoord.y)*corner3;\n"
+                                       "    highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
+                                       "    highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
+                                       "    highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
+                                       "    pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
+                                       "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                               : "")
+                       << "    in_f_color = vec4(1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+}
+
+void initProgramsFillNonOverlapCase (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+
+       initCommonPrograms(programCollection, caseDef);
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+                                                << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+                       << "\n"
+                       << getTessLevelsSSBODeclaration()
+                       << "\n"
+                       << "layout(location = 0) in  highp vec2 in_te_position[];\n"
+                       << "layout(location = 0) out highp vec4 in_f_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       <<      (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
+                                       "    highp vec2 corner0 = in_te_position[0];\n"
+                                       "    highp vec2 corner1 = in_te_position[1];\n"
+                                       "    highp vec2 corner2 = in_te_position[2];\n"
+                                       "    highp vec2 pos =  corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
+                                       "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                                       "    highp int numConcentricTriangles = int(round(sb_levels.inner0)) / 2 + 1;\n"
+                                       "    highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
+                                       "    highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
+                                       "    in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
+                                       "               : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+                                       "               :              vec4(0.0, 0.0, 1.0, 1.0);\n"
+                               : caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
+                                       "    highp vec2 corner0 = in_te_position[0];\n"
+                                       "    highp vec2 corner1 = in_te_position[1];\n"
+                                       "    highp vec2 corner2 = in_te_position[2];\n"
+                                       "    highp vec2 corner3 = in_te_position[3];\n"
+                                       "    highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
+                                       "                   + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
+                                       "                   + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*corner2\n"
+                                       "                   + (    gl_TessCoord.x)*(    gl_TessCoord.y)*corner3;\n"
+                                       "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                                       "    highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * sb_levels.inner0));\n"
+                                       "    highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * sb_levels.inner1));\n"
+                                       "    highp int phase = min(phaseX, phaseY) % 3;\n"
+                                       "    in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
+                                       "               : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+                                       "               :              vec4(0.0, 0.0, 1.0, 1.0);\n"
+                                       : "")
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+}
+
+void initProgramsIsolinesCase (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_ISOLINES);
+
+       initCommonPrograms(programCollection, caseDef);
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+                                                << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+                       << "\n"
+                       << getTessLevelsSSBODeclaration()
+                       << "\n"
+                       << "layout(location = 0) in  highp vec2 in_te_position[];\n"
+                       << "layout(location = 0) out highp vec4 in_f_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp vec2 corner0 = in_te_position[0];\n"
+                       << "    highp vec2 corner1 = in_te_position[1];\n"
+                       << "    highp vec2 corner2 = in_te_position[2];\n"
+                       << "    highp vec2 corner3 = in_te_position[3];\n"
+                       << "    highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
+                       << "                   + (    gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
+                       << "                   + (1.0-gl_TessCoord.x)*(    gl_TessCoord.y)*corner2\n"
+                       << "                   + (    gl_TessCoord.x)*(    gl_TessCoord.y)*corner3;\n"
+                       << "    pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
+                       << "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                       << "    highp int phaseX = int(round(gl_TessCoord.x*sb_levels.outer1));\n"
+                       << "    highp int phaseY = int(round(gl_TessCoord.y*sb_levels.outer0));\n"
+                       << "    highp int phase = (phaseX + phaseY) % 3;\n"
+                       << "    in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
+                       << "               : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+                       << "               :              vec4(0.0, 0.0, 1.0, 1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+}
+
+inline std::string getReferenceImagePathPrefix (const std::string& caseName)
+{
+       return "vulkan/data/tessellation/" + caseName + "_ref";
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.misc_draw.*
+tcu::TestCaseGroup* createMiscDrawTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "misc_draw", "Miscellaneous draw-result-verifying cases"));
+
+       static const TessPrimitiveType primitivesNoIsolines[] =
+       {
+               TESSPRIMITIVETYPE_TRIANGLES,
+               TESSPRIMITIVETYPE_QUADS,
+       };
+
+       // Triangle fill case
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+       {
+               const TessPrimitiveType primitiveType = primitivesNoIsolines[primitiveTypeNdx];
+               const SpacingMode               spacingMode       = static_cast<SpacingMode>(spacingModeNdx);
+               const std::string               caseName          = std::string() + "fill_cover_" + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName(spacingMode);
+
+               addFunctionCaseWithPrograms(group.get(), caseName, "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape",
+                                                                       initProgramsFillCoverCase, runTest, makeCaseDefinition(primitiveType, spacingMode, getReferenceImagePathPrefix(caseName)));
+       }
+
+       // Triangle non-overlap case
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+       {
+               const TessPrimitiveType primitiveType = primitivesNoIsolines[primitiveTypeNdx];
+               const SpacingMode               spacingMode       = static_cast<SpacingMode>(spacingModeNdx);
+               const std::string               caseName          = std::string() + "fill_overlap_" + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName(spacingMode);
+
+               addFunctionCaseWithPrograms(group.get(), caseName, "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape",
+                                                                       initProgramsFillNonOverlapCase, runTest, makeCaseDefinition(primitiveType, spacingMode, getReferenceImagePathPrefix(caseName)));
+       }
+
+       // Isolines
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+       {
+               const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
+               const std::string caseName    = std::string() + "isolines_" + getSpacingModeShaderName(spacingMode);
+
+               addFunctionCaseWithPrograms(group.get(), caseName, "Basic isolines render test",
+                                                                       initProgramsIsolinesCase, runTest, makeCaseDefinition(TESSPRIMITIVETYPE_ISOLINES, spacingMode, getReferenceImagePathPrefix(caseName)));
+       }
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationMiscDrawTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationMiscDrawTests.hpp
new file mode 100644 (file)
index 0000000..3b335dc
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONMISCDRAWTESTS_HPP
+#define _VKTTESSELLATIONMISCDRAWTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Miscellaneous Draw Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createMiscDrawTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONMISCDRAWTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationPrimitiveDiscardTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationPrimitiveDiscardTests.cpp
new file mode 100644 (file)
index 0000000..c7f0e14
--- /dev/null
@@ -0,0 +1,604 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Primitive Discard Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationPrimitiveDiscardTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+struct CaseDefinition
+{
+       TessPrimitiveType       primitiveType;
+       SpacingMode                     spacingMode;
+       Winding                         winding;
+       bool                            usePointMode;
+};
+
+int intPow (int base, int exp)
+{
+       DE_ASSERT(exp >= 0);
+       if (exp == 0)
+               return 1;
+       else
+       {
+               const int sub = intPow(base, exp/2);
+               if (exp % 2 == 0)
+                       return sub*sub;
+               else
+                       return sub*sub*base;
+       }
+}
+
+std::vector<float> genAttributes (void)
+{
+       // Generate input attributes (tessellation levels, and position scale and
+       // offset) for a number of primitives. Each primitive has a different
+       // combination of tessellatio levels; each level is either a valid
+       // value or an "invalid" value (negative or zero, chosen from
+       // invalidTessLevelChoices).
+
+       // \note The attributes are generated in such an order that all of the
+       //               valid attribute tuples come before the first invalid one both
+       //               in the result vector, and when scanning the resulting 2d grid
+       //               of primitives is scanned in y-major order. This makes
+       //               verification somewhat simpler.
+
+       static const float      baseTessLevels[6]                       = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
+       static const float      invalidTessLevelChoices[]       = { -0.42f, 0.0f };
+       const int                       numChoices                                      = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
+       float                           choices[6][numChoices];
+       std::vector<float>      result;
+
+       for (int levelNdx = 0; levelNdx < 6; levelNdx++)
+               for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
+                       choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
+
+       {
+               const int       numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
+               const int       numRows = numCols;
+               int                     index   = 0;
+               int                     i[6];
+               // We could do this with some generic combination-generation function, but meh, it's not that bad.
+               for (i[2] = 0; i[2] < numChoices; i[2]++) // First  outer
+               for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
+               for (i[4] = 0; i[4] < numChoices; i[4]++) // Third  outer
+               for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
+               for (i[0] = 0; i[0] < numChoices; i[0]++) // First  inner
+               for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
+               {
+                       for (int j = 0; j < 6; j++)
+                               result.push_back(choices[j][i[j]]);
+
+                       {
+                               const int col = index % numCols;
+                               const int row = index / numCols;
+                               // Position scale.
+                               result.push_back((float)2.0f / (float)numCols);
+                               result.push_back((float)2.0f / (float)numRows);
+                               // Position offset.
+                               result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
+                               result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
+                       }
+
+                       index++;
+               }
+       }
+
+       return result;
+}
+
+//! Check that white pixels are found around every non-discarded patch,
+//! and that only black pixels are found after the last non-discarded patch.
+//! Returns true on successful comparison.
+bool verifyResultImage (tcu::TestLog&                                          log,
+                                               const int                                                       numPrimitives,
+                                               const int                                                       numAttribsPerPrimitive,
+                                               const TessPrimitiveType                         primitiveType,
+                                               const std::vector<float>&                       attributes,
+                                               const tcu::ConstPixelBufferAccess       pixels)
+{
+       const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
+       const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
+
+       int lastWhitePixelRow                                                           = 0;
+       int secondToLastWhitePixelRow                                           = 0;
+       int     lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
+
+       for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
+       {
+               const float* const      attr                    = &attributes[numAttribsPerPrimitive*patchNdx];
+               const bool                      validLevels             = !isPatchDiscarded(primitiveType, &attr[2]);
+
+               if (validLevels)
+               {
+                       // Not a discarded patch; check that at least one white pixel is found in its area.
+
+                       const float* const      scale           = &attr[6];
+                       const float* const      offset          = &attr[8];
+                       const int                       x0                      = (int)((                       offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) - 1;
+                       const int                       x1                      = (int)((scale[0] + offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) + 1;
+                       const int                       y0                      = (int)((                       offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) - 1;
+                       const int                       y1                      = (int)((scale[1] + offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) + 1;
+                       bool                            pixelOk         = false;
+
+                       if (y1 > lastWhitePixelRow)
+                       {
+                               secondToLastWhitePixelRow       = lastWhitePixelRow;
+                               lastWhitePixelRow                       = y1;
+                       }
+                       lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
+
+                       for (int y = y0; y <= y1 && !pixelOk; y++)
+                       for (int x = x0; x <= x1 && !pixelOk; x++)
+                       {
+                               if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
+                                       continue;
+
+                               if (pixels.getPixel(x, y) == white)
+                                       pixelOk = true;
+                       }
+
+                       if (!pixelOk)
+                       {
+                               log << tcu::TestLog::Message
+                                       << "Failure: expected at least one white pixel in the rectangle "
+                                       << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]"
+                                       << tcu::TestLog::EndMessage
+                                       << tcu::TestLog::Message
+                                       << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
+                                       << getTessellationLevelsString(&attr[0], &attr[1])
+                                       << tcu::TestLog::EndMessage;
+
+                               return false;
+                       }
+               }
+               else
+               {
+                       // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
+
+                       for (int y = 0; y < pixels.getHeight(); y++)
+                       for (int x = 0; x < pixels.getWidth(); x++)
+                       {
+                               if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
+                               {
+                                       if (pixels.getPixel(x, y) != black)
+                                       {
+                                               log << tcu::TestLog::Message
+                                                       << "Failure: expected all pixels to be black in the area "
+                                                       << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
+                                                               ? std::string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
+                                                                                               + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
+                                                               : std::string() + "y > " + de::toString(lastWhitePixelRow))
+                                                       << " (they all correspond to patches that should be discarded)"
+                                                       << tcu::TestLog::EndMessage
+                                                       << tcu::TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << tcu::TestLog::EndMessage;
+
+                                               return false;
+                                       }
+                               }
+                       }
+                       break;
+               }
+       }
+       return true;
+}
+
+int expectedVertexCount (const int                                     numPrimitives,
+                                                const int                                      numAttribsPerPrimitive,
+                                                const TessPrimitiveType        primitiveType,
+                                                const SpacingMode                      spacingMode,
+                                                const std::vector<float>&      attributes)
+{
+       int count = 0;
+       for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
+               count += referenceVertexCount(primitiveType, spacingMode, true, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
+       return count;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 1) out;\n"
+                       << "\n"
+                       << "layout(location = 0) in highp float in_tc_attr[];\n"
+                       << "\n"
+                       << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
+                       << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_te_positionScale  = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
+                       << "    in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
+                       << "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+                       << "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+                       << "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+                       << "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+                                                << getSpacingModeShaderName(caseDef.spacingMode) << ", "
+                                                << getWindingShaderName(caseDef.winding)
+                                                << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
+                       << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
+                       << "\n"
+                       << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+                       << "    int  numInvocations;\n"
+                       << "} sb_out;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    atomicAdd(sb_out.numInvocations, 1);\n"
+                       << "\n"
+                       << "    gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = vec4(1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test that patch is discarded if relevant outer level <= 0.0
+ *
+ * Draws patches with different combinations of tessellation levels,
+ * varying which levels are negative. Verifies by checking that white
+ * pixels exist inside the area of valid primitives, and only black pixels
+ * exist inside the area of discarded primitives. An additional sanity
+ * test is done, checking that the number of primitives written by shader is
+ * correct.
+ *//*--------------------------------------------------------------------*/
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       const DeviceInterface&  vk                                      = context.getDeviceInterface();
+       const VkDevice                  device                          = context.getDevice();
+       const VkQueue                   queue                           = context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = context.getDefaultAllocator();
+
+       const std::vector<float>        attributes                              = genAttributes();
+       const int                                       numAttribsPerPrimitive  = 6 + 2 + 2; // Tess levels, scale, offset.
+       const int                                       numPrimitives                   = static_cast<int>(attributes.size() / numAttribsPerPrimitive);
+       const int                                       numExpectedVertices             = expectedVertexCount(numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, caseDef.spacingMode, attributes);
+
+       // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
+       {
+               bool discardedPatchEncountered = false;
+               for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
+               {
+                       const bool discard = isPatchDiscarded(caseDef.primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
+                       DE_ASSERT(discard || !discardedPatchEncountered);
+                       discardedPatchEncountered = discard;
+               }
+               DE_UNREF(discardedPatchEncountered);
+       }
+
+       // Vertex input attributes buffer
+
+       const VkFormat     vertexFormat            = VK_FORMAT_R32_SFLOAT;
+       const deUint32     vertexStride            = tcu::getPixelSize(mapVkFormat(vertexFormat));
+       const VkDeviceSize vertexDataSizeBytes = sizeInBytes(attributes);
+       const Buffer       vertexBuffer            (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       DE_ASSERT(static_cast<int>(attributes.size()) == numPrimitives * numAttribsPerPrimitive);
+       DE_ASSERT(sizeof(attributes[0]) == vertexStride);
+
+       {
+               const Allocation& alloc = vertexBuffer.getAllocation();
+               deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+               // No barrier needed, flushed memory is automatically visible
+       }
+
+       // Output buffer: number of invocations
+
+       const VkDeviceSize resultBufferSizeBytes = sizeof(deInt32);
+       const Buffer       resultBuffer                  (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       {
+               const Allocation& alloc = resultBuffer.getAllocation();
+               deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+       }
+
+       // Color attachment
+
+       const tcu::IVec2                          renderSize                             = tcu::IVec2(256, 256);
+       const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
+       const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+       const Image                                       colorAttachmentImage           (vk, device, allocator,
+                                                                                                                        makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+                                                                                                                        MemoryRequirement::Any);
+
+       // Color output buffer: image will be copied here for verification
+
+       const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+       const Buffer colorBuffer(vk, device, allocator,
+               makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+       const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(vk, device);
+
+       // Pipeline
+
+       const Unique<VkImageView>          colorAttachmentView(makeImageView     (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+       const Unique<VkRenderPass>         renderPass             (makeRenderPass        (vk, device, colorFormat));
+       const Unique<VkFramebuffer>        framebuffer            (makeFramebuffer       (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+       const Unique<VkPipelineLayout> pipelineLayout     (makePipelineLayout(vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>        cmdPool                        (makeCommandPool       (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>  cmdBuffer                  (makeCommandBuffer (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setRenderSize                            (renderSize)
+               .setPatchControlPoints            (numAttribsPerPrimitive)
+               .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                            context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                                            (vk, device, *pipelineLayout, *renderPass));
+
+       context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
+               << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels"
+               << tcu::TestLog::EndMessage;
+
+       // Draw commands
+
+       beginCommandBuffer(vk, *cmdBuffer);
+
+       // Change color attachment image layout
+       {
+               const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+                       (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                       VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+       }
+
+       // Begin render pass
+       {
+               const VkRect2D renderArea = {
+                       makeOffset2D(0, 0),
+                       makeExtent2D(renderSize.x(), renderSize.y()),
+               };
+               const tcu::Vec4 clearColor = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
+
+               beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+       }
+
+       vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+       vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+       {
+               const VkDeviceSize vertexBufferOffset = 0ull;
+               vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+       }
+
+       vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(attributes.size()), 1u, 0u, 0u);
+       endRenderPass(vk, *cmdBuffer);
+
+       // Copy render result to a host-visible buffer
+       {
+               const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+       }
+       {
+               const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+               vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
+       }
+       {
+               const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+                       VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                       0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+       }
+       {
+               const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+                       VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                       0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+       }
+
+       endCommandBuffer(vk, *cmdBuffer);
+       submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+       {
+               // Log rendered image
+               const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+               invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+               const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+
+               tcu::TestLog& log = context.getTestContext().getLog();
+               log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
+
+               // Verify case result
+               const Allocation& resultAlloc = resultBuffer.getAllocation();
+               invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+               const deInt32 numResultVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+
+               if (numResultVertices < numExpectedVertices)
+               {
+                       log << tcu::TestLog::Message
+                               << "Failure: expected " << numExpectedVertices << " vertices from shader invocations, but got only " << numResultVertices
+                               << tcu::TestLog::EndMessage;
+                       return tcu::TestStatus::fail("Wrong number of tessellation coordinates");
+               }
+               else if (numResultVertices == numExpectedVertices)
+               {
+                       log << tcu::TestLog::Message
+                               << "Note: shader invocations generated " << numResultVertices << " vertices"
+                               << tcu::TestLog::EndMessage;
+               }
+               else
+               {
+                       log << tcu::TestLog::Message
+                               << "Note: shader invocations generated " << numResultVertices << " vertices (expected " << numExpectedVertices << ", got "
+                               << (numResultVertices - numExpectedVertices) << " extra)"
+                               << tcu::TestLog::EndMessage;
+               }
+
+               return (verifyResultImage(log, numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, attributes, imagePixelAccess)
+                               ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image verification failed"));
+       }
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.primitive_discard.*
+//! \note Original test used transform feedback (TF) to capture the number of output vertices. The behavior of TF differs significantly from SSBO approach,
+//!       especially for non-point_mode rendering. TF returned all coordinates, while SSBO computes the count based on the number of shader invocations
+//!       which yields a much smaller number because invocations for duplicate coordinates are often eliminated.
+//!       Because of this, the test was changed to:
+//!       - always compute the number of expected coordinates as if point_mode was enabled
+//!       - not fail if implementation returned more coordinates than expected
+tcu::TestCaseGroup* createPrimitiveDiscardTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0"));
+
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; primitiveTypeNdx++)
+       for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; spacingModeNdx++)
+       for (int windingNdx = 0; windingNdx < WINDING_LAST; windingNdx++)
+       for (int usePointModeNdx = 0; usePointModeNdx <= 1; usePointModeNdx++)
+       {
+               const CaseDefinition caseDef =
+               {
+                       (TessPrimitiveType)primitiveTypeNdx,
+                       (SpacingMode)spacingModeNdx,
+                       (Winding)windingNdx,
+                       (usePointModeNdx != 0),
+               };
+
+               const std::string caseName = std::string() + getTessPrimitiveTypeShaderName(caseDef.primitiveType)
+                                                                        + "_" + getSpacingModeShaderName(caseDef.spacingMode)
+                                                                        + "_" + getWindingShaderName(caseDef.winding)
+                                                                        + (caseDef.usePointMode ? "_point_mode" : "");
+
+               addFunctionCaseWithPrograms(group.get(), caseName, "", initPrograms, test, caseDef);
+       }
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationPrimitiveDiscardTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationPrimitiveDiscardTests.hpp
new file mode 100644 (file)
index 0000000..1ca9faf
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONPRIMITIVEDISCARDTESTS_HPP
+#define _VKTTESSELLATIONPRIMITIVEDISCARDTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Primitive Discard Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createPrimitiveDiscardTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONPRIMITIVEDISCARDTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationShaderInputOutputTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationShaderInputOutputTests.cpp
new file mode 100644 (file)
index 0000000..50c1cb1
--- /dev/null
@@ -0,0 +1,862 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Shader Input/Output Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationShaderInputOutputTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageIO.hpp"
+#include "tcuTexture.hpp"
+#include "tcuImageCompare.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+       RENDER_SIZE = 256,
+};
+
+//! Generic test code used by all test cases.
+tcu::TestStatus runTest (Context&                                                      context,
+                                                const int                                                      numPrimitives,
+                                                const int                                                      inPatchSize,
+                                                const int                                                      outPatchSize,
+                                                const VkFormat                                         vertexFormat,
+                                                const void*                                            vertexData,
+                                                const VkDeviceSize                                     vertexDataSizeBytes,
+                                                const tcu::ConstPixelBufferAccess&     referenceImageAccess)
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+       const DeviceInterface&  vk                                      = context.getDeviceInterface();
+       const VkDevice                  device                          = context.getDevice();
+       const VkQueue                   queue                           = context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = context.getDefaultAllocator();
+
+       // Vertex input: may be just some abstract numbers
+
+       const Buffer vertexBuffer(vk, device, allocator,
+               makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       {
+               const Allocation& alloc = vertexBuffer.getAllocation();
+               deMemcpy(alloc.getHostPtr(), vertexData, static_cast<std::size_t>(vertexDataSizeBytes));
+
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+               // No barrier needed, flushed memory is automatically visible
+       }
+
+       // Color attachment
+
+       const tcu::IVec2                          renderSize                             = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+       const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
+       const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+       const Image                                       colorAttachmentImage           (vk, device, allocator,
+                                                                                                                        makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+                                                                                                                        MemoryRequirement::Any);
+
+       // Color output buffer: image will be copied here for verification
+
+       const VkDeviceSize      colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+       const Buffer            colorBuffer                      (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+       // Pipeline
+
+       const Unique<VkImageView>               colorAttachmentView(makeImageView                                               (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+       const Unique<VkRenderPass>              renderPass                 (makeRenderPass                                              (vk, device, colorFormat));
+       const Unique<VkFramebuffer>             framebuffer                (makeFramebuffer                                             (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+       const Unique<VkPipelineLayout>  pipelineLayout     (makePipelineLayoutWithoutDescriptors(vk, device));
+       const Unique<VkCommandPool>             cmdPool                    (makeCommandPool                                             (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>   cmdBuffer                  (makeCommandBuffer                                   (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setRenderSize                            (renderSize)
+               .setVertexInputSingleAttribute(vertexFormat, tcu::getPixelSize(mapVkFormat(vertexFormat)))
+               .setPatchControlPoints            (inPatchSize)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                      context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,        context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader                                        (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                            context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                                            (vk, device, *pipelineLayout, *renderPass));
+
+       {
+               tcu::TestLog& log = context.getTestContext().getLog();
+               log << tcu::TestLog::Message
+                       << "Note: input patch size is " << inPatchSize << ", output patch size is " << outPatchSize
+                       << tcu::TestLog::EndMessage;
+       }
+
+       // Draw commands
+
+       beginCommandBuffer(vk, *cmdBuffer);
+
+       // Change color attachment image layout
+       {
+               const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+                       (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                       VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+       }
+
+       // Begin render pass
+       {
+               const VkRect2D renderArea = {
+                       makeOffset2D(0, 0),
+                       makeExtent2D(renderSize.x(), renderSize.y()),
+               };
+               const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+               beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+       }
+
+       vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+       {
+               const VkDeviceSize vertexBufferOffset = 0ull;
+               vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+       }
+
+       // Process enough vertices to make a patch.
+       vk.cmdDraw(*cmdBuffer, numPrimitives * inPatchSize, 1u, 0u, 0u);
+       endRenderPass(vk, *cmdBuffer);
+
+       // Copy render result to a host-visible buffer
+       {
+               const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+       }
+       {
+               const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+               vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
+       }
+       {
+               const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+                       VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                       0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+       }
+
+       endCommandBuffer(vk, *cmdBuffer);
+       submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+       {
+               const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+               invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+               // Verify case result
+               const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+               tcu::TestLog& log = context.getTestContext().getLog();
+               const bool ok = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", referenceImageAccess, resultImageAccess, 0.002f, tcu::COMPARE_LOG_RESULT);
+
+               return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+       }
+}
+
+namespace PatchVertexCount
+{
+
+struct CaseDefinition
+{
+       int                             inPatchSize;
+       int                             outPatchSize;
+       std::string             referenceImagePath;
+};
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = " << caseDef.outPatchSize << ") out;\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_tc_attr[];\n"
+                       << "layout(location = 0) out highp float in_te_attr[];\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" << caseDef.inPatchSize << "/" << caseDef.outPatchSize << "];\n"
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = 5.0;\n"
+                       << "    gl_TessLevelInner[1] = 5.0;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = 5.0;\n"
+                       << "    gl_TessLevelOuter[1] = 5.0;\n"
+                       << "    gl_TessLevelOuter[2] = 5.0;\n"
+                       << "    gl_TessLevelOuter[3] = 5.0;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_QUADS) << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp   float in_te_attr[];\n"
+                       << "layout(location = 0) out mediump vec4  in_f_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
+                       << "    highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" << caseDef.outPatchSize << "-1)))];\n"
+                       << "    gl_Position = vec4(x, y, 0.0, 1.0);\n"
+                       << "    in_f_color = vec4(1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  mediump vec4 in_f_color;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = in_f_color;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+       // Input vertex attribute data
+       std::vector<float> vertexData;
+       vertexData.reserve(caseDef.inPatchSize);
+       for (int i = 0; i < caseDef.inPatchSize; ++i)
+       {
+               const float f = static_cast<float>(i) / (caseDef.inPatchSize - 1);
+               vertexData.push_back(f*f);
+       }
+       const VkDeviceSize vertexBufferSize = sizeof(float) * vertexData.size();
+
+       // Load reference image
+       tcu::TextureLevel referenceImage;
+       tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), caseDef.referenceImagePath.c_str());
+
+       const int numPrimitives = 1;
+
+       return runTest(context, numPrimitives, caseDef.inPatchSize, caseDef.outPatchSize,
+                                  VK_FORMAT_R32_SFLOAT, &vertexData[0], vertexBufferSize, referenceImage.getAccess());
+}
+
+} // PatchVertexCountTest ns
+
+namespace PerPatchData
+{
+
+enum CaseType
+{
+       CASETYPE_PRIMITIVE_ID_TCS,
+       CASETYPE_PRIMITIVE_ID_TES,
+       CASETYPE_PATCH_VERTICES_IN_TCS,
+       CASETYPE_PATCH_VERTICES_IN_TES,
+       CASETYPE_TESS_LEVEL_INNER0_TES,
+       CASETYPE_TESS_LEVEL_INNER1_TES,
+       CASETYPE_TESS_LEVEL_OUTER0_TES,
+       CASETYPE_TESS_LEVEL_OUTER1_TES,
+       CASETYPE_TESS_LEVEL_OUTER2_TES,
+       CASETYPE_TESS_LEVEL_OUTER3_TES,
+};
+
+enum Constants
+{
+       OUTPUT_PATCH_SIZE       = 5,
+       INPUT_PATCH_SIZE        = 10,
+};
+
+struct CaseDefinition
+{
+       CaseType                caseType;
+       std::string             caseName;
+       bool                    usesReferenceImageFromFile;
+       std::string             referenceImagePath;
+       std::string             caseDescription;
+};
+
+int getNumPrimitives (const CaseType type)
+{
+       return (type == CASETYPE_PRIMITIVE_ID_TCS || type == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1);
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = " << OUTPUT_PATCH_SIZE << ") out;\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_tc_attr[];\n"
+                       << "layout(location = 0) out highp float in_te_attr[];\n"
+                       << "\n"
+                       << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS          ? "layout(location = 1) patch out mediump int in_te_primitiveIDFromTCS;\n" :
+                               caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "layout(location = 1) patch out mediump int in_te_patchVerticesInFromTCS;\n" : "")
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
+                       << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS          ? "    in_te_primitiveIDFromTCS = gl_PrimitiveID;\n" :
+                               caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "    in_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n" : "")
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = 9.0;\n"
+                       << "    gl_TessLevelInner[1] = 8.0;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = 7.0;\n"
+                       << "    gl_TessLevelOuter[1] = 6.0;\n"
+                       << "    gl_TessLevelOuter[2] = 5.0;\n"
+                       << "    gl_TessLevelOuter[3] = 4.0;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               const float xScale = 1.0f / getNumPrimitives(caseDef.caseType);
+
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_QUADS) << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp   float in_te_attr[];\n"
+                       << "layout(location = 0) out mediump vec4  in_f_color;\n"
+                       << "\n"
+                       << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS          ? "layout(location = 1) patch in mediump int in_te_primitiveIDFromTCS;\n" :
+                               caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "layout(location = 1) patch in mediump int in_te_patchVerticesInFromTCS;\n" : "")
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp float x = (gl_TessCoord.x*float(" << xScale << ") + in_te_attr[0]) * 2.0 - 1.0;\n"
+                       << "    highp float y = gl_TessCoord.y*2.0 - 1.0;\n"
+                       << "    gl_Position = vec4(x, y, 0.0, 1.0);\n"
+                       << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS               ? "    bool ok = in_te_primitiveIDFromTCS == 3;\n" :
+                               caseDef.caseType == CASETYPE_PRIMITIVE_ID_TES           ? "    bool ok = gl_PrimitiveID == 3;\n" :
+                               caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS      ? "    bool ok = in_te_patchVerticesInFromTCS == " + de::toString(INPUT_PATCH_SIZE) + ";\n" :
+                               caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TES      ? "    bool ok = gl_PatchVerticesIn == " + de::toString(OUTPUT_PATCH_SIZE) + ";\n" :
+                               caseDef.caseType == CASETYPE_TESS_LEVEL_INNER0_TES      ? "    bool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n" :
+                               caseDef.caseType == CASETYPE_TESS_LEVEL_INNER1_TES      ? "    bool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n" :
+                               caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER0_TES      ? "    bool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n" :
+                               caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER1_TES      ? "    bool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n" :
+                               caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER2_TES      ? "    bool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n" :
+                               caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER3_TES      ? "    bool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n" : "")
+                       << "    in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  mediump vec4 in_f_color;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = in_f_color;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+//! Resize an image and fill with white color.
+void initializeWhiteReferenceImage (tcu::TextureLevel& image, const int width, const int height)
+{
+       DE_ASSERT(width > 0 && height > 0);
+
+       image.setStorage(mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM), width, height);
+       tcu::PixelBufferAccess access = image.getAccess();
+
+       const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
+
+       for (int y = 0; y < height; ++y)
+       for (int x = 0; x < width; ++x)
+               access.setPixel(white, x, y);
+}
+
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+       DE_ASSERT(!caseDef.usesReferenceImageFromFile || !caseDef.referenceImagePath.empty());
+
+       // Input vertex attribute data
+       const int                  numPrimitives        = getNumPrimitives(caseDef.caseType);
+       std::vector<float> vertexData           (INPUT_PATCH_SIZE * numPrimitives, 0.0f);
+       const VkDeviceSize vertexBufferSize = sizeof(float) * vertexData.size();
+
+       for (int i = 0; i < numPrimitives; ++i)
+               vertexData[INPUT_PATCH_SIZE * i] = static_cast<float>(i) / numPrimitives;
+
+       tcu::TextureLevel referenceImage;
+       if (caseDef.usesReferenceImageFromFile)
+               tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), caseDef.referenceImagePath.c_str());
+       else
+               initializeWhiteReferenceImage(referenceImage, RENDER_SIZE, RENDER_SIZE);
+
+       return runTest(context, numPrimitives, INPUT_PATCH_SIZE, OUTPUT_PATCH_SIZE,
+                                  VK_FORMAT_R32_SFLOAT, &vertexData[0], vertexBufferSize, referenceImage.getAccess());
+}
+
+} // PerPatchData ns
+
+namespace GLPosition
+{
+
+enum CaseType
+{
+       CASETYPE_VS_TO_TCS = 0,
+       CASETYPE_TCS_TO_TES,
+       CASETYPE_VS_TO_TCS_TO_TES,
+};
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseType caseType)
+{
+       const bool vsToTCS  = caseType == CASETYPE_VS_TO_TCS  || caseType == CASETYPE_VS_TO_TCS_TO_TES;
+       const bool tcsToTES = caseType == CASETYPE_TCS_TO_TES || caseType == CASETYPE_VS_TO_TCS_TO_TES;
+
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp vec4 in_v_attr;\n"
+                       << (!vsToTCS ? "layout(location = 0) out highp vec4 in_tc_attr;\n" : "")
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    " << (vsToTCS ? "gl_Position" : "in_tc_attr") << " = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 3) out;\n"
+                       << "\n"
+                       << (!vsToTCS  ? "layout(location = 0) in  highp vec4 in_tc_attr[];\n" : "")
+                       << (!tcsToTES ? "layout(location = 0) out highp vec4 in_te_attr[];\n" : "")
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    " << (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") << " = "
+                                         << (vsToTCS  ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") << ";\n"
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = 2.0;\n"
+                       << "    gl_TessLevelInner[1] = 3.0;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = 4.0;\n"
+                       << "    gl_TessLevelOuter[1] = 5.0;\n"
+                       << "    gl_TessLevelOuter[2] = 6.0;\n"
+                       << "    gl_TessLevelOuter[3] = 7.0;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               const std::string tesIn0 = tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]";
+               const std::string tesIn1 = tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]";
+               const std::string tesIn2 = tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]";
+
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_TRIANGLES) << ") in;\n"
+                       << "\n"
+                       << (!tcsToTES ? "layout(location = 0) in  highp vec4 in_te_attr[];\n" : "")
+                       << "layout(location = 0) out highp vec4 in_f_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp vec2 xy = gl_TessCoord.x * " << tesIn0 << ".xy\n"
+                       << "                  + gl_TessCoord.y * " << tesIn1 << ".xy\n"
+                       << "                  + gl_TessCoord.z * " << tesIn2 << ".xy;\n"
+                       << "    gl_Position = vec4(xy, 0.0, 1.0);\n"
+                       << "    in_f_color = vec4(" << tesIn0 << ".z + " << tesIn1 << ".w,\n"
+                       << "                      " << tesIn2 << ".z + " << tesIn0 << ".w,\n"
+                       << "                      " << tesIn1 << ".z + " << tesIn2 << ".w,\n"
+                       << "                      1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp   vec4 in_f_color;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = in_f_color;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+tcu::TestStatus test (Context& context, const CaseType caseType)
+{
+       DE_UNREF(caseType);
+
+       // Input vertex attribute data
+       static const float vertexData[3*4] =
+       {
+               -0.8f, -0.7f, 0.1f, 0.7f,
+               -0.5f,  0.4f, 0.2f, 0.5f,
+                0.3f,  0.2f, 0.3f, 0.45f
+       };
+
+       tcu::TextureLevel referenceImage;
+       tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), "vulkan/data/tessellation/gl_position_ref.png");
+
+       const int numPrimitives = 1;
+       const int inPatchSize   = 3;
+       const int outPatchSize  = 3;
+
+       return runTest(context, numPrimitives, inPatchSize, outPatchSize,
+                                  VK_FORMAT_R32G32B32A32_SFLOAT, vertexData, sizeof(vertexData), referenceImage.getAccess());
+}
+
+} // GLPosition ns
+
+namespace Barrier
+{
+
+enum Constants
+{
+       NUM_VERTICES = 32,
+};
+
+void initPrograms (vk::SourceCollections& programCollection)
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = " << NUM_VERTICES << ") out;\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_tc_attr[];\n"
+                       << "layout(location = 0) out highp float in_te_attr[];\n"
+                       << "\n"
+                       << "layout(location = 1) patch out highp float in_te_patchAttr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
+                       << "    in_te_patchAttr = 0.0f;\n"
+                       << "\n"
+                       << "    barrier();\n"
+                       << "\n"
+                       << "    if (gl_InvocationID == 5)\n"
+                       << "            in_te_patchAttr = float(gl_InvocationID)*0.1;\n"
+                       << "\n"
+                       << "    barrier();\n"
+                       << "\n"
+                       << "    highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n"
+                       << "\n"
+                       << "    barrier();\n"
+                       << "\n"
+                       << "    if (gl_InvocationID == " << NUM_VERTICES << "-1)\n"
+                       << "            in_te_patchAttr = float(gl_InvocationID);\n"
+                       << "\n"
+                       << "    barrier();\n"
+                       << "\n"
+                       << "    in_te_attr[gl_InvocationID] = temp;\n"
+                       << "\n"
+                       << "    barrier();\n"
+                       << "\n"
+                       << "    temp = temp + in_te_attr[(gl_InvocationID+1) % " << NUM_VERTICES << "];\n"
+                       << "\n"
+                       << "    barrier();\n"
+                       << "\n"
+                       << "    in_te_attr[gl_InvocationID] = 0.25*temp;\n"
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = 32.0;\n"
+                       << "    gl_TessLevelInner[1] = 32.0;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = 32.0;\n"
+                       << "    gl_TessLevelOuter[1] = 32.0;\n"
+                       << "    gl_TessLevelOuter[2] = 32.0;\n"
+                       << "    gl_TessLevelOuter[3] = 32.0;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_QUADS) << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) in       highp float in_te_attr[];\n"
+                       << "layout(location = 1) patch in highp float in_te_patchAttr;\n"
+                       << "\n"
+                       << "layout(location = 0) out highp float in_f_blue;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
+                       << "    highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" << NUM_VERTICES << "-1)))];\n"
+                       << "    gl_Position = vec4(x, y, 0.0, 1.0);\n"
+                       << "    in_f_blue = abs(in_te_patchAttr - float(" << NUM_VERTICES << "-1));\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp   float in_f_blue;\n"
+                       << "layout(location = 0) out mediump vec4  o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+tcu::TestStatus test (Context& context)
+{
+       // Input vertex attribute data
+       std::vector<float> vertexData           (NUM_VERTICES);
+       const VkDeviceSize vertexBufferSize = sizeof(float) * vertexData.size();
+
+       for (int i = 0; i < NUM_VERTICES; ++i)
+               vertexData[i] = static_cast<float>(i) / (NUM_VERTICES - 1);
+
+       tcu::TextureLevel referenceImage;
+       tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), "vulkan/data/tessellation/barrier_ref.png");
+
+       const int numPrimitives = 1;
+       const int inPatchSize   = NUM_VERTICES;
+       const int outPatchSize  = NUM_VERTICES;
+
+       return runTest(context, numPrimitives, inPatchSize, outPatchSize,
+                                  VK_FORMAT_R32_SFLOAT, &vertexData[0], vertexBufferSize, referenceImage.getAccess());
+}
+
+} // Barrier ns
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.shader_input_output.*
+tcu::TestCaseGroup* createShaderInputOutputTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs"));
+
+       // Patch vertex counts
+       {
+               static const struct
+               {
+                       int inPatchSize;
+                       int outPatchSize;
+               } patchVertexCountCases[] =
+               {
+                       {  5, 10 },
+                       { 10,  5 }
+               };
+
+               for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++)
+               {
+                       const int inSize        = patchVertexCountCases[caseNdx].inPatchSize;
+                       const int outSize       = patchVertexCountCases[caseNdx].outPatchSize;
+
+                       const std::string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out";
+                       const PatchVertexCount::CaseDefinition caseDef =
+                       {
+                               inSize, outSize, "vulkan/data/tessellation/" + caseName + "_ref.png"
+                       };
+
+                       addFunctionCaseWithPrograms(group.get(), caseName, "Test input and output patch vertex counts",
+                                                                               PatchVertexCount::initPrograms, PatchVertexCount::test, caseDef);
+               }
+       }
+
+       // Per patch data
+       {
+               static const PerPatchData::CaseDefinition cases[] =
+               {
+                       { PerPatchData::CASETYPE_PRIMITIVE_ID_TCS,              "primitive_id_tcs",               true, "vulkan/data/tessellation/primitive_id_tcs_ref.png", "Read gl_PrimitiveID in TCS and pass it as patch output to TES" },
+                       { PerPatchData::CASETYPE_PRIMITIVE_ID_TES,              "primitive_id_tes",               true, "vulkan/data/tessellation/primitive_id_tes_ref.png", "Read gl_PrimitiveID in TES" },
+                       { PerPatchData::CASETYPE_PATCH_VERTICES_IN_TCS, "patch_vertices_in_tcs",  false, "", "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES" },
+                       { PerPatchData::CASETYPE_PATCH_VERTICES_IN_TES, "patch_vertices_in_tes",  false, "", "Read gl_PatchVerticesIn in TES" },
+                       { PerPatchData::CASETYPE_TESS_LEVEL_INNER0_TES, "tess_level_inner_0_tes", false, "", "Read gl_TessLevelInner[0] in TES" },
+                       { PerPatchData::CASETYPE_TESS_LEVEL_INNER1_TES, "tess_level_inner_1_tes", false, "", "Read gl_TessLevelInner[1] in TES" },
+                       { PerPatchData::CASETYPE_TESS_LEVEL_OUTER0_TES, "tess_level_outer_0_tes", false, "", "Read gl_TessLevelOuter[0] in TES" },
+                       { PerPatchData::CASETYPE_TESS_LEVEL_OUTER1_TES, "tess_level_outer_1_tes", false, "", "Read gl_TessLevelOuter[1] in TES" },
+                       { PerPatchData::CASETYPE_TESS_LEVEL_OUTER2_TES, "tess_level_outer_2_tes", false, "", "Read gl_TessLevelOuter[2] in TES" },
+                       { PerPatchData::CASETYPE_TESS_LEVEL_OUTER3_TES, "tess_level_outer_3_tes", false, "", "Read gl_TessLevelOuter[3] in TES" },
+               };
+
+               for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
+                       addFunctionCaseWithPrograms(group.get(), cases[caseNdx].caseName, cases[caseNdx].caseDescription,
+                                                                               PerPatchData::initPrograms, PerPatchData::test, cases[caseNdx]);
+       }
+
+       // gl_Position
+       {
+               static const struct
+               {
+                       GLPosition::CaseType    type;
+                       std::string                             caseName;
+               } cases[] =
+               {
+                       { GLPosition::CASETYPE_VS_TO_TCS,                "gl_position_vs_to_tcs"                },
+                       { GLPosition::CASETYPE_TCS_TO_TES,               "gl_position_tcs_to_tes"               },
+                       { GLPosition::CASETYPE_VS_TO_TCS_TO_TES, "gl_position_vs_to_tcs_to_tes" },
+               };
+
+               for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
+                       addFunctionCaseWithPrograms(group.get(), cases[caseNdx].caseName, "Pass gl_Position between VS and TCS, or between TCS and TES",
+                                                                               GLPosition::initPrograms, GLPosition::test, cases[caseNdx].type);
+       }
+
+       // Barrier
+       addFunctionCaseWithPrograms(group.get(), "barrier", "Basic barrier usage", Barrier::initPrograms, Barrier::test);
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationShaderInputOutputTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationShaderInputOutputTests.hpp
new file mode 100644 (file)
index 0000000..947f251
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONSHADERINPUTOUTPUTTESTS_HPP
+#define _VKTTESSELLATIONSHADERINPUTOUTPUTTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Shader Input/Output Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createShaderInputOutputTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONSHADERINPUTOUTPUTTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationTests.cpp
new file mode 100644 (file)
index 0000000..c71f468
--- /dev/null
@@ -0,0 +1,69 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationTests.hpp"
+#include "vktTestGroupUtil.hpp"
+#include "vktTessellationLimitsTests.hpp"
+#include "vktTessellationCoordinatesTests.hpp"
+#include "vktTessellationWindingTests.hpp"
+#include "vktTessellationShaderInputOutputTests.hpp"
+#include "vktTessellationMiscDrawTests.hpp"
+#include "vktTessellationCommonEdgeTests.hpp"
+#include "vktTessellationFractionalSpacingTests.hpp"
+#include "vktTessellationPrimitiveDiscardTests.hpp"
+#include "vktTessellationInvarianceTests.hpp"
+#include "vktTessellationUserDefinedIO.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+namespace
+{
+
+void createChildren (tcu::TestCaseGroup* tessellationTests)
+{
+       tcu::TestContext& testCtx = tessellationTests->getTestContext();
+
+       tessellationTests->addChild(createLimitsTests                   (testCtx));
+       tessellationTests->addChild(createCoordinatesTests              (testCtx));
+       tessellationTests->addChild(createWindingTests                  (testCtx));
+       tessellationTests->addChild(createShaderInputOutputTests(testCtx));
+       tessellationTests->addChild(createMiscDrawTests                 (testCtx));
+       tessellationTests->addChild(createCommonEdgeTests               (testCtx));
+       tessellationTests->addChild(createFractionalSpacingTests(testCtx));
+       tessellationTests->addChild(createPrimitiveDiscardTests (testCtx));
+       tessellationTests->addChild(createInvarianceTests               (testCtx));
+       tessellationTests->addChild(createUserDefinedIOTests    (testCtx));
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
+{
+       return createTestGroup(testCtx, "tessellation", "Tessellation tests", createChildren);
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationTests.hpp
new file mode 100644 (file)
index 0000000..8744e52
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONTESTS_HPP
+#define _VKTTESSELLATIONTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationUserDefinedIO.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationUserDefinedIO.cpp
new file mode 100644 (file)
index 0000000..adfcf44
--- /dev/null
@@ -0,0 +1,1034 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation User Defined IO Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationUserDefinedIO.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuImageIO.hpp"
+
+#include "gluVarType.hpp"
+#include "gluVarTypeUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deSharedPtr.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+       NUM_PER_PATCH_BLOCKS            = 2,
+       NUM_PER_PATCH_ARRAY_ELEMS       = 3,
+       NUM_OUTPUT_VERTICES                     = 5,
+       NUM_TESS_LEVELS                         = 6,
+       MAX_TESSELLATION_PATCH_SIZE = 32,
+       RENDER_SIZE                                     = 256,
+};
+
+enum IOType
+{
+       IO_TYPE_PER_PATCH = 0,
+       IO_TYPE_PER_PATCH_ARRAY,
+       IO_TYPE_PER_PATCH_BLOCK,
+       IO_TYPE_PER_PATCH_BLOCK_ARRAY,
+       IO_TYPE_PER_VERTEX,
+       IO_TYPE_PER_VERTEX_BLOCK,
+
+       IO_TYPE_LAST
+};
+
+enum VertexIOArraySize
+{
+       VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
+       VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN,           //!< Use gl_MaxPatchVertices as size for per-vertex input array.
+       VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN,                         //!< Minimum maxTessellationPatchSize required by the spec.
+
+       VERTEX_IO_ARRAY_SIZE_LAST
+};
+
+struct CaseDefinition
+{
+       TessPrimitiveType       primitiveType;
+       IOType                          ioType;
+       VertexIOArraySize       vertexIOArraySize;
+       std::string                     referenceImagePath;
+};
+
+typedef std::string (*BasicTypeVisitFunc)(const std::string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
+
+class TopLevelObject
+{
+public:
+       virtual                                 ~TopLevelObject                                 (void) {}
+
+       virtual std::string             name                                                    (void) const = 0;
+       virtual std::string             declare                                                 (void) const = 0;
+       virtual std::string             declareArray                                    (const std::string& arraySizeExpr) const = 0;
+       virtual std::string             glslTraverseBasicTypeArray              (const int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
+                                                                                                                        const int indentationDepth,
+                                                                                                                        BasicTypeVisitFunc) const = 0;
+       virtual std::string             glslTraverseBasicType                   (const int indentationDepth,
+                                                                                                                        BasicTypeVisitFunc) const = 0;
+       virtual int                             numBasicSubobjectsInElementType (void) const = 0;
+       virtual std::string             basicSubobjectAtIndex                   (const int index, const int arraySize) const = 0;
+};
+
+std::string glslTraverseBasicTypes (const std::string&                 rootName,
+                                                                       const glu::VarType&                     rootType,
+                                                                       const int                                       arrayNestingDepth,
+                                                                       const int                                       indentationDepth,
+                                                                       const BasicTypeVisitFunc        visit)
+{
+       if (rootType.isBasicType())
+               return visit(rootName, rootType.getBasicType(), indentationDepth);
+       else if (rootType.isArrayType())
+       {
+               const std::string indentation   = std::string(indentationDepth, '\t');
+               const std::string loopIndexName = "i" + de::toString(arrayNestingDepth);
+               const std::string arrayLength   = de::toString(rootType.getArraySize());
+               return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; ++" + loopIndexName + ")\n" +
+                          indentation + "{\n" +
+                          glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
+                          indentation + "}\n";
+       }
+       else if (rootType.isStructType())
+       {
+               const glu::StructType&  structType = *rootType.getStructPtr();
+               const int                               numMembers = structType.getNumMembers();
+               std::string                             result;
+
+               for (int membNdx = 0; membNdx < numMembers; ++membNdx)
+               {
+                       const glu::StructMember& member = structType.getMember(membNdx);
+                       result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
+               }
+
+               return result;
+       }
+       else
+       {
+               DE_ASSERT(false);
+               return DE_NULL;
+       }
+}
+
+//! Used as the 'visit' argument for glslTraverseBasicTypes.
+std::string glslAssignBasicTypeObject (const std::string& name, const glu::DataType type, const int indentationDepth)
+{
+       const int                       scalarSize      = glu::getDataTypeScalarSize(type);
+       const std::string       indentation     = std::string(indentationDepth, '\t');
+       std::ostringstream      result;
+
+       result << indentation << name << " = ";
+
+       if (type != glu::TYPE_FLOAT)
+               result << std::string() << glu::getDataTypeName(type) << "(";
+       for (int i = 0; i < scalarSize; ++i)
+               result << (i > 0 ? ", v+" + de::floatToString(0.8f*i, 1) : "v");
+       if (type != glu::TYPE_FLOAT)
+               result << ")";
+       result << ";\n"
+                  << indentation << "v += 0.4;\n";
+       return result.str();
+}
+
+//! Used as the 'visit' argument for glslTraverseBasicTypes.
+std::string glslCheckBasicTypeObject (const std::string& name, const glu::DataType type, const int indentationDepth)
+{
+       const int                       scalarSize      = glu::getDataTypeScalarSize(type);
+       const std::string       indentation     = std::string(indentationDepth, '\t');
+       std::ostringstream      result;
+
+       result << indentation << "allOk = allOk && compare_" << glu::getDataTypeName(type) << "(" << name << ", ";
+
+       if (type != glu::TYPE_FLOAT)
+               result << std::string() << glu::getDataTypeName(type) << "(";
+       for (int i = 0; i < scalarSize; ++i)
+               result << (i > 0 ? ", v+" + de::floatToString(0.8f*i, 1) : "v");
+       if (type != glu::TYPE_FLOAT)
+               result << ")";
+       result << ");\n"
+                  << indentation << "v += 0.4;\n"
+                  << indentation << "if (allOk) ++firstFailedInputIndex;\n";
+
+       return result.str();
+}
+
+int numBasicSubobjectsInElementType (const std::vector<de::SharedPtr<TopLevelObject> >& objects)
+{
+       int result = 0;
+       for (int i = 0; i < static_cast<int>(objects.size()); ++i)
+               result += objects[i]->numBasicSubobjectsInElementType();
+       return result;
+}
+
+std::string basicSubobjectAtIndex (const int subobjectIndex, const std::vector<de::SharedPtr<TopLevelObject> >& objects, const int topLevelArraySize)
+{
+       int currentIndex = 0;
+       int objectIndex  = 0;
+
+       for (; currentIndex < subobjectIndex; ++objectIndex)
+               currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
+
+       if (currentIndex > subobjectIndex)
+       {
+               --objectIndex;
+               currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
+       }
+
+       return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
+}
+
+int numBasicSubobjects (const glu::VarType& type)
+{
+       if (type.isBasicType())
+               return 1;
+       else if (type.isArrayType())
+               return type.getArraySize()*numBasicSubobjects(type.getElementType());
+       else if (type.isStructType())
+       {
+               const glu::StructType&  structType      = *type.getStructPtr();
+               int                                             result          = 0;
+               for (int i = 0; i < structType.getNumMembers(); ++i)
+                       result += numBasicSubobjects(structType.getMember(i).getType());
+               return result;
+       }
+       else
+       {
+               DE_ASSERT(false);
+               return -1;
+       }
+}
+
+class Variable : public TopLevelObject
+{
+public:
+       Variable (const std::string& name_, const glu::VarType& type, const bool isArray)
+               : m_name                (name_)
+               , m_type                (type)
+               , m_isArray             (isArray)
+       {
+               DE_ASSERT(!type.isArrayType());
+       }
+
+       std::string             name                                                            (void) const { return m_name; }
+       std::string             declare                                                         (void) const;
+       std::string             declareArray                                            (const std::string& arraySizeExpr) const;
+       std::string             glslTraverseBasicTypeArray                      (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc) const;
+       std::string             glslTraverseBasicType                           (const int indentationDepth, BasicTypeVisitFunc) const;
+       int                             numBasicSubobjectsInElementType         (void) const;
+       std::string             basicSubobjectAtIndex                           (const int index, const int arraySize) const;
+
+private:
+       std::string             m_name;
+       glu::VarType    m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
+       const bool              m_isArray;
+};
+
+std::string Variable::declare (void) const
+{
+       DE_ASSERT(!m_isArray);
+       return de::toString(glu::declare(m_type, m_name)) + ";\n";
+}
+
+std::string Variable::declareArray (const std::string& sizeExpr) const
+{
+       DE_ASSERT(m_isArray);
+       return de::toString(glu::declare(m_type, m_name)) + "[" + sizeExpr + "];\n";
+}
+
+std::string Variable::glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+       DE_ASSERT(m_isArray);
+
+       const bool                      traverseAsArray = numArrayElements >= 0;
+       const std::string       traversedName   = m_name + (!traverseAsArray ? "[gl_InvocationID]" : "");
+       const glu::VarType      type                    = traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
+
+       return glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
+}
+
+std::string Variable::glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+       DE_ASSERT(!m_isArray);
+       return glslTraverseBasicTypes(m_name, m_type, 0, indentationDepth, visit);
+}
+
+int Variable::numBasicSubobjectsInElementType (void) const
+{
+       return numBasicSubobjects(m_type);
+}
+
+std::string Variable::basicSubobjectAtIndex (const int subobjectIndex, const int arraySize) const
+{
+       const glu::VarType      type             = m_isArray ? glu::VarType(m_type, arraySize) : m_type;
+       int                                     currentIndex = 0;
+
+       for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type); basicIt != glu::BasicTypeIterator::end(&type); ++basicIt)
+       {
+               if (currentIndex == subobjectIndex)
+                       return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
+               ++currentIndex;
+       }
+       DE_ASSERT(false);
+       return DE_NULL;
+}
+
+class IOBlock : public TopLevelObject
+{
+public:
+       struct Member
+       {
+               std::string             name;
+               glu::VarType    type;
+
+               Member (const std::string& n, const glu::VarType& t) : name(n), type(t) {}
+       };
+
+       IOBlock (const std::string& blockName, const std::string& interfaceName, const std::vector<Member>& members)
+               : m_blockName           (blockName)
+               , m_interfaceName       (interfaceName)
+               , m_members                     (members)
+       {
+       }
+
+       std::string                     name                                                            (void) const { return m_interfaceName; }
+       std::string                     declare                                                         (void) const;
+       std::string                     declareArray                                            (const std::string& arraySizeExpr) const;
+       std::string                     glslTraverseBasicTypeArray                      (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc) const;
+       std::string                     glslTraverseBasicType                           (const int indentationDepth, BasicTypeVisitFunc) const;
+       int                                     numBasicSubobjectsInElementType         (void) const;
+       std::string                     basicSubobjectAtIndex                           (const int index, const int arraySize) const;
+
+private:
+       std::string                     m_blockName;
+       std::string                     m_interfaceName;
+       std::vector<Member>     m_members;
+};
+
+std::string IOBlock::declare (void) const
+{
+       std::ostringstream buf;
+
+       buf << m_blockName << "\n"
+               << "{\n";
+
+       for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+               buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
+
+       buf << "} " << m_interfaceName << ";\n";
+       return buf.str();
+}
+
+std::string IOBlock::declareArray (const std::string& sizeExpr) const
+{
+       std::ostringstream buf;
+
+       buf << m_blockName << "\n"
+               << "{\n";
+
+       for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+               buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
+
+       buf << "} " << m_interfaceName << "[" << sizeExpr << "];\n";
+       return buf.str();
+}
+
+std::string IOBlock::glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+       if (numArrayElements >= 0)
+       {
+               const std::string       indentation = std::string(indentationDepth, '\t');
+               std::ostringstream      result;
+
+               result << indentation << "for (int i0 = 0; i0 < " << numArrayElements << "; ++i0)\n"
+                          << indentation << "{\n";
+               for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+                       result << glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth + 1, visit);
+               result << indentation + "}\n";
+               return result.str();
+       }
+       else
+       {
+               std::ostringstream result;
+               for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+                       result << glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
+               return result.str();
+       }
+}
+
+std::string IOBlock::glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+       std::ostringstream result;
+       for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+               result << glslTraverseBasicTypes(m_interfaceName + "." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
+       return result.str();
+}
+
+int IOBlock::numBasicSubobjectsInElementType (void) const
+{
+       int result = 0;
+       for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+               result += numBasicSubobjects(m_members[i].type);
+       return result;
+}
+
+std::string IOBlock::basicSubobjectAtIndex (const int subobjectIndex, const int arraySize) const
+{
+       int currentIndex = 0;
+       for (int arrayNdx = 0; arrayNdx < arraySize; ++arrayNdx)
+       for (int memberNdx = 0; memberNdx < static_cast<int>(m_members.size()); ++memberNdx)
+       {
+               const glu::VarType& membType = m_members[memberNdx].type;
+               for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType); basicIt != glu::BasicTypeIterator::end(&membType); ++basicIt)
+               {
+                       if (currentIndex == subobjectIndex)
+                               return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
+                       currentIndex++;
+               }
+       }
+       DE_ASSERT(false);
+       return DE_NULL;
+}
+
+class UserDefinedIOTest : public TestCase
+{
+public:
+                                                       UserDefinedIOTest       (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef);
+       void                                    initPrograms            (vk::SourceCollections& programCollection) const;
+       TestInstance*                   createInstance          (Context& context) const;
+
+private:
+       const CaseDefinition                                            m_caseDef;
+       std::vector<glu::StructType>                            m_structTypes;
+       std::vector<de::SharedPtr<TopLevelObject> >     m_tcsOutputs;
+       std::vector<de::SharedPtr<TopLevelObject> >     m_tesInputs;
+       std::string                                                                     m_tcsDeclarations;
+       std::string                                                                     m_tcsStatements;
+       std::string                                                                     m_tesDeclarations;
+       std::string                                                                     m_tesStatements;
+};
+
+UserDefinedIOTest::UserDefinedIOTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+       : TestCase      (testCtx, name, description)
+       , m_caseDef     (caseDef)
+{
+       const bool                      isPerPatchIO                            = m_caseDef.ioType == IO_TYPE_PER_PATCH                         ||
+                                                                                                         m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY           ||
+                                                                                                         m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK           ||
+                                                                                                         m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
+
+       const bool                      isExplicitVertexArraySize       = m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
+                                                                                                         m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN;
+
+       const std::string       vertexAttrArrayInputSize        = m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT                                  ? ""
+                                                                                                       : m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN   ? "gl_MaxPatchVertices"
+                                                                                                       : m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN                 ? de::toString(MAX_TESSELLATION_PATCH_SIZE)
+                                                                                                       : DE_NULL;
+
+       const char* const       maybePatch                                      = isPerPatchIO ? "patch " : "";
+       const std::string       outMaybePatch                           = std::string() + maybePatch + "out ";
+       const std::string       inMaybePatch                            = std::string() + maybePatch + "in ";
+       const bool                      useBlock                                        = m_caseDef.ioType == IO_TYPE_PER_VERTEX_BLOCK          ||
+                                                                                                         m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK           ||
+                                                                                                         m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
+       const int                       wrongNumElements                        = -2;
+
+       std::ostringstream tcsDeclarations;
+       std::ostringstream tcsStatements;
+       std::ostringstream tesDeclarations;
+       std::ostringstream tesStatements;
+
+       // Indices 0 and 1 are taken, see initPrograms()
+       int tcsNextOutputLocation = 2;
+       int tesNextInputLocation  = 2;
+
+       m_structTypes.push_back(glu::StructType("S"));
+
+       const glu::VarType      highpFloat              (glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
+       glu::StructType&        structType              = m_structTypes.back();
+       const glu::VarType      structVarType   (&structType);
+       bool                            usedStruct              = false;
+
+       structType.addMember("x", glu::VarType(glu::TYPE_INT,            glu::PRECISION_HIGHP));
+       structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
+
+       // It is illegal to have a structure containing an array as an output variable
+       if (useBlock)
+               structType.addMember("z", glu::VarType(highpFloat, 2));
+
+       if (useBlock)
+       {
+               std::vector<IOBlock::Member> blockMembers;
+
+               // use leaner block to make sure it is not larger than allowed (per-patch storage is very limited)
+               const bool useLightweightBlock = (m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY);
+
+               if (!useLightweightBlock)
+                       blockMembers.push_back(IOBlock::Member("blockS",        structVarType));
+
+               blockMembers.push_back(IOBlock::Member("blockFa",       glu::VarType(highpFloat, 3)));
+               blockMembers.push_back(IOBlock::Member("blockSa",       glu::VarType(structVarType, 2)));
+               blockMembers.push_back(IOBlock::Member("blockF",        highpFloat));
+
+               m_tcsOutputs.push_back  (de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
+               m_tesInputs.push_back   (de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
+
+               usedStruct = true;
+       }
+       else
+       {
+               const Variable var0("in_te_s", structVarType,   m_caseDef.ioType != IO_TYPE_PER_PATCH);
+               const Variable var1("in_te_f", highpFloat,              m_caseDef.ioType != IO_TYPE_PER_PATCH);
+
+               if (m_caseDef.ioType != IO_TYPE_PER_PATCH_ARRAY)
+               {
+                       // Arrays of structures are disallowed, add struct cases only if not arrayed variable
+                       m_tcsOutputs.push_back  (de::SharedPtr<TopLevelObject>(new Variable(var0)));
+                       m_tesInputs.push_back   (de::SharedPtr<TopLevelObject>(new Variable(var0)));
+
+                       usedStruct = true;
+               }
+
+               m_tcsOutputs.push_back  (de::SharedPtr<TopLevelObject>(new Variable(var1)));
+               m_tesInputs.push_back   (de::SharedPtr<TopLevelObject>(new Variable(var1)));
+       }
+
+       if (usedStruct)
+               tcsDeclarations << de::toString(glu::declare(structType)) + ";\n";
+
+       tcsStatements << "\t{\n"
+                                 << "\t\thighp float v = 1.3;\n";
+
+       for (int tcsOutputNdx = 0; tcsOutputNdx < static_cast<int>(m_tcsOutputs.size()); ++tcsOutputNdx)
+       {
+               const TopLevelObject&   output          = *m_tcsOutputs[tcsOutputNdx];
+               const int                               numElements     = !isPerPatchIO                                                                         ? -1    //!< \note -1 means indexing with gl_InstanceID
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH                         ? 1
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY           ? NUM_PER_PATCH_ARRAY_ELEMS
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK           ? 1
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY     ? NUM_PER_PATCH_BLOCKS
+                                                                                       : wrongNumElements;
+               const bool                              isArray         = (numElements != 1);
+
+               DE_ASSERT(numElements != wrongNumElements);
+
+               // \note: TCS output arrays are always implicitly-sized
+               tcsDeclarations << "layout(location = " << tcsNextOutputLocation << ") ";
+               if (isArray)
+                       tcsDeclarations << outMaybePatch << output.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY                     ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
+                                                                                                                                 : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY   ? de::toString(NUM_PER_PATCH_BLOCKS)
+                                                                                                                                 : "");
+               else
+                       tcsDeclarations << outMaybePatch << output.declare();
+
+               tcsNextOutputLocation += output.numBasicSubobjectsInElementType();
+
+               if (!isPerPatchIO)
+                       tcsStatements << "\t\tv += float(gl_InvocationID)*" << de::floatToString(0.4f * output.numBasicSubobjectsInElementType(), 1) << ";\n";
+
+               tcsStatements << "\n\t\t// Assign values to output " << output.name() << "\n";
+               if (isArray)
+                       tcsStatements << output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
+               else
+                       tcsStatements << output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
+
+               if (!isPerPatchIO)
+                       tcsStatements << "\t\tv += float(" << de::toString(NUM_OUTPUT_VERTICES) << "-gl_InvocationID-1)*" << de::floatToString(0.4f * output.numBasicSubobjectsInElementType(), 1) << ";\n";
+       }
+       tcsStatements << "\t}\n";
+
+       tcsDeclarations << "\n"
+                                       << "layout(location = 0) in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
+
+       if (usedStruct)
+               tesDeclarations << de::toString(glu::declare(structType)) << ";\n";
+
+       tesStatements << "\tbool allOk = true;\n"
+                                 << "\thighp uint firstFailedInputIndex = 0u;\n"
+                                 << "\t{\n"
+                                 << "\t\thighp float v = 1.3;\n";
+
+       for (int tesInputNdx = 0; tesInputNdx < static_cast<int>(m_tesInputs.size()); ++tesInputNdx)
+       {
+               const TopLevelObject&   input           = *m_tesInputs[tesInputNdx];
+               const int                               numElements     = !isPerPatchIO                                                                         ? NUM_OUTPUT_VERTICES
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH                         ? 1
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK           ? 1
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY           ? NUM_PER_PATCH_ARRAY_ELEMS
+                                                                                       : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY     ? NUM_PER_PATCH_BLOCKS
+                                                                                       : wrongNumElements;
+               const bool                              isArray         = (numElements != 1);
+
+               DE_ASSERT(numElements != wrongNumElements);
+
+               tesDeclarations << "layout(location = " << tesNextInputLocation << ") ";
+               if (isArray)
+                       tesDeclarations << inMaybePatch << input.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY               ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
+                                                                                                                               : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY     ? de::toString(NUM_PER_PATCH_BLOCKS)
+                                                                                                                               : isExplicitVertexArraySize                                                     ? de::toString(vertexAttrArrayInputSize)
+                                                                                                                               : "");
+               else
+                       tesDeclarations << inMaybePatch + input.declare();
+
+               tesNextInputLocation += input.numBasicSubobjectsInElementType();
+
+               tesStatements << "\n\t\t// Check values in input " << input.name() << "\n";
+               if (isArray)
+                       tesStatements << input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
+               else
+                       tesStatements << input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
+       }
+       tesStatements << "\t}\n";
+
+       m_tcsDeclarations = tcsDeclarations.str();
+       m_tcsStatements   = tcsStatements.str();
+       m_tesDeclarations = tesDeclarations.str();
+       m_tesStatements   = tesStatements.str();
+}
+
+void UserDefinedIOTest::initPrograms (vk::SourceCollections& programCollection) const
+{
+       // Vertex shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp float in_v_attr;\n"
+                       << "layout(location = 0) out highp float in_tc_attr;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    in_tc_attr = in_v_attr;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = " << NUM_OUTPUT_VERTICES << ") out;\n"
+                       << "\n"
+                       << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
+                       << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
+                       << "\n"
+                       << m_tcsDeclarations
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << m_tcsStatements
+                       << "\n"
+                       << "    gl_TessLevelInner[0] = in_tc_attr[0];\n"
+                       << "    gl_TessLevelInner[1] = in_tc_attr[1];\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+                       << "    gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+                       << "    gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+                       << "    gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+                       << "\n"
+                       << "    in_te_positionScale  = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
+                       << "    in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(m_caseDef.primitiveType) << ") in;\n"
+                       << "\n"
+                       << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
+                       << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
+                       << "\n"
+                       << m_tesDeclarations
+                       << "\n"
+                       << "layout(location = 0) out highp vec4 in_f_color;\n"
+                       << "\n"
+                       << "// Will contain the index of the first incorrect input,\n"
+                       << "// or the number of inputs if all are correct\n"
+                       << "layout (set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+                       << "    int  numInvocations;\n"
+                       << "    uint firstFailedInputIndex[];\n"
+                       << "} sb_out;\n"
+                       << "\n"
+                       << "bool compare_int   (int   a, int   b) { return a == b; }\n"
+                       << "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
+                       << "bool compare_vec4  (vec4  a, vec4  b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << m_tesStatements
+                       << "\n"
+                       << "    gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
+                       << "    in_f_color  = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+                       << "                        : vec4(1.0, 0.0, 0.0, 1.0);\n"
+                       << "\n"
+                       << "    int index = atomicAdd(sb_out.numInvocations, 1);\n"
+                       << "    sb_out.firstFailedInputIndex[index] = firstFailedInputIndex;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) in  highp   vec4 in_f_color;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = in_f_color;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+class UserDefinedIOTestInstance : public TestInstance
+{
+public:
+                                                       UserDefinedIOTestInstance       (Context&                                                                                       context,
+                                                                                                                const CaseDefinition                                                           caseDef,
+                                                                                                                const std::vector<de::SharedPtr<TopLevelObject> >&     tesInputs);
+       tcu::TestStatus                 iterate                                         (void);
+
+private:
+       const CaseDefinition                                                            m_caseDef;
+       const std::vector<de::SharedPtr<TopLevelObject> >       m_tesInputs;
+};
+
+UserDefinedIOTestInstance::UserDefinedIOTestInstance (Context& context, const CaseDefinition caseDef, const std::vector<de::SharedPtr<TopLevelObject> >& tesInputs)
+       : TestInstance          (context)
+       , m_caseDef                     (caseDef)
+       , m_tesInputs           (tesInputs)
+{
+}
+
+tcu::TestStatus UserDefinedIOTestInstance::iterate (void)
+{
+       requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+       const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
+       const VkDevice                  device                          = m_context.getDevice();
+       const VkQueue                   queue                           = m_context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = m_context.getDefaultAllocator();
+
+       const int                               numAttributes                           = NUM_TESS_LEVELS + 2 + 2;
+       static const float              attributes[numAttributes]       = { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f };
+       const int                               refNumVertices                          = referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
+       const int                               refNumUniqueVertices            = referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, true, &attributes[0], &attributes[2]);
+
+       // Vertex input attributes buffer: to pass tessellation levels
+
+       const VkFormat     vertexFormat                         = VK_FORMAT_R32_SFLOAT;
+       const deUint32     vertexStride                         = tcu::getPixelSize(mapVkFormat(vertexFormat));
+       const VkDeviceSize vertexDataSizeBytes          = numAttributes * vertexStride;
+       const Buffer       vertexBuffer                         (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       {
+               const Allocation& alloc = vertexBuffer.getAllocation();
+               deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+       }
+
+       // Output buffer: number of invocations and verification indices
+
+       const int                  resultBufferMaxVertices      = refNumVertices;
+       const VkDeviceSize resultBufferSizeBytes    = sizeof(deInt32) + resultBufferMaxVertices * sizeof(deUint32);
+       const Buffer       resultBuffer             (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+       {
+               const Allocation& alloc = resultBuffer.getAllocation();
+               deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+               flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+       }
+
+       // Color attachment
+
+       const tcu::IVec2                          renderSize                             = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+       const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
+       const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+       const Image                                       colorAttachmentImage           (vk, device, allocator,
+                                                                                                                        makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+                                                                                                                        MemoryRequirement::Any);
+
+       // Color output buffer: image will be copied here for verification
+
+       const VkDeviceSize      colorBufferSizeBytes    = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+       const Buffer            colorBuffer                             (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+       // Descriptors
+
+       const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+               .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+               .build(vk, device));
+
+       const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+               .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+       const Unique<VkDescriptorSet> descriptorSet    (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+       const VkDescriptorBufferInfo  resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+       DescriptorSetUpdateBuilder()
+               .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+               .update(vk, device);
+
+       // Pipeline
+
+       const Unique<VkImageView>      colorAttachmentView(makeImageView     (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+       const Unique<VkRenderPass>     renderPass         (makeRenderPass    (vk, device, colorFormat));
+       const Unique<VkFramebuffer>    framebuffer        (makeFramebuffer   (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+       const Unique<VkPipelineLayout> pipelineLayout     (makePipelineLayout(vk, device, *descriptorSetLayout));
+       const Unique<VkCommandPool>    cmdPool            (makeCommandPool   (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer>  cmdBuffer          (makeCommandBuffer (vk, device, *cmdPool));
+
+       const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+               .setRenderSize                (renderSize)
+               .setPatchControlPoints        (numAttributes)
+               .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+               .setShader                    (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader                    (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                                m_context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                        (vk, device, *pipelineLayout, *renderPass));
+
+       // Begin draw
+
+       beginCommandBuffer(vk, *cmdBuffer);
+
+       // Change color attachment image layout
+       {
+               const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+                       (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                       VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+       }
+
+       {
+               const VkRect2D renderArea = {
+                       makeOffset2D(0, 0),
+                       makeExtent2D(renderSize.x(), renderSize.y()),
+               };
+               const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+               beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+       }
+
+       vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+       vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
+       {
+               const VkDeviceSize vertexBufferOffset = 0ull;
+               vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+       }
+
+       vk.cmdDraw(*cmdBuffer, numAttributes, 1u, 0u, 0u);
+       endRenderPass(vk, *cmdBuffer);
+
+       // Copy render result to a host-visible buffer
+       {
+               const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                       *colorAttachmentImage, colorImageSubresourceRange);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+                       0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+       }
+       {
+               const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+               vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
+       }
+       {
+               const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+                       VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+               vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                       0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+       }
+
+       endCommandBuffer(vk, *cmdBuffer);
+       submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+       // Verification
+
+       bool isImageCompareOK = false;
+       {
+               const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+               invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+               // Load reference image
+               tcu::TextureLevel referenceImage;
+               tcu::ImageIO::loadPNG(referenceImage, m_context.getTestContext().getArchive(), m_caseDef.referenceImagePath.c_str());
+
+               // Verify case result
+               const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+               isImageCompareOK = tcu::fuzzyCompare(m_context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
+                                                                                        referenceImage.getAccess(), resultImageAccess, 0.02f, tcu::COMPARE_LOG_RESULT);
+       }
+       {
+               const Allocation& resultAlloc = resultBuffer.getAllocation();
+               invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+               const deInt32                   numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+               const deUint32* const   vertices    = reinterpret_cast<deUint32*>(static_cast<deUint8*>(resultAlloc.getHostPtr()) + sizeof(deInt32));
+
+               // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+               DE_ASSERT(numVertices <= refNumVertices);
+
+               if (numVertices < refNumUniqueVertices)
+               {
+                       m_context.getTestContext().getLog()
+                               << tcu::TestLog::Message << "Failure: got " << numVertices << " vertices, but expected at least " << refNumUniqueVertices << tcu::TestLog::EndMessage;
+
+                       return tcu::TestStatus::fail("Wrong number of vertices");
+               }
+               else
+               {
+                       tcu::TestLog&   log                                     = m_context.getTestContext().getLog();
+                       const int               topLevelArraySize       = (m_caseDef.ioType == IO_TYPE_PER_PATCH                        ? 1
+                                                                                               : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY           ? NUM_PER_PATCH_ARRAY_ELEMS
+                                                                                               : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK           ? 1
+                                                                                               : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY     ? NUM_PER_PATCH_BLOCKS
+                                                                                               : NUM_OUTPUT_VERTICES);
+                       const deUint32  numTEInputs                     = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
+
+                       for (int vertexNdx = 0; vertexNdx < numVertices; ++vertexNdx)
+                               if (vertices[vertexNdx] > numTEInputs)
+                               {
+                                       log << tcu::TestLog::Message
+                                               << "Failure: out_te_firstFailedInputIndex has value " << vertices[vertexNdx]
+                                               << ", but should be in range [0, " << numTEInputs << "]" << tcu::TestLog::EndMessage;
+
+                                       return tcu::TestStatus::fail("Invalid values returned from shader");
+                               }
+                               else if (vertices[vertexNdx] != numTEInputs)
+                               {
+                                       log << tcu::TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
+                                               << basicSubobjectAtIndex(vertices[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << tcu::TestLog::EndMessage;
+
+                                       return tcu::TestStatus::fail("Invalid input value in tessellation evaluation shader");
+                               }
+               }
+       }
+       return (isImageCompareOK ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
+}
+
+TestInstance* UserDefinedIOTest::createInstance (Context& context) const
+{
+       return new UserDefinedIOTestInstance(context, m_caseDef, m_tesInputs);
+}
+
+} // anonymous
+
+//! These tests correspond roughly to dEQP-GLES31.functional.tessellation.user_defined_io.*
+//! Original GLES test queried maxTessellationPatchSize, but this can't be done at the stage the shader source is prepared.
+//! Instead, we use minimum supported value.
+//! Negative tests weren't ported because vktShaderLibrary doesn't support tests that are expected to fail shader compilation.
+tcu::TestCaseGroup* createUserDefinedIOTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs"));
+
+       static const struct
+       {
+               const char*     name;
+               const char*     description;
+               IOType          ioType;
+       } ioCases[] =
+       {
+               { "per_patch",                                  "Per-patch TCS outputs",                                        IO_TYPE_PER_PATCH                               },
+               { "per_patch_array",                    "Per-patch array TCS outputs",                          IO_TYPE_PER_PATCH_ARRAY                 },
+               { "per_patch_block",                    "Per-patch TCS outputs in IO block",            IO_TYPE_PER_PATCH_BLOCK                 },
+               { "per_patch_block_array",              "Per-patch TCS outputs in IO block array",      IO_TYPE_PER_PATCH_BLOCK_ARRAY   },
+               { "per_vertex",                                 "Per-vertex TCS outputs",                                       IO_TYPE_PER_VERTEX                              },
+               { "per_vertex_block",                   "Per-vertex TCS outputs in IO block",           IO_TYPE_PER_VERTEX_BLOCK                },
+       };
+
+       static const struct
+       {
+               const char*                     name;
+               VertexIOArraySize       vertexIOArraySize;
+       } vertexArraySizeCases[] =
+       {
+               { "vertex_io_array_size_implicit",                      VERTEX_IO_ARRAY_SIZE_IMPLICIT                                   },
+               { "vertex_io_array_size_shader_builtin",        VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN    },
+               { "vertex_io_array_size_spec_min",                      VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN                  },
+       };
+
+       for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(ioCases); ++caseNdx)
+       {
+               de::MovePtr<tcu::TestCaseGroup> ioTypeGroup (new tcu::TestCaseGroup(testCtx, ioCases[caseNdx].name, ioCases[caseNdx].description));
+               for (int arrayCaseNdx = 0; arrayCaseNdx < DE_LENGTH_OF_ARRAY(vertexArraySizeCases); ++arrayCaseNdx)
+               {
+                       de::MovePtr<tcu::TestCaseGroup> vertexArraySizeGroup (new tcu::TestCaseGroup(testCtx, vertexArraySizeCases[arrayCaseNdx].name, ""));
+                       for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
+                       {
+                               const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
+                               const std::string               primitiveName = getTessPrimitiveTypeShaderName(primitiveType);
+                               const CaseDefinition    caseDef           = { primitiveType, ioCases[caseNdx].ioType, vertexArraySizeCases[arrayCaseNdx].vertexIOArraySize,
+                                                                                                                 std::string() + "vulkan/data/tessellation/user_defined_io_" + primitiveName + "_ref.png" };
+
+                               vertexArraySizeGroup->addChild(new UserDefinedIOTest(testCtx, primitiveName, "", caseDef));
+                       }
+                       ioTypeGroup->addChild(vertexArraySizeGroup.release());
+               }
+               group->addChild(ioTypeGroup.release());
+       }
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationUserDefinedIO.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationUserDefinedIO.hpp
new file mode 100644 (file)
index 0000000..a60aa06
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONUSERDEFINEDIOTESTS_HPP
+#define _VKTTESSELLATIONUSERDEFINEDIOTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation User Defined IO Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createUserDefinedIOTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONUSERDEFINEDIOTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.cpp
new file mode 100644 (file)
index 0000000..087d852
--- /dev/null
@@ -0,0 +1,1170 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Utilities
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "deMath.h"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+VkBufferCreateInfo makeBufferCreateInfo (const VkDeviceSize                    bufferSize,
+                                                                                const VkBufferUsageFlags       usage)
+{
+       const VkBufferCreateInfo bufferCreateInfo =
+       {
+               VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,   // VkStructureType              sType;
+               DE_NULL,                                                                // const void*                  pNext;
+               (VkBufferCreateFlags)0,                                 // VkBufferCreateFlags  flags;
+               bufferSize,                                                             // VkDeviceSize                 size;
+               usage,                                                                  // VkBufferUsageFlags   usage;
+               VK_SHARING_MODE_EXCLUSIVE,                              // VkSharingMode                sharingMode;
+               0u,                                                                             // deUint32                             queueFamilyIndexCount;
+               DE_NULL,                                                                // const deUint32*              pQueueFamilyIndices;
+       };
+       return bufferCreateInfo;
+}
+
+VkBufferMemoryBarrier makeBufferMemoryBarrier (const VkAccessFlags     srcAccessMask,
+                                                                                          const VkAccessFlags  dstAccessMask,
+                                                                                          const VkBuffer               buffer,
+                                                                                          const VkDeviceSize   offset,
+                                                                                          const VkDeviceSize   bufferSizeBytes)
+{
+       const VkBufferMemoryBarrier barrier =
+       {
+               VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,        // VkStructureType      sType;
+               DE_NULL,                                                                        // const void*          pNext;
+               srcAccessMask,                                                          // VkAccessFlags        srcAccessMask;
+               dstAccessMask,                                                          // VkAccessFlags        dstAccessMask;
+               VK_QUEUE_FAMILY_IGNORED,                                        // deUint32                     srcQueueFamilyIndex;
+               VK_QUEUE_FAMILY_IGNORED,                                        // deUint32                     destQueueFamilyIndex;
+               buffer,                                                                         // VkBuffer                     buffer;
+               offset,                                                                         // VkDeviceSize         offset;
+               bufferSizeBytes,                                                        // VkDeviceSize         size;
+       };
+       return barrier;
+}
+
+VkImageMemoryBarrier makeImageMemoryBarrier    (const VkAccessFlags                    srcAccessMask,
+                                                                                        const VkAccessFlags                    dstAccessMask,
+                                                                                        const VkImageLayout                    oldLayout,
+                                                                                        const VkImageLayout                    newLayout,
+                                                                                        const VkImage                                  image,
+                                                                                        const VkImageSubresourceRange  subresourceRange)
+{
+       const VkImageMemoryBarrier barrier =
+       {
+               VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,                 // VkStructureType                      sType;
+               DE_NULL,                                                                                // const void*                          pNext;
+               srcAccessMask,                                                                  // VkAccessFlags                        outputMask;
+               dstAccessMask,                                                                  // VkAccessFlags                        inputMask;
+               oldLayout,                                                                              // VkImageLayout                        oldLayout;
+               newLayout,                                                                              // VkImageLayout                        newLayout;
+               VK_QUEUE_FAMILY_IGNORED,                                                // deUint32                                     srcQueueFamilyIndex;
+               VK_QUEUE_FAMILY_IGNORED,                                                // deUint32                                     destQueueFamilyIndex;
+               image,                                                                                  // VkImage                                      image;
+               subresourceRange,                                                               // VkImageSubresourceRange      subresourceRange;
+       };
+       return barrier;
+}
+
+Move<VkCommandPool> makeCommandPool (const DeviceInterface& vk, const VkDevice device, const deUint32 queueFamilyIndex)
+{
+       const VkCommandPoolCreateInfo info =
+       {
+               VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,                     // VkStructureType                      sType;
+               DE_NULL,                                                                                        // const void*                          pNext;
+               VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,        // VkCommandPoolCreateFlags     flags;
+               queueFamilyIndex,                                                                       // deUint32                                     queueFamilyIndex;
+       };
+       return createCommandPool(vk, device, &info);
+}
+
+Move<VkCommandBuffer> makeCommandBuffer (const DeviceInterface& vk, const VkDevice device, const VkCommandPool commandPool)
+{
+       const VkCommandBufferAllocateInfo info =
+       {
+               VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,         // VkStructureType              sType;
+               DE_NULL,                                                                                        // const void*                  pNext;
+               commandPool,                                                                            // VkCommandPool                commandPool;
+               VK_COMMAND_BUFFER_LEVEL_PRIMARY,                                        // VkCommandBufferLevel level;
+               1u,                                                                                                     // deUint32                             commandBufferCount;
+       };
+       return allocateCommandBuffer(vk, device, &info);
+}
+
+Move<VkDescriptorSet> makeDescriptorSet (const DeviceInterface&                        vk,
+                                                                                const VkDevice                                 device,
+                                                                                const VkDescriptorPool                 descriptorPool,
+                                                                                const VkDescriptorSetLayout    setLayout)
+{
+       const VkDescriptorSetAllocateInfo info =
+       {
+               VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,         // VkStructureType                              sType;
+               DE_NULL,                                                                                        // const void*                                  pNext;
+               descriptorPool,                                                                         // VkDescriptorPool                             descriptorPool;
+               1u,                                                                                                     // deUint32                                             descriptorSetCount;
+               &setLayout,                                                                                     // const VkDescriptorSetLayout* pSetLayouts;
+       };
+       return allocateDescriptorSet(vk, device, &info);
+}
+
+Move<VkPipelineLayout> makePipelineLayout (const DeviceInterface&              vk,
+                                                                                  const VkDevice                               device,
+                                                                                  const VkDescriptorSetLayout  descriptorSetLayout)
+{
+       const VkPipelineLayoutCreateInfo info =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,          // VkStructureType                              sType;
+               DE_NULL,                                                                                        // const void*                                  pNext;
+               (VkPipelineLayoutCreateFlags)0,                                         // VkPipelineLayoutCreateFlags  flags;
+               1u,                                                                                                     // deUint32                                             setLayoutCount;
+               &descriptorSetLayout,                                                           // const VkDescriptorSetLayout* pSetLayouts;
+               0u,                                                                                                     // deUint32                                             pushConstantRangeCount;
+               DE_NULL,                                                                                        // const VkPushConstantRange*   pPushConstantRanges;
+       };
+       return createPipelineLayout(vk, device, &info);
+}
+
+Move<VkPipelineLayout> makePipelineLayoutWithoutDescriptors (const DeviceInterface&            vk,
+                                                                                                                        const VkDevice                         device)
+{
+       const VkPipelineLayoutCreateInfo info =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,          // VkStructureType                              sType;
+               DE_NULL,                                                                                        // const void*                                  pNext;
+               (VkPipelineLayoutCreateFlags)0,                                         // VkPipelineLayoutCreateFlags  flags;
+               0u,                                                                                                     // deUint32                                             setLayoutCount;
+               DE_NULL,                                                                                        // const VkDescriptorSetLayout* pSetLayouts;
+               0u,                                                                                                     // deUint32                                             pushConstantRangeCount;
+               DE_NULL,                                                                                        // const VkPushConstantRange*   pPushConstantRanges;
+       };
+       return createPipelineLayout(vk, device, &info);
+}
+
+Move<VkPipeline> makeComputePipeline (const DeviceInterface&           vk,
+                                                                         const VkDevice                                device,
+                                                                         const VkPipelineLayout                pipelineLayout,
+                                                                         const VkShaderModule                  shaderModule,
+                                                                         const VkSpecializationInfo*   specInfo)
+{
+       const VkPipelineShaderStageCreateInfo shaderStageInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,    // VkStructureType                                      sType;
+               DE_NULL,                                                                                                // const void*                                          pNext;
+               (VkPipelineShaderStageCreateFlags)0,                                    // VkPipelineShaderStageCreateFlags     flags;
+               VK_SHADER_STAGE_COMPUTE_BIT,                                                    // VkShaderStageFlagBits                        stage;
+               shaderModule,                                                                                   // VkShaderModule                                       module;
+               "main",                                                                                                 // const char*                                          pName;
+               specInfo,                                                                                               // const VkSpecializationInfo*          pSpecializationInfo;
+       };
+       const VkComputePipelineCreateInfo pipelineInfo =
+       {
+               VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,         // VkStructureType                                      sType;
+               DE_NULL,                                                                                        // const void*                                          pNext;
+               (VkPipelineCreateFlags)0,                                                       // VkPipelineCreateFlags                        flags;
+               shaderStageInfo,                                                                        // VkPipelineShaderStageCreateInfo      stage;
+               pipelineLayout,                                                                         // VkPipelineLayout                                     layout;
+               DE_NULL,                                                                                        // VkPipeline                                           basePipelineHandle;
+               0,                                                                                                      // deInt32                                                      basePipelineIndex;
+       };
+       return createComputePipeline(vk, device, DE_NULL , &pipelineInfo);
+}
+
+VkImageCreateInfo makeImageCreateInfo (const tcu::IVec2& size, const VkFormat format, const VkImageUsageFlags usage, const deUint32 numArrayLayers)
+{
+       const VkImageCreateInfo imageInfo =
+       {
+               VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,            // VkStructureType          sType;
+               DE_NULL,                                                                        // const void*              pNext;
+               (VkImageCreateFlags)0,                                          // VkImageCreateFlags       flags;
+               VK_IMAGE_TYPE_2D,                                                       // VkImageType              imageType;
+               format,                                                                         // VkFormat                 format;
+               makeExtent3D(size.x(), size.y(), 1),            // VkExtent3D               extent;
+               1u,                                                                                     // uint32_t                 mipLevels;
+               numArrayLayers,                                                         // uint32_t                 arrayLayers;
+               VK_SAMPLE_COUNT_1_BIT,                                          // VkSampleCountFlagBits    samples;
+               VK_IMAGE_TILING_OPTIMAL,                                        // VkImageTiling            tiling;
+               usage,                                                                          // VkImageUsageFlags        usage;
+               VK_SHARING_MODE_EXCLUSIVE,                                      // VkSharingMode            sharingMode;
+               0u,                                                                                     // uint32_t                 queueFamilyIndexCount;
+               DE_NULL,                                                                        // const uint32_t*          pQueueFamilyIndices;
+               VK_IMAGE_LAYOUT_UNDEFINED,                                      // VkImageLayout            initialLayout;
+       };
+       return imageInfo;
+}
+
+Move<VkImageView> makeImageView (const DeviceInterface&                        vk,
+                                                                const VkDevice                                 vkDevice,
+                                                                const VkImage                                  image,
+                                                                const VkImageViewType                  viewType,
+                                                                const VkFormat                                 format,
+                                                                const VkImageSubresourceRange  subresourceRange)
+{
+       const VkImageViewCreateInfo imageViewParams =
+       {
+               VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,               // VkStructureType                      sType;
+               DE_NULL,                                                                                // const void*                          pNext;
+               (VkImageViewCreateFlags)0,                                              // VkImageViewCreateFlags       flags;
+               image,                                                                                  // VkImage                                      image;
+               viewType,                                                                               // VkImageViewType                      viewType;
+               format,                                                                                 // VkFormat                                     format;
+               makeComponentMappingRGBA(),                                             // VkComponentMapping           components;
+               subresourceRange,                                                               // VkImageSubresourceRange      subresourceRange;
+       };
+       return createImageView(vk, vkDevice, &imageViewParams);
+}
+
+VkBufferImageCopy makeBufferImageCopy (const VkExtent3D                                        extent,
+                                                                          const VkImageSubresourceLayers       subresourceLayers)
+{
+       const VkBufferImageCopy copyParams =
+       {
+               0ull,                                                                           //      VkDeviceSize                            bufferOffset;
+               0u,                                                                                     //      deUint32                                        bufferRowLength;
+               0u,                                                                                     //      deUint32                                        bufferImageHeight;
+               subresourceLayers,                                                      //      VkImageSubresourceLayers        imageSubresource;
+               makeOffset3D(0, 0, 0),                                          //      VkOffset3D                                      imageOffset;
+               extent,                                                                         //      VkExtent3D                                      imageExtent;
+       };
+       return copyParams;
+}
+
+void beginCommandBuffer (const DeviceInterface& vk, const VkCommandBuffer commandBuffer)
+{
+       const VkCommandBufferBeginInfo info =
+       {
+               VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,    // VkStructureType                          sType;
+               DE_NULL,                                                                                // const void*                              pNext;
+               VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,    // VkCommandBufferUsageFlags                flags;
+               DE_NULL,                                                                                // const VkCommandBufferInheritanceInfo*    pInheritanceInfo;
+       };
+       VK_CHECK(vk.beginCommandBuffer(commandBuffer, &info));
+}
+
+void endCommandBuffer (const DeviceInterface& vk, const VkCommandBuffer commandBuffer)
+{
+       VK_CHECK(vk.endCommandBuffer(commandBuffer));
+}
+
+void submitCommandsAndWait (const DeviceInterface&     vk,
+                                                       const VkDevice                  device,
+                                                       const VkQueue                   queue,
+                                                       const VkCommandBuffer   commandBuffer)
+{
+       const VkFenceCreateInfo fenceInfo =
+       {
+               VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,    // VkStructureType              sType;
+               DE_NULL,                                                                // const void*                  pNext;
+               (VkFenceCreateFlags)0,                                  // VkFenceCreateFlags   flags;
+       };
+       const Unique<VkFence> fence(createFence(vk, device, &fenceInfo));
+
+       const VkSubmitInfo submitInfo =
+       {
+               VK_STRUCTURE_TYPE_SUBMIT_INFO,          // VkStructureType                sType;
+               DE_NULL,                                                        // const void*                    pNext;
+               0u,                                                                     // uint32_t                       waitSemaphoreCount;
+               DE_NULL,                                                        // const VkSemaphore*             pWaitSemaphores;
+               DE_NULL,                                                        // const VkPipelineStageFlags*    pWaitDstStageMask;
+               1u,                                                                     // uint32_t                       commandBufferCount;
+               &commandBuffer,                                         // const VkCommandBuffer*         pCommandBuffers;
+               0u,                                                                     // uint32_t                       signalSemaphoreCount;
+               DE_NULL,                                                        // const VkSemaphore*             pSignalSemaphores;
+       };
+       VK_CHECK(vk.queueSubmit(queue, 1u, &submitInfo, *fence));
+       VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), DE_TRUE, ~0ull));
+}
+
+void beginRenderPass (const DeviceInterface&   vk,
+                                         const VkCommandBuffer         commandBuffer,
+                                         const VkRenderPass            renderPass,
+                                         const VkFramebuffer           framebuffer,
+                                         const VkRect2D&                       renderArea,
+                                         const tcu::Vec4&                      clearColor)
+{
+       const VkClearValue clearValue = makeClearValueColor(clearColor);
+
+       const VkRenderPassBeginInfo renderPassBeginInfo = {
+               VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,               // VkStructureType         sType;
+               DE_NULL,                                                                                // const void*             pNext;
+               renderPass,                                                                             // VkRenderPass            renderPass;
+               framebuffer,                                                                    // VkFramebuffer           framebuffer;
+               renderArea,                                                                             // VkRect2D                renderArea;
+               1u,                                                                                             // uint32_t                clearValueCount;
+               &clearValue,                                                                    // const VkClearValue*     pClearValues;
+       };
+
+       vk.cmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+}
+
+void beginRenderPassWithRasterizationDisabled (const DeviceInterface&  vk,
+                                                                                          const VkCommandBuffer        commandBuffer,
+                                                                                          const VkRenderPass           renderPass,
+                                                                                          const VkFramebuffer          framebuffer)
+{
+       const VkRect2D renderArea = {{ 0, 0 }, { 0, 0 }};
+
+       const VkRenderPassBeginInfo renderPassBeginInfo = {
+               VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,               // VkStructureType         sType;
+               DE_NULL,                                                                                // const void*             pNext;
+               renderPass,                                                                             // VkRenderPass            renderPass;
+               framebuffer,                                                                    // VkFramebuffer           framebuffer;
+               renderArea,                                                                             // VkRect2D                renderArea;
+               0u,                                                                                             // uint32_t                clearValueCount;
+               DE_NULL,                                                                                // const VkClearValue*     pClearValues;
+       };
+
+       vk.cmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+}
+
+void endRenderPass (const DeviceInterface&     vk,
+                                       const VkCommandBuffer   commandBuffer)
+{
+       vk.cmdEndRenderPass(commandBuffer);
+}
+
+Move<VkRenderPass> makeRenderPass (const DeviceInterface&      vk,
+                                                                  const VkDevice                       device,
+                                                                  const VkFormat                       colorFormat)
+{
+       const VkAttachmentDescription colorAttachmentDescription =
+       {
+               (VkAttachmentDescriptionFlags)0,                                        // VkAttachmentDescriptionFlags         flags;
+               colorFormat,                                                                            // VkFormat                                                     format;
+               VK_SAMPLE_COUNT_1_BIT,                                                          // VkSampleCountFlagBits                        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;
+       };
+
+       const VkAttachmentReference colorAttachmentReference =
+       {
+               0u,                                                                                                     // deUint32                     attachment;
+               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout        layout;
+       };
+
+       const VkAttachmentReference depthAttachmentReference =
+       {
+               VK_ATTACHMENT_UNUSED,                                                           // deUint32                     attachment;
+               VK_IMAGE_LAYOUT_UNDEFINED                                                       // VkImageLayout        layout;
+       };
+
+       const VkSubpassDescription subpassDescription =
+       {
+               (VkSubpassDescriptionFlags)0,                                           // VkSubpassDescriptionFlags            flags;
+               VK_PIPELINE_BIND_POINT_GRAPHICS,                                        // VkPipelineBindPoint                          pipelineBindPoint;
+               0u,                                                                                                     // deUint32                                                     inputAttachmentCount;
+               DE_NULL,                                                                                        // const VkAttachmentReference*         pInputAttachments;
+               1u,                                                                                                     // deUint32                                                     colorAttachmentCount;
+               &colorAttachmentReference,                                                      // const VkAttachmentReference*         pColorAttachments;
+               DE_NULL,                                                                                        // const VkAttachmentReference*         pResolveAttachments;
+               &depthAttachmentReference,                                                      // const VkAttachmentReference*         pDepthStencilAttachment;
+               0u,                                                                                                     // deUint32                                                     preserveAttachmentCount;
+               DE_NULL                                                                                         // const deUint32*                                      pPreserveAttachments;
+       };
+
+       const VkRenderPassCreateInfo renderPassInfo =
+       {
+               VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,                      // VkStructureType                                      sType;
+               DE_NULL,                                                                                        // const void*                                          pNext;
+               (VkRenderPassCreateFlags)0,                                                     // VkRenderPassCreateFlags                      flags;
+               1u,                                                                                                     // deUint32                                                     attachmentCount;
+               &colorAttachmentDescription,                                            // const VkAttachmentDescription*       pAttachments;
+               1u,                                                                                                     // deUint32                                                     subpassCount;
+               &subpassDescription,                                                            // const VkSubpassDescription*          pSubpasses;
+               0u,                                                                                                     // deUint32                                                     dependencyCount;
+               DE_NULL                                                                                         // const VkSubpassDependency*           pDependencies;
+       };
+
+       return createRenderPass(vk, device, &renderPassInfo);
+}
+
+Move<VkRenderPass> makeRenderPassWithoutAttachments (const DeviceInterface&    vk,
+                                                                                                        const VkDevice                 device)
+{
+       const VkAttachmentReference unusedAttachment =
+       {
+               VK_ATTACHMENT_UNUSED,                                                           // deUint32                     attachment;
+               VK_IMAGE_LAYOUT_UNDEFINED                                                       // VkImageLayout        layout;
+       };
+
+       const VkSubpassDescription subpassDescription =
+       {
+               (VkSubpassDescriptionFlags)0,                                           // VkSubpassDescriptionFlags            flags;
+               VK_PIPELINE_BIND_POINT_GRAPHICS,                                        // VkPipelineBindPoint                          pipelineBindPoint;
+               0u,                                                                                                     // deUint32                                                     inputAttachmentCount;
+               DE_NULL,                                                                                        // const VkAttachmentReference*         pInputAttachments;
+               0u,                                                                                                     // deUint32                                                     colorAttachmentCount;
+               DE_NULL,                                                                                        // const VkAttachmentReference*         pColorAttachments;
+               DE_NULL,                                                                                        // const VkAttachmentReference*         pResolveAttachments;
+               &unusedAttachment,                                                                      // const VkAttachmentReference*         pDepthStencilAttachment;
+               0u,                                                                                                     // deUint32                                                     preserveAttachmentCount;
+               DE_NULL                                                                                         // const deUint32*                                      pPreserveAttachments;
+       };
+
+       const VkRenderPassCreateInfo renderPassInfo =
+       {
+               VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,                      // VkStructureType                                      sType;
+               DE_NULL,                                                                                        // const void*                                          pNext;
+               (VkRenderPassCreateFlags)0,                                                     // VkRenderPassCreateFlags                      flags;
+               0u,                                                                                                     // deUint32                                                     attachmentCount;
+               DE_NULL,                                                                                        // const VkAttachmentDescription*       pAttachments;
+               1u,                                                                                                     // deUint32                                                     subpassCount;
+               &subpassDescription,                                                            // const VkSubpassDescription*          pSubpasses;
+               0u,                                                                                                     // deUint32                                                     dependencyCount;
+               DE_NULL                                                                                         // const VkSubpassDependency*           pDependencies;
+       };
+
+       return createRenderPass(vk, device, &renderPassInfo);
+}
+
+Move<VkFramebuffer> makeFramebuffer (const DeviceInterface&            vk,
+                                                                        const VkDevice                         device,
+                                                                        const VkRenderPass                     renderPass,
+                                                                        const VkImageView                      colorAttachment,
+                                                                        const deUint32                         width,
+                                                                        const deUint32                         height,
+                                                                        const deUint32                         layers)
+{
+       const VkFramebufferCreateInfo framebufferInfo = {
+               VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,              // VkStructureType                             sType;
+               DE_NULL,                                                                                // const void*                                 pNext;
+               (VkFramebufferCreateFlags)0,                                    // VkFramebufferCreateFlags                    flags;
+               renderPass,                                                                             // VkRenderPass                                renderPass;
+               1u,                                                                                             // uint32_t                                    attachmentCount;
+               &colorAttachment,                                                               // const VkImageView*                          pAttachments;
+               width,                                                                                  // uint32_t                                    width;
+               height,                                                                                 // uint32_t                                    height;
+               layers,                                                                                 // uint32_t                                    layers;
+       };
+
+       return createFramebuffer(vk, device, &framebufferInfo);
+}
+
+Move<VkFramebuffer> makeFramebufferWithoutAttachments (const DeviceInterface&          vk,
+                                                                                                          const VkDevice                               device,
+                                                                                                          const VkRenderPass                   renderPass)
+{
+       const VkFramebufferCreateInfo framebufferInfo = {
+               VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,              // VkStructureType                             sType;
+               DE_NULL,                                                                                // const void*                                 pNext;
+               (VkFramebufferCreateFlags)0,                                    // VkFramebufferCreateFlags                    flags;
+               renderPass,                                                                             // VkRenderPass                                renderPass;
+               0u,                                                                                             // uint32_t                                    attachmentCount;
+               DE_NULL,                                                                                // const VkImageView*                          pAttachments;
+               0u,                                                                                             // uint32_t                                    width;
+               0u,                                                                                             // uint32_t                                    height;
+               0u,                                                                                             // uint32_t                                    layers;
+       };
+
+       return createFramebuffer(vk, device, &framebufferInfo);
+}
+
+GraphicsPipelineBuilder& GraphicsPipelineBuilder::setShader (const DeviceInterface&                    vk,
+                                                                                                                        const VkDevice                                 device,
+                                                                                                                        const VkShaderStageFlagBits    stage,
+                                                                                                                        const ProgramBinary&                   binary,
+                                                                                                                        const VkSpecializationInfo*    specInfo)
+{
+       VkShaderModule module;
+       switch (stage)
+       {
+               case (VK_SHADER_STAGE_VERTEX_BIT):
+                       DE_ASSERT(m_vertexShaderModule.get() == DE_NULL);
+                       m_vertexShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+                       module = *m_vertexShaderModule;
+                       break;
+
+               case (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT):
+                       DE_ASSERT(m_tessControlShaderModule.get() == DE_NULL);
+                       m_tessControlShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+                       module = *m_tessControlShaderModule;
+                       break;
+
+               case (VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT):
+                       DE_ASSERT(m_tessEvaluationShaderModule.get() == DE_NULL);
+                       m_tessEvaluationShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+                       module = *m_tessEvaluationShaderModule;
+                       break;
+
+               case (VK_SHADER_STAGE_GEOMETRY_BIT):
+                       DE_ASSERT(m_geometryShaderModule.get() == DE_NULL);
+                       m_geometryShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+                       module = *m_geometryShaderModule;
+                       break;
+
+               case (VK_SHADER_STAGE_FRAGMENT_BIT):
+                       DE_ASSERT(m_fragmentShaderModule.get() == DE_NULL);
+                       m_fragmentShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+                       module = *m_fragmentShaderModule;
+                       break;
+
+               default:
+                       DE_FATAL("Invalid shader stage");
+                       return *this;
+       }
+
+       const VkPipelineShaderStageCreateInfo pipelineShaderStageInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,    // VkStructureType                                              sType;
+               DE_NULL,                                                                                                // const void*                                                  pNext;
+               (VkPipelineShaderStageCreateFlags)0,                                    // VkPipelineShaderStageCreateFlags             flags;
+               stage,                                                                                                  // VkShaderStageFlagBits                                stage;
+               module,                                                                                                 // VkShaderModule                                               module;
+               "main",                                                                                                 // const char*                                                  pName;
+               specInfo,                                                                                               // const VkSpecializationInfo*                  pSpecializationInfo;
+       };
+
+       m_shaderStageFlags |= stage;
+       m_shaderStages.push_back(pipelineShaderStageInfo);
+
+       return *this;
+}
+
+GraphicsPipelineBuilder& GraphicsPipelineBuilder::setVertexInputSingleAttribute (const VkFormat vertexFormat, const deUint32 stride)
+{
+       const VkVertexInputBindingDescription bindingDesc =
+       {
+               0u,                                                                     // uint32_t                             binding;
+               stride,                                                         // uint32_t                             stride;
+               VK_VERTEX_INPUT_RATE_VERTEX,            // VkVertexInputRate    inputRate;
+       };
+       const VkVertexInputAttributeDescription attributeDesc =
+       {
+               0u,                                                                     // uint32_t                     location;
+               0u,                                                                     // uint32_t                     binding;
+               vertexFormat,                                           // VkFormat                     format;
+               0u,                                                                     // uint32_t                     offset;
+       };
+
+       m_vertexInputBindings.clear();
+       m_vertexInputBindings.push_back(bindingDesc);
+
+       m_vertexInputAttributes.clear();
+       m_vertexInputAttributes.push_back(attributeDesc);
+
+       return *this;
+}
+
+template<typename T>
+inline const T* dataPointer (const std::vector<T>& vec)
+{
+       return (vec.size() != 0 ? &vec[0] : DE_NULL);
+}
+
+Move<VkPipeline> GraphicsPipelineBuilder::build (const DeviceInterface&        vk,
+                                                                                                const VkDevice                 device,
+                                                                                                const VkPipelineLayout pipelineLayout,
+                                                                                                const VkRenderPass             renderPass)
+{
+       const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,              // VkStructureType                             sType;
+               DE_NULL,                                                                                                                // const void*                                 pNext;
+               (VkPipelineVertexInputStateCreateFlags)0,                                               // VkPipelineVertexInputStateCreateFlags       flags;
+               static_cast<deUint32>(m_vertexInputBindings.size()),                    // uint32_t                                    vertexBindingDescriptionCount;
+               dataPointer(m_vertexInputBindings),                                                             // const VkVertexInputBindingDescription*      pVertexBindingDescriptions;
+               static_cast<deUint32>(m_vertexInputAttributes.size()),                  // uint32_t                                    vertexAttributeDescriptionCount;
+               dataPointer(m_vertexInputAttributes),                                                   // const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions;
+       };
+
+       const VkPrimitiveTopology topology = (m_shaderStageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
+                                                                                                                                                                                                                : m_primitiveTopology;
+       const VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,    // VkStructureType                             sType;
+               DE_NULL,                                                                                                                // const void*                                 pNext;
+               (VkPipelineInputAssemblyStateCreateFlags)0,                                             // VkPipelineInputAssemblyStateCreateFlags     flags;
+               topology,                                                                                                               // VkPrimitiveTopology                         topology;
+               VK_FALSE,                                                                                                               // VkBool32                                    primitiveRestartEnable;
+       };
+
+       const VkPipelineTessellationStateCreateInfo pipelineTessellationStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,              // VkStructureType                             sType;
+               DE_NULL,                                                                                                                // const void*                                 pNext;
+               (VkPipelineTessellationStateCreateFlags)0,                                              // VkPipelineTessellationStateCreateFlags      flags;
+               m_patchControlPoints,                                                                                   // uint32_t                                    patchControlPoints;
+       };
+
+       const VkViewport viewport = makeViewport(
+               0.0f, 0.0f,
+               static_cast<float>(m_renderSize.x()), static_cast<float>(m_renderSize.y()),
+               0.0f, 1.0f);
+
+       const VkRect2D scissor = {
+               makeOffset2D(0, 0),
+               makeExtent2D(m_renderSize.x(), m_renderSize.y()),
+       };
+
+       const VkPipelineViewportStateCreateInfo pipelineViewportStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,  // VkStructureType                             sType;
+               DE_NULL,                                                                                                // const void*                                 pNext;
+               (VkPipelineViewportStateCreateFlags)0,                                  // VkPipelineViewportStateCreateFlags          flags;
+               1u,                                                                                                             // uint32_t                                    viewportCount;
+               &viewport,                                                                                              // const VkViewport*                           pViewports;
+               1u,                                                                                                             // uint32_t                                    scissorCount;
+               &scissor,                                                                                               // const VkRect2D*                             pScissors;
+       };
+
+       const bool isRasterizationDisabled = ((m_shaderStageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) == 0);
+       const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,             // VkStructureType                          sType;
+               DE_NULL,                                                                                                                // const void*                              pNext;
+               (VkPipelineRasterizationStateCreateFlags)0,                                             // VkPipelineRasterizationStateCreateFlags  flags;
+               VK_FALSE,                                                                                                               // VkBool32                                 depthClampEnable;
+               isRasterizationDisabled,                                                                                // VkBool32                                 rasterizerDiscardEnable;
+               VK_POLYGON_MODE_FILL,                                                                                   // VkPolygonMode                                                        polygonMode;
+               m_cullModeFlags,                                                                                                // VkCullModeFlags                                                      cullMode;
+               m_frontFace,                                                                                                    // VkFrontFace                                                          frontFace;
+               VK_FALSE,                                                                                                               // VkBool32                                                                     depthBiasEnable;
+               0.0f,                                                                                                                   // float                                                                        depthBiasConstantFactor;
+               0.0f,                                                                                                                   // float                                                                        depthBiasClamp;
+               0.0f,                                                                                                                   // float                                                                        depthBiasSlopeFactor;
+               1.0f,                                                                                                                   // float                                                                        lineWidth;
+       };
+
+       const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,       // VkStructureType                                                      sType;
+               DE_NULL,                                                                                                        // const void*                                                          pNext;
+               (VkPipelineMultisampleStateCreateFlags)0,                                       // VkPipelineMultisampleStateCreateFlags        flags;
+               VK_SAMPLE_COUNT_1_BIT,                                                                          // VkSampleCountFlagBits                                        rasterizationSamples;
+               VK_FALSE,                                                                                                       // VkBool32                                                                     sampleShadingEnable;
+               0.0f,                                                                                                           // float                                                                        minSampleShading;
+               DE_NULL,                                                                                                        // const VkSampleMask*                                          pSampleMask;
+               VK_FALSE,                                                                                                       // VkBool32                                                                     alphaToCoverageEnable;
+               VK_FALSE                                                                                                        // VkBool32                                                                     alphaToOneEnable;
+       };
+
+       const VkStencilOpState stencilOpState = makeStencilOpState(
+               VK_STENCIL_OP_KEEP,             // stencil fail
+               VK_STENCIL_OP_KEEP,             // depth & stencil pass
+               VK_STENCIL_OP_KEEP,             // depth only fail
+               VK_COMPARE_OP_NEVER,    // compare op
+               0u,                                             // compare mask
+               0u,                                             // write mask
+               0u);                                    // reference
+
+       const VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,     // VkStructureType                                                      sType;
+               DE_NULL,                                                                                                        // const void*                                                          pNext;
+               (VkPipelineDepthStencilStateCreateFlags)0,                                      // VkPipelineDepthStencilStateCreateFlags       flags;
+               VK_FALSE,                                                                                                       // VkBool32                                                                     depthTestEnable;
+               VK_FALSE,                                                                                                       // VkBool32                                                                     depthWriteEnable;
+               VK_COMPARE_OP_LESS,                                                                                     // VkCompareOp                                                          depthCompareOp;
+               VK_FALSE,                                                                                                       // VkBool32                                                                     depthBoundsTestEnable;
+               VK_FALSE,                                                                                                       // VkBool32                                                                     stencilTestEnable;
+               stencilOpState,                                                                                         // VkStencilOpState                                                     front;
+               stencilOpState,                                                                                         // VkStencilOpState                                                     back;
+               0.0f,                                                                                                           // float                                                                        minDepthBounds;
+               1.0f,                                                                                                           // float                                                                        maxDepthBounds;
+       };
+
+       const VkColorComponentFlags colorComponentsAll = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+       const VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState =
+       {
+               m_blendEnable,                                          // VkBool32                                     blendEnable;
+               VK_BLEND_FACTOR_SRC_ALPHA,                      // VkBlendFactor                        srcColorBlendFactor;
+               VK_BLEND_FACTOR_ONE,                            // VkBlendFactor                        dstColorBlendFactor;
+               VK_BLEND_OP_ADD,                                        // VkBlendOp                            colorBlendOp;
+               VK_BLEND_FACTOR_SRC_ALPHA,                      // VkBlendFactor                        srcAlphaBlendFactor;
+               VK_BLEND_FACTOR_ONE,                            // VkBlendFactor                        dstAlphaBlendFactor;
+               VK_BLEND_OP_ADD,                                        // VkBlendOp                            alphaBlendOp;
+               colorComponentsAll,                                     // VkColorComponentFlags        colorWriteMask;
+       };
+
+       const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo =
+       {
+               VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,       // VkStructureType                                                              sType;
+               DE_NULL,                                                                                                        // const void*                                                                  pNext;
+               (VkPipelineColorBlendStateCreateFlags)0,                                        // VkPipelineColorBlendStateCreateFlags                 flags;
+               VK_FALSE,                                                                                                       // VkBool32                                                                             logicOpEnable;
+               VK_LOGIC_OP_COPY,                                                                                       // VkLogicOp                                                                    logicOp;
+               1u,                                                                                                                     // deUint32                                                                             attachmentCount;
+               &pipelineColorBlendAttachmentState,                                                     // const VkPipelineColorBlendAttachmentState*   pAttachments;
+               { 0.0f, 0.0f, 0.0f, 0.0f },                                                                     // float                                                                                blendConstants[4];
+       };
+
+       const VkGraphicsPipelineCreateInfo graphicsPipelineInfo =
+       {
+               VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,                                                // VkStructureType                                                                      sType;
+               DE_NULL,                                                                                                                                // const void*                                                                          pNext;
+               (VkPipelineCreateFlags)0,                                                                                               // VkPipelineCreateFlags                                                        flags;
+               static_cast<deUint32>(m_shaderStages.size()),                                                   // deUint32                                                                                     stageCount;
+               &m_shaderStages[0],                                                                                                             // const VkPipelineShaderStageCreateInfo*                       pStages;
+               &vertexInputStateInfo,                                                                                                  // const VkPipelineVertexInputStateCreateInfo*          pVertexInputState;
+               &pipelineInputAssemblyStateInfo,                                                                                // const VkPipelineInputAssemblyStateCreateInfo*        pInputAssemblyState;
+               (m_shaderStageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ? &pipelineTessellationStateInfo : DE_NULL), // const VkPipelineTessellationStateCreateInfo*             pTessellationState;
+               (isRasterizationDisabled ? DE_NULL : &pipelineViewportStateInfo),               // const VkPipelineViewportStateCreateInfo*                     pViewportState;
+               &pipelineRasterizationStateInfo,                                                                                // const VkPipelineRasterizationStateCreateInfo*        pRasterizationState;
+               (isRasterizationDisabled ? DE_NULL : &pipelineMultisampleStateInfo),    // const VkPipelineMultisampleStateCreateInfo*          pMultisampleState;
+               (isRasterizationDisabled ? DE_NULL : &pipelineDepthStencilStateInfo),   // const VkPipelineDepthStencilStateCreateInfo*         pDepthStencilState;
+               (isRasterizationDisabled ? DE_NULL : &pipelineColorBlendStateInfo),             // const VkPipelineColorBlendStateCreateInfo*           pColorBlendState;
+               DE_NULL,                                                                                                                                // const VkPipelineDynamicStateCreateInfo*                      pDynamicState;
+               pipelineLayout,                                                                                                                 // VkPipelineLayout                                                                     layout;
+               renderPass,                                                                                                                             // VkRenderPass                                                                         renderPass;
+               0u,                                                                                                                                             // deUint32                                                                                     subpass;
+               DE_NULL,                                                                                                                                // VkPipeline                                                                           basePipelineHandle;
+               0,                                                                                                                                              // deInt32                                                                                      basePipelineIndex;
+       };
+
+       return createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineInfo);
+}
+
+float getClampedTessLevel (const SpacingMode mode, const float tessLevel)
+{
+       switch (mode)
+       {
+               case SPACINGMODE_EQUAL:                         return de::max(1.0f, tessLevel);
+               case SPACINGMODE_FRACTIONAL_ODD:        return de::max(1.0f, tessLevel);
+               case SPACINGMODE_FRACTIONAL_EVEN:       return de::max(2.0f, tessLevel);
+               default:
+                       DE_ASSERT(false);
+                       return 0.0f;
+       }
+}
+
+int getRoundedTessLevel (const SpacingMode mode, const float clampedTessLevel)
+{
+       static const int MINIMUM_MAX_TESS_GEN_LEVEL = 64;       //!< Minimum maxTessellationGenerationLevel defined by the spec.
+
+       int result = (int)deFloatCeil(clampedTessLevel);
+
+       switch (mode)
+       {
+               case SPACINGMODE_EQUAL:                                                                                 break;
+               case SPACINGMODE_FRACTIONAL_ODD:        result += 1 - result % 2;       break;
+               case SPACINGMODE_FRACTIONAL_EVEN:       result += result % 2;           break;
+               default:
+                       DE_ASSERT(false);
+       }
+       DE_ASSERT(de::inRange<int>(result, 1, MINIMUM_MAX_TESS_GEN_LEVEL));
+
+       return result;
+}
+
+int getClampedRoundedTessLevel (const SpacingMode mode, const float tessLevel)
+{
+       return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel));
+}
+
+void getClampedRoundedTriangleTessLevels (const SpacingMode    spacingMode,
+                                                                                 const float*          innerSrc,
+                                                                                 const float*          outerSrc,
+                                                                                 int*                          innerDst,
+                                                                                 int*                          outerDst)
+{
+       innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]);
+       for (int i = 0; i < 3; i++)
+               outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
+}
+
+void getClampedRoundedQuadTessLevels (const SpacingMode spacingMode,
+                                                                         const float*          innerSrc,
+                                                                         const float*          outerSrc,
+                                                                         int*                          innerDst,
+                                                                         int*                          outerDst)
+{
+       for (int i = 0; i < 2; i++)
+               innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]);
+       for (int i = 0; i < 4; i++)
+               outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
+}
+
+void getClampedRoundedIsolineTessLevels (const SpacingMode     spacingMode,
+                                                                                const float*           outerSrc,
+                                                                                int*                           outerDst)
+{
+       outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL,     outerSrc[0]);
+       outerDst[1] = getClampedRoundedTessLevel(spacingMode,           outerSrc[1]);
+}
+
+int numOuterTessellationLevels (const TessPrimitiveType primType)
+{
+       switch (primType)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:       return 3;
+               case TESSPRIMITIVETYPE_QUADS:           return 4;
+               case TESSPRIMITIVETYPE_ISOLINES:        return 2;
+               default:
+                       DE_ASSERT(false);
+                       return 0;
+       }
+}
+
+bool isPatchDiscarded (const TessPrimitiveType primitiveType, const float* outerLevels)
+{
+       const int numOuterLevels = numOuterTessellationLevels(primitiveType);
+       for (int i = 0; i < numOuterLevels; i++)
+               if (outerLevels[i] <= 0.0f)
+                       return true;
+       return false;
+}
+
+std::string getTessellationLevelsString (const TessLevels& tessLevels, const TessPrimitiveType primitiveType)
+{
+       std::ostringstream str;
+       switch (primitiveType)
+       {
+               case TESSPRIMITIVETYPE_ISOLINES:
+                       str << "inner: { }, "
+                               << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << " }";
+                       break;
+
+               case TESSPRIMITIVETYPE_TRIANGLES:
+                       str << "inner: { " << tessLevels.inner[0] << " }, "
+                               << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << ", " << tessLevels.outer[2] << " }";
+                       break;
+
+               case TESSPRIMITIVETYPE_QUADS:
+                       str << "inner: { " << tessLevels.inner[0] << ", " << tessLevels.inner[1] << " }, "
+                               << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << ", " << tessLevels.outer[2] << ", " << tessLevels.outer[3] << " }";
+                       break;
+
+               default:
+                       DE_ASSERT(false);
+       }
+
+       return str.str();
+}
+
+//! Assumes array sizes inner[2] and outer[4].
+std::string getTessellationLevelsString (const float* inner, const float* outer)
+{
+       const TessLevels tessLevels =
+       {
+               { inner[0], inner[1] },
+               { outer[0], outer[1], outer[2], outer[3] }
+       };
+       return getTessellationLevelsString(tessLevels, TESSPRIMITIVETYPE_QUADS);
+}
+
+// \note The tessellation coordinates generated by this function could break some of the rules given in the spec
+// (e.g. it may not exactly hold that u+v+w == 1.0f, or [uvw] + (1.0f-[uvw]) == 1.0f).
+std::vector<tcu::Vec3> generateReferenceTriangleTessCoords (const SpacingMode  spacingMode,
+                                                                                                                       const int                       inner,
+                                                                                                                       const int                       outer0,
+                                                                                                                       const int                       outer1,
+                                                                                                                       const int                       outer2)
+{
+       std::vector<tcu::Vec3> tessCoords;
+
+       if (inner == 1)
+       {
+               if (outer0 == 1 && outer1 == 1 && outer2 == 1)
+               {
+                       tessCoords.push_back(tcu::Vec3(1.0f, 0.0f, 0.0f));
+                       tessCoords.push_back(tcu::Vec3(0.0f, 1.0f, 0.0f));
+                       tessCoords.push_back(tcu::Vec3(0.0f, 0.0f, 1.0f));
+                       return tessCoords;
+               }
+               else
+                       return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+                                                                                                          outer0, outer1, outer2);
+       }
+       else
+       {
+               for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(tcu::Vec3(    0.0f,        v, 1.0f - v)); }
+               for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(tcu::Vec3(1.0f - v,     0.0f,        v)); }
+               for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(tcu::Vec3(       v, 1.0f - v,     0.0f)); }
+
+               const int numInnerTriangles = inner/2;
+               for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
+               {
+                       const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
+
+                       if (curInnerTriangleLevel == 0)
+                               tessCoords.push_back(tcu::Vec3(1.0f/3.0f));
+                       else
+                       {
+                               const float             minUVW          = (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner);
+                               const float             maxUVW          = 1.0f - 2.0f*minUVW;
+                               const tcu::Vec3 corners[3]      =
+                               {
+                                       tcu::Vec3(maxUVW, minUVW, minUVW),
+                                       tcu::Vec3(minUVW, maxUVW, minUVW),
+                                       tcu::Vec3(minUVW, minUVW, maxUVW)
+                               };
+
+                               for (int i = 0; i < curInnerTriangleLevel; i++)
+                               {
+                                       const float f = (float)i / (float)curInnerTriangleLevel;
+                                       for (int j = 0; j < 3; j++)
+                                               tessCoords.push_back((1.0f - f)*corners[j] + f*corners[(j+1)%3]);
+                               }
+                       }
+               }
+
+               return tessCoords;
+       }
+}
+
+// \note The tessellation coordinates generated by this function could break some of the rules given in the spec
+// (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
+std::vector<tcu::Vec3> generateReferenceQuadTessCoords (const SpacingMode      spacingMode,
+                                                                                                               const int                       inner0,
+                                                                                                               const int                       inner1,
+                                                                                                               const int                       outer0,
+                                                                                                               const int                       outer1,
+                                                                                                               const int                       outer2,
+                                                                                                               const int                       outer3)
+{
+       std::vector<tcu::Vec3> tessCoords;
+
+       if (inner0 == 1 || inner1 == 1)
+       {
+               if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
+               {
+                       tessCoords.push_back(tcu::Vec3(0.0f, 0.0f, 0.0f));
+                       tessCoords.push_back(tcu::Vec3(1.0f, 0.0f, 0.0f));
+                       tessCoords.push_back(tcu::Vec3(0.0f, 1.0f, 0.0f));
+                       tessCoords.push_back(tcu::Vec3(1.0f, 1.0f, 0.0f));
+                       return tessCoords;
+               }
+               else
+                       return generateReferenceQuadTessCoords(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+                                                                                                                               inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+                                                                                                                               outer0, outer1, outer2, outer3);
+       }
+       else
+       {
+               for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(tcu::Vec3(    0.0f,        v, 0.0f)); }
+               for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(tcu::Vec3(1.0f - v,     0.0f, 0.0f)); }
+               for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(tcu::Vec3(    1.0f, 1.0f - v, 0.0f)); }
+               for (int i = 0; i < outer3; i++) { const float v = (float)i / (float)outer3; tessCoords.push_back(tcu::Vec3(       v,     1.0f, 0.0f)); }
+
+               for (int innerVtxY = 0; innerVtxY < inner1-1; innerVtxY++)
+               for (int innerVtxX = 0; innerVtxX < inner0-1; innerVtxX++)
+                       tessCoords.push_back(tcu::Vec3((float)(innerVtxX + 1) / (float)inner0,
+                                                                                  (float)(innerVtxY + 1) / (float)inner1,
+                                                                                  0.0f));
+
+               return tessCoords;
+       }
+}
+
+// \note The tessellation coordinates generated by this function could break some of the rules given in the spec
+// (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
+std::vector<tcu::Vec3> generateReferenceIsolineTessCoords (const int outer0, const int outer1)
+{
+       std::vector<tcu::Vec3> tessCoords;
+
+       for (int y = 0; y < outer0;   y++)
+       for (int x = 0; x < outer1+1; x++)
+               tessCoords.push_back(tcu::Vec3((float)x / (float)outer1,
+                                                                          (float)y / (float)outer0,
+                                                                          0.0f));
+
+       return tessCoords;
+}
+
+static int referencePointModePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
+{
+       if (isPatchDiscarded(primitiveType, outerLevels))
+               return 0;
+
+       switch (primitiveType)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:
+               {
+                       int inner;
+                       int outer[3];
+                       getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
+                       return static_cast<int>(generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size());
+               }
+
+               case TESSPRIMITIVETYPE_QUADS:
+               {
+                       int inner[2];
+                       int outer[4];
+                       getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
+                       return static_cast<int>(generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]).size());
+               }
+
+               case TESSPRIMITIVETYPE_ISOLINES:
+               {
+                       int outer[2];
+                       getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
+                       return static_cast<int>(generateReferenceIsolineTessCoords(outer[0], outer[1]).size());
+               }
+
+               default:
+                       DE_ASSERT(false);
+                       return 0;
+       }
+}
+
+static int referenceTriangleNonPointModePrimitiveCount (const SpacingMode spacingMode, const int inner, const int outer0, const int outer1, const int outer2)
+{
+       if (inner == 1)
+       {
+               if (outer0 == 1 && outer1 == 1 && outer2 == 1)
+                       return 1;
+               else
+                       return referenceTriangleNonPointModePrimitiveCount(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+                                                                                                                                                       outer0, outer1, outer2);
+       }
+       else
+       {
+               int result = outer0 + outer1 + outer2;
+
+               const int numInnerTriangles = inner/2;
+               for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
+               {
+                       const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
+
+                       if (curInnerTriangleLevel == 1)
+                               result += 4;
+                       else
+                               result += 2*3*curInnerTriangleLevel;
+               }
+
+               return result;
+       }
+}
+
+static int referenceQuadNonPointModePrimitiveCount (const SpacingMode spacingMode, const int inner0, const int inner1, const int outer0, const int outer1, const int outer2, const int outer3)
+{
+       if (inner0 == 1 || inner1 == 1)
+       {
+               if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
+                       return 2;
+               else
+                       return referenceQuadNonPointModePrimitiveCount(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+                                                                                                                                               inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+                                                                                                                                               outer0, outer1, outer2, outer3);
+       }
+       else
+               return 2*(inner0-2)*(inner1-2) + 2*(inner0-2) + 2*(inner1-2) + outer0+outer1+outer2+outer3;
+}
+
+static inline int referenceIsolineNonPointModePrimitiveCount (const int outer0, const int outer1)
+{
+       return outer0*outer1;
+}
+
+static int referenceNonPointModePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
+{
+       if (isPatchDiscarded(primitiveType, outerLevels))
+               return 0;
+
+       switch (primitiveType)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:
+               {
+                       int inner;
+                       int outer[3];
+                       getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
+                       return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]);
+               }
+
+               case TESSPRIMITIVETYPE_QUADS:
+               {
+                       int inner[2];
+                       int outer[4];
+                       getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
+                       return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
+               }
+
+               case TESSPRIMITIVETYPE_ISOLINES:
+               {
+                       int outer[2];
+                       getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
+                       return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]);
+               }
+
+               default:
+                       DE_ASSERT(false);
+                       return 0;
+       }
+}
+
+int numVerticesPerPrimitive (const TessPrimitiveType primitiveType, const bool usePointMode)
+{
+       if (usePointMode)
+               return 1;
+
+       switch (primitiveType)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:       return 3;
+               case TESSPRIMITIVETYPE_QUADS:           return 3;  // quads are composed of two triangles
+               case TESSPRIMITIVETYPE_ISOLINES:        return 2;
+               default:
+                       DE_ASSERT(false);
+                       return 0;
+       }
+}
+
+int referencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels)
+{
+       return usePointMode ? referencePointModePrimitiveCount          (primitiveType, spacingMode, innerLevels, outerLevels)
+                                               : referenceNonPointModePrimitiveCount   (primitiveType, spacingMode, innerLevels, outerLevels);
+}
+
+//! In point mode this should return the number of unique vertices, while in non-point mode the maximum theoretical number of verticies.
+//! Actual implementation will likely return a much smaller number because the shader isn't required to be run for duplicate coordinates.
+int referenceVertexCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels)
+{
+       return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels)
+                  * numVerticesPerPrimitive(primitiveType, usePointMode);
+}
+
+void requireFeatures (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const FeatureFlags flags)
+{
+       const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
+
+       if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader)
+               throw tcu::NotSupportedError("Tessellation shader not supported");
+
+       if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader)
+               throw tcu::NotSupportedError("Geometry shader not supported");
+
+       if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64)
+               throw tcu::NotSupportedError("Double-precision floats not supported");
+
+       if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics)
+               throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline");
+
+       if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics)
+               throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader");
+
+       if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) && !features.shaderTessellationAndGeometryPointSize)
+               throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in");
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.hpp
new file mode 100644 (file)
index 0000000..552e2f5
--- /dev/null
@@ -0,0 +1,394 @@
+#ifndef _VKTTESSELLATIONUTIL_HPP
+#define _VKTTESSELLATIONUTIL_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Utilities
+ *//*--------------------------------------------------------------------*/
+
+#include "vkDefs.hpp"
+#include "vkMemUtil.hpp"
+#include "vkRef.hpp"
+#include "vkPrograms.hpp"
+#include "vkRefUtil.hpp"
+#include "vkQueryUtil.hpp"
+
+#include "tcuVector.hpp"
+
+#include "deStringUtil.hpp"
+
+#include <algorithm>  // sort
+#include <iterator>   // distance
+
+namespace vkt
+{
+namespace tessellation
+{
+
+class Buffer
+{
+public:
+                                                                               Buffer                  (const vk::DeviceInterface&             vk,
+                                                                                                                const vk::VkDevice                             device,
+                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                const vk::VkBufferCreateInfo&  bufferCreateInfo,
+                                                                                                                const vk::MemoryRequirement    memoryRequirement)
+
+                                                                                       : m_buffer              (createBuffer(vk, device, &bufferCreateInfo))
+                                                                                       , m_allocation  (allocator.allocate(getBufferMemoryRequirements(vk, device, *m_buffer), memoryRequirement))
+                                                                               {
+                                                                                       VK_CHECK(vk.bindBufferMemory(device, *m_buffer, m_allocation->getMemory(), m_allocation->getOffset()));
+                                                                               }
+
+       const vk::VkBuffer&                                     get                             (void) const { return *m_buffer; }
+       const vk::VkBuffer&                                     operator*               (void) const { return get(); }
+       vk::Allocation&                                         getAllocation   (void) const { return *m_allocation; }
+
+private:
+       const vk::Unique<vk::VkBuffer>          m_buffer;
+       const de::UniquePtr<vk::Allocation>     m_allocation;
+
+       // "deleted"
+                                                                               Buffer                  (const Buffer&);
+       Buffer&                                                         operator=               (const Buffer&);
+};
+
+class Image
+{
+public:
+                                                                               Image                   (const vk::DeviceInterface&             vk,
+                                                                                                                const vk::VkDevice                             device,
+                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                const vk::VkImageCreateInfo&   imageCreateInfo,
+                                                                                                                const vk::MemoryRequirement    memoryRequirement)
+
+                                                                                       : m_image               (createImage(vk, device, &imageCreateInfo))
+                                                                                       , m_allocation  (allocator.allocate(getImageMemoryRequirements(vk, device, *m_image), memoryRequirement))
+                                                                               {
+                                                                                       VK_CHECK(vk.bindImageMemory(device, *m_image, m_allocation->getMemory(), m_allocation->getOffset()));
+                                                                               }
+
+       const vk::VkImage&                                      get                             (void) const { return *m_image; }
+       const vk::VkImage&                                      operator*               (void) const { return get(); }
+       vk::Allocation&                                         getAllocation   (void) const { return *m_allocation; }
+
+private:
+       const vk::Unique<vk::VkImage>           m_image;
+       const de::UniquePtr<vk::Allocation>     m_allocation;
+
+       // "deleted"
+                                                                               Image                   (const Image&);
+       Image&                                                          operator=               (const Image&);
+};
+
+class GraphicsPipelineBuilder
+{
+public:
+                                                               GraphicsPipelineBuilder (void) : m_renderSize                   (0, 0)
+                                                                                                                          , m_shaderStageFlags         (0u)
+                                                                                                                          , m_cullModeFlags            (vk::VK_CULL_MODE_NONE)
+                                                                                                                          , m_frontFace                        (vk::VK_FRONT_FACE_COUNTER_CLOCKWISE)
+                                                                                                                          , m_patchControlPoints       (1u)
+                                                                                                                          , m_blendEnable                      (false)
+                                                                                                                          , m_primitiveTopology        (vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) {}
+
+       GraphicsPipelineBuilder&        setRenderSize                                   (const tcu::IVec2& size) { m_renderSize = size; return *this; }
+       GraphicsPipelineBuilder&        setShader                                               (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkShaderStageFlagBits stage, const vk::ProgramBinary& binary, const vk::VkSpecializationInfo* specInfo);
+       GraphicsPipelineBuilder&        setPatchControlPoints                   (const deUint32 controlPoints) { m_patchControlPoints = controlPoints; return *this; }
+       GraphicsPipelineBuilder&        setCullModeFlags                                (const vk::VkCullModeFlags cullModeFlags) { m_cullModeFlags = cullModeFlags; return *this; }
+       GraphicsPipelineBuilder&        setFrontFace                                    (const vk::VkFrontFace frontFace) { m_frontFace = frontFace; return *this; }
+       GraphicsPipelineBuilder&        setBlend                                                (const bool enable) { m_blendEnable = enable; return *this; }
+
+       //! Applies only to pipelines without tessellation shaders.
+       GraphicsPipelineBuilder&        setPrimitiveTopology                    (const vk::VkPrimitiveTopology topology) { m_primitiveTopology = topology; return *this; }
+
+       GraphicsPipelineBuilder&        addVertexBinding                                (const vk::VkVertexInputBindingDescription vertexBinding) { m_vertexInputBindings.push_back(vertexBinding); return *this; }
+       GraphicsPipelineBuilder&        addVertexAttribute                              (const vk::VkVertexInputAttributeDescription vertexAttribute) { m_vertexInputAttributes.push_back(vertexAttribute); return *this; }
+
+       //! Basic vertex input configuration (uses biding 0, location 0, etc.)
+       GraphicsPipelineBuilder&        setVertexInputSingleAttribute   (const vk::VkFormat vertexFormat, const deUint32 stride);
+
+       vk::Move<vk::VkPipeline>        build                                                   (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkRenderPass renderPass);
+
+private:
+       tcu::IVec2                                                                                      m_renderSize;
+       vk::Move<vk::VkShaderModule>                                            m_vertexShaderModule;
+       vk::Move<vk::VkShaderModule>                                            m_fragmentShaderModule;
+       vk::Move<vk::VkShaderModule>                                            m_geometryShaderModule;
+       vk::Move<vk::VkShaderModule>                                            m_tessControlShaderModule;
+       vk::Move<vk::VkShaderModule>                                            m_tessEvaluationShaderModule;
+       std::vector<vk::VkPipelineShaderStageCreateInfo>        m_shaderStages;
+       std::vector<vk::VkVertexInputBindingDescription>        m_vertexInputBindings;
+       std::vector<vk::VkVertexInputAttributeDescription>      m_vertexInputAttributes;
+       vk::VkShaderStageFlags                                                          m_shaderStageFlags;
+       vk::VkCullModeFlags                                                                     m_cullModeFlags;
+       vk::VkFrontFace                                                                         m_frontFace;
+       deUint32                                                                                        m_patchControlPoints;
+       bool                                                                                            m_blendEnable;
+       vk::VkPrimitiveTopology                                                         m_primitiveTopology;
+
+       GraphicsPipelineBuilder (const GraphicsPipelineBuilder&); // "deleted"
+       GraphicsPipelineBuilder& operator= (const GraphicsPipelineBuilder&);
+};
+
+struct TessLevels
+{
+       float inner[2];
+       float outer[4];
+};
+
+enum TessPrimitiveType
+{
+       TESSPRIMITIVETYPE_TRIANGLES = 0,
+       TESSPRIMITIVETYPE_QUADS,
+       TESSPRIMITIVETYPE_ISOLINES,
+
+       TESSPRIMITIVETYPE_LAST,
+};
+
+enum SpacingMode
+{
+       SPACINGMODE_EQUAL = 0,
+       SPACINGMODE_FRACTIONAL_ODD,
+       SPACINGMODE_FRACTIONAL_EVEN,
+
+       SPACINGMODE_LAST,
+};
+
+enum Winding
+{
+       WINDING_CCW = 0,
+       WINDING_CW,
+
+       WINDING_LAST,
+};
+
+enum FeatureFlagBits
+{
+       FEATURE_TESSELLATION_SHADER                                                     = 1u << 0,
+       FEATURE_GEOMETRY_SHADER                                                         = 1u << 1,
+       FEATURE_SHADER_FLOAT_64                                                         = 1u << 2,
+       FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS                      = 1u << 3,
+       FEATURE_FRAGMENT_STORES_AND_ATOMICS                                     = 1u << 4,
+       FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE     = 1u << 5,
+};
+typedef deUint32 FeatureFlags;
+
+vk::VkBufferCreateInfo                 makeBufferCreateInfo                                            (const vk::VkDeviceSize bufferSize, const vk::VkBufferUsageFlags usage);
+vk::VkImageCreateInfo                  makeImageCreateInfo                                                     (const tcu::IVec2& size, const vk::VkFormat format, const vk::VkImageUsageFlags usage, const deUint32 numArrayLayers);
+vk::Move<vk::VkCommandPool>            makeCommandPool                                                         (const vk::DeviceInterface& vk, const vk::VkDevice device, const deUint32 queueFamilyIndex);
+vk::Move<vk::VkCommandBuffer>  makeCommandBuffer                                                       (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkCommandPool commandPool);
+vk::Move<vk::VkDescriptorSet>  makeDescriptorSet                                                       (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkDescriptorPool descriptorPool, const vk::VkDescriptorSetLayout setLayout);
+vk::Move<vk::VkPipelineLayout> makePipelineLayout                                                      (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkDescriptorSetLayout descriptorSetLayout);
+vk::Move<vk::VkPipelineLayout> makePipelineLayoutWithoutDescriptors            (const vk::DeviceInterface& vk, const vk::VkDevice device);
+vk::Move<vk::VkPipeline>               makeComputePipeline                                                     (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkShaderModule shaderModule, const vk::VkSpecializationInfo* specInfo);
+vk::Move<vk::VkRenderPass>             makeRenderPass                                                          (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkFormat colorFormat);
+vk::Move<vk::VkRenderPass>             makeRenderPassWithoutAttachments                        (const vk::DeviceInterface& vk, const vk::VkDevice device);
+vk::Move<vk::VkFramebuffer>            makeFramebuffer                                                         (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkRenderPass renderPass, const vk::VkImageView colorAttachment, const deUint32 width, const deUint32 height, const deUint32 layers);
+vk::Move<vk::VkFramebuffer>            makeFramebufferWithoutAttachments                       (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkRenderPass renderPass);
+vk::Move<vk::VkImageView>              makeImageView                                                           (const vk::DeviceInterface& vk, const vk::VkDevice vkDevice, const vk::VkImage image, const vk::VkImageViewType viewType, const vk::VkFormat format, const vk::VkImageSubresourceRange subresourceRange);
+vk::VkBufferImageCopy                  makeBufferImageCopy                                                     (const vk::VkExtent3D extent, const vk::VkImageSubresourceLayers subresourceLayers);
+vk::VkBufferMemoryBarrier              makeBufferMemoryBarrier                                         (const vk::VkAccessFlags srcAccessMask, const vk::VkAccessFlags dstAccessMask, const vk::VkBuffer buffer, const vk::VkDeviceSize offset, const vk::VkDeviceSize bufferSizeBytes);
+vk::VkImageMemoryBarrier               makeImageMemoryBarrier                                          (const vk::VkAccessFlags srcAccessMask, const vk::VkAccessFlags dstAccessMask, const vk::VkImageLayout oldLayout, const vk::VkImageLayout newLayout, const vk::VkImage image, const vk::VkImageSubresourceRange subresourceRange);
+
+void                                                   beginCommandBuffer                                                      (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer);
+void                                                   endCommandBuffer                                                        (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer);
+void                                                   submitCommandsAndWait                                           (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkQueue queue, const vk::VkCommandBuffer commandBuffer);
+void                                                   beginRenderPass                                                         (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer, const vk::VkRenderPass renderPass, const vk::VkFramebuffer framebuffer, const vk::VkRect2D& renderArea, const tcu::Vec4& clearColor);
+void                                                   beginRenderPassWithRasterizationDisabled        (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer, const vk::VkRenderPass renderPass, const vk::VkFramebuffer framebuffer);
+void                                                   endRenderPass                                                           (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer);
+void                                                   requireFeatures                                                         (const vk::InstanceInterface& vki, const vk::VkPhysicalDevice physDevice, const FeatureFlags flags);
+
+float                                                  getClampedTessLevel                                                     (const SpacingMode mode, const float tessLevel);
+int                                                            getRoundedTessLevel                                                     (const SpacingMode mode, const float clampedTessLevel);
+int                                                            getClampedRoundedTessLevel                                      (const SpacingMode mode, const float tessLevel);
+void                                                   getClampedRoundedTriangleTessLevels                     (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst);
+void                                                   getClampedRoundedQuadTessLevels                         (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst);
+void                                                   getClampedRoundedIsolineTessLevels                      (const SpacingMode mode, const float* outerSrc, int* outerDst);
+int                                                            numOuterTessellationLevels                                      (const TessPrimitiveType primitiveType);
+std::string                                            getTessellationLevelsString                                     (const TessLevels& tessLevels, const TessPrimitiveType primitiveType);
+std::string                                            getTessellationLevelsString                                     (const float* inner, const float* outer);
+bool                                                   isPatchDiscarded                                                        (const TessPrimitiveType primitiveType, const float* outerLevels);
+std::vector<tcu::Vec3>                 generateReferenceTriangleTessCoords                     (const SpacingMode spacingMode, const int inner, const int outer0, const int outer1, const int outer2);
+std::vector<tcu::Vec3>                 generateReferenceQuadTessCoords                         (const SpacingMode spacingMode, const int inner0, const int inner1, const int outer0, const int outer1, const int outer2, const int outer3);
+std::vector<tcu::Vec3>                 generateReferenceIsolineTessCoords                      (const int outer0, const int outer1);
+int                                                            referenceVertexCount                                            (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels);
+int                                                            referencePrimitiveCount                                         (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels);
+int                                                            numVerticesPerPrimitive                                         (const TessPrimitiveType primitiveType, const bool usePointMode);
+
+static inline const char* getTessPrimitiveTypeShaderName (const TessPrimitiveType type)
+{
+       switch (type)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:       return "triangles";
+               case TESSPRIMITIVETYPE_QUADS:           return "quads";
+               case TESSPRIMITIVETYPE_ISOLINES:        return "isolines";
+               default:
+                       DE_ASSERT(false);
+                       return DE_NULL;
+       }
+}
+
+static inline const char* getSpacingModeShaderName (SpacingMode mode)
+{
+       switch (mode)
+       {
+               case SPACINGMODE_EQUAL:                         return "equal_spacing";
+               case SPACINGMODE_FRACTIONAL_ODD:        return "fractional_odd_spacing";
+               case SPACINGMODE_FRACTIONAL_EVEN:       return "fractional_even_spacing";
+               default:
+                       DE_ASSERT(false);
+                       return DE_NULL;
+       }
+}
+
+static inline const char* getWindingShaderName (const Winding winding)
+{
+       switch (winding)
+       {
+               case WINDING_CCW:       return "ccw";
+               case WINDING_CW:        return "cw";
+               default:
+                       DE_ASSERT(false);
+                       return DE_NULL;
+       }
+}
+
+static inline const char* getGeometryShaderInputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode)
+{
+       if (usePointMode)
+               return "points";
+
+       switch (type)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:
+               case TESSPRIMITIVETYPE_QUADS:
+                       return "triangles";
+
+               case TESSPRIMITIVETYPE_ISOLINES:
+                       return "lines";
+
+               default:
+                       DE_ASSERT(false);
+                       return DE_NULL;
+       }
+}
+
+static inline const char* getGeometryShaderOutputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode)
+{
+       if (usePointMode)
+               return "points";
+
+       switch (type)
+       {
+               case TESSPRIMITIVETYPE_TRIANGLES:
+               case TESSPRIMITIVETYPE_QUADS:
+                       return "triangle_strip";
+
+               case TESSPRIMITIVETYPE_ISOLINES:
+                       return "line_strip";
+
+               default:
+                       DE_ASSERT(false);
+                       return DE_NULL;
+       }
+}
+
+template<typename T>
+inline std::size_t sizeInBytes (const std::vector<T>& vec)
+{
+       return vec.size() * sizeof(vec[0]);
+}
+
+template <typename T>
+static std::vector<T> sorted (const std::vector<T>& unsorted)
+{
+       std::vector<T> result = unsorted;
+       std::sort(result.begin(), result.end());
+       return result;
+}
+
+template <typename T, typename P>
+static std::vector<T> sorted (const std::vector<T>& unsorted, P pred)
+{
+       std::vector<T> result = unsorted;
+       std::sort(result.begin(), result.end(), pred);
+       return result;
+}
+
+template <typename IterT>
+std::string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
+{
+       const int                       bigInt                  = ~0u/2;
+       const std::string       baseIndentation = std::string(numIndentationSpaces, ' ');
+       const std::string       deepIndentation = baseIndentation + std::string(4, ' ');
+       const int                       wrapLength              = wrapLengthParam > 0 ? wrapLengthParam : bigInt;
+       const int                       length                  = static_cast<int>(std::distance(begin, end));
+       std::string                     result;
+
+       if (length > wrapLength)
+               result += "(amount: " + de::toString(length) + ") ";
+       result += std::string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " ");
+
+       {
+               int index = 0;
+               for (IterT it = begin; it != end; ++it)
+               {
+                       if (it != begin)
+                               result += std::string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : "");
+                       result += de::toString(*it);
+                       index++;
+               }
+
+               result += length > wrapLength ? "\n"+baseIndentation : " ";
+       }
+
+       result += "}";
+       return result;
+}
+
+template <typename ContainerT>
+std::string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
+{
+       return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
+}
+
+//! Copy 'count' objects of type T from 'memory' into a vector.
+//! 'offset' is the offset of first object in memory, and 'stride' is the distance between consecutive objects.
+template<typename T>
+std::vector<T> readInterleavedData (const int count, const void* memory, const int offset, const int stride)
+{
+       std::vector<T> results(count);
+       const deUint8* pData = static_cast<const deUint8*>(memory) + offset;
+
+       for (int i = 0; i < count; ++i)
+       {
+               deMemcpy(&results[i], pData, sizeof(T));
+               pData += stride;
+       }
+
+       return results;
+}
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONUTIL_HPP
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationWindingTests.cpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationWindingTests.cpp
new file mode 100644 (file)
index 0000000..883c0c3
--- /dev/null
@@ -0,0 +1,437 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Winding Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationWindingTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuRGBA.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+std::string getCaseName (const TessPrimitiveType primitiveType, const Winding winding)
+{
+       std::ostringstream str;
+       str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getWindingShaderName(winding);
+       return str.str();
+}
+
+inline VkFrontFace mapFrontFace (const Winding winding)
+{
+       switch (winding)
+       {
+               case WINDING_CCW:       return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+               case WINDING_CW:        return VK_FRONT_FACE_CLOCKWISE;
+               default:
+                       DE_ASSERT(false);
+                       return VK_FRONT_FACE_LAST;
+       }
+}
+
+//! Returns true when the image passes the verification.
+bool verifyResultImage (tcu::TestLog&                                          log,
+                                               const tcu::ConstPixelBufferAccess       image,
+                                               const TessPrimitiveType                         primitiveType,
+                                               const Winding                                           winding,
+                                               const Winding                                           frontFaceWinding)
+{
+       const int totalNumPixels        = image.getWidth()*image.getHeight();
+       const int badPixelTolerance = (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(image.getWidth(), image.getHeight()) : 0);
+
+       const tcu::Vec4 white = tcu::RGBA::white().toVec();
+       const tcu::Vec4 red   = tcu::RGBA::red().toVec();
+
+       int numWhitePixels = 0;
+       int numRedPixels   = 0;
+       for (int y = 0; y < image.getHeight();  y++)
+       for (int x = 0; x < image.getWidth();   x++)
+       {
+               numWhitePixels += image.getPixel(x, y) == white ? 1 : 0;
+               numRedPixels   += image.getPixel(x, y) == red   ? 1 : 0;
+       }
+
+       DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
+
+       log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << tcu::TestLog::EndMessage;
+
+       const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels;
+       if (otherPixels > badPixelTolerance)
+       {
+               log << tcu::TestLog::Message
+                       << "Failure: Got " << otherPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")"
+                       << tcu::TestLog::EndMessage;
+               return false;
+       }
+
+       if (frontFaceWinding == winding)
+       {
+               if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+               {
+                       if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
+                       {
+                               log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << tcu::TestLog::EndMessage;
+                               return false;
+                       }
+               }
+               else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
+               {
+                       if (numWhitePixels != totalNumPixels)
+                       {
+                               log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << tcu::TestLog::EndMessage;
+                               return false;
+                       }
+               }
+               else
+                       DE_ASSERT(false);
+       }
+       else
+       {
+               if (numWhitePixels != 0)
+               {
+                       log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)" << tcu::TestLog::EndMessage;
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+class WindingTest : public TestCase
+{
+public:
+                                                               WindingTest             (tcu::TestContext&                      testCtx,
+                                                                                                const TessPrimitiveType        primitiveType,
+                                                                                                const Winding                          winding);
+
+       void                                            initPrograms    (SourceCollections&                     programCollection) const;
+       TestInstance*                           createInstance  (Context&                                       context) const;
+
+private:
+       const TessPrimitiveType         m_primitiveType;
+       const Winding                           m_winding;
+};
+
+WindingTest::WindingTest (tcu::TestContext&                    testCtx,
+                                                 const TessPrimitiveType       primitiveType,
+                                                 const Winding                         winding)
+       : TestCase                      (testCtx, getCaseName(primitiveType, winding), "")
+       , m_primitiveType       (primitiveType)
+       , m_winding                     (winding)
+{
+}
+
+void WindingTest::initPrograms (SourceCollections& programCollection) const
+{
+       // Vertex shader - no inputs
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+       }
+
+       // Tessellation control shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(vertices = 1) out;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    gl_TessLevelInner[0] = 5.0;\n"
+                       << "    gl_TessLevelInner[1] = 5.0;\n"
+                       << "\n"
+                       << "    gl_TessLevelOuter[0] = 5.0;\n"
+                       << "    gl_TessLevelOuter[1] = 5.0;\n"
+                       << "    gl_TessLevelOuter[2] = 5.0;\n"
+                       << "    gl_TessLevelOuter[3] = 5.0;\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+       }
+
+       // Tessellation evaluation shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "#extension GL_EXT_tessellation_shader : require\n"
+                       << "\n"
+                       << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
+                                                << getWindingShaderName(m_winding) << ") in;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+       }
+
+       // Fragment shader
+       {
+               std::ostringstream src;
+               src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+                       << "\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    o_color = vec4(1.0);\n"
+                       << "}\n";
+
+               programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+       }
+}
+
+class WindingTestInstance : public TestInstance
+{
+public:
+                                                               WindingTestInstance (Context&                                   context,
+                                                                                                        const TessPrimitiveType        primitiveType,
+                                                                                                        const Winding                          winding);
+
+       tcu::TestStatus                         iterate                         (void);
+
+private:
+       const TessPrimitiveType         m_primitiveType;
+       const Winding                           m_winding;
+};
+
+WindingTestInstance::WindingTestInstance (Context&                                     context,
+                                                                                 const TessPrimitiveType       primitiveType,
+                                                                                 const Winding                         winding)
+       : TestInstance          (context)
+       , m_primitiveType       (primitiveType)
+       , m_winding                     (winding)
+{
+}
+
+tcu::TestStatus WindingTestInstance::iterate (void)
+{
+       const DeviceInterface&  vk                                      = m_context.getDeviceInterface();
+       const VkDevice                  device                          = m_context.getDevice();
+       const VkQueue                   queue                           = m_context.getUniversalQueue();
+       const deUint32                  queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       Allocator&                              allocator                       = m_context.getDefaultAllocator();
+
+       // Color attachment
+
+       const tcu::IVec2                          renderSize                             = tcu::IVec2(64, 64);
+       const VkFormat                            colorFormat                            = VK_FORMAT_R8G8B8A8_UNORM;
+       const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+       const Image                                       colorAttachmentImage           (vk, device, allocator,
+                                                                                                                        makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+                                                                                                                        MemoryRequirement::Any);
+
+       // Color output buffer: image will be copied here for verification
+
+       const VkDeviceSize      colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+       const Buffer            colorBuffer                      (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+       // Pipeline
+
+       const Unique<VkImageView>               colorAttachmentView(makeImageView                       (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+       const Unique<VkRenderPass>              renderPass         (makeRenderPass                      (vk, device, colorFormat));
+       const Unique<VkFramebuffer>             framebuffer        (makeFramebuffer                     (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+       const Unique<VkPipelineLayout>  pipelineLayout     (makePipelineLayoutWithoutDescriptors(vk, device));
+
+       const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
+
+       // Front face is static state, so we have to create two pipelines.
+
+       const Unique<VkPipeline> pipelineCounterClockwise(GraphicsPipelineBuilder()
+               .setRenderSize   (renderSize)
+               .setCullModeFlags(cullMode)
+               .setFrontFace    (VK_FRONT_FACE_COUNTER_CLOCKWISE)
+               .setShader               (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader               (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                                m_context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                   (vk, device, *pipelineLayout, *renderPass));
+
+       const Unique<VkPipeline> pipelineClockwise(GraphicsPipelineBuilder()
+               .setRenderSize   (renderSize)
+               .setCullModeFlags(cullMode)
+               .setFrontFace    (VK_FRONT_FACE_CLOCKWISE)
+               .setShader               (vk, device, VK_SHADER_STAGE_VERTEX_BIT,                                  m_context.getBinaryCollection().get("vert"), DE_NULL)
+               .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,    m_context.getBinaryCollection().get("tesc"), DE_NULL)
+               .setShader               (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+               .setShader               (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT,                                m_context.getBinaryCollection().get("frag"), DE_NULL)
+               .build                   (vk, device, *pipelineLayout, *renderPass));
+
+       const struct // not static
+       {
+               Winding         frontFaceWinding;
+               VkPipeline      pipeline;
+       } testCases[] =
+       {
+               { WINDING_CCW,  *pipelineCounterClockwise },
+               { WINDING_CW,   *pipelineClockwise                },
+       };
+
+       tcu::TestLog& log = m_context.getTestContext().getLog();
+       log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage;
+
+       bool success = true;
+
+       // Draw commands
+
+       const Unique<VkCommandPool>   cmdPool  (makeCommandPool  (vk, device, queueFamilyIndex));
+       const Unique<VkCommandBuffer> cmdBuffer(makeCommandBuffer(vk, device, *cmdPool));
+
+       for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx)
+       {
+               const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding;
+
+               log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding)) << tcu::TestLog::EndMessage;
+
+               // Reset the command buffer and begin.
+               beginCommandBuffer(vk, *cmdBuffer);
+
+               // Change color attachment image layout
+               {
+                       // State is slightly different on the first iteration.
+                       const VkImageLayout currentLayout = (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+                       const VkAccessFlags srcFlags      = (caseNdx == 0 ? (VkAccessFlags)0          : VK_ACCESS_TRANSFER_READ_BIT);
+
+                       const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+                               srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+                               currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+                               *colorAttachmentImage, colorImageSubresourceRange);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+                               0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+               }
+
+               // Begin render pass
+               {
+                       const VkRect2D renderArea = {
+                               makeOffset2D(0, 0),
+                               makeExtent2D(renderSize.x(), renderSize.y()),
+                       };
+                       const tcu::Vec4 clearColor = tcu::RGBA::red().toVec();
+
+                       beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+               }
+
+               vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline);
+
+               // Process a single abstract vertex.
+               vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
+               endRenderPass(vk, *cmdBuffer);
+
+               // Copy render result to a host-visible buffer
+               {
+                       const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+                               VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+                               VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+                               *colorAttachmentImage, colorImageSubresourceRange);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+                               0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+               }
+               {
+                       const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+                       vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, &copyRegion);
+               }
+               {
+                       const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+                               VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+                       vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+                               0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+               }
+
+               endCommandBuffer(vk, *cmdBuffer);
+               submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+               {
+                       // Log rendered image
+                       const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+                       invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+                       const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+                       log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
+
+                       // Verify case result
+                       success = success && verifyResultImage(log, imagePixelAccess, m_primitiveType, m_winding, frontFaceWinding);
+               }
+       }  // for windingNdx
+
+       return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+}
+
+TestInstance* WindingTest::createInstance (Context& context) const
+{
+       requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+       return new WindingTestInstance(context, m_primitiveType, m_winding);
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.winding.*
+tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "winding", "Test the cw and ccw input layout qualifiers"));
+
+       static const TessPrimitiveType primitivesNoIsolines[] =
+       {
+               TESSPRIMITIVETYPE_TRIANGLES,
+               TESSPRIMITIVETYPE_QUADS,
+       };
+
+       for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
+       for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
+               group->addChild(new WindingTest(testCtx, primitivesNoIsolines[primitiveTypeNdx], (Winding)windingNdx));
+
+       return group.release();
+}
+
+} // tessellation
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/tessellation/vktTessellationWindingTests.hpp b/external/vulkancts/modules/vulkan/tessellation/vktTessellationWindingTests.hpp
new file mode 100644 (file)
index 0000000..8610891
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _VKTTESSELLATIONWINDINGTESTS_HPP
+#define _VKTTESSELLATIONWINDINGTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2016 The Khronos Group Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Tessellation Winding Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONWINDINGTESTS_HPP
index f654e67..8277e91 100644 (file)
@@ -66,6 +66,7 @@
 #include "vktWsiTests.hpp"
 #include "vktSynchronization.hpp"
 #include "vktSparseResourcesTests.hpp"
+#include "vktTessellationTests.hpp"
 
 #include <vector>
 #include <sstream>
@@ -378,6 +379,7 @@ void TestPackage::init (void)
        addChild(wsi::createTests                       (m_testCtx));
        addChild(createSynchronizationTests     (m_testCtx));
        addChild(sparse::createTests            (m_testCtx));
+       addChild(tessellation::createTests      (m_testCtx));
 }
 
 } // vkt
index 00a155b..10d938b 100644 (file)
@@ -22,3 +22,6 @@ dEQP-VK.glsl.builtin.function.common.frexp.vec4_highp_vertex
 
 # Issue 340: Cube map corner texel selection
 dEQP-VK.pipeline.sampler.view_type.cube.format.r5g6b5_unorm_pack16.mag_filter.linear
+
+# Issue 384: glslang incorrect stuct member decorations
+dEQP-VK.tessellation.user_defined_io.*
index 6d64c8b..159ff20 100644 (file)
@@ -81520,3 +81520,297 @@ dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_16
 dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_17
 dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_20
 dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_24
+dEQP-VK.tessellation.limits.max_tessellation_generation_level
+dEQP-VK.tessellation.limits.max_tessellation_patch_size
+dEQP-VK.tessellation.limits.max_tessellation_control_per_vertex_input_components
+dEQP-VK.tessellation.limits.max_tessellation_control_per_vertex_output_components
+dEQP-VK.tessellation.limits.max_tessellation_control_per_patch_output_components
+dEQP-VK.tessellation.limits.max_tessellation_control_total_output_components
+dEQP-VK.tessellation.limits.max_tessellation_evaluation_input_components
+dEQP-VK.tessellation.limits.max_tessellation_evaluation_output_components
+dEQP-VK.tessellation.tesscoord.triangles_equal_spacing
+dEQP-VK.tessellation.tesscoord.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.tesscoord.triangles_fractional_even_spacing
+dEQP-VK.tessellation.tesscoord.quads_equal_spacing
+dEQP-VK.tessellation.tesscoord.quads_fractional_odd_spacing
+dEQP-VK.tessellation.tesscoord.quads_fractional_even_spacing
+dEQP-VK.tessellation.tesscoord.isolines_equal_spacing
+dEQP-VK.tessellation.tesscoord.isolines_fractional_odd_spacing
+dEQP-VK.tessellation.tesscoord.isolines_fractional_even_spacing
+dEQP-VK.tessellation.winding.triangles_ccw
+dEQP-VK.tessellation.winding.triangles_cw
+dEQP-VK.tessellation.winding.quads_ccw
+dEQP-VK.tessellation.winding.quads_cw
+dEQP-VK.tessellation.shader_input_output.patch_vertices_5_in_10_out
+dEQP-VK.tessellation.shader_input_output.patch_vertices_10_in_5_out
+dEQP-VK.tessellation.shader_input_output.primitive_id_tcs
+dEQP-VK.tessellation.shader_input_output.primitive_id_tes
+dEQP-VK.tessellation.shader_input_output.patch_vertices_in_tcs
+dEQP-VK.tessellation.shader_input_output.patch_vertices_in_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_inner_0_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_inner_1_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_0_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_1_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_2_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_3_tes
+dEQP-VK.tessellation.shader_input_output.gl_position_vs_to_tcs
+dEQP-VK.tessellation.shader_input_output.gl_position_tcs_to_tes
+dEQP-VK.tessellation.shader_input_output.gl_position_vs_to_tcs_to_tes
+dEQP-VK.tessellation.shader_input_output.barrier
+dEQP-VK.tessellation.misc_draw.fill_cover_triangles_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_triangles_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_triangles_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_quads_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_quads_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_quads_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_triangles_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_triangles_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_triangles_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_quads_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_quads_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_quads_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.isolines_equal_spacing
+dEQP-VK.tessellation.misc_draw.isolines_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.isolines_fractional_even_spacing
+dEQP-VK.tessellation.common_edge.triangles_equal_spacing
+dEQP-VK.tessellation.common_edge.triangles_equal_spacing_precise
+dEQP-VK.tessellation.common_edge.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.common_edge.triangles_fractional_odd_spacing_precise
+dEQP-VK.tessellation.common_edge.triangles_fractional_even_spacing
+dEQP-VK.tessellation.common_edge.triangles_fractional_even_spacing_precise
+dEQP-VK.tessellation.common_edge.quads_equal_spacing
+dEQP-VK.tessellation.common_edge.quads_equal_spacing_precise
+dEQP-VK.tessellation.common_edge.quads_fractional_odd_spacing
+dEQP-VK.tessellation.common_edge.quads_fractional_odd_spacing_precise
+dEQP-VK.tessellation.common_edge.quads_fractional_even_spacing
+dEQP-VK.tessellation.common_edge.quads_fractional_even_spacing_precise
+dEQP-VK.tessellation.fractional_spacing.odd
+dEQP-VK.tessellation.fractional_spacing.even
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_cw
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_cw
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_cw
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_division.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.quads_equal_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.triangle_set.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.triangle_set.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.triangle_set.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.triangle_set.quads_equal_spacing
+dEQP-VK.tessellation.invariance.triangle_set.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.triangle_set.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.quads_equal_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.quads_equal_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_cw_point_mode