blob: af6570418027ce2b6c926ee39a19d85b4d7b10e0 [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan CTS Framework
* --------------------
*
* Copyright (c) 2021 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.
*
*-------------------------------------------------------------------------*/
#include <iostream>
#include <fstream>
#include <sstream>
#include <json/json.h>
#include "deCommandLine.hpp"
#include "deDirectoryIterator.hpp"
#include "tcuCommandLine.hpp"
#include "tcuPlatform.hpp"
#include "tcuTestContext.hpp"
#include "tcuResource.hpp"
#include "tcuTestLog.hpp"
#include "vkPlatform.hpp"
#include "vktTestCase.hpp"
#include "vksStructsVKSC.hpp"
#include "vksCacheBuilder.hpp"
namespace opt
{
DE_DECLARE_COMMAND_LINE_OPT(CompilerDataPath, std::string);
DE_DECLARE_COMMAND_LINE_OPT(CompilerOutputFile, std::string);
DE_DECLARE_COMMAND_LINE_OPT(LogFile, std::string);
DE_DECLARE_COMMAND_LINE_OPT(FilePrefix, std::string);
void registerOptions(de::cmdline::Parser &parser)
{
using de::cmdline::NamedValue;
using de::cmdline::Option;
parser << Option<CompilerDataPath>("p", "path", "Offline pipeline data directory", "");
parser << Option<CompilerOutputFile>("o", "out", "Output file with pipeline cache", "");
parser << Option<LogFile>("l", "log", "Log file", "dummy.log");
parser << Option<FilePrefix>("x", "prefix", "Prefix for input files", "");
}
} // namespace opt
enum PipelineType
{
PT_UNDEFINED_PIPELINE = 0,
PT_GRAPHICS_PIPELINE,
PT_COMPUTE_PIPELINE,
};
void importFilesForExternalCompiler(vksc_server::VulkanPipelineCacheInput &input, const std::string &path,
const std::string &filePrefix)
{
vksc_server::json::Context context;
for (de::DirectoryIterator iter(path); iter.hasItem(); iter.next())
{
const de::FilePath filePath = iter.getItem();
if (filePath.getType() != de::FilePath::TYPE_FILE)
continue;
if (filePath.getFileExtension() != "json")
continue;
if (!filePrefix.empty() && filePath.getBaseName().find(filePrefix) != 0)
continue;
std::string fileContents;
{
std::ifstream file(filePath.getPath());
std::stringstream buffer;
buffer << file.rdbuf();
fileContents = buffer.str();
}
Json::Value jsonRoot;
std::string errors;
bool parsingSuccessful =
context.reader->parse(fileContents.c_str(), fileContents.c_str() + fileContents.size(), &jsonRoot, &errors);
if (!parsingSuccessful)
TCU_THROW(InternalError,
(std::string("JSON parsing error. File ") + filePath.getPath() + " Error : " + errors).c_str());
// decide what pipeline type will be created later
PipelineType pipelineType = PT_UNDEFINED_PIPELINE;
if (jsonRoot.isMember("GraphicsPipelineState"))
pipelineType = PT_GRAPHICS_PIPELINE;
else if (jsonRoot.isMember("ComputePipelineState"))
pipelineType = PT_COMPUTE_PIPELINE;
if (pipelineType == PT_UNDEFINED_PIPELINE)
TCU_THROW(InternalError, (std::string("JSON - unknown pipeline. File ") + filePath.getPath()).c_str());
const Json::Value &jsonGraphicsPipelineState = jsonRoot["GraphicsPipelineState"];
const Json::Value &jsonComputePipelineState = jsonRoot["ComputePipelineState"];
const Json::Value &jsonPipelineState =
(pipelineType == PT_GRAPHICS_PIPELINE) ? jsonGraphicsPipelineState : jsonComputePipelineState;
vksc_server::VulkanJsonPipelineDescription pipelineDescription;
{
const Json::Value &jsonSamplerYcbcrConversions = jsonPipelineState["YcbcrSamplers"];
if (!jsonSamplerYcbcrConversions.isNull())
{
for (Json::ArrayIndex i = 0; i < jsonSamplerYcbcrConversions.size(); ++i)
{
const Json::Value::Members membersNames = jsonSamplerYcbcrConversions[i].getMemberNames();
const Json::Value &value = jsonSamplerYcbcrConversions[i][membersNames[0]];
uint64_t index;
std::istringstream(membersNames[0]) >> index;
input.samplerYcbcrConversions[vk::VkSamplerYcbcrConversion(reinterpret_cast<void *>(index))] =
std::string(fileContents.begin() + value.getOffsetStart(),
fileContents.begin() + value.getOffsetLimit());
}
}
const Json::Value &jsonSamplers = jsonPipelineState["ImmutableSamplers"];
if (!jsonSamplers.isNull())
{
for (Json::ArrayIndex i = 0; i < jsonSamplers.size(); ++i)
{
const Json::Value::Members membersNames = jsonSamplers[i].getMemberNames();
const Json::Value &value = jsonSamplers[i][membersNames[0]];
uint64_t index;
std::istringstream(membersNames[0]) >> index;
input.samplers[vk::VkSampler(reinterpret_cast<void *>(index))] = std::string(
fileContents.begin() + value.getOffsetStart(), fileContents.begin() + value.getOffsetLimit());
}
}
const Json::Value &jsonDescriptorSetLayouts = jsonPipelineState["DescriptorSetLayouts"];
if (!jsonDescriptorSetLayouts.isNull())
{
for (Json::ArrayIndex i = 0; i < jsonDescriptorSetLayouts.size(); ++i)
{
const Json::Value::Members membersNames = jsonDescriptorSetLayouts[i].getMemberNames();
const Json::Value &value = jsonDescriptorSetLayouts[i][membersNames[0]];
uint64_t index;
std::istringstream(membersNames[0]) >> index;
input.descriptorSetLayouts[vk::VkDescriptorSetLayout(reinterpret_cast<void *>(index))] =
std::string(fileContents.begin() + value.getOffsetStart(),
fileContents.begin() + value.getOffsetLimit());
}
}
uint64_t pipelineLayoutHandle = 0u;
uint64_t renderPassHandle = 0u;
std::map<std::string, uint64_t> stages;
const Json::Value &jsonComputePipeline = jsonPipelineState["ComputePipeline"];
if (!jsonComputePipeline.isNull())
{
pipelineDescription.pipelineContents =
std::string(fileContents.begin() + jsonComputePipeline.getOffsetStart(),
fileContents.begin() + jsonComputePipeline.getOffsetLimit());
pipelineLayoutHandle = jsonComputePipeline["layout"].asUInt64();
const Json::Value &jsonStage = jsonComputePipeline["stage"];
stages[jsonStage["stage"].asString()] = jsonStage["module"].asUInt64();
}
const Json::Value &jsonGraphicsPipeline = jsonPipelineState["GraphicsPipeline"];
if (!jsonGraphicsPipeline.isNull())
{
pipelineDescription.pipelineContents =
std::string(fileContents.begin() + jsonGraphicsPipeline.getOffsetStart(),
fileContents.begin() + jsonGraphicsPipeline.getOffsetLimit());
pipelineLayoutHandle = jsonGraphicsPipeline["layout"].asUInt64();
renderPassHandle = jsonGraphicsPipeline["renderPass"].asUInt64();
const Json::Value &jsonStages = jsonGraphicsPipeline["pStages"];
for (Json::ArrayIndex i = 0; i < jsonStages.size(); ++i)
stages[jsonStages[i]["stage"].asString()] = jsonStages[i]["module"].asUInt64();
}
const Json::Value &jsonPipelineLayout = jsonPipelineState["PipelineLayout"];
if (!jsonPipelineLayout.isNull() && pipelineLayoutHandle != 0u)
{
input.pipelineLayouts[vk::VkPipelineLayout(reinterpret_cast<void *>(pipelineLayoutHandle))] =
std::string(fileContents.begin() + jsonPipelineLayout.getOffsetStart(),
fileContents.begin() + jsonPipelineLayout.getOffsetLimit());
}
const Json::Value &jsonRenderPass = jsonPipelineState["Renderpass"];
if (!jsonRenderPass.isNull() && renderPassHandle != 0u)
{
input.renderPasses[vk::VkRenderPass(reinterpret_cast<void *>(renderPassHandle))] =
std::string(fileContents.begin() + jsonRenderPass.getOffsetStart(),
fileContents.begin() + jsonRenderPass.getOffsetLimit());
}
const Json::Value &jsonRenderPass2 = jsonPipelineState["Renderpass2"];
if (!jsonRenderPass2.isNull() && renderPassHandle != 0u)
{
input.renderPasses[vk::VkRenderPass(reinterpret_cast<void *>(renderPassHandle))] =
std::string(fileContents.begin() + jsonRenderPass.getOffsetStart(),
fileContents.begin() + jsonRenderPass.getOffsetLimit());
}
const Json::Value &jsonShaderFileNames = jsonPipelineState["ShaderFileNames"];
if (!jsonShaderFileNames.isNull())
{
for (Json::ArrayIndex i = 0; i < jsonShaderFileNames.size(); ++i)
{
std::string stageName = jsonShaderFileNames[i]["stage"].asString();
std::string fileName = jsonShaderFileNames[i]["filename"].asString();
auto it = stages.find(stageName);
if (it == end(stages))
TCU_THROW(InternalError,
(std::string("JSON - missing shader stage. File ") + filePath.getPath()).c_str());
de::FilePath shaderPath(path);
shaderPath.join(de::FilePath(fileName));
std::ifstream iFile(shaderPath.getPath(), std::ios::in | std::ios::binary);
if (!iFile)
TCU_THROW(InternalError, (std::string("JSON - missing shader file ") + fileName + ". File " +
filePath.getPath())
.c_str());
auto fileBegin = iFile.tellg();
iFile.seekg(0, std::ios::end);
auto fileEnd = iFile.tellg();
iFile.seekg(0, std::ios::beg);
std::size_t fileSize = static_cast<std::size_t>(fileEnd - fileBegin);
std::vector<uint8_t> shaderData(fileSize);
iFile.read(reinterpret_cast<char *>(shaderData.data()), fileSize);
if (iFile.fail())
TCU_THROW(InternalError, (std::string("JSON - error reading shader file ") + fileName +
". File " + filePath.getPath())
.c_str());
vk::VkShaderModuleCreateInfo smCI{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
vk::VkShaderModuleCreateFlags(0u), // VkShaderModuleCreateFlags flags;
fileSize, // uintptr_t codeSize;
reinterpret_cast<uint32_t *>(shaderData.data()) // const uint32_t* pCode;
};
input.shaderModules[vk::VkShaderModule(reinterpret_cast<void *>(it->second))] =
vksc_server::json::writeJSON_VkShaderModuleCreateInfo(smCI);
}
}
const Json::Value &jsonPhysicalDeviceFeatures = jsonPipelineState["PhysicalDeviceFeatures"];
if (!jsonPhysicalDeviceFeatures.isNull())
{
pipelineDescription.deviceFeatures =
std::string(fileContents.begin() + jsonPhysicalDeviceFeatures.getOffsetStart(),
fileContents.begin() + jsonPhysicalDeviceFeatures.getOffsetLimit());
}
}
const Json::Value &jsonEnabledExtensions = jsonRoot["EnabledExtensions"];
if (!jsonEnabledExtensions.isNull())
{
for (Json::ArrayIndex i = 0; i < jsonEnabledExtensions.size(); ++i)
pipelineDescription.deviceExtensions.push_back(jsonEnabledExtensions[i].asString());
}
const Json::Value &jsonPipelineUUID = jsonRoot["PipelineUUID"];
if (!jsonPipelineUUID.isNull())
{
pipelineDescription.id.sType = VK_STRUCTURE_TYPE_PIPELINE_OFFLINE_CREATE_INFO;
pipelineDescription.id.pNext = DE_NULL;
for (Json::ArrayIndex i = 0; i < jsonPipelineUUID.size(); ++i)
pipelineDescription.id.pipelineIdentifier[i] = uint8_t(jsonPipelineUUID[i].asUInt());
pipelineDescription.id.matchControl = VK_PIPELINE_MATCH_CONTROL_APPLICATION_UUID_EXACT_MATCH;
pipelineDescription.id.poolEntrySize = 0u;
}
input.pipelines.push_back(pipelineDescription);
}
}
tcu::Platform *createPlatform(void);
int main(int argc, char **argv)
{
de::cmdline::CommandLine cmdLine;
// Parse command line.
{
de::cmdline::Parser parser;
opt::registerOptions(parser);
if (!parser.parse(argc, argv, &cmdLine, std::cerr))
{
parser.help(std::cout);
return EXIT_FAILURE;
}
}
try
{
// load JSON files into VulkanPipelineCacheInput
vksc_server::VulkanPipelineCacheInput input;
importFilesForExternalCompiler(input, cmdLine.getOption<opt::CompilerDataPath>(),
cmdLine.getOption<opt::FilePrefix>());
// create Vulkan instance
tcu::CommandLine cmdLineDummy{"--deqp-vk-device-id=0"};
tcu::DirArchive archive{""};
tcu::TestLog log{cmdLine.getOption<opt::LogFile>().c_str()};
log.supressLogging(true);
de::SharedPtr<tcu::Platform> platform{createPlatform()};
#ifdef DE_PLATFORM_USE_LIBRARY_TYPE
de::SharedPtr<vk::Library> library{
platform->getVulkanPlatform().createLibrary(vk::Platform::LIBRARY_TYPE_VULKAN, DE_NULL)};
#else
de::SharedPtr<vk::Library> library{platform->getVulkanPlatform().createLibrary(DE_NULL)};
#endif
tcu::TestContext tcx{*platform, archive, log, cmdLineDummy, nullptr};
vk::BinaryCollection collection{};
vkt::Context context(tcx, library->getPlatformInterface(), collection,
de::SharedPtr<vk::ResourceInterface>{new vk::ResourceInterfaceStandard{tcx}});
// create pipeline cache
std::vector<uint8_t> binary = vksc_server::buildPipelineCache(
input, library->getPlatformInterface(), context.getInstance(), context.getInstanceInterface(),
context.getPhysicalDevice(), context.getUniversalQueueFamilyIndex());
// write pipeline cache to output file
std::ofstream oFile(cmdLine.getOption<opt::CompilerOutputFile>().c_str(), std::ios::out | std::ios::binary);
if (!oFile)
TCU_THROW(InternalError,
(std::string("Cannot create file : ") + cmdLine.getOption<opt::CompilerOutputFile>().c_str()));
oFile.write(reinterpret_cast<char *>(binary.data()), binary.size());
}
catch (const std::exception &e)
{
std::cout << e.what() << std::endl;
}
return EXIT_SUCCESS;
}