/*-------------------------------------------------------------------------
* 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