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
42namespace vkt
43{
44namespace pipeline
45{
46
47namespace
48{
49
50using namespace vk;
51
52constexpr uint32_t kVertexCount = 4u;
53
54void 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
65void 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
73void 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
91void 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
107void sampleShadingWithDynamicSampleCountPrograms (vk::SourceCollections& programCollection, PipelineConstructionType)
108{
109	initFullScreenQuadVertexProgram(programCollection, "vert");
110	initBlueAndAtomicCounterFragmentProgram(programCollection, "frag");
111}
112
113void 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 */
133tcu::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
423using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
424
425} // anonymous namespace
426
427tcu::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