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