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