blob: 63eb5d39a8777eeef671a95a6b72fafc08d164ae [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2019 Google LLC
* Copyright (c) 2019 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Functional tests using amber
*//*--------------------------------------------------------------------*/
#include <amber/amber.h>
#include "amber/recipe.h"
#include <iostream>
#include "deDefs.hpp"
#include "deUniquePtr.hpp"
#include "deFilePath.hpp"
#include "vkPrograms.hpp"
#include "vktTestCaseUtil.hpp"
#include "tcuTestLog.hpp"
#include "vktAmberTestCase.hpp"
#include "vktAmberHelper.hpp"
#include "tcuResource.hpp"
#include "tcuTestLog.hpp"
#include "vkSpirVProgram.hpp"
#include "vkImageUtil.hpp"
namespace vkt
{
namespace cts_amber
{
AmberTestCase::AmberTestCase(tcu::TestContext &testCtx, const char *name, const char *description,
const std::string &readFilename)
: TestCase(testCtx, name)
, m_recipe(nullptr)
, m_readFilename(readFilename)
{
(void)description;
}
AmberTestCase::~AmberTestCase(void)
{
delete m_recipe;
}
TestInstance *AmberTestCase::createInstance(Context &ctx) const
{
return new AmberTestInstance(ctx, m_recipe, nullptr);
}
static amber::EngineConfig *createEngineConfig(Context &ctx, vk::VkDevice customDevice)
{
vk::VkDevice dev = customDevice != nullptr ? customDevice : ctx.getDevice();
vk::VkQueue queue;
vk::DeviceDriver vk(ctx.getPlatformInterface(), ctx.getInstance(), dev, ctx.getUsedApiVersion(),
ctx.getTestContext().getCommandLine());
vk.getDeviceQueue(dev, ctx.getUniversalQueueFamilyIndex(), 0, &queue);
amber::EngineConfig *vkConfig = GetVulkanConfig(
ctx.getInstance(), ctx.getPhysicalDevice(), dev, &ctx.getDeviceFeatures(), &ctx.getDeviceFeatures2(),
&ctx.getDeviceProperties(), &ctx.getDeviceProperties2(), ctx.getInstanceExtensions(), ctx.getDeviceExtensions(),
ctx.getUniversalQueueFamilyIndex(), queue, ctx.getInstanceProcAddr());
return vkConfig;
}
// Returns true if the given feature is supported by the device.
// Throws an internal error If the feature is not recognized at all.
static bool isFeatureSupported(const vkt::Context &ctx, const std::string &feature)
{
if (feature == "Storage16BitFeatures.storageBuffer16BitAccess")
return ctx.get16BitStorageFeatures().storageBuffer16BitAccess;
if (feature == "Float16Int8Features.shaderFloat16")
return ctx.getShaderFloat16Int8Features().shaderFloat16;
if (feature == "Float16Int8Features.shaderInt8")
return ctx.getShaderFloat16Int8Features().shaderInt8;
if (feature == "Features.shaderFloat64")
return ctx.getDeviceFeatures().shaderFloat64;
if (feature == "Features.shaderInt16")
return ctx.getDeviceFeatures().shaderInt16;
if (feature == "Features.shaderInt64")
return ctx.getDeviceFeatures().shaderInt64;
if (feature == "Features.depthClamp")
return ctx.getDeviceFeatures().depthClamp;
if (feature == "Features.tessellationShader")
return ctx.getDeviceFeatures().tessellationShader;
if (feature == "Features.shaderTessellationAndGeometryPointSize")
return ctx.getDeviceFeatures().shaderTessellationAndGeometryPointSize;
if (feature == "Features.geometryShader")
return ctx.getDeviceFeatures().geometryShader;
if (feature == "Features.fragmentStoresAndAtomics")
return ctx.getDeviceFeatures().fragmentStoresAndAtomics;
if (feature == "Features.vertexPipelineStoresAndAtomics")
return ctx.getDeviceFeatures().vertexPipelineStoresAndAtomics;
if (feature == "Features.fillModeNonSolid")
return ctx.getDeviceFeatures().fillModeNonSolid;
if (feature == "Features.shaderStorageImageMultisample")
return ctx.getDeviceFeatures().shaderStorageImageMultisample;
if (feature == "Features.sampleRateShading")
return ctx.getDeviceFeatures().sampleRateShading;
if (feature == "VariablePointerFeatures.variablePointersStorageBuffer")
return ctx.getVariablePointersFeatures().variablePointersStorageBuffer;
if (feature == "VariablePointerFeatures.variablePointers")
return ctx.getVariablePointersFeatures().variablePointers;
if (feature == "SubgroupSupportedStages.fragment")
return (ctx.getSubgroupProperties().supportedStages & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0;
if (feature == "SubgroupSupportedOperations.vote")
return (ctx.getSubgroupProperties().supportedOperations & vk::VK_SUBGROUP_FEATURE_VOTE_BIT) != 0;
if (feature == "SubgroupSupportedOperations.basic")
return (ctx.getSubgroupProperties().supportedOperations & vk::VK_SUBGROUP_FEATURE_BASIC_BIT) != 0;
if (feature == "SubgroupSupportedOperations.ballot")
return (ctx.getSubgroupProperties().supportedOperations & vk::VK_SUBGROUP_FEATURE_BALLOT_BIT) != 0;
if (feature == "Storage16BitFeatures.storageBuffer16BitAccess")
return ctx.get16BitStorageFeatures().storageBuffer16BitAccess;
if (feature == "Storage8BitFeatures.storageBuffer8BitAccess")
return ctx.get8BitStorageFeatures().storageBuffer8BitAccess;
if (feature == "IndexTypeUint8Features.indexTypeUint8")
return ctx.getIndexTypeUint8Features().indexTypeUint8;
if (feature == "RayTracingPipelineFeaturesKHR.rayTracingPipeline")
return ctx.getRayTracingPipelineFeatures().rayTracingPipeline;
if (feature == "AccelerationStructureFeaturesKHR.accelerationStructure")
return ctx.getAccelerationStructureFeatures().accelerationStructure;
if (feature == "BufferDeviceAddressFeatures.bufferDeviceAddress")
return ctx.getBufferDeviceAddressFeatures().bufferDeviceAddress;
std::string message = std::string("Unexpected feature name: ") + feature;
TCU_THROW(InternalError, message.c_str());
}
// Returns true if the given property is supported by the device.
// Throws an internal error if the property is not recognized at all.
static bool isPropertySupported(const vkt::Context &ctx, const std::string &prop)
{
if (prop == "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat16")
return ctx.getFloatControlsProperties().shaderSignedZeroInfNanPreserveFloat16;
if (prop == "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat32")
return ctx.getFloatControlsProperties().shaderSignedZeroInfNanPreserveFloat32;
if (prop == "FloatControlsProperties.shaderSignedZeroInfNanPreserveFloat64")
return ctx.getFloatControlsProperties().shaderSignedZeroInfNanPreserveFloat64;
if (prop == "FloatControlsProperties.shaderDenormPreserveFloat16")
return ctx.getFloatControlsProperties().shaderDenormPreserveFloat16;
if (prop == "FloatControlsProperties.shaderDenormPreserveFloat32")
return ctx.getFloatControlsProperties().shaderDenormPreserveFloat32;
if (prop == "FloatControlsProperties.shaderDenormPreserveFloat64")
return ctx.getFloatControlsProperties().shaderDenormPreserveFloat64;
if (prop == "FloatControlsProperties.shaderDenormFlushToZeroFloat16")
return ctx.getFloatControlsProperties().shaderDenormFlushToZeroFloat16;
if (prop == "FloatControlsProperties.shaderDenormFlushToZeroFloat32")
return ctx.getFloatControlsProperties().shaderDenormFlushToZeroFloat32;
if (prop == "FloatControlsProperties.shaderDenormFlushToZeroFloat64")
return ctx.getFloatControlsProperties().shaderDenormFlushToZeroFloat64;
if (prop == "FloatControlsProperties.shaderRoundingModeRTEFloat16")
return ctx.getFloatControlsProperties().shaderRoundingModeRTEFloat16;
if (prop == "FloatControlsProperties.shaderRoundingModeRTEFloat32")
return ctx.getFloatControlsProperties().shaderRoundingModeRTEFloat32;
if (prop == "FloatControlsProperties.shaderRoundingModeRTEFloat64")
return ctx.getFloatControlsProperties().shaderRoundingModeRTEFloat64;
if (prop == "FloatControlsProperties.shaderRoundingModeRTZFloat16")
return ctx.getFloatControlsProperties().shaderRoundingModeRTZFloat16;
if (prop == "FloatControlsProperties.shaderRoundingModeRTZFloat32")
return ctx.getFloatControlsProperties().shaderRoundingModeRTZFloat32;
if (prop == "FloatControlsProperties.shaderRoundingModeRTZFloat64")
return ctx.getFloatControlsProperties().shaderRoundingModeRTZFloat64;
std::string message = std::string("Unexpected property name: ") + prop;
TCU_THROW(InternalError, message.c_str());
}
void AmberTestCase::delayedInit(void)
{
// Make sure the input can be parsed before we use it.
if (!parse(m_readFilename))
{
std::string message = "Failed to parse Amber file: " + m_readFilename;
TCU_THROW(InternalError, message.c_str());
}
}
void AmberTestCase::checkSupport(Context &ctx) const
{
// Check for instance and device extensions as declared by the test code.
if (m_required_extensions.size())
{
std::set<std::string> device_extensions(ctx.getDeviceExtensions().begin(), ctx.getDeviceExtensions().end());
std::set<std::string> instance_extensions(ctx.getInstanceExtensions().begin(),
ctx.getInstanceExtensions().end());
std::string missing;
for (std::set<std::string>::iterator iter = m_required_extensions.begin(); iter != m_required_extensions.end();
++iter)
{
const std::string extension = *iter;
if ((device_extensions.count(extension) == 0) && (instance_extensions.count(extension) == 0))
{
missing += " " + extension;
}
}
if (missing.size() > 0)
{
std::string message("Test requires unsupported extensions:");
message += missing;
TCU_THROW(NotSupportedError, message.c_str());
}
}
// Check for required features. Do this after extensions are checked because
// some feature checks are only valid when corresponding extensions are enabled.
if (m_required_features.size())
{
std::string missing;
for (std::set<std::string>::iterator iter = m_required_features.begin(); iter != m_required_features.end();
++iter)
{
const std::string feature = *iter;
if (!isFeatureSupported(ctx, feature))
{
missing += " " + feature;
}
}
if (missing.size() > 0)
{
std::string message("Test requires unsupported features:");
message += missing;
TCU_THROW(NotSupportedError, message.c_str());
}
}
// Check for required properties
if (!m_required_properties.empty())
{
std::string missing;
for (const auto &prop : m_required_properties)
{
if (!isPropertySupported(ctx, prop))
{
missing += " " + prop;
}
}
if (!missing.empty())
{
std::string message("Test requires unsupported properties:");
message += missing;
TCU_THROW(NotSupportedError, message.c_str());
}
}
for (auto req : m_imageRequirements)
checkImageSupport(ctx.getInstanceInterface(), ctx.getPhysicalDevice(), req);
for (auto req : m_bufferRequirements)
{
vk::VkFormatProperties prop;
ctx.getInstanceInterface().getPhysicalDeviceFormatProperties(ctx.getPhysicalDevice(), req.m_format, &prop);
if ((req.m_featureFlags & prop.bufferFeatures) != req.m_featureFlags)
{
TCU_THROW(NotSupportedError, "Buffer format doesn't support required feature flags");
}
}
if (m_checkSupportCallback)
(m_checkSupportCallback)(ctx, m_name);
}
class Delegate : public amber::Delegate
{
public:
Delegate(tcu::TestContext &testCtx);
amber::Result LoadBufferData(const std::string file_name, amber::BufferDataFileType file_type,
amber::BufferInfo *buffer) const override;
amber::Result LoadFile(const std::string file_name, std::vector<char> *buffer) const override;
void Log(const std::string & /*message*/) override
{
DE_FATAL("amber::Delegate::Log unimplemented");
}
bool LogGraphicsCalls(void) const override
{
return m_logGraphicsCalls;
}
void SetLogGraphicsCalls(bool log_graphics_calls)
{
m_logGraphicsCalls = log_graphics_calls;
}
bool LogExecuteCalls(void) const override
{
return m_logExecuteCalls;
}
void SetLogExecuteCalls(bool log_execute_calls)
{
m_logExecuteCalls = log_execute_calls;
}
bool LogGraphicsCallsTime(void) const override
{
return m_logGraphicsCallsTime;
}
void SetLogGraphicsCallsTime(bool log_graphics_calls_time)
{
m_logGraphicsCallsTime = log_graphics_calls_time;
}
uint64_t GetTimestampNs(void) const override
{
DE_FATAL("amber::Delegate::GetTimestampNs unimplemented");
return 0;
}
void SetScriptPath(std::string path)
{
m_path = path;
}
private:
tcu::TestContext &m_testCtx;
std::string m_path;
bool m_logGraphicsCalls;
bool m_logGraphicsCallsTime;
bool m_logExecuteCalls;
};
Delegate::Delegate(tcu::TestContext &testCtx)
: m_testCtx(testCtx)
, m_path("")
, m_logGraphicsCalls(false)
, m_logGraphicsCallsTime(false)
, m_logExecuteCalls(false)
{
}
amber::Result Delegate::LoadBufferData(const std::string file_name, amber::BufferDataFileType file_type,
amber::BufferInfo *buffer) const
{
const tcu::Archive &archive = m_testCtx.getArchive();
const de::FilePath filePath = de::FilePath(m_path).join(file_name);
de::UniquePtr<tcu::Resource> file(archive.getResource(filePath.getPath()));
int numBytes = file->getSize();
std::vector<uint8_t> bytes(numBytes);
if (file_type == amber::BufferDataFileType::kPng)
return amber::Result("Amber PNG loading unimplemented");
file->read(bytes.data(), numBytes);
if (bytes.empty())
return amber::Result("Failed to load buffer data " + file_name);
for (uint8_t byte : bytes)
{
amber::Value value;
value.SetIntValue(static_cast<uint64_t>(byte));
buffer->values.push_back(value);
}
buffer->width = 1;
buffer->height = 1;
return {};
}
amber::Result Delegate::LoadFile(const std::string file_name, std::vector<char> *buffer) const
{
if (!buffer)
{
return amber::Result("Buffer pointer is null.");
}
const tcu::Archive &archive = m_testCtx.getArchive();
const de::FilePath filePath = de::FilePath(m_path).join(file_name);
de::UniquePtr<tcu::Resource> file(archive.getResource(filePath.getPath()));
int numBytes = file->getSize();
std::vector<uint8_t> bytes(numBytes);
file->read(bytes.data(), numBytes);
if (bytes.empty())
return amber::Result("Failed to load buffer data " + file_name);
// Convert uint8_t vector to char vector
buffer->assign(bytes.begin(), bytes.end());
return {};
}
bool AmberTestCase::parse(const std::string &readFilename)
{
std::string script = ShaderSourceProvider::getSource(m_testCtx.getArchive(), readFilename.c_str());
if (script.empty())
return false;
Delegate delegate(m_testCtx);
delegate.SetScriptPath(de::FilePath(readFilename).getDirName());
m_recipe = new amber::Recipe();
amber::Amber am(&delegate);
amber::Result r = am.Parse(script, m_recipe);
m_recipe->SetFenceTimeout(~0u); // infinity of miliseconds
if (!r.IsSuccess())
{
getTestContext().getLog() << tcu::TestLog::Message << "Failed to parse Amber test " << readFilename << ": "
<< r.Error() << "\n"
<< tcu::TestLog::EndMessage;
// TODO(dneto): Enhance Amber to not require this.
m_recipe->SetImpl(nullptr);
return false;
}
return true;
}
void AmberTestCase::initPrograms(vk::SourceCollections &programCollection) const
{
std::vector<amber::ShaderInfo> shaders = m_recipe->GetShaderInfo();
for (size_t i = 0; i < shaders.size(); ++i)
{
const amber::ShaderInfo &shader = shaders[i];
vk::SpirvVersion spirvVersion = vk::SPIRV_VERSION_1_0;
DE_STATIC_ASSERT(vk::SPIRV_VERSION_LAST == vk::SPIRV_VERSION_1_6 + 1);
if (shader.target_env == "spv1.6")
spirvVersion = vk::SPIRV_VERSION_1_6;
else if (shader.target_env == "spv1.5")
spirvVersion = vk::SPIRV_VERSION_1_5;
else if (shader.target_env == "spv1.4")
spirvVersion = vk::SPIRV_VERSION_1_4;
else if (shader.target_env == "spv1.3")
spirvVersion = vk::SPIRV_VERSION_1_3;
else if (shader.target_env == "spv1.2")
spirvVersion = vk::SPIRV_VERSION_1_2;
else if (shader.target_env == "spv1.1")
spirvVersion = vk::SPIRV_VERSION_1_1;
/* Hex encoded shaders do not need to be pre-compiled */
if (shader.format == amber::kShaderFormatSpirvHex)
continue;
if (shader.format == amber::kShaderFormatSpirvAsm)
{
programCollection.spirvAsmSources.add(shader.shader_name) << shader.shader_source << m_asm_options;
}
else if (shader.format == amber::kShaderFormatGlsl)
{
bool allowSpirv14 = (spirvVersion == vk::SPIRV_VERSION_1_4);
switch (shader.type)
{
case amber::kShaderTypeCompute:
programCollection.glslSources.add(shader.shader_name)
<< glu::ComputeSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, allowSpirv14);
break;
case amber::kShaderTypeGeometry:
programCollection.glslSources.add(shader.shader_name)
<< glu::GeometrySource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, allowSpirv14);
break;
case amber::kShaderTypeFragment:
programCollection.glslSources.add(shader.shader_name)
<< glu::FragmentSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, allowSpirv14);
break;
case amber::kShaderTypeVertex:
programCollection.glslSources.add(shader.shader_name)
<< glu::VertexSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, allowSpirv14);
break;
case amber::kShaderTypeTessellationControl:
programCollection.glslSources.add(shader.shader_name)
<< glu::TessellationControlSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, allowSpirv14);
break;
case amber::kShaderTypeTessellationEvaluation:
programCollection.glslSources.add(shader.shader_name)
<< glu::TessellationEvaluationSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, allowSpirv14);
break;
case amber::kShaderTypeRayGeneration:
programCollection.glslSources.add(shader.shader_name)
<< glu::RaygenSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, true);
break;
case amber::kShaderTypeAnyHit:
programCollection.glslSources.add(shader.shader_name)
<< glu::AnyHitSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, true);
break;
case amber::kShaderTypeClosestHit:
programCollection.glslSources.add(shader.shader_name)
<< glu::ClosestHitSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, true);
break;
case amber::kShaderTypeMiss:
programCollection.glslSources.add(shader.shader_name)
<< glu::MissSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, true);
break;
case amber::kShaderTypeIntersection:
programCollection.glslSources.add(shader.shader_name)
<< glu::IntersectionSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, true);
break;
case amber::kShaderTypeCall:
programCollection.glslSources.add(shader.shader_name)
<< glu::CallableSource(shader.shader_source)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, spirvVersion, 0u, true);
break;
case amber::kShaderTypeMulti:
DE_ASSERT(false && "Multi shaders not supported");
break;
default:
DE_ASSERT(false && "Unknown shader");
break;
}
}
else
{
DE_ASSERT(false && "Shader format not supported");
}
}
}
tcu::TestStatus AmberTestInstance::iterate(void)
{
amber::Amber am(nullptr);
amber::Options amber_options;
amber::ShaderMap shaderMap;
amber::Result r;
amber_options.engine = amber::kEngineTypeVulkan;
amber_options.config = createEngineConfig(m_context, m_customDevice);
amber_options.execution_type = amber::ExecutionType::kExecute;
// Amber should not execute any graphic related shaders when using --deqp-compute-only=enable flag
if (m_context.getTestContext().getCommandLine().isComputeOnly())
{
std::vector<amber::ShaderInfo> shaders_info = m_recipe->GetShaderInfo();
for (amber::ShaderInfo info : shaders_info)
{
if (info.type != amber::ShaderType::kShaderTypeCompute)
{
TCU_THROW(NotSupportedError, "Non compute shaders are not allow when using --deqp-compute-only=enable");
}
}
}
// Check for extensions as declared by the Amber script itself. Throw an internal
// error if that's more demanding.
r = am.AreAllRequirementsSupported(m_recipe, &amber_options);
if (!r.IsSuccess())
{
// dEQP does not to rely on external code to determine whether
// a test is supported. So throw an internal error here instead
// of a NotSupportedError. If an Amber test is not supported, then
// you must override this method and throw a NotSupported exception
// before reach here.
TCU_THROW(InternalError, r.Error().c_str());
}
std::vector<amber::ShaderInfo> shaders = m_recipe->GetShaderInfo();
for (size_t i = 0; i < shaders.size(); ++i)
{
const amber::ShaderInfo &shader = shaders[i];
if (!m_context.getBinaryCollection().contains(shader.shader_name))
continue;
const vk::ProgramBinary &prog = m_context.getBinaryCollection().get(shader.shader_name);
prog.setUsed();
size_t len = prog.getSize();
/* This is a compiled spir-v binary which must be made of 4-byte words. We
* are moving into a word sized vector so divide by 4
*/
std::vector<uint32_t> data;
data.resize(len >> 2);
deMemcpy(data.data(), prog.getBinary(), len);
shaderMap[shader.shader_name] = data;
}
r = am.ExecuteWithShaderData(m_recipe, &amber_options, shaderMap);
if (!r.IsSuccess())
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << r.Error() << "\n" << tcu::TestLog::EndMessage;
}
delete amber_options.config;
return r.IsSuccess() ? tcu::TestStatus::pass("Pass") : tcu::TestStatus::fail("Fail");
}
void AmberTestCase::setSpirVAsmBuildOptions(const vk::SpirVAsmBuildOptions &asm_options)
{
m_asm_options = asm_options;
}
void AmberTestCase::addRequirement(const std::string &requirement)
{
if (requirement.find(".") != std::string::npos)
m_required_features.insert(requirement);
else
m_required_extensions.insert(requirement);
}
void AmberTestCase::addPropertyRequirement(const std::string &requirement)
{
m_required_properties.insert(requirement);
}
void AmberTestCase::addImageRequirement(vk::VkImageCreateInfo info)
{
m_imageRequirements.push_back(info);
}
void AmberTestCase::addBufferRequirement(BufferRequirement req)
{
m_bufferRequirements.push_back(req);
}
bool AmberTestCase::validateRequirements()
{
if (!parse(m_readFilename))
{
std::string message = "Failed to parse Amber file: " + m_readFilename;
m_testCtx.getLog() << tcu::TestLog::Message << message << tcu::TestLog::EndMessage;
return false;
}
// Check if the list of required CTS features and extensions matches the
// one in the recipe. Throw InternalError if they do not match.
const auto &deviceExtensions = m_recipe->GetRequiredInstanceExtensions();
const auto &instanceExtensions = m_recipe->GetRequiredDeviceExtensions();
auto requiredFeatures = m_recipe->GetRequiredFeatures();
auto requiredProperties = m_recipe->GetRequiredProperties();
for (auto &req : requiredFeatures)
{
if (req.find(".") == std::string::npos)
req = "Features." + req;
}
std::set<std::string> allRequirements;
allRequirements.insert(begin(deviceExtensions), end(deviceExtensions));
allRequirements.insert(begin(instanceExtensions), end(instanceExtensions));
allRequirements.insert(begin(requiredFeatures), end(requiredFeatures));
allRequirements.insert(begin(requiredProperties), end(requiredProperties));
std::set<std::string> ctsRequirements = m_required_features;
ctsRequirements.insert(begin(m_required_properties), end(m_required_properties));
ctsRequirements.insert(begin(m_required_extensions), end(m_required_extensions));
if (allRequirements != ctsRequirements)
{
auto &log = m_testCtx.getLog();
log << tcu::TestLog::Message << "ERROR: CTS and Amber test requirement mismatch." << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Amber filename: " << m_readFilename << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "CTS requirements:" << tcu::TestLog::EndMessage;
for (const auto &ctsReq : ctsRequirements)
log << tcu::TestLog::Message << " " << ctsReq << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Amber requirements:" << tcu::TestLog::EndMessage;
for (const auto &amberReq : allRequirements)
log << tcu::TestLog::Message << " " << amberReq << tcu::TestLog::EndMessage;
// Repeat message for cerr so it's visible in console log.
std::cerr << "ERROR: CTS and Amber test requirement mismatch.\n";
std::cerr << "Amber filename: " << m_readFilename << "\n";
std::cerr << "CTS requirements:\n";
for (const auto &ctsReq : ctsRequirements)
std::cerr << " " << ctsReq << "\n";
std::cerr << "Amber requirements:\n";
for (const auto &amberReq : allRequirements)
std::cerr << " " << amberReq << "\n";
return false;
}
return true;
}
} // namespace cts_amber
} // namespace vkt