/*------------------------------------------------------------------------- * 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 <vector> #include <iostream> using std::vector; using tcu::TestLog; namespace deqp { namespace gls { static const char* glErrorToString (deUint32 error) { switch (error) { case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; break; case GL_INVALID_ENUM: return "GL_INVALID_ENUM"; break; case GL_INVALID_FRAMEBUFFER_OPERATION: return "GL_INVALID_FRAMEBUFFER_OPERATION"; break; case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; break; case GL_INVALID_VALUE: return "GL_INVALID_VALUE"; break; case 0: return "<none>"; break; default: // \todo [mika] Handle uknown errors? DE_ASSERT(false); return NULL; break; } } 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 (deUint32 texture); private: glu::ShaderProgram* m_program; glu::RenderContext& m_renderCtx; deUint32 m_coordBuffer; deUint32 m_indexBuffer; deUint32 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 (deUint32 texture) { deUint32 coordLoc = -1; deUint32 texLoc = -1; GLU_CHECK_CALL(glUseProgram(m_program->getProgram())); coordLoc = glGetAttribLocation(m_program->getProgram(), "a_coord"); GLU_CHECK(); TCU_CHECK(coordLoc != (deUint32)-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 != (deUint32)-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 (deUint32 buffer, int size); private: glu::ShaderProgram* m_program; glu::RenderContext& m_renderCtx; deUint32 m_coordBuffer; deUint32 m_indexBuffer; deUint32 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 (deUint32 buffer, int size) { DE_UNREF(size); DE_ASSERT((size_t)size >= sizeof(GLubyte) * 4 * 6); GLU_CHECK_CALL(glUseProgram(m_program->getProgram())); deUint32 bufferLoc = glGetAttribLocation(m_program->getProgram(), "a_buffer"); TCU_CHECK(bufferLoc != (deUint32)-1); deUint32 coordLoc = glGetAttribLocation(m_program->getProgram(), "a_coord"); TCU_CHECK(coordLoc != (deUint32)-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; } deUint32 getGLError (void) const { return m_glError; } int getObjectCount (void) const { return m_objectCount; } deUint32 getBytes (void) const { return m_bytesRequired; } static const char* resultToString (Result result); private: void allocateTexture (de::Random& rnd); void allocateBuffer (de::Random& rnd); vector<deUint32> m_buffers; vector<deUint32> m_textures; int m_seed; int m_objectCount; deUint32 m_bytesRequired; MemObjectType m_objectTypes; Result m_result; MemObjectConfig m_config; deUint32 m_glError; vector<deUint8> m_dummyData; 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.useDummyData) { int dummySize = deMax32(m_config.maxBufferSize, m_config.maxTextureSize*m_config.maxTextureSize*4); m_dummyData = vector<deUint8>(dummySize); } else if (m_config.write) m_dummyData = vector<deUint8>(128); } MemObjectAllocator::~MemObjectAllocator (void) { } bool MemObjectAllocator::allocUntilFailure (void) { de::Random rnd(m_seed); GLU_CHECK_MSG("Error in init"); try { const deUint64 timeoutUs = 10000000; // 10s deUint64 beginTimeUs = deGetMicroseconds(); deUint64 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) { deUint32 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; deUint32 tex = 0; deUint32 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.useDummyData) { DE_ASSERT((int)m_dummyData.size() >= width*height*4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, &(m_dummyData[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_dummyData[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; deUint32 buffer = 0; deUint32 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.useDummyData) { DE_ASSERT((int)m_dummyData.size() >= size); glBufferData(GL_ARRAY_BUFFER, size, &(m_dummyData[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_dummyData[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"; break; case RESULT_GEN_TEXTURES_FAILED: return "glGenTextures failed"; break; case RESULT_GEN_BUFFERS_FAILED: return "glGenBuffers failed"; break; case RESULT_BUFFER_DATA_FAILED: return "glBufferData failed"; break; case RESULT_BUFFER_SUB_DATA_FAILED: return "glBufferSubData failed"; break; case RESULT_TEXTURE_IMAGE_FAILED: return "glTexImage2D failed"; break; case RESULT_TEXTURE_SUB_IMAGE_FAILED: return "glTexSubImage2D failed"; break; case RESULT_BIND_TEXTURE_FAILED: return "glBindTexture failed"; break; case RESULT_BIND_BUFFER_FAILED: return "glBindBuffer failed"; break; case RESULT_DELETE_TEXTURES_FAILED: return "glDeleteTextures failed"; break; case RESULT_DELETE_BUFFERS_FAILED: return "glDeleteBuffers failed"; break; case RESULT_RENDER_FAILED: return "Rendering result failed"; break; default: DE_ASSERT(false); return NULL; } } MemoryStressCase::MemoryStressCase (tcu::TestContext& ctx, glu::RenderContext& renderContext, deUint32 objectTypes, int minTextureSize, int maxTextureSize, int minBufferSize, int maxBufferSize, bool write, bool use, bool useDummyData, 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.useDummyData = useDummyData; 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 = (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; } } // gls } // deqp