Commit eeec3b14 by Jamie Madill Committed by Commit Bot

dEQP: Add override for ES2 Color Clear Test.

This test has a bug where the masked clear flag is not being set properly, leading to incorrect test behaviour and missing coverage. Until this bug is fixed in dEQP we can override it with a fixed version. Also remove a prior override that had an upstreamed fix. Bug: angleproject:2455 Change-Id: Ia2c57251b2a97eb5b4d8a4bdb0ce31796a66ea01 Reviewed-on: https://chromium-review.googlesource.com/1013859Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org> Reviewed-by: 's avatarLuc Ferron <lucferron@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent aaa19de0
...@@ -135,7 +135,10 @@ ...@@ -135,7 +135,10 @@
'<(deqp_path)/modules/gles2/functional/es2fBufferWriteTests.hpp', '<(deqp_path)/modules/gles2/functional/es2fBufferWriteTests.hpp',
'<(deqp_path)/modules/gles2/functional/es2fClippingTests.cpp', '<(deqp_path)/modules/gles2/functional/es2fClippingTests.cpp',
'<(deqp_path)/modules/gles2/functional/es2fClippingTests.hpp', '<(deqp_path)/modules/gles2/functional/es2fClippingTests.hpp',
'<(deqp_path)/modules/gles2/functional/es2fColorClearTest.cpp', # Work around a bug in this test by using a local override.
# TODO(jmadill): Restore after rolling dEQP.
#'<(deqp_path)/modules/gles2/functional/es2fColorClearTest.cpp',
'<(angle_path)/src/tests/deqp_support/es2fColorClearTest.cpp',
'<(deqp_path)/modules/gles2/functional/es2fColorClearTest.hpp', '<(deqp_path)/modules/gles2/functional/es2fColorClearTest.hpp',
'<(deqp_path)/modules/gles2/functional/es2fDebugMarkerTests.cpp', '<(deqp_path)/modules/gles2/functional/es2fDebugMarkerTests.cpp',
'<(deqp_path)/modules/gles2/functional/es2fDebugMarkerTests.hpp', '<(deqp_path)/modules/gles2/functional/es2fDebugMarkerTests.hpp',
...@@ -1114,8 +1117,7 @@ ...@@ -1114,8 +1117,7 @@
'<(deqp_path)/framework/qphelper/qpCrashHandler.c', '<(deqp_path)/framework/qphelper/qpCrashHandler.c',
'<(deqp_path)/framework/qphelper/qpDebugOut.c', '<(deqp_path)/framework/qphelper/qpDebugOut.c',
'<(deqp_path)/framework/qphelper/qpInfo.c', '<(deqp_path)/framework/qphelper/qpInfo.c',
# TODO(jmadill): Restore this when we upstream the change. '<(deqp_path)/framework/qphelper/qpTestLog.c',
#'<(deqp_path)/framework/qphelper/qpTestLog.c',
'<(deqp_path)/framework/qphelper/qpWatchDog.c', '<(deqp_path)/framework/qphelper/qpWatchDog.c',
'<(deqp_path)/framework/qphelper/qpXmlWriter.c', '<(deqp_path)/framework/qphelper/qpXmlWriter.c',
'<(deqp_path)/framework/randomshaders/rsgBinaryOps.cpp', '<(deqp_path)/framework/randomshaders/rsgBinaryOps.cpp',
...@@ -1184,8 +1186,6 @@ ...@@ -1184,8 +1186,6 @@
'<(deqp_path)/modules/glshared/glsTextureTestUtil.cpp', '<(deqp_path)/modules/glshared/glsTextureTestUtil.cpp',
'<(deqp_path)/modules/glshared/glsUniformBlockCase.cpp', '<(deqp_path)/modules/glshared/glsUniformBlockCase.cpp',
'<(deqp_path)/modules/glshared/glsVertexArrayTests.cpp', '<(deqp_path)/modules/glshared/glsVertexArrayTests.cpp',
# TODO(jmadill): Remove this when we upstream the change.
'<(angle_path)/src/tests/deqp_support/qpTestLog.c',
'<(angle_path)/src/tests/deqp_support/tcuANGLENativeDisplayFactory.cpp', '<(angle_path)/src/tests/deqp_support/tcuANGLENativeDisplayFactory.cpp',
'<(angle_path)/src/tests/deqp_support/tcuANGLENativeDisplayFactory.h', '<(angle_path)/src/tests/deqp_support/tcuANGLENativeDisplayFactory.h',
# TODO(jmadill): integrate with dEQP # TODO(jmadill): integrate with dEQP
......
...@@ -188,9 +188,10 @@ ...@@ -188,9 +188,10 @@
1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.depth.srgb8 = FAIL 1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.depth.srgb8 = FAIL
// Vulkan failures // Vulkan failures
2455 VULKAN : dEQP-GLES2.functional.color_clear.masked_scissor* = SKIP 2455 VULKAN : dEQP-GLES2.functional.color_clear.masked_rg* = FAIL
2161 VULKAN : dEQP-GLES2.functional.color_clear.complex_* = SKIP 2455 VULKAN : dEQP-GLES2.functional.color_clear.masked_scissor* = FAIL
2161 VULKAN : dEQP-GLES2.functional.color_clear.long_masked_* = SKIP 2455 VULKAN : dEQP-GLES2.functional.color_clear.complex_* = FAIL
2455 VULKAN : dEQP-GLES2.functional.color_clear.long_masked_* = FAIL
2161 VULKAN : dEQP-GLES2.functional.prerequisite.* = SKIP 2161 VULKAN : dEQP-GLES2.functional.prerequisite.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.implementation_limits.* = SKIP 2161 VULKAN : dEQP-GLES2.functional.implementation_limits.* = SKIP
2161 VULKAN : dEQP-GLES2.functional.buffer.* = SKIP 2161 VULKAN : dEQP-GLES2.functional.buffer.* = SKIP
......
/*-------------------------------------------------------------------------
* drawElements Quality Program OpenGL ES 2.0 Module
* -------------------------------------------------
*
* 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 Screen clearing test.
*//*--------------------------------------------------------------------*/
#include "es2fColorClearTest.hpp"
#include "tcuRGBA.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include "gluRenderContext.hpp"
#include "gluPixelTransfer.hpp"
#include "tcuVector.hpp"
#include "tcuRenderTarget.hpp"
#include "deRandom.hpp"
#include "deInt32.h"
#include "glwFunctions.hpp"
#include "glwEnums.hpp"
#include <vector>
using tcu::RGBA;
using tcu::Surface;
using tcu::TestLog;
using namespace std;
namespace deqp
{
namespace gles2
{
namespace Functional
{
class ColorClearCase : public TestCase
{
public:
ColorClearCase(Context& context, const char* name, int numIters, int numClearsMin, int numClearsMax, bool testAlpha, bool testScissoring, bool testColorMasks, bool firstClearFull)
: TestCase (context, name, name)
, m_numIters (numIters)
, m_numClearsMin (numClearsMin)
, m_numClearsMax (numClearsMax)
, m_testAlpha (testAlpha)
, m_testScissoring (testScissoring)
, m_testColorMasks (testColorMasks)
, m_firstClearFull (firstClearFull)
{
m_curIter = 0;
}
virtual ~ColorClearCase (void)
{
}
virtual IterateResult iterate (void);
private:
const int m_numIters;
const int m_numClearsMin;
const int m_numClearsMax;
const bool m_testAlpha;
const bool m_testScissoring;
const bool m_testColorMasks;
const bool m_firstClearFull;
int m_curIter;
};
class ClearInfo
{
public:
ClearInfo (const tcu::IVec4& rect, deUint8 colorMask, tcu::RGBA color)
: m_rect(rect), m_colorMask(colorMask), m_color(color)
{
}
tcu::IVec4 m_rect;
deUint8 m_colorMask;
tcu::RGBA m_color;
};
TestCase::IterateResult ColorClearCase::iterate (void)
{
TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
const tcu::RenderTarget& renderTarget = m_context.getRenderTarget();
const tcu::PixelFormat& pixelFormat = renderTarget.getPixelFormat();
const int targetWidth = renderTarget.getWidth();
const int targetHeight = renderTarget.getHeight();
const int numPixels = targetWidth * targetHeight;
de::Random rnd (deInt32Hash(m_curIter));
vector<deUint8> pixelKnownChannelMask (numPixels, 0);
Surface refImage (targetWidth, targetHeight);
Surface resImage (targetWidth, targetHeight);
Surface diffImage (targetWidth, targetHeight);
int numClears = rnd.getUint32() % (m_numClearsMax + 1 - m_numClearsMin) + m_numClearsMin;
std::vector<ClearInfo> clearOps;
if (m_testScissoring)
gl.enable(GL_SCISSOR_TEST);
for (int clearNdx = 0; clearNdx < numClears; clearNdx++)
{
// Rectangle.
int clearX;
int clearY;
int clearWidth;
int clearHeight;
if (!m_testScissoring || (clearNdx == 0 && m_firstClearFull))
{
clearX = 0;
clearY = 0;
clearWidth = targetWidth;
clearHeight = targetHeight;
}
else
{
clearX = (rnd.getUint32() % (2*targetWidth)) - targetWidth;
clearY = (rnd.getUint32() % (2*targetHeight)) - targetHeight;
clearWidth = (rnd.getUint32() % targetWidth);
clearHeight = (rnd.getUint32() % targetHeight);
}
gl.scissor(clearX, clearY, clearWidth, clearHeight);
// Color.
int r = (int)(rnd.getUint32() & 0xFF);
int g = (int)(rnd.getUint32() & 0xFF);
int b = (int)(rnd.getUint32() & 0xFF);
int a = m_testAlpha ? (int)(rnd.getUint32() & 0xFF) : 0xFF;
RGBA clearCol(r, g, b, a);
gl.clearColor(float(r)/255.0f, float(g)/255.0f, float(b)/255.0f, float(a)/255.0f);
// Mask.
deUint8 clearMask;
if (!m_testColorMasks || (clearNdx == 0 && m_firstClearFull))
clearMask = 0xF;
else
clearMask = (rnd.getUint32() & 0xF);
gl.colorMask((clearMask&0x1) != 0, (clearMask&0x2) != 0, (clearMask&0x4) != 0, (clearMask&0x8) != 0);
// Clear & store op.
gl.clear(GL_COLOR_BUFFER_BIT);
clearOps.push_back(ClearInfo(tcu::IVec4(clearX, clearY, clearWidth, clearHeight), clearMask, clearCol));
// Let watchdog know we're alive.
m_testCtx.touchWatchdog();
}
// Compute reference image.
{
for (int y = 0; y < targetHeight; y++)
{
std::vector<ClearInfo> scanlineClearOps;
// Find all rectangles affecting this scanline.
for (int opNdx = 0; opNdx < (int)clearOps.size(); opNdx++)
{
ClearInfo& op = clearOps[opNdx];
if (de::inBounds(y, op.m_rect.y(), op.m_rect.y()+op.m_rect.w()))
scanlineClearOps.push_back(op);
}
// Compute reference for scanline.
int x = 0;
while (x < targetWidth)
{
tcu::RGBA spanColor;
deUint8 spanKnownMask = 0;
int spanLength = (targetWidth - x);
for (int opNdx = (int)scanlineClearOps.size() - 1; opNdx >= 0; opNdx--)
{
const ClearInfo& op = scanlineClearOps[opNdx];
if (de::inBounds(x, op.m_rect.x(), op.m_rect.x()+op.m_rect.z()) &&
de::inBounds(y, op.m_rect.y(), op.m_rect.y()+op.m_rect.w()))
{
// Compute span length until end of given rectangle.
spanLength = deMin32(spanLength, op.m_rect.x() + op.m_rect.z() - x);
tcu::RGBA clearCol = op.m_color;
deUint8 clearMask = op.m_colorMask;
if ((clearMask & 0x1) && !(spanKnownMask & 0x1)) spanColor.setRed(clearCol.getRed());
if ((clearMask & 0x2) && !(spanKnownMask & 0x2)) spanColor.setGreen(clearCol.getGreen());
if ((clearMask & 0x4) && !(spanKnownMask & 0x4)) spanColor.setBlue(clearCol.getBlue());
if ((clearMask & 0x8) && !(spanKnownMask & 0x8)) spanColor.setAlpha(clearCol.getAlpha());
spanKnownMask |= clearMask;
// Break if have all colors.
if (spanKnownMask == 0xF)
break;
}
else if (op.m_rect.x() > x)
spanLength = deMin32(spanLength, op.m_rect.x() - x);
}
// Set reference alpha channel to 0xFF, if no alpha in target.
if (pixelFormat.alphaBits == 0)
spanColor.setAlpha(0xFF);
// Fill the span.
for (int ndx = 0; ndx < spanLength; ndx++)
{
refImage.setPixel(x + ndx, y, spanColor);
pixelKnownChannelMask[y*targetWidth + x + ndx] |= spanKnownMask;
}
x += spanLength;
}
}
}
glu::readPixels(m_context.getRenderContext(), 0, 0, resImage.getAccess());
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels");
// Compute difference image.
RGBA colorThreshold = pixelFormat.getColorThreshold();
RGBA matchColor(0, 255, 0, 255);
RGBA diffColor(255, 0, 0, 255);
RGBA maxDiff(0, 0, 0, 0);
bool isImageOk = true;
if (gl.isEnabled(GL_DITHER))
{
colorThreshold.setRed(colorThreshold.getRed() + 1);
colorThreshold.setGreen(colorThreshold.getGreen() + 1);
colorThreshold.setBlue(colorThreshold.getBlue() + 1);
colorThreshold.setAlpha(colorThreshold.getAlpha() + 1);
}
for (int y = 0; y < targetHeight; y++)
for (int x = 0; x < targetWidth; x++)
{
int offset = (y*targetWidth + x);
RGBA refRGBA = refImage.getPixel(x, y);
RGBA resRGBA = resImage.getPixel(x, y);
deUint8 colMask = pixelKnownChannelMask[offset];
RGBA diff = computeAbsDiffMasked(refRGBA, resRGBA, colMask);
bool isPixelOk = diff.isBelowThreshold(colorThreshold);
diffImage.setPixel(x, y, isPixelOk ? matchColor : diffColor);
isImageOk = isImageOk && isPixelOk;
maxDiff = max(maxDiff, diff);
}
if (isImageOk)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
m_testCtx.getLog() << tcu::TestLog::Message << "Image comparison failed, max diff = " << maxDiff << ", threshold = " << colorThreshold << tcu::TestLog::EndMessage;
log << TestLog::ImageSet("Result", "Resulting framebuffer")
<< TestLog::Image("Result", "Resulting framebuffer", resImage)
<< TestLog::Image("Reference", "Reference image", refImage)
<< TestLog::Image("DiffMask", "Failing pixels", diffImage)
<< TestLog::EndImageSet;
return TestCase::STOP;
}
bool isFinal = (++m_curIter == m_numIters);
// On final frame, dump images.
if (isFinal)
{
log << TestLog::ImageSet("Result", "Resulting framebuffer")
<< TestLog::Image("Result", "Resulting framebuffer", resImage)
<< TestLog::EndImageSet;
}
return isFinal ? TestCase::STOP : TestCase::CONTINUE;
}
ColorClearTest::ColorClearTest (Context& context) : TestCaseGroup(context, "color_clear", "Color Clear Tests")
{
}
ColorClearTest::~ColorClearTest (void)
{
}
void ColorClearTest::init (void)
{
// name iters, #..#, alpha?, scissor,masks, 1stfull?
addChild(new ColorClearCase(m_context, "single_rgb", 30, 1,3, false, false, false, true ));
addChild(new ColorClearCase(m_context, "single_rgba", 30, 1,3, true, false, false, true ));
addChild(new ColorClearCase(m_context, "multiple_rgb", 15, 4,20, false, false, false, true ));
addChild(new ColorClearCase(m_context, "multiple_rgba", 15, 4,20, true, false, false, true ));
addChild(new ColorClearCase(m_context, "long_rgb", 2, 100,500, false, false, false, true ));
addChild(new ColorClearCase(m_context, "long_rgba", 2, 100,500, true, false, false, true ));
addChild(new ColorClearCase(m_context, "subclears_rgb", 15, 4,30, false, false, false, false ));
addChild(new ColorClearCase(m_context, "subclears_rgba", 15, 4,30, true, false, false, false ));
addChild(new ColorClearCase(m_context, "short_scissored_rgb", 30, 2,4, false, true, false, true ));
addChild(new ColorClearCase(m_context, "scissored_rgb", 15, 4,30, false, true, false, true ));
addChild(new ColorClearCase(m_context, "scissored_rgba", 15, 4,30, true, true, false, true ));
addChild(new ColorClearCase(m_context, "masked_rgb", 15, 4,30, false, false, true, true ));
addChild(new ColorClearCase(m_context, "masked_rgba", 15, 4,30, true, false, true, true ));
addChild(new ColorClearCase(m_context, "masked_scissored_rgb", 15, 4,30, false, true, true, true ));
addChild(new ColorClearCase(m_context, "masked_scissored_rgba", 15, 4,30, true, true, true, true ));
addChild(new ColorClearCase(m_context, "complex_rgb", 15, 5,50, false, true, true, false ));
addChild(new ColorClearCase(m_context, "complex_rgba", 15, 5,50, true, true, true, false ));
addChild(new ColorClearCase(m_context, "long_masked_rgb", 2, 100,500, false, true, true, true ));
addChild(new ColorClearCase(m_context, "long_masked_rgba", 2, 100,500, true, true, true, true ));
}
} // Functional
} // gles2
} // deqp
/*-------------------------------------------------------------------------
* drawElements TestLog Library
* ----------------------------
*
* 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 case result logging
*//*--------------------------------------------------------------------*/
#include "qpTestLog.h"
#include "qpXmlWriter.h"
#include "qpInfo.h"
#include "qpDebugOut.h"
#include "deMemory.h"
#include "deInt32.h"
#include "deString.h"
#include "deMutex.h"
#if defined(QP_SUPPORT_PNG)
# include <png.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#if (DE_OS == DE_OS_WIN32)
# include <windows.h>
# include <io.h>
#endif
#if defined(DE_DEBUG)
/* Utils for verifying container (Section, ImageSet, EglConfigSet) usage in debug builds. */
typedef enum ContainerType_e
{
CONTAINERTYPE_SECTION = 0,
CONTAINERTYPE_IMAGESET,
CONTAINERTYPE_EGLCONFIGSET,
CONTAINERTYPE_SHADERPROGRAM,
CONTAINERTYPE_SAMPLELIST,
CONTAINERTYPE_SAMPLEINFO,
CONTAINERTYPE_SAMPLE,
CONTAINERTYPE_LAST
} ContainerType;
DE_INLINE deBool childContainersOk (ContainerType type)
{
return type == CONTAINERTYPE_SECTION || type == CONTAINERTYPE_SAMPLELIST;
}
enum
{
MAX_CONTAINER_STACK_DEPTH = 32
};
typedef struct ContainerStack_s
{
int numElements;
ContainerType elements[MAX_CONTAINER_STACK_DEPTH];
} ContainerStack;
DE_INLINE void ContainerStack_reset (ContainerStack* stack)
{
deMemset(stack, 0, sizeof(ContainerStack));
}
DE_INLINE deBool ContainerStack_isEmpty (const ContainerStack* stack)
{
return stack->numElements == 0;
}
DE_INLINE deBool ContainerStack_push (ContainerStack* stack, ContainerType type)
{
if (stack->numElements == MAX_CONTAINER_STACK_DEPTH)
return DE_FALSE;
if (stack->numElements > 0 && !childContainersOk(stack->elements[stack->numElements-1]))
return DE_FALSE;
stack->elements[stack->numElements] = type;
stack->numElements += 1;
return DE_TRUE;
}
DE_INLINE ContainerType ContainerStack_pop (ContainerStack* stack)
{
DE_ASSERT(stack->numElements > 0);
stack->numElements -= 1;
return stack->elements[stack->numElements];
}
DE_INLINE ContainerType ContainerStack_getTop (const ContainerStack* stack)
{
if (stack->numElements > 0)
return stack->elements[stack->numElements-1];
else
return CONTAINERTYPE_LAST;
}
#endif
/* qpTestLog instance */
struct qpTestLog_s
{
deUint32 flags; /*!< Logging flags. */
deMutex lock; /*!< Lock for mutable state below. */
/* State protected by lock. */
FILE* outputFile;
qpXmlWriter* writer;
deBool isSessionOpen;
deBool isCaseOpen;
#if defined(DE_DEBUG)
ContainerStack containerStack; /*!< For container usage verification. */
#endif
};
/* Maps integer to string. */
typedef struct qpKeyStringMap_s
{
int key;
char* string;
} qpKeyStringMap;
static const char* LOG_FORMAT_VERSION = "0.3.4";
/* Mapping enum to above strings... */
static const qpKeyStringMap s_qpTestTypeMap[] =
{
{ QP_TEST_CASE_TYPE_SELF_VALIDATE, "SelfValidate" },
{ QP_TEST_CASE_TYPE_PERFORMANCE, "Performance" },
{ QP_TEST_CASE_TYPE_CAPABILITY, "Capability" },
{ QP_TEST_CASE_TYPE_ACCURACY, "Accuracy" },
{ QP_TEST_CASE_TYPE_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTestTypeMap) == QP_TEST_CASE_TYPE_LAST + 1);
static const qpKeyStringMap s_qpTestResultMap[] =
{
{ QP_TEST_RESULT_PASS, "Pass" },
{ QP_TEST_RESULT_FAIL, "Fail" },
{ QP_TEST_RESULT_QUALITY_WARNING, "QualityWarning" },
{ QP_TEST_RESULT_COMPATIBILITY_WARNING, "CompatibilityWarning" },
{ QP_TEST_RESULT_PENDING, "Pending" }, /* should not be needed here */
{ QP_TEST_RESULT_NOT_SUPPORTED, "NotSupported" },
{ QP_TEST_RESULT_RESOURCE_ERROR, "ResourceError" },
{ QP_TEST_RESULT_INTERNAL_ERROR, "InternalError" },
{ QP_TEST_RESULT_CRASH, "Crash" },
{ QP_TEST_RESULT_TIMEOUT, "Timeout" },
/* Add new values here if needed, remember to update qpTestResult enumeration. */
{ QP_TEST_RESULT_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTestResultMap) == QP_TEST_RESULT_LAST + 1);
/* Key tag to string mapping. */
static const qpKeyStringMap s_qpTagMap[] =
{
{ QP_KEY_TAG_NONE, DE_NULL },
{ QP_KEY_TAG_PERFORMANCE, "Performance" },
{ QP_KEY_TAG_QUALITY, "Quality" },
{ QP_KEY_TAG_PRECISION, "Precision" },
{ QP_KEY_TAG_TIME, "Time" },
{ QP_KEY_TAG_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpTagMap) == QP_KEY_TAG_LAST + 1);
/* Sample value tag to string mapping. */
static const qpKeyStringMap s_qpSampleValueTagMap[] =
{
{ QP_SAMPLE_VALUE_TAG_PREDICTOR, "Predictor" },
{ QP_SAMPLE_VALUE_TAG_RESPONSE, "Response" },
{ QP_SAMPLE_VALUE_TAG_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpSampleValueTagMap) == QP_SAMPLE_VALUE_TAG_LAST + 1);
/* Image compression mode to string mapping. */
static const qpKeyStringMap s_qpImageCompressionModeMap[] =
{
{ QP_IMAGE_COMPRESSION_MODE_NONE, "None" },
{ QP_IMAGE_COMPRESSION_MODE_PNG, "PNG" },
{ QP_IMAGE_COMPRESSION_MODE_BEST, DE_NULL }, /* not allowed to be written! */
{ QP_IMAGE_COMPRESSION_MODE_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpImageCompressionModeMap) == QP_IMAGE_COMPRESSION_MODE_LAST + 1);
/* Image format to string mapping. */
static const qpKeyStringMap s_qpImageFormatMap[] =
{
{ QP_IMAGE_FORMAT_RGB888, "RGB888" },
{ QP_IMAGE_FORMAT_RGBA8888, "RGBA8888" },
{ QP_IMAGE_FORMAT_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpImageFormatMap) == QP_IMAGE_FORMAT_LAST + 1);
/* Shader type to string mapping. */
static const qpKeyStringMap s_qpShaderTypeMap[] =
{
{ QP_SHADER_TYPE_VERTEX, "VertexShader" },
{ QP_SHADER_TYPE_FRAGMENT, "FragmentShader" },
{ QP_SHADER_TYPE_GEOMETRY, "GeometryShader" },
{ QP_SHADER_TYPE_TESS_CONTROL, "TessControlShader" },
{ QP_SHADER_TYPE_TESS_EVALUATION, "TessEvaluationShader" },
{ QP_SHADER_TYPE_COMPUTE, "ComputeShader" },
{ QP_SHADER_TYPE_LAST, DE_NULL }
};
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_qpShaderTypeMap) == QP_SHADER_TYPE_LAST + 1);
static void qpTestLog_flushFile (qpTestLog* log)
{
DE_ASSERT(log && log->outputFile);
fflush(log->outputFile);
#if (DE_OS == DE_OS_WIN32) && (DE_COMPILER == DE_COMPILER_MSC)
/* \todo [petri] Is this really necessary? */
FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(log->outputFile)));
#endif
}
#define QP_LOOKUP_STRING(KEYMAP, KEY) qpLookupString(KEYMAP, DE_LENGTH_OF_ARRAY(KEYMAP), (int)(KEY))
static const char* qpLookupString (const qpKeyStringMap* keyMap, int keyMapSize, int key)
{
DE_ASSERT(keyMap);
DE_ASSERT(deInBounds32(key, 0, keyMapSize));
DE_ASSERT(keyMap[key].key == key);
DE_UNREF(keyMapSize); /* for asserting only */
return keyMap[key].string;
}
DE_INLINE void int32ToString (int val, char buf[32])
{
deSprintf(&buf[0], 32, "%d", val);
}
DE_INLINE void int64ToString (deInt64 val, char buf[32])
{
deSprintf(&buf[0], 32, "%lld", (long long int)val);
}
DE_INLINE void floatToString (float value, char* buf, size_t bufSize)
{
deSprintf(buf, bufSize, "%f", value);
}
DE_INLINE void doubleToString (double value, char* buf, size_t bufSize)
{
deSprintf(buf, bufSize, "%f", value);
}
static deBool beginSession (qpTestLog* log)
{
DE_ASSERT(log && !log->isSessionOpen);
/* Write session info. */
fprintf(log->outputFile, "#sessionInfo releaseName %s\n", qpGetReleaseName());
fprintf(log->outputFile, "#sessionInfo releaseId 0x%08x\n", qpGetReleaseId());
fprintf(log->outputFile, "#sessionInfo targetName \"%s\"\n", qpGetTargetName());
/* Write out #beginSession. */
fprintf(log->outputFile, "#beginSession\n");
qpTestLog_flushFile(log);
log->isSessionOpen = DE_TRUE;
return DE_TRUE;
}
static deBool endSession (qpTestLog* log)
{
DE_ASSERT(log && log->isSessionOpen);
/* Make sure xml is flushed. */
qpXmlWriter_flush(log->writer);
/* Write out #endSession. */
fprintf(log->outputFile, "\n#endSession\n");
qpTestLog_flushFile(log);
log->isSessionOpen = DE_FALSE;
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Create a file based logger instance
* \param fileName Name of the file where to put logs
* \return qpTestLog instance, or DE_NULL if cannot create file
*//*--------------------------------------------------------------------*/
qpTestLog* qpTestLog_createFileLog (const char* fileName, deUint32 flags)
{
qpTestLog* log = (qpTestLog*)deCalloc(sizeof(qpTestLog));
if (!log)
return DE_NULL;
DE_ASSERT(fileName && fileName[0]); /* must have filename. */
#if defined(DE_DEBUG)
ContainerStack_reset(&log->containerStack);
#endif
qpPrintf("Writing test log into %s\n", fileName);
/* Create output file. */
log->outputFile = fopen(fileName, "wb");
if (!log->outputFile)
{
qpPrintf("ERROR: Unable to open test log output file '%s'.\n", fileName);
qpTestLog_destroy(log);
return DE_NULL;
}
log->flags = flags;
log->writer = qpXmlWriter_createFileWriter(log->outputFile, 0, !(flags & QP_TEST_LOG_NO_FLUSH));
log->lock = deMutex_create(DE_NULL);
log->isSessionOpen = DE_FALSE;
log->isCaseOpen = DE_FALSE;
if (!log->writer)
{
qpPrintf("ERROR: Unable to create output XML writer to file '%s'.\n", fileName);
qpTestLog_destroy(log);
return DE_NULL;
}
if (!log->lock)
{
qpPrintf("ERROR: Unable to create mutex.\n");
qpTestLog_destroy(log);
return DE_NULL;
}
beginSession(log);
return log;
}
/*--------------------------------------------------------------------*//*!
* \brief Destroy a logger instance
* \param a qpTestLog instance
*//*--------------------------------------------------------------------*/
void qpTestLog_destroy (qpTestLog* log)
{
DE_ASSERT(log);
if (log->isSessionOpen)
endSession(log);
if (log->writer)
qpXmlWriter_destroy(log->writer);
if (log->outputFile)
fclose(log->outputFile);
if (log->lock)
deMutex_destroy(log->lock);
deFree(log);
}
/*--------------------------------------------------------------------*//*!
* \brief Log start of test case
* \param log qpTestLog instance
* \param testCasePath Full test case path (as seen in Candy).
* \param testCaseType Test case type
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_startCase (qpTestLog* log, const char* testCasePath, qpTestCaseType testCaseType)
{
const char* typeStr = QP_LOOKUP_STRING(s_qpTestTypeMap, testCaseType);
int numResultAttribs = 0;
qpXmlAttribute resultAttribs[8];
DE_ASSERT(log && testCasePath && (testCasePath[0] != 0));
deMutex_lock(log->lock);
DE_ASSERT(!log->isCaseOpen);
DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
/* Flush XML and write out #beginTestCaseResult. */
qpXmlWriter_flush(log->writer);
fprintf(log->outputFile, "\n#beginTestCaseResult %s\n", testCasePath);
if (!(log->flags & QP_TEST_LOG_NO_FLUSH))
qpTestLog_flushFile(log);
log->isCaseOpen = DE_TRUE;
/* Fill in attributes. */
resultAttribs[numResultAttribs++] = qpSetStringAttrib("Version", LOG_FORMAT_VERSION);
resultAttribs[numResultAttribs++] = qpSetStringAttrib("CasePath", testCasePath);
resultAttribs[numResultAttribs++] = qpSetStringAttrib("CaseType", typeStr);
if (!qpXmlWriter_startDocument(log->writer) ||
!qpXmlWriter_startElement(log->writer, "TestCaseResult", numResultAttribs, resultAttribs))
{
qpPrintf("qpTestLog_startCase(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Log end of test case
* \param log qpTestLog instance
* \param result Test result
* \param description Description of a problem in case of error
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_endCase (qpTestLog* log, qpTestResult result, const char* resultDetails)
{
const char* statusStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
qpXmlAttribute statusAttrib = qpSetStringAttrib("StatusCode", statusStr);
deMutex_lock(log->lock);
DE_ASSERT(log->isCaseOpen);
DE_ASSERT(ContainerStack_isEmpty(&log->containerStack));
/* <Result StatusCode="Pass">Result details</Result>
* </TestCaseResult>
*/
if (!qpXmlWriter_startElement(log->writer, "Result", 1, &statusAttrib) ||
(resultDetails && !qpXmlWriter_writeString(log->writer, resultDetails)) ||
!qpXmlWriter_endElement(log->writer, "Result") ||
!qpXmlWriter_endElement(log->writer, "TestCaseResult") ||
!qpXmlWriter_endDocument(log->writer)) /* Close any XML elements still open */
{
qpPrintf("qpTestLog_endCase(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
/* Flush XML and write #endTestCaseResult. */
qpXmlWriter_flush(log->writer);
fprintf(log->outputFile, "\n#endTestCaseResult\n");
if (!(log->flags & QP_TEST_LOG_NO_FLUSH))
qpTestLog_flushFile(log);
log->isCaseOpen = DE_FALSE;
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Abrupt termination of logging.
* \param log qpTestLog instance
* \param result Result code, only Crash and Timeout are allowed.
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_terminateCase (qpTestLog* log, qpTestResult result)
{
const char* resultStr = QP_LOOKUP_STRING(s_qpTestResultMap, result);
DE_ASSERT(log);
DE_ASSERT(result == QP_TEST_RESULT_CRASH || result == QP_TEST_RESULT_TIMEOUT);
deMutex_lock(log->lock);
if (!log->isCaseOpen)
{
deMutex_unlock(log->lock);
return DE_FALSE; /* Soft error. This is called from error handler. */
}
/* Flush XML and write #terminateTestCaseResult. */
qpXmlWriter_flush(log->writer);
fprintf(log->outputFile, "\n#terminateTestCaseResult %s\n", resultStr);
qpTestLog_flushFile(log);
log->isCaseOpen = DE_FALSE;
#if defined(DE_DEBUG)
ContainerStack_reset(&log->containerStack);
#endif
deMutex_unlock(log->lock);
return DE_TRUE;
}
static deBool qpTestLog_writeKeyValuePair (qpTestLog* log, const char* elementName, const char* name, const char* description, const char* unit, qpKeyValueTag tag, const char* text)
{
const char* tagString = QP_LOOKUP_STRING(s_qpTagMap, tag);
qpXmlAttribute attribs[8];
int numAttribs = 0;
DE_ASSERT(log && elementName && text);
deMutex_lock(log->lock);
/* Fill in attributes. */
if (name) attribs[numAttribs++] = qpSetStringAttrib("Name", name);
if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
if (tagString) attribs[numAttribs++] = qpSetStringAttrib("Tag", tagString);
if (unit) attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
if (!qpXmlWriter_startElement(log->writer, elementName, numAttribs, attribs) ||
!qpXmlWriter_writeString(log->writer, text) ||
!qpXmlWriter_endElement(log->writer, elementName))
{
qpPrintf("qpTestLog_writeKeyValuePair(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write a message to output log
* \param log qpTestLog instance
* \param format Format string of message
* \param ... Parameters for message
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeMessage (qpTestLog* log, const char* format, ...)
{
char buffer[1024];
va_list args;
/* \todo [petri] Handle buffer overflows! */
va_start(args, format);
buffer[DE_LENGTH_OF_ARRAY(buffer) - 1] = 0;
vsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
/* <Text>text</Text> */
return qpTestLog_writeKeyValuePair(log, "Text", DE_NULL, DE_NULL, DE_NULL, QP_KEY_TAG_LAST, buffer);
}
/*--------------------------------------------------------------------*//*!
* \brief Write key-value-pair into log
* \param log qpTestLog instance
* \param name Unique identifier for entry
* \param description Human readable description
* \param tag Optional tag
* \param value Value of the key-value-pair
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeText (qpTestLog* log, const char* name, const char* description, qpKeyValueTag tag, const char* text)
{
/* <Text Name="name" Description="description" Tag="tag">text</Text> */
return qpTestLog_writeKeyValuePair(log, "Text", name, description, DE_NULL, tag, text);
}
/*--------------------------------------------------------------------*//*!
* \brief Write key-value-pair into log
* \param log qpTestLog instance
* \param name Unique identifier for entry
* \param description Human readable description
* \param tag Optional tag
* \param value Value of the key-value-pair
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeInteger (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, deInt64 value)
{
char tmpString[64];
int64ToString(value, tmpString);
/* <Number Name="name" Description="description" Tag="Performance">15</Number> */
return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
}
/*--------------------------------------------------------------------*//*!
* \brief Write key-value-pair into log
* \param log qpTestLog instance
* \param name Unique identifier for entry
* \param description Human readable description
* \param tag Optional tag
* \param value Value of the key-value-pair
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeFloat (qpTestLog* log, const char* name, const char* description, const char* unit, qpKeyValueTag tag, float value)
{
char tmpString[64];
floatToString(value, tmpString, sizeof(tmpString));
/* <Number Name="name" Description="description" Tag="Performance">15</Number> */
return qpTestLog_writeKeyValuePair(log, "Number", name, description, unit, tag, tmpString);
}
typedef struct Buffer_s
{
size_t capacity;
size_t size;
deUint8* data;
} Buffer;
void Buffer_init (Buffer* buffer)
{
buffer->capacity = 0;
buffer->size = 0;
buffer->data = DE_NULL;
}
void Buffer_deinit (Buffer* buffer)
{
deFree(buffer->data);
Buffer_init(buffer);
}
deBool Buffer_resize (Buffer* buffer, size_t newSize)
{
/* Grow buffer if necessary. */
if (newSize > buffer->capacity)
{
size_t newCapacity = (size_t)deAlign32(deMax32(2*(int)buffer->capacity, (int)newSize), 512);
deUint8* newData = (deUint8*)deMalloc(newCapacity);
if (!newData)
return DE_FALSE;
memcpy(newData, buffer->data, buffer->size);
deFree(buffer->data);
buffer->data = newData;
buffer->capacity = newCapacity;
}
buffer->size = newSize;
return DE_TRUE;
}
deBool Buffer_append (Buffer* buffer, const deUint8* data, size_t numBytes)
{
size_t offset = buffer->size;
if (!Buffer_resize(buffer, buffer->size + numBytes))
return DE_FALSE;
/* Append bytes. */
memcpy(&buffer->data[offset], data, numBytes);
return DE_TRUE;
}
#if defined(QP_SUPPORT_PNG)
void pngWriteData (png_structp png, png_bytep dataPtr, png_size_t numBytes)
{
Buffer* buffer = (Buffer*)png_get_io_ptr(png);
if (!Buffer_append(buffer, (const deUint8*)dataPtr, numBytes))
png_error(png, "unable to resize PNG write buffer!");
}
void pngFlushData (png_structp png)
{
DE_UNREF(png);
/* nada */
}
static deBool writeCompressedPNG (png_structp png, png_infop info, png_byte** rowPointers, int width, int height, int colorFormat)
{
if (setjmp(png_jmpbuf(png)) == 0)
{
/* Write data. */
png_set_IHDR(png, info, (png_uint_32)width, (png_uint_32)height,
8,
colorFormat,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
png_write_info(png, info);
png_write_image(png, rowPointers);
png_write_end(png, NULL);
return DE_TRUE;
}
else
return DE_FALSE;
}
static deBool compressImagePNG (Buffer* buffer, qpImageFormat imageFormat, int width, int height, int rowStride, const void* data)
{
deBool compressOk = DE_FALSE;
png_structp png = DE_NULL;
png_infop info = DE_NULL;
png_byte** rowPointers = DE_NULL;
deBool hasAlpha = imageFormat == QP_IMAGE_FORMAT_RGBA8888;
int ndx;
/* Handle format. */
DE_ASSERT(imageFormat == QP_IMAGE_FORMAT_RGB888 || imageFormat == QP_IMAGE_FORMAT_RGBA8888);
/* Allocate & set row pointers. */
rowPointers = (png_byte**)deMalloc((size_t)height * sizeof(png_byte*));
if (!rowPointers)
return DE_FALSE;
for (ndx = 0; ndx < height; ndx++)
rowPointers[ndx] = (png_byte*)((const deUint8*)data + ndx*rowStride);
/* Initialize PNG compressor. */
png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info = png ? png_create_info_struct(png) : DE_NULL;
if (png && info)
{
/* Set our own write function. */
png_set_write_fn(png, buffer, pngWriteData, pngFlushData);
compressOk = writeCompressedPNG(png, info, rowPointers, width, height,
hasAlpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB);
}
/* Cleanup & return. */
if (png && info)
{
png_destroy_info_struct(png, &info);
png_destroy_write_struct(&png, DE_NULL);
}
else if (png)
png_destroy_write_struct(&png, &info);
deFree(rowPointers);
return compressOk;
}
#endif /* QP_SUPPORT_PNG */
/*--------------------------------------------------------------------*//*!
* \brief Start image set
* \param log qpTestLog instance
* \param name Unique identifier for the set
* \param description Human readable description
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_startImageSet (qpTestLog* log, const char* name, const char* description)
{
qpXmlAttribute attribs[4];
int numAttribs = 0;
DE_ASSERT(log && name);
deMutex_lock(log->lock);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
if (description)
attribs[numAttribs++] = qpSetStringAttrib("Description", description);
/* <ImageSet Name="<name>"> */
if (!qpXmlWriter_startElement(log->writer, "ImageSet", numAttribs, attribs))
{
qpPrintf("qpTestLog_startImageSet(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_IMAGESET));
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief End image set
* \param log qpTestLog instance
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_endImageSet (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
/* <ImageSet Name="<name>"> */
if (!qpXmlWriter_endElement(log->writer, "ImageSet"))
{
qpPrintf("qpTestLog_endImageSet(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_IMAGESET);
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write base64 encoded raw image data into log
* \param log qpTestLog instance
* \param name Unique name (matching names can be compared across BatchResults).
* \param description Textual description (shown in Candy).
* \param compressionMode Compression mode
* \param imageFormat Color format
* \param width Width in pixels
* \param height Height in pixels
* \param stride Data stride (offset between rows)
* \param data Pointer to pixel data
* \return 0 if OK, otherwise <0
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeImage (
qpTestLog* log,
const char* name,
const char* description,
qpImageCompressionMode compressionMode,
qpImageFormat imageFormat,
int width,
int height,
int stride,
const void* data)
{
char widthStr[32];
char heightStr[32];
qpXmlAttribute attribs[8];
int numAttribs = 0;
Buffer compressedBuffer;
const void* writeDataPtr = DE_NULL;
size_t writeDataBytes = ~(size_t)0;
DE_ASSERT(log && name);
DE_ASSERT(deInRange32(width, 1, 16384));
DE_ASSERT(deInRange32(height, 1, 16384));
DE_ASSERT(data);
if (log->flags & QP_TEST_LOG_EXCLUDE_IMAGES)
return DE_TRUE; /* Image not logged. */
Buffer_init(&compressedBuffer);
/* BEST compression mode defaults to PNG. */
if (compressionMode == QP_IMAGE_COMPRESSION_MODE_BEST)
{
#if defined(QP_SUPPORT_PNG)
compressionMode = QP_IMAGE_COMPRESSION_MODE_PNG;
#else
compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
#endif
}
#if defined(QP_SUPPORT_PNG)
/* Try storing with PNG compression. */
if (compressionMode == QP_IMAGE_COMPRESSION_MODE_PNG)
{
deBool compressOk = compressImagePNG(&compressedBuffer, imageFormat, width, height, stride, data);
if (compressOk)
{
writeDataPtr = compressedBuffer.data;
writeDataBytes = compressedBuffer.size;
}
else
{
/* Fall-back to default compression. */
qpPrintf("WARNING: PNG compression failed -- storing image uncompressed.\n");
compressionMode = QP_IMAGE_COMPRESSION_MODE_NONE;
}
}
#endif
/* Handle image compression. */
switch (compressionMode)
{
case QP_IMAGE_COMPRESSION_MODE_NONE:
{
int pixelSize = imageFormat == QP_IMAGE_FORMAT_RGB888 ? 3 : 4;
int packedStride = pixelSize*width;
if (packedStride == stride)
writeDataPtr = data;
else
{
/* Need to re-pack pixels. */
if (Buffer_resize(&compressedBuffer, (size_t)(packedStride*height)))
{
int row;
for (row = 0; row < height; row++)
memcpy(&compressedBuffer.data[packedStride*row], &((const deUint8*)data)[row*stride], (size_t)(pixelSize*width));
}
else
{
qpPrintf("ERROR: Failed to pack pixels for writing.\n");
Buffer_deinit(&compressedBuffer);
return DE_FALSE;
}
}
writeDataBytes = (size_t)(packedStride*height);
break;
}
#if defined(QP_SUPPORT_PNG)
case QP_IMAGE_COMPRESSION_MODE_PNG:
DE_ASSERT(writeDataPtr); /* Already handled. */
break;
#endif
default:
qpPrintf("qpTestLog_writeImage(): Unknown compression mode: %s\n", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
Buffer_deinit(&compressedBuffer);
return DE_FALSE;
}
/* Fill in attributes. */
int32ToString(width, widthStr);
int32ToString(height, heightStr);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
attribs[numAttribs++] = qpSetStringAttrib("Width", widthStr);
attribs[numAttribs++] = qpSetStringAttrib("Height", heightStr);
attribs[numAttribs++] = qpSetStringAttrib("Format", QP_LOOKUP_STRING(s_qpImageFormatMap, imageFormat));
attribs[numAttribs++] = qpSetStringAttrib("CompressionMode", QP_LOOKUP_STRING(s_qpImageCompressionModeMap, compressionMode));
if (description) attribs[numAttribs++] = qpSetStringAttrib("Description", description);
/* \note Log lock is acquired after compression! */
deMutex_lock(log->lock);
/* <Image ID="result" Name="Foobar" Width="640" Height="480" Format="RGB888" CompressionMode="None">base64 data</Image> */
if (!qpXmlWriter_startElement(log->writer, "Image", numAttribs, attribs) ||
!qpXmlWriter_writeBase64(log->writer, (const deUint8*)writeDataPtr, writeDataBytes) ||
!qpXmlWriter_endElement(log->writer, "Image"))
{
qpPrintf("qpTestLog_writeImage(): Writing XML failed\n");
deMutex_unlock(log->lock);
Buffer_deinit(&compressedBuffer);
return DE_FALSE;
}
deMutex_unlock(log->lock);
/* Free compressed data if allocated. */
Buffer_deinit(&compressedBuffer);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write a OpenGL ES shader program into the log.
* \param linkOk Shader program link result, false on failure
* \param linkInfoLog Implementation provided linkage log
*//*--------------------------------------------------------------------*/
deBool qpTestLog_startShaderProgram (qpTestLog* log, deBool linkOk, const char* linkInfoLog)
{
qpXmlAttribute programAttribs[4];
int numProgramAttribs = 0;
DE_ASSERT(log);
deMutex_lock(log->lock);
programAttribs[numProgramAttribs++] = qpSetStringAttrib("LinkStatus", linkOk ? "OK" : "Fail");
if (!qpXmlWriter_startElement(log->writer, "ShaderProgram", numProgramAttribs, programAttribs) ||
!qpXmlWriter_writeStringElement(log->writer, "InfoLog", linkInfoLog))
{
qpPrintf("qpTestLog_startShaderProgram(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SHADERPROGRAM));
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief End shader program
* \param log qpTestLog instance
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_endShaderProgram (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
/* </ShaderProgram> */
if (!qpXmlWriter_endElement(log->writer, "ShaderProgram"))
{
qpPrintf("qpTestLog_endShaderProgram(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write a OpenGL ES shader into the log.
* \param type Shader type
* \param source Shader source
* \param compileOk Shader compilation result, false on failure
* \param infoLog Implementation provided shader compilation log
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeShader (qpTestLog* log, qpShaderType type, const char* source, deBool compileOk, const char* infoLog)
{
const char* tagName = QP_LOOKUP_STRING(s_qpShaderTypeMap, type);
const char* sourceStr = ((log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) == 0 || !compileOk) ? source : "";
int numShaderAttribs = 0;
qpXmlAttribute shaderAttribs[4];
deMutex_lock(log->lock);
DE_ASSERT(source);
DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
shaderAttribs[numShaderAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
if (!qpXmlWriter_startElement(log->writer, tagName, numShaderAttribs, shaderAttribs) ||
!qpXmlWriter_writeStringElement(log->writer, "ShaderSource", sourceStr) ||
!qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
!qpXmlWriter_endElement(log->writer, tagName))
{
qpPrintf("qpTestLog_writeShader(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Start writing a set of EGL configurations into the log.
*//*--------------------------------------------------------------------*/
deBool qpTestLog_startEglConfigSet (qpTestLog* log, const char* name, const char* description)
{
qpXmlAttribute attribs[4];
int numAttribs = 0;
DE_ASSERT(log && name);
deMutex_lock(log->lock);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
if (description)
attribs[numAttribs++] = qpSetStringAttrib("Description", description);
/* <EglConfigSet Name="<name>"> */
if (!qpXmlWriter_startElement(log->writer, "EglConfigSet", numAttribs, attribs))
{
qpPrintf("qpTestLog_startEglImageSet(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_EGLCONFIGSET));
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief End an EGL config set
*//*--------------------------------------------------------------------*/
deBool qpTestLog_endEglConfigSet (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
/* <EglConfigSet Name="<name>"> */
if (!qpXmlWriter_endElement(log->writer, "EglConfigSet"))
{
qpPrintf("qpTestLog_endEglImageSet(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_EGLCONFIGSET);
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write an EGL config inside an EGL config set
* \see qpElgConfigInfo for details
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeEglConfig (qpTestLog* log, const qpEglConfigInfo* config)
{
qpXmlAttribute attribs[64];
int numAttribs = 0;
DE_ASSERT(log && config);
deMutex_lock(log->lock);
attribs[numAttribs++] = qpSetIntAttrib ("BufferSize", config->bufferSize);
attribs[numAttribs++] = qpSetIntAttrib ("RedSize", config->redSize);
attribs[numAttribs++] = qpSetIntAttrib ("GreenSize", config->greenSize);
attribs[numAttribs++] = qpSetIntAttrib ("BlueSize", config->blueSize);
attribs[numAttribs++] = qpSetIntAttrib ("LuminanceSize", config->luminanceSize);
attribs[numAttribs++] = qpSetIntAttrib ("AlphaSize", config->alphaSize);
attribs[numAttribs++] = qpSetIntAttrib ("AlphaMaskSize", config->alphaMaskSize);
attribs[numAttribs++] = qpSetBoolAttrib ("BindToTextureRGB", config->bindToTextureRGB);
attribs[numAttribs++] = qpSetBoolAttrib ("BindToTextureRGBA", config->bindToTextureRGBA);
attribs[numAttribs++] = qpSetStringAttrib ("ColorBufferType", config->colorBufferType);
attribs[numAttribs++] = qpSetStringAttrib ("ConfigCaveat", config->configCaveat);
attribs[numAttribs++] = qpSetIntAttrib ("ConfigID", config->configID);
attribs[numAttribs++] = qpSetStringAttrib ("Conformant", config->conformant);
attribs[numAttribs++] = qpSetIntAttrib ("DepthSize", config->depthSize);
attribs[numAttribs++] = qpSetIntAttrib ("Level", config->level);
attribs[numAttribs++] = qpSetIntAttrib ("MaxPBufferWidth", config->maxPBufferWidth);
attribs[numAttribs++] = qpSetIntAttrib ("MaxPBufferHeight", config->maxPBufferHeight);
attribs[numAttribs++] = qpSetIntAttrib ("MaxPBufferPixels", config->maxPBufferPixels);
attribs[numAttribs++] = qpSetIntAttrib ("MaxSwapInterval", config->maxSwapInterval);
attribs[numAttribs++] = qpSetIntAttrib ("MinSwapInterval", config->minSwapInterval);
attribs[numAttribs++] = qpSetBoolAttrib ("NativeRenderable", config->nativeRenderable);
attribs[numAttribs++] = qpSetStringAttrib ("RenderableType", config->renderableType);
attribs[numAttribs++] = qpSetIntAttrib ("SampleBuffers", config->sampleBuffers);
attribs[numAttribs++] = qpSetIntAttrib ("Samples", config->samples);
attribs[numAttribs++] = qpSetIntAttrib ("StencilSize", config->stencilSize);
attribs[numAttribs++] = qpSetStringAttrib ("SurfaceTypes", config->surfaceTypes);
attribs[numAttribs++] = qpSetStringAttrib ("TransparentType", config->transparentType);
attribs[numAttribs++] = qpSetIntAttrib ("TransparentRedValue", config->transparentRedValue);
attribs[numAttribs++] = qpSetIntAttrib ("TransparentGreenValue", config->transparentGreenValue);
attribs[numAttribs++] = qpSetIntAttrib ("TransparentBlueValue", config->transparentBlueValue);
DE_ASSERT(numAttribs <= DE_LENGTH_OF_ARRAY(attribs));
if (!qpXmlWriter_startElement(log->writer, "EglConfig", numAttribs, attribs) ||
!qpXmlWriter_endElement(log->writer, "EglConfig"))
{
qpPrintf("qpTestLog_writeEglConfig(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Start section in log.
* \param log qpTestLog instance
* \param name Section name
* \param description Human readable description
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_startSection (qpTestLog* log, const char* name, const char* description)
{
qpXmlAttribute attribs[2];
int numAttribs = 0;
DE_ASSERT(log && name);
deMutex_lock(log->lock);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
if (description)
attribs[numAttribs++] = qpSetStringAttrib("Description", description);
/* <Section Name="<name>" Description="<description>"> */
if (!qpXmlWriter_startElement(log->writer, "Section", numAttribs, attribs))
{
qpPrintf("qpTestLog_startSection(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SECTION));
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief End section in log.
* \param log qpTestLog instance
* \return true if ok, false otherwise
*//*--------------------------------------------------------------------*/
deBool qpTestLog_endSection (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
/* </Section> */
if (!qpXmlWriter_endElement(log->writer, "Section"))
{
qpPrintf("qpTestLog_endSection(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SECTION);
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write OpenCL compute kernel source into the log.
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeKernelSource (qpTestLog* log, const char* source)
{
const char* sourceStr = (log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) != 0 ? "" : source;
DE_ASSERT(log);
deMutex_lock(log->lock);
if (!qpXmlWriter_writeStringElement(log->writer, "KernelSource", sourceStr))
{
qpPrintf("qpTestLog_writeKernelSource(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write a SPIR-V module assembly source into the log.
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeSpirVAssemblySource (qpTestLog* log, const char* source)
{
const char* const sourceStr = (log->flags & QP_TEST_LOG_EXCLUDE_SHADER_SOURCES) != 0 ? "" : source;
deMutex_lock(log->lock);
DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SHADERPROGRAM);
if (!qpXmlWriter_writeStringElement(log->writer, "SpirVAssemblySource", sourceStr))
{
qpPrintf("qpTestLog_writeSpirVAssemblySource(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
/*--------------------------------------------------------------------*//*!
* \brief Write OpenCL kernel compilation results into the log
*//*--------------------------------------------------------------------*/
deBool qpTestLog_writeCompileInfo (qpTestLog* log, const char* name, const char* description, deBool compileOk, const char* infoLog)
{
int numAttribs = 0;
qpXmlAttribute attribs[3];
DE_ASSERT(log && name && description && infoLog);
deMutex_lock(log->lock);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
attribs[numAttribs++] = qpSetStringAttrib("Description", description);
attribs[numAttribs++] = qpSetStringAttrib("CompileStatus", compileOk ? "OK" : "Fail");
if (!qpXmlWriter_startElement(log->writer, "CompileInfo", numAttribs, attribs) ||
!qpXmlWriter_writeStringElement(log->writer, "InfoLog", infoLog) ||
!qpXmlWriter_endElement(log->writer, "CompileInfo"))
{
qpPrintf("qpTestLog_writeCompileInfo(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_startSampleList (qpTestLog* log, const char* name, const char* description)
{
int numAttribs = 0;
qpXmlAttribute attribs[2];
DE_ASSERT(log && name && description);
deMutex_lock(log->lock);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
attribs[numAttribs++] = qpSetStringAttrib("Description", description);
if (!qpXmlWriter_startElement(log->writer, "SampleList", numAttribs, attribs))
{
qpPrintf("qpTestLog_startSampleList(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLELIST));
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_startSampleInfo (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
if (!qpXmlWriter_startElement(log->writer, "SampleInfo", 0, DE_NULL))
{
qpPrintf("qpTestLog_startSampleInfo(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLEINFO));
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_writeValueInfo (qpTestLog* log, const char* name, const char* description, const char* unit, qpSampleValueTag tag)
{
const char* tagName = QP_LOOKUP_STRING(s_qpSampleValueTagMap, tag);
int numAttribs = 0;
qpXmlAttribute attribs[4];
DE_ASSERT(log && name && description && tagName);
deMutex_lock(log->lock);
DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
attribs[numAttribs++] = qpSetStringAttrib("Name", name);
attribs[numAttribs++] = qpSetStringAttrib("Description", description);
attribs[numAttribs++] = qpSetStringAttrib("Tag", tagName);
if (unit)
attribs[numAttribs++] = qpSetStringAttrib("Unit", unit);
if (!qpXmlWriter_startElement(log->writer, "ValueInfo", numAttribs, attribs) ||
!qpXmlWriter_endElement(log->writer, "ValueInfo"))
{
qpPrintf("qpTestLog_writeValueInfo(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_endSampleInfo (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
if (!qpXmlWriter_endElement(log->writer, "SampleInfo"))
{
qpPrintf("qpTestLog_endSampleInfo(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLEINFO);
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_startSample (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
if (!qpXmlWriter_startElement(log->writer, "Sample", 0, DE_NULL))
{
qpPrintf("qpTestLog_startSample(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_push(&log->containerStack, CONTAINERTYPE_SAMPLE));
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_writeValueFloat (qpTestLog* log, double value)
{
char tmpString[512];
doubleToString(value, tmpString, (int)sizeof(tmpString));
deMutex_lock(log->lock);
DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
{
qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_writeValueInteger (qpTestLog* log, deInt64 value)
{
char tmpString[64];
int64ToString(value, tmpString);
deMutex_lock(log->lock);
DE_ASSERT(ContainerStack_getTop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
if (!qpXmlWriter_writeStringElement(log->writer, "Value", &tmpString[0]))
{
qpPrintf("qpTestLog_writeSampleValue(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_endSample (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
if (!qpXmlWriter_endElement(log->writer, "Sample"))
{
qpPrintf("qpTestLog_endSample(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLE);
deMutex_unlock(log->lock);
return DE_TRUE;
}
deBool qpTestLog_endSampleList (qpTestLog* log)
{
DE_ASSERT(log);
deMutex_lock(log->lock);
if (!qpXmlWriter_endElement(log->writer, "SampleList"))
{
qpPrintf("qpTestLog_endSampleList(): Writing XML failed\n");
deMutex_unlock(log->lock);
return DE_FALSE;
}
DE_ASSERT(ContainerStack_pop(&log->containerStack) == CONTAINERTYPE_SAMPLELIST);
deMutex_unlock(log->lock);
return DE_TRUE;
}
deUint32 qpTestLog_getLogFlags (const qpTestLog* log)
{
DE_ASSERT(log);
return log->flags;
}
const char* qpGetTestResultName (qpTestResult result)
{
return QP_LOOKUP_STRING(s_qpTestResultMap, result);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment