Merge \\\\\"DO NOT MERGE CP vertex array test and warning fixes\\\\\" into marshmallo...
[platform/upstream/VK-GL-CTS.git] / framework / qphelper / qpTestLog.c
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
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
81 DE_INLINE void ContainerStack_reset (ContainerStack* stack)
82 {
83         deMemset(stack, 0, sizeof(ContainerStack));
84 }
85
86 DE_INLINE deBool ContainerStack_isEmpty (const ContainerStack* stack)
87 {
88         return stack->numElements == 0;
89 }
90
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
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
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.4";
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
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
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
272 DE_INLINE void int32ToString (int val, char buf[32])
273 {
274         deSprintf(&buf[0], 32, "%d", val);
275 }
276
277 DE_INLINE void int64ToString (deInt64 val, char buf[32])
278 {
279         deSprintf(&buf[0], 32, "%lld", (long long int)val);
280 }
281
282 DE_INLINE void floatToString (float value, char* buf, size_t bufSize)
283 {
284         deSprintf(buf, bufSize, "%f", value);
285 }
286
287 DE_INLINE void doubleToString (double value, char* buf, size_t bufSize)
288 {
289         deSprintf(buf, bufSize, "%f", value);
290 }
291
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
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  *//*--------------------------------------------------------------------*/
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, !(flags & QP_TEST_LOG_NO_FLUSH));
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  *//*--------------------------------------------------------------------*/
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  *//*--------------------------------------------------------------------*/
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         if (!(log->flags & QP_TEST_LOG_NO_FLUSH))
423                 qpTestLog_flushFile(log);
424
425         log->isCaseOpen = DE_TRUE;
426
427         /* Fill in attributes. */
428         resultAttribs[numResultAttribs++] = qpSetStringAttrib("Version", LOG_FORMAT_VERSION);
429         resultAttribs[numResultAttribs++] = qpSetStringAttrib("CasePath", testCasePath);
430         resultAttribs[numResultAttribs++] = qpSetStringAttrib("CaseType", typeStr);
431
432         if (!qpXmlWriter_startDocument(log->writer) ||
433                 !qpXmlWriter_startElement(log->writer, "TestCaseResult", numResultAttribs, resultAttribs))
434         {
435                 qpPrintf("qpTestLog_startCase(): Writing XML failed\n");
436                 deMutex_unlock(log->lock);
437                 return DE_FALSE;
438         }
439
440         deMutex_unlock(log->lock);
441         return DE_TRUE;
442 }
443
444 /*--------------------------------------------------------------------*//*!
445  * \brief Log end of test case
446  * \param log qpTestLog instance
447  * \param result Test result
448  * \param description Description of a problem in case of error
449  * \return true if ok, false otherwise
450  *//*--------------------------------------------------------------------*/
451 deBool qpTestLog_endCase (qpTestLog* log, qpTestResult result, const char* resultDetails)
452 {
453         const char*             statusStr               = QP_LOOKUP_STRING(s_qpTestResultMap, result);
454         qpXmlAttribute  statusAttrib    = qpSetStringAttrib("StatusCode", statusStr);
455
456         DE_ASSERT(log && log->isCaseOpen);
457         DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
458         deMutex_lock(log->lock);
459
460         /* <Result StatusCode="Pass">Result details</Result>
461          * </TestCaseResult>
462          */
463         if (!qpXmlWriter_startElement(log->writer, "Result", 1, &statusAttrib) ||
464                 (resultDetails && !qpXmlWriter_writeString(log->writer, resultDetails)) ||
465                 !qpXmlWriter_endElement(log->writer, "Result") ||
466                 !qpXmlWriter_endElement(log->writer, "TestCaseResult") ||
467                 !qpXmlWriter_endDocument(log->writer))          /* Close any XML elements still open */
468         {
469                 qpPrintf("qpTestLog_endCase(): Writing XML failed\n");
470                 deMutex_unlock(log->lock);
471                 return DE_FALSE;
472         }
473
474         /* Flush XML and write #endTestCaseResult. */
475         qpXmlWriter_flush(log->writer);
476         fprintf(log->outputFile, "\n#endTestCaseResult\n");
477         if (!(log->flags & QP_TEST_LOG_NO_FLUSH))
478                 qpTestLog_flushFile(log);
479
480         log->isCaseOpen = DE_FALSE;
481
482         deMutex_unlock(log->lock);
483         return DE_TRUE;
484 }
485
486 /*--------------------------------------------------------------------*//*!
487  * \brief Abrupt termination of logging.
488  * \param log           qpTestLog instance
489  * \param result        Result code, only Crash and Timeout are allowed.
490  * \return true if ok, false otherwise
491  *//*--------------------------------------------------------------------*/
492 deBool qpTestLog_terminateCase (qpTestLog* log, qpTestResult result)
493 {
494         const char* resultStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
495
496         DE_ASSERT(log);
497         DE_ASSERT(result == QP_TEST_RESULT_CRASH || result == QP_TEST_RESULT_TIMEOUT);
498
499         deMutex_lock(log->lock);
500
501         if (!log->isCaseOpen)
502         {
503                 deMutex_unlock(log->lock);
504                 return DE_FALSE; /* Soft error. This is called from error handler. */
505         }
506
507         /* Flush XML and write #terminateTestCaseResult. */
508         qpXmlWriter_flush(log->writer);
509         fprintf(log->outputFile, "\n#terminateTestCaseResult %s\n", resultStr);
510         qpTestLog_flushFile(log);
511
512         log->isCaseOpen = DE_FALSE;
513
514 #if defined(DE_DEBUG)
515         ContainerStack_reset(&log->containerStack);
516 #endif
517
518         deMutex_unlock(log->lock);
519         return DE_TRUE;
520 }
521
522 static deBool qpTestLog_writeKeyValuePair (qpTestLog* log, const char* elementName, const char* name, const char* description, const char* unit, qpKeyValueTag tag, const char* text)
523 {
524         const char*             tagString = QP_LOOKUP_STRING(s_qpTagMap, tag);
525         qpXmlAttribute  attribs[8];
526         int                             numAttribs = 0;
527
528         DE_ASSERT(log && elementName && text);
529         deMutex_lock(log->lock);
530
531         /* Fill in attributes. */
532         if (name)                       attribs[numAttribs++] = qpSetStringAttrib("Name", name);
533         if (description)        attribs[numAttribs++] = qpSetStringAttrib("Description", description);
534         if (tagString)          attribs[numAttribs++] = qpSetStringAttrib("Tag", tagString);
535         if (unit)                       attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
536
537         if (!qpXmlWriter_startElement(log->writer, elementName, numAttribs, attribs) ||
538                 !qpXmlWriter_writeString(log->writer, text) ||
539                 !qpXmlWriter_endElement(log->writer, elementName))
540         {
541                 qpPrintf("qpTestLog_writeKeyValuePair(): Writing XML failed\n");
542                 deMutex_unlock(log->lock);
543                 return DE_FALSE;
544         }
545
546         deMutex_unlock(log->lock);
547         return DE_TRUE;
548 }
549
550 /*--------------------------------------------------------------------*//*!
551  * \brief Write a message to output log
552  * \param log           qpTestLog instance
553  * \param format        Format string of message
554  * \param ...           Parameters for message
555  * \return true if ok, false otherwise
556  *//*--------------------------------------------------------------------*/
557 deBool qpTestLog_writeMessage (qpTestLog* log, const char* format, ...)
558 {
559         char    buffer[1024];
560         va_list args;
561
562         /* \todo [petri] Handle buffer overflows! */
563
564         va_start(args, format);
565         buffer[DE_LENGTH_OF_ARRAY(buffer) - 1] = 0;
566         vsnprintf(buffer, sizeof(buffer), format, args);
567         va_end(args);
568
569         printf("%s\n", buffer);
570
571         /* <Text>text</Text> */
572         return qpTestLog_writeKeyValuePair(log, "Text", DE_NULL, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, buffer);
573 }
574
575 /*--------------------------------------------------------------------*//*!
576  * \brief Write key-value-pair into log
577  * \param log                   qpTestLog instance
578  * \param name                  Unique identifier for entry
579  * \param description   Human readable description
580  * \param tag                   Optional tag
581  * \param value                 Value of the key-value-pair
582  * \return true if ok, false otherwise
583  *//*--------------------------------------------------------------------*/
584 deBool qpTestLog_writeText (qpTestLog* log, const char* name, const char* description, qpKeyValueTag tag, const char* text)
585 {
586         /* <Text Name="name" Description="description" Tag="tag">text</Text> */
587         return qpTestLog_writeKeyValuePair(log, "Text", name, description, DE_NULL, tag, text);
588 }
589
590 /*--------------------------------------------------------------------*//*!
591  * \brief Write key-value-pair into log
592  * \param log                   qpTestLog instance
593  * \param name                  Unique identifier for entry
594  * \param description   Human readable description
595  * \param tag                   Optional tag
596  * \param value                 Value of the key-value-pair
597  * \return true if ok, false otherwise
598  *//*--------------------------------------------------------------------*/
599 deBool qpTestLog_writeInteger (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
600 {
601         char tmpString[64];
602         int64ToString(value, tmpString);
603
604         printf("%s = %lld %s\n", description, (signed long long)value, unit ? unit : "");
605
606         /* <Number Name="name" Description="description" Tag="Performance">15</Number> */
607         return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
608 }
609
610 /*--------------------------------------------------------------------*//*!
611  * \brief Write key-value-pair into log
612  * \param log                   qpTestLog instance
613  * \param name                  Unique identifier for entry
614  * \param description   Human readable description
615  * \param tag                   Optional tag
616  * \param value                 Value of the key-value-pair
617  * \return true if ok, false otherwise
618  *//*--------------------------------------------------------------------*/
619 deBool qpTestLog_writeFloat (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
620 {
621         char tmpString[64];
622         floatToString(value, tmpString, sizeof(tmpString));
623
624         printf("%s = %f %s\n", description, value, unit ? unit : "");
625
626         /* <Number Name="name" Description="description" Tag="Performance">15</Number> */
627         return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
628 }
629
630 typedef struct Buffer_s
631 {
632         size_t          capacity;
633         size_t          size;
634         deUint8*        data;
635 } Buffer;
636
637 void Buffer_init (Buffer* buffer)
638 {
639         buffer->capacity        = 0;
640         buffer->size            = 0;
641         buffer->data            = DE_NULL;
642 }
643
644 void Buffer_deinit (Buffer* buffer)
645 {
646         deFree(buffer->data);
647         Buffer_init(buffer);
648 }
649
650 deBool Buffer_resize (Buffer* buffer, size_t newSize)
651 {
652         /* Grow buffer if necessary. */
653         if (newSize > buffer->capacity)
654         {
655                 size_t          newCapacity     = (size_t)deAlign32(deMax32(2*(int)buffer->capacity, (int)newSize), 512);
656                 deUint8*        newData         = (deUint8*)deMalloc(newCapacity);
657                 if (!newData)
658                         return DE_FALSE;
659
660                 memcpy(newData, buffer->data, buffer->size);
661                 deFree(buffer->data);
662                 buffer->data            = newData;
663                 buffer->capacity        = newCapacity;
664         }
665
666         buffer->size = newSize;
667         return DE_TRUE;
668 }
669
670 deBool Buffer_append (Buffer* buffer, const deUint8* data, size_t numBytes)
671 {
672         size_t offset = buffer->size;
673
674         if (!Buffer_resize(buffer, buffer->size + numBytes))
675                 return DE_FALSE;
676
677         /* Append bytes. */
678         memcpy(&buffer->data[offset], data, numBytes);
679         return DE_TRUE;
680 }
681
682 #if defined(QP_SUPPORT_PNG)
683 void pngWriteData (png_structp png, png_bytep dataPtr, png_size_t numBytes)
684 {
685         Buffer* buffer = (Buffer*)png_get_io_ptr(png);
686         if (!Buffer_append(buffer, (const deUint8*)dataPtr, numBytes))
687                 png_error(png, "unable to resize PNG write buffer!");
688 }
689
690 void pngFlushData (png_structp png)
691 {
692         DE_UNREF(png);
693         /* nada */
694 }
695
696 static deBool writeCompressedPNG (png_structp png, png_infop info, png_byte** rowPointers, int width, int height, int colorFormat)
697 {
698         if (setjmp(png_jmpbuf(png)) == 0)
699         {
700                 /* Write data. */
701                 png_set_IHDR(png, info, (png_uint_32)width, (png_uint_32)height,
702                         8,
703                         colorFormat,
704                         PNG_INTERLACE_NONE,
705                         PNG_COMPRESSION_TYPE_BASE,
706                         PNG_FILTER_TYPE_BASE);
707                 png_write_info(png, info);
708                 png_write_image(png, rowPointers);
709                 png_write_end(png, NULL);
710
711                 return DE_TRUE;
712         }
713         else
714                 return DE_FALSE;
715 }
716
717 static deBool compressImagePNG (Buffer* buffer, qpImageFormat imageFormat, int width, int height, int rowStride, const void* data)
718 {
719         deBool                  compressOk              = DE_FALSE;
720         png_structp             png                             = DE_NULL;
721         png_infop               info                    = DE_NULL;
722         png_byte**              rowPointers             = DE_NULL;
723         deBool                  hasAlpha                = imageFormat == QP_IMAGE_FORMAT_RGBA8888;
724         int                             ndx;
725
726         /* Handle format. */
727         DE_ASSERT(imageFormat == QP_IMAGE_FORMAT_RGB888 || imageFormat == QP_IMAGE_FORMAT_RGBA8888);
728
729         /* Allocate & set row pointers. */
730         rowPointers = (png_byte**)deMalloc((size_t)height * sizeof(png_byte*));
731         if (!rowPointers)
732                 return DE_FALSE;
733
734         for (ndx = 0; ndx < height; ndx++)
735                 rowPointers[ndx] = (png_byte*)((const deUint8*)data + ndx*rowStride);
736
737         /* Initialize PNG compressor. */
738         png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
739         info = png ? png_create_info_struct(png) : DE_NULL;
740         if (png && info)
741         {
742                 /* Set our own write function. */
743                 png_set_write_fn(png, buffer, pngWriteData, pngFlushData);
744
745                 compressOk = writeCompressedPNG(png, info, rowPointers, width, height,
746                                                                                 hasAlpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
747         }
748
749         /* Cleanup & return. */
750         if (png && info)
751         {
752                 png_destroy_info_struct(png, &info);
753                 png_destroy_write_struct(&png, DE_NULL);
754         }
755         else if (png)
756                 png_destroy_write_struct(&png, &info);
757
758         deFree(rowPointers);
759         return compressOk;
760 }
761 #endif /* QP_SUPPORT_PNG */
762
763 /*--------------------------------------------------------------------*//*!
764  * \brief Start image set
765  * \param log                   qpTestLog instance
766  * \param name                  Unique identifier for the set
767  * \param description   Human readable description
768  * \return true if ok, false otherwise
769  *//*--------------------------------------------------------------------*/
770 deBool qpTestLog_startImageSet (qpTestLog* log, const char* name, const char* description)
771 {
772         qpXmlAttribute  attribs[4];
773         int                             numAttribs = 0;
774
775         DE_ASSERT(log && name);
776         deMutex_lock(log->lock);
777
778         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
779         if (description)
780                 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
781
782         /* <ImageSet Name="<name>"> */
783         if (!qpXmlWriter_startElement(log->writer, "ImageSet", numAttribs, attribs))
784         {
785                 qpPrintf("qpTestLog_startImageSet(): Writing XML failed\n");
786                 deMutex_unlock(log->lock);
787                 return DE_FALSE;
788         }
789
790         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_IMAGESET));
791
792         deMutex_unlock(log->lock);
793         return DE_TRUE;
794 }
795
796 /*--------------------------------------------------------------------*//*!
797  * \brief End image set
798  * \param log                   qpTestLog instance
799  * \return true if ok, false otherwise
800  *//*--------------------------------------------------------------------*/
801 deBool qpTestLog_endImageSet (qpTestLog* log)
802 {
803         DE_ASSERT(log);
804         deMutex_lock(log->lock);
805
806         /* <ImageSet Name="<name>"> */
807         if (!qpXmlWriter_endElement(log->writer, "ImageSet"))
808         {
809                 qpPrintf("qpTestLog_endImageSet(): Writing XML failed\n");
810                 deMutex_unlock(log->lock);
811                 return DE_FALSE;
812         }
813
814         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_IMAGESET);
815
816         deMutex_unlock(log->lock);
817         return DE_TRUE;
818 }
819
820 /*--------------------------------------------------------------------*//*!
821  * \brief Write base64 encoded raw image data into log
822  * \param log                           qpTestLog instance
823  * \param name                          Unique name (matching names can be compared across BatchResults).
824  * \param description           Textual description (shown in Candy).
825  * \param compressionMode       Compression mode
826  * \param imageFormat           Color format
827  * \param width                         Width in pixels
828  * \param height                        Height in pixels
829  * \param stride                        Data stride (offset between rows)
830  * \param data                          Pointer to pixel data
831  * \return 0 if OK, otherwise <0
832  *//*--------------------------------------------------------------------*/
833 deBool qpTestLog_writeImage     (
834         qpTestLog*                              log,
835         const char*                             name,
836         const char*                             description,
837         qpImageCompressionMode  compressionMode,
838         qpImageFormat                   imageFormat,
839         int                                             width,
840         int                                             height,
841         int                                             stride,
842         const void*                             data)
843 {
844         char                    widthStr[32];
845         char                    heightStr[32];
846         qpXmlAttribute  attribs[8];
847         int                             numAttribs                      = 0;
848         Buffer                  compressedBuffer;
849         const void*             writeDataPtr            = DE_NULL;
850         size_t                  writeDataBytes          = ~(size_t)0;
851
852         DE_ASSERT(log && name);
853         DE_ASSERT(deInRange32(width, 1, 16384));
854         DE_ASSERT(deInRange32(height, 1, 16384));
855         DE_ASSERT(data);
856
857         if (log->flags & QP_TEST_LOG_EXCLUDE_IMAGES)
858                 return DE_TRUE; /* Image not logged. */
859
860         Buffer_init(&compressedBuffer);
861
862         /* BEST compression mode defaults to PNG. */
863         if (compressionMode == QP_IMAGE_COMPRESSION_MODE_BEST)
864         {
865 #if defined(QP_SUPPORT_PNG)
866                 compressionMode = QP_IMAGE_COMPRESSION_MODE_PNG;
867 #else
868                 compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
869 #endif
870         }
871
872 #if defined(QP_SUPPORT_PNG)
873         /* Try storing with PNG compression. */
874         if (compressionMode == QP_IMAGE_COMPRESSION_MODE_PNG)
875         {
876                 deBool compressOk = compressImagePNG(&compressedBuffer, imageFormat, width, height, stride, data);
877                 if (compressOk)
878                 {
879                         writeDataPtr    = compressedBuffer.data;
880                         writeDataBytes  = compressedBuffer.size;
881                 }
882                 else
883                 {
884                         /* Fall-back to default compression. */
885                         qpPrintf("WARNING: PNG compression failed -- storing image uncompressed.\n");
886                         compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
887                 }
888         }
889 #endif
890
891         /* Handle image compression. */
892         switch (compressionMode)
893         {
894                 case QP_IMAGE_COMPRESSION_MODE_NONE:
895                 {
896                         int pixelSize           = imageFormat == QP_IMAGE_FORMAT_RGB888 ? 3 : 4;
897                         int packedStride        = pixelSize*width;
898
899                         if (packedStride == stride)
900                                 writeDataPtr = data;
901                         else
902                         {
903                                 /* Need to re-pack pixels. */
904                                 if (Buffer_resize(&compressedBuffer, (size_t)(packedStride*height)))
905                                 {
906                                         int row;
907                                         for (row = 0; row < height; row++)
908                                                 memcpy(&compressedBuffer.data[packedStride*row], &((const deUint8*)data)[row*stride], (size_t)(pixelSize*width));
909                                 }
910                                 else
911                                 {
912                                         qpPrintf("ERROR: Failed to pack pixels for writing.\n");
913                                         Buffer_deinit(&compressedBuffer);
914                                         return DE_FALSE;
915                                 }
916                         }
917
918                         writeDataBytes = (size_t)(packedStride*height);
919                         break;
920                 }
921
922 #if defined(QP_SUPPORT_PNG)
923                 case QP_IMAGE_COMPRESSION_MODE_PNG:
924                         DE_ASSERT(writeDataPtr); /* Already handled. */
925                         break;
926 #endif
927
928                 default:
929                         qpPrintf("qpTestLog_writeImage(): Unknown compression mode: %s\n", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
930                         Buffer_deinit(&compressedBuffer);
931                         return DE_FALSE;
932         }
933
934         /* Fill in attributes. */
935         int32ToString(width, widthStr);
936         int32ToString(height, heightStr);
937         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
938         attribs[numAttribs++] = qpSetStringAttrib("Width", widthStr);
939         attribs[numAttribs++] = qpSetStringAttrib("Height", heightStr);
940         attribs[numAttribs++] = qpSetStringAttrib("Format", QP_LOOKUP_STRING(s_qpImageFormatMap, imageFormat));
941         attribs[numAttribs++] = qpSetStringAttrib("CompressionMode", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
942         if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
943
944         /* \note Log lock is acquired after compression! */
945         deMutex_lock(log->lock);
946
947         /* <Image ID="result" Name="Foobar" Width="640" Height="480" Format="RGB888" CompressionMode="None">base64 data</Image> */
948         if (!qpXmlWriter_startElement(log->writer, "Image", numAttribs, attribs) ||
949                 !qpXmlWriter_writeBase64(log->writer, (const deUint8*)writeDataPtr, writeDataBytes) ||
950                 !qpXmlWriter_endElement(log->writer, "Image"))
951         {
952                 qpPrintf("qpTestLog_writeImage(): Writing XML failed\n");
953                 deMutex_unlock(log->lock);
954                 Buffer_deinit(&compressedBuffer);
955                 return DE_FALSE;
956         }
957
958         deMutex_unlock(log->lock);
959
960         /* Free compressed data if allocated. */
961         Buffer_deinit(&compressedBuffer);
962
963         return DE_TRUE;
964 }
965
966 /*--------------------------------------------------------------------*//*!
967  * \brief Write a OpenGL ES shader program into the log.
968  * \param linkOk                        Shader program link result, false on failure
969  * \param linkInfoLog           Implementation provided linkage log
970  *//*--------------------------------------------------------------------*/
971 deBool qpTestLog_startShaderProgram (qpTestLog* log, deBool linkOk, const char* linkInfoLog)
972 {
973         qpXmlAttribute  programAttribs[4];
974         int                             numProgramAttribs = 0;
975
976         DE_ASSERT(log);
977         deMutex_lock(log->lock);
978
979         programAttribs[numProgramAttribs++] = qpSetStringAttrib("LinkStatus", linkOk ? "OK" : "Fail");
980
981         if (!qpXmlWriter_startElement(log->writer, "ShaderProgram", numProgramAttribs, programAttribs) ||
982                 !qpXmlWriter_writeStringElement(log->writer, "InfoLog", linkInfoLog))
983         {
984                 qpPrintf("qpTestLog_startShaderProgram(): Writing XML failed\n");
985                 deMutex_unlock(log->lock);
986                 return DE_FALSE;
987         }
988
989         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SHADERPROGRAM));
990
991         deMutex_unlock(log->lock);
992         return DE_TRUE;
993 }
994
995 /*--------------------------------------------------------------------*//*!
996  * \brief End shader program
997  * \param log                   qpTestLog instance
998  * \return true if ok, false otherwise
999  *//*--------------------------------------------------------------------*/
1000 deBool qpTestLog_endShaderProgram (qpTestLog* log)
1001 {
1002         DE_ASSERT(log);
1003         deMutex_lock(log->lock);
1004
1005         /* </ShaderProgram> */
1006         if (!qpXmlWriter_endElement(log->writer, "ShaderProgram"))
1007         {
1008                 qpPrintf("qpTestLog_endShaderProgram(): Writing XML failed\n");
1009                 deMutex_unlock(log->lock);
1010                 return DE_FALSE;
1011         }
1012
1013         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1014
1015         deMutex_unlock(log->lock);
1016         return DE_TRUE;
1017 }
1018
1019 /*--------------------------------------------------------------------*//*!
1020  * \brief Write a OpenGL ES shader into the log.
1021  * \param type                          Shader type
1022  * \param source                        Shader source
1023  * \param compileOk                     Shader compilation result, false on failure
1024  * \param infoLog                       Implementation provided shader compilation log
1025  *//*--------------------------------------------------------------------*/
1026 deBool qpTestLog_writeShader (qpTestLog* log, qpShaderType type, const char* source, deBool compileOk, const char* infoLog)
1027 {
1028         const char*             tagName                         = QP_LOOKUP_STRING(s_qpShaderTypeMap, type);
1029         const char*             sourceStr                       = ((log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) == 0 || !compileOk) ? source : "";
1030         int                             numShaderAttribs        = 0;
1031         qpXmlAttribute  shaderAttribs[4];
1032
1033         DE_ASSERT(log && source);
1034         DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1035         deMutex_lock(log->lock);
1036
1037         shaderAttribs[numShaderAttribs++]       = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1038
1039         if (!qpXmlWriter_startElement(log->writer, tagName, numShaderAttribs, shaderAttribs) ||
1040                 !qpXmlWriter_writeStringElement(log->writer, "ShaderSource", sourceStr) ||
1041                 !qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1042                 !qpXmlWriter_endElement(log->writer, tagName))
1043         {
1044                 qpPrintf("qpTestLog_writeShader(): Writing XML failed\n");
1045                 deMutex_unlock(log->lock);
1046                 return DE_FALSE;
1047         }
1048
1049         deMutex_unlock(log->lock);
1050         return DE_TRUE;
1051 }
1052
1053 /*--------------------------------------------------------------------*//*!
1054  * \brief Start writing a set of EGL configurations into the log.
1055  *//*--------------------------------------------------------------------*/
1056 deBool qpTestLog_startEglConfigSet (qpTestLog* log, const char* name, const char* description)
1057 {
1058         qpXmlAttribute  attribs[4];
1059         int                             numAttribs = 0;
1060
1061         DE_ASSERT(log && name);
1062         deMutex_lock(log->lock);
1063
1064         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1065         if (description)
1066                 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1067
1068         /* <EglConfigSet Name="<name>"> */
1069         if (!qpXmlWriter_startElement(log->writer, "EglConfigSet", numAttribs, attribs))
1070         {
1071                 qpPrintf("qpTestLog_startEglImageSet(): Writing XML failed\n");
1072                 deMutex_unlock(log->lock);
1073                 return DE_FALSE;
1074         }
1075
1076         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_EGLCONFIGSET));
1077
1078         deMutex_unlock(log->lock);
1079         return DE_TRUE;
1080 }
1081
1082 /*--------------------------------------------------------------------*//*!
1083  * \brief End an EGL config set
1084  *//*--------------------------------------------------------------------*/
1085 deBool qpTestLog_endEglConfigSet (qpTestLog* log)
1086 {
1087         DE_ASSERT(log);
1088         deMutex_lock(log->lock);
1089
1090         /* <EglConfigSet Name="<name>"> */
1091         if (!qpXmlWriter_endElement(log->writer, "EglConfigSet"))
1092         {
1093                 qpPrintf("qpTestLog_endEglImageSet(): Writing XML failed\n");
1094                 deMutex_unlock(log->lock);
1095                 return DE_FALSE;
1096         }
1097
1098         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_EGLCONFIGSET);
1099
1100         deMutex_unlock(log->lock);
1101         return DE_TRUE;
1102 }
1103
1104 /*--------------------------------------------------------------------*//*!
1105  * \brief Write an EGL config inside an EGL config set
1106  * \see   qpElgConfigInfo for details
1107  *//*--------------------------------------------------------------------*/
1108 deBool qpTestLog_writeEglConfig (qpTestLog* log, const qpEglConfigInfo* config)
1109 {
1110         qpXmlAttribute  attribs[64];
1111         int                             numAttribs = 0;
1112
1113         DE_ASSERT(log && config);
1114         deMutex_lock(log->lock);
1115
1116         attribs[numAttribs++] = qpSetIntAttrib          ("BufferSize", config->bufferSize);
1117         attribs[numAttribs++] = qpSetIntAttrib          ("RedSize", config->redSize);
1118         attribs[numAttribs++] = qpSetIntAttrib          ("GreenSize", config->greenSize);
1119         attribs[numAttribs++] = qpSetIntAttrib          ("BlueSize", config->blueSize);
1120         attribs[numAttribs++] = qpSetIntAttrib          ("LuminanceSize", config->luminanceSize);
1121         attribs[numAttribs++] = qpSetIntAttrib          ("AlphaSize", config->alphaSize);
1122         attribs[numAttribs++] = qpSetIntAttrib          ("AlphaMaskSize", config->alphaMaskSize);
1123         attribs[numAttribs++] = qpSetBoolAttrib         ("BindToTextureRGB", config->bindToTextureRGB);
1124         attribs[numAttribs++] = qpSetBoolAttrib         ("BindToTextureRGBA", config->bindToTextureRGBA);
1125         attribs[numAttribs++] = qpSetStringAttrib       ("ColorBufferType", config->colorBufferType);
1126         attribs[numAttribs++] = qpSetStringAttrib       ("ConfigCaveat", config->configCaveat);
1127         attribs[numAttribs++] = qpSetIntAttrib          ("ConfigID", config->configID);
1128         attribs[numAttribs++] = qpSetStringAttrib       ("Conformant", config->conformant);
1129         attribs[numAttribs++] = qpSetIntAttrib          ("DepthSize", config->depthSize);
1130         attribs[numAttribs++] = qpSetIntAttrib          ("Level", config->level);
1131         attribs[numAttribs++] = qpSetIntAttrib          ("MaxPBufferWidth", config->maxPBufferWidth);
1132         attribs[numAttribs++] = qpSetIntAttrib          ("MaxPBufferHeight", config->maxPBufferHeight);
1133         attribs[numAttribs++] = qpSetIntAttrib          ("MaxPBufferPixels", config->maxPBufferPixels);
1134         attribs[numAttribs++] = qpSetIntAttrib          ("MaxSwapInterval", config->maxSwapInterval);
1135         attribs[numAttribs++] = qpSetIntAttrib          ("MinSwapInterval", config->minSwapInterval);
1136         attribs[numAttribs++] = qpSetBoolAttrib         ("NativeRenderable", config->nativeRenderable);
1137         attribs[numAttribs++] = qpSetStringAttrib       ("RenderableType", config->renderableType);
1138         attribs[numAttribs++] = qpSetIntAttrib          ("SampleBuffers", config->sampleBuffers);
1139         attribs[numAttribs++] = qpSetIntAttrib          ("Samples", config->samples);
1140         attribs[numAttribs++] = qpSetIntAttrib          ("StencilSize", config->stencilSize);
1141         attribs[numAttribs++] = qpSetStringAttrib       ("SurfaceTypes", config->surfaceTypes);
1142         attribs[numAttribs++] = qpSetStringAttrib       ("TransparentType", config->transparentType);
1143         attribs[numAttribs++] = qpSetIntAttrib          ("TransparentRedValue", config->transparentRedValue);
1144         attribs[numAttribs++] = qpSetIntAttrib          ("TransparentGreenValue", config->transparentGreenValue);
1145         attribs[numAttribs++] = qpSetIntAttrib          ("TransparentBlueValue", config->transparentBlueValue);
1146         DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
1147
1148         if (!qpXmlWriter_startElement(log->writer, "EglConfig", numAttribs, attribs) ||
1149                 !qpXmlWriter_endElement(log->writer, "EglConfig"))
1150         {
1151                 qpPrintf("qpTestLog_writeEglConfig(): Writing XML failed\n");
1152                 deMutex_unlock(log->lock);
1153                 return DE_FALSE;
1154         }
1155
1156         deMutex_unlock(log->lock);
1157         return DE_TRUE;
1158 }
1159
1160 /*--------------------------------------------------------------------*//*!
1161  * \brief Start section in log.
1162  * \param log                   qpTestLog instance
1163  * \param name                  Section name
1164  * \param description   Human readable description
1165  * \return true if ok, false otherwise
1166  *//*--------------------------------------------------------------------*/
1167 deBool qpTestLog_startSection (qpTestLog* log, const char* name, const char* description)
1168 {
1169         qpXmlAttribute  attribs[2];
1170         int                             numAttribs = 0;
1171
1172         DE_ASSERT(log && name);
1173         deMutex_lock(log->lock);
1174
1175         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1176         if (description)
1177                 attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1178
1179         /* <Section Name="<name>" Description="<description>"> */
1180         if (!qpXmlWriter_startElement(log->writer, "Section", numAttribs, attribs))
1181         {
1182                 qpPrintf("qpTestLog_startSection(): Writing XML failed\n");
1183                 deMutex_unlock(log->lock);
1184                 return DE_FALSE;
1185         }
1186
1187         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SECTION));
1188
1189         deMutex_unlock(log->lock);
1190         return DE_TRUE;
1191 }
1192
1193 /*--------------------------------------------------------------------*//*!
1194  * \brief End section in log.
1195  * \param log                   qpTestLog instance
1196  * \return true if ok, false otherwise
1197  *//*--------------------------------------------------------------------*/
1198 deBool qpTestLog_endSection (qpTestLog* log)
1199 {
1200         DE_ASSERT(log);
1201         deMutex_lock(log->lock);
1202
1203         /* </Section> */
1204         if (!qpXmlWriter_endElement(log->writer, "Section"))
1205         {
1206                 qpPrintf("qpTestLog_endSection(): Writing XML failed\n");
1207                 deMutex_unlock(log->lock);
1208                 return DE_FALSE;
1209         }
1210
1211         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SECTION);
1212
1213         deMutex_unlock(log->lock);
1214         return DE_TRUE;
1215 }
1216
1217 /*--------------------------------------------------------------------*//*!
1218  * \brief Write OpenCL compute kernel source into the log.
1219  *//*--------------------------------------------------------------------*/
1220 deBool qpTestLog_writeKernelSource (qpTestLog* log, const char* source)
1221 {
1222         const char*             sourceStr       = (log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) != 0 ? "" : source;
1223
1224         DE_ASSERT(log);
1225         deMutex_lock(log->lock);
1226
1227         if (!qpXmlWriter_writeStringElement(log->writer, "KernelSource", sourceStr))
1228         {
1229                 qpPrintf("qpTestLog_writeKernelSource(): Writing XML failed\n");
1230                 deMutex_unlock(log->lock);
1231                 return DE_FALSE;
1232         }
1233
1234         deMutex_unlock(log->lock);
1235         return DE_TRUE;
1236 }
1237
1238 /*--------------------------------------------------------------------*//*!
1239  * \brief Write a SPIR-V module assembly source into the log.
1240  *//*--------------------------------------------------------------------*/
1241 deBool qpTestLog_writeSpirVAssemblySource (qpTestLog* log, const char* source)
1242 {
1243         DE_ASSERT(log);
1244         DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
1245         deMutex_lock(log->lock);
1246
1247         if (!qpXmlWriter_writeStringElement(log->writer, "SpirVAssemblySource", source))
1248         {
1249                 qpPrintf("qpTestLog_writeSpirVAssemblySource(): Writing XML failed\n");
1250                 deMutex_unlock(log->lock);
1251                 return DE_FALSE;
1252         }
1253
1254         deMutex_unlock(log->lock);
1255         return DE_TRUE;
1256 }
1257
1258 /*--------------------------------------------------------------------*//*!
1259  * \brief Write OpenCL kernel compilation results into the log
1260  *//*--------------------------------------------------------------------*/
1261 deBool qpTestLog_writeCompileInfo (qpTestLog* log, const char* name, const char* description, deBool compileOk, const char* infoLog)
1262 {
1263         int                             numAttribs = 0;
1264         qpXmlAttribute  attribs[3];
1265
1266         DE_ASSERT(log && name && description && infoLog);
1267         deMutex_lock(log->lock);
1268
1269         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1270         attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1271         attribs[numAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
1272
1273         if (!qpXmlWriter_startElement(log->writer, "CompileInfo", numAttribs, attribs) ||
1274                 !qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
1275                 !qpXmlWriter_endElement(log->writer, "CompileInfo"))
1276         {
1277                 qpPrintf("qpTestLog_writeCompileInfo(): Writing XML failed\n");
1278                 deMutex_unlock(log->lock);
1279                 return DE_FALSE;
1280         }
1281
1282         deMutex_unlock(log->lock);
1283         return DE_TRUE;
1284 }
1285
1286 deBool qpTestLog_startSampleList (qpTestLog* log, const char* name, const char* description)
1287 {
1288         int                             numAttribs = 0;
1289         qpXmlAttribute  attribs[2];
1290
1291         DE_ASSERT(log && name && description);
1292         deMutex_lock(log->lock);
1293
1294         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1295         attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1296
1297         if (!qpXmlWriter_startElement(log->writer, "SampleList", numAttribs, attribs))
1298         {
1299                 qpPrintf("qpTestLog_startSampleList(): Writing XML failed\n");
1300                 deMutex_unlock(log->lock);
1301                 return DE_FALSE;
1302         }
1303
1304         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLELIST));
1305
1306         deMutex_unlock(log->lock);
1307         return DE_TRUE;
1308 }
1309
1310 deBool qpTestLog_startSampleInfo (qpTestLog* log)
1311 {
1312         DE_ASSERT(log);
1313         deMutex_lock(log->lock);
1314
1315         if (!qpXmlWriter_startElement(log->writer, "SampleInfo", 0, DE_NULL))
1316         {
1317                 qpPrintf("qpTestLog_startSampleInfo(): Writing XML failed\n");
1318                 deMutex_unlock(log->lock);
1319                 return DE_FALSE;
1320         }
1321
1322         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLEINFO));
1323
1324         deMutex_unlock(log->lock);
1325         return DE_TRUE;
1326 }
1327
1328 deBool qpTestLog_writeValueInfo (qpTestLog* log, const char* name, const char* description, const char* unit, qpSampleValueTag tag)
1329 {
1330         const char*             tagName         = QP_LOOKUP_STRING(s_qpSampleValueTagMap, tag);
1331         int                             numAttribs      = 0;
1332         qpXmlAttribute  attribs[4];
1333
1334         DE_ASSERT(log && name && description && tagName);
1335         deMutex_lock(log->lock);
1336
1337         DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1338
1339         attribs[numAttribs++] = qpSetStringAttrib("Name", name);
1340         attribs[numAttribs++] = qpSetStringAttrib("Description", description);
1341         attribs[numAttribs++] = qpSetStringAttrib("Tag", tagName);
1342
1343         if (unit)
1344                 attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
1345
1346         if (!qpXmlWriter_startElement(log->writer, "ValueInfo", numAttribs, attribs) ||
1347                 !qpXmlWriter_endElement(log->writer, "ValueInfo"))
1348         {
1349                 qpPrintf("qpTestLog_writeValueInfo(): Writing XML failed\n");
1350                 deMutex_unlock(log->lock);
1351                 return DE_FALSE;
1352         }
1353
1354         deMutex_unlock(log->lock);
1355         return DE_TRUE;
1356 }
1357
1358 deBool qpTestLog_endSampleInfo (qpTestLog* log)
1359 {
1360         DE_ASSERT(log);
1361         deMutex_lock(log->lock);
1362
1363         if (!qpXmlWriter_endElement(log->writer, "SampleInfo"))
1364         {
1365                 qpPrintf("qpTestLog_endSampleInfo(): Writing XML failed\n");
1366                 deMutex_unlock(log->lock);
1367                 return DE_FALSE;
1368         }
1369
1370         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
1371
1372         deMutex_unlock(log->lock);
1373         return DE_TRUE;
1374 }
1375
1376 deBool qpTestLog_startSample (qpTestLog* log)
1377 {
1378         DE_ASSERT(log);
1379         deMutex_lock(log->lock);
1380
1381         DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1382
1383         if (!qpXmlWriter_startElement(log->writer, "Sample", 0, DE_NULL))
1384         {
1385                 qpPrintf("qpTestLog_startSample(): Writing XML failed\n");
1386                 deMutex_unlock(log->lock);
1387                 return DE_FALSE;
1388         }
1389
1390         DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLE));
1391
1392         deMutex_unlock(log->lock);
1393         return DE_TRUE;
1394 }
1395
1396 deBool qpTestLog_writeValueFloat (qpTestLog* log, double value)
1397 {
1398         char tmpString[512];
1399         doubleToString(value, tmpString, (int)sizeof(tmpString));
1400
1401         deMutex_lock(log->lock);
1402
1403         DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1404
1405         if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1406         {
1407                 qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1408                 deMutex_unlock(log->lock);
1409                 return DE_FALSE;
1410         }
1411
1412         deMutex_unlock(log->lock);
1413         return DE_TRUE;
1414 }
1415
1416 deBool qpTestLog_writeValueInteger (qpTestLog* log, deInt64 value)
1417 {
1418         char tmpString[64];
1419         int64ToString(value, tmpString);
1420
1421         deMutex_lock(log->lock);
1422
1423         DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1424
1425         if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
1426         {
1427                 qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
1428                 deMutex_unlock(log->lock);
1429                 return DE_FALSE;
1430         }
1431
1432         deMutex_unlock(log->lock);
1433         return DE_TRUE;
1434 }
1435
1436 deBool qpTestLog_endSample (qpTestLog* log)
1437 {
1438         DE_ASSERT(log);
1439         deMutex_lock(log->lock);
1440
1441         if (!qpXmlWriter_endElement(log->writer, "Sample"))
1442         {
1443                 qpPrintf("qpTestLog_endSample(): Writing XML failed\n");
1444                 deMutex_unlock(log->lock);
1445                 return DE_FALSE;
1446         }
1447
1448         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
1449
1450         deMutex_unlock(log->lock);
1451         return DE_TRUE;
1452 }
1453
1454 deBool qpTestLog_endSampleList (qpTestLog* log)
1455 {
1456         DE_ASSERT(log);
1457         deMutex_lock(log->lock);
1458
1459         if (!qpXmlWriter_endElement(log->writer, "SampleList"))
1460         {
1461                 qpPrintf("qpTestLog_endSampleList(): Writing XML failed\n");
1462                 deMutex_unlock(log->lock);
1463                 return DE_FALSE;
1464         }
1465
1466         DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
1467
1468         deMutex_unlock(log->lock);
1469         return DE_TRUE;
1470 }
1471
1472 deUint32 qpTestLog_getLogFlags (const qpTestLog* log)
1473 {
1474         DE_ASSERT(log);
1475         return log->flags;
1476 }
1477
1478 const char* qpGetTestResultName (qpTestResult result)
1479 {
1480         return QP_LOOKUP_STRING(s_qpTestResultMap, result);
1481 }