• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 The Khronos Group Inc.
6  * Copyright (c) 2019 Google Inc.
7  * Copyright (c) 2017 Codeplay Software Ltd.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  */ /*!
22  * \file
23  * \brief Subgroups Tests
24  */ /*--------------------------------------------------------------------*/
25 
26 #include "vktSubgroupsShapeTests.hpp"
27 #include "vktSubgroupsTestsUtils.hpp"
28 
29 #include <string>
30 #include <vector>
31 
32 using namespace tcu;
33 using namespace std;
34 using namespace vk;
35 using namespace vkt;
36 
37 namespace
38 {
39 enum OpType
40 {
41 	OPTYPE_CLUSTERED = 0,
42 	OPTYPE_QUAD,
43 	OPTYPE_LAST
44 };
45 
46 struct CaseDefinition
47 {
48 	OpType				opType;
49 	VkShaderStageFlags	shaderStage;
50 	de::SharedPtr<bool>	geometryPointSizeSupported;
51 	deBool				requiredSubgroupSize;
52 };
53 
checkVertexPipelineStages(const void * internalData,vector<const void * > datas,deUint32 width,deUint32)54 static bool checkVertexPipelineStages (const void*			internalData,
55 									   vector<const void*>	datas,
56 									   deUint32				width,
57 									   deUint32)
58 {
59 	DE_UNREF(internalData);
60 
61 	return subgroups::check(datas, width, 1);
62 }
63 
checkCompute(const void * internalData,vector<const void * > datas,const deUint32 numWorkgroups[3],const deUint32 localSize[3],deUint32)64 static bool checkCompute (const void*			internalData,
65 						  vector<const void*>	datas,
66 						  const deUint32		numWorkgroups[3],
67 						  const deUint32		localSize[3],
68 						  deUint32)
69 {
70 	DE_UNREF(internalData);
71 
72 	return subgroups::checkCompute(datas, numWorkgroups, localSize, 1);
73 }
74 
getOpTypeName(const OpType opType)75 string getOpTypeName (const OpType opType)
76 {
77 	switch (opType)
78 	{
79 		case OPTYPE_CLUSTERED:	return "clustered";
80 		case OPTYPE_QUAD:		return "quad";
81 		default:				TCU_THROW(InternalError, "Unsupported op type");
82 	}
83 }
84 
getExtHeader(const CaseDefinition & caseDef)85 string getExtHeader (const CaseDefinition& caseDef)
86 {
87 	const string	testExtensions	= (OPTYPE_CLUSTERED == caseDef.opType)
88 									? "#extension GL_KHR_shader_subgroup_clustered: enable\n"
89 									: "#extension GL_KHR_shader_subgroup_quad: enable\n";
90 	const string	extensions		= testExtensions
91 									+ "#extension GL_KHR_shader_subgroup_ballot: enable\n";
92 
93 	return extensions;
94 }
95 
getBodySource(const CaseDefinition & caseDef)96 string getBodySource (const CaseDefinition& caseDef)
97 {
98 	ostringstream	bdy;
99 
100 	bdy << "  uint tempResult = 0x1;\n"
101 		<< "  uvec4 mask = subgroupBallot(true);\n";
102 
103 	if (OPTYPE_CLUSTERED == caseDef.opType)
104 	{
105 		for (deUint32 i = 1; i <= subgroups::maxSupportedSubgroupSize(); i *= 2)
106 		{
107 			bdy << "  if (gl_SubgroupSize >= " << i << ")\n"
108 				<< "  {\n"
109 				<< "    uvec4 contribution = uvec4(0);\n"
110 				<< "    const uint modID = gl_SubgroupInvocationID % 32;\n"
111 				<< "    switch (gl_SubgroupInvocationID / 32)\n"
112 				<< "    {\n"
113 				<< "    case 0: contribution.x = 1 << modID; break;\n"
114 				<< "    case 1: contribution.y = 1 << modID; break;\n"
115 				<< "    case 2: contribution.z = 1 << modID; break;\n"
116 				<< "    case 3: contribution.w = 1 << modID; break;\n"
117 				<< "    }\n"
118 				<< "    uvec4 result = subgroupClusteredOr(contribution, " << i << ");\n"
119 				<< "    uint rootID = gl_SubgroupInvocationID & ~(" << i - 1 << ");\n"
120 				<< "    for (uint i = 0; i < " << i << "; i++)\n"
121 				<< "    {\n"
122 				<< "      uint nextID = rootID + i;\n"
123 				<< "      if (subgroupBallotBitExtract(mask, nextID) ^^ subgroupBallotBitExtract(result, nextID))\n"
124 				<< "      {\n"
125 				<< "        tempResult = 0;\n"
126 				<< "      }\n"
127 				<< "    }\n"
128 				<< "  }\n";
129 		}
130 	}
131 	else
132 	{
133 		bdy << "  uint cluster[4] =\n"
134 			<< "  {\n"
135 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 0),\n"
136 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 1),\n"
137 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 2),\n"
138 			<< "    subgroupQuadBroadcast(gl_SubgroupInvocationID, 3)\n"
139 			<< "  };\n"
140 			<< "  uint rootID = gl_SubgroupInvocationID & ~0x3;\n"
141 			<< "  for (uint i = 0; i < 4; i++)\n"
142 			<< "  {\n"
143 			<< "    uint nextID = rootID + i;\n"
144 			<< "    if (subgroupBallotBitExtract(mask, nextID) && (cluster[i] != nextID))\n"
145 			<< "    {\n"
146 			<< "      tempResult = mask.x;\n"
147 			<< "    }\n"
148 			<< "  }\n";
149 	}
150 
151 	bdy << "  tempRes = tempResult;\n";
152 
153 	return bdy.str();
154 }
155 
getFramebufferPerStageHeadDeclarations(const CaseDefinition & caseDef)156 vector<string> getFramebufferPerStageHeadDeclarations (const CaseDefinition& caseDef)
157 {
158 	vector<string>	result;
159 
160 	DE_UNREF(caseDef);
161 
162 	result.push_back("layout(location = 0) out float result;\n");
163 	result.push_back("layout(location = 0) out float out_color;\n");
164 	result.push_back("layout(location = 0) out float out_color[];\n");
165 	result.push_back("layout(location = 0) out float out_color;\n");
166 
167 	return result;
168 }
169 
initFrameBufferPrograms(SourceCollections & programCollection,CaseDefinition caseDef)170 void initFrameBufferPrograms (SourceCollections& programCollection, CaseDefinition caseDef)
171 {
172 	const ShaderBuildOptions	buildOptions		(programCollection.usedVulkanVersion, SPIRV_VERSION_1_3, 0u);
173 	const string				extHeader			= getExtHeader(caseDef);
174 	const string				testSrc				= getBodySource(caseDef);
175 	const vector<string>		headDeclarations	= getFramebufferPerStageHeadDeclarations(caseDef);
176 	const bool					pointSizeSupported	= *caseDef.geometryPointSizeSupported;
177 
178 	subgroups::initStdFrameBufferPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT, pointSizeSupported, extHeader, testSrc, "", headDeclarations);
179 }
180 
getPerStageHeadDeclarations(const CaseDefinition & caseDef)181 vector<string> getPerStageHeadDeclarations (const CaseDefinition& caseDef)
182 {
183 	const deUint32	stageCount	= subgroups::getStagesCount(caseDef.shaderStage);
184 	const bool		fragment	= (caseDef.shaderStage & VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
185 	vector<string>	result		(stageCount, string());
186 
187 	if (fragment)
188 		result.reserve(result.size() + 1);
189 
190 	for (size_t i = 0; i < result.size(); ++i)
191 	{
192 		result[i] =
193 			"layout(set = 0, binding = " + de::toString(i) + ", std430) buffer Buffer1\n"
194 			"{\n"
195 			"  uint result[];\n"
196 			"};\n";
197 	}
198 
199 	if (fragment)
200 	{
201 		const string	fragPart	=
202 			"layout(location = 0) out uint result;\n";
203 
204 		result.push_back(fragPart);
205 	}
206 
207 	return result;
208 }
209 
initPrograms(SourceCollections & programCollection,CaseDefinition caseDef)210 void initPrograms (SourceCollections& programCollection, CaseDefinition caseDef)
211 {
212 	const SpirvVersion			spirvVersion		= isAllRayTracingStages(caseDef.shaderStage) ? SPIRV_VERSION_1_4 : SPIRV_VERSION_1_3;
213 	const ShaderBuildOptions	buildOptions		(programCollection.usedVulkanVersion, spirvVersion, 0u);
214 	const string				extHeader			= getExtHeader(caseDef);
215 	const string				testSrc				= getBodySource(caseDef);
216 	const vector<string>		headDeclarations	= getPerStageHeadDeclarations(caseDef);
217 	const bool					pointSizeSupport	= *caseDef.geometryPointSizeSupported;
218 
219 	subgroups::initStdPrograms(programCollection, buildOptions, caseDef.shaderStage, VK_FORMAT_R32_UINT, pointSizeSupport, extHeader, testSrc, "", headDeclarations);
220 }
221 
supportedCheck(Context & context,CaseDefinition caseDef)222 void supportedCheck (Context& context, CaseDefinition caseDef)
223 {
224 	if (!subgroups::isSubgroupSupported(context))
225 		TCU_THROW(NotSupportedError, "Subgroup operations are not supported");
226 
227 	if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_BALLOT_BIT))
228 	{
229 		TCU_THROW(NotSupportedError, "Device does not support subgroup ballot operations");
230 	}
231 
232 	if (OPTYPE_CLUSTERED == caseDef.opType)
233 	{
234 		if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_CLUSTERED_BIT))
235 		{
236 			TCU_THROW(NotSupportedError, "Subgroup shape tests require that clustered operations are supported!");
237 		}
238 	}
239 
240 	if (OPTYPE_QUAD == caseDef.opType)
241 	{
242 		if (!subgroups::isSubgroupFeatureSupportedForDevice(context, VK_SUBGROUP_FEATURE_QUAD_BIT))
243 		{
244 			TCU_THROW(NotSupportedError, "Subgroup shape tests require that quad operations are supported!");
245 		}
246 	}
247 
248 	if (caseDef.requiredSubgroupSize)
249 	{
250 		context.requireDeviceFunctionality("VK_EXT_subgroup_size_control");
251 
252 		const VkPhysicalDeviceSubgroupSizeControlFeaturesEXT&	subgroupSizeControlFeatures		= context.getSubgroupSizeControlFeaturesEXT();
253 		const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT&	subgroupSizeControlProperties	= context.getSubgroupSizeControlPropertiesEXT();
254 
255 		if (subgroupSizeControlFeatures.subgroupSizeControl == DE_FALSE)
256 			TCU_THROW(NotSupportedError, "Device does not support varying subgroup sizes nor required subgroup size");
257 
258 		if (subgroupSizeControlFeatures.computeFullSubgroups == DE_FALSE)
259 			TCU_THROW(NotSupportedError, "Device does not support full subgroups in compute shaders");
260 
261 		if ((subgroupSizeControlProperties.requiredSubgroupSizeStages & caseDef.shaderStage) != caseDef.shaderStage)
262 			TCU_THROW(NotSupportedError, "Required subgroup size is not supported for shader stage");
263 	}
264 
265 	*caseDef.geometryPointSizeSupported = subgroups::isTessellationAndGeometryPointSizeSupported(context);
266 
267 	if (isAllRayTracingStages(caseDef.shaderStage))
268 	{
269 		context.requireDeviceFunctionality("VK_KHR_ray_tracing_pipeline");
270 	}
271 
272 	subgroups::supportedCheckShader(context, caseDef.shaderStage);
273 }
274 
noSSBOtest(Context & context,const CaseDefinition caseDef)275 TestStatus noSSBOtest (Context& context, const CaseDefinition caseDef)
276 {
277 	switch (caseDef.shaderStage)
278 	{
279 		case VK_SHADER_STAGE_VERTEX_BIT:					return subgroups::makeVertexFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
280 		case VK_SHADER_STAGE_GEOMETRY_BIT:					return subgroups::makeGeometryFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages);
281 		case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:		return subgroups::makeTessellationEvaluationFrameBufferTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, caseDef.shaderStage);
282 		case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:	return subgroups::makeTessellationEvaluationFrameBufferTest(context,  VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, caseDef.shaderStage);
283 		default:											TCU_THROW(InternalError, "Unhandled shader stage");
284 	}
285 }
286 
test(Context & context,const CaseDefinition caseDef)287 TestStatus test (Context& context, const CaseDefinition caseDef)
288 {
289 	if (isAllComputeStages(caseDef.shaderStage))
290 	{
291 		const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT&	subgroupSizeControlProperties	= context.getSubgroupSizeControlPropertiesEXT();
292 		TestLog&												log								= context.getTestContext().getLog();
293 
294 		if (caseDef.requiredSubgroupSize == DE_FALSE)
295 			return subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkCompute);
296 
297 		log << TestLog::Message << "Testing required subgroup size range [" <<  subgroupSizeControlProperties.minSubgroupSize << ", "
298 			<< subgroupSizeControlProperties.maxSubgroupSize << "]" << TestLog::EndMessage;
299 
300 		// According to the spec, requiredSubgroupSize must be a power-of-two integer.
301 		for (deUint32 size = subgroupSizeControlProperties.minSubgroupSize; size <= subgroupSizeControlProperties.maxSubgroupSize; size *= 2)
302 		{
303 			TestStatus result = subgroups::makeComputeTest(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkCompute,
304 																size, VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT);
305 			if (result.getCode() != QP_TEST_RESULT_PASS)
306 			{
307 				log << TestLog::Message << "subgroupSize " << size << " failed" << TestLog::EndMessage;
308 				return result;
309 			}
310 		}
311 
312 		return TestStatus::pass("OK");
313 	}
314 	else if (isAllGraphicsStages(caseDef.shaderStage))
315 	{
316 		const VkShaderStageFlags	stages	= subgroups::getPossibleGraphicsSubgroupStages(context, caseDef.shaderStage);
317 
318 		return subgroups::allStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
319 	}
320 	else if (isAllRayTracingStages(caseDef.shaderStage))
321 	{
322 		const VkShaderStageFlags	stages	= subgroups::getPossibleRayTracingSubgroupStages(context, caseDef.shaderStage);
323 
324 		return subgroups::allRayTracingStages(context, VK_FORMAT_R32_UINT, DE_NULL, 0, DE_NULL, checkVertexPipelineStages, stages);
325 	}
326 	else
327 		TCU_THROW(InternalError, "Unknown stage or invalid stage set");
328 }
329 }
330 
331 namespace vkt
332 {
333 namespace subgroups
334 {
createSubgroupsShapeTests(TestContext & testCtx)335 TestCaseGroup* createSubgroupsShapeTests (TestContext& testCtx)
336 {
337 	de::MovePtr<TestCaseGroup>	group				(new TestCaseGroup(testCtx, "shape", "Subgroup shape category tests"));
338 	de::MovePtr<TestCaseGroup>	graphicGroup		(new TestCaseGroup(testCtx, "graphics", "Subgroup shape category tests: graphics"));
339 	de::MovePtr<TestCaseGroup>	computeGroup		(new TestCaseGroup(testCtx, "compute", "Subgroup shape category tests: compute"));
340 	de::MovePtr<TestCaseGroup>	framebufferGroup	(new TestCaseGroup(testCtx, "framebuffer", "Subgroup shape category tests: framebuffer"));
341 	de::MovePtr<TestCaseGroup>	raytracingGroup		(new TestCaseGroup(testCtx, "ray_tracing", "Subgroup shape category tests: ray tracing"));
342 	const VkShaderStageFlags	stages[]			=
343 	{
344 		VK_SHADER_STAGE_VERTEX_BIT,
345 		VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
346 		VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
347 		VK_SHADER_STAGE_GEOMETRY_BIT,
348 	};
349 	const deBool				boolValues[]		=
350 	{
351 		DE_FALSE,
352 		DE_TRUE
353 	};
354 
355 	for (int opTypeIndex = 0; opTypeIndex < OPTYPE_LAST; ++opTypeIndex)
356 	{
357 		const OpType	opType	= static_cast<OpType>(opTypeIndex);
358 		const string	op		= de::toLower(getOpTypeName(opType));
359 
360 		for (size_t groupSizeNdx = 0; groupSizeNdx < DE_LENGTH_OF_ARRAY(boolValues); ++groupSizeNdx)
361 		{
362 			const deBool			requiredSubgroupSize	= boolValues[groupSizeNdx];
363 			const string			testName				= op + (requiredSubgroupSize ? "_requiredsubgroupsize" : "");
364 			const CaseDefinition	caseDef					=
365 			{
366 				opType,							//  OpType				opType;
367 				VK_SHADER_STAGE_COMPUTE_BIT,	//  VkShaderStageFlags	shaderStage;
368 				de::SharedPtr<bool>(new bool),	//  de::SharedPtr<bool>	geometryPointSizeSupported;
369 				requiredSubgroupSize			//  deBool				requiredSubgroupSize;
370 			};
371 
372 			addFunctionCaseWithPrograms(computeGroup.get(), testName, "", supportedCheck, initPrograms, test, caseDef);
373 		}
374 
375 		{
376 			const CaseDefinition	caseDef		=
377 			{
378 				opType,							//  OpType				opType;
379 				VK_SHADER_STAGE_ALL_GRAPHICS,	//  VkShaderStageFlags	shaderStage;
380 				de::SharedPtr<bool>(new bool),	//  de::SharedPtr<bool>	geometryPointSizeSupported;
381 				DE_FALSE						//  deBool				requiredSubgroupSize;
382 			};
383 
384 			addFunctionCaseWithPrograms(graphicGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
385 		}
386 
387 		{
388 			const CaseDefinition	caseDef		=
389 			{
390 				opType,							//  OpType				opType;
391 				SHADER_STAGE_ALL_RAY_TRACING,	//  VkShaderStageFlags	shaderStage;
392 				de::SharedPtr<bool>(new bool),	//  de::SharedPtr<bool>	geometryPointSizeSupported;
393 				DE_FALSE						//  deBool				requiredSubgroupSize;
394 			};
395 
396 			addFunctionCaseWithPrograms(raytracingGroup.get(), op, "", supportedCheck, initPrograms, test, caseDef);
397 		}
398 
399 		for (int stageIndex = 0; stageIndex < DE_LENGTH_OF_ARRAY(stages); ++stageIndex)
400 		{
401 			const CaseDefinition	caseDef		=
402 			{
403 				opType,							//  OpType				opType;
404 				stages[stageIndex],				//  VkShaderStageFlags	shaderStage;
405 				de::SharedPtr<bool>(new bool),	//  de::SharedPtr<bool>	geometryPointSizeSupported;
406 				DE_FALSE						//  deBool				requiredSubgroupSize;
407 			};
408 			const string			testName	= op + "_" + getShaderStageName(caseDef.shaderStage);
409 
410 			addFunctionCaseWithPrograms(framebufferGroup.get(), testName, "", supportedCheck, initFrameBufferPrograms, noSSBOtest, caseDef);
411 		}
412 	}
413 
414 	group->addChild(graphicGroup.release());
415 	group->addChild(computeGroup.release());
416 	group->addChild(framebufferGroup.release());
417 	group->addChild(raytracingGroup.release());
418 
419 	return group.release();
420 }
421 
422 } // subgroups
423 } // vkt
424