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