1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 Module
3 * -------------------------------------------------
5 * Copyright 2014 The Android Open Source Project
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
21 * \brief Vertex texture tests.
22 *//*--------------------------------------------------------------------*/
24 #include "es2fVertexTextureTests.hpp"
25 #include "glsTextureTestUtil.hpp"
26 #include "gluTexture.hpp"
27 #include "gluPixelTransfer.hpp"
28 #include "gluTextureUtil.hpp"
29 #include "tcuVector.hpp"
30 #include "tcuMatrix.hpp"
31 #include "tcuTextureUtil.hpp"
32 #include "tcuTexVerifierUtil.hpp"
33 #include "tcuImageCompare.hpp"
34 #include "deRandom.hpp"
59 using namespace gls::TextureTestUtil;
60 using namespace glu::TextureTestUtil;
62 using glu::TextureTestUtil::TEXTURETYPE_2D;
63 using glu::TextureTestUtil::TEXTURETYPE_CUBE;
70 // The 2D case draws four images.
71 static const int MAX_2D_RENDER_WIDTH = 128*2;
72 static const int MAX_2D_RENDER_HEIGHT = 128*2;
74 // The cube map case draws four 3-by-2 image groups.
75 static const int MAX_CUBE_RENDER_WIDTH = 28*2*3;
76 static const int MAX_CUBE_RENDER_HEIGHT = 28*2*2;
78 static const int GRID_SIZE_2D = 127;
79 static const int GRID_SIZE_CUBE = 63;
81 // Helpers for making texture coordinates "safe", i.e. move them further from coordinate bounary.
83 // Moves x towards the closest K+targetFraction, where K is an integer.
84 // E.g. moveTowardsFraction(x, 0.5f) moves x away from integer boundaries.
85 static inline float moveTowardsFraction (float x, float targetFraction)
87 const float strictness = 0.5f;
88 DE_ASSERT(0.0f < strictness && strictness <= 1.0f);
89 DE_ASSERT(de::inBounds(targetFraction, 0.0f, 1.0f));
90 const float y = x + 0.5f - targetFraction;
91 return deFloatFloor(y) + deFloatFrac(y)*(1.0f-strictness) + strictness*0.5f - 0.5f + targetFraction;
94 static inline float safeCoord (float raw, int scale, float fraction)
96 const float scaleFloat = (float)scale;
97 return moveTowardsFraction(raw*scaleFloat, fraction) / scaleFloat;
101 static inline tcu::Vector<float, Size> safeCoords (const tcu::Vector<float, Size>& raw, const tcu::Vector<int, Size>& scale, const tcu::Vector<float, Size>& fraction)
103 tcu::Vector<float, Size> result;
104 for (int i = 0; i < Size; i++)
105 result[i] = safeCoord(raw[i], scale[i], fraction[i]);
109 static inline Vec2 safe2DTexCoords (const Vec2& raw, const IVec2& textureSize)
111 return safeCoords(raw, textureSize, Vec2(0.5f));
119 Rect (int x_, int y_, int w_, int h_) : x(x_), y(y_), w(w_), h(h_) {}
120 IVec2 pos (void) const { return IVec2(x, y); }
121 IVec2 size (void) const { return IVec2(w, h); }
129 template <TextureType> struct TexTypeTcuClass;
130 template <> struct TexTypeTcuClass<TEXTURETYPE_2D> { typedef tcu::Texture2D t; };
131 template <> struct TexTypeTcuClass<TEXTURETYPE_CUBE> { typedef tcu::TextureCube t; };
133 template <TextureType> struct TexTypeSizeDims;
134 template <> struct TexTypeSizeDims<TEXTURETYPE_2D> { enum { V = 2 }; };
135 template <> struct TexTypeSizeDims<TEXTURETYPE_CUBE> { enum { V = 2 }; };
137 template <TextureType> struct TexTypeCoordDims;
138 template <> struct TexTypeCoordDims<TEXTURETYPE_2D> { enum { V = 2 }; };
139 template <> struct TexTypeCoordDims<TEXTURETYPE_CUBE> { enum { V = 3 }; };
141 template <TextureType TexType> struct TexTypeSizeIVec { typedef tcu::Vector<int, TexTypeSizeDims<TexType>::V> t; };
142 template <TextureType TexType> struct TexTypeCoordVec { typedef tcu::Vector<float, TexTypeCoordDims<TexType>::V> t; };
144 template <TextureType> struct TexTypeCoordParams;
147 TexTypeCoordParams<TEXTURETYPE_2D>
152 TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_) : scale(scale_), bias(bias_) {}
156 TexTypeCoordParams<TEXTURETYPE_CUBE>
162 TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_, tcu::CubeFace face_) : scale(scale_), bias(bias_), face(face_) {}
165 /*--------------------------------------------------------------------*//*!
166 * \brief Quad grid class containing position and texture coordinate data.
168 * A quad grid of size S means a grid consisting of S*S quads (S rows and
169 * S columns). The quads are rectangles with main axis aligned sides, and
170 * each consists of two triangles. Note that although there are only
171 * (S+1)*(S+1) distinct vertex positions, there are S*S*4 distinct vertices
172 * because we want texture coordinates to be constant across the vertices
173 * of a quad (to avoid interpolation issues), and thus each quad needs its
176 * Pointers returned by get*Ptr() are suitable for gl calls such as
177 * glVertexAttribPointer() (for position and tex coord) or glDrawElements()
179 *//*--------------------------------------------------------------------*/
180 template <TextureType TexType>
181 class PosTexCoordQuadGrid
184 enum { TEX_COORD_DIMS = TexTypeCoordDims <TexType>::V };
185 typedef typename TexTypeCoordVec<TexType>::t TexCoordVec;
186 typedef typename TexTypeSizeIVec<TexType>::t TexSizeIVec;
187 typedef TexTypeCoordParams<TexType> TexCoordParams;
190 PosTexCoordQuadGrid (int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords);
192 int getSize (void) const { return m_gridSize; }
193 Vec4 getQuadLDRU (int col, int row) const; //!< Vec4(leftX, downY, rightX, upY)
194 const TexCoordVec& getQuadTexCoord (int col, int row) const;
196 int getNumIndices (void) const { return m_gridSize*m_gridSize*3*2; }
197 const float* getPositionPtr (void) const { DE_STATIC_ASSERT(sizeof(Vec2) == 2*sizeof(float)); return (float*)&m_positions[0]; }
198 const float* getTexCoordPtr (void) const { DE_STATIC_ASSERT(sizeof(TexCoordVec) == TEX_COORD_DIMS*(int)sizeof(float)); return (float*)&m_texCoords[0]; }
199 const deUint16* getIndexPtr (void) const { return &m_indices[0]; }
202 void initializeTexCoords (const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords);
204 const int m_gridSize;
205 vector<Vec2> m_positions;
206 vector<TexCoordVec> m_texCoords;
207 vector<deUint16> m_indices;
210 template <TextureType TexType>
211 Vec4 PosTexCoordQuadGrid<TexType>::getQuadLDRU (int col, int row) const
213 int ndx00 = (row*m_gridSize + col) * 4;
214 int ndx11 = ndx00 + 3;
216 return Vec4(m_positions[ndx00].x(),
217 m_positions[ndx00].y(),
218 m_positions[ndx11].x(),
219 m_positions[ndx11].y());
222 template <TextureType TexType>
223 const typename TexTypeCoordVec<TexType>::t& PosTexCoordQuadGrid<TexType>::getQuadTexCoord (int col, int row) const
225 return m_texCoords[(row*m_gridSize + col) * 4];
228 template <TextureType TexType>
229 PosTexCoordQuadGrid<TexType>::PosTexCoordQuadGrid (int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
230 : m_gridSize(gridSize)
232 DE_ASSERT(m_gridSize > 0 && m_gridSize*m_gridSize <= (int)std::numeric_limits<deUint16>::max() + 1);
234 const float gridSizeFloat = (float)m_gridSize;
236 m_positions.reserve(m_gridSize*m_gridSize*4);
237 m_indices.reserve(m_gridSize*m_gridSize*3*2);
239 for (int y = 0; y < m_gridSize; y++)
240 for (int x = 0; x < m_gridSize; x++)
242 float fx0 = (float)(x+0) / gridSizeFloat;
243 float fx1 = (float)(x+1) / gridSizeFloat;
244 float fy0 = (float)(y+0) / gridSizeFloat;
245 float fy1 = (float)(y+1) / gridSizeFloat;
247 Vec2 quadVertices[4] = { Vec2(fx0, fy0), Vec2(fx1, fy0), Vec2(fx0, fy1), Vec2(fx1, fy1) };
249 int firstNdx = (int)m_positions.size();
251 for (int i = 0; i < DE_LENGTH_OF_ARRAY(quadVertices); i++)
252 m_positions.push_back(safeCoords(quadVertices[i], renderSize, Vec2(0.0f)) * 2.0f - 1.0f);
254 m_indices.push_back(deUint16(firstNdx + 0));
255 m_indices.push_back(deUint16(firstNdx + 1));
256 m_indices.push_back(deUint16(firstNdx + 2));
258 m_indices.push_back(deUint16(firstNdx + 1));
259 m_indices.push_back(deUint16(firstNdx + 3));
260 m_indices.push_back(deUint16(firstNdx + 2));
263 m_texCoords.reserve(m_gridSize*m_gridSize*4);
264 initializeTexCoords(textureSize, texCoordParams, useSafeTexCoords);
266 DE_ASSERT((int)m_positions.size() == m_gridSize*m_gridSize*4);
267 DE_ASSERT((int)m_indices.size() == m_gridSize*m_gridSize*3*2);
268 DE_ASSERT((int)m_texCoords.size() == m_gridSize*m_gridSize*4);
272 void PosTexCoordQuadGrid<TEXTURETYPE_2D>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
274 DE_ASSERT(m_texCoords.empty());
276 const float gridSizeFloat = (float)m_gridSize;
278 for (int y = 0; y < m_gridSize; y++)
279 for (int x = 0; x < m_gridSize; x++)
281 Vec2 rawCoord = Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) * texCoordParams.scale + texCoordParams.bias;
283 for (int i = 0; i < 4; i++)
284 m_texCoords.push_back(useSafeTexCoords ? safe2DTexCoords(rawCoord, textureSize) : rawCoord);
289 void PosTexCoordQuadGrid<TEXTURETYPE_CUBE>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords)
291 DE_ASSERT(m_texCoords.empty());
293 const float gridSizeFloat = (float)m_gridSize;
294 vector<float> texBoundaries;
295 computeQuadTexCoordCube(texBoundaries, texCoordParams.face);
296 const Vec3 coordA = Vec3(texBoundaries[0], texBoundaries[1], texBoundaries[2]);
297 const Vec3 coordB = Vec3(texBoundaries[3], texBoundaries[4], texBoundaries[5]);
298 const Vec3 coordC = Vec3(texBoundaries[6], texBoundaries[7], texBoundaries[8]);
299 const Vec3 coordAB = coordB - coordA;
300 const Vec3 coordAC = coordC - coordA;
302 for (int y = 0; y < m_gridSize; y++)
303 for (int x = 0; x < m_gridSize; x++)
305 const Vec2 rawFaceCoord = texCoordParams.scale * Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) + texCoordParams.bias;
306 const Vec2 safeFaceCoord = useSafeTexCoords ? safe2DTexCoords(rawFaceCoord, textureSize) : rawFaceCoord;
307 const Vec3 texCoord = coordA + coordAC*safeFaceCoord.x() + coordAB*safeFaceCoord.y();
309 for (int i = 0; i < 4; i++)
310 m_texCoords.push_back(texCoord);
316 static inline bool isLevelNearest (deUint32 filter)
318 return filter == GL_NEAREST || filter == GL_NEAREST_MIPMAP_NEAREST || filter == GL_NEAREST_MIPMAP_LINEAR;
321 static inline IVec2 getTextureSize (const glu::Texture2D& tex)
323 const tcu::Texture2D& ref = tex.getRefTexture();
324 return IVec2(ref.getWidth(), ref.getHeight());
327 static inline IVec2 getTextureSize (const glu::TextureCube& tex)
329 const tcu::TextureCube& ref = tex.getRefTexture();
330 return IVec2(ref.getSize(), ref.getSize());
333 template <TextureType TexType>
334 static void setPixelColors (const vector<Vec4>& quadColors, const Rect& region, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst)
336 const int gridSize = grid.getSize();
338 for (int y = 0; y < gridSize; y++)
339 for (int x = 0; x < gridSize; x++)
341 const Vec4 color = quadColors[y*gridSize + x];
342 const Vec4 ldru = grid.getQuadLDRU(x, y) * 0.5f + 0.5f; // [-1, 1] -> [0, 1]
343 const int ix0 = deCeilFloatToInt32(ldru.x() * (float)region.w - 0.5f);
344 const int ix1 = deCeilFloatToInt32(ldru.z() * (float)region.w - 0.5f);
345 const int iy0 = deCeilFloatToInt32(ldru.y() * (float)region.h - 0.5f);
346 const int iy1 = deCeilFloatToInt32(ldru.w() * (float)region.h - 0.5f);
348 for (int iy = iy0; iy < iy1; iy++)
349 for (int ix = ix0; ix < ix1; ix++)
351 DE_ASSERT(deInBounds32(ix + region.x, 0, dst.getWidth()));
352 DE_ASSERT(deInBounds32(iy + region.y, 0, dst.getHeight()));
354 dst.setPixel(ix + region.x, iy + region.y, tcu::RGBA(color));
359 static inline Vec4 sample (const tcu::Texture2D& tex, const Vec2& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), lod); }
360 static inline Vec4 sample (const tcu::TextureCube& tex, const Vec3& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), coord.z(), lod); }
362 template <TextureType TexType>
363 void computeReference (const typename TexTypeTcuClass<TexType>::t& texture, float lod, const tcu::Sampler& sampler, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst, const Rect& dstRegion)
365 const int gridSize = grid.getSize();
366 vector<Vec4> quadColors (gridSize*gridSize);
368 for (int y = 0; y < gridSize; y++)
369 for (int x = 0; x < gridSize; x++)
371 const int ndx = y*gridSize + x;
372 const typename TexTypeCoordVec<TexType>::t& coord = grid.getQuadTexCoord(x, y);
374 quadColors[ndx] = sample(texture, coord, lod, sampler);
377 setPixelColors(quadColors, dstRegion, grid, dst);
380 static bool compareImages (const glu::RenderContext& renderCtx, tcu::TestLog& log, const tcu::Surface& ref, const tcu::Surface& res)
382 DE_ASSERT(renderCtx.getRenderTarget().getNumSamples() == 0);
384 const tcu::RGBA threshold = renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(15,15,15,15);
385 return tcu::pixelThresholdCompare(log, "Result", "Image compare result", ref, res, threshold, tcu::COMPARE_LOG_RESULT);
388 class Vertex2DTextureCase : public TestCase
391 Vertex2DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
392 ~Vertex2DTextureCase (void);
396 IterateResult iterate (void);
399 typedef PosTexCoordQuadGrid<TEXTURETYPE_2D> Grid;
401 Vertex2DTextureCase (const Vertex2DTextureCase& other);
402 Vertex2DTextureCase& operator= (const Vertex2DTextureCase& other);
404 float calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const;
405 void setupShaderInputs (int textureNdx, float lod, const Grid& grid) const;
406 void renderCell (int textureNdx, float lod, const Grid& grid) const;
407 void computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
409 const deUint32 m_minFilter;
410 const deUint32 m_magFilter;
411 const deUint32 m_wrapS;
412 const deUint32 m_wrapT;
414 const glu::ShaderProgram* m_program;
415 glu::Texture2D* m_textures[2]; // 2 textures, a gradient texture and a grid texture.
418 Vertex2DTextureCase::Vertex2DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
419 : TestCase (testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
420 , m_minFilter (minFilter)
421 , m_magFilter (magFilter)
424 , m_program (DE_NULL)
426 m_textures[0] = DE_NULL;
427 m_textures[1] = DE_NULL;
430 Vertex2DTextureCase::~Vertex2DTextureCase(void)
432 Vertex2DTextureCase::deinit();
435 void Vertex2DTextureCase::init (void)
437 const char* const vertexShader =
438 "attribute highp vec2 a_position;\n"
439 "attribute highp vec2 a_texCoord;\n"
440 "uniform highp sampler2D u_texture;\n"
441 "uniform highp float u_lod;\n"
442 "varying mediump vec4 v_color;\n"
446 " gl_Position = vec4(a_position, 0.0, 1.0);\n"
447 " v_color = texture2DLod(u_texture, a_texCoord, u_lod);\n"
450 const char* const fragmentShader =
451 "varying mediump vec4 v_color;\n"
455 " gl_FragColor = v_color;\n"
458 if (m_context.getRenderTarget().getNumSamples() != 0)
459 throw tcu::NotSupportedError("MSAA config not supported by this test");
461 DE_ASSERT(!m_program);
462 m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader));
464 if(!m_program->isOk())
466 m_testCtx.getLog() << *m_program;
468 GLint maxVertexTextures;
469 glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
471 if (maxVertexTextures < 1)
472 throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
474 TCU_FAIL("Failed to compile shader");
477 // Make the textures.
480 // Compute suitable power-of-two sizes (for mipmaps).
481 const int texWidth = 1 << deLog2Ceil32(MAX_2D_RENDER_WIDTH / 2);
482 const int texHeight = 1 << deLog2Ceil32(MAX_2D_RENDER_HEIGHT / 2);
484 for (int i = 0; i < 2; i++)
486 DE_ASSERT(!m_textures[i]);
487 m_textures[i] = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight);
490 const bool mipmaps = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight));
491 const int numLevels = mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1;
492 const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
493 const Vec4 cBias = fmtInfo.valueMin;
494 const Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin;
496 // Fill first with gradient texture.
497 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
499 const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias;
500 const Vec4 gMax = Vec4( 1.0f, 1.0f, 1.0f, 0.0f)*cScale + cBias;
502 m_textures[0]->getRefTexture().allocLevel(levelNdx);
503 tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax);
506 // Fill second with grid texture.
507 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
509 const deUint32 step = 0x00ffffff / numLevels;
510 const deUint32 rgb = step*levelNdx;
511 const deUint32 colorA = 0xff000000 | rgb;
512 const deUint32 colorB = 0xff000000 | ~rgb;
514 m_textures[1]->getRefTexture().allocLevel(levelNdx);
515 tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
519 for (int i = 0; i < 2; i++)
520 m_textures[i]->upload();
522 catch (const std::exception&)
524 // Clean up to save memory.
525 Vertex2DTextureCase::deinit();
530 void Vertex2DTextureCase::deinit (void)
532 for (int i = 0; i < 2; i++)
534 delete m_textures[i];
535 m_textures[i] = DE_NULL;
542 float Vertex2DTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const
544 const tcu::Texture2D& refTexture = m_textures[textureNdx]->getRefTexture();
545 const Vec2 srcSize = Vec2((float)refTexture.getWidth(), (float)refTexture.getHeight());
546 const Vec2 sizeRatio = texScale*srcSize / dstSize;
548 // \note In this particular case dv/dx and du/dy are zero, simplifying the expression.
549 return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y()));
552 Vertex2DTextureCase::IterateResult Vertex2DTextureCase::iterate (void)
554 const int viewportWidth = deMin32(m_context.getRenderTarget().getWidth(), MAX_2D_RENDER_WIDTH);
555 const int viewportHeight = deMin32(m_context.getRenderTarget().getHeight(), MAX_2D_RENDER_HEIGHT);
557 const int viewportXOffsetMax = m_context.getRenderTarget().getWidth() - viewportWidth;
558 const int viewportYOffsetMax = m_context.getRenderTarget().getHeight() - viewportHeight;
560 de::Random rnd (deStringHash(getName()));
562 const int viewportXOffset = rnd.getInt(0, viewportXOffsetMax);
563 const int viewportYOffset = rnd.getInt(0, viewportYOffsetMax);
565 glUseProgram(m_program->getProgram());
567 // Divide viewport into 4 cells.
568 const int leftWidth = viewportWidth / 2;
569 const int rightWidth = viewportWidth - leftWidth;
570 const int bottomHeight = viewportHeight / 2;
571 const int topHeight = viewportHeight - bottomHeight;
574 glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
575 glClear(GL_COLOR_BUFFER_BIT);
577 // Texture scaling and offsetting vectors.
578 const Vec2 texMinScale (+1.8f, +1.8f);
579 const Vec2 texMinOffset (-0.3f, -0.2f);
580 const Vec2 texMagScale (+0.3f, +0.3f);
581 const Vec2 texMagOffset (+0.9f, +0.8f);
583 // Surface for the reference image.
584 tcu::Surface refImage(viewportWidth, viewportHeight);
591 const Vec2 texCoordScale;
592 const Vec2 texCoordOffset;
593 Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {}
596 Render(Rect(0, 0, leftWidth, bottomHeight), 0, texMinScale, texMinOffset),
597 Render(Rect(leftWidth, 0, rightWidth, bottomHeight), 0, texMagScale, texMagOffset),
598 Render(Rect(0, bottomHeight, leftWidth, topHeight), 1, texMinScale, texMinOffset),
599 Render(Rect(leftWidth, bottomHeight, rightWidth, topHeight), 1, texMagScale, texMagOffset)
602 for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
604 const Render& rend = renders[renderNdx];
605 const float lod = calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx);
606 const bool useSafeTexCoords = isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
607 const Grid grid (GRID_SIZE_2D, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
608 TexTypeCoordParams<TEXTURETYPE_2D>(rend.texCoordScale, rend.texCoordOffset), useSafeTexCoords);
610 glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
611 renderCell (rend.textureNdx, lod, grid);
612 computeReferenceCell (rend.textureNdx, lod, grid, refImage, rend.region);
616 // Read back rendered results.
617 tcu::Surface resImage(viewportWidth, viewportHeight);
618 glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
624 const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
626 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
627 isOk ? "Pass" : "Image comparison failed");
633 void Vertex2DTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
635 const deUint32 programID = m_program->getProgram();
640 const int positionLoc = glGetAttribLocation(programID, "a_position");
641 if (positionLoc != -1)
643 glEnableVertexAttribArray(positionLoc);
644 glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
649 const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
650 if (texCoordLoc != -1)
652 glEnableVertexAttribArray(texCoordLoc);
653 glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
660 const int lodLoc = glGetUniformLocation(programID, "u_lod");
662 glUniform1f(lodLoc, lod);
665 glActiveTexture(GL_TEXTURE0);
666 glBindTexture(GL_TEXTURE_2D, m_textures[textureNdx]->getGLTexture());
667 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS);
668 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT);
669 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter);
670 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter);
673 const int texLoc = glGetUniformLocation(programID, "u_texture");
675 glUniform1i(texLoc, 0);
679 // Renders one sub-image with given parameters.
680 void Vertex2DTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
682 setupShaderInputs(textureNdx, lod, grid);
683 glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
686 void Vertex2DTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
688 computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter), grid, dst, dstRegion);
691 class VertexCubeTextureCase : public TestCase
694 VertexCubeTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT);
695 ~VertexCubeTextureCase (void);
699 IterateResult iterate (void);
702 typedef PosTexCoordQuadGrid<TEXTURETYPE_CUBE> Grid;
704 VertexCubeTextureCase (const VertexCubeTextureCase& other);
705 VertexCubeTextureCase& operator= (const VertexCubeTextureCase& other);
707 float calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const;
708 void setupShaderInputs (int textureNdx, float lod, const Grid& grid) const;
709 void renderCell (int textureNdx, float lod, const Grid& grid) const;
710 void computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const;
712 const deUint32 m_minFilter;
713 const deUint32 m_magFilter;
714 const deUint32 m_wrapS;
715 const deUint32 m_wrapT;
717 const glu::ShaderProgram* m_program;
718 glu::TextureCube* m_textures[2]; // 2 textures, a gradient texture and a grid texture.
721 VertexCubeTextureCase::VertexCubeTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT)
722 : TestCase (testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc)
723 , m_minFilter (minFilter)
724 , m_magFilter (magFilter)
727 , m_program (DE_NULL)
729 m_textures[0] = DE_NULL;
730 m_textures[1] = DE_NULL;
733 VertexCubeTextureCase::~VertexCubeTextureCase(void)
735 VertexCubeTextureCase::deinit();
738 void VertexCubeTextureCase::init (void)
740 const char* const vertexShader =
741 "attribute highp vec2 a_position;\n"
742 "attribute highp vec3 a_texCoord;\n"
743 "uniform highp samplerCube u_texture;\n"
744 "uniform highp float u_lod;\n"
745 "varying mediump vec4 v_color;\n"
749 " gl_Position = vec4(a_position, 0.0, 1.0);\n"
750 " v_color = textureCubeLod(u_texture, a_texCoord, u_lod);\n"
753 const char* const fragmentShader =
754 "varying mediump vec4 v_color;\n"
758 " gl_FragColor = v_color;\n"
761 if (m_context.getRenderTarget().getNumSamples() != 0)
762 throw tcu::NotSupportedError("MSAA config not supported by this test");
764 DE_ASSERT(!m_program);
765 m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader));
767 if(!m_program->isOk())
769 m_testCtx.getLog() << *m_program;
771 GLint maxVertexTextures;
772 glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures);
774 if (maxVertexTextures < 1)
775 throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__);
777 TCU_FAIL("Failed to compile shader");
780 // Make the textures.
783 // Compute suitable power-of-two sizes (for mipmaps).
784 const int texWidth = 1 << deLog2Ceil32(MAX_CUBE_RENDER_WIDTH / 3 / 2);
785 const int texHeight = 1 << deLog2Ceil32(MAX_CUBE_RENDER_HEIGHT / 2 / 2);
787 DE_ASSERT(texWidth == texHeight);
790 for (int i = 0; i < 2; i++)
792 DE_ASSERT(!m_textures[i]);
793 m_textures[i] = new glu::TextureCube(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth);
796 const bool mipmaps = deIsPowerOfTwo32(texWidth) != DE_FALSE;
797 const int numLevels = mipmaps ? deLog2Floor32(texWidth)+1 : 1;
798 const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat());
799 const Vec4 cBias = fmtInfo.valueMin;
800 const Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin;
802 // Fill first with gradient texture.
803 static const Vec4 gradients[tcu::CUBEFACE_LAST][2] =
805 { Vec4(-1.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x
806 { Vec4( 0.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x
807 { Vec4(-1.0f, 0.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y
808 { Vec4(-1.0f, -1.0f, 0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y
809 { Vec4(-1.0f, -1.0f, -1.0f, 0.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z
810 { Vec4( 0.0f, 0.0f, 0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) } // positive z
812 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
814 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
816 m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
817 tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias);
821 // Fill second with grid texture.
822 for (int face = 0; face < tcu::CUBEFACE_LAST; face++)
824 for (int levelNdx = 0; levelNdx < numLevels; levelNdx++)
826 const deUint32 step = 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST);
827 const deUint32 rgb = step*levelNdx*face;
828 const deUint32 colorA = 0xff000000 | rgb;
829 const deUint32 colorB = 0xff000000 | ~rgb;
831 m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx);
832 tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias);
837 for (int i = 0; i < 2; i++)
838 m_textures[i]->upload();
840 catch (const std::exception&)
842 // Clean up to save memory.
843 VertexCubeTextureCase::deinit();
848 void VertexCubeTextureCase::deinit (void)
850 for (int i = 0; i < 2; i++)
852 delete m_textures[i];
853 m_textures[i] = DE_NULL;
860 float VertexCubeTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const
862 const tcu::TextureCube& refTexture = m_textures[textureNdx]->getRefTexture();
863 const Vec2 srcSize = Vec2((float)refTexture.getSize(), (float)refTexture.getSize());
864 const Vec2 sizeRatio = texScale*srcSize / dstSize;
866 // \note In this particular case, dv/dx and du/dy are zero, simplifying the expression.
867 return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y()));
870 VertexCubeTextureCase::IterateResult VertexCubeTextureCase::iterate (void)
872 const int viewportWidth = deMin32(m_context.getRenderTarget().getWidth(), MAX_CUBE_RENDER_WIDTH);
873 const int viewportHeight = deMin32(m_context.getRenderTarget().getHeight(), MAX_CUBE_RENDER_HEIGHT);
875 const int viewportXOffsetMax = m_context.getRenderTarget().getWidth() - viewportWidth;
876 const int viewportYOffsetMax = m_context.getRenderTarget().getHeight() - viewportHeight;
878 de::Random rnd (deStringHash(getName()));
880 const int viewportXOffset = rnd.getInt(0, viewportXOffsetMax);
881 const int viewportYOffset = rnd.getInt(0, viewportYOffsetMax);
883 glUseProgram(m_program->getProgram());
885 // Divide viewport into 4 areas.
886 const int leftWidth = viewportWidth / 2;
887 const int rightWidth = viewportWidth - leftWidth;
888 const int bottomHeight = viewportHeight / 2;
889 const int topHeight = viewportHeight - bottomHeight;
892 glClearColor(0.125f, 0.25f, 0.5f, 1.0f);
893 glClear(GL_COLOR_BUFFER_BIT);
895 // Texture scaling and offsetting vectors.
896 const Vec2 texMinScale (1.0f, 1.0f);
897 const Vec2 texMinOffset (0.0f, 0.0f);
898 const Vec2 texMagScale (0.3f, 0.3f);
899 const Vec2 texMagOffset (0.5f, 0.3f);
901 // Surface for the reference image.
902 tcu::Surface refImage(viewportWidth, viewportHeight);
904 // Each of the four areas is divided into 6 cells.
905 const int defCellWidth = viewportWidth / 2 / 3;
906 const int defCellHeight = viewportHeight / 2 / 2;
908 for (int i = 0; i < tcu::CUBEFACE_LAST; i++)
910 const int cellOffsetX = defCellWidth * (i % 3);
911 const int cellOffsetY = defCellHeight * (i / 3);
912 const bool isRightmostCell = i == 2 || i == 5;
913 const bool isTopCell = i >= 3;
914 const int leftCellWidth = isRightmostCell ? leftWidth - cellOffsetX : defCellWidth;
915 const int rightCellWidth = isRightmostCell ? rightWidth - cellOffsetX : defCellWidth;
916 const int bottomCellHeight = isTopCell ? bottomHeight - cellOffsetY : defCellHeight;
917 const int topCellHeight = isTopCell ? topHeight - cellOffsetY : defCellHeight;
923 const Vec2 texCoordScale;
924 const Vec2 texCoordOffset;
925 Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {}
928 Render(Rect(cellOffsetX + 0, cellOffsetY + 0, leftCellWidth, bottomCellHeight), 0, texMinScale, texMinOffset),
929 Render(Rect(cellOffsetX + leftWidth, cellOffsetY + 0, rightCellWidth, bottomCellHeight), 0, texMagScale, texMagOffset),
930 Render(Rect(cellOffsetX + 0, cellOffsetY + bottomHeight, leftCellWidth, topCellHeight), 1, texMinScale, texMinOffset),
931 Render(Rect(cellOffsetX + leftWidth, cellOffsetY + bottomHeight, rightCellWidth, topCellHeight), 1, texMagScale, texMagOffset)
934 for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++)
936 const Render& rend = renders[renderNdx];
937 const float lod = calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx);
938 const bool useSafeTexCoords = isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter);
939 const Grid grid (GRID_SIZE_CUBE, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]),
940 TexTypeCoordParams<TEXTURETYPE_CUBE>(rend.texCoordScale, rend.texCoordOffset, (tcu::CubeFace)i), useSafeTexCoords);
942 glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h);
943 renderCell (rend.textureNdx, lod, grid);
944 computeReferenceCell (rend.textureNdx, lod, grid, refImage, rend.region);
948 // Read back rendered results.
949 tcu::Surface resImage(viewportWidth, viewportHeight);
950 glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess());
956 const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage);
958 m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL,
959 isOk ? "Pass" : "Image comparison failed");
965 void VertexCubeTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const
967 const deUint32 programID = m_program->getProgram();
972 const int positionLoc = glGetAttribLocation(programID, "a_position");
973 if (positionLoc != -1)
975 glEnableVertexAttribArray(positionLoc);
976 glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr());
981 const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord");
982 if (texCoordLoc != -1)
984 glEnableVertexAttribArray(texCoordLoc);
985 glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr());
992 const int lodLoc = glGetUniformLocation(programID, "u_lod");
994 glUniform1f(lodLoc, lod);
997 glActiveTexture(GL_TEXTURE0);
998 glBindTexture(GL_TEXTURE_CUBE_MAP, m_textures[textureNdx]->getGLTexture());
999 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, m_wrapS);
1000 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, m_wrapT);
1001 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, m_minFilter);
1002 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, m_magFilter);
1005 const int texLoc = glGetUniformLocation(programID, "u_texture");
1007 glUniform1i(texLoc, 0);
1011 // Renders one cube face with given parameters.
1012 void VertexCubeTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const
1014 setupShaderInputs(textureNdx, lod, grid);
1015 glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr());
1018 // Computes reference for one cube face with given parameters.
1019 void VertexCubeTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const
1021 tcu::Sampler sampler = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter);
1022 computeReference(m_textures[textureNdx]->getRefTexture(), lod, sampler, grid, dst, dstRegion);
1025 VertexTextureTests::VertexTextureTests (Context& context)
1026 : TestCaseGroup(context, "vertex", "Vertex Texture Tests")
1030 VertexTextureTests::~VertexTextureTests(void)
1034 void VertexTextureTests::init (void)
1036 // 2D and cube map groups, and their filtering and wrap sub-groups.
1037 TestCaseGroup* const group2D = new TestCaseGroup(m_context, "2d", "2D Vertex Texture Tests");
1038 TestCaseGroup* const groupCube = new TestCaseGroup(m_context, "cube", "Cube Map Vertex Texture Tests");
1039 TestCaseGroup* const filteringGroup2D = new TestCaseGroup(m_context, "filtering", "2D Vertex Texture Filtering Tests");
1040 TestCaseGroup* const wrapGroup2D = new TestCaseGroup(m_context, "wrap", "2D Vertex Texture Wrap Tests");
1041 TestCaseGroup* const filteringGroupCube = new TestCaseGroup(m_context, "filtering", "Cube Map Vertex Texture Filtering Tests");
1042 TestCaseGroup* const wrapGroupCube = new TestCaseGroup(m_context, "wrap", "Cube Map Vertex Texture Wrap Tests");
1044 group2D->addChild(filteringGroup2D);
1045 group2D->addChild(wrapGroup2D);
1046 groupCube->addChild(filteringGroupCube);
1047 groupCube->addChild(wrapGroupCube);
1050 addChild(groupCube);
1058 { "clamp", GL_CLAMP_TO_EDGE },
1059 { "repeat", GL_REPEAT },
1060 { "mirror", GL_MIRRORED_REPEAT }
1067 } minFilterModes[] =
1069 { "nearest", GL_NEAREST },
1070 { "linear", GL_LINEAR },
1071 { "nearest_mipmap_nearest", GL_NEAREST_MIPMAP_NEAREST },
1072 { "linear_mipmap_nearest", GL_LINEAR_MIPMAP_NEAREST },
1073 { "nearest_mipmap_linear", GL_NEAREST_MIPMAP_LINEAR },
1074 { "linear_mipmap_linear", GL_LINEAR_MIPMAP_LINEAR }
1081 } magFilterModes[] =
1083 { "nearest", GL_NEAREST },
1084 { "linear", GL_LINEAR }
1087 #define FOR_EACH(ITERATOR, ARRAY, BODY) \
1088 for (int (ITERATOR) = 0; (ITERATOR) < DE_LENGTH_OF_ARRAY(ARRAY); (ITERATOR)++) \
1093 FOR_EACH(minFilter, minFilterModes,
1094 FOR_EACH(magFilter, magFilterModes,
1095 FOR_EACH(wrapMode, wrapModes,
1097 const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
1099 filteringGroup2D->addChild(new Vertex2DTextureCase(m_context,
1101 minFilterModes[minFilter].mode,
1102 magFilterModes[magFilter].mode,
1103 wrapModes[wrapMode].mode,
1104 wrapModes[wrapMode].mode));
1107 FOR_EACH(wrapSMode, wrapModes,
1108 FOR_EACH(wrapTMode, wrapModes,
1110 const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
1112 wrapGroup2D->addChild(new Vertex2DTextureCase(m_context,
1114 GL_LINEAR_MIPMAP_LINEAR,
1116 wrapModes[wrapSMode].mode,
1117 wrapModes[wrapTMode].mode));
1122 FOR_EACH(minFilter, minFilterModes,
1123 FOR_EACH(magFilter, magFilterModes,
1124 FOR_EACH(wrapMode, wrapModes,
1126 const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name;
1128 filteringGroupCube->addChild(new VertexCubeTextureCase(m_context,
1130 minFilterModes[minFilter].mode,
1131 magFilterModes[magFilter].mode,
1132 wrapModes[wrapMode].mode,
1133 wrapModes[wrapMode].mode));
1136 FOR_EACH(wrapSMode, wrapModes,
1137 FOR_EACH(wrapTMode, wrapModes,
1139 const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name;
1141 wrapGroupCube->addChild(new VertexCubeTextureCase(m_context,
1143 GL_LINEAR_MIPMAP_LINEAR,
1145 wrapModes[wrapSMode].mode,
1146 wrapModes[wrapTMode].mode));