1 /*-------------------------------------------------------------------------
2 * drawElements TestLog Library
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 case result logging
22 *//*--------------------------------------------------------------------*/
23
24 #include "qpTestLog.h"
25 #include "qpXmlWriter.h"
26 #include "qpInfo.h"
27 #include "qpDebugOut.h"
28
29 #include "deMemory.h"
30 #include "deInt32.h"
31 #include "deString.h"
32
33 #include "deMutex.h"
34
35 #if defined(QP_SUPPORT_PNG)
36 # include <png.h>
37 #endif
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42
43 #if (DE_OS == DE_OS_WIN32)
44 # include <windows.h>
45 # include <io.h>
46 #endif
47
48 #if defined(DE_DEBUG)
49
50 /* Utils for verifying container (Section, ImageSet, EglConfigSet) usage in debug builds. */
51
52 typedef enum ContainerType_e
53 {
54 CONTAINERTYPE_SECTION = 0,
55 CONTAINERTYPE_IMAGESET,
56 CONTAINERTYPE_EGLCONFIGSET,
57 CONTAINERTYPE_SHADERPROGRAM,
58 CONTAINERTYPE_SAMPLELIST,
59 CONTAINERTYPE_SAMPLEINFO,
60 CONTAINERTYPE_SAMPLE,
61
62 CONTAINERTYPE_LAST
63 } ContainerType;
64
childContainersOk(ContainerType type)65 DE_INLINE deBool childContainersOk (ContainerType type)
66 {
67 return type == CONTAINERTYPE_SECTION || type == CONTAINERTYPE_SAMPLELIST;
68 }
69
70 enum
71 {
72 MAX_CONTAINER_STACK_DEPTH = 32
73 };
74
75 typedef struct ContainerStack_s
76 {
77 int numElements;
78 ContainerType elements[MAX_CONTAINER_STACK_DEPTH];
79 } ContainerStack;
80
ContainerStack_reset(ContainerStack * stack)81 DE_INLINE void ContainerStack_reset (ContainerStack* stack)
82 {
83 deMemset(stack, 0, sizeof(ContainerStack));
84 }
85
ContainerStack_isEmpty(const ContainerStack * stack)86 DE_INLINE deBool ContainerStack_isEmpty (const ContainerStack* stack)
87 {
88 return stack->numElements == 0;
89 }
90
ContainerStack_push(ContainerStack * stack,ContainerType type)91 DE_INLINE deBool ContainerStack_push (ContainerStack* stack, ContainerType type)
92 {
93 if (stack->numElements == MAX_CONTAINER_STACK_DEPTH)
94 return DE_FALSE;
95
96 if (stack->numElements > 0 && !childContainersOk(stack->elements[stack->numElements-1]))
97 return DE_FALSE;
98
99 stack->elements[stack->numElements] = type;
100 stack->numElements += 1;
101
102 return DE_TRUE;
103 }
104
ContainerStack_pop(ContainerStack * stack)105 DE_INLINE ContainerType ContainerStack_pop (ContainerStack* stack)
106 {
107 DE_ASSERT(stack->numElements > 0);
108 stack->numElements -= 1;
109 return stack->elements[stack->numElements];
110 }
111
ContainerStack_getTop(const ContainerStack * stack)112 DE_INLINE ContainerType ContainerStack_getTop (const ContainerStack* stack)
113 {
114 if (stack->numElements > 0)
115 return stack->elements[stack->numElements-1];
116 else
117 return CONTAINERTYPE_LAST;
118 }
119
120 #endif
121
122 /* qpTestLog instance */
123 struct qpTestLog_s
124 {
125 deUint32 flags; /*!< Logging flags. */
126
127 deMutex lock; /*!< Lock for mutable state below. */
128
129 /* State protected by lock. */
130 FILE* outputFile;
131 qpXmlWriter* writer;
132 deBool isSessionOpen;
133 deBool isCaseOpen;
134
135 #if defined(DE_DEBUG)
136 ContainerStack containerStack; /*!< For container usage verification. */
137 #endif
138 };
139
140 /* Maps integer to string. */
141 typedef struct qpKeyStringMap_s
142 {
143 int key;
144 char* string;
145 } qpKeyStringMap;
146
147 static const char* LOG_FORMAT_VERSION = "0.3.3";
148
149 /* Mapping enum to above strings... */
150 static const qpKeyStringMap s_qpTestTypeMap[] =
151 {
152 { QP_TEST_CASE_TYPE_SELF_VALIDATE, "SelfValidate" },
153 { QP_TEST_CASE_TYPE_PERFORMANCE, "Performance" },
154 { QP_TEST_CASE_TYPE_CAPABILITY, "Capability" },
155 { QP_TEST_CASE_TYPE_ACCURACY, "Accuracy" },
156
157 { QP_TEST_CASE_TYPE_LAST, DE_NULL }
158 };
159
160 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTestTypeMap) == QP_TEST_CASE_TYPE_LAST + 1);
161
162 static const qpKeyStringMap s_qpTestResultMap[] =
163 {
164 { QP_TEST_RESULT_PASS, "Pass" },
165 { QP_TEST_RESULT_FAIL, "Fail" },
166 { QP_TEST_RESULT_QUALITY_WARNING, "QualityWarning" },
167 { QP_TEST_RESULT_COMPATIBILITY_WARNING, "CompatibilityWarning" },
168 { QP_TEST_RESULT_PENDING, "Pending" }, /* should not be needed here */
169 { QP_TEST_RESULT_NOT_SUPPORTED, "NotSupported" },
170 { QP_TEST_RESULT_RESOURCE_ERROR, "ResourceError" },
171 { QP_TEST_RESULT_INTERNAL_ERROR, "InternalError" },
172 { QP_TEST_RESULT_CRASH, "Crash" },
173 { QP_TEST_RESULT_TIMEOUT, "Timeout" },
174
175 /* Add new values here if needed, remember to update qpTestResult enumeration. */
176
177 { QP_TEST_RESULT_LAST, DE_NULL }
178 };
179
180 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTestResultMap) == QP_TEST_RESULT_LAST + 1);
181
182 /* Key tag to string mapping. */
183
184 static const qpKeyStringMap s_qpTagMap[] =
185 {
186 { QP_KEY_TAG_NONE, DE_NULL },
187 { QP_KEY_TAG_PERFORMANCE, "Performance" },
188 { QP_KEY_TAG_QUALITY, "Quality" },
189 { QP_KEY_TAG_PRECISION, "Precision" },
190 { QP_KEY_TAG_TIME, "Time" },
191
192 { QP_KEY_TAG_LAST, DE_NULL }
193 };
194
195 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTagMap) == QP_KEY_TAG_LAST + 1);
196
197 /* Sample value tag to string mapping. */
198
199 static const qpKeyStringMap s_qpSampleValueTagMap[] =
200 {
201 { QP_SAMPLE_VALUE_TAG_PREDICTOR, "Predictor" },
202 { QP_SAMPLE_VALUE_TAG_RESPONSE, "Response" },
203
204 { QP_SAMPLE_VALUE_TAG_LAST, DE_NULL }
205 };
206
207 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpSampleValueTagMap) == QP_SAMPLE_VALUE_TAG_LAST + 1);
208
209 /* Image compression mode to string mapping. */
210
211 static const qpKeyStringMap s_qpImageCompressionModeMap[] =
212 {
213 { QP_IMAGE_COMPRESSION_MODE_NONE, "None" },
214 { QP_IMAGE_COMPRESSION_MODE_PNG, "PNG" },
215
216 { QP_IMAGE_COMPRESSION_MODE_BEST, DE_NULL }, /* not allowed to be written! */
217
218 { QP_IMAGE_COMPRESSION_MODE_LAST, DE_NULL }
219 };
220
221 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpImageCompressionModeMap) == QP_IMAGE_COMPRESSION_MODE_LAST + 1);
222
223 /* Image format to string mapping. */
224
225 static const qpKeyStringMap s_qpImageFormatMap[] =
226 {
227 { QP_IMAGE_FORMAT_RGB888, "RGB888" },
228 { QP_IMAGE_FORMAT_RGBA8888, "RGBA8888" },
229
230 { QP_IMAGE_FORMAT_LAST, DE_NULL }
231 };
232
233 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpImageFormatMap) == QP_IMAGE_FORMAT_LAST + 1);
234
235 /* Shader type to string mapping. */
236
237 static const qpKeyStringMap s_qpShaderTypeMap[] =
238 {
239 { QP_SHADER_TYPE_VERTEX, "VertexShader" },
240 { QP_SHADER_TYPE_FRAGMENT, "FragmentShader" },
241 { QP_SHADER_TYPE_GEOMETRY, "GeometryShader" },
242 { QP_SHADER_TYPE_TESS_CONTROL, "TessControlShader" },
243 { QP_SHADER_TYPE_TESS_EVALUATION, "TessEvaluationShader" },
244 { QP_SHADER_TYPE_COMPUTE, "ComputeShader" },
245
246 { QP_SHADER_TYPE_LAST, DE_NULL }
247 };
248
249 DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpShaderTypeMap) == QP_SHADER_TYPE_LAST + 1);
250
qpTestLog_flushFile(qpTestLog * log)251 static void qpTestLog_flushFile (qpTestLog* log)
252 {
253 DE_ASSERT(log && log->outputFile);
254 fflush(log->outputFile);
255 #if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
256 /* \todo [petri] Is this really necessary? */
257 FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(log->outputFile)));
258 #endif
259 }
260
261 #define QP_LOOKUP_STRING(KEYMAP, KEY) qpLookupString(KEYMAP, DE_LENGTH_OF_ARRAY(KEYMAP), (int)(KEY))
262
qpLookupString(const qpKeyStringMap * keyMap,int keyMapSize,int key)263 static const char* qpLookupString (const qpKeyStringMap* keyMap, int keyMapSize, int key)
264 {
265 DE_ASSERT(keyMap);
266 DE_ASSERT(deInBounds32(key, 0, keyMapSize));
267 DE_ASSERT(keyMap[key].key == key);
268 DE_UNREF(keyMapSize); /* for asserting only */
269 return keyMap[key].string;
270 }
271
int32ToString(int val,char buf[32])272 DE_INLINE void int32ToString (int val, char buf[32])
273 {
274 deSprintf(&buf[0], 32, "%d", val);
275 }
276
int64ToString(deInt64 val,char buf[32])277 DE_INLINE void int64ToString (deInt64 val, char buf[32])
278 {
279 deSprintf(&buf[0], 32, "%lld", (long long int)val);
280 }
281
floatToString(float value,char * buf,int bufSize)282 DE_INLINE void floatToString (float value, char* buf, int bufSize)
283 {
284 deSprintf(buf, bufSize, "%f", value);
285 }
286
doubleToString(double value,char * buf,int bufSize)287 DE_INLINE void doubleToString (double value, char* buf, int bufSize)
288 {
289 deSprintf(buf, bufSize, "%f", value);
290 }
291
beginSession(qpTestLog * log)292 static deBool beginSession (qpTestLog* log)
293 {
294 DE_ASSERT(log && !log->isSessionOpen);
295
296 /* Write session info. */
297 fprintf(log->outputFile, "#sessionInfo releaseName %s\n", qpGetReleaseName());
298 fprintf(log->outputFile, "#sessionInfo releaseId 0x%08x\n", qpGetReleaseId());
299 fprintf(log->outputFile, "#sessionInfo targetName \"%s\"\n", qpGetTargetName());
300
301 /* Write out #beginSession. */
302 fprintf(log->outputFile, "#beginSession\n");
303 qpTestLog_flushFile(log);
304
305 log->isSessionOpen = DE_TRUE;
306
307 return DE_TRUE;
308 }
309
endSession(qpTestLog * log)310 static deBool endSession (qpTestLog* log)
311 {
312 DE_ASSERT(log && log->isSessionOpen);
313
314 /* Make sure xml is flushed. */
315 qpXmlWriter_flush(log->writer);
316
317 /* Write out #endSession. */
318 fprintf(log->outputFile, "\n#endSession\n");
319 qpTestLog_flushFile(log);
320
321 log->isSessionOpen = DE_FALSE;
322
323 return DE_TRUE;
324 }
325
326 /*--------------------------------------------------------------------*//*!
327 * \brief Create a file based logger instance
328 * \param fileName Name of the file where to put logs
329 * \return qpTestLog instance, or DE_NULL if cannot create file
330 *//*--------------------------------------------------------------------*/
qpTestLog_createFileLog(const char * fileName,deUint32 flags)331 qpTestLog* qpTestLog_createFileLog (const char* fileName, deUint32 flags)
332 {
333 qpTestLog* log = (qpTestLog*)deCalloc(sizeof(qpTestLog));
334 if (!log)
335 return DE_NULL;
336
337 DE_ASSERT(fileName && fileName[0]); /* must have filename. */
338
339 #if defined(DE_DEBUG)
340 ContainerStack_reset(&log->containerStack);
341 #endif
342
343 /* Create output file. */
344 log->outputFile = fopen(fileName, "wb");
345 if (!log->outputFile)
346 {
347 qpPrintf("ERROR: Unable to open test log output file '%s'.\n", fileName);
348 qpTestLog_destroy(log);
349 return DE_NULL;
350 }
351
352 log->flags = flags;
353 log->writer = qpXmlWriter_createFileWriter(log->outputFile, 0);
354 log->lock = deMutex_create(DE_NULL);
355 log->isSessionOpen = DE_FALSE;
356 log->isCaseOpen = DE_FALSE;
357
358 if (!log->writer)
359 {
360 qpPrintf("ERROR: Unable to create output XML writer to file '%s'.\n", fileName);
361 qpTestLog_destroy(log);
362 return DE_NULL;
363 }
364
365 if (!log->lock)
366 {
367 qpPrintf("ERROR: Unable to create mutex.\n");
368 qpTestLog_destroy(log);
369 return DE_NULL;
370 }
371
372 beginSession(log);
373
374 return log;
375 }
376
377 /*--------------------------------------------------------------------*//*!
378 * \brief Destroy a logger instance
379 * \param a qpTestLog instance
380 *//*--------------------------------------------------------------------*/
qpTestLog_destroy(qpTestLog * log)381 void qpTestLog_destroy (qpTestLog* log)
382 {
383 DE_ASSERT(log);
384
385 if (log->isSessionOpen)
386 endSession(log);
387
388 if (log->writer)
389 qpXmlWriter_destroy(log->writer);
390
391 if (log->outputFile)
392 fclose(log->outputFile);
393
394 if (log->lock)
395 deMutex_destroy(log->lock);
396
397 deFree(log);
398 }
399
400 /*--------------------------------------------------------------------*//*!
401 * \brief Log start of test case
402 * \param log qpTestLog instance
403 * \param testCasePath Full test case path (as seen in Candy).
404 * \param testCaseType Test case type
405 * \return true if ok, false otherwise
406 *//*--------------------------------------------------------------------*/
qpTestLog_startCase(qpTestLog * log,const char * testCasePath,qpTestCaseType testCaseType)407 deBool qpTestLog_startCase (qpTestLog* log, const char* testCasePath, qpTestCaseType testCaseType)
408 {
409 const char* typeStr = QP_LOOKUP_STRING(s_qpTestTypeMap, testCaseType);
410 int numResultAttribs = 0;
411 qpXmlAttribute resultAttribs[8];
412
413 DE_ASSERT(log && testCasePath && (testCasePath[0] != 0));
414 deMutex_lock(log->lock);
415
416 DE_ASSERT(!log->isCaseOpen);
417 DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
418
419 /* Flush XML and write out #beginTestCaseResult. */
420 qpXmlWriter_flush(log->writer);
421 fprintf(log->outputFile, "\n#beginTestCaseResult %s\n", testCasePath);
422 qpTestLog_flushFile(log);
423
424 log->isCaseOpen = DE_TRUE;
425
426 /* Fill in attributes. */
427 resultAttribs[numResultAttribs++] = qpSetStringAttrib("Version", LOG_FORMAT_VERSION);
428 resultAttribs[numResultAttribs++] = qpSetStringAttrib("CasePath", testCasePath);
429 resultAttribs[numResultAttribs++] = qpSetStringAttrib("CaseType", typeStr);
430
431 if (!qpXmlWriter_startDocument(log->writer) ||
432 !qpXmlWriter_startElement(log->writer, "TestCaseResult", numResultAttribs, resultAttribs))
433 {
434 qpPrintf("qpTestLog_startCase(): Writing XML failed\n");
435 deMutex_unlock(log->lock);
436 return DE_FALSE;
437 }
438
439 deMutex_unlock(log->lock);
440 return DE_TRUE;
441 }
442
443 /*--------------------------------------------------------------------*//*!
444 * \brief Log end of test case
445 * \param log qpTestLog instance
446 * \param result Test result
447 * \param description Description of a problem in case of error
448 * \return true if ok, false otherwise
449 *//*--------------------------------------------------------------------*/
qpTestLog_endCase(qpTestLog * log,qpTestResult result,const char * resultDetails)450 deBool qpTestLog_endCase (qpTestLog* log, qpTestResult result, const char* resultDetails)
451 {
452 const char* statusStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
453 qpXmlAttribute statusAttrib = qpSetStringAttrib("StatusCode", statusStr);
454
455 DE_ASSERT(log && log->isCaseOpen);
456 DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
457 deMutex_lock(log->lock);
458
459 /* <Result StatusCode="Pass">Result details</Result>
460 * </TestCaseResult>
461 */
462 if (!qpXmlWriter_startElement(log->writer, "Result", 1, &statusAttrib) ||
463 (resultDetails && !qpXmlWriter_writeString(log->writer, resultDetails)) ||
464 !qpXmlWriter_endElement(log->writer, "Result") ||
465 !qpXmlWriter_endElement(log->writer, "TestCaseResult") ||
466 !qpXmlWriter_endDocument(log->writer)) /* Close any XML elements still open */
467 {
468 qpPrintf("qpTestLog_endCase(): Writing XML failed\n");
469 deMutex_unlock(log->lock);
470 return DE_FALSE;
471 }
472
473 /* Flush XML and write #endTestCaseResult. */
474 qpXmlWriter_flush(log->writer);
475 fprintf(log->outputFile, "\n#endTestCaseResult\n");
476 qpTestLog_flushFile(log);
477
478 log->isCaseOpen = DE_FALSE;
479
480 deMutex_unlock(log->lock);
481 return DE_TRUE;
482 }
483
484 /*--------------------------------------------------------------------*//*!
485 * \brief Abrupt termination of logging.
486 * \param log qpTestLog instance
487 * \param result Result code, only Crash and Timeout are allowed.
488 * \return true if ok, false otherwise
489 *//*--------------------------------------------------------------------*/
qpTestLog_terminateCase(qpTestLog * log,qpTestResult result)490 deBool qpTestLog_terminateCase (qpTestLog* log, qpTestResult result)
491 {
492 const char* resultStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
493
494 DE_ASSERT(log);
495 DE_ASSERT(result == QP_TEST_RESULT_CRASH || result == QP_TEST_RESULT_TIMEOUT);
496
497 deMutex_lock(log->lock);
498
499 if (!log->isCaseOpen)
500 {
501 deMutex_unlock(log->lock);
502 return DE_FALSE; /* Soft error. This is called from error handler. */
503 }
504
505 /* Flush XML and write #terminateTestCaseResult. */
506 qpXmlWriter_flush(log->writer);
507 fprintf(log->outputFile, "\n#terminateTestCaseResult %s\n", resultStr);
508 qpTestLog_flushFile(log);
509
510 log->isCaseOpen = DE_FALSE;
511
512 #if defined(DE_DEBUG)
513 ContainerStack_reset(&log->containerStack);
514 #endif
515
516 deMutex_unlock(log->lock);
517 return DE_TRUE;
518 }
519
qpTestLog_writeKeyValuePair(qpTestLog * log,const char * elementName,const char * name,const char * description,const char * unit,qpKeyValueTag tag,const char * text)520 static deBool qpTestLog_writeKeyValuePair (qpTestLog* log, const char* elementName, const char* name, const char* description, const char* unit, qpKeyValueTag tag, const char* text)
521 {
522 const char* tagString = QP_LOOKUP_STRING(s_qpTagMap, tag);
523 qpXmlAttribute attribs[8];
524 int numAttribs = 0;
525
526 DE_ASSERT(log && elementName && text);
527 deMutex_lock(log->lock);
528
529 /* Fill in attributes. */
530 if (name) attribs[numAttribs++] = qpSetStringAttrib("Name", name);
531 if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
532 if (tagString) attribs[numAttribs++] = qpSetStringAttrib("Tag", tagString);
533 if (unit) attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
534
535 if (!qpXmlWriter_startElement(log->writer, elementName, numAttribs, attribs) ||
536 !qpXmlWriter_writeString(log->writer, text) ||
537 !qpXmlWriter_endElement(log->writer, elementName))
538 {
539 qpPrintf("qpTestLog_writeKeyValuePair(): Writing XML failed\n");
540 deMutex_unlock(log->lock);
541 return DE_FALSE;
542 }
543
544 deMutex_unlock(log->lock);
545 return DE_TRUE;
546 }
547
548 /*--------------------------------------------------------------------*//*!
549 * \brief Write a message to output log
550 * \param log qpTestLog instance
551 * \param format Format string of message
552 * \param ... Parameters for message
553 * \return true if ok, false otherwise
554 *//*--------------------------------------------------------------------*/
qpTestLog_writeMessage(qpTestLog * log,const char * format,...)555 deBool qpTestLog_writeMessage (qpTestLog* log, const char* format, ...)
556 {
557 char buffer[1024];
558 va_list args;
559
560 /* \todo [petri] Handle buffer overflows! */
561
562 va_start(args, format);
563 buffer[DE_LENGTH_OF_ARRAY(buffer) - 1] = 0;
564 vsnprintf(buffer, sizeof(buffer), format, args);
565 va_end(args);
566
567 printf("%s\n", buffer);
568
569 /* <Text>text</Text> */
570 return qpTestLog_writeKeyValuePair(log, "Text", DE_NULL, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, buffer);
571 }
572
573 /*--------------------------------------------------------------------*//*!
574 * \brief Write key-value-pair into log
575 * \param log qpTestLog instance
576 * \param name Unique identifier for entry
577 * \param description Human readable description
578 * \param tag Optional tag
579 * \param value Value of the key-value-pair
580 * \return true if ok, false otherwise
581 *//*--------------------------------------------------------------------*/
qpTestLog_writeText(qpTestLog * log,const char * name,const char * description,qpKeyValueTag tag,const char * text)582 deBool qpTestLog_writeText (qpTestLog* log, const char* name, const char* description, qpKeyValueTag tag, const char* text)
583 {
584 /* <Text Name="name" Description="description" Tag="tag">text</Text> */
585 return qpTestLog_writeKeyValuePair(log, "Text", name, description, DE_NULL, tag, text);
586 }
587
588 /*--------------------------------------------------------------------*//*!
589 * \brief Write key-value-pair into log
590 * \param log qpTestLog instance
591 * \param name Unique identifier for entry
592 * \param description Human readable description
593 * \param tag Optional tag
594 * \param value Value of the key-value-pair
595 * \return true if ok, false otherwise
596 *//*--------------------------------------------------------------------*/
qpTestLog_writeInteger(qpTestLog * log,const char * name,const char * description,const char * unit,qpKeyValueTag tag,deInt64 value)597 deBool qpTestLog_writeInteger (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
598 {
599 char tmpString[64];
600 int64ToString(value, tmpString);
601
602 printf("%s = %lld %s\n", description, (signed long long)value, unit ? unit : "");
603
604 /* <Number Name="name" Description="description" Tag="Performance">15</Number> */
605 return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
606 }
607
608 /*--------------------------------------------------------------------*//*!
609 * \brief Write key-value-pair into log
610 * \param log qpTestLog instance
611 * \param name Unique identifier for entry
612 * \param description Human readable description
613 * \param tag Optional tag
614 * \param value Value of the key-value-pair
615 * \return true if ok, false otherwise
616 *//*--------------------------------------------------------------------*/
qpTestLog_writeFloat(qpTestLog * log,const char * name,const char * description,const char * unit,qpKeyValueTag tag,float value)617 deBool qpTestLog_writeFloat (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
618 {
619 char tmpString[64];
620 floatToString(value, tmpString, sizeof(tmpString));
621
622 printf("%s = %f %s\n", description, value, unit ? unit : "");
623
624 /* <Number Name="name" Description="description" Tag="Performance">15</Number> */
625 return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
626 }
627
628 typedef struct Buffer_s
629 {
630 int capacity;
631 int size;
632 deUint8* data;
633 } Buffer;
634
Buffer_init(Buffer * buffer)635 void Buffer_init (Buffer* buffer)
636 {
637 buffer->capacity = 0;
638 buffer->size = 0;
639 buffer->data = DE_NULL;
640 }
641
Buffer_deinit(Buffer * buffer)642 void Buffer_deinit (Buffer* buffer)
643 {
644 deFree(buffer->data);
645 Buffer_init(buffer);
646 }
647
Buffer_resize(Buffer * buffer,int newSize)648 deBool Buffer_resize (Buffer* buffer, int newSize)
649 {
650 /* Grow buffer if necessary. */
651 if (newSize > buffer->capacity)
652 {
653 int newCapacity = deAlign32(deMax32(2*buffer->capacity, newSize), 512);
654 deUint8* newData = (deUint8*)deMalloc(newCapacity);
655 if (!newData)
656 return DE_FALSE;
657
658 memcpy(newData, buffer->data, buffer->size);
659 deFree(buffer->data);
660 buffer->data = newData;
661 buffer->capacity = newCapacity;
662 }
663
664 buffer->size = newSize;
665 return DE_TRUE;
666 }
667
Buffer_append(Buffer * buffer,const deUint8 * data,int numBytes)668 deBool Buffer_append (Buffer* buffer, const deUint8* data, int numBytes)
669 {
670 int offset = buffer->size;
671
672 if (!Buffer_resize(buffer, buffer->size + numBytes))
673 return DE_FALSE;
674
675 /* Append bytes. */
676 memcpy(&buffer->data[offset], data, numBytes);
677 return DE_TRUE;
678 }
679
680 #if defined(QP_SUPPORT_PNG)
pngWriteData(png_structp png,png_bytep dataPtr,png_size_t numBytes)681 void pngWriteData (png_structp png, png_bytep dataPtr, png_size_t numBytes)
682 {
683 Buffer* buffer = (Buffer*)png_get_io_ptr(png);
684 if (!Buffer_append(buffer, (const deUint8*)dataPtr, (int)numBytes))
685 png_error(png, "unable to resize PNG write buffer!");
686 }
687
pngFlushData(png_structp png)688 void pngFlushData (png_structp png)
689 {
690 DE_UNREF(png);
691 /* nada */
692 }
693
writeCompressedPNG(png_structp png,png_infop info,png_byte ** rowPointers,int width,int height,int colorFormat)694 static deBool writeCompressedPNG (png_structp png, png_infop info, png_byte** rowPointers, int width, int height, int colorFormat)
695 {
696 if (setjmp(png_jmpbuf(png)) == 0)
697 {
698 /* Write data. */
699 png_set_IHDR(png, info, width, height,
700 8,
701 colorFormat,
702 PNG_INTERLACE_NONE,
703 PNG_COMPRESSION_TYPE_BASE,
704 PNG_FILTER_TYPE_BASE);
705 png_write_info(png, info);
706 png_write_image(png, rowPointers);
707 png_write_end(png, NULL);
708
709 return DE_TRUE;
710 }
711 else
712 return DE_FALSE;
713 }
714
compressImagePNG(Buffer * buffer,qpImageFormat imageFormat,int width,int height,int rowStride,const void * data)715 static deBool compressImagePNG (Buffer* buffer, qpImageFormat imageFormat, int width, int height, int rowStride, const void* data)
716 {
717 deBool compressOk = DE_FALSE;
718 png_structp png = DE_NULL;
719 png_infop info = DE_NULL;
720 png_byte** rowPointers = DE_NULL;
721 deBool hasAlpha = imageFormat == QP_IMAGE_FORMAT_RGBA8888;
722 int ndx;
723
724 /* Handle format. */
725 DE_ASSERT(imageFormat == QP_IMAGE_FORMAT_RGB888 || imageFormat == QP_IMAGE_FORMAT_RGBA8888);
726
727 /* Allocate & set row pointers. */
728 rowPointers = (png_byte**)deMalloc(height * sizeof(png_byte*));
729 if (!rowPointers)
730 return DE_FALSE;
731
732 for (ndx = 0; ndx < height; ndx++)
733 rowPointers[ndx] = (png_byte*)((const deUint8*)data + ndx*rowStride);
734
735 /* Initialize PNG compressor. */
736 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
737 info = png ? png_create_info_struct(png) : DE_NULL;
738 if (png && info)
739 {
740 /* Set our own write function. */
741 png_set_write_fn(png, buffer, pngWriteData, pngFlushData);
742
743 compressOk = writeCompressedPNG(png, info, rowPointers, width, height,
744 hasAlpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
745 }
746
747 /* Cleanup & return. */
748 if (png && info)
749 {
750 png_destroy_info_struct(png, &info);
751 png_destroy_write_struct(&png, DE_NULL);
752 }
753 else if (png)
754 png_destroy_write_struct(&png, &info);
755
756 deFree(rowPointers);
757 return compressOk;
758 }
759 #endif /* QP_SUPPORT_PNG */
760
761 /*--------------------------------------------------------------------*//*!
762 * \brief Start image set
763 * \param log qpTestLog instance
764 * \param name Unique identifier for the set
765 * \param description Human readable description
766 * \return true if ok, false otherwise
767 *//*--------------------------------------------------------------------*/
qpTestLog_startImageSet(qpTestLog * log,const char * name,const char * description)768 deBool qpTestLog_startImageSet (qpTestLog* log, const char* name, const char* description)
769 {
770 qpXmlAttribute attribs[4];
771 int numAttribs = 0;
772
773 DE_ASSERT(log && name);
774 deMutex_lock(log->lock);
775
776 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
777 if (description)
778 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
779
780 /* <ImageSet Name="<name>"> */
781 if (!qpXmlWriter_startElement(log->writer, "ImageSet", numAttribs, attribs))
782 {
783 qpPrintf("qpTestLog_startImageSet(): Writing XML failed\n");
784 deMutex_unlock(log->lock);
785 return DE_FALSE;
786 }
787
788 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_IMAGESET));
789
790 deMutex_unlock(log->lock);
791 return DE_TRUE;
792 }
793
794 /*--------------------------------------------------------------------*//*!
795 * \brief End image set
796 * \param log qpTestLog instance
797 * \return true if ok, false otherwise
798 *//*--------------------------------------------------------------------*/
qpTestLog_endImageSet(qpTestLog * log)799 deBool qpTestLog_endImageSet (qpTestLog* log)
800 {
801 DE_ASSERT(log);
802 deMutex_lock(log->lock);
803
804 /* <ImageSet Name="<name>"> */
805 if (!qpXmlWriter_endElement(log->writer, "ImageSet"))
806 {
807 qpPrintf("qpTestLog_endImageSet(): Writing XML failed\n");
808 deMutex_unlock(log->lock);
809 return DE_FALSE;
810 }
811
812 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_IMAGESET);
813
814 deMutex_unlock(log->lock);
815 return DE_TRUE;
816 }
817
818 /*--------------------------------------------------------------------*//*!
819 * \brief Write base64 encoded raw image data into log
820 * \param log qpTestLog instance
821 * \param name Unique name (matching names can be compared across BatchResults).
822 * \param description Textual description (shown in Candy).
823 * \param compressionMode Compression mode
824 * \param imageFormat Color format
825 * \param width Width in pixels
826 * \param height Height in pixels
827 * \param stride Data stride (offset between rows)
828 * \param data Pointer to pixel data
829 * \return 0 if OK, otherwise <0
830 *//*--------------------------------------------------------------------*/
qpTestLog_writeImage(qpTestLog * log,const char * name,const char * description,qpImageCompressionMode compressionMode,qpImageFormat imageFormat,int width,int height,int stride,const void * data)831 deBool qpTestLog_writeImage (
832 qpTestLog* log,
833 const char* name,
834 const char* description,
835 qpImageCompressionMode compressionMode,
836 qpImageFormat imageFormat,
837 int width,
838 int height,
839 int stride,
840 const void* data)
841 {
842 char widthStr[32];
843 char heightStr[32];
844 qpXmlAttribute attribs[8];
845 int numAttribs = 0;
846 Buffer compressedBuffer;
847 const void* writeDataPtr = DE_NULL;
848 int writeDataBytes = -1;
849
850 DE_ASSERT(log && name);
851 DE_ASSERT(deInRange32(width, 1, 16384));
852 DE_ASSERT(deInRange32(height, 1, 16384));
853 DE_ASSERT(data);
854
855 if (log->flags & QP_TEST_LOG_EXCLUDE_IMAGES)
856 return DE_TRUE; /* Image not logged. */
857
858 Buffer_init(&compressedBuffer);
859
860 /* BEST compression mode defaults to PNG. */
861 if (compressionMode == QP_IMAGE_COMPRESSION_MODE_BEST)
862 {
863 #if defined(QP_SUPPORT_PNG)
864 compressionMode = QP_IMAGE_COMPRESSION_MODE_PNG;
865 #else
866 compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
867 #endif
868 }
869
870 #if defined(QP_SUPPORT_PNG)
871 /* Try storing with PNG compression. */
872 if (compressionMode == QP_IMAGE_COMPRESSION_MODE_PNG)
873 {
874 deBool compressOk = compressImagePNG(&compressedBuffer, imageFormat, width, height, stride, data);
875 if (compressOk)
876 {
877 writeDataPtr = compressedBuffer.data;
878 writeDataBytes = compressedBuffer.size;
879 }
880 else
881 {
882 /* Fall-back to default compression. */
883 qpPrintf("WARNING: PNG compression failed -- storing image uncompressed.\n");
884 compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
885 }
886 }
887 #endif
888
889 /* Handle image compression. */
890 switch (compressionMode)
891 {
892 case QP_IMAGE_COMPRESSION_MODE_NONE:
893 {
894 int pixelSize = imageFormat == QP_IMAGE_FORMAT_RGB888 ? 3 : 4;
895 int packedStride = pixelSize*width;
896
897 if (packedStride == stride)
898 writeDataPtr = data;
899 else
900 {
901 /* Need to re-pack pixels. */
902 if (Buffer_resize(&compressedBuffer, packedStride*height))
903 {
904 int row;
905 for (row = 0; row < height; row++)
906 memcpy(&compressedBuffer.data[packedStride*row], &((const deUint8*)data)[row*stride], pixelSize*width);
907 }
908 else
909 {
910 qpPrintf("ERROR: Failed to pack pixels for writing.\n");
911 Buffer_deinit(&compressedBuffer);
912 return DE_FALSE;
913 }
914 }
915
916 writeDataBytes = packedStride*height;
917 break;
918 }
919
920 #if defined(QP_SUPPORT_PNG)
921 case QP_IMAGE_COMPRESSION_MODE_PNG:
922 DE_ASSERT(writeDataPtr); /* Already handled. */
923 break;
924 #endif
925
926 default:
927 qpPrintf("qpTestLog_writeImage(): Unknown compression mode: %s\n", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
928 Buffer_deinit(&compressedBuffer);
929 return DE_FALSE;
930 }
931
932 /* Fill in attributes. */
933 int32ToString(width, widthStr);
934 int32ToString(height, heightStr);
935 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
936 attribs[numAttribs++] = qpSetStringAttrib("Width", widthStr);
937 attribs[numAttribs++] = qpSetStringAttrib("Height", heightStr);
938 attribs[numAttribs++] = qpSetStringAttrib("Format", QP_LOOKUP_STRING(s_qpImageFormatMap, imageFormat));
939 attribs[numAttribs++] = qpSetStringAttrib("CompressionMode", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
940 if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
941
942 /* \note Log lock is acquired after compression! */
943 deMutex_lock(log->lock);
944
945 /* <Image ID="result" Name="Foobar" Width="640" Height="480" Format="RGB888" CompressionMode="None">base64 data</Image> */
946 if (!qpXmlWriter_startElement(log->writer, "Image", numAttribs, attribs) ||
947 !qpXmlWriter_writeBase64(log->writer, (const deUint8*)writeDataPtr, writeDataBytes) ||
948 !qpXmlWriter_endElement(log->writer, "Image"))
949 {
950 qpPrintf("qpTestLog_writeImage(): Writing XML failed\n");
951 deMutex_unlock(log->lock);
952 Buffer_deinit(&compressedBuffer);
953 return DE_FALSE;
954 }
955
956 deMutex_unlock(log->lock);
957
958 /* Free compressed data if allocated. */
959 Buffer_deinit(&compressedBuffer);
960
961 return DE_TRUE;
962 }
963
964 /*--------------------------------------------------------------------*//*!
965 * \brief Write a OpenGL ES shader program into the log.
966 * \param linkOk Shader program link result, false on failure
967 * \param linkInfoLog Implementation provided linkage log
968 *//*--------------------------------------------------------------------*/
qpTestLog_startShaderProgram(qpTestLog * log,deBool linkOk,const char * linkInfoLog)969 deBool qpTestLog_startShaderProgram (qpTestLog* log, deBool linkOk, const char* linkInfoLog)
970 {
971 qpXmlAttribute programAttribs[4];
972 int numProgramAttribs = 0;
973
974 DE_ASSERT(log);
975 deMutex_lock(log->lock);
976
977 programAttribs[numProgramAttribs++] = qpSetStringAttrib("LinkStatus", linkOk ? "OK" : "Fail");
978
979 if (!qpXmlWriter_startElement(log->writer, "ShaderProgram", numProgramAttribs, programAttribs) ||
980 !qpXmlWriter_writeStringElement(log->writer, "InfoLog", linkInfoLog))
981 {
982 qpPrintf("qpTestLog_startShaderProgram(): Writing XML failed\n");
983 deMutex_unlock(log->lock);
984 return DE_FALSE;
985 }
986
987 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SHADERPROGRAM));
988
989 deMutex_unlock(log->lock);
990 return DE_TRUE;
991 }
992
993 /*--------------------------------------------------------------------*//*!
994 * \brief End shader program
995 * \param log qpTestLog instance
996 * \return true if ok, false otherwise
997 *//*--------------------------------------------------------------------*/
qpTestLog_endShaderProgram(qpTestLog * log)998 deBool qpTestLog_endShaderProgram (qpTestLog* log)
999 {
1000 DE_ASSERT(log);
1001 deMutex_lock(log->lock);
1002
1003 /* </ShaderProgram> */
1004 if (!qpXmlWriter_endElement(log->writer, "ShaderProgram"))
1005 {
1006 qpPrintf("qpTestLog_endShaderProgram(): Writing XML failed\n");
1007 deMutex_unlock(log->lock);
1008 return DE_FALSE;
1009 }
1010
1011 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1012
1013 deMutex_unlock(log->lock);
1014 return DE_TRUE;
1015 }
1016
1017 /*--------------------------------------------------------------------*//*!
1018 * \brief Write a OpenGL ES shader into the log.
1019 * \param type Shader type
1020 * \param source Shader source
1021 * \param compileOk Shader compilation result, false on failure
1022 * \param infoLog Implementation provided shader compilation log
1023 *//*--------------------------------------------------------------------*/
qpTestLog_writeShader(qpTestLog * log,qpShaderType type,const char * source,deBool compileOk,const char * infoLog)1024 deBool qpTestLog_writeShader (qpTestLog* log, qpShaderType type, const char* source, deBool compileOk, const char* infoLog)
1025 {
1026 const char* tagName = QP_LOOKUP_STRING(s_qpShaderTypeMap, type);
1027 int numShaderAttribs = 0;
1028 qpXmlAttribute shaderAttribs[4];
1029
1030 DE_ASSERT(log && source);
1031 DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1032 deMutex_lock(log->lock);
1033
1034 shaderAttribs[numShaderAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1035
1036 if (!qpXmlWriter_startElement(log->writer, tagName, numShaderAttribs, shaderAttribs) ||
1037 !qpXmlWriter_writeStringElement(log->writer, "ShaderSource", source) ||
1038 !qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1039 !qpXmlWriter_endElement(log->writer, tagName))
1040 {
1041 qpPrintf("qpTestLog_writeShader(): Writing XML failed\n");
1042 deMutex_unlock(log->lock);
1043 return DE_FALSE;
1044 }
1045
1046 deMutex_unlock(log->lock);
1047 return DE_TRUE;
1048 }
1049
1050 /*--------------------------------------------------------------------*//*!
1051 * \brief Start writing a set of EGL configurations into the log.
1052 *//*--------------------------------------------------------------------*/
qpTestLog_startEglConfigSet(qpTestLog * log,const char * name,const char * description)1053 deBool qpTestLog_startEglConfigSet (qpTestLog* log, const char* name, const char* description)
1054 {
1055 qpXmlAttribute attribs[4];
1056 int numAttribs = 0;
1057
1058 DE_ASSERT(log && name);
1059 deMutex_lock(log->lock);
1060
1061 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1062 if (description)
1063 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1064
1065 /* <EglConfigSet Name="<name>"> */
1066 if (!qpXmlWriter_startElement(log->writer, "EglConfigSet", numAttribs, attribs))
1067 {
1068 qpPrintf("qpTestLog_startEglImageSet(): Writing XML failed\n");
1069 deMutex_unlock(log->lock);
1070 return DE_FALSE;
1071 }
1072
1073 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_EGLCONFIGSET));
1074
1075 deMutex_unlock(log->lock);
1076 return DE_TRUE;
1077 }
1078
1079 /*--------------------------------------------------------------------*//*!
1080 * \brief End an EGL config set
1081 *//*--------------------------------------------------------------------*/
qpTestLog_endEglConfigSet(qpTestLog * log)1082 deBool qpTestLog_endEglConfigSet (qpTestLog* log)
1083 {
1084 DE_ASSERT(log);
1085 deMutex_lock(log->lock);
1086
1087 /* <EglConfigSet Name="<name>"> */
1088 if (!qpXmlWriter_endElement(log->writer, "EglConfigSet"))
1089 {
1090 qpPrintf("qpTestLog_endEglImageSet(): Writing XML failed\n");
1091 deMutex_unlock(log->lock);
1092 return DE_FALSE;
1093 }
1094
1095 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_EGLCONFIGSET);
1096
1097 deMutex_unlock(log->lock);
1098 return DE_TRUE;
1099 }
1100
1101 /*--------------------------------------------------------------------*//*!
1102 * \brief Write an EGL config inside an EGL config set
1103 * \see qpElgConfigInfo for details
1104 *//*--------------------------------------------------------------------*/
qpTestLog_writeEglConfig(qpTestLog * log,const qpEglConfigInfo * config)1105 deBool qpTestLog_writeEglConfig (qpTestLog* log, const qpEglConfigInfo* config)
1106 {
1107 qpXmlAttribute attribs[64];
1108 int numAttribs = 0;
1109
1110 DE_ASSERT(log && config);
1111 deMutex_lock(log->lock);
1112
1113 attribs[numAttribs++] = qpSetIntAttrib ("BufferSize", config->bufferSize);
1114 attribs[numAttribs++] = qpSetIntAttrib ("RedSize", config->redSize);
1115 attribs[numAttribs++] = qpSetIntAttrib ("GreenSize", config->greenSize);
1116 attribs[numAttribs++] = qpSetIntAttrib ("BlueSize", config->blueSize);
1117 attribs[numAttribs++] = qpSetIntAttrib ("LuminanceSize", config->luminanceSize);
1118 attribs[numAttribs++] = qpSetIntAttrib ("AlphaSize", config->alphaSize);
1119 attribs[numAttribs++] = qpSetIntAttrib ("AlphaMaskSize", config->alphaMaskSize);
1120 attribs[numAttribs++] = qpSetBoolAttrib ("BindToTextureRGB", config->bindToTextureRGB);
1121 attribs[numAttribs++] = qpSetBoolAttrib ("BindToTextureRGBA", config->bindToTextureRGBA);
1122 attribs[numAttribs++] = qpSetStringAttrib ("ColorBufferType", config->colorBufferType);
1123 attribs[numAttribs++] = qpSetStringAttrib ("ConfigCaveat", config->configCaveat);
1124 attribs[numAttribs++] = qpSetIntAttrib ("ConfigID", config->configID);
1125 attribs[numAttribs++] = qpSetStringAttrib ("Conformant", config->conformant);
1126 attribs[numAttribs++] = qpSetIntAttrib ("DepthSize", config->depthSize);
1127 attribs[numAttribs++] = qpSetIntAttrib ("Level", config->level);
1128 attribs[numAttribs++] = qpSetIntAttrib ("MaxPBufferWidth", config->maxPBufferWidth);
1129 attribs[numAttribs++] = qpSetIntAttrib ("MaxPBufferHeight", config->maxPBufferHeight);
1130 attribs[numAttribs++] = qpSetIntAttrib ("MaxPBufferPixels", config->maxPBufferPixels);
1131 attribs[numAttribs++] = qpSetIntAttrib ("MaxSwapInterval", config->maxSwapInterval);
1132 attribs[numAttribs++] = qpSetIntAttrib ("MinSwapInterval", config->minSwapInterval);
1133 attribs[numAttribs++] = qpSetBoolAttrib ("NativeRenderable", config->nativeRenderable);
1134 attribs[numAttribs++] = qpSetStringAttrib ("RenderableType", config->renderableType);
1135 attribs[numAttribs++] = qpSetIntAttrib ("SampleBuffers", config->sampleBuffers);
1136 attribs[numAttribs++] = qpSetIntAttrib ("Samples", config->samples);
1137 attribs[numAttribs++] = qpSetIntAttrib ("StencilSize", config->stencilSize);
1138 attribs[numAttribs++] = qpSetStringAttrib ("SurfaceTypes", config->surfaceTypes);
1139 attribs[numAttribs++] = qpSetStringAttrib ("TransparentType", config->transparentType);
1140 attribs[numAttribs++] = qpSetIntAttrib ("TransparentRedValue", config->transparentRedValue);
1141 attribs[numAttribs++] = qpSetIntAttrib ("TransparentGreenValue", config->transparentGreenValue);
1142 attribs[numAttribs++] = qpSetIntAttrib ("TransparentBlueValue", config->transparentBlueValue);
1143 DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
1144
1145 if (!qpXmlWriter_startElement(log->writer, "EglConfig", numAttribs, attribs) ||
1146 !qpXmlWriter_endElement(log->writer, "EglConfig"))
1147 {
1148 qpPrintf("qpTestLog_writeEglConfig(): Writing XML failed\n");
1149 deMutex_unlock(log->lock);
1150 return DE_FALSE;
1151 }
1152
1153 deMutex_unlock(log->lock);
1154 return DE_TRUE;
1155 }
1156
1157 /*--------------------------------------------------------------------*//*!
1158 * \brief Start section in log.
1159 * \param log qpTestLog instance
1160 * \param name Section name
1161 * \param description Human readable description
1162 * \return true if ok, false otherwise
1163 *//*--------------------------------------------------------------------*/
qpTestLog_startSection(qpTestLog * log,const char * name,const char * description)1164 deBool qpTestLog_startSection (qpTestLog* log, const char* name, const char* description)
1165 {
1166 qpXmlAttribute attribs[2];
1167 int numAttribs = 0;
1168
1169 DE_ASSERT(log && name);
1170 deMutex_lock(log->lock);
1171
1172 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1173 if (description)
1174 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1175
1176 /* <Section Name="<name>" Description="<description>"> */
1177 if (!qpXmlWriter_startElement(log->writer, "Section", numAttribs, attribs))
1178 {
1179 qpPrintf("qpTestLog_startSection(): Writing XML failed\n");
1180 deMutex_unlock(log->lock);
1181 return DE_FALSE;
1182 }
1183
1184 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SECTION));
1185
1186 deMutex_unlock(log->lock);
1187 return DE_TRUE;
1188 }
1189
1190 /*--------------------------------------------------------------------*//*!
1191 * \brief End section in log.
1192 * \param log qpTestLog instance
1193 * \return true if ok, false otherwise
1194 *//*--------------------------------------------------------------------*/
qpTestLog_endSection(qpTestLog * log)1195 deBool qpTestLog_endSection (qpTestLog* log)
1196 {
1197 DE_ASSERT(log);
1198 deMutex_lock(log->lock);
1199
1200 /* </Section> */
1201 if (!qpXmlWriter_endElement(log->writer, "Section"))
1202 {
1203 qpPrintf("qpTestLog_endSection(): Writing XML failed\n");
1204 deMutex_unlock(log->lock);
1205 return DE_FALSE;
1206 }
1207
1208 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SECTION);
1209
1210 deMutex_unlock(log->lock);
1211 return DE_TRUE;
1212 }
1213
1214 /*--------------------------------------------------------------------*//*!
1215 * \brief Write OpenCL compute kernel source into the log.
1216 *//*--------------------------------------------------------------------*/
qpTestLog_writeKernelSource(qpTestLog * log,const char * source)1217 deBool qpTestLog_writeKernelSource (qpTestLog* log, const char* source)
1218 {
1219 DE_ASSERT(log);
1220 deMutex_lock(log->lock);
1221
1222 if (!qpXmlWriter_writeStringElement(log->writer, "KernelSource", source))
1223 {
1224 qpPrintf("qpTestLog_writeKernelSource(): Writing XML failed\n");
1225 deMutex_unlock(log->lock);
1226 return DE_FALSE;
1227 }
1228
1229 deMutex_unlock(log->lock);
1230 return DE_TRUE;
1231 }
1232
1233 /*--------------------------------------------------------------------*//*!
1234 * \brief Write OpenCL kernel compilation results into the log
1235 *//*--------------------------------------------------------------------*/
qpTestLog_writeCompileInfo(qpTestLog * log,const char * name,const char * description,deBool compileOk,const char * infoLog)1236 deBool qpTestLog_writeCompileInfo (qpTestLog* log, const char* name, const char* description, deBool compileOk, const char* infoLog)
1237 {
1238 int numAttribs = 0;
1239 qpXmlAttribute attribs[3];
1240
1241 DE_ASSERT(log && name && description && infoLog);
1242 deMutex_lock(log->lock);
1243
1244 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1245 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1246 attribs[numAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1247
1248 if (!qpXmlWriter_startElement(log->writer, "CompileInfo", numAttribs, attribs) ||
1249 !qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1250 !qpXmlWriter_endElement(log->writer, "CompileInfo"))
1251 {
1252 qpPrintf("qpTestLog_writeCompileInfo(): Writing XML failed\n");
1253 deMutex_unlock(log->lock);
1254 return DE_FALSE;
1255 }
1256
1257 deMutex_unlock(log->lock);
1258 return DE_TRUE;
1259 }
1260
qpTestLog_startSampleList(qpTestLog * log,const char * name,const char * description)1261 deBool qpTestLog_startSampleList (qpTestLog* log, const char* name, const char* description)
1262 {
1263 int numAttribs = 0;
1264 qpXmlAttribute attribs[2];
1265
1266 DE_ASSERT(log && name && description);
1267 deMutex_lock(log->lock);
1268
1269 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1270 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1271
1272 if (!qpXmlWriter_startElement(log->writer, "SampleList", numAttribs, attribs))
1273 {
1274 qpPrintf("qpTestLog_startSampleList(): Writing XML failed\n");
1275 deMutex_unlock(log->lock);
1276 return DE_FALSE;
1277 }
1278
1279 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLELIST));
1280
1281 deMutex_unlock(log->lock);
1282 return DE_TRUE;
1283 }
1284
qpTestLog_startSampleInfo(qpTestLog * log)1285 deBool qpTestLog_startSampleInfo (qpTestLog* log)
1286 {
1287 DE_ASSERT(log);
1288 deMutex_lock(log->lock);
1289
1290 if (!qpXmlWriter_startElement(log->writer, "SampleInfo", 0, DE_NULL))
1291 {
1292 qpPrintf("qpTestLog_startSampleInfo(): Writing XML failed\n");
1293 deMutex_unlock(log->lock);
1294 return DE_FALSE;
1295 }
1296
1297 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLEINFO));
1298
1299 deMutex_unlock(log->lock);
1300 return DE_TRUE;
1301 }
1302
qpTestLog_writeValueInfo(qpTestLog * log,const char * name,const char * description,const char * unit,qpSampleValueTag tag)1303 deBool qpTestLog_writeValueInfo (qpTestLog* log, const char* name, const char* description, const char* unit, qpSampleValueTag tag)
1304 {
1305 const char* tagName = QP_LOOKUP_STRING(s_qpSampleValueTagMap, tag);
1306 int numAttribs = 0;
1307 qpXmlAttribute attribs[4];
1308
1309 DE_ASSERT(log && name && description && tagName);
1310 deMutex_lock(log->lock);
1311
1312 DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1313
1314 attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1315 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1316 attribs[numAttribs++] = qpSetStringAttrib("Tag", tagName);
1317
1318 if (unit)
1319 attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
1320
1321 if (!qpXmlWriter_startElement(log->writer, "ValueInfo", numAttribs, attribs) ||
1322 !qpXmlWriter_endElement(log->writer, "ValueInfo"))
1323 {
1324 qpPrintf("qpTestLog_writeValueInfo(): Writing XML failed\n");
1325 deMutex_unlock(log->lock);
1326 return DE_FALSE;
1327 }
1328
1329 deMutex_unlock(log->lock);
1330 return DE_TRUE;
1331 }
1332
qpTestLog_endSampleInfo(qpTestLog * log)1333 deBool qpTestLog_endSampleInfo (qpTestLog* log)
1334 {
1335 DE_ASSERT(log);
1336 deMutex_lock(log->lock);
1337
1338 if (!qpXmlWriter_endElement(log->writer, "SampleInfo"))
1339 {
1340 qpPrintf("qpTestLog_endSampleInfo(): Writing XML failed\n");
1341 deMutex_unlock(log->lock);
1342 return DE_FALSE;
1343 }
1344
1345 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1346
1347 deMutex_unlock(log->lock);
1348 return DE_TRUE;
1349 }
1350
qpTestLog_startSample(qpTestLog * log)1351 deBool qpTestLog_startSample (qpTestLog* log)
1352 {
1353 DE_ASSERT(log);
1354 deMutex_lock(log->lock);
1355
1356 DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1357
1358 if (!qpXmlWriter_startElement(log->writer, "Sample", 0, DE_NULL))
1359 {
1360 qpPrintf("qpTestLog_startSample(): Writing XML failed\n");
1361 deMutex_unlock(log->lock);
1362 return DE_FALSE;
1363 }
1364
1365 DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLE));
1366
1367 deMutex_unlock(log->lock);
1368 return DE_TRUE;
1369 }
1370
qpTestLog_writeValueFloat(qpTestLog * log,double value)1371 deBool qpTestLog_writeValueFloat (qpTestLog* log, double value)
1372 {
1373 char tmpString[512];
1374 doubleToString(value, tmpString, (int)sizeof(tmpString));
1375
1376 deMutex_lock(log->lock);
1377
1378 DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1379
1380 if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1381 {
1382 qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1383 deMutex_unlock(log->lock);
1384 return DE_FALSE;
1385 }
1386
1387 deMutex_unlock(log->lock);
1388 return DE_TRUE;
1389 }
1390
qpTestLog_writeValueInteger(qpTestLog * log,deInt64 value)1391 deBool qpTestLog_writeValueInteger (qpTestLog* log, deInt64 value)
1392 {
1393 char tmpString[64];
1394 int64ToString(value, tmpString);
1395
1396 deMutex_lock(log->lock);
1397
1398 DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1399
1400 if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1401 {
1402 qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1403 deMutex_unlock(log->lock);
1404 return DE_FALSE;
1405 }
1406
1407 deMutex_unlock(log->lock);
1408 return DE_TRUE;
1409 }
1410
qpTestLog_endSample(qpTestLog * log)1411 deBool qpTestLog_endSample (qpTestLog* log)
1412 {
1413 DE_ASSERT(log);
1414 deMutex_lock(log->lock);
1415
1416 if (!qpXmlWriter_endElement(log->writer, "Sample"))
1417 {
1418 qpPrintf("qpTestLog_endSample(): Writing XML failed\n");
1419 deMutex_unlock(log->lock);
1420 return DE_FALSE;
1421 }
1422
1423 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1424
1425 deMutex_unlock(log->lock);
1426 return DE_TRUE;
1427 }
1428
qpTestLog_endSampleList(qpTestLog * log)1429 deBool qpTestLog_endSampleList (qpTestLog* log)
1430 {
1431 DE_ASSERT(log);
1432 deMutex_lock(log->lock);
1433
1434 if (!qpXmlWriter_endElement(log->writer, "SampleList"))
1435 {
1436 qpPrintf("qpTestLog_endSampleList(): Writing XML failed\n");
1437 deMutex_unlock(log->lock);
1438 return DE_FALSE;
1439 }
1440
1441 DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1442
1443 deMutex_unlock(log->lock);
1444 return DE_TRUE;
1445 }
1446
qpTestLog_getLogFlags(const qpTestLog * log)1447 deUint32 qpTestLog_getLogFlags (const qpTestLog* log)
1448 {
1449 DE_ASSERT(log);
1450 return log->flags;
1451 }
1452
qpGetTestResultName(qpTestResult result)1453 const char* qpGetTestResultName (qpTestResult result)
1454 {
1455 return QP_LOOKUP_STRING(s_qpTestResultMap, result);
1456 }
1457