1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Texture unit usage tests.
22 *
23 * \todo [2012-07-12 nuutti] Come up with a good way to make these tests faster.
24 *//*--------------------------------------------------------------------*/
25
26 #include "es3fTextureUnitTests.hpp"
27 #include "glsTextureTestUtil.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "gluContextInfo.hpp"
30 #include "gluTextureUtil.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuImageCompare.hpp"
33 #include "tcuMatrix.hpp"
34 #include "tcuRenderTarget.hpp"
35 #include "sglrContextUtil.hpp"
36 #include "sglrReferenceContext.hpp"
37 #include "sglrGLContext.hpp"
38 #include "deMath.h"
39 #include "deRandom.hpp"
40 #include "deStringUtil.hpp"
41
42 #include "glwEnums.hpp"
43 #include "glwFunctions.hpp"
44
45 using std::string;
46 using std::vector;
47 using tcu::IVec2;
48 using tcu::IVec3;
49 using tcu::Mat3;
50 using tcu::Mat4;
51 using tcu::Vec2;
52 using tcu::Vec3;
53 using tcu::Vec4;
54 using namespace glw; // GL types
55
56 namespace deqp
57 {
58
59 using namespace gls::TextureTestUtil;
60
61 namespace gles3
62 {
63 namespace Functional
64 {
65
66 static const int VIEWPORT_WIDTH = 128;
67 static const int VIEWPORT_HEIGHT = 128;
68
69 static const int TEXTURE_WIDTH_2D = 128;
70 static const int TEXTURE_HEIGHT_2D = 128;
71
72 // \note Cube map texture size is larger in order to make minifications possible - otherwise would need to display different faces at same time.
73 static const int TEXTURE_WIDTH_CUBE = 256;
74 static const int TEXTURE_HEIGHT_CUBE = 256;
75
76 static const int TEXTURE_WIDTH_2D_ARRAY = 64;
77 static const int TEXTURE_HEIGHT_2D_ARRAY = 64;
78 static const int TEXTURE_LAYERS_2D_ARRAY = 4;
79
80 static const int TEXTURE_WIDTH_3D = 32;
81 static const int TEXTURE_HEIGHT_3D = 32;
82 static const int TEXTURE_DEPTH_3D = 32;
83
84 static const int GRID_CELL_SIZE = 8;
85
86 static const GLenum s_testSizedInternalFormats[] = {
87 GL_RGBA32F, GL_RGBA32I, GL_RGBA32UI, GL_RGBA16F, GL_RGBA16I, GL_RGBA16UI, GL_RGBA8, GL_RGBA8I,
88 GL_RGBA8UI, GL_SRGB8_ALPHA8, GL_RGB10_A2, GL_RGB10_A2UI, GL_RGBA4, GL_RGB5_A1, GL_RGBA8_SNORM, GL_RGB8,
89 GL_RGB565, GL_R11F_G11F_B10F, GL_RGB32F, GL_RGB32I, GL_RGB32UI, GL_RGB16F, GL_RGB16I, GL_RGB16UI,
90 GL_RGB8_SNORM, GL_RGB8I, GL_RGB8UI, GL_SRGB8, GL_RGB9_E5, GL_RG32F, GL_RG32I, GL_RG32UI,
91 GL_RG16F, GL_RG16I, GL_RG16UI, GL_RG8, GL_RG8I, GL_RG8UI, GL_RG8_SNORM, GL_R32F,
92 GL_R32I, GL_R32UI, GL_R16F, GL_R16I, GL_R16UI, GL_R8, GL_R8I, GL_R8UI,
93 GL_R8_SNORM};
94
95 static const GLenum s_testWrapModes[] = {
96 GL_CLAMP_TO_EDGE,
97 GL_REPEAT,
98 GL_MIRRORED_REPEAT,
99 };
100
101 static const GLenum s_testMinFilters[] = {GL_NEAREST,
102 GL_LINEAR,
103 GL_NEAREST_MIPMAP_NEAREST,
104 GL_LINEAR_MIPMAP_NEAREST,
105 GL_NEAREST_MIPMAP_LINEAR,
106 GL_LINEAR_MIPMAP_LINEAR};
107
108 static const GLenum s_testNonMipmapMinFilters[] = {GL_NEAREST, GL_LINEAR};
109
110 static const GLenum s_testNearestMinFilters[] = {GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST};
111
112 static const GLenum s_testMagFilters[] = {GL_NEAREST, GL_LINEAR};
113
114 static const GLenum s_cubeFaceTargets[] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
115 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
116 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};
117
118 // 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).
matExtend3To4(const Mat3 & mat)119 static Mat4 matExtend3To4(const Mat3 &mat)
120 {
121 Mat4 res;
122 for (int rowNdx = 0; rowNdx < 3; rowNdx++)
123 {
124 Vec3 row = mat.getRow(rowNdx);
125 res.setRow(rowNdx, Vec4(row.x(), row.y(), row.z(), 0.0f));
126 }
127 res.setRow(3, Vec4(0.0f, 0.0f, 0.0f, 1.0f));
128
129 return res;
130 }
131
generateMultiTexFragmentShader(int numUnits,const vector<GLenum> & unitTypes,const vector<glu::DataType> & samplerTypes)132 static string generateMultiTexFragmentShader(int numUnits, const vector<GLenum> &unitTypes,
133 const vector<glu::DataType> &samplerTypes)
134 {
135 // The fragment shader calculates the average of a set of textures.
136
137 string samplersStr;
138 string matricesStr;
139 string scalesStr;
140 string biasesStr;
141 string lookupsStr;
142
143 string colorMultiplier = "(1.0/" + de::toString(numUnits) + ".0)";
144
145 for (int ndx = 0; ndx < numUnits; ndx++)
146 {
147 string ndxStr = de::toString(ndx);
148 string samplerName = "u_sampler" + ndxStr;
149 string transformationName = "u_trans" + ndxStr;
150 string scaleName = "u_texScale" + ndxStr;
151 string biasName = "u_texBias" + ndxStr;
152
153 samplersStr +=
154 string("") + "uniform highp " + glu::getDataTypeName(samplerTypes[ndx]) + " " + samplerName + ";\n";
155 matricesStr += "uniform highp mat4 " + transformationName + ";\n";
156 scalesStr += "uniform highp vec4 " + scaleName + ";\n";
157 biasesStr += "uniform highp vec4 " + biasName + ";\n";
158
159 string lookupCoord = transformationName + "*vec4(v_coord, 1.0, 1.0)";
160
161 if (unitTypes[ndx] == GL_TEXTURE_2D)
162 lookupCoord = "vec2(" + lookupCoord + ")";
163 else
164 lookupCoord = "vec3(" + lookupCoord + ")";
165
166 lookupsStr += "\tcolor += " + colorMultiplier + "*(vec4(texture(" + samplerName + ", " + lookupCoord + "))*" +
167 scaleName + " + " + biasName + ");\n";
168 }
169
170 return "#version 300 es\n"
171 "layout(location = 0) out mediump vec4 o_color;\n" +
172 samplersStr + matricesStr + scalesStr + biasesStr +
173 "in highp vec2 v_coord;\n"
174 "\n"
175 "void main (void)\n"
176 "{\n"
177 " mediump vec4 color = vec4(0.0);\n" +
178 lookupsStr +
179 " o_color = color;\n"
180 "}\n";
181 }
182
generateShaderProgramDeclaration(int numUnits,const vector<GLenum> & unitTypes,const vector<glu::DataType> & samplerTypes)183 static sglr::pdec::ShaderProgramDeclaration generateShaderProgramDeclaration(int numUnits,
184 const vector<GLenum> &unitTypes,
185 const vector<glu::DataType> &samplerTypes)
186 {
187 sglr::pdec::ShaderProgramDeclaration decl;
188
189 decl << sglr::pdec::VertexAttribute("a_position", rr::GENERICVECTYPE_FLOAT);
190 decl << sglr::pdec::VertexAttribute("a_coord", rr::GENERICVECTYPE_FLOAT);
191 decl << sglr::pdec::VertexToFragmentVarying(rr::GENERICVECTYPE_FLOAT);
192 decl << sglr::pdec::FragmentOutput(rr::GENERICVECTYPE_FLOAT);
193
194 for (int ndx = 0; ndx < numUnits; ++ndx)
195 {
196 string samplerName = "u_sampler" + de::toString(ndx);
197 string transformationName = "u_trans" + de::toString(ndx);
198 string scaleName = "u_texScale" + de::toString(ndx);
199 string biasName = "u_texBias" + de::toString(ndx);
200
201 decl << sglr::pdec::Uniform(samplerName, samplerTypes[ndx]);
202 decl << sglr::pdec::Uniform(transformationName, glu::TYPE_FLOAT_MAT4);
203 decl << sglr::pdec::Uniform(scaleName, glu::TYPE_FLOAT_VEC4);
204 decl << sglr::pdec::Uniform(biasName, glu::TYPE_FLOAT_VEC4);
205 }
206
207 decl << sglr::pdec::VertexSource("#version 300 es\n"
208 "in highp vec4 a_position;\n"
209 "in highp vec2 a_coord;\n"
210 "out highp vec2 v_coord;\n"
211 "\n"
212 "void main (void)\n"
213 "{\n"
214 " gl_Position = a_position;\n"
215 " v_coord = a_coord;\n"
216 "}\n");
217 decl << sglr::pdec::FragmentSource(generateMultiTexFragmentShader(numUnits, unitTypes, samplerTypes));
218
219 return decl;
220 }
221
222 // Calculates values that will be used in calculateLod().
calculateLodDerivateParts(const Mat4 & transformation)223 static tcu::Vector<tcu::Vec2, 3> calculateLodDerivateParts(const Mat4 &transformation)
224 {
225 // Calculate transformed coordinates of three screen corners.
226 Vec3 trans00 = (transformation * Vec4(0.0f, 0.0f, 1.0f, 1.0f)).xyz();
227 Vec3 trans01 = (transformation * Vec4(0.0f, 1.0f, 1.0f, 1.0f)).xyz();
228 Vec3 trans10 = (transformation * Vec4(1.0f, 0.0f, 1.0f, 1.0f)).xyz();
229
230 return tcu::Vector<tcu::Vec2, 3>(Vec2(trans10.x() - trans00.x(), trans01.x() - trans00.x()),
231 Vec2(trans10.y() - trans00.y(), trans01.y() - trans00.y()),
232 Vec2(trans10.z() - trans00.z(), trans01.z() - trans00.z()));
233 }
234
235 // Calculates the maximum allowed lod from derivates
calculateLodMax(const tcu::Vector<tcu::Vec2,3> & derivateParts,const tcu::IVec3 & textureSize,const Vec2 & screenDerivate)236 static float calculateLodMax(const tcu::Vector<tcu::Vec2, 3> &derivateParts, const tcu::IVec3 &textureSize,
237 const Vec2 &screenDerivate)
238 {
239 float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
240 float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
241 float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
242 float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
243 float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
244 float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
245
246 const float mu = de::max(de::abs(dudx), de::abs(dudy));
247 const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
248 const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
249 return deFloatLog2(mu + mv + mw);
250 }
251
252 // Calculates the minimum allowed lod from derivates
calculateLodMin(const tcu::Vector<tcu::Vec2,3> & derivateParts,const tcu::IVec3 & textureSize,const Vec2 & screenDerivate)253 static float calculateLodMin(const tcu::Vector<tcu::Vec2, 3> &derivateParts, const tcu::IVec3 &textureSize,
254 const Vec2 &screenDerivate)
255 {
256 float dudx = derivateParts[0].x() * (float)textureSize.x() * screenDerivate.x();
257 float dudy = derivateParts[0].y() * (float)textureSize.x() * screenDerivate.y();
258 float dvdx = derivateParts[1].x() * (float)textureSize.y() * screenDerivate.x();
259 float dvdy = derivateParts[1].y() * (float)textureSize.y() * screenDerivate.y();
260 float dwdx = derivateParts[2].x() * (float)textureSize.z() * screenDerivate.x();
261 float dwdy = derivateParts[2].y() * (float)textureSize.z() * screenDerivate.y();
262
263 const float mu = de::max(de::abs(dudx), de::abs(dudy));
264 const float mv = de::max(de::abs(dvdx), de::abs(dvdy));
265 const float mw = de::max(de::abs(dwdx), de::abs(dwdy));
266 return deFloatLog2(de::max(mu, de::max(mv, mw)));
267 }
268
269 class MultiTexShader : public sglr::ShaderProgram
270 {
271 public:
272 MultiTexShader(uint32_t randSeed, int numUnits, const vector<GLenum> &unitTypes,
273 const vector<glu::DataType> &samplerTypes, const vector<Vec4> &texScales,
274 const vector<Vec4> &texBiases,
275 const vector<int> &
276 num2dArrayLayers); // \note 2d array layer "coordinate" isn't normalized, so this is needed here.
277
278 void setUniforms(sglr::Context &context, uint32_t program) const;
279 void makeSafeLods(
280 const vector<IVec3> &textureSizes,
281 const IVec2 &viewportSize); // Modifies texture coordinates so that LODs aren't too close to x.5 or 0.0 .
282
283 private:
284 void shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets, const int numPackets) const;
285 void shadeFragments(rr::FragmentPacket *packets, const int numPackets,
286 const rr::FragmentShadingContext &context) const;
287
288 int m_numUnits;
289 vector<GLenum> m_unitTypes; // 2d, cube map, 2d array or 3d.
290 vector<Vec4> m_texScales;
291 vector<Vec4> m_texBiases;
292 vector<Mat4> m_transformations;
293 vector<tcu::Vector<tcu::Vec2, 3>> m_lodDerivateParts; // Parts of lod derivates; computed in init(), used in eval().
294 };
295
MultiTexShader(uint32_t randSeed,int numUnits,const vector<GLenum> & unitTypes,const vector<glu::DataType> & samplerTypes,const vector<Vec4> & texScales,const vector<Vec4> & texBiases,const vector<int> & num2dArrayLayers)296 MultiTexShader::MultiTexShader(uint32_t randSeed, int numUnits, const vector<GLenum> &unitTypes,
297 const vector<glu::DataType> &samplerTypes, const vector<Vec4> &texScales,
298 const vector<Vec4> &texBiases, const vector<int> &num2dArrayLayers)
299 : sglr::ShaderProgram(generateShaderProgramDeclaration(numUnits, unitTypes, samplerTypes))
300 , m_numUnits(numUnits)
301 , m_unitTypes(unitTypes)
302 , m_texScales(texScales)
303 , m_texBiases(texBiases)
304 {
305 // 2d-to-cube-face transformations.
306 // \note 2d coordinates range from 0 to 1 and cube face coordinates from -1 to 1, so scaling is done as well.
307 static const float s_cubeTransforms[][3 * 3] = {// Face -X: (x, y, 1) -> (-1, -(2*y-1), +(2*x-1))
308 {0.0f, 0.0f, -1.0f, 0.0f, -2.0f, 1.0f, 2.0f, 0.0f, -1.0f},
309 // Face +X: (x, y, 1) -> (+1, -(2*y-1), -(2*x-1))
310 {0.0f, 0.0f, 1.0f, 0.0f, -2.0f, 1.0f, -2.0f, 0.0f, 1.0f},
311 // Face -Y: (x, y, 1) -> (+(2*x-1), -1, -(2*y-1))
312 {2.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, -2.0f, 1.0f},
313 // Face +Y: (x, y, 1) -> (+(2*x-1), +1, +(2*y-1))
314 {2.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, -1.0f},
315 // Face -Z: (x, y, 1) -> (-(2*x-1), -(2*y-1), -1)
316 {-2.0f, 0.0f, 1.0f, 0.0f, -2.0f, 1.0f, 0.0f, 0.0f, -1.0f},
317 // Face +Z: (x, y, 1) -> (+(2*x-1), -(2*y-1), +1)
318 {2.0f, 0.0f, -1.0f, 0.0f, -2.0f, 1.0f, 0.0f, 0.0f, 1.0f}};
319
320 // Generate transformation matrices.
321
322 de::Random rnd(randSeed);
323
324 m_transformations.reserve(m_numUnits);
325 m_lodDerivateParts.reserve(m_numUnits);
326
327 int tex2dArrayNdx = 0; // Keep track of 2d texture array index.
328
329 DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
330
331 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
332 {
333 if (m_unitTypes[unitNdx] == GL_TEXTURE_2D)
334 {
335 float rotAngle = rnd.getFloat(0.0f, 2.0f * DE_PI);
336 float xScaleFactor = rnd.getFloat(0.7f, 1.5f);
337 float yScaleFactor = rnd.getFloat(0.7f, 1.5f);
338 float xShearAmount = rnd.getFloat(0.0f, 0.5f);
339 float yShearAmount = rnd.getFloat(0.0f, 0.5f);
340 float xTranslationAmount = rnd.getFloat(-0.5f, 0.5f);
341 float yTranslationAmount = rnd.getFloat(-0.5f, 0.5f);
342
343 static const float
344 tempOffsetData[3 * 3] = // For temporarily centering the coordinates to get nicer transformations.
345 {1.0f, 0.0f, -0.5f, 0.0f, 1.0f, -0.5f, 0.0f, 0.0f, 1.0f};
346 float rotTransfData[3 * 3] = {deFloatCos(rotAngle),
347 -deFloatSin(rotAngle),
348 0.0f,
349 deFloatSin(rotAngle),
350 deFloatCos(rotAngle),
351 0.0f,
352 0.0f,
353 0.0f,
354 1.0f};
355 float scaleTransfData[3 * 3] = {xScaleFactor, 0.0f, 0.0f, 0.0f, yScaleFactor, 0.0f, 0.0f, 0.0f, 1.0f};
356 float xShearTransfData[3 * 3] = {1.0f, xShearAmount, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
357 float yShearTransfData[3 * 3] = {1.0f, 0.0f, 0.0f, yShearAmount, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f};
358 float translationTransfData[3 * 3] = {1.0f, 0.0f, xTranslationAmount, 0.0f, 1.0f, yTranslationAmount, 0.0f,
359 0.0f, 1.0f};
360
361 Mat4 transformation = matExtend3To4(Mat3(tempOffsetData) * Mat3(translationTransfData) *
362 Mat3(rotTransfData) * Mat3(scaleTransfData) * Mat3(xShearTransfData) *
363 Mat3(yShearTransfData) * (Mat3(tempOffsetData) * (-1.0f)));
364
365 m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
366 m_transformations.push_back(transformation);
367 }
368 else if (m_unitTypes[unitNdx] == GL_TEXTURE_CUBE_MAP)
369 {
370 DE_STATIC_ASSERT((int)tcu::CUBEFACE_LAST == DE_LENGTH_OF_ARRAY(s_cubeTransforms));
371
372 float planarTransData[3 * 3];
373
374 // 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.
375
376 for (int i = 0; i < DE_LENGTH_OF_ARRAY(planarTransData); i++)
377 {
378 if (i == 0 || i == 4)
379 planarTransData[i] = rnd.getFloat(0.1f, 0.9f); // Two first diagonal cells control the scaling.
380 else if (i == 8)
381 planarTransData[i] = 1.0f;
382 else
383 planarTransData[i] = 0.0f;
384 }
385
386 int faceNdx = rnd.getInt(0, (int)tcu::CUBEFACE_LAST - 1);
387 Mat3 planarTrans(planarTransData); // Planar, face-agnostic transformation.
388 Mat4 finalTrans = matExtend3To4(
389 Mat3(s_cubeTransforms[faceNdx]) *
390 planarTrans); // Final transformation from planar to cube map coordinates, including the transformation just generated.
391 Mat4 planarTrans4x4 = matExtend3To4(planarTrans);
392
393 m_lodDerivateParts.push_back(calculateLodDerivateParts(planarTrans4x4));
394 m_transformations.push_back(finalTrans);
395 }
396 else
397 {
398 DE_ASSERT(m_unitTypes[unitNdx] == GL_TEXTURE_3D || m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY);
399
400 float transData[4 * 4];
401
402 for (int i = 0; i < 4 * 4; i++)
403 {
404 float sign = rnd.getBool() ? 1.0f : -1.0f;
405 transData[i] = rnd.getFloat(0.7f, 1.4f) * sign;
406 }
407
408 Mat4 transformation(transData);
409
410 if (m_unitTypes[unitNdx] == GL_TEXTURE_2D_ARRAY)
411 {
412 // Z direction: Translate by 0.5 and scale by layer amount.
413
414 float numLayers = (float)num2dArrayLayers[tex2dArrayNdx];
415
416 static const float zTranslationTransfData[4 * 4] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
417 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f};
418
419 float zScaleTransfData[4 * 4] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
420 0.0f, 0.0f, numLayers, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f};
421
422 transformation = transformation * Mat4(zScaleTransfData) * Mat4(zTranslationTransfData);
423
424 tex2dArrayNdx++;
425 }
426
427 m_lodDerivateParts.push_back(calculateLodDerivateParts(transformation));
428 m_transformations.push_back(Mat4(transformation));
429 }
430 }
431 }
432
setUniforms(sglr::Context & ctx,uint32_t program) const433 void MultiTexShader::setUniforms(sglr::Context &ctx, uint32_t program) const
434 {
435 ctx.useProgram(program);
436
437 // Sampler and matrix uniforms.
438
439 for (int ndx = 0; ndx < m_numUnits; ndx++)
440 {
441 string ndxStr = de::toString(ndx);
442
443 ctx.uniform1i(ctx.getUniformLocation(program, ("u_sampler" + ndxStr).c_str()), ndx);
444 ctx.uniformMatrix4fv(ctx.getUniformLocation(program, ("u_trans" + ndxStr).c_str()), 1, GL_FALSE,
445 (GLfloat *)&m_transformations[ndx].getColumnMajorData()[0]);
446 ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texScale" + ndxStr).c_str()), 1, m_texScales[ndx].getPtr());
447 ctx.uniform4fv(ctx.getUniformLocation(program, ("u_texBias" + ndxStr).c_str()), 1, m_texBiases[ndx].getPtr());
448 }
449 }
450
makeSafeLods(const vector<IVec3> & textureSizes,const IVec2 & viewportSize)451 void MultiTexShader::makeSafeLods(const vector<IVec3> &textureSizes, const IVec2 &viewportSize)
452 {
453 DE_ASSERT((int)textureSizes.size() == m_numUnits);
454
455 static const float shrinkScaleMat2dData[3 * 3] = {0.95f, 0.0f, 0.0f, 0.0f, 0.95f, 0.0f, 0.0f, 0.0f, 1.0f};
456 static const float shrinkScaleMat3dData[3 * 3] = {0.95f, 0.0f, 0.0f, 0.0f, 0.95f, 0.0f, 0.0f, 0.0f, 0.95f};
457 Mat4 shrinkScaleMat2d = matExtend3To4(Mat3(shrinkScaleMat2dData));
458 Mat4 shrinkScaleMat3d = matExtend3To4(Mat3(shrinkScaleMat3dData));
459
460 Vec2 screenDerivate(1.0f / (float)viewportSize.x(), 1.0f / (float)viewportSize.y());
461
462 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
463 {
464 // 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.
465 for (;;)
466 {
467 const float threshold = 0.1f;
468 const float epsilon = 0.01f;
469
470 const float lodMax = calculateLodMax(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
471 const float lodMin = calculateLodMin(m_lodDerivateParts[unitNdx], textureSizes[unitNdx], screenDerivate);
472
473 const int32_t maxLevel =
474 (lodMax + epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMax + epsilon + 0.5f) - 1);
475 const int32_t minLevel =
476 (lodMin - epsilon < 0.5f) ? (0) : (deCeilFloatToInt32(lodMin - epsilon + 0.5f) - 1);
477
478 if (de::abs(lodMax) < threshold || (lodMax > 0.0f && de::abs(deFloatFrac(lodMax) - 0.5f) < threshold) ||
479 de::abs(lodMin) < threshold || (lodMin > 0.0f && de::abs(deFloatFrac(lodMin) - 0.5f) < threshold) ||
480 maxLevel != minLevel)
481 {
482 m_transformations[unitNdx] =
483 (m_unitTypes[unitNdx] == GL_TEXTURE_3D ? shrinkScaleMat3d : shrinkScaleMat2d) *
484 m_transformations[unitNdx];
485 m_lodDerivateParts[unitNdx] = calculateLodDerivateParts(m_transformations[unitNdx]);
486 }
487 else
488 break;
489 }
490 }
491 }
492
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const493 void MultiTexShader::shadeVertices(const rr::VertexAttrib *inputs, rr::VertexPacket *const *packets,
494 const int numPackets) const
495 {
496 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
497 {
498 rr::VertexPacket &packet = *(packets[packetNdx]);
499
500 packet.position = rr::readVertexAttribFloat(inputs[0], packet.instanceNdx, packet.vertexNdx);
501 packet.outputs[0] = rr::readVertexAttribFloat(inputs[1], packet.instanceNdx, packet.vertexNdx);
502 }
503 }
504
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const505 void MultiTexShader::shadeFragments(rr::FragmentPacket *packets, const int numPackets,
506 const rr::FragmentShadingContext &context) const
507 {
508 DE_ASSERT((int)m_unitTypes.size() == m_numUnits);
509 DE_ASSERT((int)m_transformations.size() == m_numUnits);
510 DE_ASSERT((int)m_lodDerivateParts.size() == m_numUnits);
511
512 for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
513 {
514 rr::FragmentPacket &packet = packets[packetNdx];
515 const float colorMultiplier = 1.0f / (float)m_numUnits;
516 Vec4 outColors[4] = {Vec4(0.0f), Vec4(0.0f), Vec4(0.0f), Vec4(0.0f)};
517
518 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
519 {
520 tcu::Vec4 texSamples[4];
521
522 // Read tex coords
523 const tcu::Vec2 texCoords[4] = {
524 rr::readTriangleVarying<float>(packet, context, 0, 0).xy(),
525 rr::readTriangleVarying<float>(packet, context, 0, 1).xy(),
526 rr::readTriangleVarying<float>(packet, context, 0, 2).xy(),
527 rr::readTriangleVarying<float>(packet, context, 0, 3).xy(),
528 };
529
530 // Transform
531 tcu::Vec3 coords3D[4] = {
532 (m_transformations[unitNdx] * Vec4(texCoords[0].x(), texCoords[0].y(), 1.0f, 1.0f)).xyz(),
533 (m_transformations[unitNdx] * Vec4(texCoords[1].x(), texCoords[1].y(), 1.0f, 1.0f)).xyz(),
534 (m_transformations[unitNdx] * Vec4(texCoords[2].x(), texCoords[2].y(), 1.0f, 1.0f)).xyz(),
535 (m_transformations[unitNdx] * Vec4(texCoords[3].x(), texCoords[3].y(), 1.0f, 1.0f)).xyz(),
536 };
537
538 // To 2D
539 const tcu::Vec2 coords2D[4] = {
540 coords3D[0].xy(),
541 coords3D[1].xy(),
542 coords3D[2].xy(),
543 coords3D[3].xy(),
544 };
545
546 // Sample
547 switch (m_unitTypes[unitNdx])
548 {
549 case GL_TEXTURE_2D:
550 m_uniforms[4 * unitNdx].sampler.tex2D->sample4(texSamples, coords2D);
551 break;
552 case GL_TEXTURE_CUBE_MAP:
553 m_uniforms[4 * unitNdx].sampler.texCube->sample4(texSamples, coords3D);
554 break;
555 case GL_TEXTURE_2D_ARRAY:
556 m_uniforms[4 * unitNdx].sampler.tex2DArray->sample4(texSamples, coords3D);
557 break;
558 case GL_TEXTURE_3D:
559 m_uniforms[4 * unitNdx].sampler.tex3D->sample4(texSamples, coords3D);
560 break;
561 default:
562 DE_ASSERT(false);
563 }
564
565 // Add to sum
566 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
567 outColors[fragNdx] +=
568 colorMultiplier * (texSamples[fragNdx] * m_texScales[unitNdx] + m_texBiases[unitNdx]);
569 }
570
571 // output
572 for (int fragNdx = 0; fragNdx < 4; ++fragNdx)
573 rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, outColors[fragNdx]);
574 }
575 }
576
577 class TextureUnitCase : public TestCase
578 {
579 public:
580 enum CaseType
581 {
582 CASE_ONLY_2D = 0,
583 CASE_ONLY_CUBE,
584 CASE_ONLY_2D_ARRAY,
585 CASE_ONLY_3D,
586 CASE_MIXED,
587
588 CASE_LAST
589 };
590 TextureUnitCase(Context &context, const char *name, const char *desc,
591 int numUnits /* \note If non-positive, use all units */, CaseType caseType, uint32_t randSeed);
592 ~TextureUnitCase(void);
593
594 void init(void);
595 void deinit(void);
596 IterateResult iterate(void);
597
598 private:
599 struct TextureParameters
600 {
601 GLenum internalFormat;
602 GLenum wrapModeS;
603 GLenum wrapModeT;
604 GLenum wrapModeR;
605 GLenum minFilter;
606 GLenum magFilter;
607 };
608
609 TextureUnitCase(const TextureUnitCase &other);
610 TextureUnitCase &operator=(const TextureUnitCase &other);
611
612 void upload2dTexture(int texNdx, sglr::Context &context);
613 void uploadCubeTexture(int texNdx, sglr::Context &context);
614 void upload2dArrayTexture(int texNdx, sglr::Context &context);
615 void upload3dTexture(int texNdx, sglr::Context &context);
616
617 void render(sglr::Context &context);
618
619 const int m_numUnitsParam;
620 const CaseType m_caseType;
621 const uint32_t m_randSeed;
622
623 int m_numTextures; //!< \note Needed in addition to m_numUnits since same texture may be bound to many texture units.
624 int m_numUnits; //!< = m_numUnitsParam > 0 ? m_numUnitsParam : implementationDefinedMaximum
625
626 vector<GLenum> m_textureTypes;
627 vector<TextureParameters> m_textureParams;
628 vector<tcu::Texture2D *> m_textures2d;
629 vector<tcu::TextureCube *> m_texturesCube;
630 vector<tcu::Texture2DArray *> m_textures2dArray;
631 vector<tcu::Texture3D *> m_textures3d;
632 vector<int> m_unitTextures; //!< Which texture is used in a particular unit.
633 vector<int>
634 m_ndxTexType; //!< Index of a texture in m_textures2d, m_texturesCube, m_textures2dArray or m_textures3d, depending on texture type.
635 MultiTexShader *m_shader;
636 };
637
TextureUnitCase(Context & context,const char * name,const char * desc,int numUnits,CaseType caseType,uint32_t randSeed)638 TextureUnitCase::TextureUnitCase(Context &context, const char *name, const char *desc, int numUnits, CaseType caseType,
639 uint32_t randSeed)
640 : TestCase(context, tcu::NODETYPE_SELF_VALIDATE, name, desc)
641 , m_numUnitsParam(numUnits)
642 , m_caseType(caseType)
643 , m_randSeed(randSeed)
644 , m_numTextures(0)
645 , m_numUnits(0)
646 , m_shader(DE_NULL)
647 {
648 }
649
~TextureUnitCase(void)650 TextureUnitCase::~TextureUnitCase(void)
651 {
652 TextureUnitCase::deinit();
653 }
654
deinit(void)655 void TextureUnitCase::deinit(void)
656 {
657 for (vector<tcu::Texture2D *>::iterator i = m_textures2d.begin(); i != m_textures2d.end(); i++)
658 delete *i;
659 m_textures2d.clear();
660
661 for (vector<tcu::TextureCube *>::iterator i = m_texturesCube.begin(); i != m_texturesCube.end(); i++)
662 delete *i;
663 m_texturesCube.clear();
664
665 for (vector<tcu::Texture2DArray *>::iterator i = m_textures2dArray.begin(); i != m_textures2dArray.end(); i++)
666 delete *i;
667 m_textures2dArray.clear();
668
669 for (vector<tcu::Texture3D *>::iterator i = m_textures3d.begin(); i != m_textures3d.end(); i++)
670 delete *i;
671 m_textures3d.clear();
672
673 delete m_shader;
674 m_shader = DE_NULL;
675 }
676
init(void)677 void TextureUnitCase::init(void)
678 {
679 m_numUnits = m_numUnitsParam > 0 ? m_numUnitsParam : m_context.getContextInfo().getInt(GL_MAX_TEXTURE_IMAGE_UNITS);
680
681 // Make the textures.
682
683 try
684 {
685 tcu::TestLog &log = m_testCtx.getLog();
686 de::Random rnd(m_randSeed);
687
688 if (rnd.getFloat() < 0.7f)
689 m_numTextures = m_numUnits; // In most cases use one unit per texture.
690 else
691 m_numTextures =
692 rnd.getInt(deMax32(1, m_numUnits - 2), m_numUnits); // Sometimes assign same texture to multiple units.
693
694 log << tcu::TestLog::Message
695 << ("Using " + de::toString(m_numUnits) + " texture unit(s) and " + de::toString(m_numTextures) +
696 " texture(s)")
697 .c_str()
698 << tcu::TestLog::EndMessage;
699
700 m_textureTypes.reserve(m_numTextures);
701 m_textureParams.reserve(m_numTextures);
702 m_ndxTexType.reserve(m_numTextures);
703
704 // Generate textures.
705
706 for (int texNdx = 0; texNdx < m_numTextures; texNdx++)
707 {
708 // Either fixed or randomized target types, and randomized parameters for every texture.
709
710 TextureParameters params;
711
712 DE_STATIC_ASSERT(CASE_ONLY_2D == 0 && CASE_MIXED + 1 == CASE_LAST);
713
714 int texType = m_caseType == CASE_MIXED ? rnd.getInt(0, (int)CASE_MIXED - 1) : (int)m_caseType;
715 bool is2dTex = texType == 0;
716 bool isCubeTex = texType == 1;
717 bool is2dArrayTex = texType == 2;
718 bool is3dTex = texType == 3;
719
720 DE_ASSERT(is2dTex || isCubeTex || is2dArrayTex || is3dTex);
721
722 GLenum type = is2dTex ? GL_TEXTURE_2D :
723 isCubeTex ? GL_TEXTURE_CUBE_MAP :
724 is2dArrayTex ? GL_TEXTURE_2D_ARRAY :
725 GL_TEXTURE_3D;
726 const int texWidth = is2dTex ? TEXTURE_WIDTH_2D :
727 isCubeTex ? TEXTURE_WIDTH_CUBE :
728 is2dArrayTex ? TEXTURE_WIDTH_2D_ARRAY :
729 TEXTURE_WIDTH_3D;
730 const int texHeight = is2dTex ? TEXTURE_HEIGHT_2D :
731 isCubeTex ? TEXTURE_HEIGHT_CUBE :
732 is2dArrayTex ? TEXTURE_HEIGHT_2D_ARRAY :
733 TEXTURE_HEIGHT_3D;
734
735 const int texDepth = is3dTex ? TEXTURE_DEPTH_3D : 1;
736 const int texLayers = is2dArrayTex ? TEXTURE_LAYERS_2D_ARRAY : 1;
737
738 bool mipmaps = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight) && deIsPowerOfTwo32(texDepth));
739 int numLevels = mipmaps ? deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth)) + 1 : 1;
740
741 params.internalFormat =
742 s_testSizedInternalFormats[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testSizedInternalFormats) - 1)];
743
744 bool isFilterable = glu::isGLInternalColorFormatFilterable(params.internalFormat);
745
746 params.wrapModeS = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
747 params.wrapModeT = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
748 params.wrapModeR = s_testWrapModes[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testWrapModes) - 1)];
749
750 params.magFilter =
751 isFilterable ? s_testMagFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMagFilters) - 1)] : GL_NEAREST;
752
753 if (mipmaps)
754 params.minFilter =
755 isFilterable ?
756 s_testMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testMinFilters) - 1)] :
757 s_testNearestMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNearestMinFilters) - 1)];
758 else
759 params.minFilter =
760 isFilterable ?
761 s_testNonMipmapMinFilters[rnd.getInt(0, DE_LENGTH_OF_ARRAY(s_testNonMipmapMinFilters) - 1)] :
762 GL_NEAREST;
763
764 m_textureTypes.push_back(type);
765 m_textureParams.push_back(params);
766
767 // Create new texture.
768
769 tcu::TextureFormat texFormat = glu::mapGLInternalFormat((uint32_t)params.internalFormat);
770
771 if (is2dTex)
772 {
773 m_ndxTexType.push_back(
774 (int)m_textures2d.size()); // Remember the index this texture has in the 2d texture vector.
775 m_textures2d.push_back(new tcu::Texture2D(texFormat, texWidth, texHeight));
776 }
777 else if (isCubeTex)
778 {
779 m_ndxTexType.push_back(
780 (int)m_texturesCube.size()); // Remember the index this texture has in the cube texture vector.
781 DE_ASSERT(texWidth == texHeight);
782 m_texturesCube.push_back(new tcu::TextureCube(texFormat, texWidth));
783 }
784 else if (is2dArrayTex)
785 {
786 m_ndxTexType.push_back(
787 (int)m_textures2dArray
788 .size()); // Remember the index this texture has in the 2d array texture vector.
789 m_textures2dArray.push_back(new tcu::Texture2DArray(texFormat, texWidth, texHeight, texLayers));
790 }
791 else
792 {
793 m_ndxTexType.push_back(
794 (int)m_textures3d.size()); // Remember the index this texture has in the 3d vector.
795 m_textures3d.push_back(new tcu::Texture3D(texFormat, texWidth, texHeight, texDepth));
796 }
797
798 tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(texFormat);
799 Vec4 cBias = fmtInfo.valueMin;
800 Vec4 cScale = fmtInfo.valueMax - fmtInfo.valueMin;
801
802 // Fill with grid texture.
803
804 int numFaces = isCubeTex ? (int)tcu::CUBEFACE_LAST : 1;
805
806 for (int face = 0; face < numFaces; face++)
807 {
808 uint32_t rgb = rnd.getUint32() & 0x00ffffff;
809 uint32_t alpha = 0xff000000;
810
811 uint32_t colorA = alpha | rgb;
812 uint32_t colorB = alpha | ((~rgb) & 0x00ffffff);
813
814 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
815 {
816 if (is2dTex)
817 m_textures2d.back()->allocLevel(levelNdx);
818 else if (isCubeTex)
819 m_texturesCube.back()->allocLevel((tcu::CubeFace)face, levelNdx);
820 else if (is2dArrayTex)
821 m_textures2dArray.back()->allocLevel(levelNdx);
822 else
823 m_textures3d.back()->allocLevel(levelNdx);
824
825 int curCellSize = deMax32(1, GRID_CELL_SIZE >> levelNdx); // \note Scale grid cell size for mipmaps.
826
827 tcu::PixelBufferAccess access =
828 is2dTex ? m_textures2d.back()->getLevel(levelNdx) :
829 isCubeTex ? m_texturesCube.back()->getLevelFace(levelNdx, (tcu::CubeFace)face) :
830 is2dArrayTex ? m_textures2dArray.back()->getLevel(levelNdx) :
831 m_textures3d.back()->getLevel(levelNdx);
832
833 tcu::fillWithGrid(access, curCellSize, tcu::RGBA(colorA).toVec() * cScale + cBias,
834 tcu::RGBA(colorB).toVec() * cScale + cBias);
835 }
836 }
837 }
838
839 // Assign a texture index to each unit.
840
841 m_unitTextures.reserve(m_numUnits);
842
843 // \note Every texture is used at least once.
844 for (int i = 0; i < m_numTextures; i++)
845 m_unitTextures.push_back(i);
846
847 // Assign a random texture to remaining units.
848 while ((int)m_unitTextures.size() < m_numUnits)
849 m_unitTextures.push_back(rnd.getInt(0, m_numTextures - 1));
850
851 rnd.shuffle(m_unitTextures.begin(), m_unitTextures.end());
852
853 // Generate information for shader.
854
855 vector<GLenum> unitTypes;
856 vector<Vec4> texScales;
857 vector<Vec4> texBiases;
858 vector<glu::DataType> samplerTypes;
859 vector<int> num2dArrayLayers;
860
861 unitTypes.reserve(m_numUnits);
862 texScales.reserve(m_numUnits);
863 texBiases.reserve(m_numUnits);
864 samplerTypes.reserve(m_numUnits);
865 num2dArrayLayers.reserve(m_numUnits);
866
867 for (int i = 0; i < m_numUnits; i++)
868 {
869 int texNdx = m_unitTextures[i];
870 GLenum type = m_textureTypes[texNdx];
871 tcu::TextureFormat fmt = glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat);
872 tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(fmt);
873
874 unitTypes.push_back(type);
875
876 if (type == GL_TEXTURE_2D_ARRAY)
877 num2dArrayLayers.push_back(m_textures2dArray[m_ndxTexType[texNdx]]->getNumLayers());
878
879 texScales.push_back(fmtInfo.lookupScale);
880 texBiases.push_back(fmtInfo.lookupBias);
881
882 switch (type)
883 {
884 case GL_TEXTURE_2D:
885 samplerTypes.push_back(glu::getSampler2DType(fmt));
886 break;
887 case GL_TEXTURE_CUBE_MAP:
888 samplerTypes.push_back(glu::getSamplerCubeType(fmt));
889 break;
890 case GL_TEXTURE_2D_ARRAY:
891 samplerTypes.push_back(glu::getSampler2DArrayType(fmt));
892 break;
893 case GL_TEXTURE_3D:
894 samplerTypes.push_back(glu::getSampler3DType(fmt));
895 break;
896 default:
897 DE_ASSERT(false);
898 }
899 }
900
901 // Create shader.
902
903 DE_ASSERT(m_shader == DE_NULL);
904 m_shader = new MultiTexShader(rnd.getUint32(), m_numUnits, unitTypes, samplerTypes, texScales, texBiases,
905 num2dArrayLayers);
906 }
907 catch (const std::exception &)
908 {
909 // Clean up to save memory.
910 TextureUnitCase::deinit();
911 throw;
912 }
913 }
914
iterate(void)915 TextureUnitCase::IterateResult TextureUnitCase::iterate(void)
916 {
917 glu::RenderContext &renderCtx = m_context.getRenderContext();
918 const tcu::RenderTarget &renderTarget = renderCtx.getRenderTarget();
919 tcu::TestLog &log = m_testCtx.getLog();
920 de::Random rnd(m_randSeed);
921
922 int viewportWidth = deMin32(VIEWPORT_WIDTH, renderTarget.getWidth());
923 int viewportHeight = deMin32(VIEWPORT_HEIGHT, renderTarget.getHeight());
924 int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportWidth);
925 int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportHeight);
926
927 tcu::Surface gles3Frame(viewportWidth, viewportHeight);
928 tcu::Surface refFrame(viewportWidth, viewportHeight);
929
930 {
931 // First we do some tricks to make the LODs safer wrt. precision issues. See MultiTexShader::makeSafeLods().
932
933 vector<IVec3> texSizes;
934 texSizes.reserve(m_numUnits);
935
936 for (int i = 0; i < m_numUnits; i++)
937 {
938 int texNdx = m_unitTextures[i];
939 int texNdxInType = m_ndxTexType[texNdx];
940 GLenum type = m_textureTypes[texNdx];
941
942 switch (type)
943 {
944 case GL_TEXTURE_2D:
945 texSizes.push_back(
946 IVec3(m_textures2d[texNdxInType]->getWidth(), m_textures2d[texNdxInType]->getHeight(), 0));
947 break;
948 case GL_TEXTURE_CUBE_MAP:
949 texSizes.push_back(
950 IVec3(m_texturesCube[texNdxInType]->getSize(), m_texturesCube[texNdxInType]->getSize(), 0));
951 break;
952 case GL_TEXTURE_2D_ARRAY:
953 texSizes.push_back(IVec3(m_textures2dArray[texNdxInType]->getWidth(),
954 m_textures2dArray[texNdxInType]->getHeight(), 0));
955 break;
956 case GL_TEXTURE_3D:
957 texSizes.push_back(IVec3(m_textures3d[texNdxInType]->getWidth(),
958 m_textures3d[texNdxInType]->getHeight(),
959 m_textures3d[texNdxInType]->getDepth()));
960 break;
961 default:
962 DE_ASSERT(false);
963 }
964 }
965
966 m_shader->makeSafeLods(texSizes, IVec2(viewportWidth, viewportHeight));
967 }
968
969 // Render using GLES3.
970 {
971 sglr::GLContext context(renderCtx, log, sglr::GLCONTEXT_LOG_CALLS | sglr::GLCONTEXT_LOG_PROGRAMS,
972 tcu::IVec4(viewportX, viewportY, viewportWidth, viewportHeight));
973
974 render(context);
975
976 context.readPixels(gles3Frame, 0, 0, viewportWidth, viewportHeight);
977 }
978
979 // Render reference image.
980 {
981 sglr::ReferenceContextBuffers buffers(
982 tcu::PixelFormat(8, 8, 8, renderTarget.getPixelFormat().alphaBits ? 8 : 0), 0 /* depth */, 0 /* stencil */,
983 viewportWidth, viewportHeight);
984 sglr::ReferenceContext context(sglr::ReferenceContextLimits(renderCtx), buffers.getColorbuffer(),
985 buffers.getDepthbuffer(), buffers.getStencilbuffer());
986
987 render(context);
988
989 context.readPixels(refFrame, 0, 0, viewportWidth, viewportHeight);
990 }
991
992 // Compare images.
993 const float threshold = 0.001f;
994 bool isOk = tcu::fuzzyCompare(log, "ComparisonResult", "Image comparison result", refFrame, gles3Frame, threshold,
995 tcu::COMPARE_LOG_RESULT);
996
997 // Store test result.
998 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
999 isOk ? "Pass" : "Image comparison failed");
1000
1001 return STOP;
1002 }
1003
upload2dTexture(int texNdx,sglr::Context & context)1004 void TextureUnitCase::upload2dTexture(int texNdx, sglr::Context &context)
1005 {
1006 int ndx2d = m_ndxTexType[texNdx];
1007 const tcu::Texture2D *texture = m_textures2d[ndx2d];
1008 glu::TransferFormat formatGl =
1009 glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1010
1011 context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1012
1013 for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1014 {
1015 if (texture->isLevelEmpty(levelNdx))
1016 continue;
1017
1018 tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx);
1019 int width = access.getWidth();
1020 int height = access.getHeight();
1021
1022 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * width);
1023
1024 context.texImage2D(GL_TEXTURE_2D, levelNdx, m_textureParams[texNdx].internalFormat, width, height,
1025 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1026 GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d texture image data");
1027 }
1028 }
1029
uploadCubeTexture(int texNdx,sglr::Context & context)1030 void TextureUnitCase::uploadCubeTexture(int texNdx, sglr::Context &context)
1031 {
1032 int ndxCube = m_ndxTexType[texNdx];
1033 const tcu::TextureCube *texture = m_texturesCube[ndxCube];
1034 glu::TransferFormat formatGl =
1035 glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1036
1037 context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1038
1039 for (int face = 0; face < (int)tcu::CUBEFACE_LAST; face++)
1040 {
1041 for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1042 {
1043 if (texture->isLevelEmpty((tcu::CubeFace)face, levelNdx))
1044 continue;
1045
1046 tcu::ConstPixelBufferAccess access = texture->getLevelFace(levelNdx, (tcu::CubeFace)face);
1047 int width = access.getWidth();
1048 int height = access.getHeight();
1049
1050 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * width);
1051
1052 context.texImage2D(s_cubeFaceTargets[face], levelNdx, m_textureParams[texNdx].internalFormat, width, height,
1053 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1054 GLU_EXPECT_NO_ERROR(context.getError(), "Set cube map image data");
1055 }
1056 }
1057 }
1058
upload2dArrayTexture(int texNdx,sglr::Context & context)1059 void TextureUnitCase::upload2dArrayTexture(int texNdx, sglr::Context &context)
1060 {
1061 int ndx2dArray = m_ndxTexType[texNdx];
1062 const tcu::Texture2DArray *texture = m_textures2dArray[ndx2dArray];
1063 glu::TransferFormat formatGl =
1064 glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1065
1066 context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1067
1068 for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1069 {
1070 if (texture->isLevelEmpty(levelNdx))
1071 continue;
1072
1073 tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx);
1074 int width = access.getWidth();
1075 int height = access.getHeight();
1076 int layers = access.getDepth();
1077
1078 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * width);
1079 DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize() * width * height);
1080
1081 context.texImage3D(GL_TEXTURE_2D_ARRAY, levelNdx, m_textureParams[texNdx].internalFormat, width, height, layers,
1082 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1083 GLU_EXPECT_NO_ERROR(context.getError(), "Set 2d array texture image data");
1084 }
1085 }
1086
upload3dTexture(int texNdx,sglr::Context & context)1087 void TextureUnitCase::upload3dTexture(int texNdx, sglr::Context &context)
1088 {
1089 int ndx3d = m_ndxTexType[texNdx];
1090 const tcu::Texture3D *texture = m_textures3d[ndx3d];
1091 glu::TransferFormat formatGl =
1092 glu::getTransferFormat(glu::mapGLInternalFormat(m_textureParams[texNdx].internalFormat));
1093
1094 context.pixelStorei(GL_UNPACK_ALIGNMENT, 1);
1095
1096 for (int levelNdx = 0; levelNdx < texture->getNumLevels(); levelNdx++)
1097 {
1098 if (texture->isLevelEmpty(levelNdx))
1099 continue;
1100
1101 tcu::ConstPixelBufferAccess access = texture->getLevel(levelNdx);
1102 int width = access.getWidth();
1103 int height = access.getHeight();
1104 int depth = access.getDepth();
1105
1106 DE_ASSERT(access.getRowPitch() == access.getFormat().getPixelSize() * width);
1107 DE_ASSERT(access.getSlicePitch() == access.getFormat().getPixelSize() * width * height);
1108
1109 context.texImage3D(GL_TEXTURE_3D, levelNdx, m_textureParams[texNdx].internalFormat, width, height, depth,
1110 0 /* border */, formatGl.format, formatGl.dataType, access.getDataPtr());
1111 GLU_EXPECT_NO_ERROR(context.getError(), "Set 3d texture image data");
1112 }
1113 }
1114
render(sglr::Context & context)1115 void TextureUnitCase::render(sglr::Context &context)
1116 {
1117 // Setup textures.
1118
1119 vector<uint32_t> textureGLNames;
1120 vector<bool> isTextureSetUp(
1121 m_numTextures,
1122 false); // \note Same texture may be bound to multiple units, but we only want to set up parameters and data once per texture.
1123
1124 textureGLNames.resize(m_numTextures);
1125 context.genTextures(m_numTextures, &textureGLNames[0]);
1126 GLU_EXPECT_NO_ERROR(context.getError(), "Generate textures");
1127
1128 for (int unitNdx = 0; unitNdx < m_numUnits; unitNdx++)
1129 {
1130 int texNdx = m_unitTextures[unitNdx];
1131
1132 // Bind texture to unit.
1133 context.activeTexture(GL_TEXTURE0 + unitNdx);
1134 GLU_EXPECT_NO_ERROR(context.getError(), "Set active texture");
1135 context.bindTexture(m_textureTypes[texNdx], textureGLNames[texNdx]);
1136 GLU_EXPECT_NO_ERROR(context.getError(), "Bind texture");
1137
1138 if (!isTextureSetUp[texNdx])
1139 {
1140 // Binding this texture for first time, so set parameters and data.
1141
1142 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_S, m_textureParams[texNdx].wrapModeS);
1143 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_T, m_textureParams[texNdx].wrapModeT);
1144 if (m_textureTypes[texNdx] == GL_TEXTURE_3D)
1145 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_WRAP_R, m_textureParams[texNdx].wrapModeR);
1146 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MIN_FILTER, m_textureParams[texNdx].minFilter);
1147 context.texParameteri(m_textureTypes[texNdx], GL_TEXTURE_MAG_FILTER, m_textureParams[texNdx].magFilter);
1148 GLU_EXPECT_NO_ERROR(context.getError(), "Set texture parameters");
1149
1150 switch (m_textureTypes[texNdx])
1151 {
1152 case GL_TEXTURE_2D:
1153 upload2dTexture(texNdx, context);
1154 break;
1155 case GL_TEXTURE_CUBE_MAP:
1156 uploadCubeTexture(texNdx, context);
1157 break;
1158 case GL_TEXTURE_2D_ARRAY:
1159 upload2dArrayTexture(texNdx, context);
1160 break;
1161 case GL_TEXTURE_3D:
1162 upload3dTexture(texNdx, context);
1163 break;
1164 default:
1165 DE_ASSERT(false);
1166 }
1167
1168 isTextureSetUp[texNdx] = true; // Don't set up this texture's parameters and data again later.
1169 }
1170 }
1171
1172 GLU_EXPECT_NO_ERROR(context.getError(), "Set textures");
1173
1174 // Setup shader
1175
1176 uint32_t shaderID = context.createProgram(m_shader);
1177
1178 // Draw.
1179
1180 context.clearColor(0.125f, 0.25f, 0.5f, 1.0f);
1181 context.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
1182 m_shader->setUniforms(context, shaderID);
1183 sglr::drawQuad(context, shaderID, Vec3(-1.0f, -1.0f, 0.0f), Vec3(1.0f, 1.0f, 0.0f));
1184 GLU_EXPECT_NO_ERROR(context.getError(), "Draw");
1185
1186 // Delete previously generated texture names.
1187
1188 context.deleteTextures(m_numTextures, &textureGLNames[0]);
1189 GLU_EXPECT_NO_ERROR(context.getError(), "Delete textures");
1190 }
1191
TextureUnitTests(Context & context)1192 TextureUnitTests::TextureUnitTests(Context &context) : TestCaseGroup(context, "units", "Texture Unit Usage Tests")
1193 {
1194 }
1195
~TextureUnitTests(void)1196 TextureUnitTests::~TextureUnitTests(void)
1197 {
1198 }
1199
init(void)1200 void TextureUnitTests::init(void)
1201 {
1202 const int numTestsPerGroup = 10;
1203
1204 static const int unitCounts[] = {
1205 2, 4, 8,
1206 -1 // \note Negative stands for the implementation-specified maximum.
1207 };
1208
1209 for (int unitCountNdx = 0; unitCountNdx < DE_LENGTH_OF_ARRAY(unitCounts); unitCountNdx++)
1210 {
1211 int numUnits = unitCounts[unitCountNdx];
1212
1213 string countGroupName = (unitCounts[unitCountNdx] < 0 ? "all" : de::toString(numUnits)) + "_units";
1214
1215 tcu::TestCaseGroup *countGroup = new tcu::TestCaseGroup(m_testCtx, countGroupName.c_str(), "");
1216 addChild(countGroup);
1217
1218 DE_STATIC_ASSERT((int)TextureUnitCase::CASE_ONLY_2D == 0);
1219
1220 for (int caseType = (int)TextureUnitCase::CASE_ONLY_2D; caseType < (int)TextureUnitCase::CASE_LAST; caseType++)
1221 {
1222 const char *caseTypeGroupName =
1223 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D ? "only_2d" :
1224 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_CUBE ? "only_cube" :
1225 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_2D_ARRAY ? "only_2d_array" :
1226 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_ONLY_3D ? "only_3d" :
1227 (TextureUnitCase::CaseType)caseType == TextureUnitCase::CASE_MIXED ? "mixed" :
1228 DE_NULL;
1229
1230 DE_ASSERT(caseTypeGroupName != DE_NULL);
1231
1232 tcu::TestCaseGroup *caseTypeGroup = new tcu::TestCaseGroup(m_testCtx, caseTypeGroupName, "");
1233 countGroup->addChild(caseTypeGroup);
1234
1235 for (int testNdx = 0; testNdx < numTestsPerGroup; testNdx++)
1236 caseTypeGroup->addChild(new TextureUnitCase(m_context, de::toString(testNdx).c_str(), "", numUnits,
1237 (TextureUnitCase::CaseType)caseType,
1238 deUint32Hash((uint32_t)testNdx)));
1239 }
1240 }
1241 }
1242
1243 } // namespace Functional
1244 } // namespace gles3
1245 } // namespace deqp
1246