1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2014 The Android Open Source Project
6  * Copyright (c) 2016 The Khronos Group Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22 * \brief Tessellation Geometry Interaction - Passthrough
23 *//*--------------------------------------------------------------------*/
24 
25 #include "vktTessellationGeometryPassthroughTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28 
29 #include "tcuTestLog.hpp"
30 #include "tcuImageCompare.hpp"
31 
32 #include "vkDefs.hpp"
33 #include "vkBarrierUtil.hpp"
34 #include "vkQueryUtil.hpp"
35 #include "vkBuilderUtil.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkImageUtil.hpp"
38 #include "vkCmdUtil.hpp"
39 #include "vkObjUtil.hpp"
40 #include "vkBufferWithMemory.hpp"
41 #include "vkImageWithMemory.hpp"
42 
43 #include "deUniquePtr.hpp"
44 
45 #include <string>
46 #include <vector>
47 
48 namespace vkt
49 {
50 namespace tessellation
51 {
52 
53 using namespace vk;
54 
55 namespace
56 {
57 
addVertexAndFragmentShaders(vk::SourceCollections & programCollection)58 void addVertexAndFragmentShaders(vk::SourceCollections &programCollection)
59 {
60     // Vertex shader
61     {
62         std::ostringstream src;
63         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
64             << "\n"
65             << "layout(location = 0) in  highp vec4 a_position;\n"
66             << "layout(location = 0) out highp vec4 v_vertex_color;\n"
67             << "\n"
68             << "void main (void)\n"
69             << "{\n"
70             << "    gl_Position = a_position;\n"
71             << "    v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n"
72             << "}\n";
73 
74         programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
75     }
76 
77     // Fragment shader
78     {
79         std::ostringstream src;
80         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
81             << "\n"
82             << "layout(location = 0) in  highp   vec4 v_fragment_color;\n"
83             << "layout(location = 0) out mediump vec4 fragColor;\n"
84             << "void main (void)\n"
85             << "{\n"
86             << "    fragColor = v_fragment_color;\n"
87             << "}\n";
88 
89         programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
90     }
91 }
92 
93 //! Tessellation evaluation shader used in passthrough geometry shader case.
generateTessellationEvaluationShader(const TessPrimitiveType primitiveType,const std::string & colorOutputName)94 std::string generateTessellationEvaluationShader(const TessPrimitiveType primitiveType,
95                                                  const std::string &colorOutputName)
96 {
97     std::ostringstream src;
98     src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
99         << "#extension GL_EXT_tessellation_shader : require\n"
100         << "layout(" << getTessPrimitiveTypeShaderName(primitiveType) << ") in;\n"
101         << "\n"
102         << "layout(location = 0) in  highp vec4 v_patch_color[];\n"
103         << "layout(location = 0) out highp vec4 " << colorOutputName << ";\n"
104         << "\n"
105         << "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
106         << "void main (void)\n"
107         << "{\n";
108 
109     if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
110         src << "    vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, "
111                "1.3));\n"
112             << "    vec3 cweights = gl_TessCoord;\n"
113             << "    gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + "
114                "weights.z * gl_in[2].gl_Position.xyz, 1.0);\n"
115             << "    " << colorOutputName
116             << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n";
117     else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES)
118         src << "    vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n"
119             << "    vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n"
120             << "    vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n"
121             << "    vec2 cweights = gl_TessCoord.xy;\n"
122             << "    gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), "
123                "mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n"
124             << "    " << colorOutputName
125             << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], "
126                "cweights.y), cweights.x);\n";
127     else
128         DE_ASSERT(false);
129 
130     src << "}\n";
131 
132     return src.str();
133 }
134 
135 class IdentityGeometryShaderTestCase : public TestCase
136 {
137 public:
138     void initPrograms(vk::SourceCollections &programCollection) const;
139     void checkSupport(Context &context) const;
140     TestInstance *createInstance(Context &context) const;
141 
IdentityGeometryShaderTestCase(tcu::TestContext & testCtx,const std::string & name,const TessPrimitiveType primitiveType)142     IdentityGeometryShaderTestCase(tcu::TestContext &testCtx, const std::string &name,
143                                    const TessPrimitiveType primitiveType)
144         : TestCase(testCtx, name)
145         , m_primitiveType(primitiveType)
146     {
147     }
148 
149 private:
150     const TessPrimitiveType m_primitiveType;
151 };
152 
checkSupport(Context & context) const153 void IdentityGeometryShaderTestCase::checkSupport(Context &context) const
154 {
155 #ifndef CTS_USES_VULKANSC
156     checkSupportPrimitive(context, m_primitiveType);
157 #else
158     DE_UNREF(context);
159 #endif // CTS_USES_VULKANSC
160 }
161 
initPrograms(vk::SourceCollections & programCollection) const162 void IdentityGeometryShaderTestCase::initPrograms(vk::SourceCollections &programCollection) const
163 {
164     addVertexAndFragmentShaders(programCollection);
165 
166     // Tessellation control
167     {
168         std::ostringstream src;
169         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
170             << "#extension GL_EXT_tessellation_shader : require\n"
171             << "layout(vertices = 4) out;\n"
172             << "\n"
173             << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
174             << "    float inner0;\n"
175             << "    float inner1;\n"
176             << "    float outer0;\n"
177             << "    float outer1;\n"
178             << "    float outer2;\n"
179             << "    float outer3;\n"
180             << "} sb_levels;\n"
181             << "\n"
182             << "layout(location = 0) in  highp vec4 v_vertex_color[];\n"
183             << "layout(location = 0) out highp vec4 v_patch_color[];\n"
184             << "\n"
185             << "void main (void)\n"
186             << "{\n"
187             << "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
188             << "    v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
189             << "\n"
190             << "    gl_TessLevelInner[0] = sb_levels.inner0;\n"
191             << "    gl_TessLevelInner[1] = sb_levels.inner1;\n"
192             << "    gl_TessLevelOuter[0] = sb_levels.outer0;\n"
193             << "    gl_TessLevelOuter[1] = sb_levels.outer1;\n"
194             << "    gl_TessLevelOuter[2] = sb_levels.outer2;\n"
195             << "    gl_TessLevelOuter[3] = sb_levels.outer3;\n"
196             << "}\n";
197 
198         programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
199     }
200 
201     // Tessellation evaluation shader
202     {
203         programCollection.glslSources.add("tese_to_frag") << glu::TessellationEvaluationSource(
204             generateTessellationEvaluationShader(m_primitiveType, "v_fragment_color"));
205         programCollection.glslSources.add("tese_to_geom") << glu::TessellationEvaluationSource(
206             generateTessellationEvaluationShader(m_primitiveType, "v_evaluated_color"));
207     }
208 
209     // Geometry shader
210     {
211         std::ostringstream src;
212         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
213             << "#extension GL_EXT_geometry_shader : require\n"
214             << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(m_primitiveType, false) << ") in;\n"
215             << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(m_primitiveType, false)
216             << ", max_vertices=" << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n"
217             << "\n"
218             << "layout(location = 0) in  highp vec4 v_evaluated_color[];\n"
219             << "layout(location = 0) out highp vec4 v_fragment_color;\n"
220             << "\n"
221             << "void main (void)\n"
222             << "{\n"
223             << "    for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n"
224             << "    {\n"
225             << "        gl_Position = gl_in[ndx].gl_Position;\n"
226             << "        v_fragment_color = v_evaluated_color[ndx];\n"
227             << "        EmitVertex();\n"
228             << "    }\n"
229             << "}\n";
230 
231         programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
232     }
233 }
234 
235 class IdentityTessellationShaderTestCase : public TestCase
236 {
237 public:
238     void initPrograms(vk::SourceCollections &programCollection) const;
239     void checkSupport(Context &context) const;
240     TestInstance *createInstance(Context &context) const;
241 
IdentityTessellationShaderTestCase(tcu::TestContext & testCtx,const std::string & name,const TessPrimitiveType primitiveType)242     IdentityTessellationShaderTestCase(tcu::TestContext &testCtx, const std::string &name,
243                                        const TessPrimitiveType primitiveType)
244         : TestCase(testCtx, name)
245         , m_primitiveType(primitiveType)
246     {
247     }
248 
249 private:
250     const TessPrimitiveType m_primitiveType;
251 };
252 
checkSupport(Context & context) const253 void IdentityTessellationShaderTestCase::checkSupport(Context &context) const
254 {
255 #ifndef CTS_USES_VULKANSC
256     checkSupportPrimitive(context, m_primitiveType);
257 #else
258     DE_UNREF(context);
259 #endif // CTS_USES_VULKANSC
260 }
261 
262 //! Geometry shader used in passthrough tessellation shader case.
generateGeometryShader(const TessPrimitiveType primitiveType,const std::string & colorSourceName)263 std::string generateGeometryShader(const TessPrimitiveType primitiveType, const std::string &colorSourceName)
264 {
265     const int numEmitVertices = (primitiveType == TESSPRIMITIVETYPE_ISOLINES ? 11 : 8);
266 
267     std::ostringstream src;
268     src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
269         << "#extension GL_EXT_geometry_shader : require\n"
270         << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, false) << ") in;\n"
271         << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, false)
272         << ", max_vertices=" << numEmitVertices << ") out;\n"
273         << "\n"
274         << "layout(location = 0) in  highp vec4 " << colorSourceName << "[];\n"
275         << "layout(location = 0) out highp vec4 v_fragment_color;\n"
276         << "\n"
277         << "void main (void)\n"
278         << "{\n";
279 
280     if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
281     {
282         src << "    vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n"
283             << "\n"
284             << "    for (int ndx = 0; ndx < 4; ++ndx)\n"
285             << "    {\n"
286             << "        gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n"
287             << "        v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
288             << "        EmitVertex();\n"
289             << "\n"
290             << "        gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n"
291             << "        v_fragment_color = " << colorSourceName << "[ndx % 3];\n"
292             << "        EmitVertex();\n"
293             << "    }\n";
294     }
295     else if (primitiveType == TESSPRIMITIVETYPE_ISOLINES)
296     {
297         src << "    vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - "
298                "gl_in[0].gl_Position.x, 0.0, 0.0);\n"
299             << "    for (int i = 0; i <= 10; ++i)\n"
300             << "    {\n"
301             << "        float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n"
302             << "        float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n"
303             << "        gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n"
304             << "        v_fragment_color = mix(" << colorSourceName << "[0], " << colorSourceName << "[1], xweight);\n"
305             << "        EmitVertex();\n"
306             << "    }\n";
307     }
308     else
309         DE_ASSERT(false);
310 
311     src << "}\n";
312 
313     return src.str();
314 }
315 
initPrograms(vk::SourceCollections & programCollection) const316 void IdentityTessellationShaderTestCase::initPrograms(vk::SourceCollections &programCollection) const
317 {
318     addVertexAndFragmentShaders(programCollection);
319 
320     // Tessellation control
321     {
322         std::ostringstream src;
323         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
324             << "#extension GL_EXT_tessellation_shader : require\n"
325             << "layout(vertices = " << numVerticesPerPrimitive(m_primitiveType, false) << ") out;\n"
326             << "\n"
327             << "layout(location = 0) in  highp vec4 v_vertex_color[];\n"
328             << "layout(location = 0) out highp vec4 v_control_color[];\n"
329             << "\n"
330             << "void main (void)\n"
331             << "{\n"
332             << "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
333             << "    v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n"
334             << "\n"
335             << "    gl_TessLevelInner[0] = 1.0;\n"
336             << "    gl_TessLevelInner[1] = 1.0;\n"
337             << "    gl_TessLevelOuter[0] = 1.0;\n"
338             << "    gl_TessLevelOuter[1] = 1.0;\n"
339             << "    gl_TessLevelOuter[2] = 1.0;\n"
340             << "    gl_TessLevelOuter[3] = 1.0;\n"
341             << "}\n";
342 
343         programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
344     }
345 
346     // Tessellation evaluation shader
347     {
348         std::ostringstream src;
349         src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
350             << "#extension GL_EXT_tessellation_shader : require\n"
351             << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ") in;\n"
352             << "\n"
353             << "layout(location = 0) in  highp vec4 v_control_color[];\n"
354             << "layout(location = 0) out highp vec4 v_evaluated_color;\n"
355             << "\n"
356             << "// note: No need to use precise gl_Position since we do not require gapless geometry\n"
357             << "void main (void)\n"
358             << "{\n";
359 
360         if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
361             src << "    gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + "
362                    "gl_TessCoord.z * gl_in[2].gl_Position;\n"
363                 << "    v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] "
364                    "+ gl_TessCoord.z * v_control_color[2];\n";
365         else if (m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
366             src << "    gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n"
367                 << "    v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n";
368         else
369             DE_ASSERT(false);
370 
371         src << "}\n";
372 
373         programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
374     }
375 
376     // Geometry shader
377     {
378         programCollection.glslSources.add("geom_from_tese")
379             << glu::GeometrySource(generateGeometryShader(m_primitiveType, "v_evaluated_color"));
380         programCollection.glslSources.add("geom_from_vert")
381             << glu::GeometrySource(generateGeometryShader(m_primitiveType, "v_vertex_color"));
382     }
383 }
384 
getPixelBufferAccess(const DeviceInterface & vk,const VkDevice device,const BufferWithMemory & colorBuffer,const VkFormat colorFormat,const tcu::IVec2 & renderSize)385 inline tcu::ConstPixelBufferAccess getPixelBufferAccess(const DeviceInterface &vk, const VkDevice device,
386                                                         const BufferWithMemory &colorBuffer, const VkFormat colorFormat,
387                                                         const tcu::IVec2 &renderSize)
388 {
389     const Allocation &alloc = colorBuffer.getAllocation();
390 
391     invalidateAlloc(vk, device, alloc);
392 
393     return tcu::ConstPixelBufferAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr());
394 }
395 
396 //! When a test case disables tessellation stage and we need to derive a primitive type.
getPrimitiveTopology(const TessPrimitiveType primitiveType)397 VkPrimitiveTopology getPrimitiveTopology(const TessPrimitiveType primitiveType)
398 {
399     switch (primitiveType)
400     {
401     case TESSPRIMITIVETYPE_TRIANGLES:
402     case TESSPRIMITIVETYPE_QUADS:
403         return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
404 
405     case TESSPRIMITIVETYPE_ISOLINES:
406         return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
407 
408     default:
409         DE_ASSERT(false);
410         return VK_PRIMITIVE_TOPOLOGY_LAST;
411     }
412 }
413 
414 enum Constants
415 {
416     PIPELINE_CASES = 2,
417     RENDER_SIZE    = 256,
418 };
419 
420 class PassthroughTestInstance : public TestInstance
421 {
422 public:
423     struct PipelineDescription
424     {
425         bool useTessellation;
426         bool useGeometry;
427         std::string tessEvalShaderName;
428         std::string geomShaderName;
429         std::string description;
430 
PipelineDescriptionvkt::tessellation::__anond056bd9e0111::PassthroughTestInstance::PipelineDescription431         PipelineDescription(void) : useTessellation(), useGeometry()
432         {
433         }
434     };
435 
436     struct Params
437     {
438         bool useTessLevels;
439         TessLevels tessLevels;
440         TessPrimitiveType primitiveType;
441         int inputPatchVertices;
442         std::vector<tcu::Vec4> vertices;
443         PipelineDescription
444             pipelineCases[PIPELINE_CASES]; //!< Each test case renders with two pipelines and compares results
445         std::string message;
446 
Paramsvkt::tessellation::__anond056bd9e0111::PassthroughTestInstance::Params447         Params(void) : useTessLevels(), tessLevels(), primitiveType(), inputPatchVertices()
448         {
449         }
450     };
451 
PassthroughTestInstance(Context & context,const Params & params)452     PassthroughTestInstance(Context &context, const Params &params) : TestInstance(context), m_params(params)
453     {
454     }
455     tcu::TestStatus iterate(void);
456 
457 private:
458     const Params m_params;
459 };
460 
iterate(void)461 tcu::TestStatus PassthroughTestInstance::iterate(void)
462 {
463     requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(),
464                     FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER);
465     DE_STATIC_ASSERT(PIPELINE_CASES == 2);
466 
467     const DeviceInterface &vk       = m_context.getDeviceInterface();
468     const VkDevice device           = m_context.getDevice();
469     const VkQueue queue             = m_context.getUniversalQueue();
470     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
471     Allocator &allocator            = m_context.getDefaultAllocator();
472 
473     // Tessellation levels
474     const BufferWithMemory tessLevelsBuffer(
475         vk, device, allocator, makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
476         MemoryRequirement::HostVisible);
477 
478     if (m_params.useTessLevels)
479     {
480         const Allocation &alloc            = tessLevelsBuffer.getAllocation();
481         TessLevels *const bufferTessLevels = static_cast<TessLevels *>(alloc.getHostPtr());
482 
483         *bufferTessLevels = m_params.tessLevels;
484         flushAlloc(vk, device, alloc);
485     }
486 
487     // Vertex attributes
488 
489     const VkDeviceSize vertexDataSizeBytes = sizeInBytes(m_params.vertices);
490     const VkFormat vertexFormat            = VK_FORMAT_R32G32B32A32_SFLOAT;
491     const BufferWithMemory vertexBuffer(vk, device, allocator,
492                                         makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
493                                         MemoryRequirement::HostVisible);
494 
495     {
496         const Allocation &alloc = vertexBuffer.getAllocation();
497 
498         deMemcpy(alloc.getHostPtr(), &m_params.vertices[0], static_cast<std::size_t>(vertexDataSizeBytes));
499         flushAlloc(vk, device, alloc);
500     }
501 
502     // Descriptors - make descriptor for tessellation levels, even if we don't use them, to simplify code
503 
504     const Unique<VkDescriptorSetLayout> descriptorSetLayout(
505         DescriptorSetLayoutBuilder()
506             .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
507             .build(vk, device));
508 
509     const Unique<VkDescriptorPool> descriptorPool(
510         DescriptorPoolBuilder()
511             .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
512             .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
513 
514     const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
515     const VkDescriptorBufferInfo tessLevelsBufferInfo =
516         makeDescriptorBufferInfo(*tessLevelsBuffer, 0ull, sizeof(TessLevels));
517 
518     DescriptorSetUpdateBuilder()
519         .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
520                      VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
521         .update(vk, device);
522 
523     // Color attachment
524 
525     const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
526     const VkFormat colorFormat  = VK_FORMAT_R8G8B8A8_UNORM;
527     const VkImageSubresourceRange colorImageSubresourceRange =
528         makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
529     const ImageWithMemory colorAttachmentImage(
530         vk, device, allocator,
531         makeImageCreateInfo(renderSize, colorFormat,
532                             VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
533         MemoryRequirement::Any);
534 
535     // Color output buffer: image will be copied here for verification.
536     //                      We use two buffers, one for each case.
537 
538     const VkDeviceSize colorBufferSizeBytes =
539         renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
540     const BufferWithMemory colorBuffer1(vk, device, allocator,
541                                         makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
542                                         MemoryRequirement::HostVisible);
543     const BufferWithMemory colorBuffer2(vk, device, allocator,
544                                         makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
545                                         MemoryRequirement::HostVisible);
546     const BufferWithMemory *const colorBuffer[PIPELINE_CASES] = {&colorBuffer1, &colorBuffer2};
547 
548     // Pipeline
549 
550     const Unique<VkImageView> colorAttachmentView(makeImageView(
551         vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
552     const Unique<VkRenderPass> renderPass(makeRenderPass(vk, device, colorFormat));
553     const Unique<VkFramebuffer> framebuffer(
554         makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y()));
555     const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
556     const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
557     const Unique<VkCommandBuffer> cmdBuffer(
558         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
559 
560     // Message explaining the test
561     {
562         tcu::TestLog &log = m_context.getTestContext().getLog();
563         log << tcu::TestLog::Message << m_params.message << tcu::TestLog::EndMessage;
564 
565         if (m_params.useTessLevels)
566             log << tcu::TestLog::Message
567                 << "Tessellation levels: " << getTessellationLevelsString(m_params.tessLevels, m_params.primitiveType)
568                 << tcu::TestLog::EndMessage;
569     }
570 
571     for (int pipelineNdx = 0; pipelineNdx < PIPELINE_CASES; ++pipelineNdx)
572     {
573         const PipelineDescription &pipelineDescription = m_params.pipelineCases[pipelineNdx];
574         GraphicsPipelineBuilder pipelineBuilder;
575 
576         pipelineBuilder.setPrimitiveTopology(getPrimitiveTopology(m_params.primitiveType))
577             .setRenderSize(renderSize)
578             .setBlend(true)
579             .setVertexInputSingleAttribute(vertexFormat, tcu::getPixelSize(mapVkFormat(vertexFormat)))
580             .setPatchControlPoints(m_params.inputPatchVertices)
581             .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
582             .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL);
583 
584         if (pipelineDescription.useTessellation)
585             pipelineBuilder
586                 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
587                            m_context.getBinaryCollection().get("tesc"), DE_NULL)
588                 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
589                            m_context.getBinaryCollection().get(pipelineDescription.tessEvalShaderName), DE_NULL);
590 
591         if (pipelineDescription.useGeometry)
592             pipelineBuilder.setShader(vk, device, VK_SHADER_STAGE_GEOMETRY_BIT,
593                                       m_context.getBinaryCollection().get(pipelineDescription.geomShaderName), DE_NULL);
594 
595         const Unique<VkPipeline> pipeline(pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
596 
597         // Draw commands
598 
599         beginCommandBuffer(vk, *cmdBuffer);
600 
601         // Change color attachment image layout
602         {
603             // State is slightly different on the first iteration.
604             const VkImageLayout currentLayout =
605                 (pipelineNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
606             const VkAccessFlags srcFlags =
607                 (pipelineNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
608 
609             const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
610                 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
611                 *colorAttachmentImage, colorImageSubresourceRange);
612 
613             vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
614                                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
615                                   &colorAttachmentLayoutBarrier);
616         }
617 
618         // Begin render pass
619         {
620             const VkRect2D renderArea = makeRect2D(renderSize);
621             const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
622 
623             beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
624         }
625 
626         vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
627         {
628             const VkDeviceSize vertexBufferOffset = 0ull;
629             vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
630         }
631 
632         if (m_params.useTessLevels)
633             vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u,
634                                      &descriptorSet.get(), 0u, DE_NULL);
635 
636         vk.cmdDraw(*cmdBuffer, static_cast<uint32_t>(m_params.vertices.size()), 1u, 0u, 0u);
637         endRenderPass(vk, *cmdBuffer);
638 
639         // Copy render result to a host-visible buffer
640         copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, colorBuffer[pipelineNdx]->get(), renderSize);
641 
642         endCommandBuffer(vk, *cmdBuffer);
643         submitCommandsAndWait(vk, device, queue, *cmdBuffer);
644     }
645 
646     // Verify results
647 
648     tcu::ConstPixelBufferAccess image0 = getPixelBufferAccess(vk, device, *colorBuffer[0], colorFormat, renderSize);
649     tcu::ConstPixelBufferAccess image1 = getPixelBufferAccess(vk, device, *colorBuffer[1], colorFormat, renderSize);
650 
651     const tcu::UVec4 colorThreshold(8, 8, 8, 255);
652     const tcu::IVec3 positionDeviation(1, 1, 0); // 3x3 search kernel
653     const bool ignoreOutOfBounds = true;
654 
655     tcu::TestLog &log = m_context.getTestContext().getLog();
656     log << tcu::TestLog::Message << "In image comparison:\n"
657         << "  Reference - " << m_params.pipelineCases[0].description << "\n"
658         << "  Result    - " << m_params.pipelineCases[1].description << "\n"
659         << tcu::TestLog::EndMessage;
660 
661     const bool ok = tcu::intThresholdPositionDeviationCompare(log, "ImageCompare", "Image comparison", image0, image1,
662                                                               colorThreshold, positionDeviation, ignoreOutOfBounds,
663                                                               tcu::COMPARE_LOG_RESULT);
664 
665     return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
666 }
667 
createInstance(Context & context) const668 TestInstance *IdentityGeometryShaderTestCase::createInstance(Context &context) const
669 {
670     PassthroughTestInstance::Params params;
671 
672     const float level          = 14.0;
673     params.useTessLevels       = true;
674     params.tessLevels.inner[0] = level;
675     params.tessLevels.inner[1] = level;
676     params.tessLevels.outer[0] = level;
677     params.tessLevels.outer[1] = level;
678     params.tessLevels.outer[2] = level;
679     params.tessLevels.outer[3] = level;
680 
681     params.primitiveType      = m_primitiveType;
682     params.inputPatchVertices = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
683 
684     params.vertices.push_back(tcu::Vec4(-0.9f, -0.9f, 0.0f, 1.0f));
685     params.vertices.push_back(tcu::Vec4(-0.9f, 0.9f, 0.0f, 1.0f));
686     params.vertices.push_back(tcu::Vec4(0.9f, -0.9f, 0.0f, 1.0f));
687     params.vertices.push_back(tcu::Vec4(0.9f, 0.9f, 0.0f, 1.0f));
688 
689     params.pipelineCases[0].useTessellation    = true;
690     params.pipelineCases[0].useGeometry        = true;
691     params.pipelineCases[0].tessEvalShaderName = "tese_to_geom";
692     params.pipelineCases[0].geomShaderName     = "geom";
693     params.pipelineCases[0].description        = "passthrough geometry shader";
694 
695     params.pipelineCases[1].useTessellation    = true;
696     params.pipelineCases[1].useGeometry        = false;
697     params.pipelineCases[1].tessEvalShaderName = "tese_to_frag";
698     params.pipelineCases[1].geomShaderName     = "geom";
699     params.pipelineCases[1].description        = "no geometry shader in the pipeline";
700 
701     params.message =
702         "Testing tessellating shader program output does not change when a passthrough geometry shader is attached.\n"
703         "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n"
704         "Using additive blending to detect overlap.\n";
705 
706     return new PassthroughTestInstance(context, params);
707 }
708 
createInstance(Context & context) const709 TestInstance *IdentityTessellationShaderTestCase::createInstance(Context &context) const
710 {
711     PassthroughTestInstance::Params params;
712 
713     params.useTessLevels      = false;
714     params.primitiveType      = m_primitiveType;
715     params.inputPatchVertices = (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
716 
717     params.vertices.push_back(tcu::Vec4(-0.4f, 0.4f, 0.0f, 1.0f));
718     params.vertices.push_back(tcu::Vec4(0.0f, -0.5f, 0.0f, 1.0f));
719     if (params.inputPatchVertices == 3)
720         params.vertices.push_back(tcu::Vec4(0.4f, 0.4f, 0.0f, 1.0f));
721 
722     params.pipelineCases[0].useTessellation    = true;
723     params.pipelineCases[0].useGeometry        = true;
724     params.pipelineCases[0].tessEvalShaderName = "tese";
725     params.pipelineCases[0].geomShaderName     = "geom_from_tese";
726     params.pipelineCases[0].description        = "passthrough tessellation shaders";
727 
728     params.pipelineCases[1].useTessellation    = false;
729     params.pipelineCases[1].useGeometry        = true;
730     params.pipelineCases[1].tessEvalShaderName = "tese";
731     params.pipelineCases[1].geomShaderName     = "geom_from_vert";
732     params.pipelineCases[1].description        = "no tessellation shaders in the pipeline";
733 
734     params.message =
735         "Testing geometry shading shader program output does not change when a passthrough tessellation shader is "
736         "attached.\n"
737         "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n"
738         "Using additive blending to detect overlap.\n";
739 
740     return new PassthroughTestInstance(context, params);
741 }
742 
makeIdentityGeometryShaderCase(tcu::TestContext & testCtx,const TessPrimitiveType primitiveType)743 inline TestCase *makeIdentityGeometryShaderCase(tcu::TestContext &testCtx, const TessPrimitiveType primitiveType)
744 {
745     // Passthrough geometry shader has no effect
746     return new IdentityGeometryShaderTestCase(
747         testCtx,
748         "tessellate_" + de::toString(getTessPrimitiveTypeShaderName(primitiveType)) + "_passthrough_geometry_no_change",
749         primitiveType);
750 }
751 
makeIdentityTessellationShaderCase(tcu::TestContext & testCtx,const TessPrimitiveType primitiveType)752 inline TestCase *makeIdentityTessellationShaderCase(tcu::TestContext &testCtx, const TessPrimitiveType primitiveType)
753 {
754     // Passthrough tessellation shader has no effect
755     return new IdentityTessellationShaderTestCase(testCtx,
756                                                   "passthrough_tessellation_geometry_shade_" +
757                                                       de::toString(getTessPrimitiveTypeShaderName(primitiveType)) +
758                                                       "_no_change",
759                                                   primitiveType);
760 }
761 
762 } // namespace
763 
764 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.passthrough.*
createGeometryPassthroughTests(tcu::TestContext & testCtx)765 tcu::TestCaseGroup *createGeometryPassthroughTests(tcu::TestContext &testCtx)
766 {
767     // Render various types with either passthrough geometry or tessellation shader
768     de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "passthrough"));
769 
770     // Passthrough geometry shader
771     group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES));
772     group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_QUADS));
773     group->addChild(makeIdentityGeometryShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES));
774 
775     // Passthrough tessellation shader
776     group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_TRIANGLES));
777     group->addChild(makeIdentityTessellationShaderCase(testCtx, TESSPRIMITIVETYPE_ISOLINES));
778 
779     return group.release();
780 }
781 
782 } // namespace tessellation
783 } // namespace vkt
784