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		default:
64			throw xe::Error("Invalid shader type");
65	}
66}
67
68static void writeShaderProgram (const CommandLine& cmdLine, const std::string& casePath, const xe::ri::ShaderProgram& shaderProgram, int programNdx)
69{
70	const string basePath = string(de::FilePath::join(cmdLine.dstPath, casePath).getPath()) + "." + de::toString(programNdx);
71
72	for (int shaderNdx = 0; shaderNdx < shaderProgram.shaders.getNumItems(); shaderNdx++)
73	{
74		const xe::ri::Shader&	shader		= dynamic_cast<const xe::ri::Shader&>(shaderProgram.shaders.getItem(shaderNdx));
75		const string			shaderPath	= basePath + "." + getShaderTypeSuffix(shader.shaderType);
76
77		if (de::FilePath(shaderPath).exists())
78			throw xe::Error("File '" + shaderPath + "' exists already");
79
80		{
81			std::ofstream out(shaderPath.c_str(), std::ifstream::binary|std::ifstream::out);
82
83			if (!out.good())
84				throw xe::Error("Failed to open '" + shaderPath + "'");
85
86			out.write(shader.source.source.c_str(), shader.source.source.size());
87		}
88	}
89}
90
91struct StackEntry
92{
93	const xe::ri::List*		list;
94	int						curNdx;
95
96	explicit StackEntry (const xe::ri::List* list_) : list(list_), curNdx(0) {}
97};
98
99static void extractShaderPrograms (const CommandLine& cmdLine, const std::string& casePath, const xe::TestCaseResult& result)
100{
101	vector<StackEntry>	itemListStack;
102	int					programNdx		= 0;
103
104	itemListStack.push_back(StackEntry(&result.resultItems));
105
106	while (!itemListStack.empty())
107	{
108		StackEntry& curEntry = itemListStack.back();
109
110		if (curEntry.curNdx < curEntry.list->getNumItems())
111		{
112			const xe::ri::Item&	curItem	= curEntry.list->getItem(curEntry.curNdx);
113			curEntry.curNdx += 1;
114
115			if (curItem.getType() == xe::ri::TYPE_SHADERPROGRAM)
116			{
117				writeShaderProgram(cmdLine, casePath, static_cast<const xe::ri::ShaderProgram&>(curItem), programNdx);
118				programNdx += 1;
119			}
120			else if (curItem.getType() == xe::ri::TYPE_SECTION)
121				itemListStack.push_back(StackEntry(&static_cast<const xe::ri::Section&>(curItem).items));
122		}
123		else
124			itemListStack.pop_back();
125	}
126
127	if (programNdx == 0)
128		std::cout << "WARNING: no shader programs found in '" << casePath << "'\n";
129}
130
131class ShaderProgramExtractHandler : public xe::TestLogHandler
132{
133public:
134	ShaderProgramExtractHandler (const CommandLine& cmdLine)
135		: m_cmdLine(cmdLine)
136	{
137	}
138
139	void setSessionInfo (const xe::SessionInfo&)
140	{
141		// Ignored.
142	}
143
144	xe::TestCaseResultPtr startTestCaseResult (const char* casePath)
145	{
146		return xe::TestCaseResultPtr(new xe::TestCaseResultData(casePath));
147	}
148
149	void testCaseResultUpdated (const xe::TestCaseResultPtr&)
150	{
151		// Ignored.
152	}
153
154	void testCaseResultComplete (const xe::TestCaseResultPtr& caseData)
155	{
156		if (caseData->getDataSize() > 0)
157		{
158			xe::TestCaseResult					fullResult;
159			xe::TestResultParser::ParseResult	parseResult;
160
161			m_testResultParser.init(&fullResult);
162			parseResult = m_testResultParser.parse(caseData->getData(), caseData->getDataSize());
163			DE_UNREF(parseResult);
164
165			extractShaderPrograms(m_cmdLine, caseData->getTestCasePath(), fullResult);
166		}
167	}
168
169private:
170	const CommandLine&		m_cmdLine;
171	xe::TestResultParser	m_testResultParser;
172};
173
174static void extractShaderProgramsFromLogFile (const CommandLine& cmdLine)
175{
176	std::ifstream					in				(cmdLine.filename.c_str(), std::ifstream::binary|std::ifstream::in);
177	ShaderProgramExtractHandler		resultHandler	(cmdLine);
178	xe::TestLogParser				parser			(&resultHandler);
179	deUint8							buf				[1024];
180	int								numRead			= 0;
181
182	if (!in.good())
183		throw std::runtime_error(string("Failed to open '") + cmdLine.filename + "'");
184
185	for (;;)
186	{
187		in.read((char*)&buf[0], DE_LENGTH_OF_ARRAY(buf));
188		numRead = (int)in.gcount();
189
190		if (numRead <= 0)
191			break;
192
193		parser.parse(&buf[0], numRead);
194	}
195
196	in.close();
197}
198
199static void printHelp (const char* binName)
200{
201	printf("%s: [filename] [dst path (optional)]\n", binName);
202}
203
204static bool parseCommandLine (CommandLine& cmdLine, int argc, const char* const* argv)
205{
206	for (int argNdx = 1; argNdx < argc; argNdx++)
207	{
208		const char* arg = argv[argNdx];
209
210		if (!deStringBeginsWith(arg, "--"))
211		{
212			if (cmdLine.filename.empty())
213				cmdLine.filename = arg;
214			else if (cmdLine.dstPath.empty())
215				cmdLine.dstPath = arg;
216			else
217				return false;
218		}
219		else
220			return false;
221	}
222
223	if (cmdLine.filename.empty())
224		return false;
225
226	return true;
227}
228
229int main (int argc, const char* const* argv)
230{
231	try
232	{
233		CommandLine cmdLine;
234
235		if (!parseCommandLine(cmdLine, argc, argv))
236		{
237			printHelp(argv[0]);
238			return -1;
239		}
240
241		extractShaderProgramsFromLogFile(cmdLine);
242	}
243	catch (const std::exception& e)
244	{
245		printf("FATAL ERROR: %s\n", e.what());
246		return -1;
247	}
248
249	return 0;
250}
251