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