blob: e9f1fe9bcaf766f3ae04779ef9c99dfe95e9c281 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2015 Google 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 Utility for pre-compiling source programs to SPIR-V
*//*--------------------------------------------------------------------*/
#include "tcuDefs.hpp"
#include "tcuCommandLine.hpp"
#include "tcuPlatform.hpp"
#include "tcuResource.hpp"
#include "tcuTestLog.hpp"
#include "tcuTestHierarchyIterator.hpp"
#include "deUniquePtr.hpp"
#include "vkPrograms.hpp"
#include "vkBinaryRegistry.hpp"
#include "vktTestCase.hpp"
#include "vktTestPackage.hpp"
#include "deUniquePtr.hpp"
#include "deCommandLine.hpp"
#include <iostream>
using std::vector;
using std::string;
using de::UniquePtr;
using de::MovePtr;
namespace vkt
{
tcu::TestPackageRoot* createRoot (tcu::TestContext& testCtx)
{
vector<tcu::TestNode*> children;
children.push_back(new TestPackage(testCtx));
return new tcu::TestPackageRoot(testCtx, children);
}
enum BuildMode
{
BUILDMODE_BUILD = 0,
BUILDMODE_VERIFY,
BUILDMODE_LAST
};
struct BuildStats
{
int numSucceeded;
int numFailed;
BuildStats (void)
: numSucceeded (0)
, numFailed (0)
{
}
};
namespace // anonymous
{
vk::ProgramBinary* compileProgram (const glu::ProgramSources& source, glu::ShaderProgramInfo* buildInfo)
{
return vk::buildProgram(source, vk::PROGRAM_FORMAT_SPIRV, buildInfo);
}
vk::ProgramBinary* compileProgram (const vk::SpirVAsmSource& source, vk::SpirVProgramInfo* buildInfo)
{
return vk::assembleProgram(source, buildInfo);
}
void writeVerboseLogs (const glu::ShaderProgramInfo& buildInfo)
{
for (size_t shaderNdx = 0; shaderNdx < buildInfo.shaders.size(); shaderNdx++)
{
const glu::ShaderInfo& shaderInfo = buildInfo.shaders[shaderNdx];
const char* const shaderName = getShaderTypeName(shaderInfo.type);
tcu::print("%s source:\n---\n%s\n---\n", shaderName, shaderInfo.source.c_str());
tcu::print("%s compile log:\n---\n%s\n---\n", shaderName, shaderInfo.infoLog.c_str());
}
}
void writeVerboseLogs (const vk::SpirVProgramInfo& buildInfo)
{
tcu::print("source:\n---\n%s\n---\n", buildInfo.source->program.str().c_str());
tcu::print("compile log:\n---\n%s\n---\n", buildInfo.infoLog.c_str());
}
template <typename InfoType, typename IteratorType>
void buildProgram (const std::string& casePath,
bool printLogs,
IteratorType iter,
BuildMode mode,
BuildStats* stats,
vk::BinaryRegistryReader* reader,
vk::BinaryRegistryWriter* writer)
{
InfoType buildInfo;
try
{
const vk::ProgramIdentifier progId (casePath, iter.getName());
const UniquePtr<vk::ProgramBinary> binary (compileProgram(iter.getProgram(), &buildInfo));
if (mode == BUILDMODE_BUILD)
writer->storeProgram(progId, *binary);
else
{
DE_ASSERT(mode == BUILDMODE_VERIFY);
const UniquePtr<vk::ProgramBinary> storedBinary (reader->loadProgram(progId));
if (binary->getSize() != storedBinary->getSize())
throw tcu::Exception("Binary size doesn't match");
if (deMemCmp(binary->getBinary(), storedBinary->getBinary(), binary->getSize()))
throw tcu::Exception("Binary contents don't match");
}
tcu::print(" OK: %s\n", iter.getName().c_str());
stats->numSucceeded += 1;
}
catch (const std::exception& e)
{
tcu::print(" ERROR: %s: %s\n", iter.getName().c_str(), e.what());
if (printLogs)
{
writeVerboseLogs(buildInfo);
}
stats->numFailed += 1;
}
}
} // anonymous
BuildStats buildPrograms (tcu::TestContext& testCtx, const std::string& dstPath, BuildMode mode, bool verbose)
{
const UniquePtr<tcu::TestPackageRoot> root (createRoot(testCtx));
tcu::DefaultHierarchyInflater inflater (testCtx);
tcu::TestHierarchyIterator iterator (*root, inflater, testCtx.getCommandLine());
const tcu::DirArchive srcArchive (dstPath.c_str());
UniquePtr<vk::BinaryRegistryWriter> writer (mode == BUILDMODE_BUILD ? new vk::BinaryRegistryWriter(dstPath) : DE_NULL);
UniquePtr<vk::BinaryRegistryReader> reader (mode == BUILDMODE_VERIFY ? new vk::BinaryRegistryReader(srcArchive, "") : DE_NULL);
BuildStats stats;
const bool printLogs = verbose;
while (iterator.getState() != tcu::TestHierarchyIterator::STATE_FINISHED)
{
if (iterator.getState() == tcu::TestHierarchyIterator::STATE_ENTER_NODE &&
tcu::isTestNodeTypeExecutable(iterator.getNode()->getNodeType()))
{
const TestCase* const testCase = dynamic_cast<TestCase*>(iterator.getNode());
const string casePath = iterator.getNodePath();
vk::SourceCollections progs;
tcu::print("%s\n", casePath.c_str());
testCase->initPrograms(progs);
for (vk::GlslSourceCollection::Iterator progIter = progs.glslSources.begin(); progIter != progs.glslSources.end(); ++progIter)
{
buildProgram<glu::ShaderProgramInfo, vk::GlslSourceCollection::Iterator>(casePath, printLogs, progIter, mode, &stats, reader.get(), writer.get());
}
for (vk::SpirVAsmCollection::Iterator progIter = progs.spirvAsmSources.begin(); progIter != progs.spirvAsmSources.end(); ++progIter)
{
buildProgram<vk::SpirVProgramInfo, vk::SpirVAsmCollection::Iterator>(casePath, printLogs, progIter, mode, &stats, reader.get(), writer.get());
}
}
iterator.next();
}
if (mode == BUILDMODE_BUILD)
writer->writeIndex();
return stats;
}
} // vkt
namespace opt
{
DE_DECLARE_COMMAND_LINE_OPT(DstPath, std::string);
DE_DECLARE_COMMAND_LINE_OPT(Mode, vkt::BuildMode);
DE_DECLARE_COMMAND_LINE_OPT(Verbose, bool);
DE_DECLARE_COMMAND_LINE_OPT(Cases, std::string);
} // opt
void registerOptions (de::cmdline::Parser& parser)
{
using de::cmdline::Option;
using de::cmdline::NamedValue;
static const NamedValue<vkt::BuildMode> s_modes[] =
{
{ "build", vkt::BUILDMODE_BUILD },
{ "verify", vkt::BUILDMODE_VERIFY }
};
parser << Option<opt::DstPath> ("d", "dst-path", "Destination path", "out")
<< Option<opt::Mode> ("m", "mode", "Build mode", s_modes, "build")
<< Option<opt::Verbose> ("v", "verbose", "Verbose output")
<< Option<opt::Cases> ("n", "deqp-case", "Case path filter (works as in test binaries)");
}
int main (int argc, const char* argv[])
{
de::cmdline::CommandLine cmdLine;
tcu::CommandLine deqpCmdLine;
{
de::cmdline::Parser parser;
registerOptions(parser);
if (!parser.parse(argc, argv, &cmdLine, std::cerr))
{
parser.help(std::cout);
return -1;
}
}
{
vector<const char*> deqpArgv;
deqpArgv.push_back("unused");
if (cmdLine.hasOption<opt::Cases>())
{
deqpArgv.push_back("--deqp-case");
deqpArgv.push_back(cmdLine.getOption<opt::Cases>().c_str());
}
if (!deqpCmdLine.parse((int)deqpArgv.size(), &deqpArgv[0]))
return -1;
}
try
{
tcu::DirArchive archive (".");
tcu::TestLog log (deqpCmdLine.getLogFileName(), deqpCmdLine.getLogFlags());
tcu::Platform platform;
tcu::TestContext testCtx (platform, archive, log, deqpCmdLine, DE_NULL);
const vkt::BuildStats stats = vkt::buildPrograms(testCtx,
cmdLine.getOption<opt::DstPath>(),
cmdLine.getOption<opt::Mode>(),
cmdLine.getOption<opt::Verbose>());
tcu::print("DONE: %d passed, %d failed\n", stats.numSucceeded, stats.numFailed);
return stats.numFailed == 0 ? 0 : -1;
}
catch (const std::exception& e)
{
tcu::die("%s", e.what());
}
}