| /*------------------------------------------------------------------------- |
| * drawElements Quality Program Tester Core |
| * ---------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Test Log C++ Wrapper. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "deCommandLine.h" |
| #include "tcuTestLog.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuSurface.hpp" |
| #include "deMath.h" |
| |
| #include <limits> |
| |
| namespace tcu |
| { |
| |
| class LogWriteFailedError : public ResourceError |
| { |
| public: |
| LogWriteFailedError (void) : ResourceError("Writing to test log failed") {} |
| }; |
| |
| enum |
| { |
| MAX_IMAGE_SIZE_2D = 4096, |
| MAX_IMAGE_SIZE_3D = 128 |
| }; |
| |
| // LogImage |
| |
| LogImage::LogImage (const std::string& name, const std::string& description, const Surface& surface, qpImageCompressionMode compression) |
| : m_name (name) |
| , m_description (description) |
| , m_access (surface.getAccess()) |
| , m_scale (1.0f, 1.0f, 1.0f, 1.0f) |
| , m_bias (0.0f, 0.0f, 0.0f, 0.0f) |
| , m_compression (compression) |
| { |
| } |
| |
| LogImage::LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, qpImageCompressionMode compression) |
| : m_name (name) |
| , m_description (description) |
| , m_access (access) |
| , m_scale (1.0f, 1.0f, 1.0f, 1.0f) |
| , m_bias (0.0f, 0.0f, 0.0f, 0.0f) |
| , m_compression (compression) |
| { |
| // Simplify combined formats that only use a single channel |
| if (tcu::isCombinedDepthStencilType(m_access.getFormat().type)) |
| { |
| if (m_access.getFormat().order == tcu::TextureFormat::D) |
| m_access = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH); |
| else if (m_access.getFormat().order == tcu::TextureFormat::S) |
| m_access = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL); |
| } |
| |
| // Implicit scale and bias |
| if (m_access.getFormat().order != tcu::TextureFormat::DS) |
| computePixelScaleBias(m_access, m_scale, m_bias); |
| else |
| { |
| // Pack D and S bias and scale to R and G |
| const ConstPixelBufferAccess depthAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH); |
| const ConstPixelBufferAccess stencilAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL); |
| tcu::Vec4 depthScale; |
| tcu::Vec4 depthBias; |
| tcu::Vec4 stencilScale; |
| tcu::Vec4 stencilBias; |
| |
| computePixelScaleBias(depthAccess, depthScale, depthBias); |
| computePixelScaleBias(stencilAccess, stencilScale, stencilBias); |
| |
| m_scale = tcu::Vec4(depthScale.x(), stencilScale.x(), 0.0f, 0.0f); |
| m_bias = tcu::Vec4(depthBias.x(), stencilBias.x(), 0.0f, 0.0f); |
| } |
| } |
| |
| LogImage::LogImage (const std::string& name, const std::string& description, const ConstPixelBufferAccess& access, const Vec4& scale, const Vec4& bias, qpImageCompressionMode compression) |
| : m_name (name) |
| , m_description (description) |
| , m_access (access) |
| , m_scale (scale) |
| , m_bias (bias) |
| , m_compression (compression) |
| { |
| // Cannot set scale and bias of combined formats |
| DE_ASSERT(access.getFormat().order != tcu::TextureFormat::DS); |
| |
| // Simplify access |
| if (tcu::isCombinedDepthStencilType(access.getFormat().type)) |
| { |
| if (access.getFormat().order == tcu::TextureFormat::D) |
| m_access = tcu::getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_DEPTH); |
| if (access.getFormat().order == tcu::TextureFormat::S) |
| m_access = tcu::getEffectiveDepthStencilAccess(access, tcu::Sampler::MODE_STENCIL); |
| else |
| { |
| // Cannot log a DS format |
| DE_ASSERT(false); |
| return; |
| } |
| } |
| } |
| |
| void LogImage::write (TestLog& log) const |
| { |
| if (m_access.getFormat().order != tcu::TextureFormat::DS) |
| log.writeImage(m_name.c_str(), m_description.c_str(), m_access, m_scale, m_bias, m_compression); |
| else |
| { |
| const ConstPixelBufferAccess depthAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_DEPTH); |
| const ConstPixelBufferAccess stencilAccess = tcu::getEffectiveDepthStencilAccess(m_access, tcu::Sampler::MODE_STENCIL); |
| |
| log.startImageSet(m_name.c_str(), m_description.c_str()); |
| log.writeImage("Depth", "Depth channel", depthAccess, m_scale.swizzle(0, 0, 0, 0), m_bias.swizzle(0, 0, 0, 0), m_compression); |
| log.writeImage("Stencil", "Stencil channel", stencilAccess, m_scale.swizzle(1, 1, 1, 1), m_bias.swizzle(1, 1, 1, 1), m_compression); |
| log.endImageSet(); |
| } |
| } |
| |
| // MessageBuilder |
| |
| MessageBuilder::MessageBuilder (const MessageBuilder& other) |
| : m_log(other.m_log) |
| { |
| m_str.str(other.m_str.str()); |
| } |
| |
| MessageBuilder& MessageBuilder::operator= (const MessageBuilder& other) |
| { |
| m_log = other.m_log; |
| m_str.str(other.m_str.str()); |
| return *this; |
| } |
| |
| TestLog& MessageBuilder::operator<< (const TestLog::EndMessageToken&) |
| { |
| m_log->writeMessage(m_str.str().c_str()); |
| return *m_log; |
| } |
| |
| // SampleBuilder |
| |
| TestLog& SampleBuilder::operator<< (const TestLog::EndSampleToken&) |
| { |
| m_log->startSample(); |
| |
| for (std::vector<Value>::const_iterator val = m_values.begin(); val != m_values.end(); ++val) |
| { |
| if (val->type == Value::TYPE_FLOAT64) |
| m_log->writeSampleValue(val->value.float64); |
| else if (val->type == Value::TYPE_INT64) |
| m_log->writeSampleValue(val->value.int64); |
| else |
| DE_ASSERT(false); |
| } |
| |
| m_log->endSample(); |
| |
| return *m_log; |
| } |
| |
| // TestLog |
| |
| TestLog::TestLog (const char* fileName, int argc, char** argv, deUint32 flags) |
| : m_log(qpTestLog_createFileLog(fileName, argc, argv, flags)) |
| { |
| if (!m_log) |
| throw ResourceError(std::string("Failed to open test log file '") + fileName + "'"); |
| } |
| |
| TestLog::TestLog (const char* fileName, const std::string& cmdLine, deUint32 flags) |
| { |
| |
| deCommandLine* parsedCmdLine = deCommandLine_parse(cmdLine.c_str()); |
| if (!parsedCmdLine) |
| throw std::bad_alloc(); |
| |
| m_log = qpTestLog_createFileLog(fileName, parsedCmdLine->numArgs, parsedCmdLine->args, flags); |
| deCommandLine_destroy(parsedCmdLine); |
| |
| if (!m_log) |
| throw ResourceError(std::string("Failed to open test log file '") + fileName + "'"); |
| } |
| |
| TestLog::~TestLog (void) |
| { |
| qpTestLog_destroy(m_log); |
| } |
| |
| void TestLog::writeMessage (const char* msgStr) |
| { |
| if (qpTestLog_writeText(m_log, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, msgStr) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startImageSet (const char* name, const char* description) |
| { |
| if (qpTestLog_startImageSet(m_log, name, description) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endImageSet (void) |
| { |
| if (qpTestLog_endImageSet(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| template <int Size> |
| static Vector<int, Size> computeScaledSize (const Vector<int, Size>& imageSize, int maxSize) |
| { |
| bool allInRange = true; |
| for (int i = 0; i < Size; i++) |
| allInRange = allInRange && (imageSize[i] <= maxSize); |
| |
| if (allInRange) |
| return imageSize; |
| else |
| { |
| float d = 1.0f; |
| for (int i = 0; i < Size; i++) |
| d = de::max(d, (float)imageSize[i] / (float)maxSize); |
| |
| Vector<int, Size> res; |
| for (int i = 0; i < Size; i++) |
| res[i] = de::max(1, deRoundFloatToInt32((float)imageSize[i] / d)); |
| |
| return res; |
| } |
| } |
| |
| void TestLog::writeImage (const char* name, const char* description, const ConstPixelBufferAccess& access, const Vec4& pixelScale, const Vec4& pixelBias, qpImageCompressionMode compressionMode) |
| { |
| const TextureFormat& format = access.getFormat(); |
| int width = access.getWidth(); |
| int height = access.getHeight(); |
| int depth = access.getDepth(); |
| |
| // Writing a combined image does not make sense |
| DE_ASSERT(!tcu::isCombinedDepthStencilType(access.getFormat().type)); |
| |
| // Do not bother with preprocessing if images are not stored |
| if ((qpTestLog_getLogFlags(m_log) & QP_TEST_LOG_EXCLUDE_IMAGES) != 0) |
| return; |
| |
| if (depth == 1 && format.type == TextureFormat::UNORM_INT8 |
| && width <= MAX_IMAGE_SIZE_2D && height <= MAX_IMAGE_SIZE_2D |
| && (format.order == TextureFormat::RGB || format.order == TextureFormat::RGBA) |
| && access.getPixelPitch() == access.getFormat().getPixelSize() |
| && pixelBias[0] == 0.0f && pixelBias[1] == 0.0f && pixelBias[2] == 0.0f && pixelBias[3] == 0.0f |
| && pixelScale[0] == 1.0f && pixelScale[1] == 1.0f && pixelScale[2] == 1.0f && pixelScale[3] == 1.0f) |
| { |
| // Fast-path. |
| bool isRGBA = format.order == TextureFormat::RGBA; |
| |
| writeImage(name, description, compressionMode, |
| isRGBA ? QP_IMAGE_FORMAT_RGBA8888 : QP_IMAGE_FORMAT_RGB888, |
| width, height, access.getRowPitch(), access.getDataPtr()); |
| } |
| else if (depth == 1) |
| { |
| Sampler sampler (Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::LINEAR, Sampler::NEAREST); |
| IVec2 logImageSize = computeScaledSize(IVec2(width, height), MAX_IMAGE_SIZE_2D); |
| tcu::TextureLevel logImage (TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageSize.x(), logImageSize.y(), 1); |
| PixelBufferAccess logImageAccess = logImage.getAccess(); |
| std::ostringstream longDesc; |
| |
| longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")"; |
| |
| for (int y = 0; y < logImage.getHeight(); y++) |
| { |
| for (int x = 0; x < logImage.getWidth(); x++) |
| { |
| float yf = ((float)y + 0.5f) / (float)logImage.getHeight(); |
| float xf = ((float)x + 0.5f) / (float)logImage.getWidth(); |
| Vec4 s = access.sample2D(sampler, sampler.minFilter, xf, yf, 0)*pixelScale + pixelBias; |
| |
| logImageAccess.setPixel(s, x, y); |
| } |
| } |
| |
| writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888, |
| logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(), |
| logImageAccess.getDataPtr()); |
| } |
| else |
| { |
| // Isometric splat volume rendering. |
| const float blendFactor = 0.85f; |
| IVec3 scaledSize = computeScaledSize(IVec3(width, height, depth), MAX_IMAGE_SIZE_3D); |
| int w = scaledSize.x(); |
| int h = scaledSize.y(); |
| int d = scaledSize.z(); |
| int logImageW = w+d - 1; |
| int logImageH = w+d+h; |
| std::vector<float> blendImage (logImageW*logImageH*4, 0.0f); |
| PixelBufferAccess blendImageAccess (TextureFormat(TextureFormat::RGBA, TextureFormat::FLOAT), logImageW, logImageH, 1, &blendImage[0]); |
| tcu::TextureLevel logImage (TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8), logImageW, logImageH, 1); |
| PixelBufferAccess logImageAccess = logImage.getAccess(); |
| Sampler sampler (Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::CLAMP_TO_EDGE, Sampler::NEAREST, Sampler::NEAREST); |
| std::ostringstream longDesc; |
| |
| // \note Back-to-front. |
| for (int z = d-1; z >= 0; z--) |
| { |
| for (int y = 0; y < h; y++) |
| { |
| for (int x = 0; x < w; x++) |
| { |
| int px = w - (x + 1) + z; |
| int py = (w + d + h) - (x + y + z + 1); |
| |
| float xf = ((float)x + 0.5f) / (float)w; |
| float yf = ((float)y + 0.5f) / (float)h; |
| float zf = ((float)z + 0.5f) / (float)d; |
| |
| Vec4 p = blendImageAccess.getPixel(px, py); |
| Vec4 s = access.sample3D(sampler, sampler.minFilter, xf, yf, zf); |
| Vec4 b = s + p*blendFactor; |
| |
| blendImageAccess.setPixel(b, px, py); |
| } |
| } |
| } |
| |
| // Scale blend image nicely. |
| longDesc << description << " (p' = p * " << pixelScale << " + " << pixelBias << ")"; |
| |
| // Write to final image. |
| tcu::clear(logImageAccess, tcu::IVec4(0x33, 0x66, 0x99, 0xff)); |
| |
| for (int z = 0; z < d; z++) |
| { |
| for (int y = 0; y < h; y++) |
| { |
| for (int x = 0; x < w; x++) |
| { |
| if (z != 0 && !(x == 0 || y == h-1 || y == h-2)) |
| continue; |
| |
| int px = w - (x + 1) + z; |
| int py = (w + d + h) - (x + y + z + 1); |
| Vec4 s = blendImageAccess.getPixel(px, py)*pixelScale + pixelBias; |
| |
| logImageAccess.setPixel(s, px, py); |
| } |
| } |
| } |
| |
| writeImage(name, longDesc.str().c_str(), compressionMode, QP_IMAGE_FORMAT_RGBA8888, |
| logImageAccess.getWidth(), logImageAccess.getHeight(), logImageAccess.getRowPitch(), |
| logImageAccess.getDataPtr()); |
| } |
| } |
| |
| void TestLog::writeImage (const char* name, const char* description, qpImageCompressionMode compressionMode, qpImageFormat format, int width, int height, int stride, const void* data) |
| { |
| if (qpTestLog_writeImage(m_log, name, description, compressionMode, format, width, height, stride, data) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startSection (const char* name, const char* description) |
| { |
| if (qpTestLog_startSection(m_log, name, description) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endSection (void) |
| { |
| if (qpTestLog_endSection(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startShaderProgram (bool linkOk, const char* linkInfoLog) |
| { |
| if (qpTestLog_startShaderProgram(m_log, linkOk?DE_TRUE:DE_FALSE, linkInfoLog) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endShaderProgram (void) |
| { |
| if (qpTestLog_endShaderProgram(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeShader (qpShaderType type, const char* source, bool compileOk, const char* infoLog) |
| { |
| if (qpTestLog_writeShader(m_log, type, source, compileOk?DE_TRUE:DE_FALSE, infoLog) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeSpirVAssemblySource (const char* source) |
| { |
| if (qpTestLog_writeSpirVAssemblySource(m_log, source) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeKernelSource (const char* source) |
| { |
| if (qpTestLog_writeKernelSource(m_log, source) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeCompileInfo (const char* name, const char* description, bool compileOk, const char* infoLog) |
| { |
| if (qpTestLog_writeCompileInfo(m_log, name, description, compileOk ? DE_TRUE : DE_FALSE, infoLog) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeFloat (const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value) |
| { |
| if (qpTestLog_writeFloat(m_log, name, description, unit, tag, value) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeInteger (const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value) |
| { |
| if (qpTestLog_writeInteger(m_log, name, description, unit, tag, value) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startEglConfigSet (const char* name, const char* description) |
| { |
| if (qpTestLog_startEglConfigSet(m_log, name, description) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeEglConfig (const qpEglConfigInfo* config) |
| { |
| if (qpTestLog_writeEglConfig(m_log, config) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endEglConfigSet (void) |
| { |
| if (qpTestLog_endEglConfigSet(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startCase (const char* testCasePath, qpTestCaseType testCaseType) |
| { |
| if (qpTestLog_startCase(m_log, testCasePath, testCaseType) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endCase (qpTestResult result, const char* description) |
| { |
| if (qpTestLog_endCase(m_log, result, description) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::terminateCase (qpTestResult result) |
| { |
| if (qpTestLog_terminateCase(m_log, result) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startTestsCasesTime (void) |
| { |
| if (qpTestLog_startTestsCasesTime(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endTestsCasesTime (void) |
| { |
| if (qpTestLog_endTestsCasesTime(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startSampleList (const std::string& name, const std::string& description) |
| { |
| if (qpTestLog_startSampleList(m_log, name.c_str(), description.c_str()) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startSampleInfo (void) |
| { |
| if (qpTestLog_startSampleInfo(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeValueInfo (const std::string& name, const std::string& description, const std::string& unit, qpSampleValueTag tag) |
| { |
| if (qpTestLog_writeValueInfo(m_log, name.c_str(), description.c_str(), unit.empty() ? DE_NULL : unit.c_str(), tag) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endSampleInfo (void) |
| { |
| if (qpTestLog_endSampleInfo(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::startSample (void) |
| { |
| if (qpTestLog_startSample(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeSampleValue (double value) |
| { |
| if (qpTestLog_writeValueFloat(m_log, value) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::writeSampleValue (deInt64 value) |
| { |
| if (qpTestLog_writeValueInteger(m_log, value) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endSample (void) |
| { |
| if (qpTestLog_endSample(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| void TestLog::endSampleList (void) |
| { |
| if (qpTestLog_endSampleList(m_log) == DE_FALSE) |
| throw LogWriteFailedError(); |
| } |
| |
| bool TestLog::isShaderLoggingEnabled (void) |
| { |
| return (qpTestLog_getLogFlags(m_log) & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) == 0; |
| } |
| |
| const TestLog::BeginMessageToken TestLog::Message = TestLog::BeginMessageToken(); |
| const TestLog::EndMessageToken TestLog::EndMessage = TestLog::EndMessageToken(); |
| const TestLog::EndImageSetToken TestLog::EndImageSet = TestLog::EndImageSetToken(); |
| const TestLog::EndSectionToken TestLog::EndSection = TestLog::EndSectionToken(); |
| const TestLog::EndShaderProgramToken TestLog::EndShaderProgram = TestLog::EndShaderProgramToken(); |
| const TestLog::SampleInfoToken TestLog::SampleInfo = TestLog::SampleInfoToken(); |
| const TestLog::EndSampleInfoToken TestLog::EndSampleInfo = TestLog::EndSampleInfoToken(); |
| const TestLog::BeginSampleToken TestLog::Sample = TestLog::BeginSampleToken(); |
| const TestLog::EndSampleToken TestLog::EndSample = TestLog::EndSampleToken(); |
| const TestLog::EndSampleListToken TestLog::EndSampleList = TestLog::EndSampleListToken(); |
| |
| } // tcu |