510 lines
19 KiB
C++
510 lines
19 KiB
C++
#ifndef _GLSVERTEXARRAYTESTS_HPP
|
|
#define _GLSVERTEXARRAYTESTS_HPP
|
|
/*-------------------------------------------------------------------------
|
|
* 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 Vertex array and buffer tests
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "tcuTestCase.hpp"
|
|
#include "tcuVector.hpp"
|
|
#include "tcuSurface.hpp"
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluCallLogWrapper.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "deFloat16.h"
|
|
#include "deMath.h"
|
|
#include "tcuFloat.hpp"
|
|
#include "tcuPixelFormat.hpp"
|
|
#include "sglrContext.hpp"
|
|
|
|
namespace sglr
|
|
{
|
|
|
|
class ReferenceContextBuffers;
|
|
class ReferenceContext;
|
|
class Context;
|
|
|
|
} // sglr
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gls
|
|
{
|
|
|
|
class Array
|
|
{
|
|
public:
|
|
enum Target
|
|
{
|
|
// \note [mika] Are these actualy used somewhere?
|
|
TARGET_ELEMENT_ARRAY = 0,
|
|
TARGET_ARRAY,
|
|
|
|
TARGET_LAST
|
|
};
|
|
|
|
enum InputType
|
|
{
|
|
INPUTTYPE_FLOAT = 0,
|
|
INPUTTYPE_FIXED,
|
|
INPUTTYPE_DOUBLE,
|
|
|
|
INPUTTYPE_BYTE,
|
|
INPUTTYPE_SHORT,
|
|
|
|
INPUTTYPE_UNSIGNED_BYTE,
|
|
INPUTTYPE_UNSIGNED_SHORT,
|
|
|
|
INPUTTYPE_INT,
|
|
INPUTTYPE_UNSIGNED_INT,
|
|
INPUTTYPE_HALF,
|
|
INPUTTYPE_UNSIGNED_INT_2_10_10_10,
|
|
INPUTTYPE_INT_2_10_10_10,
|
|
|
|
INPUTTYPE_LAST
|
|
};
|
|
|
|
enum OutputType
|
|
{
|
|
OUTPUTTYPE_FLOAT = 0,
|
|
OUTPUTTYPE_VEC2,
|
|
OUTPUTTYPE_VEC3,
|
|
OUTPUTTYPE_VEC4,
|
|
|
|
OUTPUTTYPE_INT,
|
|
OUTPUTTYPE_UINT,
|
|
|
|
OUTPUTTYPE_IVEC2,
|
|
OUTPUTTYPE_IVEC3,
|
|
OUTPUTTYPE_IVEC4,
|
|
|
|
OUTPUTTYPE_UVEC2,
|
|
OUTPUTTYPE_UVEC3,
|
|
OUTPUTTYPE_UVEC4,
|
|
|
|
OUTPUTTYPE_LAST
|
|
};
|
|
|
|
enum Usage
|
|
{
|
|
USAGE_DYNAMIC_DRAW = 0,
|
|
USAGE_STATIC_DRAW,
|
|
USAGE_STREAM_DRAW,
|
|
|
|
USAGE_STREAM_READ,
|
|
USAGE_STREAM_COPY,
|
|
|
|
USAGE_STATIC_READ,
|
|
USAGE_STATIC_COPY,
|
|
|
|
USAGE_DYNAMIC_READ,
|
|
USAGE_DYNAMIC_COPY,
|
|
|
|
USAGE_LAST
|
|
};
|
|
|
|
enum Storage
|
|
{
|
|
STORAGE_USER = 0,
|
|
STORAGE_BUFFER,
|
|
|
|
STORAGE_LAST
|
|
};
|
|
|
|
enum Primitive
|
|
{
|
|
PRIMITIVE_POINTS = 0,
|
|
PRIMITIVE_TRIANGLES,
|
|
PRIMITIVE_TRIANGLE_FAN,
|
|
PRIMITIVE_TRIANGLE_STRIP,
|
|
|
|
PRIMITIVE_LAST
|
|
};
|
|
|
|
static std::string targetToString (Target target);
|
|
static std::string inputTypeToString (InputType type);
|
|
static std::string outputTypeToString (OutputType type);
|
|
static std::string usageTypeToString (Usage usage);
|
|
static std::string storageToString (Storage storage);
|
|
static std::string primitiveToString (Primitive primitive);
|
|
static int inputTypeSize (InputType type);
|
|
|
|
virtual ~Array (void) {}
|
|
virtual void data (Target target, int size, const char* data, Usage usage) = 0;
|
|
virtual void subdata (Target target, int offset, int size, const char* data) = 0;
|
|
virtual void bind (int attribNdx, int offset, int size, InputType inType, OutputType outType, bool normalized, int stride) = 0;
|
|
virtual void unBind (void) = 0;
|
|
|
|
virtual bool isBound (void) const = 0;
|
|
virtual int getComponentCount (void) const = 0;
|
|
virtual Target getTarget (void) const = 0;
|
|
virtual InputType getInputType (void) const = 0;
|
|
virtual OutputType getOutputType (void) const = 0;
|
|
virtual Storage getStorageType (void) const = 0;
|
|
virtual bool getNormalized (void) const = 0;
|
|
virtual int getStride (void) const = 0;
|
|
virtual int getAttribNdx (void) const = 0;
|
|
virtual void setAttribNdx (int attribNdx) = 0;
|
|
};
|
|
|
|
class ContextArray : public Array
|
|
{
|
|
public:
|
|
ContextArray (Storage storage, sglr::Context& context);
|
|
virtual ~ContextArray (void);
|
|
virtual void data (Target target, int size, const char* data, Usage usage);
|
|
virtual void subdata (Target target, int offset, int size, const char* data);
|
|
virtual void bind (int attribNdx, int offset, int size, InputType inType, OutputType outType, bool normalized, int stride);
|
|
virtual void bindIndexArray (Array::Target storage);
|
|
virtual void unBind (void) { m_bound = false; }
|
|
virtual bool isBound (void) const { return m_bound; }
|
|
|
|
virtual int getComponentCount (void) const { return m_componentCount; }
|
|
virtual Array::Target getTarget (void) const { return m_target; }
|
|
virtual Array::InputType getInputType (void) const { return m_inputType; }
|
|
virtual Array::OutputType getOutputType (void) const { return m_outputType; }
|
|
virtual Array::Storage getStorageType (void) const { return m_storage; }
|
|
virtual bool getNormalized (void) const { return m_normalize; }
|
|
virtual int getStride (void) const { return m_stride; }
|
|
virtual int getAttribNdx (void) const { return m_attribNdx; }
|
|
virtual void setAttribNdx (int attribNdx) { m_attribNdx = attribNdx; }
|
|
|
|
void glBind (deUint32 loc);
|
|
static deUint32 targetToGL (Array::Target target);
|
|
static deUint32 usageToGL (Array::Usage usage);
|
|
static deUint32 inputTypeToGL (Array::InputType type);
|
|
static std::string outputTypeToGLType (Array::OutputType type);
|
|
static deUint32 primitiveToGL (Array::Primitive primitive);
|
|
|
|
private:
|
|
Storage m_storage;
|
|
sglr::Context& m_ctx;
|
|
deUint32 m_glBuffer;
|
|
|
|
bool m_bound;
|
|
int m_attribNdx;
|
|
int m_size;
|
|
char* m_data;
|
|
int m_componentCount;
|
|
Array::Target m_target;
|
|
Array::InputType m_inputType;
|
|
Array::OutputType m_outputType;
|
|
bool m_normalize;
|
|
int m_stride;
|
|
int m_offset;
|
|
};
|
|
|
|
class ContextArrayPack
|
|
{
|
|
public:
|
|
ContextArrayPack (glu::RenderContext& renderCtx, sglr::Context& drawContext);
|
|
virtual ~ContextArrayPack (void);
|
|
virtual Array* getArray (int i);
|
|
virtual int getArrayCount (void);
|
|
virtual void newArray (Array::Storage storage);
|
|
virtual void render (Array::Primitive primitive, int firstVertex, int vertexCount, bool useVao, float coordScale, float colorScale);
|
|
|
|
const tcu::Surface& getSurface (void) const { return m_screen; }
|
|
private:
|
|
void updateProgram (void);
|
|
glu::RenderContext& m_renderCtx;
|
|
sglr::Context& m_ctx;
|
|
|
|
std::vector<ContextArray*> m_arrays;
|
|
sglr::ShaderProgram* m_program;
|
|
tcu::Surface m_screen;
|
|
};
|
|
|
|
class GLValue
|
|
{
|
|
public:
|
|
template<class Type>
|
|
class WrappedType
|
|
{
|
|
public:
|
|
static WrappedType<Type> create (Type value) { WrappedType<Type> v; v.m_value = value; return v; }
|
|
static WrappedType<Type> fromFloat (float value) { WrappedType<Type> v; v.m_value = (Type)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) 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;
|
|
};
|
|
|
|
template<class Type>
|
|
class WrappedFloatType
|
|
{
|
|
public:
|
|
static WrappedFloatType<Type> create (Type value) { WrappedFloatType<Type> v; v.m_value = value; return v; }
|
|
static WrappedFloatType<Type> fromFloat (float value) { WrappedFloatType<Type> v; v.m_value = (Type)value; return v; }
|
|
inline Type getValue (void) const { return m_value; }
|
|
|
|
inline WrappedFloatType<Type> operator+ (const WrappedFloatType<Type>& other) const { return WrappedFloatType<Type>::create((Type)(m_value + other.getValue())); }
|
|
inline WrappedFloatType<Type> operator* (const WrappedFloatType<Type>& other) const { return WrappedFloatType<Type>::create((Type)(m_value * other.getValue())); }
|
|
inline WrappedFloatType<Type> operator/ (const WrappedFloatType<Type>& other) const { return WrappedFloatType<Type>::create((Type)(m_value / other.getValue())); }
|
|
inline WrappedFloatType<Type> operator% (const WrappedFloatType<Type>& other) const { return WrappedFloatType<Type>::create((Type)(deMod(m_value, other.getValue()))); }
|
|
inline WrappedFloatType<Type> operator- (const WrappedFloatType<Type>& other) const { return WrappedFloatType<Type>::create((Type)(m_value - other.getValue())); }
|
|
|
|
inline WrappedFloatType<Type>& operator+= (const WrappedFloatType<Type>& other) { m_value += other.getValue(); return *this; }
|
|
inline WrappedFloatType<Type>& operator*= (const WrappedFloatType<Type>& other) { m_value *= other.getValue(); return *this; }
|
|
inline WrappedFloatType<Type>& operator/= (const WrappedFloatType<Type>& other) { m_value /= other.getValue(); return *this; }
|
|
inline WrappedFloatType<Type>& operator-= (const WrappedFloatType<Type>& other) { m_value -= other.getValue(); return *this; }
|
|
|
|
inline bool operator== (const WrappedFloatType<Type>& other) const { return m_value == other.m_value; }
|
|
inline bool operator!= (const WrappedFloatType<Type>& other) const { return m_value != other.m_value; }
|
|
inline bool operator< (const WrappedFloatType<Type>& other) const { return m_value < other.m_value; }
|
|
inline bool operator> (const WrappedFloatType<Type>& other) const { return m_value > other.m_value; }
|
|
inline bool operator<= (const WrappedFloatType<Type>& other) const { return m_value <= other.m_value; }
|
|
inline bool operator>= (const WrappedFloatType<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 WrappedFloatType<float> Float;
|
|
typedef WrappedFloatType<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; }
|
|
static Half fromFloat (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(deFloatMod(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; }
|
|
static Fixed fromFloat (float value) { Fixed v; v.m_value = (deInt32)(value * 32768.0f); 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) 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(Array::INPUTTYPE_LAST) {}
|
|
explicit GLValue (Float value) : type(Array::INPUTTYPE_FLOAT), fl(value) {}
|
|
explicit GLValue (Fixed value) : type(Array::INPUTTYPE_FIXED), fi(value) {}
|
|
explicit GLValue (Byte value) : type(Array::INPUTTYPE_BYTE), b(value) {}
|
|
explicit GLValue (Ubyte value) : type(Array::INPUTTYPE_UNSIGNED_BYTE), ub(value) {}
|
|
explicit GLValue (Short value) : type(Array::INPUTTYPE_SHORT), s(value) {}
|
|
explicit GLValue (Ushort value) : type(Array::INPUTTYPE_UNSIGNED_SHORT), us(value) {}
|
|
explicit GLValue (Int value) : type(Array::INPUTTYPE_INT), i(value) {}
|
|
explicit GLValue (Uint value) : type(Array::INPUTTYPE_UNSIGNED_INT), ui(value) {}
|
|
explicit GLValue (Half value) : type(Array::INPUTTYPE_HALF), h(value) {}
|
|
explicit GLValue (Double value) : type(Array::INPUTTYPE_DOUBLE), d(value) {}
|
|
|
|
float toFloat (void) const;
|
|
|
|
static GLValue getMaxValue (Array::InputType type);
|
|
static GLValue getMinValue (Array::InputType type);
|
|
|
|
Array::InputType type;
|
|
|
|
union
|
|
{
|
|
Float fl;
|
|
Fixed fi;
|
|
Double d;
|
|
Byte b;
|
|
Ubyte ub;
|
|
Short s;
|
|
Ushort us;
|
|
Int i;
|
|
Uint ui;
|
|
Half h;
|
|
};
|
|
};
|
|
|
|
class VertexArrayTest : public tcu::TestCase
|
|
{
|
|
public:
|
|
VertexArrayTest (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name ,const char* desc);
|
|
virtual ~VertexArrayTest (void);
|
|
virtual void init (void);
|
|
virtual void deinit (void);
|
|
|
|
protected:
|
|
VertexArrayTest (const VertexArrayTest& other);
|
|
VertexArrayTest& operator= (const VertexArrayTest& other);
|
|
|
|
void compare (void);
|
|
|
|
glu::RenderContext& m_renderCtx;
|
|
|
|
sglr::ReferenceContextBuffers* m_refBuffers;
|
|
sglr::ReferenceContext* m_refContext;
|
|
sglr::Context* m_glesContext;
|
|
|
|
ContextArrayPack* m_glArrayPack;
|
|
ContextArrayPack* m_rrArrayPack;
|
|
bool m_isOk;
|
|
|
|
int m_maxDiffRed;
|
|
int m_maxDiffGreen;
|
|
int m_maxDiffBlue;
|
|
};
|
|
|
|
class MultiVertexArrayTest : public VertexArrayTest
|
|
{
|
|
public:
|
|
class Spec
|
|
{
|
|
public:
|
|
class ArraySpec
|
|
{
|
|
public:
|
|
ArraySpec (Array::InputType inputType, Array::OutputType outputType, Array::Storage storage, Array::Usage usage, int componetCount, int offset, int stride, bool normalize, GLValue min, GLValue max);
|
|
|
|
Array::InputType inputType;
|
|
Array::OutputType outputType;
|
|
Array::Storage storage;
|
|
Array::Usage usage;
|
|
int componentCount;
|
|
int offset;
|
|
int stride;
|
|
bool normalize;
|
|
GLValue min;
|
|
GLValue max;
|
|
};
|
|
|
|
std::string getName (void) const;
|
|
std::string getDesc (void) const;
|
|
|
|
Array::Primitive primitive;
|
|
int drawCount; //!<Number of primitives to draw
|
|
int first;
|
|
|
|
std::vector<ArraySpec> arrays;
|
|
};
|
|
|
|
MultiVertexArrayTest (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const Spec& spec, const char* name, const char* desc);
|
|
virtual ~MultiVertexArrayTest (void);
|
|
virtual IterateResult iterate (void);
|
|
|
|
private:
|
|
bool isUnalignedBufferOffsetTest (void) const;
|
|
bool isUnalignedBufferStrideTest (void) const;
|
|
|
|
Spec m_spec;
|
|
int m_iteration;
|
|
};
|
|
|
|
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();
|
|
}
|
|
|
|
} // gls
|
|
} // deqp
|
|
|
|
#endif // _GLSVERTEXARRAYTESTS_HPP
|