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