• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2023 The Khronos Group Inc.
6  * Copyright (c) 2023 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 Extended dynamic state misc tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktPipelineExtendedDynamicStateMiscTests.hpp"
26 #include "vktTestCase.hpp"
27 #include "vktTestCaseUtil.hpp"
28 #include "vkBuilderUtil.hpp"
29 #include "vkCmdUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkObjUtil.hpp"
32 
33 #include "tcuImageCompare.hpp"
34 
35 #include "deUniquePtr.hpp"
36 
37 #include <sstream>
38 #include <vector>
39 #include <memory>
40 #include <utility>
41 
42 namespace vkt
43 {
44 namespace pipeline
45 {
46 
47 namespace
48 {
49 
50 using namespace vk;
51 
52 constexpr uint32_t kVertexCount = 4u;
53 
checkDynamicRasterizationSamplesSupport(Context & context)54 void checkDynamicRasterizationSamplesSupport (Context& context)
55 {
56 #ifndef CTS_USES_VULKANSC
57 	if (!context.getExtendedDynamicState3FeaturesEXT().extendedDynamicState3RasterizationSamples)
58 		TCU_THROW(NotSupportedError, "extendedDynamicState3RasterizationSamples not supported");
59 #else
60 	DE_UNREF(context);
61 	TCU_THROW(NotSupportedError, "extendedDynamicState3RasterizationSamples not supported");
62 #endif // CTS_USES_VULKANSC
63 }
64 
sampleShadingWithDynamicSampleCountSupport(Context & context,PipelineConstructionType pipelineConstructionType)65 void sampleShadingWithDynamicSampleCountSupport (Context& context, PipelineConstructionType pipelineConstructionType)
66 {
67 	checkPipelineConstructionRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), pipelineConstructionType);
68 	checkDynamicRasterizationSamplesSupport(context);
69 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_FRAGMENT_STORES_AND_ATOMICS);
70 	context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SAMPLE_RATE_SHADING);
71 }
72 
initFullScreenQuadVertexProgram(vk::SourceCollections & programCollection,const char * name)73 void initFullScreenQuadVertexProgram (vk::SourceCollections& programCollection, const char* name)
74 {
75 	std::ostringstream vert;
76 	vert
77 		<< "#version 460\n"
78 		<< "vec2 positions[" << kVertexCount << "] = vec2[](\n"
79 		<< "    vec2(-1.0, -1.0),\n"
80 		<< "    vec2(-1.0,  1.0),\n"
81 		<< "    vec2( 1.0, -1.0),\n"
82 		<< "    vec2( 1.0,  1.0)\n"
83 		<< ");\n"
84 		<< "void main (void) {\n"
85 		<< "    gl_Position = vec4(positions[gl_VertexIndex % " << kVertexCount << "], 0.0, 1.0);\n"
86 		<< "}\n"
87 		;
88 	programCollection.glslSources.add(name) << glu::VertexSource(vert.str());
89 }
90 
initBlueAndAtomicCounterFragmentProgram(vk::SourceCollections & programCollection,const char * name)91 void initBlueAndAtomicCounterFragmentProgram (vk::SourceCollections& programCollection, const char* name)
92 {
93 	std::ostringstream frag;
94 	frag
95 		<< "#version 460\n"
96 		<< "layout (location=0) out vec4 outColor;\n"
97 		<< "layout (set=0, binding=0) buffer InvocationCounterBlock { uint invocations; } counterBuffer;\n"
98 		<< "void main (void) {\n"
99 		<< "    uint sampleId = gl_SampleID;\n" // Enable sample shading for shader objects by reading gl_SampleID
100 		<< "    atomicAdd(counterBuffer.invocations, 1u);\n"
101 		<< "    outColor = vec4(0.0, 0.0, 1.0, 1.0);\n"
102 		<< "}\n"
103 		;
104 	programCollection.glslSources.add(name) << glu::FragmentSource(frag.str());
105 }
106 
sampleShadingWithDynamicSampleCountPrograms(vk::SourceCollections & programCollection,PipelineConstructionType)107 void sampleShadingWithDynamicSampleCountPrograms (vk::SourceCollections& programCollection, PipelineConstructionType)
108 {
109 	initFullScreenQuadVertexProgram(programCollection, "vert");
110 	initBlueAndAtomicCounterFragmentProgram(programCollection, "frag");
111 }
112 
verifyValueInRange(uint32_t value,uint32_t minValue,uint32_t maxValue,const char * valueDesc)113 void verifyValueInRange (uint32_t value, uint32_t minValue, uint32_t maxValue, const char* valueDesc)
114 {
115 	if (value < minValue || value > maxValue)
116 	{
117 		std::ostringstream msg;
118 		msg << "Unexpected value found for " << valueDesc << ": " << value << " not in range [" << minValue << ", " << maxValue << "]";
119 		TCU_FAIL(msg.str());
120 	}
121 }
122 /*
123  * begin cmdbuf
124  * bind pipeline with sample shading disabled
125  * call vkCmdSetRasterizationSamplesEXT(samples > 1)
126  * draw
127  * bind pipeline with sample shading enabled
128  * draw
129  * sample shading should work for both draws with the expected number of samples
130  *
131  * Each draw will use one half of the framebuffer, controlled by the viewport and scissor.
132  */
sampleShadingWithDynamicSampleCount(Context & context,PipelineConstructionType constructionType)133 tcu::TestStatus sampleShadingWithDynamicSampleCount (Context& context, PipelineConstructionType constructionType)
134 {
135 	const auto			ctx					= context.getContextCommonData();
136 	const tcu::IVec3	fbExtent			(2, 2, 1);
137 	const auto			vkExtent			= makeExtent3D(fbExtent);
138 	const auto			colorFormat			= VK_FORMAT_R8G8B8A8_UNORM;
139 	const auto			colorUsage			= (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
140 	const auto			descriptorType		= VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
141 	const auto			descriptorStages	= VK_SHADER_STAGE_FRAGMENT_BIT;
142 	const auto			kNumDraws			= 2u;
143 	const auto			bindPoint			= VK_PIPELINE_BIND_POINT_GRAPHICS;
144 	const auto			colorSRR			= makeDefaultImageSubresourceRange();
145 	const auto			kMultiSampleCount	= VK_SAMPLE_COUNT_4_BIT;
146 	const auto			kSingleSampleCount	= VK_SAMPLE_COUNT_1_BIT;
147 	const tcu::Vec4		clearColor			(0.0f, 0.0f, 0.0f, 0.0f);
148 	const tcu::Vec4		geomColor			(0.0f, 0.0f, 1.0f, 1.0f); // Must match frag shader.
149 	const auto			topology			= VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
150 
151 	// Color buffers.
152 	ImageWithBuffer colorBuffer		(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, VK_IMAGE_TYPE_2D, colorSRR, 1u, kMultiSampleCount);
153 	ImageWithBuffer resolveBuffer	(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, VK_IMAGE_TYPE_2D, colorSRR, 1u, kSingleSampleCount);
154 
155 	// Counter buffers.
156 	using BufferPtr = std::unique_ptr<BufferWithMemory>;
157 	using BufferVec = std::vector<BufferPtr>;
158 
159 	const auto			counterBufferSize	= static_cast<VkDeviceSize>(sizeof(uint32_t));
160 	const auto			counterBufferInfo	= makeBufferCreateInfo(counterBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
161 
162 	BufferVec counterBuffers;
163 
164 	for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
165 	{
166 		BufferPtr			counterBuffer		(new BufferWithMemory(ctx.vkd, ctx.device, ctx.allocator, counterBufferInfo, MemoryRequirement::HostVisible));
167 		auto&				counterBufferAlloc	= counterBuffer->getAllocation();
168 		void*				counterBufferPtr	= counterBufferAlloc.getHostPtr();
169 
170 		deMemset(counterBufferPtr, 0, static_cast<size_t>(counterBufferSize));
171 		flushAlloc(ctx.vkd, ctx.device, counterBufferAlloc);
172 
173 		counterBuffers.emplace_back(std::move(counterBuffer));
174 	}
175 
176 	// Descriptor set layout, pool and set.
177 	DescriptorSetLayoutBuilder setLayoutbuilder;
178 	setLayoutbuilder.addSingleBinding(descriptorType, descriptorStages);
179 	const auto setLayout = setLayoutbuilder.build(ctx.vkd, ctx.device);
180 
181 	DescriptorPoolBuilder poolBuilder;
182 	poolBuilder.addType(descriptorType, kNumDraws);
183 	const auto descriptorPool = poolBuilder.build(ctx.vkd, ctx.device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, kNumDraws);
184 
185 	using DescriptorSetVec = std::vector<Move<VkDescriptorSet>>;
186 	DescriptorSetVec descriptorSets;
187 
188 	for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
189 	{
190 		descriptorSets.emplace_back(makeDescriptorSet(ctx.vkd, ctx.device, *descriptorPool, *setLayout));
191 
192 		DescriptorSetUpdateBuilder updateBuilder;
193 		const auto counterBufferDescriptorInfo = makeDescriptorBufferInfo(counterBuffers.at(drawIdx)->get(), 0ull, counterBufferSize);
194 		updateBuilder.writeSingle(*descriptorSets.back(), DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &counterBufferDescriptorInfo);
195 		updateBuilder.update(ctx.vkd, ctx.device);
196 	}
197 
198 	// Render pass and framebuffer.
199 	const std::vector<VkAttachmentDescription> attachmentDescs
200 	{
201 		// Multisample attachment.
202 		makeAttachmentDescription(
203 			0u,
204 			colorFormat,
205 			kMultiSampleCount,
206 			VK_ATTACHMENT_LOAD_OP_CLEAR,
207 			VK_ATTACHMENT_STORE_OP_DONT_CARE,
208 			VK_ATTACHMENT_LOAD_OP_DONT_CARE,
209 			VK_ATTACHMENT_STORE_OP_DONT_CARE,
210 			VK_IMAGE_LAYOUT_UNDEFINED,
211 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL),
212 
213 		// Resolve attachment.
214 		makeAttachmentDescription(
215 			0u,
216 			colorFormat,
217 			kSingleSampleCount,
218 			VK_ATTACHMENT_LOAD_OP_DONT_CARE,
219 			VK_ATTACHMENT_STORE_OP_STORE,
220 			VK_ATTACHMENT_LOAD_OP_DONT_CARE,
221 			VK_ATTACHMENT_STORE_OP_DONT_CARE,
222 			VK_IMAGE_LAYOUT_UNDEFINED,
223 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL),
224 	};
225 
226 	const auto colorAttRef			= makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
227 	const auto resolveAttRef		= makeAttachmentReference(1u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
228 	const auto subpassDescription	= makeSubpassDescription(0u, bindPoint, 0u, nullptr, 1u, &colorAttRef, &resolveAttRef, nullptr, 0u, nullptr);
229 
230 	const VkRenderPassCreateInfo renderPassCreateInfo =
231 	{
232 		VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,	//	VkStructureType					sType;
233 		nullptr,									//	const void*						pNext;
234 		0u,											//	VkRenderPassCreateFlags			flags;
235 		de::sizeU32(attachmentDescs),				//	uint32_t						attachmentCount;
236 		de::dataOrNull(attachmentDescs),			//	const VkAttachmentDescription*	pAttachments;
237 		1u,											//	uint32_t						subpassCount;
238 		&subpassDescription,						//	const VkSubpassDescription*		pSubpasses;
239 		0u,											//	uint32_t						dependencyCount;
240 		nullptr,									//	const VkSubpassDependency*		pDependencies;
241 	};
242 	auto renderPass = RenderPassWrapper(constructionType, ctx.vkd, ctx.device, &renderPassCreateInfo);
243 
244 	const std::vector<VkImage>		images		{ colorBuffer.getImage(), resolveBuffer.getImage() };
245 	const std::vector<VkImageView>	imageViews	{ colorBuffer.getImageView(), resolveBuffer.getImageView() };
246 	renderPass.createFramebuffer(ctx.vkd, ctx.device, de::sizeU32(imageViews), de::dataOrNull(images), de::dataOrNull(imageViews), vkExtent.width, vkExtent.height);
247 
248 	// Pipelines.
249 	const auto& binaries		= context.getBinaryCollection();
250 	const auto& vertModule		= ShaderWrapper(ctx.vkd, ctx.device, binaries.get("vert"));
251 	const auto& fragModule		= ShaderWrapper(ctx.vkd, ctx.device, binaries.get("frag"));
252 
253 	const std::vector<VkDynamicState>		dynamicStates		{
254 #ifndef CTS_USES_VULKANSC
255 		VK_DYNAMIC_STATE_RASTERIZATION_SAMPLES_EXT,
256 #endif // CTS_USES_VULKANSC
257 		VK_DYNAMIC_STATE_SCISSOR,
258 		VK_DYNAMIC_STATE_VIEWPORT,
259 	};
260 
261 	const VkPipelineDynamicStateCreateInfo	dynamicStateInfo	=
262 	{
263 		VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,	//	VkStructureType						sType;
264 		nullptr,												//	const void*							pNext;
265 		0u,														//	VkPipelineDynamicStateCreateFlags	flags;
266 		de::sizeU32(dynamicStates),								//	uint32_t							dynamicStateCount;
267 		de::dataOrNull(dynamicStates),							//	const VkDynamicState*				pDynamicStates;
268 	};
269 
270 	const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = initVulkanStructureConst();
271 
272 	VkPipelineMultisampleStateCreateInfo multisampleStateCreateInfo =
273 	{
274 		VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,	//	VkStructureType							sType;
275 		nullptr,													//	const void*								pNext;
276 		0u,															//	VkPipelineMultisampleStateCreateFlags	flags;
277 		VK_SAMPLE_COUNT_64_BIT,										//	VkSampleCountFlagBits					rasterizationSamples;
278 		VK_FALSE,													//	VkBool32								sampleShadingEnable;
279 		1.0f,														//	float									minSampleShading;
280 		nullptr,													//	const VkSampleMask*						pSampleMask;
281 		VK_FALSE,													//	VkBool32								alphaToCoverageEnable;
282 		VK_FALSE,													//	VkBool32								alphaToOneEnable;
283 	};
284 
285 	const std::vector<VkViewport>	staticViewports		(1u, makeViewport(0u, 0u));
286 	const std::vector<VkRect2D>		staticScissors		(1u, makeRect2D(0u, 0u));
287 	const PipelineLayoutWrapper		pipelineLayout		(constructionType, ctx.vkd, ctx.device, *setLayout);
288 	const auto						renderArea			= makeRect2D(fbExtent);
289 	const int						halfWidth			= fbExtent.x() / 2;
290 	const uint32_t					halfWidthU			= static_cast<uint32_t>(halfWidth);
291 	const float						halfWidthF			= static_cast<float>(halfWidth);
292 	const float						heightF				= static_cast<float>(vkExtent.height);
293 	const std::vector<VkRect2D>		dynamicScissors		{ makeRect2D(0, 0, halfWidthU, vkExtent.height), makeRect2D(halfWidth, 0, halfWidthU, vkExtent.height) };
294 	const std::vector<VkViewport>	dynamicViewports
295 	{
296 		makeViewport(0.0f, 0.0f, halfWidthF, heightF, 0.0f, 1.0f),
297 		makeViewport(halfWidthF, 0.0f, halfWidthF, heightF, 0.0f, 1.0f),
298 	};
299 
300 	using WrapperPtr = std::unique_ptr<GraphicsPipelineWrapper>;
301 	using WrapperVec = std::vector<WrapperPtr>;
302 
303 	WrapperVec wrappers;
304 
305 	for (const auto sampleShadingEnable : { false, true })
306 	{
307 		multisampleStateCreateInfo.sampleShadingEnable = sampleShadingEnable;
308 
309 		WrapperPtr pipelineWrapper(new GraphicsPipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device, context.getDeviceExtensions(), constructionType));
310 		pipelineWrapper->setDefaultTopology(topology)
311 						.setDefaultRasterizationState()
312 						.setDefaultColorBlendState()
313 						.setDynamicState(&dynamicStateInfo)
314 						.setupVertexInputState(&vertexInputStateCreateInfo)
315 						.setupPreRasterizationShaderState(
316 							staticViewports,
317 							staticScissors,
318 							pipelineLayout,
319 							*renderPass,
320 							0u,
321 							vertModule)
322 						.setupFragmentShaderState(
323 							pipelineLayout,
324 							*renderPass,
325 							0u,
326 							fragModule,
327 							nullptr,
328 							&multisampleStateCreateInfo)
329 						.setupFragmentOutputState(
330 							*renderPass,
331 							0u,
332 							nullptr,
333 							&multisampleStateCreateInfo)
334 						.setMonolithicPipelineLayout(pipelineLayout)
335 						.buildPipeline();
336 
337 		wrappers.emplace_back(std::move(pipelineWrapper));
338 	}
339 
340 	CommandPoolWithBuffer cmd (ctx.vkd, ctx.device, ctx.qfIndex);
341 	const auto cmdBuffer = cmd.cmdBuffer.get();
342 
343 	beginCommandBuffer(ctx.vkd, cmdBuffer);
344 	renderPass.begin(ctx.vkd, cmdBuffer, renderArea, clearColor);
345 	for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
346 	{
347 		wrappers.at(drawIdx)->bind(cmdBuffer);
348 		if (drawIdx == 0u)
349 		{
350 #ifndef CTS_USES_VULKANSC
351 			ctx.vkd.cmdSetRasterizationSamplesEXT(cmdBuffer, kMultiSampleCount);
352 #else
353 			DE_ASSERT(false);
354 #endif // CTS_USES_VULKANSC
355 		}
356 #ifndef CTS_USES_VULKANSC
357 		if (isConstructionTypeShaderObject(constructionType))
358 		{
359 			ctx.vkd.cmdSetScissorWithCount(cmdBuffer, 1u, &dynamicScissors.at(drawIdx));
360 			ctx.vkd.cmdSetViewportWithCount(cmdBuffer, 1u, &dynamicViewports.at(drawIdx));
361 		}
362 		else
363 #endif // CTS_USES_VULKANSC
364 		{
365 			ctx.vkd.cmdSetScissor(cmdBuffer, 0u, 1u, &dynamicScissors.at(drawIdx));
366 			ctx.vkd.cmdSetViewport(cmdBuffer, 0u, 1u, &dynamicViewports.at(drawIdx));
367 		}
368 		ctx.vkd.cmdBindDescriptorSets(cmdBuffer, bindPoint, *pipelineLayout, 0u, 1u, &descriptorSets.at(drawIdx).get(), 0u, nullptr);
369 		ctx.vkd.cmdDraw(cmdBuffer, kVertexCount, 1u, 0u, 0u);
370 	}
371 	renderPass.end(ctx.vkd, cmdBuffer);
372 	copyImageToBuffer(
373 		ctx.vkd,
374 		cmdBuffer,
375 		resolveBuffer.getImage(),
376 		resolveBuffer.getBuffer(),
377 		fbExtent.swizzle(0, 1),
378 		VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
379 		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
380 		1u,
381 		VK_IMAGE_ASPECT_COLOR_BIT,
382 		VK_IMAGE_ASPECT_COLOR_BIT,
383 		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
384 	endCommandBuffer(ctx.vkd, cmdBuffer);
385 	submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
386 
387 	// Verify resolve buffer and counter buffers.
388 	auto& log = context.getTestContext().getLog();
389 	{
390 		const tcu::Vec4	threshold			(0.0f, 0.0f, 0.0f, 0.0f); // Expect exact results.
391 		const auto		tcuFormat			= mapVkFormat(colorFormat);
392 		const auto&		resolveBufferAlloc	= resolveBuffer.getBufferAllocation();
393 		const auto		resolveBufferData	= resolveBufferAlloc.getHostPtr();
394 
395 		invalidateAlloc(ctx.vkd, ctx.device, resolveBufferAlloc);
396 		const tcu::ConstPixelBufferAccess resultAccess(tcuFormat, fbExtent, resolveBufferData);
397 
398 		if (!tcu::floatThresholdCompare(log, "Result", "", geomColor, resultAccess, threshold, tcu::COMPARE_LOG_ON_ERROR))
399 			return tcu::TestStatus::fail("Unexpected color buffer results -- check log for details");
400 	}
401 	{
402 		std::vector<uint32_t> counterResults (kNumDraws, 0u);
403 		for (uint32_t drawIdx = 0u; drawIdx < kNumDraws; ++drawIdx)
404 		{
405 			const auto& bufferAlloc = counterBuffers.at(drawIdx)->getAllocation();
406 			invalidateAlloc(ctx.vkd, ctx.device, bufferAlloc);
407 			deMemcpy(&counterResults.at(drawIdx), bufferAlloc.getHostPtr(), sizeof(counterResults.at(drawIdx)));
408 			log << tcu::TestLog::Message << "Draw " << drawIdx << ": " << counterResults.at(drawIdx) << " invocations" << tcu::TestLog::EndMessage;
409 		}
410 
411 		// The first result is run without sample shading enabled, so it can have any value from 1 to 4 invocations per pixel.
412 		// The second result runs with sample shading enabled, so it must have exactly 4 invocations per pixel.
413 		const uint32_t minInvs = (vkExtent.width * vkExtent.height) / 2u;
414 		const uint32_t maxInvs = minInvs * static_cast<uint32_t>(kMultiSampleCount);
415 
416 		verifyValueInRange(counterResults.at(0u), minInvs, maxInvs, "invocation counter without sample shading");
417 		verifyValueInRange(counterResults.at(1u), maxInvs, maxInvs, "invocation counter with sample shading");
418 	}
419 
420 	return tcu::TestStatus::pass("Pass");
421 }
422 
423 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
424 
425 } // anonymous namespace
426 
createExtendedDynamicStateMiscTests(tcu::TestContext & testCtx,vk::PipelineConstructionType pipelineConstructionType)427 tcu::TestCaseGroup* createExtendedDynamicStateMiscTests (tcu::TestContext& testCtx, vk::PipelineConstructionType pipelineConstructionType)
428 {
429 	GroupPtr miscGroup (new tcu::TestCaseGroup(testCtx, "misc"));
430 	addFunctionCaseWithPrograms(miscGroup.get(), "sample_shading_dynamic_sample_count", sampleShadingWithDynamicSampleCountSupport, sampleShadingWithDynamicSampleCountPrograms, sampleShadingWithDynamicSampleCount, pipelineConstructionType);
431 	return miscGroup.release();
432 }
433 
434 } // pipeline
435 } // vkt
436