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