1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2022 The Khronos Group Inc.
6 * Copyright (c) 2022 Valve Corporation.
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 Tests involving dynamic patch control points
23 *//*--------------------------------------------------------------------*/
24 
25 #include "vktPipelineDynamicControlPoints.hpp"
26 #include "vktTestCase.hpp"
27 #include "vkPrograms.hpp"
28 #include "vkRefUtil.hpp"
29 #include "vktTestCaseUtil.hpp"
30 #include "vkImageWithMemory.hpp"
31 
32 #include "vktPipelineImageUtil.hpp"
33 #include "vktTestCase.hpp"
34 
35 #include "vkDefs.hpp"
36 #include "vkTypeUtil.hpp"
37 #include "vkQueryUtil.hpp"
38 #include "vkObjUtil.hpp"
39 #include "vkBufferWithMemory.hpp"
40 #include "vkImageWithMemory.hpp"
41 #include "vkBuilderUtil.hpp"
42 #include "vkCmdUtil.hpp"
43 #include "vkImageUtil.hpp"
44 
45 #include "tcuVector.hpp"
46 #include "tcuMaybe.hpp"
47 #include "tcuImageCompare.hpp"
48 #include "tcuDefs.hpp"
49 #include "tcuTextureUtil.hpp"
50 #include "tcuTestLog.hpp"
51 #include "tcuVectorUtil.hpp"
52 #include "tcuStringTemplate.hpp"
53 
54 #include "deUniquePtr.hpp"
55 #include "deStringUtil.hpp"
56 
57 #include <vector>
58 #include <sstream>
59 #include <algorithm>
60 #include <utility>
61 #include <iterator>
62 #include <string>
63 #include <limits>
64 #include <memory>
65 #include <functional>
66 #include <cstddef>
67 #include <set>
68 
69 namespace vkt
70 {
71 namespace pipeline
72 {
73 
74 struct TestConfig {
75 	vk::PipelineConstructionType constructionType;
76 	bool changeOutput;
77 	bool firstClockwise;
78 	bool secondClockwise;
79 	vk::VkCullModeFlags cullMode;
80 	tcu::Vec4 expectedFirst;
81 	tcu::Vec4 expectedSecond;
82 };
83 
84 class DynamicControlPointsTestCase : public vkt::TestCase
85 {
86 public:
87 	DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, const std::string& description, TestConfig config);
88 	void            initPrograms            (vk::SourceCollections& programCollection) const override;
89 	TestInstance*   createInstance          (Context& context) const override;
90 	void            checkSupport            (Context& context) const override;
91 
92 private:
93 	TestConfig m_config;
94 };
95 
96 
97 class DynamicControlPointsTestInstance : public vkt::TestInstance
98 {
99 public:
100 	DynamicControlPointsTestInstance(Context& context, TestConfig config);
101 	virtual tcu::TestStatus iterate (void);
102 private:
103 	TestConfig m_config;
104 };
105 
106 
DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, const std::string& description, TestConfig config)107 DynamicControlPointsTestCase::DynamicControlPointsTestCase(tcu::TestContext& context, const std::string& name, const std::string& description,
108 	TestConfig config) : vkt::TestCase (context, name, description), m_config(config)
109 {
110 }
111 
checkSupport(Context& context) const112 void DynamicControlPointsTestCase::checkSupport(Context& context) const
113 {
114 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
115 	checkPipelineLibraryRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_config.constructionType);
116 	const auto& eds2Features  = context.getExtendedDynamicState2FeaturesEXT();
117 	if (!eds2Features.extendedDynamicState2PatchControlPoints) {
118 		TCU_THROW(NotSupportedError, "Dynamic patch control points aren't supported");
119 	}
120 }
121 
initPrograms(vk::SourceCollections& collection) const122 void DynamicControlPointsTestCase::initPrograms(vk::SourceCollections& collection) const
123 {
124 	const std::string firstWinding = m_config.firstClockwise ? "cw" : "ccw";
125 	const std::string secondWinding = m_config.secondClockwise ? "cw" : "ccw";
126 
127 	{
128 		std::ostringstream src;
129 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
130 		<< "vec2 positions[6] = vec2[](\n"
131 		<< "		vec2(-1.0, -1.0),"
132 		<< "		vec2(-1.0, 1.0),"
133 		<< "		vec2(1.0, -1.0),"
134 		<< "		vec2(1.0, -1.0),"
135 		<< "		vec2(-1.0, 1.0),"
136 		<< "		vec2(1.0, 1.0)"
137 		<< ");\n"
138 		<< "void main() {\n"
139 		<< "		gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);\n"
140 		<< "}";
141 		collection.glslSources.add("vert") << glu::VertexSource(src.str());
142 	}
143 
144 	{
145 		std::ostringstream src;
146 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
147 		<< "layout(location = 0) out vec4 outColor;\n"
148 		<< "layout(location = 0) in vec3 fragColor;"
149 		<< "void main() {\n"
150 		<< "	outColor = vec4(fragColor, 1.0);\n"
151 		<< "}";
152 		collection.glslSources.add("frag") << glu::FragmentSource(src.str());
153 	}
154 
155 	{
156 		std::ostringstream src;
157 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
158 		<< "layout(vertices = 3) out;\n"
159 		<< "void main (void)\n"
160 		<< "{\n"
161 		<< "    gl_TessLevelInner[0] = 2.0;\n"
162 		<< "    gl_TessLevelOuter[0] = 2.0;\n"
163 		<< "    gl_TessLevelOuter[1] = 2.0;\n"
164 		<< "    gl_TessLevelOuter[2] = 2.0;\n"
165 		<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
166 		<< "}\n";
167 		collection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
168 	}
169 
170 	{
171 		std::ostringstream src;
172 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
173 		<< "layout(triangles, " << firstWinding << ") in;\n"
174 		<< "layout(location = 0) out vec3 fragColor;"
175 		<< "\n"
176 		<< "void main (void)\n"
177 		<< "{\n"
178 		<< "    vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position;\n"
179 		<< "    vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position;\n"
180 		<< "    vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position;\n"
181 		<< "    gl_Position = p0 + p1 + p2;\n"
182 		<< "    fragColor = vec3(1.0, 0.0, 1.0); "
183 		<< "}\n";
184 		collection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
185 	}
186 
187 	{
188 		std::ostringstream src;
189 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
190 		<< "layout(vertices = " << (m_config.changeOutput ? 4 : 3) <<  ") out;\n"
191 		<< "void main (void)\n"
192 		<< "{\n"
193 		<< "    gl_TessLevelInner[0] = 2;\n"
194 		<< "    gl_TessLevelOuter[0] = 2.0;\n"
195 		<< "    gl_TessLevelOuter[1] = 2.0;\n"
196 		<< "    gl_TessLevelOuter[2] = 2.0;\n"
197 		<< "if (gl_InvocationID < 3) {"
198 		<< "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
199 		<< "} else {"
200 		<< "    gl_out[gl_InvocationID].gl_Position = vec4(1.0, 0.0, 1.0, 1.0);"
201 		<< "}"
202 		<< "}\n";
203 		collection.glslSources.add("tesc2") << glu::TessellationControlSource(src.str());
204 	}
205 
206 	{
207 		std::ostringstream src;
208 		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
209 		<< "layout(triangles, " << secondWinding << ") in;\n"
210 		<< "layout(location = 0) out vec3 fragColor;"
211 		<< "\n"
212 		<< "void main (void)\n"
213 		<< "{\n"
214 		<< "    vec4 p0 = gl_TessCoord.x * gl_in[0].gl_Position;\n"
215 		<< "    vec4 p1 = gl_TessCoord.y * gl_in[1].gl_Position;\n"
216 		<< "    vec4 p2 = gl_TessCoord.z * gl_in[2].gl_Position;\n"
217 		<< "    gl_Position = p0 + p1 + p2;\n";
218 		if (m_config.changeOutput) {
219 			src << "    fragColor = vec3(gl_in[3].gl_Position.xyz);";
220 		} else {
221 			src << "    fragColor = vec3(1.0, 0.0, 1.0);";
222 		}
223 		src << "}\n";
224 		collection.glslSources.add("tese2") << glu::TessellationEvaluationSource(src.str());
225 	}
226 }
227 
createInstance(Context& context) const228 TestInstance* DynamicControlPointsTestCase::createInstance(Context& context) const
229 {
230 	return new DynamicControlPointsTestInstance(context, m_config);
231 }
232 
DynamicControlPointsTestInstance(Context& context, TestConfig config)233 DynamicControlPointsTestInstance::DynamicControlPointsTestInstance(Context& context, TestConfig config) :
234 	vkt::TestInstance (context), m_config (config) { }
235 
236 //make a buffer to read an image back after rendering
makeBufferForImage(const vk::DeviceInterface& vkd, const vk::VkDevice device, vk::Allocator& allocator, tcu::TextureFormat tcuFormat, vk::VkExtent3D imageExtent)237 std::unique_ptr<vk::BufferWithMemory> makeBufferForImage(const vk::DeviceInterface& vkd, const vk::VkDevice device, vk::Allocator& allocator, tcu::TextureFormat tcuFormat, vk::VkExtent3D imageExtent)
238 {
239 	const auto	outBufferSize		= static_cast<vk::VkDeviceSize>(static_cast<uint32_t>(tcu::getPixelSize(tcuFormat)) * imageExtent.width * imageExtent.height);
240 	const auto	outBufferUsage	= vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT;
241 	const auto	outBufferInfo		= makeBufferCreateInfo(outBufferSize, outBufferUsage);
242 	auto outBuffer = std::unique_ptr<vk::BufferWithMemory>(new vk::BufferWithMemory(vkd, device, allocator, outBufferInfo, vk::MemoryRequirement::HostVisible));
243 
244 	return outBuffer;
245 }
246 
iterate(void)247 tcu::TestStatus DynamicControlPointsTestInstance::iterate(void)
248 {
249 	const auto& vkd			= m_context.getDeviceInterface();
250 	const auto  device		= m_context.getDevice();
251 	auto& alloc				= m_context.getDefaultAllocator();
252 	auto imageFormat		= vk::VK_FORMAT_R8G8B8A8_UNORM;
253 	auto imageExtent		= vk::makeExtent3D(4, 4, 1u);
254 	auto mappedFormat		= mapVkFormat(imageFormat);
255 
256 	const tcu::IVec3 imageDim	(static_cast<int>(imageExtent.width), static_cast<int>(imageExtent.height), static_cast<int>(imageExtent.depth));
257 	const tcu::IVec2 imageSize	(imageDim.x(), imageDim.y());
258 
259 	de::MovePtr<vk::ImageWithMemory>  colorAttachment;
260 
261 	vk::GraphicsPipelineWrapper pipeline1(vkd, device, m_config.constructionType);
262 	vk::GraphicsPipelineWrapper pipeline2(vkd, device, m_config.constructionType);
263 	const auto  qIndex      = m_context.getUniversalQueueFamilyIndex();
264 
265 	const auto  imageUsage      = static_cast<vk::VkImageUsageFlags>(vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
266 	const vk::VkImageCreateInfo imageCreateInfo =
267 	{
268 		vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType				sType;
269 		nullptr,									//	const void*					pNext;
270 		0u,											//	VkImageCreateFlags			flags;
271 		vk::VK_IMAGE_TYPE_2D,						//	VkImageType					imageType;
272 		imageFormat,								//	VkFormat					format;
273 		imageExtent,								//	VkExtent3D					extent;
274 		1u,											//	deUint32					mipLevels;
275 		1u,											//	deUint32					arrayLayers;
276 		vk::VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits		samples;
277 		vk::VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling				tiling;
278 		imageUsage,									//	VkImageUsageFlags			usage;
279 		vk::VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode				sharingMode;
280 		0,											//	deUint32					queueFamilyIndexCount;
281 		nullptr,									//	const deUint32*				pQueueFamilyIndices;
282 		vk::VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout				initialLayout;
283 	};
284 
285 	const auto subresourceRange		= vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
286 	colorAttachment               = de::MovePtr<vk::ImageWithMemory>(new vk::ImageWithMemory(vkd, device, alloc, imageCreateInfo, vk::MemoryRequirement::Any));
287 	auto colorAttachmentView      = vk::makeImageView(vkd, device, colorAttachment->get(), vk::VK_IMAGE_VIEW_TYPE_2D, imageFormat, subresourceRange);
288 
289 	auto renderPass   = makeRenderPass(vkd, device, imageFormat);
290 	auto framebuffer  = makeFramebuffer(vkd, device, renderPass.get(), colorAttachmentView.get(), imageExtent.width, imageExtent.height);
291 
292 	//buffer to read the output image
293 	auto outBuffer = makeBufferForImage(vkd, device, alloc, mappedFormat, imageExtent);
294 	auto&		outBufferAlloc	= outBuffer->getAllocation();
295 	void*		outBufferData		= outBufferAlloc.getHostPtr();
296 
297 	const vk::VkPipelineVertexInputStateCreateInfo vertexInputState = vk::initVulkanStructure();
298 
299 	const std::vector<vk::VkViewport>	viewport_left	{ vk::makeViewport(0.0f, 0.0f, (float)imageExtent.width / 2, (float)imageExtent.height, 0.0f, 1.0f) };
300 	const std::vector<vk::VkViewport>	viewport_right  { vk::makeViewport((float)imageExtent.width / 2, 0.0f, (float)imageExtent.width / 2, (float)imageExtent.height, 0.0f, 1.0f) };
301 	const std::vector<vk::VkRect2D>		scissors_left	{ vk::makeRect2D(0.0f, 0.0f, imageExtent.width / 2, imageExtent.height) };
302 	const std::vector<vk::VkRect2D>		scissors_right	{ vk::makeRect2D(imageExtent.width / 2, 0.0, imageExtent.width / 2, imageExtent.height) };
303 	auto graphicsPipelineLayout = vk::makePipelineLayout(vkd, device);
304 
305 	auto vtxshader  = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"));
306 	auto frgshader  = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"));
307 	auto tscshader1 = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("tesc"));
308 	auto tscshader2 = vk::createShaderModule(vkd, device,
309 		m_config.changeOutput ? m_context.getBinaryCollection().get("tesc2") : m_context.getBinaryCollection().get("tesc"));
310 	auto tseshader1 = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("tese"));
311 	auto tseshader2 = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("tese2"));
312 
313 	vk::VkPipelineRasterizationStateCreateInfo raster = {
314 		vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,		// VkStructureType								sType
315 		DE_NULL,															// const void*									pNext
316 		0u,																	// VkPipelineRasterizationStateCreateFlags		flags
317 		VK_FALSE,															// VkBool32										depthClampEnable
318 		VK_FALSE,															// VkBool32										rasterizerDiscardEnable
319 		vk::VK_POLYGON_MODE_FILL,											// VkPolygonMode								polygonMode
320 		m_config.cullMode,													// VkCullModeFlags								cullMode
321 		vk::VK_FRONT_FACE_COUNTER_CLOCKWISE,								// VkFrontFace									frontFace
322 		VK_FALSE,															// VkBool32										depthBiasEnable
323 		0.0f,																// float										depthBiasConstantFactor
324 		0.0f,																// float										depthBiasClamp
325 		0.0f,																// float										depthBiasSlopeFactor
326 		1.0f																// float										lineWidth
327 	};
328 
329 	vk::VkDynamicState dynamicStates = vk::VK_DYNAMIC_STATE_PATCH_CONTROL_POINTS_EXT;
330 		vk::VkPipelineDynamicStateCreateInfo dynamicInfo = {
331 		vk::VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
332 		nullptr,
333 		0,
334 		1,
335 		&dynamicStates
336 	};
337 
338 	pipeline1.setDefaultTopology(vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)
339 		.setDynamicState(&dynamicInfo)
340 		.setDefaultRasterizationState()
341 		.setDefaultMultisampleState()
342 		.setDefaultDepthStencilState()
343 		.setDefaultColorBlendState()
344 		.setupVertexInputState(&vertexInputState)
345 		.setupPreRasterizationShaderState(
346 			viewport_left,
347 			scissors_left,
348 			*graphicsPipelineLayout,
349 			*renderPass,
350 			0u,
351 			*vtxshader, &raster,
352 			*tscshader1, *tseshader1)
353 		.setupFragmentShaderState(*graphicsPipelineLayout, *renderPass, 0u,
354 			*frgshader, 0)
355 		.setupFragmentOutputState(*renderPass, 0u)
356 		.setMonolithicPipelineLayout(*graphicsPipelineLayout).buildPipeline();
357 
358 	pipeline2.setDefaultTopology(vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST)
359 		.setDynamicState(&dynamicInfo)
360 		.setDefaultRasterizationState()
361 		.setDefaultMultisampleState()
362 		.setDefaultDepthStencilState()
363 		.setDefaultColorBlendState()
364 		.setupVertexInputState(&vertexInputState)
365 		.setupPreRasterizationShaderState(
366 			viewport_right,
367 			scissors_right,
368 			*graphicsPipelineLayout,
369 			*renderPass,
370 			0u,
371 			*vtxshader, &raster,
372 			*tscshader2, *tseshader2)
373 		.setupFragmentShaderState(*graphicsPipelineLayout, *renderPass, 0u,
374 			*frgshader, 0)
375 		.setupFragmentOutputState(*renderPass, 0u)
376 		.setMonolithicPipelineLayout(*graphicsPipelineLayout).buildPipeline();
377 
378 	auto commandPool = createCommandPool(vkd, device, vk::VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, qIndex);
379 	auto commandBuffer = vk::allocateCommandBuffer(vkd, device, commandPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
380 
381 	const tcu::Vec4 clearColor(1.0f, 1.0f, 1.0f, 1.0f);
382 
383 	const vk::VkRect2D renderArea =
384 	{
385 		{ 0u, 0u },
386 		{ imageExtent.width, imageExtent.height }
387 	};
388 
389 	//render 2 triangles with each pipeline, covering the entire screen
390 	//depending on the test settings one of them might be culled
391 	vk::beginCommandBuffer(vkd, commandBuffer.get());
392 	vk::beginRenderPass(vkd, *commandBuffer, *renderPass, *framebuffer, renderArea, clearColor);
393 	vkd.cmdSetPatchControlPointsEXT(commandBuffer.get(), 3);
394 	vkd.cmdBindPipeline(commandBuffer.get(), vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline1.getPipeline());
395 	vkd.cmdDraw(commandBuffer.get(), 6, 1, 0, 0);
396 	vkd.cmdBindPipeline(commandBuffer.get(), vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline2.getPipeline());
397 	vkd.cmdDraw(commandBuffer.get(), 6, 1, 0, 0);
398 	vk::endRenderPass(vkd, commandBuffer.get());
399 	vk::copyImageToBuffer(vkd, commandBuffer.get(), colorAttachment.get()->get(), (*outBuffer).get(), imageSize);
400 	vk::endCommandBuffer(vkd, commandBuffer.get());
401 	vk::submitCommandsAndWait(vkd, device, m_context.getUniversalQueue(), commandBuffer.get());
402 
403 	invalidateAlloc(vkd, device, outBufferAlloc);
404 	tcu::ConstPixelBufferAccess outPixels(mappedFormat, imageDim, outBufferData);
405 
406 	auto expectedFirst = m_config.expectedFirst;
407 	auto expectedSecond = m_config.expectedSecond;
408 
409 	tcu::TextureLevel referenceLevel(mappedFormat, imageExtent.height, imageExtent.height);
410 	tcu::PixelBufferAccess reference = referenceLevel.getAccess();
411 	tcu::clear(getSubregion(reference, 0, 0, imageExtent.width / 2, imageExtent.height), expectedFirst);
412 	tcu::clear(getSubregion(reference, imageExtent.width / 2, 0, imageExtent.width / 2, imageExtent.height), expectedSecond);
413 
414 	if (!tcu::floatThresholdCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference, outPixels, tcu::Vec4(0.0), tcu::COMPARE_LOG_ON_ERROR))
415 		return tcu::TestStatus::fail("Color output does not match reference, image added to log");
416 
417 	return tcu::TestStatus::pass("Pass");
418 }
419 
createDynamicControlPointTests(tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType)420 tcu::TestCaseGroup* createDynamicControlPointTests (tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType)
421 {
422 	de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "dynamic_control_points", "Tests checking dynamic bind points and switching pipelines"));
423 
424 	group->addChild(new DynamicControlPointsTestCase(testCtx, "change_output", "test switching pipelines with dynamic control points while changing the number of tcs invocations",
425 		TestConfig {
426 			pipelineConstructionType,
427 			true,
428 			false,
429 			false,
430 			vk::VK_CULL_MODE_NONE,
431 			tcu::Vec4(1.0, 0.0, 1.0, 1.0),
432 			tcu::Vec4(1.0, 0.0, 1.0, 1.0),
433 	}));
434 
435 	group->addChild(new DynamicControlPointsTestCase(testCtx, "change_winding", "test switching pipelines with dynamic control points while switching winding",
436 		TestConfig {
437 			pipelineConstructionType,
438 			false,
439 			true,
440 			false,
441 			vk::VK_CULL_MODE_FRONT_BIT,
442 			tcu::Vec4(1.0, 1.0, 1.0, 1.0),
443 			tcu::Vec4(1.0, 0.0, 1.0, 1.0)
444 	}));
445 
446 	group->addChild(new DynamicControlPointsTestCase(testCtx, "change_output_winding", "test switching pipelines with dynamic control points while switching winding and number of tcs invocations",
447 		TestConfig {
448 			pipelineConstructionType,
449 			true,
450 			true,
451 			false,
452 			vk::VK_CULL_MODE_FRONT_BIT,
453 			tcu::Vec4(1.0, 1.0, 1.0, 1.0),
454 			tcu::Vec4(1.0, 0.0, 1.0, 1.0)
455 	}));
456 	return group.release();
457 }
458 
459 } // pipeline
460 } // vkt
461