blob: 1f10fba3e97deefcae744b16539d69ed17153274 [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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(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 "<none>";
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<uint32_t> m_buffers;
vector<uint32_t> 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<uint8_t> 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<uint8_t>(unusedSize);
}
else if (m_config.write)
m_unusedData = vector<uint8_t>(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