tcuTestLog.cpp revision fcbb91dc538f174f8798417e5a44f74ec2b092c0
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	// Simplify combined formats that only use a single channel
67	if (tcu::isCombinedDepthStencilType(m_access.getFormat().type))
68	{
69		if (m_access.getFormat().order == tcu::TextureFormat::D)
70			m_access = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH);
71		else if (m_access.getFormat().order == tcu::TextureFormat::S)
72			m_access = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL);
73	}
74
75	// Implicit scale and bias
76	if (m_access.getFormat().order != tcu::TextureFormat::DS)
77		computePixelScaleBias(m_access, m_scale, m_bias);
78	else
79	{
80		// Pack D and S bias and scale to R and G
81		const ConstPixelBufferAccess	depthAccess		= tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH);
82		const ConstPixelBufferAccess	stencilAccess	= tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL);
83		tcu::Vec4						depthScale;
84		tcu::Vec4						depthBias;
85		tcu::Vec4						stencilScale;
86		tcu::Vec4						stencilBias;
87
88		computePixelScaleBias(depthAccess, depthScale, depthBias);
89		computePixelScaleBias(stencilAccess, stencilScale, stencilBias);
90
91		m_scale = tcu::Vec4(depthScale.x(), stencilScale.x(), 0.0f, 0.0f);
92		m_bias = tcu::Vec4(depthBias.x(), stencilBias.x(), 0.0f, 0.0f);
93	}
94}
95
96LogImage::LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, const Vec4& scale, const Vec4& bias, qpImageCompressionMode compression)
97	: m_name		(name)
98	, m_description	(description)
99	, m_access		(access)
100	, m_scale		(scale)
101	, m_bias		(bias)
102	, m_compression	(compression)
103{
104	// Cannot set scale and bias of combined formats
105	DE_ASSERT(access.getFormat().order != tcu::TextureFormat::DS);
106
107	// Simplify access
108	if (tcu::isCombinedDepthStencilType(access.getFormat().type))
109	{
110		if (access.getFormat().order == tcu::TextureFormat::D)
111			m_access = tcu::getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH);
112		if (access.getFormat().order == tcu::TextureFormat::S)
113			m_access = tcu::getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL);
114		else
115		{
116			// Cannot log a DS format
117			DE_ASSERT(false);
118			return;
119		}
120	}
121}
122
123void LogImage::write (TestLog& log) const
124{
125	if (m_access.getFormat().order != tcu::TextureFormat::DS)
126		log.writeImage(m_name.c_str(), m_description.c_str(), m_access, m_scale, m_bias, m_compression);
127	else
128	{
129		const ConstPixelBufferAccess	depthAccess		= tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH);
130		const ConstPixelBufferAccess	stencilAccess	= tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL);
131
132		log.startImageSet(m_name.c_str(), m_description.c_str());
133		log.writeImage("Depth", "Depth channel", depthAccess, m_scale.swizzle(0, 0, 0, 0), m_bias.swizzle(0, 0, 0, 0), m_compression);
134		log.writeImage("Stencil", "Stencil channel", stencilAccess, m_scale.swizzle(1, 1, 1, 1), m_bias.swizzle(1, 1, 1, 1), m_compression);
135		log.endImageSet();
136	}
137}
138
139// MessageBuilder
140
141MessageBuilder::MessageBuilder (const MessageBuilder& other)
142	: m_log(other.m_log)
143{
144	m_str.str(other.m_str.str());
145}
146
147MessageBuilder& MessageBuilder::operator= (const MessageBuilder& other)
148{
149	m_log = other.m_log;
150	m_str.str(other.m_str.str());
151	return *this;
152}
153
154TestLog& MessageBuilder::operator<< (const TestLog::EndMessageToken&)
155{
156	m_log->writeMessage(m_str.str().c_str());
157	return *m_log;
158}
159
160// SampleBuilder
161
162TestLog& SampleBuilder::operator<< (const TestLog::EndSampleToken&)
163{
164	m_log->startSample();
165
166	for (std::vector<Value>::const_iterator val = m_values.begin(); val != m_values.end(); ++val)
167	{
168		if (val->type == Value::TYPE_FLOAT64)
169			m_log->writeSampleValue(val->value.float64);
170		else if (val->type == Value::TYPE_INT64)
171			m_log->writeSampleValue(val->value.int64);
172		else
173			DE_ASSERT(false);
174	}
175
176	m_log->endSample();
177
178	return *m_log;
179}
180
181// TestLog
182
183TestLog::TestLog (const char* fileName, deUint32 flags)
184	: m_log(qpTestLog_createFileLog(fileName, flags))
185{
186	if (!m_log)
187		throw ResourceError(std::string("Failed to open test log file '") + fileName + "'");
188}
189
190TestLog::~TestLog (void)
191{
192	qpTestLog_destroy(m_log);
193}
194
195void TestLog::writeMessage (const char* msgStr)
196{
197	if (qpTestLog_writeText(m_log, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, msgStr) == DE_FALSE)
198		throw LogWriteFailedError();
199}
200
201void TestLog::startImageSet (const char* name, const char* description)
202{
203	if (qpTestLog_startImageSet(m_log, name, description) == DE_FALSE)
204		throw LogWriteFailedError();
205}
206
207void TestLog::endImageSet (void)
208{
209	if (qpTestLog_endImageSet(m_log) == DE_FALSE)
210		throw LogWriteFailedError();
211}
212
213template <int Size>
214static Vector<int, Size> computeScaledSize (const Vector<int, Size>& imageSize, int maxSize)
215{
216	bool allInRange = true;
217	for (int i = 0; i < Size; i++)
218		allInRange = allInRange && (imageSize[i] <= maxSize);
219
220	if (allInRange)
221		return imageSize;
222	else
223	{
224		float d = 1.0f;
225		for (int i = 0; i < Size; i++)
226			d = de::max(d, (float)imageSize[i] / (float)maxSize);
227
228		Vector<int, Size> res;
229		for (int i = 0; i < Size; i++)
230			res[i] = deRoundFloatToInt32((float)imageSize[i] / d);
231
232		return res;
233	}
234}
235
236void TestLog::writeImage (const char* name, const char* description, const ConstPixelBufferAccess& access, const Vec4& pixelScale, const Vec4& pixelBias, qpImageCompressionMode compressionMode)
237{
238	const TextureFormat&	format		= access.getFormat();
239	int						width		= access.getWidth();
240	int						height		= access.getHeight();
241	int						depth		= access.getDepth();
242
243	// Writing a combined image does not make sense
244	DE_ASSERT(!tcu::isCombinedDepthStencilType(access.getFormat().type));
245
246	// Do not bother with preprocessing if images are not stored
247	if ((qpTestLog_getLogFlags(m_log) & QP_TEST_LOG_EXCLUDE_IMAGES) != 0)
248		return;
249
250	if (depth == 1 && format.type == TextureFormat::UNORM_INT8 &&
251		width <= MAX_IMAGE_SIZE_2D && height <= MAX_IMAGE_SIZE_2D &&
252		(format.order == TextureFormat::RGB || format.order == TextureFormat::RGBA)
253		&& pixelBias[0] == 0.0f && pixelBias[1] == 0.0f && pixelBias[2] == 0.0f && pixelBias[3] == 0.0f
254		&& pixelScale[0] == 1.0f && pixelScale[1] == 1.0f && pixelScale[2] == 1.0f && pixelScale[3] == 1.0f)
255	{
256		// Fast-path.
257		bool isRGBA = format.order == TextureFormat::RGBA;
258
259		writeImage(name, description, compressionMode,
260				   isRGBA ? QP_IMAGE_FORMAT_RGBA8888 : QP_IMAGE_FORMAT_RGB888,
261				   width, height, access.getRowPitch(), access.getDataPtr());
262	}
263	else if (depth == 1)
264	{
265		Sampler				sampler			(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::LINEAR, Sampler::NEAREST);
266		IVec2				logImageSize	= computeScaledSize(IVec2(width, height), MAX_IMAGE_SIZE_2D);
267		tcu::TextureLevel	logImage		(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageSize.x(), logImageSize.y(), 1);
268		PixelBufferAccess	logImageAccess	= logImage.getAccess();
269		std::ostringstream	longDesc;
270
271		longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")";
272
273		for (int y = 0; y < logImage.getHeight(); y++)
274		{
275			for (int x = 0; x < logImage.getWidth(); x++)
276			{
277				float	yf	= ((float)y + 0.5f) / (float)logImage.getHeight();
278				float	xf	= ((float)x + 0.5f) / (float)logImage.getWidth();
279				Vec4	s	= access.sample2D(sampler, sampler.minFilter, xf, yf, 0)*pixelScale + pixelBias;
280
281				logImageAccess.setPixel(s, x, y);
282			}
283		}
284
285		writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888,
286				   logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(),
287				   logImageAccess.getDataPtr());
288	}
289	else
290	{
291		// Isometric splat volume rendering.
292		const float			blendFactor			= 0.85f;
293		IVec3				scaledSize			= computeScaledSize(IVec3(width, height, depth), MAX_IMAGE_SIZE_3D);
294		int					w					= scaledSize.x();
295		int					h					= scaledSize.y();
296		int					d					= scaledSize.z();
297		int					logImageW			= w+d - 1;
298		int					logImageH			= w+d+h;
299		std::vector<float>	blendImage			(logImageW*logImageH*4, 0.0f);
300		PixelBufferAccess	blendImageAccess	(TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT), logImageW, logImageH, 1, &blendImage[0]);
301		tcu::TextureLevel	logImage			(TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageW, logImageH, 1);
302		PixelBufferAccess	logImageAccess		= logImage.getAccess();
303		Sampler				sampler				(Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::NEAREST, Sampler::NEAREST);
304		std::ostringstream	longDesc;
305
306		// \note Back-to-front.
307		for (int z = d-1; z >= 0; z--)
308		{
309			for (int y = 0; y < h; y++)
310			{
311				for (int x = 0; x < w; x++)
312				{
313					int		px	= w - (x + 1) + z;
314					int		py	= (w + d + h) - (x + y + z + 1);
315
316					float	xf	= ((float)x + 0.5f) / (float)w;
317					float	yf	= ((float)y + 0.5f) / (float)h;
318					float	zf	= ((float)z + 0.5f) / (float)d;
319
320					Vec4	p	= blendImageAccess.getPixel(px, py);
321					Vec4	s	= access.sample3D(sampler, sampler.minFilter, xf, yf, zf);
322					Vec4	b	= s + p*blendFactor;
323
324					blendImageAccess.setPixel(b, px, py);
325				}
326			}
327		}
328
329		// Scale blend image nicely.
330		longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")";
331
332		// Write to final image.
333		tcu::clear(logImageAccess, tcu::IVec4(0x33, 0x66, 0x99, 0xff));
334
335		for (int z = 0; z < d; z++)
336		{
337			for (int y = 0; y < h; y++)
338			{
339				for (int x = 0; x < w; x++)
340				{
341					if (z != 0 && !(x == 0 || y == h-1 || y == h-2))
342						continue;
343
344					int		px	= w - (x + 1) + z;
345					int		py	= (w + d + h) - (x + y + z + 1);
346					Vec4	s	= blendImageAccess.getPixel(px, py)*pixelScale + pixelBias;
347
348					logImageAccess.setPixel(s, px, py);
349				}
350			}
351		}
352
353		writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888,
354				   logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(),
355				   logImageAccess.getDataPtr());
356	}
357}
358
359void TestLog::writeImage (const char* name, const char* description, qpImageCompressionMode compressionMode, qpImageFormat format, int width, int height, int stride, const void* data)
360{
361	if (qpTestLog_writeImage(m_log, name, description, compressionMode, format, width, height, stride, data) == DE_FALSE)
362		throw LogWriteFailedError();
363}
364
365void TestLog::startSection (const char* name, const char* description)
366{
367	if (qpTestLog_startSection(m_log, name, description) == DE_FALSE)
368		throw LogWriteFailedError();
369}
370
371void TestLog::endSection (void)
372{
373	if (qpTestLog_endSection(m_log) == DE_FALSE)
374		throw LogWriteFailedError();
375}
376
377void TestLog::startShaderProgram (bool linkOk, const char* linkInfoLog)
378{
379	if (qpTestLog_startShaderProgram(m_log, linkOk?DE_TRUE:DE_FALSE, linkInfoLog) == DE_FALSE)
380		throw LogWriteFailedError();
381}
382
383void TestLog::endShaderProgram (void)
384{
385	if (qpTestLog_endShaderProgram(m_log) == DE_FALSE)
386		throw LogWriteFailedError();
387}
388
389void TestLog::writeShader (qpShaderType type, const char* source, bool compileOk, const char* infoLog)
390{
391	if (qpTestLog_writeShader(m_log, type, source, compileOk?DE_TRUE:DE_FALSE, infoLog) == DE_FALSE)
392		throw LogWriteFailedError();
393}
394
395void TestLog::writeKernelSource (const char* source)
396{
397	if (qpTestLog_writeKernelSource(m_log, source) == DE_FALSE)
398		throw LogWriteFailedError();
399}
400
401void TestLog::writeCompileInfo (const char* name, const char* description, bool compileOk, const char* infoLog)
402{
403	if (qpTestLog_writeCompileInfo(m_log, name, description, compileOk ? DE_TRUE : DE_FALSE, infoLog) == DE_FALSE)
404		throw LogWriteFailedError();
405}
406
407void TestLog::writeFloat (const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
408{
409	if (qpTestLog_writeFloat(m_log, name, description, unit, tag, value) == DE_FALSE)
410		throw LogWriteFailedError();
411}
412
413void TestLog::writeInteger (const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
414{
415	if (qpTestLog_writeInteger(m_log, name, description, unit, tag, value) == DE_FALSE)
416		throw LogWriteFailedError();
417}
418
419void TestLog::startEglConfigSet (const char* name, const char* description)
420{
421	if (qpTestLog_startEglConfigSet(m_log, name, description) == DE_FALSE)
422		throw LogWriteFailedError();
423}
424
425void TestLog::writeEglConfig (const qpEglConfigInfo* config)
426{
427	if (qpTestLog_writeEglConfig(m_log, config) == DE_FALSE)
428		throw LogWriteFailedError();
429}
430
431void TestLog::endEglConfigSet (void)
432{
433	if (qpTestLog_endEglConfigSet(m_log) == DE_FALSE)
434		throw LogWriteFailedError();
435}
436
437void TestLog::startCase (const char* testCasePath, qpTestCaseType testCaseType)
438{
439	if (qpTestLog_startCase(m_log, testCasePath, testCaseType) == DE_FALSE)
440		throw LogWriteFailedError();
441}
442
443void TestLog::endCase (qpTestResult result, const char* description)
444{
445	if (qpTestLog_endCase(m_log, result, description) == DE_FALSE)
446		throw LogWriteFailedError();
447}
448
449void TestLog::terminateCase (qpTestResult result)
450{
451	if (qpTestLog_terminateCase(m_log, result) == DE_FALSE)
452		throw LogWriteFailedError();
453}
454
455void TestLog::startSampleList (const std::string& name, const std::string& description)
456{
457	if (qpTestLog_startSampleList(m_log, name.c_str(), description.c_str()) == DE_FALSE)
458		throw LogWriteFailedError();
459}
460
461void TestLog::startSampleInfo (void)
462{
463	if (qpTestLog_startSampleInfo(m_log) == DE_FALSE)
464		throw LogWriteFailedError();
465}
466
467void TestLog::writeValueInfo (const std::string& name, const std::string& description, const std::string& unit, qpSampleValueTag tag)
468{
469	if (qpTestLog_writeValueInfo(m_log, name.c_str(), description.c_str(), unit.empty() ? DE_NULL : unit.c_str(), tag) == DE_FALSE)
470		throw LogWriteFailedError();
471}
472
473void TestLog::endSampleInfo (void)
474{
475	if (qpTestLog_endSampleInfo(m_log) == DE_FALSE)
476		throw LogWriteFailedError();
477}
478
479void TestLog::startSample (void)
480{
481	if (qpTestLog_startSample(m_log) == DE_FALSE)
482		throw LogWriteFailedError();
483}
484
485void TestLog::writeSampleValue (double value)
486{
487	if (qpTestLog_writeValueFloat(m_log, value) == DE_FALSE)
488		throw LogWriteFailedError();
489}
490
491void TestLog::writeSampleValue (deInt64 value)
492{
493	if (qpTestLog_writeValueInteger(m_log, value) == DE_FALSE)
494		throw LogWriteFailedError();
495}
496
497void TestLog::endSample (void)
498{
499	if (qpTestLog_endSample(m_log) == DE_FALSE)
500		throw LogWriteFailedError();
501}
502
503void TestLog::endSampleList (void)
504{
505	if (qpTestLog_endSampleList(m_log) == DE_FALSE)
506		throw LogWriteFailedError();
507}
508
509const TestLog::BeginMessageToken		TestLog::Message			= TestLog::BeginMessageToken();
510const TestLog::EndMessageToken			TestLog::EndMessage			= TestLog::EndMessageToken();
511const TestLog::EndImageSetToken			TestLog::EndImageSet		= TestLog::EndImageSetToken();
512const TestLog::EndSectionToken			TestLog::EndSection			= TestLog::EndSectionToken();
513const TestLog::EndShaderProgramToken	TestLog::EndShaderProgram	= TestLog::EndShaderProgramToken();
514const TestLog::SampleInfoToken			TestLog::SampleInfo			= TestLog::SampleInfoToken();
515const TestLog::EndSampleInfoToken		TestLog::EndSampleInfo		= TestLog::EndSampleInfoToken();
516const TestLog::BeginSampleToken			TestLog::Sample				= TestLog::BeginSampleToken();
517const TestLog::EndSampleToken			TestLog::EndSample			= TestLog::EndSampleToken();
518const TestLog::EndSampleListToken		TestLog::EndSampleList		= TestLog::EndSampleListToken();
519
520} // tcu
521