• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 Google LLC
6  * Copyright (c) 2019 The Khronos Group Inc.
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 Functional tests using amber
23  *//*--------------------------------------------------------------------*/
24 
25 #include <amber/amber.h>
26 #include "amber/recipe.h"
27 
28 #include <iostream>
29 
30 #include "deDefs.hpp"
31 #include "deUniquePtr.hpp"
32 #include "deFilePath.hpp"
33 #include "vktTestCaseUtil.hpp"
34 #include "tcuTestLog.hpp"
35 #include "vktAmberTestCase.hpp"
36 #include "vktAmberHelper.hpp"
37 #include "tcuResource.hpp"
38 #include "tcuTestLog.hpp"
39 #include "vkSpirVProgram.hpp"
40 #include "vkImageUtil.hpp"
41 
42 namespace vkt
43 {
44 namespace cts_amber
45 {
46 
AmberTestCase(tcu::TestContext & testCtx,const char * name,const char * description,const std::string & readFilename)47 AmberTestCase::AmberTestCase (tcu::TestContext&		testCtx,
48 							  const char*			name,
49 							  const char*			description,
50 							  const std::string&	readFilename)
51 	: TestCase(testCtx, name, description),
52 	  m_recipe(DE_NULL),
53 	  m_readFilename(readFilename)
54 {
55 }
56 
~AmberTestCase(void)57 AmberTestCase::~AmberTestCase (void)
58 {
59 	delete m_recipe;
60 }
61 
createInstance(Context & ctx) const62 TestInstance* AmberTestCase::createInstance (Context& ctx) const
63 {
64 	return new AmberTestInstance(ctx, m_recipe);
65 }
66 
createEngineConfig(Context & ctx)67 static amber::EngineConfig* createEngineConfig (Context& ctx)
68 {
69 	amber::EngineConfig* vkConfig = GetVulkanConfig(ctx.getInstance(),
70 			ctx.getPhysicalDevice(), ctx.getDevice(), &ctx.getDeviceFeatures(),
71 			&ctx.getDeviceFeatures2(), ctx.getInstanceExtensions(),
72 			ctx.getDeviceExtensions(), ctx.getUniversalQueueFamilyIndex(),
73 			ctx.getUniversalQueue(), ctx.getInstanceProcAddr());
74 
75 	return vkConfig;
76 }
77 
78 // Returns true if the given feature is supported by the device.
79 // Throws an internal error If the feature is not recognized at all.
isFeatureSupported(const vkt::Context & ctx,const std::string & feature)80 static bool isFeatureSupported(const vkt::Context& ctx, const std::string& feature)
81 {
82 	if (feature == "Features.shaderInt16")
83 		return ctx.getDeviceFeatures().shaderInt16;
84 	if (feature == "Features.shaderInt64")
85 		return ctx.getDeviceFeatures().shaderInt64;
86 	if (feature == "Features.tessellationShader")
87 		return ctx.getDeviceFeatures().tessellationShader;
88 	if (feature == "Features.geometryShader")
89 		return ctx.getDeviceFeatures().geometryShader;
90 	if (feature == "Features.fragmentStoresAndAtomics")
91 		return ctx.getDeviceFeatures().fragmentStoresAndAtomics;
92 	if (feature == "Features.vertexPipelineStoresAndAtomics")
93 		return ctx.getDeviceFeatures().vertexPipelineStoresAndAtomics;
94 	if (feature == "Features.fillModeNonSolid")
95 		return ctx.getDeviceFeatures().fillModeNonSolid;
96 	if (feature == "Features.shaderStorageImageMultisample")
97 		return ctx.getDeviceFeatures().shaderStorageImageMultisample;
98 	if (feature == "VariablePointerFeatures.variablePointersStorageBuffer")
99 		return ctx.getVariablePointersFeatures().variablePointersStorageBuffer;
100 	if (feature == "VariablePointerFeatures.variablePointers")
101 		return ctx.getVariablePointersFeatures().variablePointers;
102 	if (feature == "SubgroupProperties.supportedStages.fragment")
103 		return (ctx.getSubgroupProperties().supportedStages & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
104 	if (feature == "SubgroupProperties.supportedOperations.vote")
105 		return (ctx.getSubgroupProperties().supportedOperations & vk::VK_SUBGROUP_FEATURE_VOTE_BIT) != 0;
106 	if (feature == "SubgroupProperties.supportedOperations.ballot")
107 		return (ctx.getSubgroupProperties().supportedOperations & vk::VK_SUBGROUP_FEATURE_BALLOT_BIT) != 0;
108 
109 	std::string message = std::string("Unexpected feature name: ") + feature;
110 	TCU_THROW(InternalError, message.c_str());
111 }
112 
delayedInit(void)113 void AmberTestCase::delayedInit(void)
114 {
115 	// Make sure the input can be parsed before we use it.
116 	if (!parse(m_readFilename))
117 	{
118 		std::string message = "Failed to parse Amber file: " + m_readFilename;
119 		TCU_THROW(InternalError, message.c_str());
120 	}
121 }
122 
checkSupport(Context & ctx) const123 void AmberTestCase::checkSupport(Context& ctx) const
124 {
125 	// Check for instance and device extensions as declared by the test code.
126 	if (m_required_extensions.size())
127 	{
128 		std::set<std::string> device_extensions(ctx.getDeviceExtensions().begin(),
129 												ctx.getDeviceExtensions().end());
130 		std::set<std::string> instance_extensions(ctx.getInstanceExtensions().begin(),
131 												  ctx.getInstanceExtensions().end());
132 		std::string missing;
133 		for (std::set<std::string>::iterator iter = m_required_extensions.begin();
134 			 iter != m_required_extensions.end();
135 			 ++iter)
136 		{
137 			const std::string extension = *iter;
138 			if ((device_extensions.count(extension) == 0) &&
139 				(instance_extensions.count(extension) == 0))
140 			{
141 				missing += " " + extension;
142 			}
143 		}
144 		if (missing.size() > 0)
145 		{
146 			std::string message("Test requires unsupported extensions:");
147 			message += missing;
148 			TCU_THROW(NotSupportedError, message.c_str());
149 		}
150 	}
151 
152 	// Check for required features.  Do this after extensions are checked because
153 	// some feature checks are only valid when corresponding extensions are enabled.
154 	if (m_required_features.size())
155 	{
156 		std::string missing;
157 		for (std::set<std::string>::iterator iter = m_required_features.begin();
158 			 iter != m_required_features.end();
159 			 ++iter)
160 		{
161 			const std::string feature = *iter;
162 			if (!isFeatureSupported(ctx, feature))
163 			{
164 				missing += " " + feature;
165 			}
166 		}
167 		if (missing.size() > 0)
168 		{
169 			std::string message("Test requires unsupported features:");
170 			message += missing;
171 			TCU_THROW(NotSupportedError, message.c_str());
172 		}
173 	}
174 
175 	for (auto req : m_imageRequirements)
176 		checkImageSupport(ctx.getInstanceInterface(), ctx.getPhysicalDevice(), req);
177 
178 	for (auto req : m_bufferRequirements)
179 	{
180 		vk::VkFormatProperties prop;
181 		ctx.getInstanceInterface().getPhysicalDeviceFormatProperties(ctx.getPhysicalDevice(), req.m_format, &prop);
182 
183 		if ((req.m_featureFlags & prop.bufferFeatures) != req.m_featureFlags)
184 		{
185 			TCU_THROW(NotSupportedError, "Buffer format doesn't support required feature flags");
186 		}
187 	}
188 
189 	if (m_name == "triangle_fan" &&
190 		ctx.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
191 		!ctx.getPortabilitySubsetFeatures().triangleFans)
192 	{
193 		TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Triangle fans are not supported by this implementation");
194 	}
195 }
196 
197 class Delegate : public amber::Delegate
198 {
199 public:
200 					Delegate				(tcu::TestContext& testCtx);
201 
202 	amber::Result	LoadBufferData			(const std::string			file_name,
203 											 amber::BufferDataFileType	file_type,
204 											 amber::BufferInfo*			buffer) const override;
205 
Log(const std::string &)206 	void			Log						(const std::string& /*message*/) override	{ DE_FATAL("amber::Delegate::Log unimplemented"); }
LogGraphicsCalls(void) const207 	bool			LogGraphicsCalls		(void) const override						{ return m_logGraphicsCalls; }
SetLogGraphicsCalls(bool log_graphics_calls)208 	void			SetLogGraphicsCalls		(bool log_graphics_calls)					{ m_logGraphicsCalls = log_graphics_calls; }
LogExecuteCalls(void) const209 	bool			LogExecuteCalls			(void) const override						{ return m_logExecuteCalls; }
SetLogExecuteCalls(bool log_execute_calls)210 	void			SetLogExecuteCalls		(bool log_execute_calls)					{ m_logExecuteCalls = log_execute_calls; }
LogGraphicsCallsTime(void) const211 	bool			LogGraphicsCallsTime	(void) const override						{ return m_logGraphicsCallsTime; }
SetLogGraphicsCallsTime(bool log_graphics_calls_time)212 	void			SetLogGraphicsCallsTime	(bool log_graphics_calls_time)				{ m_logGraphicsCallsTime = log_graphics_calls_time; }
GetTimestampNs(void) const213 	deUint64		GetTimestampNs			(void) const override						{ DE_FATAL("amber::Delegate::GetTimestampNs unimplemented"); return 0; }
SetScriptPath(std::string path)214 	void			SetScriptPath			(std::string path)							{ m_path = path; }
215 
216 private:
217 	tcu::TestContext&	m_testCtx;
218 	std::string			m_path;
219 	bool				m_logGraphicsCalls;
220 	bool				m_logGraphicsCallsTime;
221 	bool				m_logExecuteCalls;
222 };
223 
Delegate(tcu::TestContext & testCtx)224 Delegate::Delegate (tcu::TestContext& testCtx)
225 	: m_testCtx					(testCtx)
226 	, m_path					("")
227 	, m_logGraphicsCalls		(false)
228 	, m_logGraphicsCallsTime	(false)
229 	, m_logExecuteCalls			(false)
230 {
231 }
232 
LoadBufferData(const std::string file_name,amber::BufferDataFileType file_type,amber::BufferInfo * buffer) const233 amber::Result Delegate::LoadBufferData (const std::string			file_name,
234 										amber::BufferDataFileType	file_type,
235 										amber::BufferInfo*			buffer) const
236 {
237 	const tcu::Archive&				archive		= m_testCtx.getArchive();
238 	const de::FilePath				filePath	= de::FilePath(m_path).join(file_name);
239 	de::UniquePtr<tcu::Resource>	file		(archive.getResource(filePath.getPath()));
240 	int								numBytes	= file->getSize();
241 	std::vector<deUint8>			bytes		(numBytes);
242 
243 	if (file_type == amber::BufferDataFileType::kPng)
244 		return amber::Result("Amber PNG loading unimplemented");
245 
246 	file->read(bytes.data(), numBytes);
247 
248 	if (bytes.empty())
249 		return amber::Result("Failed to load buffer data " + file_name);
250 
251 	for (deUint8 byte : bytes)
252 	{
253 		amber::Value value;
254 		value.SetIntValue(static_cast<deUint64>(byte));
255 		buffer->values.push_back(value);
256 	}
257 
258 	buffer->width	= 1;
259 	buffer->height	= 1;
260 
261 	return {};
262 }
263 
parse(const std::string & readFilename)264 bool AmberTestCase::parse (const std::string& readFilename)
265 {
266 	std::string script = ShaderSourceProvider::getSource(m_testCtx.getArchive(), readFilename.c_str());
267 	if (script.empty())
268 		return false;
269 
270 	Delegate delegate (m_testCtx);
271 	delegate.SetScriptPath(de::FilePath(readFilename).getDirName());
272 
273 	m_recipe = new amber::Recipe();
274 
275 	amber::Amber am (&delegate);
276 	amber::Result r = am.Parse(script, m_recipe);
277 
278 	m_recipe->SetFenceTimeout(~0u); // infinity of miliseconds
279 
280 	if (!r.IsSuccess())
281 	{
282 		getTestContext().getLog()
283 			<< tcu::TestLog::Message
284 			<< "Failed to parse Amber test "
285 			<< readFilename
286 			<< ": "
287 			<< r.Error()
288 			<< "\n"
289 			<< tcu::TestLog::EndMessage;
290 		// TODO(dneto): Enhance Amber to not require this.
291 		m_recipe->SetImpl(DE_NULL);
292 		return false;
293 	}
294 	return true;
295 }
296 
initPrograms(vk::SourceCollections & programCollection) const297 void AmberTestCase::initPrograms (vk::SourceCollections& programCollection) const
298 {
299 	std::vector<amber::ShaderInfo> shaders = m_recipe->GetShaderInfo();
300 	for (size_t i = 0; i < shaders.size(); ++i)
301 	{
302 		const amber::ShaderInfo& shader = shaders[i];
303 
304 		vk::SpirvVersion spirvVersion = vk::SPIRV_VERSION_1_0;
305 		DE_STATIC_ASSERT(vk::SPIRV_VERSION_LAST == vk::SPIRV_VERSION_1_5 + 1);
306 		if (shader.target_env == "spv1.5")
307 			spirvVersion = vk::SPIRV_VERSION_1_5;
308 		else if (shader.target_env == "spv1.4")
309 			spirvVersion = vk::SPIRV_VERSION_1_4;
310 		else if (shader.target_env == "spv1.3")
311 			spirvVersion = vk::SPIRV_VERSION_1_3;
312 		else if (shader.target_env == "spv1.2")
313 			spirvVersion = vk::SPIRV_VERSION_1_2;
314 		else if (shader.target_env == "spv1.1")
315 			spirvVersion = vk::SPIRV_VERSION_1_1;
316 
317 		/* Hex encoded shaders do not need to be pre-compiled */
318 		if (shader.format == amber::kShaderFormatSpirvHex)
319 			continue;
320 
321 		if (shader.format == amber::kShaderFormatSpirvAsm)
322 		{
323 			programCollection.spirvAsmSources.add(shader.shader_name) << shader.shader_source << m_asm_options;
324 		}
325 		else if (shader.format == amber::kShaderFormatGlsl)
326 		{
327 			switch (shader.type)
328 			{
329 				case amber::kShaderTypeCompute:
330 					programCollection.glslSources.add(shader.shader_name)
331 						<< glu::ComputeSource(shader.shader_source)
332 						<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
333 					break;
334 				case amber::kShaderTypeGeometry:
335 					programCollection.glslSources.add(shader.shader_name)
336 						<< glu::GeometrySource(shader.shader_source)
337 						<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
338 					break;
339 				case amber::kShaderTypeFragment:
340 					programCollection.glslSources.add(shader.shader_name)
341 						<< glu::FragmentSource(shader.shader_source)
342 						<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
343 					break;
344 				case amber::kShaderTypeVertex:
345 					programCollection.glslSources.add(shader.shader_name)
346 						<< glu::VertexSource(shader.shader_source)
347 						<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
348 					break;
349 				case amber::kShaderTypeTessellationControl:
350 					programCollection.glslSources.add(shader.shader_name)
351 						<< glu::TessellationControlSource(shader.shader_source)
352 						<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
353 					break;
354 				case amber::kShaderTypeTessellationEvaluation:
355 					programCollection.glslSources.add(shader.shader_name)
356 						<< glu::TessellationEvaluationSource(shader.shader_source)
357 						<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u);
358 					break;
359 				case amber::kShaderTypeMulti:
360 					DE_ASSERT(false && "Multi shaders not supported");
361 					break;
362 			}
363 		}
364 		else
365 		{
366 			DE_ASSERT(false && "Shader format not supported");
367 		}
368 	}
369 }
370 
iterate(void)371 tcu::TestStatus AmberTestInstance::iterate (void)
372 {
373 	amber::Amber		am (DE_NULL);
374 	amber::Options		amber_options;
375 	amber::ShaderMap	shaderMap;
376 	amber::Result		r;
377 
378 	amber_options.engine			= amber::kEngineTypeVulkan;
379 	amber_options.config			= createEngineConfig(m_context);
380 	amber_options.execution_type	= amber::ExecutionType::kExecute;
381 
382 	// Check for extensions as declared by the Amber script itself.  Throw an internal
383 	// error if that's more demanding.
384 	r = am.AreAllRequirementsSupported(m_recipe, &amber_options);
385 	if (!r.IsSuccess())
386 	{
387 		// dEQP does not to rely on external code to determine whether
388 		// a test is supported.  So throw an internal error here instead
389 		// of a NotSupportedError.  If an Amber test is not supported, then
390 		// you must override this method and throw a NotSupported exception
391 		// before reach here.
392 		TCU_THROW(InternalError, r.Error().c_str());
393 	}
394 
395 	std::vector<amber::ShaderInfo> shaders = m_recipe->GetShaderInfo();
396 	for (size_t i = 0; i < shaders.size(); ++i)
397 	{
398 		const amber::ShaderInfo& shader = shaders[i];
399 
400 		if (!m_context.getBinaryCollection().contains(shader.shader_name))
401 			continue;
402 
403 		size_t len = m_context.getBinaryCollection().get(shader.shader_name).getSize();
404 		/* This is a compiled spir-v binary which must be made of 4-byte words. We
405 		 * are moving into a word sized vector so divide by 4
406 		 */
407 		std::vector<deUint32> data;
408 		data.resize(len >> 2);
409 		deMemcpy(data.data(), m_context.getBinaryCollection().get(shader.shader_name).getBinary(), len);
410 
411 		shaderMap[shader.shader_name] = data;
412 	}
413 
414 	r = am.ExecuteWithShaderData(m_recipe, &amber_options, shaderMap);
415 	if (!r.IsSuccess()) {
416 		m_context.getTestContext().getLog()
417 			<< tcu::TestLog::Message
418 			<< r.Error()
419 			<< "\n"
420 			<< tcu::TestLog::EndMessage;
421 	}
422 
423 	delete amber_options.config;
424 
425 	return r.IsSuccess() ? tcu::TestStatus::pass("Pass") :tcu::TestStatus::fail("Fail");
426 }
427 
setSpirVAsmBuildOptions(const vk::SpirVAsmBuildOptions & asm_options)428 void AmberTestCase::setSpirVAsmBuildOptions (const vk::SpirVAsmBuildOptions& asm_options)
429 {
430 	m_asm_options = asm_options;
431 }
432 
addRequirement(const std::string & requirement)433 void AmberTestCase::addRequirement (const std::string& requirement)
434 {
435 	if (requirement.find(".") != std::string::npos)
436 		m_required_features.insert(requirement);
437 	else
438 		m_required_extensions.insert(requirement);
439 }
440 
addImageRequirement(vk::VkImageCreateInfo info)441 void AmberTestCase::addImageRequirement (vk::VkImageCreateInfo info)
442 {
443 	m_imageRequirements.push_back(info);
444 }
445 
addBufferRequirement(BufferRequirement req)446 void AmberTestCase::addBufferRequirement (BufferRequirement req)
447 {
448 	m_bufferRequirements.push_back(req);
449 }
450 
451 } // cts_amber
452 } // vkt
453