| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL ES 3.0 Module |
| * ------------------------------------------------- |
| * |
| * Copyright 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Vertex texture tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es3fVertexTextureTests.hpp" |
| #include "glsTextureTestUtil.hpp" |
| #include "gluTexture.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluTextureUtil.hpp" |
| #include "tcuVector.hpp" |
| #include "tcuMatrix.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "deMath.h" |
| #include "deRandom.hpp" |
| #include "deString.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include <limits> |
| |
| #include "glw.h" |
| |
| using tcu::TestLog; |
| using tcu::Vec2; |
| using tcu::Vec3; |
| using tcu::Vec4; |
| using tcu::IVec2; |
| using tcu::IVec3; |
| using tcu::IVec4; |
| using tcu::Mat3; |
| using std::string; |
| using std::vector; |
| |
| namespace deqp |
| { |
| |
| using namespace gls::TextureTestUtil; |
| using namespace glu::TextureTestUtil; |
| |
| using glu::TextureTestUtil::TEXTURETYPE_2D; |
| using glu::TextureTestUtil::TEXTURETYPE_CUBE; |
| using glu::TextureTestUtil::TEXTURETYPE_2D_ARRAY; |
| using glu::TextureTestUtil::TEXTURETYPE_3D; |
| |
| namespace gles3 |
| { |
| namespace Functional |
| { |
| |
| static const int WIDTH_2D_ARRAY = 128; |
| static const int HEIGHT_2D_ARRAY = 128; |
| static const int LAYERS_2D_ARRAY = 8; |
| |
| static const int WIDTH_3D = 64; |
| static const int HEIGHT_3D = 64; |
| static const int DEPTH_3D = 64; |
| |
| // The 2D case draws four images. |
| static const int MAX_2D_RENDER_WIDTH = 128*2; |
| static const int MAX_2D_RENDER_HEIGHT = 128*2; |
| |
| // The cube map case draws four 3-by-2 image groups. |
| static const int MAX_CUBE_RENDER_WIDTH = 28*2*3; |
| static const int MAX_CUBE_RENDER_HEIGHT = 28*2*2; |
| |
| static const int MAX_2D_ARRAY_RENDER_WIDTH = 128*2; |
| static const int MAX_2D_ARRAY_RENDER_HEIGHT = 128*2; |
| |
| static const int MAX_3D_RENDER_WIDTH = 128*2; |
| static const int MAX_3D_RENDER_HEIGHT = 128*2; |
| |
| static const int GRID_SIZE_2D = 127; |
| static const int GRID_SIZE_CUBE = 63; |
| static const int GRID_SIZE_2D_ARRAY = 127; |
| static const int GRID_SIZE_3D = 127; |
| |
| // Helpers for making texture coordinates "safe", i.e. move them further from coordinate bounary. |
| |
| // Moves x towards the closest K+targetFraction, where K is an integer. |
| // E.g. moveTowardsFraction(x, 0.5f) moves x away from integer boundaries. |
| static inline float moveTowardsFraction (float x, float targetFraction) |
| { |
| const float strictness = 0.5f; |
| DE_ASSERT(0.0f < strictness && strictness <= 1.0f); |
| DE_ASSERT(de::inBounds(targetFraction, 0.0f, 1.0f)); |
| const float y = x + 0.5f - targetFraction; |
| return deFloatFloor(y) + deFloatFrac(y)*(1.0f-strictness) + strictness*0.5f - 0.5f + targetFraction; |
| } |
| |
| static inline float safeCoord (float raw, int scale, float fraction) |
| { |
| const float scaleFloat = (float)scale; |
| return moveTowardsFraction(raw*scaleFloat, fraction) / scaleFloat; |
| } |
| |
| template <int Size> |
| 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) |
| { |
| tcu::Vector<float, Size> result; |
| for (int i = 0; i < Size; i++) |
| result[i] = safeCoord(raw[i], scale[i], fraction[i]); |
| return result; |
| } |
| |
| static inline Vec2 safe2DTexCoords (const Vec2& raw, const IVec2& textureSize) |
| { |
| return safeCoords(raw, textureSize, Vec2(0.5f)); |
| } |
| |
| static inline Vec3 safe2DArrayTexCoords (const Vec3& raw, const IVec3& textureSize) |
| { |
| return safeCoords(raw, textureSize, Vec3(0.5f, 0.5f, 0.0f)); |
| } |
| |
| static inline Vec3 safe3DTexCoords (const Vec3& raw, const IVec3& textureSize) |
| { |
| return safeCoords(raw, textureSize, Vec3(0.5f)); |
| } |
| |
| namespace |
| { |
| |
| struct Rect |
| { |
| Rect (int x_, int y_, int w_, int h_) : x(x_), y(y_), w(w_), h(h_) {} |
| IVec2 pos (void) const { return IVec2(x, y); } |
| IVec2 size (void) const { return IVec2(w, h); } |
| |
| int x; |
| int y; |
| int w; |
| int h; |
| }; |
| |
| template <TextureType> struct TexTypeTcuClass; |
| template <> struct TexTypeTcuClass<TEXTURETYPE_2D> { typedef tcu::Texture2D t; }; |
| template <> struct TexTypeTcuClass<TEXTURETYPE_CUBE> { typedef tcu::TextureCube t; }; |
| template <> struct TexTypeTcuClass<TEXTURETYPE_2D_ARRAY> { typedef tcu::Texture2DArray t; }; |
| template <> struct TexTypeTcuClass<TEXTURETYPE_3D> { typedef tcu::Texture3D t; }; |
| |
| template <TextureType> struct TexTypeSizeDims; |
| template <> struct TexTypeSizeDims<TEXTURETYPE_2D> { enum { V = 2 }; }; |
| template <> struct TexTypeSizeDims<TEXTURETYPE_CUBE> { enum { V = 2 }; }; |
| template <> struct TexTypeSizeDims<TEXTURETYPE_2D_ARRAY> { enum { V = 3 }; }; |
| template <> struct TexTypeSizeDims<TEXTURETYPE_3D> { enum { V = 3 }; }; |
| |
| template <TextureType> struct TexTypeCoordDims; |
| template <> struct TexTypeCoordDims<TEXTURETYPE_2D> { enum { V = 2 }; }; |
| template <> struct TexTypeCoordDims<TEXTURETYPE_CUBE> { enum { V = 3 }; }; |
| template <> struct TexTypeCoordDims<TEXTURETYPE_2D_ARRAY> { enum { V = 3 }; }; |
| template <> struct TexTypeCoordDims<TEXTURETYPE_3D> { enum { V = 3 }; }; |
| |
| template <TextureType TexType> struct TexTypeSizeIVec { typedef tcu::Vector<int, TexTypeSizeDims<TexType>::V> t; }; |
| template <TextureType TexType> struct TexTypeCoordVec { typedef tcu::Vector<float, TexTypeCoordDims<TexType>::V> t; }; |
| |
| template <TextureType> struct TexTypeCoordParams; |
| |
| template <> struct |
| TexTypeCoordParams<TEXTURETYPE_2D> |
| { |
| Vec2 scale; |
| Vec2 bias; |
| |
| TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_) : scale(scale_), bias(bias_) {} |
| }; |
| |
| template <> struct |
| TexTypeCoordParams<TEXTURETYPE_CUBE> |
| { |
| Vec2 scale; |
| Vec2 bias; |
| tcu::CubeFace face; |
| |
| TexTypeCoordParams (const Vec2& scale_, const Vec2& bias_, tcu::CubeFace face_) : scale(scale_), bias(bias_), face(face_) {} |
| }; |
| |
| template <> struct |
| TexTypeCoordParams<TEXTURETYPE_2D_ARRAY> |
| { |
| Mat3 transform; |
| |
| TexTypeCoordParams (const Mat3& transform_) : transform(transform_) {} |
| }; |
| |
| template <> struct |
| TexTypeCoordParams<TEXTURETYPE_3D> |
| { |
| Mat3 transform; |
| |
| TexTypeCoordParams (const Mat3& transform_) : transform(transform_) {} |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Quad grid class containing position and texture coordinate data. |
| * |
| * A quad grid of size S means a grid consisting of S*S quads (S rows and |
| * S columns). The quads are rectangles with main axis aligned sides, and |
| * each consists of two triangles. Note that although there are only |
| * (S+1)*(S+1) distinct vertex positions, there are S*S*4 distinct vertices |
| * because we want texture coordinates to be constant across the vertices |
| * of a quad (to avoid interpolation issues), and thus each quad needs its |
| * own 4 vertices. |
| * |
| * Pointers returned by get*Ptr() are suitable for gl calls such as |
| * glVertexAttribPointer() (for position and tex coord) or glDrawElements() |
| * (for indices). |
| *//*--------------------------------------------------------------------*/ |
| template <TextureType TexType> |
| class PosTexCoordQuadGrid |
| { |
| private: |
| enum { TEX_COORD_DIMS = TexTypeCoordDims <TexType>::V }; |
| typedef typename TexTypeCoordVec<TexType>::t TexCoordVec; |
| typedef typename TexTypeSizeIVec<TexType>::t TexSizeIVec; |
| typedef TexTypeCoordParams<TexType> TexCoordParams; |
| |
| public: |
| PosTexCoordQuadGrid (int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords); |
| |
| int getSize (void) const { return m_gridSize; } |
| Vec4 getQuadLDRU (int col, int row) const; //!< Vec4(leftX, downY, rightX, upY) |
| const TexCoordVec& getQuadTexCoord (int col, int row) const; |
| |
| int getNumIndices (void) const { return m_gridSize*m_gridSize*3*2; } |
| const float* getPositionPtr (void) const { DE_STATIC_ASSERT(sizeof(Vec2) == 2*sizeof(float)); return (float*)&m_positions[0]; } |
| const float* getTexCoordPtr (void) const { DE_STATIC_ASSERT(sizeof(TexCoordVec) == TEX_COORD_DIMS*(int)sizeof(float)); return (float*)&m_texCoords[0]; } |
| const deUint16* getIndexPtr (void) const { return &m_indices[0]; } |
| |
| private: |
| void initializeTexCoords (const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords); |
| |
| const int m_gridSize; |
| vector<Vec2> m_positions; |
| vector<TexCoordVec> m_texCoords; |
| vector<deUint16> m_indices; |
| }; |
| |
| template <TextureType TexType> |
| Vec4 PosTexCoordQuadGrid<TexType>::getQuadLDRU (int col, int row) const |
| { |
| int ndx00 = (row*m_gridSize + col) * 4; |
| int ndx11 = ndx00 + 3; |
| |
| return Vec4(m_positions[ndx00].x(), |
| m_positions[ndx00].y(), |
| m_positions[ndx11].x(), |
| m_positions[ndx11].y()); |
| } |
| |
| template <TextureType TexType> |
| const typename TexTypeCoordVec<TexType>::t& PosTexCoordQuadGrid<TexType>::getQuadTexCoord (int col, int row) const |
| { |
| return m_texCoords[(row*m_gridSize + col) * 4]; |
| } |
| |
| template <TextureType TexType> |
| PosTexCoordQuadGrid<TexType>::PosTexCoordQuadGrid (int gridSize, const IVec2& renderSize, const TexSizeIVec& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords) |
| : m_gridSize(gridSize) |
| { |
| DE_ASSERT(m_gridSize > 0 && m_gridSize*m_gridSize <= (int)std::numeric_limits<deUint16>::max() + 1); |
| |
| const float gridSizeFloat = (float)m_gridSize; |
| |
| m_positions.reserve(m_gridSize*m_gridSize*4); |
| m_indices.reserve(m_gridSize*m_gridSize*3*2); |
| |
| for (int y = 0; y < m_gridSize; y++) |
| for (int x = 0; x < m_gridSize; x++) |
| { |
| float fx0 = (float)(x+0) / gridSizeFloat; |
| float fx1 = (float)(x+1) / gridSizeFloat; |
| float fy0 = (float)(y+0) / gridSizeFloat; |
| float fy1 = (float)(y+1) / gridSizeFloat; |
| |
| Vec2 quadVertices[4] = { Vec2(fx0, fy0), Vec2(fx1, fy0), Vec2(fx0, fy1), Vec2(fx1, fy1) }; |
| |
| int firstNdx = (int)m_positions.size(); |
| |
| for (int i = 0; i < DE_LENGTH_OF_ARRAY(quadVertices); i++) |
| m_positions.push_back(safeCoords(quadVertices[i], renderSize, Vec2(0.0f)) * 2.0f - 1.0f); |
| |
| m_indices.push_back(deUint16(firstNdx + 0)); |
| m_indices.push_back(deUint16(firstNdx + 1)); |
| m_indices.push_back(deUint16(firstNdx + 2)); |
| |
| m_indices.push_back(deUint16(firstNdx + 1)); |
| m_indices.push_back(deUint16(firstNdx + 3)); |
| m_indices.push_back(deUint16(firstNdx + 2)); |
| } |
| |
| m_texCoords.reserve(m_gridSize*m_gridSize*4); |
| initializeTexCoords(textureSize, texCoordParams, useSafeTexCoords); |
| |
| DE_ASSERT((int)m_positions.size() == m_gridSize*m_gridSize*4); |
| DE_ASSERT((int)m_indices.size() == m_gridSize*m_gridSize*3*2); |
| DE_ASSERT((int)m_texCoords.size() == m_gridSize*m_gridSize*4); |
| } |
| |
| template <> |
| void PosTexCoordQuadGrid<TEXTURETYPE_2D>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords) |
| { |
| DE_ASSERT(m_texCoords.empty()); |
| |
| const float gridSizeFloat = (float)m_gridSize; |
| |
| for (int y = 0; y < m_gridSize; y++) |
| for (int x = 0; x < m_gridSize; x++) |
| { |
| Vec2 rawCoord = Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) * texCoordParams.scale + texCoordParams.bias; |
| |
| for (int i = 0; i < 4; i++) |
| m_texCoords.push_back(useSafeTexCoords ? safe2DTexCoords(rawCoord, textureSize) : rawCoord); |
| } |
| } |
| |
| template <> |
| void PosTexCoordQuadGrid<TEXTURETYPE_CUBE>::initializeTexCoords (const IVec2& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords) |
| { |
| DE_ASSERT(m_texCoords.empty()); |
| |
| const float gridSizeFloat = (float)m_gridSize; |
| vector<float> texBoundaries; |
| computeQuadTexCoordCube(texBoundaries, texCoordParams.face); |
| const Vec3 coordA = Vec3(texBoundaries[0], texBoundaries[1], texBoundaries[2]); |
| const Vec3 coordB = Vec3(texBoundaries[3], texBoundaries[4], texBoundaries[5]); |
| const Vec3 coordC = Vec3(texBoundaries[6], texBoundaries[7], texBoundaries[8]); |
| const Vec3 coordAB = coordB - coordA; |
| const Vec3 coordAC = coordC - coordA; |
| |
| for (int y = 0; y < m_gridSize; y++) |
| for (int x = 0; x < m_gridSize; x++) |
| { |
| const Vec2 rawFaceCoord = texCoordParams.scale * Vec2((float)x / gridSizeFloat, (float)y / gridSizeFloat) + texCoordParams.bias; |
| const Vec2 safeFaceCoord = useSafeTexCoords ? safe2DTexCoords(rawFaceCoord, textureSize) : rawFaceCoord; |
| const Vec3 texCoord = coordA + coordAC*safeFaceCoord.x() + coordAB*safeFaceCoord.y(); |
| |
| for (int i = 0; i < 4; i++) |
| m_texCoords.push_back(texCoord); |
| } |
| } |
| |
| template <> |
| void PosTexCoordQuadGrid<TEXTURETYPE_2D_ARRAY>::initializeTexCoords (const IVec3& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords) |
| { |
| DE_ASSERT(m_texCoords.empty()); |
| |
| const float gridSizeFloat = (float)m_gridSize; |
| |
| for (int y = 0; y < m_gridSize; y++) |
| for (int x = 0; x < m_gridSize; x++) |
| { |
| const Vec3 rawCoord = texCoordParams.transform * Vec3((float)x / gridSizeFloat, (float)y / gridSizeFloat, 1.0f); |
| |
| for (int i = 0; i < 4; i++) |
| m_texCoords.push_back(useSafeTexCoords ? safe2DArrayTexCoords(rawCoord, textureSize) : rawCoord); |
| } |
| } |
| |
| template <> |
| void PosTexCoordQuadGrid<TEXTURETYPE_3D>::initializeTexCoords (const IVec3& textureSize, const TexCoordParams& texCoordParams, bool useSafeTexCoords) |
| { |
| DE_ASSERT(m_texCoords.empty()); |
| |
| const float gridSizeFloat = (float)m_gridSize; |
| |
| for (int y = 0; y < m_gridSize; y++) |
| for (int x = 0; x < m_gridSize; x++) |
| { |
| Vec3 rawCoord = texCoordParams.transform * Vec3((float)x / gridSizeFloat, (float)y / gridSizeFloat, 1.0f); |
| |
| for (int i = 0; i < 4; i++) |
| m_texCoords.push_back(useSafeTexCoords ? safe3DTexCoords(rawCoord, textureSize) : rawCoord); |
| } |
| } |
| |
| } // anonymous |
| |
| static inline bool isLevelNearest (deUint32 filter) |
| { |
| return filter == GL_NEAREST || filter == GL_NEAREST_MIPMAP_NEAREST || filter == GL_NEAREST_MIPMAP_LINEAR; |
| } |
| |
| static inline IVec2 getTextureSize (const glu::Texture2D& tex) |
| { |
| const tcu::Texture2D& ref = tex.getRefTexture(); |
| return IVec2(ref.getWidth(), ref.getHeight()); |
| } |
| |
| static inline IVec2 getTextureSize (const glu::TextureCube& tex) |
| { |
| const tcu::TextureCube& ref = tex.getRefTexture(); |
| return IVec2(ref.getSize(), ref.getSize()); |
| } |
| |
| static inline IVec3 getTextureSize (const glu::Texture2DArray& tex) |
| { |
| const tcu::Texture2DArray& ref = tex.getRefTexture(); |
| return IVec3(ref.getWidth(), ref.getHeight(), ref.getNumLayers()); |
| } |
| |
| static inline IVec3 getTextureSize (const glu::Texture3D& tex) |
| { |
| const tcu::Texture3D& ref = tex.getRefTexture(); |
| return IVec3(ref.getWidth(), ref.getHeight(), ref.getDepth()); |
| } |
| |
| template <TextureType TexType> |
| static void setPixelColors (const vector<Vec4>& quadColors, const Rect& region, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst) |
| { |
| const int gridSize = grid.getSize(); |
| |
| for (int y = 0; y < gridSize; y++) |
| for (int x = 0; x < gridSize; x++) |
| { |
| const Vec4 color = quadColors[y*gridSize + x]; |
| const Vec4 ldru = grid.getQuadLDRU(x, y) * 0.5f + 0.5f; // [-1, 1] -> [0, 1] |
| const int ix0 = deCeilFloatToInt32(ldru.x() * (float)region.w - 0.5f); |
| const int ix1 = deCeilFloatToInt32(ldru.z() * (float)region.w - 0.5f); |
| const int iy0 = deCeilFloatToInt32(ldru.y() * (float)region.h - 0.5f); |
| const int iy1 = deCeilFloatToInt32(ldru.w() * (float)region.h - 0.5f); |
| |
| for (int iy = iy0; iy < iy1; iy++) |
| for (int ix = ix0; ix < ix1; ix++) |
| { |
| DE_ASSERT(deInBounds32(ix + region.x, 0, dst.getWidth())); |
| DE_ASSERT(deInBounds32(iy + region.y, 0, dst.getHeight())); |
| |
| dst.setPixel(ix + region.x, iy + region.y, tcu::RGBA(color)); |
| } |
| } |
| } |
| |
| 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); } |
| 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); } |
| static inline Vec4 sample (const tcu::Texture2DArray& tex, const Vec3& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), coord.z(), lod); } |
| static inline Vec4 sample (const tcu::Texture3D& tex, const Vec3& coord, float lod, const tcu::Sampler& sam) { return tex.sample(sam, coord.x(), coord.y(), coord.z(), lod); } |
| |
| template <TextureType TexType> |
| void computeReference (const typename TexTypeTcuClass<TexType>::t& texture, float lod, const tcu::Sampler& sampler, const PosTexCoordQuadGrid<TexType>& grid, tcu::Surface& dst, const Rect& dstRegion) |
| { |
| const int gridSize = grid.getSize(); |
| vector<Vec4> quadColors (gridSize*gridSize); |
| |
| for (int y = 0; y < gridSize; y++) |
| for (int x = 0; x < gridSize; x++) |
| { |
| const int ndx = y*gridSize + x; |
| const typename TexTypeCoordVec<TexType>::t& coord = grid.getQuadTexCoord(x, y); |
| |
| quadColors[ndx] = sample(texture, coord, lod, sampler); |
| } |
| |
| setPixelColors(quadColors, dstRegion, grid, dst); |
| } |
| |
| static bool compareImages (const glu::RenderContext& renderCtx, tcu::TestLog& log, const tcu::Surface& ref, const tcu::Surface& res) |
| { |
| DE_ASSERT(renderCtx.getRenderTarget().getNumSamples() == 0); |
| |
| const tcu::RGBA threshold = renderCtx.getRenderTarget().getPixelFormat().getColorThreshold() + tcu::RGBA(15,15,15,15); |
| return tcu::pixelThresholdCompare(log, "Result", "Image compare result", ref, res, threshold, tcu::COMPARE_LOG_RESULT); |
| } |
| |
| class Vertex2DTextureCase : public TestCase |
| { |
| public: |
| Vertex2DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT); |
| ~Vertex2DTextureCase (void); |
| |
| void init (void); |
| void deinit (void); |
| IterateResult iterate (void); |
| |
| private: |
| typedef PosTexCoordQuadGrid<TEXTURETYPE_2D> Grid; |
| |
| Vertex2DTextureCase (const Vertex2DTextureCase& other); |
| Vertex2DTextureCase& operator= (const Vertex2DTextureCase& other); |
| |
| float calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const; |
| void setupShaderInputs (int textureNdx, float lod, const Grid& grid) const; |
| void renderCell (int textureNdx, float lod, const Grid& grid) const; |
| void computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const; |
| |
| const deUint32 m_minFilter; |
| const deUint32 m_magFilter; |
| const deUint32 m_wrapS; |
| const deUint32 m_wrapT; |
| |
| const glu::ShaderProgram* m_program; |
| glu::Texture2D* m_textures[2]; // 2 textures, a gradient texture and a grid texture. |
| }; |
| |
| Vertex2DTextureCase::Vertex2DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT) |
| : TestCase (testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc) |
| , m_minFilter (minFilter) |
| , m_magFilter (magFilter) |
| , m_wrapS (wrapS) |
| , m_wrapT (wrapT) |
| , m_program (DE_NULL) |
| { |
| m_textures[0] = DE_NULL; |
| m_textures[1] = DE_NULL; |
| } |
| |
| Vertex2DTextureCase::~Vertex2DTextureCase(void) |
| { |
| Vertex2DTextureCase::deinit(); |
| } |
| |
| void Vertex2DTextureCase::init (void) |
| { |
| const char* const vertexShader = |
| "#version 300 es\n" |
| "in highp vec2 a_position;\n" |
| "in highp vec2 a_texCoord;\n" |
| "uniform highp sampler2D u_texture;\n" |
| "uniform highp float u_lod;\n" |
| "out mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position, 0.0, 1.0);\n" |
| " v_color = textureLod(u_texture, a_texCoord, u_lod);\n" |
| "}\n"; |
| |
| const char* const fragmentShader = |
| "#version 300 es\n" |
| "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" |
| "in mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " dEQP_FragColor = v_color;\n" |
| "}\n"; |
| |
| if (m_context.getRenderTarget().getNumSamples() != 0) |
| throw tcu::NotSupportedError("MSAA config not supported by this test"); |
| |
| DE_ASSERT(!m_program); |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader)); |
| |
| if(!m_program->isOk()) |
| { |
| m_testCtx.getLog() << *m_program; |
| |
| GLint maxVertexTextures; |
| glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures); |
| |
| if (maxVertexTextures < 1) |
| throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__); |
| else |
| TCU_FAIL("Failed to compile shader"); |
| } |
| |
| // Make the textures. |
| try |
| { |
| // Compute suitable power-of-two sizes (for mipmaps). |
| const int texWidth = 1 << deLog2Ceil32(MAX_2D_RENDER_WIDTH / 2); |
| const int texHeight = 1 << deLog2Ceil32(MAX_2D_RENDER_HEIGHT / 2); |
| |
| for (int i = 0; i < 2; i++) |
| { |
| DE_ASSERT(!m_textures[i]); |
| m_textures[i] = new glu::Texture2D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight); |
| } |
| |
| const bool mipmaps = (deIsPowerOfTwo32(texWidth) && deIsPowerOfTwo32(texHeight)); |
| const int numLevels = mipmaps ? deLog2Floor32(de::max(texWidth, texHeight))+1 : 1; |
| const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat()); |
| const Vec4 cBias = fmtInfo.valueMin; |
| const Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin; |
| |
| // Fill first with gradient texture. |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias; |
| const Vec4 gMax = Vec4( 1.0f, 1.0f, 1.0f, 0.0f)*cScale + cBias; |
| |
| m_textures[0]->getRefTexture().allocLevel(levelNdx); |
| tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax); |
| } |
| |
| // Fill second with grid texture. |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const deUint32 step = 0x00ffffff / numLevels; |
| const deUint32 rgb = step*levelNdx; |
| const deUint32 colorA = 0xff000000 | rgb; |
| const deUint32 colorB = 0xff000000 | ~rgb; |
| |
| m_textures[1]->getRefTexture().allocLevel(levelNdx); |
| tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias); |
| } |
| |
| // Upload. |
| for (int i = 0; i < 2; i++) |
| m_textures[i]->upload(); |
| } |
| catch (const std::exception&) |
| { |
| // Clean up to save memory. |
| Vertex2DTextureCase::deinit(); |
| throw; |
| } |
| } |
| |
| void Vertex2DTextureCase::deinit (void) |
| { |
| for (int i = 0; i < 2; i++) |
| { |
| delete m_textures[i]; |
| m_textures[i] = DE_NULL; |
| } |
| |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| float Vertex2DTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const |
| { |
| const tcu::Texture2D& refTexture = m_textures[textureNdx]->getRefTexture(); |
| const Vec2 srcSize = Vec2((float)refTexture.getWidth(), (float)refTexture.getHeight()); |
| const Vec2 sizeRatio = texScale*srcSize / dstSize; |
| |
| // \note In this particular case dv/dx and du/dy are zero, simplifying the expression. |
| return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y())); |
| } |
| |
| Vertex2DTextureCase::IterateResult Vertex2DTextureCase::iterate (void) |
| { |
| const int viewportWidth = deMin32(m_context.getRenderTarget().getWidth(), MAX_2D_RENDER_WIDTH); |
| const int viewportHeight = deMin32(m_context.getRenderTarget().getHeight(), MAX_2D_RENDER_HEIGHT); |
| |
| const int viewportXOffsetMax = m_context.getRenderTarget().getWidth() - viewportWidth; |
| const int viewportYOffsetMax = m_context.getRenderTarget().getHeight() - viewportHeight; |
| |
| de::Random rnd (deStringHash(getName())); |
| |
| const int viewportXOffset = rnd.getInt(0, viewportXOffsetMax); |
| const int viewportYOffset = rnd.getInt(0, viewportYOffsetMax); |
| |
| glUseProgram(m_program->getProgram()); |
| |
| // Divide viewport into 4 cells. |
| const int leftWidth = viewportWidth / 2; |
| const int rightWidth = viewportWidth - leftWidth; |
| const int bottomHeight = viewportHeight / 2; |
| const int topHeight = viewportHeight - bottomHeight; |
| |
| // Clear. |
| glClearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Texture scaling and offsetting vectors. |
| const Vec2 texMinScale (+1.8f, +1.8f); |
| const Vec2 texMinOffset (-0.3f, -0.2f); |
| const Vec2 texMagScale (+0.3f, +0.3f); |
| const Vec2 texMagOffset (+0.9f, +0.8f); |
| |
| // Surface for the reference image. |
| tcu::Surface refImage(viewportWidth, viewportHeight); |
| |
| { |
| const struct Render |
| { |
| const Rect region; |
| int textureNdx; |
| const Vec2 texCoordScale; |
| const Vec2 texCoordOffset; |
| Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {} |
| } renders[] = |
| { |
| Render(Rect(0, 0, leftWidth, bottomHeight), 0, texMinScale, texMinOffset), |
| Render(Rect(leftWidth, 0, rightWidth, bottomHeight), 0, texMagScale, texMagOffset), |
| Render(Rect(0, bottomHeight, leftWidth, topHeight), 1, texMinScale, texMinOffset), |
| Render(Rect(leftWidth, bottomHeight, rightWidth, topHeight), 1, texMagScale, texMagOffset) |
| }; |
| |
| for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++) |
| { |
| const Render& rend = renders[renderNdx]; |
| const float lod = calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx); |
| const bool useSafeTexCoords = isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter); |
| const Grid grid (GRID_SIZE_2D, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]), |
| TexTypeCoordParams<TEXTURETYPE_2D>(rend.texCoordScale, rend.texCoordOffset), useSafeTexCoords); |
| |
| glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h); |
| renderCell (rend.textureNdx, lod, grid); |
| computeReferenceCell (rend.textureNdx, lod, grid, refImage, rend.region); |
| } |
| } |
| |
| // Read back rendered results. |
| tcu::Surface resImage(viewportWidth, viewportHeight); |
| glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess()); |
| |
| glUseProgram(0); |
| |
| // Compare and log. |
| { |
| const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage); |
| |
| m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, |
| isOk ? "Pass" : "Image comparison failed"); |
| } |
| |
| return STOP; |
| } |
| |
| void Vertex2DTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const |
| { |
| const deUint32 programID = m_program->getProgram(); |
| |
| // SETUP ATTRIBUTES. |
| |
| { |
| const int positionLoc = glGetAttribLocation(programID, "a_position"); |
| if (positionLoc != -1) |
| { |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr()); |
| } |
| } |
| |
| { |
| const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord"); |
| if (texCoordLoc != -1) |
| { |
| glEnableVertexAttribArray(texCoordLoc); |
| glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr()); |
| } |
| } |
| |
| // SETUP UNIFORMS. |
| |
| { |
| const int lodLoc = glGetUniformLocation(programID, "u_lod"); |
| if (lodLoc != -1) |
| glUniform1f(lodLoc, lod); |
| } |
| |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, m_textures[textureNdx]->getGLTexture()); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, m_wrapS); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, m_wrapT); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, m_minFilter); |
| glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, m_magFilter); |
| |
| { |
| const int texLoc = glGetUniformLocation(programID, "u_texture"); |
| if (texLoc != -1) |
| glUniform1i(texLoc, 0); |
| } |
| } |
| |
| // Renders one sub-image with given parameters. |
| void Vertex2DTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const |
| { |
| setupShaderInputs(textureNdx, lod, grid); |
| glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr()); |
| } |
| |
| void Vertex2DTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const |
| { |
| computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter), grid, dst, dstRegion); |
| } |
| |
| class VertexCubeTextureCase : public TestCase |
| { |
| public: |
| VertexCubeTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT); |
| ~VertexCubeTextureCase (void); |
| |
| void init (void); |
| void deinit (void); |
| IterateResult iterate (void); |
| |
| private: |
| typedef PosTexCoordQuadGrid<TEXTURETYPE_CUBE> Grid; |
| |
| VertexCubeTextureCase (const VertexCubeTextureCase& other); |
| VertexCubeTextureCase& operator= (const VertexCubeTextureCase& other); |
| |
| float calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const; |
| void setupShaderInputs (int textureNdx, float lod, const Grid& grid) const; |
| void renderCell (int textureNdx, float lod, const Grid& grid) const; |
| void computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const; |
| |
| const deUint32 m_minFilter; |
| const deUint32 m_magFilter; |
| const deUint32 m_wrapS; |
| const deUint32 m_wrapT; |
| |
| const glu::ShaderProgram* m_program; |
| glu::TextureCube* m_textures[2]; // 2 textures, a gradient texture and a grid texture. |
| }; |
| |
| VertexCubeTextureCase::VertexCubeTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT) |
| : TestCase (testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc) |
| , m_minFilter (minFilter) |
| , m_magFilter (magFilter) |
| , m_wrapS (wrapS) |
| , m_wrapT (wrapT) |
| , m_program (DE_NULL) |
| { |
| m_textures[0] = DE_NULL; |
| m_textures[1] = DE_NULL; |
| } |
| |
| VertexCubeTextureCase::~VertexCubeTextureCase(void) |
| { |
| VertexCubeTextureCase::deinit(); |
| } |
| |
| void VertexCubeTextureCase::init (void) |
| { |
| const char* const vertexShader = |
| "#version 300 es\n" |
| "in highp vec2 a_position;\n" |
| "in highp vec3 a_texCoord;\n" |
| "uniform highp samplerCube u_texture;\n" |
| "uniform highp float u_lod;\n" |
| "out mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position, 0.0, 1.0);\n" |
| " v_color = textureLod(u_texture, a_texCoord, u_lod);\n" |
| "}\n"; |
| |
| const char* const fragmentShader = |
| "#version 300 es\n" |
| "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" |
| "in mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " dEQP_FragColor = v_color;\n" |
| "}\n"; |
| |
| if (m_context.getRenderTarget().getNumSamples() != 0) |
| throw tcu::NotSupportedError("MSAA config not supported by this test"); |
| |
| DE_ASSERT(!m_program); |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShader, fragmentShader)); |
| |
| if(!m_program->isOk()) |
| { |
| m_testCtx.getLog() << *m_program; |
| |
| GLint maxVertexTextures; |
| glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures); |
| |
| if (maxVertexTextures < 1) |
| throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__); |
| else |
| TCU_FAIL("Failed to compile shader"); |
| } |
| |
| // Make the textures. |
| try |
| { |
| // Compute suitable power-of-two sizes (for mipmaps). |
| const int texWidth = 1 << deLog2Ceil32(MAX_CUBE_RENDER_WIDTH / 3 / 2); |
| const int texHeight = 1 << deLog2Ceil32(MAX_CUBE_RENDER_HEIGHT / 2 / 2); |
| |
| DE_ASSERT(texWidth == texHeight); |
| DE_UNREF(texHeight); |
| |
| for (int i = 0; i < 2; i++) |
| { |
| DE_ASSERT(!m_textures[i]); |
| m_textures[i] = new glu::TextureCube(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth); |
| } |
| |
| const bool mipmaps = deIsPowerOfTwo32(texWidth) != DE_FALSE; |
| const int numLevels = mipmaps ? deLog2Floor32(texWidth)+1 : 1; |
| const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat()); |
| const Vec4 cBias = fmtInfo.valueMin; |
| const Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin; |
| |
| // Fill first with gradient texture. |
| static const Vec4 gradients[tcu::CUBEFACE_LAST][2] = |
| { |
| { Vec4(-1.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative x |
| { Vec4( 0.0f, -1.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive x |
| { Vec4(-1.0f, 0.0f, -1.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // negative y |
| { Vec4(-1.0f, -1.0f, 0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) }, // positive y |
| { Vec4(-1.0f, -1.0f, -1.0f, 0.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f) }, // negative z |
| { Vec4( 0.0f, 0.0f, 0.0f, 2.0f), Vec4(1.0f, 1.0f, 1.0f, 0.0f) } // positive z |
| }; |
| for (int face = 0; face < tcu::CUBEFACE_LAST; face++) |
| { |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| m_textures[0]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx); |
| tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), gradients[face][0]*cScale + cBias, gradients[face][1]*cScale + cBias); |
| } |
| } |
| |
| // Fill second with grid texture. |
| for (int face = 0; face < tcu::CUBEFACE_LAST; face++) |
| { |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const deUint32 step = 0x00ffffff / (numLevels*tcu::CUBEFACE_LAST); |
| const deUint32 rgb = step*levelNdx*face; |
| const deUint32 colorA = 0xff000000 | rgb; |
| const deUint32 colorB = 0xff000000 | ~rgb; |
| |
| m_textures[1]->getRefTexture().allocLevel((tcu::CubeFace)face, levelNdx); |
| tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevelFace(levelNdx, (tcu::CubeFace)face), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias); |
| } |
| } |
| |
| // Upload. |
| for (int i = 0; i < 2; i++) |
| m_textures[i]->upload(); |
| } |
| catch (const std::exception&) |
| { |
| // Clean up to save memory. |
| VertexCubeTextureCase::deinit(); |
| throw; |
| } |
| } |
| |
| void VertexCubeTextureCase::deinit (void) |
| { |
| for (int i = 0; i < 2; i++) |
| { |
| delete m_textures[i]; |
| m_textures[i] = DE_NULL; |
| } |
| |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| float VertexCubeTextureCase::calculateLod (const Vec2& texScale, const Vec2& dstSize, int textureNdx) const |
| { |
| const tcu::TextureCube& refTexture = m_textures[textureNdx]->getRefTexture(); |
| const Vec2 srcSize = Vec2((float)refTexture.getSize(), (float)refTexture.getSize()); |
| const Vec2 sizeRatio = texScale*srcSize / dstSize; |
| |
| // \note In this particular case, dv/dx and du/dy are zero, simplifying the expression. |
| return deFloatLog2(de::max(sizeRatio.x(), sizeRatio.y())); |
| } |
| |
| VertexCubeTextureCase::IterateResult VertexCubeTextureCase::iterate (void) |
| { |
| const int viewportWidth = deMin32(m_context.getRenderTarget().getWidth(), MAX_CUBE_RENDER_WIDTH); |
| const int viewportHeight = deMin32(m_context.getRenderTarget().getHeight(), MAX_CUBE_RENDER_HEIGHT); |
| |
| const int viewportXOffsetMax = m_context.getRenderTarget().getWidth() - viewportWidth; |
| const int viewportYOffsetMax = m_context.getRenderTarget().getHeight() - viewportHeight; |
| |
| de::Random rnd (deStringHash(getName())); |
| |
| const int viewportXOffset = rnd.getInt(0, viewportXOffsetMax); |
| const int viewportYOffset = rnd.getInt(0, viewportYOffsetMax); |
| |
| glUseProgram(m_program->getProgram()); |
| |
| // Divide viewport into 4 areas. |
| const int leftWidth = viewportWidth / 2; |
| const int rightWidth = viewportWidth - leftWidth; |
| const int bottomHeight = viewportHeight / 2; |
| const int topHeight = viewportHeight - bottomHeight; |
| |
| // Clear. |
| glClearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Texture scaling and offsetting vectors. |
| const Vec2 texMinScale (1.0f, 1.0f); |
| const Vec2 texMinOffset (0.0f, 0.0f); |
| const Vec2 texMagScale (0.3f, 0.3f); |
| const Vec2 texMagOffset (0.5f, 0.3f); |
| |
| // Surface for the reference image. |
| tcu::Surface refImage(viewportWidth, viewportHeight); |
| |
| // Each of the four areas is divided into 6 cells. |
| const int defCellWidth = viewportWidth / 2 / 3; |
| const int defCellHeight = viewportHeight / 2 / 2; |
| |
| for (int i = 0; i < tcu::CUBEFACE_LAST; i++) |
| { |
| const int cellOffsetX = defCellWidth * (i % 3); |
| const int cellOffsetY = defCellHeight * (i / 3); |
| const bool isRightmostCell = i == 2 || i == 5; |
| const bool isTopCell = i >= 3; |
| const int leftCellWidth = isRightmostCell ? leftWidth - cellOffsetX : defCellWidth; |
| const int rightCellWidth = isRightmostCell ? rightWidth - cellOffsetX : defCellWidth; |
| const int bottomCellHeight = isTopCell ? bottomHeight - cellOffsetY : defCellHeight; |
| const int topCellHeight = isTopCell ? topHeight - cellOffsetY : defCellHeight; |
| |
| const struct Render |
| { |
| const Rect region; |
| int textureNdx; |
| const Vec2 texCoordScale; |
| const Vec2 texCoordOffset; |
| Render (const Rect& r, int tN, const Vec2& tS, const Vec2& tO) : region(r), textureNdx(tN), texCoordScale(tS), texCoordOffset(tO) {} |
| } renders[] = |
| { |
| Render(Rect(cellOffsetX + 0, cellOffsetY + 0, leftCellWidth, bottomCellHeight), 0, texMinScale, texMinOffset), |
| Render(Rect(cellOffsetX + leftWidth, cellOffsetY + 0, rightCellWidth, bottomCellHeight), 0, texMagScale, texMagOffset), |
| Render(Rect(cellOffsetX + 0, cellOffsetY + bottomHeight, leftCellWidth, topCellHeight), 1, texMinScale, texMinOffset), |
| Render(Rect(cellOffsetX + leftWidth, cellOffsetY + bottomHeight, rightCellWidth, topCellHeight), 1, texMagScale, texMagOffset) |
| }; |
| |
| for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++) |
| { |
| const Render& rend = renders[renderNdx]; |
| const float lod = calculateLod(rend.texCoordScale, rend.region.size().asFloat(), rend.textureNdx); |
| const bool useSafeTexCoords = isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter); |
| const Grid grid (GRID_SIZE_CUBE, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]), |
| TexTypeCoordParams<TEXTURETYPE_CUBE>(rend.texCoordScale, rend.texCoordOffset, (tcu::CubeFace)i), useSafeTexCoords); |
| |
| glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h); |
| renderCell (rend.textureNdx, lod, grid); |
| computeReferenceCell (rend.textureNdx, lod, grid, refImage, rend.region); |
| } |
| } |
| |
| // Read back rendered results. |
| tcu::Surface resImage(viewportWidth, viewportHeight); |
| glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess()); |
| |
| glUseProgram(0); |
| |
| // Compare and log. |
| { |
| const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage); |
| |
| m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, |
| isOk ? "Pass" : "Image comparison failed"); |
| } |
| |
| return STOP; |
| } |
| |
| void VertexCubeTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const |
| { |
| const deUint32 programID = m_program->getProgram(); |
| |
| // SETUP ATTRIBUTES. |
| |
| { |
| const int positionLoc = glGetAttribLocation(programID, "a_position"); |
| if (positionLoc != -1) |
| { |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr()); |
| } |
| } |
| |
| { |
| const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord"); |
| if (texCoordLoc != -1) |
| { |
| glEnableVertexAttribArray(texCoordLoc); |
| glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr()); |
| } |
| } |
| |
| // SETUP UNIFORMS. |
| |
| { |
| const int lodLoc = glGetUniformLocation(programID, "u_lod"); |
| if (lodLoc != -1) |
| glUniform1f(lodLoc, lod); |
| } |
| |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_CUBE_MAP, m_textures[textureNdx]->getGLTexture()); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, m_wrapS); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, m_wrapT); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, m_minFilter); |
| glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, m_magFilter); |
| |
| { |
| const int texLoc = glGetUniformLocation(programID, "u_texture"); |
| if (texLoc != -1) |
| glUniform1i(texLoc, 0); |
| } |
| } |
| |
| // Renders one cube face with given parameters. |
| void VertexCubeTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const |
| { |
| setupShaderInputs(textureNdx, lod, grid); |
| glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr()); |
| } |
| |
| // Computes reference for one cube face with given parameters. |
| void VertexCubeTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const |
| { |
| tcu::Sampler sampler = glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter); |
| sampler.seamlessCubeMap = true; |
| computeReference(m_textures[textureNdx]->getRefTexture(), lod, sampler, grid, dst, dstRegion); |
| } |
| |
| class Vertex2DArrayTextureCase : public TestCase |
| { |
| public: |
| Vertex2DArrayTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT); |
| ~Vertex2DArrayTextureCase (void); |
| |
| void init (void); |
| void deinit (void); |
| IterateResult iterate (void); |
| |
| private: |
| typedef PosTexCoordQuadGrid<TEXTURETYPE_2D_ARRAY> Grid; |
| |
| Vertex2DArrayTextureCase (const Vertex2DArrayTextureCase& other); |
| Vertex2DArrayTextureCase& operator= (const Vertex2DArrayTextureCase& other); |
| |
| float calculateLod (const Mat3& transf, const Vec2& dstSize, int textureNdx) const; |
| void setupShaderInputs (int textureNdx, float lod, const Grid& grid) const; |
| void renderCell (int textureNdx, float lod, const Grid& grid) const; |
| void computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const; |
| |
| const deUint32 m_minFilter; |
| const deUint32 m_magFilter; |
| const deUint32 m_wrapS; |
| const deUint32 m_wrapT; |
| |
| const glu::ShaderProgram* m_program; |
| glu::Texture2DArray* m_textures[2]; // 2 textures, a gradient texture and a grid texture. |
| }; |
| |
| Vertex2DArrayTextureCase::Vertex2DArrayTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT) |
| : TestCase (testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc) |
| , m_minFilter (minFilter) |
| , m_magFilter (magFilter) |
| , m_wrapS (wrapS) |
| , m_wrapT (wrapT) |
| , m_program (DE_NULL) |
| { |
| m_textures[0] = DE_NULL; |
| m_textures[1] = DE_NULL; |
| } |
| |
| Vertex2DArrayTextureCase::~Vertex2DArrayTextureCase(void) |
| { |
| Vertex2DArrayTextureCase::deinit(); |
| } |
| |
| void Vertex2DArrayTextureCase::init (void) |
| { |
| const char* const vertexShaderSource = |
| "#version 300 es\n" |
| "in highp vec2 a_position;\n" |
| "in highp vec3 a_texCoord;\n" |
| "uniform highp sampler2DArray u_texture;\n" |
| "uniform highp float u_lod;\n" |
| "out mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position, 0.0, 1.0);\n" |
| " v_color = textureLod(u_texture, a_texCoord, u_lod);\n" |
| "}\n"; |
| |
| const char* const fragmentShaderSource = |
| "#version 300 es\n" |
| "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" |
| "in mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " dEQP_FragColor = v_color;\n" |
| "}\n"; |
| |
| if (m_context.getRenderTarget().getNumSamples() != 0) |
| throw tcu::NotSupportedError("MSAA config not supported by this test"); |
| |
| // Create shader. |
| |
| DE_ASSERT(!m_program); |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource)); |
| |
| if(!m_program->isOk()) |
| { |
| m_testCtx.getLog() << *m_program; |
| |
| GLint maxVertexTextures; |
| glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures); |
| |
| if (maxVertexTextures < 1) |
| throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__); |
| else |
| TCU_FAIL("Failed to compile shader"); |
| } |
| |
| // Make the textures. |
| |
| try |
| { |
| const int texWidth = WIDTH_2D_ARRAY; |
| const int texHeight = HEIGHT_2D_ARRAY; |
| const int texLayers = LAYERS_2D_ARRAY; |
| |
| for (int i = 0; i < 2; i++) |
| { |
| DE_ASSERT(!m_textures[i]); |
| m_textures[i] = new glu::Texture2DArray(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight, texLayers); |
| } |
| |
| const int numLevels = deLog2Floor32(de::max(texWidth, texHeight)) + 1; |
| const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat()); |
| const Vec4 cBias = fmtInfo.valueMin; |
| const Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin; |
| |
| // Fill first with gradient texture. |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias; |
| const Vec4 gMax = Vec4( 1.0f, 1.0f, 1.0f, 0.0f)*cScale + cBias; |
| |
| m_textures[0]->getRefTexture().allocLevel(levelNdx); |
| tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax); |
| } |
| |
| // Fill second with grid texture. |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const deUint32 step = 0x00ffffff / numLevels; |
| const deUint32 rgb = step*levelNdx; |
| const deUint32 colorA = 0xff000000 | rgb; |
| const deUint32 colorB = 0xff000000 | ~rgb; |
| |
| m_textures[1]->getRefTexture().allocLevel(levelNdx); |
| tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias); |
| } |
| |
| // Upload. |
| for (int i = 0; i < 2; i++) |
| m_textures[i]->upload(); |
| } |
| catch (const std::exception&) |
| { |
| // Clean up to save memory. |
| Vertex2DArrayTextureCase::deinit(); |
| throw; |
| } |
| } |
| |
| void Vertex2DArrayTextureCase::deinit (void) |
| { |
| for (int i = 0; i < 2; i++) |
| { |
| delete m_textures[i]; |
| m_textures[i] = DE_NULL; |
| } |
| |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| float Vertex2DArrayTextureCase::calculateLod (const Mat3& transf, const Vec2& dstSize, int textureNdx) const |
| { |
| const tcu::Texture2DArray& refTexture = m_textures[textureNdx]->getRefTexture(); |
| const int texWidth = refTexture.getWidth(); |
| const int texHeight = refTexture.getHeight(); |
| |
| // Calculate transformed coordinates of three screen corners. |
| const Vec2 trans00 = (transf * Vec3(0.0f, 0.0f, 1.0f)).xy(); |
| const Vec2 trans01 = (transf * Vec3(0.0f, 1.0f, 1.0f)).xy(); |
| const Vec2 trans10 = (transf * Vec3(1.0f, 0.0f, 1.0f)).xy(); |
| |
| // Derivates. |
| const float dudx = (trans10.x() - trans00.x()) * (float)texWidth / dstSize.x(); |
| const float dudy = (trans01.x() - trans00.x()) * (float)texWidth / dstSize.y(); |
| const float dvdx = (trans10.y() - trans00.y()) * (float)texHeight / dstSize.x(); |
| const float dvdy = (trans01.y() - trans00.y()) * (float)texHeight / dstSize.y(); |
| |
| return deFloatLog2(deFloatSqrt(de::max(dudx*dudx + dvdx*dvdx, dudy*dudy + dvdy*dvdy))); |
| } |
| |
| Vertex2DArrayTextureCase::IterateResult Vertex2DArrayTextureCase::iterate (void) |
| { |
| const int viewportWidth = deMin32(m_context.getRenderTarget().getWidth(), MAX_2D_ARRAY_RENDER_WIDTH); |
| const int viewportHeight = deMin32(m_context.getRenderTarget().getHeight(), MAX_2D_ARRAY_RENDER_HEIGHT); |
| |
| const int viewportXOffsetMax = m_context.getRenderTarget().getWidth() - viewportWidth; |
| const int viewportYOffsetMax = m_context.getRenderTarget().getHeight() - viewportHeight; |
| |
| de::Random rnd (deStringHash(getName())); |
| |
| const int viewportXOffset = rnd.getInt(0, viewportXOffsetMax); |
| const int viewportYOffset = rnd.getInt(0, viewportYOffsetMax); |
| |
| glUseProgram(m_program->getProgram()); |
| |
| // Divide viewport into 4 cells. |
| const int leftWidth = viewportWidth / 2; |
| const int rightWidth = viewportWidth - leftWidth; |
| const int bottomHeight = viewportHeight / 2; |
| const int topHeight = viewportHeight - bottomHeight; |
| |
| // Clear. |
| glClearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Shear by layer count to get all layers visible. |
| static const float layerShearTransfData[] = |
| { |
| 1.0f, 0.0f, 0.0f, |
| 0.0f, 1.0f, 0.0f, |
| (float)LAYERS_2D_ARRAY, 0.0f, 0.0f |
| }; |
| |
| // Minification and magnification transformations. |
| static const float texMinTransfData[] = |
| { |
| 2.1f, 0.0f, -0.3f, |
| 0.0f, 2.1f, -0.3f, |
| 0.0f, 0.0f, 1.0f |
| }; |
| static const float texMagTransfData[] = |
| { |
| 0.4f, 0.0f, 0.8f, |
| 0.0f, 0.4f, 0.8f, |
| 0.0f, 0.0f, 1.0f |
| }; |
| |
| // Transformation matrices for minification and magnification. |
| const Mat3 texMinTransf = Mat3(layerShearTransfData) * Mat3(texMinTransfData); |
| const Mat3 texMagTransf = Mat3(layerShearTransfData) * Mat3(texMagTransfData); |
| |
| // Surface for the reference image. |
| tcu::Surface refImage(viewportWidth, viewportHeight); |
| |
| { |
| const struct Render |
| { |
| const Rect region; |
| int textureNdx; |
| const Mat3 texTransform; |
| Render (const Rect& r, int tN, const Mat3& tT) : region(r), textureNdx(tN), texTransform(tT) {} |
| } renders[] = |
| { |
| Render(Rect(0, 0, leftWidth, bottomHeight), 0, texMinTransf), |
| Render(Rect(leftWidth, 0, rightWidth, bottomHeight), 0, texMagTransf), |
| Render(Rect(0, bottomHeight, leftWidth, topHeight), 1, texMinTransf), |
| Render(Rect(leftWidth, bottomHeight, rightWidth, topHeight), 1, texMagTransf) |
| }; |
| |
| for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++) |
| { |
| const Render& rend = renders[renderNdx]; |
| const float lod = calculateLod(rend.texTransform, rend.region.size().asFloat(), rend.textureNdx); |
| const bool useSafeTexCoords = isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter); |
| const Grid grid (GRID_SIZE_2D_ARRAY, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]), |
| TexTypeCoordParams<TEXTURETYPE_2D_ARRAY>(rend.texTransform), useSafeTexCoords); |
| |
| glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h); |
| renderCell (rend.textureNdx, lod, grid); |
| computeReferenceCell (rend.textureNdx, lod, grid, refImage, rend.region); |
| } |
| } |
| |
| // Read back rendered results. |
| tcu::Surface resImage(viewportWidth, viewportHeight); |
| glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess()); |
| |
| glUseProgram(0); |
| |
| // Compare and log. |
| { |
| const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage); |
| |
| m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, |
| isOk ? "Pass" : "Image comparison failed"); |
| } |
| |
| return STOP; |
| } |
| |
| void Vertex2DArrayTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const |
| { |
| const deUint32 programID = m_program->getProgram(); |
| |
| // SETUP ATTRIBUTES. |
| |
| { |
| const int positionLoc = glGetAttribLocation(programID, "a_position"); |
| if (positionLoc != -1) |
| { |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr()); |
| } |
| } |
| |
| { |
| const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord"); |
| if (texCoordLoc != -1) |
| { |
| glEnableVertexAttribArray(texCoordLoc); |
| glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr()); |
| } |
| } |
| |
| // SETUP UNIFORMS. |
| |
| { |
| const int lodLoc = glGetUniformLocation(programID, "u_lod"); |
| if (lodLoc != -1) |
| glUniform1f(lodLoc, lod); |
| } |
| |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D_ARRAY, m_textures[textureNdx]->getGLTexture()); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, m_wrapS); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, m_wrapT); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, m_minFilter); |
| glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, m_magFilter); |
| |
| { |
| const int texLoc = glGetUniformLocation(programID, "u_texture"); |
| if (texLoc != -1) |
| glUniform1i(texLoc, 0); |
| } |
| } |
| |
| // Renders one sub-image with given parameters. |
| void Vertex2DArrayTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const |
| { |
| setupShaderInputs(textureNdx, lod, grid); |
| glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr()); |
| } |
| |
| // Computes reference for one sub-image with given parameters. |
| void Vertex2DArrayTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const |
| { |
| computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_minFilter, m_magFilter), grid, dst, dstRegion); |
| } |
| |
| class Vertex3DTextureCase : public TestCase |
| { |
| public: |
| Vertex3DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR); |
| ~Vertex3DTextureCase (void); |
| |
| void init (void); |
| void deinit (void); |
| IterateResult iterate (void); |
| |
| private: |
| typedef PosTexCoordQuadGrid<TEXTURETYPE_3D> Grid; |
| |
| Vertex3DTextureCase (const Vertex3DTextureCase& other); |
| Vertex3DTextureCase& operator= (const Vertex3DTextureCase& other); |
| |
| float calculateLod (const Mat3& transf, const Vec2& dstSize, int textureNdx) const; |
| void setupShaderInputs (int textureNdx, float lod, const Grid& grid) const; |
| void renderCell (int textureNdx, float lod, const Grid& grid) const; |
| void computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const; |
| |
| const deUint32 m_minFilter; |
| const deUint32 m_magFilter; |
| const deUint32 m_wrapS; |
| const deUint32 m_wrapT; |
| const deUint32 m_wrapR; |
| |
| const glu::ShaderProgram* m_program; |
| glu::Texture3D* m_textures[2]; // 2 textures, a gradient texture and a grid texture. |
| }; |
| |
| Vertex3DTextureCase::Vertex3DTextureCase (Context& testCtx, const char* name, const char* desc, deUint32 minFilter, deUint32 magFilter, deUint32 wrapS, deUint32 wrapT, deUint32 wrapR) |
| : TestCase (testCtx, tcu::NODETYPE_SELF_VALIDATE, name, desc) |
| , m_minFilter (minFilter) |
| , m_magFilter (magFilter) |
| , m_wrapS (wrapS) |
| , m_wrapT (wrapT) |
| , m_wrapR (wrapR) |
| , m_program (DE_NULL) |
| { |
| m_textures[0] = DE_NULL; |
| m_textures[1] = DE_NULL; |
| } |
| |
| Vertex3DTextureCase::~Vertex3DTextureCase(void) |
| { |
| Vertex3DTextureCase::deinit(); |
| } |
| |
| void Vertex3DTextureCase::init (void) |
| { |
| const char* const vertexShaderSource = |
| "#version 300 es\n" |
| "in highp vec2 a_position;\n" |
| "in highp vec3 a_texCoord;\n" |
| "uniform highp sampler3D u_texture;\n" |
| "uniform highp float u_lod;\n" |
| "out mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position, 0.0, 1.0);\n" |
| " v_color = textureLod(u_texture, a_texCoord, u_lod);\n" |
| "}\n"; |
| |
| const char* const fragmentShaderSource = |
| "#version 300 es\n" |
| "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" |
| "in mediump vec4 v_color;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " dEQP_FragColor = v_color;\n" |
| "}\n"; |
| |
| if (m_context.getRenderTarget().getNumSamples() != 0) |
| throw tcu::NotSupportedError("MSAA config not supported by this test"); |
| |
| // Create shader. |
| |
| DE_ASSERT(!m_program); |
| m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource)); |
| |
| if(!m_program->isOk()) |
| { |
| m_testCtx.getLog() << *m_program; |
| |
| GLint maxVertexTextures; |
| glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextures); |
| |
| if (maxVertexTextures < 1) |
| throw tcu::NotSupportedError("Vertex texture image units not supported", "", __FILE__, __LINE__); |
| else |
| TCU_FAIL("Failed to compile shader"); |
| } |
| |
| // Make the textures. |
| |
| try |
| { |
| const int texWidth = WIDTH_3D; |
| const int texHeight = HEIGHT_3D; |
| const int texDepth = DEPTH_3D; |
| |
| for (int i = 0; i < 2; i++) |
| { |
| DE_ASSERT(!m_textures[i]); |
| m_textures[i] = new glu::Texture3D(m_context.getRenderContext(), GL_RGB, GL_UNSIGNED_BYTE, texWidth, texHeight, texDepth); |
| } |
| |
| const int numLevels = deLog2Floor32(de::max(de::max(texWidth, texHeight), texDepth)) + 1; |
| const tcu::TextureFormatInfo fmtInfo = tcu::getTextureFormatInfo(m_textures[0]->getRefTexture().getFormat()); |
| const Vec4 cBias = fmtInfo.valueMin; |
| const Vec4 cScale = fmtInfo.valueMax-fmtInfo.valueMin; |
| |
| // Fill first with gradient texture. |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const Vec4 gMin = Vec4(-0.5f, -0.5f, -0.5f, 2.0f)*cScale + cBias; |
| const Vec4 gMax = Vec4( 1.0f, 1.0f, 1.0f, 0.0f)*cScale + cBias; |
| |
| m_textures[0]->getRefTexture().allocLevel(levelNdx); |
| tcu::fillWithComponentGradients(m_textures[0]->getRefTexture().getLevel(levelNdx), gMin, gMax); |
| } |
| |
| // Fill second with grid texture. |
| for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) |
| { |
| const deUint32 step = 0x00ffffff / numLevels; |
| const deUint32 rgb = step*levelNdx; |
| const deUint32 colorA = 0xff000000 | rgb; |
| const deUint32 colorB = 0xff000000 | ~rgb; |
| |
| m_textures[1]->getRefTexture().allocLevel(levelNdx); |
| tcu::fillWithGrid(m_textures[1]->getRefTexture().getLevel(levelNdx), 4, tcu::RGBA(colorA).toVec()*cScale + cBias, tcu::RGBA(colorB).toVec()*cScale + cBias); |
| } |
| |
| // Upload. |
| for (int i = 0; i < 2; i++) |
| m_textures[i]->upload(); |
| } |
| catch (const std::exception&) |
| { |
| // Clean up to save memory. |
| Vertex3DTextureCase::deinit(); |
| throw; |
| } |
| } |
| |
| void Vertex3DTextureCase::deinit (void) |
| { |
| for (int i = 0; i < 2; i++) |
| { |
| delete m_textures[i]; |
| m_textures[i] = DE_NULL; |
| } |
| |
| delete m_program; |
| m_program = DE_NULL; |
| } |
| |
| float Vertex3DTextureCase::calculateLod (const Mat3& transf, const Vec2& dstSize, int textureNdx) const |
| { |
| const tcu::Texture3D& refTexture = m_textures[textureNdx]->getRefTexture(); |
| const int srcWidth = refTexture.getWidth(); |
| const int srcHeight = refTexture.getHeight(); |
| const int srcDepth = refTexture.getDepth(); |
| |
| // Calculate transformed coordinates of three screen corners. |
| const Vec3 trans00 = transf * Vec3(0.0f, 0.0f, 1.0f); |
| const Vec3 trans01 = transf * Vec3(0.0f, 1.0f, 1.0f); |
| const Vec3 trans10 = transf * Vec3(1.0f, 0.0f, 1.0f); |
| |
| // Derivates. |
| const float dudx = (trans10.x() - trans00.x()) * (float)srcWidth / dstSize.x(); |
| const float dudy = (trans01.x() - trans00.x()) * (float)srcWidth / dstSize.y(); |
| const float dvdx = (trans10.y() - trans00.y()) * (float)srcHeight / dstSize.x(); |
| const float dvdy = (trans01.y() - trans00.y()) * (float)srcHeight / dstSize.y(); |
| const float dwdx = (trans10.z() - trans00.z()) * (float)srcDepth / dstSize.x(); |
| const float dwdy = (trans01.z() - trans00.z()) * (float)srcDepth / dstSize.y(); |
| |
| return deFloatLog2(deFloatSqrt(de::max(dudx*dudx + dvdx*dvdx + dwdx*dwdx, dudy*dudy + dvdy*dvdy + dwdy*dwdy))); |
| } |
| |
| Vertex3DTextureCase::IterateResult Vertex3DTextureCase::iterate (void) |
| { |
| const int viewportWidth = deMin32(m_context.getRenderTarget().getWidth(), MAX_3D_RENDER_WIDTH); |
| const int viewportHeight = deMin32(m_context.getRenderTarget().getHeight(), MAX_3D_RENDER_HEIGHT); |
| |
| const int viewportXOffsetMax = m_context.getRenderTarget().getWidth() - viewportWidth; |
| const int viewportYOffsetMax = m_context.getRenderTarget().getHeight() - viewportHeight; |
| |
| de::Random rnd (deStringHash(getName())); |
| |
| const int viewportXOffset = rnd.getInt(0, viewportXOffsetMax); |
| const int viewportYOffset = rnd.getInt(0, viewportYOffsetMax); |
| |
| glUseProgram(m_program->getProgram()); |
| |
| // Divide viewport into 4 cells. |
| const int leftWidth = viewportWidth / 2; |
| const int rightWidth = viewportWidth - leftWidth; |
| const int bottomHeight = viewportHeight / 2; |
| const int topHeight = viewportHeight - bottomHeight; |
| |
| // Clear. |
| glClearColor(0.125f, 0.25f, 0.5f, 1.0f); |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| // Shear to get all slices visible. |
| static const float depthShearTransfData[] = |
| { |
| 1.0f, 0.0f, 0.0f, |
| 0.0f, 1.0f, 0.0f, |
| 1.0f, 1.0f, 0.0f |
| }; |
| |
| // Minification and magnification transformations. |
| static const float texMinTransfData[] = |
| { |
| 2.2f, 0.0f, -0.3f, |
| 0.0f, 2.2f, -0.3f, |
| 0.0f, 0.0f, 1.0f |
| }; |
| static const float texMagTransfData[] = |
| { |
| 0.4f, 0.0f, 0.8f, |
| 0.0f, 0.4f, 0.8f, |
| 0.0f, 0.0f, 1.0f |
| }; |
| |
| // Transformation matrices for minification and magnification. |
| const Mat3 texMinTransf = Mat3(depthShearTransfData) * Mat3(texMinTransfData); |
| const Mat3 texMagTransf = Mat3(depthShearTransfData) * Mat3(texMagTransfData); |
| |
| // Surface for the reference image. |
| tcu::Surface refImage(viewportWidth, viewportHeight); |
| |
| { |
| const struct Render |
| { |
| const Rect region; |
| int textureNdx; |
| const Mat3 texTransform; |
| Render (const Rect& r, int tN, const Mat3& tT) : region(r), textureNdx(tN), texTransform(tT) {} |
| } renders[] = |
| { |
| Render(Rect(0, 0, leftWidth, bottomHeight), 0, texMinTransf), |
| Render(Rect(leftWidth, 0, rightWidth, bottomHeight), 0, texMagTransf), |
| Render(Rect(0, bottomHeight, leftWidth, topHeight), 1, texMinTransf), |
| Render(Rect(leftWidth, bottomHeight, rightWidth, topHeight), 1, texMagTransf) |
| }; |
| |
| for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renders); renderNdx++) |
| { |
| const Render& rend = renders[renderNdx]; |
| const float lod = calculateLod(rend.texTransform, rend.region.size().asFloat(), rend.textureNdx); |
| const bool useSafeTexCoords = isLevelNearest(lod > 0.0f ? m_minFilter : m_magFilter); |
| const Grid grid (GRID_SIZE_3D, rend.region.size(), getTextureSize(*m_textures[rend.textureNdx]), |
| TexTypeCoordParams<TEXTURETYPE_3D>(rend.texTransform), useSafeTexCoords); |
| |
| glViewport(viewportXOffset + rend.region.x, viewportYOffset + rend.region.y, rend.region.w, rend.region.h); |
| renderCell (rend.textureNdx, lod, grid); |
| computeReferenceCell (rend.textureNdx, lod, grid, refImage, rend.region); |
| } |
| } |
| |
| // Read back rendered results. |
| tcu::Surface resImage(viewportWidth, viewportHeight); |
| glu::readPixels(m_context.getRenderContext(), viewportXOffset, viewportYOffset, resImage.getAccess()); |
| |
| glUseProgram(0); |
| |
| // Compare and log. |
| { |
| const bool isOk = compareImages(m_context.getRenderContext(), m_testCtx.getLog(), refImage, resImage); |
| |
| m_testCtx.setTestResult(isOk ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, |
| isOk ? "Pass" : "Image comparison failed"); |
| } |
| |
| return STOP; |
| } |
| |
| void Vertex3DTextureCase::setupShaderInputs (int textureNdx, float lod, const Grid& grid) const |
| { |
| const deUint32 programID = m_program->getProgram(); |
| |
| // SETUP ATTRIBUTES. |
| |
| { |
| const int positionLoc = glGetAttribLocation(programID, "a_position"); |
| if (positionLoc != -1) |
| { |
| glEnableVertexAttribArray(positionLoc); |
| glVertexAttribPointer(positionLoc, 2, GL_FLOAT, GL_FALSE, 0, grid.getPositionPtr()); |
| } |
| } |
| |
| { |
| const int texCoordLoc = glGetAttribLocation(programID, "a_texCoord"); |
| if (texCoordLoc != -1) |
| { |
| glEnableVertexAttribArray(texCoordLoc); |
| glVertexAttribPointer(texCoordLoc, 3, GL_FLOAT, GL_FALSE, 0, grid.getTexCoordPtr()); |
| } |
| } |
| |
| // SETUP UNIFORMS. |
| |
| { |
| const int lodLoc = glGetUniformLocation(programID, "u_lod"); |
| if (lodLoc != -1) |
| glUniform1f(lodLoc, lod); |
| } |
| |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_3D, m_textures[textureNdx]->getGLTexture()); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, m_wrapS); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, m_wrapT); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, m_wrapR); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, m_minFilter); |
| glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, m_magFilter); |
| |
| { |
| const int texLoc = glGetUniformLocation(programID, "u_texture"); |
| if (texLoc != -1) |
| glUniform1i(texLoc, 0); |
| } |
| } |
| |
| // Renders one sub-image with given parameters. |
| void Vertex3DTextureCase::renderCell (int textureNdx, float lod, const Grid& grid) const |
| { |
| setupShaderInputs(textureNdx, lod, grid); |
| glDrawElements(GL_TRIANGLES, grid.getNumIndices(), GL_UNSIGNED_SHORT, grid.getIndexPtr()); |
| } |
| |
| // Computes reference for one sub-image with given parameters. |
| void Vertex3DTextureCase::computeReferenceCell (int textureNdx, float lod, const Grid& grid, tcu::Surface& dst, const Rect& dstRegion) const |
| { |
| computeReference(m_textures[textureNdx]->getRefTexture(), lod, glu::mapGLSampler(m_wrapS, m_wrapT, m_wrapR, m_minFilter, m_magFilter), grid, dst, dstRegion); |
| } |
| |
| VertexTextureTests::VertexTextureTests (Context& context) |
| : TestCaseGroup(context, "vertex", "Vertex Texture Tests") |
| { |
| } |
| |
| VertexTextureTests::~VertexTextureTests(void) |
| { |
| } |
| |
| void VertexTextureTests::init (void) |
| { |
| // 2D and cube map groups, and their filtering and wrap sub-groups. |
| TestCaseGroup* const group2D = new TestCaseGroup(m_context, "2d", "2D Vertex Texture Tests"); |
| TestCaseGroup* const groupCube = new TestCaseGroup(m_context, "cube", "Cube Map Vertex Texture Tests"); |
| TestCaseGroup* const group2DArray = new TestCaseGroup(m_context, "2d_array", "2D Array Vertex Texture Tests"); |
| TestCaseGroup* const group3D = new TestCaseGroup(m_context, "3d", "3D Vertex Texture Tests"); |
| TestCaseGroup* const filteringGroup2D = new TestCaseGroup(m_context, "filtering", "2D Vertex Texture Filtering Tests"); |
| TestCaseGroup* const wrapGroup2D = new TestCaseGroup(m_context, "wrap", "2D Vertex Texture Wrap Tests"); |
| TestCaseGroup* const filteringGroupCube = new TestCaseGroup(m_context, "filtering", "Cube Map Vertex Texture Filtering Tests"); |
| TestCaseGroup* const wrapGroupCube = new TestCaseGroup(m_context, "wrap", "Cube Map Vertex Texture Wrap Tests"); |
| TestCaseGroup* const filteringGroup2DArray = new TestCaseGroup(m_context, "filtering", "2D Array Vertex Texture Filtering Tests"); |
| TestCaseGroup* const wrapGroup2DArray = new TestCaseGroup(m_context, "wrap", "2D Array Vertex Texture Wrap Tests"); |
| TestCaseGroup* const filteringGroup3D = new TestCaseGroup(m_context, "filtering", "3D Vertex Texture Filtering Tests"); |
| TestCaseGroup* const wrapGroup3D = new TestCaseGroup(m_context, "wrap", "3D Vertex Texture Wrap Tests"); |
| |
| group2D->addChild(filteringGroup2D); |
| group2D->addChild(wrapGroup2D); |
| groupCube->addChild(filteringGroupCube); |
| groupCube->addChild(wrapGroupCube); |
| group2DArray->addChild(filteringGroup2DArray); |
| group2DArray->addChild(wrapGroup2DArray); |
| group3D->addChild(filteringGroup3D); |
| group3D->addChild(wrapGroup3D); |
| |
| addChild(group2D); |
| addChild(groupCube); |
| addChild(group2DArray); |
| addChild(group3D); |
| |
| static const struct |
| { |
| const char* name; |
| GLenum mode; |
| } wrapModes[] = |
| { |
| { "clamp", GL_CLAMP_TO_EDGE }, |
| { "repeat", GL_REPEAT }, |
| { "mirror", GL_MIRRORED_REPEAT } |
| }; |
| |
| static const struct |
| { |
| const char* name; |
| GLenum mode; |
| } minFilterModes[] = |
| { |
| { "nearest", GL_NEAREST }, |
| { "linear", GL_LINEAR }, |
| { "nearest_mipmap_nearest", GL_NEAREST_MIPMAP_NEAREST }, |
| { "linear_mipmap_nearest", GL_LINEAR_MIPMAP_NEAREST }, |
| { "nearest_mipmap_linear", GL_NEAREST_MIPMAP_LINEAR }, |
| { "linear_mipmap_linear", GL_LINEAR_MIPMAP_LINEAR } |
| }; |
| |
| static const struct |
| { |
| const char* name; |
| GLenum mode; |
| } magFilterModes[] = |
| { |
| { "nearest", GL_NEAREST }, |
| { "linear", GL_LINEAR } |
| }; |
| |
| #define FOR_EACH(ITERATOR, ARRAY, BODY) \ |
| for (int ITERATOR = 0; ITERATOR < DE_LENGTH_OF_ARRAY(ARRAY); ITERATOR++) \ |
| BODY |
| |
| // 2D cases. |
| |
| FOR_EACH(minFilter, minFilterModes, |
| FOR_EACH(magFilter, magFilterModes, |
| FOR_EACH(wrapMode, wrapModes, |
| { |
| const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name; |
| |
| filteringGroup2D->addChild(new Vertex2DTextureCase(m_context, |
| name.c_str(), "", |
| minFilterModes[minFilter].mode, |
| magFilterModes[magFilter].mode, |
| wrapModes[wrapMode].mode, |
| wrapModes[wrapMode].mode)); |
| }))); |
| |
| FOR_EACH(wrapSMode, wrapModes, |
| FOR_EACH(wrapTMode, wrapModes, |
| { |
| const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name; |
| |
| wrapGroup2D->addChild(new Vertex2DTextureCase(m_context, |
| name.c_str(), "", |
| GL_LINEAR_MIPMAP_LINEAR, |
| GL_LINEAR, |
| wrapModes[wrapSMode].mode, |
| wrapModes[wrapTMode].mode)); |
| })); |
| |
| // Cube map cases. |
| |
| FOR_EACH(minFilter, minFilterModes, |
| FOR_EACH(magFilter, magFilterModes, |
| FOR_EACH(wrapMode, wrapModes, |
| { |
| const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name; |
| |
| filteringGroupCube->addChild(new VertexCubeTextureCase(m_context, |
| name.c_str(), "", |
| minFilterModes[minFilter].mode, |
| magFilterModes[magFilter].mode, |
| wrapModes[wrapMode].mode, |
| wrapModes[wrapMode].mode)); |
| }))); |
| |
| FOR_EACH(wrapSMode, wrapModes, |
| FOR_EACH(wrapTMode, wrapModes, |
| { |
| const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name; |
| |
| wrapGroupCube->addChild(new VertexCubeTextureCase(m_context, |
| name.c_str(), "", |
| GL_LINEAR_MIPMAP_LINEAR, |
| GL_LINEAR, |
| wrapModes[wrapSMode].mode, |
| wrapModes[wrapTMode].mode)); |
| })); |
| |
| // 2D array cases. |
| |
| FOR_EACH(minFilter, minFilterModes, |
| FOR_EACH(magFilter, magFilterModes, |
| FOR_EACH(wrapMode, wrapModes, |
| { |
| const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name; |
| |
| filteringGroup2DArray->addChild(new Vertex2DArrayTextureCase(m_context, |
| name.c_str(), "", |
| minFilterModes[minFilter].mode, |
| magFilterModes[magFilter].mode, |
| wrapModes[wrapMode].mode, |
| wrapModes[wrapMode].mode)); |
| }))); |
| |
| FOR_EACH(wrapSMode, wrapModes, |
| FOR_EACH(wrapTMode, wrapModes, |
| { |
| const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name; |
| |
| wrapGroup2DArray->addChild(new Vertex2DArrayTextureCase(m_context, |
| name.c_str(), "", |
| GL_LINEAR_MIPMAP_LINEAR, |
| GL_LINEAR, |
| wrapModes[wrapSMode].mode, |
| wrapModes[wrapTMode].mode)); |
| })); |
| |
| // 3D cases. |
| |
| FOR_EACH(minFilter, minFilterModes, |
| FOR_EACH(magFilter, magFilterModes, |
| FOR_EACH(wrapMode, wrapModes, |
| { |
| const string name = string("") + minFilterModes[minFilter].name + "_" + magFilterModes[magFilter].name + "_" + wrapModes[wrapMode].name; |
| |
| filteringGroup3D->addChild(new Vertex3DTextureCase(m_context, |
| name.c_str(), "", |
| minFilterModes[minFilter].mode, |
| magFilterModes[magFilter].mode, |
| wrapModes[wrapMode].mode, |
| wrapModes[wrapMode].mode, |
| wrapModes[wrapMode].mode)); |
| }))); |
| |
| FOR_EACH(wrapSMode, wrapModes, |
| FOR_EACH(wrapTMode, wrapModes, |
| FOR_EACH(wrapRMode, wrapModes, |
| { |
| const string name = string("") + wrapModes[wrapSMode].name + "_" + wrapModes[wrapTMode].name + "_" + wrapModes[wrapRMode].name; |
| |
| wrapGroup3D->addChild(new Vertex3DTextureCase(m_context, |
| name.c_str(), "", |
| GL_LINEAR_MIPMAP_LINEAR, |
| GL_LINEAR, |
| wrapModes[wrapSMode].mode, |
| wrapModes[wrapTMode].mode, |
| wrapModes[wrapRMode].mode)); |
| }))); |
| } |
| |
| } // Functional |
| } // gles3 |
| } // deqp |