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