blob: 87a20f9a7ea8cba016e752f386bcaec2ab116cc7 [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2017 The Khronos Group Inc.
*
* 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 glcRobustnessTests.cpp
* \brief Conformance tests for the Robustness feature functionality.
*/ /*-------------------------------------------------------------------*/
#include "glcRobustnessTests.hpp"
#include "deSharedPtr.hpp"
#include "glcRobustBufferAccessBehaviorTests.hpp"
#include "gluContextInfo.hpp"
#include "gluPlatform.hpp"
#include "gluRenderContext.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuCommandLine.hpp"
#include "tcuTestLog.hpp"
#include <cstring>
using namespace glw;
using namespace glcts::RobustBufferAccessBehavior;
namespace glcts
{
namespace ResetNotificationStrategy
{
class RobustnessBase : public tcu::TestCase
{
public:
RobustnessBase(tcu::TestContext& testCtx, const char* name, const char* description, glu::ApiType apiType);
glu::RenderContext* createRobustContext(glu::ResetNotificationStrategy reset);
private:
glu::ApiType m_ApiType;
};
RobustnessBase::RobustnessBase(tcu::TestContext& testCtx, const char* name, const char* description,
glu::ApiType apiType)
: tcu::TestCase(testCtx, name, description), m_ApiType(apiType)
{
}
glu::RenderContext* RobustnessBase::createRobustContext(glu::ResetNotificationStrategy reset)
{
/* Create test context to verify if GL_KHR_robustness extension is available */
{
deqp::Context context(m_testCtx, glu::ContextType(m_ApiType));
if (!context.getContextInfo().isExtensionSupported("GL_KHR_robustness") &&
!contextSupports(context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
{
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED,
"GL_KHR_robustness extension not supported");
return NULL;
}
}
glu::RenderConfig renderCfg(glu::ContextType(m_ApiType, glu::CONTEXT_ROBUST));
const tcu::CommandLine& commandLine = m_testCtx.getCommandLine();
glu::parseRenderConfig(&renderCfg, commandLine);
if (commandLine.getSurfaceType() == tcu::SURFACETYPE_WINDOW)
renderCfg.resetNotificationStrategy = reset;
else
throw tcu::NotSupportedError("Test not supported in non-windowed context");
/* Try to create core/es robusness context */
return createRenderContext(m_testCtx.getPlatform(), commandLine, renderCfg);
}
class NoResetNotificationCase : public RobustnessBase
{
typedef glw::GLenum(GLW_APIENTRY* PFNGLGETGRAPHICSRESETSTATUS)();
public:
NoResetNotificationCase(tcu::TestContext& testCtx, const char* name, const char* description, glu::ApiType apiType)
: RobustnessBase(testCtx, name, description, apiType)
{
}
virtual IterateResult iterate(void)
{
glu::ResetNotificationStrategy strategy = glu::RESET_NOTIFICATION_STRATEGY_NO_RESET_NOTIFICATION;
de::SharedPtr<glu::RenderContext> robustContext(createRobustContext(strategy));
if (!robustContext.get())
return STOP;
glw::GLint reset = 0;
const glw::Functions& gl = robustContext->getFunctions();
gl.getIntegerv(GL_RESET_NOTIFICATION_STRATEGY, &reset);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv");
if (reset != GL_NO_RESET_NOTIFICATION)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! glGet returned wrong value [" << reset
<< ", expected " << GL_NO_RESET_NOTIFICATION << "]." << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
return STOP;
}
glw::GLint status = gl.getGraphicsResetStatus();
if (status != GL_NO_ERROR)
{
m_testCtx.getLog() << tcu::TestLog::Message
<< "Test failed! glGetGraphicsResetStatus returned wrong value [" << status
<< ", expected " << GL_NO_ERROR << "]." << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
return STOP;
}
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
};
class LoseContextOnResetCase : public RobustnessBase
{
public:
LoseContextOnResetCase(tcu::TestContext& testCtx, const char* name, const char* description, glu::ApiType apiType)
: RobustnessBase(testCtx, name, description, apiType)
{
}
virtual IterateResult iterate(void)
{
glu::ResetNotificationStrategy strategy = glu::RESET_NOTIFICATION_STRATEGY_LOSE_CONTEXT_ON_RESET;
de::SharedPtr<glu::RenderContext> robustContext(createRobustContext(strategy));
if (!robustContext.get())
return STOP;
glw::GLint reset = 0;
const glw::Functions& gl = robustContext->getFunctions();
gl.getIntegerv(GL_RESET_NOTIFICATION_STRATEGY, &reset);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv");
if (reset != GL_LOSE_CONTEXT_ON_RESET)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! glGet returned wrong value [" << reset
<< ", expected " << GL_LOSE_CONTEXT_ON_RESET << "]." << tcu::TestLog::EndMessage;
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
return STOP;
}
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
return STOP;
}
};
} // ResetNotificationStrategy namespace
namespace RobustBufferAccessBehavior
{
static deqp::Context* createContext(tcu::TestContext& testCtx, glu::ApiType apiType)
{
deqp::Context* context = new deqp::Context(testCtx, glu::ContextType(apiType));
if (!context)
{
testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Pointer to context is NULL.");
return DE_NULL;
}
if (!(contextSupports(context->getRenderContext().getType(), glu::ApiType::es(3, 2)) ||
(context->getContextInfo().isExtensionSupported("GL_KHR_robustness") &&
context->getContextInfo().isExtensionSupported("GL_KHR_robust_buffer_access_behavior"))))
{
testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
delete context;
return DE_NULL;
}
return context;
}
/** Implementation of test GetnUniformTest. Description follows:
*
* This test verifies if read uniform variables to the buffer with bufSize less than expected result with GL_INVALID_OPERATION error;
**/
class GetnUniformTest : public tcu::TestCase
{
public:
/* Public methods */
GetnUniformTest(tcu::TestContext& testCtx, glu::ApiType apiType);
virtual ~GetnUniformTest()
{
}
/* Public methods inherited from TestCase */
virtual tcu::TestNode::IterateResult iterate(void);
private:
/* Private methods */
std::string getComputeShader(bool glslES320);
bool verifyResult(const void* inputData, const void* resultData, int size, const char* method);
bool verifyError(glw::GLint error, glw::GLint expectedError, const char* method);
glu::ApiType m_ApiType;
};
/** Constructor
*
* @param context Test context
**/
GetnUniformTest::GetnUniformTest(tcu::TestContext& testCtx, glu::ApiType apiType)
: tcu::TestCase(testCtx, "getnuniform", "Verifies if read uniform variables to the buffer with bufSize less than "
"expected result with GL_INVALID_OPERATION")
, m_ApiType(apiType)
{
/* Nothing to be done here */
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult GetnUniformTest::iterate()
{
de::SharedPtr<deqp::Context> context(createContext(m_testCtx, m_ApiType));
if (!context.get())
return STOP;
glu::RenderContext& renderContext = context->getRenderContext();
const Functions& gl = renderContext.getFunctions();
const GLfloat input4f[] = { 1.0f, 5.4f, 3.14159f, 1.28f };
const GLint input3i[] = { 10, -20, -30 };
const GLuint input4ui[] = { 10, 20, 30, 40 };
/* Test result indicator */
bool test_result = true;
/* Iterate over all cases */
Program program(gl);
/* Compute Shader */
bool glslES320 = contextSupports(renderContext.getType(), glu::ApiType::es(3, 2));
const std::string& cs = getComputeShader(glslES320);
/* Shaders initialization */
program.Init(cs /* cs */, "" /* fs */, "" /* gs */, "" /* tcs */, "" /* tes */, "" /* vs */);
program.Use();
/* Initialize shader storage buffer */
GLuint buf;
gl.genBuffers(1, &buf);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenBuffers");
gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buf);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindBufferBase");
gl.bufferData(GL_SHADER_STORAGE_BUFFER, 16, DE_NULL, GL_STREAM_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "BufferData");
/* passing uniform values */
gl.programUniform4fv(program.m_id, 11, 1, input4f);
GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramUniform4fv");
gl.programUniform3iv(program.m_id, 12, 1, input3i);
GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramUniform3iv");
gl.programUniform4uiv(program.m_id, 13, 1, input4ui);
GLU_EXPECT_NO_ERROR(gl.getError(), "ProgramUniform4uiv");
gl.dispatchCompute(1, 1, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "DispatchCompute");
/* veryfing gfetnUniform error messages */
GLfloat result4f[4];
GLint result3i[3];
GLuint result4ui[4];
gl.getnUniformfv(program.m_id, 11, sizeof(GLfloat) * 4, result4f);
test_result = test_result &&
verifyResult((void*)input4f, (void*)result4f, sizeof(GLfloat) * 4, "getnUniformfv [false negative]");
test_result = test_result && verifyError(gl.getError(), GL_NO_ERROR, "getnUniformfv [false negative]");
gl.getnUniformfv(program.m_id, 11, sizeof(GLfloat) * 3, result4f);
test_result = test_result && verifyError(gl.getError(), GL_INVALID_OPERATION, "getnUniformfv [false positive]");
gl.getnUniformiv(program.m_id, 12, sizeof(GLint) * 3, result3i);
test_result = test_result &&
verifyResult((void*)input3i, (void*)result3i, sizeof(GLint) * 3, "getnUniformiv [false negative]");
test_result = test_result && verifyError(gl.getError(), GL_NO_ERROR, "getnUniformiv [false negative]");
gl.getnUniformiv(program.m_id, 12, sizeof(GLint) * 2, result3i);
test_result = test_result && verifyError(gl.getError(), GL_INVALID_OPERATION, "getnUniformiv [false positive]");
gl.getnUniformuiv(program.m_id, 13, sizeof(GLuint) * 4, result4ui);
test_result = test_result && verifyResult((void*)input4ui, (void*)result4ui, sizeof(GLuint) * 4,
"getnUniformuiv [false negative]");
test_result = test_result && verifyError(gl.getError(), GL_NO_ERROR, "getnUniformuiv [false negative]");
gl.getnUniformuiv(program.m_id, 13, sizeof(GLuint) * 3, result4ui);
test_result = test_result && verifyError(gl.getError(), GL_INVALID_OPERATION, "getnUniformuiv [false positive]");
/* Set result */
if (true == test_result)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
gl.deleteBuffers(1, &buf);
/* Done */
return tcu::TestNode::STOP;
}
std::string GetnUniformTest::getComputeShader(bool glslES320)
{
std::stringstream shader;
shader << "#version " << (glslES320 ? "320 es\n" : "450\n");
shader << "layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
"layout (location = 11) uniform vec4 inputf;\n"
"layout (location = 12) uniform ivec3 inputi;\n"
"layout (location = 13) uniform uvec4 inputu;\n"
"layout (std140, binding = 0) buffer ssbo {\n"
" float valuef;\n"
" int valuei;\n"
" uint valueu;\n"
"};\n"
"void main()\n"
"{\n"
" valuef = inputf.r + inputf.g + inputf.b + inputf.a;\n"
" valuei = inputi.r + inputi.g + inputi.b;\n"
" valueu = inputu.r + inputu.g + inputu.b + inputu.a;\n"
"}\n";
return shader.str();
}
bool GetnUniformTest::verifyResult(const void* inputData, const void* resultData, int size, const char* method)
{
int diff = memcmp(inputData, resultData, size);
if (diff != 0)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! " << method << " result is not as expected."
<< tcu::TestLog::EndMessage;
return false;
}
return true;
}
bool GetnUniformTest::verifyError(GLint error, GLint expectedError, const char* method)
{
if (error != expectedError)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! " << method << " throws unexpected error ["
<< error << "]." << tcu::TestLog::EndMessage;
return false;
}
return true;
}
/** Implementation of test ReadnPixelsTest. Description follows:
*
* This test verifies if read pixels to the buffer with bufSize less than expected result with GL_INVALID_OPERATION error;
**/
class ReadnPixelsTest : public tcu::TestCase
{
public:
/* Public methods */
ReadnPixelsTest(tcu::TestContext& testCtx, glu::ApiType apiType);
virtual ~ReadnPixelsTest()
{
}
/* Public methods inherited from TestCase */
virtual tcu::TestNode::IterateResult iterate(void);
private:
/* Private methods */
void cleanTexture(deqp::Context& context, glw::GLuint texture_id);
bool verifyResults(deqp::Context& context);
bool verifyError(glw::GLint error, glw::GLint expectedError, const char* method);
glu::ApiType m_ApiType;
};
/** Constructor
*
* @param context Test context
**/
ReadnPixelsTest::ReadnPixelsTest(tcu::TestContext& testCtx, glu::ApiType apiType)
: tcu::TestCase(testCtx, "readnpixels",
"Verifies if read pixels to the buffer with bufSize less than expected result "
"with GL_INVALID_OPERATION error")
, m_ApiType(apiType)
{
/* Nothing to be done here */
}
/** Execute test
*
* @return tcu::TestNode::STOP
**/
tcu::TestNode::IterateResult ReadnPixelsTest::iterate()
{
de::SharedPtr<deqp::Context> context(createContext(m_testCtx, m_ApiType));
if (!context.get())
return STOP;
static const GLuint elements[] = {
0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5, 0, 5, 6, 0, 6, 7, 0, 7, 8, 0, 8, 1,
};
static const GLfloat vertices[] = {
0.0f, 0.0f, 0.0f, 1.0f, /* 0 */
-1.0f, 0.0f, 0.0f, 1.0f, /* 1 */
-1.0f, 1.0f, 0.0f, 1.0f, /* 2 */
0.0f, 1.0f, 0.0f, 1.0f, /* 3 */
1.0f, 1.0f, 0.0f, 1.0f, /* 4 */
1.0f, 0.0f, 0.0f, 1.0f, /* 5 */
1.0f, -1.0f, 0.0f, 1.0f, /* 6 */
0.0f, -1.0f, 0.0f, 1.0f, /* 7 */
-1.0f, -1.0f, 0.0f, 1.0f, /* 8 */
};
bool glslES320 = contextSupports(context->getRenderContext().getType(), glu::ApiType::es(3, 2));
std::string fs("#version ");
fs += (glslES320 ? "320 es\n" : "450\n");
fs += "layout (location = 0) out lowp uvec4 out_fs_color;\n"
"\n"
"void main()\n"
"{\n"
" out_fs_color = uvec4(1, 0, 0, 1);\n"
"}\n"
"\n";
std::string vs("#version ");
vs += (glslES320 ? "320 es\n" : "450\n");
vs += "layout (location = 0) in vec4 in_vs_position;\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = in_vs_position;\n"
"}\n"
"\n";
static const GLuint height = 8;
static const GLuint width = 8;
static const GLuint n_vertices = 24;
/* GL entry points */
const Functions& gl = context->getRenderContext().getFunctions();
/* Test case objects */
Program program(gl);
Texture texture(gl);
Buffer elements_buffer(gl);
Buffer vertices_buffer(gl);
VertexArray vao(gl);
/* Vertex array initialization */
VertexArray::Generate(gl, vao.m_id);
VertexArray::Bind(gl, vao.m_id);
/* Texture initialization */
Texture::Generate(gl, texture.m_id);
Texture::Bind(gl, texture.m_id, GL_TEXTURE_2D);
Texture::Storage(gl, GL_TEXTURE_2D, 1, GL_R8UI, width, height, 0);
Texture::Bind(gl, 0, GL_TEXTURE_2D);
/* Framebuffer initialization */
GLuint fbo;
gl.genFramebuffers(1, &fbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers");
gl.bindFramebuffer(GL_FRAMEBUFFER, fbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer");
gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture.m_id, 0);
GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture2D");
/* Buffers initialization */
elements_buffer.InitData(GL_ELEMENT_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(elements), elements);
vertices_buffer.InitData(GL_ARRAY_BUFFER, GL_DYNAMIC_DRAW, sizeof(vertices), vertices);
/* Shaders initialization */
program.Init("" /* cs */, fs, "" /* gs */, "" /* tcs */, "" /* tes */, vs);
Program::Use(gl, program.m_id);
/* Vertex buffer initialization */
vertices_buffer.Bind();
gl.bindVertexBuffer(0 /* bindindex = location */, vertices_buffer.m_id, 0 /* offset */, 16 /* stride */);
gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 16, NULL);
gl.enableVertexAttribArray(0 /* location */);
/* Binding elements/indices buffer */
elements_buffer.Bind();
cleanTexture(*context, texture.m_id);
gl.drawElements(GL_TRIANGLES, n_vertices, GL_UNSIGNED_INT, 0 /* indices */);
GLU_EXPECT_NO_ERROR(gl.getError(), "DrawElements");
/* Set result */
if (verifyResults(*context))
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
/* Done */
return tcu::TestNode::STOP;
}
/** Fill texture with value 128
*
* @param texture_id Id of texture
**/
void ReadnPixelsTest::cleanTexture(deqp::Context& context, glw::GLuint texture_id)
{
static const GLuint height = 8;
static const GLuint width = 8;
const Functions& gl = context.getRenderContext().getFunctions();
GLubyte pixels[width * height];
for (GLuint i = 0; i < width * height; ++i)
{
pixels[i] = 64;
}
Texture::Bind(gl, texture_id, GL_TEXTURE_2D);
Texture::SubImage(gl, GL_TEXTURE_2D, 0 /* level */, 0 /* x */, 0 /* y */, 0 /* z */, width, height, 0 /* depth */,
GL_RED_INTEGER, GL_UNSIGNED_BYTE, pixels);
/* Unbind */
Texture::Bind(gl, 0, GL_TEXTURE_2D);
}
/** Verifies glReadnPixels results
*
* @return true when glReadnPixels , false otherwise
**/
bool ReadnPixelsTest::verifyResults(deqp::Context& context)
{
static const GLuint height = 8;
static const GLuint width = 8;
static const GLuint pixel_size = 4 * sizeof(GLuint);
const Functions& gl = context.getRenderContext().getFunctions();
//Valid buffer size test
const GLint bufSizeValid = width * height * pixel_size;
GLubyte pixelsValid[bufSizeValid];
gl.readnPixels(0, 0, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, bufSizeValid, pixelsValid);
GLU_EXPECT_NO_ERROR(gl.getError(), "ReadnPixels");
//Verify glReadnPixels result
for (unsigned int i = 0; i < width * height; ++i)
{
const size_t offset = i * pixel_size;
const GLuint value = *(GLuint*)(pixelsValid + offset);
if (value != 1)
{
context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid pixel value: " << value
<< ". Offset: " << offset << tcu::TestLog::EndMessage;
return false;
}
}
//Invalid buffer size test
const GLint bufSizeInvalid = width * height * pixel_size - 1;
GLubyte pixelsInvalid[bufSizeInvalid];
gl.readnPixels(0, 0, width, height, GL_RGBA_INTEGER, GL_UNSIGNED_INT, bufSizeInvalid, pixelsInvalid);
if (!verifyError(gl.getError(), GL_INVALID_OPERATION, "ReadnPixels [false positive]"))
return false;
return true;
}
/** Verify operation errors
*
* @param error OpenGL ES error code
* @param expectedError Expected error code
* @param method Method name marker
*
* @return true when error is as expected, false otherwise
**/
bool ReadnPixelsTest::verifyError(GLint error, GLint expectedError, const char* method)
{
if (error != expectedError)
{
m_testCtx.getLog() << tcu::TestLog::Message << "Test failed! " << method << " throws unexpected error ["
<< error << "]." << tcu::TestLog::EndMessage;
return false;
}
return true;
}
} // RobustBufferAccessBehavior namespace
RobustnessTests::RobustnessTests(tcu::TestContext& testCtx, glu::ApiType apiType)
: tcu::TestCaseGroup(testCtx, "robustness",
"Verifies API coverage and functionality of GL_KHR_robustness extension.")
, m_ApiType(apiType)
{
}
void RobustnessTests::init(void)
{
tcu::TestCaseGroup::init();
try
{
addChild(new ResetNotificationStrategy::NoResetNotificationCase(
m_testCtx, "no_reset_notification", "Verifies if NO_RESET_NOTIFICATION strategy works as expected.",
m_ApiType));
addChild(new ResetNotificationStrategy::LoseContextOnResetCase(
m_testCtx, "lose_context_on_reset", "Verifies if LOSE_CONTEXT_ON_RESET strategy works as expected.",
m_ApiType));
addChild(new RobustBufferAccessBehavior::GetnUniformTest(m_testCtx, m_ApiType));
addChild(new RobustBufferAccessBehavior::ReadnPixelsTest(m_testCtx, m_ApiType));
}
catch (...)
{
// Destroy context.
tcu::TestCaseGroup::deinit();
throw;
}
}
} // glcts namespace