blob: 2eacfe438d55b005a05a00e118a286c33685d17a [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2015-2016 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
* \brief
*/ /*-------------------------------------------------------------------*/
/**
*/ /*!
* \file gl4cDirectStateAccessProgramPipelinesTests.cpp
* \brief Conformance tests for the Direct State Access feature functionality (Program Pipelines part).
*/ /*-----------------------------------------------------------------------------------------------------------*/
/* Includes. */
#include "gl4cDirectStateAccessTests.hpp"
#include "deSharedPtr.hpp"
#include "gluContextInfo.hpp"
#include "gluDefs.hpp"
#include "gluPixelTransfer.hpp"
#include "gluStrUtil.hpp"
#include "tcuFuzzyImageCompare.hpp"
#include "tcuImageCompare.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include "glw.h"
#include "glwFunctions.hpp"
namespace gl4cts
{
namespace DirectStateAccess
{
namespace ProgramPipelines
{
/******************************** Creation Test Implementation ********************************/
/** @brief Creation Test constructor.
*
* @param [in] context OpenGL context.
*/
CreationTest::CreationTest(deqp::Context& context)
: deqp::TestCase(context, "program_pipelines_creation", "Program Pipeline Objects Creation Test")
{
/* Intentionally left blank. */
}
/** @brief Iterate Creation Test cases.
*
* @return Iteration result.
*/
tcu::TestNode::IterateResult CreationTest::iterate()
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Get context setup. */
bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)));
bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access");
if ((!is_at_least_gl_45) && (!is_arb_direct_state_access))
{
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
return STOP;
}
/* Running tests. */
bool is_ok = true;
bool is_error = false;
/* Program pipeline objects */
static const glw::GLuint program_pipelines_count = 2;
glw::GLuint program_pipelines_legacy[program_pipelines_count] = {};
glw::GLuint program_pipelines_dsa[program_pipelines_count] = {};
try
{
/* Check legacy state creation. */
gl.genProgramPipelines(program_pipelines_count, program_pipelines_legacy);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenProgramPipelines have failed");
for (glw::GLuint i = 0; i < program_pipelines_count; ++i)
{
if (gl.isProgramPipeline(program_pipelines_legacy[i]))
{
is_ok = false;
/* Log. */
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "GenProgramPipelines has created default objects, but it should create only a names."
<< tcu::TestLog::EndMessage;
}
}
/* Check direct state creation. */
gl.createProgramPipelines(program_pipelines_count, program_pipelines_dsa);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgramPipelines have failed");
for (glw::GLuint i = 0; i < program_pipelines_count; ++i)
{
if (!gl.isProgramPipeline(program_pipelines_dsa[i]))
{
is_ok = false;
/* Log. */
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "CreateProgramPipelines has not created default objects."
<< tcu::TestLog::EndMessage;
}
}
}
catch (...)
{
is_ok = false;
is_error = true;
}
/* Cleanup. */
for (glw::GLuint i = 0; i < program_pipelines_count; ++i)
{
if (program_pipelines_legacy[i])
{
gl.deleteProgramPipelines(1, &program_pipelines_legacy[i]);
program_pipelines_legacy[i] = 0;
}
if (program_pipelines_dsa[i])
{
gl.deleteProgramPipelines(1, &program_pipelines_dsa[i]);
program_pipelines_dsa[i] = 0;
}
}
/* Result's setup. */
if (is_ok)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
if (is_error)
{
m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
}
return STOP;
}
/******************************** Defaults Test Implementation ********************************/
/** @brief Defaults Test constructor.
*
* @param [in] context OpenGL context.
*/
DefaultsTest::DefaultsTest(deqp::Context& context)
: deqp::TestCase(context, "program_pipelines_defaults", "Program Pipelines Defaults Test")
, m_program_pipeline_dsa(0)
{
/* Intentionally left blank. */
}
/** @brief Iterate Defaults Test cases.
*
* @return Iteration result.
*/
tcu::TestNode::IterateResult DefaultsTest::iterate()
{
/* Get context setup. */
bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)));
bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access");
if ((!is_at_least_gl_45) && (!is_arb_direct_state_access))
{
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
return STOP;
}
/* Running tests. */
bool is_ok = true;
bool is_error = false;
try
{
prepare();
is_ok &= testProgramPipelineParameter(GL_ACTIVE_PROGRAM, 0);
is_ok &= testProgramPipelineParameter(GL_VERTEX_SHADER, 0);
is_ok &= testProgramPipelineParameter(GL_GEOMETRY_SHADER, 0);
is_ok &= testProgramPipelineParameter(GL_FRAGMENT_SHADER, 0);
is_ok &= testProgramPipelineParameter(GL_COMPUTE_SHADER, 0);
is_ok &= testProgramPipelineParameter(GL_TESS_CONTROL_SHADER, 0);
is_ok &= testProgramPipelineParameter(GL_TESS_EVALUATION_SHADER, 0);
is_ok &= testProgramPipelineParameter(GL_VALIDATE_STATUS, 0);
is_ok &= testProgramPipelineParameter(GL_INFO_LOG_LENGTH, 0);
is_ok &= testProgramPipelineInfoLog(DE_NULL);
}
catch (...)
{
is_ok = false;
is_error = true;
}
/* Clean up. */
clean();
/* Result's setup. */
if (is_ok)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
if (is_error)
{
m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
}
return STOP;
}
/** @brief Create Program Pipeline Objects.
*
* @note The function may throw if unexpected error has occured.
*
* @return True if test succeeded, false otherwise.
*/
void DefaultsTest::prepare()
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Program Pipeline object creation */
gl.createProgramPipelines(1, &m_program_pipeline_dsa);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgramPipelines have failed");
}
/** @brief Test if Program Pipeline Parameter has value as expected.
*
* @note The function may throw if unexpected error has occured.
*
* @param [in] pname Parameter name enumeration. Must be one of
* GL_ACTIVE_PROGRAM, GL_VERTEX_SHADER, GL_TESS_CONTROL_SHADER,
* GL_TESS_EVALUATION_SHADER, GL_GEOMETRY_SHADER,
* GL_FRAGMENT_SHADER, GL_INFO_LOG_LENGTH.
* @param [in] expected_value Reference value to be compared.
*
* @return True if test succeeded, false otherwise.
*/
bool DefaultsTest::testProgramPipelineParameter(glw::GLenum pname, glw::GLint expected_value)
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Get data. */
glw::GLint value = -1;
gl.getProgramPipelineiv(m_program_pipeline_dsa, pname, &value);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineiv have failed");
if (-1 == value)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetProgramPipelineiv with parameter "
<< pname << " has not returned anything and error has not been generated."
<< tcu::TestLog::EndMessage;
return false;
}
else
{
if (expected_value != value)
{
m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetProgramPipelineiv with parameter "
<< pname << " has returned " << value << ", however " << expected_value
<< " was expected." << tcu::TestLog::EndMessage;
return false;
}
}
return true;
}
/** @brief Test if Program Pipeline Parameter has value as expected.
*
* @note The function may throw if unexpected error has occured.
*
* @param [in] expected_value Reference value to be compared.
*
* @return True if test succeeded, false otherwise.
*/
bool DefaultsTest::testProgramPipelineInfoLog(glw::GLchar* expected_value)
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Comparison limit. */
static const glw::GLsizei max_log_size = 4096;
/* Storage for data. */
glw::GLchar log[max_log_size] = { 0 };
/* Storage fetched length. */
glw::GLsizei log_size = 0;
/* Fetch. */
gl.getProgramPipelineInfoLog(m_program_pipeline_dsa, max_log_size, &log_size, log);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramPipelineInfoLog have failed");
/* Comparison. */
if (DE_NULL == expected_value)
{
if (0 != log_size)
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message
<< "glGetProgramPipelineInfoLog returned unexpectedly non-empty string: " << log << "."
<< tcu::TestLog::EndMessage;
return false;
}
return true;
}
if (0 != strcmp(log, expected_value))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "glGetProgramPipelineInfoLog returned string: " << log
<< ", but is not the same as expected: " << expected_value << "." << tcu::TestLog::EndMessage;
return false;
}
return true;
}
/** @brief Release GL objects.
*/
void DefaultsTest::clean()
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
if (m_program_pipeline_dsa)
{
gl.deleteProgramPipelines(1, &m_program_pipeline_dsa);
m_program_pipeline_dsa = 0;
}
}
/******************************** Errors Test Implementation ********************************/
/** @brief Errors Test constructor.
*
* @param [in] context OpenGL context.
*/
ErrorsTest::ErrorsTest(deqp::Context& context)
: deqp::TestCase(context, "program_pipelines_errors", "Program Pipeline Objects Errors Test")
{
/* Intentionally left blank. */
}
/** @brief Iterate Errors Test cases.
*
* @return Iteration result.
*/
tcu::TestNode::IterateResult ErrorsTest::iterate()
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Get context setup. */
bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)));
bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access");
if ((!is_at_least_gl_45) && (!is_arb_direct_state_access))
{
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
return STOP;
}
/* Running tests. */
bool is_ok = true;
bool is_error = false;
glw::GLuint program_pipeline_dsa = 0;
try
{
/* Check direct state creation. */
gl.createProgramPipelines(-1, &program_pipeline_dsa);
glw::GLenum error = GL_NO_ERROR;
if (GL_INVALID_VALUE != (error = gl.getError()))
{
is_ok = false;
/* Log. */
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "CreateProgramPipelines has not generated INVALID_VALUE error when callded "
"with negative number of objects to be created."
<< "Instead, " << glu::getErrorStr(error) << " error value was generated." << tcu::TestLog::EndMessage;
}
}
catch (...)
{
is_ok = false;
is_error = true;
}
/* Cleanup. */
if (program_pipeline_dsa)
{
gl.deleteProgramPipelines(1, &program_pipeline_dsa);
program_pipeline_dsa = 0;
}
/* Result's setup. */
if (is_ok)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
if (is_error)
{
m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
}
return STOP;
}
/******************************** Functional Test Implementation ********************************/
/** @brief Functional Test constructor.
*
* @param [in] context OpenGL context.
*/
FunctionalTest::FunctionalTest(deqp::Context& context)
: deqp::TestCase(context, "program_pipelines_functional", "Program Pipeline Objects Functional Test")
, m_fbo(0)
, m_rbo(0)
, m_vao(0)
, m_spo_v(0)
, m_spo_f(0)
, m_ppo(0)
{
/* Intentionally left blank. */
}
/** @brief Iterate Functional Test cases.
*
* @return Iteration result.
*/
tcu::TestNode::IterateResult FunctionalTest::iterate()
{
/* Get context setup. */
bool is_at_least_gl_45 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5)));
bool is_arb_direct_state_access = m_context.getContextInfo().isExtensionSupported("GL_ARB_direct_state_access");
if ((!is_at_least_gl_45) && (!is_arb_direct_state_access))
{
m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not Supported");
return STOP;
}
/* Running tests. */
bool is_ok = true;
bool is_error = false;
try
{
prepareFramebuffer();
prepareVertexArrayObject();
prepareShaderPrograms();
preparePipeline();
draw();
is_ok &= checkFramebufferContent();
}
catch (...)
{
is_ok = false;
is_error = true;
}
/* Clean-up. */
clean();
/* Result's setup. */
if (is_ok)
{
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
}
else
{
if (is_error)
{
m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
}
else
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
}
}
return STOP;
}
/** @brief Function prepares framebuffer with RGBA8 color attachment.
* Viewport is set up. Content of the framebuffer is cleared.
*
* @note The function may throw if unexpected error has occured.
*/
void FunctionalTest::prepareFramebuffer()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Prepare framebuffer. */
gl.genFramebuffers(1, &m_fbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed.");
gl.genRenderbuffers(1, &m_rbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed.");
gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed.");
gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed.");
gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1 /* x size */, 1 /* y size */);
GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed.");
gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed.");
if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
throw 0;
}
gl.viewport(0, 0, 1, 1);
GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed.");
/* Clear framebuffer's content. */
gl.clearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed.");
gl.clear(GL_COLOR_BUFFER_BIT);
GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed.");
}
/** @brief Function generate and bind empty vertex array object.
*
* @note The function may throw if unexpected error has occured.
*/
void FunctionalTest::prepareVertexArrayObject()
{
/* Shortcut for GL functionality */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.genVertexArrays(1, &m_vao);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed.");
gl.bindVertexArray(m_vao);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed.");
}
/** @brief Function builds test's GLSL shader program.
* If succeded, the program will be set to be used.
*
* @note The function may throw if unexpected error has occured.
*/
void FunctionalTest::prepareShaderPrograms()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Log size limit. */
static const glw::GLsizei max_log_size = 4096;
/* Sanity check. */
if (m_spo_v || m_spo_f)
{
throw 0;
}
/* Create Vertex Shader Program. */
m_spo_v = gl.createShaderProgramv(GL_VERTEX_SHADER, 1, &s_vertex_shader);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv call failed.");
glw::GLint status = GL_TRUE;
gl.validateProgram(m_spo_v);
GLU_EXPECT_NO_ERROR(gl.getError(), "glValidateProgram call failed.");
gl.getProgramiv(m_spo_v, GL_VALIDATE_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed.");
if (GL_FALSE == status)
{
/* Storage for data. */
glw::GLchar log[max_log_size] = { 0 };
/* Storage fetched length. */
glw::GLsizei log_size = 0;
gl.getProgramInfoLog(m_spo_v, max_log_size, &log_size, log);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed.");
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Vertex shader program building failed with log: " << log
<< tcu::TestLog::EndMessage;
throw 0;
}
m_spo_f = gl.createShaderProgramv(GL_FRAGMENT_SHADER, 1, &s_fragment_shader);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShaderProgramv call failed.");
status = GL_TRUE;
gl.validateProgram(m_spo_f);
GLU_EXPECT_NO_ERROR(gl.getError(), "glValidateProgram call failed.");
gl.getProgramiv(m_spo_f, GL_VALIDATE_STATUS, &status);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed.");
if (GL_FALSE == status)
{
/* Storage for data. */
glw::GLchar log[max_log_size] = { 0 };
/* Storage fetched length. */
glw::GLsizei log_size = 0;
gl.getProgramInfoLog(m_spo_f, max_log_size, &log_size, log);
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed.");
m_context.getTestContext().getLog() << tcu::TestLog::Message
<< "Fragment shader program building failed with log: " << log
<< tcu::TestLog::EndMessage;
throw 0;
}
}
/** @brief Function prepares program pipeline object.
*
* @note The function may throw if unexpected error has occured.
*/
void FunctionalTest::preparePipeline()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Sanity check. */
if (m_ppo)
{
throw 0;
}
/* Create, use and set up program pipeline. */
gl.createProgramPipelines(1, &m_ppo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgramPipelines call failed.");
gl.bindProgramPipeline(m_ppo);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindProgramPipeline call failed.");
gl.useProgramStages(m_ppo, GL_VERTEX_SHADER_BIT, m_spo_v);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages call failed.");
gl.useProgramStages(m_ppo, GL_FRAGMENT_SHADER_BIT, m_spo_f);
GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgramStages call failed.");
}
/** @brief Function draws a quad.
*
* @note The function may throw if unexpected error has occured.
*/
void FunctionalTest::draw()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays have failed");
}
/** @brief Check content of the framebuffer and compare it with expected data.
*
* @note The function may throw if unexpected error has occured.
*
* @return True if succeeded, false otherwise.
*/
bool FunctionalTest::checkFramebufferContent()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Fetch framebuffer data. */
glw::GLubyte pixel[4] = { 0 };
gl.readPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels have failed");
/* Comparison with expected values. */
if ((255 != pixel[0]) || (0 != pixel[1]) || (0 != pixel[2]) || (255 != pixel[3]))
{
m_context.getTestContext().getLog()
<< tcu::TestLog::Message << "Frameuffer content (" << (unsigned int)pixel[0] << ", "
<< (unsigned int)pixel[1] << ", " << (unsigned int)pixel[2] << ", " << (unsigned int)pixel[3]
<< ") is different than expected (255, 0, 0, 255)." << tcu::TestLog::EndMessage;
return false;
}
return true;
}
/** @brief Release all GL objects.
*/
void FunctionalTest::clean()
{
/* Shortcut for GL functionality. */
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
/* Release framebuffer. */
if (m_fbo)
{
gl.deleteFramebuffers(1, &m_fbo);
m_fbo = 0;
}
/* Release renderbuffer. */
if (m_rbo)
{
gl.deleteRenderbuffers(1, &m_rbo);
m_rbo = 0;
}
/* Release vertex array object. */
if (m_vao)
{
gl.deleteVertexArrays(1, &m_vao);
m_vao = 0;
}
/* Release shader programs. */
if (m_spo_v)
{
gl.deleteProgram(m_spo_v);
m_spo_v = 0;
}
if (m_spo_f)
{
gl.deleteProgram(m_spo_f);
m_spo_f = 0;
}
/* Release program pipelines. */
if (m_ppo)
{
gl.bindProgramPipeline(0);
gl.deleteProgramPipelines(1, &m_ppo);
m_ppo = 0;
}
}
/* Vertex shader source code. */
const glw::GLchar* FunctionalTest::s_vertex_shader = "#version 450\n"
"\n"
"out gl_PerVertex\n"
"{\n"
" vec4 gl_Position;\n"
" float gl_PointSize;\n"
" float gl_ClipDistance[];\n"
"};\n"
"\n"
"void main()\n"
"{\n"
" switch(gl_VertexID)\n"
" {\n"
" case 0:\n"
" gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
" break;\n"
" case 1:\n"
" gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n"
" break;\n"
" case 2:\n"
" gl_Position = vec4(-1.0,-1.0, 0.0, 1.0);\n"
" break;\n"
" case 3:\n"
" gl_Position = vec4( 1.0,-1.0, 0.0, 1.0);\n"
" break;\n"
" }\n"
"}\n";
/* Fragment shader source program. */
const glw::GLchar* FunctionalTest::s_fragment_shader = "#version 450\n"
"\n"
"out vec4 color;\n"
"\n"
"void main()\n"
"{\n"
" color = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
} /* ProgramPipelines namespace. */
} /* DirectStateAccess namespace. */
} /* gl4cts namespace. */