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 ¶ms) : 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