| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL (ES) 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 Draw tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "glsDrawTest.hpp" |
| |
| #include "deRandom.h" |
| #include "deRandom.hpp" |
| #include "deMath.h" |
| #include "deStringUtil.hpp" |
| #include "deFloat16.h" |
| #include "deUniquePtr.hpp" |
| #include "deArrayUtil.hpp" |
| |
| #include "tcuTestLog.hpp" |
| #include "tcuPixelFormat.hpp" |
| #include "tcuRGBA.hpp" |
| #include "tcuSurface.hpp" |
| #include "tcuVector.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuFloat.hpp" |
| #include "tcuTextureUtil.hpp" |
| |
| #include "gluContextInfo.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluCallLogWrapper.hpp" |
| |
| #include "sglrContext.hpp" |
| #include "sglrReferenceContext.hpp" |
| #include "sglrGLContext.hpp" |
| |
| #include "rrGenericVector.hpp" |
| |
| #include <cstring> |
| #include <cmath> |
| #include <vector> |
| #include <sstream> |
| #include <limits> |
| |
| #include "glwDefs.hpp" |
| #include "glwEnums.hpp" |
| |
| namespace deqp |
| { |
| namespace gls |
| { |
| namespace |
| { |
| |
| using tcu::TestLog; |
| using namespace glw; // GL types |
| |
| const int MAX_RENDER_TARGET_SIZE = 512; |
| |
| // Utils |
| |
| static GLenum targetToGL(DrawTestSpec::Target target) |
| { |
| static const GLenum targets[] = { |
| GL_ELEMENT_ARRAY_BUFFER, // TARGET_ELEMENT_ARRAY = 0, |
| GL_ARRAY_BUFFER // TARGET_ARRAY, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::TARGET_LAST>(targets, (int)target); |
| } |
| |
| static GLenum usageToGL(DrawTestSpec::Usage usage) |
| { |
| static const GLenum usages[] = { |
| GL_DYNAMIC_DRAW, // USAGE_DYNAMIC_DRAW = 0, |
| GL_STATIC_DRAW, // USAGE_STATIC_DRAW, |
| GL_STREAM_DRAW, // USAGE_STREAM_DRAW, |
| |
| GL_STREAM_READ, // USAGE_STREAM_READ, |
| GL_STREAM_COPY, // USAGE_STREAM_COPY, |
| |
| GL_STATIC_READ, // USAGE_STATIC_READ, |
| GL_STATIC_COPY, // USAGE_STATIC_COPY, |
| |
| GL_DYNAMIC_READ, // USAGE_DYNAMIC_READ, |
| GL_DYNAMIC_COPY // USAGE_DYNAMIC_COPY, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::USAGE_LAST>(usages, (int)usage); |
| } |
| |
| static GLenum inputTypeToGL(DrawTestSpec::InputType type) |
| { |
| static const GLenum types[] = { |
| GL_FLOAT, // INPUTTYPE_FLOAT = 0, |
| GL_FIXED, // INPUTTYPE_FIXED, |
| GL_DOUBLE, // INPUTTYPE_DOUBLE |
| GL_BYTE, // INPUTTYPE_BYTE, |
| GL_SHORT, // INPUTTYPE_SHORT, |
| GL_UNSIGNED_BYTE, // INPUTTYPE_UNSIGNED_BYTE, |
| GL_UNSIGNED_SHORT, // INPUTTYPE_UNSIGNED_SHORT, |
| |
| GL_INT, // INPUTTYPE_INT, |
| GL_UNSIGNED_INT, // INPUTTYPE_UNSIGNED_INT, |
| GL_HALF_FLOAT, // INPUTTYPE_HALF, |
| GL_UNSIGNED_INT_2_10_10_10_REV, // INPUTTYPE_UNSIGNED_INT_2_10_10_10, |
| GL_INT_2_10_10_10_REV // INPUTTYPE_INT_2_10_10_10, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::INPUTTYPE_LAST>(types, (int)type); |
| } |
| |
| static std::string outputTypeToGLType(DrawTestSpec::OutputType type) |
| { |
| static const char *types[] = { |
| "float", // OUTPUTTYPE_FLOAT = 0, |
| "vec2", // OUTPUTTYPE_VEC2, |
| "vec3", // OUTPUTTYPE_VEC3, |
| "vec4", // OUTPUTTYPE_VEC4, |
| |
| "int", // OUTPUTTYPE_INT, |
| "uint", // OUTPUTTYPE_UINT, |
| |
| "ivec2", // OUTPUTTYPE_IVEC2, |
| "ivec3", // OUTPUTTYPE_IVEC3, |
| "ivec4", // OUTPUTTYPE_IVEC4, |
| |
| "uvec2", // OUTPUTTYPE_UVEC2, |
| "uvec3", // OUTPUTTYPE_UVEC3, |
| "uvec4", // OUTPUTTYPE_UVEC4, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::OUTPUTTYPE_LAST>(types, (int)type); |
| } |
| |
| static GLenum primitiveToGL(DrawTestSpec::Primitive primitive) |
| { |
| static const GLenum primitives[] = { |
| GL_POINTS, // PRIMITIVE_POINTS = 0, |
| GL_TRIANGLES, // PRIMITIVE_TRIANGLES, |
| GL_TRIANGLE_FAN, // PRIMITIVE_TRIANGLE_FAN, |
| GL_TRIANGLE_STRIP, // PRIMITIVE_TRIANGLE_STRIP, |
| GL_LINES, // PRIMITIVE_LINES |
| GL_LINE_STRIP, // PRIMITIVE_LINE_STRIP |
| GL_LINE_LOOP, // PRIMITIVE_LINE_LOOP |
| GL_LINES_ADJACENCY, // PRIMITIVE_LINES_ADJACENCY |
| GL_LINE_STRIP_ADJACENCY, // PRIMITIVE_LINE_STRIP_ADJACENCY |
| GL_TRIANGLES_ADJACENCY, // PRIMITIVE_TRIANGLES_ADJACENCY |
| GL_TRIANGLE_STRIP_ADJACENCY, // PRIMITIVE_TRIANGLE_STRIP_ADJACENCY |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::PRIMITIVE_LAST>(primitives, (int)primitive); |
| } |
| |
| static uint32_t indexTypeToGL(DrawTestSpec::IndexType indexType) |
| { |
| static const GLenum indexTypes[] = { |
| GL_UNSIGNED_BYTE, // INDEXTYPE_BYTE = 0, |
| GL_UNSIGNED_SHORT, // INDEXTYPE_SHORT, |
| GL_UNSIGNED_INT, // INDEXTYPE_INT, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::INDEXTYPE_LAST>(indexTypes, (int)indexType); |
| } |
| |
| static bool inputTypeIsFloatType(DrawTestSpec::InputType type) |
| { |
| if (type == DrawTestSpec::INPUTTYPE_FLOAT) |
| return true; |
| if (type == DrawTestSpec::INPUTTYPE_FIXED) |
| return true; |
| if (type == DrawTestSpec::INPUTTYPE_HALF) |
| return true; |
| if (type == DrawTestSpec::INPUTTYPE_DOUBLE) |
| return true; |
| return false; |
| } |
| |
| static bool outputTypeIsFloatType(DrawTestSpec::OutputType type) |
| { |
| if (type == DrawTestSpec::OUTPUTTYPE_FLOAT || type == DrawTestSpec::OUTPUTTYPE_VEC2 || |
| type == DrawTestSpec::OUTPUTTYPE_VEC3 || type == DrawTestSpec::OUTPUTTYPE_VEC4) |
| return true; |
| |
| return false; |
| } |
| |
| static bool outputTypeIsIntType(DrawTestSpec::OutputType type) |
| { |
| if (type == DrawTestSpec::OUTPUTTYPE_INT || type == DrawTestSpec::OUTPUTTYPE_IVEC2 || |
| type == DrawTestSpec::OUTPUTTYPE_IVEC3 || type == DrawTestSpec::OUTPUTTYPE_IVEC4) |
| return true; |
| |
| return false; |
| } |
| |
| static bool outputTypeIsUintType(DrawTestSpec::OutputType type) |
| { |
| if (type == DrawTestSpec::OUTPUTTYPE_UINT || type == DrawTestSpec::OUTPUTTYPE_UVEC2 || |
| type == DrawTestSpec::OUTPUTTYPE_UVEC3 || type == DrawTestSpec::OUTPUTTYPE_UVEC4) |
| return true; |
| |
| return false; |
| } |
| |
| static size_t getElementCount(DrawTestSpec::Primitive primitive, size_t primitiveCount) |
| { |
| switch (primitive) |
| { |
| case DrawTestSpec::PRIMITIVE_POINTS: |
| return primitiveCount; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES: |
| return primitiveCount * 3; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_FAN: |
| return primitiveCount + 2; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP: |
| return primitiveCount + 2; |
| case DrawTestSpec::PRIMITIVE_LINES: |
| return primitiveCount * 2; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP: |
| return primitiveCount + 1; |
| case DrawTestSpec::PRIMITIVE_LINE_LOOP: |
| return (primitiveCount == 1) ? (2) : (primitiveCount); |
| case DrawTestSpec::PRIMITIVE_LINES_ADJACENCY: |
| return primitiveCount * 4; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP_ADJACENCY: |
| return primitiveCount + 3; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES_ADJACENCY: |
| return primitiveCount * 6; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP_ADJACENCY: |
| return primitiveCount * 2 + 4; |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| struct MethodInfo |
| { |
| bool indexed; |
| bool instanced; |
| bool ranged; |
| bool first; |
| bool baseVertex; |
| bool indirect; |
| }; |
| |
| static MethodInfo getMethodInfo(gls::DrawTestSpec::DrawMethod method) |
| { |
| static const MethodInfo infos[] = { |
| // indexed instanced ranged first baseVertex indirect |
| {false, false, false, true, false, false}, //!< DRAWMETHOD_DRAWARRAYS, |
| {false, true, false, true, false, false}, //!< DRAWMETHOD_DRAWARRAYS_INSTANCED, |
| {false, true, false, true, false, true}, //!< DRAWMETHOD_DRAWARRAYS_INDIRECT, |
| {true, false, false, false, false, false}, //!< DRAWMETHOD_DRAWELEMENTS, |
| {true, false, true, false, false, false}, //!< DRAWMETHOD_DRAWELEMENTS_RANGED, |
| {true, true, false, false, false, false}, //!< DRAWMETHOD_DRAWELEMENTS_INSTANCED, |
| {true, true, false, false, true, true}, //!< DRAWMETHOD_DRAWELEMENTS_INDIRECT, |
| {true, false, false, false, true, false}, //!< DRAWMETHOD_DRAWELEMENTS_BASEVERTEX, |
| {true, true, false, false, true, false}, //!< DRAWMETHOD_DRAWELEMENTS_INSTANCED_BASEVERTEX, |
| {true, false, true, false, true, false}, //!< DRAWMETHOD_DRAWELEMENTS_RANGED_BASEVERTEX, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::DRAWMETHOD_LAST>(infos, (int)method); |
| } |
| |
| template <class T> |
| inline static void alignmentSafeAssignment(char *dst, T val) |
| { |
| std::memcpy(dst, &val, sizeof(T)); |
| } |
| |
| static bool checkSpecsShaderCompatible(const DrawTestSpec &a, const DrawTestSpec &b) |
| { |
| // Only the attributes matter |
| if (a.attribs.size() != b.attribs.size()) |
| return false; |
| |
| for (size_t ndx = 0; ndx < a.attribs.size(); ++ndx) |
| { |
| // Only the output type (== shader input type) matters and the usage in the shader. |
| |
| if (a.attribs[ndx].additionalPositionAttribute != b.attribs[ndx].additionalPositionAttribute) |
| return false; |
| |
| // component counts need not to match |
| if (outputTypeIsFloatType(a.attribs[ndx].outputType) && outputTypeIsFloatType(b.attribs[ndx].outputType)) |
| continue; |
| if (outputTypeIsIntType(a.attribs[ndx].outputType) && outputTypeIsIntType(b.attribs[ndx].outputType)) |
| continue; |
| if (outputTypeIsUintType(a.attribs[ndx].outputType) && outputTypeIsUintType(b.attribs[ndx].outputType)) |
| continue; |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // generate random vectors in a way that does not depend on argument evaluation order |
| |
| tcu::Vec4 generateRandomVec4(de::Random &random) |
| { |
| tcu::Vec4 retVal; |
| |
| for (int i = 0; i < 4; ++i) |
| retVal[i] = random.getFloat(); |
| |
| return retVal; |
| } |
| |
| tcu::IVec4 generateRandomIVec4(de::Random &random) |
| { |
| tcu::IVec4 retVal; |
| |
| for (int i = 0; i < 4; ++i) |
| retVal[i] = random.getUint32(); |
| |
| return retVal; |
| } |
| |
| tcu::UVec4 generateRandomUVec4(de::Random &random) |
| { |
| tcu::UVec4 retVal; |
| |
| for (int i = 0; i < 4; ++i) |
| retVal[i] = random.getUint32(); |
| |
| return retVal; |
| } |
| |
| // IterationLogSectionEmitter |
| |
| class IterationLogSectionEmitter |
| { |
| public: |
| IterationLogSectionEmitter(tcu::TestLog &log, size_t testIteration, size_t testIterations, |
| const std::string &description, bool enabled); |
| ~IterationLogSectionEmitter(void); |
| |
| private: |
| IterationLogSectionEmitter(const IterationLogSectionEmitter &); // delete |
| IterationLogSectionEmitter &operator=(const IterationLogSectionEmitter &); // delete |
| |
| tcu::TestLog &m_log; |
| bool m_enabled; |
| }; |
| |
| IterationLogSectionEmitter::IterationLogSectionEmitter(tcu::TestLog &log, size_t testIteration, size_t testIterations, |
| const std::string &description, bool enabled) |
| : m_log(log) |
| , m_enabled(enabled) |
| { |
| if (m_enabled) |
| { |
| std::ostringstream buf; |
| buf << "Iteration " << (testIteration + 1) << "/" << testIterations; |
| |
| if (!description.empty()) |
| buf << " - " << description; |
| |
| m_log << tcu::TestLog::Section(buf.str(), buf.str()); |
| } |
| } |
| |
| IterationLogSectionEmitter::~IterationLogSectionEmitter(void) |
| { |
| if (m_enabled) |
| m_log << tcu::TestLog::EndSection; |
| } |
| |
| // GLValue |
| |
| class GLValue |
| { |
| public: |
| template <class Type> |
| class WrappedType |
| { |
| public: |
| static WrappedType<Type> create(Type value) |
| { |
| WrappedType<Type> v; |
| v.m_value = value; |
| return v; |
| } |
| inline Type getValue(void) const |
| { |
| return m_value; |
| } |
| |
| inline WrappedType<Type> operator+(const WrappedType<Type> &other) const |
| { |
| return WrappedType<Type>::create((Type)(m_value + other.getValue())); |
| } |
| inline WrappedType<Type> operator*(const WrappedType<Type> &other) const |
| { |
| return WrappedType<Type>::create((Type)(m_value * other.getValue())); |
| } |
| inline WrappedType<Type> operator/(const WrappedType<Type> &other) const |
| { |
| return WrappedType<Type>::create((Type)(m_value / other.getValue())); |
| } |
| inline WrappedType<Type> operator-(const WrappedType<Type> &other) const |
| { |
| return WrappedType<Type>::create((Type)(m_value - other.getValue())); |
| } |
| |
| inline WrappedType<Type> &operator+=(const WrappedType<Type> &other) |
| { |
| m_value += other.getValue(); |
| return *this; |
| } |
| inline WrappedType<Type> &operator*=(const WrappedType<Type> &other) |
| { |
| m_value *= other.getValue(); |
| return *this; |
| } |
| inline WrappedType<Type> &operator/=(const WrappedType<Type> &other) |
| { |
| m_value /= other.getValue(); |
| return *this; |
| } |
| inline WrappedType<Type> &operator-=(const WrappedType<Type> &other) |
| { |
| m_value -= other.getValue(); |
| return *this; |
| } |
| |
| inline bool operator==(const WrappedType<Type> &other) const |
| { |
| return m_value == other.m_value; |
| } |
| inline bool operator!=(const WrappedType<Type> &other) const |
| { |
| return m_value != other.m_value; |
| } |
| inline bool operator<(const WrappedType<Type> &other) const |
| { |
| return m_value < other.m_value; |
| } |
| inline bool operator>(const WrappedType<Type> &other) const |
| { |
| return m_value > other.m_value; |
| } |
| inline bool operator<=(const WrappedType<Type> &other) const |
| { |
| return m_value <= other.m_value; |
| } |
| inline bool operator>=(const WrappedType<Type> &other) const |
| { |
| return m_value >= other.m_value; |
| } |
| |
| inline operator Type(void) const |
| { |
| return m_value; |
| } |
| template <class T> |
| inline T to(void) const |
| { |
| return (T)m_value; |
| } |
| |
| private: |
| Type m_value; |
| }; |
| |
| typedef WrappedType<int16_t> Short; |
| typedef WrappedType<uint16_t> Ushort; |
| |
| typedef WrappedType<int8_t> Byte; |
| typedef WrappedType<uint8_t> Ubyte; |
| |
| typedef WrappedType<float> Float; |
| typedef WrappedType<double> Double; |
| |
| typedef WrappedType<int32_t> Int; |
| typedef WrappedType<uint32_t> Uint; |
| |
| class Half |
| { |
| public: |
| static Half create(float value) |
| { |
| Half h; |
| h.m_value = floatToHalf(value); |
| return h; |
| } |
| inline deFloat16 getValue(void) const |
| { |
| return m_value; |
| } |
| |
| inline Half operator+(const Half &other) const |
| { |
| return create(halfToFloat(m_value) + halfToFloat(other.getValue())); |
| } |
| inline Half operator*(const Half &other) const |
| { |
| return create(halfToFloat(m_value) * halfToFloat(other.getValue())); |
| } |
| inline Half operator/(const Half &other) const |
| { |
| return create(halfToFloat(m_value) / halfToFloat(other.getValue())); |
| } |
| inline Half operator-(const Half &other) const |
| { |
| return create(halfToFloat(m_value) - halfToFloat(other.getValue())); |
| } |
| |
| inline Half &operator+=(const Half &other) |
| { |
| m_value = floatToHalf(halfToFloat(other.getValue()) + halfToFloat(m_value)); |
| return *this; |
| } |
| inline Half &operator*=(const Half &other) |
| { |
| m_value = floatToHalf(halfToFloat(other.getValue()) * halfToFloat(m_value)); |
| return *this; |
| } |
| inline Half &operator/=(const Half &other) |
| { |
| m_value = floatToHalf(halfToFloat(other.getValue()) / halfToFloat(m_value)); |
| return *this; |
| } |
| inline Half &operator-=(const Half &other) |
| { |
| m_value = floatToHalf(halfToFloat(other.getValue()) - halfToFloat(m_value)); |
| return *this; |
| } |
| |
| inline bool operator==(const Half &other) const |
| { |
| return m_value == other.m_value; |
| } |
| inline bool operator!=(const Half &other) const |
| { |
| return m_value != other.m_value; |
| } |
| inline bool operator<(const Half &other) const |
| { |
| return halfToFloat(m_value) < halfToFloat(other.m_value); |
| } |
| inline bool operator>(const Half &other) const |
| { |
| return halfToFloat(m_value) > halfToFloat(other.m_value); |
| } |
| inline bool operator<=(const Half &other) const |
| { |
| return halfToFloat(m_value) <= halfToFloat(other.m_value); |
| } |
| inline bool operator>=(const Half &other) const |
| { |
| return halfToFloat(m_value) >= halfToFloat(other.m_value); |
| } |
| |
| template <class T> |
| inline T to(void) const |
| { |
| return (T)halfToFloat(m_value); |
| } |
| |
| inline static deFloat16 floatToHalf(float f); |
| inline static float halfToFloat(deFloat16 h); |
| |
| private: |
| deFloat16 m_value; |
| }; |
| |
| class Fixed |
| { |
| public: |
| static Fixed create(int32_t value) |
| { |
| Fixed v; |
| v.m_value = value; |
| return v; |
| } |
| inline int32_t getValue(void) const |
| { |
| return m_value; |
| } |
| |
| inline Fixed operator+(const Fixed &other) const |
| { |
| return create(m_value + other.getValue()); |
| } |
| inline Fixed operator*(const Fixed &other) const |
| { |
| return create(m_value * other.getValue()); |
| } |
| inline Fixed operator/(const Fixed &other) const |
| { |
| return create(m_value / other.getValue()); |
| } |
| inline Fixed operator-(const Fixed &other) const |
| { |
| return create(m_value - other.getValue()); |
| } |
| |
| inline Fixed &operator+=(const Fixed &other) |
| { |
| m_value += other.getValue(); |
| return *this; |
| } |
| inline Fixed &operator*=(const Fixed &other) |
| { |
| m_value *= other.getValue(); |
| return *this; |
| } |
| inline Fixed &operator/=(const Fixed &other) |
| { |
| m_value /= other.getValue(); |
| return *this; |
| } |
| inline Fixed &operator-=(const Fixed &other) |
| { |
| m_value -= other.getValue(); |
| return *this; |
| } |
| |
| inline bool operator==(const Fixed &other) const |
| { |
| return m_value == other.m_value; |
| } |
| inline bool operator!=(const Fixed &other) const |
| { |
| return m_value != other.m_value; |
| } |
| inline bool operator<(const Fixed &other) const |
| { |
| return m_value < other.m_value; |
| } |
| inline bool operator>(const Fixed &other) const |
| { |
| return m_value > other.m_value; |
| } |
| inline bool operator<=(const Fixed &other) const |
| { |
| return m_value <= other.m_value; |
| } |
| inline bool operator>=(const Fixed &other) const |
| { |
| return m_value >= other.m_value; |
| } |
| |
| inline operator int32_t(void) const |
| { |
| return m_value; |
| } |
| template <class T> |
| inline T to(void) const |
| { |
| return (T)m_value; |
| } |
| |
| private: |
| int32_t m_value; |
| }; |
| |
| // \todo [mika] This is pretty messy |
| GLValue(void) : type(DrawTestSpec::INPUTTYPE_LAST) |
| { |
| } |
| explicit GLValue(Float value) : type(DrawTestSpec::INPUTTYPE_FLOAT), fl(value) |
| { |
| } |
| explicit GLValue(Fixed value) : type(DrawTestSpec::INPUTTYPE_FIXED), fi(value) |
| { |
| } |
| explicit GLValue(Byte value) : type(DrawTestSpec::INPUTTYPE_BYTE), b(value) |
| { |
| } |
| explicit GLValue(Ubyte value) : type(DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE), ub(value) |
| { |
| } |
| explicit GLValue(Short value) : type(DrawTestSpec::INPUTTYPE_SHORT), s(value) |
| { |
| } |
| explicit GLValue(Ushort value) : type(DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT), us(value) |
| { |
| } |
| explicit GLValue(Int value) : type(DrawTestSpec::INPUTTYPE_INT), i(value) |
| { |
| } |
| explicit GLValue(Uint value) : type(DrawTestSpec::INPUTTYPE_UNSIGNED_INT), ui(value) |
| { |
| } |
| explicit GLValue(Half value) : type(DrawTestSpec::INPUTTYPE_HALF), h(value) |
| { |
| } |
| explicit GLValue(Double value) : type(DrawTestSpec::INPUTTYPE_DOUBLE), d(value) |
| { |
| } |
| |
| float toFloat(void) const; |
| |
| static GLValue getMaxValue(DrawTestSpec::InputType type); |
| static GLValue getMinValue(DrawTestSpec::InputType type); |
| |
| DrawTestSpec::InputType type; |
| |
| union |
| { |
| Float fl; |
| Fixed fi; |
| Double d; |
| Byte b; |
| Ubyte ub; |
| Short s; |
| Ushort us; |
| Int i; |
| Uint ui; |
| Half h; |
| }; |
| }; |
| |
| inline deFloat16 GLValue::Half::floatToHalf(float f) |
| { |
| // No denorm support. |
| tcu::Float<uint16_t, 5, 10, 15, tcu::FLOAT_HAS_SIGN> v(f); |
| DE_ASSERT(!v.isNaN() && !v.isInf()); |
| return v.bits(); |
| } |
| |
| inline float GLValue::Half::halfToFloat(deFloat16 h) |
| { |
| return tcu::Float16((uint16_t)h).asFloat(); |
| } |
| |
| float GLValue::toFloat(void) const |
| { |
| switch (type) |
| { |
| case DrawTestSpec::INPUTTYPE_FLOAT: |
| return fl.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_BYTE: |
| return b.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE: |
| return ub.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_SHORT: |
| return s.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT: |
| return us.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_FIXED: |
| { |
| int maxValue = 65536; |
| return (float)(double(2 * fi.getValue() + 1) / (maxValue - 1)); |
| } |
| |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_INT: |
| return (float)ui.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_INT: |
| return (float)i.getValue(); |
| |
| case DrawTestSpec::INPUTTYPE_HALF: |
| return h.to<float>(); |
| |
| case DrawTestSpec::INPUTTYPE_DOUBLE: |
| return d.to<float>(); |
| |
| default: |
| DE_ASSERT(false); |
| return 0.0f; |
| } |
| } |
| |
| GLValue GLValue::getMaxValue(DrawTestSpec::InputType type) |
| { |
| GLValue rangesHi[(int)DrawTestSpec::INPUTTYPE_LAST]; |
| |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_FLOAT] = GLValue(Float::create(127.0f)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_DOUBLE] = GLValue(Double::create(127.0f)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_BYTE] = GLValue(Byte::create(127)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE] = GLValue(Ubyte::create(255)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT] = GLValue(Ushort::create(65530)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_SHORT] = GLValue(Short::create(32760)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_FIXED] = GLValue(Fixed::create(32760)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_INT] = GLValue(Int::create(2147483647)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_UNSIGNED_INT] = GLValue(Uint::create(4294967295u)); |
| rangesHi[(int)DrawTestSpec::INPUTTYPE_HALF] = GLValue(Half::create(256.0f)); |
| |
| return rangesHi[(int)type]; |
| } |
| |
| GLValue GLValue::getMinValue(DrawTestSpec::InputType type) |
| { |
| GLValue rangesLo[(int)DrawTestSpec::INPUTTYPE_LAST]; |
| |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_FLOAT] = GLValue(Float::create(-127.0f)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_DOUBLE] = GLValue(Double::create(-127.0f)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_BYTE] = GLValue(Byte::create(-127)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE] = GLValue(Ubyte::create(0)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT] = GLValue(Ushort::create(0)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_SHORT] = GLValue(Short::create(-32760)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_FIXED] = GLValue(Fixed::create(-32760)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_INT] = GLValue(Int::create(-2147483647)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_UNSIGNED_INT] = GLValue(Uint::create(0)); |
| rangesLo[(int)DrawTestSpec::INPUTTYPE_HALF] = GLValue(Half::create(-256.0f)); |
| |
| return rangesLo[(int)type]; |
| } |
| |
| template <typename T> |
| struct GLValueTypeTraits; |
| |
| template <> |
| struct GLValueTypeTraits<GLValue::Float> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_FLOAT; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Double> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_DOUBLE; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Byte> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_BYTE; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Ubyte> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Ushort> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Short> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_SHORT; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Fixed> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_FIXED; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Int> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_INT; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Uint> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_UNSIGNED_INT; |
| }; |
| template <> |
| struct GLValueTypeTraits<GLValue::Half> |
| { |
| static const DrawTestSpec::InputType Type = DrawTestSpec::INPUTTYPE_HALF; |
| }; |
| |
| template <typename T> |
| inline T extractGLValue(const GLValue &v); |
| |
| template <> |
| GLValue::Float inline extractGLValue<GLValue::Float>(const GLValue &v) |
| { |
| return v.fl; |
| } |
| template <> |
| GLValue::Double inline extractGLValue<GLValue::Double>(const GLValue &v) |
| { |
| return v.d; |
| } |
| template <> |
| GLValue::Byte inline extractGLValue<GLValue::Byte>(const GLValue &v) |
| { |
| return v.b; |
| } |
| template <> |
| GLValue::Ubyte inline extractGLValue<GLValue::Ubyte>(const GLValue &v) |
| { |
| return v.ub; |
| } |
| template <> |
| GLValue::Ushort inline extractGLValue<GLValue::Ushort>(const GLValue &v) |
| { |
| return v.us; |
| } |
| template <> |
| GLValue::Short inline extractGLValue<GLValue::Short>(const GLValue &v) |
| { |
| return v.s; |
| } |
| template <> |
| GLValue::Fixed inline extractGLValue<GLValue::Fixed>(const GLValue &v) |
| { |
| return v.fi; |
| } |
| template <> |
| GLValue::Int inline extractGLValue<GLValue::Int>(const GLValue &v) |
| { |
| return v.i; |
| } |
| template <> |
| GLValue::Uint inline extractGLValue<GLValue::Uint>(const GLValue &v) |
| { |
| return v.ui; |
| } |
| template <> |
| GLValue::Half inline extractGLValue<GLValue::Half>(const GLValue &v) |
| { |
| return v.h; |
| } |
| |
| template <class T> |
| inline T getRandom(deRandom &rnd, T min, T max); |
| |
| template <> |
| inline GLValue::Float getRandom(deRandom &rnd, GLValue::Float min, GLValue::Float max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Float::create(min + deRandom_getFloat(&rnd) * (max.to<float>() - min.to<float>())); |
| } |
| |
| template <> |
| inline GLValue::Double getRandom(deRandom &rnd, GLValue::Double min, GLValue::Double max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Double::create(min + deRandom_getFloat(&rnd) * (max.to<float>() - min.to<float>())); |
| } |
| |
| template <> |
| inline GLValue::Short getRandom(deRandom &rnd, GLValue::Short min, GLValue::Short max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Short::create( |
| (min == max ? min : (int16_t)(min + (deRandom_getUint32(&rnd) % (max.to<int>() - min.to<int>()))))); |
| } |
| |
| template <> |
| inline GLValue::Ushort getRandom(deRandom &rnd, GLValue::Ushort min, GLValue::Ushort max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Ushort::create( |
| (min == max ? min : (uint16_t)(min + (deRandom_getUint32(&rnd) % (max.to<int>() - min.to<int>()))))); |
| } |
| |
| template <> |
| inline GLValue::Byte getRandom(deRandom &rnd, GLValue::Byte min, GLValue::Byte max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Byte::create( |
| (min == max ? min : (int8_t)(min + (deRandom_getUint32(&rnd) % (max.to<int>() - min.to<int>()))))); |
| } |
| |
| template <> |
| inline GLValue::Ubyte getRandom(deRandom &rnd, GLValue::Ubyte min, GLValue::Ubyte max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Ubyte::create( |
| (min == max ? min : (uint8_t)(min + (deRandom_getUint32(&rnd) % (max.to<int>() - min.to<int>()))))); |
| } |
| |
| template <> |
| inline GLValue::Fixed getRandom(deRandom &rnd, GLValue::Fixed min, GLValue::Fixed max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Fixed::create( |
| (min == max ? min : min + (deRandom_getUint32(&rnd) % (max.to<uint32_t>() - min.to<uint32_t>())))); |
| } |
| |
| template <> |
| inline GLValue::Half getRandom(deRandom &rnd, GLValue::Half min, GLValue::Half max) |
| { |
| if (max < min) |
| return min; |
| |
| float fMax = max.to<float>(); |
| float fMin = min.to<float>(); |
| GLValue::Half h = GLValue::Half::create(fMin + deRandom_getFloat(&rnd) * (fMax - fMin)); |
| return h; |
| } |
| |
| template <> |
| inline GLValue::Int getRandom(deRandom &rnd, GLValue::Int min, GLValue::Int max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Int::create( |
| (min == max ? min : min + (deRandom_getUint32(&rnd) % (max.to<uint32_t>() - min.to<uint32_t>())))); |
| } |
| |
| template <> |
| inline GLValue::Uint getRandom(deRandom &rnd, GLValue::Uint min, GLValue::Uint max) |
| { |
| if (max < min) |
| return min; |
| |
| return GLValue::Uint::create( |
| (min == max ? min : min + (deRandom_getUint32(&rnd) % (max.to<uint32_t>() - min.to<uint32_t>())))); |
| } |
| |
| // Minimum difference required between coordinates |
| template <class T> |
| inline T minValue(void); |
| |
| template <> |
| inline GLValue::Float minValue(void) |
| { |
| return GLValue::Float::create(4 * 1.0f); |
| } |
| |
| template <> |
| inline GLValue::Double minValue(void) |
| { |
| return GLValue::Double::create(4 * 1.0f); |
| } |
| |
| template <> |
| inline GLValue::Short minValue(void) |
| { |
| return GLValue::Short::create(4 * 256); |
| } |
| |
| template <> |
| inline GLValue::Ushort minValue(void) |
| { |
| return GLValue::Ushort::create(4 * 256); |
| } |
| |
| template <> |
| inline GLValue::Byte minValue(void) |
| { |
| return GLValue::Byte::create(4 * 1); |
| } |
| |
| template <> |
| inline GLValue::Ubyte minValue(void) |
| { |
| return GLValue::Ubyte::create(4 * 2); |
| } |
| |
| template <> |
| inline GLValue::Fixed minValue(void) |
| { |
| return GLValue::Fixed::create(4 * 1); |
| } |
| |
| template <> |
| inline GLValue::Int minValue(void) |
| { |
| return GLValue::Int::create(4 * 16777216); |
| } |
| |
| template <> |
| inline GLValue::Uint minValue(void) |
| { |
| return GLValue::Uint::create(4 * 16777216); |
| } |
| |
| template <> |
| inline GLValue::Half minValue(void) |
| { |
| return GLValue::Half::create(4 * 1.0f); |
| } |
| |
| template <class T> |
| inline T abs(T val); |
| |
| template <> |
| inline GLValue::Fixed abs(GLValue::Fixed val) |
| { |
| return GLValue::Fixed::create(0x7FFFu & val.getValue()); |
| } |
| |
| template <> |
| inline GLValue::Ubyte abs(GLValue::Ubyte val) |
| { |
| return val; |
| } |
| |
| template <> |
| inline GLValue::Byte abs(GLValue::Byte val) |
| { |
| return GLValue::Byte::create(0x7Fu & val.getValue()); |
| } |
| |
| template <> |
| inline GLValue::Ushort abs(GLValue::Ushort val) |
| { |
| return val; |
| } |
| |
| template <> |
| inline GLValue::Short abs(GLValue::Short val) |
| { |
| return GLValue::Short::create(0x7FFFu & val.getValue()); |
| } |
| |
| template <> |
| inline GLValue::Float abs(GLValue::Float val) |
| { |
| return GLValue::Float::create(std::fabs(val.to<float>())); |
| } |
| |
| template <> |
| inline GLValue::Double abs(GLValue::Double val) |
| { |
| return GLValue::Double::create(std::fabs(val.to<float>())); |
| } |
| |
| template <> |
| inline GLValue::Uint abs(GLValue::Uint val) |
| { |
| return val; |
| } |
| |
| template <> |
| inline GLValue::Int abs(GLValue::Int val) |
| { |
| return GLValue::Int::create(0x7FFFFFFFu & val.getValue()); |
| } |
| |
| template <> |
| inline GLValue::Half abs(GLValue::Half val) |
| { |
| return GLValue::Half::create(std::fabs(val.to<float>())); |
| } |
| |
| // AttributeArray |
| |
| class AttributeArray |
| { |
| public: |
| AttributeArray(DrawTestSpec::Storage storage, sglr::Context &context); |
| ~AttributeArray(void); |
| |
| void data(DrawTestSpec::Target target, size_t size, const char *data, DrawTestSpec::Usage usage); |
| void setupArray(bool bound, int offset, int size, DrawTestSpec::InputType inType, DrawTestSpec::OutputType outType, |
| bool normalized, int stride, int instanceDivisor, const rr::GenericVec4 &defaultAttrib, |
| bool isPositionAttr, bool bgraComponentOrder); |
| void bindAttribute(uint32_t loc); |
| void bindIndexArray(DrawTestSpec::Target storage); |
| |
| int getComponentCount(void) const |
| { |
| return m_componentCount; |
| } |
| DrawTestSpec::Target getTarget(void) const |
| { |
| return m_target; |
| } |
| DrawTestSpec::InputType getInputType(void) const |
| { |
| return m_inputType; |
| } |
| DrawTestSpec::OutputType getOutputType(void) const |
| { |
| return m_outputType; |
| } |
| DrawTestSpec::Storage getStorageType(void) const |
| { |
| return m_storage; |
| } |
| bool getNormalized(void) const |
| { |
| return m_normalize; |
| } |
| int getStride(void) const |
| { |
| return m_stride; |
| } |
| bool isBound(void) const |
| { |
| return m_bound; |
| } |
| bool isPositionAttribute(void) const |
| { |
| return m_isPositionAttr; |
| } |
| |
| private: |
| DrawTestSpec::Storage m_storage; |
| sglr::Context &m_ctx; |
| uint32_t m_glBuffer; |
| |
| int m_size; |
| char *m_data; |
| int m_componentCount; |
| bool m_bound; |
| DrawTestSpec::Target m_target; |
| DrawTestSpec::InputType m_inputType; |
| DrawTestSpec::OutputType m_outputType; |
| bool m_normalize; |
| int m_stride; |
| int m_offset; |
| rr::GenericVec4 m_defaultAttrib; |
| int m_instanceDivisor; |
| bool m_isPositionAttr; |
| bool m_bgraOrder; |
| }; |
| |
| AttributeArray::AttributeArray(DrawTestSpec::Storage storage, sglr::Context &context) |
| : m_storage(storage) |
| , m_ctx(context) |
| , m_glBuffer(0) |
| , m_size(0) |
| , m_data(DE_NULL) |
| , m_componentCount(1) |
| , m_bound(false) |
| , m_target(DrawTestSpec::TARGET_ARRAY) |
| , m_inputType(DrawTestSpec::INPUTTYPE_FLOAT) |
| , m_outputType(DrawTestSpec::OUTPUTTYPE_VEC4) |
| , m_normalize(false) |
| , m_stride(0) |
| , m_offset(0) |
| , m_instanceDivisor(0) |
| , m_isPositionAttr(false) |
| , m_bgraOrder(false) |
| { |
| if (m_storage == DrawTestSpec::STORAGE_BUFFER) |
| { |
| m_ctx.genBuffers(1, &m_glBuffer); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glGenBuffers()"); |
| } |
| } |
| |
| AttributeArray::~AttributeArray(void) |
| { |
| if (m_storage == DrawTestSpec::STORAGE_BUFFER) |
| { |
| m_ctx.deleteBuffers(1, &m_glBuffer); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDeleteBuffers()"); |
| } |
| else if (m_storage == DrawTestSpec::STORAGE_USER) |
| delete[] m_data; |
| else |
| DE_ASSERT(false); |
| } |
| |
| void AttributeArray::data(DrawTestSpec::Target target, size_t size, const char *ptr, DrawTestSpec::Usage usage) |
| { |
| m_size = (int)size; |
| m_target = target; |
| |
| if (m_storage == DrawTestSpec::STORAGE_BUFFER) |
| { |
| m_ctx.bindBuffer(targetToGL(target), m_glBuffer); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glBindBuffer()"); |
| |
| m_ctx.bufferData(targetToGL(target), size, ptr, usageToGL(usage)); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glBufferData()"); |
| } |
| else if (m_storage == DrawTestSpec::STORAGE_USER) |
| { |
| if (m_data) |
| delete[] m_data; |
| |
| m_data = new char[size]; |
| std::memcpy(m_data, ptr, size); |
| } |
| else |
| DE_ASSERT(false); |
| } |
| |
| void AttributeArray::setupArray(bool bound, int offset, int size, DrawTestSpec::InputType inputType, |
| DrawTestSpec::OutputType outType, bool normalized, int stride, int instanceDivisor, |
| const rr::GenericVec4 &defaultAttrib, bool isPositionAttr, bool bgraComponentOrder) |
| { |
| m_componentCount = size; |
| m_bound = bound; |
| m_inputType = inputType; |
| m_outputType = outType; |
| m_normalize = normalized; |
| m_stride = stride; |
| m_offset = offset; |
| m_defaultAttrib = defaultAttrib; |
| m_instanceDivisor = instanceDivisor; |
| m_isPositionAttr = isPositionAttr; |
| m_bgraOrder = bgraComponentOrder; |
| } |
| |
| void AttributeArray::bindAttribute(uint32_t loc) |
| { |
| if (!isBound()) |
| { |
| switch (m_inputType) |
| { |
| case DrawTestSpec::INPUTTYPE_FLOAT: |
| { |
| tcu::Vec4 attr = m_defaultAttrib.get<float>(); |
| |
| switch (m_componentCount) |
| { |
| case 1: |
| m_ctx.vertexAttrib1f(loc, attr.x()); |
| break; |
| case 2: |
| m_ctx.vertexAttrib2f(loc, attr.x(), attr.y()); |
| break; |
| case 3: |
| m_ctx.vertexAttrib3f(loc, attr.x(), attr.y(), attr.z()); |
| break; |
| case 4: |
| m_ctx.vertexAttrib4f(loc, attr.x(), attr.y(), attr.z(), attr.w()); |
| break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| break; |
| } |
| case DrawTestSpec::INPUTTYPE_INT: |
| { |
| tcu::IVec4 attr = m_defaultAttrib.get<int32_t>(); |
| m_ctx.vertexAttribI4i(loc, attr.x(), attr.y(), attr.z(), attr.w()); |
| break; |
| } |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_INT: |
| { |
| tcu::UVec4 attr = m_defaultAttrib.get<uint32_t>(); |
| m_ctx.vertexAttribI4ui(loc, attr.x(), attr.y(), attr.z(), attr.w()); |
| break; |
| } |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| else |
| { |
| const uint8_t *basePtr = DE_NULL; |
| |
| if (m_storage == DrawTestSpec::STORAGE_BUFFER) |
| { |
| m_ctx.bindBuffer(targetToGL(m_target), m_glBuffer); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glBindBuffer()"); |
| |
| basePtr = DE_NULL; |
| } |
| else if (m_storage == DrawTestSpec::STORAGE_USER) |
| { |
| m_ctx.bindBuffer(targetToGL(m_target), 0); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glBindBuffer()"); |
| |
| basePtr = (const uint8_t *)m_data; |
| } |
| else |
| DE_ASSERT(false); |
| |
| if (!inputTypeIsFloatType(m_inputType)) |
| { |
| // Input is not float type |
| |
| if (outputTypeIsFloatType(m_outputType)) |
| { |
| const int size = (m_bgraOrder) ? (GL_BGRA) : (m_componentCount); |
| |
| DE_ASSERT(!(m_bgraOrder && m_componentCount != 4)); |
| |
| // Output type is float type |
| m_ctx.vertexAttribPointer(loc, size, inputTypeToGL(m_inputType), m_normalize, m_stride, |
| basePtr + m_offset); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glVertexAttribPointer()"); |
| } |
| else |
| { |
| // Output type is int type |
| m_ctx.vertexAttribIPointer(loc, m_componentCount, inputTypeToGL(m_inputType), m_stride, |
| basePtr + m_offset); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glVertexAttribIPointer()"); |
| } |
| } |
| else |
| { |
| // Input type is float type |
| |
| // Output type must be float type |
| DE_ASSERT(outputTypeIsFloatType(m_outputType)); |
| |
| m_ctx.vertexAttribPointer(loc, m_componentCount, inputTypeToGL(m_inputType), m_normalize, m_stride, |
| basePtr + m_offset); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glVertexAttribPointer()"); |
| } |
| |
| if (m_instanceDivisor) |
| m_ctx.vertexAttribDivisor(loc, m_instanceDivisor); |
| } |
| } |
| |
| void AttributeArray::bindIndexArray(DrawTestSpec::Target target) |
| { |
| if (m_storage == DrawTestSpec::STORAGE_USER) |
| { |
| } |
| else if (m_storage == DrawTestSpec::STORAGE_BUFFER) |
| { |
| m_ctx.bindBuffer(targetToGL(target), m_glBuffer); |
| } |
| } |
| |
| // DrawTestShaderProgram |
| |
| class DrawTestShaderProgram : public sglr::ShaderProgram |
| { |
| public: |
| DrawTestShaderProgram(const glu::RenderContext &ctx, const std::vector<AttributeArray *> &arrays); |
| |
| void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const; |
| void shadeFragments(rr::FragmentPacket *packets, const int numPackets, |
| const rr::FragmentShadingContext &context) const; |
| |
| private: |
| static std::string genVertexSource(const glu::RenderContext &ctx, const std::vector<AttributeArray *> &arrays); |
| static std::string genFragmentSource(const glu::RenderContext &ctx); |
| static void generateShaderParams(std::map<std::string, std::string> ¶ms, glu::ContextType type); |
| static rr::GenericVecType mapOutputType(const DrawTestSpec::OutputType &type); |
| static int getComponentCount(const DrawTestSpec::OutputType &type); |
| |
| static sglr::pdec::ShaderProgramDeclaration createProgramDeclaration(const glu::RenderContext &ctx, |
| const std::vector<AttributeArray *> &arrays); |
| |
| std::vector<int> m_componentCount; |
| std::vector<bool> m_isCoord; |
| std::vector<rr::GenericVecType> m_attrType; |
| }; |
| |
| DrawTestShaderProgram::DrawTestShaderProgram(const glu::RenderContext &ctx, const std::vector<AttributeArray *> &arrays) |
| : sglr::ShaderProgram(createProgramDeclaration(ctx, arrays)) |
| , m_componentCount(arrays.size()) |
| , m_isCoord(arrays.size()) |
| , m_attrType(arrays.size()) |
| { |
| for (int arrayNdx = 0; arrayNdx < (int)arrays.size(); arrayNdx++) |
| { |
| m_componentCount[arrayNdx] = getComponentCount(arrays[arrayNdx]->getOutputType()); |
| m_isCoord[arrayNdx] = arrays[arrayNdx]->isPositionAttribute(); |
| m_attrType[arrayNdx] = mapOutputType(arrays[arrayNdx]->getOutputType()); |
| } |
| } |
| |
| template <typename T> |
| void calcShaderColorCoord(tcu::Vec2 &coord, tcu::Vec3 &color, const tcu::Vector<T, 4> &attribValue, bool isCoordinate, |
| int numComponents) |
| { |
| if (isCoordinate) |
| switch (numComponents) |
| { |
| case 1: |
| coord += tcu::Vec2((float)attribValue.x(), (float)attribValue.x()); |
| break; |
| case 2: |
| coord += tcu::Vec2((float)attribValue.x(), (float)attribValue.y()); |
| break; |
| case 3: |
| coord += tcu::Vec2((float)attribValue.x() + (float)attribValue.z(), (float)attribValue.y()); |
| break; |
| case 4: |
| coord += tcu::Vec2((float)attribValue.x() + (float)attribValue.z(), |
| (float)attribValue.y() + (float)attribValue.w()); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| else |
| { |
| switch (numComponents) |
| { |
| case 1: |
| color = color * (float)attribValue.x(); |
| break; |
| |
| case 2: |
| color.x() = color.x() * (float)attribValue.x(); |
| color.y() = color.y() * (float)attribValue.y(); |
| break; |
| |
| case 3: |
| color.x() = color.x() * (float)attribValue.x(); |
| color.y() = color.y() * (float)attribValue.y(); |
| color.z() = color.z() * (float)attribValue.z(); |
| break; |
| |
| case 4: |
| color.x() = color.x() * (float)attribValue.x() * (float)attribValue.w(); |
| color.y() = color.y() * (float)attribValue.y() * (float)attribValue.w(); |
| color.z() = color.z() * (float)attribValue.z() * (float)attribValue.w(); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| } |
| |
| void DrawTestShaderProgram::shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, |
| const int numPackets) const |
| { |
| const float u_coordScale = getUniformByName("u_coordScale").value.f; |
| const float u_colorScale = getUniformByName("u_colorScale").value.f; |
| |
| for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) |
| { |
| const size_t varyingLocColor = 0; |
| |
| rr::VertexPacket &packet = *packets[packetNdx]; |
| |
| // Calc output color |
| tcu::Vec2 coord = tcu::Vec2(0.0, 0.0); |
| tcu::Vec3 color = tcu::Vec3(1.0, 1.0, 1.0); |
| |
| for (int attribNdx = 0; attribNdx < (int)m_attrType.size(); attribNdx++) |
| { |
| const int numComponents = m_componentCount[attribNdx]; |
| const bool isCoord = m_isCoord[attribNdx]; |
| |
| switch (m_attrType[attribNdx]) |
| { |
| case rr::GENERICVECTYPE_FLOAT: |
| calcShaderColorCoord(coord, color, |
| rr::readVertexAttribFloat(inputs[attribNdx], packet.instanceNdx, packet.vertexNdx), |
| isCoord, numComponents); |
| break; |
| case rr::GENERICVECTYPE_INT32: |
| calcShaderColorCoord(coord, color, |
| rr::readVertexAttribInt(inputs[attribNdx], packet.instanceNdx, packet.vertexNdx), |
| isCoord, numComponents); |
| break; |
| case rr::GENERICVECTYPE_UINT32: |
| calcShaderColorCoord(coord, color, |
| rr::readVertexAttribUint(inputs[attribNdx], packet.instanceNdx, packet.vertexNdx), |
| isCoord, numComponents); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| |
| // Transform position |
| { |
| packet.position = tcu::Vec4(u_coordScale * coord.x(), u_coordScale * coord.y(), 1.0f, 1.0f); |
| packet.pointSize = 1.0f; |
| } |
| |
| // Pass color to FS |
| { |
| packet.outputs[varyingLocColor] = |
| tcu::Vec4(u_colorScale * color.x(), u_colorScale * color.y(), u_colorScale * color.z(), 1.0f) * 0.5f + |
| tcu::Vec4(0.5f, 0.5f, 0.5f, 0.5f); |
| } |
| } |
| } |
| |
| void DrawTestShaderProgram::shadeFragments(rr::FragmentPacket *packets, const int numPackets, |
| const rr::FragmentShadingContext &context) const |
| { |
| const size_t varyingLocColor = 0; |
| |
| for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx) |
| { |
| rr::FragmentPacket &packet = packets[packetNdx]; |
| |
| for (int fragNdx = 0; fragNdx < 4; ++fragNdx) |
| rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, |
| rr::readVarying<float>(packet, context, varyingLocColor, fragNdx)); |
| } |
| } |
| |
| std::string DrawTestShaderProgram::genVertexSource(const glu::RenderContext &ctx, |
| const std::vector<AttributeArray *> &arrays) |
| { |
| std::map<std::string, std::string> params; |
| std::stringstream vertexShaderTmpl; |
| |
| generateShaderParams(params, ctx.getType()); |
| |
| vertexShaderTmpl << "${VTX_HDR}"; |
| |
| for (int arrayNdx = 0; arrayNdx < (int)arrays.size(); arrayNdx++) |
| { |
| vertexShaderTmpl << "${VTX_IN} highp " << outputTypeToGLType(arrays[arrayNdx]->getOutputType()) << " a_" |
| << arrayNdx << ";\n"; |
| } |
| |
| vertexShaderTmpl << "uniform highp float u_coordScale;\n" |
| "uniform highp float u_colorScale;\n" |
| "${VTX_OUT} ${COL_PRECISION} vec4 v_color;\n" |
| "void main(void)\n" |
| "{\n" |
| "\tgl_PointSize = 1.0;\n" |
| "\thighp vec2 coord = vec2(0.0, 0.0);\n" |
| "\thighp vec3 color = vec3(1.0, 1.0, 1.0);\n"; |
| |
| for (int arrayNdx = 0; arrayNdx < (int)arrays.size(); arrayNdx++) |
| { |
| const bool isPositionAttr = arrays[arrayNdx]->isPositionAttribute(); |
| |
| if (isPositionAttr) |
| { |
| switch (arrays[arrayNdx]->getOutputType()) |
| { |
| case (DrawTestSpec::OUTPUTTYPE_FLOAT): |
| case (DrawTestSpec::OUTPUTTYPE_INT): |
| case (DrawTestSpec::OUTPUTTYPE_UINT): |
| vertexShaderTmpl << "\tcoord += vec2(float(a_" << arrayNdx << "), float(a_" << arrayNdx << "));\n"; |
| break; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC2): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC2): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC2): |
| vertexShaderTmpl << "\tcoord += vec2(a_" << arrayNdx << ".xy);\n"; |
| break; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC3): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC3): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC3): |
| vertexShaderTmpl << "\tcoord += vec2(a_" << arrayNdx |
| << ".xy);\n" |
| "\tcoord.x += float(a_" |
| << arrayNdx << ".z);\n"; |
| break; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC4): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC4): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC4): |
| vertexShaderTmpl << "\tcoord += vec2(a_" << arrayNdx |
| << ".xy);\n" |
| "\tcoord += vec2(a_" |
| << arrayNdx << ".zw);\n"; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| else |
| { |
| switch (arrays[arrayNdx]->getOutputType()) |
| { |
| case (DrawTestSpec::OUTPUTTYPE_FLOAT): |
| case (DrawTestSpec::OUTPUTTYPE_INT): |
| case (DrawTestSpec::OUTPUTTYPE_UINT): |
| vertexShaderTmpl << "\tcolor = color * float(a_" << arrayNdx << ");\n"; |
| break; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC2): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC2): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC2): |
| vertexShaderTmpl << "\tcolor.rg = color.rg * vec2(a_" << arrayNdx << ".xy);\n"; |
| break; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC3): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC3): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC3): |
| vertexShaderTmpl << "\tcolor = color.rgb * vec3(a_" << arrayNdx << ".xyz);\n"; |
| break; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC4): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC4): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC4): |
| vertexShaderTmpl << "\tcolor = color.rgb * vec3(a_" << arrayNdx << ".xyz) * float(a_" << arrayNdx |
| << ".w);\n"; |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| } |
| |
| vertexShaderTmpl << "\tv_color = vec4(u_colorScale * color, 1.0) * 0.5 + vec4(0.5, 0.5, 0.5, 0.5);\n" |
| "\tgl_Position = vec4(u_coordScale * coord, 1.0, 1.0);\n" |
| "}\n"; |
| |
| return tcu::StringTemplate(vertexShaderTmpl.str().c_str()).specialize(params); |
| } |
| |
| std::string DrawTestShaderProgram::genFragmentSource(const glu::RenderContext &ctx) |
| { |
| std::map<std::string, std::string> params; |
| |
| generateShaderParams(params, ctx.getType()); |
| |
| static const char *fragmentShaderTmpl = "${FRAG_HDR}" |
| "${FRAG_IN} ${COL_PRECISION} vec4 v_color;\n" |
| "void main(void)\n" |
| "{\n" |
| "\t${FRAG_COLOR} = v_color;\n" |
| "}\n"; |
| |
| return tcu::StringTemplate(fragmentShaderTmpl).specialize(params); |
| } |
| |
| void DrawTestShaderProgram::generateShaderParams(std::map<std::string, std::string> ¶ms, glu::ContextType type) |
| { |
| if (glu::isGLSLVersionSupported(type, glu::GLSL_VERSION_300_ES)) |
| { |
| params["VTX_IN"] = "in"; |
| params["VTX_OUT"] = "out"; |
| params["FRAG_IN"] = "in"; |
| params["FRAG_COLOR"] = "dEQP_FragColor"; |
| params["VTX_HDR"] = "#version 300 es\n"; |
| params["FRAG_HDR"] = "#version 300 es\nlayout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| params["COL_PRECISION"] = "mediump"; |
| } |
| else if (glu::isGLSLVersionSupported(type, glu::GLSL_VERSION_100_ES)) |
| { |
| params["VTX_IN"] = "attribute"; |
| params["VTX_OUT"] = "varying"; |
| params["FRAG_IN"] = "varying"; |
| params["FRAG_COLOR"] = "gl_FragColor"; |
| params["VTX_HDR"] = ""; |
| params["FRAG_HDR"] = ""; |
| params["COL_PRECISION"] = "mediump"; |
| } |
| else if (glu::isGLSLVersionSupported(type, glu::GLSL_VERSION_430)) |
| { |
| params["VTX_IN"] = "in"; |
| params["VTX_OUT"] = "out"; |
| params["FRAG_IN"] = "in"; |
| params["FRAG_COLOR"] = "dEQP_FragColor"; |
| params["VTX_HDR"] = "#version 430\n"; |
| params["FRAG_HDR"] = "#version 430\nlayout(location = 0) out highp vec4 dEQP_FragColor;\n"; |
| params["COL_PRECISION"] = "highp"; |
| } |
| else if (glu::isGLSLVersionSupported(type, glu::GLSL_VERSION_330)) |
| { |
| params["VTX_IN"] = "in"; |
| params["VTX_OUT"] = "out"; |
| params["FRAG_IN"] = "in"; |
| params["FRAG_COLOR"] = "dEQP_FragColor"; |
| params["VTX_HDR"] = "#version 330\n"; |
| params["FRAG_HDR"] = "#version 330\nlayout(location = 0) out mediump vec4 dEQP_FragColor;\n"; |
| params["COL_PRECISION"] = "mediump"; |
| } |
| else |
| DE_ASSERT(false); |
| } |
| |
| rr::GenericVecType DrawTestShaderProgram::mapOutputType(const DrawTestSpec::OutputType &type) |
| { |
| switch (type) |
| { |
| case (DrawTestSpec::OUTPUTTYPE_FLOAT): |
| case (DrawTestSpec::OUTPUTTYPE_VEC2): |
| case (DrawTestSpec::OUTPUTTYPE_VEC3): |
| case (DrawTestSpec::OUTPUTTYPE_VEC4): |
| return rr::GENERICVECTYPE_FLOAT; |
| |
| case (DrawTestSpec::OUTPUTTYPE_INT): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC2): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC3): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC4): |
| return rr::GENERICVECTYPE_INT32; |
| |
| case (DrawTestSpec::OUTPUTTYPE_UINT): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC2): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC3): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC4): |
| return rr::GENERICVECTYPE_UINT32; |
| |
| default: |
| DE_ASSERT(false); |
| return rr::GENERICVECTYPE_LAST; |
| } |
| } |
| |
| int DrawTestShaderProgram::getComponentCount(const DrawTestSpec::OutputType &type) |
| { |
| switch (type) |
| { |
| case (DrawTestSpec::OUTPUTTYPE_FLOAT): |
| case (DrawTestSpec::OUTPUTTYPE_INT): |
| case (DrawTestSpec::OUTPUTTYPE_UINT): |
| return 1; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC2): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC2): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC2): |
| return 2; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC3): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC3): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC3): |
| return 3; |
| |
| case (DrawTestSpec::OUTPUTTYPE_VEC4): |
| case (DrawTestSpec::OUTPUTTYPE_IVEC4): |
| case (DrawTestSpec::OUTPUTTYPE_UVEC4): |
| return 4; |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| sglr::pdec::ShaderProgramDeclaration DrawTestShaderProgram::createProgramDeclaration( |
| const glu::RenderContext &ctx, const std::vector<AttributeArray *> &arrays) |
| { |
| sglr::pdec::ShaderProgramDeclaration decl; |
| |
| for (int arrayNdx = 0; arrayNdx < (int)arrays.size(); arrayNdx++) |
| decl << sglr::pdec::VertexAttribute(std::string("a_") + de::toString(arrayNdx), |
| mapOutputType(arrays[arrayNdx]->getOutputType())); |
| |
| decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT); |
| decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT); |
| |
| decl << sglr::pdec::VertexSource(genVertexSource(ctx, arrays)); |
| decl << sglr::pdec::FragmentSource(genFragmentSource(ctx)); |
| |
| decl << sglr::pdec::Uniform("u_coordScale", glu::TYPE_FLOAT); |
| decl << sglr::pdec::Uniform("u_colorScale", glu::TYPE_FLOAT); |
| |
| return decl; |
| } |
| |
| class RandomArrayGenerator |
| { |
| public: |
| static char *generateArray(int seed, int elementCount, int componentCount, int offset, int stride, |
| DrawTestSpec::InputType type); |
| static char *generateIndices(int seed, int elementCount, DrawTestSpec::IndexType type, int offset, int min, int max, |
| int indexBase); |
| static rr::GenericVec4 generateAttributeValue(int seed, DrawTestSpec::InputType type); |
| |
| private: |
| template <typename T> |
| static char *createIndices(int seed, int elementCount, int offset, int min, int max, int indexBase); |
| |
| static char *generateBasicArray(int seed, int elementCount, int componentCount, int offset, int stride, |
| DrawTestSpec::InputType type); |
| template <typename T, typename GLType> |
| static char *createBasicArray(int seed, int elementCount, int componentCount, int offset, int stride); |
| static char *generatePackedArray(int seed, int elementCount, int componentCount, int offset, int stride); |
| }; |
| |
| char *RandomArrayGenerator::generateArray(int seed, int elementCount, int componentCount, int offset, int stride, |
| DrawTestSpec::InputType type) |
| { |
| if (type == DrawTestSpec::INPUTTYPE_INT_2_10_10_10 || type == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10) |
| return generatePackedArray(seed, elementCount, componentCount, offset, stride); |
| else |
| return generateBasicArray(seed, elementCount, componentCount, offset, stride, type); |
| } |
| |
| char *RandomArrayGenerator::generateBasicArray(int seed, int elementCount, int componentCount, int offset, int stride, |
| DrawTestSpec::InputType type) |
| { |
| switch (type) |
| { |
| case DrawTestSpec::INPUTTYPE_FLOAT: |
| return createBasicArray<float, GLValue::Float>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_DOUBLE: |
| return createBasicArray<double, GLValue::Double>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_SHORT: |
| return createBasicArray<int16_t, GLValue::Short>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT: |
| return createBasicArray<uint16_t, GLValue::Ushort>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_BYTE: |
| return createBasicArray<int8_t, GLValue::Byte>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE: |
| return createBasicArray<uint8_t, GLValue::Ubyte>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_FIXED: |
| return createBasicArray<int32_t, GLValue::Fixed>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_INT: |
| return createBasicArray<int32_t, GLValue::Int>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_INT: |
| return createBasicArray<uint32_t, GLValue::Uint>(seed, elementCount, componentCount, offset, stride); |
| case DrawTestSpec::INPUTTYPE_HALF: |
| return createBasicArray<deFloat16, GLValue::Half>(seed, elementCount, componentCount, offset, stride); |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| return DE_NULL; |
| } |
| |
| #if (DE_COMPILER == DE_COMPILER_GCC) && (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8) |
| // GCC 4.8/4.9 incorrectly emits array-bounds warning from createBasicArray() |
| #define GCC_ARRAY_BOUNDS_FALSE_NEGATIVE 1 |
| #endif |
| |
| #if defined(GCC_ARRAY_BOUNDS_FALSE_NEGATIVE) |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Warray-bounds" |
| #endif |
| |
| template <typename T, typename GLType> |
| char *RandomArrayGenerator::createBasicArray(int seed, int elementCount, int componentCount, int offset, int stride) |
| { |
| DE_ASSERT(componentCount >= 1 && componentCount <= 4); |
| |
| const GLType min = extractGLValue<GLType>(GLValue::getMinValue(GLValueTypeTraits<GLType>::Type)); |
| const GLType max = extractGLValue<GLType>(GLValue::getMaxValue(GLValueTypeTraits<GLType>::Type)); |
| |
| const size_t componentSize = sizeof(T); |
| const size_t elementSize = componentSize * componentCount; |
| const size_t bufferSize = offset + (elementCount - 1) * stride + elementSize; |
| |
| char *data = new char[bufferSize]; |
| char *writePtr = data + offset; |
| |
| GLType previousComponents[4]; |
| |
| deRandom rnd; |
| deRandom_init(&rnd, seed); |
| |
| for (int vertexNdx = 0; vertexNdx < elementCount; vertexNdx++) |
| { |
| GLType components[4]; |
| |
| for (int componentNdx = 0; componentNdx < componentCount; componentNdx++) |
| { |
| components[componentNdx] = getRandom<GLType>(rnd, min, max); |
| |
| // Try to not create vertex near previous |
| if (vertexNdx != 0 && abs(components[componentNdx] - previousComponents[componentNdx]) < minValue<GLType>()) |
| { |
| // Too close, try again (but only once) |
| components[componentNdx] = getRandom<GLType>(rnd, min, max); |
| } |
| } |
| |
| for (int componentNdx = 0; componentNdx < componentCount; componentNdx++) |
| previousComponents[componentNdx] = components[componentNdx]; |
| |
| for (int componentNdx = 0; componentNdx < componentCount; componentNdx++) |
| alignmentSafeAssignment(writePtr + componentNdx * componentSize, components[componentNdx].getValue()); |
| |
| writePtr += stride; |
| } |
| |
| return data; |
| } |
| |
| #if defined(GCC_ARRAY_BOUNDS_FALSE_NEGATIVE) |
| #pragma GCC diagnostic pop |
| #endif |
| |
| char *RandomArrayGenerator::generatePackedArray(int seed, int elementCount, int componentCount, int offset, int stride) |
| { |
| DE_ASSERT(componentCount == 4); |
| DE_UNREF(componentCount); |
| |
| const uint32_t limit10 = (1 << 10); |
| const uint32_t limit2 = (1 << 2); |
| const size_t elementSize = 4; |
| const size_t bufferSize = offset + (elementCount - 1) * stride + elementSize; |
| |
| char *data = new char[bufferSize]; |
| char *writePtr = data + offset; |
| |
| deRandom rnd; |
| deRandom_init(&rnd, seed); |
| |
| for (int vertexNdx = 0; vertexNdx < elementCount; vertexNdx++) |
| { |
| const uint32_t x = deRandom_getUint32(&rnd) % limit10; |
| const uint32_t y = deRandom_getUint32(&rnd) % limit10; |
| const uint32_t z = deRandom_getUint32(&rnd) % limit10; |
| const uint32_t w = deRandom_getUint32(&rnd) % limit2; |
| const uint32_t packedValue = (w << 30) | (z << 20) | (y << 10) | (x); |
| |
| alignmentSafeAssignment(writePtr, packedValue); |
| writePtr += stride; |
| } |
| |
| return data; |
| } |
| |
| char *RandomArrayGenerator::generateIndices(int seed, int elementCount, DrawTestSpec::IndexType type, int offset, |
| int min, int max, int indexBase) |
| { |
| char *data = DE_NULL; |
| |
| switch (type) |
| { |
| case DrawTestSpec::INDEXTYPE_BYTE: |
| data = createIndices<uint8_t>(seed, elementCount, offset, min, max, indexBase); |
| break; |
| |
| case DrawTestSpec::INDEXTYPE_SHORT: |
| data = createIndices<uint16_t>(seed, elementCount, offset, min, max, indexBase); |
| break; |
| |
| case DrawTestSpec::INDEXTYPE_INT: |
| data = createIndices<uint32_t>(seed, elementCount, offset, min, max, indexBase); |
| break; |
| |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| return data; |
| } |
| |
| template <typename T> |
| char *RandomArrayGenerator::createIndices(int seed, int elementCount, int offset, int min, int max, int indexBase) |
| { |
| const size_t elementSize = sizeof(T); |
| const size_t bufferSize = offset + elementCount * elementSize; |
| |
| char *data = new char[bufferSize]; |
| char *writePtr = data + offset; |
| |
| uint32_t oldNdx1 = uint32_t(-1); |
| uint32_t oldNdx2 = uint32_t(-1); |
| |
| deRandom rnd; |
| deRandom_init(&rnd, seed); |
| |
| DE_ASSERT(indexBase >= 0); // watch for underflows |
| |
| if (min < 0 || (size_t)min > std::numeric_limits<T>::max() || max < 0 || |
| (size_t)max > std::numeric_limits<T>::max() || min > max) |
| DE_FATAL("Invalid range"); |
| |
| for (int elementNdx = 0; elementNdx < elementCount; ++elementNdx) |
| { |
| uint32_t ndx = getRandom(rnd, GLValue::Uint::create(min), GLValue::Uint::create(max)).getValue(); |
| |
| // Try not to generate same index as any of previous two. This prevents |
| // generation of degenerate triangles and lines. If [min, max] is too |
| // small this cannot be guaranteed. |
| |
| if (ndx == oldNdx1) |
| ++ndx; |
| if (ndx > (uint32_t)max) |
| ndx = min; |
| if (ndx == oldNdx2) |
| ++ndx; |
| if (ndx > (uint32_t)max) |
| ndx = min; |
| if (ndx == oldNdx1) |
| ++ndx; |
| if (ndx > (uint32_t)max) |
| ndx = min; |
| |
| oldNdx2 = oldNdx1; |
| oldNdx1 = ndx; |
| |
| ndx += indexBase; |
| |
| alignmentSafeAssignment<T>(writePtr + elementSize * elementNdx, T(ndx)); |
| } |
| |
| return data; |
| } |
| |
| rr::GenericVec4 RandomArrayGenerator::generateAttributeValue(int seed, DrawTestSpec::InputType type) |
| { |
| de::Random random(seed); |
| |
| switch (type) |
| { |
| case DrawTestSpec::INPUTTYPE_FLOAT: |
| return rr::GenericVec4(generateRandomVec4(random)); |
| |
| case DrawTestSpec::INPUTTYPE_INT: |
| return rr::GenericVec4(generateRandomIVec4(random)); |
| |
| case DrawTestSpec::INPUTTYPE_UNSIGNED_INT: |
| return rr::GenericVec4(generateRandomUVec4(random)); |
| |
| default: |
| DE_ASSERT(false); |
| return rr::GenericVec4(tcu::Vec4(1, 1, 1, 1)); |
| } |
| } |
| |
| } // namespace |
| |
| // AttributePack |
| |
| class AttributePack |
| { |
| public: |
| AttributePack(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, sglr::Context &drawContext, |
| const tcu::UVec2 &screenSize, bool useVao, bool logEnabled); |
| ~AttributePack(void); |
| |
| AttributeArray *getArray(int i); |
| int getArrayCount(void); |
| |
| void newArray(DrawTestSpec::Storage storage); |
| void clearArrays(void); |
| void updateProgram(void); |
| |
| void render(DrawTestSpec::Primitive primitive, DrawTestSpec::DrawMethod drawMethod, int firstVertex, |
| int vertexCount, DrawTestSpec::IndexType indexType, const void *indexOffset, int rangeStart, |
| int rangeEnd, int instanceCount, int indirectOffset, int baseVertex, float coordScale, float colorScale, |
| AttributeArray *indexArray); |
| |
| const tcu::Surface &getSurface(void) const |
| { |
| return m_screen; |
| } |
| |
| private: |
| tcu::TestContext &m_testCtx; |
| glu::RenderContext &m_renderCtx; |
| sglr::Context &m_ctx; |
| |
| std::vector<AttributeArray *> m_arrays; |
| sglr::ShaderProgram *m_program; |
| tcu::Surface m_screen; |
| const bool m_useVao; |
| const bool m_logEnabled; |
| uint32_t m_programID; |
| uint32_t m_vaoID; |
| }; |
| |
| AttributePack::AttributePack(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, sglr::Context &drawContext, |
| const tcu::UVec2 &screenSize, bool useVao, bool logEnabled) |
| : m_testCtx(testCtx) |
| , m_renderCtx(renderCtx) |
| , m_ctx(drawContext) |
| , m_program(DE_NULL) |
| , m_screen(screenSize.x(), screenSize.y()) |
| , m_useVao(useVao) |
| , m_logEnabled(logEnabled) |
| , m_programID(0) |
| , m_vaoID(0) |
| { |
| if (m_useVao) |
| m_ctx.genVertexArrays(1, &m_vaoID); |
| } |
| |
| AttributePack::~AttributePack(void) |
| { |
| clearArrays(); |
| |
| if (m_programID) |
| m_ctx.deleteProgram(m_programID); |
| |
| if (m_program) |
| delete m_program; |
| |
| if (m_useVao) |
| m_ctx.deleteVertexArrays(1, &m_vaoID); |
| } |
| |
| AttributeArray *AttributePack::getArray(int i) |
| { |
| return m_arrays.at(i); |
| } |
| |
| int AttributePack::getArrayCount(void) |
| { |
| return (int)m_arrays.size(); |
| } |
| |
| void AttributePack::newArray(DrawTestSpec::Storage storage) |
| { |
| m_arrays.push_back(new AttributeArray(storage, m_ctx)); |
| } |
| |
| void AttributePack::clearArrays(void) |
| { |
| for (std::vector<AttributeArray *>::iterator itr = m_arrays.begin(); itr != m_arrays.end(); itr++) |
| delete *itr; |
| m_arrays.clear(); |
| } |
| |
| void AttributePack::updateProgram(void) |
| { |
| if (m_programID) |
| m_ctx.deleteProgram(m_programID); |
| if (m_program) |
| delete m_program; |
| |
| m_program = new DrawTestShaderProgram(m_renderCtx, m_arrays); |
| m_programID = m_ctx.createProgram(m_program); |
| } |
| |
| void AttributePack::render(DrawTestSpec::Primitive primitive, DrawTestSpec::DrawMethod drawMethod, int firstVertex, |
| int vertexCount, DrawTestSpec::IndexType indexType, const void *indexOffset, int rangeStart, |
| int rangeEnd, int instanceCount, int indirectOffset, int baseVertex, float coordScale, |
| float colorScale, AttributeArray *indexArray) |
| { |
| DE_ASSERT(m_program != DE_NULL); |
| DE_ASSERT(m_programID != 0); |
| |
| m_ctx.viewport(0, 0, m_screen.getWidth(), m_screen.getHeight()); |
| m_ctx.clearColor(0.0, 0.0, 0.0, 1.0); |
| m_ctx.clear(GL_COLOR_BUFFER_BIT); |
| |
| m_ctx.useProgram(m_programID); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glUseProgram()"); |
| |
| m_ctx.uniform1f(m_ctx.getUniformLocation(m_programID, "u_coordScale"), coordScale); |
| m_ctx.uniform1f(m_ctx.getUniformLocation(m_programID, "u_colorScale"), colorScale); |
| |
| if (m_useVao) |
| m_ctx.bindVertexArray(m_vaoID); |
| |
| if (indexArray) |
| indexArray->bindIndexArray(DrawTestSpec::TARGET_ELEMENT_ARRAY); |
| |
| for (int arrayNdx = 0; arrayNdx < (int)m_arrays.size(); arrayNdx++) |
| { |
| std::stringstream attribName; |
| attribName << "a_" << arrayNdx; |
| |
| uint32_t loc = m_ctx.getAttribLocation(m_programID, attribName.str().c_str()); |
| |
| if (m_arrays[arrayNdx]->isBound()) |
| { |
| m_ctx.enableVertexAttribArray(loc); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glEnableVertexAttribArray()"); |
| } |
| |
| m_arrays[arrayNdx]->bindAttribute(loc); |
| } |
| |
| if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWARRAYS) |
| { |
| m_ctx.drawArrays(primitiveToGL(primitive), firstVertex, vertexCount); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawArrays()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWARRAYS_INSTANCED) |
| { |
| m_ctx.drawArraysInstanced(primitiveToGL(primitive), firstVertex, vertexCount, instanceCount); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawArraysInstanced()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS) |
| { |
| m_ctx.drawElements(primitiveToGL(primitive), vertexCount, indexTypeToGL(indexType), indexOffset); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawElements()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_RANGED) |
| { |
| m_ctx.drawRangeElements(primitiveToGL(primitive), rangeStart, rangeEnd, vertexCount, indexTypeToGL(indexType), |
| indexOffset); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawRangeElements()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INSTANCED) |
| { |
| m_ctx.drawElementsInstanced(primitiveToGL(primitive), vertexCount, indexTypeToGL(indexType), indexOffset, |
| instanceCount); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawElementsInstanced()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWARRAYS_INDIRECT) |
| { |
| struct DrawCommand |
| { |
| GLuint count; |
| GLuint primCount; |
| GLuint first; |
| GLuint reservedMustBeZero; |
| }; |
| uint8_t *buffer = new uint8_t[sizeof(DrawCommand) + indirectOffset]; |
| |
| { |
| DrawCommand command; |
| |
| command.count = vertexCount; |
| command.primCount = instanceCount; |
| command.first = firstVertex; |
| command.reservedMustBeZero = 0; |
| |
| memcpy(buffer + indirectOffset, &command, sizeof(command)); |
| |
| if (m_logEnabled) |
| m_testCtx.getLog() << tcu::TestLog::Message << "DrawArraysIndirectCommand:\n" |
| << "\tcount: " << command.count << "\n" |
| << "\tprimCount: " << command.primCount << "\n" |
| << "\tfirst: " << command.first << "\n" |
| << "\treservedMustBeZero: " << command.reservedMustBeZero << "\n" |
| << tcu::TestLog::EndMessage; |
| } |
| |
| GLuint indirectBuf = 0; |
| m_ctx.genBuffers(1, &indirectBuf); |
| m_ctx.bindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuf); |
| m_ctx.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(DrawCommand) + indirectOffset, buffer, GL_STATIC_DRAW); |
| delete[] buffer; |
| |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "Setup draw indirect buffer"); |
| |
| m_ctx.drawArraysIndirect(primitiveToGL(primitive), glu::BufferOffsetAsPointer(indirectOffset)); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawArraysIndirect()"); |
| |
| m_ctx.deleteBuffers(1, &indirectBuf); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INDIRECT) |
| { |
| struct DrawCommand |
| { |
| GLuint count; |
| GLuint primCount; |
| GLuint firstIndex; |
| GLint baseVertex; |
| GLuint reservedMustBeZero; |
| }; |
| uint8_t *buffer = new uint8_t[sizeof(DrawCommand) + indirectOffset]; |
| |
| { |
| DrawCommand command; |
| |
| // index offset must be converted to firstIndex by dividing with the index element size |
| DE_ASSERT(((const uint8_t *)indexOffset - (const uint8_t *)DE_NULL) % |
| gls::DrawTestSpec::indexTypeSize(indexType) == |
| 0); // \note This is checked in spec validation |
| |
| command.count = vertexCount; |
| command.primCount = instanceCount; |
| command.firstIndex = (glw::GLuint)(((const uint8_t *)indexOffset - (const uint8_t *)DE_NULL) / |
| gls::DrawTestSpec::indexTypeSize(indexType)); |
| command.baseVertex = baseVertex; |
| command.reservedMustBeZero = 0; |
| |
| memcpy(buffer + indirectOffset, &command, sizeof(command)); |
| |
| if (m_logEnabled) |
| m_testCtx.getLog() << tcu::TestLog::Message << "DrawElementsIndirectCommand:\n" |
| << "\tcount: " << command.count << "\n" |
| << "\tprimCount: " << command.primCount << "\n" |
| << "\tfirstIndex: " << command.firstIndex << "\n" |
| << "\tbaseVertex: " << command.baseVertex << "\n" |
| << "\treservedMustBeZero: " << command.reservedMustBeZero << "\n" |
| << tcu::TestLog::EndMessage; |
| } |
| |
| GLuint indirectBuf = 0; |
| m_ctx.genBuffers(1, &indirectBuf); |
| m_ctx.bindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuf); |
| m_ctx.bufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(DrawCommand) + indirectOffset, buffer, GL_STATIC_DRAW); |
| delete[] buffer; |
| |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "Setup draw indirect buffer"); |
| |
| m_ctx.drawElementsIndirect(primitiveToGL(primitive), indexTypeToGL(indexType), |
| glu::BufferOffsetAsPointer(indirectOffset)); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawArraysIndirect()"); |
| |
| m_ctx.deleteBuffers(1, &indirectBuf); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_BASEVERTEX) |
| { |
| m_ctx.drawElementsBaseVertex(primitiveToGL(primitive), vertexCount, indexTypeToGL(indexType), indexOffset, |
| baseVertex); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawElementsBaseVertex()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INSTANCED_BASEVERTEX) |
| { |
| m_ctx.drawElementsInstancedBaseVertex(primitiveToGL(primitive), vertexCount, indexTypeToGL(indexType), |
| indexOffset, instanceCount, baseVertex); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawElementsInstancedBaseVertex()"); |
| } |
| else if (drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_RANGED_BASEVERTEX) |
| { |
| m_ctx.drawRangeElementsBaseVertex(primitiveToGL(primitive), rangeStart, rangeEnd, vertexCount, |
| indexTypeToGL(indexType), indexOffset, baseVertex); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDrawRangeElementsBaseVertex()"); |
| } |
| else |
| DE_ASSERT(false); |
| |
| for (int arrayNdx = 0; arrayNdx < (int)m_arrays.size(); arrayNdx++) |
| { |
| if (m_arrays[arrayNdx]->isBound()) |
| { |
| std::stringstream attribName; |
| attribName << "a_" << arrayNdx; |
| |
| uint32_t loc = m_ctx.getAttribLocation(m_programID, attribName.str().c_str()); |
| |
| m_ctx.disableVertexAttribArray(loc); |
| GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glDisableVertexAttribArray()"); |
| } |
| } |
| |
| if (m_useVao) |
| m_ctx.bindVertexArray(0); |
| |
| m_ctx.useProgram(0); |
| m_ctx.readPixels(m_screen, 0, 0, m_screen.getWidth(), m_screen.getHeight()); |
| } |
| |
| // DrawTestSpec |
| |
| DrawTestSpec::AttributeSpec DrawTestSpec::AttributeSpec::createAttributeArray(InputType inputType, |
| OutputType outputType, Storage storage, |
| Usage usage, int componentCount, |
| int offset, int stride, bool normalize, |
| int instanceDivisor) |
| { |
| DrawTestSpec::AttributeSpec spec; |
| |
| spec.inputType = inputType; |
| spec.outputType = outputType; |
| spec.storage = storage; |
| spec.usage = usage; |
| spec.componentCount = componentCount; |
| spec.offset = offset; |
| spec.stride = stride; |
| spec.normalize = normalize; |
| spec.instanceDivisor = instanceDivisor; |
| |
| spec.useDefaultAttribute = false; |
| |
| return spec; |
| } |
| |
| DrawTestSpec::AttributeSpec DrawTestSpec::AttributeSpec::createDefaultAttribute(InputType inputType, |
| OutputType outputType, |
| int componentCount) |
| { |
| DE_ASSERT(inputType == INPUTTYPE_INT || inputType == INPUTTYPE_UNSIGNED_INT || inputType == INPUTTYPE_FLOAT); |
| DE_ASSERT(inputType == INPUTTYPE_FLOAT || componentCount == 4); |
| |
| DrawTestSpec::AttributeSpec spec; |
| |
| spec.inputType = inputType; |
| spec.outputType = outputType; |
| spec.storage = DrawTestSpec::STORAGE_LAST; |
| spec.usage = DrawTestSpec::USAGE_LAST; |
| spec.componentCount = componentCount; |
| spec.offset = 0; |
| spec.stride = 0; |
| spec.normalize = 0; |
| spec.instanceDivisor = 0; |
| |
| spec.useDefaultAttribute = true; |
| |
| return spec; |
| } |
| |
| DrawTestSpec::AttributeSpec::AttributeSpec(void) |
| { |
| inputType = DrawTestSpec::INPUTTYPE_LAST; |
| outputType = DrawTestSpec::OUTPUTTYPE_LAST; |
| storage = DrawTestSpec::STORAGE_LAST; |
| usage = DrawTestSpec::USAGE_LAST; |
| componentCount = 0; |
| offset = 0; |
| stride = 0; |
| normalize = false; |
| instanceDivisor = 0; |
| useDefaultAttribute = false; |
| additionalPositionAttribute = false; |
| bgraComponentOrder = false; |
| } |
| |
| int DrawTestSpec::AttributeSpec::hash(void) const |
| { |
| if (useDefaultAttribute) |
| { |
| return 1 * int(inputType) + 7 * int(outputType) + 13 * componentCount; |
| } |
| else |
| { |
| return 1 * int(inputType) + 2 * int(outputType) + 3 * int(storage) + 5 * int(usage) + 7 * componentCount + |
| 11 * offset + 13 * stride + 17 * (normalize ? 0 : 1) + 19 * instanceDivisor; |
| } |
| } |
| |
| bool DrawTestSpec::AttributeSpec::valid(glu::ApiType ctxType) const |
| { |
| const bool inputTypeFloat = inputType == DrawTestSpec::INPUTTYPE_FLOAT || |
| inputType == DrawTestSpec::INPUTTYPE_FIXED || inputType == DrawTestSpec::INPUTTYPE_HALF; |
| const bool inputTypeUnsignedInteger = inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE || |
| inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT || |
| inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT || |
| inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10; |
| const bool inputTypeSignedInteger = |
| inputType == DrawTestSpec::INPUTTYPE_BYTE || inputType == DrawTestSpec::INPUTTYPE_SHORT || |
| inputType == DrawTestSpec::INPUTTYPE_INT || inputType == DrawTestSpec::INPUTTYPE_INT_2_10_10_10; |
| const bool inputTypePacked = inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || |
| inputType == DrawTestSpec::INPUTTYPE_INT_2_10_10_10; |
| |
| const bool outputTypeFloat = |
| outputType == DrawTestSpec::OUTPUTTYPE_FLOAT || outputType == DrawTestSpec::OUTPUTTYPE_VEC2 || |
| outputType == DrawTestSpec::OUTPUTTYPE_VEC3 || outputType == DrawTestSpec::OUTPUTTYPE_VEC4; |
| const bool outputTypeSignedInteger = |
| outputType == DrawTestSpec::OUTPUTTYPE_INT || outputType == DrawTestSpec::OUTPUTTYPE_IVEC2 || |
| outputType == DrawTestSpec::OUTPUTTYPE_IVEC3 || outputType == DrawTestSpec::OUTPUTTYPE_IVEC4; |
| const bool outputTypeUnsignedInteger = |
| outputType == DrawTestSpec::OUTPUTTYPE_UINT || outputType == DrawTestSpec::OUTPUTTYPE_UVEC2 || |
| outputType == DrawTestSpec::OUTPUTTYPE_UVEC3 || outputType == DrawTestSpec::OUTPUTTYPE_UVEC4; |
| |
| if (useDefaultAttribute) |
| { |
| if (inputType != DrawTestSpec::INPUTTYPE_INT && inputType != DrawTestSpec::INPUTTYPE_UNSIGNED_INT && |
| inputType != DrawTestSpec::INPUTTYPE_FLOAT) |
| return false; |
| |
| if (inputType != DrawTestSpec::INPUTTYPE_FLOAT && componentCount != 4) |
| return false; |
| |
| // no casting allowed (undefined results) |
| if (inputType == DrawTestSpec::INPUTTYPE_INT && !outputTypeSignedInteger) |
| return false; |
| if (inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT && !outputTypeUnsignedInteger) |
| return false; |
| } |
| |
| if (inputTypePacked && componentCount != 4) |
| return false; |
| |
| // Invalid conversions: |
| |
| // float -> [u]int |
| if (inputTypeFloat && !outputTypeFloat) |
| return false; |
| |
| // uint -> int (undefined results) |
| if (inputTypeUnsignedInteger && outputTypeSignedInteger) |
| return false; |
| |
| // int -> uint (undefined results) |
| if (inputTypeSignedInteger && outputTypeUnsignedInteger) |
| return false; |
| |
| // packed -> non-float (packed formats are converted to floats) |
| if (inputTypePacked && !outputTypeFloat) |
| return false; |
| |
| // Invalid normalize. Normalize is only valid if output type is float |
| if (normalize && !outputTypeFloat) |
| return false; |
| |
| // Allow reverse order (GL_BGRA) only for packed and 4-component ubyte |
| if (bgraComponentOrder && componentCount != 4) |
| return false; |
| if (bgraComponentOrder && inputType != DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10 && |
| inputType != DrawTestSpec::INPUTTYPE_INT_2_10_10_10 && inputType != DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE) |
| return false; |
| if (bgraComponentOrder && normalize != true) |
| return false; |
| |
| // GLES2 limits |
| if (ctxType == glu::ApiType::es(2, 0)) |
| { |
| if (inputType != DrawTestSpec::INPUTTYPE_FLOAT && inputType != DrawTestSpec::INPUTTYPE_FIXED && |
| inputType != DrawTestSpec::INPUTTYPE_BYTE && inputType != DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE && |
| inputType != DrawTestSpec::INPUTTYPE_SHORT && inputType != DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT) |
| return false; |
| |
| if (!outputTypeFloat) |
| return false; |
| |
| if (bgraComponentOrder) |
| return false; |
| } |
| |
| // GLES3 limits |
| if (ctxType.getProfile() == glu::PROFILE_ES && ctxType.getMajorVersion() == 3) |
| { |
| if (bgraComponentOrder) |
| return false; |
| } |
| |
| // No user pointers in GL core |
| if (ctxType.getProfile() == glu::PROFILE_CORE) |
| { |
| if (!useDefaultAttribute && storage == DrawTestSpec::STORAGE_USER) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DrawTestSpec::AttributeSpec::isBufferAligned(void) const |
| { |
| const bool inputTypePacked = inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || |
| inputType == DrawTestSpec::INPUTTYPE_INT_2_10_10_10; |
| |
| // Buffer alignment, offset is a multiple of underlying data type size? |
| if (storage == STORAGE_BUFFER) |
| { |
| int dataTypeSize = gls::DrawTestSpec::inputTypeSize(inputType); |
| if (inputTypePacked) |
| dataTypeSize = 4; |
| |
| if (offset % dataTypeSize != 0) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool DrawTestSpec::AttributeSpec::isBufferStrideAligned(void) const |
| { |
| const bool inputTypePacked = inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10 || |
| inputType == DrawTestSpec::INPUTTYPE_INT_2_10_10_10; |
| |
| // Buffer alignment, offset is a multiple of underlying data type size? |
| if (storage == STORAGE_BUFFER) |
| { |
| int dataTypeSize = gls::DrawTestSpec::inputTypeSize(inputType); |
| if (inputTypePacked) |
| dataTypeSize = 4; |
| |
| if (stride % dataTypeSize != 0) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::string DrawTestSpec::targetToString(Target target) |
| { |
| static const char *targets[] = { |
| "element_array", // TARGET_ELEMENT_ARRAY = 0, |
| "array" // TARGET_ARRAY, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::TARGET_LAST>(targets, (int)target); |
| } |
| |
| std::string DrawTestSpec::inputTypeToString(InputType type) |
| { |
| static const char *types[] = { |
| "float", // INPUTTYPE_FLOAT = 0, |
| "fixed", // INPUTTYPE_FIXED, |
| "double", // INPUTTYPE_DOUBLE |
| |
| "byte", // INPUTTYPE_BYTE, |
| "short", // INPUTTYPE_SHORT, |
| |
| "unsigned_byte", // INPUTTYPE_UNSIGNED_BYTE, |
| "unsigned_short", // INPUTTYPE_UNSIGNED_SHORT, |
| |
| "int", // INPUTTYPE_INT, |
| "unsigned_int", // INPUTTYPE_UNSIGNED_INT, |
| "half", // INPUTTYPE_HALF, |
| "unsigned_int2_10_10_10", // INPUTTYPE_UNSIGNED_INT_2_10_10_10, |
| "int2_10_10_10" // INPUTTYPE_INT_2_10_10_10, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::INPUTTYPE_LAST>(types, (int)type); |
| } |
| |
| std::string DrawTestSpec::outputTypeToString(OutputType type) |
| { |
| static const char *types[] = { |
| "float", // OUTPUTTYPE_FLOAT = 0, |
| "vec2", // OUTPUTTYPE_VEC2, |
| "vec3", // OUTPUTTYPE_VEC3, |
| "vec4", // OUTPUTTYPE_VEC4, |
| |
| "int", // OUTPUTTYPE_INT, |
| "uint", // OUTPUTTYPE_UINT, |
| |
| "ivec2", // OUTPUTTYPE_IVEC2, |
| "ivec3", // OUTPUTTYPE_IVEC3, |
| "ivec4", // OUTPUTTYPE_IVEC4, |
| |
| "uvec2", // OUTPUTTYPE_UVEC2, |
| "uvec3", // OUTPUTTYPE_UVEC3, |
| "uvec4", // OUTPUTTYPE_UVEC4, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::OUTPUTTYPE_LAST>(types, (int)type); |
| } |
| |
| std::string DrawTestSpec::usageTypeToString(Usage usage) |
| { |
| static const char *usages[] = { |
| "dynamic_draw", // USAGE_DYNAMIC_DRAW = 0, |
| "static_draw", // USAGE_STATIC_DRAW, |
| "stream_draw", // USAGE_STREAM_DRAW, |
| |
| "stream_read", // USAGE_STREAM_READ, |
| "stream_copy", // USAGE_STREAM_COPY, |
| |
| "static_read", // USAGE_STATIC_READ, |
| "static_copy", // USAGE_STATIC_COPY, |
| |
| "dynamic_read", // USAGE_DYNAMIC_READ, |
| "dynamic_copy", // USAGE_DYNAMIC_COPY, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::USAGE_LAST>(usages, (int)usage); |
| } |
| |
| std::string DrawTestSpec::storageToString(Storage storage) |
| { |
| static const char *storages[] = { |
| "user_ptr", // STORAGE_USER = 0, |
| "buffer" // STORAGE_BUFFER, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::STORAGE_LAST>(storages, (int)storage); |
| } |
| |
| std::string DrawTestSpec::primitiveToString(Primitive primitive) |
| { |
| static const char *primitives[] = { |
| "points", // PRIMITIVE_POINTS , |
| "triangles", // PRIMITIVE_TRIANGLES, |
| "triangle_fan", // PRIMITIVE_TRIANGLE_FAN, |
| "triangle_strip", // PRIMITIVE_TRIANGLE_STRIP, |
| "lines", // PRIMITIVE_LINES |
| "line_strip", // PRIMITIVE_LINE_STRIP |
| "line_loop", // PRIMITIVE_LINE_LOOP |
| "lines_adjacency", // PRIMITIVE_LINES_ADJACENCY |
| "line_strip_adjacency", // PRIMITIVE_LINE_STRIP_ADJACENCY |
| "triangles_adjacency", // PRIMITIVE_TRIANGLES_ADJACENCY |
| "triangle_strip_adjacency", // PRIMITIVE_TRIANGLE_STRIP_ADJACENCY |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::PRIMITIVE_LAST>(primitives, (int)primitive); |
| } |
| |
| std::string DrawTestSpec::indexTypeToString(IndexType type) |
| { |
| static const char *indexTypes[] = { |
| "byte", // INDEXTYPE_BYTE = 0, |
| "short", // INDEXTYPE_SHORT, |
| "int", // INDEXTYPE_INT, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::INDEXTYPE_LAST>(indexTypes, (int)type); |
| } |
| |
| std::string DrawTestSpec::drawMethodToString(DrawTestSpec::DrawMethod method) |
| { |
| static const char *methods[] = { |
| "draw_arrays", //!< DRAWMETHOD_DRAWARRAYS |
| "draw_arrays_instanced", //!< DRAWMETHOD_DRAWARRAYS_INSTANCED |
| "draw_arrays_indirect", //!< DRAWMETHOD_DRAWARRAYS_INDIRECT |
| "draw_elements", //!< DRAWMETHOD_DRAWELEMENTS |
| "draw_range_elements", //!< DRAWMETHOD_DRAWELEMENTS_RANGED |
| "draw_elements_instanced", //!< DRAWMETHOD_DRAWELEMENTS_INSTANCED |
| "draw_elements_indirect", //!< DRAWMETHOD_DRAWELEMENTS_INDIRECT |
| "draw_elements_base_vertex", //!< DRAWMETHOD_DRAWELEMENTS_BASEVERTEX, |
| "draw_elements_instanced_base_vertex", //!< DRAWMETHOD_DRAWELEMENTS_INSTANCED_BASEVERTEX, |
| "draw_range_elements_base_vertex", //!< DRAWMETHOD_DRAWELEMENTS_RANGED_BASEVERTEX, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::DRAWMETHOD_LAST>(methods, (int)method); |
| } |
| |
| int DrawTestSpec::inputTypeSize(InputType type) |
| { |
| static const int size[] = { |
| (int)sizeof(float), // INPUTTYPE_FLOAT = 0, |
| (int)sizeof(int32_t), // INPUTTYPE_FIXED, |
| (int)sizeof(double), // INPUTTYPE_DOUBLE |
| |
| (int)sizeof(int8_t), // INPUTTYPE_BYTE, |
| (int)sizeof(int16_t), // INPUTTYPE_SHORT, |
| |
| (int)sizeof(uint8_t), // INPUTTYPE_UNSIGNED_BYTE, |
| (int)sizeof(uint16_t), // INPUTTYPE_UNSIGNED_SHORT, |
| |
| (int)sizeof(int32_t), // INPUTTYPE_INT, |
| (int)sizeof(uint32_t), // INPUTTYPE_UNSIGNED_INT, |
| (int)sizeof(deFloat16), // INPUTTYPE_HALF, |
| (int)sizeof(uint32_t) / 4, // INPUTTYPE_UNSIGNED_INT_2_10_10_10, |
| (int)sizeof(uint32_t) / 4 // INPUTTYPE_INT_2_10_10_10, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::INPUTTYPE_LAST>(size, (int)type); |
| } |
| |
| int DrawTestSpec::indexTypeSize(IndexType type) |
| { |
| static const int size[] = { |
| sizeof(uint8_t), // INDEXTYPE_BYTE, |
| sizeof(uint16_t), // INDEXTYPE_SHORT, |
| sizeof(uint32_t), // INDEXTYPE_INT, |
| }; |
| |
| return de::getSizedArrayElement<DrawTestSpec::INDEXTYPE_LAST>(size, (int)type); |
| } |
| |
| std::string DrawTestSpec::getName(void) const |
| { |
| const MethodInfo methodInfo = getMethodInfo(drawMethod); |
| const bool hasFirst = methodInfo.first; |
| const bool instanced = methodInfo.instanced; |
| const bool ranged = methodInfo.ranged; |
| const bool indexed = methodInfo.indexed; |
| |
| std::stringstream name; |
| |
| for (size_t ndx = 0; ndx < attribs.size(); ++ndx) |
| { |
| const AttributeSpec &attrib = attribs[ndx]; |
| |
| if (attribs.size() > 1) |
| name << "attrib" << ndx << "_"; |
| |
| if (ndx == 0 || attrib.additionalPositionAttribute) |
| name << "pos_"; |
| else |
| name << "col_"; |
| |
| if (attrib.useDefaultAttribute) |
| { |
| name << "non_array_" << DrawTestSpec::inputTypeToString((DrawTestSpec::InputType)attrib.inputType) << "_" |
| << attrib.componentCount << "_" << DrawTestSpec::outputTypeToString(attrib.outputType) << "_"; |
| } |
| else |
| { |
| name << DrawTestSpec::storageToString(attrib.storage) << "_" << attrib.offset << "_" << attrib.stride << "_" |
| << DrawTestSpec::inputTypeToString((DrawTestSpec::InputType)attrib.inputType); |
| if (attrib.inputType != DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10 && |
| attrib.inputType != DrawTestSpec::INPUTTYPE_INT_2_10_10_10) |
| name << attrib.componentCount; |
| name << "_" << (attrib.normalize ? "normalized_" : "") |
| << DrawTestSpec::outputTypeToString(attrib.outputType) << "_" |
| << DrawTestSpec::usageTypeToString(attrib.usage) << "_" << attrib.instanceDivisor << "_"; |
| } |
| } |
| |
| if (indexed) |
| name << "index_" << DrawTestSpec::indexTypeToString(indexType) << "_" |
| << DrawTestSpec::storageToString(indexStorage) << "_" |
| << "offset" << indexPointerOffset << "_"; |
| if (hasFirst) |
| name << "first" << first << "_"; |
| if (ranged) |
| name << "ranged_" << indexMin << "_" << indexMax << "_"; |
| if (instanced) |
| name << "instances" << instanceCount << "_"; |
| |
| switch (primitive) |
| { |
| case DrawTestSpec::PRIMITIVE_POINTS: |
| name << "points_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES: |
| name << "triangles_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_FAN: |
| name << "triangle_fan_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP: |
| name << "triangle_strip_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINES: |
| name << "lines_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP: |
| name << "line_strip_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_LOOP: |
| name << "line_loop_"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINES_ADJACENCY: |
| name << "line_adjancency"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP_ADJACENCY: |
| name << "line_strip_adjancency"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES_ADJACENCY: |
| name << "triangles_adjancency"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP_ADJACENCY: |
| name << "triangle_strip_adjancency"; |
| break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| name << primitiveCount; |
| |
| return name.str(); |
| } |
| |
| std::string DrawTestSpec::getDesc(void) const |
| { |
| std::stringstream desc; |
| |
| for (size_t ndx = 0; ndx < attribs.size(); ++ndx) |
| { |
| const AttributeSpec &attrib = attribs[ndx]; |
| |
| if (attrib.useDefaultAttribute) |
| { |
| desc << "Attribute " << ndx << ": default, " |
| << ((ndx == 0 || attrib.additionalPositionAttribute) ? ("position ,") : ("color ,")) |
| << "input datatype " << DrawTestSpec::inputTypeToString((DrawTestSpec::InputType)attrib.inputType) |
| << ", " |
| << "input component count " << attrib.componentCount << ", " |
| << "used as " << DrawTestSpec::outputTypeToString(attrib.outputType) << ", "; |
| } |
| else |
| { |
| desc << "Attribute " << ndx << ": " |
| << ((ndx == 0 || attrib.additionalPositionAttribute) ? ("position ,") : ("color ,")) << "Storage in " |
| << DrawTestSpec::storageToString(attrib.storage) << ", " |
| << "stride " << attrib.stride << ", " |
| << "input datatype " << DrawTestSpec::inputTypeToString((DrawTestSpec::InputType)attrib.inputType) |
| << ", " |
| << "input component count " << attrib.componentCount << ", " |
| << (attrib.normalize ? "normalized, " : "") << "used as " |
| << DrawTestSpec::outputTypeToString(attrib.outputType) << ", " |
| << "instance divisor " << attrib.instanceDivisor << ", "; |
| } |
| } |
| |
| if (drawMethod == DRAWMETHOD_DRAWARRAYS) |
| { |
| desc << "drawArrays(), " |
| << "first " << first << ", "; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWARRAYS_INSTANCED) |
| { |
| desc << "drawArraysInstanced(), " |
| << "first " << first << ", " |
| << "instance count " << instanceCount << ", "; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS) |
| { |
| desc << "drawElements(), " |
| << "index type " << DrawTestSpec::indexTypeToString(indexType) << ", " |
| << "index storage in " << DrawTestSpec::storageToString(indexStorage) << ", " |
| << "index offset " << indexPointerOffset << ", "; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_RANGED) |
| { |
| desc << "drawElementsRanged(), " |
| << "index type " << DrawTestSpec::indexTypeToString(indexType) << ", " |
| << "index storage in " << DrawTestSpec::storageToString(indexStorage) << ", " |
| << "index offset " << indexPointerOffset << ", " |
| << "range start " << indexMin << ", " |
| << "range end " << indexMax << ", "; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_INSTANCED) |
| { |
| desc << "drawElementsInstanced(), " |
| << "index type " << DrawTestSpec::indexTypeToString(indexType) << ", " |
| << "index storage in " << DrawTestSpec::storageToString(indexStorage) << ", " |
| << "index offset " << indexPointerOffset << ", " |
| << "instance count " << instanceCount << ", "; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWARRAYS_INDIRECT) |
| { |
| desc << "drawArraysIndirect(), " |
| << "first " << first << ", " |
| << "instance count " << instanceCount << ", " |
| << "indirect offset " << indirectOffset << ", "; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_INDIRECT) |
| { |
| desc << "drawElementsIndirect(), " |
| << "index type " << DrawTestSpec::indexTypeToString(indexType) << ", " |
| << "index storage in " << DrawTestSpec::storageToString(indexStorage) << ", " |
| << "index offset " << indexPointerOffset << ", " |
| << "instance count " << instanceCount << ", " |
| << "indirect offset " << indirectOffset << ", " |
| << "base vertex " << baseVertex << ", "; |
| } |
| else |
| DE_ASSERT(false); |
| |
| desc << primitiveCount; |
| |
| switch (primitive) |
| { |
| case DrawTestSpec::PRIMITIVE_POINTS: |
| desc << "points"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES: |
| desc << "triangles"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_FAN: |
| desc << "triangles (fan)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP: |
| desc << "triangles (strip)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINES: |
| desc << "lines"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP: |
| desc << "lines (strip)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_LOOP: |
| desc << "lines (loop)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINES_ADJACENCY: |
| desc << "lines (adjancency)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP_ADJACENCY: |
| desc << "lines (strip, adjancency)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES_ADJACENCY: |
| desc << "triangles (adjancency)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP_ADJACENCY: |
| desc << "triangles (strip, adjancency)"; |
| break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| return desc.str(); |
| } |
| |
| std::string DrawTestSpec::getMultilineDesc(void) const |
| { |
| std::stringstream desc; |
| |
| for (size_t ndx = 0; ndx < attribs.size(); ++ndx) |
| { |
| const AttributeSpec &attrib = attribs[ndx]; |
| |
| if (attrib.useDefaultAttribute) |
| { |
| desc << "Attribute " << ndx << ": default, " |
| << ((ndx == 0 || attrib.additionalPositionAttribute) ? ("position\n") : ("color\n")) |
| << "\tinput datatype " << DrawTestSpec::inputTypeToString((DrawTestSpec::InputType)attrib.inputType) |
| << "\n" |
| << "\tinput component count " << attrib.componentCount << "\n" |
| << "\tused as " << DrawTestSpec::outputTypeToString(attrib.outputType) << "\n"; |
| } |
| else |
| { |
| desc << "Attribute " << ndx << ": " |
| << ((ndx == 0 || attrib.additionalPositionAttribute) ? ("position\n") : ("color\n")) << "\tStorage in " |
| << DrawTestSpec::storageToString(attrib.storage) << "\n" |
| << "\tstride " << attrib.stride << "\n" |
| << "\tinput datatype " << DrawTestSpec::inputTypeToString((DrawTestSpec::InputType)attrib.inputType) |
| << "\n" |
| << "\tinput component count " << attrib.componentCount << "\n" |
| << (attrib.normalize ? "\tnormalized\n" : "") << "\tused as " |
| << DrawTestSpec::outputTypeToString(attrib.outputType) << "\n" |
| << "\tinstance divisor " << attrib.instanceDivisor << "\n"; |
| } |
| } |
| |
| if (drawMethod == DRAWMETHOD_DRAWARRAYS) |
| { |
| desc << "drawArrays()\n" |
| << "\tfirst " << first << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWARRAYS_INSTANCED) |
| { |
| desc << "drawArraysInstanced()\n" |
| << "\tfirst " << first << "\n" |
| << "\tinstance count " << instanceCount << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS) |
| { |
| desc << "drawElements()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_RANGED) |
| { |
| desc << "drawElementsRanged()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n" |
| << "\trange start " << indexMin << "\n" |
| << "\trange end " << indexMax << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_INSTANCED) |
| { |
| desc << "drawElementsInstanced()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n" |
| << "\tinstance count " << instanceCount << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWARRAYS_INDIRECT) |
| { |
| desc << "drawArraysIndirect()\n" |
| << "\tfirst " << first << "\n" |
| << "\tinstance count " << instanceCount << "\n" |
| << "\tindirect offset " << indirectOffset << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_INDIRECT) |
| { |
| desc << "drawElementsIndirect()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n" |
| << "\tinstance count " << instanceCount << "\n" |
| << "\tindirect offset " << indirectOffset << "\n" |
| << "\tbase vertex " << baseVertex << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_BASEVERTEX) |
| { |
| desc << "drawElementsBaseVertex()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n" |
| << "\tbase vertex " << baseVertex << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_INSTANCED_BASEVERTEX) |
| { |
| desc << "drawElementsInstancedBaseVertex()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n" |
| << "\tinstance count " << instanceCount << "\n" |
| << "\tbase vertex " << baseVertex << "\n"; |
| } |
| else if (drawMethod == DRAWMETHOD_DRAWELEMENTS_RANGED_BASEVERTEX) |
| { |
| desc << "drawRangeElementsBaseVertex()\n" |
| << "\tindex type " << DrawTestSpec::indexTypeToString(indexType) << "\n" |
| << "\tindex storage in " << DrawTestSpec::storageToString(indexStorage) << "\n" |
| << "\tindex offset " << indexPointerOffset << "\n" |
| << "\tbase vertex " << baseVertex << "\n" |
| << "\trange start " << indexMin << "\n" |
| << "\trange end " << indexMax << "\n"; |
| } |
| else |
| DE_ASSERT(false); |
| |
| desc << "\t" << primitiveCount << " "; |
| |
| switch (primitive) |
| { |
| case DrawTestSpec::PRIMITIVE_POINTS: |
| desc << "points"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES: |
| desc << "triangles"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_FAN: |
| desc << "triangles (fan)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP: |
| desc << "triangles (strip)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINES: |
| desc << "lines"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP: |
| desc << "lines (strip)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_LOOP: |
| desc << "lines (loop)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINES_ADJACENCY: |
| desc << "lines (adjancency)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_LINE_STRIP_ADJACENCY: |
| desc << "lines (strip, adjancency)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLES_ADJACENCY: |
| desc << "triangles (adjancency)"; |
| break; |
| case DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP_ADJACENCY: |
| desc << "triangles (strip, adjancency)"; |
| break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| |
| desc << "\n"; |
| |
| return desc.str(); |
| } |
| |
| DrawTestSpec::DrawTestSpec(void) |
| { |
| primitive = PRIMITIVE_LAST; |
| primitiveCount = 0; |
| drawMethod = DRAWMETHOD_LAST; |
| indexType = INDEXTYPE_LAST; |
| indexPointerOffset = 0; |
| indexStorage = STORAGE_LAST; |
| first = 0; |
| indexMin = 0; |
| indexMax = 0; |
| instanceCount = 0; |
| indirectOffset = 0; |
| baseVertex = 0; |
| } |
| |
| int DrawTestSpec::hash(void) const |
| { |
| // Use only drawmode-relevant values in "hashing" as the unrelevant values might not be set (causing non-deterministic behavior). |
| const MethodInfo methodInfo = getMethodInfo(drawMethod); |
| const bool arrayed = methodInfo.first; |
| const bool instanced = methodInfo.instanced; |
| const bool ranged = methodInfo.ranged; |
| const bool indexed = methodInfo.indexed; |
| const bool indirect = methodInfo.indirect; |
| const bool hasBaseVtx = methodInfo.baseVertex; |
| |
| const int indexHash = (!indexed) ? (0) : (int(indexType) + 10 * indexPointerOffset + 100 * int(indexStorage)); |
| const int arrayHash = (!arrayed) ? (0) : (first); |
| const int indexRangeHash = (!ranged) ? (0) : (indexMin + 10 * indexMax); |
| const int instanceHash = (!instanced) ? (0) : (instanceCount); |
| const int indirectHash = (!indirect) ? (0) : (indirectOffset); |
| const int baseVtxHash = (!hasBaseVtx) ? (0) : (baseVertex); |
| const int basicHash = int(primitive) + 10 * primitiveCount + 100 * int(drawMethod); |
| |
| return indexHash + 3 * arrayHash + 5 * indexRangeHash + 7 * instanceHash + 13 * basicHash + |
| 17 * (int)attribs.size() + 19 * primitiveCount + 23 * indirectHash + 27 * baseVtxHash; |
| } |
| |
| bool DrawTestSpec::valid(void) const |
| { |
| DE_ASSERT(apiType.getProfile() != glu::PROFILE_LAST); |
| DE_ASSERT(primitive != PRIMITIVE_LAST); |
| DE_ASSERT(drawMethod != DRAWMETHOD_LAST); |
| |
| const MethodInfo methodInfo = getMethodInfo(drawMethod); |
| |
| for (int ndx = 0; ndx < (int)attribs.size(); ++ndx) |
| if (!attribs[ndx].valid(apiType)) |
| return false; |
| |
| if (methodInfo.ranged) |
| { |
| uint32_t maxIndexValue = 0; |
| if (indexType == INDEXTYPE_BYTE) |
| maxIndexValue = GLValue::getMaxValue(INPUTTYPE_UNSIGNED_BYTE).ub.getValue(); |
| else if (indexType == INDEXTYPE_SHORT) |
| maxIndexValue = GLValue::getMaxValue(INPUTTYPE_UNSIGNED_SHORT).us.getValue(); |
| else if (indexType == INDEXTYPE_INT) |
| maxIndexValue = GLValue::getMaxValue(INPUTTYPE_UNSIGNED_INT).ui.getValue(); |
| else |
| DE_ASSERT(false); |
| |
| if (indexMin > indexMax) |
| return false; |
| if (indexMin < 0 || indexMax < 0) |
| return false; |
| if ((uint32_t)indexMin > maxIndexValue || (uint32_t)indexMax > maxIndexValue) |
| return false; |
| } |
| |
| if (methodInfo.first && first < 0) |
| return false; |
| |
| // GLES2 limits |
| if (apiType == glu::ApiType::es(2, 0)) |
| { |
| if (drawMethod != gls::DrawTestSpec::DRAWMETHOD_DRAWARRAYS && |
| drawMethod != gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS) |
| return false; |
| if (drawMethod == gls::DrawTestSpec::DRAWMETHOD_DRAWELEMENTS && |
| (indexType != INDEXTYPE_BYTE && indexType != INDEXTYPE_SHORT)) |
| return false; |
| } |
| |
| // Indirect limitations |
| if (methodInfo.indirect) |
| { |
| // Indirect offset alignment |
| if (indirectOffset % 4 != 0) |
| return false; |
| |
| // All attribute arrays must be stored in a buffer |
| for (int ndx = 0; ndx < (int)attribs.size(); ++ndx) |
| if (!attribs[ndx].useDefaultAttribute && attribs[ndx].storage == gls::DrawTestSpec::STORAGE_USER) |
| return false; |
| } |
| if (drawMethod == DRAWMETHOD_DRAWELEMENTS_INDIRECT) |
| { |
| // index offset must be convertable to firstIndex |
| if (indexPointerOffset % gls::DrawTestSpec::indexTypeSize(indexType) != 0) |
| return false; |
| |
| // Indices must be in a buffer |
| if (indexStorage != STORAGE_BUFFER) |
| return false; |
| } |
| |
| // Do not allow user pointer in GL core |
| if (apiType.getProfile() == glu::PROFILE_CORE) |
| { |
| if (methodInfo.indexed && indexStorage == DrawTestSpec::STORAGE_USER) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| DrawTestSpec::CompatibilityTestType DrawTestSpec::isCompatibilityTest(void) const |
| { |
| const MethodInfo methodInfo = getMethodInfo(drawMethod); |
| |
| bool bufferAlignmentBad = false; |
| bool strideAlignmentBad = false; |
| |
| // Attribute buffer alignment |
| for (int ndx = 0; ndx < (int)attribs.size(); ++ndx) |
| if (!attribs[ndx].isBufferAligned()) |
| bufferAlignmentBad = true; |
| |
| // Attribute stride alignment |
| for (int ndx = 0; ndx < (int)attribs.size(); ++ndx) |
| if (!attribs[ndx].isBufferStrideAligned()) |
| strideAlignmentBad = true; |
| |
| // Index buffer alignment |
| if (methodInfo.indexed) |
| { |
| if (indexStorage == STORAGE_BUFFER) |
| { |
| int indexSize = 0; |
| if (indexType == INDEXTYPE_BYTE) |
| indexSize = 1; |
| else if (indexType == INDEXTYPE_SHORT) |
| indexSize = 2; |
| else if (indexType == INDEXTYPE_INT) |
| indexSize = 4; |
| else |
| DE_ASSERT(false); |
| |
| if (indexPointerOffset % indexSize != 0) |
| bufferAlignmentBad = true; |
| } |
| } |
| |
| // \note combination bad alignment & stride is treated as bad offset |
| if (bufferAlignmentBad) |
| return COMPATIBILITY_UNALIGNED_OFFSET; |
| else if (strideAlignmentBad) |
| return COMPATIBILITY_UNALIGNED_STRIDE; |
| else |
| return COMPATIBILITY_NONE; |
| } |
| |
| enum PrimitiveClass |
| { |
| PRIMITIVECLASS_POINT = 0, |
| PRIMITIVECLASS_LINE, |
| PRIMITIVECLASS_TRIANGLE, |
| |
| PRIMITIVECLASS_LAST |
| }; |
| |
| static PrimitiveClass getDrawPrimitiveClass(gls::DrawTestSpec::Primitive primitiveType) |
| { |
| switch (primitiveType) |
| { |
| case gls::DrawTestSpec::PRIMITIVE_POINTS: |
| return PRIMITIVECLASS_POINT; |
| |
| case gls::DrawTestSpec::PRIMITIVE_LINES: |
| case gls::DrawTestSpec::PRIMITIVE_LINE_STRIP: |
| case gls::DrawTestSpec::PRIMITIVE_LINE_LOOP: |
| case gls::DrawTestSpec::PRIMITIVE_LINES_ADJACENCY: |
| case gls::DrawTestSpec::PRIMITIVE_LINE_STRIP_ADJACENCY: |
| return PRIMITIVECLASS_LINE; |
| |
| case gls::DrawTestSpec::PRIMITIVE_TRIANGLES: |
| case gls::DrawTestSpec::PRIMITIVE_TRIANGLE_FAN: |
| case gls::DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP: |
| case gls::DrawTestSpec::PRIMITIVE_TRIANGLES_ADJACENCY: |
| case gls::DrawTestSpec::PRIMITIVE_TRIANGLE_STRIP_ADJACENCY: |
| return PRIMITIVECLASS_TRIANGLE; |
| |
| default: |
| DE_ASSERT(false); |
| return PRIMITIVECLASS_LAST; |
| } |
| } |
| |
| static bool containsLineCases(const std::vector<DrawTestSpec> &m_specs) |
| { |
| for (int ndx = 0; ndx < (int)m_specs.size(); ++ndx) |
| { |
| if (getDrawPrimitiveClass(m_specs[ndx].primitive) == PRIMITIVECLASS_LINE) |
| return true; |
| } |
| return false; |
| } |
| |
| // DrawTest |
| |
| DrawTest::DrawTest(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const DrawTestSpec &spec, const char *name, |
| const char *desc) |
| : TestCase(testCtx, name, desc) |
| , m_renderCtx(renderCtx) |
| , m_contextInfo(DE_NULL) |
| , m_refBuffers(DE_NULL) |
| , m_refContext(DE_NULL) |
| , m_glesContext(DE_NULL) |
| , m_glArrayPack(DE_NULL) |
| , m_rrArrayPack(DE_NULL) |
| , m_maxDiffRed(-1) |
| , m_maxDiffGreen(-1) |
| , m_maxDiffBlue(-1) |
| , m_iteration(0) |
| , m_result() // \note no per-iteration result logging (only one iteration) |
| { |
| addIteration(spec); |
| } |
| |
| DrawTest::DrawTest(tcu::TestContext &testCtx, glu::RenderContext &renderCtx, const char *name, const char *desc) |
| : TestCase(testCtx, name, desc) |
| , m_renderCtx(renderCtx) |
| , m_contextInfo(DE_NULL) |
| , m_refBuffers(DE_NULL) |
| , m_refContext(DE_NULL) |
| , m_glesContext(DE_NULL) |
| , m_glArrayPack(DE_NULL) |
| , m_rrArrayPack(DE_NULL) |
| , m_maxDiffRed(-1) |
| , m_maxDiffGreen(-1) |
| , m_maxDiffBlue(-1) |
| , m_iteration(0) |
| , m_result(testCtx.getLog(), "Iteration result: ") |
| { |
| } |
| |
| DrawTest::~DrawTest(void) |
| { |
| deinit(); |
| } |
| |
| void DrawTest::addIteration(const DrawTestSpec &spec, const char *description) |
| { |
| // Validate spec |
| const bool validSpec = spec.valid(); |
| DE_ASSERT(validSpec); |
| |
| if (!validSpec) |
| return; |
| |
| // Check the context type is the same with other iterations |
| if (!m_specs.empty()) |
| { |
| const bool validContext = m_specs[0].apiType == spec.apiType; |
| DE_ASSERT(validContext); |
| |
| if (!validContext) |
| return; |
| } |
| |
| m_specs.push_back(spec); |
| |
| if (description) |
| m_iteration_descriptions.push_back(std::string(description)); |
| else |
| m_iteration_descriptions.push_back(std::string()); |
| } |
| |
| void DrawTest::init(void) |
| { |
| DE_ASSERT(!m_specs.empty()); |
| DE_ASSERT(contextSupports(m_renderCtx.getType(), m_specs[0].apiType)); |
| |
| const int renderTargetWidth = de::min(MAX_RENDER_TARGET_SIZE, m_renderCtx.getRenderTarget().getWidth()); |
| const int renderTargetHeight = de::min(MAX_RENDER_TARGET_SIZE, m_renderCtx.getRenderTarget().getHeight()); |
| |
| // lines have significantly different rasterization in MSAA mode |
| const bool isLineCase = containsLineCases(m_specs); |
| const bool isMSAACase = m_renderCtx.getRenderTarget().getNumSamples() > 1; |
| const int renderTargetSamples = (isMSAACase && isLineCase) ? (4) : (1); |
| |
| sglr::ReferenceContextLimits limits(m_renderCtx); |
| bool useVao = false; |
| |
| m_glesContext = |
| new sglr::GLContext(m_renderCtx, m_testCtx.getLog(), sglr::GLCONTEXT_LOG_CALLS | sglr::GLCONTEXT_LOG_PROGRAMS, |
| tcu::IVec4(0, 0, renderTargetWidth, renderTargetHeight)); |
| |
| if (m_renderCtx.getType().getAPI() == glu::ApiType::es(2, 0) || |
| m_renderCtx.getType().getAPI() == glu::ApiType::es(3, 0)) |
| useVao = false; |
| else if (contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 1)) || |
| glu::isContextTypeGLCore(m_renderCtx.getType())) |
| useVao = true; |
| else |
| DE_FATAL("Unknown context type"); |
| |
| m_refBuffers = new sglr::ReferenceContextBuffers(m_renderCtx.getRenderTarget().getPixelFormat(), 0, 0, |
| renderTargetWidth, renderTargetHeight, renderTargetSamples); |
| m_refContext = new sglr::ReferenceContext(limits, m_refBuffers->getColorbuffer(), m_refBuffers->getDepthbuffer(), |
| m_refBuffers->getStencilbuffer()); |
| |
| m_glArrayPack = new AttributePack(m_testCtx, m_renderCtx, *m_glesContext, |
| tcu::UVec2(renderTargetWidth, renderTargetHeight), useVao, true); |
| m_rrArrayPack = new AttributePack(m_testCtx, m_renderCtx, *m_refContext, |
| tcu::UVec2(renderTargetWidth, renderTargetHeight), useVao, false); |
| |
| m_maxDiffRed = |
| deCeilFloatToInt32(256.0f * (6.0f / (float)(1 << m_renderCtx.getRenderTarget().getPixelFormat().redBits))); |
| m_maxDiffGreen = |
| deCeilFloatToInt32(256.0f * (6.0f / (float)(1 << m_renderCtx.getRenderTarget().getPixelFormat().greenBits))); |
| m_maxDiffBlue = |
| deCeilFloatToInt32(256.0f * (6.0f / (float)(1 << m_renderCtx.getRenderTarget().getPixelFormat().blueBits))); |
| m_contextInfo = glu::ContextInfo::create(m_renderCtx); |
| } |
| |
| void DrawTest::deinit(void) |
| { |
| delete m_glArrayPack; |
| delete m_rrArrayPack; |
| delete m_refBuffers; |
| delete m_refContext; |
| delete m_glesContext; |
| delete m_contextInfo; |
| |
| m_glArrayPack = DE_NULL; |
| m_rrArrayPack = DE_NULL; |
| m_refBuffers = DE_NULL; |
| m_refContext = DE_NULL; |
| m_glesContext = DE_NULL; |
| m_contextInfo = DE_NULL; |
| } |
| |
| DrawTest::IterateResult DrawTest::iterate(void) |
| { |
| const int specNdx = (m_iteration / 2); |
| const DrawTestSpec &spec = m_specs[specNdx]; |
| |
| if (spec.drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_BASEVERTEX || |
| spec.drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_INSTANCED_BASEVERTEX || |
| spec.drawMethod == DrawTestSpec::DRAWMETHOD_DRAWELEMENTS_RANGED_BASEVERTEX) |
| { |
| const bool supportsES32orGL45 = contextSupports(m_renderCtx.getType(), glu::ApiType::es(3, 2)) || |
| contextSupports(m_renderCtx.getType(), glu::ApiType::core(4, 5)); |
| TCU_CHECK_AND_THROW(NotSupportedError, |
| supportsES32orGL45 || |
| m_contextInfo->isExtensionSupported("GL_EXT_draw_elements_base_vertex"), |
| "GL_EXT_draw_elements_base_vertex is not supported."); |
| } |
| |
| const bool drawStep = (m_iteration % 2) == 0; |
| const bool compareStep = (m_iteration % 2) == 1; |
| const IterateResult iterateResult = ((size_t)m_iteration + 1 == m_specs.size() * 2) ? (STOP) : (CONTINUE); |
| const bool updateProgram = |
| (m_iteration == 0) || |
| (drawStep && !checkSpecsShaderCompatible(m_specs[specNdx], |
| m_specs[specNdx - 1])); // try to use the same shader in all iterations |
| IterationLogSectionEmitter sectionEmitter(m_testCtx.getLog(), specNdx, m_specs.size(), |
| m_iteration_descriptions[specNdx], drawStep && m_specs.size() != 1); |
| |
| if (drawStep) |
| { |
| const MethodInfo methodInfo = getMethodInfo(spec.drawMethod); |
| const bool indexed = methodInfo.indexed; |
| const bool instanced = methodInfo.instanced; |
| const bool ranged = methodInfo.ranged; |
| const bool hasFirst = methodInfo.first; |
| const bool hasBaseVtx = methodInfo.baseVertex; |
| |
| const size_t primitiveElementCount = |
| getElementCount(spec.primitive, spec.primitiveCount); // !< elements to be drawn |
| const int indexMin = (ranged) ? (spec.indexMin) : (0); |
| const int firstAddition = (hasFirst) ? (spec.first) : (0); |
| const int baseVertexAddition = (hasBaseVtx && spec.baseVertex > 0) ? |
| (spec.baseVertex) : |
| (0); // spec.baseVertex > 0 => Create bigger attribute buffer |
| const int indexBase = (hasBaseVtx && spec.baseVertex < 0) ? (-spec.baseVertex) : |
| (0); // spec.baseVertex < 0 => Create bigger indices |
| const size_t elementCount = |
| primitiveElementCount + indexMin + firstAddition + |
| baseVertexAddition; // !< elements in buffer (buffer should have at least primitiveElementCount ACCESSIBLE (index range, first) elements) |
| const int maxElementIndex = (int)primitiveElementCount + indexMin + firstAddition - 1; |
| const int indexMax = |
| de::max(0, (ranged) ? (de::clamp<int>(spec.indexMax, 0, maxElementIndex)) : (maxElementIndex)); |
| float coordScale = getCoordScale(spec); |
| float colorScale = getColorScale(spec); |
| |
| rr::GenericVec4 nullAttribValue; |
| |
| // Log info |
| m_testCtx.getLog() << TestLog::Message << spec.getMultilineDesc() << TestLog::EndMessage; |
| m_testCtx.getLog() << TestLog::Message << TestLog::EndMessage; // extra line for clarity |
| |
| // Data |
| |
| m_glArrayPack->clearArrays(); |
| m_rrArrayPack->clearArrays(); |
| |
| for (int attribNdx = 0; attribNdx < (int)spec.attribs.size(); attribNdx++) |
| { |
| DrawTestSpec::AttributeSpec attribSpec = spec.attribs[attribNdx]; |
| const bool isPositionAttr = (attribNdx == 0) || (attribSpec.additionalPositionAttribute); |
| |
| if (attribSpec.useDefaultAttribute) |
| { |
| const int seed = 10 * attribSpec.hash() + 100 * spec.hash() + attribNdx; |
| rr::GenericVec4 attribValue = RandomArrayGenerator::generateAttributeValue(seed, attribSpec.inputType); |
| |
| m_glArrayPack->newArray(DrawTestSpec::STORAGE_USER); |
| m_rrArrayPack->newArray(DrawTestSpec::STORAGE_USER); |
| |
| m_glArrayPack->getArray(attribNdx)->setupArray(false, 0, attribSpec.componentCount, |
| attribSpec.inputType, attribSpec.outputType, false, 0, 0, |
| attribValue, isPositionAttr, false); |
| m_rrArrayPack->getArray(attribNdx)->setupArray(false, 0, attribSpec.componentCount, |
| attribSpec.inputType, attribSpec.outputType, false, 0, 0, |
| attribValue, isPositionAttr, false); |
| } |
| else |
| { |
| const int seed = attribSpec.hash() + 100 * spec.hash() + attribNdx; |
| const size_t elementSize = |
| attribSpec.componentCount * DrawTestSpec::inputTypeSize(attribSpec.inputType); |
| const size_t stride = (attribSpec.stride == 0) ? (elementSize) : (attribSpec.stride); |
| const size_t evaluatedElementCount = (instanced && attribSpec.instanceDivisor > 0) ? |
| (spec.instanceCount / attribSpec.instanceDivisor + 1) : |
| (elementCount); |
| const size_t referencedElementCount = |
| (ranged) ? (de::max<size_t>(evaluatedElementCount, spec.indexMax + 1)) : (evaluatedElementCount); |
| const size_t bufferSize = attribSpec.offset + stride * (referencedElementCount - 1) + elementSize; |
| const char *data = |
| RandomArrayGenerator::generateArray(seed, (int)referencedElementCount, attribSpec.componentCount, |
| attribSpec.offset, (int)stride, attribSpec.inputType); |
| |
| try |
| { |
| m_glArrayPack->newArray(attribSpec.storage); |
| m_rrArrayPack->newArray(attribSpec.storage); |
| |
| m_glArrayPack->getArray(attribNdx)->data(DrawTestSpec::TARGET_ARRAY, bufferSize, data, |
| attribSpec.usage); |
| m_rrArrayPack->getArray(attribNdx)->data(DrawTestSpec::TARGET_ARRAY, bufferSize, data, |
| attribSpec.usage); |
| |
| m_glArrayPack->getArray(attribNdx)->setupArray( |
| true, attribSpec.offset, attribSpec.componentCount, attribSpec.inputType, attribSpec.outputType, |
| attribSpec.normalize, attribSpec.stride, attribSpec.instanceDivisor, nullAttribValue, |
| isPositionAttr, attribSpec.bgraComponentOrder); |
| m_rrArrayPack->getArray(attribNdx)->setupArray( |
| true, attribSpec.offset, attribSpec.componentCount, attribSpec.inputType, attribSpec.outputType, |
| attribSpec.normalize, attribSpec.stride, attribSpec.instanceDivisor, nullAttribValue, |
| isPositionAttr, attribSpec.bgraComponentOrder); |
| |
| delete[] data; |
| data = NULL; |
| } |
| catch (...) |
| { |
| delete[] data; |
| throw; |
| } |
| } |
| } |
| |
| // Shader program |
| if (updateProgram) |
| { |
| m_glArrayPack->updateProgram(); |
| m_rrArrayPack->updateProgram(); |
| } |
| |
| // Draw |
| try |
| { |
| // indices |
| if (indexed) |
| { |
| const int seed = spec.hash(); |
| const size_t indexElementSize = DrawTestSpec::indexTypeSize(spec.indexType); |
| const size_t indexArraySize = spec.indexPointerOffset + indexElementSize * elementCount; |
| const char *indexArray = RandomArrayGenerator::generateIndices( |
| seed, (int)elementCount, spec.indexType, spec.indexPointerOffset, indexMin, indexMax, indexBase); |
| const char *indexPointerBase = |
| (spec.indexStorage == DrawTestSpec::STORAGE_USER) ? (indexArray) : ((char *)DE_NULL); |
| const char *indexPointer = indexPointerBase + spec.indexPointerOffset; |
| |
| de::UniquePtr<AttributeArray> glArray(new AttributeArray(spec.indexStorage, *m_glesContext)); |
| de::UniquePtr<AttributeArray> rrArray(new AttributeArray(spec.indexStorage, *m_refContext)); |
| |
| try |
| { |
| glArray->data(DrawTestSpec::TARGET_ELEMENT_ARRAY, indexArraySize, indexArray, |
| DrawTestSpec::USAGE_STATIC_DRAW); |
| rrArray->data(DrawTestSpec::TARGET_ELEMENT_ARRAY, indexArraySize, indexArray, |
| DrawTestSpec::USAGE_STATIC_DRAW); |
| |
| m_glArrayPack->render(spec.primitive, spec.drawMethod, 0, (int)primitiveElementCount, |
| spec.indexType, indexPointer, spec.indexMin, spec.indexMax, |
| spec.instanceCount, spec.indirectOffset, spec.baseVertex, coordScale, |
| colorScale, glArray.get()); |
| m_rrArrayPack->render(spec.primitive, spec.drawMethod, 0, (int)primitiveElementCount, |
| spec.indexType, indexPointer, spec.indexMin, spec.indexMax, |
| spec.instanceCount, spec.indirectOffset, spec.baseVertex, coordScale, |
| colorScale, rrArray.get()); |
| |
| delete[] indexArray; |
| indexArray = NULL; |
| } |
| catch (...) |
| { |
| delete[] indexArray; |
| throw; |
| } |
| } |
| else |
| { |
| m_glArrayPack->render(spec.primitive, spec.drawMethod, spec.first, (int)primitiveElementCount, |
| DrawTestSpec::INDEXTYPE_LAST, DE_NULL, 0, 0, spec.instanceCount, |
| spec.indirectOffset, 0, coordScale, colorScale, DE_NULL); |
| m_testCtx.touchWatchdog(); |
| m_rrArrayPack->render(spec.primitive, spec.drawMethod, spec.first, (int)primitiveElementCount, |
| DrawTestSpec::INDEXTYPE_LAST, DE_NULL, 0, 0, spec.instanceCount, |
| spec.indirectOffset, 0, coordScale, colorScale, DE_NULL); |
| } |
| } |
| catch (glu::Error &err) |
| { |
| // GL Errors are ok if the mode is not properly aligned |
| |
| const DrawTestSpec::CompatibilityTestType ctype = spec.isCompatibilityTest(); |
| |
| m_testCtx.getLog() << TestLog::Message << "Got error: " << err.what() << TestLog::EndMessage; |
| |
| if (ctype == DrawTestSpec::COMPATIBILITY_UNALIGNED_OFFSET) |
| m_result.addResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Failed to draw with unaligned buffers."); |
| else if (ctype == DrawTestSpec::COMPATIBILITY_UNALIGNED_STRIDE) |
| m_result.addResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Failed to draw with unaligned stride."); |
| else |
| throw; |
| } |
| } |
| else if (compareStep) |
| { |
| if (!compare(spec.primitive)) |
| { |
| const DrawTestSpec::CompatibilityTestType ctype = spec.isCompatibilityTest(); |
| |
| if (ctype == DrawTestSpec::COMPATIBILITY_UNALIGNED_OFFSET) |
| m_result.addResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Failed to draw with unaligned buffers."); |
| else if (ctype == DrawTestSpec::COMPATIBILITY_UNALIGNED_STRIDE) |
| m_result.addResult(QP_TEST_RESULT_COMPATIBILITY_WARNING, "Failed to draw with unaligned stride."); |
| else |
| m_result.addResult(QP_TEST_RESULT_FAIL, "Image comparison failed."); |
| } |
| } |
| else |
| { |
| DE_ASSERT(false); |
| return STOP; |
| } |
| |
| m_result.setTestContextResult(m_testCtx); |
| |
| m_iteration++; |
| return iterateResult; |
| } |
| |
| static bool isBlack(const tcu::RGBA &c) |
| { |
| // ignore alpha channel |
| return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0; |
| } |
| |
| static bool isEdgeTripletComponent(int c1, int c2, int c3, int renderTargetDifference) |
| { |
| const int roundingDifference = 2 * renderTargetDifference; // src and dst pixels rounded to different directions |
| const int d1 = c2 - c1; |
| const int d2 = c3 - c2; |
| const int rampDiff = de::abs(d2 - d1); |
| |
| return rampDiff > roundingDifference; |
| } |
| |
| static bool isEdgeTriplet(const tcu::RGBA &c1, const tcu::RGBA &c2, const tcu::RGBA &c3, |
| const tcu::IVec3 &renderTargetThreshold) |
| { |
| // black (background color) and non-black is always an edge |
| { |
| const bool b1 = isBlack(c1); |
| const bool b2 = isBlack(c2); |
| const bool b3 = isBlack(c3); |
| |
| // both pixels with coverage and pixels without coverage |
| if ((b1 && b2 && b3) == false && (b1 || b2 || b3) == true) |
| return true; |
| // all black |
| if (b1 && b2 && b3) |
| return false; |
| // all with coverage |
| DE_ASSERT(!b1 && !b2 && !b3); |
| } |
| |
| // Color is always linearly interpolated => component values change nearly linearly |
| // in any constant direction on triangle hull. (df/dx ~= C). |
| |
| // Edge detection (this function) is run against the reference image |
| // => no dithering to worry about |
| |
| return isEdgeTripletComponent(c1.getRed(), c2.getRed(), c3.getRed(), renderTargetThreshold.x()) || |
| isEdgeTripletComponent(c1.getGreen(), c2.getGreen(), c3.getGreen(), renderTargetThreshold.y()) || |
| isEdgeTripletComponent(c1.getBlue(), c2.getBlue(), c3.getBlue(), renderTargetThreshold.z()); |
| } |
| |
| static bool pixelNearEdge(int x, int y, const tcu::Surface &ref, const tcu::IVec3 &renderTargetThreshold) |
| { |
| // should not be called for edge pixels |
| DE_ASSERT(x >= 1 && x <= ref.getWidth() - 2); |
| DE_ASSERT(y >= 1 && y <= ref.getHeight() - 2); |
| |
| // horizontal |
| |
| for (int dy = -1; dy < 2; ++dy) |
| { |
| const tcu::RGBA c1 = ref.getPixel(x - 1, y + dy); |
| const tcu::RGBA c2 = ref.getPixel(x, y + dy); |
| const tcu::RGBA c3 = ref.getPixel(x + 1, y + dy); |
| if (isEdgeTriplet(c1, c2, c3, renderTargetThreshold)) |
| return true; |
| } |
| |
| // vertical |
| |
| for (int dx = -1; dx < 2; ++dx) |
| { |
| const tcu::RGBA c1 = ref.getPixel(x + dx, y - 1); |
| const tcu::RGBA c2 = ref.getPixel(x + dx, y); |
| const tcu::RGBA c3 = ref.getPixel(x + dx, y + 1); |
| if (isEdgeTriplet(c1, c2, c3, renderTargetThreshold)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static uint32_t getVisualizationGrayscaleColor(const tcu::RGBA &c) |
| { |
| // make triangle coverage and error pixels obvious by converting coverage to grayscale |
| if (isBlack(c)) |
| return 0; |
| else |
| return 50u + (uint32_t)(c.getRed() + c.getBlue() + c.getGreen()) / 8u; |
| } |
| |
| static bool pixelNearLineIntersection(int x, int y, const tcu::Surface &target) |
| { |
| // should not be called for edge pixels |
| DE_ASSERT(x >= 1 && x <= target.getWidth() - 2); |
| DE_ASSERT(y >= 1 && y <= target.getHeight() - 2); |
| |
| int coveredPixels = 0; |
| |
| for (int dy = -1; dy < 2; dy++) |
| for (int dx = -1; dx < 2; dx++) |
| { |
| const bool targetCoverage = !isBlack(target.getPixel(x + dx, y + dy)); |
| if (targetCoverage) |
| { |
| ++coveredPixels; |
| |
| // A single thin line cannot have more than 3 covered pixels in a 3x3 area |
| if (coveredPixels >= 4) |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static inline bool colorsEqual(const tcu::RGBA &colorA, const tcu::RGBA &colorB, const tcu::IVec3 &compareThreshold) |
| { |
| enum |
| { |
| TCU_RGBA_RGB_MASK = tcu::RGBA::RED_MASK | tcu::RGBA::GREEN_MASK | tcu::RGBA::BLUE_MASK |
| }; |
| |
| return tcu::compareThresholdMasked(colorA, colorB, |
| tcu::RGBA(compareThreshold.x(), compareThreshold.y(), compareThreshold.z(), 0), |
| TCU_RGBA_RGB_MASK); |
| } |
| |
| // search 3x3 are for matching color |
| static bool pixelNeighborhoodContainsColor(const tcu::Surface &target, int x, int y, const tcu::RGBA &color, |
| const tcu::IVec3 &compareThreshold) |
| { |
| // should not be called for edge pixels |
| DE_ASSERT(x >= 1 && x <= target.getWidth() - 2); |
| DE_ASSERT(y >= 1 && y <= target.getHeight() - 2); |
| |
| for (int dy = -1; dy < 2; dy++) |
| for (int dx = -1; dx < 2; dx++) |
| { |
| const tcu::RGBA targetCmpPixel = target.getPixel(x + dx, y + dy); |
| if (colorsEqual(color, targetCmpPixel, compareThreshold)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // search 3x3 are for matching coverage (coverage == (color != background color)) |
| static bool pixelNeighborhoodContainsCoverage(const tcu::Surface &target, int x, int y, bool coverage) |
| { |
| // should not be called for edge pixels |
| DE_ASSERT(x >= 1 && x <= target.getWidth() - 2); |
| DE_ASSERT(y >= 1 && y <= target.getHeight() - 2); |
| |
| for (int dy = -1; dy < 2; dy++) |
| for (int dx = -1; dx < 2; dx++) |
| { |
| const bool targetCmpCoverage = !isBlack(target.getPixel(x + dx, y + dy)); |
| if (targetCmpCoverage == coverage) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool edgeRelaxedImageCompare(tcu::TestLog &log, const char *imageSetName, const char *imageSetDesc, |
| const tcu::Surface &reference, const tcu::Surface &result, |
| const tcu::IVec3 &compareThreshold, const tcu::IVec3 &renderTargetThreshold, |
| int maxAllowedInvalidPixels) |
| { |
| DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); |
| |
| const tcu::IVec4 green(0, 255, 0, 255); |
| const tcu::IVec4 red(255, 0, 0, 255); |
| const int width = reference.getWidth(); |
| const int height = reference.getHeight(); |
| tcu::TextureLevel errorMask(tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), width, |
| height); |
| const tcu::PixelBufferAccess errorAccess = errorMask.getAccess(); |
| int numFailingPixels = 0; |
| |
| // clear errormask edges which would otherwise be transparent |
| |
| tcu::clear(tcu::getSubregion(errorAccess, 0, 0, width, 1), green); |
| tcu::clear(tcu::getSubregion(errorAccess, 0, height - 1, width, 1), green); |
| tcu::clear(tcu::getSubregion(errorAccess, 0, 0, 1, height), green); |
| tcu::clear(tcu::getSubregion(errorAccess, width - 1, 0, 1, height), green); |
| |
| // skip edge pixels since coverage on edge cannot be verified |
| |
| for (int y = 1; y < height - 1; ++y) |
| for (int x = 1; x < width - 1; ++x) |
| { |
| const tcu::RGBA refPixel = reference.getPixel(x, y); |
| const tcu::RGBA screenPixel = result.getPixel(x, y); |
| const bool directMatch = colorsEqual(refPixel, screenPixel, compareThreshold); |
| const bool isOkReferencePixel = |
| directMatch || |
| pixelNeighborhoodContainsColor( |
| result, x, y, refPixel, |
| compareThreshold); // screen image has a matching pixel nearby (~= If something is drawn on reference, it must be drawn to screen too.) |
| const bool isOkScreenPixel = |
| directMatch || |
| pixelNeighborhoodContainsColor( |
| reference, x, y, screenPixel, |
| compareThreshold); // reference image has a matching pixel nearby (~= If something is drawn on screen, it must be drawn to reference too.) |
| |
| if (isOkScreenPixel && isOkReferencePixel) |
| { |
| // pixel valid, write greenish pixels to make the result image easier to read |
| const uint32_t grayscaleValue = getVisualizationGrayscaleColor(screenPixel); |
| errorAccess.setPixel(tcu::UVec4(grayscaleValue, 255, grayscaleValue, 255), x, y); |
| } |
| else if (!pixelNearEdge(x, y, reference, renderTargetThreshold)) |
| { |
| // non-edge pixel values must be within threshold of the reference values |
| errorAccess.setPixel(red, x, y); |
| ++numFailingPixels; |
| } |
| else |
| { |
| // we are on/near an edge, verify only coverage (coverage == not background colored) |
| const bool referenceCoverage = !isBlack(refPixel); |
| const bool screenCoverage = !isBlack(screenPixel); |
| const bool isOkReferenceCoverage = pixelNeighborhoodContainsCoverage( |
| result, x, y, referenceCoverage); // Check reference pixel against screen pixel |
| const bool isOkScreenCoverage = pixelNeighborhoodContainsCoverage( |
| reference, x, y, screenCoverage); // Check screen pixels against reference pixel |
| |
| if (isOkScreenCoverage && isOkReferenceCoverage) |
| { |
| // pixel valid, write greenish pixels to make the result image easier to read |
| const uint32_t grayscaleValue = getVisualizationGrayscaleColor(screenPixel); |
| errorAccess.setPixel(tcu::UVec4(grayscaleValue, 255, grayscaleValue, 255), x, y); |
| } |
| else |
| { |
| // coverage does not match |
| errorAccess.setPixel(red, x, y); |
| ++numFailingPixels; |
| } |
| } |
| } |
| |
| log << TestLog::Message << "Comparing images:\n" |
| << "\tallowed deviation in pixel positions = 1\n" |
| << "\tnumber of allowed invalid pixels = " << maxAllowedInvalidPixels << "\n" |
| << "\tnumber of invalid pixels = " << numFailingPixels << TestLog::EndMessage; |
| |
| if (numFailingPixels > maxAllowedInvalidPixels) |
| { |
| log << TestLog::Message << "Image comparison failed. Color threshold = (" << compareThreshold.x() << ", " |
| << compareThreshold.y() << ", " << compareThreshold.z() << ")" << TestLog::EndMessage |
| << TestLog::ImageSet(imageSetName, imageSetDesc) << TestLog::Image("Result", "Result", result) |
| << TestLog::Image("Reference", "Reference", reference) |
| << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet; |
| |
| return false; |
| } |
| else |
| { |
| log << TestLog::ImageSet(imageSetName, imageSetDesc) << TestLog::Image("Result", "Result", result) |
| << TestLog::EndImageSet; |
| |
| return true; |
| } |
| } |
| |
| static bool intersectionRelaxedLineImageCompare(tcu::TestLog &log, const char *imageSetName, const char *imageSetDesc, |
| const tcu::Surface &reference, const tcu::Surface &result, |
| const tcu::IVec3 &compareThreshold, int maxAllowedInvalidPixels) |
| { |
| DE_ASSERT(result.getWidth() == reference.getWidth() && result.getHeight() == reference.getHeight()); |
| |
| const tcu::IVec4 green(0, 255, 0, 255); |
| const tcu::IVec4 red(255, 0, 0, 255); |
| const int width = reference.getWidth(); |
| const int height = reference.getHeight(); |
| tcu::TextureLevel errorMask(tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8), width, |
| height); |
| const tcu::PixelBufferAccess errorAccess = errorMask.getAccess(); |
| int numFailingPixels = 0; |
| |
| // clear errormask edges which would otherwise be transparent |
| |
| tcu::clear(tcu::getSubregion(errorAccess, 0, 0, width, 1), green); |
| tcu::clear(tcu::getSubregion(errorAccess, 0, height - 1, width, 1), green); |
| tcu::clear(tcu::getSubregion(errorAccess, 0, 0, 1, height), green); |
| tcu::clear(tcu::getSubregion(errorAccess, width - 1, 0, 1, height), green); |
| |
| // skip edge pixels since coverage on edge cannot be verified |
| |
| for (int y = 1; y < height - 1; ++y) |
| for (int x = 1; x < width - 1; ++x) |
| { |
| const tcu::RGBA refPixel = reference.getPixel(x, y); |
| const tcu::RGBA screenPixel = result.getPixel(x, y); |
| const bool directMatch = colorsEqual(refPixel, screenPixel, compareThreshold); |
| const bool isOkScreenPixel = |
| directMatch || |
| pixelNeighborhoodContainsColor( |
| reference, x, y, screenPixel, |
| compareThreshold); // reference image has a matching pixel nearby (~= If something is drawn on screen, it must be drawn to reference too.) |
| const bool isOkReferencePixel = |
| directMatch || |
| pixelNeighborhoodContainsColor( |
| result, x, y, refPixel, |
| compareThreshold); // screen image has a matching pixel nearby (~= If something is drawn on reference, it must be drawn to screen too.) |
| |
| if (isOkScreenPixel && isOkReferencePixel) |
| { |
| // pixel valid, write greenish pixels to make the result image easier to read |
| const uint32_t grayscaleValue = getVisualizationGrayscaleColor(screenPixel); |
| errorAccess.setPixel(tcu::UVec4(grayscaleValue, 255, grayscaleValue, 255), x, y); |
| } |
| else if (!pixelNearLineIntersection(x, y, reference) && !pixelNearLineIntersection(x, y, result)) |
| { |
| // non-intersection pixel values must be within threshold of the reference values |
| errorAccess.setPixel(red, x, y); |
| ++numFailingPixels; |
| } |
| else |
| { |
| // pixel is near a line intersection |
| // we are on/near an edge, verify only coverage (coverage == not background colored) |
| const bool referenceCoverage = !isBlack(refPixel); |
| const bool screenCoverage = !isBlack(screenPixel); |
| const bool isOkScreenCoverage = pixelNeighborhoodContainsCoverage( |
| reference, x, y, screenCoverage); // Check screen pixels against reference pixel |
| const bool isOkReferenceCoverage = pixelNeighborhoodContainsCoverage( |
| result, x, y, referenceCoverage); // Check reference pixel against screen pixel |
| |
| if (isOkScreenCoverage && isOkReferenceCoverage) |
| { |
| // pixel valid, write greenish pixels to make the result image easier to read |
| const uint32_t grayscaleValue = getVisualizationGrayscaleColor(screenPixel); |
| errorAccess.setPixel(tcu::UVec4(grayscaleValue, 255, grayscaleValue, 255), x, y); |
| } |
| else |
| { |
| // coverage does not match |
| errorAccess.setPixel(red, x, y); |
| ++numFailingPixels; |
| } |
| } |
| } |
| |
| log << TestLog::Message << "Comparing images:\n" |
| << "\tallowed deviation in pixel positions = 1\n" |
| << "\tnumber of allowed invalid pixels = " << maxAllowedInvalidPixels << "\n" |
| << "\tnumber of invalid pixels = " << numFailingPixels << TestLog::EndMessage; |
| |
| if (numFailingPixels > maxAllowedInvalidPixels) |
| { |
| log << TestLog::Message << "Image comparison failed. Color threshold = (" << compareThreshold.x() << ", " |
| << compareThreshold.y() << ", " << compareThreshold.z() << ")" << TestLog::EndMessage |
| << TestLog::ImageSet(imageSetName, imageSetDesc) << TestLog::Image("Result", "Result", result) |
| << TestLog::Image("Reference", "Reference", reference) |
| << TestLog::Image("ErrorMask", "Error mask", errorMask) << TestLog::EndImageSet; |
| |
| return false; |
| } |
| else |
| { |
| log << TestLog::ImageSet(imageSetName, imageSetDesc) << TestLog::Image("Result", "Result", result) |
| << TestLog::EndImageSet; |
| |
| return true; |
| } |
| } |
| |
| bool DrawTest::compare(gls::DrawTestSpec::Primitive primitiveType) |
| { |
| const tcu::Surface &ref = m_rrArrayPack->getSurface(); |
| const tcu::Surface &screen = m_glArrayPack->getSurface(); |
| |
| if (m_renderCtx.getRenderTarget().getNumSamples() > 1) |
| { |
| // \todo [mika] Improve compare when using multisampling |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Warning: Comparision of result from multisample render targets are not as stricts as " |
| "without multisampling. Might produce false positives!" |
| << tcu::TestLog::EndMessage; |
| return tcu::fuzzyCompare(m_testCtx.getLog(), "Compare Results", "Compare Results", ref.getAccess(), |
| screen.getAccess(), 0.3f, tcu::COMPARE_LOG_RESULT); |
| } |
| else |
| { |
| const PrimitiveClass primitiveClass = getDrawPrimitiveClass(primitiveType); |
| const int maxAllowedInvalidPixelsWithPoints = 0; //!< points are unlikely to have overlapping fragments |
| const int maxAllowedInvalidPixelsWithLines = 5; //!< line are allowed to have a few bad pixels |
| const int maxAllowedInvalidPixelsWithTriangles = 10; |
| |
| switch (primitiveClass) |
| { |
| case PRIMITIVECLASS_POINT: |
| { |
| // Point are extremely unlikely to have overlapping regions, don't allow any no extra / missing pixels |
| return tcu::intThresholdPositionDeviationErrorThresholdCompare( |
| m_testCtx.getLog(), "CompareResult", "Result of rendering", ref.getAccess(), screen.getAccess(), |
| tcu::UVec4(m_maxDiffRed, m_maxDiffGreen, m_maxDiffBlue, 256), |
| tcu::IVec3(1, 1, 0), //!< 3x3 search kernel |
| true, //!< relax comparison on the image boundary |
| maxAllowedInvalidPixelsWithPoints, //!< error threshold |
| tcu::COMPARE_LOG_RESULT); |
| } |
| |
| case PRIMITIVECLASS_LINE: |
| { |
| // Lines can potentially have a large number of overlapping pixels. Pixel comparison may potentially produce |
| // false negatives in such pixels if for example the pixel in question is overdrawn by another line in the |
| // reference image but not in the resultin image. Relax comparison near line intersection points (areas) and |
| // compare only coverage, not color, in such pixels |
| return intersectionRelaxedLineImageCompare(m_testCtx.getLog(), "CompareResult", "Result of rendering", ref, |
| screen, tcu::IVec3(m_maxDiffRed, m_maxDiffGreen, m_maxDiffBlue), |
| maxAllowedInvalidPixelsWithLines); |
| } |
| |
| case PRIMITIVECLASS_TRIANGLE: |
| { |
| // Triangles are likely to partially or fully overlap. Pixel difference comparison is fragile in pixels |
| // where there could be potential overlapping since the pixels might be covered by one triangle in the |
| // reference image and by the other in the result image. Relax comparsion near primitive edges and |
| // compare only coverage, not color, in such pixels. |
| const tcu::IVec3 renderTargetThreshold = |
| m_renderCtx.getRenderTarget().getPixelFormat().getColorThreshold().toIVec().xyz(); |
| |
| return edgeRelaxedImageCompare(m_testCtx.getLog(), "CompareResult", "Result of rendering", ref, screen, |
| tcu::IVec3(m_maxDiffRed, m_maxDiffGreen, m_maxDiffBlue), |
| renderTargetThreshold, maxAllowedInvalidPixelsWithTriangles); |
| } |
| |
| default: |
| DE_ASSERT(false); |
| return false; |
| } |
| } |
| } |
| |
| float DrawTest::getCoordScale(const DrawTestSpec &spec) const |
| { |
| float maxValue = 1.0f; |
| |
| for (int arrayNdx = 0; arrayNdx < (int)spec.attribs.size(); arrayNdx++) |
| { |
| DrawTestSpec::AttributeSpec attribSpec = spec.attribs[arrayNdx]; |
| const bool isPositionAttr = (arrayNdx == 0) || (attribSpec.additionalPositionAttribute); |
| float attrMaxValue = 0; |
| |
| if (!isPositionAttr) |
| continue; |
| |
| if (attribSpec.inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10) |
| { |
| if (attribSpec.normalize) |
| attrMaxValue += 1.0f; |
| else |
| attrMaxValue += 1024.0f; |
| } |
| else if (attribSpec.inputType == DrawTestSpec::INPUTTYPE_INT_2_10_10_10) |
| { |
| if (attribSpec.normalize) |
| attrMaxValue += 1.0f; |
| else |
| attrMaxValue += 512.0f; |
| } |
| else |
| { |
| const float max = GLValue::getMaxValue(attribSpec.inputType).toFloat(); |
| |
| attrMaxValue += |
| (attribSpec.normalize && !inputTypeIsFloatType(attribSpec.inputType)) ? (1.0f) : (max * 1.1f); |
| } |
| |
| if (attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_VEC3 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_VEC4 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_IVEC3 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_IVEC4 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_UVEC3 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_UVEC4) |
| attrMaxValue *= 2; |
| |
| maxValue += attrMaxValue; |
| } |
| |
| return 1.0f / maxValue; |
| } |
| |
| float DrawTest::getColorScale(const DrawTestSpec &spec) const |
| { |
| float colorScale = 1.0f; |
| |
| for (int arrayNdx = 1; arrayNdx < (int)spec.attribs.size(); arrayNdx++) |
| { |
| DrawTestSpec::AttributeSpec attribSpec = spec.attribs[arrayNdx]; |
| const bool isPositionAttr = (arrayNdx == 0) || (attribSpec.additionalPositionAttribute); |
| |
| if (isPositionAttr) |
| continue; |
| |
| if (attribSpec.inputType == DrawTestSpec::INPUTTYPE_UNSIGNED_INT_2_10_10_10) |
| { |
| if (!attribSpec.normalize) |
| colorScale *= 1.0f / 1024.0f; |
| } |
| else if (attribSpec.inputType == DrawTestSpec::INPUTTYPE_INT_2_10_10_10) |
| { |
| if (!attribSpec.normalize) |
| colorScale *= 1.0f / 512.0f; |
| } |
| else |
| { |
| const float max = GLValue::getMaxValue(attribSpec.inputType).toFloat(); |
| |
| colorScale *= |
| (attribSpec.normalize && !inputTypeIsFloatType(attribSpec.inputType) ? 1.0f : float(1.0 / double(max))); |
| if (attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_VEC4 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_UVEC4 || |
| attribSpec.outputType == DrawTestSpec::OUTPUTTYPE_IVEC4) |
| colorScale *= |
| (attribSpec.normalize && !inputTypeIsFloatType(attribSpec.inputType) ? 1.0f : |
| float(1.0 / double(max))); |
| } |
| } |
| |
| return colorScale; |
| } |
| |
| } // namespace gls |
| } // namespace deqp |