1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Test Executor
3 * ------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Extract shader programs from log.
22 *//*--------------------------------------------------------------------*/
23
24#include "xeTestLogParser.hpp"
25#include "xeTestResultParser.hpp"
26#include "deFilePath.hpp"
27#include "deStringUtil.hpp"
28#include "deString.h"
29
30#include <vector>
31#include <string>
32#include <cstdio>
33#include <cstdlib>
34#include <fstream>
35#include <iostream>
36#include <stdexcept>
37
38using std::vector;
39using std::string;
40using std::set;
41using std::map;
42
43struct CommandLine
44{
45	CommandLine (void)
46	{
47	}
48
49	string		filename;
50	string		dstPath;
51};
52
53static const char* getShaderTypeSuffix (const xe::ri::Shader::ShaderType shaderType)
54{
55	switch (shaderType)
56	{
57		case xe::ri::Shader::SHADERTYPE_VERTEX:				return "vert";
58		case xe::ri::Shader::SHADERTYPE_FRAGMENT:			return "frag";
59		case xe::ri::Shader::SHADERTYPE_GEOMETRY:			return "geom";
60		case xe::ri::Shader::SHADERTYPE_TESS_CONTROL:		return "tesc";
61		case xe::ri::Shader::SHADERTYPE_TESS_EVALUATION:	return "tese";
62		case xe::ri::Shader::SHADERTYPE_COMPUTE:			return "comp";
63		case xe::ri::Shader::SHADERTYPE_RAYGEN:				return "rgen";
64		case xe::ri::Shader::SHADERTYPE_ANY_HIT:			return "ahit";
65		case xe::ri::Shader::SHADERTYPE_CLOSEST_HIT:		return "chit";
66		case xe::ri::Shader::SHADERTYPE_MISS:				return "miss";
67		case xe::ri::Shader::SHADERTYPE_INTERSECTION:		return "sect";
68		case xe::ri::Shader::SHADERTYPE_CALLABLE:			return "call";
69		case xe::ri::Shader::SHADERTYPE_TASK:				return "task";
70		case xe::ri::Shader::SHADERTYPE_MESH:				return "mesh";
71
72		default:
73			throw xe::Error("Invalid shader type");
74	}
75}
76
77static void writeShaderProgram (const CommandLine& cmdLine, const std::string& casePath, const xe::ri::ShaderProgram& shaderProgram, int programNdx)
78{
79	const string basePath = string(de::FilePath::join(cmdLine.dstPath, casePath).getPath()) + "." + de::toString(programNdx);
80
81	for (int shaderNdx = 0; shaderNdx < shaderProgram.shaders.getNumItems(); shaderNdx++)
82	{
83		const xe::ri::Shader&	shader		= dynamic_cast<const xe::ri::Shader&>(shaderProgram.shaders.getItem(shaderNdx));
84		const string			shaderPath	= basePath + "." + getShaderTypeSuffix(shader.shaderType);
85
86		if (de::FilePath(shaderPath).exists())
87			throw xe::Error("File '" + shaderPath + "' exists already");
88
89		{
90			std::ofstream out(shaderPath.c_str(), std::ifstream::binary|std::ifstream::out);
91
92			if (!out.good())
93				throw xe::Error("Failed to open '" + shaderPath + "'");
94
95			out.write(shader.source.source.c_str(), shader.source.source.size());
96		}
97	}
98}
99
100struct StackEntry
101{
102	const xe::ri::List*		list;
103	int						curNdx;
104
105	explicit StackEntry (const xe::ri::List* list_) : list(list_), curNdx(0) {}
106};
107
108static void extractShaderPrograms (const CommandLine& cmdLine, const std::string& casePath, const xe::TestCaseResult& result)
109{
110	vector<StackEntry>	itemListStack;
111	int					programNdx		= 0;
112
113	itemListStack.push_back(StackEntry(&result.resultItems));
114
115	while (!itemListStack.empty())
116	{
117		StackEntry& curEntry = itemListStack.back();
118
119		if (curEntry.curNdx < curEntry.list->getNumItems())
120		{
121			const xe::ri::Item&	curItem	= curEntry.list->getItem(curEntry.curNdx);
122			curEntry.curNdx += 1;
123
124			if (curItem.getType() == xe::ri::TYPE_SHADERPROGRAM)
125			{
126				writeShaderProgram(cmdLine, casePath, static_cast<const xe::ri::ShaderProgram&>(curItem), programNdx);
127				programNdx += 1;
128			}
129			else if (curItem.getType() == xe::ri::TYPE_SECTION)
130				itemListStack.push_back(StackEntry(&static_cast<const xe::ri::Section&>(curItem).items));
131		}
132		else
133			itemListStack.pop_back();
134	}
135
136	if (programNdx == 0)
137		std::cout << "WARNING: no shader programs found in '" << casePath << "'\n";
138}
139
140class ShaderProgramExtractHandler : public xe::TestLogHandler
141{
142public:
143	ShaderProgramExtractHandler (const CommandLine& cmdLine)
144		: m_cmdLine(cmdLine)
145	{
146	}
147
148	void setSessionInfo (const xe::SessionInfo&)
149	{
150		// Ignored.
151	}
152
153	xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
154	{
155		return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
156	}
157
158	void testCaseResultUpdated (const xe::TestCaseResultPtr&)
159	{
160		// Ignored.
161	}
162
163	void testCaseResultComplete (const xe::TestCaseResultPtr& caseData)
164	{
165		if (caseData->getDataSize() > 0)
166		{
167			xe::TestCaseResult					fullResult;
168			xe::TestResultParser::ParseResult	parseResult;
169
170			m_testResultParser.init(&fullResult);
171			parseResult = m_testResultParser.parse(caseData->getData(), caseData->getDataSize());
172			DE_UNREF(parseResult);
173
174			extractShaderPrograms(m_cmdLine, caseData->getTestCasePath(), fullResult);
175		}
176	}
177
178private:
179	const CommandLine&		m_cmdLine;
180	xe::TestResultParser	m_testResultParser;
181};
182
183static void extractShaderProgramsFromLogFile (const CommandLine& cmdLine)
184{
185	std::ifstream					in				(cmdLine.filename.c_str(), std::ifstream::binary|std::ifstream::in);
186	ShaderProgramExtractHandler		resultHandler	(cmdLine);
187	xe::TestLogParser				parser			(&resultHandler);
188	deUint8							buf				[1024];
189	int								numRead			= 0;
190
191	if (!in.good())
192		throw std::runtime_error(string("Failed to open '") + cmdLine.filename + "'");
193
194	for (;;)
195	{
196		in.read((char*)&buf[0], DE_LENGTH_OF_ARRAY(buf));
197		numRead = (int)in.gcount();
198
199		if (numRead <= 0)
200			break;
201
202		parser.parse(&buf[0], numRead);
203	}
204
205	in.close();
206}
207
208static void printHelp (const char* binName)
209{
210	printf("%s: [filename] [dst path (optional)]\n", binName);
211}
212
213static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
214{
215	for (int argNdx = 1; argNdx < argc; argNdx++)
216	{
217		const char* arg = argv[argNdx];
218
219		if (!deStringBeginsWith(arg, "--"))
220		{
221			if (cmdLine.filename.empty())
222				cmdLine.filename = arg;
223			else if (cmdLine.dstPath.empty())
224				cmdLine.dstPath = arg;
225			else
226				return false;
227		}
228		else
229			return false;
230	}
231
232	if (cmdLine.filename.empty())
233		return false;
234
235	return true;
236}
237
238int main (int argc, const char* const* argv)
239{
240	try
241	{
242		CommandLine cmdLine;
243
244		if (!parseCommandLine(cmdLine, argc, argv))
245		{
246			printHelp(argv[0]);
247			return -1;
248		}
249
250		extractShaderProgramsFromLogFile(cmdLine);
251	}
252	catch (const std::exception& e)
253	{
254		printf("FATAL ERROR: %s\n", e.what());
255		return -1;
256	}
257
258	return 0;
259}
260