/*------------------------------------------------------------------------- * 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 gl3cTransformFeedback.cpp * \brief Transform Feedback Test Suite Implementation */ /*-------------------------------------------------------------------*/ /* Includes. */ #include "gl3cTransformFeedbackTests.hpp" #include "gluContextInfo.hpp" #include "gluDefs.hpp" #include "gluRenderContext.hpp" #include "gluStrUtil.hpp" #include "tcuTestLog.hpp" #include #include #include #include #include /* Stringify macro. */ #define _STR(s) STR(s) #define STR(s) #s /* Unused attribute / variable MACRO. Some methods of clesses' heirs do not need all function parameters. This triggers warnings on GCC platform. This macro will silence them. */ #ifdef __GNUC__ #define UNUSED __attribute__((__unused__)) #else #define UNUSED #endif gl3cts::TransformFeedback::Tests::Tests(deqp::Context& context) : TestCaseGroup(context, "transform_feedback", "Transform Feedback Test Suite") { addChild(new TransformFeedback::APIErrors(m_context)); addChild(new TransformFeedback::LinkingErrors(m_context)); addChild(new TransformFeedback::Limits(m_context)); addChild(new TransformFeedback::CaptureVertexInterleaved(m_context, "capture_vertex_interleaved_test", "Transform Feedback Capture Vertex Interleaved Test")); addChild(new TransformFeedback::CaptureGeometryInterleaved(m_context, "capture_geometry_interleaved_test", "Transform Feedback Capture Geometry Interleaved Test")); addChild(new TransformFeedback::CaptureVertexSeparate(m_context, "capture_vertex_separate_test", "Transform Feedback Capture Vertex Separate Test")); addChild(new TransformFeedback::CaptureGeometrySeparate(m_context, "capture_geometry_separate_test", "Transform Feedback Capture Geometry Separate Test")); addChild(new TransformFeedback::CheckGetXFBVarying(m_context, "get_xfb_varying", "Transform Feedback Varying Getters Test")); addChild(new TransformFeedback::QueryVertexInterleaved(m_context, "query_vertex_interleaved_test", "Transform Feedback Query Vertex Interleaved Test")); addChild(new TransformFeedback::QueryGeometryInterleaved(m_context, "query_geometry_interleaved_test", "Transform Feedback Query Geometry Interleaved Test")); addChild(new TransformFeedback::QueryVertexSeparate(m_context, "query_vertex_separate_test", "Transform Feedback Query Vertex Separate Test")); addChild(new TransformFeedback::QueryGeometrySeparate(m_context, "query_geometry_separate_test", "Transform Feedback Query Geometry Separate Test")); addChild(new TransformFeedback::DiscardVertex(m_context, "discard_vertex_test", "Transform Feedback Discard Vertex Test")); addChild(new TransformFeedback::DiscardGeometry(m_context, "discard_geometry_test", "Transform Feedback Discard Geometry Test")); addChild(new TransformFeedback::DrawXFB(m_context, "draw_xfb_test", "Transform Feedback Draw Test")); addChild(new TransformFeedback::DrawXFBFeedback(m_context, "draw_xfb_feedbackk_test", "Transform Feedback Draw Feedback Test")); addChild( new TransformFeedback::DrawXFBStream(m_context, "draw_xfb_stream_test", "Transform Feedback Draw Stream Test")); addChild(new TransformFeedback::CaptureSpecialInterleaved(m_context, "capture_special_interleaved_test", "Transform Feedback Capture Special Test")); addChild(new TransformFeedback::DrawXFBInstanced(m_context, "draw_xfb_instanced_test", "Transform Feedback Draw Instanced Test")); addChild(new TransformFeedback::DrawXFBStreamInstanced(m_context, "draw_xfb_stream_instanced_test", "Transform Feedback Draw Stream Instanced Test")); } gl3cts::TransformFeedback::Tests::~Tests(void) { } void gl3cts::TransformFeedback::Tests::init(void) { } gl3cts::TransformFeedback::APIErrors::APIErrors(deqp::Context& context) : deqp::TestCase(context, "api_errors_test", "Transform Feedback API Errors Test") , m_context(context) , m_buffer_0(0) , m_buffer_1(0) , m_vertex_array_object(0) , m_transform_feedback_object_0(0) , m_transform_feedback_object_1(0) , m_query_object(0) , m_program_id_with_input_output(0) , m_program_id_with_output(0) , m_program_id_without_output(0) , m_program_id_with_geometry_shader(0) , m_program_id_with_tessellation_shaders(0) , m_glBindBufferOffsetEXT(DE_NULL) , m_glGetIntegerIndexedvEXT(DE_NULL) , m_glGetBooleanIndexedvEXT(DE_NULL) { } gl3cts::TransformFeedback::APIErrors::~APIErrors(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::APIErrors::iterate(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initializations. */ bool is_at_least_gl_30 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 0))); bool is_at_least_gl_33 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 3))); bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_at_least_gl_42 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 2))); bool is_ext_tf_1 = m_context.getContextInfo().isExtensionSupported("GL_EXT_transform_feedback"); bool is_arb_tf_2 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback2"); bool is_arb_tf_3 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback3"); bool is_arb_tf_instanced = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback_instanced"); if (is_ext_tf_1) { /* Extension query. */ m_glBindBufferOffsetEXT = (BindBufferOffsetEXT_ProcAddress)m_context.getRenderContext().getProcAddress("glBindBufferOffsetEXT"); m_glGetIntegerIndexedvEXT = (GetIntegerIndexedvEXT_ProcAddress)m_context.getRenderContext().getProcAddress("glGetIntegerIndexedvEXT"); m_glGetBooleanIndexedvEXT = (GetBooleanIndexedvEXT_ProcAddress)m_context.getRenderContext().getProcAddress("glGetBooleanIndexedvEXT"); } if (is_at_least_gl_40 || is_arb_tf_2) { /* Create transform feedback objects. */ gl.genTransformFeedbacks(1, &m_transform_feedback_object_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); } if (is_at_least_gl_40 || is_arb_tf_3) { /* Create query object. */ gl.genQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries call failed."); } if (is_at_least_gl_42 || is_arb_tf_instanced) { /* Create transform feedback objects. */ gl.genTransformFeedbacks(1, &m_transform_feedback_object_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); } /* Default result. */ bool is_ok = true; bool test_error = false; /* Entities setup. */ try { /* VAO setup. */ gl.genVertexArrays(1, &m_vertex_array_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vertex_array_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Buffer setup. */ gl.genBuffers(1, &m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 16, NULL, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.genBuffers(1, &m_buffer_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_buffer_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, m_buffer_1_size, m_buffer_1_data, GL_STATIC_DRAW); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); /* Programs setup. */ m_program_id_with_input_output = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_with_input_output, s_fragment_shader, &m_varying_name, 1, GL_INTERLEAVED_ATTRIBS); m_program_id_with_output = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_with_output, s_fragment_shader, &m_varying_name, 1, GL_INTERLEAVED_ATTRIBS, true); m_program_id_without_output = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_without_output, s_fragment_shader, NULL, 0, GL_SEPARATE_ATTRIBS); is_ok = is_ok && m_program_id_with_input_output && m_program_id_with_output && m_program_id_without_output; if (is_at_least_gl_33) { m_program_id_with_geometry_shader = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), m_geometry_shader, NULL, NULL, s_vertex_shader_without_output, s_fragment_shader, &m_varying_name, 1, GL_INTERLEAVED_ATTRIBS); is_ok = is_ok && m_program_id_with_geometry_shader; } if (is_at_least_gl_40) { m_program_id_with_tessellation_shaders = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, m_tessellation_control_shader, m_tessellation_evaluation_shader, s_vertex_shader_without_output, s_fragment_shader, &m_varying_name, 1, GL_INTERLEAVED_ATTRIBS); is_ok = is_ok && m_program_id_with_tessellation_shaders; } } catch (...) { is_ok = false; test_error = true; } /* Iterating tests. */ try { if (is_at_least_gl_30 || is_ext_tf_1) { is_ok = is_ok && testExtension1(); } if (is_at_least_gl_40 || is_arb_tf_2) { is_ok = is_ok && testExtension2(); } if (is_at_least_gl_40 || is_arb_tf_3) { is_ok = is_ok && testExtension3(); } if (is_at_least_gl_42 || is_arb_tf_instanced) { is_ok = is_ok && testInstanced(); } } catch (...) { is_ok = false; test_error = true; } /* Deinitialization. */ if (m_vertex_array_object) { gl.deleteVertexArrays(1, &m_vertex_array_object); m_vertex_array_object = 0; } if (m_buffer_0) { gl.deleteBuffers(1, &m_buffer_0); // silently unbinds m_buffer_0 = 0; } if (m_buffer_1) { gl.deleteBuffers(1, &m_buffer_1); // silently unbinds m_buffer_1 = 0; } if (m_transform_feedback_object_0) { gl.deleteTransformFeedbacks(1, &m_transform_feedback_object_0); m_transform_feedback_object_0 = 0; } if (m_transform_feedback_object_1) { gl.deleteTransformFeedbacks(1, &m_transform_feedback_object_1); m_transform_feedback_object_1 = 0; } if (m_query_object) { gl.deleteQueries(1, &m_query_object); m_query_object = 0; } if (m_program_id_with_input_output) { gl.deleteProgram(m_program_id_with_input_output); m_program_id_with_input_output = 0; } if (m_program_id_with_output) { gl.deleteProgram(m_program_id_with_output); m_program_id_with_output = 0; } if (m_program_id_without_output) { gl.deleteProgram(m_program_id_without_output); m_program_id_without_output = 0; } if (m_program_id_with_geometry_shader) { gl.deleteProgram(m_program_id_with_geometry_shader); m_program_id_with_geometry_shader = 0; } if (m_program_id_with_tessellation_shaders) { gl.deleteProgram(m_program_id_with_tessellation_shaders); m_program_id_with_tessellation_shaders = 0; } /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } bool gl3cts::TransformFeedback::APIErrors::testExtension1(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* INVALID_VALUE is generated by BindBufferRange, BindBufferOffset and BindBufferBase when is greater or equal to MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS; */ glw::GLint index_count = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &index_count); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (index_count == 0) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetIntegerv did not returned any value." << tcu::TestLog::EndMessage; throw 0; } gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0, 0, 16); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BindBufferRange " "when was greater or equal to " "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS." << tcu::TestLog::EndMessage; return false; } if (DE_NULL != m_glBindBufferOffsetEXT) { m_glBindBufferOffsetEXT(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0, 0); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BindBufferOffset " "when was greater or equal to " "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS." << tcu::TestLog::EndMessage; return false; } } gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by " "BindBufferBase when was greater or equal to " "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by BindBufferRange when is less or equal to zero; */ gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0, 0, 0); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BindBufferRange when was less or equal to zero." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by BindBufferRange and BindBufferOffset when is not word-aligned; */ gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0, 3, 4); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BindBufferRange when was not word-aligned." << tcu::TestLog::EndMessage; return false; } if (DE_NULL != m_glBindBufferOffsetEXT) { m_glBindBufferOffsetEXT(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0, 3); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BindBufferOffset when was not word-aligned." << tcu::TestLog::EndMessage; return false; } } /* INVALID_VALUE is generated by BindBufferRange when is not word-aligned; */ gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, index_count, m_buffer_0, 0, 3); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BindBufferRange when was not word-aligned." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by BindBufferRange, BindBufferOffset and BindBufferBase when is TRANSFORM_FEEDBACK_BUFFER and transform feedback is active; */ gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0, 0, 16); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by BindBufferRange " "when was TRANSFORM_FEEDBACK_BUFFER and transform " "feedback was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } if (DE_NULL != m_glBindBufferOffsetEXT) { m_glBindBufferOffsetEXT(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0, 0); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by BindBufferOffset " "when was TRANSFORM_FEEDBACK_BUFFER and transform " "feedback was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } } gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by " "BindBufferBase when was TRANSFORM_FEEDBACK_BUFFER and transform " "feedback was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* INVALID_OPERATION is generated by UseProgram when transform feedback is active; */ gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.useProgram(0); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by UseProgram when transform feedback was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* INVALID_OPERATION is generated by LinkProgram when is currently active and transform feedback is active; */ gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.linkProgram(m_program_id_with_output); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by LinkProgram when was " "currently active and transform feedback was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* INVALID_OPERATION is generated by BeginTransformFeedback when transform feedback is active; */ gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.beginTransformFeedback(GL_POINTS); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by BeginTransformFeedback when transform feedback was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* INVALID_OPERATION is generated by EndTransformFeedback when transform feedback is inactive; */ gl.endTransformFeedback(); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by EndTransformFeedback when transform feedback was inactive." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by draw command when generated primitives type does not match ; */ gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_LINES, 0, 2); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by draw command when generated " "primitives type does not match ." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* INVALID_OPERATION is generated by BeginTransformFeedback when any binding point used by XFB does not have buffer bound; */ gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by BeginTransformFeedback when any " "binding point used by XFB does not have buffer bound." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by BeginTransformFeedback when no program is active; */ gl.useProgram(0); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by BeginTransformFeedback when no program was active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } /* INVALID_OPERATION is generated by BeginTransformFeedback when no variable are specified to be captured in the active program; */ gl.useProgram(m_program_id_without_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); gl.beginTransformFeedback(GL_POINTS); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by BeginTransformFeedback when no variable " "are specified to be captured in the active program." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } /* INVALID_VALUE is generated by TransformFeedbackVaryings when is not id of the program object; */ unsigned short int invalid_name = 1; while (gl.isProgram(invalid_name) || gl.isShader(invalid_name)) { ++invalid_name; /* Make sure that this loop ends someday, bad day. */ if (invalid_name == USHRT_MAX) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Cannot find unused trnasform feedback object." << tcu::TestLog::EndMessage; throw 0; } } gl.transformFeedbackVaryings((glw::GLuint)invalid_name, 1, &m_varying_name, GL_INTERLEAVED_ATTRIBS); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by TransformFeedbackVaryings when " " was not id of the program object." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by TransformFeedbackVaryings when is SEPARATE_ATTRIBS and is exceeds limits; */ glw::GLint max_separate_attribs = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &max_separate_attribs); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_separate_attribs == 0) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by TransformFeedbackVaryings when " " was SEPARATE_ATTRIBS and was exceeds limits." << tcu::TestLog::EndMessage; throw 0; } glw::GLint more_than_max_separate_attribs = max_separate_attribs + 1; glw::GLchar** attrib = new glw::GLchar*[more_than_max_separate_attribs]; for (glw::GLint i = 0; i < more_than_max_separate_attribs; ++i) { std::string new_attrib = "a" + gl3cts::TransformFeedback::Utilities::itoa(i); size_t new_attrib_size = new_attrib.size(); attrib[i] = new glw::GLchar[new_attrib_size + 1]; memset(attrib[i], 0, new_attrib_size + 1); memcpy(attrib[i], new_attrib.c_str(), new_attrib_size); } gl.transformFeedbackVaryings(m_program_id_with_output, more_than_max_separate_attribs, attrib, GL_SEPARATE_ATTRIBS); for (glw::GLint i = 0; i < more_than_max_separate_attribs; ++i) { delete[] attrib[i]; } delete[] attrib; if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by TransformFeedbackVaryings when " " was SEPARATE_ATTRIBS and exceeded limits." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by GetTransformFeedbackVarying when is greater than or equal to TRANSFORM_FEEDBACK_VARYINGS; */ glw::GLint transform_feedback_varyings = 0; gl.getProgramiv(m_program_id_with_output, GL_TRANSFORM_FEEDBACK_VARYINGS, &transform_feedback_varyings); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); if (transform_feedback_varyings == 0) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "glGetProgramiv failed to return GL_TRANSFORM_FEEDBACK_VARYINGS." << tcu::TestLog::EndMessage; throw 0; } glw::GLchar tmp_buffer[256]; glw::GLsizei tmp_size = 0; glw::GLenum tmp_type = GL_NONE; gl.getTransformFeedbackVarying(m_program_id_with_output, transform_feedback_varyings, sizeof(tmp_buffer), NULL, &tmp_size, &tmp_type, tmp_buffer); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by GetTransformFeedbackVarying when " " was greater than or equal to TRANSFORM_FEEDBACK_VARYINGS." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by GetIntegerIndexdv when exceeds the limits of MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS and is one of the following: * TRANSFORM_FEEDBACK_BUFFER_BINDING, * TRANSFORM_FEEDBACK_BUFFER_START, * TRANSFORM_FEEDBACK_BUFFER_SIZE; */ if (DE_NULL != m_glGetIntegerIndexedvEXT) { glw::GLint tmp_int_value; m_glGetIntegerIndexedvEXT(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, more_than_max_separate_attribs, &tmp_int_value); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by GetIntegerIndexdv when exceeds the " "limits of MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS and was " "TRANSFORM_FEEDBACK_BUFFER_BINDING." << tcu::TestLog::EndMessage; return false; } m_glGetIntegerIndexedvEXT(GL_TRANSFORM_FEEDBACK_BUFFER_START, more_than_max_separate_attribs, &tmp_int_value); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by GetIntegerIndexdv when exceeds the " "limits of MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS and was " "GL_TRANSFORM_FEEDBACK_BUFFER_START." << tcu::TestLog::EndMessage; return false; } m_glGetIntegerIndexedvEXT(GL_TRANSFORM_FEEDBACK_BUFFER_SIZE, more_than_max_separate_attribs, &tmp_int_value); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by GetIntegerIndexdv when exceeds the " "limits of MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS and was " "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE." << tcu::TestLog::EndMessage; return false; } } /* INVALID_VALUE is generated by GetBooleanIndexedv when exceeds the limits of MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS and is TRANSFORM_FEEDBACK_BUFFER_BINDING. */ if (DE_NULL != m_glGetBooleanIndexedvEXT) { glw::GLboolean tmp_bool_value; m_glGetBooleanIndexedvEXT(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, more_than_max_separate_attribs, &tmp_bool_value); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by GetBooleanIndexedv when exceeds the " "limits of MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS and was " "TRANSFORM_FEEDBACK_BUFFER_BINDING." << tcu::TestLog::EndMessage; return false; } } return true; } bool gl3cts::TransformFeedback::APIErrors::testExtension2(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Bind Transform Feedback Object */ gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transform_feedback_object_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback call failed."); /* INVALID_OPERATION is generated by PauseTransformFeedback if current transform feedback is not active or paused; */ gl.pauseTransformFeedback(); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION is not generated by PauseTransformFeedback if " "current transform feedback is not active or paused." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by ResumeTransformFeedback if current transform feedback is not active; */ gl.resumeTransformFeedback(); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION is not generated by ResumeTransformFeedback if " "current transform feedback is not active." << tcu::TestLog::EndMessage; return false; } /* Prepare program and buffer. */ gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); /* INVALID_OPERATION is generated by DrawTransformFeedback when EndTransformFeedback was never called for the object named . */ gl.drawTransformFeedback(GL_POINTS, m_transform_feedback_object_0); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION is not generated by DrawTransformFeedback when " "EndTransformFeedback was never called for the object named ." << tcu::TestLog::EndMessage; return false; } /* Make Transform Feedback Active */ gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); /* INVALID_OPERATION is generated by BindTransformFeedback if current transform feedback is active and not paused; */ gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transform_feedback_object_0); if (GL_INVALID_OPERATION != gl.getError()) { gl.endTransformFeedback(); m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION is not generated by BindTransformFeedback if current " "transform feedback is active and not paused." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by DeleteTransformFeedbacks if any of is active; */ gl.deleteTransformFeedbacks(1, &m_transform_feedback_object_0); if (GL_INVALID_OPERATION != gl.getError()) { gl.endTransformFeedback(); m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION is not generated by DeleteTransformFeedbacks if any of is active." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by ResumeTransformFeedback if current transform feedback is not not paused; */ gl.resumeTransformFeedback(); if (GL_INVALID_OPERATION != gl.getError()) { gl.endTransformFeedback(); m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION is not generated by ResumeTransformFeedback if " "current transform feedback is not not paused." << tcu::TestLog::EndMessage; return false; } /* pause transform feedback */ gl.pauseTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glPauseTransformFeedback call failed."); /* No error is generated by draw command when transform feedback is paused and primitive modes do not match; */ gl.drawArrays(GL_LINES, 0, 2); if (GL_NO_ERROR != gl.getError()) { gl.endTransformFeedback(); m_context.getTestContext().getLog() << tcu::TestLog::Message << "No error is not generated by draw command when transform feedback is " "paused and primitive modes do not match." << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by LinkProgram when is used by some transform feedback object that is currently not active; */ gl.linkProgram(m_program_id_with_output); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by LinkProgram when was " "used by some transform feedback object that is currently not active." << tcu::TestLog::EndMessage; gl.endTransformFeedback(); return false; } /* No error is generated by UseProgram when transform feedback is paused; */ gl.useProgram(0); if (GL_NO_ERROR != gl.getError()) { gl.endTransformFeedback(); m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUseProgram unexpectedly failed when transform feedback is paused." << tcu::TestLog::EndMessage; return false; } gl.useProgram(m_program_id_with_output); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); /* End Transform Feedback and make draw. */ gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); /* INVALID_VALUE is generated by DrawTransformFeedback if is not name of transform feedback object; */ unsigned short int invalid_name = 1; while (gl.isTransformFeedback(invalid_name)) { ++invalid_name; /* Make sure that this loop ends someday, bad day. */ if (invalid_name == USHRT_MAX) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Cannot find unused trnasform feedback object." << tcu::TestLog::EndMessage; throw 0; } } gl.drawTransformFeedback(GL_POINTS, (glw::GLuint)invalid_name); if (GL_INVALID_VALUE != gl.getError()) { gl.endTransformFeedback(); m_context.getTestContext().getLog() << tcu::TestLog::Message << "glUseProgram unexpectedly failed when transform feedback is paused." << tcu::TestLog::EndMessage; return false; } return true; } bool gl3cts::TransformFeedback::APIErrors::testExtension3(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* INVALID_VALUE is generated by BeginQueryIndexed, EndQueryIndexed and GetQueryIndexediv when is TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN and exceeds limits of MAX_VERTEX_STREAMS INVALID_VALUE is generated by BeginQueryIndexed, EndQueryIndexed and GetQueryIndexediv when is PRIMITIVES_GENERATED and exceeds limits of MAX_VERTEX_STREAMS */ glw::GLint max_vertex_streams = 0; gl.getIntegerv(GL_MAX_VERTEX_STREAMS, &max_vertex_streams); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_vertex_streams == 0) { /* Nothing returned. */ throw 0; } ++max_vertex_streams; static const glw::GLenum target[] = { GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_PRIMITIVES_GENERATED }; static const glw::GLchar* target_str[] = { STR(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN), STR(GL_PRIMITIVES_GENERATED) }; static const glw::GLuint target_count = sizeof(target) / sizeof(target[0]); for (glw::GLuint i = 0; i < target_count; ++i) { gl.beginQueryIndexed(target[i], max_vertex_streams, m_query_object); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by BeginQueryIndexed, EndQueryIndexed and" "GetQueryIndexediv when was " << target_str[i] << " and " " exceeded limits of MAX_VERTEX_STREAMS." << tcu::TestLog::EndMessage; return false; } gl.endQueryIndexed(target[i], max_vertex_streams); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by EndQueryIndexed " "when was " << target_str[i] << " and " " exceeded limits of MAX_VERTEX_STREAMS." << tcu::TestLog::EndMessage; return false; } glw::GLint param = 0; gl.getQueryIndexediv(target[i], max_vertex_streams, GL_QUERY_COUNTER_BITS, ¶m); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by " "GetQueryIndexediv when was " << target_str[i] << " and " " exceeded limits of MAX_VERTEX_STREAMS." << tcu::TestLog::EndMessage; return false; } } /* INVALID_OPERATION is generated by EndQueryIndexed when name of active query at of is zero */ gl.endQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by EndQueryIndexed when name of active " "query at of is zero" << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by DrawTransformFeedbackStream when exceeds limits of MAX_VERTEX_STREAMS */ gl.drawTransformFeedbackStream(GL_POINTS, m_transform_feedback_object_0, max_vertex_streams); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_VALUE was not generated by DrawTransformFeedbackStream when " "exceeded limits of MAX_VERTEX_STREAMS" << tcu::TestLog::EndMessage; return false; } /* INVALID_OPERATION is generated by TransformFeedbackVaryings when contains any of the special names while is not INTERLEAVED_ATTRIBS */ static const glw::GLchar* tf_varying_names[] = { "gl_NextBuffer", "gl_SkipComponents1", "gl_SkipComponents2", "gl_SkipComponents3", "gl_SkipComponents4" }; static const glw::GLuint tf_varying_names_count = sizeof(tf_varying_names) / sizeof(tf_varying_names[0]); for (glw::GLuint i = 0; i < tf_varying_names_count; ++i) { gl.transformFeedbackVaryings(m_program_id_with_output, 1, &tf_varying_names[i], GL_SEPARATE_ATTRIBS); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by TransformFeedbackVaryings when " " contained any of the special names while was " "GL_SEPARATE_ATTRIBS." << tcu::TestLog::EndMessage; return false; } } /* INVALID_OPERATION is generated by TransformFeedbackVaryings when contains more "gl_NextBuffer" entries than allowed limit of MAX_TRANSFORM_FEEDBACK_BUFFERS */ glw::GLint max_transform_feedback_buffers = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, &max_transform_feedback_buffers); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_buffers == 0) { /* Nothing returned. */ throw 0; } glw::GLint more_than_max_transform_feedback_buffers = max_transform_feedback_buffers + 1; const glw::GLchar** tf_next_buffer_varying_names = new const glw::GLchar*[more_than_max_transform_feedback_buffers]; if (DE_NULL == tf_next_buffer_varying_names) { /* Allocation error. */ throw 0; } for (glw::GLint i = 0; i < more_than_max_transform_feedback_buffers; ++i) { tf_next_buffer_varying_names[i] = tf_varying_names[0]; } gl.transformFeedbackVaryings(m_program_id_with_output, more_than_max_transform_feedback_buffers, tf_next_buffer_varying_names, GL_INTERLEAVED_ATTRIBS); delete[] tf_next_buffer_varying_names; if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by TransformFeedbackVaryings when " " contained more \"gl_NextBuffer\" entries than allowed limit of " "MAX_TRANSFORM_FEEDBACK_BUFFER." << tcu::TestLog::EndMessage; return false; } return true; } bool gl3cts::TransformFeedback::APIErrors::testInstanced(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_tessellation = m_context.getContextInfo().isExtensionSupported("GL_ARB_tessellation_shader"); bool has_patches = is_at_least_gl_40 || is_tessellation; /* INVALID_ENUM is generated by DrawTransformFeedbackInstanced and DrawTransformFeedbackStreamInstanced if is invalid */ glw::GLenum _supported_mode[] = { GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP, GL_LINES, GL_LINE_STRIP_ADJACENCY, GL_LINES_ADJACENCY, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_TRIANGLES, GL_TRIANGLE_STRIP_ADJACENCY, GL_TRIANGLES_ADJACENCY, GL_QUADS, GL_PATCHES }; std::set supported_mode(_supported_mode, _supported_mode + sizeof(_supported_mode) / sizeof(_supported_mode[0]) - (has_patches ? 0 : 1)); int mode = 0; while (supported_mode.find(mode) != supported_mode.end()) { mode++; } gl.drawTransformFeedbackInstanced(mode, m_transform_feedback_object_0, 1); if (GL_INVALID_ENUM != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_ENUM was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced when was invalid." << tcu::TestLog::EndMessage; return false; } gl.drawTransformFeedbackStreamInstanced(mode, m_transform_feedback_object_0, 0, 1); if (GL_INVALID_ENUM != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_ENUM was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced when was invalid." << tcu::TestLog::EndMessage; return false; } if (m_program_id_with_geometry_shader != 0) { /* INVALID_OPERATION is generated by DrawTransformFeedbackInstanced and DrawTransformFeedbackStreamInstanced if does not match geometry shader */ gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transform_feedback_object_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback call failed."); gl.useProgram(m_program_id_with_geometry_shader); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vertex_array_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.drawTransformFeedbackInstanced(GL_LINES, m_transform_feedback_object_0, 1); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced if did not match geometry shader." << tcu::TestLog::EndMessage; return false; } gl.drawTransformFeedbackStreamInstanced(GL_LINES, m_transform_feedback_object_0, 0, 1); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced if did not match geometry shader." << tcu::TestLog::EndMessage; return false; } } /* All of the below tests concern themselves with GL_PATCHES and * tessellation shaders */ if (m_program_id_with_tessellation_shaders == 0) { return true; } /* INVALID_OPERATION is generated by DrawTransformFeedbackInstanced and DrawTransformFeedbackStreamInstanced if does not match tessellation */ gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transform_feedback_object_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback call failed."); gl.useProgram(m_program_id_with_tessellation_shaders); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.bindVertexArray(m_vertex_array_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.beginTransformFeedback(GL_LINES); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_PATCHES, 0, 2); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.drawTransformFeedbackInstanced(GL_POINTS, m_transform_feedback_object_0, 1); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced if did not match geometry shader." << tcu::TestLog::EndMessage; return false; } gl.drawTransformFeedbackStreamInstanced(GL_POINTS, m_transform_feedback_object_0, 0, 1); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced if did not match geometry shader." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by DrawTransformFeedbackStreamInstanced if is greater than or equal to MAX_VERTEX_STREAMS */ glw::GLint max_vertex_streams = 0; gl.getIntegerv(GL_MAX_VERTEX_STREAMS, &max_vertex_streams); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_vertex_streams == 0) { /* Failed to query GL_MAX_VERTEX_STREAMS. */ throw 0; } glw::GLint more_than_max_vertex_streams = max_vertex_streams + 1; gl.drawTransformFeedbackStreamInstanced(GL_PATCHES, m_transform_feedback_object_0, more_than_max_vertex_streams, 1); if (GL_INVALID_VALUE != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced if did not match geometry shader." << tcu::TestLog::EndMessage; return false; } /* INVALID_VALUE is generated by DrawTransformFeedbackInstanced and DrawTransformFeedbackStreamInstanced if is not name of transform feedback object */ unsigned short int invalid_name = 1; while (gl.isTransformFeedback(invalid_name)) { ++invalid_name; /* Make sure that this loop ends someday, bad day. */ if (invalid_name == USHRT_MAX) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Cannot find unused trnasform feedback object." << tcu::TestLog::EndMessage; throw 0; } } gl.drawTransformFeedbackInstanced(GL_PATCHES, (glw::GLuint)invalid_name, 1); if (GL_INVALID_VALUE != gl.getError()) { gl.endTransformFeedback(); return false; } gl.drawTransformFeedbackStreamInstanced(GL_PATCHES, (glw::GLuint)invalid_name, 0, 1); if (GL_INVALID_VALUE != gl.getError()) { gl.endTransformFeedback(); return false; } /* INVALID_OPERATION is generated if by DrawTransformFeedbackStreamInstanced if EndTransformFeedback was never called for the object named . return true */ gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transform_feedback_object_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback call failed."); gl.drawTransformFeedbackStreamInstanced(GL_PATCHES, m_transform_feedback_object_1, 0, 1); if (GL_INVALID_OPERATION != gl.getError()) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "INVALID_OPERATION was not generated by DrawTransformFeedbackInstanced and " "DrawTransformFeedbackStreamInstanced if did not match geometry shader." << tcu::TestLog::EndMessage; return false; } return true; } const glw::GLchar* gl3cts::TransformFeedback::APIErrors::m_tessellation_control_shader = "#version 400\n" "\n" "layout (vertices = 2 ) out;\n" "\n" "void main()\n" "{\n" " gl_TessLevelOuter[1] = 3.0;\n" " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::m_tessellation_evaluation_shader = "#version 400\n" "\n" "layout(isolines, equal_spacing, ccw) in;\n" "\n" "out float result;\n" "\n" "void main()\n" "{\n" " result = 0.5;\n" " gl_Position = gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::m_geometry_shader = "#version 150\n" "\n" "layout(points) in;\n" "layout(points, max_vertices = 2) out;\n" "\n" "out float result;\n" "\n" "void main()\n" "{\n" " gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, 0.0, 0.0);\n" " result = 0.0;\n" " EmitVertex();\n" "\n" " gl_Position = gl_in[0].gl_Position + vec4(1.0, 0.0, 0.0, 0.0);\n" " result = 1.0;\n" " EmitVertex();\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::s_vertex_shader_with_output = "#version 130\n" "\n" "out float result;\n" "\n" "void main()\n" "{\n" " result = float(gl_VertexID);\n" " gl_Position = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::s_vertex_shader_with_input_output = "#version 130\n" "\n" "in float v_input;\n" "\n" "out float result;\n" "\n" "void main()\n" "{\n" " result = float(gl_VertexID);\n" " gl_Position = vec4(v_input);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::s_vertex_shader_without_output = "#version 130\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::s_fragment_shader = "#version 130\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::APIErrors::m_varying_name = "result"; const glw::GLfloat gl3cts::TransformFeedback::APIErrors::m_buffer_1_data[] = { 3.14159265359f, 2.7182818f }; const glw::GLsizei gl3cts::TransformFeedback::APIErrors::m_buffer_1_size = sizeof(gl3cts::TransformFeedback::APIErrors::m_buffer_1_data); /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::LinkingErrors::LinkingErrors(deqp::Context& context) : deqp::TestCase(context, "linking_errors_test", "Transform Feedback Linking Errors Test"), m_context(context) { /* Left intentionally blank. */ } gl3cts::TransformFeedback::LinkingErrors::~LinkingErrors(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::LinkingErrors::iterate(void) { bool is_ok = true; bool test_error = false; try { is_ok = is_ok && testNoVertexNoGeometry(); is_ok = is_ok && testInvalidVarying(); is_ok = is_ok && testRepeatedVarying(); is_ok = is_ok && testTooManyVaryings(); } catch (...) { is_ok = false; test_error = true; } /* Result's setup. */ if (is_ok) { m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } bool gl3cts::TransformFeedback::LinkingErrors::testNoVertexNoGeometry(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check if link process fails under the following conditions: specified by TransformFeedbackVaryings is non-zero and program has neither vertex nor geometry shader; */ glw::GLint linking_status = 1; glw::GLuint program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, NULL, s_fragment_shader, &s_valid_transform_feedback_varying, 1, GL_INTERLEAVED_ATTRIBS, false, &linking_status); if ((GL_FALSE != linking_status) || program) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking unexpectedly succeded when Transform Feedback varying was " "specified but program had neither vertex nor geometry shader stages." << tcu::TestLog::EndMessage; if (program) { gl.deleteProgram(program); } return false; } /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking failed as expected when Transform Feedback varying was specified " "but program had neither vertex nor geometry shader stages." << tcu::TestLog::EndMessage; return true; } bool gl3cts::TransformFeedback::LinkingErrors::testInvalidVarying(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check if link process fails under the following conditions: specified by TransformFeedbackVaryings contains name of variable that is not available for capture; */ std::string vertex_shader(s_vertex_shader_template); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( vertex_shader, "TEMPLATE_INPUT_OUTPUT_DECLARATIONS", "in float data;\n"); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(vertex_shader, "TEMPLATE_OUTPUT_SETTERS", ""); glw::GLint linking_status = 1; glw::GLuint program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, vertex_shader.c_str(), s_fragment_shader, &s_invalid_transform_feedback_varying, 1, GL_INTERLEAVED_ATTRIBS, false, &linking_status); if ((GL_FALSE != linking_status) || program) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking unexpectedly succeded when Transform Feedback varying was specified with name of variable (" << s_invalid_transform_feedback_varying << ") that is not available for capture." << tcu::TestLog::EndMessage; if (program) { gl.deleteProgram(program); } return false; } /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking failed as expected when Transform Feedback varying was specified with name of variable (" << s_invalid_transform_feedback_varying << ") that is not available for capture." << tcu::TestLog::EndMessage; return true; } bool gl3cts::TransformFeedback::LinkingErrors::testRepeatedVarying(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check if link process fails under the following conditions: specified by TransformFeedbackVaryings contains name of variable more than once; */ std::string vertex_shader(s_vertex_shader_template); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( vertex_shader, "TEMPLATE_INPUT_OUTPUT_DECLARATIONS", "out float result;\n"); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(vertex_shader, "TEMPLATE_OUTPUT_SETTERS", " result = 0.577215664901532;\n"); glw::GLint linking_status = 1; glw::GLuint program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, vertex_shader.c_str(), s_fragment_shader, s_repeated_transform_feedback_varying, s_repeated_transform_feedback_varying_count, GL_INTERLEAVED_ATTRIBS, false, &linking_status); if ((GL_FALSE != linking_status) || program) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking unexpectedly succeded when Transform Feedback varying was specified twice." << tcu::TestLog::EndMessage; if (program) { gl.deleteProgram(program); } return false; } /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking failed as expected when Transform Feedback varying was specified twice." << tcu::TestLog::EndMessage; return true; } bool gl3cts::TransformFeedback::LinkingErrors::testTooManyVaryings(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check if link process fails under the following conditions: number of components specified to capture exceeds limits MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS or MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS. */ /* Fetching limits. */ glw::GLint max_transform_feedback_separate_components = 0; glw::GLint max_transform_feedback_interleaved_components = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &max_transform_feedback_separate_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_separate_components == 0) { throw 0; } gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &max_transform_feedback_interleaved_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_interleaved_components == 0) { throw 0; } glw::GLint more_than_max_transform_feedback_components = deMax32(max_transform_feedback_separate_components, max_transform_feedback_interleaved_components) + 1; /* Preparing source code. */ std::string vertex_shader(s_vertex_shader_template); std::string transform_feedback_variable_declarations(""); std::string transform_feedback_variable_setters(""); std::vector transform_feedback_varyings(more_than_max_transform_feedback_components); std::vector transform_feedback_varyings_c(more_than_max_transform_feedback_components); for (glw::GLint i = 0; i < more_than_max_transform_feedback_components; ++i) { std::string varying = "result_"; varying.append(gl3cts::TransformFeedback::Utilities::itoa(i)); transform_feedback_varyings[i] = varying; transform_feedback_varyings_c[i] = transform_feedback_varyings[i].c_str(); transform_feedback_variable_declarations.append("out float "); transform_feedback_variable_declarations.append(varying); transform_feedback_variable_declarations.append(";\n"); transform_feedback_variable_setters.append(" "); transform_feedback_variable_setters.append(varying); transform_feedback_variable_setters.append(" = "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * i)); transform_feedback_variable_setters.append(".0;\n"); } vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( vertex_shader, "TEMPLATE_INPUT_OUTPUT_DECLARATIONS", transform_feedback_variable_declarations); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(vertex_shader, "TEMPLATE_OUTPUT_SETTERS", transform_feedback_variable_setters); glw::GLuint program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, vertex_shader.c_str(), s_fragment_shader, &transform_feedback_varyings_c[0], more_than_max_transform_feedback_components, GL_INTERLEAVED_ATTRIBS); /* Note: we check for program as not only linking shall fail, but also glTransformFeedbackVaryings shall return an error. */ if (program) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking unexpectedly succeded when too many Transform Feedback varying " "were specified in INTERLEAVED mode." << tcu::TestLog::EndMessage; if (program) { gl.deleteProgram(program); } return false; } program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, vertex_shader.c_str(), s_fragment_shader, &transform_feedback_varyings_c[0], more_than_max_transform_feedback_components, GL_SEPARATE_ATTRIBS); if (program) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking unexpectedly succeded when too many Transform Feedback " "varyings were specified in SEPARATE mode." << tcu::TestLog::EndMessage; if (program) { gl.deleteProgram(program); } return false; } /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Linking failed as expected when too many Transform Feedback varyings were specified." << tcu::TestLog::EndMessage; return true; } const glw::GLchar* gl3cts::TransformFeedback::LinkingErrors::s_fragment_shader = "#version 130\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::LinkingErrors::s_vertex_shader_template = "#version 130\n" "\n" "TEMPLATE_INPUT_OUTPUT_DECLARATIONS" "\n" "void main()\n" "{\n" "TEMPLATE_OUTPUT_SETTERS" "\n" " gl_Position = vec4(1.618033988749);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::LinkingErrors::s_valid_transform_feedback_varying = "result"; const glw::GLchar* gl3cts::TransformFeedback::LinkingErrors::s_invalid_transform_feedback_varying = "data"; const glw::GLchar* gl3cts::TransformFeedback::LinkingErrors::s_repeated_transform_feedback_varying[] = { "result", "result" }; const glw::GLsizei gl3cts::TransformFeedback::LinkingErrors::s_repeated_transform_feedback_varying_count = sizeof(s_repeated_transform_feedback_varying) / sizeof(s_repeated_transform_feedback_varying[0]); /*-----------------------------------------------------------------------------------------------*/ const glw::GLint gl3cts::TransformFeedback::Limits::s_min_value_of_max_transform_feedback_interleaved_components = 64; const glw::GLint gl3cts::TransformFeedback::Limits::s_min_value_of_max_transform_feedback_separate_attribs = 4; const glw::GLint gl3cts::TransformFeedback::Limits::s_min_value_of_max_transform_feedback_separate_components = 4; const glw::GLint gl3cts::TransformFeedback::Limits::s_min_value_of_max_transform_feedback_buffers = 4; const glw::GLint gl3cts::TransformFeedback::Limits::s_min_value_of_max_vertex_streams = 1; gl3cts::TransformFeedback::Limits::Limits(deqp::Context& context) : deqp::TestCase(context, "limits_test", "Transform Feedback Limits Test"), m_context(context) { } gl3cts::TransformFeedback::Limits::~Limits(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::Limits::iterate(void) { /* Initializations. */ bool is_at_least_gl_30 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 0))); bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_ext_tf_1 = m_context.getContextInfo().isExtensionSupported("GL_EXT_transform_feedback"); bool is_arb_tf_3 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback3"); bool is_ok = true; bool test_error = false; /* Tests. */ try { if (is_at_least_gl_30 || is_ext_tf_1) { is_ok = is_ok && test_max_transform_feedback_interleaved_components(); is_ok = is_ok && test_max_transform_feedback_separate_attribs(); is_ok = is_ok && test_max_transform_feedback_separate_components(); } if (is_at_least_gl_40 || is_arb_tf_3) { is_ok = is_ok && test_max_transform_feedback_buffers(); is_ok = is_ok && test_max_vertex_streams(); } } catch (...) { is_ok = false; test_error = true; } /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Limits are in range of specification." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } bool gl3cts::TransformFeedback::Limits::test_max_transform_feedback_interleaved_components(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check that MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS is at least 64. */ glw::GLint max_transform_feedback_interleaved_components = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &max_transform_feedback_interleaved_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_interleaved_components < s_min_value_of_max_transform_feedback_interleaved_components) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS is equal to " << max_transform_feedback_interleaved_components << " which is less than expected " << s_min_value_of_max_transform_feedback_interleaved_components << "." << tcu::TestLog::EndMessage; return false; } return true; } bool gl3cts::TransformFeedback::Limits::test_max_transform_feedback_separate_attribs(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check that MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS is at least 4. */ glw::GLint max_transform_feedback_separate_attribs = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &max_transform_feedback_separate_attribs); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_separate_attribs < s_min_value_of_max_transform_feedback_separate_attribs) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS is equal to " << max_transform_feedback_separate_attribs << " which is less than expected " << s_min_value_of_max_transform_feedback_separate_attribs << "." << tcu::TestLog::EndMessage; return false; } return true; } bool gl3cts::TransformFeedback::Limits::test_max_transform_feedback_separate_components(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check that MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS is at least 4. */ glw::GLint max_transform_feedback_separate_components = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &max_transform_feedback_separate_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_separate_components < s_min_value_of_max_transform_feedback_separate_components) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS is equal to " << max_transform_feedback_separate_components << " which is less than expected " << s_min_value_of_max_transform_feedback_separate_components << "." << tcu::TestLog::EndMessage; return false; } return true; } bool gl3cts::TransformFeedback::Limits::test_max_transform_feedback_buffers(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check that MAX_TRANSFORM_FEEDBACK_BUFFERS is at least 4. */ glw::GLint max_transform_feedback_buffers = 0; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_BUFFERS, &max_transform_feedback_buffers); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_buffers < s_min_value_of_max_transform_feedback_buffers) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "MAX_TRANSFORM_FEEDBACK_BUFFERS is equal to " << max_transform_feedback_buffers << " which is less than expected " << s_min_value_of_max_transform_feedback_buffers << "." << tcu::TestLog::EndMessage; return false; } return true; } bool gl3cts::TransformFeedback::Limits::test_max_vertex_streams(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check that MAX_VERTEX_STREAMS is at least 1. */ glw::GLint max_vertex_streams = 0; gl.getIntegerv(GL_MAX_VERTEX_STREAMS, &max_vertex_streams); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_vertex_streams < s_min_value_of_max_vertex_streams) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "MAX_VERTEX_STREAMS is equal to " << max_vertex_streams << " which is less than expected " << s_min_value_of_max_vertex_streams << "." << tcu::TestLog::EndMessage; return false; } return true; } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::CaptureVertexInterleaved::CaptureVertexInterleaved(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program(0) , m_framebuffer(0) , m_renderbuffer(0) , m_buffer(0) , m_buffer_size(0) , m_vertex_array_object(0) , m_max_transform_feedback_components(0) , m_attrib_type(GL_INTERLEAVED_ATTRIBS) , m_max_vertices_drawn(8) , m_glBindBufferOffsetEXT(DE_NULL) { } gl3cts::TransformFeedback::CaptureVertexInterleaved::~CaptureVertexInterleaved(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::CaptureVertexInterleaved::iterate(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initializations. */ bool is_at_least_gl_30 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 0))); bool is_ext_tf_1 = m_context.getContextInfo().isExtensionSupported("GL_EXT_transform_feedback"); bool is_ok = true; bool test_error = false; try { if (is_ext_tf_1) { /* Extension query. */ m_glBindBufferOffsetEXT = (BindBufferOffsetEXT_ProcAddress)m_context.getRenderContext().getProcAddress("glBindBufferOffsetEXT"); if (DE_NULL == m_glBindBufferOffsetEXT) { throw 0; } } if (is_at_least_gl_30 || is_ext_tf_1) { fetchLimits(); buildProgram(); createFramebuffer(); createTransformFeedbackBuffer(); createVertexArrayObject(); gl.useProgram(m_program); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); for (glw::GLint i_bind_case = 0; (i_bind_case < BIND_BUFFER_CASES_COUNT) && is_ok; ++i_bind_case) { if ((i_bind_case == BIND_BUFFER_OFFSET_CASE) && (DE_NULL == m_glBindBufferOffsetEXT)) { continue; } bindBuffer((BindBufferCase)i_bind_case); for (glw::GLuint i_primitive_case = 0; (i_primitive_case < s_primitive_cases_count) && is_ok; ++i_primitive_case) { draw(i_primitive_case); is_ok = is_ok && checkFramebuffer(s_primitive_cases[i_primitive_case]); is_ok = is_ok && checkTransformFeedbackBuffer((BindBufferCase)i_bind_case, s_primitive_cases[i_primitive_case]); } } } } catch (...) { is_ok = false; test_error = true; } /* Clean objects. */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Capture Vertex have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::CaptureVertexInterleaved::fetchLimits(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetching limits. */ gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &m_max_transform_feedback_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (m_max_transform_feedback_components == 0) { throw 0; } glw::GLint max_varyings_components = 0; gl.getIntegerv(GL_MAX_VARYING_COMPONENTS, &max_varyings_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_varyings_components == 0) { throw 0; } if (m_max_transform_feedback_components > max_varyings_components) { m_max_transform_feedback_components = max_varyings_components; } } void gl3cts::TransformFeedback::CaptureVertexInterleaved::buildProgram(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Preparing source code. */ std::string vertex_shader(s_vertex_shader_source_code_template); /* Storage for vertex shader source code. */ std::string transform_feedback_variable_declarations( ""); /* String to contain all custom outputs from vertex shader. */ std::string transform_feedback_variable_setters( ""); /* String containing all initializations of custom outputs from vertex shader. */ std::vector transform_feedback_varyings(m_max_transform_feedback_components); /* Varyings array. */ std::vector transform_feedback_varyings_c( m_max_transform_feedback_components); /* Varyings array in C form to pass to the GL. */ glw::GLint user_defined_transform_feedback_interleaved_varyings_count = m_max_transform_feedback_components /* total max to be written by the shader */ / 4 /* components per vec4 */ - 1 /* gl_Position */; glw::GLint all_transform_feedback_interleaved_varyings_count = user_defined_transform_feedback_interleaved_varyings_count + 1 /* gl_Position */; /* Most of varyings is declarated output variables. */ for (glw::GLint i = 0; i < user_defined_transform_feedback_interleaved_varyings_count; ++i) { std::string varying = "result_"; varying.append(gl3cts::TransformFeedback::Utilities::itoa(i)); transform_feedback_varyings[i] = varying; transform_feedback_varyings_c[i] = transform_feedback_varyings[i].c_str(); transform_feedback_variable_declarations.append("out vec4 "); transform_feedback_variable_declarations.append(varying); transform_feedback_variable_declarations.append(";\n"); transform_feedback_variable_setters.append(" "); transform_feedback_variable_setters.append(varying); transform_feedback_variable_setters.append(" = vec4("); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4)); transform_feedback_variable_setters.append(".0, "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4 + 1)); transform_feedback_variable_setters.append(".0, "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4 + 2)); transform_feedback_variable_setters.append(".0, "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4 + 3)); transform_feedback_variable_setters.append(".0);\n"); } /* Last four varying components are gl_Position components. */ transform_feedback_varyings[user_defined_transform_feedback_interleaved_varyings_count] = "gl_Position"; transform_feedback_varyings_c[user_defined_transform_feedback_interleaved_varyings_count] = transform_feedback_varyings[user_defined_transform_feedback_interleaved_varyings_count].c_str(); /* Preprocess vertex shader source code template. */ vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( vertex_shader, "TEMPLATE_INPUT_OUTPUT_DECLARATIONS", transform_feedback_variable_declarations); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(vertex_shader, "TEMPLATE_OUTPUT_SETTERS", transform_feedback_variable_setters); vertex_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( vertex_shader, "TEMPLATE_RASTERIZATION_EPSILON", gl3cts::TransformFeedback::Utilities::ftoa(s_rasterization_epsilon)); /* Compile, link and check. */ m_program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, vertex_shader.c_str(), s_fragment_shader_source_code, &transform_feedback_varyings_c[0], all_transform_feedback_interleaved_varyings_count, m_attrib_type); if (0 == m_program) { throw 0; } } void gl3cts::TransformFeedback::CaptureVertexInterleaved::createFramebuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Setting clear color */ gl.clearColor(0.f, 0.f, 0.f, 1.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); /* Creating framebuffer */ gl.genFramebuffers(1, &m_framebuffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_renderbuffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32F, s_framebuffer_size, s_framebuffer_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, s_framebuffer_size, s_framebuffer_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); } void gl3cts::TransformFeedback::CaptureVertexInterleaved::createTransformFeedbackBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Creating xfb buffer */ gl.genBuffers(1, &m_buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffer); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); m_buffer_size = static_cast(m_max_transform_feedback_components * m_max_vertices_drawn * sizeof(glw::GLfloat)); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffer_size, NULL, GL_DYNAMIC_READ); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); } void gl3cts::TransformFeedback::CaptureVertexInterleaved::createVertexArrayObject(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* VAO Creations */ gl.genVertexArrays(1, &m_vertex_array_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vertex_array_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); } void gl3cts::TransformFeedback::CaptureVertexInterleaved::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Draw */ gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); gl.beginTransformFeedback(s_primitive_cases_xfb[primitive_case]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedbac call failed."); gl.drawElements(s_primitive_cases[primitive_case], s_element_indices_counts[primitive_case], GL_UNSIGNED_INT, s_element_indices[primitive_case]); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedbac call failed."); } bool gl3cts::TransformFeedback::CaptureVertexInterleaved::checkFramebuffer(glw::GLenum primitive_type UNUSED) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch framebuffer. */ std::vector pixels(s_framebuffer_size * s_framebuffer_size); if (s_framebuffer_size > 0) { gl.readPixels(0, 0, s_framebuffer_size, s_framebuffer_size, GL_RED, GL_FLOAT, pixels.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); } /* Check results. Note: assuming that s_buffer_size == 2 -> then all points shall be drawn. */ for (std::vector::iterator i = pixels.begin(); i != pixels.end(); ++i) { if (fabs(*i - 0.5f) > 0.0625f /* precision */) { return false; } } return true; } bool gl3cts::TransformFeedback::CaptureVertexInterleaved::checkTransformFeedbackBuffer(BindBufferCase bind_case UNUSED, glw::GLenum primitive_type) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Check */ glw::GLuint number_of_vertices = 0; switch (primitive_type) { case GL_POINTS: number_of_vertices = 4; break; case GL_LINES: number_of_vertices = 4; break; case GL_LINE_LOOP: number_of_vertices = 8; break; case GL_LINE_STRIP: number_of_vertices = 6; break; case GL_TRIANGLES: number_of_vertices = 6; break; case GL_TRIANGLE_STRIP: number_of_vertices = 6; break; case GL_TRIANGLE_FAN: number_of_vertices = 6; break; default: throw 0; } glw::GLfloat* results = (glw::GLfloat*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); bool is_ok = true; for (glw::GLuint j = 0; (j < number_of_vertices) && is_ok; ++j) { for (glw::GLint i = 0; i < m_max_transform_feedback_components - 4; ++i) { glw::GLfloat result = results[i + j * m_max_transform_feedback_components]; glw::GLfloat reference = (glw::GLfloat)(i); if (fabs(result - reference) > 0.125 /* precision */) { is_ok = false; break; } } /* gl_Position */ glw::GLfloat result[4] = { results[(j + 1) * m_max_transform_feedback_components - 4], results[(j + 1) * m_max_transform_feedback_components - 3], results[(j + 1) * m_max_transform_feedback_components - 2], results[(j + 1) * m_max_transform_feedback_components - 1] }; if ((fabs(fabs(result[0]) - 1.0 + s_rasterization_epsilon) > 0.125 /* precision */) || (fabs(fabs(result[1]) - 1.0 + s_rasterization_epsilon) > 0.125 /* precision */) || (fabs(result[2]) > 0.125 /* precision */) || (fabs(result[3] - 1.0) > 0.125 /* precision */)) { is_ok = false; break; } } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); return is_ok; } void gl3cts::TransformFeedback::CaptureVertexInterleaved::bindBuffer(BindBufferCase bind_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); switch (bind_case) { case BIND_BUFFER_BASE_CASE: gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer); break; case BIND_BUFFER_RANGE_CASE: gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer, 0, m_buffer_size); break; case BIND_BUFFER_OFFSET_CASE: if (DE_NULL == m_glBindBufferOffsetEXT) { throw 0; } m_glBindBufferOffsetEXT(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_buffer, 0); break; default: throw 0; } } void gl3cts::TransformFeedback::CaptureVertexInterleaved::clean(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_program) { gl.deleteProgram(m_program); m_program = 0; } if (m_framebuffer) { gl.deleteFramebuffers(1, &m_framebuffer); m_framebuffer = 0; } if (m_renderbuffer) { gl.deleteRenderbuffers(1, &m_renderbuffer); m_renderbuffer = 0; } cleanBuffer(); if (m_vertex_array_object) { gl.deleteVertexArrays(1, &m_vertex_array_object); m_vertex_array_object = 0; } } void gl3cts::TransformFeedback::CaptureVertexInterleaved::cleanBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (m_buffer) { gl.deleteBuffers(1, &m_buffer); m_buffer = 0; } } const glw::GLchar* gl3cts::TransformFeedback::CaptureVertexInterleaved::s_vertex_shader_source_code_template = "#version 130\n" "\n" "TEMPLATE_INPUT_OUTPUT_DECLARATIONS" "\n" "void main()\n" "{\n" "TEMPLATE_OUTPUT_SETTERS" "\n" " vec4 position = vec4(0.0);\n" "\n" " /* Note: The points are moved 0.0625 from the borders to\n" " reduce non-XFB related rasterization problems. */\n" " switch(gl_VertexID)\n" " {\n" " case 0:\n" " position = vec4(-1.0 + TEMPLATE_RASTERIZATION_EPSILON, 1.0 - TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" " break;\n" " case 1:\n" " position = vec4( 1.0 - TEMPLATE_RASTERIZATION_EPSILON, 1.0 - TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" " break;\n" " case 2:\n" " position = vec4(-1.0 + TEMPLATE_RASTERIZATION_EPSILON, -1.0 + TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" " break;\n" " case 3:\n" " position = vec4( 1.0 - TEMPLATE_RASTERIZATION_EPSILON, -1.0 + TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" " break;\n" " }\n" "\n" " gl_Position = position;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::CaptureVertexInterleaved::s_fragment_shader_source_code = "#version 130\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(0.5);\n" "}\n"; const glw::GLuint gl3cts::TransformFeedback::CaptureVertexInterleaved::s_element_indices[][s_max_element_indices_count] = { { 0, 1, 2, 3 }, { 0, 1, 2, 3 }, { 0, 1, 3, 2 }, { 0, 1, 3, 2 }, { 2, 0, 1, 2, 1, 3 }, { 0, 1, 2, 3 }, { 2, 0, 1, 3 } }; const glw::GLuint gl3cts::TransformFeedback::CaptureVertexInterleaved::s_primitive_cases_count = sizeof(s_element_indices) / sizeof(s_element_indices[0]); const glw::GLenum gl3cts::TransformFeedback::CaptureVertexInterleaved::s_primitive_cases[] = { GL_POINTS, GL_LINES, GL_LINE_LOOP, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }; const glw::GLenum gl3cts::TransformFeedback::CaptureVertexInterleaved::s_primitive_cases_xfb[] = { GL_POINTS, GL_LINES, GL_LINES, GL_LINES, GL_TRIANGLES, GL_TRIANGLES, GL_TRIANGLES }; const glw::GLuint gl3cts::TransformFeedback::CaptureVertexInterleaved::s_element_indices_counts[] = { 4, 4, 4, 4, 6, 4, 4 }; const glw::GLuint gl3cts::TransformFeedback::CaptureVertexInterleaved::s_framebuffer_size = 2; /* If you change this, update checkFramebuffer function according. */ const glw::GLfloat gl3cts::TransformFeedback::CaptureVertexInterleaved::s_rasterization_epsilon = 0.0625; /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::CaptureGeometryInterleaved::CaptureGeometryInterleaved(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) { } gl3cts::TransformFeedback::CaptureGeometryInterleaved::~CaptureGeometryInterleaved(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::CaptureGeometryInterleaved::iterate(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initializations. */ bool is_at_least_gl_30 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 0))); bool is_at_least_gl_32 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 2))); bool is_ext_tf_1 = m_context.getContextInfo().isExtensionSupported("GL_EXT_transform_feedback"); bool is_arb_gs_4 = m_context.getContextInfo().isExtensionSupported("GL_ARB_geometry_shader4"); bool is_ok = true; bool test_error = false; /* Tests. */ try { if ((is_at_least_gl_30 || is_ext_tf_1) && (is_at_least_gl_32 || is_arb_gs_4)) { fetchLimits(); createFramebuffer(); createTransformFeedbackBuffer(); createVertexArrayObject(); for (glw::GLuint i_primitive_case = 0; (i_primitive_case < s_geometry_interleaved_primitive_cases_count) && is_ok; ++i_primitive_case) { buildProgram(i_primitive_case); gl.useProgram(m_program); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); for (glw::GLint i_bind_case = 0; (i_bind_case < BIND_BUFFER_CASES_COUNT) && is_ok; ++i_bind_case) { if ((i_bind_case == BIND_BUFFER_OFFSET_CASE) && (DE_NULL == m_glBindBufferOffsetEXT)) { continue; } bindBuffer((BindBufferCase)i_bind_case); draw(i_primitive_case); is_ok = is_ok && checkFramebuffer(s_primitive_cases[i_primitive_case]); is_ok = is_ok && checkTransformFeedbackBuffer((BindBufferCase)i_bind_case, s_geometry_interleaved_primitive_cases_xfb[i_primitive_case]); } gl.deleteProgram(m_program); m_program = 0; GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteProgram call failed."); } } } catch (...) { is_ok = false; test_error = true; } /* Clean objects. */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Capture Geometry have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::CaptureGeometryInterleaved::fetchLimits(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetching limits. */ gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &m_max_transform_feedback_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (m_max_transform_feedback_components == 0) { throw 0; } glw::GLint max_geometry_total_components = 0; gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &max_geometry_total_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_geometry_total_components == 0) { throw 0; } if (m_max_transform_feedback_components * 4 > max_geometry_total_components) { m_max_transform_feedback_components = max_geometry_total_components / 4; } } void gl3cts::TransformFeedback::CaptureGeometryInterleaved::buildProgram(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Preparing source code. */ std::string geometry_shader(s_geometry_shader_source_code_template); /* Storage for vertex shader source code. */ std::string transform_feedback_variable_declarations( ""); /* String to contain all custom outputs from vertex shader. */ std::string transform_feedback_variable_setters( ""); /* String containing all initializations of custom outputs from vertex shader. */ std::vector transform_feedback_varyings(m_max_transform_feedback_components); /* Varyings array. */ std::vector transform_feedback_varyings_c( m_max_transform_feedback_components); /* Varyings array in C form to pass to the GL. */ glw::GLint user_defined_transform_feedback_interleaved_varyings_count = m_max_transform_feedback_components /* total max to be written by the shader */ / 4 /* components per vec4 */ // / 4 /* number of vertices */ - 1 /* gl_Position */; glw::GLint all_transform_feedback_interleaved_varyings_count = user_defined_transform_feedback_interleaved_varyings_count + 1 /* gl_Position */; /* Most of varyings is declarated output variables. */ for (glw::GLint i = 0; i < user_defined_transform_feedback_interleaved_varyings_count; ++i) { /* Preparing variable name. */ std::string varying = "result_"; varying.append(gl3cts::TransformFeedback::Utilities::itoa(i)); transform_feedback_varyings[i] = varying; transform_feedback_varyings_c[i] = transform_feedback_varyings[i].c_str(); /* Preparing variable declaration. */ transform_feedback_variable_declarations.append("out vec4 "); transform_feedback_variable_declarations.append(varying); transform_feedback_variable_declarations.append(";\n"); /* Preparing variable setters. */ transform_feedback_variable_setters.append(" "); transform_feedback_variable_setters.append(varying); transform_feedback_variable_setters.append(" = vec4("); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4)); transform_feedback_variable_setters.append(".0, "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4 + 1)); transform_feedback_variable_setters.append(".0, "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4 + 2)); transform_feedback_variable_setters.append(".0, "); transform_feedback_variable_setters.append(gl3cts::TransformFeedback::Utilities::itoa(i * 4 + 3)); transform_feedback_variable_setters.append(".0);\n"); } /* Last four varying components are gl_Position components. */ transform_feedback_varyings[user_defined_transform_feedback_interleaved_varyings_count /* gl_Position */] = "gl_Position"; transform_feedback_varyings_c[user_defined_transform_feedback_interleaved_varyings_count /* gl_Position */] = transform_feedback_varyings[user_defined_transform_feedback_interleaved_varyings_count /* gl_Position */] .c_str(); /* Preprocess vertex shader source code template. */ geometry_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( geometry_shader, "TEMPLATE_PRIMITIVE_TYPE", s_geometry_interleaved_primitive_cases[primitive_case]); geometry_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( geometry_shader, "TEMPLATE_INPUT_OUTPUT_DECLARATIONS", transform_feedback_variable_declarations); geometry_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(geometry_shader, "TEMPLATE_OUTPUT_SETTERS", transform_feedback_variable_setters); geometry_shader = gl3cts::TransformFeedback::Utilities::preprocessCode( geometry_shader, "TEMPLATE_RASTERIZATION_EPSILON", gl3cts::TransformFeedback::Utilities::ftoa(s_rasterization_epsilon)); /* Compile, link and check. */ m_program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), geometry_shader.c_str(), NULL, NULL, s_blank_vertex_shader_source_code, s_fragment_shader_source_code, &transform_feedback_varyings_c[0], all_transform_feedback_interleaved_varyings_count, m_attrib_type); if (0 == m_program) { throw 0; } } void gl3cts::TransformFeedback::CaptureGeometryInterleaved::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); gl.beginTransformFeedback(s_geometry_interleaved_primitive_cases_xfb[primitive_case]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedbac call failed."); } const glw::GLchar* gl3cts::TransformFeedback::CaptureGeometryInterleaved::s_geometry_shader_source_code_template = "#version 150\n" "\n" "layout(points) in;\n" "layout(TEMPLATE_PRIMITIVE_TYPE, max_vertices = 4) out;\n" "\n" "TEMPLATE_INPUT_OUTPUT_DECLARATIONS" "\n" "void main()\n" "{\n" " /* Note: The points are moved 0.0625 from the borders to\n" " reduce non-XFB related rasterization problems. */\n" "\n" " gl_Position = vec4(-1.0 + TEMPLATE_RASTERIZATION_EPSILON, 1.0 - TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" "TEMPLATE_OUTPUT_SETTERS" " EmitVertex();\n" "\n" " gl_Position = vec4( 1.0 - TEMPLATE_RASTERIZATION_EPSILON, 1.0 - TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" "TEMPLATE_OUTPUT_SETTERS" " EmitVertex();\n" "\n" " gl_Position = vec4(-1.0 + TEMPLATE_RASTERIZATION_EPSILON, -1.0 + TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" "TEMPLATE_OUTPUT_SETTERS" " EmitVertex();\n" "\n" " gl_Position = vec4( 1.0 - TEMPLATE_RASTERIZATION_EPSILON, -1.0 + TEMPLATE_RASTERIZATION_EPSILON, 0.0, " "1.0);\n" "TEMPLATE_OUTPUT_SETTERS" " EmitVertex();\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::CaptureGeometryInterleaved::s_blank_vertex_shader_source_code = "#version 130\n" "\n" "void main()\n" "{\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::CaptureGeometryInterleaved::s_geometry_interleaved_primitive_cases[] = { "points", "line_strip", "triangle_strip" }; const glw::GLenum gl3cts::TransformFeedback::CaptureGeometryInterleaved::s_geometry_interleaved_primitive_cases_xfb[] = { GL_POINTS, GL_LINES, GL_TRIANGLES }; const glw::GLuint gl3cts::TransformFeedback::CaptureGeometryInterleaved::s_geometry_interleaved_primitive_cases_count = sizeof(s_geometry_interleaved_primitive_cases) / sizeof(s_geometry_interleaved_primitive_cases[0]); /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::CaptureVertexSeparate::CaptureVertexSeparate(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) , m_buffers(DE_NULL) , m_max_transform_feedback_separate_attribs(0) { m_attrib_type = GL_SEPARATE_ATTRIBS; } void gl3cts::TransformFeedback::CaptureVertexSeparate::fetchLimits(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetching limits. */ glw::GLint max_transform_feedback_separate_components; gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &max_transform_feedback_separate_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_transform_feedback_separate_components < 4) { throw 0; } gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &m_max_transform_feedback_separate_attribs); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (m_max_transform_feedback_separate_attribs == 0) { throw 0; } m_max_transform_feedback_components = m_max_transform_feedback_separate_attribs * 4 /* vec4 is used */; glw::GLint max_varyings_components = 0; gl.getIntegerv(GL_MAX_VARYING_COMPONENTS, &max_varyings_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_varyings_components == 0) { throw 0; } if (m_max_transform_feedback_components > max_varyings_components) { m_max_transform_feedback_components = max_varyings_components; } } void gl3cts::TransformFeedback::CaptureVertexSeparate::createTransformFeedbackBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); m_buffers = new glw::GLuint[m_max_transform_feedback_components]; if (DE_NULL == m_buffers) { throw 0; } gl.genBuffers(m_max_transform_feedback_separate_attribs, m_buffers); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); m_buffer_size = static_cast(m_max_vertices_drawn * 4 /* vec4 */ * sizeof(glw::GLfloat)); for (glw::GLint i = 0; i < m_max_transform_feedback_separate_attribs; ++i) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffers[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffer_size, NULL, GL_DYNAMIC_READ); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); } } void gl3cts::TransformFeedback::CaptureVertexSeparate::bindBuffer(BindBufferCase bind_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); switch (bind_case) { case BIND_BUFFER_BASE_CASE: for (glw::GLint i = 0; i < m_max_transform_feedback_separate_attribs; ++i) { gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, m_buffers[i]); } break; case BIND_BUFFER_RANGE_CASE: for (glw::GLint i = 0; i < m_max_transform_feedback_separate_attribs; ++i) { gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, i, m_buffers[i], 0, m_buffer_size); } break; case BIND_BUFFER_OFFSET_CASE: for (glw::GLint i = 0; i < m_max_transform_feedback_separate_attribs; ++i) { m_glBindBufferOffsetEXT(GL_TRANSFORM_FEEDBACK_BUFFER, i, m_buffers[i], 0); } break; default: throw 0; } } void gl3cts::TransformFeedback::CaptureVertexSeparate::cleanBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); if (DE_NULL != m_buffers) { gl.deleteBuffers(m_max_transform_feedback_separate_attribs, m_buffers); delete[] m_buffers; m_buffers = DE_NULL; } } bool gl3cts::TransformFeedback::CaptureVertexSeparate::checkTransformFeedbackBuffer(BindBufferCase bind_case UNUSED, glw::GLenum primitive_type) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint number_of_vertices = 0; switch (primitive_type) { case GL_POINTS: number_of_vertices = 4; break; case GL_LINES: number_of_vertices = 4; break; case GL_LINE_LOOP: number_of_vertices = 8; break; case GL_LINE_STRIP: number_of_vertices = 6; break; case GL_TRIANGLES: number_of_vertices = 6; break; case GL_TRIANGLE_STRIP: number_of_vertices = 6; break; case GL_TRIANGLE_FAN: number_of_vertices = 6; break; default: throw 0; } bool is_ok = true; for (glw::GLint i = 0; i < m_max_transform_feedback_separate_attribs - 1; ++i) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffers[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLfloat* results = (glw::GLfloat*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); for (glw::GLuint j = 0; (j < number_of_vertices) && is_ok; ++j) { for (glw::GLuint k = 0; k < 4 /* vec4 */; ++k) { glw::GLfloat result = results[j * 4 + k]; glw::GLfloat reference = (glw::GLfloat)(i * 4 + k); if (fabs(result - reference) > 0.125 /* precision */) { is_ok = false; break; } } } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); } /* gl_Position */ if (is_ok) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_buffers[m_max_transform_feedback_separate_attribs - 1]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLfloat* results = (glw::GLfloat*)gl.mapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, GL_READ_ONLY); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBuffer call failed."); for (glw::GLuint j = 0; (j < number_of_vertices) && is_ok; ++j) { glw::GLfloat result[4] = { results[j * 4], results[j * 4 + 1], results[j * 4 + 2], results[j * 4 + 3] }; if ((fabs(fabs(result[0]) - 1.0 + s_rasterization_epsilon) > 0.125 /* precision */) || (fabs(fabs(result[1]) - 1.0 + s_rasterization_epsilon) > 0.125 /* precision */) || (fabs(result[2]) > 0.125 /* precision */) || (fabs(result[3] - 1.0) > 0.125 /* precision */)) { is_ok = false; break; } } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); } return is_ok; } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::CaptureGeometrySeparate::CaptureGeometrySeparate(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) , CaptureVertexSeparate(context, test_name, test_description) , CaptureGeometryInterleaved(context, test_name, test_description) , m_buffers(DE_NULL) , m_max_transform_feedback_separate_attribs(0) { } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::CheckGetXFBVarying::CheckGetXFBVarying(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_max_xfb_interleaved_components(0) , m_max_xfb_separate_attributes(0) , m_max_xfb_separate_components(0) , m_max_varying_components(0) , m_max_varying_vectors(0) , m_max_geometry_total_output_components(0) { } gl3cts::TransformFeedback::CheckGetXFBVarying::~CheckGetXFBVarying(void) { } void gl3cts::TransformFeedback::CheckGetXFBVarying::fetchLimits(void) { const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetching limits. */ gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS, &m_max_xfb_interleaved_components); gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &m_max_xfb_separate_attributes); gl.getIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS, &m_max_xfb_separate_components); gl.getIntegerv(GL_MAX_VARYING_COMPONENTS, &m_max_varying_components); gl.getIntegerv(GL_MAX_VARYING_VECTORS, &m_max_varying_vectors); gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &m_max_geometry_total_output_components); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); } glw::GLuint gl3cts::TransformFeedback::CheckGetXFBVarying::numberOfAttributes(glw::GLuint capture_way, glw::GLuint shader_case, glw::GLuint varying_type) { /* Setup limits of the case. */ const glw::GLuint max_total_components = ((s_shader_cases[shader_case].geometry_shader == DE_NULL) ? m_max_varying_components : m_max_geometry_total_output_components) - 4 /* gl_Position is not captured */; const glw::GLuint attribute_components = s_varying_types[varying_type].components_count; const glw::GLuint max_xfb_components = (s_capture_ways[capture_way] == GL_INTERLEAVED_ATTRIBS) ? m_max_xfb_interleaved_components : (attribute_components * m_max_xfb_separate_components); if (s_capture_ways[capture_way] == GL_SEPARATE_ATTRIBS) { if (attribute_components > glw::GLuint(m_max_xfb_separate_components)) { return 0; } } /* Setup number of attributes. */ glw::GLuint number_of_attributes = max_xfb_components / attribute_components; if (s_capture_ways[capture_way] == GL_SEPARATE_ATTRIBS && number_of_attributes > glw::GLuint(m_max_xfb_separate_attributes)) { number_of_attributes = m_max_xfb_separate_attributes; } /* Clamp to limits. */ if (number_of_attributes * attribute_components > max_total_components) { number_of_attributes = max_total_components / attribute_components; } /* Vectors limit. */ if (attribute_components <= 4) { if (number_of_attributes > glw::GLuint(m_max_varying_vectors)) { number_of_attributes = m_max_varying_vectors; } } else { if (number_of_attributes > glw::GLuint(m_max_varying_vectors) / 4) { number_of_attributes = glw::GLuint(m_max_varying_vectors) / 4; } } /* Return. */ return number_of_attributes; } tcu::TestNode::IterateResult gl3cts::TransformFeedback::CheckGetXFBVarying::iterate(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initializations. */ bool is_at_least_gl_30 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 0))); bool is_at_least_gl_32 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 2))); bool is_ext_tf_1 = m_context.getContextInfo().isExtensionSupported("GL_EXT_transform_feedback"); bool is_arb_gs_4 = m_context.getContextInfo().isExtensionSupported("GL_ARB_geometry_shader4"); bool is_ok = true; bool test_error = false; glw::GLuint program = 0; /* Tests. */ try { if ((is_at_least_gl_30 || is_ext_tf_1) && (is_at_least_gl_32 || is_arb_gs_4)) { fetchLimits(); for (glw::GLuint i = 0; (i < s_capture_ways_count) && is_ok; ++i) { for (glw::GLuint j = 0; (j < s_shader_cases_count) && is_ok; ++j) { for (glw::GLuint k = 0; (k < s_varying_types_count) && is_ok; ++k) { glw::GLuint n = numberOfAttributes(i, j, k); if (n) { program = buildProgram(i, j, k, n); is_ok = is_ok && (program != 0); is_ok = is_ok && check(program, i, j, k, n); gl.deleteProgram(program); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteProgram call failed."); program = 0; } } } } } } catch (...) { is_ok = false; test_error = true; if (program) { gl.deleteProgram(program); program = 0; } } /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Test checking Get Transform Feedback Varying have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } glw::GLuint gl3cts::TransformFeedback::CheckGetXFBVarying::buildProgram(glw::GLuint capture_way, glw::GLuint shader_case, glw::GLuint varying_type, glw::GLuint number_of_attributes) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Preparing source code. */ std::string xfb_variable_declarations(""); std::string xfb_variable_setters(""); std::vector xfb_varyings(number_of_attributes); std::vector xfb_varyings_c(number_of_attributes); /* Most of varyings is declarated output variables. */ for (glw::GLuint i = 0; i < number_of_attributes; ++i) { /* Varying name: result_# */ std::string varying = "result_"; varying.append(gl3cts::TransformFeedback::Utilities::itoa(i)); xfb_varyings[i] = varying; xfb_varyings_c[i] = xfb_varyings[i].c_str(); /* Varying declaration: out TYPE result_#;*/ xfb_variable_declarations.append("out "); xfb_variable_declarations.append(s_varying_types[varying_type].name); xfb_variable_declarations.append(" "); xfb_variable_declarations.append(varying); xfb_variable_declarations.append(";\n"); /* Varying setter: result_# = TYPE(#); */ xfb_variable_setters.append(" "); xfb_variable_setters.append(varying); xfb_variable_setters.append(" = "); xfb_variable_setters.append(s_varying_types[varying_type].name); xfb_variable_setters.append("("); xfb_variable_setters.append("2"); //gl3cts::TransformFeedback::Utilities::itoa(i)); if (s_varying_types[varying_type].float_component) { /* if varying is float varying setter is: result_# = TYPE(#.0); */ xfb_variable_setters.append(".0"); } xfb_variable_setters.append(");\n"); } /* Preprocess vertex shader source code template. */ const glw::GLchar* vertex_shader = s_shader_cases[shader_case].vertex_shader; const glw::GLchar* geometry_shader = s_shader_cases[shader_case].geometry_shader; std::string xfb_shader; if (DE_NULL == s_shader_cases[shader_case].geometry_shader) { /* XFB tested in vertex shader. */ xfb_shader = vertex_shader; } else { /* XFB tested in geometry shader. */ xfb_shader = geometry_shader; } /* Preprocess shader. */ xfb_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(xfb_shader, "TEMPLATE_OUTPUT_DECLARATIONS", xfb_variable_declarations); xfb_shader = gl3cts::TransformFeedback::Utilities::preprocessCode(xfb_shader, "TEMPLATE_OUTPUT_SETTERS", xfb_variable_setters); if (DE_NULL == s_shader_cases[shader_case].geometry_shader) { /* XFB tested in vertex shader. */ vertex_shader = xfb_shader.c_str(); } else { /* XFB tested in geometry shader. */ geometry_shader = xfb_shader.c_str(); } /* Compile, link and check. */ glw::GLuint program = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), geometry_shader, NULL, NULL, vertex_shader, s_generic_fragment_shader, &xfb_varyings_c[0], number_of_attributes, s_capture_ways[capture_way]); /* Check compilation status. */ if (0 == program) { m_context.getTestContext().getLog() << tcu::TestLog::Message << "Building program has failed.\nVertex shader:\n" << vertex_shader << "Geometry shader:\n" << ((DE_NULL == geometry_shader) ? "" : geometry_shader) << "Fragment shader:\n" << s_generic_fragment_shader << tcu::TestLog::EndMessage; throw 0; } return program; } bool gl3cts::TransformFeedback::CheckGetXFBVarying::check(glw::GLuint program, glw::GLuint capture_way, glw::GLuint shader_case UNUSED, glw::GLuint varying_type, glw::GLuint number_of_attributes) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint max_length = 0; /* Inspect glGetTransformFeedbackVarying. */ for (glw::GLuint i = 0; i < number_of_attributes; ++i) { const glw::GLsizei bufSize = 18; glw::GLsizei length = 0; glw::GLsizei size = 0; glw::GLenum type = GL_NONE; glw::GLchar name[18] = { 0 }; /* Size of bufSize. */ gl.getTransformFeedbackVarying(program, i, bufSize, &length, &size, &type, name); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetTransformFeedbackVarying call failed."); max_length = deMaxu32(max_length, glw::GLuint(length)); /* Check name. */ if (length) { std::string varying = name; std::string varying_ref = "result_"; varying_ref.append(gl3cts::TransformFeedback::Utilities::itoa(i)); if (0 != varying.compare(varying_ref)) { return false; } } else { return false; } /* Check size. */ const glw::GLuint size_ref = 1; if (size != size_ref) { return false; } /* Check type. */ if (type != s_varying_types[varying_type].type) { return false; } } /* Inspect glGetProgramiv. */ glw::GLint xfb_varyings = 0; glw::GLint xfb_mode = 0; glw::GLint xfb_varying_max_length = 0; gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYINGS, &xfb_varyings); gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_BUFFER_MODE, &xfb_mode); gl.getProgramiv(program, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH, &xfb_varying_max_length); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); if (glw::GLuint(xfb_varyings) != number_of_attributes) { return false; } if (glw::GLenum(xfb_mode) != s_capture_ways[capture_way]) { return false; } if (glw::GLuint(xfb_varying_max_length) < max_length) { return false; } return true; } const glw::GLchar* gl3cts::TransformFeedback::CheckGetXFBVarying::s_generic_fragment_shader = "#version 130\n" "\n" "out vec4 color;\n" "\n" "void main()\n" "{\n" " color = vec4(1.0);\n" "}\n"; const struct gl3cts::TransformFeedback::CheckGetXFBVarying::ShaderCase gl3cts::TransformFeedback::CheckGetXFBVarying::s_shader_cases[] = { { /* Vertex Shader. */ "#version 130\n" "\n" "TEMPLATE_OUTPUT_DECLARATIONS" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" "TEMPLATE_OUTPUT_SETTERS" "}\n", /* Geometry Shader. */ NULL }, { /* Vertex Shader. */ "#version 130\n" "\n" "void main()\n" "{\n" "}\n", /* Geometry Shader. */ "#version 150\n" "\n" "layout(points) in;\n" "layout(points, max_vertices = 1) out;\n" "\n" "TEMPLATE_OUTPUT_DECLARATIONS" "\n" "void main()\n" "{\n" " gl_Position = vec4(1.0);\n" "TEMPLATE_OUTPUT_SETTERS" " EmitVertex();\n" "}\n" } }; const glw::GLuint gl3cts::TransformFeedback::CheckGetXFBVarying::s_shader_cases_count = sizeof(s_shader_cases) / sizeof(s_shader_cases[0]); const struct gl3cts::TransformFeedback::CheckGetXFBVarying::VaryingType gl3cts::TransformFeedback::CheckGetXFBVarying::s_varying_types[] = { /* type, name, #components, is component float */ { GL_FLOAT, "float", 1, true }, { GL_FLOAT_VEC2, "vec2", 2, true }, { GL_FLOAT_VEC3, "vec3", 3, true }, { GL_FLOAT_VEC4, "vec4", 4, true }, { GL_INT, "int", 1, false }, { GL_INT_VEC2, "ivec2", 2, false }, { GL_INT_VEC3, "ivec3", 3, false }, { GL_INT_VEC4, "ivec4", 4, false }, { GL_UNSIGNED_INT, "uint", 1, false }, { GL_UNSIGNED_INT_VEC2, "uvec2", 2, false }, { GL_UNSIGNED_INT_VEC3, "uvec3", 3, false }, { GL_UNSIGNED_INT_VEC4, "uvec4", 4, false }, { GL_FLOAT_MAT2, "mat2", 4, true }, { GL_FLOAT_MAT3, "mat3", 9, true }, { GL_FLOAT_MAT4, "mat4", 16, true } }; const glw::GLuint gl3cts::TransformFeedback::CheckGetXFBVarying::s_varying_types_count = sizeof(s_varying_types) / sizeof(s_varying_types[0]); const glw::GLenum gl3cts::TransformFeedback::CheckGetXFBVarying::s_capture_ways[] = { GL_INTERLEAVED_ATTRIBS, GL_SEPARATE_ATTRIBS }; const glw::GLuint gl3cts::TransformFeedback::CheckGetXFBVarying::s_capture_ways_count = sizeof(s_capture_ways) / sizeof(s_capture_ways[0]); /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::QueryVertexInterleaved::QueryVertexInterleaved(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description), m_query_object(0) { m_max_vertices_drawn = 3; /* Make buffer smaller up to 3 vertices. */ } void gl3cts::TransformFeedback::QueryVertexInterleaved::createTransformFeedbackBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create buffer object. */ gl3cts::TransformFeedback::CaptureVertexInterleaved::createTransformFeedbackBuffer(); /* Create query object. */ gl.genQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries call failed."); } void gl3cts::TransformFeedback::QueryVertexInterleaved::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery call failed."); gl3cts::TransformFeedback::CaptureVertexInterleaved::draw(primitive_case); gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery call failed."); } bool gl3cts::TransformFeedback::QueryVertexInterleaved::checkTransformFeedbackBuffer(BindBufferCase bind_case UNUSED, glw::GLenum primitive_type) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint number_of_primitives; gl.getQueryObjectuiv(m_query_object, GL_QUERY_RESULT, &number_of_primitives); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv call failed."); /* expected result */ glw::GLuint number_of_primitives_reference = (primitive_type == GL_POINTS) ? 3 : 1; /* m_max_vertices_drawn == 3 */ if (number_of_primitives_reference != number_of_primitives) { return false; } return true; } void gl3cts::TransformFeedback::QueryVertexInterleaved::clean(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Delete query object. */ gl.deleteQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteQueries call failed."); /* Other */ gl3cts::TransformFeedback::CaptureVertexInterleaved::clean(); } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::QueryGeometryInterleaved::QueryGeometryInterleaved(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) , CaptureGeometryInterleaved(context, test_name, test_description) { m_query_object = 0; m_max_vertices_drawn = 3; /* Make buffer smaller up to 3 vertices. */ } void gl3cts::TransformFeedback::QueryGeometryInterleaved::createTransformFeedbackBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create buffer object. */ gl3cts::TransformFeedback::CaptureGeometryInterleaved::createTransformFeedbackBuffer(); /* Create query object. */ gl.genQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries call failed."); } void gl3cts::TransformFeedback::QueryGeometryInterleaved::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery call failed."); gl3cts::TransformFeedback::CaptureGeometryInterleaved::draw(primitive_case); gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery call failed."); } bool gl3cts::TransformFeedback::QueryGeometryInterleaved::checkTransformFeedbackBuffer(BindBufferCase bind_case UNUSED, glw::GLenum primitive_type) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint number_of_primitives; gl.getQueryObjectuiv(m_query_object, GL_QUERY_RESULT, &number_of_primitives); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv call failed."); /* expected result */ glw::GLuint number_of_primitives_reference = (primitive_type == GL_POINTS) ? 3 : 1; /* m_max_vertices_drawn == 3 */ if (number_of_primitives_reference != number_of_primitives) { return false; } return true; } void gl3cts::TransformFeedback::QueryGeometryInterleaved::clean(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Delete query object. */ gl.deleteQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteQueries call failed."); /* Other. */ gl3cts::TransformFeedback::CaptureGeometryInterleaved::clean(); } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::QueryVertexSeparate::QueryVertexSeparate(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) , CaptureVertexSeparate(context, test_name, test_description) { m_query_object = 0; m_max_vertices_drawn = 3; /* Make buffer smaller up to 3 vertices. */ } void gl3cts::TransformFeedback::QueryVertexSeparate::createTransformFeedbackBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create buffer object. */ gl3cts::TransformFeedback::CaptureVertexSeparate::createTransformFeedbackBuffer(); /* Create query object. */ gl.genQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries call failed."); } void gl3cts::TransformFeedback::QueryVertexSeparate::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery call failed."); gl3cts::TransformFeedback::CaptureVertexSeparate::draw(primitive_case); gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery call failed."); } bool gl3cts::TransformFeedback::QueryVertexSeparate::checkTransformFeedbackBuffer(BindBufferCase bind_case UNUSED, glw::GLenum primitive_type) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint number_of_primitives; gl.getQueryObjectuiv(m_query_object, GL_QUERY_RESULT, &number_of_primitives); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv call failed."); /* expected result */ glw::GLuint number_of_primitives_reference = (primitive_type == GL_POINTS) ? 3 : 1; /* m_max_vertices_drawn == 3 */ if (number_of_primitives_reference != number_of_primitives) { return false; } return true; } void gl3cts::TransformFeedback::QueryVertexSeparate::clean(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Delete query object. */ gl.deleteQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteQueries call failed."); /* Other */ gl3cts::TransformFeedback::CaptureVertexSeparate::clean(); } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::QueryGeometrySeparate::QueryGeometrySeparate(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) , CaptureVertexSeparate(context, test_name, test_description) , CaptureGeometrySeparate(context, test_name, test_description) { m_query_object = 0; m_max_vertices_drawn = 3; /* Make buffer smaller up to 3 vertices. */ } void gl3cts::TransformFeedback::QueryGeometrySeparate::createTransformFeedbackBuffer(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Create buffer object. */ gl3cts::TransformFeedback::CaptureGeometrySeparate::createTransformFeedbackBuffer(); /* Create query object. */ gl.genQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries call failed."); } void gl3cts::TransformFeedback::QueryGeometrySeparate::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.beginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery call failed."); gl3cts::TransformFeedback::CaptureGeometrySeparate::draw(primitive_case); gl.endQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery call failed."); } bool gl3cts::TransformFeedback::QueryGeometrySeparate::checkTransformFeedbackBuffer(BindBufferCase bind_case UNUSED, glw::GLenum primitive_type) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLuint number_of_primitives; gl.getQueryObjectuiv(m_query_object, GL_QUERY_RESULT, &number_of_primitives); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryObjectuiv call failed."); /* expected result */ glw::GLuint number_of_primitives_reference = (primitive_type == GL_POINTS) ? 3 : 1; /* m_max_vertices_drawn == 3 */ if (number_of_primitives_reference != number_of_primitives) { return false; } return true; } void gl3cts::TransformFeedback::QueryGeometrySeparate::clean(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Delete query object. */ gl.deleteQueries(1, &m_query_object); GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteQueries call failed."); /* Other */ gl3cts::TransformFeedback::CaptureGeometrySeparate::clean(); } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DiscardVertex::DiscardVertex(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) { } void gl3cts::TransformFeedback::DiscardVertex::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Must clear before rasterizer discard */ gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl3cts::TransformFeedback::CaptureVertexInterleaved::draw(primitive_case); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } bool gl3cts::TransformFeedback::DiscardVertex::checkFramebuffer(glw::GLuint primitive_case UNUSED) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch framebuffer. */ std::vector pixels(s_framebuffer_size * s_framebuffer_size); if (s_framebuffer_size > 0) { gl.readPixels(0, 0, s_framebuffer_size, s_framebuffer_size, GL_RED, GL_FLOAT, pixels.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); } /* Check results. Note: assuming that s_buffer_size == 2 -> then all points shall be drawn. */ for (std::vector::iterator i = pixels.begin(); i != pixels.end(); ++i) { if (fabs(*i) > 0.0625f /* precision */) { return false; } } return true; } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DiscardGeometry::DiscardGeometry(deqp::Context& context, const char* test_name, const char* test_description) : CaptureVertexInterleaved(context, test_name, test_description) , CaptureGeometryInterleaved(context, test_name, test_description) { } void gl3cts::TransformFeedback::DiscardGeometry::draw(glw::GLuint primitive_case) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Must clear before rasterizer discard */ gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl3cts::TransformFeedback::CaptureGeometryInterleaved::draw(primitive_case); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } bool gl3cts::TransformFeedback::DiscardGeometry::checkFramebuffer(glw::GLuint primitive_case UNUSED) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Fetch framebuffer. */ std::vector pixels(s_framebuffer_size * s_framebuffer_size); if (s_framebuffer_size > 0) { gl.readPixels(0, 0, s_framebuffer_size, s_framebuffer_size, GL_RED, GL_FLOAT, pixels.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); } /* Check results. Note: assuming that s_buffer_size == 2 -> then all points shall be drawn. */ for (std::vector::iterator i = pixels.begin(); i != pixels.end(); ++i) { if (fabs(*i) > 0.0625f /* precision */) { return false; } } return true; } /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DrawXFB::DrawXFB(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program_id_xfb(0) , m_program_id_draw(0) , m_fbo_id(0) , m_rbo_id(0) , m_vao_id(0) { memset(m_xfb_id, 0, sizeof(m_xfb_id)); memset(m_bo_id, 0, sizeof(m_bo_id)); } gl3cts::TransformFeedback::DrawXFB::~DrawXFB(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::DrawXFB::iterate(void) { /* Initializations. */ bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_arb_tf_2 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback2"); bool is_ok = true; bool test_error = false; /* Tests. */ try { if (is_at_least_gl_40 || is_arb_tf_2) { for (glw::GLuint i = 0; (i < s_capture_modes_count) && is_ok; ++i) { prepare(s_capture_modes[i]); bindVAO(m_vao_id); useProgram(m_program_id_xfb); for (glw::GLuint j = 0; (j < s_xfb_count) && is_ok; ++j) { bindXFB(m_xfb_id[j]); bindBOForXFB(s_capture_modes[i], m_bo_id[j]); useColour(m_program_id_xfb, s_colours[j][0], s_colours[j][1], s_colours[j][2], s_colours[j][3]); useGeometrySet(m_program_id_xfb, false); drawForCapture(true, true, false, false); is_ok = is_ok && inspectXFBState(true, true); } for (glw::GLuint j = 0; (j < s_xfb_count) && is_ok; ++j) { bindXFB(m_xfb_id[j]); useColour(m_program_id_xfb, s_colours[j][0], s_colours[j][1], s_colours[j][2], s_colours[j][3]); useGeometrySet(m_program_id_xfb, true); drawForCapture(false, false, true, true); is_ok = is_ok && inspectXFBState(false, false); } useProgram(m_program_id_draw); for (glw::GLuint j = 0; (j < s_xfb_count) && is_ok; ++j) { bindXFB(m_xfb_id[j]); bindBOForDraw(m_program_id_draw, s_capture_modes[i], m_bo_id[j]); drawToFramebuffer(m_xfb_id[j]); is_ok = is_ok && checkFramebuffer(s_colours[j][0], s_colours[j][1], s_colours[j][2], s_colours[j][3]); } clean(); } } } catch (...) { is_ok = false; test_error = true; clean(); } /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { /* Log error. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB have approached error." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { /* Log fail. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB have failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::DrawXFB::prepare(glw::GLenum capture_mode) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare programs. */ m_program_id_xfb = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_xfb, s_fragment_shader, s_xfb_varyings, s_xfb_varyings_count, capture_mode); if (0 == m_program_id_xfb) { throw 0; } m_program_id_draw = gl3cts::TransformFeedback::Utilities::buildProgram(gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_draw, s_fragment_shader, NULL, 0, capture_mode); if (0 == m_program_id_draw) { throw 0; } /* Prepare transform feedbacks. */ gl.genTransformFeedbacks(s_xfb_count, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); /* Prepare buffer objects. */ gl.genBuffers(s_xfb_count, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); for (glw::GLuint i = 0; i < s_xfb_count; ++i) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_capture_size, NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); } /* Prepare framebuffer. */ gl.clearColor(0.f, 0.f, 0.f, 1.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.genFramebuffers(1, &m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); /* Create empty Vertex Array Object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); } void gl3cts::TransformFeedback::DrawXFB::bindXFB(glw::GLuint xfb_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedback call failed."); } void gl3cts::TransformFeedback::DrawXFB::bindVAO(glw::GLuint vao_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindVertexArray(vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); } void gl3cts::TransformFeedback::DrawXFB::bindBOForXFB(glw::GLenum capture_mode, glw::GLuint bo_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); switch (capture_mode) { case GL_INTERLEAVED_ATTRIBS: gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); break; case GL_SEPARATE_ATTRIBS: for (glw::GLuint i = 0; i < s_xfb_varyings_count; ++i) { gl.bindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, i, bo_id, i * s_capture_size / s_xfb_varyings_count, (i + 1) * s_capture_size / s_xfb_varyings_count); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase call failed."); } break; default: throw 0; } } void gl3cts::TransformFeedback::DrawXFB::bindBOForDraw(glw::GLuint program_id, glw::GLenum capture_mode, glw::GLuint bo_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindBuffer(GL_ARRAY_BUFFER, bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLuint position_location = gl.getAttribLocation(program_id, "position"); glw::GLuint color_location = gl.getAttribLocation(program_id, "color"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation call failed."); glw::GLvoid* color_offset = (capture_mode == GL_INTERLEAVED_ATTRIBS) ? (glw::GLvoid*)(4 /* components */ * sizeof(glw::GLfloat)) : (glw::GLvoid*)(4 /* components */ * 6 /* vertices */ * sizeof(glw::GLfloat)); glw::GLuint stride = static_cast((capture_mode == GL_INTERLEAVED_ATTRIBS) ? (4 /* components */ * 2 /* position and color */ * sizeof(glw::GLfloat)) : (4 /* components */ * sizeof(glw::GLfloat))); gl.vertexAttribPointer(position_location, 4, GL_FLOAT, GL_FALSE, stride, NULL); gl.vertexAttribPointer(color_location, 4, GL_FLOAT, GL_FALSE, stride, color_offset); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(position_location); gl.enableVertexAttribArray(color_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); } void gl3cts::TransformFeedback::DrawXFB::useProgram(glw::GLuint program_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); } void gl3cts::TransformFeedback::DrawXFB::useColour(glw::GLuint program_id, glw::GLfloat r, glw::GLfloat g, glw::GLfloat b, glw::GLfloat a) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint location = gl.getUniformLocation(program_id, "color"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation call failed."); gl.uniform4f(location, r, g, b, a); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4f call failed."); } void gl3cts::TransformFeedback::DrawXFB::useGeometrySet(glw::GLuint program_id, bool invert_sign) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint location = gl.getUniformLocation(program_id, "invert_sign"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation call failed."); gl.uniform1f(location, invert_sign ? -1.f : 1.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniform4f call failed."); } void gl3cts::TransformFeedback::DrawXFB::clean() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_program_id_xfb) { gl.deleteProgram(m_program_id_xfb); m_program_id_xfb = 0; } if (m_program_id_draw) { gl.deleteProgram(m_program_id_draw); m_program_id_draw = 1; } for (glw::GLuint i = 0; i < s_xfb_count; ++i) { if (m_xfb_id[i]) { gl.deleteTransformFeedbacks(1, &m_xfb_id[i]); m_xfb_id[i] = 0; } } for (glw::GLuint i = 0; i < s_xfb_count; ++i) { if (m_bo_id[i]) { gl.deleteBuffers(1, &m_bo_id[i]); m_bo_id[i] = 0; } } if (m_vao_id) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } if (m_fbo_id) { gl.deleteFramebuffers(1, &m_fbo_id); m_fbo_id = 0; } if (m_rbo_id) { gl.deleteRenderbuffers(1, &m_rbo_id); m_rbo_id = 0; } } void gl3cts::TransformFeedback::DrawXFB::drawForCapture(bool begin_xfb, bool pause_xfb, bool resume_xfb, bool end_xfb) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); if (begin_xfb) { gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); } if (resume_xfb) { gl.resumeTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glResumeTransformFeedback call failed."); } gl.drawArrays(GL_POINTS, 0, 3); if (pause_xfb) { gl.pauseTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glPauseTransformFeedback call failed."); } if (end_xfb) { gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); } gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } void gl3cts::TransformFeedback::DrawXFB::drawToFramebuffer(glw::GLuint xfb_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.clearColor(0.f, 0.f, 0.f, 0.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); gl.drawTransformFeedback(GL_TRIANGLES, xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawTransformFeedback call failed."); } bool gl3cts::TransformFeedback::DrawXFB::checkFramebuffer(glw::GLfloat r, glw::GLfloat g, glw::GLfloat b, glw::GLfloat a) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Number of pixels. */ const glw::GLuint number_of_pixels = s_view_size * s_view_size; /* Fetch framebuffer. */ std::vector pixels(number_of_pixels * 4 /* components */); if (s_view_size > 0) { gl.readPixels(0, 0, s_view_size, s_view_size, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data()); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); } /* Convert color to integer. */ glw::GLubyte ir = (glw::GLubyte)(255.f * r); glw::GLubyte ig = (glw::GLubyte)(255.f * g); glw::GLubyte ib = (glw::GLubyte)(255.f * b); glw::GLubyte ia = (glw::GLubyte)(255.f * a); /* Check results. */ for (glw::GLuint i = 0; i < number_of_pixels; ++i) { if ((pixels[i * 4 /* components */] != ir) || (pixels[i * 4 /* components */ + 1] != ig) || (pixels[i * 4 /* components */ + 2] != ib) || (pixels[i * 4 /* components */ + 3] != ia)) { return false; } } return true; } bool gl3cts::TransformFeedback::DrawXFB::inspectXFBState(bool shall_be_paused, bool shall_be_active) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint is_paused = 0; glw::GLint is_active = 0; gl.getIntegerv(GL_TRANSFORM_FEEDBACK_PAUSED, &is_paused); gl.getIntegerv(GL_TRANSFORM_FEEDBACK_ACTIVE, &is_active); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if ((is_paused == -1) || (is_active == -1)) { throw 0; } if (shall_be_paused ^ (is_paused == GL_TRUE)) { return false; } if (shall_be_active ^ (is_active == GL_TRUE)) { return false; } return true; } const glw::GLchar* gl3cts::TransformFeedback::DrawXFB::s_vertex_shader_xfb = "#version 130\n" "\n" "uniform vec4 color;\n" "uniform float invert_sign;\n" "out vec4 colour;\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 * invert_sign, 1.0, 0.0, 1.0);\n" " break;\n" " case 2:\n" " gl_Position = vec4( 1.0, 1.0 * invert_sign, 0.0, 1.0);\n" " break;\n" " }\n" "\n" " colour = color;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFB::s_vertex_shader_draw = "#version 130\n" "\n" "in vec4 color;\n" "in vec4 position;\n" "out vec4 colour;\n" "\n" "void main()\n" "{\n" " gl_Position = position;\n" " colour = color;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFB::s_fragment_shader = "#version 130\n" "\n" "in vec4 colour;\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = colour;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFB::s_xfb_varyings[s_xfb_varyings_count] = { "gl_Position", "colour" }; const glw::GLenum gl3cts::TransformFeedback::DrawXFB::s_capture_modes[] = { GL_INTERLEAVED_ATTRIBS, GL_SEPARATE_ATTRIBS }; const glw::GLuint gl3cts::TransformFeedback::DrawXFB::s_capture_modes_count = sizeof(s_capture_modes) / sizeof(s_capture_modes[0]); const glw::GLfloat gl3cts::TransformFeedback::DrawXFB::s_colours[s_xfb_count][4] = { { 1.f, 0.f, 0.f, 1.f }, { 0.f, 1.f, 0.f, 1.f }, { 0.f, 0.f, 1.f, 1.f } }; /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DrawXFBFeedback::DrawXFBFeedback(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program_id(0) , m_xfb_id(0) , m_source_bo_index(0) { memset(m_bo_id, 1, sizeof(m_bo_id)); memset(m_bo_id, 1, sizeof(m_vao_id)); } gl3cts::TransformFeedback::DrawXFBFeedback::~DrawXFBFeedback(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::DrawXFBFeedback::iterate(void) { /* Initializations. */ bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_arb_tf_2 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback2"); bool is_ok = true; bool test_error = false; /* Tests. */ try { if (is_at_least_gl_40 || is_arb_tf_2) { prepareAndBind(); draw(true); swapBuffers(); draw(false); swapBuffers(); draw(false); is_ok = is_ok && check(); } } catch (...) { is_ok = false; test_error = true; } /* Clean GL objects. */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Feedback have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { /* Log error. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Feedback have approached error." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { /* Log fail. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Feedback have failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::DrawXFBFeedback::prepareAndBind() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare programs. */ m_program_id = gl3cts::TransformFeedback::Utilities::buildProgram(gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader, s_fragment_shader, &s_xfb_varying, 1, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id) { throw 0; } gl.useProgram(m_program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); /* Prepare transform feedbacks. */ gl.genTransformFeedbacks(1, &m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedbacks call failed."); /* Prepare buffer objects. */ gl.genBuffers(s_bo_count, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_ARRAY_BUFFER, s_bo_size, s_initial_data, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_id[1]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_bo_size, NULL, GL_DYNAMIC_COPY); GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); /* Setup vertex arrays. */ gl.genVertexArrays(s_bo_count, m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); glw::GLuint position_location = gl.getAttribLocation(m_program_id, "position"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation call failed."); for (glw::GLuint i = 0; i < 2; ++i) { gl.bindVertexArray(m_vao_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.vertexAttribPointer(position_location, 4, GL_FLOAT, GL_FALSE, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(position_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); } gl.bindBuffer(GL_ARRAY_BUFFER, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bindVertexArray(m_vao_id[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); } void gl3cts::TransformFeedback::DrawXFBFeedback::swapBuffers() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_id[m_source_bo_index]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); m_source_bo_index = (m_source_bo_index + 1) % 2; gl.bindVertexArray(m_vao_id[(m_source_bo_index)]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); } void gl3cts::TransformFeedback::DrawXFBFeedback::draw(bool is_first_draw) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); if (is_first_draw) { gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); } else { gl.drawTransformFeedback(GL_POINTS, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawTransformFeedback call failed."); } gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } bool gl3cts::TransformFeedback::DrawXFBFeedback::check() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLfloat* results = (glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, s_bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange call failed."); bool is_ok = false; if (results) { if ((results[0] == 8.f) && (results[1] == 16.f) && (results[2] == 24.f) && (results[3] == 32.f)) { is_ok = true; } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); } return is_ok; } void gl3cts::TransformFeedback::DrawXFBFeedback::clean() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_program_id) { gl.deleteProgram(m_program_id); m_program_id = 0; } if (m_xfb_id) { gl.deleteTransformFeedbacks(1, &m_xfb_id); m_xfb_id = 0; } for (glw::GLuint i = 0; i < s_bo_count; ++i) { if (m_bo_id[i]) { gl.deleteBuffers(1, &m_bo_id[i]); m_bo_id[i] = 0; } } for (glw::GLuint i = 0; i < s_bo_count; ++i) { if (m_vao_id[i]) { gl.deleteVertexArrays(1, &m_vao_id[i]); m_vao_id[i] = 0; } } } const glw::GLchar* gl3cts::TransformFeedback::DrawXFBFeedback::s_vertex_shader = "#version 130\n" "\n" "in vec4 position;\n" "\n" "void main()\n" "{\n" " gl_Position = position * 2.0;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBFeedback::s_fragment_shader = "#version 130\n" "\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBFeedback::s_xfb_varying = "gl_Position"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBFeedback::s_attrib = "position"; const glw::GLfloat gl3cts::TransformFeedback::DrawXFBFeedback::s_initial_data[] = { 1.f, 2.f, 3.f, 4.f }; const glw::GLuint gl3cts::TransformFeedback::DrawXFBFeedback::s_draw_vertex_count = sizeof(s_initial_data) / sizeof(s_initial_data[0]) / 4 /* components */; const glw::GLuint gl3cts::TransformFeedback::DrawXFBFeedback::s_bo_size = sizeof(s_initial_data); /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::CaptureSpecialInterleaved::CaptureSpecialInterleaved(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program_id(0) , m_vao_id(0) , m_xfb_id(0) { memset(m_bo_id, 0, sizeof(m_bo_id)); } gl3cts::TransformFeedback::CaptureSpecialInterleaved::~CaptureSpecialInterleaved(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::CaptureSpecialInterleaved::iterate(void) { /* Initializations. */ bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_arb_tf_3 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback3"); bool is_ok = true; bool test_error = false; /* Tests. */ try { if (is_at_least_gl_40 || is_arb_tf_3) { prepareAndBind(); draw(); is_ok = is_ok && check(); } } catch (...) { is_ok = false; test_error = true; } /* Clean GL objects. */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Capture Special Interleaved have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { /* Log error. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Capture Special Interleaved have approached error." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { /* Log fail. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Capture Special Interleaved have failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::CaptureSpecialInterleaved::prepareAndBind() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare programs. */ m_program_id = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader, s_fragment_shader, s_xfb_varyings, s_xfb_varyings_count, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id) { throw 0; } gl.useProgram(m_program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); /* Prepare transform feedbacks. */ gl.genTransformFeedbacks(1, &m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedbacks call failed."); /* Create empty Vertex Array Object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Prepare buffer objects. */ gl.genBuffers(s_bo_ids_count, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); for (glw::GLuint i = 0; i < s_bo_ids_count; ++i) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_bo_size, NULL, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, m_bo_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); } } void gl3cts::TransformFeedback::CaptureSpecialInterleaved::draw() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } bool gl3cts::TransformFeedback::CaptureSpecialInterleaved::check() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); bool is_ok = true; gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLfloat* results = (glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, s_bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange call failed."); if ((results[0] != 1.0) || (results[1] != 2.0) || (results[2] != 3.0) || (results[3] != 4.0) || /* gl_SkipComponents4 here */ (results[8] != 5.0) || (results[9] != 6.0) || (results[10] != 7.0) || (results[11] != 8.0)) { is_ok = false; } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id[1]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); results = (glw::GLfloat*)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, s_bo_size, GL_MAP_READ_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange call failed."); if ((results[0] != 9.0) || (results[1] != 10.0) || (results[2] != 11.0) || (results[3] != 12.0) || /* gl_SkipComponents4 here */ (results[8] != 13.0) || (results[9] != 14.0) || (results[10] != 15.0) || (results[11] != 16.0)) { is_ok = false; } gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer call failed."); return is_ok; } void gl3cts::TransformFeedback::CaptureSpecialInterleaved::clean() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_program_id) { gl.deleteProgram(m_program_id); m_program_id = 0; } if (m_xfb_id) { gl.deleteTransformFeedbacks(1, &m_xfb_id); m_xfb_id = 0; } for (glw::GLuint i = 0; i < s_bo_ids_count; ++i) { if (m_bo_id[i]) { gl.deleteBuffers(1, &m_bo_id[i]); m_bo_id[i] = 0; } } if (m_vao_id) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } } const glw::GLchar* gl3cts::TransformFeedback::CaptureSpecialInterleaved::s_vertex_shader = "#version 130\n" "\n" "out vec4 variable_1;\n" "out vec4 variable_2;\n" "out vec4 variable_3;\n" "out vec4 variable_4;\n" "\n" "void main()\n" "{\n" " variable_1 = vec4(1.0, 2.0, 3.0, 4.0);\n" " variable_2 = vec4(5.0, 6.0, 7.0, 8.0);\n" " variable_3 = vec4(9.0, 10.0, 11.0, 12.0);\n" " variable_4 = vec4(13.0, 14.0, 15.0, 16.0);\n" "\n" " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::CaptureSpecialInterleaved::s_fragment_shader = "#version 130\n" "\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::CaptureSpecialInterleaved::s_xfb_varyings[] = { "variable_1", "gl_SkipComponents4", "variable_2", "gl_NextBuffer", "variable_3", "gl_SkipComponents4", "variable_4" }; const glw::GLuint gl3cts::TransformFeedback::CaptureSpecialInterleaved::s_xfb_varyings_count = sizeof(s_xfb_varyings) / sizeof(s_xfb_varyings[0]); const glw::GLuint gl3cts::TransformFeedback::CaptureSpecialInterleaved::s_bo_size = 3 /*number of variables / empty places */ * 4 /* vec4 */ * sizeof(glw::GLfloat); /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DrawXFBStream::DrawXFBStream(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program_id_generate(0) , m_program_id_draw(0) , m_vao_id(0) , m_xfb_id(0) , m_fbo_id(0) , m_rbo_id(0) { memset(m_bo_id, 0, sizeof(m_bo_id)); memset(m_qo_id, 0, sizeof(m_qo_id)); } gl3cts::TransformFeedback::DrawXFBStream::~DrawXFBStream(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::DrawXFBStream::iterate(void) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Initializations. */ bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_arb_tf_3 = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback3"); bool is_arb_gpu_shader5 = m_context.getContextInfo().isExtensionSupported("GL_ARB_gpu_shader5"); glw::GLint max_vertex_streams = 0; bool is_ok = true; bool test_error = false; /* Tests. */ try { if (is_at_least_gl_40 || (is_arb_tf_3 && is_arb_gpu_shader5)) { gl.getIntegerv(GL_MAX_VERTEX_STREAMS, &max_vertex_streams); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed."); if (max_vertex_streams >= 2) { prepareObjects(); useProgram(m_program_id_generate); drawForXFB(); is_ok = is_ok && inspectQueries(); useProgram(m_program_id_draw); setupVertexArray(m_bo_id[0]); drawForFramebuffer(0); setupVertexArray(m_bo_id[1]); drawForFramebuffer(1); is_ok = is_ok && check(); } } } catch (...) { is_ok = false; test_error = true; } /* Clean GL objects. */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Stream have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { /* Log error. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Stream have approached error." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { /* Log fail. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Stream have failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::DrawXFBStream::prepareObjects() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare programs. */ m_program_id_generate = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), s_geometry_shader, NULL, NULL, s_vertex_shader_blank, s_fragment_shader, s_xfb_varyings, s_xfb_varyings_count, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id_generate) { throw 0; } m_program_id_draw = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_pass, s_fragment_shader, NULL, 0, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id_draw) { throw 0; } /* Prepare transform feedbacks. */ gl.genTransformFeedbacks(1, &m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedbacks call failed."); /* Create empty Vertex Array Object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Prepare buffer objects. */ gl.genBuffers(s_bo_ids_count, m_bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); for (glw::GLuint i = 0; i < s_bo_ids_count; ++i) { gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_bo_size, NULL, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, i, m_bo_id[i]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); } /* Generate queries */ gl.genQueries(s_qo_ids_count, m_qo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries call failed."); /* Prepare framebuffer. */ gl.clearColor(0.f, 0.f, 0.f, 1.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.genFramebuffers(1, &m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R8, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); } void gl3cts::TransformFeedback::DrawXFBStream::setupVertexArray(glw::GLuint bo_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindBuffer(GL_ARRAY_BUFFER, bo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLuint position_location = gl.getAttribLocation(m_program_id_draw, "position"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation call failed."); gl.vertexAttribPointer(position_location, 4, GL_FLOAT, GL_FALSE, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(position_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); } void gl3cts::TransformFeedback::DrawXFBStream::useProgram(glw::GLuint program_id) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(program_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); } void gl3cts::TransformFeedback::DrawXFBStream::drawForXFB() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.beginQueryIndexed(GL_PRIMITIVES_GENERATED, 0, m_qo_id[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQueryIndexed call failed."); gl.beginQueryIndexed(GL_PRIMITIVES_GENERATED, 1, m_qo_id[1]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQueryIndexed call failed."); gl.beginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0, m_qo_id[2]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQueryIndexed call failed."); gl.beginQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 1, m_qo_id[3]); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQueryIndexed call failed."); gl.drawArrays(GL_POINTS, 0, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endQueryIndexed(GL_PRIMITIVES_GENERATED, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQueryIndexed call failed."); gl.endQueryIndexed(GL_PRIMITIVES_GENERATED, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQueryIndexed call failed."); gl.endQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQueryIndexed call failed."); gl.endQueryIndexed(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, 1); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQueryIndexed call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } void gl3cts::TransformFeedback::DrawXFBStream::drawForFramebuffer(glw::GLuint stream) { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.drawTransformFeedbackStream(GL_TRIANGLES, m_xfb_id, stream); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawTransformFeedbackStream call failed."); } bool gl3cts::TransformFeedback::DrawXFBStream::inspectQueries() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); glw::GLint generated_primitives_to_stream_0 = 0; glw::GLint generated_primitives_to_stream_1 = 0; gl.getQueryObjectiv(m_qo_id[0], GL_QUERY_RESULT, &generated_primitives_to_stream_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryIndexediv call failed."); gl.getQueryObjectiv(m_qo_id[1], GL_QUERY_RESULT, &generated_primitives_to_stream_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryIndexediv call failed."); glw::GLint primitives_written_to_xfb_to_stream_0 = 0; glw::GLint primitives_written_to_xfb_to_stream_1 = 0; gl.getQueryObjectiv(m_qo_id[2], GL_QUERY_RESULT, &primitives_written_to_xfb_to_stream_0); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryIndexediv call failed."); gl.getQueryObjectiv(m_qo_id[3], GL_QUERY_RESULT, &primitives_written_to_xfb_to_stream_1); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetQueryIndexediv call failed."); if ((generated_primitives_to_stream_0 == 3) && (generated_primitives_to_stream_1 == 3) && (primitives_written_to_xfb_to_stream_0 == 3) && (primitives_written_to_xfb_to_stream_1 == 3)) { return true; } return false; } bool gl3cts::TransformFeedback::DrawXFBStream::check() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Number of pixels. */ const glw::GLuint number_of_pixels = s_view_size * s_view_size; /* Fetch framebuffer. */ std::vector pixels(number_of_pixels); gl.readPixels(0, 0, s_view_size, s_view_size, GL_RED, GL_FLOAT, &pixels[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); /* Check results. */ for (glw::GLuint i = 0; i < number_of_pixels; ++i) { if (fabs(pixels[i] - 1.f) > 0.0625 /* precision, expected result == 1.0 */) { return false; } } return true; } void gl3cts::TransformFeedback::DrawXFBStream::clean() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_program_id_generate) { gl.deleteProgram(m_program_id_generate); m_program_id_generate = 0; } if (m_program_id_draw) { glw::GLuint position_location = gl.getAttribLocation(m_program_id_draw, "position"); gl.disableVertexAttribArray(position_location); gl.deleteProgram(m_program_id_draw); m_program_id_draw = 0; } if (m_xfb_id) { gl.deleteTransformFeedbacks(1, &m_xfb_id); m_xfb_id = 0; } for (glw::GLuint i = 0; i < s_bo_ids_count; ++i) { if (m_bo_id[i]) { gl.deleteBuffers(1, &m_bo_id[i]); m_bo_id[i] = 0; } } if (m_vao_id) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } if (m_fbo_id) { gl.deleteFramebuffers(1, &m_fbo_id); m_fbo_id = 0; } if (m_rbo_id) { gl.deleteRenderbuffers(1, &m_rbo_id); m_rbo_id = 0; } } const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStream::s_vertex_shader_blank = "#version 130\n" "\n" "void main()\n" "{\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStream::s_vertex_shader_pass = "#version 130\n" "\n" "in vec4 position;\n" "\n" "void main()\n" "{\n" " gl_Position = position;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStream::s_geometry_shader = "#version 400\n" "\n" "layout(points) in;\n" "layout(points, max_vertices = 6) out;\n" "\n" "layout(stream = 1) out vec4 position;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" " gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" " gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" "\n" " position = vec4( 1.0, -1.0, 0.0, 1.0);\n" " EmitStreamVertex(1);\n" " position = vec4(-1.0, 1.0, 0.0, 1.0);\n" " EmitStreamVertex(1);\n" " position = vec4( 1.0, 1.0, 0.0, 1.0);\n" " EmitStreamVertex(1);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStream::s_fragment_shader = "#version 130\n" "\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStream::s_xfb_varyings[] = { "gl_Position", "gl_NextBuffer", "position" }; const glw::GLuint gl3cts::TransformFeedback::DrawXFBStream::s_xfb_varyings_count = sizeof(s_xfb_varyings) / sizeof(s_xfb_varyings[0]); const glw::GLuint gl3cts::TransformFeedback::DrawXFBStream::s_bo_size = 3 /* triangles */ * 4 /* vec4 */ * sizeof(glw::GLfloat); const glw::GLuint gl3cts::TransformFeedback::DrawXFBStream::s_view_size = 2; /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DrawXFBInstanced::DrawXFBInstanced(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program_id_generate(0) , m_program_id_draw(0) , m_vao_id(0) , m_xfb_id(0) , m_bo_id_xfb(0) , m_bo_id_uniform(0) , m_fbo_id(0) , m_rbo_id(0) , m_glGetUniformBlockIndex(DE_NULL) , m_glUniformBlockBinding(DE_NULL) { } gl3cts::TransformFeedback::DrawXFBInstanced::~DrawXFBInstanced(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::DrawXFBInstanced::iterate(void) { /* Initializations. */ bool is_at_least_gl_42 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 2))); bool is_at_least_gl_31 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 1))); bool is_arb_tf_instanced = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback_instanced"); bool is_arb_ubo = m_context.getContextInfo().isExtensionSupported("GL_ARB_uniform_buffer_object"); bool is_ok = true; bool test_error = false; if (is_arb_ubo) { m_glGetUniformBlockIndex = (GetUniformBlockIndex_ProcAddress)m_context.getRenderContext().getProcAddress("glGetUniformBlockIndex"); m_glUniformBlockBinding = (UniformBlockBinding_ProcAddress)m_context.getRenderContext().getProcAddress("glUniformBlockBinding"); if (DE_NULL == m_glGetUniformBlockIndex || DE_NULL == m_glUniformBlockBinding) { throw 0; } } try { if (is_at_least_gl_42 || ((is_at_least_gl_31 || is_arb_ubo) && is_arb_tf_instanced)) { prepareObjects(); drawForXFB(); drawInstanced(); is_ok = is_ok && check(); } } catch (...) { is_ok = false; test_error = true; } /* Clean GL objects */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Instanced have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { /* Log error. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Instanced have approached error." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { /* Log fail. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Instanced have failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::DrawXFBInstanced::prepareObjects() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare programs. */ m_program_id_generate = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_generate, s_fragment_shader, &s_xfb_varying, 1, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id_generate) { throw 0; } m_program_id_draw = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_draw, s_fragment_shader, NULL, 0, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id_draw) { throw 0; } /* Prepare transform feedbacks. */ gl.genTransformFeedbacks(1, &m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedbacks call failed."); /* Create empty Vertex Array Object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Prepare xfb buffer object. */ gl.genBuffers(1, &m_bo_id_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_bo_xfb_size, NULL, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_id_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); /* Prepare uniform buffer object. */ gl.genBuffers(1, &m_bo_id_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_UNIFORM_BUFFER, m_bo_id_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_UNIFORM_BUFFER, s_bo_uniform_size, s_bo_uniform_data, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_UNIFORM_BUFFER, 0, m_bo_id_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); glw::GLuint uniform_index = m_glGetUniformBlockIndex(m_program_id_draw, s_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex call failed."); if (GL_INVALID_INDEX == uniform_index) { throw 0; } m_glUniformBlockBinding(m_program_id_draw, uniform_index, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformBlockBinding call failed."); /* Prepare framebuffer. */ gl.clearColor(0.f, 0.f, 0.f, 1.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.genFramebuffers(1, &m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R8, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); } void gl3cts::TransformFeedback::DrawXFBInstanced::drawForXFB() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(m_program_id_generate); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_POINTS, 0, 4 /* quad vertex count */); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } void gl3cts::TransformFeedback::DrawXFBInstanced::drawInstanced() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_xfb); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLuint position_location = gl.getAttribLocation(m_program_id_draw, "position"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation call failed."); gl.vertexAttribPointer(position_location, 4, GL_FLOAT, GL_FALSE, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(position_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.clear(GL_COLOR_BUFFER_BIT); GLU_EXPECT_NO_ERROR(gl.getError(), "glClear call failed."); gl.useProgram(m_program_id_draw); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.drawTransformFeedbackInstanced(GL_TRIANGLE_STRIP, m_xfb_id, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); } bool gl3cts::TransformFeedback::DrawXFBInstanced::check() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Number of pixels. */ const glw::GLuint number_of_pixels = s_view_size * s_view_size; /* Fetch framebuffer. */ std::vector pixels(number_of_pixels); gl.readPixels(0, 0, s_view_size, s_view_size, GL_RED, GL_FLOAT, &pixels[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); /* Check results. */ for (glw::GLuint i = 0; i < number_of_pixels; ++i) { if (fabs(pixels[i] - 1.f) > 0.0625 /* precision, expected result == 1.0 */) { return false; } } return true; } void gl3cts::TransformFeedback::DrawXFBInstanced::clean() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_program_id_generate) { gl.deleteProgram(m_program_id_generate); m_program_id_generate = 0; } if (m_program_id_draw) { glw::GLuint position_location = gl.getAttribLocation(m_program_id_draw, "position"); gl.disableVertexAttribArray(position_location); gl.deleteProgram(m_program_id_draw); m_program_id_draw = 0; } if (m_xfb_id) { gl.deleteTransformFeedbacks(1, &m_xfb_id); m_xfb_id = 0; } if (m_bo_id_xfb) { gl.deleteBuffers(1, &m_bo_id_xfb); m_bo_id_xfb = 0; } if (m_bo_id_uniform) { gl.deleteBuffers(1, &m_bo_id_uniform); m_bo_id_uniform = 0; } if (m_vao_id) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } if (m_fbo_id) { gl.deleteFramebuffers(1, &m_fbo_id); m_fbo_id = 0; } if (m_rbo_id) { gl.deleteRenderbuffers(1, &m_rbo_id); m_rbo_id = 0; } } const glw::GLchar* gl3cts::TransformFeedback::DrawXFBInstanced::s_vertex_shader_generate = "#version 140\n" "\n" "void main()\n" "{\n" " switch(gl_VertexID % 4)\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"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBInstanced::s_vertex_shader_draw = "#version 140\n" "\n" "uniform MatrixBlock\n" "{\n" " mat4 transformation_0;\n" " mat4 transformation_1;\n" " mat4 transformation_2;\n" " mat4 transformation_3;\n" "};\n" "\n" "in vec4 position;\n" "\n" "void main()\n" "{\n" " switch(gl_InstanceID % 4)\n" " {\n" " case 0:\n" " gl_Position = position * transformation_0;\n" " break;\n" " case 1:\n" " gl_Position = position * transformation_1;\n" " break;\n" " case 2:\n" " gl_Position = position * transformation_2;\n" " break;\n" " case 3:\n" " gl_Position = position * transformation_3;\n" " break;\n" " }\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBInstanced::s_fragment_shader = "#version 130\n" "\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBInstanced::s_xfb_varying = "gl_Position"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBInstanced::s_uniform = "MatrixBlock"; const glw::GLuint gl3cts::TransformFeedback::DrawXFBInstanced::s_bo_xfb_size = 4 /* vertex count */ * 4 /* vec4 components */ * sizeof(glw::GLfloat) /* data type size */; const glw::GLfloat gl3cts::TransformFeedback::DrawXFBInstanced::s_bo_uniform_data[] = { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, -0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, -0.5f, 0.0f, 0.5f, 0.0f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; const glw::GLuint gl3cts::TransformFeedback::DrawXFBInstanced::s_bo_uniform_size = sizeof(s_bo_uniform_data); const glw::GLuint gl3cts::TransformFeedback::DrawXFBInstanced::s_view_size = 4; /*-----------------------------------------------------------------------------------------------*/ gl3cts::TransformFeedback::DrawXFBStreamInstanced::DrawXFBStreamInstanced(deqp::Context& context, const char* test_name, const char* test_description) : deqp::TestCase(context, test_name, test_description) , m_context(context) , m_program_id_generate(0) , m_program_id_draw(0) , m_vao_id(0) , m_xfb_id(0) , m_bo_id_xfb_position(0) , m_bo_id_xfb_color(0) , m_bo_id_uniform(0) , m_fbo_id(0) , m_rbo_id(0) , m_glGetUniformBlockIndex(DE_NULL) , m_glUniformBlockBinding(DE_NULL) { } gl3cts::TransformFeedback::DrawXFBStreamInstanced::~DrawXFBStreamInstanced(void) { } tcu::TestNode::IterateResult gl3cts::TransformFeedback::DrawXFBStreamInstanced::iterate(void) { /* Initializations. */ bool is_at_least_gl_31 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(3, 1))); bool is_at_least_gl_40 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0))); bool is_at_least_gl_42 = (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 2))); bool is_arb_tf_instanced = m_context.getContextInfo().isExtensionSupported("GL_ARB_transform_feedback_instanced"); bool is_arb_ubo = m_context.getContextInfo().isExtensionSupported("GL_ARB_uniform_buffer_object"); bool is_arb_gpu_shader5 = m_context.getContextInfo().isExtensionSupported("GL_ARB_gpu_shader5"); bool is_ok = true; bool test_error = false; if (is_arb_ubo) { m_glGetUniformBlockIndex = (GetUniformBlockIndex_ProcAddress)m_context.getRenderContext().getProcAddress("glGetUniformBlockIndex"); m_glUniformBlockBinding = (UniformBlockBinding_ProcAddress)m_context.getRenderContext().getProcAddress("glUniformBlockBinding"); if (DE_NULL == m_glGetUniformBlockIndex || DE_NULL == m_glUniformBlockBinding) { throw 0; } } /* Test. */ try { if (is_at_least_gl_42 || ((is_at_least_gl_31 || is_arb_ubo) && is_arb_gpu_shader5 && is_arb_tf_instanced) || (is_at_least_gl_40 && is_arb_tf_instanced)) { prepareObjects(); drawForXFB(); drawStreamInstanced(); is_ok = is_ok && check(); } } catch (...) { is_ok = false; test_error = true; } /* Clean GL objects */ clean(); /* Result's setup. */ if (is_ok) { /* Log success. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Stream Instanced have passed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); } else { if (test_error) { /* Log error. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Stream Instanced have approached error." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error"); } else { /* Log fail. */ m_context.getTestContext().getLog() << tcu::TestLog::Message << "Draw XFB Stream Instanced have failed." << tcu::TestLog::EndMessage; m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); } } return STOP; } void gl3cts::TransformFeedback::DrawXFBStreamInstanced::prepareObjects() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Prepare programs. */ m_program_id_generate = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), s_geometry_shader_generate, NULL, NULL, s_vertex_shader_blank, s_fragment_shader_blank, s_xfb_varyings, s_xfb_varyings_count, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id_generate) { throw 0; } m_program_id_draw = gl3cts::TransformFeedback::Utilities::buildProgram( gl, m_context.getTestContext().getLog(), NULL, NULL, NULL, s_vertex_shader_draw, s_fragment_shader_draw, NULL, 0, GL_INTERLEAVED_ATTRIBS); if (0 == m_program_id_draw) { throw 0; } /* Prepare transform feedbacks. */ gl.genTransformFeedbacks(1, &m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTransformFeedbacks call failed."); gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_xfb_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTransformFeedbacks call failed."); /* Create empty Vertex Array Object */ gl.genVertexArrays(1, &m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays call failed."); gl.bindVertexArray(m_vao_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray call failed."); /* Prepare xfb buffer objects. */ gl.genBuffers(1, &m_bo_id_xfb_position); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id_xfb_position); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_bo_xfb_size, NULL, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_bo_id_xfb_position); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); gl.genBuffers(1, &m_bo_id_xfb_color); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id_xfb_color); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, s_bo_xfb_size, NULL, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, m_bo_id_xfb_color); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); /* Prepare uniform buffer object. */ gl.genBuffers(1, &m_bo_id_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers call failed."); gl.bindBuffer(GL_UNIFORM_BUFFER, m_bo_id_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); gl.bufferData(GL_UNIFORM_BUFFER, s_bo_uniform_size, s_bo_uniform_data, GL_DYNAMIC_COPY); /* allocation */ GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData call failed."); gl.bindBufferBase(GL_UNIFORM_BUFFER, 0, m_bo_id_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange call failed."); glw::GLuint uniform_index = m_glGetUniformBlockIndex(m_program_id_draw, s_uniform); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex call failed."); if (GL_INVALID_INDEX == uniform_index) { throw 0; } m_glUniformBlockBinding(m_program_id_draw, uniform_index, 0); GLU_EXPECT_NO_ERROR(gl.getError(), "glUniformBlockBinding call failed."); /* Prepare framebuffer. */ gl.clearColor(0.f, 0.f, 0.f, 1.f); GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor call failed."); gl.genFramebuffers(1, &m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers call failed."); gl.genRenderbuffers(1, &m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glGenRenderbuffers call failed."); gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer call failed."); gl.bindRenderbuffer(GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindRenderbuffer call failed."); gl.renderbufferStorage(GL_RENDERBUFFER, GL_R8, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glRenderbufferStorage call failed."); gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_rbo_id); GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferRenderbuffer call failed."); if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { throw 0; } gl.viewport(0, 0, s_view_size, s_view_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport call failed."); } void gl3cts::TransformFeedback::DrawXFBStreamInstanced::drawForXFB() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(m_program_id_generate); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.enable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable call failed."); gl.beginTransformFeedback(GL_POINTS); GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback call failed."); gl.drawArrays(GL_POINTS, 0, 4 /* quad vertex count */); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); gl.endTransformFeedback(); GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback call failed."); gl.disable(GL_RASTERIZER_DISCARD); GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable call failed."); } void gl3cts::TransformFeedback::DrawXFBStreamInstanced::drawStreamInstanced() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_xfb_position); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLuint position_location = gl.getAttribLocation(m_program_id_draw, "position"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation call failed."); gl.vertexAttribPointer(position_location, 4, GL_FLOAT, GL_FALSE, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(position_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id_xfb_color); GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer call failed."); glw::GLuint color_location = gl.getAttribLocation(m_program_id_draw, "color"); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation call failed."); gl.vertexAttribPointer(color_location, 4, GL_FLOAT, GL_FALSE, 0, NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer call failed."); gl.enableVertexAttribArray(color_location); GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray call failed."); gl.useProgram(m_program_id_draw); GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram call failed."); gl.drawTransformFeedbackStreamInstanced(GL_TRIANGLE_STRIP, m_xfb_id, 0, 4); GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays call failed."); } bool gl3cts::TransformFeedback::DrawXFBStreamInstanced::check() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); /* Number of pixels. */ const glw::GLuint number_of_pixels = s_view_size * s_view_size; /* Fetch framebuffer. */ std::vector pixels(number_of_pixels); gl.readPixels(0, 0, s_view_size, s_view_size, GL_RED, GL_FLOAT, &pixels[0]); GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels call failed."); /* Check results. */ for (glw::GLuint i = 0; i < number_of_pixels; ++i) { if (fabs(pixels[i] - 1.f) > 0.0625 /* precision, expected result == 1.0 */) { return false; } } return true; } void gl3cts::TransformFeedback::DrawXFBStreamInstanced::clean() { /* Functions handler */ const glw::Functions& gl = m_context.getRenderContext().getFunctions(); gl.useProgram(0); if (m_program_id_generate) { gl.deleteProgram(m_program_id_generate); m_program_id_generate = 0; } if (m_program_id_draw) { glw::GLuint position_location = gl.getAttribLocation(m_program_id_draw, "position"); gl.disableVertexAttribArray(position_location); glw::GLuint color_location = gl.getAttribLocation(m_program_id_draw, "color"); gl.disableVertexAttribArray(color_location); gl.deleteProgram(m_program_id_draw); m_program_id_draw = 0; } if (m_xfb_id) { gl.deleteTransformFeedbacks(1, &m_xfb_id); m_xfb_id = 0; } if (m_bo_id_xfb_position) { gl.deleteBuffers(1, &m_bo_id_xfb_position); m_bo_id_xfb_position = 0; } if (m_bo_id_xfb_color) { gl.deleteBuffers(1, &m_bo_id_xfb_color); m_bo_id_xfb_position = 0; } if (m_bo_id_uniform) { gl.deleteBuffers(1, &m_bo_id_uniform); m_bo_id_uniform = 0; } if (m_vao_id) { gl.deleteVertexArrays(1, &m_vao_id); m_vao_id = 0; } if (m_fbo_id) { gl.deleteFramebuffers(1, &m_fbo_id); m_fbo_id = 0; } if (m_rbo_id) { gl.deleteRenderbuffers(1, &m_rbo_id); m_rbo_id = 0; } } const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_vertex_shader_blank = "#version 140\n" "\n" "void main()\n" "{\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_geometry_shader_generate = "#version 400\n" "\n" "layout(points) in;\n" "layout(points, max_vertices = 8) out;\n" "\n" "layout(stream = 1) out vec4 color;\n" "\n" "void main()\n" "{\n" " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" " gl_Position = vec4( 1.0, -1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" " gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" " gl_Position = vec4( 1.0, 1.0, 0.0, 1.0);\n" " EmitStreamVertex(0);\n" "\n" " color = vec4(1.0, 1.0, 1.0, 1.0);\n" " EmitStreamVertex(1);\n" " color = vec4(1.0, 1.0, 1.0, 1.0);\n" " EmitStreamVertex(1);\n" " color = vec4(1.0, 1.0, 1.0, 1.0);\n" " EmitStreamVertex(1);\n" " color = vec4(1.0, 1.0, 1.0, 1.0);\n" " EmitStreamVertex(1);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_vertex_shader_draw = "#version 140\n" "\n" "uniform MatrixBlock\n" "{\n" " mat4 transformation_0;\n" " mat4 transformation_1;\n" " mat4 transformation_2;\n" " mat4 transformation_3;\n" "};\n" "\n" "in vec4 position;\n" "in vec4 color;\n" "out vec4 colour;\n" "\n" "void main()\n" "{\n" " switch(gl_InstanceID % 4)\n" " {\n" " case 0:\n" " gl_Position = position * transformation_0;\n" " break;\n" " case 1:\n" " gl_Position = position * transformation_1;\n" " break;\n" " case 2:\n" " gl_Position = position * transformation_2;\n" " break;\n" " case 3:\n" " gl_Position = position * transformation_3;\n" " break;\n" " }\n" " colour = color;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_fragment_shader_blank = "#version 130\n" "\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = vec4(1.0);\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_fragment_shader_draw = "#version 130\n" "\n" "in vec4 colour;\n" "out vec4 pixel;\n" "\n" "void main()\n" "{\n" " pixel = colour;\n" "}\n"; const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_xfb_varyings[] = { "gl_Position", "gl_NextBuffer", "color" }; const glw::GLuint gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_xfb_varyings_count = sizeof(s_xfb_varyings) / sizeof(s_xfb_varyings[0]); const glw::GLchar* gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_uniform = "MatrixBlock"; const glw::GLuint gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_bo_xfb_size = 4 /* vertex count */ * 4 /* vec4 components */ * sizeof(glw::GLfloat) /* data type size */; const glw::GLfloat gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_bo_uniform_data[] = { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, -0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, -0.5f, 0.0f, 0.5f, 0.0f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; const glw::GLuint gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_bo_uniform_size = sizeof(s_bo_uniform_data); const glw::GLuint gl3cts::TransformFeedback::DrawXFBStreamInstanced::s_view_size = 4; /*-----------------------------------------------------------------------------------------------*/ glw::GLuint gl3cts::TransformFeedback::Utilities::buildProgram( glw::Functions const& gl, tcu::TestLog& log, glw::GLchar const* const geometry_shader_source, glw::GLchar const* const tessellation_control_shader_source, glw::GLchar const* const tessellation_evaluation_shader_source, glw::GLchar const* const vertex_shader_source, glw::GLchar const* const fragment_shader_source, glw::GLchar const* const* const transform_feedback_varyings, glw::GLsizei const transform_feedback_varyings_count, glw::GLenum const transform_feedback_varyings_mode, bool const do_not_detach, glw::GLint* linking_status) { glw::GLuint program = 0; struct Shader { glw::GLchar const* const source; glw::GLenum const type; glw::GLuint id; } shader[] = { { geometry_shader_source, GL_GEOMETRY_SHADER, 0 }, { tessellation_control_shader_source, GL_TESS_CONTROL_SHADER, 0 }, { tessellation_evaluation_shader_source, GL_TESS_EVALUATION_SHADER, 0 }, { vertex_shader_source, GL_VERTEX_SHADER, 0 }, { fragment_shader_source, GL_FRAGMENT_SHADER, 0 } }; glw::GLuint const shader_count = sizeof(shader) / sizeof(shader[0]); try { /* Create program. */ program = gl.createProgram(); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram call failed."); /* Shader compilation. */ for (glw::GLuint i = 0; i < shader_count; ++i) { if (DE_NULL != shader[i].source) { shader[i].id = gl.createShader(shader[i].type); GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader call failed."); gl.attachShader(program, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader call failed."); gl.shaderSource(shader[i].id, 1, &(shader[i].source), NULL); GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource call failed."); gl.compileShader(shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader call failed."); glw::GLint status = GL_FALSE; gl.getShaderiv(shader[i].id, GL_COMPILE_STATUS, &status); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); if (GL_FALSE == status) { glw::GLint log_size = 0; gl.getShaderiv(shader[i].id, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getShaderInfoLog(shader[i].id, log_size, NULL, &log_text[0]); log << tcu::TestLog::Message << "Shader compilation has failed.\n" << "Shader type: " << glu::getShaderTypeStr(shader[i].type) << "\n" << "Shader compilation error log:\n" << log_text << "\n" << "Shader source code:\n" << shader[i].source << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog call failed."); throw 0; } } } /* Link. */ if (transform_feedback_varyings_count) { gl.transformFeedbackVaryings(program, transform_feedback_varyings_count, transform_feedback_varyings, transform_feedback_varyings_mode); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); } gl.linkProgram(program); GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings call failed."); glw::GLint status = GL_FALSE; gl.getProgramiv(program, GL_LINK_STATUS, &status); if (DE_NULL != linking_status) { *linking_status = status; } if (GL_TRUE == status) { if (!do_not_detach) { for (glw::GLuint i = 0; i < shader_count; ++i) { if (shader[i].id) { gl.detachShader(program, shader[i].id); GLU_EXPECT_NO_ERROR(gl.getError(), "glDetachShader call failed."); } } } } else { glw::GLint log_size = 0; gl.getProgramiv(program, GL_INFO_LOG_LENGTH, &log_size); GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv call failed."); glw::GLchar* log_text = new glw::GLchar[log_size]; gl.getProgramInfoLog(program, log_size, NULL, &log_text[0]); log << tcu::TestLog::Message << "Program linkage has failed due to:\n" << log_text << "\n" << tcu::TestLog::EndMessage; delete[] log_text; GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog call failed."); throw 0; } } catch (...) { if (program) { gl.deleteProgram(program); program = 0; } } for (glw::GLuint i = 0; i < shader_count; ++i) { if (0 != shader[i].id) { gl.deleteShader(shader[i].id); shader[i].id = 0; } } return program; } /** @brief Substitute key with value within source code. * * @param [in] source Source code to be prerocessed. * @param [in] key Key to be substituted. * @param [in] value Value to be inserted. * * @return Resulting string. */ std::string gl3cts::TransformFeedback::Utilities::preprocessCode(std::string source, std::string key, std::string value) { std::string destination = source; while (true) { /* Find token in source code. */ size_t position = destination.find(key, 0); /* No more occurences of this key. */ if (position == std::string::npos) { break; } /* Replace token with sub_code. */ destination.replace(position, key.size(), value); } return destination; } /** @brief Convert an integer to a string. * * @param [in] i Integer to be converted. * * @return String representing integer. */ std::string gl3cts::TransformFeedback::Utilities::itoa(glw::GLint i) { std::stringstream stream; stream << i; return stream.str(); } /** @brief Convert an float to a string. * * @param [in] f Float to be converted. * * @return String representing integer. */ std::string gl3cts::TransformFeedback::Utilities::ftoa(glw::GLfloat f) { std::stringstream stream; stream << f; return stream.str(); }