/*------------------------------------------------------------------------- * 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 Memory object stress test *//*--------------------------------------------------------------------*/ #include "glsMemoryStressCase.hpp" #include "gluShaderProgram.hpp" #include "tcuTestLog.hpp" #include "tcuCommandLine.hpp" #include "deRandom.hpp" #include "deClock.h" #include "deString.h" #include "glw.h" #include #include using std::vector; using tcu::TestLog; namespace deqp { namespace gls { static const char *glErrorToString(uint32_t error) { switch (error) { case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; case 0: return ""; default: // \todo [mika] Handle uknown errors? DE_ASSERT(false); return NULL; } } static const float s_quadCoords[] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f}; static const GLubyte s_quadIndices[] = {0, 1, 2, 2, 3, 0}; class TextureRenderer { public: TextureRenderer(tcu::TestLog &log, glu::RenderContext &renderContext); ~TextureRenderer(void); void render(uint32_t texture); private: glu::ShaderProgram *m_program; glu::RenderContext &m_renderCtx; uint32_t m_coordBuffer; uint32_t m_indexBuffer; uint32_t m_vao; static const char *s_vertexShaderGLES2; static const char *s_fragmentShaderGLES2; static const char *s_vertexShaderGLES3; static const char *s_fragmentShaderGLES3; static const char *s_vertexShaderGL3; static const char *s_fragmentShaderGL3; }; const char *TextureRenderer::s_vertexShaderGLES2 = "attribute mediump vec2 a_coord;\n" "varying mediump vec2 v_texCoord;\n" "void main (void)\n" "{\n" "\tv_texCoord = 0.5 * (a_coord + vec2(1.0));\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char *TextureRenderer::s_fragmentShaderGLES2 = "varying mediump vec2 v_texCoord;\n" "uniform sampler2D u_texture;\n" "void main (void)\n" "{\n" "\tgl_FragColor = texture2D(u_texture, v_texCoord);\n" "}\n"; const char *TextureRenderer::s_vertexShaderGLES3 = "#version 300 es\n" "in mediump vec2 a_coord;\n" "out mediump vec2 v_texCoord;\n" "void main (void)\n" "{\n" "\tv_texCoord = 0.5 * (a_coord + vec2(1.0));\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char *TextureRenderer::s_fragmentShaderGLES3 = "#version 300 es\n" "in mediump vec2 v_texCoord;\n" "uniform sampler2D u_texture;\n" "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" "void main (void)\n" "{\n" "\tdEQP_FragColor = texture(u_texture, v_texCoord);\n" "}\n"; const char *TextureRenderer::s_vertexShaderGL3 = "#version 330\n" "in mediump vec2 a_coord;\n" "out mediump vec2 v_texCoord;\n" "void main (void)\n" "{\n" "\tv_texCoord = 0.5 * (a_coord + vec2(1.0));\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char *TextureRenderer::s_fragmentShaderGL3 = "#version 330\n" "in mediump vec2 v_texCoord;\n" "uniform sampler2D u_texture;\n" "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" "void main (void)\n" "{\n" "\tdEQP_FragColor = texture(u_texture, v_texCoord);\n" "}\n"; TextureRenderer::TextureRenderer(tcu::TestLog &log, glu::RenderContext &renderContext) : m_program(NULL) , m_renderCtx(renderContext) , m_coordBuffer(0) , m_indexBuffer(0) , m_vao(0) { const glu::ContextType ctxType = renderContext.getType(); if (glu::isGLSLVersionSupported(ctxType, glu::GLSL_VERSION_300_ES)) m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(s_vertexShaderGLES3, s_fragmentShaderGLES3)); else if (glu::isGLSLVersionSupported(ctxType, glu::GLSL_VERSION_100_ES)) m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(s_vertexShaderGLES2, s_fragmentShaderGLES2)); else if (glu::isGLSLVersionSupported(ctxType, glu::GLSL_VERSION_330)) m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(s_vertexShaderGL3, s_fragmentShaderGL3)); else DE_ASSERT(false); if (ctxType.getProfile() == glu::PROFILE_CORE) GLU_CHECK_CALL(glGenVertexArrays(1, &m_vao)); GLU_CHECK_CALL(glGenBuffers(1, &m_coordBuffer)); GLU_CHECK_CALL(glBindBuffer(GL_ARRAY_BUFFER, m_coordBuffer)); GLU_CHECK_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW)); GLU_CHECK_CALL(glGenBuffers(1, &m_indexBuffer)); GLU_CHECK_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer)); GLU_CHECK_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(s_quadIndices), s_quadIndices, GL_STATIC_DRAW)); if (!m_program->isOk()) { log << *m_program; TCU_CHECK_MSG(m_program->isOk(), "Shader compilation failed"); } } TextureRenderer::~TextureRenderer(void) { delete m_program; glDeleteBuffers(1, &m_coordBuffer); glDeleteBuffers(1, &m_indexBuffer); } void TextureRenderer::render(uint32_t texture) { uint32_t coordLoc = -1; uint32_t texLoc = -1; GLU_CHECK_CALL(glUseProgram(m_program->getProgram())); coordLoc = glGetAttribLocation(m_program->getProgram(), "a_coord"); GLU_CHECK(); TCU_CHECK(coordLoc != (uint32_t)-1); if (m_vao != 0) GLU_CHECK_CALL(glBindVertexArray(m_vao)); GLU_CHECK_CALL(glEnableVertexAttribArray(coordLoc)); GLU_CHECK_CALL(glBindBuffer(GL_ARRAY_BUFFER, m_coordBuffer)); GLU_CHECK_CALL(glVertexAttribPointer(coordLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL)); GLU_CHECK_CALL(glActiveTexture(GL_TEXTURE0)); GLU_CHECK_CALL(glBindTexture(GL_TEXTURE_2D, texture)); texLoc = glGetUniformLocation(m_program->getProgram(), "u_texture"); GLU_CHECK(); TCU_CHECK(texLoc != (uint32_t)-1); GLU_CHECK_CALL(glUniform1i(texLoc, 0)); GLU_CHECK_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer)); GLU_CHECK_CALL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL)); GLU_CHECK_CALL(glDisableVertexAttribArray(coordLoc)); if (m_vao != 0) GLU_CHECK_CALL(glBindVertexArray(0)); } class BufferRenderer { public: BufferRenderer(tcu::TestLog &log, glu::RenderContext &renderContext); ~BufferRenderer(void); void render(uint32_t buffer, int size); private: glu::ShaderProgram *m_program; glu::RenderContext &m_renderCtx; uint32_t m_coordBuffer; uint32_t m_indexBuffer; uint32_t m_vao; static const char *s_vertexShaderGLES2; static const char *s_fragmentShaderGLES2; static const char *s_vertexShaderGLES3; static const char *s_fragmentShaderGLES3; static const char *s_vertexShaderGL3; static const char *s_fragmentShaderGL3; }; const char *BufferRenderer::s_vertexShaderGLES2 = "attribute mediump vec2 a_coord;\n" "attribute mediump vec4 a_buffer;\n" "varying mediump vec4 v_buffer;\n" "void main (void)\n" "{\n" "\tv_buffer = a_buffer;\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char *BufferRenderer::s_fragmentShaderGLES2 = "varying mediump vec4 v_buffer;\n" "void main (void)\n" "{\n" "\tgl_FragColor = v_buffer;\n" "}\n"; const char *BufferRenderer::s_vertexShaderGLES3 = "#version 300 es\n" "in mediump vec2 a_coord;\n" "in mediump vec4 a_buffer;\n" "out mediump vec4 v_buffer;\n" "void main (void)\n" "{\n" "\tv_buffer = a_buffer;\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char *BufferRenderer::s_fragmentShaderGLES3 = "#version 300 es\n" "in mediump vec4 v_buffer;\n" "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" "void main (void)\n" "{\n" "\tdEQP_FragColor = v_buffer;\n" "}\n"; const char *BufferRenderer::s_vertexShaderGL3 = "#version 330\n" "in mediump vec2 a_coord;\n" "in mediump vec4 a_buffer;\n" "out mediump vec4 v_buffer;\n" "void main (void)\n" "{\n" "\tv_buffer = a_buffer;\n" "\tgl_Position = vec4(a_coord, 0.0, 1.0);\n" "}\n"; const char *BufferRenderer::s_fragmentShaderGL3 = "#version 330\n" "in mediump vec4 v_buffer;\n" "layout(location = 0) out mediump vec4 dEQP_FragColor;\n" "void main (void)\n" "{\n" "\tdEQP_FragColor = v_buffer;\n" "}\n"; BufferRenderer::BufferRenderer(tcu::TestLog &log, glu::RenderContext &renderContext) : m_program(NULL) , m_renderCtx(renderContext) , m_coordBuffer(0) , m_indexBuffer(0) , m_vao(0) { const glu::ContextType ctxType = renderContext.getType(); if (glu::isGLSLVersionSupported(ctxType, glu::GLSL_VERSION_300_ES)) m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(s_vertexShaderGLES3, s_fragmentShaderGLES3)); else if (glu::isGLSLVersionSupported(ctxType, glu::GLSL_VERSION_100_ES)) m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(s_vertexShaderGLES2, s_fragmentShaderGLES2)); else if (glu::isGLSLVersionSupported(ctxType, glu::GLSL_VERSION_330)) m_program = new glu::ShaderProgram(m_renderCtx, glu::makeVtxFragSources(s_vertexShaderGL3, s_fragmentShaderGL3)); else DE_ASSERT(false); if (ctxType.getProfile() == glu::PROFILE_CORE) GLU_CHECK_CALL(glGenVertexArrays(1, &m_vao)); GLU_CHECK_CALL(glGenBuffers(1, &m_coordBuffer)); GLU_CHECK_CALL(glBindBuffer(GL_ARRAY_BUFFER, m_coordBuffer)); GLU_CHECK_CALL(glBufferData(GL_ARRAY_BUFFER, sizeof(s_quadCoords), s_quadCoords, GL_STATIC_DRAW)); GLU_CHECK_CALL(glGenBuffers(1, &m_indexBuffer)); GLU_CHECK_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer)); GLU_CHECK_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(s_quadIndices), s_quadIndices, GL_STATIC_DRAW)); if (!m_program->isOk()) { log << *m_program; TCU_CHECK_MSG(m_program->isOk(), "Shader compilation failed"); } } BufferRenderer::~BufferRenderer(void) { delete m_program; glDeleteBuffers(1, &m_coordBuffer); glDeleteBuffers(1, &m_indexBuffer); } void BufferRenderer::render(uint32_t buffer, int size) { DE_UNREF(size); DE_ASSERT((size_t)size >= sizeof(GLubyte) * 4 * 6); GLU_CHECK_CALL(glUseProgram(m_program->getProgram())); uint32_t bufferLoc = glGetAttribLocation(m_program->getProgram(), "a_buffer"); TCU_CHECK(bufferLoc != (uint32_t)-1); uint32_t coordLoc = glGetAttribLocation(m_program->getProgram(), "a_coord"); TCU_CHECK(coordLoc != (uint32_t)-1); if (m_vao != 0) GLU_CHECK_CALL(glBindVertexArray(m_vao)); GLU_CHECK_CALL(glEnableVertexAttribArray(bufferLoc)); GLU_CHECK_CALL(glEnableVertexAttribArray(coordLoc)); GLU_CHECK_CALL(glBindBuffer(GL_ARRAY_BUFFER, m_coordBuffer)); GLU_CHECK_CALL(glVertexAttribPointer(coordLoc, 2, GL_FLOAT, GL_FALSE, 0, NULL)); GLU_CHECK_CALL(glBindBuffer(GL_ARRAY_BUFFER, buffer)); GLU_CHECK_CALL(glVertexAttribPointer(bufferLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, 0)); GLU_CHECK_CALL(glBindBuffer(GL_ARRAY_BUFFER, 0)); GLU_CHECK_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer)); GLU_CHECK_CALL(glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL)); GLU_CHECK_CALL(glDisableVertexAttribArray(bufferLoc)); GLU_CHECK_CALL(glDisableVertexAttribArray(coordLoc)); if (m_vao != 0) GLU_CHECK_CALL(glBindVertexArray(0)); } class MemObjectAllocator { public: enum Result { RESULT_GOT_BAD_ALLOC = 0, RESULT_GEN_TEXTURES_FAILED, RESULT_GEN_BUFFERS_FAILED, RESULT_BUFFER_DATA_FAILED, RESULT_BUFFER_SUB_DATA_FAILED, RESULT_TEXTURE_IMAGE_FAILED, RESULT_TEXTURE_SUB_IMAGE_FAILED, RESULT_BIND_TEXTURE_FAILED, RESULT_BIND_BUFFER_FAILED, RESULT_DELETE_TEXTURES_FAILED, RESULT_DELETE_BUFFERS_FAILED, RESULT_RENDER_FAILED, RESULT_LAST }; MemObjectAllocator(tcu::TestLog &log, glu::RenderContext &renderContext, MemObjectType objectTypes, const MemObjectConfig &config, int seed); ~MemObjectAllocator(void); bool allocUntilFailure(void); void clearObjects(void); Result getResult(void) const { return m_result; } uint32_t getGLError(void) const { return m_glError; } int getObjectCount(void) const { return m_objectCount; } uint32_t getBytes(void) const { return m_bytesRequired; } static const char *resultToString(Result result); private: void allocateTexture(de::Random &rnd); void allocateBuffer(de::Random &rnd); vector m_buffers; vector m_textures; int m_seed; int m_objectCount; uint32_t m_bytesRequired; MemObjectType m_objectTypes; Result m_result; MemObjectConfig m_config; uint32_t m_glError; vector m_unusedData; BufferRenderer m_bufferRenderer; TextureRenderer m_textureRenderer; }; MemObjectAllocator::MemObjectAllocator(tcu::TestLog &log, glu::RenderContext &renderContext, MemObjectType objectTypes, const MemObjectConfig &config, int seed) : m_seed(seed) , m_objectCount(0) , m_bytesRequired(0) , m_objectTypes(objectTypes) , m_result(RESULT_LAST) , m_config(config) , m_glError(0) , m_bufferRenderer(log, renderContext) , m_textureRenderer(log, renderContext) { DE_UNREF(renderContext); if (m_config.useUnusedData) { int unusedSize = deMax32(m_config.maxBufferSize, m_config.maxTextureSize * m_config.maxTextureSize * 4); m_unusedData = vector(unusedSize); } else if (m_config.write) m_unusedData = vector(128); } MemObjectAllocator::~MemObjectAllocator(void) { } bool MemObjectAllocator::allocUntilFailure(void) { de::Random rnd(m_seed); GLU_CHECK_MSG("Error in init"); try { const uint64_t timeoutUs = 10000000; // 10s uint64_t beginTimeUs = deGetMicroseconds(); uint64_t currentTimeUs; do { GLU_CHECK_MSG("Unkown Error"); switch (m_objectTypes) { case MEMOBJECTTYPE_TEXTURE: allocateTexture(rnd); break; case MEMOBJECTTYPE_BUFFER: allocateBuffer(rnd); break; default: { if (rnd.getBool()) allocateBuffer(rnd); else allocateTexture(rnd); break; } } if (m_result != RESULT_LAST) { glFinish(); return true; } currentTimeUs = deGetMicroseconds(); } while (currentTimeUs - beginTimeUs < timeoutUs); // Timeout if (currentTimeUs - beginTimeUs >= timeoutUs) return false; else return true; } catch (const std::bad_alloc &) { m_result = RESULT_GOT_BAD_ALLOC; return true; } } void MemObjectAllocator::clearObjects(void) { uint32_t error = 0; if (!m_textures.empty()) { glDeleteTextures((GLsizei)m_textures.size(), &(m_textures[0])); error = glGetError(); if (error != 0) { m_result = RESULT_DELETE_TEXTURES_FAILED; m_glError = error; } m_textures.clear(); } if (!m_buffers.empty()) { glDeleteBuffers((GLsizei)m_buffers.size(), &(m_buffers[0])); error = glGetError(); if (error != 0) { m_result = RESULT_DELETE_BUFFERS_FAILED; m_glError = error; } m_buffers.clear(); } } void MemObjectAllocator::allocateTexture(de::Random &rnd) { const int vectorBlockSize = 128; uint32_t tex = 0; uint32_t error = 0; int width = rnd.getInt(m_config.minTextureSize, m_config.maxTextureSize); int height = rnd.getInt(m_config.minTextureSize, m_config.maxTextureSize); glGenTextures(1, &tex); error = glGetError(); if (error != 0) { m_result = RESULT_GEN_TEXTURES_FAILED; m_glError = error; return; } if (m_textures.size() % vectorBlockSize == 0) m_textures.reserve(m_textures.size() + vectorBlockSize); m_textures.push_back(tex); glBindTexture(GL_TEXTURE_2D, tex); error = glGetError(); if (error != 0) { m_result = RESULT_BIND_TEXTURE_FAILED; m_glError = error; return; } if (m_config.useUnusedData) { DE_ASSERT((int)m_unusedData.size() >= width * height * 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &(m_unusedData[0])); } else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); error = glGetError(); if (error != 0) { m_result = RESULT_TEXTURE_IMAGE_FAILED; m_glError = error; return; } if (m_config.write) glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &(m_unusedData[0])); error = glGetError(); if (error != 0) { m_result = RESULT_TEXTURE_SUB_IMAGE_FAILED; m_glError = error; return; } if (m_config.use) { try { m_textureRenderer.render(tex); } catch (const glu::Error &err) { m_result = RESULT_RENDER_FAILED; m_glError = err.getError(); return; } catch (const glu::OutOfMemoryError &) { m_result = RESULT_RENDER_FAILED; m_glError = GL_OUT_OF_MEMORY; return; } } glBindTexture(GL_TEXTURE_2D, 0); error = glGetError(); if (error != 0) { m_result = RESULT_BIND_TEXTURE_FAILED; m_glError = error; return; } m_objectCount++; m_bytesRequired += width * height * 4; } void MemObjectAllocator::allocateBuffer(de::Random &rnd) { const int vectorBlockSize = 128; uint32_t buffer = 0; uint32_t error = 0; int size = rnd.getInt(m_config.minBufferSize, m_config.maxBufferSize); glGenBuffers(1, &buffer); error = glGetError(); if (error != 0) { m_result = RESULT_GEN_BUFFERS_FAILED; m_glError = error; return; } glBindBuffer(GL_ARRAY_BUFFER, buffer); error = glGetError(); if (error != 0) { m_result = RESULT_BIND_BUFFER_FAILED; m_glError = error; return; } if (m_buffers.size() % vectorBlockSize == 0) m_buffers.reserve(m_buffers.size() + vectorBlockSize); m_buffers.push_back(buffer); if (m_config.useUnusedData) { DE_ASSERT((int)m_unusedData.size() >= size); glBufferData(GL_ARRAY_BUFFER, size, &(m_unusedData[0]), GL_DYNAMIC_DRAW); } else glBufferData(GL_ARRAY_BUFFER, size, NULL, GL_DYNAMIC_DRAW); error = glGetError(); if (error != 0) { m_result = RESULT_BUFFER_DATA_FAILED; m_glError = error; return; } if (m_config.write) glBufferSubData(GL_ARRAY_BUFFER, 0, 1, &(m_unusedData[0])); error = glGetError(); if (error != 0) { m_result = RESULT_BUFFER_SUB_DATA_FAILED; m_glError = error; return; } if (m_config.use) { try { m_bufferRenderer.render(buffer, size); } catch (const glu::Error &err) { m_result = RESULT_RENDER_FAILED; m_glError = err.getError(); return; } catch (const glu::OutOfMemoryError &) { m_result = RESULT_RENDER_FAILED; m_glError = GL_OUT_OF_MEMORY; return; } } glBindBuffer(GL_ARRAY_BUFFER, 0); error = glGetError(); if (error != 0) { m_result = RESULT_BIND_BUFFER_FAILED; m_glError = error; return; } m_objectCount++; m_bytesRequired += size; } const char *MemObjectAllocator::resultToString(Result result) { switch (result) { case RESULT_GOT_BAD_ALLOC: return "Caught std::bad_alloc"; case RESULT_GEN_TEXTURES_FAILED: return "glGenTextures failed"; case RESULT_GEN_BUFFERS_FAILED: return "glGenBuffers failed"; case RESULT_BUFFER_DATA_FAILED: return "glBufferData failed"; case RESULT_BUFFER_SUB_DATA_FAILED: return "glBufferSubData failed"; case RESULT_TEXTURE_IMAGE_FAILED: return "glTexImage2D failed"; case RESULT_TEXTURE_SUB_IMAGE_FAILED: return "glTexSubImage2D failed"; case RESULT_BIND_TEXTURE_FAILED: return "glBindTexture failed"; case RESULT_BIND_BUFFER_FAILED: return "glBindBuffer failed"; case RESULT_DELETE_TEXTURES_FAILED: return "glDeleteTextures failed"; case RESULT_DELETE_BUFFERS_FAILED: return "glDeleteBuffers failed"; case RESULT_RENDER_FAILED: return "Rendering result failed"; default: DE_ASSERT(false); return NULL; } } MemoryStressCase::MemoryStressCase(tcu::TestContext &ctx, glu::RenderContext &renderContext, uint32_t objectTypes, int minTextureSize, int maxTextureSize, int minBufferSize, int maxBufferSize, bool write, bool use, bool useUnusedData, bool clearAfterOOM, const char *name, const char *desc) : tcu::TestCase(ctx, name, desc) , m_iteration(0) , m_iterationCount(5) , m_objectTypes((MemObjectType)objectTypes) , m_zeroAlloc(false) , m_clearAfterOOM(clearAfterOOM) , m_renderCtx(renderContext) { m_allocated.reserve(m_iterationCount); m_config.maxTextureSize = maxTextureSize; m_config.minTextureSize = minTextureSize; m_config.maxBufferSize = maxBufferSize; m_config.minBufferSize = minBufferSize; m_config.useUnusedData = useUnusedData; m_config.write = write; m_config.use = use; } MemoryStressCase::~MemoryStressCase(void) { } void MemoryStressCase::init(void) { if (!m_testCtx.getCommandLine().isOutOfMemoryTestEnabled()) { m_testCtx.getLog() << TestLog::Message << "Tests that exhaust memory are disabled, use --deqp-test-oom=enable command line option to enable." << TestLog::EndMessage; throw tcu::NotSupportedError("OOM tests disabled"); } } void MemoryStressCase::deinit(void) { TCU_CHECK(!m_zeroAlloc); } tcu::TestCase::IterateResult MemoryStressCase::iterate(void) { bool end = false; tcu::TestLog &log = m_testCtx.getLog(); MemObjectAllocator allocator(log, m_renderCtx, m_objectTypes, m_config, deStringHash(getName())); if (!allocator.allocUntilFailure()) { // Allocation timed out allocator.clearObjects(); log << TestLog::Message << "Timeout. Couldn't exhaust memory in timelimit. Allocated " << allocator.getObjectCount() << " objects." << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); return STOP; } // Try to cancel rendering operations if (m_clearAfterOOM) GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); allocator.clearObjects(); m_allocated.push_back(allocator.getObjectCount()); if (m_iteration != 0 && allocator.getObjectCount() == 0) m_zeroAlloc = true; log << TestLog::Message << "Got error when allocation object count: " << allocator.getObjectCount() << " bytes: " << allocator.getBytes() << TestLog::EndMessage; if ((allocator.getGLError() == 0) && (allocator.getResult() == MemObjectAllocator::RESULT_GOT_BAD_ALLOC)) { log << TestLog::Message << "std::bad_alloc" << TestLog::EndMessage; end = true; m_testCtx.setTestResult(QP_TEST_RESULT_RESOURCE_ERROR, "Memory allocation failed"); } else if (allocator.getGLError() != GL_OUT_OF_MEMORY) { log << TestLog::Message << "Invalid Error " << MemObjectAllocator::resultToString(allocator.getResult()) << " GLError: " << glErrorToString(allocator.getGLError()) << TestLog::EndMessage; end = true; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } if ((m_iteration + 1) == m_iterationCount) { int min = m_allocated[0]; int max = m_allocated[0]; float threshold = 50.0f; for (int allocNdx = 0; allocNdx < (int)m_allocated.size(); allocNdx++) { min = deMin32(m_allocated[allocNdx], min); max = deMax32(m_allocated[allocNdx], max); } if (min == 0 && max != 0) { log << TestLog::Message << "Allocation count zero" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } else { const float change = (float)(min - max) / (float)(max); if (change > threshold) { log << TestLog::Message << "Allocated objects max: " << max << ", min: " << min << ", difference: " << change << "% threshold: " << threshold << "%" << TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Allocation count variation"); } else m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } end = true; } GLU_CHECK_CALL(glFinish()); m_iteration++; if (end) return STOP; else return CONTINUE; } } // namespace gls } // namespace deqp