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