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