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