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 SpacingMode
166 {
167 SPACINGMODE_EQUAL = 0,
168 SPACINGMODE_FRACTIONAL_ODD,
169 SPACINGMODE_FRACTIONAL_EVEN,
170
171 SPACINGMODE_LAST,
172 };
173
174 enum Winding
175 {
176 WINDING_CCW = 0,
177 WINDING_CW,
178
179 WINDING_LAST,
180 };
181
182 enum ShaderLanguage
183 {
184 SHADER_LANGUAGE_GLSL = 0,
185 SHADER_LANGUAGE_HLSL = 1,
186
187 SHADER_LANGUAGE_LAST,
188 };
189
190 enum FeatureFlagBits
191 {
192 FEATURE_TESSELLATION_SHADER = 1u << 0,
193 FEATURE_GEOMETRY_SHADER = 1u << 1,
194 FEATURE_SHADER_FLOAT_64 = 1u << 2,
195 FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3,
196 FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4,
197 FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5,
198 };
199 typedef uint32_t FeatureFlags;
200
201 vk::VkImageCreateInfo makeImageCreateInfo(const tcu::IVec2 &size, const vk::VkFormat format,
202 const vk::VkImageUsageFlags usage, const uint32_t numArrayLayers);
203 vk::Move<vk::VkRenderPass> makeRenderPassWithoutAttachments(const vk::DeviceInterface &vk, const vk::VkDevice device);
204 vk::VkBufferImageCopy makeBufferImageCopy(const vk::VkExtent3D extent,
205 const vk::VkImageSubresourceLayers subresourceLayers);
206 void requireFeatures(const vk::InstanceInterface &vki, const vk::VkPhysicalDevice physDevice, const FeatureFlags flags);
207 float getClampedTessLevel(const SpacingMode mode, const float tessLevel);
208 int getRoundedTessLevel(const SpacingMode mode, const float clampedTessLevel);
209 int getClampedRoundedTessLevel(const SpacingMode mode, const float tessLevel);
210 void getClampedRoundedTriangleTessLevels(const SpacingMode mode, const float *innerSrc, const float *outerSrc,
211 int *innerDst, int *outerDst);
212 void getClampedRoundedQuadTessLevels(const SpacingMode mode, const float *innerSrc, const float *outerSrc,
213 int *innerDst, int *outerDst);
214 void getClampedRoundedIsolineTessLevels(const SpacingMode mode, const float *outerSrc, int *outerDst);
215 int numOuterTessellationLevels(const TessPrimitiveType primitiveType);
216 std::string getTessellationLevelsString(const TessLevels &tessLevels, const TessPrimitiveType primitiveType);
217 std::string getTessellationLevelsString(const float *inner, const float *outer);
218 bool isPatchDiscarded(const TessPrimitiveType primitiveType, const float *outerLevels);
219 std::vector<tcu::Vec3> generateReferenceTriangleTessCoords(const SpacingMode spacingMode, const int inner,
220 const int outer0, const int outer1, const int outer2);
221 std::vector<tcu::Vec3> generateReferenceQuadTessCoords(const SpacingMode spacingMode, const int inner0,
222 const int inner1, const int outer0, const int outer1,
223 const int outer2, const int outer3);
224 std::vector<tcu::Vec3> generateReferenceIsolineTessCoords(const int outer0, const int outer1);
225 int referenceVertexCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode,
226 const float *innerLevels, const float *outerLevels);
227 int referencePrimitiveCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
228 const bool usePointMode, const float *innerLevels, const float *outerLevels);
229 int numVerticesPerPrimitive(const TessPrimitiveType primitiveType, const bool usePointMode);
230
getTessPrimitiveTypeShaderName(const TessPrimitiveType type,bool forSpirv=false)231 static inline const char *getTessPrimitiveTypeShaderName(const TessPrimitiveType type, bool forSpirv = false)
232 {
233 static std::string primitiveName[][2] = {// glsl name spirv name
234 {"triangles", "Triangles"},
235 {"quads", "Quads"},
236 {"isolines", "Isolines"}};
237
238 if (type >= TESSPRIMITIVETYPE_LAST)
239 {
240 DE_FATAL("Unexpected primitive type.");
241 return DE_NULL;
242 }
243
244 return primitiveName[type][forSpirv].c_str();
245 }
246
getDomainName(const TessPrimitiveType type)247 static inline const char *getDomainName(const TessPrimitiveType type)
248 {
249 switch (type)
250 {
251 case TESSPRIMITIVETYPE_TRIANGLES:
252 return "tri";
253 case TESSPRIMITIVETYPE_QUADS:
254 return "quad";
255 case TESSPRIMITIVETYPE_ISOLINES:
256 return "isoline";
257 default:
258 DE_FATAL("Unexpected primitive type.");
259 return DE_NULL;
260 }
261 }
262
getOutputTopologyName(const TessPrimitiveType type,const Winding winding,const bool usePointMode)263 static inline const char *getOutputTopologyName(const TessPrimitiveType type, const Winding winding,
264 const bool usePointMode)
265 {
266 if (usePointMode)
267 return "point";
268 else if (type == TESSPRIMITIVETYPE_TRIANGLES || type == TESSPRIMITIVETYPE_QUADS)
269 return (winding == WINDING_CCW ? "triangle_ccw" : "triangle_cw");
270 else if (type == TESSPRIMITIVETYPE_ISOLINES)
271 return "line";
272
273 DE_FATAL("Unexpected primitive type.");
274 return DE_NULL;
275 }
276
getSpacingModeShaderName(SpacingMode mode,bool forSpirv=false)277 static inline const char *getSpacingModeShaderName(SpacingMode mode, bool forSpirv = false)
278 {
279 static std::string spacingName[][2] = {// glsl name spirv name
280 {"equal_spacing", "SpacingEqual"},
281 {"fractional_odd_spacing", "SpacingFractionalOdd"},
282 {"fractional_even_spacing", "SpacingFractionalEven"}};
283
284 if (mode >= SPACINGMODE_LAST)
285 {
286 DE_FATAL("Unexpected spacing type.");
287 return DE_NULL;
288 }
289
290 return spacingName[mode][forSpirv].c_str();
291 }
292
getPartitioningShaderName(SpacingMode mode)293 static inline const char *getPartitioningShaderName(SpacingMode mode)
294 {
295 switch (mode)
296 {
297 case SPACINGMODE_EQUAL:
298 return "integer";
299 case SPACINGMODE_FRACTIONAL_ODD:
300 return "fractional_odd";
301 case SPACINGMODE_FRACTIONAL_EVEN:
302 return "fractional_even";
303 default:
304 DE_FATAL("Unexpected spacing mode.");
305 return DE_NULL;
306 }
307 }
308
getWindingShaderName(const Winding winding)309 static inline const char *getWindingShaderName(const Winding winding)
310 {
311 switch (winding)
312 {
313 case WINDING_CCW:
314 return "ccw";
315 case WINDING_CW:
316 return "cw";
317 default:
318 DE_FATAL("Unexpected winding type.");
319 return DE_NULL;
320 }
321 }
322
getShaderLanguageName(const ShaderLanguage language)323 static inline const char *getShaderLanguageName(const ShaderLanguage language)
324 {
325 switch (language)
326 {
327 case SHADER_LANGUAGE_GLSL:
328 return "glsl";
329 case SHADER_LANGUAGE_HLSL:
330 return "hlsl";
331 default:
332 DE_FATAL("Unexpected shader language.");
333 return DE_NULL;
334 }
335 }
336
getGeometryShaderInputPrimitiveTypeShaderName(const TessPrimitiveType type,const bool usePointMode)337 static inline const char *getGeometryShaderInputPrimitiveTypeShaderName(const TessPrimitiveType type,
338 const bool usePointMode)
339 {
340 if (usePointMode)
341 return "points";
342
343 switch (type)
344 {
345 case TESSPRIMITIVETYPE_TRIANGLES:
346 case TESSPRIMITIVETYPE_QUADS:
347 return "triangles";
348
349 case TESSPRIMITIVETYPE_ISOLINES:
350 return "lines";
351
352 default:
353 DE_FATAL("Unexpected primitive type.");
354 return DE_NULL;
355 }
356 }
357
getGeometryShaderOutputPrimitiveTypeShaderName(const TessPrimitiveType type,const bool usePointMode)358 static inline const char *getGeometryShaderOutputPrimitiveTypeShaderName(const TessPrimitiveType type,
359 const bool usePointMode)
360 {
361 if (usePointMode)
362 return "points";
363
364 switch (type)
365 {
366 case TESSPRIMITIVETYPE_TRIANGLES:
367 case TESSPRIMITIVETYPE_QUADS:
368 return "triangle_strip";
369
370 case TESSPRIMITIVETYPE_ISOLINES:
371 return "line_strip";
372
373 default:
374 DE_FATAL("Unexpected primitive type.");
375 return DE_NULL;
376 }
377 }
378
379 #ifndef CTS_USES_VULKANSC
380
getPortability(const Context & context)381 static inline const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *getPortability(const Context &context)
382 {
383 if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset"))
384 return &context.getPortabilitySubsetFeatures();
385 return DE_NULL;
386 }
387
checkIsolines(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features)388 static inline void checkIsolines(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features)
389 {
390 if (!features.tessellationIsolines)
391 TCU_THROW(NotSupportedError,
392 "VK_KHR_portability_subset: Tessellation iso lines are not supported by this implementation");
393 }
394
checkPrimitive(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features,const TessPrimitiveType primitive)395 static inline void checkPrimitive(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features,
396 const TessPrimitiveType primitive)
397 {
398 if (primitive == TESSPRIMITIVETYPE_ISOLINES)
399 checkIsolines(features);
400 }
401
checkSupportPrimitive(Context & context,const TessPrimitiveType primitive)402 static inline void checkSupportPrimitive(Context &context, const TessPrimitiveType primitive)
403 {
404 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
405 checkPrimitive(*features, primitive);
406 }
407
checkPointMode(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features)408 static inline void checkPointMode(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features)
409 {
410 if (!features.tessellationPointMode)
411 TCU_THROW(NotSupportedError,
412 "VK_KHR_portability_subset: Tessellation point mode is not supported by this implementation");
413 }
414
415 #endif // CTS_USES_VULKANSC
416
417 template <typename T>
sizeInBytes(const std::vector<T> & vec)418 inline std::size_t sizeInBytes(const std::vector<T> &vec)
419 {
420 return vec.size() * sizeof(vec[0]);
421 }
422
423 template <typename T>
sorted(const std::vector<T> & unsorted)424 static std::vector<T> sorted(const std::vector<T> &unsorted)
425 {
426 std::vector<T> result = unsorted;
427 std::sort(result.begin(), result.end());
428 return result;
429 }
430
431 template <typename T, typename P>
sorted(const std::vector<T> & unsorted,P pred)432 static std::vector<T> sorted(const std::vector<T> &unsorted, P pred)
433 {
434 std::vector<T> result = unsorted;
435 std::sort(result.begin(), result.end(), pred);
436 return result;
437 }
438
439 template <typename IterT>
elemsStr(const IterT & begin,const IterT & end,int wrapLengthParam=0,int numIndentationSpaces=0)440 std::string elemsStr(const IterT &begin, const IterT &end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
441 {
442 const int bigInt = ~0u / 2;
443 const std::string baseIndentation = std::string(numIndentationSpaces, ' ');
444 const std::string deepIndentation = baseIndentation + std::string(4, ' ');
445 const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : bigInt;
446 const int length = static_cast<int>(std::distance(begin, end));
447 std::string result;
448
449 if (length > wrapLength)
450 result += "(amount: " + de::toString(length) + ") ";
451 result += std::string() + "{" + (length > wrapLength ? "\n" + deepIndentation : " ");
452
453 {
454 int index = 0;
455 for (IterT it = begin; it != end; ++it)
456 {
457 if (it != begin)
458 result += std::string() + ", " + (index % wrapLength == 0 ? "\n" + deepIndentation : "");
459 result += de::toString(*it);
460 index++;
461 }
462
463 result += length > wrapLength ? "\n" + baseIndentation : " ";
464 }
465
466 result += "}";
467 return result;
468 }
469
470 template <typename ContainerT>
containerStr(const ContainerT & c,int wrapLengthParam=0,int numIndentationSpaces=0)471 std::string containerStr(const ContainerT &c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
472 {
473 return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
474 }
475
476 //! Copy 'count' objects of type T from 'memory' into a vector.
477 //! 'offset' is the offset of first object in memory, and 'stride' is the distance between consecutive objects.
478 template <typename T>
readInterleavedData(const int count,const void * memory,const int offset,const int stride)479 std::vector<T> readInterleavedData(const int count, const void *memory, const int offset, const int stride)
480 {
481 std::vector<T> results(count);
482 const uint8_t *pData = static_cast<const uint8_t *>(memory) + offset;
483
484 for (int i = 0; i < count; ++i)
485 {
486 deMemcpy(&results[i], pData, sizeof(T));
487 pData += stride;
488 }
489
490 return results;
491 }
492
493 template <typename CaseDef, typename = bool>
494 struct PointMode
495 {
496 #ifndef CTS_USES_VULKANSC
checkvkt::tessellation::PointMode497 static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &, const CaseDef)
498 {
499 }
500 #endif // CTS_USES_VULKANSC
501 };
502
503 template <typename CaseDef>
504 struct PointMode<CaseDef, decltype(CaseDef().usePointMode)>
505 {
506 #ifndef CTS_USES_VULKANSC
checkvkt::tessellation::PointMode507 static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR &features, const CaseDef caseDef)
508 {
509 if (caseDef.usePointMode)
510 checkPointMode(features);
511 }
512 #endif // CTS_USES_VULKANSC
513 };
514
515 template <typename CaseDef>
checkSupportCase(Context & context,const CaseDef caseDef)516 void checkSupportCase(Context &context, const CaseDef caseDef)
517 {
518 #ifndef CTS_USES_VULKANSC
519 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
520 {
521 PointMode<CaseDef>::check(*features, caseDef);
522 checkPrimitive(*features, caseDef.primitiveType);
523 }
524 #else
525 DE_UNREF(context);
526 DE_UNREF(caseDef);
527 #endif // CTS_USES_VULKANSC
528 }
529
530 } // namespace tessellation
531 } // namespace vkt
532
533 #endif // _VKTTESSELLATIONUTIL_HPP
534