1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
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 Test Log C++ Wrapper.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuTestLog.hpp"
25#include "tcuTextureUtil.hpp"
26#include "tcuSurface.hpp"
27#include "deMath.h"
28
29#include <limits>
30
31namespace tcu
32{
33
34class LogWriteFailedError : public ResourceError
35{
36public:
37	LogWriteFailedError (void) : ResourceError("Writing to test log failed") {}
38};
39
40enum
41{
42	MAX_IMAGE_SIZE_2D		= 4096,
43	MAX_IMAGE_SIZE_3D		= 128
44};
45
46// LogImage
47
48LogImage::LogImage (const std::string& name, const std::string& description, const Surface& surface, qpImageCompressionMode compression)
49	: m_name		(name)
50	, m_description	(description)
51	, m_access		(surface.getAccess())
52	, m_scale		(1.0f, 1.0f, 1.0f, 1.0f)
53	, m_bias		(0.0f, 0.0f, 0.0f, 0.0f)
54	, m_compression	(compression)
55{
56}
57
58LogImage::LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, qpImageCompressionMode compression)
59	: m_name		(name)
60	, m_description	(description)
61	, m_access		(access)
62	, m_scale		(1.0f, 1.0f, 1.0f, 1.0f)
63	, m_bias		(0.0f, 0.0f, 0.0f, 0.0f)
64	, m_compression	(compression)
65{
66	computePixelScaleBias(access, m_scale, m_bias);
67}
68
69// MessageBuilder
70
71MessageBuilder::MessageBuilder (const MessageBuilder& other)
72	: m_log(other.m_log)
73{
74	m_str.str(other.m_str.str());
75}
76
77MessageBuilder& MessageBuilder::operator= (const MessageBuilder& other)
78{
79	m_log = other.m_log;
80	m_str.str(other.m_str.str());
81	return *this;
82}
83
84TestLog& MessageBuilder::operator<< (const TestLog::EndMessageToken&)
85{
86	m_log->writeMessage(m_str.str().c_str());
87	return *m_log;
88}
89
90// SampleBuilder
91
92TestLog& SampleBuilder::operator<< (const TestLog::EndSampleToken&)
93{
94	m_log->startSample();
95
96	for (std::vector<Value>::const_iterator val = m_values.begin(); val != m_values.end(); ++val)
97	{
98		if (val->type == Value::TYPE_FLOAT64)
99			m_log->writeSampleValue(val->value.float64);
100		else if (val->type == Value::TYPE_INT64)
101			m_log->writeSampleValue(val->value.int64);
102		else
103			DE_ASSERT(false);
104	}
105
106	m_log->endSample();
107
108	return *m_log;
109}
110
111// TestLog
112
113TestLog::TestLog (const char* fileName, deUint32 flags)
114	: m_log(qpTestLog_createFileLog(fileName, flags))
115{
116	if (!m_log)
117		throw ResourceError(std::string("Failed to open test log file '") + fileName + "'");
118}
119
120TestLog::~TestLog (void)
121{
122	qpTestLog_destroy(m_log);
123}
124
125void TestLog::writeMessage (const char* msgStr)
126{
127	if (qpTestLog_writeText(m_log, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, msgStr) == DE_FALSE)
128		throw LogWriteFailedError();
129}
130
131void TestLog::startImageSet (const char* name, const char* description)
132{
133	if (qpTestLog_startImageSet(m_log, name, description) == DE_FALSE)
134		throw LogWriteFailedError();
135}
136
137void TestLog::endImageSet (void)
138{
139	if (qpTestLog_endImageSet(m_log) == DE_FALSE)
140		throw LogWriteFailedError();
141}
142
143template <int Size>
144static Vector<int, Size> computeScaledSize (const Vector<int, Size>& imageSize, int maxSize)
145{
146	bool allInRange = true;
147	for (int i = 0; i < Size; i++)
148		allInRange = allInRange && (imageSize[i] <= maxSize);
149
150	if (allInRange)
151		return imageSize;
152	else
153	{
154		float d = 1.0f;
155		for (int i = 0; i < Size; i++)
156			d = de::max(d, (float)imageSize[i] / (float)maxSize);
157
158		Vector<int, Size> res;
159		for (int i = 0; i < Size; i++)
160			res[i] = deRoundFloatToInt32((float)imageSize[i] / d);
161
162		return res;
163	}
164}
165
166void TestLog::writeImage (const char* name, const char* description, const ConstPixelBufferAccess& access, const Vec4& pixelScale, const Vec4& pixelBias, qpImageCompressionMode compressionMode)
167{
168	const TextureFormat&	format		= access.getFormat();
169	int						width		= access.getWidth();
170	int						height		= access.getHeight();
171	int						depth		= access.getDepth();
172
173	if (depth == 1 && format.type == TextureFormat::UNORM_INT8 &&
174		width <= MAX_IMAGE_SIZE_2D && height <= MAX_IMAGE_SIZE_2D &&
175		(format.order == TextureFormat::RGB || format.order == TextureFormat::RGBA)
176		&& pixelBias[0] == 0.0f && pixelBias[1] == 0.0f && pixelBias[2] == 0.0f && pixelBias[3] == 0.0f
177		&& pixelScale[0] == 1.0f && pixelScale[1] == 1.0f && pixelScale[2] == 1.0f && pixelScale[3] == 1.0f)
178	{
179		// Fast-path.
180		bool isRGBA = format.order == TextureFormat::RGBA;
181
182		writeImage(name, description, compressionMode,
183				   isRGBA ? QP_IMAGE_FORMAT_RGBA8888 : QP_IMAGE_FORMAT_RGB888,
184				   width, height, access.getRowPitch(), access.getDataPtr());
185	}
186	else if (depth == 1)
187	{
188		Sampler				sampler			(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::LINEAR, Sampler::NEAREST);
189		IVec2				logImageSize	= computeScaledSize(IVec2(width, height), MAX_IMAGE_SIZE_2D);
190		tcu::TextureLevel	logImage		(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageSize.x(), logImageSize.y(), 1);
191		PixelBufferAccess	logImageAccess	= logImage.getAccess();
192		std::ostringstream	longDesc;
193
194		longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")";
195
196		for (int y = 0; y < logImage.getHeight(); y++)
197		{
198			for (int x = 0; x < logImage.getWidth(); x++)
199			{
200				float	yf	= ((float)y + 0.5f) / (float)logImage.getHeight();
201				float	xf	= ((float)x + 0.5f) / (float)logImage.getWidth();
202				Vec4	s	= access.sample2D(sampler, sampler.minFilter, xf, yf, 0)*pixelScale + pixelBias;
203
204				logImageAccess.setPixel(s, x, y);
205			}
206		}
207
208		writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888,
209				   logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(),
210				   logImageAccess.getDataPtr());
211	}
212	else
213	{
214		// Isometric splat volume rendering.
215		const float			blendFactor			= 0.85f;
216		IVec3				scaledSize			= computeScaledSize(IVec3(width, height, depth), MAX_IMAGE_SIZE_3D);
217		int					w					= scaledSize.x();
218		int					h					= scaledSize.y();
219		int					d					= scaledSize.z();
220		int					logImageW			= w+d - 1;
221		int					logImageH			= w+d+h;
222		std::vector<float>	blendImage			(logImageW*logImageH*4, 0.0f);
223		PixelBufferAccess	blendImageAccess	(TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT), logImageW, logImageH, 1, &blendImage[0]);
224		tcu::TextureLevel	logImage			(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageW, logImageH, 1);
225		PixelBufferAccess	logImageAccess		= logImage.getAccess();
226		Sampler				sampler				(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::NEAREST, Sampler::NEAREST);
227		std::ostringstream	longDesc;
228
229		// \note Back-to-front.
230		for (int z = d-1; z >= 0; z--)
231		{
232			for (int y = 0; y < h; y++)
233			{
234				for (int x = 0; x < w; x++)
235				{
236					int		px	= w - (x + 1) + z;
237					int		py	= (w + d + h) - (x + y + z + 1);
238
239					float	xf	= ((float)x + 0.5f) / (float)w;
240					float	yf	= ((float)y + 0.5f) / (float)h;
241					float	zf	= ((float)z + 0.5f) / (float)d;
242
243					Vec4	p	= blendImageAccess.getPixel(px, py);
244					Vec4	s	= access.sample3D(sampler, sampler.minFilter, xf, yf, zf);
245					Vec4	b	= s + p*blendFactor;
246
247					blendImageAccess.setPixel(b, px, py);
248				}
249			}
250		}
251
252		// Scale blend image nicely.
253		longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")";
254
255		// Write to final image.
256		tcu::clear(logImageAccess, tcu::IVec4(0x33, 0x66, 0x99, 0xff));
257
258		for (int z = 0; z < d; z++)
259		{
260			for (int y = 0; y < h; y++)
261			{
262				for (int x = 0; x < w; x++)
263				{
264					if (z != 0 && !(x == 0 || y == h-1 || y == h-2))
265						continue;
266
267					int		px	= w - (x + 1) + z;
268					int		py	= (w + d + h) - (x + y + z + 1);
269					Vec4	s	= blendImageAccess.getPixel(px, py)*pixelScale + pixelBias;
270
271					logImageAccess.setPixel(s, px, py);
272				}
273			}
274		}
275
276		writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888,
277				   logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(),
278				   logImageAccess.getDataPtr());
279	}
280}
281
282void TestLog::writeImage (const char* name, const char* description, qpImageCompressionMode compressionMode, qpImageFormat format, int width, int height, int stride, const void* data)
283{
284	if (qpTestLog_writeImage(m_log, name, description, compressionMode, format, width, height, stride, data) == DE_FALSE)
285		throw LogWriteFailedError();
286}
287
288void TestLog::startSection (const char* name, const char* description)
289{
290	if (qpTestLog_startSection(m_log, name, description) == DE_FALSE)
291		throw LogWriteFailedError();
292}
293
294void TestLog::endSection (void)
295{
296	if (qpTestLog_endSection(m_log) == DE_FALSE)
297		throw LogWriteFailedError();
298}
299
300void TestLog::startShaderProgram (bool linkOk, const char* linkInfoLog)
301{
302	if (qpTestLog_startShaderProgram(m_log, linkOk?DE_TRUE:DE_FALSE, linkInfoLog) == DE_FALSE)
303		throw LogWriteFailedError();
304}
305
306void TestLog::endShaderProgram (void)
307{
308	if (qpTestLog_endShaderProgram(m_log) == DE_FALSE)
309		throw LogWriteFailedError();
310}
311
312void TestLog::writeShader (qpShaderType type, const char* source, bool compileOk, const char* infoLog)
313{
314	if (qpTestLog_writeShader(m_log, type, source, compileOk?DE_TRUE:DE_FALSE, infoLog) == DE_FALSE)
315		throw LogWriteFailedError();
316}
317
318void TestLog::writeKernelSource (const char* source)
319{
320	if (qpTestLog_writeKernelSource(m_log, source) == DE_FALSE)
321		throw LogWriteFailedError();
322}
323
324void TestLog::writeCompileInfo (const char* name, const char* description, bool compileOk, const char* infoLog)
325{
326	if (qpTestLog_writeCompileInfo(m_log, name, description, compileOk ? DE_TRUE : DE_FALSE, infoLog) == DE_FALSE)
327		throw LogWriteFailedError();
328}
329
330void TestLog::writeFloat (const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
331{
332	if (qpTestLog_writeFloat(m_log, name, description, unit, tag, value) == DE_FALSE)
333		throw LogWriteFailedError();
334}
335
336void TestLog::writeInteger (const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
337{
338	if (qpTestLog_writeInteger(m_log, name, description, unit, tag, value) == DE_FALSE)
339		throw LogWriteFailedError();
340}
341
342void TestLog::startEglConfigSet (const char* name, const char* description)
343{
344	if (qpTestLog_startEglConfigSet(m_log, name, description) == DE_FALSE)
345		throw LogWriteFailedError();
346}
347
348void TestLog::writeEglConfig (const qpEglConfigInfo* config)
349{
350	if (qpTestLog_writeEglConfig(m_log, config) == DE_FALSE)
351		throw LogWriteFailedError();
352}
353
354void TestLog::endEglConfigSet (void)
355{
356	if (qpTestLog_endEglConfigSet(m_log) == DE_FALSE)
357		throw LogWriteFailedError();
358}
359
360void TestLog::startCase (const char* testCasePath, qpTestCaseType testCaseType)
361{
362	if (qpTestLog_startCase(m_log, testCasePath, testCaseType) == DE_FALSE)
363		throw LogWriteFailedError();
364}
365
366void TestLog::endCase (qpTestResult result, const char* description)
367{
368	if (qpTestLog_endCase(m_log, result, description) == DE_FALSE)
369		throw LogWriteFailedError();
370}
371
372void TestLog::terminateCase (qpTestResult result)
373{
374	if (qpTestLog_terminateCase(m_log, result) == DE_FALSE)
375		throw LogWriteFailedError();
376}
377
378void TestLog::startSampleList (const std::string& name, const std::string& description)
379{
380	if (qpTestLog_startSampleList(m_log, name.c_str(), description.c_str()) == DE_FALSE)
381		throw LogWriteFailedError();
382}
383
384void TestLog::startSampleInfo (void)
385{
386	if (qpTestLog_startSampleInfo(m_log) == DE_FALSE)
387		throw LogWriteFailedError();
388}
389
390void TestLog::writeValueInfo (const std::string& name, const std::string& description, const std::string& unit, qpSampleValueTag tag)
391{
392	if (qpTestLog_writeValueInfo(m_log, name.c_str(), description.c_str(), unit.empty() ? DE_NULL : unit.c_str(), tag) == DE_FALSE)
393		throw LogWriteFailedError();
394}
395
396void TestLog::endSampleInfo (void)
397{
398	if (qpTestLog_endSampleInfo(m_log) == DE_FALSE)
399		throw LogWriteFailedError();
400}
401
402void TestLog::startSample (void)
403{
404	if (qpTestLog_startSample(m_log) == DE_FALSE)
405		throw LogWriteFailedError();
406}
407
408void TestLog::writeSampleValue (double value)
409{
410	if (qpTestLog_writeValueFloat(m_log, value) == DE_FALSE)
411		throw LogWriteFailedError();
412}
413
414void TestLog::writeSampleValue (deInt64 value)
415{
416	if (qpTestLog_writeValueInteger(m_log, value) == DE_FALSE)
417		throw LogWriteFailedError();
418}
419
420void TestLog::endSample (void)
421{
422	if (qpTestLog_endSample(m_log) == DE_FALSE)
423		throw LogWriteFailedError();
424}
425
426void TestLog::endSampleList (void)
427{
428	if (qpTestLog_endSampleList(m_log) == DE_FALSE)
429		throw LogWriteFailedError();
430}
431
432const TestLog::BeginMessageToken		TestLog::Message			= TestLog::BeginMessageToken();
433const TestLog::EndMessageToken			TestLog::EndMessage			= TestLog::EndMessageToken();
434const TestLog::EndImageSetToken			TestLog::EndImageSet		= TestLog::EndImageSetToken();
435const TestLog::EndSectionToken			TestLog::EndSection			= TestLog::EndSectionToken();
436const TestLog::EndShaderProgramToken	TestLog::EndShaderProgram	= TestLog::EndShaderProgramToken();
437const TestLog::SampleInfoToken			TestLog::SampleInfo			= TestLog::SampleInfoToken();
438const TestLog::EndSampleInfoToken		TestLog::EndSampleInfo		= TestLog::EndSampleInfoToken();
439const TestLog::BeginSampleToken			TestLog::Sample				= TestLog::BeginSampleToken();
440const TestLog::EndSampleToken			TestLog::EndSample			= TestLog::EndSampleToken();
441const TestLog::EndSampleListToken		TestLog::EndSampleList		= TestLog::EndSampleListToken();
442
443} // tcu
444