| /*------------------------------------------------------------------------- |
| * drawElements Quality Program OpenGL (ES) 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 Parametrized, long-running stress case. |
| * |
| * \todo [2013-06-27 nuutti] Do certain things in a cleaner and less |
| * confusing way, such as the "redundant buffer |
| * factor" thing in LongStressCase. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "glsLongStressCase.hpp" |
| #include "tcuTestLog.hpp" |
| #include "tcuCommandLine.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuVector.hpp" |
| #include "tcuVectorUtil.hpp" |
| #include "glsTextureTestUtil.hpp" |
| #include "gluPixelTransfer.hpp" |
| #include "gluTextureUtil.hpp" |
| #include "tcuStringTemplate.hpp" |
| #include "gluStrUtil.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "deRandom.hpp" |
| #include "deStringUtil.hpp" |
| #include "deString.h" |
| #include "deSharedPtr.hpp" |
| #include "deClock.h" |
| |
| #include "glw.h" |
| |
| #include <limits> |
| #include <vector> |
| #include <iomanip> |
| #include <map> |
| #include <iomanip> |
| |
| using de::Random; |
| using de::SharedPtr; |
| using de::toString; |
| using tcu::ConstPixelBufferAccess; |
| using tcu::CubeFace; |
| using tcu::IVec2; |
| using tcu::IVec3; |
| using tcu::IVec4; |
| using tcu::TestLog; |
| using tcu::TextureFormat; |
| using tcu::TextureLevel; |
| using tcu::Vec2; |
| using tcu::Vec3; |
| using tcu::Vec4; |
| |
| using std::map; |
| using std::string; |
| using std::vector; |
| |
| namespace deqp |
| { |
| namespace gls |
| { |
| |
| using glu::TextureTestUtil::TextureType; |
| using glu::TextureTestUtil::TEXTURETYPE_2D; |
| using glu::TextureTestUtil::TEXTURETYPE_CUBE; |
| |
| static const float Mi = (float)(1 << 20); |
| |
| static const uint32_t bufferUsages[] = {GL_STATIC_DRAW, GL_STREAM_DRAW, GL_DYNAMIC_DRAW, |
| |
| GL_STATIC_READ, GL_STREAM_READ, GL_DYNAMIC_READ, |
| |
| GL_STATIC_COPY, GL_STREAM_COPY, GL_DYNAMIC_COPY}; |
| |
| static const uint32_t bufferUsagesGLES2[] = {GL_STATIC_DRAW, GL_DYNAMIC_DRAW, GL_STREAM_DRAW}; |
| |
| static const uint32_t bufferTargets[] = {GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, |
| |
| GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, GL_PIXEL_PACK_BUFFER, |
| GL_PIXEL_UNPACK_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, GL_UNIFORM_BUFFER}; |
| |
| static const uint32_t bufferTargetsGLES2[] = {GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER}; |
| |
| static inline int computePixelStore(const TextureFormat &format) |
| { |
| const int pixelSize = format.getPixelSize(); |
| if (deIsPowerOfTwo32(pixelSize)) |
| return de::min(pixelSize, 8); |
| else |
| return 1; |
| } |
| |
| static inline int getNumIterations(const tcu::TestContext &testCtx, const int defaultNumIterations) |
| { |
| const int cmdLineVal = testCtx.getCommandLine().getTestIterationCount(); |
| return cmdLineVal == 0 ? defaultNumIterations : cmdLineVal; |
| } |
| |
| static inline float triangleArea(const Vec2 &a, const Vec2 &b, const Vec2 &c) |
| { |
| const Vec2 ab = b - a; |
| const Vec2 ac = c - a; |
| return 0.5f * tcu::length(ab.x() * ac.y() - ab.y() * ac.x()); |
| } |
| |
| static inline string mangleShaderNames(const string &source, const string &manglingSuffix) |
| { |
| map<string, string> m; |
| m["NS"] = manglingSuffix; |
| return tcu::StringTemplate(source.c_str()).specialize(m); |
| } |
| |
| template <typename T, int N> |
| static inline T randomChoose(Random &rnd, const T (&arr)[N]) |
| { |
| return rnd.choose<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr)); |
| } |
| |
| static inline int nextDivisible(const int x, const int div) |
| { |
| DE_ASSERT(x >= 0); |
| DE_ASSERT(div >= 1); |
| return x == 0 ? 0 : x - 1 + div - (x - 1) % div; |
| } |
| |
| static inline string getTimeStr(const uint64_t seconds) |
| { |
| const uint64_t m = seconds / 60; |
| const uint64_t h = m / 60; |
| const uint64_t d = h / 24; |
| std::ostringstream res; |
| |
| res << d << "d " << h % 24 << "h " << m % 60 << "m " << seconds % 60 << "s"; |
| return res.str(); |
| } |
| |
| static inline string probabilityStr(const float prob) |
| { |
| return prob == 0.0f ? "never" : prob == 1.0f ? "ALWAYS" : de::floatToString(prob * 100.0f, 0) + "%"; |
| } |
| |
| static inline uint32_t randomBufferTarget(Random &rnd, const bool isGLES3) |
| { |
| return isGLES3 ? randomChoose(rnd, bufferTargets) : randomChoose(rnd, bufferTargetsGLES2); |
| } |
| |
| static inline uint32_t randomBufferUsage(Random &rnd, const bool isGLES3) |
| { |
| return isGLES3 ? randomChoose(rnd, bufferUsages) : randomChoose(rnd, bufferUsagesGLES2); |
| } |
| |
| static inline uint32_t cubeFaceToGLFace(tcu::CubeFace face) |
| { |
| switch (face) |
| { |
| case tcu::CUBEFACE_NEGATIVE_X: |
| return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; |
| case tcu::CUBEFACE_POSITIVE_X: |
| return GL_TEXTURE_CUBE_MAP_POSITIVE_X; |
| case tcu::CUBEFACE_NEGATIVE_Y: |
| return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; |
| case tcu::CUBEFACE_POSITIVE_Y: |
| return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; |
| case tcu::CUBEFACE_NEGATIVE_Z: |
| return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; |
| case tcu::CUBEFACE_POSITIVE_Z: |
| return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; |
| default: |
| DE_ASSERT(false); |
| return GL_NONE; |
| } |
| } |
| |
| #if defined(DE_DEBUG) |
| static inline bool isMatchingGLInternalFormat(const uint32_t internalFormat, const TextureFormat &texFormat) |
| { |
| switch (internalFormat) |
| { |
| // Unsized formats. |
| |
| case GL_RGBA: |
| return texFormat.order == TextureFormat::RGBA && |
| (texFormat.type == TextureFormat::UNORM_INT8 || texFormat.type == TextureFormat::UNORM_SHORT_4444 || |
| texFormat.type == TextureFormat::UNORM_SHORT_5551); |
| |
| case GL_RGB: |
| return texFormat.order == TextureFormat::RGB && |
| (texFormat.type == TextureFormat::UNORM_INT8 || texFormat.type == TextureFormat::UNORM_SHORT_565); |
| |
| case GL_LUMINANCE_ALPHA: |
| return texFormat.order == TextureFormat::LA && texFormat.type == TextureFormat::UNORM_INT8; |
| case GL_LUMINANCE: |
| return texFormat.order == TextureFormat::L && texFormat.type == TextureFormat::UNORM_INT8; |
| case GL_ALPHA: |
| return texFormat.order == TextureFormat::A && texFormat.type == TextureFormat::UNORM_INT8; |
| |
| // Sized formats. |
| |
| default: |
| return glu::mapGLInternalFormat(internalFormat) == texFormat; |
| } |
| } |
| #endif // DE_DEBUG |
| |
| static inline bool compileShader(const uint32_t shaderGL) |
| { |
| glCompileShader(shaderGL); |
| |
| int success = GL_FALSE; |
| glGetShaderiv(shaderGL, GL_COMPILE_STATUS, &success); |
| |
| return success == GL_TRUE; |
| } |
| |
| static inline bool linkProgram(const uint32_t programGL) |
| { |
| glLinkProgram(programGL); |
| |
| int success = GL_FALSE; |
| glGetProgramiv(programGL, GL_LINK_STATUS, &success); |
| |
| return success == GL_TRUE; |
| } |
| |
| static inline string getShaderInfoLog(const uint32_t shaderGL) |
| { |
| int infoLogLen = 0; |
| vector<char> infoLog; |
| glGetShaderiv(shaderGL, GL_INFO_LOG_LENGTH, &infoLogLen); |
| infoLog.resize(infoLogLen + 1); |
| glGetShaderInfoLog(shaderGL, (int)infoLog.size(), DE_NULL, &infoLog[0]); |
| return &infoLog[0]; |
| } |
| |
| static inline string getProgramInfoLog(const uint32_t programGL) |
| { |
| int infoLogLen = 0; |
| vector<char> infoLog; |
| glGetProgramiv(programGL, GL_INFO_LOG_LENGTH, &infoLogLen); |
| infoLog.resize(infoLogLen + 1); |
| glGetProgramInfoLog(programGL, (int)infoLog.size(), DE_NULL, &infoLog[0]); |
| return &infoLog[0]; |
| } |
| |
| namespace LongStressCaseInternal |
| { |
| |
| // A hacky-ish class for drawing text on screen as GL quads. |
| class DebugInfoRenderer |
| { |
| public: |
| DebugInfoRenderer(const glu::RenderContext &ctx); |
| ~DebugInfoRenderer(void) |
| { |
| delete m_prog; |
| } |
| |
| void drawInfo(uint64_t secondsElapsed, int texMem, int maxTexMem, int bufMem, int maxBufMem, int iterNdx); |
| |
| private: |
| DebugInfoRenderer(const DebugInfoRenderer &); |
| DebugInfoRenderer &operator=(const DebugInfoRenderer &); |
| |
| void render(void); |
| void addTextToBuffer(const string &text, int yOffset); |
| |
| const glu::RenderContext &m_ctx; |
| const glu::ShaderProgram *m_prog; |
| vector<float> m_posBuf; |
| vector<uint16_t> m_ndxBuf; |
| }; |
| |
| void DebugInfoRenderer::drawInfo(const uint64_t secondsElapsed, const int texMem, const int maxTexMem, const int bufMem, |
| const int maxBufMem, const int iterNdx) |
| { |
| const uint64_t m = secondsElapsed / 60; |
| const uint64_t h = m / 60; |
| const uint64_t d = h / 24; |
| |
| { |
| std::ostringstream text; |
| |
| text << std::setw(2) << std::setfill('0') << d << ":" << std::setw(2) << std::setfill('0') << h % 24 << ":" |
| << std::setw(2) << std::setfill('0') << m % 60 << ":" << std::setw(2) << std::setfill('0') |
| << secondsElapsed % 60; |
| addTextToBuffer(text.str(), 0); |
| text.str(""); |
| |
| text << std::fixed << std::setprecision(2) << (float)texMem / Mi << "/" << (float)maxTexMem / Mi; |
| addTextToBuffer(text.str(), 1); |
| text.str(""); |
| |
| text << std::fixed << std::setprecision(2) << (float)bufMem / Mi << "/" << (float)maxBufMem / Mi; |
| addTextToBuffer(text.str(), 2); |
| text.str(""); |
| |
| text << std::setw(0) << iterNdx; |
| addTextToBuffer(text.str(), 3); |
| } |
| |
| render(); |
| } |
| |
| DebugInfoRenderer::DebugInfoRenderer(const glu::RenderContext &ctx) : m_ctx(ctx), m_prog(DE_NULL) |
| { |
| DE_ASSERT(!m_prog); |
| m_prog = new glu::ShaderProgram(ctx, glu::makeVtxFragSources("attribute highp vec2 a_pos;\n" |
| "void main (void)\n" |
| "{\n" |
| " gl_Position = vec4(a_pos, -1.0, 1.0);\n" |
| "}\n", |
| |
| "void main(void)\n" |
| "{\n" |
| " gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" |
| "}\n")); |
| } |
| |
| void DebugInfoRenderer::addTextToBuffer(const string &text, const int yOffset) |
| { |
| static const char characters[] = "0123456789.:/"; |
| const int numCharacters = DE_LENGTH_OF_ARRAY(characters) - 1; // \note -1 for null byte. |
| const int charWid = 6; |
| const int charHei = 6; |
| static const string charsStr(characters); |
| |
| static const char font[numCharacters * charWid * charHei + 1] = " #### " |
| " # " |
| " #### " |
| "##### " |
| " # " |
| "######" |
| " #####" |
| "######" |
| " #### " |
| " #### " |
| " " |
| " ## " |
| " #" |
| "# #" |
| " ## " |
| "# #" |
| " #" |
| " # " |
| "# " |
| "# " |
| " # " |
| "# #" |
| "# #" |
| " " |
| " ## " |
| " # " |
| "# #" |
| " # " |
| " # " |
| " ### " |
| " # # " |
| " #### " |
| "# ### " |
| " # " |
| " #### " |
| " #####" |
| " " |
| " " |
| " # " |
| "# #" |
| " # " |
| " # " |
| " #" |
| "######" |
| " #" |
| "## #" |
| " # " |
| "# #" |
| " #" |
| " " |
| " ## " |
| " # " |
| "# #" |
| " # " |
| " # " |
| "# #" |
| " # " |
| "# #" |
| "# #" |
| " # " |
| "# #" |
| " ## " |
| " ## " |
| " ## " |
| " # " |
| " #### " |
| " ### " |
| "######" |
| " #### " |
| " # " |
| " #### " |
| " #### " |
| "# " |
| " #### " |
| "### " |
| " ## " |
| " " |
| "# "; |
| |
| for (int ndxInText = 0; ndxInText < (int)text.size(); ndxInText++) |
| { |
| const int ndxInCharset = (int)charsStr.find(text[ndxInText]); |
| DE_ASSERT(ndxInCharset < numCharacters); |
| const int fontXStart = ndxInCharset * charWid; |
| |
| for (int y = 0; y < charHei; y++) |
| { |
| float ay = -1.0f + (float)(y + 0 + yOffset * (charHei + 2)) * 0.1f / (float)(charHei + 2); |
| float by = -1.0f + (float)(y + 1 + yOffset * (charHei + 2)) * 0.1f / (float)(charHei + 2); |
| for (int x = 0; x < charWid; x++) |
| { |
| // \note Text is mirrored in x direction since on most(?) mobile devices the image is mirrored(?). |
| float ax = 1.0f - (float)(x + 0 + ndxInText * (charWid + 2)) * 0.1f / (float)(charWid + 2); |
| float bx = 1.0f - (float)(x + 1 + ndxInText * (charWid + 2)) * 0.1f / (float)(charWid + 2); |
| |
| if (font[y * numCharacters * charWid + fontXStart + x] != ' ') |
| { |
| const int vtxNdx = (int)m_posBuf.size() / 2; |
| |
| m_ndxBuf.push_back(uint16_t(vtxNdx + 0)); |
| m_ndxBuf.push_back(uint16_t(vtxNdx + 1)); |
| m_ndxBuf.push_back(uint16_t(vtxNdx + 2)); |
| |
| m_ndxBuf.push_back(uint16_t(vtxNdx + 2)); |
| m_ndxBuf.push_back(uint16_t(vtxNdx + 1)); |
| m_ndxBuf.push_back(uint16_t(vtxNdx + 3)); |
| |
| m_posBuf.push_back(ax); |
| m_posBuf.push_back(ay); |
| |
| m_posBuf.push_back(bx); |
| m_posBuf.push_back(ay); |
| |
| m_posBuf.push_back(ax); |
| m_posBuf.push_back(by); |
| |
| m_posBuf.push_back(bx); |
| m_posBuf.push_back(by); |
| } |
| } |
| } |
| } |
| } |
| |
| void DebugInfoRenderer::render(void) |
| { |
| const int prog = m_prog->getProgram(); |
| const int posloc = glGetAttribLocation(prog, "a_pos"); |
| |
| glUseProgram(prog); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| glEnableVertexAttribArray(posloc); |
| glVertexAttribPointer(posloc, 2, GL_FLOAT, 0, 0, &m_posBuf[0]); |
| glDrawElements(GL_TRIANGLES, (int)m_ndxBuf.size(), GL_UNSIGNED_SHORT, &m_ndxBuf[0]); |
| glDisableVertexAttribArray(posloc); |
| |
| m_posBuf.clear(); |
| m_ndxBuf.clear(); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Texture object helper class |
| * |
| * Each Texture owns a GL texture object that is created when the Texture |
| * is constructed and deleted when it's destructed. The class provides some |
| * convenience interface functions to e.g. upload texture data to the GL. |
| * |
| * In addition, the class tracks the approximate amount of GL memory likely |
| * used by the corresponding GL texture object; get this with |
| * getApproxMemUsage(). Also, getApproxMemUsageDiff() returns N-M, where N |
| * is the value that getApproxMemUsage() would return after a call to |
| * setData() with arguments corresponding to those given to |
| * getApproxMemUsageDiff(), and M is the value currently returned by |
| * getApproxMemUsage(). This can be used to check if we need to free some |
| * other memory before performing the setData() call, in case we have an |
| * upper limit on the amount of memory we want to use. |
| *//*--------------------------------------------------------------------*/ |
| class Texture |
| { |
| public: |
| Texture(TextureType type); |
| ~Texture(void); |
| |
| // Functions that may change the value returned by getApproxMemUsage(). |
| void setData(const ConstPixelBufferAccess &src, int width, int height, uint32_t internalFormat, bool useMipmap); |
| |
| // Functions that don't change the value returned by getApproxMemUsage(). |
| void setSubData(const ConstPixelBufferAccess &src, int xOff, int yOff, int width, int height) const; |
| void toUnit(int unit) const; |
| void setFilter(uint32_t min, uint32_t mag) const; |
| void setWrap(uint32_t s, uint32_t t) const; |
| |
| int getApproxMemUsage(void) const |
| { |
| return m_dataSizeApprox; |
| } |
| int getApproxMemUsageDiff(int width, int height, uint32_t internalFormat, bool useMipmap) const; |
| |
| private: |
| Texture(const Texture &); // Not allowed. |
| Texture &operator=(const Texture &); // Not allowed. |
| |
| static uint32_t genTexture(void) |
| { |
| uint32_t tex = 0; |
| glGenTextures(1, &tex); |
| return tex; |
| } |
| |
| uint32_t getGLBindTarget(void) const |
| { |
| DE_ASSERT(m_type == TEXTURETYPE_2D || m_type == TEXTURETYPE_CUBE); |
| return m_type == TEXTURETYPE_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP; |
| } |
| |
| const TextureType m_type; |
| const uint32_t m_textureGL; |
| |
| int m_numMipLevels; |
| uint32_t m_internalFormat; |
| int m_dataSizeApprox; |
| }; |
| |
| Texture::Texture(const TextureType type) |
| : m_type(type) |
| , m_textureGL(genTexture()) |
| , m_numMipLevels(0) |
| , m_internalFormat(0) |
| , m_dataSizeApprox(0) |
| { |
| } |
| |
| Texture::~Texture(void) |
| { |
| glDeleteTextures(1, &m_textureGL); |
| } |
| |
| int Texture::getApproxMemUsageDiff(const int width, const int height, const uint32_t internalFormat, |
| const bool useMipmap) const |
| { |
| const int numLevels = useMipmap ? deLog2Floor32(de::max(width, height)) + 1 : 1; |
| const int pixelSize = internalFormat == GL_RGBA ? 4 : |
| internalFormat == GL_RGB ? 3 : |
| internalFormat == GL_ALPHA ? 1 : |
| glu::mapGLInternalFormat(internalFormat).getPixelSize(); |
| int memUsageApproxAfter = 0; |
| |
| for (int level = 0; level < numLevels; level++) |
| memUsageApproxAfter += |
| de::max(1, width >> level) * de::max(1, height >> level) * pixelSize * (m_type == TEXTURETYPE_CUBE ? 6 : 1); |
| |
| return memUsageApproxAfter - getApproxMemUsage(); |
| } |
| |
| void Texture::setData(const ConstPixelBufferAccess &src, const int width, const int height, |
| const uint32_t internalFormat, const bool useMipmap) |
| { |
| DE_ASSERT(m_type != TEXTURETYPE_CUBE || width == height); |
| DE_ASSERT(!useMipmap || (deIsPowerOfTwo32(width) && deIsPowerOfTwo32(height))); |
| |
| const TextureFormat &format = src.getFormat(); |
| const glu::TransferFormat transfer = glu::getTransferFormat(format); |
| |
| m_numMipLevels = useMipmap ? deLog2Floor32(de::max(width, height)) + 1 : 1; |
| |
| m_internalFormat = internalFormat; |
| m_dataSizeApprox = width * height * format.getPixelSize() * (m_type == TEXTURETYPE_CUBE ? 6 : 1); |
| |
| DE_ASSERT(src.getRowPitch() == format.getPixelSize() * src.getWidth()); |
| DE_ASSERT(isMatchingGLInternalFormat(internalFormat, format)); |
| DE_ASSERT(width <= src.getWidth() && height <= src.getHeight()); |
| |
| glPixelStorei(GL_UNPACK_ALIGNMENT, computePixelStore(format)); |
| |
| if (m_type == TEXTURETYPE_2D) |
| { |
| m_dataSizeApprox = 0; |
| |
| glBindTexture(GL_TEXTURE_2D, m_textureGL); |
| for (int level = 0; level < m_numMipLevels; level++) |
| { |
| const int levelWid = de::max(1, width >> level); |
| const int levelHei = de::max(1, height >> level); |
| m_dataSizeApprox += levelWid * levelHei * format.getPixelSize(); |
| glTexImage2D(GL_TEXTURE_2D, level, internalFormat, levelWid, levelHei, 0, transfer.format, |
| transfer.dataType, src.getDataPtr()); |
| } |
| } |
| else if (m_type == TEXTURETYPE_CUBE) |
| { |
| m_dataSizeApprox = 0; |
| |
| glBindTexture(GL_TEXTURE_CUBE_MAP, m_textureGL); |
| for (int level = 0; level < m_numMipLevels; level++) |
| { |
| const int levelWid = de::max(1, width >> level); |
| const int levelHei = de::max(1, height >> level); |
| m_dataSizeApprox += 6 * levelWid * levelHei * format.getPixelSize(); |
| for (int face = 0; face < tcu::CUBEFACE_LAST; face++) |
| glTexImage2D(cubeFaceToGLFace((CubeFace)face), level, internalFormat, levelWid, levelHei, 0, |
| transfer.format, transfer.dataType, src.getDataPtr()); |
| } |
| } |
| else |
| DE_ASSERT(false); |
| } |
| |
| void Texture::setSubData(const ConstPixelBufferAccess &src, const int xOff, const int yOff, const int width, |
| const int height) const |
| { |
| const TextureFormat &format = src.getFormat(); |
| const glu::TransferFormat transfer = glu::getTransferFormat(format); |
| |
| DE_ASSERT(src.getRowPitch() == format.getPixelSize() * src.getWidth()); |
| DE_ASSERT(isMatchingGLInternalFormat(m_internalFormat, format)); |
| DE_ASSERT(width <= src.getWidth() && height <= src.getHeight()); |
| |
| glPixelStorei(GL_UNPACK_ALIGNMENT, computePixelStore(format)); |
| |
| if (m_type == TEXTURETYPE_2D) |
| { |
| glBindTexture(GL_TEXTURE_2D, m_textureGL); |
| for (int level = 0; level < m_numMipLevels; level++) |
| glTexSubImage2D(GL_TEXTURE_2D, level, xOff >> level, yOff >> level, de::max(1, width >> level), |
| de::max(1, height >> level), transfer.format, transfer.dataType, src.getDataPtr()); |
| } |
| else if (m_type == TEXTURETYPE_CUBE) |
| { |
| glBindTexture(GL_TEXTURE_CUBE_MAP, m_textureGL); |
| for (int level = 0; level < m_numMipLevels; level++) |
| { |
| for (int face = 0; face < tcu::CUBEFACE_LAST; face++) |
| glTexSubImage2D(cubeFaceToGLFace((CubeFace)face), level, xOff >> level, yOff >> level, |
| de::max(1, width >> level), de::max(1, height >> level), transfer.format, |
| transfer.dataType, src.getDataPtr()); |
| } |
| } |
| else |
| DE_ASSERT(false); |
| } |
| |
| void Texture::setFilter(const uint32_t min, const uint32_t mag) const |
| { |
| glBindTexture(getGLBindTarget(), m_textureGL); |
| glTexParameteri(getGLBindTarget(), GL_TEXTURE_MIN_FILTER, min); |
| glTexParameteri(getGLBindTarget(), GL_TEXTURE_MAG_FILTER, mag); |
| } |
| |
| void Texture::setWrap(const uint32_t s, const uint32_t t) const |
| { |
| glBindTexture(getGLBindTarget(), m_textureGL); |
| glTexParameteri(getGLBindTarget(), GL_TEXTURE_WRAP_S, s); |
| glTexParameteri(getGLBindTarget(), GL_TEXTURE_WRAP_T, t); |
| } |
| |
| void Texture::toUnit(const int unit) const |
| { |
| glActiveTexture(GL_TEXTURE0 + unit); |
| glBindTexture(getGLBindTarget(), m_textureGL); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Buffer object helper class |
| * |
| * Each Buffer owns a GL buffer object that is created when the Buffer |
| * is constructed and deleted when it's destructed. The class provides some |
| * convenience interface functions to e.g. upload buffer data to the GL. |
| * |
| * In addition, the class tracks the approximate amount of GL memory, |
| * similarly to the Texture class (see above). The getApproxMemUsageDiff() |
| * is also analoguous. |
| *//*--------------------------------------------------------------------*/ |
| class Buffer |
| { |
| public: |
| Buffer(void); |
| ~Buffer(void); |
| |
| // Functions that may change the value returned by getApproxMemUsage(). |
| template <typename T> |
| void setData(const vector<T> &src, const uint32_t target, const uint32_t usage) |
| { |
| setData(&src[0], (int)(src.size() * sizeof(T)), target, usage); |
| } |
| void setData(const void *src, int size, uint32_t target, uint32_t usage); |
| |
| // Functions that don't change the value returned by getApproxMemUsage(). |
| template <typename T> |
| void setSubData(const vector<T> &src, const int offsetElems, const int numElems, const uint32_t target) |
| { |
| setSubData(&src[offsetElems], offsetElems * (int)sizeof(T), numElems * (int)sizeof(T), target); |
| } |
| void setSubData(const void *src, int offsetBytes, int sizeBytes, uint32_t target) const; |
| void bind(const uint32_t target) const |
| { |
| glBindBuffer(target, m_bufferGL); |
| } |
| |
| int getApproxMemUsage(void) const |
| { |
| return m_dataSizeApprox; |
| } |
| template <typename T> |
| int getApproxMemUsageDiff(const vector<T> &src) const |
| { |
| return getApproxMemUsageDiff((int)(src.size() * sizeof(T))); |
| } |
| int getApproxMemUsageDiff(const int sizeBytes) const |
| { |
| return sizeBytes - getApproxMemUsage(); |
| } |
| |
| private: |
| Buffer(const Buffer &); // Not allowed. |
| Buffer &operator=(const Buffer &); // Not allowed. |
| |
| static uint32_t genBuffer(void) |
| { |
| uint32_t buf = 0; |
| glGenBuffers(1, &buf); |
| return buf; |
| } |
| |
| const uint32_t m_bufferGL; |
| int m_dataSizeApprox; |
| }; |
| |
| Buffer::Buffer(void) : m_bufferGL(genBuffer()), m_dataSizeApprox(0) |
| { |
| } |
| |
| Buffer::~Buffer(void) |
| { |
| glDeleteBuffers(1, &m_bufferGL); |
| } |
| |
| void Buffer::setData(const void *const src, const int size, const uint32_t target, const uint32_t usage) |
| { |
| bind(target); |
| glBufferData(target, size, src, usage); |
| glBindBuffer(target, 0); |
| |
| m_dataSizeApprox = size; |
| } |
| |
| void Buffer::setSubData(const void *const src, const int offsetBytes, const int sizeBytes, const uint32_t target) const |
| { |
| bind(target); |
| glBufferSubData(target, offsetBytes, sizeBytes, src); |
| glBindBuffer(target, 0); |
| } |
| |
| class Program |
| { |
| public: |
| Program(void); |
| ~Program(void); |
| |
| void setSources(const string &vertSource, const string &fragSource); |
| void build(TestLog &log); |
| void use(void) const |
| { |
| DE_ASSERT(m_isBuilt); |
| glUseProgram(m_programGL); |
| } |
| void setRandomUniforms(const vector<VarSpec> &uniforms, const string &shaderNameManglingSuffix, Random &rnd) const; |
| void setAttribute(const Buffer &attrBuf, int attrBufOffset, const VarSpec &attrSpec, |
| const string &shaderNameManglingSuffix) const; |
| void setAttributeClientMem(const void *attrData, const VarSpec &attrSpec, |
| const string &shaderNameManglingSuffix) const; |
| void disableAttributeArray(const VarSpec &attrSpec, const string &shaderNameManglingSuffix) const; |
| |
| private: |
| Program(const Program &); // Not allowed. |
| Program &operator=(const Program &); // Not allowed. |
| |
| string m_vertSource; |
| string m_fragSource; |
| |
| const uint32_t m_vertShaderGL; |
| const uint32_t m_fragShaderGL; |
| const uint32_t m_programGL; |
| bool m_hasSources; |
| bool m_isBuilt; |
| }; |
| |
| Program::Program(void) |
| : m_vertShaderGL(glCreateShader(GL_VERTEX_SHADER)) |
| , m_fragShaderGL(glCreateShader(GL_FRAGMENT_SHADER)) |
| , m_programGL(glCreateProgram()) |
| , m_hasSources(false) |
| , m_isBuilt(false) |
| { |
| glAttachShader(m_programGL, m_vertShaderGL); |
| glAttachShader(m_programGL, m_fragShaderGL); |
| } |
| |
| Program::~Program(void) |
| { |
| glDeleteShader(m_vertShaderGL); |
| glDeleteShader(m_fragShaderGL); |
| glDeleteProgram(m_programGL); |
| } |
| |
| void Program::setSources(const string &vertSource, const string &fragSource) |
| { |
| const char *const vertSourceCstr = vertSource.c_str(); |
| const char *const fragSourceCstr = fragSource.c_str(); |
| |
| m_vertSource = vertSource; |
| m_fragSource = fragSource; |
| |
| // \note In GLES2 api the source parameter type lacks one const. |
| glShaderSource(m_vertShaderGL, 1, (const char **)&vertSourceCstr, DE_NULL); |
| glShaderSource(m_fragShaderGL, 1, (const char **)&fragSourceCstr, DE_NULL); |
| |
| m_hasSources = true; |
| } |
| |
| void Program::build(TestLog &log) |
| { |
| DE_ASSERT(m_hasSources); |
| |
| const bool vertCompileOk = compileShader(m_vertShaderGL); |
| const bool fragCompileOk = compileShader(m_fragShaderGL); |
| const bool attemptLink = vertCompileOk && fragCompileOk; |
| const bool linkOk = attemptLink && linkProgram(m_programGL); |
| |
| if (!(vertCompileOk && fragCompileOk && linkOk)) |
| { |
| log << TestLog::ShaderProgram(linkOk, attemptLink ? getProgramInfoLog(m_programGL) : string("")) |
| << TestLog::Shader(QP_SHADER_TYPE_VERTEX, m_vertSource, vertCompileOk, getShaderInfoLog(m_vertShaderGL)) |
| << TestLog::Shader(QP_SHADER_TYPE_FRAGMENT, m_fragSource, fragCompileOk, getShaderInfoLog(m_fragShaderGL)) |
| << TestLog::EndShaderProgram; |
| |
| throw tcu::TestError("Program build failed"); |
| } |
| |
| m_isBuilt = true; |
| } |
| |
| void Program::setRandomUniforms(const vector<VarSpec> &uniforms, const string &shaderNameManglingSuffix, |
| Random &rnd) const |
| { |
| use(); |
| |
| for (int unifNdx = 0; unifNdx < (int)uniforms.size(); unifNdx++) |
| { |
| const VarSpec &spec = uniforms[unifNdx]; |
| const int typeScalarSize = glu::getDataTypeScalarSize(spec.type); |
| const int location = |
| glGetUniformLocation(m_programGL, mangleShaderNames(spec.name, shaderNameManglingSuffix).c_str()); |
| if (location < 0) |
| continue; |
| |
| if (glu::isDataTypeFloatOrVec(spec.type)) |
| { |
| float val[4]; |
| for (int i = 0; i < typeScalarSize; i++) |
| val[i] = rnd.getFloat(spec.minValue.f[i], spec.maxValue.f[i]); |
| |
| switch (spec.type) |
| { |
| case glu::TYPE_FLOAT: |
| glUniform1f(location, val[0]); |
| break; |
| case glu::TYPE_FLOAT_VEC2: |
| glUniform2f(location, val[0], val[1]); |
| break; |
| case glu::TYPE_FLOAT_VEC3: |
| glUniform3f(location, val[0], val[1], val[2]); |
| break; |
| case glu::TYPE_FLOAT_VEC4: |
| glUniform4f(location, val[0], val[1], val[2], val[3]); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else if (glu::isDataTypeMatrix(spec.type)) |
| { |
| float val[4 * 4]; |
| for (int i = 0; i < typeScalarSize; i++) |
| val[i] = rnd.getFloat(spec.minValue.f[i], spec.maxValue.f[i]); |
| |
| switch (spec.type) |
| { |
| case glu::TYPE_FLOAT_MAT2: |
| glUniformMatrix2fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT3: |
| glUniformMatrix3fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT4: |
| glUniformMatrix4fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT2X3: |
| glUniformMatrix2x3fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT2X4: |
| glUniformMatrix2x4fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT3X2: |
| glUniformMatrix3x2fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT3X4: |
| glUniformMatrix3x4fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT4X2: |
| glUniformMatrix4x2fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| case glu::TYPE_FLOAT_MAT4X3: |
| glUniformMatrix4x3fv(location, 1, GL_FALSE, &val[0]); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else if (glu::isDataTypeIntOrIVec(spec.type)) |
| { |
| int val[4]; |
| for (int i = 0; i < typeScalarSize; i++) |
| val[i] = rnd.getInt(spec.minValue.i[i], spec.maxValue.i[i]); |
| |
| switch (spec.type) |
| { |
| case glu::TYPE_INT: |
| glUniform1i(location, val[0]); |
| break; |
| case glu::TYPE_INT_VEC2: |
| glUniform2i(location, val[0], val[1]); |
| break; |
| case glu::TYPE_INT_VEC3: |
| glUniform3i(location, val[0], val[1], val[2]); |
| break; |
| case glu::TYPE_INT_VEC4: |
| glUniform4i(location, val[0], val[1], val[2], val[3]); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else if (glu::isDataTypeUintOrUVec(spec.type)) |
| { |
| uint32_t val[4]; |
| for (int i = 0; i < typeScalarSize; i++) |
| { |
| DE_ASSERT(spec.minValue.i[i] >= 0 && spec.maxValue.i[i] >= 0); |
| val[i] = (uint32_t)rnd.getInt(spec.minValue.i[i], spec.maxValue.i[i]); |
| } |
| |
| switch (spec.type) |
| { |
| case glu::TYPE_UINT: |
| glUniform1ui(location, val[0]); |
| break; |
| case glu::TYPE_UINT_VEC2: |
| glUniform2ui(location, val[0], val[1]); |
| break; |
| case glu::TYPE_UINT_VEC3: |
| glUniform3ui(location, val[0], val[1], val[2]); |
| break; |
| case glu::TYPE_UINT_VEC4: |
| glUniform4ui(location, val[0], val[1], val[2], val[3]); |
| break; |
| default: |
| DE_ASSERT(false); |
| } |
| } |
| else |
| DE_ASSERT(false); |
| } |
| } |
| |
| void Program::setAttribute(const Buffer &attrBuf, const int attrBufOffset, const VarSpec &attrSpec, |
| const string &shaderNameManglingSuffix) const |
| { |
| const int attrLoc = |
| glGetAttribLocation(m_programGL, mangleShaderNames(attrSpec.name, shaderNameManglingSuffix).c_str()); |
| |
| glEnableVertexAttribArray(attrLoc); |
| attrBuf.bind(GL_ARRAY_BUFFER); |
| |
| if (glu::isDataTypeFloatOrVec(attrSpec.type)) |
| glVertexAttribPointer(attrLoc, glu::getDataTypeScalarSize(attrSpec.type), GL_FLOAT, GL_FALSE, 0, |
| (GLvoid *)(intptr_t)attrBufOffset); |
| else |
| DE_ASSERT(false); |
| } |
| |
| void Program::setAttributeClientMem(const void *const attrData, const VarSpec &attrSpec, |
| const string &shaderNameManglingSuffix) const |
| { |
| const int attrLoc = |
| glGetAttribLocation(m_programGL, mangleShaderNames(attrSpec.name, shaderNameManglingSuffix).c_str()); |
| |
| glEnableVertexAttribArray(attrLoc); |
| glBindBuffer(GL_ARRAY_BUFFER, 0); |
| |
| if (glu::isDataTypeFloatOrVec(attrSpec.type)) |
| glVertexAttribPointer(attrLoc, glu::getDataTypeScalarSize(attrSpec.type), GL_FLOAT, GL_FALSE, 0, attrData); |
| else |
| DE_ASSERT(false); |
| } |
| |
| void Program::disableAttributeArray(const VarSpec &attrSpec, const string &shaderNameManglingSuffix) const |
| { |
| const int attrLoc = |
| glGetAttribLocation(m_programGL, mangleShaderNames(attrSpec.name, shaderNameManglingSuffix).c_str()); |
| |
| glDisableVertexAttribArray(attrLoc); |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Container class for managing GL objects |
| * |
| * GLObjectManager can be used for objects of class Program, Buffer or |
| * Texture. In the manager, each such object is associated with a name that |
| * is used to access it. |
| * |
| * In addition to the making, getting and removing functions, the manager |
| * supports marking objects as "garbage", meaning they're not yet |
| * destroyed, but can be later destroyed with removeRandomGarbage(). The |
| * idea is that if we want to stress test with high memory usage, we can |
| * continuously move objects to garbage after using them, and when a memory |
| * limit is reached, we can call removeGarbageUntilUnder(limit, rnd). This |
| * way we can approximately keep our memory usage at just under the wanted |
| * limit. |
| * |
| * The manager also supports querying the approximate amount of GL memory |
| * used by its objects. |
| * |
| * \note The memory usage related functions are not currently supported |
| * for Program objects. |
| *//*--------------------------------------------------------------------*/ |
| template <typename T> |
| class GLObjectManager |
| { |
| public: |
| void make(const string &name) |
| { |
| DE_ASSERT(!has(name)); |
| m_objects[name] = SharedPtr<T>(new T); |
| } |
| void make(const string &name, gls::TextureType texType) |
| { |
| DE_ASSERT(!has(name)); |
| m_objects[name] = SharedPtr<T>(new T(texType)); |
| } |
| bool has(const string &name) const |
| { |
| return m_objects.find(name) != m_objects.end(); |
| } |
| const T &get(const string &name) const; |
| T &get(const string &name) |
| { |
| return const_cast<T &>(((const GLObjectManager<T> *)this)->get(name)); |
| } |
| void remove(const string &name) |
| { |
| const int removed = (int)m_objects.erase(name); |
| DE_ASSERT(removed); |
| DE_UNREF(removed); |
| } |
| int computeApproxMemUsage(void) const; |
| void markAsGarbage(const string &name); |
| int removeRandomGarbage(Random &rnd); |
| void removeGarbageUntilUnder(int limit, Random &rnd); |
| |
| private: |
| static const char *objTypeName(void); |
| |
| map<string, SharedPtr<T>> m_objects; |
| vector<SharedPtr<T>> m_garbageObjects; |
| }; |
| |
| template <> |
| const char *GLObjectManager<Buffer>::objTypeName(void) |
| { |
| return "buffer"; |
| } |
| template <> |
| const char *GLObjectManager<Texture>::objTypeName(void) |
| { |
| return "texture"; |
| } |
| template <> |
| const char *GLObjectManager<Program>::objTypeName(void) |
| { |
| return "program"; |
| } |
| |
| template <typename T> |
| const T &GLObjectManager<T>::get(const string &name) const |
| { |
| const typename map<string, SharedPtr<T>>::const_iterator it = m_objects.find(name); |
| DE_ASSERT(it != m_objects.end()); |
| return *it->second; |
| } |
| |
| template <typename T> |
| int GLObjectManager<T>::computeApproxMemUsage(void) const |
| { |
| int result = 0; |
| |
| for (typename map<string, SharedPtr<T>>::const_iterator it = m_objects.begin(); it != m_objects.end(); ++it) |
| result += it->second->getApproxMemUsage(); |
| |
| for (typename vector<SharedPtr<T>>::const_iterator it = m_garbageObjects.begin(); it != m_garbageObjects.end(); |
| ++it) |
| result += (*it)->getApproxMemUsage(); |
| |
| return result; |
| } |
| |
| template <typename T> |
| void GLObjectManager<T>::markAsGarbage(const string &name) |
| { |
| const typename map<string, SharedPtr<T>>::iterator it = m_objects.find(name); |
| DE_ASSERT(it != m_objects.end()); |
| m_garbageObjects.push_back(it->second); |
| m_objects.erase(it); |
| } |
| |
| template <typename T> |
| int GLObjectManager<T>::removeRandomGarbage(Random &rnd) |
| { |
| if (m_garbageObjects.empty()) |
| return -1; |
| |
| const int removeNdx = rnd.getInt(0, (int)m_garbageObjects.size() - 1); |
| const int memoryFreed = m_garbageObjects[removeNdx]->getApproxMemUsage(); |
| m_garbageObjects.erase(m_garbageObjects.begin() + removeNdx); |
| return memoryFreed; |
| } |
| |
| template <typename T> |
| void GLObjectManager<T>::removeGarbageUntilUnder(const int limit, Random &rnd) |
| { |
| int memUsage = computeApproxMemUsage(); |
| |
| while (memUsage > limit) |
| { |
| const int memReleased = removeRandomGarbage(rnd); |
| if (memReleased < 0) |
| throw tcu::InternalError(string("") + "Given " + objTypeName() + |
| " memory usage limit exceeded, and no unneeded " + objTypeName() + |
| " resources available to release"); |
| memUsage -= memReleased; |
| DE_ASSERT(memUsage == computeApproxMemUsage()); |
| } |
| } |
| |
| } // namespace LongStressCaseInternal |
| |
| using namespace LongStressCaseInternal; |
| |
| static int generateRandomAttribData(vector<uint8_t> &attrDataBuf, int &dataSizeBytesDst, const VarSpec &attrSpec, |
| const int numVertices, Random &rnd) |
| { |
| const bool isFloat = glu::isDataTypeFloatOrVec(attrSpec.type); |
| const int numComponents = glu::getDataTypeScalarSize(attrSpec.type); |
| const int componentSize = (int)(isFloat ? sizeof(GLfloat) : sizeof(GLint)); |
| const int offsetInBuf = nextDivisible((int)attrDataBuf.size(), componentSize); // Round up for alignment. |
| |
| DE_STATIC_ASSERT(sizeof(GLint) == sizeof(int)); |
| DE_STATIC_ASSERT(sizeof(GLfloat) == sizeof(float)); |
| |
| dataSizeBytesDst = numComponents * componentSize * numVertices; |
| |
| attrDataBuf.resize(offsetInBuf + dataSizeBytesDst); |
| |
| if (isFloat) |
| { |
| float *const data = (float *)&attrDataBuf[offsetInBuf]; |
| |
| for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++) |
| for (int compNdx = 0; compNdx < numComponents; compNdx++) |
| data[vtxNdx * numComponents + compNdx] = |
| rnd.getFloat(attrSpec.minValue.f[compNdx], attrSpec.maxValue.f[compNdx]); |
| } |
| else |
| { |
| DE_ASSERT(glu::isDataTypeIntOrIVec(attrSpec.type)); |
| |
| int *const data = (int *)&attrDataBuf[offsetInBuf]; |
| |
| for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++) |
| for (int compNdx = 0; compNdx < numComponents; compNdx++) |
| data[vtxNdx * numComponents + compNdx] = |
| rnd.getInt(attrSpec.minValue.i[compNdx], attrSpec.maxValue.i[compNdx]); |
| } |
| |
| return offsetInBuf; |
| } |
| |
| static int generateRandomPositionAttribData(vector<uint8_t> &attrDataBuf, int &dataSizeBytesDst, |
| const VarSpec &attrSpec, const int numVertices, Random &rnd) |
| { |
| DE_ASSERT(glu::isDataTypeFloatOrVec(attrSpec.type)); |
| |
| const int numComponents = glu::getDataTypeScalarSize(attrSpec.type); |
| DE_ASSERT(numComponents >= 2); |
| const int offsetInBuf = generateRandomAttribData(attrDataBuf, dataSizeBytesDst, attrSpec, numVertices, rnd); |
| |
| if (numComponents > 2) |
| { |
| float *const data = (float *)&attrDataBuf[offsetInBuf]; |
| |
| for (int vtxNdx = 0; vtxNdx < numVertices; vtxNdx++) |
| data[vtxNdx * numComponents + 2] = -1.0f; |
| |
| for (int triNdx = 0; triNdx < numVertices - 2; triNdx++) |
| { |
| float *const vtxAComps = &data[(triNdx + 0) * numComponents]; |
| float *const vtxBComps = &data[(triNdx + 1) * numComponents]; |
| float *const vtxCComps = &data[(triNdx + 2) * numComponents]; |
| |
| const float triArea = triangleArea(Vec2(vtxAComps[0], vtxAComps[1]), Vec2(vtxBComps[0], vtxBComps[1]), |
| Vec2(vtxCComps[0], vtxCComps[1])); |
| const float t = triArea / (triArea + 1.0f); |
| const float z = (1.0f - t) * attrSpec.minValue.f[2] + t * attrSpec.maxValue.f[2]; |
| |
| vtxAComps[2] = de::max(vtxAComps[2], z); |
| vtxBComps[2] = de::max(vtxBComps[2], z); |
| vtxCComps[2] = de::max(vtxCComps[2], z); |
| } |
| } |
| |
| return offsetInBuf; |
| } |
| |
| static void generateAttribs(vector<uint8_t> &attrDataBuf, vector<int> &attrDataOffsets, vector<int> &attrDataSizes, |
| const vector<VarSpec> &attrSpecs, const string &posAttrName, const int numVertices, |
| Random &rnd) |
| { |
| attrDataBuf.clear(); |
| attrDataOffsets.clear(); |
| attrDataSizes.resize(attrSpecs.size()); |
| |
| for (int i = 0; i < (int)attrSpecs.size(); i++) |
| { |
| if (attrSpecs[i].name == posAttrName) |
| attrDataOffsets.push_back( |
| generateRandomPositionAttribData(attrDataBuf, attrDataSizes[i], attrSpecs[i], numVertices, rnd)); |
| else |
| attrDataOffsets.push_back( |
| generateRandomAttribData(attrDataBuf, attrDataSizes[i], attrSpecs[i], numVertices, rnd)); |
| } |
| } |
| |
| LongStressCase::LongStressCase(tcu::TestContext &testCtx, const glu::RenderContext &renderCtx, const char *const name, |
| const char *const desc, const int maxTexMemoryUsageBytes, |
| const int maxBufMemoryUsageBytes, const int numDrawCallsPerIteration, |
| const int numTrianglesPerDrawCall, const vector<ProgramContext> &programContexts, |
| const FeatureProbabilities &probabilities, const uint32_t indexBufferUsage, |
| const uint32_t attrBufferUsage, const int redundantBufferFactor, |
| const bool showDebugInfo) |
| : tcu::TestCase(testCtx, name, desc) |
| , m_renderCtx(renderCtx) |
| , m_maxTexMemoryUsageBytes(maxTexMemoryUsageBytes) |
| , m_maxBufMemoryUsageBytes(maxBufMemoryUsageBytes) |
| , m_numDrawCallsPerIteration(numDrawCallsPerIteration) |
| , m_numTrianglesPerDrawCall(numTrianglesPerDrawCall) |
| , m_numVerticesPerDrawCall(numTrianglesPerDrawCall + 2) // \note Triangle strips are used. |
| , m_programContexts(programContexts) |
| , m_probabilities(probabilities) |
| , m_indexBufferUsage(indexBufferUsage) |
| , m_attrBufferUsage(attrBufferUsage) |
| , m_redundantBufferFactor(redundantBufferFactor) |
| , m_showDebugInfo(showDebugInfo) |
| , m_numIterations(getNumIterations(testCtx, 5)) |
| , m_isGLES3(contextSupports(renderCtx.getType(), glu::ApiType::es(3, 0))) |
| , m_currentIteration(0) |
| , m_startTimeSeconds((uint64_t)-1) |
| , m_lastLogTime((uint64_t)-1) |
| , m_lastLogIteration(0) |
| , m_currentLogEntryNdx(0) |
| , m_rnd(deStringHash(getName()) ^ testCtx.getCommandLine().getBaseSeed()) |
| , m_programs(DE_NULL) |
| , m_buffers(DE_NULL) |
| , m_textures(DE_NULL) |
| , m_debugInfoRenderer(DE_NULL) |
| { |
| DE_ASSERT(m_numVerticesPerDrawCall <= |
| (int)std::numeric_limits<uint16_t>::max() + 1); // \note Vertices are referred to with 16-bit indices. |
| DE_ASSERT(m_redundantBufferFactor > 0); |
| } |
| |
| LongStressCase::~LongStressCase(void) |
| { |
| LongStressCase::deinit(); |
| } |
| |
| void LongStressCase::init(void) |
| { |
| // Generate unused texture data for each texture spec in m_programContexts. |
| |
| DE_ASSERT(!m_programContexts.empty()); |
| DE_ASSERT(m_programResources.empty()); |
| m_programResources.resize(m_programContexts.size()); |
| |
| for (int progCtxNdx = 0; progCtxNdx < (int)m_programContexts.size(); progCtxNdx++) |
| { |
| const ProgramContext &progCtx = m_programContexts[progCtxNdx]; |
| ProgramResources &progRes = m_programResources[progCtxNdx]; |
| |
| for (int texSpecNdx = 0; texSpecNdx < (int)progCtx.textureSpecs.size(); texSpecNdx++) |
| { |
| const TextureSpec &spec = progCtx.textureSpecs[texSpecNdx]; |
| const TextureFormat format = glu::mapGLTransferFormat(spec.format, spec.dataType); |
| |
| // If texture data with the same format has already been generated, re-use that (don't care much about contents). |
| |
| SharedPtr<TextureLevel> unusedTex; |
| |
| for (int prevProgCtxNdx = 0; prevProgCtxNdx < (int)m_programResources.size(); prevProgCtxNdx++) |
| { |
| const vector<SharedPtr<TextureLevel>> &prevProgCtxTextures = |
| m_programResources[prevProgCtxNdx].unusedTextures; |
| |
| for (int texNdx = 0; texNdx < (int)prevProgCtxTextures.size(); texNdx++) |
| { |
| if (prevProgCtxTextures[texNdx]->getFormat() == format) |
| { |
| unusedTex = prevProgCtxTextures[texNdx]; |
| break; |
| } |
| } |
| } |
| |
| if (!unusedTex) |
| unusedTex = SharedPtr<TextureLevel>(new TextureLevel(format)); |
| |
| if (unusedTex->getWidth() < spec.width || unusedTex->getHeight() < spec.height) |
| { |
| unusedTex->setSize(spec.width, spec.height); |
| tcu::fillWithComponentGradients(unusedTex->getAccess(), spec.minValue, spec.maxValue); |
| } |
| |
| progRes.unusedTextures.push_back(unusedTex); |
| } |
| } |
| |
| m_vertexIndices.clear(); |
| for (int i = 0; i < m_numVerticesPerDrawCall; i++) |
| m_vertexIndices.push_back((uint16_t)i); |
| m_rnd.shuffle(m_vertexIndices.begin(), m_vertexIndices.end()); |
| |
| DE_ASSERT(!m_programs && !m_buffers && !m_textures); |
| m_programs = new GLObjectManager<Program>; |
| m_buffers = new GLObjectManager<Buffer>; |
| m_textures = new GLObjectManager<Texture>; |
| |
| m_currentIteration = 0; |
| |
| { |
| TestLog &log = m_testCtx.getLog(); |
| |
| log << TestLog::Message |
| << "Number of iterations: " << (m_numIterations > 0 ? toString(m_numIterations) : "infinite") |
| << TestLog::EndMessage << TestLog::Message |
| << "Number of draw calls per iteration: " << m_numDrawCallsPerIteration << TestLog::EndMessage |
| << TestLog::Message << "Number of triangles per draw call: " << m_numTrianglesPerDrawCall |
| << TestLog::EndMessage << TestLog::Message << "Using triangle strips" << TestLog::EndMessage |
| << TestLog::Message |
| << "Approximate texture memory usage limit: " << de::floatToString((float)m_maxTexMemoryUsageBytes / Mi, 2) |
| << " MiB" << TestLog::EndMessage << TestLog::Message |
| << "Approximate buffer memory usage limit: " << de::floatToString((float)m_maxBufMemoryUsageBytes / Mi, 2) |
| << " MiB" << TestLog::EndMessage << TestLog::Message |
| << "Default vertex attribute data buffer usage parameter: " << glu::getUsageName(m_attrBufferUsage) |
| << TestLog::EndMessage << TestLog::Message |
| << "Default vertex index data buffer usage parameter: " << glu::getUsageName(m_indexBufferUsage) |
| << TestLog::EndMessage |
| |
| << TestLog::Section("ProbabilityParams", "Per-iteration probability parameters") << TestLog::Message |
| << "Program re-build: " << probabilityStr(m_probabilities.rebuildProgram) << TestLog::EndMessage |
| << TestLog::Message << "Texture re-upload: " << probabilityStr(m_probabilities.reuploadTexture) |
| << TestLog::EndMessage << TestLog::Message |
| << "Buffer re-upload: " << probabilityStr(m_probabilities.reuploadBuffer) << TestLog::EndMessage |
| << TestLog::Message << "Use glTexImage* instead of glTexSubImage* when uploading texture: " |
| << probabilityStr(m_probabilities.reuploadWithTexImage) << TestLog::EndMessage << TestLog::Message |
| << "Use glBufferData* instead of glBufferSubData* when uploading buffer: " |
| << probabilityStr(m_probabilities.reuploadWithBufferData) << TestLog::EndMessage << TestLog::Message |
| << "Delete texture after using it, even if could re-use it: " |
| << probabilityStr(m_probabilities.deleteTexture) << TestLog::EndMessage << TestLog::Message |
| << "Delete buffer after using it, even if could re-use it: " << probabilityStr(m_probabilities.deleteBuffer) |
| << TestLog::EndMessage << TestLog::Message |
| << "Don't re-use texture, and only delete if memory limit is hit: " |
| << probabilityStr(m_probabilities.wastefulTextureMemoryUsage) << TestLog::EndMessage << TestLog::Message |
| << "Don't re-use buffer, and only delete if memory limit is hit: " |
| << probabilityStr(m_probabilities.wastefulBufferMemoryUsage) << TestLog::EndMessage << TestLog::Message |
| << "Use client memory (instead of GL buffers) for vertex attribute data: " |
| << probabilityStr(m_probabilities.clientMemoryAttributeData) << TestLog::EndMessage << TestLog::Message |
| << "Use client memory (instead of GL buffers) for vertex index data: " |
| << probabilityStr(m_probabilities.clientMemoryIndexData) << TestLog::EndMessage << TestLog::Message |
| << "Use random target parameter when uploading buffer data: " |
| << probabilityStr(m_probabilities.randomBufferUploadTarget) << TestLog::EndMessage << TestLog::Message |
| << "Use random usage parameter when uploading buffer data: " |
| << probabilityStr(m_probabilities.randomBufferUsage) << TestLog::EndMessage << TestLog::Message |
| << "Use glDrawArrays instead of glDrawElements: " << probabilityStr(m_probabilities.useDrawArrays) |
| << TestLog::EndMessage << TestLog::Message |
| << "Use separate buffers for each attribute, instead of one array for all: " |
| << probabilityStr(m_probabilities.separateAttributeBuffers) << TestLog::EndMessage << TestLog::EndSection |
| << TestLog::Message << "Using " << m_programContexts.size() << " program(s)" << TestLog::EndMessage; |
| |
| bool anyProgramsFailed = false; |
| for (int progCtxNdx = 0; progCtxNdx < (int)m_programContexts.size(); progCtxNdx++) |
| { |
| const ProgramContext &progCtx = m_programContexts[progCtxNdx]; |
| glu::ShaderProgram prog(m_renderCtx, |
| glu::makeVtxFragSources(mangleShaderNames(progCtx.vertexSource, ""), |
| mangleShaderNames(progCtx.fragmentSource, ""))); |
| log << TestLog::Section("ShaderProgram" + toString(progCtxNdx), "Shader program " + toString(progCtxNdx)) |
| << prog << TestLog::EndSection; |
| if (!prog.isOk()) |
| anyProgramsFailed = true; |
| } |
| |
| if (anyProgramsFailed) |
| throw tcu::TestError("One or more shader programs failed to compile"); |
| } |
| |
| DE_ASSERT(!m_debugInfoRenderer); |
| if (m_showDebugInfo) |
| m_debugInfoRenderer = new DebugInfoRenderer(m_renderCtx); |
| } |
| |
| void LongStressCase::deinit(void) |
| { |
| m_programResources.clear(); |
| |
| delete m_programs; |
| m_programs = DE_NULL; |
| |
| delete m_buffers; |
| m_buffers = DE_NULL; |
| |
| delete m_textures; |
| m_textures = DE_NULL; |
| |
| delete m_debugInfoRenderer; |
| m_debugInfoRenderer = DE_NULL; |
| } |
| |
| LongStressCase::IterateResult LongStressCase::iterate(void) |
| { |
| TestLog &log = m_testCtx.getLog(); |
| const int renderWidth = m_renderCtx.getRenderTarget().getWidth(); |
| const int renderHeight = m_renderCtx.getRenderTarget().getHeight(); |
| const bool useClientMemoryIndexData = m_rnd.getFloat() < m_probabilities.clientMemoryIndexData; |
| const bool useDrawArrays = m_rnd.getFloat() < m_probabilities.useDrawArrays; |
| const bool separateAttributeBuffers = m_rnd.getFloat() < m_probabilities.separateAttributeBuffers; |
| const int progContextNdx = m_rnd.getInt(0, (int)m_programContexts.size() - 1); |
| const ProgramContext &programContext = m_programContexts[progContextNdx]; |
| ProgramResources &programResources = m_programResources[progContextNdx]; |
| const string programName = "prog" + toString(progContextNdx); |
| const string textureNamePrefix = "tex" + toString(progContextNdx) + "_"; |
| const string unitedAttrBufferNamePrefix = "attrBuf" + toString(progContextNdx) + "_"; |
| const string indexBufferName = "indexBuf" + toString(progContextNdx); |
| const string separateAttrBufNamePrefix = "attrBuf" + toString(progContextNdx) + "_"; |
| |
| if (m_currentIteration == 0) |
| m_lastLogTime = m_startTimeSeconds = deGetTime(); |
| |
| // Make or re-compile programs. |
| { |
| const bool hadProgram = m_programs->has(programName); |
| |
| if (!hadProgram) |
| m_programs->make(programName); |
| |
| Program &prog = m_programs->get(programName); |
| |
| if (!hadProgram || m_rnd.getFloat() < m_probabilities.rebuildProgram) |
| { |
| programResources.shaderNameManglingSuffix = |
| toString((uint16_t)deUint64Hash((uint64_t)m_currentIteration ^ deGetTime())); |
| |
| prog.setSources( |
| mangleShaderNames(programContext.vertexSource, programResources.shaderNameManglingSuffix), |
| mangleShaderNames(programContext.fragmentSource, programResources.shaderNameManglingSuffix)); |
| |
| prog.build(log); |
| } |
| |
| prog.use(); |
| } |
| |
| Program &program = m_programs->get(programName); |
| |
| // Make or re-upload textures. |
| |
| for (int texNdx = 0; texNdx < (int)programContext.textureSpecs.size(); texNdx++) |
| { |
| const string texName = textureNamePrefix + toString(texNdx); |
| const bool hadTexture = m_textures->has(texName); |
| const TextureSpec &spec = programContext.textureSpecs[texNdx]; |
| |
| if (!hadTexture) |
| m_textures->make(texName, spec.textureType); |
| |
| if (!hadTexture || m_rnd.getFloat() < m_probabilities.reuploadTexture) |
| { |
| Texture &texture = m_textures->get(texName); |
| |
| m_textures->removeGarbageUntilUnder( |
| m_maxTexMemoryUsageBytes - |
| texture.getApproxMemUsageDiff(spec.width, spec.height, spec.internalFormat, spec.useMipmap), |
| m_rnd); |
| |
| if (!hadTexture || m_rnd.getFloat() < m_probabilities.reuploadWithTexImage) |
| texture.setData(programResources.unusedTextures[texNdx]->getAccess(), spec.width, spec.height, |
| spec.internalFormat, spec.useMipmap); |
| else |
| texture.setSubData(programResources.unusedTextures[texNdx]->getAccess(), 0, 0, spec.width, spec.height); |
| |
| texture.toUnit(0); |
| texture.setWrap(spec.sWrap, spec.tWrap); |
| texture.setFilter(spec.minFilter, spec.magFilter); |
| } |
| } |
| |
| // Bind textures to units, in random order (because when multiple texture specs have same unit, we want to pick one randomly). |
| |
| { |
| vector<int> texSpecIndices(programContext.textureSpecs.size()); |
| for (int i = 0; i < (int)texSpecIndices.size(); i++) |
| texSpecIndices[i] = i; |
| m_rnd.shuffle(texSpecIndices.begin(), texSpecIndices.end()); |
| for (int i = 0; i < (int)texSpecIndices.size(); i++) |
| m_textures->get(textureNamePrefix + toString(texSpecIndices[i])) |
| .toUnit(programContext.textureSpecs[i].textureUnit); |
| } |
| |
| // Make or re-upload index buffer. |
| |
| if (!useDrawArrays) |
| { |
| m_rnd.shuffle(m_vertexIndices.begin(), m_vertexIndices.end()); |
| |
| if (!useClientMemoryIndexData) |
| { |
| const bool hadIndexBuffer = m_buffers->has(indexBufferName); |
| |
| if (!hadIndexBuffer) |
| m_buffers->make(indexBufferName); |
| |
| Buffer &indexBuf = m_buffers->get(indexBufferName); |
| |
| if (!hadIndexBuffer || m_rnd.getFloat() < m_probabilities.reuploadBuffer) |
| { |
| m_buffers->removeGarbageUntilUnder( |
| m_maxBufMemoryUsageBytes - indexBuf.getApproxMemUsageDiff(m_vertexIndices), m_rnd); |
| const uint32_t target = m_rnd.getFloat() < m_probabilities.randomBufferUploadTarget ? |
| randomBufferTarget(m_rnd, m_isGLES3) : |
| GL_ELEMENT_ARRAY_BUFFER; |
| |
| if (!hadIndexBuffer || m_rnd.getFloat() < m_probabilities.reuploadWithBufferData) |
| indexBuf.setData(m_vertexIndices, target, |
| m_rnd.getFloat() < m_probabilities.randomBufferUsage ? |
| randomBufferUsage(m_rnd, m_isGLES3) : |
| m_indexBufferUsage); |
| else |
| indexBuf.setSubData(m_vertexIndices, 0, m_numVerticesPerDrawCall, target); |
| } |
| } |
| } |
| |
| // Set vertex attributes. If not using client-memory data, make or re-upload attribute buffers. |
| |
| generateAttribs(programResources.attrDataBuf, programResources.attrDataOffsets, programResources.attrDataSizes, |
| programContext.attributes, programContext.positionAttrName, m_numVerticesPerDrawCall, m_rnd); |
| |
| if (!(m_rnd.getFloat() < m_probabilities.clientMemoryAttributeData)) |
| { |
| if (separateAttributeBuffers) |
| { |
| for (int attrNdx = 0; attrNdx < (int)programContext.attributes.size(); attrNdx++) |
| { |
| const int usedRedundantBufferNdx = m_rnd.getInt(0, m_redundantBufferFactor - 1); |
| |
| for (int redundantBufferNdx = 0; redundantBufferNdx < m_redundantBufferFactor; redundantBufferNdx++) |
| { |
| const string curAttrBufName = |
| separateAttrBufNamePrefix + toString(attrNdx) + "_" + toString(redundantBufferNdx); |
| const bool hadCurAttrBuffer = m_buffers->has(curAttrBufName); |
| |
| if (!hadCurAttrBuffer) |
| m_buffers->make(curAttrBufName); |
| |
| Buffer &curAttrBuf = m_buffers->get(curAttrBufName); |
| |
| if (!hadCurAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadBuffer) |
| { |
| m_buffers->removeGarbageUntilUnder( |
| m_maxBufMemoryUsageBytes - |
| curAttrBuf.getApproxMemUsageDiff(programResources.attrDataSizes[attrNdx]), |
| m_rnd); |
| const uint32_t target = m_rnd.getFloat() < m_probabilities.randomBufferUploadTarget ? |
| randomBufferTarget(m_rnd, m_isGLES3) : |
| GL_ARRAY_BUFFER; |
| |
| if (!hadCurAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadWithBufferData) |
| curAttrBuf.setData(&programResources.attrDataBuf[programResources.attrDataOffsets[attrNdx]], |
| programResources.attrDataSizes[attrNdx], target, |
| m_rnd.getFloat() < m_probabilities.randomBufferUsage ? |
| randomBufferUsage(m_rnd, m_isGLES3) : |
| m_attrBufferUsage); |
| else |
| curAttrBuf.setSubData( |
| &programResources.attrDataBuf[programResources.attrDataOffsets[attrNdx]], 0, |
| programResources.attrDataSizes[attrNdx], target); |
| } |
| |
| if (redundantBufferNdx == usedRedundantBufferNdx) |
| program.setAttribute(curAttrBuf, 0, programContext.attributes[attrNdx], |
| programResources.shaderNameManglingSuffix); |
| } |
| } |
| } |
| else |
| { |
| const int usedRedundantBufferNdx = m_rnd.getInt(0, m_redundantBufferFactor - 1); |
| |
| for (int redundantBufferNdx = 0; redundantBufferNdx < m_redundantBufferFactor; redundantBufferNdx++) |
| { |
| const string attrBufName = unitedAttrBufferNamePrefix + toString(redundantBufferNdx); |
| const bool hadAttrBuffer = m_buffers->has(attrBufName); |
| |
| if (!hadAttrBuffer) |
| m_buffers->make(attrBufName); |
| |
| Buffer &attrBuf = m_buffers->get(attrBufName); |
| |
| if (!hadAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadBuffer) |
| { |
| m_buffers->removeGarbageUntilUnder( |
| m_maxBufMemoryUsageBytes - attrBuf.getApproxMemUsageDiff(programResources.attrDataBuf), m_rnd); |
| const uint32_t target = m_rnd.getFloat() < m_probabilities.randomBufferUploadTarget ? |
| randomBufferTarget(m_rnd, m_isGLES3) : |
| GL_ARRAY_BUFFER; |
| |
| if (!hadAttrBuffer || m_rnd.getFloat() < m_probabilities.reuploadWithBufferData) |
| attrBuf.setData(programResources.attrDataBuf, target, |
| m_rnd.getFloat() < m_probabilities.randomBufferUsage ? |
| randomBufferUsage(m_rnd, m_isGLES3) : |
| m_attrBufferUsage); |
| else |
| attrBuf.setSubData(programResources.attrDataBuf, 0, (int)programResources.attrDataBuf.size(), |
| target); |
| } |
| |
| if (redundantBufferNdx == usedRedundantBufferNdx) |
| { |
| for (int i = 0; i < (int)programContext.attributes.size(); i++) |
| program.setAttribute(attrBuf, programResources.attrDataOffsets[i], programContext.attributes[i], |
| programResources.shaderNameManglingSuffix); |
| } |
| } |
| } |
| } |
| else |
| { |
| for (int i = 0; i < (int)programContext.attributes.size(); i++) |
| program.setAttributeClientMem(&programResources.attrDataBuf[programResources.attrDataOffsets[i]], |
| programContext.attributes[i], programResources.shaderNameManglingSuffix); |
| } |
| |
| // Draw. |
| |
| glViewport(0, 0, renderWidth, renderHeight); |
| |
| glClearDepthf(1.0f); |
| glClear(GL_DEPTH_BUFFER_BIT); |
| glEnable(GL_DEPTH_TEST); |
| |
| for (int i = 0; i < m_numDrawCallsPerIteration; i++) |
| { |
| program.use(); |
| program.setRandomUniforms(programContext.uniforms, programResources.shaderNameManglingSuffix, m_rnd); |
| |
| if (useDrawArrays) |
| glDrawArrays(GL_TRIANGLE_STRIP, 0, m_numVerticesPerDrawCall); |
| else |
| { |
| if (useClientMemoryIndexData) |
| { |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); |
| glDrawElements(GL_TRIANGLE_STRIP, m_numVerticesPerDrawCall, GL_UNSIGNED_SHORT, &m_vertexIndices[0]); |
| } |
| else |
| { |
| m_buffers->get(indexBufferName).bind(GL_ELEMENT_ARRAY_BUFFER); |
| glDrawElements(GL_TRIANGLE_STRIP, m_numVerticesPerDrawCall, GL_UNSIGNED_SHORT, DE_NULL); |
| } |
| } |
| } |
| |
| for (int i = 0; i < (int)programContext.attributes.size(); i++) |
| program.disableAttributeArray(programContext.attributes[i], programResources.shaderNameManglingSuffix); |
| |
| if (m_showDebugInfo) |
| m_debugInfoRenderer->drawInfo(deGetTime() - m_startTimeSeconds, m_textures->computeApproxMemUsage(), |
| m_maxTexMemoryUsageBytes, m_buffers->computeApproxMemUsage(), |
| m_maxBufMemoryUsageBytes, m_currentIteration); |
| |
| if (m_currentIteration > 0) |
| { |
| // Log if a certain amount of time has passed since last log entry (or if this is the last iteration). |
| |
| const uint64_t loggingIntervalSeconds = 10; |
| const uint64_t time = deGetTime(); |
| const uint64_t timeDiff = time - m_lastLogTime; |
| const int iterDiff = m_currentIteration - m_lastLogIteration; |
| |
| if (timeDiff >= loggingIntervalSeconds || m_currentIteration == m_numIterations - 1) |
| { |
| log << TestLog::Section("LogEntry" + toString(m_currentLogEntryNdx), |
| "Log entry " + toString(m_currentLogEntryNdx)) |
| << TestLog::Message << "Time elapsed: " << getTimeStr(time - m_startTimeSeconds) << TestLog::EndMessage |
| << TestLog::Message << "Frame number: " << m_currentIteration << TestLog::EndMessage << TestLog::Message |
| << "Time since last log entry: " << timeDiff << "s" << TestLog::EndMessage << TestLog::Message |
| << "Frames since last log entry: " << iterDiff << TestLog::EndMessage << TestLog::Message |
| << "Average frame time since last log entry: " |
| << de::floatToString((float)timeDiff / (float)iterDiff, 2) << "s" << TestLog::EndMessage |
| << TestLog::Message << "Approximate texture memory usage: " |
| << de::floatToString((float)m_textures->computeApproxMemUsage() / Mi, 2) << " MiB / " |
| << de::floatToString((float)m_maxTexMemoryUsageBytes / Mi, 2) << " MiB" << TestLog::EndMessage |
| << TestLog::Message << "Approximate buffer memory usage: " |
| << de::floatToString((float)m_buffers->computeApproxMemUsage() / Mi, 2) << " MiB / " |
| << de::floatToString((float)m_maxBufMemoryUsageBytes / Mi, 2) << " MiB" << TestLog::EndMessage |
| << TestLog::EndSection; |
| |
| m_lastLogTime = time; |
| m_lastLogIteration = m_currentIteration; |
| m_currentLogEntryNdx++; |
| } |
| } |
| |
| // Possibly remove or set-as-garbage some objects, depending on given probabilities. |
| |
| for (int texNdx = 0; texNdx < (int)programContext.textureSpecs.size(); texNdx++) |
| { |
| const string texName = textureNamePrefix + toString(texNdx); |
| if (m_rnd.getFloat() < m_probabilities.deleteTexture) |
| m_textures->remove(texName); |
| else if (m_rnd.getFloat() < m_probabilities.wastefulTextureMemoryUsage) |
| m_textures->markAsGarbage(texName); |
| } |
| |
| if (m_buffers->has(indexBufferName)) |
| { |
| if (m_rnd.getFloat() < m_probabilities.deleteBuffer) |
| m_buffers->remove(indexBufferName); |
| else if (m_rnd.getFloat() < m_probabilities.wastefulBufferMemoryUsage) |
| m_buffers->markAsGarbage(indexBufferName); |
| } |
| |
| if (separateAttributeBuffers) |
| { |
| for (int attrNdx = 0; attrNdx < (int)programContext.attributes.size(); attrNdx++) |
| { |
| const string curAttrBufNamePrefix = separateAttrBufNamePrefix + toString(attrNdx) + "_"; |
| |
| if (m_buffers->has(curAttrBufNamePrefix + "0")) |
| { |
| if (m_rnd.getFloat() < m_probabilities.deleteBuffer) |
| { |
| for (int i = 0; i < m_redundantBufferFactor; i++) |
| m_buffers->remove(curAttrBufNamePrefix + toString(i)); |
| } |
| else if (m_rnd.getFloat() < m_probabilities.wastefulBufferMemoryUsage) |
| { |
| for (int i = 0; i < m_redundantBufferFactor; i++) |
| m_buffers->markAsGarbage(curAttrBufNamePrefix + toString(i)); |
| } |
| } |
| } |
| } |
| else |
| { |
| if (m_buffers->has(unitedAttrBufferNamePrefix + "0")) |
| { |
| if (m_rnd.getFloat() < m_probabilities.deleteBuffer) |
| { |
| for (int i = 0; i < m_redundantBufferFactor; i++) |
| m_buffers->remove(unitedAttrBufferNamePrefix + toString(i)); |
| } |
| else if (m_rnd.getFloat() < m_probabilities.wastefulBufferMemoryUsage) |
| { |
| for (int i = 0; i < m_redundantBufferFactor; i++) |
| m_buffers->markAsGarbage(unitedAttrBufferNamePrefix + toString(i)); |
| } |
| } |
| } |
| |
| GLU_CHECK_MSG("End of LongStressCase::iterate()"); |
| |
| m_currentIteration++; |
| if (m_currentIteration == m_numIterations) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Passed"); |
| return STOP; |
| } |
| else |
| return CONTINUE; |
| } |
| |
| } // namespace gls |
| } // namespace deqp |