1 #ifndef _VKTTESSELLATIONUTIL_HPP
2 #define _VKTTESSELLATIONUTIL_HPP
3 /*------------------------------------------------------------------------
4 * Vulkan Conformance Tests
5 * ------------------------
6 *
7 * Copyright (c) 2014 The Android Open Source Project
8 * Copyright (c) 2016 The Khronos Group Inc.
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 *
22 *//*!
23 * \file
24 * \brief Tessellation Utilities
25 *//*--------------------------------------------------------------------*/
26
27 #include "vkDefs.hpp"
28 #include "vkMemUtil.hpp"
29 #include "vkRef.hpp"
30 #include "vkPrograms.hpp"
31 #include "vkRefUtil.hpp"
32 #include "vkQueryUtil.hpp"
33 #include "vkObjUtil.hpp"
34 #include "vktTestCase.hpp"
35
36 #include "tcuVector.hpp"
37 #include "tcuMaybe.hpp"
38
39 #include "deStringUtil.hpp"
40
41 #include <algorithm> // sort
42 #include <iterator> // distance
43
44 namespace vkt
45 {
46 namespace tessellation
47 {
48
49 class GraphicsPipelineBuilder
50 {
51 public:
GraphicsPipelineBuilder(void)52 GraphicsPipelineBuilder(void)
53 : m_renderSize(0, 0)
54 , m_shaderStageFlags(0u)
55 , m_cullModeFlags(vk::VK_CULL_MODE_NONE)
56 , m_frontFace(vk::VK_FRONT_FACE_COUNTER_CLOCKWISE)
57 , m_patchControlPoints(1u)
58 , m_blendEnable(false)
59 , m_primitiveTopology(vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
60 , m_tessellationDomainOrigin(tcu::Nothing)
61 {
62 }
63
setRenderSize(const tcu::IVec2 & size)64 GraphicsPipelineBuilder &setRenderSize(const tcu::IVec2 &size)
65 {
66 m_renderSize = size;
67 return *this;
68 }
69 GraphicsPipelineBuilder &setShader(const vk::DeviceInterface &vk, const vk::VkDevice device,
70 const vk::VkShaderStageFlagBits stage, const vk::ProgramBinary &binary,
71 const vk::VkSpecializationInfo *specInfo);
setPatchControlPoints(const uint32_t controlPoints)72 GraphicsPipelineBuilder &setPatchControlPoints(const uint32_t controlPoints)
73 {
74 m_patchControlPoints = controlPoints;
75 return *this;
76 }
setCullModeFlags(const vk::VkCullModeFlags cullModeFlags)77 GraphicsPipelineBuilder &setCullModeFlags(const vk::VkCullModeFlags cullModeFlags)
78 {
79 m_cullModeFlags = cullModeFlags;
80 return *this;
81 }
setFrontFace(const vk::VkFrontFace frontFace)82 GraphicsPipelineBuilder &setFrontFace(const vk::VkFrontFace frontFace)
83 {
84 m_frontFace = frontFace;
85 return *this;
86 }
setBlend(const bool enable)87 GraphicsPipelineBuilder &setBlend(const bool enable)
88 {
89 m_blendEnable = enable;
90 return *this;
91 }
92
93 //! Applies only to pipelines without tessellation shaders.
setPrimitiveTopology(const vk::VkPrimitiveTopology topology)94 GraphicsPipelineBuilder &setPrimitiveTopology(const vk::VkPrimitiveTopology topology)
95 {
96 m_primitiveTopology = topology;
97 return *this;
98 }
99
addVertexBinding(const vk::VkVertexInputBindingDescription vertexBinding)100 GraphicsPipelineBuilder &addVertexBinding(const vk::VkVertexInputBindingDescription vertexBinding)
101 {
102 m_vertexInputBindings.push_back(vertexBinding);
103 return *this;
104 }
addVertexAttribute(const vk::VkVertexInputAttributeDescription vertexAttribute)105 GraphicsPipelineBuilder &addVertexAttribute(const vk::VkVertexInputAttributeDescription vertexAttribute)
106 {
107 m_vertexInputAttributes.push_back(vertexAttribute);
108 return *this;
109 }
110
111 //! Basic vertex input configuration (uses biding 0, location 0, etc.)
112 GraphicsPipelineBuilder &setVertexInputSingleAttribute(const vk::VkFormat vertexFormat, const uint32_t stride);
113
114 //! If tessellation domain origin is set, pipeline requires VK__maintenance2
setTessellationDomainOrigin(const vk::VkTessellationDomainOrigin domainOrigin)115 GraphicsPipelineBuilder &setTessellationDomainOrigin(const vk::VkTessellationDomainOrigin domainOrigin)
116 {
117 return setTessellationDomainOrigin(tcu::just(domainOrigin));
118 }
setTessellationDomainOrigin(const tcu::Maybe<vk::VkTessellationDomainOrigin> & domainOrigin)119 GraphicsPipelineBuilder &setTessellationDomainOrigin(const tcu::Maybe<vk::VkTessellationDomainOrigin> &domainOrigin)
120 {
121 m_tessellationDomainOrigin = domainOrigin;
122 return *this;
123 }
124
125 vk::Move<vk::VkPipeline> build(const vk::DeviceInterface &vk, const vk::VkDevice device,
126 const vk::VkPipelineLayout pipelineLayout, const vk::VkRenderPass renderPass);
127
128 private:
129 tcu::IVec2 m_renderSize;
130 vk::Move<vk::VkShaderModule> m_vertexShaderModule;
131 vk::Move<vk::VkShaderModule> m_fragmentShaderModule;
132 vk::Move<vk::VkShaderModule> m_geometryShaderModule;
133 vk::Move<vk::VkShaderModule> m_tessControlShaderModule;
134 vk::Move<vk::VkShaderModule> m_tessEvaluationShaderModule;
135 std::vector<vk::VkPipelineShaderStageCreateInfo> m_shaderStages;
136 std::vector<vk::VkVertexInputBindingDescription> m_vertexInputBindings;
137 std::vector<vk::VkVertexInputAttributeDescription> m_vertexInputAttributes;
138 vk::VkShaderStageFlags m_shaderStageFlags;
139 vk::VkCullModeFlags m_cullModeFlags;
140 vk::VkFrontFace m_frontFace;
141 uint32_t m_patchControlPoints;
142 bool m_blendEnable;
143 vk::VkPrimitiveTopology m_primitiveTopology;
144 tcu::Maybe<vk::VkTessellationDomainOrigin> m_tessellationDomainOrigin;
145
146 GraphicsPipelineBuilder(const GraphicsPipelineBuilder &); // "deleted"
147 GraphicsPipelineBuilder &operator=(const GraphicsPipelineBuilder &);
148 };
149
150 struct TessLevels
151 {
152 float inner[2];
153 float outer[4];
154 };
155
156 enum TessPrimitiveType
157 {
158 TESSPRIMITIVETYPE_TRIANGLES = 0,
159 TESSPRIMITIVETYPE_QUADS,
160 TESSPRIMITIVETYPE_ISOLINES,
161
162 TESSPRIMITIVETYPE_LAST,
163 };
164
165 enum DrawType
166 {
167 DRAWTYPE_DRAW = 0,
168 DRAWTYPE_DRAW_INDIRECT,
169
170 DRAWTYPE_LAST,
171 };
172
173 enum SpacingMode
174 {
175 SPACINGMODE_EQUAL = 0,
176 SPACINGMODE_FRACTIONAL_ODD,
177 SPACINGMODE_FRACTIONAL_EVEN,
178
179 SPACINGMODE_LAST,
180 };
181
182 enum Winding
183 {
184 WINDING_CCW = 0,
185 WINDING_CW,
186
187 WINDING_LAST,
188 };
189
190 enum ShaderLanguage
191 {
192 SHADER_LANGUAGE_GLSL = 0,
193 SHADER_LANGUAGE_HLSL = 1,
194
195 SHADER_LANGUAGE_LAST,
196 };
197
198 enum FeatureFlagBits
199 {
200 FEATURE_TESSELLATION_SHADER = 1u << 0,
201 FEATURE_GEOMETRY_SHADER = 1u << 1,
202 FEATURE_SHADER_FLOAT_64 = 1u << 2,
203 FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3,
204 FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4,
205 FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5,
206 };
207 typedef uint32_t FeatureFlags;
208
209 vk::VkImageCreateInfo makeImageCreateInfo(const tcu::IVec2 &size, const vk::VkFormat format,
210 const vk::VkImageUsageFlags usage, const uint32_t numArrayLayers);
211 vk::Move<vk::VkRenderPass> makeRenderPassWithoutAttachments(const vk::DeviceInterface &vk, const vk::VkDevice device);
212 vk::VkBufferImageCopy makeBufferImageCopy(const vk::VkExtent3D extent,
213 const vk::VkImageSubresourceLayers subresourceLayers);
214 void requireFeatures(const vk::InstanceInterface &vki, const vk::VkPhysicalDevice physDevice, const FeatureFlags flags);
215 float getClampedTessLevel(const SpacingMode mode, const float tessLevel);
216 int getRoundedTessLevel(const SpacingMode mode, const float clampedTessLevel);
217 int getClampedRoundedTessLevel(const SpacingMode mode, const float tessLevel);
218 void getClampedRoundedTriangleTessLevels(const SpacingMode mode, const float *innerSrc, const float *outerSrc,
219 int *innerDst, int *outerDst);
220 void getClampedRoundedQuadTessLevels(const SpacingMode mode, const float *innerSrc, const float *outerSrc,
221 int *innerDst, int *outerDst);
222 void getClampedRoundedIsolineTessLevels(const SpacingMode mode, const float *outerSrc, int *outerDst);
223 int numOuterTessellationLevels(const TessPrimitiveType primitiveType);
224 std::string getTessellationLevelsString(const TessLevels &tessLevels, const TessPrimitiveType primitiveType);
225 std::string getTessellationLevelsString(const float *inner, const float *outer);
226 bool isPatchDiscarded(const TessPrimitiveType primitiveType, const float *outerLevels);
227 std::vector<tcu::Vec3> generateReferenceTriangleTessCoords(const SpacingMode spacingMode, const int inner,
228 const int outer0, const int outer1, const int outer2);
229 std::vector<tcu::Vec3> generateReferenceQuadTessCoords(const SpacingMode spacingMode, const int inner0,
230 const int inner1, const int outer0, const int outer1,
231 const int outer2, const int outer3);
232 std::vector<tcu::Vec3> generateReferenceIsolineTessCoords(const int outer0, const int outer1);
233 int referenceVertexCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode,
234 const float *innerLevels, const float *outerLevels);
235 int referencePrimitiveCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
236 const bool usePointMode, const float *innerLevels, const float *outerLevels);
237 int numVerticesPerPrimitive(const TessPrimitiveType primitiveType, const bool usePointMode);
238
getTessPrimitiveTypeShaderName(const TessPrimitiveType type,bool forSpirv=false)239 static inline const char *getTessPrimitiveTypeShaderName(const TessPrimitiveType type, bool forSpirv = false)
240 {
241 static std::string primitiveName[][2] = {// glsl name spirv name
242 {"triangles", "Triangles"},
243 {"quads", "Quads"},
244 {"isolines", "Isolines"}};
245
246 if (type >= TESSPRIMITIVETYPE_LAST)
247 {
248 DE_FATAL("Unexpected primitive type.");
249 return nullptr;
250 }
251
252 return primitiveName[type][forSpirv].c_str();
253 }
254
getDomainName(const TessPrimitiveType type)255 static inline const char *getDomainName(const TessPrimitiveType type)
256 {
257 switch (type)
258 {
259 case TESSPRIMITIVETYPE_TRIANGLES:
260 return "tri";
261 case TESSPRIMITIVETYPE_QUADS:
262 return "quad";
263 case TESSPRIMITIVETYPE_ISOLINES:
264 return "isoline";
265 default:
266 DE_FATAL("Unexpected primitive type.");
267 return nullptr;
268 }
269 }
270
getDrawName(const DrawType type)271 static inline const char *getDrawName(const DrawType type)
272 {
273 switch (type)
274 {
275 case DRAWTYPE_DRAW:
276 return "draw";
277 case DRAWTYPE_DRAW_INDIRECT:
278 return "draw_indirect";
279 default:
280 DE_FATAL("Unexpected draw type.");
281 return nullptr;
282 }
283 }
284
getOutputTopologyName(const TessPrimitiveType type,const Winding winding,const bool usePointMode)285 static inline const char *getOutputTopologyName(const TessPrimitiveType type, const Winding winding,
286 const bool usePointMode)
287 {
288 if (usePointMode)
289 return "point";
290 else if (type == TESSPRIMITIVETYPE_TRIANGLES || type == TESSPRIMITIVETYPE_QUADS)
291 return (winding == WINDING_CCW ? "triangle_ccw" : "triangle_cw");
292 else if (type == TESSPRIMITIVETYPE_ISOLINES)
293 return "line";
294
295 DE_FATAL("Unexpected primitive type.");
296 return nullptr;
297 }
298
getSpacingModeShaderName(SpacingMode mode,bool forSpirv=false)299 static inline const char *getSpacingModeShaderName(SpacingMode mode, bool forSpirv = false)
300 {
301 static std::string spacingName[][2] = {// glsl name spirv name
302 {"equal_spacing", "SpacingEqual"},
303 {"fractional_odd_spacing", "SpacingFractionalOdd"},
304 {"fractional_even_spacing", "SpacingFractionalEven"}};
305
306 if (mode >= SPACINGMODE_LAST)
307 {
308 DE_FATAL("Unexpected spacing type.");
309 return nullptr;
310 }
311
312 return spacingName[mode][forSpirv].c_str();
313 }
314
getPartitioningShaderName(SpacingMode mode)315 static inline const char *getPartitioningShaderName(SpacingMode mode)
316 {
317 switch (mode)
318 {
319 case SPACINGMODE_EQUAL:
320 return "integer";
321 case SPACINGMODE_FRACTIONAL_ODD:
322 return "fractional_odd";
323 case SPACINGMODE_FRACTIONAL_EVEN:
324 return "fractional_even";
325 default:
326 DE_FATAL("Unexpected spacing mode.");
327 return nullptr;
328 }
329 }
330
getWindingShaderName(const Winding winding)331 static inline const char *getWindingShaderName(const Winding winding)
332 {
333 switch (winding)
334 {
335 case WINDING_CCW:
336 return "ccw";
337 case WINDING_CW:
338 return "cw";
339 default:
340 DE_FATAL("Unexpected winding type.");
341 return nullptr;
342 }
343 }
344
getShaderLanguageName(const ShaderLanguage language)345 static inline const char *getShaderLanguageName(const ShaderLanguage language)
346 {
347 switch (language)
348 {
349 case SHADER_LANGUAGE_GLSL:
350 return "glsl";
351 case SHADER_LANGUAGE_HLSL:
352 return "hlsl";
353 default:
354 DE_FATAL("Unexpected shader language.");
355 return nullptr;
356 }
357 }
358
getGeometryShaderInputPrimitiveTypeShaderName(const TessPrimitiveType type,const bool usePointMode)359 static inline const char *getGeometryShaderInputPrimitiveTypeShaderName(const TessPrimitiveType type,
360 const bool usePointMode)
361 {
362 if (usePointMode)
363 return "points";
364
365 switch (type)
366 {
367 case TESSPRIMITIVETYPE_TRIANGLES:
368 case TESSPRIMITIVETYPE_QUADS:
369 return "triangles";
370
371 case TESSPRIMITIVETYPE_ISOLINES:
372 return "lines";
373
374 default:
375 DE_FATAL("Unexpected primitive type.");
376 return nullptr;
377 }
378 }
379
getGeometryShaderOutputPrimitiveTypeShaderName(const TessPrimitiveType type,const bool usePointMode)380 static inline const char *getGeometryShaderOutputPrimitiveTypeShaderName(const TessPrimitiveType type,
381 const bool usePointMode)
382 {
383 if (usePointMode)
384 return "points";
385
386 switch (type)
387 {
388 case TESSPRIMITIVETYPE_TRIANGLES:
389 case TESSPRIMITIVETYPE_QUADS:
390 return "triangle_strip";
391
392 case TESSPRIMITIVETYPE_ISOLINES:
393 return "line_strip";
394
395 default:
396 DE_FATAL("Unexpected primitive type.");
397 return nullptr;
398 }
399 }
400
401 #ifndef CTS_USES_VULKANSC
402
getPortability(const Context & context)403 static inline const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *getPortability(const Context &context)
404 {
405 if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset"))
406 return &context.getPortabilitySubsetFeatures();
407 return nullptr;
408 }
409
checkIsolines(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features)410 static inline void checkIsolines(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features)
411 {
412 if (!features.tessellationIsolines)
413 TCU_THROW(NotSupportedError,
414 "VK_KHR_portability_subset: Tessellation iso lines are not supported by this implementation");
415 }
416
checkPrimitive(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features,const TessPrimitiveType primitive)417 static inline void checkPrimitive(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features,
418 const TessPrimitiveType primitive)
419 {
420 if (primitive == TESSPRIMITIVETYPE_ISOLINES)
421 checkIsolines(features);
422 }
423
checkSupportPrimitive(Context & context,const TessPrimitiveType primitive)424 static inline void checkSupportPrimitive(Context &context, const TessPrimitiveType primitive)
425 {
426 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
427 checkPrimitive(*features, primitive);
428 }
429
checkPointMode(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features)430 static inline void checkPointMode(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features)
431 {
432 if (!features.tessellationPointMode)
433 TCU_THROW(NotSupportedError,
434 "VK_KHR_portability_subset: Tessellation point mode is not supported by this implementation");
435 }
436
437 #endif // CTS_USES_VULKANSC
438
439 template <typename T>
sizeInBytes(const std::vector<T> & vec)440 inline std::size_t sizeInBytes(const std::vector<T> &vec)
441 {
442 return vec.size() * sizeof(vec[0]);
443 }
444
445 template <typename T>
sorted(const std::vector<T> & unsorted)446 static std::vector<T> sorted(const std::vector<T> &unsorted)
447 {
448 std::vector<T> result = unsorted;
449 std::sort(result.begin(), result.end());
450 return result;
451 }
452
453 template <typename T, typename P>
sorted(const std::vector<T> & unsorted,P pred)454 static std::vector<T> sorted(const std::vector<T> &unsorted, P pred)
455 {
456 std::vector<T> result = unsorted;
457 std::sort(result.begin(), result.end(), pred);
458 return result;
459 }
460
461 template <typename IterT>
elemsStr(const IterT & begin,const IterT & end,int wrapLengthParam=0,int numIndentationSpaces=0)462 std::string elemsStr(const IterT &begin, const IterT &end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
463 {
464 const int bigInt = ~0u / 2;
465 const std::string baseIndentation = std::string(numIndentationSpaces, ' ');
466 const std::string deepIndentation = baseIndentation + std::string(4, ' ');
467 const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : bigInt;
468 const int length = static_cast<int>(std::distance(begin, end));
469 std::string result;
470
471 if (length > wrapLength)
472 result += "(amount: " + de::toString(length) + ") ";
473 result += std::string() + "{" + (length > wrapLength ? "\n" + deepIndentation : " ");
474
475 {
476 int index = 0;
477 for (IterT it = begin; it != end; ++it)
478 {
479 if (it != begin)
480 result += std::string() + ", " + (index % wrapLength == 0 ? "\n" + deepIndentation : "");
481 result += de::toString(*it);
482 index++;
483 }
484
485 result += length > wrapLength ? "\n" + baseIndentation : " ";
486 }
487
488 result += "}";
489 return result;
490 }
491
492 template <typename ContainerT>
containerStr(const ContainerT & c,int wrapLengthParam=0,int numIndentationSpaces=0)493 std::string containerStr(const ContainerT &c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
494 {
495 return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
496 }
497
498 //! Copy 'count' objects of type T from 'memory' into a vector.
499 //! 'offset' is the offset of first object in memory, and 'stride' is the distance between consecutive objects.
500 template <typename T>
readInterleavedData(const int count,const void * memory,const int offset,const int stride)501 std::vector<T> readInterleavedData(const int count, const void *memory, const int offset, const int stride)
502 {
503 std::vector<T> results(count);
504 const uint8_t *pData = static_cast<const uint8_t *>(memory) + offset;
505
506 for (int i = 0; i < count; ++i)
507 {
508 deMemcpy(&results[i], pData, sizeof(T));
509 pData += stride;
510 }
511
512 return results;
513 }
514
515 template <typename CaseDef, typename = bool>
516 struct PointMode
517 {
518 #ifndef CTS_USES_VULKANSC
checkvkt::tessellation::PointMode519 static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &, const CaseDef)
520 {
521 }
522 #endif // CTS_USES_VULKANSC
523 };
524
525 template <typename CaseDef>
526 struct PointMode<CaseDef, decltype(CaseDef().usePointMode)>
527 {
528 #ifndef CTS_USES_VULKANSC
checkvkt::tessellation::PointMode529 static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features, const CaseDef caseDef)
530 {
531 if (caseDef.usePointMode)
532 checkPointMode(features);
533 }
534 #endif // CTS_USES_VULKANSC
535 };
536
537 template <typename CaseDef>
checkSupportCase(Context & context,const CaseDef caseDef)538 void checkSupportCase(Context &context, const CaseDef caseDef)
539 {
540 #ifndef CTS_USES_VULKANSC
541 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
542 {
543 PointMode<CaseDef>::check(*features, caseDef);
544 checkPrimitive(*features, caseDef.primitiveType);
545 }
546 #else
547 DE_UNREF(context);
548 DE_UNREF(caseDef);
549 #endif // CTS_USES_VULKANSC
550 }
551
552 } // namespace tessellation
553 } // namespace vkt
554
555 #endif // _VKTTESSELLATIONUTIL_HPP
556