• 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 Vulkan Fragment Shader Invocation and Sample Cound Tests
23  *//*--------------------------------------------------------------------*/
24 #include "vktQueryPoolFragInvocationTests.hpp"
25 #include "tcuImageCompare.hpp"
26 #include "vkBarrierUtil.hpp"
27 #include "vkCmdUtil.hpp"
28 #include "vkImageUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vktTestCaseUtil.hpp"
32 
33 #include <sstream>
34 
35 namespace vkt
36 {
37 namespace QueryPool
38 {
39 
40 namespace
41 {
42 
43 using namespace vk;
44 
45 enum class QueryType { INVOCATIONS = 0, OCCLUSION };
46 
getQueryTypeName(const QueryType qType)47 std::string getQueryTypeName (const QueryType qType)
48 {
49 	switch (qType)
50 	{
51 	case QueryType::INVOCATIONS:		return "frag_invs";
52 	case QueryType::OCCLUSION:			return "occlusion";
53 	default: break;
54 	}
55 
56 	DE_ASSERT(false);
57 	return "";
58 }
59 
60 struct TestParams
61 {
62 	const QueryType	queryType;
63 	const bool		secondary;
64 };
65 
getFlatColor(void)66 tcu::Vec4 getFlatColor (void)
67 {
68 	return tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
69 }
70 
getClearColor(void)71 tcu::Vec4 getClearColor (void)
72 {
73 	return tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
74 }
75 
checkSupport(Context & context,TestParams params)76 void checkSupport (Context& context, TestParams params)
77 {
78 	if (params.secondary)
79 		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_INHERITED_QUERIES);
80 
81 	if (params.queryType == QueryType::OCCLUSION)
82 		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_OCCLUSION_QUERY_PRECISE);
83 	else if (params.queryType == QueryType::INVOCATIONS)
84 		context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_PIPELINE_STATISTICS_QUERY);
85 }
86 
initPrograms(vk::SourceCollections & programCollection,TestParams)87 void initPrograms (vk::SourceCollections& programCollection, TestParams)
88 {
89 	std::ostringstream vert;
90 	vert
91 		<< "#version 460\n"
92 		<< "vec2 positions[3] = vec2[](\n"
93 		<< "    vec2(-1.0, -1.0),"
94 		<< "    vec2(3.0, -1.0),"
95 		<< "    vec2(-1.0, 3.0)"
96 		<< ");\n"
97 		<< "void main() {\n"
98 		<< "    gl_Position = vec4(positions[gl_VertexIndex % 3], 0.0, 1.0);\n"
99 		<< "}"
100 		;
101 	programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
102 
103 	std::ostringstream frag;
104 	frag
105 		<< "#version 460\n"
106 		<< "layout (location=0) out vec4 outColor;\n"
107 		<< "void main() {\n"
108 		<< "    outColor = vec4" << getFlatColor() << ";\n"
109 		<< "}";
110 		;
111 	programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
112 }
113 
recordRenderPassCommands(const DeviceInterface & vkd,const VkCommandBuffer cmdBuffer,const VkPipelineBindPoint bindPoint,const VkPipeline pipeline)114 void recordRenderPassCommands (const DeviceInterface& vkd, const VkCommandBuffer cmdBuffer, const VkPipelineBindPoint bindPoint, const VkPipeline pipeline)
115 {
116 	vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline);
117 	vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u);
118 }
119 
testInvocations(Context & context,const TestParams params)120 tcu::TestStatus testInvocations (Context& context, const TestParams params)
121 {
122 	const auto			ctx					= context.getContextCommonData();
123 	const tcu::IVec3	fbExtent			(64, 64, 1);
124 	const auto			vkExtent			= makeExtent3D(fbExtent);
125 	const auto			colorFormat			= VK_FORMAT_R8G8B8A8_UNORM;
126 	const auto			colorSRR			= makeDefaultImageSubresourceRange();
127 	const auto			colorSRL			= makeDefaultImageSubresourceLayers();
128 	const auto			colorUsage			= (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
129 	const auto			imageType			= VK_IMAGE_TYPE_2D;
130 	const auto			bindPoint			= VK_PIPELINE_BIND_POINT_GRAPHICS;
131 
132 	ImageWithBuffer colorBuffer (ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, imageType, colorSRR);
133 
134 	const auto&	binaries	= context.getBinaryCollection();
135 	const auto	vertModule	= createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
136 	const auto	fragModule	= createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
137 
138 	const auto pipelineLayout	= makePipelineLayout(ctx.vkd, ctx.device);
139 	const auto renderPass		= makeRenderPass(ctx.vkd, ctx.device, colorFormat);
140 	const auto framebuffer		= makeFramebuffer(ctx.vkd, ctx.device, renderPass.get(), colorBuffer.getImageView(), vkExtent.width, vkExtent.height);
141 
142 	const bool isInvQuery	= (params.queryType == QueryType::INVOCATIONS);
143 	const auto queryType	= (isInvQuery ? VK_QUERY_TYPE_PIPELINE_STATISTICS : VK_QUERY_TYPE_OCCLUSION);
144 	const auto statFlags	= (isInvQuery ? static_cast<VkQueryPipelineStatisticFlags>(VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT) : 0u);
145 	const auto controlFlags	= (isInvQuery ? 0u : static_cast<VkQueryControlFlags>(VK_QUERY_CONTROL_PRECISE_BIT));
146 
147 	const VkQueryPoolCreateInfo queryPoolCreateInfo =
148 	{
149 		VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,	//	VkStructureType					sType;
150 		nullptr,									//	const void*						pNext;
151 		0u,											//	VkQueryPoolCreateFlags			flags;
152 		queryType,									//	VkQueryType						queryType;
153 		1u,											//	uint32_t						queryCount;
154 		statFlags,									//	VkQueryPipelineStatisticFlags	pipelineStatistics;
155 	};
156 	const auto queryPool = createQueryPool(ctx.vkd, ctx.device, &queryPoolCreateInfo);
157 
158 	const std::vector<VkViewport>	viewports	(1u, makeViewport(vkExtent));
159 	const std::vector<VkRect2D>		scissors	(1u, makeRect2D(fbExtent));
160 
161 	const VkPipelineVertexInputStateCreateInfo inputStateCreateInfo = initVulkanStructure();
162 
163 	const auto pipeline = makeGraphicsPipeline(ctx.vkd, ctx.device, pipelineLayout.get(),
164 		vertModule.get(), VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, fragModule.get(), renderPass.get(),
165 		viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0u, 0u,
166 		&inputStateCreateInfo);
167 
168 	CommandPoolWithBuffer	cmd					(ctx.vkd, ctx.device, ctx.qfIndex);
169 	VkCommandBuffer			primaryCmdBuffer	= cmd.cmdBuffer.get();
170 	Move<VkCommandBuffer>	secCmdBufferPtr;
171 
172 	if (params.secondary)
173 	{
174 		secCmdBufferPtr	= allocateCommandBuffer(ctx.vkd, ctx.device, cmd.cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_SECONDARY);
175 		const auto secCmdBuffer = secCmdBufferPtr.get();
176 
177 		const VkCommandBufferInheritanceInfo inheritanceInfo =
178 		{
179 			VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,				//	VkStructureType					sType;
180 			nullptr,														//	const void*						pNext;
181 			renderPass.get(),												//	VkRenderPass					renderPass;
182 			0u,																//	uint32_t						subpass;
183 			framebuffer.get(),												//	VkFramebuffer					framebuffer;
184 			((queryType == VK_QUERY_TYPE_OCCLUSION) ? VK_TRUE : VK_FALSE),	//	VkBool32						occlusionQueryEnable;
185 			controlFlags,													//	VkQueryControlFlags				queryFlags;
186 			statFlags,														//	VkQueryPipelineStatisticFlags	pipelineStatistics;
187 		};
188 
189 		const auto usageFlags = (VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT);
190 		const VkCommandBufferBeginInfo beginInfo =
191 		{
192 			VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,	//	VkStructureType							sType;
193 			nullptr,										//	const void*								pNext;
194 			usageFlags,										//	VkCommandBufferUsageFlags				flags;
195 			&inheritanceInfo,								//	const VkCommandBufferInheritanceInfo*	pInheritanceInfo;
196 		};
197 
198 		VK_CHECK(ctx.vkd.beginCommandBuffer(secCmdBuffer, &beginInfo));
199 		recordRenderPassCommands(ctx.vkd, secCmdBuffer, bindPoint, pipeline.get());
200 		endCommandBuffer(ctx.vkd, secCmdBuffer);
201 	}
202 
203 	const auto subpassContents	= (params.secondary ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
204 	const auto clearColor		= makeClearValueColor(getClearColor());
205 
206 	beginCommandBuffer(ctx.vkd, primaryCmdBuffer);
207 	ctx.vkd.cmdResetQueryPool(primaryCmdBuffer, queryPool.get(), 0u, 1u);
208 	ctx.vkd.cmdBeginQuery(primaryCmdBuffer, queryPool.get(), 0u, controlFlags);
209 	beginRenderPass(ctx.vkd, primaryCmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor, subpassContents);
210 	if (!params.secondary)
211 		recordRenderPassCommands(ctx.vkd, primaryCmdBuffer, bindPoint, pipeline.get());
212 	else
213 		ctx.vkd.cmdExecuteCommands(primaryCmdBuffer, 1u, &secCmdBufferPtr.get());
214 	endRenderPass(ctx.vkd, primaryCmdBuffer);
215 	ctx.vkd.cmdEndQuery(primaryCmdBuffer, queryPool.get(), 0u);
216 	{
217 		const auto preTransferBarrier = makeImageMemoryBarrier(
218 			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
219 			VK_ACCESS_TRANSFER_READ_BIT,
220 			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
221 			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
222 			colorBuffer.getImage(), colorSRR);
223 		cmdPipelineImageMemoryBarrier(ctx.vkd, primaryCmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &preTransferBarrier);
224 
225 		const auto copyRegion = makeBufferImageCopy(vkExtent, colorSRL);
226 		ctx.vkd.cmdCopyImageToBuffer(primaryCmdBuffer, colorBuffer.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer.getBuffer(), 1u, &copyRegion);
227 
228 		const auto preHostBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
229 		cmdPipelineMemoryBarrier(ctx.vkd, primaryCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &preHostBarrier);
230 	}
231 	endCommandBuffer(ctx.vkd, primaryCmdBuffer);
232 	submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, primaryCmdBuffer);
233 
234 	const auto resultAllocation = colorBuffer.getBufferAllocation();
235 	invalidateAlloc(ctx.vkd, ctx.device, resultAllocation);
236 
237 	uint32_t queryResult = 0u;
238 	VK_CHECK(ctx.vkd.getQueryPoolResults(ctx.device, queryPool.get(), 0u, 1u, sizeof(queryResult), &queryResult, static_cast<VkDeviceSize>(sizeof(queryResult)), VK_QUERY_RESULT_WAIT_BIT));
239 
240 	const auto expectedResult	= vkExtent.width * vkExtent.height * vkExtent.depth;
241 	const bool needsExact		= (!isInvQuery);
242 
243 	if (needsExact)
244 	{
245 		if (expectedResult != queryResult)
246 		{
247 			std::ostringstream msg;
248 			msg << "Framebuffer size: " << vkExtent.width << "x" << vkExtent.height << "; expected query result to be " << expectedResult << " but found " << queryResult;
249 			return tcu::TestStatus::fail(msg.str());
250 		}
251 	}
252 	else
253 	{
254 		if (queryResult < expectedResult)
255 		{
256 			std::ostringstream msg;
257 			msg << "Framebuffer size: " << vkExtent.width << "x" << vkExtent.height << "; expected query result to be at least " << expectedResult << " but found " << queryResult;
258 			return tcu::TestStatus::fail(msg.str());
259 		}
260 	}
261 
262 	const auto					tcuFormat		= mapVkFormat(colorFormat);
263 	auto&						log				= context.getTestContext().getLog();
264 	const tcu::Vec4				colorThreshold	(0.0f, 0.0f, 0.0f, 0.0f); // Expect exact color result.
265 	tcu::ConstPixelBufferAccess	resultAccess	(tcuFormat, fbExtent, resultAllocation.getHostPtr());
266 
267 	if (!tcu::floatThresholdCompare(log, "Result", "", getFlatColor(), resultAccess, colorThreshold, tcu::COMPARE_LOG_ON_ERROR))
268 		return tcu::TestStatus::fail("Unexpected results in color buffer -- check log for details");
269 
270 	return tcu::TestStatus::pass("Pass");
271 }
272 
273 } // anonymous namespace
274 
createFragInvocationTests(tcu::TestContext & testContext)275 tcu::TestCaseGroup* createFragInvocationTests (tcu::TestContext& testContext)
276 {
277 	using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
278 
279 	// Test implementations do not optimize out fragment shader invocations
280 	GroupPtr mainGroup (new tcu::TestCaseGroup(testContext, "frag_invocations"));
281 
282 	for (const auto queryType : { QueryType::OCCLUSION, QueryType::INVOCATIONS })
283 	{
284 		const auto groupName = getQueryTypeName(queryType);
285 		GroupPtr queryTypeGroup (new tcu::TestCaseGroup(testContext, groupName.c_str()));
286 
287 		for (const auto secondaryCase : { false, true })
288 		{
289 			const auto testName = (secondaryCase ? "secondary" : "primary");
290 			const TestParams params { queryType, secondaryCase };
291 			addFunctionCaseWithPrograms(queryTypeGroup.get(), testName, checkSupport, initPrograms, testInvocations, params);
292 		}
293 
294 		mainGroup->addChild(queryTypeGroup.release());
295 	}
296 
297 	return mainGroup.release();
298 }
299 
300 } // QueryPool
301 } // vkt
302