/*-------------------------------------------------------------------------
* 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 Texture unit usage tests.
*
* \todo [2012-07-12 nuutti] Come up with a good way to make these tests faster.
*//*--------------------------------------------------------------------*/
#include "es3fTextureUnitTests.hpp"
#include "glsTextureTestUtil.hpp"
#include "gluTextureUtil.hpp"
#include "gluContextInfo.hpp"
#include "gluTextureUtil.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuImageCompare.hpp"
#include "tcuMatrix.hpp"
#include "tcuRenderTarget.hpp"
#include "sglrContextUtil.hpp"
#include "sglrReferenceContext.hpp"
#include "sglrGLContext.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;
using tcu::IVec2;
using tcu::IVec3;
using tcu::Mat3;
using tcu::Mat4;
using std::vector;
using std::string;
using namespace glw; // GL types
namespace deqp
{
using namespace gls::TextureTestUtil;
namespace gles3
{
namespace Functional
{
static const int VIEWPORT_WIDTH = 128;
static const int VIEWPORT_HEIGHT = 128;
static const int TEXTURE_WIDTH_2D = 128;
static const int TEXTURE_HEIGHT_2D = 128;
// \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time.
static const int TEXTURE_WIDTH_CUBE = 256;
static const int TEXTURE_HEIGHT_CUBE = 256;
static const int TEXTURE_WIDTH_2D_ARRAY = 64;
static const int TEXTURE_HEIGHT_2D_ARRAY = 64;
static const int TEXTURE_LAYERS_2D_ARRAY = 4;
static const int TEXTURE_WIDTH_3D = 32;
static const int TEXTURE_HEIGHT_3D = 32;
static const int TEXTURE_DEPTH_3D = 32;
static const int GRID_CELL_SIZE = 8;
static const GLenum s_testSizedInternalFormats[] =
{
GL_RGBA32F,
GL_RGBA32I,
GL_RGBA32UI,
GL_RGBA16F,
GL_RGBA16I,
GL_RGBA16UI,
GL_RGBA8,
GL_RGBA8I,
GL_RGBA8UI,
GL_SRGB8_ALPHA8,
GL_RGB10_A2,
GL_RGB10_A2UI,
GL_RGBA4,
GL_RGB5_A1,
GL_RGBA8_SNORM,
GL_RGB8,
GL_RGB565,
GL_R11F_G11F_B10F,
GL_RGB32F,
GL_RGB32I,
GL_RGB32UI,
GL_RGB16F,
GL_RGB16I,
GL_RGB16UI,
GL_RGB8_SNORM,
GL_RGB8I,
GL_RGB8UI,
GL_SRGB8,
GL_RGB9_E5,
GL_RG32F,
GL_RG32I,
GL_RG32UI,
GL_RG16F,
GL_RG16I,
GL_RG16UI,
GL_RG8,
GL_RG8I,
GL_RG8UI,
GL_RG8_SNORM,
GL_R32F,
GL_R32I,
GL_R32UI,
GL_R16F,
GL_R16I,
GL_R16UI,
GL_R8,
GL_R8I,
GL_R8UI,
GL_R8_SNORM
};
static const GLenum s_testWrapModes[] =
{
GL_CLAMP_TO_EDGE,
GL_REPEAT,
GL_MIRRORED_REPEAT,
};
static const GLenum s_testMinFilters[] =
{
GL_NEAREST,
GL_LINEAR,
GL_NEAREST_MIPMAP_NEAREST,
GL_LINEAR_MIPMAP_NEAREST,
GL_NEAREST_MIPMAP_LINEAR,
GL_LINEAR_MIPMAP_LINEAR
};
static const GLenum s_testNonMipmapMinFilters[] =
{
GL_NEAREST,
GL_LINEAR
};
static const GLenum s_testNearestMinFilters[] =
{
GL_NEAREST,
GL_NEAREST_MIPMAP_NEAREST
};
static const GLenum s_testMagFilters[] =
{
GL_NEAREST,
GL_LINEAR
};
static const GLenum s_cubeFaceTargets[] =
{
GL_TEXTURE_CUBE_MAP_POSITIVE_X,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
};
// Extend a 3x3 transformation matrix to an equivalent 4x4 transformation matrix (i.e. 1.0 in right-down cell, 0.0's in other new cells).
static Mat4 matExtend3To4 (const Mat3& mat)
{
Mat4 res;
for (int rowNdx = 0; rowNdx < 3; rowNdx++)
{
Vec3 row = mat.getRow(rowNdx);
res.setRow(rowNdx, Vec4(row.x(), row.y(), row.z(), 0.0f));
}
res.setRow(3, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
return res;
}
static string generateMultiTexFragmentShader (int numUnits, const vector<GLenum>& unitTypes, const vector<glu::DataType>& samplerTypes)
{
// The fragment shader calculates the average of a set of textures.
string samplersStr;
string matricesStr;
string scalesStr;
string biasesStr;
string lookupsStr;
string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)";
for (int ndx = 0; ndx < numUnits; ndx++)
{
string ndxStr = de::toString(ndx);
string samplerName = "u_sampler" + ndxStr;
string transformationName = "u_trans" + ndxStr;
string scaleName = "u_texScale" + ndxStr;
string biasName = "u_texBias" + ndxStr;
samplersStr += string("") + "uniform highp " + glu::getDataTypeName(samplerTypes[ndx]) + " " + samplerName + ";\n";
matricesStr += "uniform highp mat4 " + transformationName + ";\n";
scalesStr += "uniform highp vec4 " + scaleName + ";\n";
biasesStr += "uniform highp vec4 " + biasName + ";\n";
string lookupCoord = transformationName + "*vec4(v_coord, 1.0, 1.0)";
if (unitTypes[ndx] == GL_TEXTURE_2D)
lookupCoord = "vec2(" + lookupCoord + ")";
else
lookupCoord = "vec3(" + lookupCoord + ")";
lookupsStr += "\tcolor += " + colorMultiplier + "*(vec4(texture(" + samplerName + ", " + lookupCoord + "))*" + scaleName + " + " + biasName + ");\n";
}
return "#version 300 es\n"
"layout(location = 0) out mediump vec4 o_color;\n" +
samplersStr +
matricesStr +
scalesStr +
biasesStr +
"in highp vec2 v_coord;\n"
"\n"
"void main (void)\n"
"{\n"
" mediump vec4 color = vec4(0.0);\n" +
lookupsStr +
" o_color = color;\n"
"}\n";
}
static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration (int numUnits, const vector<GLenum>& unitTypes, const vector<glu::DataType>& samplerTypes)
{
sglr::pdec::ShaderProgramDeclaration decl;
decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT);
decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT);
decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT);
decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT);
for (int ndx = 0; ndx < numUnits; ++ndx)
{
string samplerName = "u_sampler" + de::toString(ndx);
string transformationName = "u_trans" + de::toString(ndx);
string scaleName = "u_texScale" + de::toString(ndx);
string biasName = "u_texBias" + de::toString(ndx);
decl << sglr::pdec::Uniform(samplerName, samplerTypes[ndx]);
decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT4);
decl << sglr::pdec::Uniform(scaleName, glu::TYPE_FLOAT_VEC4);
decl << sglr::pdec::Uniform(biasName, glu::TYPE_FLOAT_VEC4);
}
decl << sglr::pdec::VertexSource("#version 300 es\n"
"in highp vec4 a_position;\n"
"in highp vec2 a_coord;\n"
"out highp vec2 v_coord;\n"
"\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" v_coord = a_coord;\n"
"}\n");
decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes, samplerTypes));
return decl;
}
// Calculates values that will be used in calculateLod().
static tcu::Vector<tcu::Vec2, 3> calculateLodDerivateParts (const Mat4& transformation)
{
// Calculate transformed coordinates of three screen corners.
Vec3 trans00 = (transformation * Vec4(0.0f, 0.0f, 1.0f, 1.0f)).xyz();
Vec3 trans01 = (transformation * Vec4(0.0f, 1.0f, 1.0f, 1.0f)).xyz();
Vec3 trans10 = (transformation * Vec4(1.0f, 0.0f, 1.0f, 1.0f)).xyz();
return tcu::Vector<tcu::Vec2, 3>(Vec2(trans10.x() - trans00.x(), trans01.x() - trans00.x()),
Vec2(trans10.y() - trans00.y(), trans01.y() - trans00.y()),
Vec2(trans10.z() - trans00.z(), trans01.z() - trans00.z()));
}
// Calculates the maximum allowed lod from derivates
static float calculateLodMax(const tcu::Vector<tcu::Vec2, 3>& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate)
{
float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
const float mu = de::max(de::abs(dudx), de::abs(dudy));
const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
return deFloatLog2(mu + mv + mw);
}
// Calculates the minimum allowed lod from derivates
static float calculateLodMin(const tcu::Vector<tcu::Vec2, 3>& derivateParts, const tcu::IVec3& textureSize, const Vec2& screenDerivate)
{
float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
const float mu = de::max(de::abs(dudx), de::abs(dudy));
const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
return deFloatLog2(de::max(mu, de::max(mv, mw)));
}
class MultiTexShader : public sglr::ShaderProgram
{
public:
MultiTexShader (deUint32 randSeed,
int numUnits,
const vector<GLenum>& unitTypes,
const vector<glu::DataType>& samplerTypes,
const vector<Vec4>& texScales,
const vector<Vec4>& texBiases,
const vector<int>& num2dArrayLayers); // \note 2d array layer "coordinate" isn't normalized, so this is needed here.
void setUniforms (sglr::Context& context, deUint32 program) const;
void makeSafeLods (const vector<IVec3>& textureSizes, const IVec2& viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 .
private:
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;
int m_numUnits;
vector<GLenum> m_unitTypes; // 2d, cube map, 2d array or 3d.
vector<Vec4> m_texScales;
vector<Vec4> m_texBiases;
vector<Mat4> m_transformations;
vector<tcu::Vector<tcu::Vec2, 3> > m_lodDerivateParts; // Parts of lod derivates; computed in init(), used in eval().
};
MultiTexShader::MultiTexShader (deUint32 randSeed,
int numUnits,
const vector<GLenum>& unitTypes,
const vector<glu::DataType>& samplerTypes,
const vector<Vec4>& texScales,
const vector<Vec4>& texBiases,
const vector<int>& num2dArrayLayers)
: sglr::ShaderProgram (generateShaderProgramDeclaration(numUnits, unitTypes, samplerTypes))
, m_numUnits (numUnits)
, m_unitTypes (unitTypes)
, m_texScales (texScales)
, m_texBiases (texBiases)
{
// 2d-to-cube-face transformations.
// \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well.
static const float s_cubeTransforms[][3*3] =
{
// Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1))
{ 0.0f, 0.0f, -1.0f,
0.0f, -2.0f, 1.0f,
2.0f, 0.0f, -1.0f },
// Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1))
{ 0.0f, 0.0f, 1.0f,
0.0f, -2.0f, 1.0f,
-2.0f, 0.0f, 1.0f },
// Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1))
{ 2.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, -2.0f, 1.0f },
// Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1))
{ 2.0f, 0.0f, -1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 2.0f, -1.0f },
// Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1)
{ -2.0f, 0.0f, 1.0f,
0.0f, -2.0f, 1.0f,
0.0f, 0.0f, -1.0f },
// Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1)
{ 2.0f, 0.0f, -1.0f,
0.0f, -2.0f, 1.0f,
0.0f, 0.0f, 1.0f }
};
// Generate transformation matrices.
de::Random rnd(randSeed);
m_transformations.reserve(m_numUnits);
m_lodDerivateParts.reserve(m_numUnits);
int tex2dArrayNdx = 0; // Keep track of 2d texture array index.
DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
{
if (m_unitTypes[unitNdx] == GL_TEXTURE_2D)
{
float rotAngle = rnd.getFloat(0.0f, 2.0f*DE_PI);
float xScaleFactor = rnd.getFloat(0.7f, 1.5f);
float yScaleFactor = rnd.getFloat(0.7f, 1.5f);
float xShearAmount = rnd.getFloat(0.0f, 0.5f);
float yShearAmount = rnd.getFloat(0.0f, 0.5f);
float xTranslationAmount = rnd.getFloat(-0.5f, 0.5f);
float yTranslationAmount = rnd.getFloat(-0.5f, 0.5f);
static const float tempOffsetData[3*3] = // For temporarily centering the coordinates to get nicer transformations.
{
1.0f, 0.0f, -0.5f,
0.0f, 1.0f, -0.5f,
0.0f, 0.0f, 1.0f
};
float rotTransfData[3*3] =
{
deFloatCos(rotAngle), -deFloatSin(rotAngle), 0.0f,
deFloatSin(rotAngle), deFloatCos(rotAngle), 0.0f,
0.0f, 0.0f, 1.0f
};
float scaleTransfData[3*3] =
{
xScaleFactor, 0.0f, 0.0f,
0.0f, yScaleFactor, 0.0f,
0.0f, 0.0f, 1.0f
};
float xShearTransfData[3*3] =
{
1.0f, xShearAmount, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
float yShearTransfData[3*3] =
{
1.0f, 0.0f, 0.0f,
yShearAmount, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f
};
float translationTransfData[3*3] =
{
1.0f, 0.0f, xTranslationAmount,
0.0f, 1.0f, yTranslationAmount,
0.0f, 0.0f, 1.0f
};
Mat4 transformation = matExtend3To4(Mat3(tempOffsetData) *
Mat3(translationTransfData) *
Mat3(rotTransfData) *
Mat3(scaleTransfData) *
Mat3(xShearTransfData) *
Mat3(yShearTransfData) *
(Mat3(tempOffsetData) * (-1.0f)));
m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
m_transformations.push_back(transformation);
}
else if (m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP)
{
DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms));
float planarTransData[3*3];
// In case of a cube map, we only want to render one face, so the transformation needs to be restricted - only enlarging scaling is done.
for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++)
{
if (i == 0 || i == 4)
planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling.
else if (i == 8)
planarTransData[i] = 1.0f;
else
planarTransData[i] = 0.0f;
}
int faceNdx = rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1);
Mat3 planarTrans (planarTransData); // Planar, face-agnostic transformation.
Mat4 finalTrans = matExtend3To4(Mat3(s_cubeTransforms[faceNdx]) * planarTrans); // Final transformation from planar to cube map coordinates, including the transformation just generated.
Mat4 planarTrans4x4 = matExtend3To4(planarTrans);
m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans4x4));
m_transformations.push_back(finalTrans);
}
else
{
DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_3D || m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY);
float transData[4*4];
for (int i = 0; i < 4*4; i++)
{
float sign = rnd.getBool() ? 1.0f : -1.0f;
transData[i] = rnd.getFloat(0.7f, 1.4f) * sign;
}
Mat4 transformation(transData);
if (m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY)
{
// Z direction: Translate by 0.5 and scale by layer amount.
float numLayers = (float)num2dArrayLayers[tex2dArrayNdx];
static const float zTranslationTransfData[4*4] =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.5f,
0.0f, 0.0f, 0.0f, 1.0f
};
float zScaleTransfData[4*4] =
{
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, numLayers, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
transformation = transformation * Mat4(zScaleTransfData) * Mat4(zTranslationTransfData);
tex2dArrayNdx++;
}
m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
m_transformations.push_back(Mat4(transformation));
}
}
}
void MultiTexShader::setUniforms (sglr::Context& ctx, deUint32 program) const
{
ctx.useProgram(program);
// Sampler and matrix uniforms.
for (int ndx = 0; ndx < m_numUnits; ndx++)
{
string ndxStr = de::toString(ndx);
ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx);
ctx.uniformMatrix4fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE, (GLfloat*)&m_transformations[ndx].getColumnMajorData()[0]);
ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texScale" + ndxStr).c_str()), 1, m_texScales[ndx].getPtr());
ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texBias" + ndxStr).c_str()), 1, m_texBiases[ndx].getPtr());
}
}
void MultiTexShader::makeSafeLods (const vector<IVec3>& textureSizes, const IVec2& viewportSize)
{
DE_ASSERT((int)textureSizes.size() == m_numUnits);
static const float shrinkScaleMat2dData[3*3] =
{
0.95f, 0.0f, 0.0f,
0.0f, 0.95f, 0.0f,
0.0f, 0.0f, 1.0f
};
static const float shrinkScaleMat3dData[3*3] =
{
0.95f, 0.0f, 0.0f,
0.0f, 0.95f, 0.0f,
0.0f, 0.0f, 0.95f
};
Mat4 shrinkScaleMat2d = matExtend3To4(Mat3(shrinkScaleMat2dData));
Mat4 shrinkScaleMat3d = matExtend3To4(Mat3(shrinkScaleMat3dData));
Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y());
for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
{
// As long as LOD is too close to 0.0 or is positive and too close to a something-and-a-half (0.5, 1.5, 2.5 etc) or allowed lod range could round to different levels, zoom in a little to get a safer LOD.
for (;;)
{
const float threshold = 0.1f;
const float epsilon = 0.01f;
const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
const deInt32 maxLevel = (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1);
const deInt32 minLevel = (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1);
if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) ||
de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) ||
maxLevel != minLevel)
{
m_transformations[unitNdx] = (m_unitTypes[unitNdx] == GL_TEXTURE_3D ? shrinkScaleMat3d : shrinkScaleMat2d) * m_transformations[unitNdx];
m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]);
}
else
break;
}
}
}
void MultiTexShader::shadeVertices (const rr::VertexAttrib* inputs, rr::VertexPacket* const* packets, const int numPackets) const
{
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
{
rr::VertexPacket& packet = *(packets[packetNdx]);
packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
packet.outputs[0] = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
}
}
void MultiTexShader::shadeFragments (rr::FragmentPacket* packets, const int numPackets, const rr::FragmentShadingContext& context) const
{
DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
DE_ASSERT((int)m_transformations.size() == m_numUnits);
DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits);
for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
{
rr::FragmentPacket& packet = packets[packetNdx];
const float colorMultiplier = 1.0f / (float)m_numUnits;
Vec4 outColors[4] = { Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f) };
for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
{
tcu::Vec4 texSamples[4];
// Read tex coords
const tcu::Vec2 texCoords[4] =
{
rr::readTriangleVarying<float>(packet, context, 0, 0).xy(),
rr::readTriangleVarying<float>(packet, context, 0, 1).xy(),
rr::readTriangleVarying<float>(packet, context, 0, 2).xy(),
rr::readTriangleVarying<float>(packet, context, 0, 3).xy(),
};
// Transform
tcu::Vec3 coords3D[4] =
{
(m_transformations[unitNdx] * Vec4(texCoords[0].x(), texCoords[0].y(), 1.0f, 1.0f)).xyz(),
(m_transformations[unitNdx] * Vec4(texCoords[1].x(), texCoords[1].y(), 1.0f, 1.0f)).xyz(),
(m_transformations[unitNdx] * Vec4(texCoords[2].x(), texCoords[2].y(), 1.0f, 1.0f)).xyz(),
(m_transformations[unitNdx] * Vec4(texCoords[3].x(), texCoords[3].y(), 1.0f, 1.0f)).xyz(),
};
// To 2D
const tcu::Vec2 coords2D[4] =
{
coords3D[0].xy(),
coords3D[1].xy(),
coords3D[2].xy(),
coords3D[3].xy(),
};
// Sample
switch (m_unitTypes[unitNdx])
{
case GL_TEXTURE_2D: m_uniforms[4*unitNdx].sampler.tex2D->sample4(texSamples, coords2D); break;
case GL_TEXTURE_CUBE_MAP: m_uniforms[4*unitNdx].sampler.texCube->sample4(texSamples, coords3D); break;
case GL_TEXTURE_2D_ARRAY: m_uniforms[4*unitNdx].sampler.tex2DArray->sample4(texSamples, coords3D); break;
case GL_TEXTURE_3D: m_uniforms[4*unitNdx].sampler.tex3D->sample4(texSamples, coords3D); break;
default:
DE_ASSERT(DE_FALSE);
}
// Add to sum
for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
outColors[fragNdx] += colorMultiplier * (texSamples[fragNdx]*m_texScales[unitNdx] + m_texBiases[unitNdx]);
}
// output
for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]);
}
}
class TextureUnitCase : public TestCase
{
public:
enum CaseType
{
CASE_ONLY_2D = 0,
CASE_ONLY_CUBE,
CASE_ONLY_2D_ARRAY,
CASE_ONLY_3D,
CASE_MIXED,
CASE_LAST
};
TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits /* \note If non-positive, use all units */, CaseType caseType, deUint32 randSeed);
~TextureUnitCase (void);
void init (void);
void deinit (void);
IterateResult iterate (void);
private:
struct TextureParameters
{
GLenum internalFormat;
GLenum wrapModeS;
GLenum wrapModeT;
GLenum wrapModeR;
GLenum minFilter;
GLenum magFilter;
};
TextureUnitCase (const TextureUnitCase& other);
TextureUnitCase& operator= (const TextureUnitCase& other);
void upload2dTexture (int texNdx, sglr::Context& context);
void uploadCubeTexture (int texNdx, sglr::Context& context);
void upload2dArrayTexture (int texNdx, sglr::Context& context);
void upload3dTexture (int texNdx, sglr::Context& context);
void render (sglr::Context& context);
const int m_numUnitsParam;
const CaseType m_caseType;
const deUint32 m_randSeed;
int m_numTextures; //!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units.
int m_numUnits; //!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum
vector<GLenum> m_textureTypes;
vector<TextureParameters> m_textureParams;
vector<tcu::Texture2D*> m_textures2d;
vector<tcu::TextureCube*> m_texturesCube;
vector<tcu::Texture2DArray*> m_textures2dArray;
vector<tcu::Texture3D*> m_textures3d;
vector<int> m_unitTextures; //!< Which texture is used in a particular unit.
vector<int> m_ndxTexType; //!< Index of a texture in m_textures2d, m_texturesCube, m_textures2dArray or m_textures3d, depending on texture type.
MultiTexShader* m_shader;
};
TextureUnitCase::TextureUnitCase (Context& context, const char* name, const char* desc, int numUnits, CaseType caseType, deUint32 randSeed)
: TestCase (context, tcu::NODETYPE_SELF_VALIDATE, name, desc)
, m_numUnitsParam (numUnits)
, m_caseType (caseType)
, m_randSeed (randSeed)
, m_shader (DE_NULL)
{
}
TextureUnitCase::~TextureUnitCase (void)
{
TextureUnitCase::deinit();
}
void TextureUnitCase::deinit (void)
{
for (vector<tcu::Texture2D*>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++)
delete *i;
m_textures2d.clear();
for (vector<tcu::TextureCube*>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++)
delete *i;
m_texturesCube.clear();
for (vector<tcu::Texture2DArray*>::iterator i = m_textures2dArray.begin(); i != m_textures2dArray.end(); i++)
delete *i;
m_textures2dArray.clear();
for (vector<tcu::Texture3D*>::iterator i = m_textures3d.begin(); i != m_textures3d.end(); i++)
delete *i;
m_textures3d.clear();
delete m_shader;
m_shader = DE_NULL;
}
void TextureUnitCase::init (void)
{
m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS);
// Make the textures.
try
{
tcu::TestLog& log = m_testCtx.getLog();
de::Random rnd (m_randSeed);
if (rnd.getFloat() < 0.7f)
m_numTextures = m_numUnits; // In most cases use one unit per texture.
else
m_numTextures = rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits); // Sometimes assign same texture to multiple units.
log << tcu::TestLog::Message << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) + " texture(s)").c_str() << tcu::TestLog::EndMessage;
m_textureTypes.reserve(m_numTextures);
m_textureParams.reserve(m_numTextures);
m_ndxTexType.reserve(m_numTextures);
// Generate textures.
for (int texNdx = 0; texNdx < m_numTextures; texNdx++)
{
// Either fixed or randomized target types, and randomized parameters for every texture.
TextureParameters params;
DE_STATIC_ASSERT(CASE_ONLY_2D == 0 && CASE_MIXED + 1 == CASE_LAST);
int texType = m_caseType == CASE_MIXED ? rnd.getInt(0, (int)CASE_MIXED - 1) : (int)m_caseType;
bool is2dTex = texType == 0;
bool isCubeTex = texType == 1;
bool is2dArrayTex = texType == 2;
bool is3dTex = texType == 3;
DE_ASSERT(is2dTex || isCubeTex || is2dArrayTex || is3dTex);
GLenum type = is2dTex ? GL_TEXTURE_2D : isCubeTex ? GL_TEXTURE_CUBE_MAP : is2dArrayTex ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_3D;
const int texWidth = is2dTex ? TEXTURE_WIDTH_2D : isCubeTex ? TEXTURE_WIDTH_CUBE : is2dArrayTex ? TEXTURE_WIDTH_2D_ARRAY : TEXTURE_WIDTH_3D;
const int texHeight = is2dTex ? TEXTURE_HEIGHT_2D : isCubeTex ? TEXTURE_HEIGHT_CUBE : is2dArrayTex ? TEXTURE_HEIGHT_2D_ARRAY : TEXTURE_HEIGHT_3D;
const int texDepth = is3dTex ? TEXTURE_DEPTH_3D : 1;
const int texLayers = is2dArrayTex ? TEXTURE_LAYERS_2D_ARRAY : 1;
bool mipmaps = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight) && deIsPowerOfTwo32(texDepth));
int numLevels = mipmaps ? deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth))+1 : 1;
params.internalFormat = s_testSizedInternalFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testSizedInternalFormats) - 1)];
bool isFilterable = glu::isGLInternalColorFormatFilterable(params.internalFormat);
params.wrapModeS = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
params.wrapModeT = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
params.wrapModeR = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
params.magFilter = isFilterable ? s_testMagFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)] : GL_NEAREST;
if (mipmaps)
params.minFilter = isFilterable ?
s_testMinFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)] :
s_testNearestMinFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNearestMinFilters) - 1)];
else
params.minFilter = isFilterable ?
s_testNonMipmapMinFilters [rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)] :
GL_NEAREST;
m_textureTypes.push_back(type);
m_textureParams.push_back(params);
// Create new texture.
tcu::TextureFormat texFormat = glu::mapGLInternalFormat((deUint32)params.internalFormat);
if (is2dTex)
{
m_ndxTexType.push_back((int)m_textures2d.size()); // Remember the index this texture has in the 2d texture vector.
m_textures2d.push_back(new tcu::Texture2D(texFormat, texWidth, texHeight));
}
else if (isCubeTex)
{
m_ndxTexType.push_back((int)m_texturesCube.size()); // Remember the index this texture has in the cube texture vector.
DE_ASSERT(texWidth == texHeight);
m_texturesCube.push_back(new tcu::TextureCube(texFormat, texWidth));
}
else if (is2dArrayTex)
{
m_ndxTexType.push_back((int)m_textures2dArray.size()); // Remember the index this texture has in the 2d array texture vector.
m_textures2dArray.push_back(new tcu::Texture2DArray(texFormat, texWidth, texHeight, texLayers));
}
else
{
m_ndxTexType.push_back((int)m_textures3d.size()); // Remember the index this texture has in the 3d vector.
m_textures3d.push_back(new tcu::Texture3D(texFormat, texWidth, texHeight, texDepth));
}
tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFormat);
Vec4 cBias = fmtInfo.valueMin;
Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin;
// Fill with grid texture.
int numFaces = isCubeTex ? (int)tcu::CUBEFACE_LAST : 1;
for (int face = 0; face < numFaces; face++)
{
deUint32 rgb = rnd.getUint32() & 0x00ffffff;
deUint32 alpha = 0xff000000;
deUint32 colorA = alpha | rgb;
deUint32 colorB = alpha | ((~rgb) & 0x00ffffff);
for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
{
if (is2dTex)
m_textures2d.back()->allocLevel(levelNdx);
else if (isCubeTex)
m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx);
else if (is2dArrayTex)
m_textures2dArray.back()->allocLevel(levelNdx);
else
m_textures3d.back()->allocLevel(levelNdx);
int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps.
tcu::PixelBufferAccess access = is2dTex ? m_textures2d.back()->getLevel(levelNdx)
: isCubeTex ? m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face)
: is2dArrayTex ? m_textures2dArray.back()->getLevel(levelNdx)
: m_textures3d.back()->getLevel(levelNdx);
tcu::fillWithGrid(access, curCellSize, toVec4(tcu::RGBA(colorA))*cScale + cBias, toVec4(tcu::RGBA(colorB))*cScale + cBias);
}
}
}
// Assign a texture index to each unit.
m_unitTextures.reserve(m_numUnits);
// \note Every texture is used at least once.
for (int i = 0; i < m_numTextures; i++)
m_unitTextures.push_back(i);
// Assign a random texture to remaining units.
while ((int)m_unitTextures.size() < m_numUnits)
m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1));
rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end());
// Generate information for shader.
vector<GLenum> unitTypes;
vector<Vec4> texScales;
vector<Vec4> texBiases;
vector<glu::DataType> samplerTypes;
vector<int> num2dArrayLayers;
unitTypes.reserve(m_numUnits);
texScales.reserve(m_numUnits);
texBiases.reserve(m_numUnits);
samplerTypes.reserve(m_numUnits);
num2dArrayLayers.reserve(m_numUnits);
for (int i = 0; i < m_numUnits; i++)
{
int texNdx = m_unitTextures[i];
GLenum type = m_textureTypes[texNdx];
tcu::TextureFormat fmt = glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat);
tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(fmt);
unitTypes.push_back(type);
if (type == GL_TEXTURE_2D_ARRAY)
num2dArrayLayers.push_back(m_textures2dArray[m_ndxTexType[texNdx]]->getNumLayers());
texScales.push_back(fmtInfo.lookupScale);
texBiases.push_back(fmtInfo.lookupBias);
switch (type)
{
case GL_TEXTURE_2D: samplerTypes.push_back(glu::getSampler2DType(fmt)); break;
case GL_TEXTURE_CUBE_MAP: samplerTypes.push_back(glu::getSamplerCubeType(fmt)); break;
case GL_TEXTURE_2D_ARRAY: samplerTypes.push_back(glu::getSampler2DArrayType(fmt)); break;
case GL_TEXTURE_3D: samplerTypes.push_back(glu::getSampler3DType(fmt)); break;
default:
DE_ASSERT(DE_FALSE);
}
}
// Create shader.
DE_ASSERT(m_shader == DE_NULL);
m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes, samplerTypes, texScales, texBiases, num2dArrayLayers);
}
catch (const std::exception&)
{
// Clean up to save memory.
TextureUnitCase::deinit();
throw;
}
}
TextureUnitCase::IterateResult TextureUnitCase::iterate (void)
{
glu::RenderContext& renderCtx = m_context.getRenderContext();
const tcu::RenderTarget& renderTarget = renderCtx.getRenderTarget();
tcu::TestLog& log = m_testCtx.getLog();
de::Random rnd (m_randSeed);
int viewportWidth = deMin32(VIEWPORT_WIDTH, renderTarget.getWidth());
int viewportHeight = deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight());
int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
tcu::Surface gles3Frame (viewportWidth, viewportHeight);
tcu::Surface refFrame (viewportWidth, viewportHeight);
{
// First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods().
vector<IVec3> texSizes;
texSizes.reserve(m_numUnits);
for (int i = 0; i < m_numUnits; i++)
{
int texNdx = m_unitTextures[i];
int texNdxInType = m_ndxTexType[texNdx];
GLenum type = m_textureTypes[texNdx];
switch (type)
{
case GL_TEXTURE_2D: texSizes.push_back(IVec3(m_textures2d[texNdxInType]->getWidth(), m_textures2d[texNdxInType]->getHeight(), 0)); break;
case GL_TEXTURE_CUBE_MAP: texSizes.push_back(IVec3(m_texturesCube[texNdxInType]->getSize(), m_texturesCube[texNdxInType]->getSize(), 0)); break;
case GL_TEXTURE_2D_ARRAY: texSizes.push_back(IVec3(m_textures2dArray[texNdxInType]->getWidth(), m_textures2dArray[texNdxInType]->getHeight(), 0)); break;
case GL_TEXTURE_3D: texSizes.push_back(IVec3(m_textures3d[texNdxInType]->getWidth(), m_textures3d[texNdxInType]->getHeight(), m_textures3d[texNdxInType]->getDepth())); break;
default:
DE_ASSERT(DE_FALSE);
}
}
m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight));
}
// Render using GLES3.
{
sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS|sglr::GLCONTEXT_LOG_PROGRAMS, tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight));
render(context);
context.readPixels(gles3Frame, 0, 0, viewportWidth, viewportHeight);
}
// Render reference image.
{
sglr::ReferenceContextBuffers buffers (tcu::PixelFormat(8,8,8,renderTarget.getPixelFormat().alphaBits?8:0), 0 /* depth */, 0 /* stencil */, viewportWidth, viewportHeight);
sglr::ReferenceContext context (sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(), buffers.getDepthbuffer(), buffers.getStencilbuffer());
render(context);
context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight);
}
// Compare images.
const float threshold = 0.001f;
bool isOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles3Frame, threshold, tcu::COMPARE_LOG_RESULT);
// Store test result.
m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
isOk ? "Pass" : "Image comparison failed");
return STOP;
}
void TextureUnitCase::upload2dTexture (int texNdx, sglr::Context& context)
{
int ndx2d = m_ndxTexType[texNdx];
const tcu::Texture2D* texture = m_textures2d[ndx2d];
glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
{
if (texture->isLevelEmpty(levelNdx))
continue;
tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx);
int width = access.getWidth();
int height = access.getHeight();
DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d texture image data");
}
}
void TextureUnitCase::uploadCubeTexture (int texNdx, sglr::Context& context)
{
int ndxCube = m_ndxTexType[texNdx];
const tcu::TextureCube* texture = m_texturesCube[ndxCube];
glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++)
{
for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
{
if (texture->isLevelEmpty((tcu::CubeFace)face, levelNdx))
continue;
tcu::ConstPixelBufferAccess access = texture->getLevelFace(levelNdx, (tcu::CubeFace)face);
int width = access.getWidth();
int height = access.getHeight();
DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].internalFormat, width, height, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
GLU_EXPECT_NO_ERROR(context.getError(), "Set cube map image data");
}
}
}
void TextureUnitCase::upload2dArrayTexture (int texNdx, sglr::Context& context)
{
int ndx2dArray = m_ndxTexType[texNdx];
const tcu::Texture2DArray* texture = m_textures2dArray[ndx2dArray];
glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
{
if (texture->isLevelEmpty(levelNdx))
continue;
tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx);
int width = access.getWidth();
int height = access.getHeight();
int layers = access.getDepth();
DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height);
context.texImage3D(GL_TEXTURE_2D_ARRAY, levelNdx, m_textureParams[texNdx].internalFormat, width, height, layers, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d array texture image data");
}
}
void TextureUnitCase::upload3dTexture (int texNdx, sglr::Context& context)
{
int ndx3d = m_ndxTexType[texNdx];
const tcu::Texture3D* texture = m_textures3d[ndx3d];
glu::TransferFormat formatGl = glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
{
if (texture->isLevelEmpty(levelNdx))
continue;
tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx);
int width = access.getWidth();
int height = access.getHeight();
int depth = access.getDepth();
DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize()*width);
DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize()*width*height);
context.texImage3D(GL_TEXTURE_3D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, depth, 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
GLU_EXPECT_NO_ERROR(context.getError(), "Set 3d texture image data");
}
}
void TextureUnitCase::render (sglr::Context& context)
{
// Setup textures.
vector<deUint32> textureGLNames;
vector<bool> isTextureSetUp(m_numTextures, false); // \note Same texture may be bound to multiple units, but we only want to set up parameters and data once per texture.
textureGLNames.resize(m_numTextures);
context.genTextures(m_numTextures, &textureGLNames[0]);
GLU_EXPECT_NO_ERROR(context.getError(), "Generate textures");
for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
{
int texNdx = m_unitTextures[unitNdx];
// Bind texture to unit.
context.activeTexture(GL_TEXTURE0 + unitNdx);
GLU_EXPECT_NO_ERROR(context.getError(), "Set active texture");
context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]);
GLU_EXPECT_NO_ERROR(context.getError(), "Bind texture");
if (!isTextureSetUp[texNdx])
{
// Binding this texture for first time, so set parameters and data.
context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS);
context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT);
if (m_textureTypes[texNdx] == GL_TEXTURE_3D)
context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_R, m_textureParams[texNdx].wrapModeR);
context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter);
context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter);
GLU_EXPECT_NO_ERROR(context.getError(), "Set texture parameters");
switch (m_textureTypes[texNdx])
{
case GL_TEXTURE_2D: upload2dTexture(texNdx, context); break;
case GL_TEXTURE_CUBE_MAP: uploadCubeTexture(texNdx, context); break;
case GL_TEXTURE_2D_ARRAY: upload2dArrayTexture(texNdx, context); break;
case GL_TEXTURE_3D: upload3dTexture(texNdx, context); break;
default:
DE_ASSERT(DE_FALSE);
}
isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later.
}
}
GLU_EXPECT_NO_ERROR(context.getError(), "Set textures");
// Setup shader
deUint32 shaderID = context.createProgram(m_shader);
// Draw.
context.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
context.clear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
m_shader->setUniforms(context, shaderID);
sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
GLU_EXPECT_NO_ERROR(context.getError(), "Draw");
// Delete previously generated texture names.
context.deleteTextures(m_numTextures, &textureGLNames[0]);
GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures");
}
TextureUnitTests::TextureUnitTests (Context& context)
: TestCaseGroup(context, "units", "Texture Unit Usage Tests")
{
}
TextureUnitTests::~TextureUnitTests (void)
{
}
void TextureUnitTests::init (void)
{
const int numTestsPerGroup = 10;
static const int unitCounts[] =
{
2,
4,
8,
-1 // \note Negative stands for the implementation-specified maximum.
};
for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++)
{
int numUnits = unitCounts[unitCountNdx];
string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units";
tcu::TestCaseGroup* countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), "");
addChild(countGroup);
DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0);
for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++)
{
const char* caseTypeGroupName = (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D ? "only_2d"
: (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE ? "only_cube"
: (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D_ARRAY ? "only_2d_array"
: (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_3D ? "only_3d"
: (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED ? "mixed"
: DE_NULL;
DE_ASSERT(caseTypeGroupName != DE_NULL);
tcu::TestCaseGroup* caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, "");
countGroup->addChild(caseTypeGroup);
for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++)
caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits, (TextureUnitCase::CaseType)caseType, deUint32Hash((deUint32)testNdx)));
}
}
}
} // Functional
} // gles3
} // deqp