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 Miscellaneous Draw Tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktTessellationMiscDrawTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30 #include "tcuImageIO.hpp"
31 #include "tcuTexture.hpp"
32 #include "tcuImageCompare.hpp"
33
34 #include "vkDefs.hpp"
35 #include "vkBarrierUtil.hpp"
36 #include "vkQueryUtil.hpp"
37 #include "vkBuilderUtil.hpp"
38 #include "vkImageUtil.hpp"
39 #include "vkTypeUtil.hpp"
40 #include "vkStrUtil.hpp"
41 #include "vkCmdUtil.hpp"
42 #include "vkObjUtil.hpp"
43 #include "vkBufferWithMemory.hpp"
44 #include "vkImageWithMemory.hpp"
45
46 #include "deUniquePtr.hpp"
47 #include "deStringUtil.hpp"
48
49 #include <string>
50 #include <vector>
51 #include <utility>
52
53 namespace vkt
54 {
55 namespace tessellation
56 {
57
58 using namespace vk;
59
60 namespace
61 {
62
63 struct CaseDefinition
64 {
65 TessPrimitiveType primitiveType;
66 SpacingMode spacingMode;
67 std::string referenceImagePathPrefix; //!< without case suffix and extension (e.g. "_1.png")
68 };
69
makeCaseDefinition(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const std::string & referenceImagePathPrefix)70 inline CaseDefinition makeCaseDefinition(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
71 const std::string &referenceImagePathPrefix)
72 {
73 CaseDefinition caseDef;
74 caseDef.primitiveType = primitiveType;
75 caseDef.spacingMode = spacingMode;
76 caseDef.referenceImagePathPrefix = referenceImagePathPrefix;
77 return caseDef;
78 }
79
genTessLevelCases(const SpacingMode spacingMode)80 std::vector<TessLevels> genTessLevelCases(const SpacingMode spacingMode)
81 {
82 static const TessLevels tessLevelCases[] = {
83 {{9.0f, 9.0f}, {9.0f, 9.0f, 9.0f, 9.0f}},
84 {{8.0f, 11.0f}, {13.0f, 15.0f, 18.0f, 21.0f}},
85 {{17.0f, 14.0f}, {3.0f, 6.0f, 9.0f, 12.0f}},
86 };
87
88 std::vector<TessLevels> resultTessLevels(DE_LENGTH_OF_ARRAY(tessLevelCases));
89
90 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); ++tessLevelCaseNdx)
91 {
92 TessLevels &tessLevels = resultTessLevels[tessLevelCaseNdx];
93
94 for (int i = 0; i < 2; ++i)
95 tessLevels.inner[i] =
96 static_cast<float>(getClampedRoundedTessLevel(spacingMode, tessLevelCases[tessLevelCaseNdx].inner[i]));
97
98 for (int i = 0; i < 4; ++i)
99 tessLevels.outer[i] =
100 static_cast<float>(getClampedRoundedTessLevel(spacingMode, tessLevelCases[tessLevelCaseNdx].outer[i]));
101 }
102
103 return resultTessLevels;
104 }
105
genVertexPositions(const TessPrimitiveType primitiveType)106 std::vector<tcu::Vec2> genVertexPositions(const TessPrimitiveType primitiveType)
107 {
108 std::vector<tcu::Vec2> positions;
109 positions.reserve(4);
110
111 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
112 {
113 positions.push_back(tcu::Vec2(0.8f, 0.6f));
114 positions.push_back(tcu::Vec2(0.0f, -0.786f));
115 positions.push_back(tcu::Vec2(-0.8f, 0.6f));
116 }
117 else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES)
118 {
119 positions.push_back(tcu::Vec2(-0.8f, -0.8f));
120 positions.push_back(tcu::Vec2(0.8f, -0.8f));
121 positions.push_back(tcu::Vec2(-0.8f, 0.8f));
122 positions.push_back(tcu::Vec2(0.8f, 0.8f));
123 }
124 else
125 DE_ASSERT(false);
126
127 return positions;
128 }
129
130 //! Common test function used by all test cases.
runTest(Context & context,const CaseDefinition caseDef)131 tcu::TestStatus runTest(Context &context, const CaseDefinition caseDef)
132 {
133 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
134
135 const DeviceInterface &vk = context.getDeviceInterface();
136 const VkDevice device = context.getDevice();
137 const VkQueue queue = context.getUniversalQueue();
138 const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
139 Allocator &allocator = context.getDefaultAllocator();
140
141 const std::vector<TessLevels> tessLevelCases = genTessLevelCases(caseDef.spacingMode);
142 const std::vector<tcu::Vec2> vertexData = genVertexPositions(caseDef.primitiveType);
143 const uint32_t inPatchSize = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
144
145 // Vertex input: positions
146
147 const VkFormat vertexFormat = VK_FORMAT_R32G32_SFLOAT;
148 const uint32_t vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
149 const VkDeviceSize vertexDataSizeBytes = sizeInBytes(vertexData);
150
151 const BufferWithMemory vertexBuffer(vk, device, allocator,
152 makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
153 MemoryRequirement::HostVisible);
154
155 DE_ASSERT(inPatchSize == vertexData.size());
156 DE_ASSERT(sizeof(vertexData[0]) == vertexStride);
157
158 {
159 const Allocation &alloc = vertexBuffer.getAllocation();
160
161 deMemcpy(alloc.getHostPtr(), &vertexData[0], static_cast<std::size_t>(vertexDataSizeBytes));
162 flushAlloc(vk, device, alloc);
163 // No barrier needed, flushed memory is automatically visible
164 }
165
166 // Color attachment
167
168 const tcu::IVec2 renderSize = tcu::IVec2(256, 256);
169 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
170 const VkImageSubresourceRange colorImageSubresourceRange =
171 makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
172 const ImageWithMemory colorAttachmentImage(
173 vk, device, allocator,
174 makeImageCreateInfo(renderSize, colorFormat,
175 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
176 MemoryRequirement::Any);
177
178 // Color output buffer: image will be copied here for verification
179
180 const VkDeviceSize colorBufferSizeBytes =
181 renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
182 const BufferWithMemory colorBuffer(vk, device, allocator,
183 makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
184 MemoryRequirement::HostVisible);
185
186 // Input buffer: tessellation levels. Data is filled in later.
187
188 const BufferWithMemory tessLevelsBuffer(
189 vk, device, allocator, makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT),
190 MemoryRequirement::HostVisible);
191
192 // Descriptors
193
194 const Unique<VkDescriptorSetLayout> descriptorSetLayout(
195 DescriptorSetLayoutBuilder()
196 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
197 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
198 .build(vk, device));
199
200 const Unique<VkDescriptorPool> descriptorPool(
201 DescriptorPoolBuilder()
202 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
203 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
204
205 const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
206
207 const VkDescriptorBufferInfo tessLevelsBufferInfo =
208 makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels));
209
210 DescriptorSetUpdateBuilder()
211 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u),
212 VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
213 .update(vk, device);
214
215 // Pipeline
216
217 const Unique<VkImageView> colorAttachmentView(makeImageView(
218 vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
219 const Unique<VkRenderPass> renderPass(makeRenderPass(vk, device, colorFormat));
220 const Unique<VkFramebuffer> framebuffer(
221 makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y()));
222 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device, *descriptorSetLayout));
223 const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
224 const Unique<VkCommandBuffer> cmdBuffer(
225 allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
226
227 const Unique<VkPipeline> pipeline(
228 GraphicsPipelineBuilder()
229 .setRenderSize(renderSize)
230 .setVertexInputSingleAttribute(vertexFormat, vertexStride)
231 .setPatchControlPoints(inPatchSize)
232 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL)
233 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"),
234 DE_NULL)
235 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
236 context.getBinaryCollection().get("tese"), DE_NULL)
237 .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
238 .build(vk, device, *pipelineLayout, *renderPass));
239
240 // Draw commands
241
242 uint32_t numPassedCases = 0;
243
244 for (uint32_t tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
245 {
246 context.getTestContext().getLog()
247 << tcu::TestLog::Message << "Tessellation levels: "
248 << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
249 << tcu::TestLog::EndMessage;
250
251 // Upload tessellation levels data to the input buffer
252 {
253 const Allocation &alloc = tessLevelsBuffer.getAllocation();
254 TessLevels *const bufferTessLevels = static_cast<TessLevels *>(alloc.getHostPtr());
255
256 *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
257 flushAlloc(vk, device, alloc);
258 }
259
260 // Reset the command buffer and begin recording.
261 beginCommandBuffer(vk, *cmdBuffer);
262
263 // Change color attachment image layout
264 {
265 // State is slightly different on the first iteration.
266 const VkImageLayout currentLayout =
267 (tessLevelCaseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
268 const VkAccessFlags srcFlags =
269 (tessLevelCaseNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
270
271 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
272 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
273 *colorAttachmentImage, colorImageSubresourceRange);
274
275 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT,
276 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
277 &colorAttachmentLayoutBarrier);
278 }
279
280 // Begin render pass
281 {
282 const VkRect2D renderArea = makeRect2D(renderSize);
283 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
284
285 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
286 }
287
288 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
289 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u,
290 &descriptorSet.get(), 0u, DE_NULL);
291 {
292 const VkDeviceSize vertexBufferOffset = 0ull;
293 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
294 }
295
296 // Process enough vertices to make a patch.
297 vk.cmdDraw(*cmdBuffer, inPatchSize, 1u, 0u, 0u);
298 endRenderPass(vk, *cmdBuffer);
299
300 // Copy render result to a host-visible buffer
301 copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
302
303 endCommandBuffer(vk, *cmdBuffer);
304 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
305
306 {
307 const Allocation &colorBufferAlloc = colorBuffer.getAllocation();
308
309 invalidateAlloc(vk, device, colorBufferAlloc);
310
311 // Verify case result
312 const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(),
313 renderSize.y(), 1, colorBufferAlloc.getHostPtr());
314
315 // Load reference image
316 const std::string referenceImagePath =
317 caseDef.referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png";
318
319 tcu::TextureLevel referenceImage;
320 tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), referenceImagePath.c_str());
321
322 if (tcu::fuzzyCompare(context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
323 referenceImage.getAccess(), resultImageAccess, 0.002f, tcu::COMPARE_LOG_RESULT))
324 ++numPassedCases;
325 }
326 } // tessLevelCaseNdx
327
328 return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
329 }
330
getTessLevelsSSBODeclaration(void)331 inline const char *getTessLevelsSSBODeclaration(void)
332 {
333 return "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
334 " float inner0;\n"
335 " float inner1;\n"
336 " float outer0;\n"
337 " float outer1;\n"
338 " float outer2;\n"
339 " float outer3;\n"
340 "} sb_levels;\n";
341 }
342
343 //! Add vertex, fragment, and tessellation control shaders.
initCommonPrograms(vk::SourceCollections & programCollection,const CaseDefinition caseDef)344 void initCommonPrograms(vk::SourceCollections &programCollection, const CaseDefinition caseDef)
345 {
346 DE_ASSERT(!programCollection.glslSources.contains("vert"));
347 DE_ASSERT(!programCollection.glslSources.contains("tesc"));
348 DE_ASSERT(!programCollection.glslSources.contains("frag"));
349
350 // Vertex shader
351 {
352 std::ostringstream src;
353 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
354 << "\n"
355 << "layout(location = 0) in highp vec2 in_v_position;\n"
356 << "layout(location = 0) out highp vec2 in_tc_position;\n"
357 << "\n"
358 << "void main (void)\n"
359 << "{\n"
360 << " in_tc_position = in_v_position;\n"
361 << "}\n";
362
363 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
364 }
365
366 // Tessellation control shader
367 {
368 const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
369
370 std::ostringstream src;
371 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
372 << "#extension GL_EXT_tessellation_shader : require\n"
373 << "\n"
374 << "layout(vertices = " << numVertices << ") out;\n"
375 << "\n"
376 << getTessLevelsSSBODeclaration() << "\n"
377 << "layout(location = 0) in highp vec2 in_tc_position[];\n"
378 << "layout(location = 0) out highp vec2 in_te_position[];\n"
379 << "\n"
380 << "void main (void)\n"
381 << "{\n"
382 << " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
383 << "\n"
384 << " gl_TessLevelInner[0] = sb_levels.inner0;\n"
385 << " gl_TessLevelInner[1] = sb_levels.inner1;\n"
386 << "\n"
387 << " gl_TessLevelOuter[0] = sb_levels.outer0;\n"
388 << " gl_TessLevelOuter[1] = sb_levels.outer1;\n"
389 << " gl_TessLevelOuter[2] = sb_levels.outer2;\n"
390 << " gl_TessLevelOuter[3] = sb_levels.outer3;\n"
391 << "}\n";
392
393 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
394 }
395
396 // Fragment shader
397 {
398 std::ostringstream src;
399 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
400 << "\n"
401 << "layout(location = 0) in highp vec4 in_f_color;\n"
402 << "layout(location = 0) out mediump vec4 o_color;\n"
403 << "\n"
404 << "void main (void)\n"
405 << "{\n"
406 << " o_color = in_f_color;\n"
407 << "}\n";
408
409 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
410 }
411 }
412
initProgramsFillCoverCase(vk::SourceCollections & programCollection,const CaseDefinition caseDef)413 void initProgramsFillCoverCase(vk::SourceCollections &programCollection, const CaseDefinition caseDef)
414 {
415 DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
416
417 initCommonPrograms(programCollection, caseDef);
418
419 // Tessellation evaluation shader
420 {
421 std::ostringstream src;
422 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
423 << "#extension GL_EXT_tessellation_shader : require\n"
424 << "\n"
425 << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
426 << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
427 << "\n"
428 << "layout(location = 0) in highp vec2 in_te_position[];\n"
429 << "layout(location = 0) out highp vec4 in_f_color;\n"
430 << "\n"
431 << "void main (void)\n"
432 << "{\n"
433 << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
434 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
435 " highp vec2 corner0 = in_te_position[0];\n"
436 " highp vec2 corner1 = in_te_position[1];\n"
437 " highp vec2 corner2 = in_te_position[2];\n"
438 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
439 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
440 " highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
441 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
442 " gl_Position = vec4(pos, 0.0, 1.0);\n" :
443 caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
444 " highp vec2 corner0 = in_te_position[0];\n"
445 " highp vec2 corner1 = in_te_position[1];\n"
446 " highp vec2 corner2 = in_te_position[2];\n"
447 " highp vec2 corner3 = in_te_position[3];\n"
448 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
449 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
450 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
451 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
452 " highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
453 " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
454 " highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
455 " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
456 " gl_Position = vec4(pos, 0.0, 1.0);\n" :
457 "")
458 << " in_f_color = vec4(1.0);\n"
459 << "}\n";
460
461 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
462 }
463 }
464
initProgramsFillNonOverlapCase(vk::SourceCollections & programCollection,const CaseDefinition caseDef)465 void initProgramsFillNonOverlapCase(vk::SourceCollections &programCollection, const CaseDefinition caseDef)
466 {
467 DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
468
469 initCommonPrograms(programCollection, caseDef);
470
471 // Tessellation evaluation shader
472 {
473 std::ostringstream src;
474 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
475 << "#extension GL_EXT_tessellation_shader : require\n"
476 << "\n"
477 << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
478 << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
479 << "\n"
480 << getTessLevelsSSBODeclaration() << "\n"
481 << "layout(location = 0) in highp vec2 in_te_position[];\n"
482 << "layout(location = 0) out highp vec4 in_f_color;\n"
483 << "\n"
484 << "void main (void)\n"
485 << "{\n"
486 << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
487 " highp vec2 corner0 = in_te_position[0];\n"
488 " highp vec2 corner1 = in_te_position[1];\n"
489 " highp vec2 corner2 = in_te_position[2];\n"
490 " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
491 " gl_Position = vec4(pos, 0.0, 1.0);\n"
492 " highp int numConcentricTriangles = int(round(sb_levels.inner0)) / 2 + 1;\n"
493 " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
494 " highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
495 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
496 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
497 " : vec4(0.0, 0.0, 1.0, 1.0);\n" :
498 caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
499 " highp vec2 corner0 = in_te_position[0];\n"
500 " highp vec2 corner1 = in_te_position[1];\n"
501 " highp vec2 corner2 = in_te_position[2];\n"
502 " highp vec2 corner3 = in_te_position[3];\n"
503 " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
504 " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
505 " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
506 " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
507 " gl_Position = vec4(pos, 0.0, 1.0);\n"
508 " highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * sb_levels.inner0));\n"
509 " highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * sb_levels.inner1));\n"
510 " highp int phase = min(phaseX, phaseY) % 3;\n"
511 " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
512 " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
513 " : vec4(0.0, 0.0, 1.0, 1.0);\n" :
514 "")
515 << "}\n";
516
517 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
518 }
519 }
520
initProgramsIsolinesCase(vk::SourceCollections & programCollection,const CaseDefinition caseDef)521 void initProgramsIsolinesCase(vk::SourceCollections &programCollection, const CaseDefinition caseDef)
522 {
523 DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_ISOLINES);
524
525 initCommonPrograms(programCollection, caseDef);
526
527 // Tessellation evaluation shader
528 {
529 std::ostringstream src;
530 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
531 << "#extension GL_EXT_tessellation_shader : require\n"
532 << "\n"
533 << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
534 << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
535 << "\n"
536 << getTessLevelsSSBODeclaration() << "\n"
537 << "layout(location = 0) in highp vec2 in_te_position[];\n"
538 << "layout(location = 0) out highp vec4 in_f_color;\n"
539 << "\n"
540 << "void main (void)\n"
541 << "{\n"
542 << " highp vec2 corner0 = in_te_position[0];\n"
543 << " highp vec2 corner1 = in_te_position[1];\n"
544 << " highp vec2 corner2 = in_te_position[2];\n"
545 << " highp vec2 corner3 = in_te_position[3];\n"
546 << " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
547 << " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
548 << " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
549 << " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
550 << " pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
551 << " gl_Position = vec4(pos, 0.0, 1.0);\n"
552 << " highp int phaseX = int(round(gl_TessCoord.x*sb_levels.outer1));\n"
553 << " highp int phaseY = int(round(gl_TessCoord.y*sb_levels.outer0));\n"
554 << " highp int phase = (phaseX + phaseY) % 3;\n"
555 << " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
556 << " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
557 << " : vec4(0.0, 0.0, 1.0, 1.0);\n"
558 << "}\n";
559
560 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
561 }
562 }
563
getReferenceImagePathPrefix(const std::string & caseName)564 inline std::string getReferenceImagePathPrefix(const std::string &caseName)
565 {
566 return "vulkan/data/tessellation/" + caseName + "_ref";
567 }
568
569 struct TessStateSwitchParams
570 {
571 const std::pair<TessPrimitiveType, TessPrimitiveType> patchTypes;
572 const std::pair<SpacingMode, SpacingMode> spacing;
573 const std::pair<VkTessellationDomainOrigin, VkTessellationDomainOrigin> domainOrigin;
574 const bool geometryShader;
575
nonDefaultDomainOriginvkt::tessellation::__anon82f050140111::TessStateSwitchParams576 bool nonDefaultDomainOrigin(void) const
577 {
578 return (domainOrigin.first != VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT ||
579 domainOrigin.second != VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT);
580 }
581 };
582
583 class TessStateSwitchInstance : public vkt::TestInstance
584 {
585 public:
TessStateSwitchInstance(Context & context,const TessStateSwitchParams & params)586 TessStateSwitchInstance(Context &context, const TessStateSwitchParams ¶ms)
587 : vkt::TestInstance(context)
588 , m_params(params)
589 {
590 }
591
~TessStateSwitchInstance(void)592 virtual ~TessStateSwitchInstance(void)
593 {
594 }
595
596 tcu::TestStatus iterate(void);
597
598 protected:
599 const TessStateSwitchParams m_params;
600 };
601
602 class TessStateSwitchCase : public vkt::TestCase
603 {
604 public:
TessStateSwitchCase(tcu::TestContext & testCtx,const std::string & name,const TessStateSwitchParams & params)605 TessStateSwitchCase(tcu::TestContext &testCtx, const std::string &name, const TessStateSwitchParams ¶ms)
606 : vkt::TestCase(testCtx, name)
607 , m_params(params)
608 {
609 }
610
~TessStateSwitchCase(void)611 virtual ~TessStateSwitchCase(void)
612 {
613 }
614
615 void checkSupport(Context &context) const;
616 void initPrograms(vk::SourceCollections &programCollection) const;
createInstance(Context & context) const617 TestInstance *createInstance(Context &context) const
618 {
619 return new TessStateSwitchInstance(context, m_params);
620 }
621
622 protected:
623 const TessStateSwitchParams m_params;
624 };
625
checkSupport(Context & context) const626 void TessStateSwitchCase::checkSupport(Context &context) const
627 {
628 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
629
630 if (m_params.geometryShader)
631 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
632
633 if (m_params.nonDefaultDomainOrigin())
634 context.requireDeviceFunctionality("VK_KHR_maintenance2");
635 }
636
initPrograms(vk::SourceCollections & programCollection) const637 void TessStateSwitchCase::initPrograms(vk::SourceCollections &programCollection) const
638 {
639 std::ostringstream vert;
640 vert << "#version 460\n"
641 << "layout (location=0) in vec4 inPos;\n"
642 << "layout (push_constant, std430) uniform PushConstantBlock { vec2 offset; } pc;\n"
643 << "out gl_PerVertex\n"
644 << "{\n"
645 << " vec4 gl_Position;\n"
646 << "};\n"
647 << "void main() {\n"
648 << " gl_Position = inPos + vec4(pc.offset, 0.0, 0.0);\n"
649 << "}\n";
650 programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
651
652 if (m_params.geometryShader)
653 {
654 std::ostringstream geom;
655 geom << "#version 460\n"
656 << "layout (triangles) in;\n"
657 << "layout (triangle_strip, max_vertices=3) out;\n"
658 << "in gl_PerVertex\n"
659 << "{\n"
660 << " vec4 gl_Position;\n"
661 << "} gl_in[3];\n"
662 << "out gl_PerVertex\n"
663 << "{\n"
664 << " vec4 gl_Position;\n"
665 << "};\n"
666 << "void main() {\n"
667 << " gl_Position = gl_in[0].gl_Position; EmitVertex();\n"
668 << " gl_Position = gl_in[1].gl_Position; EmitVertex();\n"
669 << " gl_Position = gl_in[2].gl_Position; EmitVertex();\n"
670 << " gl_PrimitiveID = gl_PrimitiveIDIn; EndPrimitive();\n"
671 << "}\n";
672 programCollection.glslSources.add("geom") << glu::GeometrySource(geom.str());
673 }
674
675 const auto even = (m_params.spacing.second == SPACINGMODE_FRACTIONAL_EVEN);
676 const auto extraLevel = (even ? "1.0" : "0.0");
677
678 std::ostringstream tesc;
679 tesc << "#version 460\n"
680 << "layout (vertices=4) out;\n"
681 << "in gl_PerVertex\n"
682 << "{\n"
683 << " vec4 gl_Position;\n"
684 << "} gl_in[gl_MaxPatchVertices];\n"
685 << "out gl_PerVertex\n"
686 << "{\n"
687 << " vec4 gl_Position;\n"
688 << "} gl_out[];\n"
689 << "void main() {\n"
690 << " const float extraLevel = " << extraLevel << ";\n"
691 << " gl_TessLevelInner[0] = 10.0 + extraLevel;\n"
692 << " gl_TessLevelInner[1] = 10.0 + extraLevel;\n"
693 << " gl_TessLevelOuter[0] = 50.0 + extraLevel;\n"
694 << " gl_TessLevelOuter[1] = 40.0 + extraLevel;\n"
695 << " gl_TessLevelOuter[2] = 30.0 + extraLevel;\n"
696 << " gl_TessLevelOuter[3] = 20.0 + extraLevel;\n"
697 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
698 << "}\n";
699 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(tesc.str());
700
701 for (uint32_t i = 0u; i < 2u; ++i)
702 {
703 const auto &primType = ((i == 0u) ? m_params.patchTypes.first : m_params.patchTypes.second);
704 const auto &spacing = ((i == 0u) ? m_params.spacing.first : m_params.spacing.second);
705
706 std::ostringstream tese;
707 tese << "#version 460\n"
708 << "layout (" << getTessPrimitiveTypeShaderName(primType) << ", " << getSpacingModeShaderName(spacing)
709 << ", ccw) in;\n"
710 << "in gl_PerVertex\n"
711 << "{\n"
712 << " vec4 gl_Position;\n"
713 << "} gl_in[gl_MaxPatchVertices];\n"
714 << "out gl_PerVertex\n"
715 << "{\n"
716 << " vec4 gl_Position;\n"
717 << "};\n"
718 << "\n"
719 << "// This assumes 2D, calculates barycentrics for point p inside triangle (a, b, c)\n"
720 << "vec3 calcBaryCoords(vec2 p, vec2 a, vec2 b, vec2 c)\n"
721 << "{\n"
722 << " const vec2 v0 = b - a;\n"
723 << " const vec2 v1 = c - a;\n"
724 << " const vec2 v2 = p - a;\n"
725 << "\n"
726 << " const float den = v0.x * v1.y - v1.x * v0.y;\n"
727 << " const float v = (v2.x * v1.y - v1.x * v2.y) / den;\n"
728 << " const float w = (v0.x * v2.y - v2.x * v0.y) / den;\n"
729 << " const float u = 1.0 - v - w;\n"
730 << "\n"
731 << " return vec3(u, v, w);\n"
732 << "}\n"
733 << "\n"
734 << "void main() {\n"
735 << ((primType == TESSPRIMITIVETYPE_QUADS)
736 // For quads.
737 ?
738 " const float u = gl_TessCoord.x;\n"
739 " const float v = gl_TessCoord.y;\n"
740 " gl_Position = (1 - u) * (1 - v) * gl_in[0].gl_Position + (1 - u) * v * gl_in[1].gl_Position "
741 "+ u * (1 - v) * gl_in[2].gl_Position + u * v * gl_in[3].gl_Position;\n"
742 // For triangles.
743 :
744 " // We have a patch with 4 corners (v0,v1,v2,v3), but triangle-based tessellation.\n"
745 " // Lets suppose the triangle covers half the patch (triangle v0,v2,v1).\n"
746 " // Expand the triangle by virtually grabbing it from the midpoint between v1 and v2 (which "
747 "should fall in the middle of the patch) and stretching that point to the fourth corner (v3).\n"
748 " const vec4 origpoint = (gl_TessCoord.x * gl_in[0].gl_Position) +\n"
749 " (gl_TessCoord.y * gl_in[2].gl_Position) +\n"
750 " (gl_TessCoord.z * gl_in[1].gl_Position);\n"
751 " const vec4 midpoint = 0.5 * gl_in[1].gl_Position + 0.5 * gl_in[2].gl_Position;\n"
752 "\n"
753 " // Find out if it falls on left or right side of the triangle.\n"
754 " vec4 halfTriangle[3];\n"
755 " vec4 stretchedHalf[3];\n"
756 "\n"
757 " if (gl_TessCoord.z >= gl_TessCoord.y)\n"
758 " {\n"
759 " halfTriangle[0] = gl_in[0].gl_Position;\n"
760 " halfTriangle[1] = midpoint;\n"
761 " halfTriangle[2] = gl_in[1].gl_Position;\n"
762 "\n"
763 " stretchedHalf[0] = gl_in[0].gl_Position;\n"
764 " stretchedHalf[1] = gl_in[3].gl_Position;\n"
765 " stretchedHalf[2] = gl_in[1].gl_Position;\n"
766 " }\n"
767 " else\n"
768 " {\n"
769 " halfTriangle[0] = gl_in[0].gl_Position;\n"
770 " halfTriangle[1] = gl_in[2].gl_Position;\n"
771 " halfTriangle[2] = midpoint;\n"
772 "\n"
773 " stretchedHalf[0] = gl_in[0].gl_Position;\n"
774 " stretchedHalf[1] = gl_in[2].gl_Position;\n"
775 " stretchedHalf[2] = gl_in[3].gl_Position;\n"
776 " }\n"
777 "\n"
778 " // Calculate the barycentric coordinates for the left or right sides.\n"
779 " vec3 sideBaryCoord = calcBaryCoords(origpoint.xy, halfTriangle[0].xy, halfTriangle[1].xy, "
780 "halfTriangle[2].xy);\n"
781 "\n"
782 " // Move the point by stretching the half triangle and dragging the midpoint vertex to v3.\n"
783 " gl_Position = sideBaryCoord.x * stretchedHalf[0] + sideBaryCoord.y * stretchedHalf[1] + "
784 "sideBaryCoord.z * stretchedHalf[2];\n")
785 << "}\n";
786 programCollection.glslSources.add("tese" + std::to_string(i)) << glu::TessellationEvaluationSource(tese.str());
787 }
788
789 std::ostringstream frag;
790 frag << "#version 460\n"
791 << "layout (location=0) out vec4 outColor;\n"
792 << "void main() {\n"
793 << " outColor = vec4(0.5, 0.5, 0.5, 1.0);\n"
794 << "}\n";
795 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
796 }
797
iterate(void)798 tcu::TestStatus TessStateSwitchInstance::iterate(void)
799 {
800 const auto &ctx = m_context.getContextCommonData();
801 const tcu::IVec3 fbExtent(128, 128, 1);
802 const auto vkExtent = makeExtent3D(fbExtent);
803 const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
804 const auto tcuFormat = mapVkFormat(colorFormat);
805 const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
806 const auto imageType = VK_IMAGE_TYPE_2D;
807 const auto colorSRR = makeDefaultImageSubresourceRange();
808 const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
809
810 ImageWithBuffer referenceBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, imageType,
811 colorSRR);
812 ImageWithBuffer resultBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, imageType,
813 colorSRR);
814
815 // Vertex buffer containing a single full-screen patch.
816 const std::vector<tcu::Vec4> vertices{
817 tcu::Vec4(-1.0f, -1.0f, 0.0f, 1.0f),
818 tcu::Vec4(-1.0f, 1.0f, 0.0f, 1.0f),
819 tcu::Vec4(1.0f, -1.0f, 0.0f, 1.0f),
820 tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f),
821 };
822 const auto vertexCount = de::sizeU32(vertices);
823 const auto patchControlPoints = vertexCount;
824
825 const auto vertexBufferSize = static_cast<VkDeviceSize>(de::dataSize(vertices));
826 const auto vertexBufferInfo = makeBufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
827 BufferWithMemory vertexBuffer(ctx.vkd, ctx.device, ctx.allocator, vertexBufferInfo, MemoryRequirement::HostVisible);
828 auto &vertexBufferAlloc = vertexBuffer.getAllocation();
829 void *vertexBufferData = vertexBufferAlloc.getHostPtr();
830 const auto vertexBufferOffset = static_cast<VkDeviceSize>(0);
831
832 deMemcpy(vertexBufferData, de::dataOrNull(vertices), de::dataSize(vertices));
833 flushAlloc(ctx.vkd, ctx.device, vertexBufferAlloc);
834
835 const auto pcSize = static_cast<uint32_t>(sizeof(tcu::Vec2));
836 const auto pcStages = static_cast<VkShaderStageFlags>(VK_SHADER_STAGE_VERTEX_BIT);
837 const auto pcRange = makePushConstantRange(pcStages, 0u, pcSize);
838
839 const auto pipelineLayout = makePipelineLayout(ctx.vkd, ctx.device, VK_NULL_HANDLE, &pcRange);
840
841 const auto renderPass = makeRenderPass(ctx.vkd, ctx.device, colorFormat);
842
843 // Framebuffers.
844 const auto framebuffer0 = makeFramebuffer(ctx.vkd, ctx.device, *renderPass, referenceBuffer.getImageView(),
845 vkExtent.width, vkExtent.height);
846 const auto framebuffer1 =
847 makeFramebuffer(ctx.vkd, ctx.device, *renderPass, resultBuffer.getImageView(), vkExtent.width, vkExtent.height);
848
849 // Viewport and scissor.
850 const std::vector<VkViewport> viewports(1u, makeViewport(fbExtent));
851 const std::vector<VkRect2D> scissors(1u, makeRect2D(fbExtent));
852
853 // Shaders.
854 const auto &binaries = m_context.getBinaryCollection();
855 const auto vertModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
856 const auto tescModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("tesc"));
857 const auto teseModule0 = createShaderModule(ctx.vkd, ctx.device, binaries.get("tese0"));
858 const auto teseModule1 = createShaderModule(ctx.vkd, ctx.device, binaries.get("tese1"));
859 const auto geomModule = (m_params.geometryShader ? createShaderModule(ctx.vkd, ctx.device, binaries.get("geom")) :
860 Move<VkShaderModule>());
861 const auto fragModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
862
863 const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
864 VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
865 nullptr, // const void* pNext;
866 0u, // VkPipelineInputAssemblyStateCreateFlags flags;
867 VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, // VkPrimitiveTopology topology;
868 VK_FALSE, // VkBool32 primitiveRestartEnable;
869 };
870
871 VkPipelineTessellationDomainOriginStateCreateInfo domainOriginStateCreateInfo = {
872 VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, // VkStructureType sType;
873 nullptr, // const void* pNext;
874 m_params.domainOrigin.first, // VkTessellationDomainOrigin domainOrigin;
875 };
876
877 const auto tessPNext = (m_params.nonDefaultDomainOrigin() ? &domainOriginStateCreateInfo : nullptr);
878 const VkPipelineTessellationStateCreateInfo tessellationStateCreateInfo = {
879 VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, // VkStructureType sType;
880 tessPNext, // const void* pNext;
881 0u, // VkPipelineTessellationStateCreateFlags flags;
882 patchControlPoints, // uint32_t patchControlPoints;
883 };
884
885 const VkPipelineViewportStateCreateInfo viewportStateCreateInfo = {
886 VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
887 nullptr, // const void* pNext;
888 0u, // VkPipelineViewportStateCreateFlags flags;
889 de::sizeU32(viewports), // uint32_t viewportCount;
890 de::dataOrNull(viewports), // const VkViewport* pViewports;
891 de::sizeU32(scissors), // uint32_t scissorCount;
892 de::dataOrNull(scissors), // const VkRect2D* pScissors;
893 };
894
895 // In the rasterization parameters, use wireframe mode to see each triangle if possible.
896 // This makes the test harder to pass by mistake.
897 // We also cull back faces, which will help test domain origin.
898 // The front face changes with the domain origin.
899 const auto frontFace =
900 ((m_params.domainOrigin.second == VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT) ?
901 VK_FRONT_FACE_COUNTER_CLOCKWISE // With the default value it's as specified in the shader.
902 :
903 VK_FRONT_FACE_CLOCKWISE); // Otherwise the winding order changes.
904 const auto polygonMode =
905 ((m_context.getDeviceFeatures().fillModeNonSolid) ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL);
906 const VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
907 VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
908 nullptr, // const void* pNext;
909 0u, // VkPipelineRasterizationStateCreateFlags flags;
910 VK_FALSE, // VkBool32 depthClampEnable;
911 VK_FALSE, // VkBool32 rasterizerDiscardEnable;
912 polygonMode, // VkPolygonMode polygonMode;
913 VK_CULL_MODE_BACK_BIT, // VkCullModeFlags cullMode;
914 frontFace, // VkFrontFace frontFace;
915 VK_FALSE, // VkBool32 depthBiasEnable;
916 0.0f, // float depthBiasConstantFactor;
917 0.0f, // float depthBiasClamp;
918 0.0f, // float depthBiasSlopeFactor;
919 1.0f, // float lineWidth;
920 };
921
922 // Create two pipelines varying the tessellation evaluation module.
923 const auto pipeline0 =
924 makeGraphicsPipeline(ctx.vkd, ctx.device, *pipelineLayout, *vertModule, *tescModule, *teseModule0, *geomModule,
925 *fragModule, *renderPass, 0u, nullptr, &inputAssemblyStateCreateInfo,
926 &tessellationStateCreateInfo, &viewportStateCreateInfo, &rasterizationStateCreateInfo);
927
928 domainOriginStateCreateInfo.domainOrigin = m_params.domainOrigin.second;
929
930 const auto pipeline1 =
931 makeGraphicsPipeline(ctx.vkd, ctx.device, *pipelineLayout, *vertModule, *tescModule, *teseModule1, *geomModule,
932 *fragModule, *renderPass, 0u, nullptr, &inputAssemblyStateCreateInfo,
933 &tessellationStateCreateInfo, &viewportStateCreateInfo, &rasterizationStateCreateInfo);
934
935 const auto cmdPool = makeCommandPool(ctx.vkd, ctx.device, ctx.qfIndex);
936 const auto cmdBufferRef = allocateCommandBuffer(ctx.vkd, ctx.device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
937 const auto cmdBufferRes = allocateCommandBuffer(ctx.vkd, ctx.device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
938
939 const tcu::Vec2 noOffset(0.0f, 0.0f);
940 const tcu::Vec2 offscreenOffset(50.0f, 50.0f);
941 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
942
943 // Reference image.
944 beginCommandBuffer(ctx.vkd, *cmdBufferRef);
945 beginRenderPass(ctx.vkd, *cmdBufferRef, *renderPass, *framebuffer0, scissors.at(0u), clearColor);
946 ctx.vkd.cmdBindVertexBuffers(*cmdBufferRef, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
947 ctx.vkd.cmdBindPipeline(*cmdBufferRef, bindPoint, *pipeline1);
948 ctx.vkd.cmdPushConstants(*cmdBufferRef, *pipelineLayout, pcStages, 0u, pcSize, &noOffset);
949 ctx.vkd.cmdDraw(*cmdBufferRef, vertexCount, 1u, 0u, 0u);
950 endRenderPass(ctx.vkd, *cmdBufferRef);
951 copyImageToBuffer(ctx.vkd, *cmdBufferRef, referenceBuffer.getImage(), referenceBuffer.getBuffer(),
952 fbExtent.swizzle(0, 1), VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
953 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1u, VK_IMAGE_ASPECT_COLOR_BIT,
954 VK_IMAGE_ASPECT_COLOR_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
955 endCommandBuffer(ctx.vkd, *cmdBufferRef);
956 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, *cmdBufferRef);
957
958 // Result image.
959 beginCommandBuffer(ctx.vkd, *cmdBufferRes);
960 beginRenderPass(ctx.vkd, *cmdBufferRes, *renderPass, *framebuffer1, scissors.at(0u), clearColor);
961 ctx.vkd.cmdBindVertexBuffers(*cmdBufferRes, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
962 // Draw offscreen first to force tessellation state emission.
963 ctx.vkd.cmdBindPipeline(*cmdBufferRes, bindPoint, *pipeline0);
964 ctx.vkd.cmdPushConstants(*cmdBufferRes, *pipelineLayout, pcStages, 0u, pcSize, &offscreenOffset);
965 ctx.vkd.cmdDraw(*cmdBufferRes, vertexCount, 1u, 0u, 0u);
966 // Draw on screen second changing some tessellation state.
967 ctx.vkd.cmdBindPipeline(*cmdBufferRes, bindPoint, *pipeline1);
968 ctx.vkd.cmdPushConstants(*cmdBufferRes, *pipelineLayout, pcStages, 0u, pcSize, &noOffset);
969 ctx.vkd.cmdDraw(*cmdBufferRes, vertexCount, 1u, 0u, 0u);
970 endRenderPass(ctx.vkd, *cmdBufferRes);
971 copyImageToBuffer(ctx.vkd, *cmdBufferRes, resultBuffer.getImage(), resultBuffer.getBuffer(), fbExtent.swizzle(0, 1),
972 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1u,
973 VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT,
974 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
975 endCommandBuffer(ctx.vkd, *cmdBufferRes);
976 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, *cmdBufferRes);
977
978 invalidateAlloc(ctx.vkd, ctx.device, referenceBuffer.getBufferAllocation());
979 invalidateAlloc(ctx.vkd, ctx.device, resultBuffer.getBufferAllocation());
980
981 tcu::ConstPixelBufferAccess referenceAccess(tcuFormat, fbExtent,
982 referenceBuffer.getBufferAllocation().getHostPtr());
983 tcu::ConstPixelBufferAccess resultAccess(tcuFormat, fbExtent, resultBuffer.getBufferAllocation().getHostPtr());
984
985 auto &log = m_context.getTestContext().getLog();
986 const float threshold = 0.005f; // 1/255 < 0.005 < 2/255
987 const tcu::Vec4 thresholdVec(threshold, threshold, threshold, 0.0f);
988
989 if (!tcu::floatThresholdCompare(log, "Result", "", referenceAccess, resultAccess, thresholdVec,
990 tcu::COMPARE_LOG_ON_ERROR))
991 return tcu::TestStatus::fail("Color result does not match reference image -- check log for details");
992
993 // Render pass and framebuffers.const DeviceCoreFeature requiredDeviceCoreFeature
994 return tcu::TestStatus::pass("Pass");
995 }
996
getDomainOriginName(VkTessellationDomainOrigin value)997 std::string getDomainOriginName(VkTessellationDomainOrigin value)
998 {
999 static const size_t prefixLen = strlen("VK_TESSELLATION_DOMAIN_ORIGIN_");
1000 std::string nameStr = getTessellationDomainOriginName(value);
1001
1002 return de::toLower(nameStr.substr(prefixLen));
1003 }
1004
1005 } // namespace
1006
1007 //! These tests correspond to dEQP-GLES31.functional.tessellation.misc_draw.*
createMiscDrawTests(tcu::TestContext & testCtx)1008 tcu::TestCaseGroup *createMiscDrawTests(tcu::TestContext &testCtx)
1009 {
1010 // Miscellaneous draw-result-verifying cases
1011 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "misc_draw"));
1012
1013 static const TessPrimitiveType primitivesNoIsolines[] = {
1014 TESSPRIMITIVETYPE_TRIANGLES,
1015 TESSPRIMITIVETYPE_QUADS,
1016 };
1017
1018 // Triangle fill case
1019 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
1020 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
1021 {
1022 const TessPrimitiveType primitiveType = primitivesNoIsolines[primitiveTypeNdx];
1023 const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
1024 const std::string caseName = std::string() + "fill_cover_" + getTessPrimitiveTypeShaderName(primitiveType) +
1025 "_" + getSpacingModeShaderName(spacingMode);
1026
1027 // Check that there are no obvious gaps in the triangle-filled area of a tessellated shape
1028 addFunctionCaseWithPrograms(
1029 group.get(), caseName, initProgramsFillCoverCase, runTest,
1030 makeCaseDefinition(primitiveType, spacingMode, getReferenceImagePathPrefix(caseName)));
1031 }
1032
1033 // Triangle non-overlap case
1034 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
1035 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
1036 {
1037 const TessPrimitiveType primitiveType = primitivesNoIsolines[primitiveTypeNdx];
1038 const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
1039 const std::string caseName = std::string() + "fill_overlap_" +
1040 getTessPrimitiveTypeShaderName(primitiveType) + "_" +
1041 getSpacingModeShaderName(spacingMode);
1042
1043 // Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape
1044 addFunctionCaseWithPrograms(
1045 group.get(), caseName, initProgramsFillNonOverlapCase, runTest,
1046 makeCaseDefinition(primitiveType, spacingMode, getReferenceImagePathPrefix(caseName)));
1047 }
1048
1049 // Isolines
1050 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
1051 {
1052 const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
1053 const std::string caseName = std::string() + "isolines_" + getSpacingModeShaderName(spacingMode);
1054
1055 // Basic isolines render test
1056 addFunctionCaseWithPrograms(
1057 group.get(), caseName, checkSupportCase, initProgramsIsolinesCase, runTest,
1058 makeCaseDefinition(TESSPRIMITIVETYPE_ISOLINES, spacingMode, getReferenceImagePathPrefix(caseName)));
1059 }
1060
1061 // Test switching tessellation parameters on the fly.
1062 for (const auto &geometryShader : {false, true})
1063 {
1064 const auto nameSuffix = (geometryShader ? "_with_geom_shader" : "");
1065
1066 static const VkTessellationDomainOrigin domainOrigins[] = {
1067 VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT,
1068 VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT,
1069 };
1070
1071 for (const auto &firstPrimitiveType : primitivesNoIsolines)
1072 for (const auto &secondPrimitiveType : primitivesNoIsolines)
1073 {
1074 if (firstPrimitiveType == secondPrimitiveType)
1075 continue;
1076
1077 const TessStateSwitchParams params{
1078 std::make_pair(firstPrimitiveType, secondPrimitiveType),
1079 std::make_pair(SPACINGMODE_EQUAL, SPACINGMODE_EQUAL),
1080 std::make_pair(VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT),
1081 geometryShader,
1082 };
1083
1084 const auto testName = std::string("switch_primitive_") +
1085 getTessPrimitiveTypeShaderName(params.patchTypes.first) + "_to_" +
1086 getTessPrimitiveTypeShaderName(params.patchTypes.second) + nameSuffix;
1087 group->addChild(new TessStateSwitchCase(testCtx, testName, params));
1088 }
1089
1090 for (const auto &firstDomainOrigin : domainOrigins)
1091 for (const auto &secondDomainOrigin : domainOrigins)
1092 {
1093 if (firstDomainOrigin == secondDomainOrigin)
1094 continue;
1095
1096 const TessStateSwitchParams params{
1097 std::make_pair(TESSPRIMITIVETYPE_QUADS, TESSPRIMITIVETYPE_QUADS),
1098 std::make_pair(SPACINGMODE_EQUAL, SPACINGMODE_EQUAL),
1099 std::make_pair(firstDomainOrigin, secondDomainOrigin),
1100 geometryShader,
1101 };
1102
1103 const auto testName = std::string("switch_domain_origin_") +
1104 getDomainOriginName(params.domainOrigin.first) + "_to_" +
1105 getDomainOriginName(params.domainOrigin.second) + nameSuffix;
1106 group->addChild(new TessStateSwitchCase(testCtx, testName, params));
1107 }
1108
1109 for (int firstSpacingModeNdx = 0; firstSpacingModeNdx < SPACINGMODE_LAST; ++firstSpacingModeNdx)
1110 for (int secondSpacingModeNdx = 0; secondSpacingModeNdx < SPACINGMODE_LAST; ++secondSpacingModeNdx)
1111 {
1112 if (firstSpacingModeNdx == secondSpacingModeNdx)
1113 continue;
1114
1115 const SpacingMode firstSpacingMode = static_cast<SpacingMode>(firstSpacingModeNdx);
1116 const SpacingMode secondSpacingMode = static_cast<SpacingMode>(secondSpacingModeNdx);
1117
1118 const TessStateSwitchParams params{
1119 std::make_pair(TESSPRIMITIVETYPE_QUADS, TESSPRIMITIVETYPE_QUADS),
1120 std::make_pair(firstSpacingMode, secondSpacingMode),
1121 std::make_pair(VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT),
1122 geometryShader,
1123 };
1124
1125 const auto testName = std::string("switch_spacing_mode_") +
1126 getSpacingModeShaderName(params.spacing.first) + "_to_" +
1127 getSpacingModeShaderName(params.spacing.second) + nameSuffix;
1128 group->addChild(new TessStateSwitchCase(testCtx, testName, params));
1129 }
1130 }
1131
1132 return group.release();
1133 }
1134
1135 } // namespace tessellation
1136 } // namespace vkt
1137