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