| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.0 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Transform feedback tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es3fTransformFeedbackTests.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuSurface.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuVector.hpp" |
| #include "tcuFormatUtil.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "gluShaderUtil.hpp" |
| #include "gluVarType.hpp" |
| #include "gluVarTypeUtil.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluRenderContext.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "gluObjectWrapper.hpp" |
| #include "glwFunctions.hpp" |
| #include "glwEnums.hpp" |
| #include "deRandom.hpp" |
| #include "deStringUtil.hpp" |
| #include "deMemory.h" |
| #include "deString.h" |
| |
| #include <set> |
| #include <map> |
| #include <algorithm> |
| |
| using std::set; |
| using std::string; |
| using std::vector; |
| |
| using std::map; |
| using std::set; |
| |
| using tcu::TestLog; |
| |
| namespace deqp |
| { |
| namespace gles3 |
| { |
| namespace Functional |
| { |
| namespace TransformFeedback |
| { |
| |
| enum |
| { |
| VIEWPORT_WIDTH = 128, |
| VIEWPORT_HEIGHT = 128, |
| BUFFER_GUARD_MULTIPLIER = |
| 2 //!< stride*BUFFER_GUARD_MULTIPLIER bytes are added to the end of tf buffer and used to check for overruns. |
| }; |
| |
| enum Interpolation |
| { |
| INTERPOLATION_SMOOTH = 0, |
| INTERPOLATION_FLAT, |
| INTERPOLATION_CENTROID, |
| |
| INTERPOLATION_LAST |
| }; |
| |
| static const char *getInterpolationName(Interpolation interp) |
| { |
| switch (interp) |
| { |
| case INTERPOLATION_SMOOTH: |
| return "smooth"; |
| case INTERPOLATION_FLAT: |
| return "flat"; |
| case INTERPOLATION_CENTROID: |
| return "centroid"; |
| default: |
| DE_ASSERT(false); |
| return DE_NULL; |
| } |
| } |
| |
| struct Varying |
| { |
| Varying(const char *name_, const glu::VarType &type_, Interpolation interp_) |
| : name(name_) |
| , type(type_) |
| , interpolation(interp_) |
| { |
| } |
| |
| std::string name; //!< Variable name. |
| glu::VarType type; //!< Variable type. |
| Interpolation interpolation; //!< Interpolation mode (smooth, flat, centroid). |
| }; |
| |
| struct VaryingNameEquals |
| { |
| VaryingNameEquals(const std::string &name_) : name(name_) |
| { |
| } |
| bool operator()(const Varying &var) const |
| { |
| return var.name == name; |
| } |
| |
| std::string name; |
| }; |
| |
| struct Attribute |
| { |
| Attribute(const std::string &name_, const glu::VarType &type_, int offset_) |
| : name(name_) |
| , type(type_) |
| , offset(offset_) |
| { |
| } |
| |
| std::string name; |
| glu::VarType type; |
| int offset; |
| }; |
| |
| struct AttributeNameEquals |
| { |
| AttributeNameEquals(const std::string &name_) : name(name_) |
| { |
| } |
| bool operator()(const Attribute &attr) const |
| { |
| return attr.name == name; |
| } |
| |
| std::string name; |
| }; |
| |
| struct Output |
| { |
| Output(void) : bufferNdx(0), offset(0) |
| { |
| } |
| |
| std::string name; |
| glu::VarType type; |
| int bufferNdx; |
| int offset; |
| vector<const Attribute *> inputs; |
| }; |
| |
| struct DrawCall |
| { |
| DrawCall(int numElements_, bool tfEnabled_) : numElements(numElements_), transformFeedbackEnabled(tfEnabled_) |
| { |
| } |
| |
| DrawCall(void) : numElements(0), transformFeedbackEnabled(false) |
| { |
| } |
| |
| int numElements; |
| bool transformFeedbackEnabled; |
| }; |
| |
| std::ostream &operator<<(std::ostream &str, const DrawCall &call) |
| { |
| return str << "(" << call.numElements << ", " << (call.transformFeedbackEnabled ? "resumed" : "paused") << ")"; |
| } |
| |
| class ProgramSpec |
| { |
| public: |
| ProgramSpec(void); |
| ~ProgramSpec(void); |
| |
| glu::StructType *createStruct(const char *name); |
| void addVarying(const char *name, const glu::VarType &type, Interpolation interp); |
| void addTransformFeedbackVarying(const char *name); |
| |
| const vector<glu::StructType *> &getStructs(void) const |
| { |
| return m_structs; |
| } |
| const vector<Varying> &getVaryings(void) const |
| { |
| return m_varyings; |
| } |
| const vector<string> &getTransformFeedbackVaryings(void) const |
| { |
| return m_transformFeedbackVaryings; |
| } |
| bool isPointSizeUsed(void) const; |
| |
| private: |
| ProgramSpec(const ProgramSpec &other); |
| ProgramSpec &operator=(const ProgramSpec &other); |
| |
| vector<glu::StructType *> m_structs; |
| vector<Varying> m_varyings; |
| vector<string> m_transformFeedbackVaryings; |
| }; |
| |
| // ProgramSpec |
| |
| ProgramSpec::ProgramSpec(void) |
| { |
| } |
| |
| ProgramSpec::~ProgramSpec(void) |
| { |
| for (vector<glu::StructType *>::iterator i = m_structs.begin(); i != m_structs.end(); i++) |
| delete *i; |
| } |
| |
| glu::StructType *ProgramSpec::createStruct(const char *name) |
| { |
| m_structs.reserve(m_structs.size() + 1); |
| m_structs.push_back(new glu::StructType(name)); |
| return m_structs.back(); |
| } |
| |
| void ProgramSpec::addVarying(const char *name, const glu::VarType &type, Interpolation interp) |
| { |
| m_varyings.push_back(Varying(name, type, interp)); |
| } |
| |
| void ProgramSpec::addTransformFeedbackVarying(const char *name) |
| { |
| m_transformFeedbackVaryings.push_back(name); |
| } |
| |
| bool ProgramSpec::isPointSizeUsed(void) const |
| { |
| return std::find(m_transformFeedbackVaryings.begin(), m_transformFeedbackVaryings.end(), "gl_PointSize") != |
| m_transformFeedbackVaryings.end(); |
| } |
| |
| static bool isProgramSupported(const glw::Functions &gl, const ProgramSpec &spec, uint32_t tfMode) |
| { |
| int maxVertexAttribs = 0; |
| int maxTfInterleavedComponents = 0; |
| int maxTfSeparateAttribs = 0; |
| int maxTfSeparateComponents = 0; |
| |
| gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); |
| gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &maxTfInterleavedComponents); |
| gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &maxTfSeparateAttribs); |
| gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &maxTfSeparateComponents); |
| |
| // Check vertex attribs. |
| int totalVertexAttribs = 1 /* a_position */ + (spec.isPointSizeUsed() ? 1 : 0); |
| for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++) |
| { |
| for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&var->type); |
| vecIter != glu::VectorTypeIterator::end(&var->type); vecIter++) |
| totalVertexAttribs += 1; |
| } |
| |
| if (totalVertexAttribs > maxVertexAttribs) |
| return false; // Vertex attribute count exceeded. |
| |
| // Check varyings. |
| int totalTfComponents = 0; |
| int totalTfAttribs = 0; |
| for (vector<string>::const_iterator iter = spec.getTransformFeedbackVaryings().begin(); |
| iter != spec.getTransformFeedbackVaryings().end(); iter++) |
| { |
| const string &name = *iter; |
| int numComponents = 0; |
| |
| if (name == "gl_Position") |
| numComponents = 4; |
| else if (name == "gl_PointSize") |
| numComponents = 1; |
| else |
| { |
| string varName = glu::parseVariableName(name.c_str()); |
| const Varying &varying = |
| *std::find_if(spec.getVaryings().begin(), spec.getVaryings().end(), VaryingNameEquals(varName)); |
| glu::TypeComponentVector varPath; |
| |
| glu::parseTypePath(name.c_str(), varying.type, varPath); |
| numComponents = glu::getVarType(varying.type, varPath).getScalarSize(); |
| } |
| |
| if (tfMode == GL_SEPARATE_ATTRIBS && numComponents > maxTfSeparateComponents) |
| return false; // Per-attribute component count exceeded. |
| |
| totalTfComponents += numComponents; |
| totalTfAttribs += 1; |
| } |
| |
| if (tfMode == GL_SEPARATE_ATTRIBS && totalTfAttribs > maxTfSeparateAttribs) |
| return false; |
| |
| if (tfMode == GL_INTERLEAVED_ATTRIBS && totalTfComponents > maxTfInterleavedComponents) |
| return false; |
| |
| return true; |
| } |
| |
| // Program |
| |
| static std::string getAttributeName(const char *varyingName, const glu::TypeComponentVector &path) |
| { |
| std::ostringstream str; |
| |
| str << "a_" << (deStringBeginsWith(varyingName, "v_") ? varyingName + 2 : varyingName); |
| |
| for (glu::TypeComponentVector::const_iterator iter = path.begin(); iter != path.end(); iter++) |
| { |
| const char *prefix = DE_NULL; |
| |
| switch (iter->type) |
| { |
| case glu::VarTypeComponent::STRUCT_MEMBER: |
| prefix = "_m"; |
| break; |
| case glu::VarTypeComponent::ARRAY_ELEMENT: |
| prefix = "_e"; |
| break; |
| case glu::VarTypeComponent::MATRIX_COLUMN: |
| prefix = "_c"; |
| break; |
| case glu::VarTypeComponent::VECTOR_COMPONENT: |
| prefix = "_s"; |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| |
| str << prefix << iter->index; |
| } |
| |
| return str.str(); |
| } |
| |
| static void genShaderSources(const ProgramSpec &spec, std::string &vertSource, std::string &fragSource, |
| bool pointSizeRequired) |
| { |
| std::ostringstream vtx; |
| std::ostringstream frag; |
| bool addPointSize = spec.isPointSizeUsed(); |
| |
| vtx << "#version 300 es\n" |
| << "in highp vec4 a_position;\n"; |
| frag << "#version 300 es\n" |
| << "layout(location = 0) out mediump vec4 o_color;\n" |
| << "uniform highp vec4 u_scale;\n" |
| << "uniform highp vec4 u_bias;\n"; |
| |
| if (addPointSize) |
| vtx << "in highp float a_pointSize;\n"; |
| |
| // Declare attributes. |
| for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++) |
| { |
| const char *name = var->name.c_str(); |
| const glu::VarType &type = var->type; |
| |
| for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&type); |
| vecIter != glu::VectorTypeIterator::end(&type); vecIter++) |
| { |
| glu::VarType attribType = glu::getVarType(type, vecIter.getPath()); |
| string attribName = getAttributeName(name, vecIter.getPath()); |
| |
| vtx << "in " << glu::declare(attribType, attribName.c_str()) << ";\n"; |
| } |
| } |
| |
| // Declare vayrings. |
| for (int ndx = 0; ndx < 2; ndx++) |
| { |
| const char *inout = ndx ? "in" : "out"; |
| std::ostringstream &str = ndx ? frag : vtx; |
| |
| // Declare structs that have type name. |
| for (vector<glu::StructType *>::const_iterator structIter = spec.getStructs().begin(); |
| structIter != spec.getStructs().end(); structIter++) |
| { |
| const glu::StructType *structPtr = *structIter; |
| if (structPtr->hasTypeName()) |
| str << glu::declare(structPtr) << ";\n"; |
| } |
| |
| for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++) |
| str << getInterpolationName(var->interpolation) << " " << inout << " " |
| << glu::declare(var->type, var->name.c_str()) << ";\n"; |
| } |
| |
| vtx << "\nvoid main (void)\n{\n" |
| << "\tgl_Position = a_position;\n"; |
| frag << "\nvoid main (void)\n{\n" |
| << "\thighp vec4 res = vec4(0.0);\n"; |
| |
| if (addPointSize) |
| vtx << "\tgl_PointSize = a_pointSize;\n"; |
| else if (pointSizeRequired) |
| vtx << "\tgl_PointSize = 1.0;\n"; |
| |
| // Generate assignments / usage. |
| for (vector<Varying>::const_iterator var = spec.getVaryings().begin(); var != spec.getVaryings().end(); var++) |
| { |
| const char *name = var->name.c_str(); |
| const glu::VarType &type = var->type; |
| |
| for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&type); |
| vecIter != glu::VectorTypeIterator::end(&type); vecIter++) |
| { |
| glu::VarType subType = glu::getVarType(type, vecIter.getPath()); |
| string attribName = getAttributeName(name, vecIter.getPath()); |
| |
| DE_ASSERT(subType.isBasicType() && glu::isDataTypeScalarOrVector(subType.getBasicType())); |
| |
| // Vertex: assign from attribute. |
| vtx << "\t" << name << vecIter << " = " << attribName << ";\n"; |
| |
| // Fragment: add to res variable. |
| int scalarSize = glu::getDataTypeScalarSize(subType.getBasicType()); |
| |
| frag << "\tres += "; |
| if (scalarSize == 1) |
| frag << "vec4(" << name << vecIter << ")"; |
| else if (scalarSize == 2) |
| frag << "vec2(" << name << vecIter << ").xxyy"; |
| else if (scalarSize == 3) |
| frag << "vec3(" << name << vecIter << ").xyzx"; |
| else if (scalarSize == 4) |
| frag << "vec4(" << name << vecIter << ")"; |
| |
| frag << ";\n"; |
| } |
| } |
| |
| frag << "\to_color = res * u_scale + u_bias;\n"; |
| |
| vtx << "}\n"; |
| frag << "}\n"; |
| |
| vertSource = vtx.str(); |
| fragSource = frag.str(); |
| } |
| |
| static glu::ShaderProgram *createVertexCaptureProgram(const glu::RenderContext &context, const ProgramSpec &spec, |
| uint32_t bufferMode, uint32_t primitiveType) |
| { |
| std::string vertSource, fragSource; |
| |
| genShaderSources(spec, vertSource, fragSource, primitiveType == GL_POINTS /* Is point size required? */); |
| |
| return new glu::ShaderProgram(context, glu::ProgramSources() |
| << glu::VertexSource(vertSource) << glu::FragmentSource(fragSource) |
| << glu::TransformFeedbackVaryings<vector<string>::const_iterator>( |
| spec.getTransformFeedbackVaryings().begin(), |
| spec.getTransformFeedbackVaryings().end()) |
| << glu::TransformFeedbackMode(bufferMode)); |
| } |
| |
| // Helpers. |
| |
| static void computeInputLayout(vector<Attribute> &attributes, int &inputStride, const vector<Varying> &varyings, |
| bool usePointSize) |
| { |
| inputStride = 0; |
| |
| // Add position. |
| attributes.push_back( |
| Attribute("a_position", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), inputStride)); |
| inputStride += 4 * (int)sizeof(uint32_t); |
| |
| if (usePointSize) |
| { |
| attributes.push_back( |
| Attribute("a_pointSize", glu::VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), inputStride)); |
| inputStride += 1 * (int)sizeof(uint32_t); |
| } |
| |
| // Compute attribute vector. |
| for (vector<Varying>::const_iterator var = varyings.begin(); var != varyings.end(); var++) |
| { |
| for (glu::VectorTypeIterator vecIter = glu::VectorTypeIterator::begin(&var->type); |
| vecIter != glu::VectorTypeIterator::end(&var->type); vecIter++) |
| { |
| glu::VarType type = vecIter.getType(); |
| string name = getAttributeName(var->name.c_str(), vecIter.getPath()); |
| |
| attributes.push_back(Attribute(name, type, inputStride)); |
| inputStride += glu::getDataTypeScalarSize(type.getBasicType()) * (int)sizeof(uint32_t); |
| } |
| } |
| } |
| |
| static void computeTransformFeedbackOutputs(vector<Output> &transformFeedbackOutputs, |
| const vector<Attribute> &attributes, const vector<Varying> &varyings, |
| const vector<string> &transformFeedbackVaryings, uint32_t bufferMode) |
| { |
| int accumulatedSize = 0; |
| |
| transformFeedbackOutputs.resize(transformFeedbackVaryings.size()); |
| for (int varNdx = 0; varNdx < (int)transformFeedbackVaryings.size(); varNdx++) |
| { |
| const string &name = transformFeedbackVaryings[varNdx]; |
| int bufNdx = (bufferMode == GL_SEPARATE_ATTRIBS ? varNdx : 0); |
| int offset = (bufferMode == GL_SEPARATE_ATTRIBS ? 0 : accumulatedSize); |
| Output &output = transformFeedbackOutputs[varNdx]; |
| |
| output.name = name; |
| output.bufferNdx = bufNdx; |
| output.offset = offset; |
| |
| if (name == "gl_Position") |
| { |
| const Attribute *posIn = |
| &(*std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_position"))); |
| output.type = posIn->type; |
| output.inputs.push_back(posIn); |
| } |
| else if (name == "gl_PointSize") |
| { |
| const Attribute *sizeIn = |
| &(*std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_pointSize"))); |
| output.type = sizeIn->type; |
| output.inputs.push_back(sizeIn); |
| } |
| else |
| { |
| string varName = glu::parseVariableName(name.c_str()); |
| const Varying &varying = *std::find_if(varyings.begin(), varyings.end(), VaryingNameEquals(varName)); |
| glu::TypeComponentVector varPath; |
| |
| glu::parseTypePath(name.c_str(), varying.type, varPath); |
| |
| output.type = glu::getVarType(varying.type, varPath); |
| |
| // Add all vectorized attributes as inputs. |
| for (glu::VectorTypeIterator iter = glu::VectorTypeIterator::begin(&output.type); |
| iter != glu::VectorTypeIterator::end(&output.type); iter++) |
| { |
| // Full path. |
| glu::TypeComponentVector fullPath(varPath.size() + iter.getPath().size()); |
| |
| std::copy(varPath.begin(), varPath.end(), fullPath.begin()); |
| std::copy(iter.getPath().begin(), iter.getPath().end(), fullPath.begin() + varPath.size()); |
| |
| string attribName = getAttributeName(varName.c_str(), fullPath); |
| const Attribute *attrib = |
| &(*std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals(attribName))); |
| |
| output.inputs.push_back(attrib); |
| } |
| } |
| |
| accumulatedSize += output.type.getScalarSize() * (int)sizeof(uint32_t); |
| } |
| } |
| |
| static uint32_t signExtend(uint32_t value, uint32_t numBits) |
| { |
| DE_ASSERT(numBits >= 1u && numBits <= 32u); |
| if (numBits == 32u) |
| return value; |
| else if ((value & (1u << (numBits - 1u))) == 0u) |
| return value; |
| else |
| return value | ~((1u << numBits) - 1u); |
| } |
| |
| static void genAttributeData(const Attribute &attrib, uint8_t *basePtr, int stride, int numElements, de::Random &rnd) |
| { |
| const int elementSize = (int)sizeof(uint32_t); |
| const bool isFloat = glu::isDataTypeFloatOrVec(attrib.type.getBasicType()); |
| const bool isInt = glu::isDataTypeIntOrIVec(attrib.type.getBasicType()); |
| const bool isUint = glu::isDataTypeUintOrUVec(attrib.type.getBasicType()); |
| const glu::Precision precision = attrib.type.getPrecision(); |
| const int numComps = glu::getDataTypeScalarSize(attrib.type.getBasicType()); |
| |
| for (int elemNdx = 0; elemNdx < numElements; elemNdx++) |
| { |
| for (int compNdx = 0; compNdx < numComps; compNdx++) |
| { |
| int offset = attrib.offset + elemNdx * stride + compNdx * elementSize; |
| if (isFloat) |
| { |
| float *comp = (float *)(basePtr + offset); |
| switch (precision) |
| { |
| case glu::PRECISION_LOWP: |
| *comp = 0.0f + 0.25f * (float)rnd.getInt(0, 4); |
| break; |
| case glu::PRECISION_MEDIUMP: |
| *comp = rnd.getFloat(-1e3f, 1e3f); |
| break; |
| case glu::PRECISION_HIGHP: |
| *comp = rnd.getFloat(-1e5f, 1e5f); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else if (isInt) |
| { |
| int *comp = (int *)(basePtr + offset); |
| switch (precision) |
| { |
| case glu::PRECISION_LOWP: |
| *comp = (int)signExtend(rnd.getUint32() & 0xff, 8); |
| break; |
| case glu::PRECISION_MEDIUMP: |
| *comp = (int)signExtend(rnd.getUint32() & 0xffff, 16); |
| break; |
| case glu::PRECISION_HIGHP: |
| *comp = (int)rnd.getUint32(); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else if (isUint) |
| { |
| uint32_t *comp = (uint32_t *)(basePtr + offset); |
| switch (precision) |
| { |
| case glu::PRECISION_LOWP: |
| *comp = rnd.getUint32() & 0xff; |
| break; |
| case glu::PRECISION_MEDIUMP: |
| *comp = rnd.getUint32() & 0xffff; |
| break; |
| case glu::PRECISION_HIGHP: |
| *comp = rnd.getUint32(); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| DE_ASSERT(false); |
| } |
| } |
| } |
| |
| static void genInputData(const vector<Attribute> &attributes, int numInputs, int inputStride, uint8_t *inputBasePtr, |
| de::Random &rnd) |
| { |
| // Random positions. |
| const Attribute &position = *std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_position")); |
| |
| for (int ndx = 0; ndx < numInputs; ndx++) |
| { |
| uint8_t *ptr = inputBasePtr + position.offset + inputStride * ndx; |
| *((float *)(ptr + 0)) = rnd.getFloat(-1.2f, 1.2f); |
| *((float *)(ptr + 4)) = rnd.getFloat(-1.2f, 1.2f); |
| *((float *)(ptr + 8)) = rnd.getFloat(-1.2f, 1.2f); |
| *((float *)(ptr + 12)) = rnd.getFloat(0.1f, 2.0f); |
| } |
| |
| // Point size. |
| vector<Attribute>::const_iterator pointSizePos = |
| std::find_if(attributes.begin(), attributes.end(), AttributeNameEquals("a_pointSize")); |
| if (pointSizePos != attributes.end()) |
| { |
| for (int ndx = 0; ndx < numInputs; ndx++) |
| { |
| uint8_t *ptr = inputBasePtr + pointSizePos->offset + inputStride * ndx; |
| *((float *)ptr) = rnd.getFloat(1.0f, 8.0f); |
| } |
| } |
| |
| // Random data for rest of components. |
| for (vector<Attribute>::const_iterator attrib = attributes.begin(); attrib != attributes.end(); attrib++) |
| { |
| if (attrib->name == "a_position" || attrib->name == "a_pointSize") |
| continue; |
| |
| genAttributeData(*attrib, inputBasePtr, inputStride, numInputs, rnd); |
| } |
| } |
| |
| static uint32_t getTransformFeedbackOutputCount(uint32_t primitiveType, int numElements) |
| { |
| switch (primitiveType) |
| { |
| case GL_TRIANGLES: |
| return numElements - numElements % 3; |
| case GL_TRIANGLE_STRIP: |
| return de::max(0, numElements - 2) * 3; |
| case GL_TRIANGLE_FAN: |
| return de::max(0, numElements - 2) * 3; |
| case GL_LINES: |
| return numElements - numElements % 2; |
| case GL_LINE_STRIP: |
| return de::max(0, numElements - 1) * 2; |
| case GL_LINE_LOOP: |
| return numElements > 1 ? numElements * 2 : 0; |
| case GL_POINTS: |
| return numElements; |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| static uint32_t getTransformFeedbackPrimitiveCount(uint32_t primitiveType, int numElements) |
| { |
| switch (primitiveType) |
| { |
| case GL_TRIANGLES: |
| return numElements / 3; |
| case GL_TRIANGLE_STRIP: |
| return de::max(0, numElements - 2); |
| case GL_TRIANGLE_FAN: |
| return de::max(0, numElements - 2); |
| case GL_LINES: |
| return numElements / 2; |
| case GL_LINE_STRIP: |
| return de::max(0, numElements - 1); |
| case GL_LINE_LOOP: |
| return numElements > 1 ? numElements : 0; |
| case GL_POINTS: |
| return numElements; |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| static uint32_t getTransformFeedbackPrimitiveMode(uint32_t primitiveType) |
| { |
| switch (primitiveType) |
| { |
| case GL_TRIANGLES: |
| case GL_TRIANGLE_STRIP: |
| case GL_TRIANGLE_FAN: |
| return GL_TRIANGLES; |
| |
| case GL_LINES: |
| case GL_LINE_LOOP: |
| case GL_LINE_STRIP: |
| return GL_LINES; |
| |
| case GL_POINTS: |
| return GL_POINTS; |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| static int getAttributeIndex(uint32_t primitiveType, int numInputs, int outNdx) |
| { |
| switch (primitiveType) |
| { |
| case GL_TRIANGLES: |
| return outNdx; |
| case GL_LINES: |
| return outNdx; |
| case GL_POINTS: |
| return outNdx; |
| |
| case GL_TRIANGLE_STRIP: |
| { |
| int triNdx = outNdx / 3; |
| int vtxNdx = outNdx % 3; |
| return (triNdx % 2 != 0 && vtxNdx < 2) ? (triNdx + 1 - vtxNdx) : (triNdx + vtxNdx); |
| } |
| |
| case GL_TRIANGLE_FAN: |
| return (outNdx % 3 != 0) ? (outNdx / 3 + outNdx % 3) : 0; |
| |
| case GL_LINE_STRIP: |
| return outNdx / 2 + outNdx % 2; |
| |
| case GL_LINE_LOOP: |
| { |
| int inNdx = outNdx / 2 + outNdx % 2; |
| return inNdx < numInputs ? inNdx : 0; |
| } |
| |
| default: |
| DE_ASSERT(false); |
| return 0; |
| } |
| } |
| |
| static bool compareTransformFeedbackOutput(tcu::TestLog &log, uint32_t primitiveType, const Output &output, |
| int numInputs, const uint8_t *inBasePtr, int inStride, |
| const uint8_t *outBasePtr, int outStride) |
| { |
| bool isOk = true; |
| int outOffset = output.offset; |
| |
| for (int attrNdx = 0; attrNdx < (int)output.inputs.size(); attrNdx++) |
| { |
| const Attribute &attribute = *output.inputs[attrNdx]; |
| glu::DataType type = attribute.type.getBasicType(); |
| int numComponents = glu::getDataTypeScalarSize(type); |
| glu::Precision precision = attribute.type.getPrecision(); |
| glu::DataType scalarType = glu::getDataTypeScalarType(type); |
| int numOutputs = getTransformFeedbackOutputCount(primitiveType, numInputs); |
| |
| for (int outNdx = 0; outNdx < numOutputs; outNdx++) |
| { |
| int inNdx = getAttributeIndex(primitiveType, numInputs, outNdx); |
| |
| for (int compNdx = 0; compNdx < numComponents; compNdx++) |
| { |
| const uint8_t *inPtr = inBasePtr + inStride * inNdx + attribute.offset + compNdx * sizeof(uint32_t); |
| const uint8_t *outPtr = outBasePtr + outStride * outNdx + outOffset + compNdx * sizeof(uint32_t); |
| uint32_t inVal = *(const uint32_t *)inPtr; |
| uint32_t outVal = *(const uint32_t *)outPtr; |
| bool isEqual = false; |
| |
| if (scalarType == glu::TYPE_FLOAT) |
| { |
| // ULP comparison is used for highp and mediump. Lowp uses threshold-comparison. |
| switch (precision) |
| { |
| case glu::PRECISION_HIGHP: |
| isEqual = de::abs((int)inVal - (int)outVal) < 2; |
| break; |
| case glu::PRECISION_MEDIUMP: |
| isEqual = de::abs((int)inVal - (int)outVal) < 2 + (1 << 13); |
| break; |
| case glu::PRECISION_LOWP: |
| { |
| float inF = *(const float *)inPtr; |
| float outF = *(const float *)outPtr; |
| isEqual = de::abs(inF - outF) < 0.1f; |
| break; |
| } |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| isEqual = (inVal == outVal); // Bit-exact match required for integer types. |
| |
| if (!isEqual) |
| { |
| log << TestLog::Message << "Mismatch in " << output.name << " (" << attribute.name |
| << "), output = " << outNdx << ", input = " << inNdx << ", component = " << compNdx |
| << TestLog::EndMessage; |
| isOk = false; |
| break; |
| } |
| } |
| |
| if (!isOk) |
| break; |
| } |
| |
| if (!isOk) |
| break; |
| |
| outOffset += numComponents * (int)sizeof(uint32_t); |
| } |
| |
| return isOk; |
| } |
| |
| static int computeTransformFeedbackPrimitiveCount(uint32_t primitiveType, const DrawCall *first, const DrawCall *end) |
| { |
| int primCount = 0; |
| |
| for (const DrawCall *call = first; call != end; ++call) |
| { |
| if (call->transformFeedbackEnabled) |
| primCount += getTransformFeedbackPrimitiveCount(primitiveType, call->numElements); |
| } |
| |
| return primCount; |
| } |
| |
| static void writeBufferGuard(const glw::Functions &gl, uint32_t target, int bufferSize, int guardSize) |
| { |
| uint8_t *ptr = (uint8_t *)gl.mapBufferRange(target, bufferSize, guardSize, GL_MAP_WRITE_BIT); |
| if (ptr) |
| deMemset(ptr, 0xcd, guardSize); |
| gl.unmapBuffer(target); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "guardband write"); |
| } |
| |
| static bool verifyGuard(const uint8_t *ptr, int guardSize) |
| { |
| for (int ndx = 0; ndx < guardSize; ndx++) |
| { |
| if (ptr[ndx] != 0xcd) |
| return false; |
| } |
| return true; |
| } |
| |
| static void logTransformFeedbackVaryings(TestLog &log, const glw::Functions &gl, uint32_t program) |
| { |
| int numTfVaryings = 0; |
| int maxNameLen = 0; |
| |
| gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYINGS, &numTfVaryings); |
| gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &maxNameLen); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "Query TF varyings"); |
| |
| log << TestLog::Message << "GL_TRANSFORM_FEEDBACK_VARYINGS = " << numTfVaryings << TestLog::EndMessage; |
| |
| vector<char> nameBuf(maxNameLen + 1); |
| |
| for (int ndx = 0; ndx < numTfVaryings; ndx++) |
| { |
| glw::GLsizei size = 0; |
| glw::GLenum type = 0; |
| |
| gl.getTransformFeedbackVarying(program, ndx, (glw::GLsizei)nameBuf.size(), DE_NULL, &size, &type, &nameBuf[0]); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetTransformFeedbackVarying()"); |
| |
| const glu::DataType dataType = glu::getDataTypeFromGLType(type); |
| const std::string typeName = dataType != glu::TYPE_LAST ? |
| std::string(glu::getDataTypeName(dataType)) : |
| (std::string("unknown(") + tcu::toHex(type).toString() + ")"); |
| |
| log << TestLog::Message << (const char *)&nameBuf[0] << ": " << typeName << "[" << size << "]" |
| << TestLog::EndMessage; |
| } |
| } |
| |
| class TransformFeedbackCase : public TestCase |
| { |
| public: |
| TransformFeedbackCase(Context &context, const char *name, const char *desc, uint32_t bufferMode, |
| uint32_t primitiveType); |
| ~TransformFeedbackCase(void); |
| |
| void init(void); |
| void deinit(void); |
| IterateResult iterate(void); |
| |
| protected: |
| ProgramSpec m_progSpec; |
| uint32_t m_bufferMode; |
| uint32_t m_primitiveType; |
| |
| private: |
| TransformFeedbackCase(const TransformFeedbackCase &other); |
| TransformFeedbackCase &operator=(const TransformFeedbackCase &other); |
| |
| bool runTest(const DrawCall *first, const DrawCall *end, uint32_t seed); |
| |
| // Derived from ProgramSpec in init() |
| int m_inputStride; |
| vector<Attribute> m_attributes; |
| vector<Output> m_transformFeedbackOutputs; |
| vector<int> m_bufferStrides; |
| |
| // GL state. |
| glu::ShaderProgram *m_program; |
| glu::TransformFeedback *m_transformFeedback; |
| vector<uint32_t> m_outputBuffers; |
| |
| int m_iterNdx; |
| }; |
| |
| TransformFeedbackCase::TransformFeedbackCase(Context &context, const char *name, const char *desc, uint32_t bufferMode, |
| uint32_t primitiveType) |
| : TestCase(context, name, desc) |
| , m_bufferMode(bufferMode) |
| , m_primitiveType(primitiveType) |
| , m_inputStride(0) |
| , m_program(DE_NULL) |
| , m_transformFeedback(DE_NULL) |
| , m_iterNdx(0) |
| { |
| } |
| |
| TransformFeedbackCase::~TransformFeedbackCase(void) |
| { |
| TransformFeedbackCase::deinit(); |
| } |
| |
| static bool hasArraysInTFVaryings(const ProgramSpec &spec) |
| { |
| for (vector<string>::const_iterator tfVar = spec.getTransformFeedbackVaryings().begin(); |
| tfVar != spec.getTransformFeedbackVaryings().end(); ++tfVar) |
| { |
| string varName = glu::parseVariableName(tfVar->c_str()); |
| vector<Varying>::const_iterator varIter = |
| std::find_if(spec.getVaryings().begin(), spec.getVaryings().end(), VaryingNameEquals(varName)); |
| |
| if (varName == "gl_Position" || varName == "gl_PointSize") |
| continue; |
| |
| DE_ASSERT(varIter != spec.getVaryings().end()); |
| |
| if (varIter->type.isArrayType()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void TransformFeedbackCase::init(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| |
| DE_ASSERT(!m_program); |
| m_program = createVertexCaptureProgram(m_context.getRenderContext(), m_progSpec, m_bufferMode, m_primitiveType); |
| |
| log << *m_program; |
| if (!m_program->isOk()) |
| { |
| const bool linkFail = m_program->getShaderInfo(glu::SHADERTYPE_VERTEX).compileOk && |
| m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk && |
| !m_program->getProgramInfo().linkOk; |
| |
| if (linkFail) |
| { |
| if (!isProgramSupported(gl, m_progSpec, m_bufferMode)) |
| throw tcu::NotSupportedError("Implementation limits execeeded", "", __FILE__, __LINE__); |
| else if (hasArraysInTFVaryings(m_progSpec)) |
| throw tcu::NotSupportedError("Capturing arrays is not supported (undefined in specification)", "", |
| __FILE__, __LINE__); |
| else |
| throw tcu::TestError("Link failed", "", __FILE__, __LINE__); |
| } |
| else |
| throw tcu::TestError("Compile failed", "", __FILE__, __LINE__); |
| } |
| |
| log << TestLog::Message << "Transform feedback varyings: " |
| << tcu::formatArray(m_progSpec.getTransformFeedbackVaryings().begin(), |
| m_progSpec.getTransformFeedbackVaryings().end()) |
| << TestLog::EndMessage; |
| |
| // Print out transform feedback points reported by GL. |
| log << TestLog::Message << "Transform feedback varyings reported by compiler:" << TestLog::EndMessage; |
| logTransformFeedbackVaryings(log, gl, m_program->getProgram()); |
| |
| // Compute input specification. |
| computeInputLayout(m_attributes, m_inputStride, m_progSpec.getVaryings(), m_progSpec.isPointSizeUsed()); |
| |
| // Build list of varyings used in transform feedback. |
| computeTransformFeedbackOutputs(m_transformFeedbackOutputs, m_attributes, m_progSpec.getVaryings(), |
| m_progSpec.getTransformFeedbackVaryings(), m_bufferMode); |
| DE_ASSERT(!m_transformFeedbackOutputs.empty()); |
| |
| // Buffer strides. |
| DE_ASSERT(m_bufferStrides.empty()); |
| if (m_bufferMode == GL_SEPARATE_ATTRIBS) |
| { |
| for (vector<Output>::const_iterator outIter = m_transformFeedbackOutputs.begin(); |
| outIter != m_transformFeedbackOutputs.end(); outIter++) |
| m_bufferStrides.push_back(outIter->type.getScalarSize() * (int)sizeof(uint32_t)); |
| } |
| else |
| { |
| int totalSize = 0; |
| for (vector<Output>::const_iterator outIter = m_transformFeedbackOutputs.begin(); |
| outIter != m_transformFeedbackOutputs.end(); outIter++) |
| totalSize += outIter->type.getScalarSize() * (int)sizeof(uint32_t); |
| |
| m_bufferStrides.push_back(totalSize); |
| } |
| |
| // \note Actual storage is allocated in iterate(). |
| m_outputBuffers.resize(m_bufferStrides.size()); |
| gl.genBuffers((glw::GLsizei)m_outputBuffers.size(), &m_outputBuffers[0]); |
| |
| DE_ASSERT(!m_transformFeedback); |
| m_transformFeedback = new glu::TransformFeedback(m_context.getRenderContext()); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "init"); |
| |
| m_iterNdx = 0; |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| |
| void TransformFeedbackCase::deinit(void) |
| { |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| |
| if (!m_outputBuffers.empty()) |
| { |
| gl.deleteBuffers((glw::GLsizei)m_outputBuffers.size(), &m_outputBuffers[0]); |
| m_outputBuffers.clear(); |
| } |
| |
| delete m_transformFeedback; |
| m_transformFeedback = DE_NULL; |
| |
| delete m_program; |
| m_program = DE_NULL; |
| |
| // Clean up state. |
| m_attributes.clear(); |
| m_transformFeedbackOutputs.clear(); |
| m_bufferStrides.clear(); |
| m_inputStride = 0; |
| } |
| |
| TransformFeedbackCase::IterateResult TransformFeedbackCase::iterate(void) |
| { |
| // Test cases. |
| static const DrawCall s_elemCount1[] = {DrawCall(1, true)}; |
| static const DrawCall s_elemCount2[] = {DrawCall(2, true)}; |
| static const DrawCall s_elemCount3[] = {DrawCall(3, true)}; |
| static const DrawCall s_elemCount4[] = {DrawCall(4, true)}; |
| static const DrawCall s_elemCount123[] = {DrawCall(123, true)}; |
| static const DrawCall s_basicPause1[] = {DrawCall(64, true), DrawCall(64, false), DrawCall(64, true)}; |
| static const DrawCall s_basicPause2[] = {DrawCall(13, true), DrawCall(5, true), DrawCall(17, false), |
| DrawCall(3, true), DrawCall(7, false)}; |
| static const DrawCall s_startPaused[] = {DrawCall(123, false), DrawCall(123, true)}; |
| static const DrawCall s_random1[] = {DrawCall(65, true), DrawCall(135, false), DrawCall(74, true), |
| DrawCall(16, false), DrawCall(226, false), DrawCall(9, true), |
| DrawCall(174, false)}; |
| static const DrawCall s_random2[] = {DrawCall(217, true), DrawCall(171, true), DrawCall(147, true), |
| DrawCall(152, false), DrawCall(55, true)}; |
| |
| static const struct |
| { |
| const DrawCall *calls; |
| int numCalls; |
| } s_iterations[] = { |
| #define ITER(ARR) {ARR, DE_LENGTH_OF_ARRAY(ARR)} |
| ITER(s_elemCount1), ITER(s_elemCount2), ITER(s_elemCount3), ITER(s_elemCount4), |
| ITER(s_elemCount123), ITER(s_basicPause1), ITER(s_basicPause2), ITER(s_startPaused), |
| ITER(s_random1), ITER(s_random2) |
| #undef ITER |
| }; |
| |
| TestLog &log = m_testCtx.getLog(); |
| bool isOk = true; |
| uint32_t seed = deStringHash(getName()) ^ deInt32Hash(m_iterNdx); |
| int numIterations = DE_LENGTH_OF_ARRAY(s_iterations); |
| const DrawCall *first = s_iterations[m_iterNdx].calls; |
| const DrawCall *end = s_iterations[m_iterNdx].calls + s_iterations[m_iterNdx].numCalls; |
| |
| std::string sectionName = std::string("Iteration") + de::toString(m_iterNdx + 1); |
| std::string sectionDesc = |
| std::string("Iteration ") + de::toString(m_iterNdx + 1) + " / " + de::toString(numIterations); |
| tcu::ScopedLogSection section(log, sectionName, sectionDesc); |
| |
| log << TestLog::Message << "Testing " << s_iterations[m_iterNdx].numCalls |
| << " draw calls, (element count, TF state): " << tcu::formatArray(first, end) << TestLog::EndMessage; |
| |
| isOk = runTest(first, end, seed); |
| |
| if (!isOk) |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed"); |
| |
| m_iterNdx += 1; |
| return (isOk && m_iterNdx < numIterations) ? CONTINUE : STOP; |
| } |
| |
| bool TransformFeedbackCase::runTest(const DrawCall *first, const DrawCall *end, uint32_t seed) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const glw::Functions &gl = m_context.getRenderContext().getFunctions(); |
| de::Random rnd(seed); |
| int numInputs = 0; //!< Sum of element counts in calls. |
| int numOutputs = 0; //!< Sum of output counts for calls that have transform feedback enabled. |
| int width = m_context.getRenderContext().getRenderTarget().getWidth(); |
| int height = m_context.getRenderContext().getRenderTarget().getHeight(); |
| int viewportW = de::min((int)VIEWPORT_WIDTH, width); |
| int viewportH = de::min((int)VIEWPORT_HEIGHT, height); |
| int viewportX = rnd.getInt(0, width - viewportW); |
| int viewportY = rnd.getInt(0, height - viewportH); |
| tcu::Surface frameWithTf(viewportW, viewportH); |
| tcu::Surface frameWithoutTf(viewportW, viewportH); |
| glu::Query primitiveQuery(m_context.getRenderContext()); |
| bool outputsOk = true; |
| bool imagesOk = true; |
| bool queryOk = true; |
| |
| // Compute totals. |
| for (const DrawCall *call = first; call != end; call++) |
| { |
| numInputs += call->numElements; |
| numOutputs += |
| call->transformFeedbackEnabled ? getTransformFeedbackOutputCount(m_primitiveType, call->numElements) : 0; |
| } |
| |
| // Input data. |
| vector<uint8_t> inputData(m_inputStride * numInputs); |
| genInputData(m_attributes, numInputs, m_inputStride, &inputData[0], rnd); |
| |
| gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformFeedback->get()); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback()"); |
| |
| // Allocate storage for transform feedback output buffers and bind to targets. |
| for (int bufNdx = 0; bufNdx < (int)m_outputBuffers.size(); bufNdx++) |
| { |
| uint32_t buffer = m_outputBuffers[bufNdx]; |
| int stride = m_bufferStrides[bufNdx]; |
| int target = bufNdx; |
| int size = stride * numOutputs; |
| int guardSize = stride * BUFFER_GUARD_MULTIPLIER; |
| const uint32_t usage = GL_DYNAMIC_READ; |
| |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer); |
| gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, size + guardSize, DE_NULL, usage); |
| writeBufferGuard(gl, GL_TRANSFORM_FEEDBACK_BUFFER, size, guardSize); |
| |
| // \todo [2012-07-30 pyry] glBindBufferRange()? |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, target, buffer); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "transform feedback buffer setup"); |
| } |
| |
| // Setup attributes. |
| for (vector<Attribute>::const_iterator attrib = m_attributes.begin(); attrib != m_attributes.end(); attrib++) |
| { |
| int loc = gl.getAttribLocation(m_program->getProgram(), attrib->name.c_str()); |
| glu::DataType scalarType = glu::getDataTypeScalarType(attrib->type.getBasicType()); |
| int numComponents = glu::getDataTypeScalarSize(attrib->type.getBasicType()); |
| const void *ptr = &inputData[0] + attrib->offset; |
| |
| if (loc >= 0) |
| { |
| gl.enableVertexAttribArray(loc); |
| |
| if (scalarType == glu::TYPE_FLOAT) |
| gl.vertexAttribPointer(loc, numComponents, GL_FLOAT, GL_FALSE, m_inputStride, ptr); |
| else if (scalarType == glu::TYPE_INT) |
| gl.vertexAttribIPointer(loc, numComponents, GL_INT, m_inputStride, ptr); |
| else if (scalarType == glu::TYPE_UINT) |
| gl.vertexAttribIPointer(loc, numComponents, GL_UNSIGNED_INT, m_inputStride, ptr); |
| } |
| } |
| |
| // Setup viewport. |
| gl.viewport(viewportX, viewportY, viewportW, viewportH); |
| |
| // Setup program. |
| gl.useProgram(m_program->getProgram()); |
| |
| gl.uniform4fv(gl.getUniformLocation(m_program->getProgram(), "u_scale"), 1, tcu::Vec4(0.01f).getPtr()); |
| gl.uniform4fv(gl.getUniformLocation(m_program->getProgram(), "u_bias"), 1, tcu::Vec4(0.5f).getPtr()); |
| |
| // Enable query. |
| gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, *primitiveQuery); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)"); |
| |
| // Draw. |
| { |
| int offset = 0; |
| bool tfEnabled = true; |
| |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| |
| gl.beginTransformFeedback(getTransformFeedbackPrimitiveMode(m_primitiveType)); |
| |
| for (const DrawCall *call = first; call != end; call++) |
| { |
| // Pause or resume transform feedback if necessary. |
| if (call->transformFeedbackEnabled != tfEnabled) |
| { |
| if (call->transformFeedbackEnabled) |
| gl.resumeTransformFeedback(); |
| else |
| gl.pauseTransformFeedback(); |
| tfEnabled = call->transformFeedbackEnabled; |
| } |
| |
| gl.drawArrays(m_primitiveType, offset, call->numElements); |
| offset += call->numElements; |
| } |
| |
| // Resume feedback before finishing it. |
| if (!tfEnabled) |
| gl.resumeTransformFeedback(); |
| |
| gl.endTransformFeedback(); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "render"); |
| } |
| |
| gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN)"); |
| |
| // Check and log query status right after submit |
| { |
| uint32_t available = GL_FALSE; |
| gl.getQueryObjectuiv(*primitiveQuery, GL_QUERY_RESULT_AVAILABLE, &available); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv()"); |
| |
| log << TestLog::Message << "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN status after submit: " |
| << (available != GL_FALSE ? "GL_TRUE" : "GL_FALSE") << TestLog::EndMessage; |
| } |
| |
| // Compare result buffers. |
| for (int bufferNdx = 0; bufferNdx < (int)m_outputBuffers.size(); bufferNdx++) |
| { |
| uint32_t buffer = m_outputBuffers[bufferNdx]; |
| int stride = m_bufferStrides[bufferNdx]; |
| int size = stride * numOutputs; |
| int guardSize = stride * BUFFER_GUARD_MULTIPLIER; |
| const void *bufPtr = DE_NULL; |
| |
| // Bind buffer for reading. |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffer); |
| bufPtr = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, size + guardSize, GL_MAP_READ_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "mapping buffer"); |
| |
| // Verify all output variables that are written to this buffer. |
| for (vector<Output>::const_iterator out = m_transformFeedbackOutputs.begin(); |
| out != m_transformFeedbackOutputs.end(); out++) |
| { |
| if (out->bufferNdx != bufferNdx) |
| continue; |
| |
| int inputOffset = 0; |
| int outputOffset = 0; |
| |
| // Process all draw calls and check ones with transform feedback enabled. |
| for (const DrawCall *call = first; call != end; call++) |
| { |
| if (call->transformFeedbackEnabled) |
| { |
| const uint8_t *inputPtr = &inputData[0] + inputOffset * m_inputStride; |
| const uint8_t *outputPtr = (const uint8_t *)bufPtr + outputOffset * stride; |
| |
| if (!compareTransformFeedbackOutput(log, m_primitiveType, *out, call->numElements, inputPtr, |
| m_inputStride, outputPtr, stride)) |
| { |
| outputsOk = false; |
| break; |
| } |
| } |
| |
| inputOffset += call->numElements; |
| outputOffset += call->transformFeedbackEnabled ? |
| getTransformFeedbackOutputCount(m_primitiveType, call->numElements) : |
| 0; |
| } |
| } |
| |
| // Verify guardband. |
| if (!verifyGuard((const uint8_t *)bufPtr + size, guardSize)) |
| { |
| log << TestLog::Message << "Error: Transform feedback buffer overrun detected" << TestLog::EndMessage; |
| outputsOk = false; |
| } |
| |
| gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); |
| } |
| |
| // Check status after mapping buffers. |
| { |
| const bool mustBeReady = !m_outputBuffers.empty(); // Mapping buffer forces synchronization. |
| const int expectedCount = computeTransformFeedbackPrimitiveCount(m_primitiveType, first, end); |
| uint32_t available = GL_FALSE; |
| uint32_t numPrimitives = 0; |
| |
| gl.getQueryObjectuiv(*primitiveQuery, GL_QUERY_RESULT_AVAILABLE, &available); |
| gl.getQueryObjectuiv(*primitiveQuery, GL_QUERY_RESULT, &numPrimitives); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv()"); |
| |
| if (!mustBeReady && available == GL_FALSE) |
| { |
| log << TestLog::Message |
| << "ERROR: GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN result not available after mapping buffers!" |
| << TestLog::EndMessage; |
| queryOk = false; |
| } |
| |
| log << TestLog::Message << "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN = " << numPrimitives |
| << TestLog::EndMessage; |
| |
| if ((int)numPrimitives != expectedCount) |
| { |
| log << TestLog::Message << "ERROR: Expected " << expectedCount << " primitives!" << TestLog::EndMessage; |
| queryOk = false; |
| } |
| } |
| |
| // Clear transform feedback state. |
| gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0); |
| for (int bufNdx = 0; bufNdx < (int)m_outputBuffers.size(); bufNdx++) |
| { |
| gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0); |
| gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, bufNdx, 0); |
| } |
| |
| // Read back rendered image. |
| glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, frameWithTf.getAccess()); |
| |
| // Render without transform feedback. |
| { |
| int offset = 0; |
| |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| |
| for (const DrawCall *call = first; call != end; call++) |
| { |
| gl.drawArrays(m_primitiveType, offset, call->numElements); |
| offset += call->numElements; |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "render"); |
| glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, frameWithoutTf.getAccess()); |
| } |
| |
| // Compare images with and without transform feedback. |
| imagesOk = tcu::pixelThresholdCompare(log, "Result", "Image comparison result", frameWithoutTf, frameWithTf, |
| tcu::RGBA(1, 1, 1, 1), tcu::COMPARE_LOG_ON_ERROR); |
| |
| if (imagesOk) |
| m_testCtx.getLog() << TestLog::Message |
| << "Rendering result comparison between TF enabled and TF disabled passed." |
| << TestLog::EndMessage; |
| else |
| m_testCtx.getLog() << TestLog::Message |
| << "ERROR: Rendering result comparison between TF enabled and TF disabled failed!" |
| << TestLog::EndMessage; |
| |
| return outputsOk && imagesOk && queryOk; |
| } |
| |
| // Test cases. |
| |
| class PositionCase : public TransformFeedbackCase |
| { |
| public: |
| PositionCase(Context &context, const char *name, const char *desc, uint32_t bufferType, uint32_t primitiveType) |
| : TransformFeedbackCase(context, name, desc, bufferType, primitiveType) |
| { |
| m_progSpec.addTransformFeedbackVarying("gl_Position"); |
| } |
| }; |
| |
| class PointSizeCase : public TransformFeedbackCase |
| { |
| public: |
| PointSizeCase(Context &context, const char *name, const char *desc, uint32_t bufferType, uint32_t primitiveType) |
| : TransformFeedbackCase(context, name, desc, bufferType, primitiveType) |
| { |
| m_progSpec.addTransformFeedbackVarying("gl_PointSize"); |
| } |
| }; |
| |
| class BasicTypeCase : public TransformFeedbackCase |
| { |
| public: |
| BasicTypeCase(Context &context, const char *name, const char *desc, uint32_t bufferType, uint32_t primitiveType, |
| glu::DataType type, glu::Precision precision, Interpolation interpolation) |
| : TransformFeedbackCase(context, name, desc, bufferType, primitiveType) |
| { |
| m_progSpec.addVarying("v_varA", glu::VarType(type, precision), interpolation); |
| m_progSpec.addVarying("v_varB", glu::VarType(type, precision), interpolation); |
| |
| m_progSpec.addTransformFeedbackVarying("v_varA"); |
| m_progSpec.addTransformFeedbackVarying("v_varB"); |
| } |
| }; |
| |
| class BasicArrayCase : public TransformFeedbackCase |
| { |
| public: |
| BasicArrayCase(Context &context, const char *name, const char *desc, uint32_t bufferType, uint32_t primitiveType, |
| glu::DataType type, glu::Precision precision, Interpolation interpolation) |
| : TransformFeedbackCase(context, name, desc, bufferType, primitiveType) |
| { |
| if (glu::isDataTypeMatrix(type) || m_bufferMode == GL_SEPARATE_ATTRIBS) |
| { |
| // \note For matrix types we need to use reduced array sizes or otherwise we will exceed maximum attribute (16) |
| // or transform feedback component count (64). |
| // On separate attribs mode maximum component count per varying is 4. |
| m_progSpec.addVarying("v_varA", glu::VarType(glu::VarType(type, precision), 1), interpolation); |
| m_progSpec.addVarying("v_varB", glu::VarType(glu::VarType(type, precision), 2), interpolation); |
| } |
| else |
| { |
| m_progSpec.addVarying("v_varA", glu::VarType(glu::VarType(type, precision), 3), interpolation); |
| m_progSpec.addVarying("v_varB", glu::VarType(glu::VarType(type, precision), 4), interpolation); |
| } |
| |
| m_progSpec.addTransformFeedbackVarying("v_varA"); |
| m_progSpec.addTransformFeedbackVarying("v_varB"); |
| } |
| }; |
| |
| class ArrayElementCase : public TransformFeedbackCase |
| { |
| public: |
| ArrayElementCase(Context &context, const char *name, const char *desc, uint32_t bufferType, uint32_t primitiveType, |
| glu::DataType type, glu::Precision precision, Interpolation interpolation) |
| : TransformFeedbackCase(context, name, desc, bufferType, primitiveType) |
| { |
| m_progSpec.addVarying("v_varA", glu::VarType(glu::VarType(type, precision), 3), interpolation); |
| m_progSpec.addVarying("v_varB", glu::VarType(glu::VarType(type, precision), 4), interpolation); |
| |
| m_progSpec.addTransformFeedbackVarying("v_varA[1]"); |
| m_progSpec.addTransformFeedbackVarying("v_varB[0]"); |
| m_progSpec.addTransformFeedbackVarying("v_varB[3]"); |
| } |
| }; |
| |
| class RandomCase : public TransformFeedbackCase |
| { |
| public: |
| RandomCase(Context &context, const char *name, const char *desc, uint32_t bufferType, uint32_t primitiveType, |
| uint32_t seed, bool elementCapture) |
| : TransformFeedbackCase(context, name, desc, bufferType, primitiveType) |
| , m_seed(seed) |
| , m_elementCapture(elementCapture) |
| { |
| } |
| |
| void init(void) |
| { |
| // \note Hard-coded indices and hackery are used when indexing this, beware. |
| static const glu::DataType typeCandidates[] = { |
| glu::TYPE_FLOAT, glu::TYPE_FLOAT_VEC2, glu::TYPE_FLOAT_VEC3, glu::TYPE_FLOAT_VEC4, |
| glu::TYPE_INT, glu::TYPE_INT_VEC2, glu::TYPE_INT_VEC3, glu::TYPE_INT_VEC4, |
| glu::TYPE_UINT, glu::TYPE_UINT_VEC2, glu::TYPE_UINT_VEC3, glu::TYPE_UINT_VEC4, |
| |
| glu::TYPE_FLOAT_MAT2, glu::TYPE_FLOAT_MAT2X3, glu::TYPE_FLOAT_MAT2X4, |
| |
| glu::TYPE_FLOAT_MAT3X2, glu::TYPE_FLOAT_MAT3, glu::TYPE_FLOAT_MAT3X4, |
| |
| glu::TYPE_FLOAT_MAT4X2, glu::TYPE_FLOAT_MAT4X3, glu::TYPE_FLOAT_MAT4}; |
| |
| static const glu::Precision precisions[] = {glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP}; |
| |
| static const Interpolation interpModes[] = {INTERPOLATION_FLAT, INTERPOLATION_SMOOTH, INTERPOLATION_CENTROID}; |
| |
| const int maxAttributeVectors = 16; |
| // const int maxTransformFeedbackComponents = 64; // \note It is enough to limit attribute set size. |
| bool isSeparateMode = m_bufferMode == GL_SEPARATE_ATTRIBS; |
| int maxTransformFeedbackVars = isSeparateMode ? 4 : maxAttributeVectors; |
| const float arrayWeight = 0.3f; |
| const float positionWeight = 0.7f; |
| const float pointSizeWeight = 0.1f; |
| const float captureFullArrayWeight = 0.5f; |
| |
| de::Random rnd(m_seed); |
| bool usePosition = rnd.getFloat() < positionWeight; |
| bool usePointSize = rnd.getFloat() < pointSizeWeight; |
| int numAttribVectorsToUse = rnd.getInt(1, maxAttributeVectors - 1 /*position*/ - (usePointSize ? 1 : 0)); |
| |
| int numAttributeVectors = 0; |
| int varNdx = 0; |
| |
| // Generate varyings. |
| while (numAttributeVectors < numAttribVectorsToUse) |
| { |
| int maxVecs = isSeparateMode ? de::min(2 /*at most 2*mat2*/, numAttribVectorsToUse - numAttributeVectors) : |
| numAttribVectorsToUse - numAttributeVectors; |
| const glu::DataType *begin = &typeCandidates[0]; |
| const glu::DataType *end = begin + (maxVecs >= 4 ? 21 : |
| maxVecs >= 3 ? 18 : |
| maxVecs >= 2 ? (isSeparateMode ? 13 : 15) : |
| 12); |
| |
| glu::DataType type = rnd.choose<glu::DataType>(begin, end); |
| glu::Precision precision = |
| rnd.choose<glu::Precision>(&precisions[0], &precisions[0] + DE_LENGTH_OF_ARRAY(precisions)); |
| Interpolation interp = |
| glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT ? |
| rnd.choose<Interpolation>(&interpModes[0], &interpModes[0] + DE_LENGTH_OF_ARRAY(interpModes)) : |
| INTERPOLATION_FLAT; |
| int numVecs = glu::isDataTypeMatrix(type) ? glu::getDataTypeMatrixNumColumns(type) : 1; |
| int numComps = glu::getDataTypeScalarSize(type); |
| int maxArrayLen = de::max(1, isSeparateMode ? 4 / numComps : maxVecs / numVecs); |
| bool useArray = rnd.getFloat() < arrayWeight; |
| int arrayLen = useArray ? rnd.getInt(1, maxArrayLen) : 1; |
| std::string name = "v_var" + de::toString(varNdx); |
| |
| if (useArray) |
| m_progSpec.addVarying(name.c_str(), glu::VarType(glu::VarType(type, precision), arrayLen), interp); |
| else |
| m_progSpec.addVarying(name.c_str(), glu::VarType(type, precision), interp); |
| |
| numAttributeVectors += arrayLen * numVecs; |
| varNdx += 1; |
| } |
| |
| // Generate transform feedback candidate set. |
| vector<string> tfCandidates; |
| |
| if (usePosition) |
| tfCandidates.push_back("gl_Position"); |
| if (usePointSize) |
| tfCandidates.push_back("gl_PointSize"); |
| |
| for (int ndx = 0; ndx < varNdx /* num varyings */; ndx++) |
| { |
| const Varying &var = m_progSpec.getVaryings()[ndx]; |
| |
| if (var.type.isArrayType()) |
| { |
| const bool captureFull = m_elementCapture ? (rnd.getFloat() < captureFullArrayWeight) : true; |
| |
| if (captureFull) |
| tfCandidates.push_back(var.name); |
| else |
| { |
| const int numElem = var.type.getArraySize(); |
| for (int elemNdx = 0; elemNdx < numElem; elemNdx++) |
| tfCandidates.push_back(var.name + "[" + de::toString(elemNdx) + "]"); |
| } |
| } |
| else |
| tfCandidates.push_back(var.name); |
| } |
| |
| // Pick random selection. |
| vector<string> tfVaryings(de::min((int)tfCandidates.size(), maxTransformFeedbackVars)); |
| rnd.choose(tfCandidates.begin(), tfCandidates.end(), tfVaryings.begin(), (int)tfVaryings.size()); |
| rnd.shuffle(tfVaryings.begin(), tfVaryings.end()); |
| |
| for (vector<string>::const_iterator var = tfVaryings.begin(); var != tfVaryings.end(); var++) |
| m_progSpec.addTransformFeedbackVarying(var->c_str()); |
| |
| TransformFeedbackCase::init(); |
| } |
| |
| private: |
| uint32_t m_seed; |
| bool m_elementCapture; |
| }; |
| |
| } // namespace TransformFeedback |
| |
| using namespace TransformFeedback; |
| |
| TransformFeedbackTests::TransformFeedbackTests(Context &context) |
| : TestCaseGroup(context, "transform_feedback", "Transform feedback tests") |
| { |
| } |
| |
| TransformFeedbackTests::~TransformFeedbackTests(void) |
| { |
| } |
| |
| void TransformFeedbackTests::init(void) |
| { |
| static const struct |
| { |
| const char *name; |
| uint32_t mode; |
| } bufferModes[] = {{"separate", GL_SEPARATE_ATTRIBS}, {"interleaved", GL_INTERLEAVED_ATTRIBS}}; |
| |
| static const struct |
| { |
| const char *name; |
| uint32_t type; |
| } primitiveTypes[] = { |
| {"points", GL_POINTS}, {"lines", GL_LINES}, {"triangles", GL_TRIANGLES} |
| |
| // Not supported by GLES3. |
| // { "line_strip", GL_LINE_STRIP }, |
| // { "line_loop", GL_LINE_LOOP }, |
| // { "triangle_fan", GL_TRIANGLE_FAN }, |
| // { "triangle_strip", GL_TRIANGLE_STRIP } |
| }; |
| |
| static const glu::DataType basicTypes[] = {glu::TYPE_FLOAT, glu::TYPE_FLOAT_VEC2, glu::TYPE_FLOAT_VEC3, |
| glu::TYPE_FLOAT_VEC4, glu::TYPE_FLOAT_MAT2, glu::TYPE_FLOAT_MAT2X3, |
| glu::TYPE_FLOAT_MAT2X4, glu::TYPE_FLOAT_MAT3X2, glu::TYPE_FLOAT_MAT3, |
| glu::TYPE_FLOAT_MAT3X4, glu::TYPE_FLOAT_MAT4X2, glu::TYPE_FLOAT_MAT4X3, |
| glu::TYPE_FLOAT_MAT4, glu::TYPE_INT, glu::TYPE_INT_VEC2, |
| glu::TYPE_INT_VEC3, glu::TYPE_INT_VEC4, glu::TYPE_UINT, |
| glu::TYPE_UINT_VEC2, glu::TYPE_UINT_VEC3, glu::TYPE_UINT_VEC4}; |
| |
| static const glu::Precision precisions[] = {glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP}; |
| |
| static const struct |
| { |
| const char *name; |
| Interpolation interp; |
| } interpModes[] = { |
| {"smooth", INTERPOLATION_SMOOTH}, {"flat", INTERPOLATION_FLAT}, {"centroid", INTERPOLATION_CENTROID}}; |
| |
| // .position |
| { |
| tcu::TestCaseGroup *positionGroup = |
| new tcu::TestCaseGroup(m_testCtx, "position", "gl_Position capture using transform feedback"); |
| addChild(positionGroup); |
| |
| for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++) |
| { |
| for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++) |
| { |
| string name = string(primitiveTypes[primitiveType].name) + "_" + bufferModes[bufferMode].name; |
| positionGroup->addChild(new PositionCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, |
| primitiveTypes[primitiveType].type)); |
| } |
| } |
| } |
| |
| // .point_size |
| { |
| tcu::TestCaseGroup *pointSizeGroup = |
| new tcu::TestCaseGroup(m_testCtx, "point_size", "gl_PointSize capture using transform feedback"); |
| addChild(pointSizeGroup); |
| |
| for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++) |
| { |
| for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++) |
| { |
| string name = string(primitiveTypes[primitiveType].name) + "_" + bufferModes[bufferMode].name; |
| pointSizeGroup->addChild(new PointSizeCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, |
| primitiveTypes[primitiveType].type)); |
| } |
| } |
| } |
| |
| // .basic_type |
| { |
| tcu::TestCaseGroup *basicTypeGroup = |
| new tcu::TestCaseGroup(m_testCtx, "basic_types", "Basic types in transform feedback"); |
| addChild(basicTypeGroup); |
| |
| for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++) |
| { |
| tcu::TestCaseGroup *modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, ""); |
| uint32_t bufferMode = bufferModes[bufferModeNdx].mode; |
| basicTypeGroup->addChild(modeGroup); |
| |
| for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++) |
| { |
| tcu::TestCaseGroup *primitiveGroup = |
| new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, ""); |
| uint32_t primitiveType = primitiveTypes[primitiveTypeNdx].type; |
| modeGroup->addChild(primitiveGroup); |
| |
| for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++) |
| { |
| glu::DataType type = basicTypes[typeNdx]; |
| bool isFloat = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT; |
| |
| for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) |
| { |
| glu::Precision precision = precisions[precNdx]; |
| |
| string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type); |
| primitiveGroup->addChild( |
| new BasicTypeCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, |
| isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT)); |
| } |
| } |
| } |
| } |
| } |
| |
| // .array |
| { |
| tcu::TestCaseGroup *arrayGroup = new tcu::TestCaseGroup(m_testCtx, "array", "Capturing whole array in TF"); |
| addChild(arrayGroup); |
| |
| for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++) |
| { |
| tcu::TestCaseGroup *modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, ""); |
| uint32_t bufferMode = bufferModes[bufferModeNdx].mode; |
| arrayGroup->addChild(modeGroup); |
| |
| for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++) |
| { |
| tcu::TestCaseGroup *primitiveGroup = |
| new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, ""); |
| uint32_t primitiveType = primitiveTypes[primitiveTypeNdx].type; |
| modeGroup->addChild(primitiveGroup); |
| |
| for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++) |
| { |
| glu::DataType type = basicTypes[typeNdx]; |
| bool isFloat = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT; |
| |
| for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) |
| { |
| glu::Precision precision = precisions[precNdx]; |
| |
| string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type); |
| primitiveGroup->addChild( |
| new BasicArrayCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, precision, |
| isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT)); |
| } |
| } |
| } |
| } |
| } |
| |
| // .array_element |
| { |
| tcu::TestCaseGroup *arrayElemGroup = |
| new tcu::TestCaseGroup(m_testCtx, "array_element", "Capturing single array element in TF"); |
| addChild(arrayElemGroup); |
| |
| for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++) |
| { |
| tcu::TestCaseGroup *modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, ""); |
| uint32_t bufferMode = bufferModes[bufferModeNdx].mode; |
| arrayElemGroup->addChild(modeGroup); |
| |
| for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++) |
| { |
| tcu::TestCaseGroup *primitiveGroup = |
| new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, ""); |
| uint32_t primitiveType = primitiveTypes[primitiveTypeNdx].type; |
| modeGroup->addChild(primitiveGroup); |
| |
| for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(basicTypes); typeNdx++) |
| { |
| glu::DataType type = basicTypes[typeNdx]; |
| bool isFloat = glu::getDataTypeScalarType(type) == glu::TYPE_FLOAT; |
| |
| for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) |
| { |
| glu::Precision precision = precisions[precNdx]; |
| |
| string name = string(glu::getPrecisionName(precision)) + "_" + glu::getDataTypeName(type); |
| primitiveGroup->addChild( |
| new ArrayElementCase(m_context, name.c_str(), "", bufferMode, primitiveType, type, |
| precision, isFloat ? INTERPOLATION_SMOOTH : INTERPOLATION_FLAT)); |
| } |
| } |
| } |
| } |
| } |
| |
| // .interpolation |
| { |
| tcu::TestCaseGroup *interpolationGroup = new tcu::TestCaseGroup( |
| m_testCtx, "interpolation", "Different interpolation modes in transform feedback varyings"); |
| addChild(interpolationGroup); |
| |
| for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(interpModes); modeNdx++) |
| { |
| Interpolation interp = interpModes[modeNdx].interp; |
| tcu::TestCaseGroup *modeGroup = new tcu::TestCaseGroup(m_testCtx, interpModes[modeNdx].name, ""); |
| |
| interpolationGroup->addChild(modeGroup); |
| |
| for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++) |
| { |
| glu::Precision precision = precisions[precNdx]; |
| |
| for (int primitiveType = 0; primitiveType < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveType++) |
| { |
| for (int bufferMode = 0; bufferMode < DE_LENGTH_OF_ARRAY(bufferModes); bufferMode++) |
| { |
| string name = string(glu::getPrecisionName(precision)) + "_vec4_" + |
| primitiveTypes[primitiveType].name + "_" + bufferModes[bufferMode].name; |
| modeGroup->addChild(new BasicTypeCase(m_context, name.c_str(), "", bufferModes[bufferMode].mode, |
| primitiveTypes[primitiveType].type, glu::TYPE_FLOAT_VEC4, |
| precision, interp)); |
| } |
| } |
| } |
| } |
| } |
| |
| // .random |
| { |
| tcu::TestCaseGroup *randomGroup = |
| new tcu::TestCaseGroup(m_testCtx, "random", "Randomized transform feedback cases"); |
| addChild(randomGroup); |
| |
| for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++) |
| { |
| tcu::TestCaseGroup *modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, ""); |
| uint32_t bufferMode = bufferModes[bufferModeNdx].mode; |
| randomGroup->addChild(modeGroup); |
| |
| for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++) |
| { |
| tcu::TestCaseGroup *primitiveGroup = |
| new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, ""); |
| uint32_t primitiveType = primitiveTypes[primitiveTypeNdx].type; |
| modeGroup->addChild(primitiveGroup); |
| |
| for (int ndx = 0; ndx < 10; ndx++) |
| { |
| uint32_t seed = deInt32Hash(bufferMode) ^ deInt32Hash(primitiveType) ^ deInt32Hash(ndx); |
| primitiveGroup->addChild(new RandomCase(m_context, de::toString(ndx + 1).c_str(), "", bufferMode, |
| primitiveType, seed, true)); |
| } |
| } |
| } |
| } |
| |
| // .random_full_array_capture |
| { |
| tcu::TestCaseGroup *randomNecGroup = |
| new tcu::TestCaseGroup(m_testCtx, "random_full_array_capture", |
| "Randomized transform feedback cases without array element capture"); |
| addChild(randomNecGroup); |
| |
| for (int bufferModeNdx = 0; bufferModeNdx < DE_LENGTH_OF_ARRAY(bufferModes); bufferModeNdx++) |
| { |
| tcu::TestCaseGroup *modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[bufferModeNdx].name, ""); |
| uint32_t bufferMode = bufferModes[bufferModeNdx].mode; |
| randomNecGroup->addChild(modeGroup); |
| |
| for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); primitiveTypeNdx++) |
| { |
| tcu::TestCaseGroup *primitiveGroup = |
| new tcu::TestCaseGroup(m_testCtx, primitiveTypes[primitiveTypeNdx].name, ""); |
| uint32_t primitiveType = primitiveTypes[primitiveTypeNdx].type; |
| modeGroup->addChild(primitiveGroup); |
| |
| for (int ndx = 0; ndx < 10; ndx++) |
| { |
| uint32_t seed = deInt32Hash(bufferMode) ^ deInt32Hash(primitiveType) ^ deInt32Hash(ndx); |
| primitiveGroup->addChild(new RandomCase(m_context, de::toString(ndx + 1).c_str(), "", bufferMode, |
| primitiveType, seed, false)); |
| } |
| } |
| } |
| } |
| } |
| |
| } // namespace Functional |
| } // namespace gles3 |
| } // namespace deqp |