/*-------------------------------------------------------------------------
* drawElements Quality Program Test Executor
* ------------------------------------------
*
* Copyright 2014 The Android Open Source Project
*
* 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 Extract shader programs from log.
*//*--------------------------------------------------------------------*/
#include "xeTestLogParser.hpp"
#include "xeTestResultParser.hpp"
#include "deFilePath.hpp"
#include "deStringUtil.hpp"
#include "deString.h"
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <stdexcept>
using std::vector;
using std::string;
using std::set;
using std::map;
struct CommandLine
{
CommandLine (void)
{
}
string filename;
string dstPath;
};
static const char* getShaderTypeSuffix (const xe::ri::Shader::ShaderType shaderType)
{
switch (shaderType)
{
case xe::ri::Shader::SHADERTYPE_VERTEX: return "vert";
case xe::ri::Shader::SHADERTYPE_FRAGMENT: return "frag";
case xe::ri::Shader::SHADERTYPE_GEOMETRY: return "geom";
case xe::ri::Shader::SHADERTYPE_TESS_CONTROL: return "tesc";
case xe::ri::Shader::SHADERTYPE_TESS_EVALUATION: return "tese";
case xe::ri::Shader::SHADERTYPE_COMPUTE: return "comp";
default:
throw xe::Error("Invalid shader type");
}
}
static void writeShaderProgram (const CommandLine& cmdLine, const std::string& casePath, const xe::ri::ShaderProgram& shaderProgram, int programNdx)
{
const string basePath = string(de::FilePath::join(cmdLine.dstPath, casePath).getPath()) + "." + de::toString(programNdx);
for (int shaderNdx = 0; shaderNdx < shaderProgram.shaders.getNumItems(); shaderNdx++)
{
const xe::ri::Shader& shader = dynamic_cast<const xe::ri::Shader&>(shaderProgram.shaders.getItem(shaderNdx));
const string shaderPath = basePath + "." + getShaderTypeSuffix(shader.shaderType);
if (de::FilePath(shaderPath).exists())
throw xe::Error("File '" + shaderPath + "' exists already");
{
std::ofstream out(shaderPath.c_str(), std::ifstream::binary|std::ifstream::out);
if (!out.good())
throw xe::Error("Failed to open '" + shaderPath + "'");
out.write(shader.source.source.c_str(), shader.source.source.size());
}
}
}
struct StackEntry
{
const xe::ri::List* list;
int curNdx;
explicit StackEntry (const xe::ri::List* list_) : list(list_), curNdx(0) {}
};
static void extractShaderPrograms (const CommandLine& cmdLine, const std::string& casePath, const xe::TestCaseResult& result)
{
vector<StackEntry> itemListStack;
int programNdx = 0;
itemListStack.push_back(StackEntry(&result.resultItems));
while (!itemListStack.empty())
{
StackEntry& curEntry = itemListStack.back();
if (curEntry.curNdx < curEntry.list->getNumItems())
{
const xe::ri::Item& curItem = curEntry.list->getItem(curEntry.curNdx);
curEntry.curNdx += 1;
if (curItem.getType() == xe::ri::TYPE_SHADERPROGRAM)
{
writeShaderProgram(cmdLine, casePath, static_cast<const xe::ri::ShaderProgram&>(curItem), programNdx);
programNdx += 1;
}
else if (curItem.getType() == xe::ri::TYPE_SECTION)
itemListStack.push_back(StackEntry(&static_cast<const xe::ri::Section&>(curItem).items));
}
else
itemListStack.pop_back();
}
if (programNdx == 0)
std::cout << "WARNING: no shader programs found in '" << casePath << "'\n";
}
class ShaderProgramExtractHandler : public xe::TestLogHandler
{
public:
ShaderProgramExtractHandler (const CommandLine& cmdLine)
: m_cmdLine(cmdLine)
{
}
void setSessionInfo (const xe::SessionInfo&)
{
// Ignored.
}
xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
{
return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
}
void testCaseResultUpdated (const xe::TestCaseResultPtr&)
{
// Ignored.
}
void testCaseResultComplete (const xe::TestCaseResultPtr& caseData)
{
if (caseData->getDataSize() > 0)
{
xe::TestCaseResult fullResult;
xe::TestResultParser::ParseResult parseResult;
m_testResultParser.init(&fullResult);
parseResult = m_testResultParser.parse(caseData->getData(), caseData->getDataSize());
DE_UNREF(parseResult);
extractShaderPrograms(m_cmdLine, caseData->getTestCasePath(), fullResult);
}
}
private:
const CommandLine& m_cmdLine;
xe::TestResultParser m_testResultParser;
};
static void extractShaderProgramsFromLogFile (const CommandLine& cmdLine)
{
std::ifstream in (cmdLine.filename.c_str(), std::ifstream::binary|std::ifstream::in);
ShaderProgramExtractHandler resultHandler (cmdLine);
xe::TestLogParser parser (&resultHandler);
deUint8 buf [1024];
int numRead = 0;
if (!in.good())
throw std::runtime_error(string("Failed to open '") + cmdLine.filename + "'");
for (;;)
{
in.read((char*)&buf[0], DE_LENGTH_OF_ARRAY(buf));
numRead = (int)in.gcount();
if (numRead <= 0)
break;
parser.parse(&buf[0], numRead);
}
in.close();
}
static void printHelp (const char* binName)
{
printf("%s: [filename] [dst path (optional)]\n", binName);
}
static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
{
for (int argNdx = 1; argNdx < argc; argNdx++)
{
const char* arg = argv[argNdx];
if (!deStringBeginsWith(arg, "--"))
{
if (cmdLine.filename.empty())
cmdLine.filename = arg;
else if (cmdLine.dstPath.empty())
cmdLine.dstPath = arg;
else
return false;
}
else
return false;
}
if (cmdLine.filename.empty())
return false;
return true;
}
int main (int argc, const char* const* argv)
{
try
{
CommandLine cmdLine;
if (!parseCommandLine(cmdLine, argc, argv))
{
printHelp(argv[0]);
return -1;
}
extractShaderProgramsFromLogFile(cmdLine);
}
catch (const std::exception& e)
{
printf("FATAL ERROR: %s\n", e.what());
return -1;
}
return 0;
}