tcuTestLog.cpp revision fcbb91dc538f174f8798417e5a44f74ec2b092c0
1/*------------------------------------------------------------------------- 2 * drawElements Quality Program Tester Core 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 Log C++ Wrapper. 22 *//*--------------------------------------------------------------------*/ 23 24#include "tcuTestLog.hpp" 25#include "tcuTextureUtil.hpp" 26#include "tcuSurface.hpp" 27#include "deMath.h" 28 29#include <limits> 30 31namespace tcu 32{ 33 34class LogWriteFailedError : public ResourceError 35{ 36public: 37 LogWriteFailedError (void) : ResourceError("Writing to test log failed") {} 38}; 39 40enum 41{ 42 MAX_IMAGE_SIZE_2D = 4096, 43 MAX_IMAGE_SIZE_3D = 128 44}; 45 46// LogImage 47 48LogImage::LogImage (const std::string& name, const std::string& description, const Surface& surface, qpImageCompressionMode compression) 49 : m_name (name) 50 , m_description (description) 51 , m_access (surface.getAccess()) 52 , m_scale (1.0f, 1.0f, 1.0f, 1.0f) 53 , m_bias (0.0f, 0.0f, 0.0f, 0.0f) 54 , m_compression (compression) 55{ 56} 57 58LogImage::LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, qpImageCompressionMode compression) 59 : m_name (name) 60 , m_description (description) 61 , m_access (access) 62 , m_scale (1.0f, 1.0f, 1.0f, 1.0f) 63 , m_bias (0.0f, 0.0f, 0.0f, 0.0f) 64 , m_compression (compression) 65{ 66 // Simplify combined formats that only use a single channel 67 if (tcu::isCombinedDepthStencilType(m_access.getFormat().type)) 68 { 69 if (m_access.getFormat().order == tcu::TextureFormat::D) 70 m_access = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH); 71 else if (m_access.getFormat().order == tcu::TextureFormat::S) 72 m_access = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL); 73 } 74 75 // Implicit scale and bias 76 if (m_access.getFormat().order != tcu::TextureFormat::DS) 77 computePixelScaleBias(m_access, m_scale, m_bias); 78 else 79 { 80 // Pack D and S bias and scale to R and G 81 const ConstPixelBufferAccess depthAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH); 82 const ConstPixelBufferAccess stencilAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL); 83 tcu::Vec4 depthScale; 84 tcu::Vec4 depthBias; 85 tcu::Vec4 stencilScale; 86 tcu::Vec4 stencilBias; 87 88 computePixelScaleBias(depthAccess, depthScale, depthBias); 89 computePixelScaleBias(stencilAccess, stencilScale, stencilBias); 90 91 m_scale = tcu::Vec4(depthScale.x(), stencilScale.x(), 0.0f, 0.0f); 92 m_bias = tcu::Vec4(depthBias.x(), stencilBias.x(), 0.0f, 0.0f); 93 } 94} 95 96LogImage::LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, const Vec4& scale, const Vec4& bias, qpImageCompressionMode compression) 97 : m_name (name) 98 , m_description (description) 99 , m_access (access) 100 , m_scale (scale) 101 , m_bias (bias) 102 , m_compression (compression) 103{ 104 // Cannot set scale and bias of combined formats 105 DE_ASSERT(access.getFormat().order != tcu::TextureFormat::DS); 106 107 // Simplify access 108 if (tcu::isCombinedDepthStencilType(access.getFormat().type)) 109 { 110 if (access.getFormat().order == tcu::TextureFormat::D) 111 m_access = tcu::getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH); 112 if (access.getFormat().order == tcu::TextureFormat::S) 113 m_access = tcu::getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL); 114 else 115 { 116 // Cannot log a DS format 117 DE_ASSERT(false); 118 return; 119 } 120 } 121} 122 123void LogImage::write (TestLog& log) const 124{ 125 if (m_access.getFormat().order != tcu::TextureFormat::DS) 126 log.writeImage(m_name.c_str(), m_description.c_str(), m_access, m_scale, m_bias, m_compression); 127 else 128 { 129 const ConstPixelBufferAccess depthAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH); 130 const ConstPixelBufferAccess stencilAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL); 131 132 log.startImageSet(m_name.c_str(), m_description.c_str()); 133 log.writeImage("Depth", "Depth channel", depthAccess, m_scale.swizzle(0, 0, 0, 0), m_bias.swizzle(0, 0, 0, 0), m_compression); 134 log.writeImage("Stencil", "Stencil channel", stencilAccess, m_scale.swizzle(1, 1, 1, 1), m_bias.swizzle(1, 1, 1, 1), m_compression); 135 log.endImageSet(); 136 } 137} 138 139// MessageBuilder 140 141MessageBuilder::MessageBuilder (const MessageBuilder& other) 142 : m_log(other.m_log) 143{ 144 m_str.str(other.m_str.str()); 145} 146 147MessageBuilder& MessageBuilder::operator= (const MessageBuilder& other) 148{ 149 m_log = other.m_log; 150 m_str.str(other.m_str.str()); 151 return *this; 152} 153 154TestLog& MessageBuilder::operator<< (const TestLog::EndMessageToken&) 155{ 156 m_log->writeMessage(m_str.str().c_str()); 157 return *m_log; 158} 159 160// SampleBuilder 161 162TestLog& SampleBuilder::operator<< (const TestLog::EndSampleToken&) 163{ 164 m_log->startSample(); 165 166 for (std::vector<Value>::const_iterator val = m_values.begin(); val != m_values.end(); ++val) 167 { 168 if (val->type == Value::TYPE_FLOAT64) 169 m_log->writeSampleValue(val->value.float64); 170 else if (val->type == Value::TYPE_INT64) 171 m_log->writeSampleValue(val->value.int64); 172 else 173 DE_ASSERT(false); 174 } 175 176 m_log->endSample(); 177 178 return *m_log; 179} 180 181// TestLog 182 183TestLog::TestLog (const char* fileName, deUint32 flags) 184 : m_log(qpTestLog_createFileLog(fileName, flags)) 185{ 186 if (!m_log) 187 throw ResourceError(std::string("Failed to open test log file '") + fileName + "'"); 188} 189 190TestLog::~TestLog (void) 191{ 192 qpTestLog_destroy(m_log); 193} 194 195void TestLog::writeMessage (const char* msgStr) 196{ 197 if (qpTestLog_writeText(m_log, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, msgStr) == DE_FALSE) 198 throw LogWriteFailedError(); 199} 200 201void TestLog::startImageSet (const char* name, const char* description) 202{ 203 if (qpTestLog_startImageSet(m_log, name, description) == DE_FALSE) 204 throw LogWriteFailedError(); 205} 206 207void TestLog::endImageSet (void) 208{ 209 if (qpTestLog_endImageSet(m_log) == DE_FALSE) 210 throw LogWriteFailedError(); 211} 212 213template <int Size> 214static Vector<int, Size> computeScaledSize (const Vector<int, Size>& imageSize, int maxSize) 215{ 216 bool allInRange = true; 217 for (int i = 0; i < Size; i++) 218 allInRange = allInRange && (imageSize[i] <= maxSize); 219 220 if (allInRange) 221 return imageSize; 222 else 223 { 224 float d = 1.0f; 225 for (int i = 0; i < Size; i++) 226 d = de::max(d, (float)imageSize[i] / (float)maxSize); 227 228 Vector<int, Size> res; 229 for (int i = 0; i < Size; i++) 230 res[i] = deRoundFloatToInt32((float)imageSize[i] / d); 231 232 return res; 233 } 234} 235 236void TestLog::writeImage (const char* name, const char* description, const ConstPixelBufferAccess& access, const Vec4& pixelScale, const Vec4& pixelBias, qpImageCompressionMode compressionMode) 237{ 238 const TextureFormat& format = access.getFormat(); 239 int width = access.getWidth(); 240 int height = access.getHeight(); 241 int depth = access.getDepth(); 242 243 // Writing a combined image does not make sense 244 DE_ASSERT(!tcu::isCombinedDepthStencilType(access.getFormat().type)); 245 246 // Do not bother with preprocessing if images are not stored 247 if ((qpTestLog_getLogFlags(m_log) & QP_TEST_LOG_EXCLUDE_IMAGES) != 0) 248 return; 249 250 if (depth == 1 && format.type == TextureFormat::UNORM_INT8 && 251 width <= MAX_IMAGE_SIZE_2D && height <= MAX_IMAGE_SIZE_2D && 252 (format.order == TextureFormat::RGB || format.order == TextureFormat::RGBA) 253 && pixelBias[0] == 0.0f && pixelBias[1] == 0.0f && pixelBias[2] == 0.0f && pixelBias[3] == 0.0f 254 && pixelScale[0] == 1.0f && pixelScale[1] == 1.0f && pixelScale[2] == 1.0f && pixelScale[3] == 1.0f) 255 { 256 // Fast-path. 257 bool isRGBA = format.order == TextureFormat::RGBA; 258 259 writeImage(name, description, compressionMode, 260 isRGBA ? QP_IMAGE_FORMAT_RGBA8888 : QP_IMAGE_FORMAT_RGB888, 261 width, height, access.getRowPitch(), access.getDataPtr()); 262 } 263 else if (depth == 1) 264 { 265 Sampler sampler (Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::LINEAR, Sampler::NEAREST); 266 IVec2 logImageSize = computeScaledSize(IVec2(width, height), MAX_IMAGE_SIZE_2D); 267 tcu::TextureLevel logImage (TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageSize.x(), logImageSize.y(), 1); 268 PixelBufferAccess logImageAccess = logImage.getAccess(); 269 std::ostringstream longDesc; 270 271 longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")"; 272 273 for (int y = 0; y < logImage.getHeight(); y++) 274 { 275 for (int x = 0; x < logImage.getWidth(); x++) 276 { 277 float yf = ((float)y + 0.5f) / (float)logImage.getHeight(); 278 float xf = ((float)x + 0.5f) / (float)logImage.getWidth(); 279 Vec4 s = access.sample2D(sampler, sampler.minFilter, xf, yf, 0)*pixelScale + pixelBias; 280 281 logImageAccess.setPixel(s, x, y); 282 } 283 } 284 285 writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888, 286 logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(), 287 logImageAccess.getDataPtr()); 288 } 289 else 290 { 291 // Isometric splat volume rendering. 292 const float blendFactor = 0.85f; 293 IVec3 scaledSize = computeScaledSize(IVec3(width, height, depth), MAX_IMAGE_SIZE_3D); 294 int w = scaledSize.x(); 295 int h = scaledSize.y(); 296 int d = scaledSize.z(); 297 int logImageW = w+d - 1; 298 int logImageH = w+d+h; 299 std::vector<float> blendImage (logImageW*logImageH*4, 0.0f); 300 PixelBufferAccess blendImageAccess (TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT), logImageW, logImageH, 1, &blendImage[0]); 301 tcu::TextureLevel logImage (TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageW, logImageH, 1); 302 PixelBufferAccess logImageAccess = logImage.getAccess(); 303 Sampler sampler (Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::NEAREST, Sampler::NEAREST); 304 std::ostringstream longDesc; 305 306 // \note Back-to-front. 307 for (int z = d-1; z >= 0; z--) 308 { 309 for (int y = 0; y < h; y++) 310 { 311 for (int x = 0; x < w; x++) 312 { 313 int px = w - (x + 1) + z; 314 int py = (w + d + h) - (x + y + z + 1); 315 316 float xf = ((float)x + 0.5f) / (float)w; 317 float yf = ((float)y + 0.5f) / (float)h; 318 float zf = ((float)z + 0.5f) / (float)d; 319 320 Vec4 p = blendImageAccess.getPixel(px, py); 321 Vec4 s = access.sample3D(sampler, sampler.minFilter, xf, yf, zf); 322 Vec4 b = s + p*blendFactor; 323 324 blendImageAccess.setPixel(b, px, py); 325 } 326 } 327 } 328 329 // Scale blend image nicely. 330 longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")"; 331 332 // Write to final image. 333 tcu::clear(logImageAccess, tcu::IVec4(0x33, 0x66, 0x99, 0xff)); 334 335 for (int z = 0; z < d; z++) 336 { 337 for (int y = 0; y < h; y++) 338 { 339 for (int x = 0; x < w; x++) 340 { 341 if (z != 0 && !(x == 0 || y == h-1 || y == h-2)) 342 continue; 343 344 int px = w - (x + 1) + z; 345 int py = (w + d + h) - (x + y + z + 1); 346 Vec4 s = blendImageAccess.getPixel(px, py)*pixelScale + pixelBias; 347 348 logImageAccess.setPixel(s, px, py); 349 } 350 } 351 } 352 353 writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888, 354 logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(), 355 logImageAccess.getDataPtr()); 356 } 357} 358 359void TestLog::writeImage (const char* name, const char* description, qpImageCompressionMode compressionMode, qpImageFormat format, int width, int height, int stride, const void* data) 360{ 361 if (qpTestLog_writeImage(m_log, name, description, compressionMode, format, width, height, stride, data) == DE_FALSE) 362 throw LogWriteFailedError(); 363} 364 365void TestLog::startSection (const char* name, const char* description) 366{ 367 if (qpTestLog_startSection(m_log, name, description) == DE_FALSE) 368 throw LogWriteFailedError(); 369} 370 371void TestLog::endSection (void) 372{ 373 if (qpTestLog_endSection(m_log) == DE_FALSE) 374 throw LogWriteFailedError(); 375} 376 377void TestLog::startShaderProgram (bool linkOk, const char* linkInfoLog) 378{ 379 if (qpTestLog_startShaderProgram(m_log, linkOk?DE_TRUE:DE_FALSE, linkInfoLog) == DE_FALSE) 380 throw LogWriteFailedError(); 381} 382 383void TestLog::endShaderProgram (void) 384{ 385 if (qpTestLog_endShaderProgram(m_log) == DE_FALSE) 386 throw LogWriteFailedError(); 387} 388 389void TestLog::writeShader (qpShaderType type, const char* source, bool compileOk, const char* infoLog) 390{ 391 if (qpTestLog_writeShader(m_log, type, source, compileOk?DE_TRUE:DE_FALSE, infoLog) == DE_FALSE) 392 throw LogWriteFailedError(); 393} 394 395void TestLog::writeKernelSource (const char* source) 396{ 397 if (qpTestLog_writeKernelSource(m_log, source) == DE_FALSE) 398 throw LogWriteFailedError(); 399} 400 401void TestLog::writeCompileInfo (const char* name, const char* description, bool compileOk, const char* infoLog) 402{ 403 if (qpTestLog_writeCompileInfo(m_log, name, description, compileOk ? DE_TRUE : DE_FALSE, infoLog) == DE_FALSE) 404 throw LogWriteFailedError(); 405} 406 407void TestLog::writeFloat (const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value) 408{ 409 if (qpTestLog_writeFloat(m_log, name, description, unit, tag, value) == DE_FALSE) 410 throw LogWriteFailedError(); 411} 412 413void TestLog::writeInteger (const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value) 414{ 415 if (qpTestLog_writeInteger(m_log, name, description, unit, tag, value) == DE_FALSE) 416 throw LogWriteFailedError(); 417} 418 419void TestLog::startEglConfigSet (const char* name, const char* description) 420{ 421 if (qpTestLog_startEglConfigSet(m_log, name, description) == DE_FALSE) 422 throw LogWriteFailedError(); 423} 424 425void TestLog::writeEglConfig (const qpEglConfigInfo* config) 426{ 427 if (qpTestLog_writeEglConfig(m_log, config) == DE_FALSE) 428 throw LogWriteFailedError(); 429} 430 431void TestLog::endEglConfigSet (void) 432{ 433 if (qpTestLog_endEglConfigSet(m_log) == DE_FALSE) 434 throw LogWriteFailedError(); 435} 436 437void TestLog::startCase (const char* testCasePath, qpTestCaseType testCaseType) 438{ 439 if (qpTestLog_startCase(m_log, testCasePath, testCaseType) == DE_FALSE) 440 throw LogWriteFailedError(); 441} 442 443void TestLog::endCase (qpTestResult result, const char* description) 444{ 445 if (qpTestLog_endCase(m_log, result, description) == DE_FALSE) 446 throw LogWriteFailedError(); 447} 448 449void TestLog::terminateCase (qpTestResult result) 450{ 451 if (qpTestLog_terminateCase(m_log, result) == DE_FALSE) 452 throw LogWriteFailedError(); 453} 454 455void TestLog::startSampleList (const std::string& name, const std::string& description) 456{ 457 if (qpTestLog_startSampleList(m_log, name.c_str(), description.c_str()) == DE_FALSE) 458 throw LogWriteFailedError(); 459} 460 461void TestLog::startSampleInfo (void) 462{ 463 if (qpTestLog_startSampleInfo(m_log) == DE_FALSE) 464 throw LogWriteFailedError(); 465} 466 467void TestLog::writeValueInfo (const std::string& name, const std::string& description, const std::string& unit, qpSampleValueTag tag) 468{ 469 if (qpTestLog_writeValueInfo(m_log, name.c_str(), description.c_str(), unit.empty() ? DE_NULL : unit.c_str(), tag) == DE_FALSE) 470 throw LogWriteFailedError(); 471} 472 473void TestLog::endSampleInfo (void) 474{ 475 if (qpTestLog_endSampleInfo(m_log) == DE_FALSE) 476 throw LogWriteFailedError(); 477} 478 479void TestLog::startSample (void) 480{ 481 if (qpTestLog_startSample(m_log) == DE_FALSE) 482 throw LogWriteFailedError(); 483} 484 485void TestLog::writeSampleValue (double value) 486{ 487 if (qpTestLog_writeValueFloat(m_log, value) == DE_FALSE) 488 throw LogWriteFailedError(); 489} 490 491void TestLog::writeSampleValue (deInt64 value) 492{ 493 if (qpTestLog_writeValueInteger(m_log, value) == DE_FALSE) 494 throw LogWriteFailedError(); 495} 496 497void TestLog::endSample (void) 498{ 499 if (qpTestLog_endSample(m_log) == DE_FALSE) 500 throw LogWriteFailedError(); 501} 502 503void TestLog::endSampleList (void) 504{ 505 if (qpTestLog_endSampleList(m_log) == DE_FALSE) 506 throw LogWriteFailedError(); 507} 508 509const TestLog::BeginMessageToken TestLog::Message = TestLog::BeginMessageToken(); 510const TestLog::EndMessageToken TestLog::EndMessage = TestLog::EndMessageToken(); 511const TestLog::EndImageSetToken TestLog::EndImageSet = TestLog::EndImageSetToken(); 512const TestLog::EndSectionToken TestLog::EndSection = TestLog::EndSectionToken(); 513const TestLog::EndShaderProgramToken TestLog::EndShaderProgram = TestLog::EndShaderProgramToken(); 514const TestLog::SampleInfoToken TestLog::SampleInfo = TestLog::SampleInfoToken(); 515const TestLog::EndSampleInfoToken TestLog::EndSampleInfo = TestLog::EndSampleInfoToken(); 516const TestLog::BeginSampleToken TestLog::Sample = TestLog::BeginSampleToken(); 517const TestLog::EndSampleToken TestLog::EndSample = TestLog::EndSampleToken(); 518const TestLog::EndSampleListToken TestLog::EndSampleList = TestLog::EndSampleListToken(); 519 520} // tcu 521