1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2018 The Khronos Group Inc.
6 * Copyright (c) 2018 The Android Open Source Project
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 Use of gl_ViewportIndex in Vertex and Tessellation Shaders
23 * (part of VK_EXT_ShaderViewportIndexLayer)
24 *//*--------------------------------------------------------------------*/
25
26 #include "vktDrawShaderViewportIndexTests.hpp"
27
28 #include "vktDrawBaseClass.hpp"
29 #include "vktTestCaseUtil.hpp"
30
31 #include "vkDefs.hpp"
32 #include "vkRef.hpp"
33 #include "vkRefUtil.hpp"
34 #include "vkTypeUtil.hpp"
35 #include "vkMemUtil.hpp"
36 #include "vkPrograms.hpp"
37 #include "vkImageUtil.hpp"
38 #include "vkQueryUtil.hpp"
39 #include "vkCmdUtil.hpp"
40 #include "vkObjUtil.hpp"
41 #include "vkBuilderUtil.hpp"
42 #include "vkBufferWithMemory.hpp"
43
44 #include "tcuTestLog.hpp"
45 #include "tcuVector.hpp"
46 #include "tcuImageCompare.hpp"
47 #include "tcuTextureUtil.hpp"
48
49 #include "deUniquePtr.hpp"
50 #include "deMath.h"
51
52 #include <vector>
53 #include <memory>
54
55 namespace vkt
56 {
57 namespace Draw
58 {
59 using namespace vk;
60 using de::UniquePtr;
61 using de::MovePtr;
62 using de::SharedPtr;
63 using tcu::Vec4;
64 using tcu::Vec2;
65 using tcu::UVec2;
66 using tcu::UVec4;
67
68 namespace
69 {
70
71 enum Constants
72 {
73 MIN_MAX_VIEWPORTS = 16, //!< Minimum number of viewports for an implementation supporting multiViewport.
74 };
75
76 struct FragmentTestParams
77 {
78 int numViewports;
79 bool writeFromVertex;
80
FragmentTestParamsvkt::Draw::__anond1ac37210111::FragmentTestParams81 FragmentTestParams (int nvp, bool write)
82 : numViewports (nvp)
83 , writeFromVertex (write)
84 {
85 if (!write)
86 DE_ASSERT(nvp == 1);
87 }
88 };
89
90 template<typename T>
sizeInBytes(const std::vector<T> & vec)91 inline VkDeviceSize sizeInBytes(const std::vector<T>& vec)
92 {
93 return vec.size() * sizeof(vec[0]);
94 }
95
makeImageCreateInfo(const VkFormat format,const UVec2 & size,VkImageUsageFlags usage)96 VkImageCreateInfo makeImageCreateInfo (const VkFormat format, const UVec2& size, VkImageUsageFlags usage)
97 {
98 const VkImageCreateInfo imageParams =
99 {
100 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
101 DE_NULL, // const void* pNext;
102 (VkImageCreateFlags)0, // VkImageCreateFlags flags;
103 VK_IMAGE_TYPE_2D, // VkImageType imageType;
104 format, // VkFormat format;
105 makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
106 1u, // deUint32 mipLevels;
107 1u, // deUint32 arrayLayers;
108 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
109 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
110 usage, // VkImageUsageFlags usage;
111 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
112 0u, // deUint32 queueFamilyIndexCount;
113 DE_NULL, // const deUint32* pQueueFamilyIndices;
114 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
115 };
116 return imageParams;
117 }
118
makeGraphicsPipeline(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkRenderPass renderPass,const VkShaderModule vertexModule,const VkShaderModule tessellationControlModule,const VkShaderModule tessellationEvaluationModule,const VkShaderModule fragmentModule,const UVec2 renderSize,const int numViewports,const std::vector<UVec4> cells)119 Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface& vk,
120 const VkDevice device,
121 const VkPipelineLayout pipelineLayout,
122 const VkRenderPass renderPass,
123 const VkShaderModule vertexModule,
124 const VkShaderModule tessellationControlModule,
125 const VkShaderModule tessellationEvaluationModule,
126 const VkShaderModule fragmentModule,
127 const UVec2 renderSize,
128 const int numViewports,
129 const std::vector<UVec4> cells)
130 {
131 const VkVertexInputBindingDescription vertexInputBindingDescription =
132 {
133 0u, // uint32_t binding;
134 sizeof(PositionColorVertex), // uint32_t stride;
135 VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
136 };
137
138 const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] =
139 {
140 {
141 0u, // uint32_t location;
142 0u, // uint32_t binding;
143 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
144 0u, // uint32_t offset;
145 },
146 {
147 1u, // uint32_t location;
148 0u, // uint32_t binding;
149 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
150 sizeof(Vec4), // uint32_t offset;
151 },
152 };
153
154 const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo =
155 {
156 VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
157 DE_NULL, // const void* pNext;
158 (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
159 1u, // uint32_t vertexBindingDescriptionCount;
160 &vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
161 DE_LENGTH_OF_ARRAY(vertexInputAttributeDescriptions), // uint32_t vertexAttributeDescriptionCount;
162 vertexInputAttributeDescriptions, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
163 };
164
165 const bool useTessellationShaders = (tessellationControlModule != DE_NULL) && (tessellationEvaluationModule != DE_NULL);
166
167 const VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo =
168 {
169 VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
170 DE_NULL, // const void* pNext;
171 (VkPipelineInputAssemblyStateCreateFlags)0, // VkPipelineInputAssemblyStateCreateFlags flags;
172 useTessellationShaders ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // VkPrimitiveTopology topology;
173 VK_FALSE, // VkBool32 primitiveRestartEnable;
174 };
175
176 DE_ASSERT(numViewports == static_cast<int>(cells.size()));
177
178 std::vector<VkViewport> viewports;
179 viewports.reserve(numViewports);
180
181 std::vector<VkRect2D> rectScissors;
182 rectScissors.reserve(numViewports);
183
184 for (std::vector<UVec4>::const_iterator it = cells.begin(); it != cells.end(); ++it) {
185 const VkViewport viewport = makeViewport(float(it->x()), float(it->y()), float(it->z()), float(it->w()), 0.0f, 1.0f);
186 viewports.push_back(viewport);
187 const VkRect2D rect = makeRect2D(renderSize);
188 rectScissors.push_back(rect);
189 }
190
191 const VkPipelineViewportStateCreateInfo pipelineViewportStateInfo =
192 {
193 VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
194 DE_NULL, // const void* pNext;
195 (VkPipelineViewportStateCreateFlags)0, // VkPipelineViewportStateCreateFlags flags;
196 static_cast<deUint32>(numViewports), // uint32_t viewportCount;
197 &viewports[0], // const VkViewport* pViewports;
198 static_cast<deUint32>(numViewports), // uint32_t scissorCount;
199 &rectScissors[0], // const VkRect2D* pScissors;
200 };
201
202 const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo =
203 {
204 VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
205 DE_NULL, // const void* pNext;
206 (VkPipelineRasterizationStateCreateFlags)0, // VkPipelineRasterizationStateCreateFlags flags;
207 VK_FALSE, // VkBool32 depthClampEnable;
208 VK_FALSE, // VkBool32 rasterizerDiscardEnable;
209 VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
210 VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
211 VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
212 VK_FALSE, // VkBool32 depthBiasEnable;
213 0.0f, // float depthBiasConstantFactor;
214 0.0f, // float depthBiasClamp;
215 0.0f, // float depthBiasSlopeFactor;
216 1.0f, // float lineWidth;
217 };
218
219 const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo =
220 {
221 VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
222 DE_NULL, // const void* pNext;
223 (VkPipelineMultisampleStateCreateFlags)0, // VkPipelineMultisampleStateCreateFlags flags;
224 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
225 VK_FALSE, // VkBool32 sampleShadingEnable;
226 0.0f, // float minSampleShading;
227 DE_NULL, // const VkSampleMask* pSampleMask;
228 VK_FALSE, // VkBool32 alphaToCoverageEnable;
229 VK_FALSE // VkBool32 alphaToOneEnable;
230 };
231
232 const VkStencilOpState stencilOpState = makeStencilOpState(
233 VK_STENCIL_OP_KEEP, // stencil fail
234 VK_STENCIL_OP_KEEP, // depth & stencil pass
235 VK_STENCIL_OP_KEEP, // depth only fail
236 VK_COMPARE_OP_ALWAYS, // compare op
237 0u, // compare mask
238 0u, // write mask
239 0u); // reference
240
241 VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo =
242 {
243 VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
244 DE_NULL, // const void* pNext;
245 (VkPipelineDepthStencilStateCreateFlags)0, // VkPipelineDepthStencilStateCreateFlags flags;
246 VK_FALSE, // VkBool32 depthTestEnable;
247 VK_FALSE, // VkBool32 depthWriteEnable;
248 VK_COMPARE_OP_LESS, // VkCompareOp depthCompareOp;
249 VK_FALSE, // VkBool32 depthBoundsTestEnable;
250 VK_FALSE, // VkBool32 stencilTestEnable;
251 stencilOpState, // VkStencilOpState front;
252 stencilOpState, // VkStencilOpState back;
253 0.0f, // float minDepthBounds;
254 1.0f, // float maxDepthBounds;
255 };
256
257 const VkColorComponentFlags colorComponentsAll = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
258 const VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState =
259 {
260 VK_FALSE, // VkBool32 blendEnable;
261 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
262 VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor;
263 VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp;
264 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
265 VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor;
266 VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp;
267 colorComponentsAll, // VkColorComponentFlags colorWriteMask;
268 };
269
270 const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo =
271 {
272 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
273 DE_NULL, // const void* pNext;
274 (VkPipelineColorBlendStateCreateFlags)0, // VkPipelineColorBlendStateCreateFlags flags;
275 VK_FALSE, // VkBool32 logicOpEnable;
276 VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
277 1u, // deUint32 attachmentCount;
278 &pipelineColorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
279 { 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConstants[4];
280 };
281
282 const VkPipelineShaderStageCreateInfo pShaderStages[] =
283 {
284 {
285 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
286 DE_NULL, // const void* pNext;
287 (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
288 VK_SHADER_STAGE_VERTEX_BIT, // VkShaderStageFlagBits stage;
289 vertexModule, // VkShaderModule module;
290 "main", // const char* pName;
291 DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
292 },
293 {
294 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
295 DE_NULL, // const void* pNext;
296 (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
297 VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlagBits stage;
298 fragmentModule, // VkShaderModule module;
299 "main", // const char* pName;
300 DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
301 },
302 {
303 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
304 DE_NULL, // const void* pNext;
305 (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
306 VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, // VkShaderStageFlagBits stage;
307 tessellationControlModule, // VkShaderModule module;
308 "main", // const char* pName;
309 DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
310 },
311 {
312 VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
313 DE_NULL, // const void* pNext;
314 (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
315 VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, // VkShaderStageFlagBits stage;
316 tessellationEvaluationModule, // VkShaderModule module;
317 "main", // const char* pName;
318 DE_NULL, // const VkSpecializationInfo* pSpecializationInfo;
319 },
320 };
321
322 const VkPipelineTessellationStateCreateInfo pipelineTessellationStateInfo =
323 {
324 VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, // VkStructureType sType;
325 DE_NULL, // const void* pNext;
326 (VkPipelineTessellationStateCreateFlags)0, // VkPipelineTessellationStateCreateFlags flags;
327 3, // uint32_t patchControlPoints;
328 };
329
330 const VkGraphicsPipelineCreateInfo graphicsPipelineInfo =
331 {
332 VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
333 DE_NULL, // const void* pNext;
334 (VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags;
335 useTessellationShaders ? deUint32(4) : deUint32(2), // deUint32 stageCount;
336 pShaderStages, // const VkPipelineShaderStageCreateInfo* pStages;
337 &vertexInputStateInfo, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
338 &pipelineInputAssemblyStateInfo, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
339 useTessellationShaders ? &pipelineTessellationStateInfo : DE_NULL, // const VkPipelineTessellationStateCreateInfo* pTessellationState;
340 &pipelineViewportStateInfo, // const VkPipelineViewportStateCreateInfo* pViewportState;
341 &pipelineRasterizationStateInfo, // const VkPipelineRasterizationStateCreateInfo* pRasterizationState;
342 &pipelineMultisampleStateInfo, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
343 &pipelineDepthStencilStateInfo, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
344 &pipelineColorBlendStateInfo, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
345 DE_NULL, // const VkPipelineDynamicStateCreateInfo* pDynamicState;
346 pipelineLayout, // VkPipelineLayout layout;
347 renderPass, // VkRenderPass renderPass;
348 0u, // deUint32 subpass;
349 DE_NULL, // VkPipeline basePipelineHandle;
350 0, // deInt32 basePipelineIndex;
351 };
352
353 return createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineInfo);
354 }
355
generateGrid(const int numCells,const UVec2 & renderSize)356 std::vector<UVec4> generateGrid (const int numCells, const UVec2& renderSize)
357 {
358 const int numCols = deCeilFloatToInt32(deFloatSqrt(static_cast<float>(numCells)));
359 const int numRows = deCeilFloatToInt32(static_cast<float>(numCells) / static_cast<float>(numCols));
360 const int rectWidth = renderSize.x() / numCols;
361 const int rectHeight = renderSize.y() / numRows;
362
363 std::vector<UVec4> cells;
364 cells.reserve(numCells);
365
366 int x = 0;
367 int y = 0;
368
369 for (int cellNdx = 0; cellNdx < numCells; ++cellNdx)
370 {
371 const bool nextRow = (cellNdx != 0) && (cellNdx % numCols == 0);
372 if (nextRow)
373 {
374 x = 0;
375 y += rectHeight;
376 }
377
378 cells.push_back(UVec4(x, y, rectWidth, rectHeight));
379
380 x += rectWidth;
381 }
382
383 return cells;
384 }
385
generateColors(const int numColors)386 std::vector<Vec4> generateColors (const int numColors)
387 {
388 const Vec4 colors[] =
389 {
390 Vec4(0.18f, 0.42f, 0.17f, 1.0f),
391 Vec4(0.29f, 0.62f, 0.28f, 1.0f),
392 Vec4(0.59f, 0.84f, 0.44f, 1.0f),
393 Vec4(0.96f, 0.95f, 0.72f, 1.0f),
394 Vec4(0.94f, 0.55f, 0.39f, 1.0f),
395 Vec4(0.82f, 0.19f, 0.12f, 1.0f),
396 Vec4(0.46f, 0.15f, 0.26f, 1.0f),
397 Vec4(0.24f, 0.14f, 0.24f, 1.0f),
398 Vec4(0.49f, 0.31f, 0.26f, 1.0f),
399 Vec4(0.78f, 0.52f, 0.33f, 1.0f),
400 Vec4(0.94f, 0.82f, 0.31f, 1.0f),
401 Vec4(0.98f, 0.65f, 0.30f, 1.0f),
402 Vec4(0.22f, 0.65f, 0.53f, 1.0f),
403 Vec4(0.67f, 0.81f, 0.91f, 1.0f),
404 Vec4(0.43f, 0.44f, 0.75f, 1.0f),
405 Vec4(0.26f, 0.24f, 0.48f, 1.0f),
406 };
407
408 DE_ASSERT(numColors <= DE_LENGTH_OF_ARRAY(colors));
409
410 return std::vector<Vec4>(colors, colors + numColors);
411 }
412
413 //! Renders a colorful grid of rectangles.
generateReferenceImage(const tcu::TextureFormat format,const UVec2 & renderSize,const Vec4 & clearColor,const std::vector<UVec4> & cells,const std::vector<Vec4> & cellColors)414 tcu::TextureLevel generateReferenceImage (const tcu::TextureFormat format,
415 const UVec2& renderSize,
416 const Vec4& clearColor,
417 const std::vector<UVec4>& cells,
418 const std::vector<Vec4>& cellColors)
419 {
420 DE_ASSERT(cells.size() == cellColors.size());
421
422 tcu::TextureLevel image(format, renderSize.x(), renderSize.y());
423 tcu::clear(image.getAccess(), clearColor);
424
425 for (std::size_t i = 0; i < cells.size(); ++i)
426 {
427 const UVec4& cell = cells[i];
428 tcu::clear(
429 tcu::getSubregion(image.getAccess(), cell.x(), cell.y(), cell.z(), cell.w()),
430 cellColors[i]);
431 }
432
433 return image;
434 }
435
initVertexTestPrograms(SourceCollections & programCollection,const int numViewports)436 void initVertexTestPrograms (SourceCollections& programCollection, const int numViewports)
437 {
438 DE_UNREF(numViewports);
439
440 // Vertex shader
441 {
442 std::ostringstream src;
443 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
444 << "#extension GL_ARB_shader_viewport_layer_array : require\n"
445 << "\n"
446 << "layout(location = 0) in vec4 in_position;\n"
447 << "layout(location = 1) in vec4 in_color;\n"
448 << "layout(location = 0) out vec4 out_color;\n"
449 << "\n"
450 << "void main(void)\n"
451 << "{\n"
452 << " gl_ViewportIndex = gl_VertexIndex / 6;\n"
453 << " gl_Position = in_position;\n"
454 << " out_color = in_color;\n"
455 << "}\n";
456
457 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
458 }
459
460 // Fragment shader
461 {
462 std::ostringstream src;
463 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
464 << "\n"
465 << "layout(location = 0) in vec4 in_color;\n"
466 << "layout(location = 0) out vec4 out_color;\n"
467 << "\n"
468 << "void main(void)\n"
469 << "{\n"
470 << " out_color = in_color;\n"
471 << "}\n";
472
473 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
474 }
475 }
476
initFragmentTestPrograms(SourceCollections & programCollection,FragmentTestParams testParams)477 void initFragmentTestPrograms (SourceCollections& programCollection, FragmentTestParams testParams)
478 {
479 // Vertex shader.
480 {
481 std::ostringstream src;
482 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
483 << "#extension GL_ARB_shader_viewport_layer_array : require\n"
484 << "\n"
485 << "layout(location = 0) in vec4 in_position;\n"
486 << "layout(location = 1) in vec4 in_color;\n"
487 << "layout(location = 0) out vec4 out_color;\n"
488 << "\n"
489 << "void main(void)\n"
490 << "{\n"
491 << (testParams.writeFromVertex ? " gl_ViewportIndex = gl_VertexIndex / 6;\n" : "")
492 << " gl_Position = in_position;\n"
493 << " out_color = in_color;\n"
494 << "}\n";
495
496 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
497 }
498
499 // Fragment shader
500 {
501 // Ignore input color and choose one using the viewport index.
502 std::ostringstream src;
503 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
504 << "\n"
505 << "layout(location = 0) in vec4 in_color;\n"
506 << "layout(location = 0) out vec4 out_color;\n"
507 << "layout(set=0, binding=0) uniform Colors {\n"
508 << " vec4 color[" << testParams.numViewports << "];\n"
509 << "};\n"
510 << "\n"
511 << "void main(void)\n"
512 << "{\n"
513 << " out_color = color[gl_ViewportIndex];\n"
514 << "}\n";
515
516 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
517 }
518 }
519
initTessellationTestPrograms(SourceCollections & programCollection,const int numViewports)520 void initTessellationTestPrograms (SourceCollections& programCollection, const int numViewports)
521 {
522 DE_UNREF(numViewports);
523
524 // Vertex shader
525 {
526 std::ostringstream src;
527 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
528 << "\n"
529 << "layout(location = 0) in vec4 in_position;\n"
530 << "layout(location = 1) in vec4 in_color;\n"
531 << "layout(location = 0) out vec4 out_color;\n"
532 << "\n"
533 << "void main(void)\n"
534 << "{\n"
535 << " gl_Position = in_position;\n"
536 << " out_color = in_color;\n"
537 << "}\n";
538
539 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
540 }
541
542 // Tessellation control shader
543 {
544 std::ostringstream src;
545 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
546 << "\n"
547 << "layout(vertices = 3) out;\n"
548 << "\n"
549 << "layout(location = 0) in vec4 in_color[];\n"
550 << "layout(location = 0) out vec4 out_color[];\n"
551 << "\n"
552 << "void main(void)\n"
553 << "{\n"
554 << " if (gl_InvocationID == 0) {\n"
555 << " gl_TessLevelInner[0] = 1.0;\n"
556 << " gl_TessLevelInner[1] = 1.0;\n"
557 << " gl_TessLevelOuter[0] = 1.0;\n"
558 << " gl_TessLevelOuter[1] = 1.0;\n"
559 << " gl_TessLevelOuter[2] = 1.0;\n"
560 << " gl_TessLevelOuter[3] = 1.0;\n"
561 << " }\n"
562 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
563 << " out_color[gl_InvocationID] = in_color[gl_InvocationID];\n"
564 << "}\n";
565
566 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
567 }
568
569 // Tessellation evaluation shader
570 {
571 std::ostringstream src;
572 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
573 << "#extension GL_ARB_shader_viewport_layer_array : require\n"
574 << "\n"
575 << "layout(triangles, equal_spacing, cw) in;\n"
576 << "\n"
577 << "layout(location = 0) in vec4 in_color[];\n"
578 << "layout(location = 0) out vec4 out_color;\n"
579 << "\n"
580 << "void main(void)\n"
581 << "{\n"
582 << " gl_ViewportIndex = gl_PrimitiveID / 2;\n"
583 << " gl_Position = gl_in[0].gl_Position * gl_TessCoord.x +\n"
584 << " gl_in[1].gl_Position * gl_TessCoord.y +\n"
585 << " gl_in[2].gl_Position * gl_TessCoord.z;\n"
586 << "\n"
587 << " out_color = in_color[0] * gl_TessCoord.x +\n"
588 << " in_color[1] * gl_TessCoord.y +\n"
589 << " in_color[2] * gl_TessCoord.z;\n"
590 << "}\n";
591
592 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
593 }
594
595 // Fragment shader
596 {
597 std::ostringstream src;
598 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
599 << "\n"
600 << "layout(location = 0) in vec4 in_color;\n"
601 << "layout(location = 0) out vec4 out_color;\n"
602 << "\n"
603 << "void main(void)\n"
604 << "{\n"
605 << " out_color = in_color;\n"
606 << "}\n";
607
608 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
609 }
610 }
611
generateVertices(const std::vector<Vec4> & colors)612 std::vector<PositionColorVertex> generateVertices (const std::vector<Vec4>& colors)
613 {
614 // Two triangles for each color (viewport).
615 std::size_t total = colors.size() * 6;
616
617 std::vector<PositionColorVertex> result;
618 result.reserve(total);
619
620 for (std::size_t i = 0; i < total; ++i)
621 {
622 Vec4 pos;
623 pos.z() = 0.0;
624 pos.w() = 1.0;
625
626 switch (i % 6) {
627 case 0: pos.xy() = Vec2(-1.0, 1.0); break;
628 case 1: pos.xy() = Vec2( 1.0, 1.0); break;
629 case 2: pos.xy() = Vec2(-1.0, -1.0); break;
630 case 3: pos.xy() = Vec2( 1.0, -1.0); break;
631 case 4: pos.xy() = Vec2( 1.0, 1.0); break;
632 case 5: pos.xy() = Vec2(-1.0, -1.0); break;
633 }
634
635 result.push_back(PositionColorVertex(pos, colors[i/6]));
636 }
637
638 return result;
639 }
640
641 // Renderer generates two triangles per viewport, each pair using a different color. The
642 // numViewports are positioned to form a grid.
643 class Renderer
644 {
645 public:
646 enum Shader {
647 VERTEX,
648 TESSELLATION,
649 FRAGMENT,
650 };
651
Renderer(Context & context,const UVec2 & renderSize,const int numViewports,const std::vector<UVec4> & cells,const VkFormat colorFormat,const Vec4 & clearColor,const std::vector<Vec4> & colors,const Shader shader)652 Renderer (Context& context,
653 const UVec2& renderSize,
654 const int numViewports,
655 const std::vector<UVec4>& cells,
656 const VkFormat colorFormat,
657 const Vec4& clearColor,
658 const std::vector<Vec4>& colors,
659 const Shader shader)
660 : m_renderSize (renderSize)
661 , m_colorFormat (colorFormat)
662 , m_colorSubresourceRange (makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u))
663 , m_clearColor (clearColor)
664 , m_numViewports (numViewports)
665 , m_colors (colors)
666 , m_vertices (generateVertices(colors))
667 , m_shader (shader)
668 {
669 const DeviceInterface& vk = context.getDeviceInterface();
670 const VkDevice device = context.getDevice();
671 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
672 Allocator& allocator = context.getDefaultAllocator();
673 const VkDeviceSize vertexBufferSize = sizeInBytes(m_vertices);
674
675 m_colorImage = makeImage (vk, device, makeImageCreateInfo(m_colorFormat, m_renderSize, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
676 m_colorImageAlloc = bindImage (vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
677 m_colorAttachment = makeImageView (vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, m_colorSubresourceRange);
678
679 m_vertexBuffer = Buffer::createAndAlloc (vk, device, makeBufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), allocator, MemoryRequirement::HostVisible);
680
681 {
682 deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &m_vertices[0], static_cast<std::size_t>(vertexBufferSize));
683 flushAlloc(vk, device, m_vertexBuffer->getBoundMemory());
684 }
685
686 if (shader == TESSELLATION)
687 {
688 m_tessellationControlModule = createShaderModule (vk, device, context.getBinaryCollection().get("tesc"), 0u);
689 m_tessellationEvaluationModule = createShaderModule (vk, device, context.getBinaryCollection().get("tese"), 0u);
690 }
691
692 m_vertexModule = createShaderModule (vk, device, context.getBinaryCollection().get("vert"), 0u);
693 m_fragmentModule = createShaderModule (vk, device, context.getBinaryCollection().get("frag"), 0u);
694 m_renderPass = makeRenderPass (vk, device, m_colorFormat);
695 m_framebuffer = makeFramebuffer (vk, device, *m_renderPass, m_colorAttachment.get(),
696 static_cast<deUint32>(m_renderSize.x()), static_cast<deUint32>(m_renderSize.y()));
697
698 if (shader == FRAGMENT)
699 {
700 vk::DescriptorSetLayoutBuilder builder;
701 builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
702 m_descriptorSetLayout = builder.build(vk, device);
703 }
704
705 m_pipelineLayout = makePipelineLayout (vk, device, (shader == FRAGMENT ? m_descriptorSetLayout.get() : DE_NULL));
706 m_pipeline = makeGraphicsPipeline (vk, device, *m_pipelineLayout, *m_renderPass, *m_vertexModule, *m_tessellationControlModule,
707 *m_tessellationEvaluationModule, *m_fragmentModule, m_renderSize, m_numViewports, cells);
708 m_cmdPool = createCommandPool (vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
709 m_cmdBuffer = allocateCommandBuffer (vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
710 }
711
draw(Context & context,const VkBuffer colorBuffer) const712 void draw (Context& context, const VkBuffer colorBuffer) const
713 {
714 const DeviceInterface& vk = context.getDeviceInterface();
715 const VkDevice device = context.getDevice();
716 const VkQueue queue = context.getUniversalQueue();
717 Allocator& allocator = context.getDefaultAllocator();
718
719 beginCommandBuffer(vk, *m_cmdBuffer);
720
721 beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
722
723 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
724 {
725 const VkBuffer vertexBuffer = m_vertexBuffer->object();
726 const VkDeviceSize vertexBufferOffset = 0ull;
727 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &vertexBuffer, &vertexBufferOffset);
728 }
729
730 // Prepare colors buffer if needed.
731 std::unique_ptr<vk::BufferWithMemory> colorsBuffer;
732 vk::Move<vk::VkDescriptorPool> descriptorPool;
733 vk::Move<vk::VkDescriptorSet> descriptorSet;
734
735 if (m_shader == FRAGMENT)
736 {
737 // Create buffer.
738 const auto colorsBufferSize = m_colors.size() * sizeof(decltype(m_colors)::value_type);
739 const auto colorsBufferCreateInfo = vk::makeBufferCreateInfo(static_cast<VkDeviceSize>(colorsBufferSize), vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
740 colorsBuffer.reset(new vk::BufferWithMemory{vk, device, allocator, colorsBufferCreateInfo, MemoryRequirement::HostVisible});
741
742 // Copy colors and flush allocation.
743 auto& colorsBufferAlloc = colorsBuffer->getAllocation();
744 deMemcpy(colorsBufferAlloc.getHostPtr(), m_colors.data(), colorsBufferSize);
745 vk::flushAlloc(vk, device, colorsBufferAlloc);
746
747 // Descriptor pool.
748 vk::DescriptorPoolBuilder poolBuilder;
749 poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1u);
750 descriptorPool = poolBuilder.build(vk, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
751
752 // Descriptor set.
753 descriptorSet = vk::makeDescriptorSet(vk, device, descriptorPool.get(), m_descriptorSetLayout.get());
754
755 // Update and bind descriptor set.
756 const auto colorsBufferDescriptorInfo = vk::makeDescriptorBufferInfo(colorsBuffer->get(), 0ull, VK_WHOLE_SIZE);
757 vk::DescriptorSetUpdateBuilder updateBuilder;
758 updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &colorsBufferDescriptorInfo);
759 updateBuilder.update(vk, device);
760
761 vk.cmdBindDescriptorSets(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
762 }
763
764 vk.cmdDraw(*m_cmdBuffer, static_cast<deUint32>(m_numViewports * 6), 1u, 0u, 0u); // two triangles per viewport
765 endRenderPass(vk, *m_cmdBuffer);
766
767 copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, tcu::IVec2(m_renderSize.x(), m_renderSize.y()));
768
769 VK_CHECK(vk.endCommandBuffer(*m_cmdBuffer));
770 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
771 }
772
773 private:
774 const UVec2 m_renderSize;
775 const VkFormat m_colorFormat;
776 const VkImageSubresourceRange m_colorSubresourceRange;
777 const Vec4 m_clearColor;
778 const int m_numViewports;
779 const std::vector<Vec4> m_colors;
780 const std::vector<PositionColorVertex> m_vertices;
781 const Shader m_shader;
782
783 Move<VkImage> m_colorImage;
784 MovePtr<Allocation> m_colorImageAlloc;
785 Move<VkImageView> m_colorAttachment;
786 SharedPtr<Buffer> m_vertexBuffer;
787 Move<VkShaderModule> m_vertexModule;
788 Move<VkShaderModule> m_tessellationControlModule;
789 Move<VkShaderModule> m_tessellationEvaluationModule;
790 Move<VkShaderModule> m_fragmentModule;
791 Move<VkRenderPass> m_renderPass;
792 Move<VkFramebuffer> m_framebuffer;
793 Move<VkDescriptorSetLayout> m_descriptorSetLayout;
794 Move<VkPipelineLayout> m_pipelineLayout;
795 Move<VkPipeline> m_pipeline;
796 Move<VkCommandPool> m_cmdPool;
797 Move<VkCommandBuffer> m_cmdBuffer;
798
799 // "deleted"
800 Renderer (const Renderer&);
801 Renderer& operator= (const Renderer&);
802 };
803
testVertexFragmentShader(Context & context,const int numViewports,Renderer::Shader shader)804 tcu::TestStatus testVertexFragmentShader (Context& context, const int numViewports, Renderer::Shader shader)
805 {
806 const DeviceInterface& vk = context.getDeviceInterface();
807 const VkDevice device = context.getDevice();
808 Allocator& allocator = context.getDefaultAllocator();
809
810 const UVec2 renderSize (128, 128);
811 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
812 const Vec4 clearColor (0.5f, 0.5f, 0.5f, 1.0f);
813 const std::vector<Vec4> colors = generateColors(numViewports);
814 const std::vector<UVec4> cells = generateGrid(numViewports, renderSize);
815
816 const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
817
818 const SharedPtr<Buffer> colorBuffer = Buffer::createAndAlloc(vk, device, makeBufferCreateInfo(colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT), allocator, MemoryRequirement::HostVisible);
819
820 // Zero buffer.
821 {
822 const Allocation alloc = colorBuffer->getBoundMemory();
823 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(colorBufferSize));
824 flushAlloc(vk, device, alloc);
825 }
826
827 {
828 context.getTestContext().getLog()
829 << tcu::TestLog::Message << "Rendering a colorful grid of " << numViewports << " rectangle(s)." << tcu::TestLog::EndMessage
830 << tcu::TestLog::Message << "Not covered area will be filled with a gray color." << tcu::TestLog::EndMessage;
831 }
832
833 // Draw
834 {
835 const Renderer renderer (context, renderSize, numViewports, cells, colorFormat, clearColor, colors, shader);
836 renderer.draw(context, colorBuffer->object());
837 }
838
839 // Log image
840 {
841 const Allocation alloc = colorBuffer->getBoundMemory();
842 invalidateAlloc(vk, device, alloc);
843
844 const tcu::ConstPixelBufferAccess resultImage (mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u, alloc.getHostPtr());
845 const tcu::TextureLevel referenceImage = generateReferenceImage(mapVkFormat(colorFormat), renderSize, clearColor, cells, colors);
846
847 // Images should now match.
848 if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare", referenceImage.getAccess(), resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
849 TCU_FAIL("Rendered image is not correct");
850 }
851
852 return tcu::TestStatus::pass("OK");
853 }
854
testVertexShader(Context & context,const int numViewports)855 tcu::TestStatus testVertexShader (Context& context, const int numViewports)
856 {
857 return testVertexFragmentShader(context, numViewports, Renderer::VERTEX);
858 }
859
testFragmentShader(Context & context,FragmentTestParams testParams)860 tcu::TestStatus testFragmentShader (Context& context, FragmentTestParams testParams)
861 {
862 return testVertexFragmentShader(context, testParams.numViewports, Renderer::FRAGMENT);
863 }
864
testTessellationShader(Context & context,const int numViewports)865 tcu::TestStatus testTessellationShader (Context& context, const int numViewports)
866 {
867 const DeviceInterface& vk = context.getDeviceInterface();
868 const VkDevice device = context.getDevice();
869 Allocator& allocator = context.getDefaultAllocator();
870
871 const UVec2 renderSize (128, 128);
872 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
873 const Vec4 clearColor (0.5f, 0.5f, 0.5f, 1.0f);
874 const std::vector<Vec4> colors = generateColors(numViewports);
875 const std::vector<UVec4> cells = generateGrid(numViewports, renderSize);
876
877 const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
878
879 const SharedPtr<Buffer> colorBuffer = Buffer::createAndAlloc(vk, device, makeBufferCreateInfo(colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT), allocator, MemoryRequirement::HostVisible);
880
881 // Zero buffer.
882 {
883 const Allocation alloc = colorBuffer->getBoundMemory();
884 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(colorBufferSize));
885 flushAlloc(vk, device, alloc);
886 }
887
888 {
889 context.getTestContext().getLog()
890 << tcu::TestLog::Message << "Rendering a colorful grid of " << numViewports << " rectangle(s)." << tcu::TestLog::EndMessage
891 << tcu::TestLog::Message << "Not covered area will be filled with a gray color." << tcu::TestLog::EndMessage;
892 }
893
894 // Draw
895 {
896 const Renderer renderer (context, renderSize, numViewports, cells, colorFormat, clearColor, colors, Renderer::TESSELLATION);
897 renderer.draw(context, colorBuffer->object());
898 }
899
900 // Log image
901 {
902 const Allocation alloc = colorBuffer->getBoundMemory();
903 invalidateAlloc(vk, device, alloc);
904
905 const tcu::ConstPixelBufferAccess resultImage (mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u, alloc.getHostPtr());
906 const tcu::TextureLevel referenceImage = generateReferenceImage(mapVkFormat(colorFormat), renderSize, clearColor, cells, colors);
907
908 // Images should now match.
909 if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare", referenceImage.getAccess(), resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
910 TCU_FAIL("Rendered image is not correct");
911 }
912
913 return tcu::TestStatus::pass("OK");
914 }
915
checkSupportVertex(Context & context,const int)916 void checkSupportVertex (Context& context, const int)
917 {
918 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
919 context.requireDeviceFunctionality("VK_EXT_shader_viewport_index_layer");
920
921 if (context.getDeviceProperties().limits.maxViewports < MIN_MAX_VIEWPORTS)
922 TCU_FAIL("multiViewport supported but maxViewports is less than the minimum required");
923 }
924
checkSupportFragment(Context & context,FragmentTestParams)925 void checkSupportFragment (Context& context, FragmentTestParams)
926 {
927 checkSupportVertex(context, 0);
928 }
929
checkSupportTessellation(Context & context,const int)930 void checkSupportTessellation (Context& context, const int)
931 {
932 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
933
934 checkSupportVertex(context, 0);
935 }
936
937 } // anonymous
938
createShaderViewportIndexTests(tcu::TestContext & testCtx)939 tcu::TestCaseGroup* createShaderViewportIndexTests (tcu::TestContext& testCtx)
940 {
941 MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "shader_viewport_index", ""));
942
943 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
944 addFunctionCaseWithPrograms(group.get(), "vertex_shader_" + de::toString(numViewports), "", checkSupportVertex, initVertexTestPrograms, testVertexShader, numViewports);
945
946 addFunctionCaseWithPrograms(group.get(), "fragment_shader_implicit", "", checkSupportFragment, initFragmentTestPrograms, testFragmentShader, FragmentTestParams(1, false));
947 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
948 addFunctionCaseWithPrograms(group.get(), "fragment_shader_" + de::toString(numViewports), "", checkSupportFragment, initFragmentTestPrograms, testFragmentShader, FragmentTestParams(numViewports, true));
949
950 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
951 addFunctionCaseWithPrograms(group.get(), "tessellation_shader_" + de::toString(numViewports), "", checkSupportTessellation, initTessellationTestPrograms, testTessellationShader, numViewports);
952
953 return group.release();
954 }
955
956 } // Draw
957 } // vkt
958