• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2021 The Khronos Group Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*
20  * \file
21  * \brief Tests for Color Write Enable
22 *//*--------------------------------------------------------------------*/
23 
24 #include "vktPipelineColorWriteEnableTests.hpp"
25 #include "vktPipelineImageUtil.hpp"
26 #include "vktTestCase.hpp"
27 
28 #include "vkDefs.hpp"
29 #include "vkTypeUtil.hpp"
30 #include "vkQueryUtil.hpp"
31 #include "vkObjUtil.hpp"
32 #include "vkBufferWithMemory.hpp"
33 #include "vkImageWithMemory.hpp"
34 #include "vkBuilderUtil.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkImageUtil.hpp"
37 
38 #include "tcuVector.hpp"
39 #include "tcuMaybe.hpp"
40 #include "tcuTestLog.hpp"
41 #include "tcuVectorUtil.hpp"
42 
43 #include "deUniquePtr.hpp"
44 #include "deStringUtil.hpp"
45 
46 #include <vector>
47 #include <sstream>
48 #include <algorithm>
49 #include <utility>
50 #include <iterator>
51 #include <string>
52 #include <limits>
53 #include <memory>
54 #include <functional>
55 
56 namespace vkt
57 {
58 namespace pipeline
59 {
60 
61 namespace
62 {
63 
64 // Framebuffer size.
65 constexpr deUint32 kFramebufferWidth  = 64u;
66 constexpr deUint32 kFramebufferHeight = 64u;
67 
68 // Image formats.
69 constexpr	vk::VkFormat	kColorFormat	= vk::VK_FORMAT_R8G8B8A8_UNORM;
70 const		tcu::Vec4		kColorThreshold	(0.005f); // 1/255 < 0.005 < 2/255.
71 
72 constexpr	deUint32		kNumColorAttachments = 3u;
73 
74 const vk::VkFormat kDepthStencilFormats[] =
75 {
76 	vk::VK_FORMAT_D32_SFLOAT_S8_UINT,
77 	vk::VK_FORMAT_D24_UNORM_S8_UINT
78 };
79 
80 constexpr auto kCoordsSize = static_cast<deUint32>(2 * sizeof(float));
81 
82 using Bool32Vec = std::vector<vk::VkBool32>;
83 
84 // Generic, to be used with any state than can be set statically and, as an option, dynamically.
85 template<typename T>
86 struct StaticAndDynamicPair
87 {
88 	T				staticValue;
89 	tcu::Maybe<T>	dynamicValue;
90 
91 	// Helper constructor to set a static value and no dynamic value.
StaticAndDynamicPairvkt::pipeline::__anon042430540111::StaticAndDynamicPair92 	StaticAndDynamicPair (const T& value)
93 		: staticValue	(value)
94 		, dynamicValue	(tcu::Nothing)
95 	{
96 	}
97 
98 	// Helper constructor to set both.
StaticAndDynamicPairvkt::pipeline::__anon042430540111::StaticAndDynamicPair99 	StaticAndDynamicPair (const T& sVal, const T& dVal)
100 		: staticValue	(sVal)
101 		, dynamicValue	(tcu::just<T>(dVal))
102 	{
103 	}
104 
105 	// If the dynamic value is present, swap static and dynamic values.
swapValuesvkt::pipeline::__anon042430540111::StaticAndDynamicPair106 	void swapValues (void)
107 	{
108 		if (!dynamicValue)
109 			return;
110 		std::swap(staticValue, dynamicValue.get());
111 	}
112 };
113 
114 const tcu::Vec4	kDefaultTriangleColor (0.0f, 0.0f, 1.0f, 1.0f);	// Opaque blue.
115 const tcu::Vec4	kDefaultClearColor    (0.0f, 0.0f, 0.0f, 1.0f);	// Opaque black.
116 
117 struct MeshParams
118 {
119 	tcu::Vec4	color;
120 	float		depth;
121 	float		scaleX;
122 	float		scaleY;
123 	float		offsetX;
124 	float		offsetY;
125 
MeshParamsvkt::pipeline::__anon042430540111::MeshParams126 	MeshParams (const tcu::Vec4&	color_		= kDefaultTriangleColor,
127 				float				depth_		= 0.0f,
128 				float				scaleX_		= 1.0f,
129 				float				scaleY_		= 1.0f,
130 				float				offsetX_	= 0.0f,
131 				float				offsetY_	= 0.0f)
132 		: color		(color_)
133 		, depth		(depth_)
134 		, scaleX	(scaleX_)
135 		, scaleY	(scaleY_)
136 		, offsetX	(offsetX_)
137 		, offsetY	(offsetY_)
138 	{}
139 };
140 
141 enum class SequenceOrdering
142 {
143 	CMD_BUFFER_START	= 0,	// Set state at the start of the command buffer.
144 	BEFORE_DRAW			= 1,	// After binding dynamic pipeline and just before drawing.
145 	BETWEEN_PIPELINES	= 2,	// After a static state pipeline has been bound but before the dynamic state pipeline has been bound.
146 	AFTER_PIPELINES		= 3,	// After a static state pipeline and a second dynamic state pipeline have been bound.
147 	BEFORE_GOOD_STATIC	= 4,	// Before a static state pipeline with the correct values has been bound.
148 	TWO_DRAWS_DYNAMIC	= 5,	// Bind bad static pipeline and draw, followed by binding correct dynamic pipeline and drawing again.
149 	TWO_DRAWS_STATIC	= 6,	// Bind bad dynamic pipeline and draw, followed by binding correct static pipeline and drawing again.
150 };
151 
152 struct TestConfig
153 {
154 	// Main sequence ordering.
155 	SequenceOrdering				sequenceOrdering;
156 
157 	// Drawing parameters.
158 	MeshParams						meshParams;
159 
160 	// Clearing parameters for the framebuffer.
161 	tcu::Vec4						clearColorValue;
162 	float							clearDepthValue;
163 
164 	// Channels to enable
165 	tcu::BVec4						channelMask;
166 
167 	// Expected output in the attachments.
168 	std::vector<tcu::Vec4>			expectedColor;
169 	float							expectedDepth;
170 
171 	// Static and dynamic pipeline configuration.
172 	StaticAndDynamicPair<Bool32Vec>	colorWriteEnableConfig;
173 
174 	// Sane defaults.
TestConfigvkt::pipeline::__anon042430540111::TestConfig175 	TestConfig (SequenceOrdering ordering)
176 		: sequenceOrdering				(ordering)
177 		, clearColorValue				(kDefaultClearColor)
178 		, clearDepthValue				(1.0f)
179 		, expectedColor					(kNumColorAttachments, kDefaultTriangleColor)
180 		, expectedDepth					(1.0f)
181 		, colorWriteEnableConfig		(Bool32Vec(1u, VK_TRUE))
182 		, m_swappedValues				(false)
183 	{
184 	}
185 
186 	// Returns true if we should use the static and dynamic values exchanged.
187 	// This makes the static part of the pipeline have the actual expected values.
isReversedvkt::pipeline::__anon042430540111::TestConfig188 	bool isReversed () const
189 	{
190 		return (sequenceOrdering == SequenceOrdering::BEFORE_GOOD_STATIC ||
191 				sequenceOrdering == SequenceOrdering::TWO_DRAWS_STATIC);
192 	}
193 
194 	// Swaps static and dynamic configuration values.
swapValuesvkt::pipeline::__anon042430540111::TestConfig195 	void swapValues ()
196 	{
197 		colorWriteEnableConfig.swapValues();
198 		m_swappedValues = !m_swappedValues;
199 	}
200 
201 	// Returns the number of iterations when recording commands.
numIterationsvkt::pipeline::__anon042430540111::TestConfig202 	deUint32 numIterations () const
203 	{
204 		deUint32 iterations = 0u;
205 
206 		switch (sequenceOrdering)
207 		{
208 		case SequenceOrdering::TWO_DRAWS_DYNAMIC:
209 		case SequenceOrdering::TWO_DRAWS_STATIC:
210 			iterations = 2u;
211 			break;
212 		default:
213 			iterations = 1u;
214 			break;
215 		}
216 
217 		return iterations;
218 	}
219 
220 private:
221 	// Color Write Enable cases as created by createColorWriteEnableTests() are based on the assumption that, when a state
222 	// has a static and a dynamic value configured at the same time, the static value is wrong and the dynamic value will give
223 	// expected results. That's appropriate for most test variants, but in some others we want to reverse the situation: a dynamic
224 	// pipeline with wrong values and a static one with good values.
225 	//
226 	// Instead of modifying how tests are created, we use isReversed() and swapValues() above, allowing us to swap static and
227 	// dynamic values and to know if we should do it for a given test case. However, we need to know were the good value is at any
228 	// given point in time in order to correctly answer some questions while running the test. m_swappedValues tracks that state.
229 	bool m_swappedValues;
230 };
231 
232 struct PushConstants
233 {
234 	tcu::Vec4	triangleColor;
235 	float		meshDepth;
236 	float		scaleX;
237 	float		scaleY;
238 	float		offsetX;
239 	float		offsetY;
240 };
241 
242 class ColorWriteEnableTest : public vkt::TestCase
243 {
244 public:
245 							ColorWriteEnableTest		(tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestConfig& testConfig);
~ColorWriteEnableTest(void)246 	virtual					~ColorWriteEnableTest		(void) {}
247 
248 	virtual void			checkSupport					(Context& context) const;
249 	virtual void			initPrograms					(vk::SourceCollections& programCollection) const;
250 	virtual TestInstance*	createInstance					(Context& context) const;
251 
252 private:
253 	TestConfig				m_testConfig;
254 };
255 
256 class ColorWriteEnableInstance : public vkt::TestInstance
257 {
258 public:
259 								ColorWriteEnableInstance	(Context& context, const TestConfig& testConfig);
~ColorWriteEnableInstance(void)260 	virtual						~ColorWriteEnableInstance	(void) {}
261 
262 	virtual tcu::TestStatus		iterate							(void);
263 
264 private:
265 	TestConfig					m_testConfig;
266 };
267 
ColorWriteEnableTest(tcu::TestContext & testCtx,const std::string & name,const std::string & description,const TestConfig & testConfig)268 ColorWriteEnableTest::ColorWriteEnableTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestConfig& testConfig)
269 	: vkt::TestCase	(testCtx, name, description)
270 	, m_testConfig	(testConfig)
271 {
272 }
273 
checkSupport(Context & context) const274 void ColorWriteEnableTest::checkSupport (Context& context) const
275 {
276 	const auto&	vki				= context.getInstanceInterface();
277 	const auto	physicalDevice	= context.getPhysicalDevice();
278 
279 	// This is always required.
280 	context.requireDeviceFunctionality("VK_EXT_color_write_enable");
281 
282 	// Check color image format support (depth/stencil will be chosen at runtime).
283 	const vk::VkFormatFeatureFlags	kColorFeatures	= (vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | vk::VK_FORMAT_FEATURE_TRANSFER_SRC_BIT);
284 	const auto						colorProperties	= vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kColorFormat);
285 
286 	if ((colorProperties.optimalTilingFeatures & kColorFeatures) != kColorFeatures)
287 		TCU_THROW(NotSupportedError, "Required color image features not supported");
288 }
289 
initPrograms(vk::SourceCollections & programCollection) const290 void ColorWriteEnableTest::initPrograms (vk::SourceCollections& programCollection) const
291 {
292 	std::ostringstream pushSource;
293 	pushSource
294 		<< "layout(push_constant, std430) uniform PushConstantsBlock {\n"
295 		<< "    vec4  triangleColor;\n"
296 		<< "    float depthValue;\n"
297 		<< "    float scaleX;\n"
298 		<< "    float scaleY;\n"
299 		<< "    float offsetX;\n"
300 		<< "    float offsetY;\n"
301 		<< "} pushConstants;\n"
302 		;
303 
304 	std::ostringstream vertSource;
305 	vertSource
306 		<< "#version 450\n"
307 		<< pushSource.str()
308 		<< "layout(location=0) in vec2 position;\n"
309 		<< "out gl_PerVertex\n"
310 		<< "{\n"
311 		<< "    vec4 gl_Position;\n"
312 		<< "};\n"
313 		<< "void main() {\n"
314 		<< "    vec2 vertexCoords = position;\n"
315 		<< "    gl_Position = vec4(vertexCoords.x * pushConstants.scaleX + pushConstants.offsetX, vertexCoords.y * pushConstants.scaleY + pushConstants.offsetY, pushConstants.depthValue, 1.0);\n"
316 		<< "}\n"
317 		;
318 
319 	std::ostringstream fragOutputs;
320 	std::ostringstream colorWrite;
321 	for (deUint32 i = 0u; i < kNumColorAttachments; ++i)
322 	{
323 		fragOutputs << "layout(location=" << i << ") out vec4 color" << i << ";\n";
324 		colorWrite << "    color" << i << " = pushConstants.triangleColor * " << powf(0.5f, static_cast<float>(i)) << ";\n";
325 	}
326 
327 	std::ostringstream fragSource;
328 	fragSource
329 		<< "#version 450\n"
330 		<< pushSource.str()
331 		<< fragOutputs.str()
332 		<< "void main() {\n"
333 		<< colorWrite.str()
334 		<< "}\n"
335 		;
336 
337 	programCollection.glslSources.add("vert") << glu::VertexSource(vertSource.str());
338 	programCollection.glslSources.add("frag") << glu::FragmentSource(fragSource.str());
339 }
340 
createInstance(Context & context) const341 TestInstance* ColorWriteEnableTest::createInstance (Context& context) const
342 {
343 	return new ColorWriteEnableInstance(context, m_testConfig);
344 }
345 
ColorWriteEnableInstance(Context & context,const TestConfig & testConfig)346 ColorWriteEnableInstance::ColorWriteEnableInstance(Context& context, const TestConfig& testConfig)
347 	: vkt::TestInstance	(context)
348 	, m_testConfig		(testConfig)
349 {
350 }
351 
logErrors(tcu::TestLog & log,const std::string & setName,const std::string & setDesc,const tcu::ConstPixelBufferAccess & result,const tcu::ConstPixelBufferAccess & errorMask)352 void logErrors(tcu::TestLog& log, const std::string& setName, const std::string& setDesc, const tcu::ConstPixelBufferAccess& result, const tcu::ConstPixelBufferAccess& errorMask)
353 {
354 	log << tcu::TestLog::ImageSet(setName, setDesc)
355 		<< tcu::TestLog::Image(setName + "Result", "Result image", result)
356 		<< tcu::TestLog::Image(setName + "ErrorMask", "Error mask with errors marked in red", errorMask)
357 		<< tcu::TestLog::EndImageSet;
358 }
359 
360 // Sets values for dynamic states if needed according to the test configuration.
setDynamicStates(const TestConfig & testConfig,const vk::DeviceInterface & vkd,vk::VkCommandBuffer cmdBuffer)361 void setDynamicStates(const TestConfig& testConfig, const vk::DeviceInterface& vkd, vk::VkCommandBuffer cmdBuffer)
362 {
363 	if (testConfig.colorWriteEnableConfig.dynamicValue)
364 	{
365 		const auto& colorWriteEnables = testConfig.colorWriteEnableConfig.dynamicValue.get();
366 		vkd.cmdSetColorWriteEnableEXT(cmdBuffer, static_cast<deUint32>(colorWriteEnables.size()), colorWriteEnables.data());
367 	}
368 }
369 
iterate(void)370 tcu::TestStatus ColorWriteEnableInstance::iterate (void)
371 {
372 	using ImageWithMemoryVec	= std::vector<std::unique_ptr<vk::ImageWithMemory>>;
373 	using ImageViewVec			= std::vector<vk::Move<vk::VkImageView>>;
374 	using FramebufferVec		= std::vector<vk::Move<vk::VkFramebuffer>>;
375 
376 	const auto&	vki					= m_context.getInstanceInterface();
377 	const auto&	vkd					= m_context.getDeviceInterface();
378 	const auto	physicalDevice		= m_context.getPhysicalDevice();
379 	const auto	device				= m_context.getDevice();
380 	auto&		allocator			= m_context.getDefaultAllocator();
381 	const auto	queue				= m_context.getUniversalQueue();
382 	const auto	queueIndex			= m_context.getUniversalQueueFamilyIndex();
383 	auto&		log					= m_context.getTestContext().getLog();
384 
385 	const auto	kReversed			= m_testConfig.isReversed();
386 	const auto	kNumIterations		= m_testConfig.numIterations();
387 	const auto	kSequenceOrdering	= m_testConfig.sequenceOrdering;
388 
389 	const auto						kFramebufferExtent	= vk::makeExtent3D(kFramebufferWidth, kFramebufferHeight, 1u);
390 	const vk::VkImageUsageFlags		kColorUsage			= (vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
391 	const vk::VkImageUsageFlags		kDSUsage			= (vk::VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
392 	const vk::VkFormatFeatureFlags	kDSFeatures			= (vk::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | vk::VK_FORMAT_FEATURE_TRANSFER_SRC_BIT);
393 
394 	// Choose depth/stencil format.
395 	vk::VkFormat dsFormat = vk::VK_FORMAT_UNDEFINED;
396 
397 	for (int formatIdx = 0; formatIdx < DE_LENGTH_OF_ARRAY(kDepthStencilFormats); ++formatIdx)
398 	{
399 		const auto dsProperties = vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kDepthStencilFormats[formatIdx]);
400 		if ((dsProperties.optimalTilingFeatures & kDSFeatures) == kDSFeatures)
401 		{
402 			dsFormat = kDepthStencilFormats[formatIdx];
403 			break;
404 		}
405 	}
406 
407 	// Note: Not Supported insted of Fail because the transfer feature is not mandatory.
408 	if (dsFormat == vk::VK_FORMAT_UNDEFINED)
409 		TCU_THROW(NotSupportedError, "Required depth/stencil image features not supported");
410 	log << tcu::TestLog::Message << "Chosen depth/stencil format: " << dsFormat << tcu::TestLog::EndMessage;
411 
412 	// Swap static and dynamic values in the test configuration so the static pipeline ends up with the expected values for cases
413 	// where we will bind the static pipeline last before drawing.
414 	if (kReversed)
415 		m_testConfig.swapValues();
416 
417 	// Create color and depth/stencil images.
418 	ImageWithMemoryVec colorImages;
419 	ImageWithMemoryVec dsImages;
420 
421 	const vk::VkImageCreateInfo colorImageInfo =
422 	{
423 		vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
424 		nullptr,									//	const void*				pNext;
425 		0u,											//	VkImageCreateFlags		flags;
426 		vk::VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
427 		kColorFormat,								//	VkFormat				format;
428 		kFramebufferExtent,							//	VkExtent3D				extent;
429 		1u,											//	deUint32				mipLevels;
430 		1u,											//	deUint32				arrayLayers;
431 		vk::VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
432 		vk::VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
433 		kColorUsage,								//	VkImageUsageFlags		usage;
434 		vk::VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
435 		1u,											//	deUint32				queueFamilyIndexCount;
436 		&queueIndex,								//	const deUint32*			pQueueFamilyIndices;
437 		vk::VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
438 	};
439 	for (deUint32 i = 0u; i < kNumIterations * kNumColorAttachments; ++i)
440 		colorImages.emplace_back(new vk::ImageWithMemory(vkd, device, allocator, colorImageInfo, vk::MemoryRequirement::Any));
441 
442 	const vk::VkImageCreateInfo dsImageInfo =
443 	{
444 		vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
445 		nullptr,									//	const void*				pNext;
446 		0u,											//	VkImageCreateFlags		flags;
447 		vk::VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
448 		dsFormat,									//	VkFormat				format;
449 		kFramebufferExtent,							//	VkExtent3D				extent;
450 		1u,											//	deUint32				mipLevels;
451 		1u,											//	deUint32				arrayLayers;
452 		vk::VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
453 		vk::VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
454 		kDSUsage,									//	VkImageUsageFlags		usage;
455 		vk::VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
456 		1u,											//	deUint32				queueFamilyIndexCount;
457 		&queueIndex,								//	const deUint32*			pQueueFamilyIndices;
458 		vk::VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
459 	};
460 	for (deUint32 i = 0u; i < kNumIterations; ++i)
461 		dsImages.emplace_back(new vk::ImageWithMemory(vkd, device, allocator, dsImageInfo, vk::MemoryRequirement::Any));
462 
463 	const auto colorSubresourceRange	= vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
464 	const auto dsSubresourceRange		= vk::makeImageSubresourceRange((vk::VK_IMAGE_ASPECT_DEPTH_BIT | vk::VK_IMAGE_ASPECT_STENCIL_BIT), 0u, 1u, 0u, 1u);
465 
466 	ImageViewVec colorImageViews;
467 	ImageViewVec dsImageViews;
468 
469 	for (const auto& img : colorImages)
470 		colorImageViews.emplace_back(vk::makeImageView(vkd, device, img->get(), vk::VK_IMAGE_VIEW_TYPE_2D, kColorFormat, colorSubresourceRange));
471 
472 	for (const auto& img : dsImages)
473 		dsImageViews.emplace_back(vk::makeImageView(vkd, device, img->get(), vk::VK_IMAGE_VIEW_TYPE_2D, dsFormat, dsSubresourceRange));
474 
475 	// Vertex buffer.
476 	// Full-screen triangle fan with 6 vertices.
477 	//
478 	// 4        3        2
479 	//  +-------+-------+
480 	//  |X      X      X|
481 	//  | X     X     X |
482 	//  |  X    X    X  |
483 	//  |   X   X   X   |
484 	//  |    X  X  X    |
485 	//  |     X X X     |
486 	//  |      XXX      |
487 	//  +-------+-------+
488 	// 5        0        1
489 	std::vector<float> vertices = {
490 		 0.0f,  1.0f,
491 		 1.0f,  1.0f,
492 		 1.0f, -1.0f,
493 		 0.0f, -1.0f,
494 		-1.0f, -1.0f,
495 		-1.0f,  1.0f,
496 	};
497 
498 	const auto vertDataSize				= vertices.size() * sizeof(float);
499 	const auto vertBufferInfo			= vk::makeBufferCreateInfo(static_cast<vk::VkDeviceSize>(vertDataSize), vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
500 	vk::BufferWithMemory vertBuffer		(vkd, device, allocator, vertBufferInfo, vk::MemoryRequirement::HostVisible);
501 	auto& alloc							= vertBuffer.getAllocation();
502 
503 	deMemcpy(reinterpret_cast<char*>(alloc.getHostPtr()), vertices.data(), vertDataSize);
504 	vk::flushAlloc(vkd, device, alloc);
505 
506 	// Descriptor set layout.
507 	vk::DescriptorSetLayoutBuilder layoutBuilder;
508 	const auto descriptorSetLayout = layoutBuilder.build(vkd, device);
509 
510 	// Pipeline layout.
511 	vk::VkShaderStageFlags pushConstantStageFlags = (vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT);
512 
513 	const vk::VkPushConstantRange pushConstantRange =
514 	{
515 		pushConstantStageFlags,							//	VkShaderStageFlags	stageFlags;
516 		0u,												//	deUint32			offset;
517 		static_cast<deUint32>(sizeof(PushConstants)),	//	deUint32			size;
518 	};
519 
520 	const vk::VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
521 	{
522 		vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,	//	VkStructureType					sType;
523 		nullptr,											//	const void*						pNext;
524 		0u,													//	VkPipelineLayoutCreateFlags		flags;
525 		1u,													//	deUint32						setLayoutCount;
526 		&descriptorSetLayout.get(),							//	const VkDescriptorSetLayout*	pSetLayouts;
527 		1u,													//	deUint32						pushConstantRangeCount;
528 		&pushConstantRange,									//	const VkPushConstantRange*		pPushConstantRanges;
529 	};
530 	const auto pipelineLayout = vk::createPipelineLayout(vkd, device, &pipelineLayoutCreateInfo);
531 
532 	// Render pass with single subpass.
533 	std::vector<vk::VkAttachmentReference> colorAttachmentReference;
534 	for (deUint32 i = 0u; i < kNumColorAttachments; ++i)
535 	{
536 		colorAttachmentReference.push_back(vk::VkAttachmentReference
537 			{
538 				i,												//	deUint32		attachment;
539 				vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL	//	VkImageLayout	layout;
540 			}
541 		);
542 	}
543 
544 	const vk::VkAttachmentReference dsAttachmentReference =
545 	{
546 		kNumColorAttachments,									//	deUint32		attachment;
547 		vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,	//	VkImageLayout	layout;
548 	};
549 
550 	const vk::VkSubpassDescription subpassDescription =
551 	{
552 		0u,										//	VkSubpassDescriptionFlags		flags;
553 		vk::VK_PIPELINE_BIND_POINT_GRAPHICS,	//	VkPipelineBindPoint				pipelineBindPoint;
554 		0u,										//	deUint32						inputAttachmentCount;
555 		nullptr,								//	const VkAttachmentReference*	pInputAttachments;
556 		kNumColorAttachments,					//	deUint32						colorAttachmentCount;
557 		colorAttachmentReference.data(),		//	const VkAttachmentReference*	pColorAttachments;
558 		nullptr,								//	const VkAttachmentReference*	pResolveAttachments;
559 		&dsAttachmentReference,					//	const VkAttachmentReference*	pDepthStencilAttachment;
560 		0u,										//	deUint32						preserveAttachmentCount;
561 		nullptr,								//	const deUint32*					pPreserveAttachments;
562 	};
563 
564 	std::vector<vk::VkAttachmentDescription> attachmentDescriptions(
565 		kNumColorAttachments,
566 		vk::VkAttachmentDescription
567 		{
568 			0u,												//	VkAttachmentDescriptionFlags	flags;
569 			kColorFormat,									//	VkFormat						format;
570 			vk::VK_SAMPLE_COUNT_1_BIT,						//	VkSampleCountFlagBits			samples;
571 			vk::VK_ATTACHMENT_LOAD_OP_CLEAR,				//	VkAttachmentLoadOp				loadOp;
572 			vk::VK_ATTACHMENT_STORE_OP_STORE,				//	VkAttachmentStoreOp				storeOp;
573 			vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,			//	VkAttachmentLoadOp				stencilLoadOp;
574 			vk::VK_ATTACHMENT_STORE_OP_DONT_CARE,			//	VkAttachmentStoreOp				stencilStoreOp;
575 			vk::VK_IMAGE_LAYOUT_UNDEFINED,					//	VkImageLayout					initialLayout;
576 			vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout					finalLayout;
577 		}
578 	);
579 
580 	attachmentDescriptions.push_back(vk::VkAttachmentDescription
581 	{
582 		0u,														//	VkAttachmentDescriptionFlags	flags;
583 		dsFormat,												//	VkFormat						format;
584 		vk::VK_SAMPLE_COUNT_1_BIT,								//	VkSampleCountFlagBits			samples;
585 		vk::VK_ATTACHMENT_LOAD_OP_CLEAR,						//	VkAttachmentLoadOp				loadOp;
586 		vk::VK_ATTACHMENT_STORE_OP_STORE,						//	VkAttachmentStoreOp				storeOp;
587 		vk::VK_ATTACHMENT_LOAD_OP_CLEAR,						//	VkAttachmentLoadOp				stencilLoadOp;
588 		vk::VK_ATTACHMENT_STORE_OP_STORE,						//	VkAttachmentStoreOp				stencilStoreOp;
589 		vk::VK_IMAGE_LAYOUT_UNDEFINED,							//	VkImageLayout					initialLayout;
590 		vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,	//	VkImageLayout					finalLayout;
591 	});
592 
593 	const vk::VkRenderPassCreateInfo renderPassCreateInfo =
594 	{
595 		vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,			//	VkStructureType					sType;
596 		nullptr,												//	const void*						pNext;
597 		0u,														//	VkRenderPassCreateFlags			flags;
598 		static_cast<deUint32>(attachmentDescriptions.size()),	//	deUint32						attachmentCount;
599 		attachmentDescriptions.data(),							//	const VkAttachmentDescription*	pAttachments;
600 		1u,														//	deUint32						subpassCount;
601 		&subpassDescription,									//	const VkSubpassDescription*		pSubpasses;
602 		0u,														//	deUint32						dependencyCount;
603 		nullptr,												//	const VkSubpassDependency*		pDependencies;
604 	};
605 	const auto renderPass = vk::createRenderPass(vkd, device, &renderPassCreateInfo);
606 
607 	// Framebuffers.
608 	FramebufferVec framebuffers;
609 
610 	DE_ASSERT(colorImageViews.size() == dsImageViews.size() * kNumColorAttachments);
611 	for (size_t imgIdx = 0; imgIdx < dsImageViews.size(); ++imgIdx)
612 	{
613 		std::vector<vk::VkImageView> attachments;
614 		for (deUint32 i = 0u; i < kNumColorAttachments; ++i)
615 			attachments.push_back(colorImageViews[imgIdx * kNumColorAttachments + i].get());
616 
617 		attachments.push_back(dsImageViews[imgIdx].get());
618 
619 		const vk::VkFramebufferCreateInfo framebufferCreateInfo =
620 		{
621 			vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,	//	VkStructureType				sType;
622 			nullptr,										//	const void*					pNext;
623 			0u,												//	VkFramebufferCreateFlags	flags;
624 			renderPass.get(),								//	VkRenderPass				renderPass;
625 			static_cast<deUint32>(attachments.size()),		//	deUint32					attachmentCount;
626 			attachments.data(),								//	const VkImageView*			pAttachments;
627 			kFramebufferWidth,								//	deUint32					width;
628 			kFramebufferHeight,								//	deUint32					height;
629 			1u,												//	deUint32					layers;
630 		};
631 
632 		framebuffers.emplace_back(vk::createFramebuffer(vkd, device, &framebufferCreateInfo));
633 	}
634 
635 	// Shader modules.
636 	const auto	vertModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
637 	const auto	fragModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"), 0u);
638 
639 	// Shader stages.
640 	std::vector<vk::VkPipelineShaderStageCreateInfo> shaderStages;
641 
642 	vk::VkPipelineShaderStageCreateInfo shaderStageCreateInfo =
643 	{
644 		vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	//	VkStructureType						sType;
645 		nullptr,													//	const void*							pNext;
646 		0u,															//	VkPipelineShaderStageCreateFlags	flags;
647 		vk::VK_SHADER_STAGE_VERTEX_BIT,								//	VkShaderStageFlagBits				stage;
648 		vertModule.get(),											//	VkShaderModule						module;
649 		"main",														//	const char*							pName;
650 		nullptr,													//	const VkSpecializationInfo*			pSpecializationInfo;
651 	};
652 
653 	shaderStages.push_back(shaderStageCreateInfo);
654 	shaderStageCreateInfo.stage = vk::VK_SHADER_STAGE_FRAGMENT_BIT;
655 	shaderStageCreateInfo.module = fragModule.get();
656 	shaderStages.push_back(shaderStageCreateInfo);
657 
658 	// Input state.
659 	const auto vertexBinding = vk::makeVertexInputBindingDescription(0u, kCoordsSize, vk::VK_VERTEX_INPUT_RATE_VERTEX);
660 	const std::vector<vk::VkVertexInputAttributeDescription> vertexAttributes = {
661 		vk::makeVertexInputAttributeDescription(0u, 0u, vk::VK_FORMAT_R32G32_SFLOAT, 0u)
662 	};
663 
664 	const vk::VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo =
665 	{
666 		vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,	//	VkStructureType								sType;
667 		nullptr,														//	const void*									pNext;
668 		0u,																//	VkPipelineVertexInputStateCreateFlags		flags;
669 		1u,																//	deUint32									vertexBindingDescriptionCount;
670 		&vertexBinding,													//	const VkVertexInputBindingDescription*		pVertexBindingDescriptions;
671 		static_cast<deUint32>(vertexAttributes.size()),					//	deUint32									vertexAttributeDescriptionCount;
672 		vertexAttributes.data(),										//	const VkVertexInputAttributeDescription*	pVertexAttributeDescriptions;
673 	};
674 
675 	// Input assembly.
676 	const vk::VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo =
677 	{
678 		vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,	//	VkStructureType							sType;
679 		nullptr,															//	const void*								pNext;
680 		0u,																	//	VkPipelineInputAssemblyStateCreateFlags	flags;
681 		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,								//	VkPrimitiveTopology						topology;
682 		VK_FALSE,															//	VkBool32								primitiveRestartEnable;
683 	};
684 
685 	// Viewport state.
686 	const vk::VkViewport viewport	(vk::makeViewport(kFramebufferWidth, kFramebufferHeight));
687 	const vk::VkRect2D scissor		(vk::makeRect2D(kFramebufferWidth, kFramebufferHeight));
688 
689 	// For the static pipeline.
690 	const vk::VkPipelineViewportStateCreateInfo viewportStateCreateInfo =
691 	{
692 		vk::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,					//	VkStructureType						sType;
693 		nullptr,																	//	const void*							pNext;
694 		0u,																			//	VkPipelineViewportStateCreateFlags	flags;
695 		1u,																			//	deUint32							viewportCount;
696 		&viewport,																	//	const VkViewport*					pViewports;
697 		1u,																			//	deUint32							scissorCount;
698 		&scissor,																	//	const VkRect2D*						pScissors;
699 	};
700 
701 	// Rasterization state.
702 	const vk::VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo =
703 	{
704 		vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,	//	VkStructureType							sType;
705 		nullptr,														//	const void*								pNext;
706 		0u,																//	VkPipelineRasterizationStateCreateFlags	flags;
707 		VK_FALSE,														//	VkBool32								depthClampEnable;
708 		VK_FALSE,														//	VkBool32								rasterizerDiscardEnable;
709 		vk::VK_POLYGON_MODE_FILL,										//	VkPolygonMode							polygonMode;
710 		vk::VK_CULL_MODE_NONE,											//	VkCullModeFlags							cullMode;
711 		vk::VK_FRONT_FACE_COUNTER_CLOCKWISE,							//	VkFrontFace								frontFace;
712 		VK_FALSE,														//	VkBool32								depthBiasEnable;
713 		0.0f,															//	float									depthBiasConstantFactor;
714 		0.0f,															//	float									depthBiasClamp;
715 		0.0f,															//	float									depthBiasSlopeFactor;
716 		1.0f,															//	float									lineWidth;
717 	};
718 
719 	// Multisample state.
720 	const vk::VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo =
721 	{
722 		vk::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,	//	VkStructureType							sType;
723 		nullptr,														//	const void*								pNext;
724 		0u,																//	VkPipelineMultisampleStateCreateFlags	flags;
725 		vk::VK_SAMPLE_COUNT_1_BIT,										//	VkSampleCountFlagBits					rasterizationSamples;
726 		VK_FALSE,														//	VkBool32								sampleShadingEnable;
727 		0.0f,															//	float									minSampleShading;
728 		nullptr,														//	const VkSampleMask*						pSampleMask;
729 		VK_FALSE,														//	VkBool32								alphaToCoverageEnable;
730 		VK_FALSE,														//	VkBool32								alphaToOneEnable;
731 	};
732 
733 	// Depth/stencil state.
734 	const vk::VkStencilOpState stencil =
735 	{
736 		vk::VK_STENCIL_OP_KEEP,		// VkStencilOp	failOp;
737 		vk::VK_STENCIL_OP_KEEP,		// VkStencilOp	passOp;
738 		vk::VK_STENCIL_OP_KEEP,		// VkStencilOp	depthFailOp;
739 		vk::VK_COMPARE_OP_ALWAYS,	// VkCompareOp	compareOp;
740 		0xFFu,						// deUint32		compareMask;
741 		0xFFu,						// deUint32		writeMask;
742 		0u,							// deUint32		reference;
743 	};
744 
745 	const vk::VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo =
746 	{
747 		vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,		//	VkStructureType							sType;
748 		nullptr,															//	const void*								pNext;
749 		0u,																	//	VkPipelineDepthStencilStateCreateFlags	flags;
750 		VK_TRUE,															//	VkBool32								depthTestEnable;
751 		VK_TRUE,															//	VkBool32								depthWriteEnable;
752 		vk::VK_COMPARE_OP_LESS,												//	VkCompareOp								depthCompareOp;
753 		VK_FALSE,															//	VkBool32								depthBoundsTestEnable;
754 		VK_FALSE,															//	VkBool32								stencilTestEnable;
755 		stencil,															//	VkStencilOpState						front;
756 		stencil,															//	VkStencilOpState						back;
757 		0.0f,																//	float									minDepthBounds;
758 		1.0f,																//	float									maxDepthBounds;
759 	};
760 
761 	// Dynamic state. Here we will set all states which have a dynamic value.
762 	std::vector<vk::VkDynamicState> dynamicStates;
763 
764 	if (m_testConfig.colorWriteEnableConfig.dynamicValue)
765 		dynamicStates.push_back(vk::VK_DYNAMIC_STATE_COLOR_WRITE_ENABLE_EXT);
766 
767 	const vk::VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo =
768 	{
769 		vk::VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,	//	VkStructureType						sType;
770 		nullptr,													//	const void*							pNext;
771 		0u,															//	VkPipelineDynamicStateCreateFlags	flags;
772 		static_cast<deUint32>(dynamicStates.size()),				//	deUint32							dynamicStateCount;
773 		dynamicStates.data(),										//	const VkDynamicState*				pDynamicStates;
774 	};
775 
776 	std::vector<vk::VkPipelineColorBlendAttachmentState> colorBlendAttachmentState(
777 		kNumColorAttachments,
778 		vk::VkPipelineColorBlendAttachmentState
779 		{
780 			VK_FALSE,								// VkBool32                 blendEnable
781 			vk::VK_BLEND_FACTOR_ZERO,				// VkBlendFactor            srcColorBlendFactor
782 			vk::VK_BLEND_FACTOR_ZERO,				// VkBlendFactor            dstColorBlendFactor
783 			vk::VK_BLEND_OP_ADD,					// VkBlendOp                colorBlendOp
784 			vk::VK_BLEND_FACTOR_ZERO,				// VkBlendFactor            srcAlphaBlendFactor
785 			vk::VK_BLEND_FACTOR_ZERO,				// VkBlendFactor            dstAlphaBlendFactor
786 			vk::VK_BLEND_OP_ADD,					// VkBlendOp                alphaBlendOp
787 			static_cast<vk::VkColorComponentFlags>(	// VkColorComponentFlags    colorWriteMask
788 				(m_testConfig.channelMask.x() ? vk::VK_COLOR_COMPONENT_R_BIT : 0)
789 				| (m_testConfig.channelMask.y() ? vk::VK_COLOR_COMPONENT_G_BIT : 0)
790 				| (m_testConfig.channelMask.z() ? vk::VK_COLOR_COMPONENT_B_BIT : 0)
791 				| (m_testConfig.channelMask.w() ? vk::VK_COLOR_COMPONENT_A_BIT : 0)
792 			)
793 		}
794 	);
795 
796 	const vk::VkPipelineColorWriteCreateInfoEXT colorWriteCreateInfo =
797 	{
798 		vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_WRITE_CREATE_INFO_EXT,		// VkStructureType	sType;
799 		nullptr,														// const void*		pNext;
800 		kNumColorAttachments,											// deUint32			attachmentCount;
801 		m_testConfig.colorWriteEnableConfig.staticValue.data()			// const VkBool32*	pColorWriteEnables;
802 	};
803 
804 	const vk::VkPipelineColorBlendStateCreateInfo colorBlendStateCreateInfo =
805 	{
806 		vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,	// VkStructureType                               sType
807 		&colorWriteCreateInfo,											// const void*                                   pNext
808 		0u,																// VkPipelineColorBlendStateCreateFlags          flags
809 		VK_FALSE,														// VkBool32                                      logicOpEnable
810 		vk::VK_LOGIC_OP_CLEAR,											// VkLogicOp                                     logicOp
811 		kNumColorAttachments,											// deUint32                                      attachmentCount
812 		colorBlendAttachmentState.data(),								// const VkPipelineColorBlendAttachmentState*    pAttachments
813 		{ 0.0f, 0.0f, 0.0f, 0.0f }										// float                                         blendConstants[4]
814 	};
815 
816 	const vk::VkGraphicsPipelineCreateInfo graphicsPipelineCreateInfoTemplate =
817 	{
818 		vk::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,	//	VkStructureType									sType;
819 		nullptr,												//	const void*										pNext;
820 		0u,														//	VkPipelineCreateFlags							flags;
821 		static_cast<deUint32>(shaderStages.size()),				//	deUint32										stageCount;
822 		shaderStages.data(),									//	const VkPipelineShaderStageCreateInfo*			pStages;
823 		&vertexInputStateCreateInfo,							//	const VkPipelineVertexInputStateCreateInfo*		pVertexInputState;
824 		&inputAssemblyStateCreateInfo,							//	const VkPipelineInputAssemblyStateCreateInfo*	pInputAssemblyState;
825 		nullptr,												//	const VkPipelineTessellationStateCreateInfo*	pTessellationState;
826 		nullptr,												//	const VkPipelineViewportStateCreateInfo*		pViewportState;
827 		&rasterizationStateCreateInfo,							//	const VkPipelineRasterizationStateCreateInfo*	pRasterizationState;
828 		&multisampleStateCreateInfo,							//	const VkPipelineMultisampleStateCreateInfo*		pMultisampleState;
829 		&depthStencilStateCreateInfo,							//	const VkPipelineDepthStencilStateCreateInfo*	pDepthStencilState;
830 		&colorBlendStateCreateInfo,								//	const VkPipelineColorBlendStateCreateInfo*		pColorBlendState;
831 		nullptr,												//	const VkPipelineDynamicStateCreateInfo*			pDynamicState;
832 		pipelineLayout.get(),									//	VkPipelineLayout								layout;
833 		renderPass.get(),										//	VkRenderPass									renderPass;
834 		0u,														//	deUint32										subpass;
835 		DE_NULL,												//	VkPipeline										basePipelineHandle;
836 		0,														//	deInt32											basePipelineIndex;
837 	};
838 
839 	vk::Move<vk::VkPipeline>	staticPipeline;
840 	const bool					bindStaticFirst		= (kSequenceOrdering == SequenceOrdering::BETWEEN_PIPELINES	||
841 													   kSequenceOrdering == SequenceOrdering::AFTER_PIPELINES	||
842 													   kSequenceOrdering == SequenceOrdering::TWO_DRAWS_DYNAMIC);
843 	const bool					useStaticPipeline	= (bindStaticFirst || kReversed);
844 
845 	// Create static pipeline when needed.
846 	if (useStaticPipeline)
847 	{
848 		auto staticPipelineCreateInfo			= graphicsPipelineCreateInfoTemplate;
849 		staticPipelineCreateInfo.pViewportState	= &viewportStateCreateInfo;
850 		staticPipeline							= vk::createGraphicsPipeline(vkd, device, DE_NULL, &staticPipelineCreateInfo);
851 	}
852 
853 	// Create dynamic pipeline.
854 	vk::Move<vk::VkPipeline> graphicsPipeline;
855 	{
856 		auto dynamicPipelineCreateInfo				= graphicsPipelineCreateInfoTemplate;
857 		dynamicPipelineCreateInfo.pDynamicState		= &dynamicStateCreateInfo;
858 		dynamicPipelineCreateInfo.pViewportState	= &viewportStateCreateInfo;
859 		graphicsPipeline							= vk::createGraphicsPipeline(vkd, device, DE_NULL, &dynamicPipelineCreateInfo);
860 	}
861 
862 	// Command buffer.
863 	const auto cmdPool		= vk::makeCommandPool(vkd, device, queueIndex);
864 	const auto cmdBufferPtr	= vk::allocateCommandBuffer(vkd , device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
865 	const auto cmdBuffer	= cmdBufferPtr.get();
866 
867 	// Clear values.
868 	std::vector<vk::VkClearValue> clearValues;
869 	auto colorClearValue = vk::makeClearValueColor(m_testConfig.clearColorValue);
870 	for (deUint32 i = 0u; i < kNumColorAttachments; ++i)
871 		clearValues.push_back(colorClearValue);
872 	clearValues.push_back(vk::makeClearValueDepthStencil(m_testConfig.clearDepthValue, 0u));
873 
874 	// Record command buffer.
875 	vk::beginCommandBuffer(vkd, cmdBuffer);
876 
877 	for (deUint32 iteration = 0u; iteration < kNumIterations; ++iteration)
878 	{
879 		// Maybe set dynamic state here.
880 		if (kSequenceOrdering == SequenceOrdering::CMD_BUFFER_START)
881 		{
882 			setDynamicStates(m_testConfig, vkd, cmdBuffer);
883 		}
884 
885 		// Begin render pass.
886 		vk::beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffers[iteration].get(), vk::makeRect2D(kFramebufferWidth, kFramebufferHeight), static_cast<deUint32>(clearValues.size()), clearValues.data());
887 
888 			// Bind a static pipeline first if needed.
889 			if (bindStaticFirst && iteration == 0u)
890 			{
891 				vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, staticPipeline.get());
892 			}
893 
894 			// Maybe set dynamic state here.
895 			if (kSequenceOrdering == SequenceOrdering::BETWEEN_PIPELINES)
896 			{
897 				setDynamicStates(m_testConfig, vkd, cmdBuffer);
898 			}
899 
900 			// Bind dynamic pipeline.
901 			if ((kSequenceOrdering != SequenceOrdering::TWO_DRAWS_DYNAMIC &&
902 				 kSequenceOrdering != SequenceOrdering::TWO_DRAWS_STATIC) ||
903 				(kSequenceOrdering == SequenceOrdering::TWO_DRAWS_DYNAMIC && iteration > 0u) ||
904 				(kSequenceOrdering == SequenceOrdering::TWO_DRAWS_STATIC && iteration == 0u))
905 			{
906 				vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline.get());
907 			}
908 
909 			if (kSequenceOrdering == SequenceOrdering::BEFORE_GOOD_STATIC ||
910 				(kSequenceOrdering == SequenceOrdering::TWO_DRAWS_DYNAMIC && iteration > 0u) ||
911 				(kSequenceOrdering == SequenceOrdering::TWO_DRAWS_STATIC && iteration == 0u))
912 			{
913 				setDynamicStates(m_testConfig, vkd, cmdBuffer);
914 			}
915 
916 			// Bind a static pipeline last if needed.
917 			if (kSequenceOrdering == SequenceOrdering::BEFORE_GOOD_STATIC ||
918 				(kSequenceOrdering == SequenceOrdering::TWO_DRAWS_STATIC && iteration > 0u))
919 			{
920 				vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, staticPipeline.get());
921 			}
922 
923 			// Push constants.
924 			PushConstants pushConstants =
925 			{
926 				m_testConfig.meshParams.color,		//	tcu::Vec4	triangleColor;
927 				m_testConfig.meshParams.depth,		//	float		meshDepth;
928 				m_testConfig.meshParams.scaleX,		//	float		scaleX;
929 				m_testConfig.meshParams.scaleY,		//	float		scaleY;
930 				m_testConfig.meshParams.offsetX,	//	float		offsetX;
931 				m_testConfig.meshParams.offsetY,	//	float		offsetY;
932 			};
933 			vkd.cmdPushConstants(cmdBuffer, pipelineLayout.get(), pushConstantStageFlags, 0u, static_cast<deUint32>(sizeof(pushConstants)), &pushConstants);
934 
935 			// Maybe set dynamic state here.
936 			if (kSequenceOrdering == SequenceOrdering::BEFORE_DRAW || kSequenceOrdering == SequenceOrdering::AFTER_PIPELINES)
937 			{
938 				setDynamicStates(m_testConfig, vkd, cmdBuffer);
939 			}
940 
941 			// Bind vertex buffer and draw.
942 			vk::VkDeviceSize offset = 0ull;
943 			vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertBuffer.get(), &offset);
944 			vkd.cmdDraw(cmdBuffer, 6u, 1u, 0u, 0u);
945 
946 		vk::endRenderPass(vkd, cmdBuffer);
947 	}
948 
949 	vk::endCommandBuffer(vkd, cmdBuffer);
950 
951 	// Submit commands.
952 	vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
953 
954 	// Read result image aspects from the last used framebuffer.
955 	const tcu::UVec2	renderSize(kFramebufferWidth, kFramebufferHeight);
956 
957 	const int kWidth	= static_cast<int>(kFramebufferWidth);
958 	const int kHeight	= static_cast<int>(kFramebufferHeight);
959 
960 	const tcu::Vec4		kGood(0.0f, 1.0f, 0.0f, 1.0f);
961 	const tcu::Vec4		kBad(1.0f, 0.0f, 0.0f, 1.0f);
962 
963 	const tcu::TextureFormat errorFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
964 
965 	bool colorMatchAll = true;
966 
967 	// Check expected values.
968 	auto nextAttachmentImage = colorImages.end() - kNumColorAttachments;
969 	for (deUint32 attachmentIndex = 0u; attachmentIndex < kNumColorAttachments; ++attachmentIndex, ++nextAttachmentImage)
970 	{
971 		const auto			colorBuffer = readColorAttachment(vkd, device, queue, queueIndex, allocator, (*nextAttachmentImage)->get(), kColorFormat, renderSize);
972 		const auto			colorAccess = colorBuffer->getAccess();
973 
974 		tcu::TextureLevel	colorError			(errorFormat, kWidth, kHeight);
975 		const auto			colorErrorAccess	= colorError.getAccess();
976 
977 		bool colorMatch = true;
978 
979 		for (int y = 0; y < kHeight; ++y)
980 		for (int x = 0; x < kWidth; ++x)
981 		{
982 			const auto colorPixel = colorAccess.getPixel(x, y);
983 
984 			bool match = tcu::boolAll(tcu::lessThan(tcu::absDiff(colorPixel, m_testConfig.expectedColor[attachmentIndex]), kColorThreshold));
985 			colorErrorAccess.setPixel((match ? kGood : kBad), x, y);
986 			if (!match)
987 				colorMatch = false;
988 		}
989 
990 		if (!colorMatch)
991 		{
992 			std::ostringstream desc;
993 			desc << "Result color image and error mask for attachment #" << attachmentIndex;
994 			logErrors(log, "Color", desc.str(), colorAccess, colorErrorAccess);
995 			colorMatchAll = false;
996 		}
997 	}
998 
999 	const auto			depthBuffer = readDepthAttachment(vkd, device, queue, queueIndex, allocator, dsImages.back()->get(), dsFormat, renderSize);
1000 	const auto			depthAccess = depthBuffer->getAccess();
1001 	tcu::TextureLevel	depthError(errorFormat, kWidth, kHeight);
1002 	const auto			depthErrorAccess = depthError.getAccess();
1003 
1004 	const auto	minDepth	= m_testConfig.expectedDepth - 1.0e-07f;
1005 	const auto	maxDepth	= m_testConfig.expectedDepth + 1.0e-07f;
1006 	bool		depthMatch	= true;
1007 
1008 	for (int y = 0; y < kHeight; ++y)
1009 	for (int x = 0; x < kWidth; ++x)
1010 	{
1011 		const auto depthPixel = depthAccess.getPixDepth(x, y);
1012 		bool match = de::inRange(depthPixel, minDepth, maxDepth);
1013 		depthErrorAccess.setPixel((match ? kGood : kBad), x, y);
1014 		if (!match)
1015 			depthMatch = false;
1016 	}
1017 
1018 	if (!depthMatch)
1019 	{
1020 		logErrors(log, "Depth", "Result depth image and error mask", depthAccess, depthErrorAccess);
1021 	}
1022 
1023 	if (!(colorMatchAll && depthMatch))
1024 	{
1025 		return tcu::TestStatus::fail("Incorrect value found in attachments; please check logged images");
1026 	}
1027 
1028 	return tcu::TestStatus::pass("Pass");
1029 }
1030 
1031 template <typename VectorType>
MaskVector(const VectorType & valueIfMaskIsFalse,const VectorType & valueIfMaskIsTrue,const std::vector<bool> & mask,bool inverse=false)1032 VectorType MaskVector(const VectorType& valueIfMaskIsFalse, const VectorType& valueIfMaskIsTrue, const std::vector<bool>& mask, bool inverse = false)
1033 {
1034 	DE_ASSERT(valueIfMaskIsFalse.size() == valueIfMaskIsTrue.size() && valueIfMaskIsFalse.size() == mask.size());
1035 
1036 	VectorType ret(mask.size());
1037 
1038 	for (size_t i = 0; i < mask.size(); ++i)
1039 	{
1040 		bool m = mask[i];
1041 		if (inverse)
1042 			m = !m;
1043 		ret[i] = m ? valueIfMaskIsTrue[i] : valueIfMaskIsFalse[i];
1044 	}
1045 
1046 	return ret;
1047 }
1048 
ApplyChannelMask(std::vector<tcu::Vec4> & meshColors,const tcu::BVec4 & channelMask,const tcu::Vec4 & clearColor)1049 void ApplyChannelMask(std::vector<tcu::Vec4>& meshColors, const tcu::BVec4& channelMask, const tcu::Vec4& clearColor)
1050 {
1051 	for (auto&& attachmentColor : meshColors)
1052 		attachmentColor = tcu::Vec4(
1053 			channelMask.x() ? attachmentColor.x() : clearColor.x(),
1054 			channelMask.y() ? attachmentColor.y() : clearColor.y(),
1055 			channelMask.z() ? attachmentColor.z() : clearColor.z(),
1056 			channelMask.w() ? attachmentColor.w() : clearColor.w()
1057 		);
1058 }
1059 
AddSingleTestCaseStatic(const std::string & name,const std::string & description,const std::vector<bool> mask,const tcu::BVec4 channelMask,bool inverse,tcu::TestCaseGroup * orderingGroup,tcu::TestContext & testCtx)1060 void AddSingleTestCaseStatic(
1061 	const std::string& name,
1062 	const std::string& description,
1063 	const std::vector<bool> mask,
1064 	const tcu::BVec4 channelMask,
1065 	bool inverse,
1066 	tcu::TestCaseGroup* orderingGroup,
1067 	tcu::TestContext& testCtx)
1068 {
1069 	TestConfig config(SequenceOrdering::CMD_BUFFER_START);
1070 
1071 	// Enable writes and expect the mesh color, or disable writes and expect the clear color.
1072 
1073 	config.clearColorValue						= tcu::Vec4(0.25f, 0.5f, 0.75f, 0.5f);
1074 	config.meshParams.color						= tcu::Vec4(1.0f, 0.75f, 0.5f, 0.25f);
1075 
1076 	const auto allVkFalse						= Bool32Vec(kNumColorAttachments, VK_FALSE);
1077 	const auto allVkTrue						= Bool32Vec(kNumColorAttachments, VK_TRUE);
1078 
1079 	config.channelMask = channelMask;
1080 
1081 	config.colorWriteEnableConfig.staticValue	= MaskVector(allVkFalse, allVkTrue, mask, inverse);
1082 
1083 	// Note colorWriteEnableConfig.dynamicValue is unset, defaults to an empty Maybe<T>
1084 
1085 	std::vector<tcu::Vec4> meshColorsPerAttachment(kNumColorAttachments);
1086 	meshColorsPerAttachment[0] = config.meshParams.color;
1087 	for (deUint32 i = 1u; i < kNumColorAttachments; ++i)
1088 		meshColorsPerAttachment[i] = meshColorsPerAttachment[i - 1] * 0.5f;
1089 
1090 	std::vector<tcu::Vec4> clearColorsPerAttachment(kNumColorAttachments, config.clearColorValue);
1091 
1092 	ApplyChannelMask(meshColorsPerAttachment, channelMask, config.clearColorValue);
1093 
1094 	config.expectedColor = MaskVector(clearColorsPerAttachment, meshColorsPerAttachment, mask, inverse);
1095 
1096 	// Depth should always be written even when color is not
1097 	config.clearDepthValue	= 0.5f;
1098 	config.meshParams.depth	= 0.25f;
1099 	config.expectedDepth	= 0.25f;
1100 
1101 	orderingGroup->addChild(new ColorWriteEnableTest(testCtx, name, description, config));
1102 }
1103 
AddSingleTestCaseDynamic(const std::string & name,const std::string & description,const std::vector<bool> mask,const tcu::BVec4 channelMask,bool inverse,tcu::TestCaseGroup * orderingGroup,tcu::TestContext & testCtx,SequenceOrdering ordering)1104 void AddSingleTestCaseDynamic(
1105 	const std::string& name,
1106 	const std::string& description,
1107 	const std::vector<bool> mask,
1108 	const tcu::BVec4 channelMask,
1109 	bool inverse,
1110 	tcu::TestCaseGroup* orderingGroup,
1111 	tcu::TestContext& testCtx,
1112 	SequenceOrdering ordering)
1113 {
1114 	TestConfig config(ordering);
1115 
1116 	// Enable writes and expect the mesh color, or disable writes and expect the clear color.
1117 
1118 	config.clearColorValue						= tcu::Vec4(0.25f, 0.5f, 0.75f, 0.5f);
1119 	config.meshParams.color						= tcu::Vec4(1.0f, 0.75f, 0.5f, 0.25f);
1120 
1121 	const auto allVkFalse						= Bool32Vec(kNumColorAttachments, VK_FALSE);
1122 	const auto allVkTrue						= Bool32Vec(kNumColorAttachments, VK_TRUE);
1123 
1124 	config.channelMask = channelMask;
1125 
1126 	config.colorWriteEnableConfig.staticValue	= inverse ? allVkTrue : allVkFalse;
1127 	config.colorWriteEnableConfig.dynamicValue	= MaskVector(allVkFalse, allVkTrue, mask, inverse);
1128 
1129 	std::vector<tcu::Vec4> meshColorsPerAttachment(kNumColorAttachments);
1130 	meshColorsPerAttachment[0] = config.meshParams.color;
1131 	for (deUint32 i = 1u; i < kNumColorAttachments; ++i)
1132 		meshColorsPerAttachment[i] = meshColorsPerAttachment[i - 1] * 0.5f;
1133 
1134 	std::vector<tcu::Vec4> clearColorsPerAttachment(kNumColorAttachments, config.clearColorValue);
1135 
1136 	ApplyChannelMask(meshColorsPerAttachment, channelMask, config.clearColorValue);
1137 
1138 	config.expectedColor = MaskVector(clearColorsPerAttachment, meshColorsPerAttachment, mask, inverse);
1139 
1140 	// Depth should always be written even when color is not
1141 	config.clearDepthValue	= 0.5f;
1142 	config.meshParams.depth	= 0.25f;
1143 	config.expectedDepth	= 0.25f;
1144 
1145 	orderingGroup->addChild(new ColorWriteEnableTest(testCtx, name, description, config));
1146 }
1147 
1148 } // anonymous namespace
1149 
createColorWriteEnableTests(tcu::TestContext & testCtx)1150 tcu::TestCaseGroup* createColorWriteEnableTests (tcu::TestContext& testCtx)
1151 {
1152 	de::MovePtr<tcu::TestCaseGroup> colorWriteEnableGroup(new tcu::TestCaseGroup(testCtx, "color_write_enable", "Tests for VK_EXT_color_write_enable"));
1153 
1154 	DE_ASSERT(kNumColorAttachments >= 2);
1155 
1156 	std::vector<bool> mask_all				(kNumColorAttachments, true);
1157 	std::vector<bool> mask_first			(kNumColorAttachments, false);	mask_first[0]												= true;
1158 	std::vector<bool> mask_second			(kNumColorAttachments, false);	mask_second[1]												= true;
1159 	std::vector<bool> mask_last				(kNumColorAttachments, false);	mask_last.back()											= true;
1160 	std::vector<bool> mask_first_and_second	(kNumColorAttachments, false);	mask_first_and_second[0]	= mask_first_and_second[1]		= true;
1161 	std::vector<bool> mask_second_and_last	(kNumColorAttachments, false);	mask_second_and_last[1]		= mask_second_and_last.back()	= true;
1162 
1163 	// Test cases for channel enables
1164 	static const struct
1165 	{
1166 		tcu::BVec4		enabledChannels;
1167 		std::string		name;
1168 		std::string		desc;
1169 	} kChannelCases[] =
1170 	{
1171 		{ tcu::BVec4(true, true, true, true), "all_channels", "Enable all channels in colorWriteMask"},
1172 		{ tcu::BVec4(true, false, false, false), "red_channel", "Red channel enabled in colorWriteMask"},
1173 		{ tcu::BVec4(false, true, false, false), "green_channel", "Green channel enabled in colorWriteMask"},
1174 		{ tcu::BVec4(false, false, true, false), "blue_channel", "Blue channel enabled in colorWriteMask"},
1175 		{ tcu::BVec4(false, false, false, true), "alpha_channel", "Alpha channel enabled in colorWriteMask"},
1176 		{ tcu::BVec4(false, false, false, false), "no_channels", "Disable all channels in colorWriteMask"},
1177 	};
1178 
1179 	// Test cases for the dynamic state
1180 	static const struct
1181 	{
1182 		SequenceOrdering	ordering;
1183 		std::string			name;
1184 		std::string			desc;
1185 	} kOrderingCases[] =
1186 	{
1187 		{ SequenceOrdering::CMD_BUFFER_START,	"cmd_buffer_start",		"Dynamic state set after command buffer start"																								},
1188 		{ SequenceOrdering::BEFORE_DRAW,		"before_draw",			"Dynamic state set just before drawing"																										},
1189 		{ SequenceOrdering::BETWEEN_PIPELINES,	"between_pipelines",	"Dynamic after a pipeline with static states has been bound and before a pipeline with dynamic states has been bound"						},
1190 		{ SequenceOrdering::AFTER_PIPELINES,	"after_pipelines",		"Dynamic state set after both a static-state pipeline and a second dynamic-state pipeline have been bound"									},
1191 		{ SequenceOrdering::BEFORE_GOOD_STATIC,	"before_good_static",	"Dynamic state set after a dynamic pipeline has been bound and before a second static-state pipeline with the right values has been bound"	},
1192 		{ SequenceOrdering::TWO_DRAWS_DYNAMIC,	"two_draws_dynamic",	"Bind bad static pipeline and draw, followed by binding correct dynamic pipeline and drawing again"											},
1193 		{ SequenceOrdering::TWO_DRAWS_STATIC,	"two_draws_static",		"Bind bad dynamic pipeline and draw, followed by binding correct static pipeline and drawing again"											},
1194 	};
1195 
1196 	for (int channelCaseIdx = 0; channelCaseIdx < DE_LENGTH_OF_ARRAY(kChannelCases); ++channelCaseIdx)
1197 	{
1198 		const auto& kChannelCase	 = kChannelCases[channelCaseIdx];
1199 		de::MovePtr<tcu::TestCaseGroup> channelGroup(new tcu::TestCaseGroup(testCtx, kChannelCase.name.c_str(), kChannelCase.desc.c_str()));
1200 
1201 		for (int orderingIdx = 0; orderingIdx < DE_LENGTH_OF_ARRAY(kOrderingCases); ++orderingIdx)
1202 		{
1203 			const auto& kOrderingCase	= kOrderingCases[orderingIdx];
1204 			const auto& kOrdering		= kOrderingCase.ordering;
1205 
1206 			de::MovePtr<tcu::TestCaseGroup> orderingGroup(new tcu::TestCaseGroup(testCtx, kOrderingCase.name.c_str(), kOrderingCase.desc.c_str()));
1207 
1208 			AddSingleTestCaseDynamic("enable_all",					"Dynamically enable writes to all color attachments",							mask_all,				kChannelCase.enabledChannels, false, orderingGroup.get(), testCtx, kOrdering);
1209 			AddSingleTestCaseDynamic("enable_first",				"Dynamically enable writes to the first color attachment",						mask_first,				kChannelCase.enabledChannels, false, orderingGroup.get(), testCtx, kOrdering);
1210 			AddSingleTestCaseDynamic("enable_second",				"Dynamically enable writes to the second color attachment",						mask_second,			kChannelCase.enabledChannels, false, orderingGroup.get(), testCtx, kOrdering);
1211 			AddSingleTestCaseDynamic("enable_last",					"Dynamically enable writes to the last color attachment",						mask_last,				kChannelCase.enabledChannels, false, orderingGroup.get(), testCtx, kOrdering);
1212 			AddSingleTestCaseDynamic("enable_first_and_second",		"Dynamically enable writes to the first two color attachments",					mask_first_and_second,	kChannelCase.enabledChannels, false, orderingGroup.get(), testCtx, kOrdering);
1213 			AddSingleTestCaseDynamic("enable_second_and_last",		"Dynamically enable writes to the second and last color attachments",			mask_second_and_last,	kChannelCase.enabledChannels, false, orderingGroup.get(), testCtx, kOrdering);
1214 
1215 			AddSingleTestCaseDynamic("disable_all",					"Dynamically disable writes to all color attachments",							mask_all,				kChannelCase.enabledChannels, true,  orderingGroup.get(), testCtx, kOrdering);
1216 			AddSingleTestCaseDynamic("disable_first",				"Dynamically disable writes to the first color attachment",						mask_first,				kChannelCase.enabledChannels, true,  orderingGroup.get(), testCtx, kOrdering);
1217 			AddSingleTestCaseDynamic("disable_second",				"Dynamically disable writes to the second color attachment",					mask_second,			kChannelCase.enabledChannels, true,  orderingGroup.get(), testCtx, kOrdering);
1218 			AddSingleTestCaseDynamic("disable_last",				"Dynamically disable writes to the last color attachment",						mask_last,				kChannelCase.enabledChannels, true,  orderingGroup.get(), testCtx, kOrdering);
1219 			AddSingleTestCaseDynamic("disable_first_and_second",	"Dynamically disable writes to the first two color attachments",				mask_first_and_second,	kChannelCase.enabledChannels, true,  orderingGroup.get(), testCtx, kOrdering);
1220 			AddSingleTestCaseDynamic("disable_second_and_last",		"Dynamically disable writes to the second and last color attachments",			mask_second_and_last,	kChannelCase.enabledChannels, true,  orderingGroup.get(), testCtx, kOrdering);
1221 
1222 			channelGroup->addChild(orderingGroup.release());
1223 		}
1224 
1225 		// Test cases for the static state
1226 		// Note that the dynamic state test cases above also test pipelines with static state (when ordering is BEFORE_GOOD_STATIC and TWO_DRAWS_STATIC).
1227 		// However they all bind a pipeline with the static state AFTER binding a pipeline with the dynamic state.
1228 		// The only case missing, then, is static state alone without any dynamic pipelines in the same render pass or command buffer.
1229 		de::MovePtr<tcu::TestCaseGroup> staticOrderingGroup(new tcu::TestCaseGroup(testCtx, "static", "Static state set"));
1230 
1231 		AddSingleTestCaseStatic("enable_all",				"Statically enable writes to all color attachments",							mask_all,				kChannelCase.enabledChannels, false, staticOrderingGroup.get(), testCtx);
1232 		AddSingleTestCaseStatic("enable_first",				"Statically enable writes to the first color attachment",						mask_first,				kChannelCase.enabledChannels, false, staticOrderingGroup.get(), testCtx);
1233 		AddSingleTestCaseStatic("enable_second",			"Statically enable writes to the second color attachment",						mask_second,			kChannelCase.enabledChannels, false, staticOrderingGroup.get(), testCtx);
1234 		AddSingleTestCaseStatic("enable_last",				"Statically enable writes to the last color attachment",						mask_last,				kChannelCase.enabledChannels, false, staticOrderingGroup.get(), testCtx);
1235 		AddSingleTestCaseStatic("enable_first_and_second",	"Statically enable writes to the first two color attachments",					mask_first_and_second,	kChannelCase.enabledChannels, false, staticOrderingGroup.get(), testCtx);
1236 		AddSingleTestCaseStatic("enable_second_and_last",	"Statically enable writes to the second and last color attachments",			mask_second_and_last,	kChannelCase.enabledChannels, false, staticOrderingGroup.get(), testCtx);
1237 
1238 		AddSingleTestCaseStatic("disable_all",				"Statically disable writes to all color attachments",							mask_all,				kChannelCase.enabledChannels, true,  staticOrderingGroup.get(), testCtx);
1239 		AddSingleTestCaseStatic("disable_first",			"Statically disable writes to the first color attachment",						mask_first,				kChannelCase.enabledChannels, true,  staticOrderingGroup.get(), testCtx);
1240 		AddSingleTestCaseStatic("disable_second",			"Statically disable writes to the second color attachment",						mask_second,			kChannelCase.enabledChannels, true,  staticOrderingGroup.get(), testCtx);
1241 		AddSingleTestCaseStatic("disable_last",				"Statically disable writes to the last color attachment",						mask_last,				kChannelCase.enabledChannels, true,  staticOrderingGroup.get(), testCtx);
1242 		AddSingleTestCaseStatic("disable_first_and_second",	"Statically disable writes to the first two color attachments",					mask_first_and_second,	kChannelCase.enabledChannels, true,  staticOrderingGroup.get(), testCtx);
1243 		AddSingleTestCaseStatic("disable_second_and_last",	"Statically disable writes to the second and last color attachments",			mask_second_and_last,	kChannelCase.enabledChannels, true,  staticOrderingGroup.get(), testCtx);
1244 
1245 		channelGroup->addChild(staticOrderingGroup.release());
1246 
1247 		colorWriteEnableGroup->addChild(channelGroup.release());
1248 	}
1249 
1250 	return colorWriteEnableGroup.release();
1251 }
1252 
1253 } // pipeline
1254 } // vkt
1255