blob: 4a6ac529b325e15fd45c4f4e99b75e6f93498b6e [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 "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(m_value + other.getValue()); }
inline WrappedType<Type> operator* (const WrappedType<Type>& other) const { return WrappedType<Type>::create(m_value * other.getValue()); }
inline WrappedType<Type> operator/ (const WrappedType<Type>& other) const { return WrappedType<Type>::create(m_value / other.getValue()); }
inline WrappedType<Type> operator- (const WrappedType<Type>& other) const { return WrappedType<Type>::create(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 : 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 : 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 : 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 : 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>()));
}
// AttriuteArray
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 subdata (DrawTestSpec::Target target, int offset, int size, const char* data);
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::subdata (DrawTestSpec::Target target, int offset, int size, const char* ptr)
{
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.bufferSubData(targetToGL(target), offset, size, ptr);
GLU_EXPECT_NO_ERROR(m_ctx.getError(), "glBufferSubData()");
}
else if (m_storage == DrawTestSpec::STORAGE_USER)
std::memcpy(m_data + offset, 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() + attribValue.z(), (float)attribValue.y()); break;
case 4: coord += tcu::Vec2((float)attribValue.x() + attribValue.z(), (float)attribValue.y() + attribValue.w()); break;
default:
DE_ASSERT(false);
}
else
{
switch (numComponents)
{
case 1:
color = color * (float)attribValue.x();
break;
case 2:
color.x() = color.x() * attribValue.x();
color.y() = color.y() * attribValue.y();
break;
case 3:
color.x() = color.x() * attribValue.x();
color.y() = color.y() * attribValue.y();
color.z() = color.z() * attribValue.z();
break;
case 4:
color.x() = color.x() * attribValue.x() * attribValue.w();
color.y() = color.y() * attribValue.y() * attribValue.w();
color.z() = color.z() * attribValue.z() * 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 void setData (char* data, DrawTestSpec::InputType type, deRandom& rnd, GLValue min, GLValue max);
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);
};
void RandomArrayGenerator::setData (char* data, DrawTestSpec::InputType type, deRandom& rnd, GLValue min, GLValue max)
{
switch (type)
{
case DrawTestSpec::INPUTTYPE_FLOAT:
{
alignmentSafeAssignment<float>(data, getRandom<GLValue::Float>(rnd, min.fl, max.fl));
break;
}
case DrawTestSpec::INPUTTYPE_SHORT:
{
alignmentSafeAssignment<deInt16>(data, getRandom<GLValue::Short>(rnd, min.s, max.s));
break;
}
case DrawTestSpec::INPUTTYPE_UNSIGNED_SHORT:
{
alignmentSafeAssignment<deUint16>(data, getRandom<GLValue::Ushort>(rnd, min.us, max.us));
break;
}
case DrawTestSpec::INPUTTYPE_BYTE:
{
alignmentSafeAssignment<deInt8>(data, getRandom<GLValue::Byte>(rnd, min.b, max.b));
break;
}
case DrawTestSpec::INPUTTYPE_UNSIGNED_BYTE:
{
alignmentSafeAssignment<deUint8>(data, getRandom<GLValue::Ubyte>(rnd, min.ub, max.ub));
break;
}
case DrawTestSpec::INPUTTYPE_FIXED:
{
alignmentSafeAssignment<deInt32>(data, getRandom<GLValue::Fixed>(rnd, min.fi, max.fi));
break;
}
case DrawTestSpec::INPUTTYPE_INT:
{
alignmentSafeAssignment<deInt32>(data, getRandom<GLValue::Int>(rnd, min.i, max.i));
break;
}
case DrawTestSpec::INPUTTYPE_UNSIGNED_INT:
{
alignmentSafeAssignment<deUint32>(data, getRandom<GLValue::Uint>(rnd, min.ui, max.ui));
break;
}
case DrawTestSpec::INPUTTYPE_HALF:
{
alignmentSafeAssignment<deFloat16>(data, getRandom<GLValue::Half>(rnd, min.h, max.h).getValue());
break;
}
default:
DE_ASSERT(false);
break;
}
}
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_ASSERT(!"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), (const deInt8*)DE_NULL + 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), (const deInt8*)DE_NULL + 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,