xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/tessellation/vktTessellationUtil.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 Utilities
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktTessellationUtil.hpp"
26 #include "vkTypeUtil.hpp"
27 #include "vkCmdUtil.hpp"
28 #include "deMath.h"
29 
30 namespace vkt
31 {
32 namespace tessellation
33 {
34 
35 using namespace vk;
36 
makeImageCreateInfo(const tcu::IVec2 & size,const VkFormat format,const VkImageUsageFlags usage,const uint32_t numArrayLayers)37 VkImageCreateInfo makeImageCreateInfo(const tcu::IVec2 &size, const VkFormat format, const VkImageUsageFlags usage,
38                                       const uint32_t numArrayLayers)
39 {
40     const VkImageCreateInfo imageInfo = {
41         VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType          sType;
42         DE_NULL,                             // const void*              pNext;
43         (VkImageCreateFlags)0,               // VkImageCreateFlags       flags;
44         VK_IMAGE_TYPE_2D,                    // VkImageType              imageType;
45         format,                              // VkFormat                 format;
46         makeExtent3D(size.x(), size.y(), 1), // VkExtent3D               extent;
47         1u,                                  // uint32_t                 mipLevels;
48         numArrayLayers,                      // uint32_t                 arrayLayers;
49         VK_SAMPLE_COUNT_1_BIT,               // VkSampleCountFlagBits    samples;
50         VK_IMAGE_TILING_OPTIMAL,             // VkImageTiling            tiling;
51         usage,                               // VkImageUsageFlags        usage;
52         VK_SHARING_MODE_EXCLUSIVE,           // VkSharingMode            sharingMode;
53         0u,                                  // uint32_t                 queueFamilyIndexCount;
54         DE_NULL,                             // const uint32_t*          pQueueFamilyIndices;
55         VK_IMAGE_LAYOUT_UNDEFINED,           // VkImageLayout            initialLayout;
56     };
57     return imageInfo;
58 }
59 
makeRenderPassWithoutAttachments(const DeviceInterface & vk,const VkDevice device)60 Move<VkRenderPass> makeRenderPassWithoutAttachments(const DeviceInterface &vk, const VkDevice device)
61 {
62     const VkAttachmentReference unusedAttachment = {
63         VK_ATTACHMENT_UNUSED,     // uint32_t attachment;
64         VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout layout;
65     };
66 
67     const VkSubpassDescription subpassDescription = {
68         (VkSubpassDescriptionFlags)0,    // VkSubpassDescriptionFlags flags;
69         VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
70         0u,                              // uint32_t inputAttachmentCount;
71         DE_NULL,                         // const VkAttachmentReference* pInputAttachments;
72         0u,                              // uint32_t colorAttachmentCount;
73         DE_NULL,                         // const VkAttachmentReference* pColorAttachments;
74         DE_NULL,                         // const VkAttachmentReference* pResolveAttachments;
75         &unusedAttachment,               // const VkAttachmentReference* pDepthStencilAttachment;
76         0u,                              // uint32_t preserveAttachmentCount;
77         DE_NULL                          // const uint32_t* pPreserveAttachments;
78     };
79 
80     const VkRenderPassCreateInfo renderPassInfo = {
81         VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
82         DE_NULL,                                   // const void* pNext;
83         (VkRenderPassCreateFlags)0,                // VkRenderPassCreateFlags flags;
84         0u,                                        // uint32_t attachmentCount;
85         DE_NULL,                                   // const VkAttachmentDescription* pAttachments;
86         1u,                                        // uint32_t subpassCount;
87         &subpassDescription,                       // const VkSubpassDescription* pSubpasses;
88         0u,                                        // uint32_t dependencyCount;
89         DE_NULL                                    // const VkSubpassDependency* pDependencies;
90     };
91 
92     return createRenderPass(vk, device, &renderPassInfo);
93 }
94 
setShader(const DeviceInterface & vk,const VkDevice device,const VkShaderStageFlagBits stage,const ProgramBinary & binary,const VkSpecializationInfo * specInfo)95 GraphicsPipelineBuilder &GraphicsPipelineBuilder::setShader(const DeviceInterface &vk, const VkDevice device,
96                                                             const VkShaderStageFlagBits stage,
97                                                             const ProgramBinary &binary,
98                                                             const VkSpecializationInfo *specInfo)
99 {
100     VkShaderModule module;
101     switch (stage)
102     {
103     case (VK_SHADER_STAGE_VERTEX_BIT):
104         DE_ASSERT(m_vertexShaderModule.get() == DE_NULL);
105         m_vertexShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
106         module               = *m_vertexShaderModule;
107         break;
108 
109     case (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT):
110         DE_ASSERT(m_tessControlShaderModule.get() == DE_NULL);
111         m_tessControlShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
112         module                    = *m_tessControlShaderModule;
113         break;
114 
115     case (VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT):
116         DE_ASSERT(m_tessEvaluationShaderModule.get() == DE_NULL);
117         m_tessEvaluationShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
118         module                       = *m_tessEvaluationShaderModule;
119         break;
120 
121     case (VK_SHADER_STAGE_GEOMETRY_BIT):
122         DE_ASSERT(m_geometryShaderModule.get() == DE_NULL);
123         m_geometryShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
124         module                 = *m_geometryShaderModule;
125         break;
126 
127     case (VK_SHADER_STAGE_FRAGMENT_BIT):
128         DE_ASSERT(m_fragmentShaderModule.get() == DE_NULL);
129         m_fragmentShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
130         module                 = *m_fragmentShaderModule;
131         break;
132 
133     default:
134         DE_FATAL("Invalid shader stage");
135         return *this;
136     }
137 
138     const VkPipelineShaderStageCreateInfo pipelineShaderStageInfo = {
139         VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
140         DE_NULL,                                             // const void* pNext;
141         (VkPipelineShaderStageCreateFlags)0,                 // VkPipelineShaderStageCreateFlags flags;
142         stage,                                               // VkShaderStageFlagBits stage;
143         module,                                              // VkShaderModule module;
144         "main",                                              // const char* pName;
145         specInfo,                                            // const VkSpecializationInfo* pSpecializationInfo;
146     };
147 
148     m_shaderStageFlags |= stage;
149     m_shaderStages.push_back(pipelineShaderStageInfo);
150 
151     return *this;
152 }
153 
setVertexInputSingleAttribute(const VkFormat vertexFormat,const uint32_t stride)154 GraphicsPipelineBuilder &GraphicsPipelineBuilder::setVertexInputSingleAttribute(const VkFormat vertexFormat,
155                                                                                 const uint32_t stride)
156 {
157     const VkVertexInputBindingDescription bindingDesc = {
158         0u,                          // uint32_t binding;
159         stride,                      // uint32_t stride;
160         VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
161     };
162     const VkVertexInputAttributeDescription attributeDesc = {
163         0u,           // uint32_t location;
164         0u,           // uint32_t binding;
165         vertexFormat, // VkFormat format;
166         0u,           // uint32_t offset;
167     };
168 
169     m_vertexInputBindings.clear();
170     m_vertexInputBindings.push_back(bindingDesc);
171 
172     m_vertexInputAttributes.clear();
173     m_vertexInputAttributes.push_back(attributeDesc);
174 
175     return *this;
176 }
177 
178 template <typename T>
dataPointer(const std::vector<T> & vec)179 inline const T *dataPointer(const std::vector<T> &vec)
180 {
181     return (vec.size() != 0 ? &vec[0] : DE_NULL);
182 }
183 
build(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkRenderPass renderPass)184 Move<VkPipeline> GraphicsPipelineBuilder::build(const DeviceInterface &vk, const VkDevice device,
185                                                 const VkPipelineLayout pipelineLayout, const VkRenderPass renderPass)
186 {
187     const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo = {
188         VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType                             sType;
189         DE_NULL,                                                   // const void*                                 pNext;
190         (VkPipelineVertexInputStateCreateFlags)0,                  // VkPipelineVertexInputStateCreateFlags       flags;
191         static_cast<uint32_t>(
192             m_vertexInputBindings.size()), // uint32_t                                    vertexBindingDescriptionCount;
193         dataPointer(m_vertexInputBindings), // const VkVertexInputBindingDescription*      pVertexBindingDescriptions;
194         static_cast<uint32_t>(
195             m_vertexInputAttributes
196                 .size()), // uint32_t                                    vertexAttributeDescriptionCount;
197         dataPointer(
198             m_vertexInputAttributes), // const VkVertexInputAttributeDescription*    pVertexAttributeDescriptions;
199     };
200 
201     const VkPrimitiveTopology topology = (m_shaderStageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ?
202                                              VK_PRIMITIVE_TOPOLOGY_PATCH_LIST :
203                                              m_primitiveTopology;
204     const VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo = {
205         VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType                             sType;
206         DE_NULL,                                    // const void*                                 pNext;
207         (VkPipelineInputAssemblyStateCreateFlags)0, // VkPipelineInputAssemblyStateCreateFlags     flags;
208         topology,                                   // VkPrimitiveTopology                         topology;
209         VK_FALSE, // VkBool32                                    primitiveRestartEnable;
210     };
211 
212     const VkPipelineTessellationDomainOriginStateCreateInfo tessellationDomainOriginStateInfo = {
213         VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, DE_NULL,
214         (!m_tessellationDomainOrigin ? VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT : *m_tessellationDomainOrigin)};
215     const VkPipelineTessellationStateCreateInfo pipelineTessellationStateInfo = {
216         VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, // VkStructureType                             sType;
217         (!m_tessellationDomainOrigin ? DE_NULL : &tessellationDomainOriginStateInfo),
218         (VkPipelineTessellationStateCreateFlags)0, // VkPipelineTessellationStateCreateFlags      flags;
219         m_patchControlPoints,                      // uint32_t                                    patchControlPoints;
220     };
221 
222     const VkViewport viewport = makeViewport(m_renderSize);
223     const VkRect2D scissor    = makeRect2D(m_renderSize);
224 
225     const bool haveRenderSize = m_renderSize.x() > 0 && m_renderSize.y() > 0;
226 
227     const VkPipelineViewportStateCreateInfo pipelineViewportStateInfo = {
228         VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType                             sType;
229         DE_NULL,                                               // const void*                                 pNext;
230         (VkPipelineViewportStateCreateFlags)0,                 // VkPipelineViewportStateCreateFlags          flags;
231         1u,                                   // uint32_t                                    viewportCount;
232         haveRenderSize ? &viewport : DE_NULL, // const VkViewport*                           pViewports;
233         1u,                                   // uint32_t                                    scissorCount;
234         haveRenderSize ? &scissor : DE_NULL,  // const VkRect2D*                             pScissors;
235     };
236 
237     const bool isRasterizationDisabled = ((m_shaderStageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) == 0);
238     const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo = {
239         VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType                          sType;
240         DE_NULL,                                                    // const void*                              pNext;
241         (VkPipelineRasterizationStateCreateFlags)0,                 // VkPipelineRasterizationStateCreateFlags  flags;
242         VK_FALSE,                // VkBool32                                 depthClampEnable;
243         isRasterizationDisabled, // VkBool32                                 rasterizerDiscardEnable;
244         VK_POLYGON_MODE_FILL,    // VkPolygonMode polygonMode;
245         m_cullModeFlags,         // VkCullModeFlags cullMode;
246         m_frontFace,             // VkFrontFace frontFace;
247         VK_FALSE,                // VkBool32 depthBiasEnable;
248         0.0f,                    // float depthBiasConstantFactor;
249         0.0f,                    // float depthBiasClamp;
250         0.0f,                    // float depthBiasSlopeFactor;
251         1.0f,                    // float lineWidth;
252     };
253 
254     const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo = {
255         VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
256         DE_NULL,                                                  // const void* pNext;
257         (VkPipelineMultisampleStateCreateFlags)0,                 // VkPipelineMultisampleStateCreateFlags flags;
258         VK_SAMPLE_COUNT_1_BIT,                                    // VkSampleCountFlagBits rasterizationSamples;
259         VK_FALSE,                                                 // VkBool32 sampleShadingEnable;
260         0.0f,                                                     // float minSampleShading;
261         DE_NULL,                                                  // const VkSampleMask* pSampleMask;
262         VK_FALSE,                                                 // VkBool32 alphaToCoverageEnable;
263         VK_FALSE                                                  // VkBool32 alphaToOneEnable;
264     };
265 
266     const VkStencilOpState stencilOpState = makeStencilOpState(VK_STENCIL_OP_KEEP,  // stencil fail
267                                                                VK_STENCIL_OP_KEEP,  // depth & stencil pass
268                                                                VK_STENCIL_OP_KEEP,  // depth only fail
269                                                                VK_COMPARE_OP_NEVER, // compare op
270                                                                0u,                  // compare mask
271                                                                0u,                  // write mask
272                                                                0u);                 // reference
273 
274     const VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo = {
275         VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
276         DE_NULL,                                                    // const void* pNext;
277         (VkPipelineDepthStencilStateCreateFlags)0,                  // VkPipelineDepthStencilStateCreateFlags flags;
278         VK_FALSE,                                                   // VkBool32 depthTestEnable;
279         VK_FALSE,                                                   // VkBool32 depthWriteEnable;
280         VK_COMPARE_OP_LESS,                                         // VkCompareOp depthCompareOp;
281         VK_FALSE,                                                   // VkBool32 depthBoundsTestEnable;
282         VK_FALSE,                                                   // VkBool32 stencilTestEnable;
283         stencilOpState,                                             // VkStencilOpState front;
284         stencilOpState,                                             // VkStencilOpState back;
285         0.0f,                                                       // float minDepthBounds;
286         1.0f,                                                       // float maxDepthBounds;
287     };
288 
289     const VkColorComponentFlags colorComponentsAll =
290         VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
291     const VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState = {
292         m_blendEnable,             // VkBool32 blendEnable;
293         VK_BLEND_FACTOR_SRC_ALPHA, // VkBlendFactor srcColorBlendFactor;
294         VK_BLEND_FACTOR_ONE,       // VkBlendFactor dstColorBlendFactor;
295         VK_BLEND_OP_ADD,           // VkBlendOp colorBlendOp;
296         VK_BLEND_FACTOR_SRC_ALPHA, // VkBlendFactor srcAlphaBlendFactor;
297         VK_BLEND_FACTOR_ONE,       // VkBlendFactor dstAlphaBlendFactor;
298         VK_BLEND_OP_ADD,           // VkBlendOp alphaBlendOp;
299         colorComponentsAll,        // VkColorComponentFlags colorWriteMask;
300     };
301 
302     const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo = {
303         VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
304         DE_NULL,                                                  // const void* pNext;
305         (VkPipelineColorBlendStateCreateFlags)0,                  // VkPipelineColorBlendStateCreateFlags flags;
306         VK_FALSE,                                                 // VkBool32 logicOpEnable;
307         VK_LOGIC_OP_COPY,                                         // VkLogicOp logicOp;
308         1u,                                                       // uint32_t attachmentCount;
309         &pipelineColorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
310         {0.0f, 0.0f, 0.0f, 0.0f},           // float blendConstants[4];
311     };
312 
313     std::vector<VkDynamicState> dynamicStates;
314     if (!haveRenderSize && !isRasterizationDisabled)
315     {
316         dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
317         dynamicStates.push_back(VK_DYNAMIC_STATE_SCISSOR);
318     }
319 
320     const VkPipelineDynamicStateCreateInfo pipelineDynamicStateInfo = {
321         VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,  // VkStructureType sType;
322         DE_NULL,                                               // const void* pNext;
323         0,                                                     // VkPipelineDynamicStateCreateFlags flags;
324         static_cast<uint32_t>(dynamicStates.size()),           // uint32_t dynamicStateCount;
325         (dynamicStates.empty() ? DE_NULL : &dynamicStates[0]), // const VkDynamicState* pDynamicStates;
326     };
327 
328     const VkGraphicsPipelineCreateInfo graphicsPipelineInfo = {
329         VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
330         DE_NULL,                                         // const void* pNext;
331         (VkPipelineCreateFlags)0,                        // VkPipelineCreateFlags flags;
332         static_cast<uint32_t>(m_shaderStages.size()),    // uint32_t stageCount;
333         &m_shaderStages[0],                              // const VkPipelineShaderStageCreateInfo* pStages;
334         &vertexInputStateInfo,           // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
335         &pipelineInputAssemblyStateInfo, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
336         (m_shaderStageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ?
337              &pipelineTessellationStateInfo :
338              DE_NULL), // const VkPipelineTessellationStateCreateInfo* pTessellationState;
339         (isRasterizationDisabled ?
340              DE_NULL :
341              &pipelineViewportStateInfo), // const VkPipelineViewportStateCreateInfo* pViewportState;
342         &pipelineRasterizationStateInfo,  // const VkPipelineRasterizationStateCreateInfo* pRasterizationState;
343         (isRasterizationDisabled ?
344              DE_NULL :
345              &pipelineMultisampleStateInfo), // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
346         (isRasterizationDisabled ?
347              DE_NULL :
348              &pipelineDepthStencilStateInfo), // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
349         (isRasterizationDisabled ?
350              DE_NULL :
351              &pipelineColorBlendStateInfo), // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
352         (dynamicStates.empty() ? DE_NULL :
353                                  &pipelineDynamicStateInfo), // const VkPipelineDynamicStateCreateInfo* pDynamicState;
354         pipelineLayout,                                      // VkPipelineLayout layout;
355         renderPass,                                          // VkRenderPass renderPass;
356         0u,                                                  // uint32_t subpass;
357         DE_NULL,                                             // VkPipeline basePipelineHandle;
358         0,                                                   // int32_t basePipelineIndex;
359     };
360 
361     return createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineInfo);
362 }
363 
getClampedTessLevel(const SpacingMode mode,const float tessLevel)364 float getClampedTessLevel(const SpacingMode mode, const float tessLevel)
365 {
366     switch (mode)
367     {
368     case SPACINGMODE_EQUAL:
369         return de::max(1.0f, tessLevel);
370     case SPACINGMODE_FRACTIONAL_ODD:
371         return de::max(1.0f, tessLevel);
372     case SPACINGMODE_FRACTIONAL_EVEN:
373         return de::max(2.0f, tessLevel);
374     default:
375         DE_ASSERT(false);
376         return 0.0f;
377     }
378 }
379 
getRoundedTessLevel(const SpacingMode mode,const float clampedTessLevel)380 int getRoundedTessLevel(const SpacingMode mode, const float clampedTessLevel)
381 {
382     static const int minimumMaxTessGenLevel = 64; //!< Minimum maxTessellationGenerationLevel defined by the spec.
383 
384     int result = (int)deFloatCeil(clampedTessLevel);
385 
386     switch (mode)
387     {
388     case SPACINGMODE_EQUAL:
389         break;
390     case SPACINGMODE_FRACTIONAL_ODD:
391         result += 1 - result % 2;
392         break;
393     case SPACINGMODE_FRACTIONAL_EVEN:
394         result += result % 2;
395         break;
396     default:
397         DE_ASSERT(false);
398     }
399     DE_ASSERT(de::inRange<int>(result, 1, minimumMaxTessGenLevel));
400     DE_UNREF(minimumMaxTessGenLevel);
401 
402     return result;
403 }
404 
getClampedRoundedTessLevel(const SpacingMode mode,const float tessLevel)405 int getClampedRoundedTessLevel(const SpacingMode mode, const float tessLevel)
406 {
407     return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel));
408 }
409 
getClampedRoundedTriangleTessLevels(const SpacingMode spacingMode,const float * innerSrc,const float * outerSrc,int * innerDst,int * outerDst)410 void getClampedRoundedTriangleTessLevels(const SpacingMode spacingMode, const float *innerSrc, const float *outerSrc,
411                                          int *innerDst, int *outerDst)
412 {
413     innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]);
414     for (int i = 0; i < 3; i++)
415         outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
416 }
417 
getClampedRoundedQuadTessLevels(const SpacingMode spacingMode,const float * innerSrc,const float * outerSrc,int * innerDst,int * outerDst)418 void getClampedRoundedQuadTessLevels(const SpacingMode spacingMode, const float *innerSrc, const float *outerSrc,
419                                      int *innerDst, int *outerDst)
420 {
421     for (int i = 0; i < 2; i++)
422         innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]);
423     for (int i = 0; i < 4; i++)
424         outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
425 }
426 
getClampedRoundedIsolineTessLevels(const SpacingMode spacingMode,const float * outerSrc,int * outerDst)427 void getClampedRoundedIsolineTessLevels(const SpacingMode spacingMode, const float *outerSrc, int *outerDst)
428 {
429     outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL, outerSrc[0]);
430     outerDst[1] = getClampedRoundedTessLevel(spacingMode, outerSrc[1]);
431 }
432 
numOuterTessellationLevels(const TessPrimitiveType primType)433 int numOuterTessellationLevels(const TessPrimitiveType primType)
434 {
435     switch (primType)
436     {
437     case TESSPRIMITIVETYPE_TRIANGLES:
438         return 3;
439     case TESSPRIMITIVETYPE_QUADS:
440         return 4;
441     case TESSPRIMITIVETYPE_ISOLINES:
442         return 2;
443     default:
444         DE_ASSERT(false);
445         return 0;
446     }
447 }
448 
isPatchDiscarded(const TessPrimitiveType primitiveType,const float * outerLevels)449 bool isPatchDiscarded(const TessPrimitiveType primitiveType, const float *outerLevels)
450 {
451     const int numOuterLevels = numOuterTessellationLevels(primitiveType);
452     for (int i = 0; i < numOuterLevels; i++)
453         if (outerLevels[i] <= 0.0f)
454             return true;
455     return false;
456 }
457 
getTessellationLevelsString(const TessLevels & tessLevels,const TessPrimitiveType primitiveType)458 std::string getTessellationLevelsString(const TessLevels &tessLevels, const TessPrimitiveType primitiveType)
459 {
460     std::ostringstream str;
461     switch (primitiveType)
462     {
463     case TESSPRIMITIVETYPE_ISOLINES:
464         str << "inner: { }, "
465             << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << " }";
466         break;
467 
468     case TESSPRIMITIVETYPE_TRIANGLES:
469         str << "inner: { " << tessLevels.inner[0] << " }, "
470             << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << ", " << tessLevels.outer[2] << " }";
471         break;
472 
473     case TESSPRIMITIVETYPE_QUADS:
474         str << "inner: { " << tessLevels.inner[0] << ", " << tessLevels.inner[1] << " }, "
475             << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << ", " << tessLevels.outer[2] << ", "
476             << tessLevels.outer[3] << " }";
477         break;
478 
479     default:
480         DE_ASSERT(false);
481     }
482 
483     return str.str();
484 }
485 
486 //! Assumes array sizes inner[2] and outer[4].
getTessellationLevelsString(const float * inner,const float * outer)487 std::string getTessellationLevelsString(const float *inner, const float *outer)
488 {
489     const TessLevels tessLevels = {{inner[0], inner[1]}, {outer[0], outer[1], outer[2], outer[3]}};
490     return getTessellationLevelsString(tessLevels, TESSPRIMITIVETYPE_QUADS);
491 }
492 
493 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec
494 // (e.g. it may not exactly hold that u+v+w == 1.0f, or [uvw] + (1.0f-[uvw]) == 1.0f).
generateReferenceTriangleTessCoords(const SpacingMode spacingMode,const int inner,const int outer0,const int outer1,const int outer2)495 std::vector<tcu::Vec3> generateReferenceTriangleTessCoords(const SpacingMode spacingMode, const int inner,
496                                                            const int outer0, const int outer1, const int outer2)
497 {
498     std::vector<tcu::Vec3> tessCoords;
499 
500     if (inner == 1)
501     {
502         if (outer0 == 1 && outer1 == 1 && outer2 == 1)
503         {
504             tessCoords.push_back(tcu::Vec3(1.0f, 0.0f, 0.0f));
505             tessCoords.push_back(tcu::Vec3(0.0f, 1.0f, 0.0f));
506             tessCoords.push_back(tcu::Vec3(0.0f, 0.0f, 1.0f));
507             return tessCoords;
508         }
509         else
510             return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
511                                                        outer0, outer1, outer2);
512     }
513     else
514     {
515         for (int i = 0; i < outer0; i++)
516         {
517             const float v = (float)i / (float)outer0;
518             tessCoords.push_back(tcu::Vec3(0.0f, v, 1.0f - v));
519         }
520         for (int i = 0; i < outer1; i++)
521         {
522             const float v = (float)i / (float)outer1;
523             tessCoords.push_back(tcu::Vec3(1.0f - v, 0.0f, v));
524         }
525         for (int i = 0; i < outer2; i++)
526         {
527             const float v = (float)i / (float)outer2;
528             tessCoords.push_back(tcu::Vec3(v, 1.0f - v, 0.0f));
529         }
530 
531         const int numInnerTriangles = inner / 2;
532         for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
533         {
534             const int curInnerTriangleLevel = inner - 2 * (innerTriangleNdx + 1);
535 
536             if (curInnerTriangleLevel == 0)
537                 tessCoords.push_back(tcu::Vec3(1.0f / 3.0f));
538             else
539             {
540                 const float minUVW         = (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner);
541                 const float maxUVW         = 1.0f - 2.0f * minUVW;
542                 const tcu::Vec3 corners[3] = {tcu::Vec3(maxUVW, minUVW, minUVW), tcu::Vec3(minUVW, maxUVW, minUVW),
543                                               tcu::Vec3(minUVW, minUVW, maxUVW)};
544 
545                 for (int i = 0; i < curInnerTriangleLevel; i++)
546                 {
547                     const float f = (float)i / (float)curInnerTriangleLevel;
548                     for (int j = 0; j < 3; j++)
549                         tessCoords.push_back((1.0f - f) * corners[j] + f * corners[(j + 1) % 3]);
550                 }
551             }
552         }
553 
554         return tessCoords;
555     }
556 }
557 
558 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec
559 // (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
generateReferenceQuadTessCoords(const SpacingMode spacingMode,const int inner0,const int inner1,const int outer0,const int outer1,const int outer2,const int outer3)560 std::vector<tcu::Vec3> generateReferenceQuadTessCoords(const SpacingMode spacingMode, const int inner0,
561                                                        const int inner1, const int outer0, const int outer1,
562                                                        const int outer2, const int outer3)
563 {
564     std::vector<tcu::Vec3> tessCoords;
565 
566     if (inner0 == 1 || inner1 == 1)
567     {
568         if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
569         {
570             tessCoords.push_back(tcu::Vec3(0.0f, 0.0f, 0.0f));
571             tessCoords.push_back(tcu::Vec3(1.0f, 0.0f, 0.0f));
572             tessCoords.push_back(tcu::Vec3(0.0f, 1.0f, 0.0f));
573             tessCoords.push_back(tcu::Vec3(1.0f, 1.0f, 0.0f));
574             return tessCoords;
575         }
576         else
577             return generateReferenceQuadTessCoords(spacingMode,
578                                                    inner0 > 1                                ? inner0 :
579                                                    spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 :
580                                                                                                2,
581                                                    inner1 > 1                                ? inner1 :
582                                                    spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 :
583                                                                                                2,
584                                                    outer0, outer1, outer2, outer3);
585     }
586     else
587     {
588         for (int i = 0; i < outer0; i++)
589         {
590             const float v = (float)i / (float)outer0;
591             tessCoords.push_back(tcu::Vec3(0.0f, v, 0.0f));
592         }
593         for (int i = 0; i < outer1; i++)
594         {
595             const float v = (float)i / (float)outer1;
596             tessCoords.push_back(tcu::Vec3(1.0f - v, 0.0f, 0.0f));
597         }
598         for (int i = 0; i < outer2; i++)
599         {
600             const float v = (float)i / (float)outer2;
601             tessCoords.push_back(tcu::Vec3(1.0f, 1.0f - v, 0.0f));
602         }
603         for (int i = 0; i < outer3; i++)
604         {
605             const float v = (float)i / (float)outer3;
606             tessCoords.push_back(tcu::Vec3(v, 1.0f, 0.0f));
607         }
608 
609         for (int innerVtxY = 0; innerVtxY < inner1 - 1; innerVtxY++)
610             for (int innerVtxX = 0; innerVtxX < inner0 - 1; innerVtxX++)
611                 tessCoords.push_back(
612                     tcu::Vec3((float)(innerVtxX + 1) / (float)inner0, (float)(innerVtxY + 1) / (float)inner1, 0.0f));
613 
614         return tessCoords;
615     }
616 }
617 
618 // \note The tessellation coordinates generated by this function could break some of the rules given in the spec
619 // (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
generateReferenceIsolineTessCoords(const int outer0,const int outer1)620 std::vector<tcu::Vec3> generateReferenceIsolineTessCoords(const int outer0, const int outer1)
621 {
622     std::vector<tcu::Vec3> tessCoords;
623 
624     for (int y = 0; y < outer0; y++)
625         for (int x = 0; x < outer1 + 1; x++)
626             tessCoords.push_back(tcu::Vec3((float)x / (float)outer1, (float)y / (float)outer0, 0.0f));
627 
628     return tessCoords;
629 }
630 
referencePointModePrimitiveCount(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)631 static int referencePointModePrimitiveCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
632                                             const float *innerLevels, const float *outerLevels)
633 {
634     if (isPatchDiscarded(primitiveType, outerLevels))
635         return 0;
636 
637     switch (primitiveType)
638     {
639     case TESSPRIMITIVETYPE_TRIANGLES:
640     {
641         int inner;
642         int outer[3];
643         getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
644         return static_cast<int>(
645             generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size());
646     }
647 
648     case TESSPRIMITIVETYPE_QUADS:
649     {
650         int inner[2];
651         int outer[4];
652         getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
653         return static_cast<int>(
654             generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3])
655                 .size());
656     }
657 
658     case TESSPRIMITIVETYPE_ISOLINES:
659     {
660         int outer[2];
661         getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
662         return static_cast<int>(generateReferenceIsolineTessCoords(outer[0], outer[1]).size());
663     }
664 
665     default:
666         DE_ASSERT(false);
667         return 0;
668     }
669 }
670 
referenceTriangleNonPointModePrimitiveCount(const SpacingMode spacingMode,const int inner,const int outer0,const int outer1,const int outer2)671 static int referenceTriangleNonPointModePrimitiveCount(const SpacingMode spacingMode, const int inner, const int outer0,
672                                                        const int outer1, const int outer2)
673 {
674     if (inner == 1)
675     {
676         if (outer0 == 1 && outer1 == 1 && outer2 == 1)
677             return 1;
678         else
679             return referenceTriangleNonPointModePrimitiveCount(
680                 spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2, outer0, outer1, outer2);
681     }
682     else
683     {
684         int result = outer0 + outer1 + outer2;
685 
686         const int numInnerTriangles = inner / 2;
687         for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
688         {
689             const int curInnerTriangleLevel = inner - 2 * (innerTriangleNdx + 1);
690 
691             if (curInnerTriangleLevel == 1)
692                 result += 4;
693             else
694                 result += 2 * 3 * curInnerTriangleLevel;
695         }
696 
697         return result;
698     }
699 }
700 
referenceQuadNonPointModePrimitiveCount(const SpacingMode spacingMode,const int inner0,const int inner1,const int outer0,const int outer1,const int outer2,const int outer3)701 static int referenceQuadNonPointModePrimitiveCount(const SpacingMode spacingMode, const int inner0, const int inner1,
702                                                    const int outer0, const int outer1, const int outer2,
703                                                    const int outer3)
704 {
705     if (inner0 == 1 || inner1 == 1)
706     {
707         if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
708             return 2;
709         else
710             return referenceQuadNonPointModePrimitiveCount(spacingMode,
711                                                            inner0 > 1                                ? inner0 :
712                                                            spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 :
713                                                                                                        2,
714                                                            inner1 > 1                                ? inner1 :
715                                                            spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 :
716                                                                                                        2,
717                                                            outer0, outer1, outer2, outer3);
718     }
719     else
720         return 2 * (inner0 - 2) * (inner1 - 2) + 2 * (inner0 - 2) + 2 * (inner1 - 2) + outer0 + outer1 + outer2 +
721                outer3;
722 }
723 
referenceIsolineNonPointModePrimitiveCount(const int outer0,const int outer1)724 static inline int referenceIsolineNonPointModePrimitiveCount(const int outer0, const int outer1)
725 {
726     return outer0 * outer1;
727 }
728 
referenceNonPointModePrimitiveCount(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const float * innerLevels,const float * outerLevels)729 static int referenceNonPointModePrimitiveCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
730                                                const float *innerLevels, const float *outerLevels)
731 {
732     if (isPatchDiscarded(primitiveType, outerLevels))
733         return 0;
734 
735     switch (primitiveType)
736     {
737     case TESSPRIMITIVETYPE_TRIANGLES:
738     {
739         int inner;
740         int outer[3];
741         getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
742         return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]);
743     }
744 
745     case TESSPRIMITIVETYPE_QUADS:
746     {
747         int inner[2];
748         int outer[4];
749         getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
750         return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2],
751                                                        outer[3]);
752     }
753 
754     case TESSPRIMITIVETYPE_ISOLINES:
755     {
756         int outer[2];
757         getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
758         return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]);
759     }
760 
761     default:
762         DE_ASSERT(false);
763         return 0;
764     }
765 }
766 
numVerticesPerPrimitive(const TessPrimitiveType primitiveType,const bool usePointMode)767 int numVerticesPerPrimitive(const TessPrimitiveType primitiveType, const bool usePointMode)
768 {
769     if (usePointMode)
770         return 1;
771 
772     switch (primitiveType)
773     {
774     case TESSPRIMITIVETYPE_TRIANGLES:
775         return 3;
776     case TESSPRIMITIVETYPE_QUADS:
777         return 3; // quads are composed of two triangles
778     case TESSPRIMITIVETYPE_ISOLINES:
779         return 2;
780     default:
781         DE_ASSERT(false);
782         return 0;
783     }
784 }
785 
referencePrimitiveCount(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const bool usePointMode,const float * innerLevels,const float * outerLevels)786 int referencePrimitiveCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode,
787                             const bool usePointMode, const float *innerLevels, const float *outerLevels)
788 {
789     return usePointMode ? referencePointModePrimitiveCount(primitiveType, spacingMode, innerLevels, outerLevels) :
790                           referenceNonPointModePrimitiveCount(primitiveType, spacingMode, innerLevels, outerLevels);
791 }
792 
793 //! In point mode this should return the number of unique vertices, while in non-point mode the maximum theoretical number of verticies.
794 //! Actual implementation will likely return a much smaller number because the shader isn't required to be run for duplicate coordinates.
referenceVertexCount(const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const bool usePointMode,const float * innerLevels,const float * outerLevels)795 int referenceVertexCount(const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode,
796                          const float *innerLevels, const float *outerLevels)
797 {
798     return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels) *
799            numVerticesPerPrimitive(primitiveType, usePointMode);
800 }
801 
requireFeatures(const InstanceInterface & vki,const VkPhysicalDevice physDevice,const FeatureFlags flags)802 void requireFeatures(const InstanceInterface &vki, const VkPhysicalDevice physDevice, const FeatureFlags flags)
803 {
804     const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
805 
806     if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader)
807         throw tcu::NotSupportedError("Tessellation shader not supported");
808 
809     if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader)
810         throw tcu::NotSupportedError("Geometry shader not supported");
811 
812     if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64)
813         throw tcu::NotSupportedError("Double-precision floats not supported");
814 
815     if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics)
816         throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline");
817 
818     if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics)
819         throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader");
820 
821     if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) &&
822         !features.shaderTessellationAndGeometryPointSize)
823         throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in");
824 }
825 
826 } // namespace tessellation
827 } // namespace vkt
828