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