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 Buffer
50 {
51 public:
Buffer(const vk::DeviceInterface & vk,const vk::VkDevice device,vk::Allocator & allocator,const vk::VkBufferCreateInfo & bufferCreateInfo,const vk::MemoryRequirement memoryRequirement)52 Buffer (const vk::DeviceInterface& vk,
53 const vk::VkDevice device,
54 vk::Allocator& allocator,
55 const vk::VkBufferCreateInfo& bufferCreateInfo,
56 const vk::MemoryRequirement memoryRequirement)
57
58 : m_buffer (createBuffer(vk, device, &bufferCreateInfo))
59 , m_allocation (allocator.allocate(getBufferMemoryRequirements(vk, device, *m_buffer), memoryRequirement))
60 {
61 VK_CHECK(vk.bindBufferMemory(device, *m_buffer, m_allocation->getMemory(), m_allocation->getOffset()));
62 }
63
get(void) const64 const vk::VkBuffer& get (void) const { return *m_buffer; }
operator *(void) const65 const vk::VkBuffer& operator* (void) const { return get(); }
getAllocation(void) const66 vk::Allocation& getAllocation (void) const { return *m_allocation; }
67
68 private:
69 const vk::Unique<vk::VkBuffer> m_buffer;
70 const de::UniquePtr<vk::Allocation> m_allocation;
71
72 // "deleted"
73 Buffer (const Buffer&);
74 Buffer& operator= (const Buffer&);
75 };
76
77 class Image
78 {
79 public:
Image(const vk::DeviceInterface & vk,const vk::VkDevice device,vk::Allocator & allocator,const vk::VkImageCreateInfo & imageCreateInfo,const vk::MemoryRequirement memoryRequirement)80 Image (const vk::DeviceInterface& vk,
81 const vk::VkDevice device,
82 vk::Allocator& allocator,
83 const vk::VkImageCreateInfo& imageCreateInfo,
84 const vk::MemoryRequirement memoryRequirement)
85
86 : m_image (createImage(vk, device, &imageCreateInfo))
87 , m_allocation (allocator.allocate(getImageMemoryRequirements(vk, device, *m_image), memoryRequirement))
88 {
89 VK_CHECK(vk.bindImageMemory(device, *m_image, m_allocation->getMemory(), m_allocation->getOffset()));
90 }
91
get(void) const92 const vk::VkImage& get (void) const { return *m_image; }
operator *(void) const93 const vk::VkImage& operator* (void) const { return get(); }
getAllocation(void) const94 vk::Allocation& getAllocation (void) const { return *m_allocation; }
95
96 private:
97 const vk::Unique<vk::VkImage> m_image;
98 const de::UniquePtr<vk::Allocation> m_allocation;
99
100 // "deleted"
101 Image (const Image&);
102 Image& operator= (const Image&);
103 };
104
105 class GraphicsPipelineBuilder
106 {
107 public:
GraphicsPipelineBuilder(void)108 GraphicsPipelineBuilder (void) : m_renderSize (0, 0)
109 , m_shaderStageFlags (0u)
110 , m_cullModeFlags (vk::VK_CULL_MODE_NONE)
111 , m_frontFace (vk::VK_FRONT_FACE_COUNTER_CLOCKWISE)
112 , m_patchControlPoints (1u)
113 , m_blendEnable (false)
114 , m_primitiveTopology (vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
115 , m_tessellationDomainOrigin (tcu::Nothing) {}
116
setRenderSize(const tcu::IVec2 & size)117 GraphicsPipelineBuilder& setRenderSize (const tcu::IVec2& size) { m_renderSize = size; return *this; }
118 GraphicsPipelineBuilder& setShader (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkShaderStageFlagBits stage, const vk::ProgramBinary& binary, const vk::VkSpecializationInfo* specInfo);
setPatchControlPoints(const deUint32 controlPoints)119 GraphicsPipelineBuilder& setPatchControlPoints (const deUint32 controlPoints) { m_patchControlPoints = controlPoints; return *this; }
setCullModeFlags(const vk::VkCullModeFlags cullModeFlags)120 GraphicsPipelineBuilder& setCullModeFlags (const vk::VkCullModeFlags cullModeFlags) { m_cullModeFlags = cullModeFlags; return *this; }
setFrontFace(const vk::VkFrontFace frontFace)121 GraphicsPipelineBuilder& setFrontFace (const vk::VkFrontFace frontFace) { m_frontFace = frontFace; return *this; }
setBlend(const bool enable)122 GraphicsPipelineBuilder& setBlend (const bool enable) { m_blendEnable = enable; return *this; }
123
124 //! Applies only to pipelines without tessellation shaders.
setPrimitiveTopology(const vk::VkPrimitiveTopology topology)125 GraphicsPipelineBuilder& setPrimitiveTopology (const vk::VkPrimitiveTopology topology) { m_primitiveTopology = topology; return *this; }
126
addVertexBinding(const vk::VkVertexInputBindingDescription vertexBinding)127 GraphicsPipelineBuilder& addVertexBinding (const vk::VkVertexInputBindingDescription vertexBinding) { m_vertexInputBindings.push_back(vertexBinding); return *this; }
addVertexAttribute(const vk::VkVertexInputAttributeDescription vertexAttribute)128 GraphicsPipelineBuilder& addVertexAttribute (const vk::VkVertexInputAttributeDescription vertexAttribute) { m_vertexInputAttributes.push_back(vertexAttribute); return *this; }
129
130 //! Basic vertex input configuration (uses biding 0, location 0, etc.)
131 GraphicsPipelineBuilder& setVertexInputSingleAttribute (const vk::VkFormat vertexFormat, const deUint32 stride);
132
133 //! If tessellation domain origin is set, pipeline requires VK__maintenance2
setTessellationDomainOrigin(const vk::VkTessellationDomainOrigin domainOrigin)134 GraphicsPipelineBuilder& setTessellationDomainOrigin (const vk::VkTessellationDomainOrigin domainOrigin) { return setTessellationDomainOrigin(tcu::just(domainOrigin)); }
setTessellationDomainOrigin(const tcu::Maybe<vk::VkTessellationDomainOrigin> & domainOrigin)135 GraphicsPipelineBuilder& setTessellationDomainOrigin (const tcu::Maybe<vk::VkTessellationDomainOrigin>& domainOrigin) { m_tessellationDomainOrigin = domainOrigin; return *this; }
136
137 vk::Move<vk::VkPipeline> build (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkRenderPass renderPass);
138
139 private:
140 tcu::IVec2 m_renderSize;
141 vk::Move<vk::VkShaderModule> m_vertexShaderModule;
142 vk::Move<vk::VkShaderModule> m_fragmentShaderModule;
143 vk::Move<vk::VkShaderModule> m_geometryShaderModule;
144 vk::Move<vk::VkShaderModule> m_tessControlShaderModule;
145 vk::Move<vk::VkShaderModule> m_tessEvaluationShaderModule;
146 std::vector<vk::VkPipelineShaderStageCreateInfo> m_shaderStages;
147 std::vector<vk::VkVertexInputBindingDescription> m_vertexInputBindings;
148 std::vector<vk::VkVertexInputAttributeDescription> m_vertexInputAttributes;
149 vk::VkShaderStageFlags m_shaderStageFlags;
150 vk::VkCullModeFlags m_cullModeFlags;
151 vk::VkFrontFace m_frontFace;
152 deUint32 m_patchControlPoints;
153 bool m_blendEnable;
154 vk::VkPrimitiveTopology m_primitiveTopology;
155 tcu::Maybe<vk::VkTessellationDomainOrigin> m_tessellationDomainOrigin;
156
157 GraphicsPipelineBuilder (const GraphicsPipelineBuilder&); // "deleted"
158 GraphicsPipelineBuilder& operator= (const GraphicsPipelineBuilder&);
159 };
160
161 struct TessLevels
162 {
163 float inner[2];
164 float outer[4];
165 };
166
167 enum TessPrimitiveType
168 {
169 TESSPRIMITIVETYPE_TRIANGLES = 0,
170 TESSPRIMITIVETYPE_QUADS,
171 TESSPRIMITIVETYPE_ISOLINES,
172
173 TESSPRIMITIVETYPE_LAST,
174 };
175
176 enum SpacingMode
177 {
178 SPACINGMODE_EQUAL = 0,
179 SPACINGMODE_FRACTIONAL_ODD,
180 SPACINGMODE_FRACTIONAL_EVEN,
181
182 SPACINGMODE_LAST,
183 };
184
185 enum Winding
186 {
187 WINDING_CCW = 0,
188 WINDING_CW,
189
190 WINDING_LAST,
191 };
192
193 enum ShaderLanguage
194 {
195 SHADER_LANGUAGE_GLSL = 0,
196 SHADER_LANGUAGE_HLSL = 1,
197
198 SHADER_LANGUAGE_LAST,
199 };
200
201 enum FeatureFlagBits
202 {
203 FEATURE_TESSELLATION_SHADER = 1u << 0,
204 FEATURE_GEOMETRY_SHADER = 1u << 1,
205 FEATURE_SHADER_FLOAT_64 = 1u << 2,
206 FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3,
207 FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4,
208 FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5,
209 };
210 typedef deUint32 FeatureFlags;
211
212 vk::VkImageCreateInfo makeImageCreateInfo (const tcu::IVec2& size, const vk::VkFormat format, const vk::VkImageUsageFlags usage, const deUint32 numArrayLayers);
213 vk::Move<vk::VkPipeline> makeComputePipeline (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkShaderModule shaderModule, const vk::VkSpecializationInfo* specInfo);
214 vk::Move<vk::VkRenderPass> makeRenderPassWithoutAttachments (const vk::DeviceInterface& vk, const vk::VkDevice device);
215 vk::VkBufferImageCopy makeBufferImageCopy (const vk::VkExtent3D extent, const vk::VkImageSubresourceLayers subresourceLayers);
216 void beginRenderPassWithRasterizationDisabled (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer, const vk::VkRenderPass renderPass, const vk::VkFramebuffer framebuffer);
217 void requireFeatures (const vk::InstanceInterface& vki, const vk::VkPhysicalDevice physDevice, const FeatureFlags flags);
218 float getClampedTessLevel (const SpacingMode mode, const float tessLevel);
219 int getRoundedTessLevel (const SpacingMode mode, const float clampedTessLevel);
220 int getClampedRoundedTessLevel (const SpacingMode mode, const float tessLevel);
221 void getClampedRoundedTriangleTessLevels (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst);
222 void getClampedRoundedQuadTessLevels (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst);
223 void getClampedRoundedIsolineTessLevels (const SpacingMode mode, const float* outerSrc, int* outerDst);
224 int numOuterTessellationLevels (const TessPrimitiveType primitiveType);
225 std::string getTessellationLevelsString (const TessLevels& tessLevels, const TessPrimitiveType primitiveType);
226 std::string getTessellationLevelsString (const float* inner, const float* outer);
227 bool isPatchDiscarded (const TessPrimitiveType primitiveType, const float* outerLevels);
228 std::vector<tcu::Vec3> generateReferenceTriangleTessCoords (const SpacingMode spacingMode, const int inner, const int outer0, const int outer1, const int outer2);
229 std::vector<tcu::Vec3> generateReferenceQuadTessCoords (const SpacingMode spacingMode, const int inner0, const int inner1, const int outer0, const int outer1, const int outer2, const int outer3);
230 std::vector<tcu::Vec3> generateReferenceIsolineTessCoords (const int outer0, const int outer1);
231 int referenceVertexCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels);
232 int referencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels);
233 int numVerticesPerPrimitive (const TessPrimitiveType primitiveType, const bool usePointMode);
234
getTessPrimitiveTypeShaderName(const TessPrimitiveType type,bool forSpirv=false)235 static inline const char* getTessPrimitiveTypeShaderName (const TessPrimitiveType type, bool forSpirv = false)
236 {
237 static std::string primitiveName[][2] =
238 {
239 // glsl name spirv name
240 { "triangles", "Triangles"},
241 { "quads" , "Quads" },
242 { "isolines" , "Isolines" }
243 };
244
245 if (type >= TESSPRIMITIVETYPE_LAST)
246 {
247 DE_FATAL("Unexpected primitive type.");
248 return DE_NULL;
249 }
250
251 return primitiveName[type][forSpirv].c_str();
252 }
253
getDomainName(const TessPrimitiveType type)254 static inline const char* getDomainName (const TessPrimitiveType type)
255 {
256 switch (type)
257 {
258 case TESSPRIMITIVETYPE_TRIANGLES: return "tri";
259 case TESSPRIMITIVETYPE_QUADS: return "quad";
260 case TESSPRIMITIVETYPE_ISOLINES: return "isoline";
261 default:
262 DE_FATAL("Unexpected primitive type.");
263 return DE_NULL;
264 }
265 }
266
getOutputTopologyName(const TessPrimitiveType type,const Winding winding,const bool usePointMode)267 static inline const char* getOutputTopologyName (const TessPrimitiveType type, const Winding winding, const bool usePointMode)
268 {
269 if (usePointMode)
270 return "point";
271 else if (type == TESSPRIMITIVETYPE_TRIANGLES || type == TESSPRIMITIVETYPE_QUADS)
272 return (winding == WINDING_CCW ? "triangle_ccw" : "triangle_cw");
273 else if (type == TESSPRIMITIVETYPE_ISOLINES)
274 return "line";
275
276 DE_FATAL("Unexpected primitive type.");
277 return DE_NULL;
278 }
279
getSpacingModeShaderName(SpacingMode mode,bool forSpirv=false)280 static inline const char* getSpacingModeShaderName (SpacingMode mode, bool forSpirv = false)
281 {
282 static std::string spacingName[][2] =
283 {
284 // glsl name spirv name
285 { "equal_spacing", "SpacingEqual"},
286 { "fractional_odd_spacing", "SpacingFractionalOdd" },
287 { "fractional_even_spacing", "SpacingFractionalEven" }
288 };
289
290 if (mode >= SPACINGMODE_LAST)
291 {
292 DE_FATAL("Unexpected spacing type.");
293 return DE_NULL;
294 }
295
296 return spacingName[mode][forSpirv].c_str();
297 }
298
getPartitioningShaderName(SpacingMode mode)299 static inline const char* getPartitioningShaderName (SpacingMode mode)
300 {
301 switch (mode)
302 {
303 case SPACINGMODE_EQUAL: return "integer";
304 case SPACINGMODE_FRACTIONAL_ODD: return "fractional_odd";
305 case SPACINGMODE_FRACTIONAL_EVEN: return "fractional_even";
306 default:
307 DE_FATAL("Unexpected spacing mode.");
308 return DE_NULL;
309 }
310 }
311
getWindingShaderName(const Winding winding)312 static inline const char* getWindingShaderName (const Winding winding)
313 {
314 switch (winding)
315 {
316 case WINDING_CCW: return "ccw";
317 case WINDING_CW: return "cw";
318 default:
319 DE_FATAL("Unexpected winding type.");
320 return DE_NULL;
321 }
322 }
323
getShaderLanguageName(const ShaderLanguage language)324 static inline const char* getShaderLanguageName (const ShaderLanguage language)
325 {
326 switch (language)
327 {
328 case SHADER_LANGUAGE_GLSL: return "glsl";
329 case SHADER_LANGUAGE_HLSL: return "hlsl";
330 default:
331 DE_FATAL("Unexpected shader language.");
332 return DE_NULL;
333 }
334 }
335
getGeometryShaderInputPrimitiveTypeShaderName(const TessPrimitiveType type,const bool usePointMode)336 static inline const char* getGeometryShaderInputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode)
337 {
338 if (usePointMode)
339 return "points";
340
341 switch (type)
342 {
343 case TESSPRIMITIVETYPE_TRIANGLES:
344 case TESSPRIMITIVETYPE_QUADS:
345 return "triangles";
346
347 case TESSPRIMITIVETYPE_ISOLINES:
348 return "lines";
349
350 default:
351 DE_FATAL("Unexpected primitive type.");
352 return DE_NULL;
353 }
354 }
355
getGeometryShaderOutputPrimitiveTypeShaderName(const TessPrimitiveType type,const bool usePointMode)356 static inline const char* getGeometryShaderOutputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode)
357 {
358 if (usePointMode)
359 return "points";
360
361 switch (type)
362 {
363 case TESSPRIMITIVETYPE_TRIANGLES:
364 case TESSPRIMITIVETYPE_QUADS:
365 return "triangle_strip";
366
367 case TESSPRIMITIVETYPE_ISOLINES:
368 return "line_strip";
369
370 default:
371 DE_FATAL("Unexpected primitive type.");
372 return DE_NULL;
373 }
374 }
375
getPortability(const Context & context)376 static inline const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* getPortability (const Context& context)
377 {
378 if (context.isDeviceFunctionalitySupported("VK_KHR_portability_subset"))
379 return &context.getPortabilitySubsetFeatures();
380 return DE_NULL;
381 }
382
checkIsolines(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features)383 static inline void checkIsolines (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features)
384 {
385 if (!features.tessellationIsolines)
386 TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Tessellation iso lines are not supported by this implementation");
387 }
388
checkPrimitive(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features,const TessPrimitiveType primitive)389 static inline void checkPrimitive (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features, const TessPrimitiveType primitive)
390 {
391 if (primitive == TESSPRIMITIVETYPE_ISOLINES)
392 checkIsolines(features);
393 }
394
checkSupportPrimitive(Context & context,const TessPrimitiveType primitive)395 static inline void checkSupportPrimitive (Context& context, const TessPrimitiveType primitive)
396 {
397 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* const features = getPortability(context))
398 checkPrimitive(*features, primitive);
399 }
400
checkPointMode(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR & features)401 static inline void checkPointMode (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features)
402 {
403 if (!features.tessellationPointMode)
404 TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Tessellation point mode is not supported by this implementation");
405 }
406
407 template<typename T>
sizeInBytes(const std::vector<T> & vec)408 inline std::size_t sizeInBytes (const std::vector<T>& vec)
409 {
410 return vec.size() * sizeof(vec[0]);
411 }
412
413 template <typename T>
sorted(const std::vector<T> & unsorted)414 static std::vector<T> sorted (const std::vector<T>& unsorted)
415 {
416 std::vector<T> result = unsorted;
417 std::sort(result.begin(), result.end());
418 return result;
419 }
420
421 template <typename T, typename P>
sorted(const std::vector<T> & unsorted,P pred)422 static std::vector<T> sorted (const std::vector<T>& unsorted, P pred)
423 {
424 std::vector<T> result = unsorted;
425 std::sort(result.begin(), result.end(), pred);
426 return result;
427 }
428
429 template <typename IterT>
elemsStr(const IterT & begin,const IterT & end,int wrapLengthParam=0,int numIndentationSpaces=0)430 std::string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
431 {
432 const int bigInt = ~0u/2;
433 const std::string baseIndentation = std::string(numIndentationSpaces, ' ');
434 const std::string deepIndentation = baseIndentation + std::string(4, ' ');
435 const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : bigInt;
436 const int length = static_cast<int>(std::distance(begin, end));
437 std::string result;
438
439 if (length > wrapLength)
440 result += "(amount: " + de::toString(length) + ") ";
441 result += std::string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " ");
442
443 {
444 int index = 0;
445 for (IterT it = begin; it != end; ++it)
446 {
447 if (it != begin)
448 result += std::string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : "");
449 result += de::toString(*it);
450 index++;
451 }
452
453 result += length > wrapLength ? "\n"+baseIndentation : " ";
454 }
455
456 result += "}";
457 return result;
458 }
459
460 template <typename ContainerT>
containerStr(const ContainerT & c,int wrapLengthParam=0,int numIndentationSpaces=0)461 std::string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
462 {
463 return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
464 }
465
466 //! Copy 'count' objects of type T from 'memory' into a vector.
467 //! 'offset' is the offset of first object in memory, and 'stride' is the distance between consecutive objects.
468 template<typename T>
readInterleavedData(const int count,const void * memory,const int offset,const int stride)469 std::vector<T> readInterleavedData (const int count, const void* memory, const int offset, const int stride)
470 {
471 std::vector<T> results(count);
472 const deUint8* pData = static_cast<const deUint8*>(memory) + offset;
473
474 for (int i = 0; i < count; ++i)
475 {
476 deMemcpy(&results[i], pData, sizeof(T));
477 pData += stride;
478 }
479
480 return results;
481 }
482
483 template <typename CaseDef, typename = bool>
484 struct PointMode
485 {
checkvkt::tessellation::PointMode486 static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR&, const CaseDef)
487 {
488 }
489 };
490
491 template <typename CaseDef>
492 struct PointMode<CaseDef, decltype(CaseDef().usePointMode)>
493 {
checkvkt::tessellation::PointMode494 static void check(const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR& features, const CaseDef caseDef)
495 {
496 if (caseDef.usePointMode)
497 checkPointMode(features);
498 }
499 };
500
501 template <typename CaseDef>
checkSupportCase(Context & context,const CaseDef caseDef)502 void checkSupportCase (Context& context, const CaseDef caseDef)
503 {
504 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* const features = getPortability(context))
505 {
506 PointMode<CaseDef>::check(*features, caseDef);
507 checkPrimitive(*features, caseDef.primitiveType);
508 }
509 }
510
511 } // tessellation
512 } // vkt
513
514 #endif // _VKTTESSELLATIONUTIL_HPP
515