You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3851 lines
130 KiB
3851 lines
130 KiB
/*-------------------------------------------------------------------------
|
|
* 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 deUint32 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<deInt16> Short;
|
|
typedef WrappedType<deUint16> Ushort;
|
|
|
|
typedef WrappedType<deInt8> Byte;
|
|
typedef WrappedType<deUint8> Ubyte;
|
|
|
|
typedef WrappedType<float> Float;
|
|
typedef WrappedType<double> Double;
|
|
|
|
typedef WrappedType<deInt32> Int;
|
|
typedef WrappedType<deUint32> 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 (deInt32 value) { Fixed v; v.m_value = value; return v; }
|
|
inline deInt32 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 deInt32 (void) const { return m_value; }
|
|
template<class T>
|
|
inline T to (void) const { return (T)m_value; }
|
|
private:
|
|
deInt32 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<deUint16, 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((deUint16)h).asFloat();
|
|
}
|
|
|
|
float GLValue::toFloat (void) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case DrawTestSpec::INPUTTYPE_FLOAT:
|
|
return fl.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_BYTE:
|
|
return b.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE:
|
|
return ub.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_SHORT:
|
|
return s.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT:
|
|
return us.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_FIXED:
|
|
{
|
|
int maxValue = 65536;
|
|
return (float)(double(2 * fi.getValue() + 1) / (maxValue - 1));
|
|
|
|
break;
|
|
}
|
|
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_INT:
|
|
return (float)ui.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_INT:
|
|
return (float)i.getValue();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_HALF:
|
|
return h.to<float>();
|
|
break;
|
|
|
|
case DrawTestSpec::INPUTTYPE_DOUBLE:
|
|
return d.to<float>();
|
|
break;
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
return 0.0f;
|
|
break;
|
|
};
|
|
}
|
|
|
|
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 : (deInt16)(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 : (deUint16)(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 : (deInt8)(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 : (deUint8)(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<deUint32>() - min.to<deUint32>()))));
|
|
}
|
|
|
|
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<deUint32>() - min.to<deUint32>()))));
|
|
}
|
|
|
|
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<deUint32>() - min.to<deUint32>()))));
|
|
}
|
|
|
|
// 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 (deUint32 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;
|
|
deUint32 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 (deUint32 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(DE_FALSE); break;
|
|
}
|
|
break;
|
|
}
|
|
case DrawTestSpec::INPUTTYPE_INT:
|
|
{
|
|
tcu::IVec4 attr = m_defaultAttrib.get<deInt32>();
|
|
m_ctx.vertexAttribI4i(loc, attr.x(), attr.y(), attr.z(), attr.w());
|
|
break;
|
|
}
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_INT:
|
|
{
|
|
tcu::UVec4 attr = m_defaultAttrib.get<deUint32>();
|
|
m_ctx.vertexAttribI4ui(loc, attr.x(), attr.y(), attr.z(), attr.w());
|
|
break;
|
|
}
|
|
default:
|
|
DE_ASSERT(DE_FALSE);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const deUint8* 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 deUint8*)m_data;
|
|
}
|
|
else
|
|
DE_ASSERT(DE_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>& params, 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>& params, 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(DE_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<deInt16, GLValue::Short> (seed, elementCount, componentCount, offset, stride);
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT: return createBasicArray<deUint16, GLValue::Ushort>(seed, elementCount, componentCount, offset, stride);
|
|
case DrawTestSpec::INPUTTYPE_BYTE: return createBasicArray<deInt8, GLValue::Byte> (seed, elementCount, componentCount, offset, stride);
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE: return createBasicArray<deUint8, GLValue::Ubyte> (seed, elementCount, componentCount, offset, stride);
|
|
case DrawTestSpec::INPUTTYPE_FIXED: return createBasicArray<deInt32, GLValue::Fixed> (seed, elementCount, componentCount, offset, stride);
|
|
case DrawTestSpec::INPUTTYPE_INT: return createBasicArray<deInt32, GLValue::Int> (seed, elementCount, componentCount, offset, stride);
|
|
case DrawTestSpec::INPUTTYPE_UNSIGNED_INT: return createBasicArray<deUint32, 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 deUint32 limit10 = (1 << 10);
|
|
const deUint32 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 deUint32 x = deRandom_getUint32(&rnd) % limit10;
|
|
const deUint32 y = deRandom_getUint32(&rnd) % limit10;
|
|
const deUint32 z = deRandom_getUint32(&rnd) % limit10;
|
|
const deUint32 w = deRandom_getUint32(&rnd) % limit2;
|
|
const deUint32 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<deUint8>(seed, elementCount, offset, min, max, indexBase);
|
|
break;
|
|
|
|
case DrawTestSpec::INDEXTYPE_SHORT:
|
|
data = createIndices<deUint16>(seed, elementCount, offset, min, max, indexBase);
|
|
break;
|
|
|
|
case DrawTestSpec::INDEXTYPE_INT:
|
|
data = createIndices<deUint32>(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;
|
|
|
|
deUint32 oldNdx1 = deUint32(-1);
|
|
deUint32 oldNdx2 = deUint32(-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)
|
|
{
|
|
deUint32 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 > (deUint32)max) ndx = min;
|
|
if (ndx == oldNdx2) ++ndx;
|
|
if (ndx > (deUint32)max) ndx = min;
|
|
if (ndx == oldNdx1) ++ndx;
|
|
if (ndx > (deUint32)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));
|
|
}
|
|
}
|
|
|
|
} // anonymous
|
|
|
|
// 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;
|
|
deUint32 m_programID;
|
|
deUint32 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;
|
|
|
|
deUint32 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;
|
|
};
|
|
deUint8* buffer = new deUint8[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;
|
|
};
|
|
deUint8* buffer = new deUint8[sizeof(DrawCommand) + indirectOffset];
|
|
|
|
{
|
|
DrawCommand command;
|
|
|
|
// index offset must be converted to firstIndex by dividing with the index element size
|
|
DE_ASSERT(((const deUint8*)indexOffset - (const deUint8*)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 deUint8*)indexOffset - (const deUint8*)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(DE_FALSE);
|
|
|
|
for (int arrayNdx = 0; arrayNdx < (int)m_arrays.size(); arrayNdx++)
|
|
{
|
|
if (m_arrays[arrayNdx]->isBound())
|
|
{
|
|
std::stringstream attribName;
|
|
attribName << "a_" << arrayNdx;
|
|
|
|
deUint32 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(deInt32), // INPUTTYPE_FIXED,
|
|
(int)sizeof(double), // INPUTTYPE_DOUBLE
|
|
|
|
(int)sizeof(deInt8), // INPUTTYPE_BYTE,
|
|
(int)sizeof(deInt16), // INPUTTYPE_SHORT,
|
|
|
|
(int)sizeof(deUint8), // INPUTTYPE_UNSIGNED_BYTE,
|
|
(int)sizeof(deUint16), // INPUTTYPE_UNSIGNED_SHORT,
|
|
|
|
(int)sizeof(deInt32), // INPUTTYPE_INT,
|
|
(int)sizeof(deUint32), // INPUTTYPE_UNSIGNED_INT,
|
|
(int)sizeof(deFloat16), // INPUTTYPE_HALF,
|
|
(int)sizeof(deUint32) / 4, // INPUTTYPE_UNSIGNED_INT_2_10_10_10,
|
|
(int)sizeof(deUint32) / 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(deUint8), // INDEXTYPE_BYTE,
|
|
sizeof(deUint16), // INDEXTYPE_SHORT,
|
|
sizeof(deUint32), // 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(DE_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(DE_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)
|
|
{
|
|
deUint32 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(DE_FALSE);
|
|
|
|
if (indexMin > indexMax)
|
|
return false;
|
|
if (indexMin < 0 || indexMax < 0)
|
|
return false;
|
|
if ((deUint32)indexMin > maxIndexValue || (deUint32)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(DE_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 deUint32 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 + (deUint32)(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 deUint32 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 deUint32 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 deUint32 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 deUint32 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;
|
|
}
|
|
|
|
} // gls
|
|
} // deqp
|