| /*------------------------------------------------------------------------- |
| * OpenGL Conformance Test Suite |
| * ----------------------------- |
| * |
| * Copyright (c) 2014-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 gl4cPipelineStatisticsQueryTests.cpp |
| * \brief Implements conformance tests for GL_ARB_pipeline_statistics_query functionality |
| */ /*-------------------------------------------------------------------*/ |
| |
| #include "gl4cPipelineStatisticsQueryTests.hpp" |
| #include "gluContextInfo.hpp" |
| #include "gluDefs.hpp" |
| #include "glwEnums.hpp" |
| #include "glwFunctions.hpp" |
| #include "tcuRenderTarget.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| #ifndef GL_VERTICES_SUBMITTED_ARB |
| #define GL_VERTICES_SUBMITTED_ARB (0x82EE) |
| #endif |
| #ifndef GL_PRIMITIVES_SUBMITTED_ARB |
| #define GL_PRIMITIVES_SUBMITTED_ARB (0x82EF) |
| #endif |
| #ifndef GL_VERTEX_SHADER_INVOCATIONS_ARB |
| #define GL_VERTEX_SHADER_INVOCATIONS_ARB (0x82F0) |
| #endif |
| #ifndef GL_TESS_CONTROL_SHADER_PATCHES_ARB |
| #define GL_TESS_CONTROL_SHADER_PATCHES_ARB (0x82F1) |
| #endif |
| #ifndef GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB |
| #define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB (0x82F2) |
| #endif |
| #ifndef GL_GEOMETRY_SHADER_INVOCATIONS |
| #define GL_GEOMETRY_SHADER_INVOCATIONS (0x887F) |
| #endif |
| #ifndef GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB |
| #define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB (0x82F3) |
| #endif |
| #ifndef GL_FRAGMENT_SHADER_INVOCATIONS_ARB |
| #define GL_FRAGMENT_SHADER_INVOCATIONS_ARB (0x82F4) |
| #endif |
| #ifndef GL_COMPUTE_SHADER_INVOCATIONS_ARB |
| #define GL_COMPUTE_SHADER_INVOCATIONS_ARB (0x82F5) |
| #endif |
| #ifndef GL_CLIPPING_INPUT_PRIMITIVES_ARB |
| #define GL_CLIPPING_INPUT_PRIMITIVES_ARB (0x82F6) |
| #endif |
| #ifndef GL_CLIPPING_OUTPUT_PRIMITIVES_ARB |
| #define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB (0x82F7) |
| #endif |
| |
| namespace glcts |
| { |
| const char* PipelineStatisticsQueryUtilities::dummy_cs_code = |
| "#version 430\n" |
| "\n" |
| "layout(local_size_x=1, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout(binding = 0) uniform atomic_uint test_counter;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicCounterIncrement(test_counter);\n" |
| "}\n"; |
| const char* PipelineStatisticsQueryUtilities::dummy_cs_code_arb = |
| "#version 420 core\n" |
| "#extension GL_ARB_compute_shader : require\n" |
| "#extension GL_ARB_shader_atomic_counters : require\n" |
| "\n" |
| "layout(local_size_x=1, local_size_y = 1, local_size_z = 1) in;\n" |
| "\n" |
| "layout(binding = 0) uniform atomic_uint test_counter;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " atomicCounterIncrement(test_counter);\n" |
| "}\n"; |
| const char* PipelineStatisticsQueryUtilities::dummy_fs_code = "#version 130\n" |
| "\n" |
| "out vec4 result;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " result = gl_FragCoord;\n" |
| "}\n"; |
| const char* PipelineStatisticsQueryUtilities::dummy_tc_code = |
| "#version 400\n" |
| "\n" |
| "layout(vertices = 3) out;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" |
| " gl_TessLevelInner[0] = 1.0;\n" |
| " gl_TessLevelInner[1] = 2.0;\n" |
| " gl_TessLevelOuter[0] = 3.0;\n" |
| " gl_TessLevelOuter[1] = 4.0;\n" |
| " gl_TessLevelOuter[2] = 5.0;\n" |
| " gl_TessLevelOuter[3] = 6.0;\n" |
| "}\n"; |
| const char* PipelineStatisticsQueryUtilities::dummy_te_code = |
| "#version 400\n" |
| "\n" |
| "layout(triangles) in;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = gl_TessCoord.xyxy * gl_in[gl_PrimitiveID].gl_Position;\n" |
| "}\n"; |
| const char* PipelineStatisticsQueryUtilities::dummy_vs_code = "#version 130\n" |
| "\n" |
| "in vec4 position;\n" |
| "\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = position;\n" |
| "}\n"; |
| |
| /** An array holding all query targets that are introduced by GL_ARB_pipeline_statistics_query */ |
| const glw::GLenum PipelineStatisticsQueryUtilities::query_targets[] = { |
| GL_VERTICES_SUBMITTED_ARB, |
| GL_PRIMITIVES_SUBMITTED_ARB, |
| GL_VERTEX_SHADER_INVOCATIONS_ARB, |
| GL_TESS_CONTROL_SHADER_PATCHES_ARB, |
| GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, |
| GL_GEOMETRY_SHADER_INVOCATIONS, |
| GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, |
| GL_FRAGMENT_SHADER_INVOCATIONS_ARB, |
| GL_COMPUTE_SHADER_INVOCATIONS_ARB, |
| GL_CLIPPING_INPUT_PRIMITIVES_ARB, |
| GL_CLIPPING_OUTPUT_PRIMITIVES_ARB, |
| }; |
| const unsigned int PipelineStatisticsQueryUtilities::n_query_targets = sizeof(query_targets) / sizeof(query_targets[0]); |
| |
| /* Offsets that point to locations in a buffer object storage that will hold |
| * query object result values for a specific value type. */ |
| const unsigned int PipelineStatisticsQueryUtilities::qo_bo_int_offset = (0); |
| const unsigned int PipelineStatisticsQueryUtilities::qo_bo_int64_offset = (0 + 4 /* int */ + 4 /* alignment */); |
| const unsigned int PipelineStatisticsQueryUtilities::qo_bo_uint_offset = (0 + 8 /* int + alignment */ + 8 /* int64 */); |
| const unsigned int PipelineStatisticsQueryUtilities::qo_bo_uint64_offset = |
| (0 + 8 /* int + alignment */ + 8 /* int64 */ + 8 /* uint + alignment */); |
| const unsigned int PipelineStatisticsQueryUtilities::qo_bo_size = 32; |
| |
| /** Buffer object size required to run the second functional test. */ |
| const unsigned int PipelineStatisticsQueryTestFunctional2::bo_size = 32; |
| |
| /** Builds body of a geometry shader, given user-specified properties. |
| * |
| * This function works in two different ways: |
| * |
| * 1) If the caller only needs the geometry shader to use a single stream, the body |
| * will be constructed in a way that ignores stream existence completely. |
| * 2) Otherwise, the shader will only be compilable by platforms that support vertex |
| * streams. |
| * |
| * The shader will emit @param n_primitives_to_emit_in_stream0 primitives on the zeroth |
| * stream, (@param n_primitives_to_emit_in_stream0 + 1) primitives on the first stream, |
| * and so on. |
| * |
| * @param gs_input Input primitive type that should be used by the geometry shader body. |
| * @param gs_output Output primitive type that should be used by the geometry shader body. |
| * @param n_primitives_to_emit_in_stream0 Number of primitives to be emitted on the zeroth vertex stream. |
| * @param n_streams Number of streams the geometry shader should emit primitives on. |
| * |
| * @return Geometry shader body. |
| **/ |
| std::string PipelineStatisticsQueryUtilities::buildGeometryShaderBody(_geometry_shader_input gs_input, |
| _geometry_shader_output gs_output, |
| unsigned int n_primitives_to_emit_in_stream0, |
| unsigned int n_streams) |
| { |
| DE_ASSERT(n_primitives_to_emit_in_stream0 >= 1); |
| DE_ASSERT(n_streams >= 1); |
| |
| /* Each stream will output (n+1) primitives, where n corresponds to the number of primitives emitted |
| * by the previous stream. Stream 0 emits user-defined number of primitievs. |
| */ |
| std::stringstream gs_body_sstream; |
| const std::string gs_input_string = getGLSLStringForGSInput(gs_input); |
| const std::string gs_output_string = getGLSLStringForGSOutput(gs_output); |
| unsigned int n_max_vertices = 0; |
| unsigned int n_vertices_required_for_gs_output = |
| PipelineStatisticsQueryUtilities::getNumberOfVerticesForGSOutput(gs_output); |
| |
| for (unsigned int n_stream = 0; n_stream < n_streams; ++n_stream) |
| { |
| n_max_vertices += n_vertices_required_for_gs_output * (n_primitives_to_emit_in_stream0 + n_stream); |
| } /* for (all streams) */ |
| |
| /* Form the preamble. Note that we need to use the right ES SL version, |
| * since vertex streams are not a core GL3.2 feature. |
| **/ |
| gs_body_sstream << ((n_streams > 1) ? "#version 400" : "#version 150\n") << "\n" |
| "layout(" |
| << gs_input_string << ") in;\n" |
| "layout(" |
| << gs_output_string << ", max_vertices=" << n_max_vertices << ") out;\n"; |
| |
| /* If we need to define multiple streams, do it now */ |
| if (n_streams > 1) |
| { |
| gs_body_sstream << "\n"; |
| |
| for (unsigned int n_stream = 0; n_stream < n_streams; ++n_stream) |
| { |
| gs_body_sstream << "layout(stream = " << n_stream << ") out vec4 out_stream" << n_stream << ";\n"; |
| } /* for (all streams) */ |
| } /* if (n_streams > 1) */ |
| |
| /* Contine forming actual body */ |
| gs_body_sstream << "\n" |
| "void main()\n" |
| "{\n"; |
| |
| /* Emit primitives */ |
| const unsigned int n_output_primitive_vertices = |
| PipelineStatisticsQueryUtilities::getNumberOfVerticesForGSOutput(gs_output); |
| |
| for (unsigned int n_stream = 0; n_stream < n_streams; ++n_stream) |
| { |
| const unsigned int n_primitives_to_emit = n_primitives_to_emit_in_stream0 + n_stream; |
| |
| for (unsigned int n_primitive = 0; n_primitive < n_primitives_to_emit; ++n_primitive) |
| { |
| for (unsigned int n_vertex = 0; n_vertex < n_output_primitive_vertices; ++n_vertex) |
| { |
| gs_body_sstream << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"; |
| |
| if (n_streams == 1) |
| { |
| gs_body_sstream << " EmitVertex();\n"; |
| } |
| else |
| { |
| gs_body_sstream << " EmitStreamVertex(" << n_stream << ");\n"; |
| } |
| } |
| |
| if (n_streams == 1) |
| { |
| gs_body_sstream << " EndPrimitive();\n"; |
| } |
| else |
| { |
| gs_body_sstream << " EndStreamPrimitive(" << n_stream << ");\n"; |
| } |
| } /* for (all primitives the caller wants the shader to emit) */ |
| } /* for (all streams) */ |
| |
| gs_body_sstream << "}\n"; |
| |
| return gs_body_sstream.str(); |
| } |
| |
| /** Executes the query and collects the result data from both query object buffer object |
| * (if these are supported by the running OpenGL implementation) and the query counters. |
| * The result data is then exposed via @param out_result. |
| * |
| * @param query_type Type of the query to be executed for the iteration. |
| * @param qo_id ID of the query object to be used for the execution. |
| * The query object must not have been assigned a type |
| * prior to the call, or the type must be a match with |
| * @param query_type . |
| * @param qo_bo_id ID of the query buffer object to use for the call. |
| * Pass 0, if the running OpenGL implementation does not |
| * support QBOs. |
| * @param pfn_draw Function pointer to caller-specific routine that is |
| * going to execute the draw call. Must not be NULL. |
| * @param draw_user_arg Caller-specific user argument to be passed with the |
| * @param pfn_draw callback. |
| * @param render_context glu::RenderContext& to be used by the method. |
| * @param test_context tcu::TestContext& to be used by the method. |
| * @param context_info glu::ContextInfo& to be used by the method. |
| * @param out_result Deref will be used to store the test execution result. |
| * Must not be NULL. Will only be modified if the method |
| * returns true. |
| * |
| * @return true if the test executed successfully, and @param out_result 's fields |
| * were modified. |
| * |
| */ |
| bool PipelineStatisticsQueryUtilities::executeQuery(glw::GLenum query_type, glw::GLuint qo_id, glw::GLuint qo_bo_id, |
| PFNQUERYDRAWHANDLERPROC pfn_draw, void* draw_user_arg, |
| const glu::RenderContext& render_context, |
| tcu::TestContext& test_context, |
| const glu::ContextInfo& context_info, |
| _test_execution_result* out_result) |
| { |
| glw::GLenum error_code = GL_NO_ERROR; |
| const glw::Functions& gl = render_context.getFunctions(); |
| bool result = true; |
| |
| /* Check if the implementation provides non-zero number of bits for the query. |
| * Queries, for which GL implementations provide zero bits of space return |
| * indeterminate values, so we should skip them */ |
| glw::GLint n_query_bits = 0; |
| |
| gl.getQueryiv(query_type, GL_QUERY_COUNTER_BITS, &n_query_bits); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message |
| << "glGetQueryiv() call failed for GL_QUERY_COUNTER_BITS pname and " |
| << PipelineStatisticsQueryUtilities::getStringForEnum(query_type) << "query target." |
| << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| if (n_query_bits == 0) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "Skipping " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(query_type) |
| << "]" |
| ": zero bits available for counter storage" |
| << tcu::TestLog::EndMessage; |
| |
| return result; |
| } |
| |
| /* Start the query */ |
| gl.beginQuery(query_type, qo_id); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message |
| << "A valid glBeginQuery() call generated the following error code:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| /* If GL_ARB_query_buffer_object is supported and the caller supplied a BO id, use |
| * it before we fire any draw calls */ |
| if (context_info.isExtensionSupported("GL_ARB_query_buffer_object") && qo_bo_id != 0) |
| { |
| gl.bindBuffer(GL_QUERY_BUFFER, qo_bo_id); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message |
| << "Could not bind a buffer object to GL_QUERY_BUFFER buffer object " |
| "binding point. Error reported:" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| /* Stop the query before we leave */ |
| gl.endQuery(query_type); |
| |
| return false; |
| } /* if (buffer binding operation failed) */ |
| } /* if (GL_ARB_query_buffer_object extension is supported and the supplied QO BO id |
| * is not 0) */ |
| else |
| { |
| /* Reset the QO BO id, so that we can skip the checks later */ |
| qo_bo_id = 0; |
| } |
| |
| /* Perform the draw calls, if any supplied call-back function pointer was supplied |
| * by the caller. */ |
| if (pfn_draw != DE_NULL) |
| { |
| pfn_draw(draw_user_arg); |
| } |
| |
| /* End the query */ |
| gl.endQuery(query_type); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "glEndQuery() call failed with error code" |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } /* if (glEndQuery() call failed) */ |
| |
| /* We now need to retrieve the result data using all query getter functions |
| * GL has to offer. This will be handled in two iterations: |
| * |
| * 1. The data will be retrieved using the getters without a QO BO being bound. |
| * 2. If QO was provided, we will need to issue all getter calls executed against |
| * the QO BO. We will then need to retrieve that data directly from the BO |
| * storage. |
| */ |
| const unsigned int iteration_index_wo_qo_bo = 0; |
| const unsigned int iteration_index_with_qo_bo = 1; |
| |
| for (unsigned int n_iteration = 0; n_iteration < 2; /* as per description */ |
| ++n_iteration) |
| { |
| glw::GLint* offset_int = DE_NULL; |
| glw::GLint64* offset_int64 = DE_NULL; |
| glw::GLuint* offset_uint = DE_NULL; |
| glw::GLuint64* offset_uint64 = DE_NULL; |
| glw::GLint result_int = INT_MAX; |
| glw::GLint64 result_int64 = LLONG_MAX; |
| bool result_int64_written = false; |
| glw::GLuint result_uint = UINT_MAX; |
| glw::GLuint64 result_uint64 = ULLONG_MAX; |
| bool result_uint64_written = false; |
| |
| /* Skip the QO BO iteration if QO BO id has not been provided */ |
| if (n_iteration == iteration_index_with_qo_bo && qo_bo_id == 0) |
| { |
| continue; |
| } |
| |
| /* Determine the offsets we should use for the getter calls */ |
| if (n_iteration == iteration_index_wo_qo_bo) |
| { |
| offset_int = &result_int; |
| offset_int64 = &result_int64; |
| offset_uint = &result_uint; |
| offset_uint64 = &result_uint64; |
| } |
| else |
| { |
| offset_int = (glw::GLint*)(deUintptr)PipelineStatisticsQueryUtilities::qo_bo_int_offset; |
| offset_int64 = (glw::GLint64*)(deUintptr)PipelineStatisticsQueryUtilities::qo_bo_int64_offset; |
| offset_uint = (glw::GLuint*)(deUintptr)PipelineStatisticsQueryUtilities::qo_bo_uint_offset; |
| offset_uint64 = (glw::GLuint64*)(deUintptr)PipelineStatisticsQueryUtilities::qo_bo_uint64_offset; |
| } |
| |
| /* Bind the QO BO if we need to use it for the getter calls */ |
| if (n_iteration == iteration_index_with_qo_bo) |
| { |
| gl.bindBuffer(GL_QUERY_BUFFER, qo_bo_id); |
| } |
| else if (qo_bo_id != 0) |
| { |
| gl.bindBuffer(GL_QUERY_BUFFER, 0 /* buffer */); |
| } |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message |
| << "glBindBuffer() call failed for GL_QUERY_BUFFER target with error " |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| /* Issue the getter calls. |
| * |
| * NOTE: 64-bit getter calls are supported only if >= GL 3.3*/ |
| if (glu::contextSupports(render_context.getType(), glu::ApiType::core(3, 3))) |
| { |
| gl.getQueryObjecti64v(qo_id, GL_QUERY_RESULT, offset_int64); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "glGetQueryObjecti64v() call failed with error " |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| result_int64_written = true; |
| } |
| else |
| { |
| result_int64_written = false; |
| } |
| |
| gl.getQueryObjectiv(qo_id, GL_QUERY_RESULT, offset_int); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "glGetQueryObjectiv() call failed with error " |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| if (glu::contextSupports(render_context.getType(), glu::ApiType::core(3, 3))) |
| { |
| gl.getQueryObjectui64v(qo_id, GL_QUERY_RESULT, offset_uint64); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "glGetQueryObjectui64v() call failed with error " |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| result_uint64_written = true; |
| } |
| else |
| { |
| result_uint64_written = false; |
| } |
| |
| gl.getQueryObjectuiv(qo_id, GL_QUERY_RESULT, offset_uint); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "glGetQueryObjectuiv() call failed with error " |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| /* If the getters wrote the result values to the BO, we need to retrieve the data |
| * from the BO storage */ |
| if (n_iteration == iteration_index_with_qo_bo) |
| { |
| /* Map the BO to process space */ |
| const unsigned char* bo_data_ptr = (const unsigned char*)gl.mapBuffer(GL_QUERY_BUFFER, GL_READ_ONLY); |
| |
| error_code = gl.getError(); |
| |
| if (error_code != GL_NO_ERROR || bo_data_ptr == NULL) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "QO BO mapping failed with error " |
| "[" |
| << error_code << "] and data ptr returned:" |
| "[" |
| << bo_data_ptr << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| |
| /* Retrieve the query result data */ |
| result_int = *(glw::GLint*)(bo_data_ptr + (int)(deIntptr)offset_int); |
| result_int64 = *(glw::GLint64*)(bo_data_ptr + (int)(deIntptr)offset_int64); |
| result_uint = *(glw::GLuint*)(bo_data_ptr + (int)(deIntptr)offset_uint); |
| result_uint64 = *(glw::GLuint64*)(bo_data_ptr + (int)(deIntptr)offset_uint64); |
| |
| /* Unmap the BO */ |
| gl.unmapBuffer(GL_QUERY_BUFFER); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_NO_ERROR) |
| { |
| test_context.getLog() << tcu::TestLog::Message << "QO BO unmapping failed with error " |
| "[" |
| << error_code << "]" << tcu::TestLog::EndMessage; |
| |
| return false; |
| } |
| } /* if (QO BO iteration) */ |
| |
| /* Store the retrieved data in user-provided location */ |
| if (n_iteration == iteration_index_with_qo_bo) |
| { |
| out_result->result_qo_int = result_int; |
| out_result->result_qo_int64 = result_int64; |
| out_result->result_qo_uint = result_uint; |
| out_result->result_qo_uint64 = result_uint64; |
| } |
| else |
| { |
| out_result->result_int = result_int; |
| out_result->result_int64 = result_int64; |
| out_result->result_uint = result_uint; |
| out_result->result_uint64 = result_uint64; |
| } |
| |
| out_result->int64_written = result_int64_written; |
| out_result->uint64_written = result_uint64_written; |
| } /* for (both iterations) */ |
| return result; |
| } |
| |
| /** Retrieves a GLenum value corresponding to internal _primitive_type |
| * enum value. |
| * |
| * @param primitive_type Internal primitive type to use for the getter call. |
| * |
| * @return Corresponding GL value that can be used for the draw calls, or |
| * GL_NONE if the conversion failed. |
| * |
| **/ |
| glw::GLenum PipelineStatisticsQueryUtilities::getEnumForPrimitiveType(_primitive_type primitive_type) |
| { |
| glw::GLenum result = GL_NONE; |
| |
| switch (primitive_type) |
| { |
| case PRIMITIVE_TYPE_POINTS: |
| result = GL_POINTS; |
| break; |
| case PRIMITIVE_TYPE_LINE_LOOP: |
| result = GL_LINE_LOOP; |
| break; |
| case PRIMITIVE_TYPE_LINE_STRIP: |
| result = GL_LINE_STRIP; |
| break; |
| case PRIMITIVE_TYPE_LINES: |
| result = GL_LINES; |
| break; |
| case PRIMITIVE_TYPE_LINES_ADJACENCY: |
| result = GL_LINES_ADJACENCY; |
| break; |
| case PRIMITIVE_TYPE_PATCHES: |
| result = GL_PATCHES; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLE_FAN: |
| result = GL_TRIANGLE_FAN; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLE_STRIP: |
| result = GL_TRIANGLE_STRIP; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLES: |
| result = GL_TRIANGLES; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLES_ADJACENCY: |
| result = GL_TRIANGLES_ADJACENCY; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized primitive type"); |
| } |
| } /* switch (primitive_type) */ |
| |
| return result; |
| } |
| |
| /** Retrieves a human-readable name for a _geometry_shader_input value. |
| * |
| * @param gs_input Internal _geometry_shader_input value to use for |
| * the conversion. |
| * |
| * @return Human-readable string or empty string, if the conversion failed. |
| * |
| **/ |
| std::string PipelineStatisticsQueryUtilities::getGLSLStringForGSInput(_geometry_shader_input gs_input) |
| { |
| std::string result; |
| |
| switch (gs_input) |
| { |
| case GEOMETRY_SHADER_INPUT_POINTS: |
| result = "points"; |
| break; |
| case GEOMETRY_SHADER_INPUT_LINES: |
| result = "lines"; |
| break; |
| case GEOMETRY_SHADER_INPUT_LINES_ADJACENCY: |
| result = "lines_adjacency"; |
| break; |
| case GEOMETRY_SHADER_INPUT_TRIANGLES: |
| result = "triangles"; |
| break; |
| case GEOMETRY_SHADER_INPUT_TRIANGLES_ADJACENCY: |
| result = "triangles_adjacency"; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized geometry shader input enum"); |
| } |
| } /* switch (gs_input) */ |
| |
| return result; |
| } |
| |
| /** Retrieves a human-readable string for a _geometry_shader_output value. |
| * |
| * @param gs_output _geometry_shader_output value to use for the conversion. |
| * |
| * @return Requested value or empty string, if the value was not recognized. |
| * |
| **/ |
| std::string PipelineStatisticsQueryUtilities::getGLSLStringForGSOutput(_geometry_shader_output gs_output) |
| { |
| std::string result; |
| |
| switch (gs_output) |
| { |
| case GEOMETRY_SHADER_OUTPUT_POINTS: |
| result = "points"; |
| break; |
| case GEOMETRY_SHADER_OUTPUT_LINE_STRIP: |
| result = "line_strip"; |
| break; |
| case GEOMETRY_SHADER_OUTPUT_TRIANGLE_STRIP: |
| result = "triangle_strip"; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized geometry shader output enum"); |
| } |
| } /* switch (gs_output) */ |
| |
| return result; |
| } |
| |
| /** Number of vertices the geometry shader can access on the input, if the shader |
| * uses @param gs_input input primitive type. |
| * |
| * @param gs_input Geometry shader input to use for the query. |
| * |
| * @return Requested value or 0 if @param gs_input was not recognized. |
| **/ |
| unsigned int PipelineStatisticsQueryUtilities::getNumberOfVerticesForGSInput(_geometry_shader_input gs_input) |
| { |
| unsigned int result = 0; |
| |
| switch (gs_input) |
| { |
| case GEOMETRY_SHADER_INPUT_POINTS: |
| result = 1; |
| break; |
| case GEOMETRY_SHADER_INPUT_LINES: |
| result = 2; |
| break; |
| case GEOMETRY_SHADER_INPUT_LINES_ADJACENCY: |
| result = 4; |
| break; |
| case GEOMETRY_SHADER_INPUT_TRIANGLES: |
| result = 3; |
| break; |
| case GEOMETRY_SHADER_INPUT_TRIANGLES_ADJACENCY: |
| result = 6; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized geometry shader input type"); |
| } |
| } /* switch (gs_input) */ |
| |
| return result; |
| } |
| |
| /** Retrieves a number of vertices that need to be emitted before the shader |
| * can end the primitive, with the primitive being complete, assuming the |
| * geometry shader outputs a primitive of type described by @param gs_output. |
| * |
| * @param gs_output Primitive type to be outputted by the geometry shader. |
| * |
| * @return As per description, or 0 if @param gs_output was not recognized. |
| **/ |
| unsigned int PipelineStatisticsQueryUtilities::getNumberOfVerticesForGSOutput(_geometry_shader_output gs_output) |
| { |
| unsigned int n_result_vertices = 0; |
| |
| switch (gs_output) |
| { |
| case GEOMETRY_SHADER_OUTPUT_LINE_STRIP: |
| n_result_vertices = 2; |
| break; |
| case GEOMETRY_SHADER_OUTPUT_POINTS: |
| n_result_vertices = 1; |
| break; |
| case GEOMETRY_SHADER_OUTPUT_TRIANGLE_STRIP: |
| n_result_vertices = 3; |
| break; |
| |
| default: |
| TCU_FAIL("Unrecognized geometry shader output type"); |
| } |
| |
| /* All done */ |
| return n_result_vertices; |
| } |
| |
| /** Returns the number of vertices a single primitive of type described by @param primitive_type |
| * consists of. |
| * |
| * @param primitive_type Primitive type to use for the query. |
| * |
| * @return Result value, or 0 if @param primive_type was not recognized. |
| **/ |
| unsigned int PipelineStatisticsQueryUtilities::getNumberOfVerticesForPrimitiveType(_primitive_type primitive_type) |
| { |
| unsigned int result = 0; |
| |
| switch (primitive_type) |
| { |
| case PRIMITIVE_TYPE_POINTS: |
| result = 1; |
| break; |
| case PRIMITIVE_TYPE_LINE_LOOP: /* fall-through */ |
| case PRIMITIVE_TYPE_LINE_STRIP: /* fall-through */ |
| case PRIMITIVE_TYPE_LINES: |
| result = 2; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLE_FAN: /* fall-through */ |
| case PRIMITIVE_TYPE_TRIANGLE_STRIP: /* fall-through */ |
| case PRIMITIVE_TYPE_TRIANGLES: |
| result = 3; |
| break; |
| case PRIMITIVE_TYPE_LINES_ADJACENCY: |
| result = 4; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLES_ADJACENCY: |
| result = 6; |
| break; |
| |
| default: |
| TCU_FAIL("Unrecognized primitive type"); |
| } /* switch (primitive_type) */ |
| |
| return result; |
| } |
| |
| /** Converts user-specified _geometry_shader_input value to a _primitive_type value. |
| * |
| * @param gs_input Input value for the conversion. |
| * |
| * @return Requested value, or PRIMITIVE_TYPE_COUNT if the user-specified value |
| * was unrecognized. |
| **/ |
| PipelineStatisticsQueryUtilities::_primitive_type PipelineStatisticsQueryUtilities::getPrimitiveTypeFromGSInput( |
| _geometry_shader_input gs_input) |
| { |
| _primitive_type result = PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_COUNT; |
| |
| switch (gs_input) |
| { |
| case GEOMETRY_SHADER_INPUT_POINTS: |
| result = PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_POINTS; |
| break; |
| case GEOMETRY_SHADER_INPUT_LINES: |
| result = PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_LINES; |
| break; |
| case GEOMETRY_SHADER_INPUT_LINES_ADJACENCY: |
| result = PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_LINES_ADJACENCY; |
| break; |
| case GEOMETRY_SHADER_INPUT_TRIANGLES: |
| result = PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_TRIANGLES; |
| break; |
| case GEOMETRY_SHADER_INPUT_TRIANGLES_ADJACENCY: |
| result = PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_TRIANGLES_ADJACENCY; |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized geometry shader input enum"); |
| } |
| } /* switch (gs_input) */ |
| |
| return result; |
| } |
| |
| /** Converts user-specified _draw_call_type value to a human-readable string. |
| * |
| * @param draw_call_type Input value to use for the conversion. |
| * |
| * @return Human-readable string, or "[?]" (without the quotation marks) if |
| * the input value was not recognized. |
| **/ |
| std::string PipelineStatisticsQueryUtilities::getStringForDrawCallType(_draw_call_type draw_call_type) |
| { |
| std::string result = "[?]"; |
| |
| switch (draw_call_type) |
| { |
| case DRAW_CALL_TYPE_GLDRAWARRAYS: |
| result = "glDrawArrays()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWARRAYSINDIRECT: |
| result = "glDrawArraysIndirect()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCED: |
| result = "glDrawArraysInstanced()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCEDBASEINSTANCE: |
| result = "glDrawArraysInstancedBaseInstance()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTS: |
| result = "glDrawElements()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSBASEVERTEX: |
| result = "glDrawElementsBaseVertex()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINDIRECT: |
| result = "glDrawElementsIndirect()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCED: |
| result = "glDrawElementsInstanced()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEINSTANCE: |
| result = "glDrawElementsInstancedBaseInstance()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCE: |
| result = "glDrawElementsInstancedBaseVertexBaseInstance()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWRANGEELEMENTS: |
| result = "glDrawRangeElements()"; |
| break; |
| case DRAW_CALL_TYPE_GLDRAWRANGEELEMENTSBASEVERTEX: |
| result = "glDrawRangeElementsBaseVertex()"; |
| break; |
| default: |
| DE_ASSERT(0); |
| break; |
| } |
| |
| return result; |
| } |
| |
| /** Converts a GL enum value expressing a pipeline statistics query type |
| * into a human-readable string. |
| * |
| * @param value Input value to use for the conversion. |
| * |
| * @return Human-readable string or "[?]" (without the quotation marks) |
| * if the input value was not recognized. |
| **/ |
| std::string PipelineStatisticsQueryUtilities::getStringForEnum(glw::GLenum value) |
| { |
| std::string result = "[?]"; |
| |
| switch (value) |
| { |
| case GL_CLIPPING_INPUT_PRIMITIVES_ARB: |
| result = "GL_CLIPPING_INPUT_PRIMITIVES_ARB"; |
| break; |
| case GL_CLIPPING_OUTPUT_PRIMITIVES_ARB: |
| result = "GL_CLIPPING_OUTPUT_PRIMITIVES_ARB"; |
| break; |
| case GL_COMPUTE_SHADER_INVOCATIONS_ARB: |
| result = "GL_COMPUTE_SHADER_INVOCATIONS_ARB"; |
| break; |
| case GL_FRAGMENT_SHADER_INVOCATIONS_ARB: |
| result = "GL_FRAGMENT_SHADER_INVOCATIONS_ARB"; |
| break; |
| case GL_GEOMETRY_SHADER_INVOCATIONS: |
| result = "GL_GEOMETRY_SHADER_INVOCATIONS"; |
| break; |
| case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: |
| result = "GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB"; |
| break; |
| case GL_PRIMITIVES_SUBMITTED_ARB: |
| result = "GL_PRIMITIVES_SUBMITTED_ARB"; |
| break; |
| case GL_TESS_CONTROL_SHADER_PATCHES_ARB: |
| result = "GL_TESS_CONTROL_SHADER_PATCHES_ARB"; |
| break; |
| case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: |
| result = "GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB"; |
| break; |
| case GL_VERTEX_SHADER_INVOCATIONS_ARB: |
| result = "GL_VERTEX_SHADER_INVOCATIONS_ARB"; |
| break; |
| case GL_VERTICES_SUBMITTED_ARB: |
| result = "GL_VERTICES_SUBMITTED_ARB"; |
| break; |
| } /* switch (value) */ |
| |
| return result; |
| } |
| |
| /** Converts a _primitive_type value into a human-readable string. |
| * |
| * @param primitive_type Input value to use for the conversion. |
| * |
| * @return Requested string or "[?]" (without the quotation marks) |
| * if the input value was not recognized. |
| **/ |
| std::string PipelineStatisticsQueryUtilities::getStringForPrimitiveType(_primitive_type primitive_type) |
| { |
| std::string result = "[?]"; |
| |
| switch (primitive_type) |
| { |
| case PRIMITIVE_TYPE_POINTS: |
| result = "GL_POINTS"; |
| break; |
| case PRIMITIVE_TYPE_LINE_LOOP: |
| result = "GL_LINE_LOOP"; |
| break; |
| case PRIMITIVE_TYPE_LINE_STRIP: |
| result = "GL_LINE_STRIP"; |
| break; |
| case PRIMITIVE_TYPE_LINES: |
| result = "GL_LINES"; |
| break; |
| case PRIMITIVE_TYPE_LINES_ADJACENCY: |
| result = "GL_LINES_ADJACENCY"; |
| break; |
| case PRIMITIVE_TYPE_PATCHES: |
| result = "GL_PATCHES"; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLE_FAN: |
| result = "GL_TRIANGLE_FAN"; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLE_STRIP: |
| result = "GL_TRIANGLE_STRIP"; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLES: |
| result = "GL_TRIANGLES"; |
| break; |
| case PRIMITIVE_TYPE_TRIANGLES_ADJACENCY: |
| result = "GL_TRIANGLES_ADJACENCY"; |
| break; |
| default: |
| DE_ASSERT(0); |
| break; |
| } |
| |
| return result; |
| } |
| |
| /** Tells if it is safe to use a specific draw call type. |
| * |
| * @param draw_call Draw call type to use for the query. |
| * |
| * @return True if corresponding GL entry-point is available. |
| */ |
| bool PipelineStatisticsQueryUtilities::isDrawCallSupported(_draw_call_type draw_call, const glw::Functions& gl) |
| { |
| |
| bool result = false; |
| |
| switch (draw_call) |
| { |
| case DRAW_CALL_TYPE_GLDRAWARRAYS: |
| result = (gl.drawArrays != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWARRAYSINDIRECT: |
| result = (gl.drawArraysIndirect != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCED: |
| result = (gl.drawArraysInstanced != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCEDBASEINSTANCE: |
| result = (gl.drawArraysInstancedBaseInstance != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTS: |
| result = (gl.drawElements != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSBASEVERTEX: |
| result = (gl.drawElementsBaseVertex != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINDIRECT: |
| result = (gl.drawElementsIndirect != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCED: |
| result = (gl.drawElementsInstanced != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEINSTANCE: |
| result = (gl.drawElementsInstancedBaseInstance != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCE: |
| result = (gl.drawElementsInstancedBaseVertexBaseInstance != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWRANGEELEMENTS: |
| result = (gl.drawRangeElements != DE_NULL); |
| break; |
| case DRAW_CALL_TYPE_GLDRAWRANGEELEMENTSBASEVERTEX: |
| result = (gl.drawRangeElementsBaseVertex != DE_NULL); |
| break; |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized draw call type"); |
| } |
| } /* switch (draw_call) */ |
| |
| return result; |
| } |
| |
| /** Tells if user-specified draw call type is an instanced draw call. |
| * |
| * @param draw_call Input value to use for the conversion. |
| * |
| * @return true if @param draw_call corresponds to an instanced draw call, |
| * false otherwise. |
| **/ |
| bool PipelineStatisticsQueryUtilities::isInstancedDrawCall(_draw_call_type draw_call) |
| { |
| bool result = |
| (draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYSINDIRECT || |
| draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCED || |
| draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCEDBASEINSTANCE || |
| draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINDIRECT || |
| draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCED || |
| draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEINSTANCE || |
| draw_call == PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCE); |
| |
| return result; |
| } |
| |
| /** Tells if the running GL implementation supports user-specified pipeline |
| * statistics query. |
| * |
| * @param value GL enum definining the pipeline statistics query type |
| * that should be used for the query. |
| * @param context_info glu::ContextInfo instance that can be used by the method. |
| * @param render_context glu::RenderContext instance that can be used by the method. |
| * |
| * @return true if the query is supported, false otherwise. This method will return |
| * true for unrecognized enums. |
| **/ |
| bool PipelineStatisticsQueryUtilities::isQuerySupported(glw::GLenum value, const glu::ContextInfo& context_info, |
| const glu::RenderContext& render_context) |
| { |
| bool result = true; |
| |
| switch (value) |
| { |
| case GL_GEOMETRY_SHADER_INVOCATIONS: |
| case GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB: |
| { |
| if (!glu::contextSupports(render_context.getType(), glu::ApiType::core(3, 2)) && |
| !context_info.isExtensionSupported("GL_ARB_geometry_shader4")) |
| { |
| result = false; |
| } |
| |
| break; |
| } |
| |
| case GL_TESS_CONTROL_SHADER_PATCHES_ARB: |
| case GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB: |
| { |
| if (!glu::contextSupports(render_context.getType(), glu::ApiType::compatibility(4, 0)) && |
| !context_info.isExtensionSupported("GL_ARB_tessellation_shader")) |
| { |
| result = false; |
| } |
| |
| break; |
| } |
| |
| case GL_COMPUTE_SHADER_INVOCATIONS_ARB: |
| { |
| if (!glu::contextSupports(render_context.getType(), glu::ApiType::core(4, 3)) && |
| !context_info.isExtensionSupported("GL_ARB_compute_shader")) |
| { |
| result = false; |
| } |
| |
| break; |
| } |
| } /* switch (value) */ |
| |
| return result; |
| } |
| |
| /** Takes a filled _test_execution_result structure and performs the validation |
| * of the embedded data. |
| * |
| * @param run_result A filled _test_execution_result structure that |
| * should be used as input by the method. |
| * @param n_expected_values Number of possible expected values. |
| * @param expected_values Array of possible expected values. |
| * @param should_check_qo_bo_values true if the method should also verify the values |
| * retrieved from a query buffer object, false |
| * if it is OK to ignore them. |
| * @param query_type Pipeline statistics query type that was used to |
| * capture the results stored in @param run_result . |
| * @param draw_call_type_ptr Type of the draw call that was used to capture the |
| * results stored in @param run_result . |
| * @param primitive_type_ptr Primitive type that was used for the draw call that |
| * was used to capture the results stored in @param |
| * run_result . |
| * @param is_primitive_restart_enabled true if "Primitive Restart" rendering mode had been enabled |
| * when the draw call used to capture the results was made. |
| * @param test_context tcu::TestContext instance that the method can use. |
| * @param verification_type Tells how the captured values should be compared against the |
| * reference value. |
| * |
| * @return true if the result values were found valid, false otherwise. |
| **/ |
| bool PipelineStatisticsQueryUtilities::verifyResultValues( |
| const _test_execution_result& run_result, unsigned int n_expected_values, const glw::GLuint64* expected_values, |
| bool should_check_qo_bo_values, const glw::GLenum query_type, const _draw_call_type* draw_call_type_ptr, |
| const _primitive_type* primitive_type_ptr, bool is_primitive_restart_enabled, tcu::TestContext& test_context, |
| _verification_type verification_type) |
| { |
| bool result = true; |
| |
| /* Make sure all values are set to one of the expected values */ |
| std::string draw_call_name; |
| std::string primitive_name; |
| |
| bool is_result_int_valid = false; |
| bool is_result_int64_valid = false; |
| bool is_result_uint_valid = false; |
| bool is_result_uint64_valid = false; |
| bool is_result_qo_int_valid = false; |
| bool is_result_qo_int64_valid = false; |
| bool is_result_qo_uint_valid = false; |
| bool is_result_qo_uint64_valid = false; |
| |
| if (draw_call_type_ptr != DE_NULL) |
| { |
| draw_call_name = getStringForDrawCallType(*draw_call_type_ptr); |
| } |
| else |
| { |
| draw_call_name = "(does not apply)"; |
| } |
| |
| if (primitive_type_ptr != DE_NULL) |
| { |
| primitive_name = getStringForPrimitiveType(*primitive_type_ptr); |
| } |
| else |
| { |
| primitive_name = "(does not apply)"; |
| } |
| |
| for (unsigned int n_expected_value = 0; n_expected_value < n_expected_values; ++n_expected_value) |
| { |
| glw::GLuint64 expected_value = 0; |
| |
| expected_value = expected_values[n_expected_value]; |
| |
| if ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| (glw::GLuint64)run_result.result_int == expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| (glw::GLuint64)run_result.result_int >= expected_value)) |
| { |
| is_result_int_valid = true; |
| } |
| |
| if (run_result.int64_written && ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| run_result.result_int64 == (glw::GLint64)expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| run_result.result_int64 >= (glw::GLint64)expected_value))) |
| { |
| is_result_int64_valid = true; |
| } |
| |
| if ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| (glw::GLuint64)run_result.result_uint == expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| (glw::GLuint64)run_result.result_uint >= expected_value)) |
| { |
| is_result_uint_valid = true; |
| } |
| |
| if (run_result.uint64_written && |
| ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && run_result.result_uint64 == expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && run_result.result_uint64 >= expected_value))) |
| { |
| is_result_uint64_valid = true; |
| } |
| |
| if (should_check_qo_bo_values) |
| { |
| if ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| (glw::GLuint64)run_result.result_qo_int == expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| (glw::GLuint64)run_result.result_qo_int >= expected_value)) |
| { |
| is_result_qo_int_valid = true; |
| } |
| |
| if (run_result.int64_written && ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| run_result.result_qo_int64 == (glw::GLint64)expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| run_result.result_qo_int64 >= (glw::GLint64)expected_value))) |
| { |
| is_result_qo_int64_valid = true; |
| } |
| |
| if ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| (glw::GLuint64)run_result.result_qo_uint == expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| (glw::GLuint64)run_result.result_qo_uint >= expected_value)) |
| { |
| is_result_qo_uint_valid = true; |
| } |
| |
| if (run_result.uint64_written && ((verification_type == VERIFICATION_TYPE_EXACT_MATCH && |
| run_result.result_qo_uint64 == expected_value) || |
| (verification_type == VERIFICATION_TYPE_EQUAL_OR_GREATER && |
| run_result.result_qo_uint64 >= expected_value))) |
| { |
| is_result_qo_uint64_valid = true; |
| } |
| } /* if (should_check_qo_bo_values) */ |
| } /* for (both expected values) */ |
| |
| if (!is_result_int_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLint>( |
| run_result.result_int, "non-QO BO int32", n_expected_values, expected_values, query_type, draw_call_name, |
| primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (run_result.int64_written && !is_result_int64_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLint64>( |
| run_result.result_int64, "non-QO BO int64", n_expected_values, expected_values, query_type, draw_call_name, |
| primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (!is_result_uint_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLuint>( |
| run_result.result_uint, "non-QO BO uint32", n_expected_values, expected_values, query_type, draw_call_name, |
| primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (run_result.uint64_written && !is_result_uint64_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLuint64>( |
| run_result.result_uint, "non-QO BO uint64", n_expected_values, expected_values, query_type, draw_call_name, |
| primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (should_check_qo_bo_values) |
| { |
| if (!is_result_qo_int_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLint>( |
| run_result.result_qo_int, "QO BO int32", n_expected_values, expected_values, query_type, draw_call_name, |
| primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (run_result.int64_written && !is_result_qo_int64_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLint64>( |
| run_result.result_qo_int64, "QO BO int64", n_expected_values, expected_values, query_type, |
| draw_call_name, primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (!is_result_qo_uint_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLuint>( |
| run_result.result_qo_uint, "QO BO uint32", n_expected_values, expected_values, query_type, |
| draw_call_name, primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| |
| if (run_result.uint64_written && !is_result_qo_uint64_valid) |
| { |
| std::string log = PipelineStatisticsQueryUtilities::getVerifyResultValuesErrorString<glw::GLuint64>( |
| run_result.result_qo_uint64, "QO BO uint64", n_expected_values, expected_values, query_type, |
| draw_call_name, primitive_name, is_primitive_restart_enabled); |
| |
| test_context.getLog() << tcu::TestLog::Message << log.c_str() << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| } |
| |
| return result; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context |
| * @param name Test name |
| * @param description Test description |
| */ |
| PipelineStatisticsQueryTestAPICoverage1::PipelineStatisticsQueryTestAPICoverage1(deqp::Context& context) |
| : TestCase(context, "api_coverage_invalid_glbeginquery_calls", |
| "Verifies that an attempt to assign a different query object type " |
| "to an object thas has already been assigned a type, results in " |
| "an error.") |
| , m_qo_id(0) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Deinitializes all GL objects that were created during test execution. */ |
| void PipelineStatisticsQueryTestAPICoverage1::deinit() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_qo_id != 0) |
| { |
| gl.deleteQueries(1, &m_qo_id); |
| |
| m_qo_id = 0; |
| } |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult PipelineStatisticsQueryTestAPICoverage1::iterate() |
| { |
| const glu::ContextInfo& context_info = m_context.getContextInfo(); |
| bool has_passed = true; |
| glu::RenderContext& render_context = m_context.getRenderContext(); |
| glu::ContextType contextType = m_context.getRenderContext().getType(); |
| |
| /* Only continue if GL_ARB_pipeline_statistics_query extension is supported */ |
| if (!glu::contextSupports(contextType, glu::ApiType::core(4, 6)) && |
| !context_info.isExtensionSupported("GL_ARB_pipeline_statistics_query")) |
| { |
| throw tcu::NotSupportedError("GL_ARB_pipeline_statistics_query extension is not supported"); |
| } |
| |
| /* Verify that a query object which has been assigned a pipeline statistics query target A, |
| * cannot be assigned another type B (assuming A != B) */ |
| const glw::Functions& gl = render_context.getFunctions(); |
| |
| for (unsigned int n_current_item = 0; n_current_item < PipelineStatisticsQueryUtilities::n_query_targets; |
| ++n_current_item) |
| { |
| glw::GLenum current_pq = PipelineStatisticsQueryUtilities::query_targets[n_current_item]; |
| |
| /* Make sure the query is supported */ |
| if (!PipelineStatisticsQueryUtilities::isQuerySupported(current_pq, context_info, render_context)) |
| { |
| continue; |
| } |
| |
| /* Generate a new query object */ |
| gl.genQueries(1, &m_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries() call failed."); |
| |
| /* Assign a type to the query object */ |
| gl.beginQuery(current_pq, m_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginQuery() call failed."); |
| |
| gl.endQuery(current_pq); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery() call failed."); |
| |
| for (unsigned int n_different_item = 0; n_different_item < PipelineStatisticsQueryUtilities::n_query_targets; |
| ++n_different_item) |
| { |
| glw::GLenum different_pq = PipelineStatisticsQueryUtilities::query_targets[n_different_item]; |
| |
| if (current_pq == different_pq) |
| { |
| /* Skip valid iterations */ |
| continue; |
| } |
| |
| /* Make sure the other query type is supported */ |
| if (!PipelineStatisticsQueryUtilities::isQuerySupported(different_pq, context_info, render_context)) |
| { |
| continue; |
| } |
| |
| /* Try using a different type for the same object */ |
| glw::GLenum error_code = GL_NO_ERROR; |
| |
| gl.beginQuery(different_pq, m_qo_id); |
| |
| /* Has GL_INVALID_OPERATION error been generated? */ |
| error_code = gl.getError(); |
| |
| if (error_code != GL_INVALID_OPERATION) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Unexpected error code " |
| "[" |
| << error_code << "]" |
| " generated when using glBeginQuery() for a query object of type " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(current_pq) |
| << "]" |
| ", when used for a query type " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(different_pq) << "]" |
| << tcu::TestLog::EndMessage; |
| |
| has_passed = false; |
| } |
| |
| if (error_code == GL_NO_ERROR) |
| { |
| /* Clean up before we continue */ |
| gl.endQuery(different_pq); |
| GLU_EXPECT_NO_ERROR(gl.getError(), |
| "glEndQuery() should not have failed for a successful glBeginQuery() call"); |
| } |
| } /* for (all query object types) */ |
| |
| /* We need to switch to a new pipeline statistics query, so |
| * delete the query object */ |
| gl.deleteQueries(1, &m_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteQueries() call failed."); |
| } /* for (all pipeline statistics query object types) */ |
| |
| if (has_passed) |
| { |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context |
| * @param name Test name |
| * @param description Test description |
| */ |
| PipelineStatisticsQueryTestAPICoverage2::PipelineStatisticsQueryTestAPICoverage2(deqp::Context& context) |
| : TestCase(context, "api_coverage_unsupported_calls", |
| "Verifies that an attempt of using unsupported pipeline statistics queries" |
| " results in a GL_INVALID_ENUM error.") |
| , m_qo_id(0) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Deinitializes all GL objects that were created during test execution. */ |
| void PipelineStatisticsQueryTestAPICoverage2::deinit() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_qo_id != 0) |
| { |
| gl.deleteQueries(1, &m_qo_id); |
| |
| m_qo_id = 0; |
| } |
| } |
| |
| /** Executes test iteration. |
| * |
| * @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed. |
| */ |
| tcu::TestNode::IterateResult PipelineStatisticsQueryTestAPICoverage2::iterate() |
| { |
| const glu::ContextInfo& context_info = m_context.getContextInfo(); |
| glw::GLenum error_code = GL_NO_ERROR; |
| bool has_passed = true; |
| glu::RenderContext& render_context = m_context.getRenderContext(); |
| const glw::Functions& gl = render_context.getFunctions(); |
| glu::ContextType contextType = m_context.getRenderContext().getType(); |
| |
| /* Only continue if GL_ARB_pipeline_statistics_query extension is supported */ |
| if (!glu::contextSupports(contextType, glu::ApiType::core(4, 6)) && |
| !m_context.getContextInfo().isExtensionSupported("GL_ARB_pipeline_statistics_query")) |
| { |
| throw tcu::NotSupportedError("GL_ARB_pipeline_statistics_query extension is not supported"); |
| } |
| |
| /* Generate a query object we will use for the tests */ |
| gl.genQueries(1, &m_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries() call failed."); |
| |
| const glw::GLenum query_types[] = { GL_GEOMETRY_SHADER_INVOCATIONS, GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB, |
| GL_TESS_CONTROL_SHADER_PATCHES_ARB, GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB, |
| GL_COMPUTE_SHADER_INVOCATIONS_ARB }; |
| const unsigned int n_query_types = sizeof(query_types) / sizeof(query_types[0]); |
| |
| for (unsigned int n_query_type = 0; n_query_type < n_query_types; ++n_query_type) |
| { |
| glw::GLenum query_type = query_types[n_query_type]; |
| |
| if (!PipelineStatisticsQueryUtilities::isQuerySupported(query_type, context_info, render_context)) |
| { |
| gl.beginQuery(query_type, m_qo_id); |
| |
| error_code = gl.getError(); |
| if (error_code != GL_INVALID_ENUM) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "glBeginQuery() call did not generate a GL_INVALID_ENUM error " |
| "for an unsupported query type " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(query_type) << "]" |
| << tcu::TestLog::EndMessage; |
| |
| has_passed = false; |
| } |
| |
| /* If the query succeeded, stop it before we continue */ |
| if (error_code == GL_NO_ERROR) |
| { |
| gl.endQuery(query_type); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEndQuery() call failed."); |
| } |
| } /* if (query is not supported) */ |
| } /* for (all query types) */ |
| |
| if (has_passed) |
| { |
| /* Test case passed */ |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| return STOP; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context |
| * @param name Test name |
| * @param description Test description |
| */ |
| PipelineStatisticsQueryTestFunctionalBase::PipelineStatisticsQueryTestFunctionalBase(deqp::Context& context, |
| const char* name, |
| const char* description) |
| : TestCase(context, name, description) |
| , m_bo_qo_id(0) |
| , m_fbo_id(0) |
| , m_po_id(0) |
| , m_qo_id(0) |
| , m_to_id(0) |
| , m_vao_id(0) |
| , m_vbo_id(0) |
| , m_to_height(64) |
| , m_to_width(64) |
| , m_vbo_indirect_arrays_argument_offset(0) |
| , m_vbo_indirect_elements_argument_offset(0) |
| , m_vbo_index_data_offset(0) |
| , m_vbo_n_indices(0) |
| , m_vbo_vertex_data_offset(0) |
| , m_current_draw_call_type(PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_COUNT) |
| , m_current_primitive_type(PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_COUNT) |
| , m_indirect_draw_call_baseinstance_argument(0) |
| , m_indirect_draw_call_basevertex_argument(0) |
| , m_indirect_draw_call_count_argument(0) |
| , m_indirect_draw_call_first_argument(0) |
| , m_indirect_draw_call_firstindex_argument(0) |
| , m_indirect_draw_call_primcount_argument(0) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Creates a program object that can be used for dispatch/draw calls, using |
| * user-specified shader bodies. The method can either create a compute program, |
| * or a regular rendering program. |
| * |
| * ID of the initialized program object is stored in m_po_id. |
| * |
| * @param cs_body Compute shader body. If not NULL, all other arguments must |
| * be NULL. |
| * @param fs_body Fragment shader body. If not NULL, @param cs_body must be NULL. |
| * @param gs_body Geometry shader body. If not NULL, @param cs_body must be NULL. |
| * @param tc_body Tess control shader body. If not NULL, @param cs_body must be NULL. |
| * @param te_body Tess evaluation shader body. If not NULL, @param cs_body must be NULL. |
| * @param vs_body Vertex shader body. If not NULL, @param cs_body must be NULL. |
| * |
| * */ |
| void PipelineStatisticsQueryTestFunctionalBase::buildProgram(const char* cs_body, const char* fs_body, |
| const char* gs_body, const char* tc_body, |
| const char* te_body, const char* vs_body) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| glw::GLuint cs_id = 0; |
| glw::GLuint fs_id = 0; |
| glw::GLuint gs_id = 0; |
| glw::GLuint tc_id = 0; |
| glw::GLuint te_id = 0; |
| glw::GLuint vs_id = 0; |
| |
| /* Sanity checks */ |
| DE_ASSERT((cs_body != DE_NULL && (fs_body == DE_NULL && gs_body == DE_NULL && tc_body == DE_NULL && |
| te_body == DE_NULL && vs_body == DE_NULL)) || |
| (cs_body == DE_NULL && (fs_body != DE_NULL || gs_body != DE_NULL || tc_body != DE_NULL || |
| te_body != DE_NULL || vs_body != DE_NULL))); |
| |
| /* Any existing program object already initialzied? Purge it before we continue */ |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteProgram() call failed."); |
| |
| m_po_id = 0; |
| } |
| |
| /* Generate all shader objects we'll need to use for the program */ |
| if (cs_body != DE_NULL) |
| { |
| cs_id = gl.createShader(GL_COMPUTE_SHADER); |
| } |
| |
| if (fs_body != DE_NULL) |
| { |
| fs_id = gl.createShader(GL_FRAGMENT_SHADER); |
| } |
| |
| if (gs_body != DE_NULL) |
| { |
| gs_id = gl.createShader(GL_GEOMETRY_SHADER); |
| } |
| |
| if (tc_body != DE_NULL) |
| { |
| tc_id = gl.createShader(GL_TESS_CONTROL_SHADER); |
| } |
| |
| if (te_body != DE_NULL) |
| { |
| te_id = gl.createShader(GL_TESS_EVALUATION_SHADER); |
| } |
| |
| if (vs_body != DE_NULL) |
| { |
| vs_id = gl.createShader(GL_VERTEX_SHADER); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed."); |
| |
| /* Create a program object */ |
| m_po_id = gl.createProgram(); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed."); |
| |
| /* Set source code of the shaders we've created */ |
| if (cs_id != 0) |
| { |
| gl.shaderSource(cs_id, 1, /* count */ |
| &cs_body, DE_NULL); /* length */ |
| } |
| |
| if (fs_id != 0) |
| { |
| gl.shaderSource(fs_id, 1, /* count */ |
| &fs_body, DE_NULL); /* length */ |
| } |
| |
| if (gs_id != 0) |
| { |
| gl.shaderSource(gs_id, 1, /* count */ |
| &gs_body, DE_NULL); /* length */ |
| } |
| |
| if (tc_id != 0) |
| { |
| gl.shaderSource(tc_id, 1, /* count */ |
| &tc_body, DE_NULL); /* length */ |
| } |
| |
| if (te_id != 0) |
| { |
| gl.shaderSource(te_id, 1, /* count */ |
| &te_body, DE_NULL); /* length */ |
| } |
| |
| if (vs_id != 0) |
| { |
| gl.shaderSource(vs_id, 1, /* count */ |
| &vs_body, DE_NULL); /* length */ |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call(s) failed."); |
| |
| /* Compile the shaders */ |
| const glw::GLuint so_ids[] = { cs_id, fs_id, gs_id, tc_id, te_id, vs_id }; |
| const unsigned int n_so_ids = sizeof(so_ids) / sizeof(so_ids[0]); |
| |
| for (unsigned int n_so_id = 0; n_so_id < n_so_ids; ++n_so_id) |
| { |
| glw::GLint compile_status = GL_FALSE; |
| glw::GLuint so_id = so_ids[n_so_id]; |
| |
| if (so_id != 0) |
| { |
| gl.compileShader(so_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed."); |
| |
| gl.getShaderiv(so_id, GL_COMPILE_STATUS, &compile_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed."); |
| |
| if (compile_status == GL_FALSE) |
| { |
| TCU_FAIL("Shader compilation failed."); |
| } |
| |
| gl.attachShader(m_po_id, so_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed."); |
| } /* if (so_id != 0) */ |
| } /* for (all shader objects) */ |
| |
| /* Link the program object */ |
| glw::GLint link_status = GL_FALSE; |
| |
| gl.linkProgram(m_po_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed."); |
| |
| gl.getProgramiv(m_po_id, GL_LINK_STATUS, &link_status); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed."); |
| |
| if (link_status == GL_FALSE) |
| { |
| TCU_FAIL("Program linking failed."); |
| } |
| |
| /* Release the shader objects - we no longer need them */ |
| if (cs_id != 0) |
| { |
| gl.deleteShader(cs_id); |
| } |
| |
| if (fs_id != 0) |
| { |
| gl.deleteShader(fs_id); |
| } |
| |
| if (gs_id != 0) |
| { |
| gl.deleteShader(gs_id); |
| } |
| |
| if (tc_id != 0) |
| { |
| gl.deleteShader(tc_id); |
| } |
| |
| if (te_id != 0) |
| { |
| gl.deleteShader(te_id); |
| } |
| |
| if (vs_id != 0) |
| { |
| gl.deleteShader(vs_id); |
| } |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call(s) failed."); |
| } |
| |
| /** Deinitializes all GL objects that were created during test execution. |
| * Also calls the inheriting object's deinitObjects() method. |
| **/ |
| void PipelineStatisticsQueryTestFunctionalBase::deinit() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_bo_qo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_bo_qo_id); |
| |
| m_bo_qo_id = 0; |
| } |
| |
| if (m_fbo_id != 0) |
| { |
| gl.deleteFramebuffers(1, &m_fbo_id); |
| |
| m_fbo_id = 0; |
| } |
| |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| |
| m_po_id = 0; |
| } |
| |
| if (m_qo_id != 0) |
| { |
| gl.deleteQueries(1, &m_qo_id); |
| |
| m_qo_id = 0; |
| } |
| |
| if (m_to_id != 0) |
| { |
| gl.deleteTextures(1, &m_to_id); |
| |
| m_to_id = 0; |
| } |
| |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| |
| if (m_vbo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_vbo_id); |
| |
| m_vbo_id = 0; |
| } |
| |
| deinitObjects(); |
| } |
| |
| /** Dummy method that should be overloaded by inheriting methods. |
| * |
| * The method can be thought as of a placeholder for code that deinitializes |
| * test-specific GL objects. |
| **/ |
| void PipelineStatisticsQueryTestFunctionalBase::deinitObjects() |
| { |
| /* Left blank intentionally - this method should be overloaded by deriving |
| * classes. |
| */ |
| } |
| |
| /** Initializes a framebuffer object that can be used by inheriting tests |
| * for holding rendered data. |
| **/ |
| void PipelineStatisticsQueryTestFunctionalBase::initFBO() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Set up a framebuffer object */ |
| gl.genFramebuffers(1, &m_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed."); |
| |
| gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed."); |
| |
| /* Set up a texture object we will later use as a color attachment for the FBO */ |
| gl.genTextures(1, &m_to_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed."); |
| |
| gl.bindTexture(GL_TEXTURE_2D, m_to_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed."); |
| |
| gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */ |
| GL_RGBA8, m_to_width, m_to_height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed."); |
| |
| /* Set up the TO as a color attachment */ |
| gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0); /* level */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed."); |
| |
| gl.viewport(0, 0, m_to_width, m_to_height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed."); |
| } |
| |
| /** A dummy method, which can be thought of as a placeholder to initialize |
| * test-specific GL objects. |
| **/ |
| void PipelineStatisticsQueryTestFunctionalBase::initObjects() |
| { |
| /* Left blank intentionally - this method should be overloaded by deriving |
| * classes. |
| */ |
| } |
| |
| /** Initializes a vertex array object which is going to be used for the draw calls. |
| * The initialized VAO's ID is going to be stored under m_vao_id. Zeroth vertex |
| * array attribute will be configured to use @param n_components_per_vertex components |
| * and will use vertex buffer object defined by ID stored in m_vbo_id, whose data |
| * are expected to start at an offset defined by m_vbo_vertex_data_offset. |
| * |
| * @param n_components_per_vertex As per description. |
| */ |
| void PipelineStatisticsQueryTestFunctionalBase::initVAO(unsigned int n_components_per_vertex) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Release an VAO that's already been created */ |
| if (m_vao_id != 0) |
| { |
| gl.deleteVertexArrays(1, &m_vao_id); |
| |
| m_vao_id = 0; |
| } |
| |
| /* Generate a new one */ |
| 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."); |
| |
| /* Set it up */ |
| gl.vertexAttribPointer(0, /* index */ |
| n_components_per_vertex, GL_FLOAT, GL_FALSE, /* normalized */ |
| 0, /* stride */ |
| (glw::GLvoid*)(deUintptr)m_vbo_vertex_data_offset); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed."); |
| |
| gl.enableVertexAttribArray(0); /* index */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed."); |
| |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| } |
| |
| /** Initializes a vertex buffer object and stores its ID under m_vbo_id. |
| * It is assumed index data is expressed in GL_UNSIGNED_INT. |
| * |
| * The following fields will be modified by the method: |
| * |
| * m_vbo_n_indices: Will hold the number of indices stored in index |
| * data buffer. |
| * m_vbo_vertex_data_offset: Will hold the offset, from which the vertex |
| * data will be stored in VBO. |
| * m_vbo_index_data_offset: Will hold the offset, from which the index |
| * data will be stored in VBO. |
| * m_vbo_indirect_arrays_argument_offset: Will hold the offset, from which |
| * glDrawArraysIndirect() arguments will be |
| * stored in VBO. |
| * m_vbo_indirect_elements_argument_offset: Will hold the offset, from which |
| * glDrawElementsIndirect() arguments will be |
| * stored in VBO. |
| * m_indirect_draw_call_firstindex_argument: Will be updated to point to the location, from |
| * which index data starts. |
| * |
| * @param raw_vertex_data Pointer to a buffer that holds vertex data |
| * which should be used when constructing the VBO. |
| * Must not be NULL. |
| * @param raw_vertex_data_size Number of bytes available for reading under |
| * @param raw_vertex_data. |
| * @param raw_index_data Pointer to a buffer that holds index data |
| * which should be used when constructing the VBO. |
| * Must not be NULL. |
| * @param raw_index_data_size Number of bytes available for reading under |
| * @param raw_index_data . |
| * @param indirect_draw_bo_count_argument Argument to be used for indirect draw calls' |
| * "count" argument. |
| * @param indirect_draw_bo_primcount_argument Argument to be used for indirect draw calls' |
| * "primcount" argument. |
| * @param indirect_draw_bo_baseinstance_argument Argument to be used for indirect draw calls' |
| * "baseinstance" argument. |
| * @param indirect_draw_bo_first_argument Argument to be used for indirect draw calls' |
| * "first" argument. |
| * @param indirect_draw_bo_basevertex_argument Argument to be used for indirect draw calls' |
| * "basevertex" argument. |
| * |
| **/ |
| void PipelineStatisticsQueryTestFunctionalBase::initVBO( |
| const float* raw_vertex_data, unsigned int raw_vertex_data_size, const unsigned int* raw_index_data, |
| unsigned int raw_index_data_size, unsigned int indirect_draw_bo_count_argument, |
| unsigned int indirect_draw_bo_primcount_argument, unsigned int indirect_draw_bo_baseinstance_argument, |
| unsigned int indirect_draw_bo_first_argument, unsigned int indirect_draw_bo_basevertex_argument) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| glu::ContextType contextType = m_context.getRenderContext().getType(); |
| |
| /* If we already have initialized a VBO, delete it before we continue */ |
| if (m_vbo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_vbo_id); |
| |
| m_vbo_id = 0; |
| } |
| |
| /* Our BO storage is formed as below: |
| * |
| * [raw vertex data] |
| * [raw index data] |
| * [indirect glDrawArrays() call arguments] |
| * [indirect glDrawElements() call arguments] |
| * |
| * We store the relevant offsets in member fields, so that they can be used by actual test |
| * implementation. |
| */ |
| const unsigned int indirect_arrays_draw_call_arguments_size = sizeof(unsigned int) * 4; /* as per spec */ |
| const unsigned int indirect_elements_draw_call_arguments_size = sizeof(unsigned int) * 5; /* as per spec */ |
| |
| m_vbo_n_indices = raw_index_data_size / sizeof(unsigned int); |
| m_vbo_vertex_data_offset = 0; |
| m_vbo_index_data_offset = raw_vertex_data_size; |
| m_vbo_indirect_arrays_argument_offset = m_vbo_index_data_offset + raw_index_data_size; |
| m_vbo_indirect_elements_argument_offset = |
| m_vbo_indirect_arrays_argument_offset + indirect_arrays_draw_call_arguments_size; |
| |
| /* Set up 'firstindex' argument so that it points at correct index data location */ |
| DE_ASSERT((m_vbo_index_data_offset % sizeof(unsigned int)) == 0); |
| |
| m_indirect_draw_call_firstindex_argument = |
| static_cast<unsigned int>(m_vbo_index_data_offset / sizeof(unsigned int)); |
| |
| /* Form indirect draw call argument buffers */ |
| unsigned int arrays_draw_call_arguments[] = { indirect_draw_bo_count_argument, indirect_draw_bo_primcount_argument, |
| indirect_draw_bo_first_argument, |
| indirect_draw_bo_baseinstance_argument }; |
| unsigned int elements_draw_call_arguments[] = { |
| indirect_draw_bo_count_argument, indirect_draw_bo_primcount_argument, m_indirect_draw_call_firstindex_argument, |
| indirect_draw_bo_basevertex_argument, indirect_draw_bo_baseinstance_argument |
| }; |
| |
| /* Set up BO storage */ |
| const unsigned int bo_data_size = |
| m_vbo_indirect_elements_argument_offset + indirect_elements_draw_call_arguments_size; |
| |
| gl.genBuffers(1, &m_vbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed."); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_vbo_id); |
| if (glu::contextSupports(contextType, glu::ApiType::core(4, 0)) || |
| m_context.getContextInfo().isExtensionSupported("GL_ARB_draw_indirect")) |
| { |
| gl.bindBuffer(GL_DRAW_INDIRECT_BUFFER, m_vbo_id); |
| } |
| gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call(s) failed."); |
| |
| gl.bufferData(GL_ARRAY_BUFFER, bo_data_size, DE_NULL, /* data */ |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, m_vbo_vertex_data_offset, raw_vertex_data_size, raw_vertex_data); |
| gl.bufferSubData(GL_ARRAY_BUFFER, m_vbo_index_data_offset, raw_index_data_size, raw_index_data); |
| gl.bufferSubData(GL_ARRAY_BUFFER, m_vbo_indirect_arrays_argument_offset, sizeof(arrays_draw_call_arguments), |
| arrays_draw_call_arguments); |
| gl.bufferSubData(GL_ARRAY_BUFFER, m_vbo_indirect_elements_argument_offset, sizeof(elements_draw_call_arguments), |
| elements_draw_call_arguments); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call failed."); |
| } |
| |
| /** Performs the actual test. |
| * |
| * @return Always STOP. |
| **/ |
| tcu::TestNode::IterateResult PipelineStatisticsQueryTestFunctionalBase::iterate() |
| { |
| bool has_passed = true; |
| glu::ContextType contextType = m_context.getRenderContext().getType(); |
| |
| /* Carry on only if GL_ARB_pipeline_statistics_query extension is supported */ |
| if (!glu::contextSupports(contextType, glu::ApiType::core(4, 6)) && |
| !m_context.getContextInfo().isExtensionSupported("GL_ARB_pipeline_statistics_query")) |
| { |
| throw tcu::NotSupportedError("GL_ARB_pipeline_statistics_query extension is not supported"); |
| } |
| |
| /* Initialize QO BO storage if GL_ARB_query_buffer_object is supported */ |
| if (m_context.getContextInfo().isExtensionSupported("GL_ARB_query_buffer_object")) |
| { |
| initQOBO(); |
| |
| DE_ASSERT(m_bo_qo_id != 0); |
| } |
| |
| /* Initialize other test-specific objects */ |
| initObjects(); |
| |
| /* Iterate through all pipeline statistics query object types */ |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| for (unsigned int n_query_target = 0; n_query_target < PipelineStatisticsQueryUtilities::n_query_targets; |
| ++n_query_target) |
| { |
| glw::GLenum current_query_target = PipelineStatisticsQueryUtilities::query_targets[n_query_target]; |
| |
| /* Make sure the query is supported */ |
| if (!PipelineStatisticsQueryUtilities::isQuerySupported(current_query_target, m_context.getContextInfo(), m_context.getRenderContext())) |
| { |
| continue; |
| } |
| |
| if (shouldExecuteForQueryTarget(current_query_target)) |
| { |
| /* Initialize the query object */ |
| gl.genQueries(1, &m_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenQueries() call failed."); |
| |
| /* Execute the test for the particular query target. */ |
| has_passed &= executeTest(current_query_target); |
| |
| /* Delete the query object */ |
| gl.deleteQueries(1, &m_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteQueries() call failed."); |
| |
| m_qo_id = 0; |
| } |
| } /* for (all query targets) */ |
| |
| if (has_passed) |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); |
| } |
| else |
| { |
| m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); |
| } |
| |
| return STOP; |
| } |
| |
| /** Initializes a query buffer object. */ |
| void PipelineStatisticsQueryTestFunctionalBase::initQOBO() |
| { |
| const glw::Functions gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Set up the buffer object we will use for storage of query object results */ |
| unsigned char bo_data[PipelineStatisticsQueryUtilities::qo_bo_size]; |
| |
| memset(bo_data, 0xFF, sizeof(bo_data)); |
| |
| gl.genBuffers(1, &m_bo_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed."); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_qo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| |
| gl.bufferData(GL_ARRAY_BUFFER, PipelineStatisticsQueryUtilities::qo_bo_size, bo_data, GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| } |
| |
| /** Executes a draw call, whose type is specified under pThis->m_current_draw_call_type. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctionalBase instance, which |
| * should be used to extract the draw call type. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctionalBase::queryCallbackDrawCallHandler(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctionalBase* pInstance = (PipelineStatisticsQueryTestFunctionalBase*)pThis; |
| const glw::Functions& gl = pInstance->m_context.getRenderContext().getFunctions(); |
| |
| /* Issue the draw call */ |
| glw::GLenum primitive_type = |
| PipelineStatisticsQueryUtilities::getEnumForPrimitiveType(pInstance->m_current_primitive_type); |
| |
| switch (pInstance->m_current_draw_call_type) |
| { |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYS: |
| { |
| gl.drawArrays(primitive_type, pInstance->m_indirect_draw_call_first_argument, |
| pInstance->m_indirect_draw_call_count_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYSINDIRECT: |
| { |
| gl.drawArraysIndirect(primitive_type, |
| (const glw::GLvoid*)(deUintptr)pInstance->m_vbo_indirect_arrays_argument_offset); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArraysIndirect() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCED: |
| { |
| gl.drawArraysInstanced(primitive_type, pInstance->m_indirect_draw_call_first_argument, |
| pInstance->m_indirect_draw_call_count_argument, |
| pInstance->m_indirect_draw_call_primcount_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArraysInstanced() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWARRAYSINSTANCEDBASEINSTANCE: |
| { |
| gl.drawArraysInstancedBaseInstance(primitive_type, pInstance->m_indirect_draw_call_first_argument, |
| pInstance->m_indirect_draw_call_count_argument, |
| pInstance->m_indirect_draw_call_primcount_argument, |
| pInstance->m_indirect_draw_call_baseinstance_argument); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTS: |
| { |
| gl.drawElements(primitive_type, pInstance->m_indirect_draw_call_count_argument, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElements() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSBASEVERTEX: |
| { |
| gl.drawElementsBaseVertex(primitive_type, pInstance->m_indirect_draw_call_count_argument, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset, |
| pInstance->m_indirect_draw_call_basevertex_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsBaseVertex() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINDIRECT: |
| { |
| gl.drawElementsIndirect(primitive_type, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_indirect_elements_argument_offset); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsIndirect() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCED: |
| { |
| gl.drawElementsInstanced(primitive_type, pInstance->m_indirect_draw_call_count_argument, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset, |
| pInstance->m_indirect_draw_call_primcount_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsInstanced() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEINSTANCE: |
| { |
| gl.drawElementsInstancedBaseInstance( |
| primitive_type, pInstance->m_indirect_draw_call_count_argument, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset, |
| pInstance->m_indirect_draw_call_primcount_argument, pInstance->m_indirect_draw_call_baseinstance_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsInstancedBaseInstance() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCE: |
| { |
| gl.drawElementsInstancedBaseVertexBaseInstance( |
| primitive_type, pInstance->m_indirect_draw_call_count_argument, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset, |
| pInstance->m_indirect_draw_call_primcount_argument, pInstance->m_indirect_draw_call_basevertex_argument, |
| pInstance->m_indirect_draw_call_baseinstance_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawElementsInstancedBaseVertexBaseInstance() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWRANGEELEMENTS: |
| { |
| gl.drawRangeElements(primitive_type, 0, /* start */ |
| pInstance->m_vbo_n_indices, pInstance->m_indirect_draw_call_count_argument, |
| GL_UNSIGNED_INT, (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawRangeElements() call failed."); |
| |
| break; |
| } |
| |
| case PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWRANGEELEMENTSBASEVERTEX: |
| { |
| gl.drawRangeElementsBaseVertex(primitive_type, 0, /* start */ |
| pInstance->m_indirect_draw_call_count_argument - 1, /* end */ |
| pInstance->m_indirect_draw_call_count_argument, GL_UNSIGNED_INT, |
| (glw::GLvoid*)(deUintptr)pInstance->m_vbo_index_data_offset, |
| pInstance->m_indirect_draw_call_basevertex_argument); |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawRangeElementsBaseVertex() call failed."); |
| |
| break; |
| } |
| |
| default: |
| { |
| TCU_FAIL("Unrecognized draw call type"); |
| } |
| } /* switch (m_current_draw_call_type) */ |
| |
| return true; |
| } |
| |
| /** Tells whether the test instance should be executed for user-specified query target. |
| * Base class implementation returns true for all values of @param query_target. |
| * |
| * @param query_target Query target to be used for the call. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctionalBase::shouldExecuteForQueryTarget(glw::GLenum query_target) |
| { |
| (void)query_target; |
| return true; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context. |
| **/ |
| PipelineStatisticsQueryTestFunctional1::PipelineStatisticsQueryTestFunctional1(deqp::Context& context) |
| : PipelineStatisticsQueryTestFunctionalBase(context, "functional_default_qo_values", |
| "Verifies that all pipeline statistics query objects " |
| "use a default value of 0.") |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Executes a test iteration for user-specified query target. |
| * |
| * @param current_query_target Pipeline statistics query target to execute the iteration |
| * for. |
| * |
| * @return true if the test passed for the iteration, false otherwise. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional1::executeTest(glw::GLenum current_query_target) |
| { |
| bool result = true; |
| PipelineStatisticsQueryUtilities::_test_execution_result run_result; |
| |
| if (!PipelineStatisticsQueryUtilities::executeQuery( |
| current_query_target, m_qo_id, m_bo_qo_id, DE_NULL, /* pfn_draw */ |
| DE_NULL, /* draw_user_arg */ |
| m_context.getRenderContext(), m_testCtx, m_context.getContextInfo(), &run_result)) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Could not retrieve test run results for query target " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(current_query_target) << "]" |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| else |
| { |
| const glw::GLuint64 expected_value = 0; |
| |
| result &= PipelineStatisticsQueryUtilities::verifyResultValues( |
| run_result, 1, &expected_value, m_bo_qo_id != 0, /* should_check_qo_bo_values */ |
| current_query_target, DE_NULL, DE_NULL, |
| false, /* is_primitive_restart_enabled */ |
| m_testCtx, PipelineStatisticsQueryUtilities::VERIFICATION_TYPE_EXACT_MATCH); |
| } /* if (run results were obtained successfully) */ |
| |
| return result; |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context |
| */ |
| PipelineStatisticsQueryTestFunctional2::PipelineStatisticsQueryTestFunctional2(deqp::Context& context) |
| : PipelineStatisticsQueryTestFunctionalBase(context, "functional_non_rendering_commands_do_not_affect_queries", |
| "Verifies that non-rendering commands do not affect query" |
| " values.") |
| , m_bo_id(0) |
| , m_fbo_draw_id(0) |
| , m_fbo_read_id(0) |
| , m_to_draw_fbo_id(0) |
| , m_to_read_fbo_id(0) |
| , m_to_height(16) |
| , m_to_width(16) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Deinitializes all GL objects that were created during test execution. */ |
| void PipelineStatisticsQueryTestFunctional2::deinitObjects() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_bo_id != 0) |
| { |
| gl.deleteBuffers(1, &m_bo_id); |
| |
| m_bo_id = 0; |
| } |
| |
| if (m_fbo_draw_id != 0) |
| { |
| gl.deleteFramebuffers(1, &m_fbo_draw_id); |
| |
| m_fbo_draw_id = 0; |
| } |
| |
| if (m_fbo_read_id != 0) |
| { |
| gl.deleteFramebuffers(1, &m_fbo_read_id); |
| |
| m_fbo_read_id = 0; |
| } |
| |
| if (m_to_draw_fbo_id != 0) |
| { |
| gl.deleteTextures(1, &m_to_draw_fbo_id); |
| |
| m_to_draw_fbo_id = 0; |
| } |
| |
| if (m_to_read_fbo_id != 0) |
| { |
| gl.deleteTextures(1, &m_to_read_fbo_id); |
| |
| m_to_read_fbo_id = 0; |
| } |
| } |
| |
| /** Callback handler which calls glBlitFramebuffer() API function and makes sure it |
| * was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeBlitFramebufferTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| /* Framebuffer objects are bound to their FB targets at this point */ |
| gl.blitFramebuffer(0, /* srcX0 */ |
| 0, /* srcY0 */ |
| data_ptr->m_to_width, /* srcX1 */ |
| data_ptr->m_to_height, /* srcY1 */ |
| 0, /* dstX0 */ |
| 0, /* dstY0 */ |
| data_ptr->m_to_width << 1, /* dstX1 */ |
| data_ptr->m_to_height << 1, /* dstY1 */ |
| GL_COLOR_BUFFER_BIT, /* mask */ |
| GL_LINEAR); /* filter */ |
| |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBlitFramebuffer() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glBufferSubData() API function and makes sure it |
| * was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeBufferSubDataTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| const unsigned int test_data_size = (PipelineStatisticsQueryTestFunctional2::bo_size / 2); |
| unsigned char test_bo_data[test_data_size]; |
| |
| memset(test_bo_data, 0xFF, test_data_size); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, data_ptr->m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed."); |
| |
| gl.bufferSubData(GL_ARRAY_BUFFER, 0, /* offset */ |
| test_data_size, test_bo_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClearBufferfv() API function and makes sure it |
| * was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearBufferfvColorBufferTest(void* pThis) |
| { |
| const glw::GLfloat clear_color[4] = { 0, 0.1f, 0.2f, 0.3f }; |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| gl.clearBufferfv(GL_COLOR, 0, /* drawbuffer */ |
| clear_color); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferfv() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClearBufferfv() API function and makes sure it |
| * was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearBufferfvDepthBufferTest(void* pThis) |
| { |
| const glw::GLfloat clear_depth = 0.1f; |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| gl.clearBufferfv(GL_DEPTH, 0, /* drawbuffer */ |
| &clear_depth); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferfv() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClearBufferiv() API function and makes sure it |
| * was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearBufferivStencilBufferTest(void* pThis) |
| { |
| const glw::GLint clear_stencil = 123; |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| gl.clearBufferiv(GL_STENCIL, 0, /* drawbuffer */ |
| &clear_stencil); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferfv() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClearBufferSubData() API function and makes sure it |
| * was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return true if glClearBufferSubData() is available, false otherwise. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearBufferSubDataTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| bool result = true; |
| |
| if (!glu::contextSupports(data_ptr->m_context.getRenderContext().getType(), glu::ApiType::core(4, 3)) && |
| gl.clearBufferSubData == NULL) |
| { |
| /* API is unavailable */ |
| return false; |
| } |
| |
| /* Execute the API call */ |
| const unsigned char value = 0xFF; |
| |
| gl.clearBufferSubData(GL_ARRAY_BUFFER, GL_R8, 0, /* offset */ |
| data_ptr->bo_size, GL_RED, GL_UNSIGNED_BYTE, &value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferSubData() call failed."); |
| |
| /* All done */ |
| return result; |
| } |
| |
| /** Callback handler which calls glClear() API function configured to clear color |
| * buffer and makes sure it was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearColorBufferTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| gl.clear(GL_COLOR_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClear() API function configured to clear depth |
| * buffer and makes sure it was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearDepthBufferTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| gl.clear(GL_DEPTH_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClear() API function configured to clear stencil |
| * buffer and makes sure it was executed successfully. |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearStencilBufferTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| |
| gl.clear(GL_STENCIL_BUFFER_BIT); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed."); |
| |
| return true; |
| } |
| |
| /** Callback handler which calls glClearTexSubImage() API function (if available). |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return true if the function is supported by the running GL implementation, false |
| * otherwise. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeClearTexSubImageTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| bool result = true; |
| |
| if (!glu::contextSupports(data_ptr->m_context.getRenderContext().getType(), glu::ApiType::core(4, 4)) && |
| gl.clearTexSubImage == NULL) |
| { |
| /* API is unavailable */ |
| return false; |
| } |
| |
| /* Execute the API call */ |
| const unsigned char test_value = 0xFF; |
| |
| gl.clearTexSubImage(data_ptr->m_to_draw_fbo_id, 0, /* level */ |
| 0, /* xoffset */ |
| 0, /* yoffset */ |
| 0, /* zoffset */ |
| data_ptr->m_to_width / 2, data_ptr->m_to_height / 2, 1, /* depth */ |
| GL_RED, GL_UNSIGNED_BYTE, &test_value); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glClearTexSubImage() call failed."); |
| |
| /* All done */ |
| return result; |
| } |
| |
| /** Callback handler which calls glCopyImageSubData() API function (if available). |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return true if the function is supported by the running GL implementation, false |
| * otherwise. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeCopyImageSubDataTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| bool result = true; |
| |
| if (!glu::contextSupports(data_ptr->m_context.getRenderContext().getType(), glu::ApiType::core(4, 3)) && |
| gl.copyImageSubData == NULL) |
| { |
| /* API is unavailable */ |
| return false; |
| } |
| |
| /* Execute the API call */ |
| gl.copyImageSubData(data_ptr->m_to_draw_fbo_id, GL_TEXTURE_2D, 0, /* srcLevel */ |
| 0, /* srcX */ |
| 0, /* srcY */ |
| 0, /* srcZ */ |
| data_ptr->m_to_read_fbo_id, GL_TEXTURE_2D, 0, /* dstLevel */ |
| 0, /* dstX */ |
| 0, /* dstY */ |
| 0, /* dstZ */ |
| data_ptr->m_to_width / 2, data_ptr->m_to_height / 2, 1); /* src_depth */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glCopyImageSubData() call failed."); |
| |
| /* All done */ |
| return result; |
| } |
| |
| /** Callback handler which calls glTexSubImage2D(). |
| * |
| * @param pThis Pointer to a PipelineStatisticsQueryTestFunctional2 instance. Must not |
| * be NULL. |
| * |
| * @return true Always true. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeTexSubImageTest(void* pThis) |
| { |
| PipelineStatisticsQueryTestFunctional2* data_ptr = (PipelineStatisticsQueryTestFunctional2*)pThis; |
| const glw::Functions& gl = data_ptr->m_context.getRenderContext().getFunctions(); |
| const unsigned int test_data_size = PipelineStatisticsQueryTestFunctional2::bo_size / 2; |
| unsigned char test_data[test_data_size]; |
| |
| memset(test_data, 0xFF, test_data_size); |
| |
| gl.texSubImage2D(GL_TEXTURE_2D, 0, /* level */ |
| 0, /* xoffset */ |
| 0, /* yoffset */ |
| data_ptr->m_to_width / 2, data_ptr->m_to_height / 2, GL_RED, GL_UNSIGNED_BYTE, test_data); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexSubImage2D() call failed."); |
| |
| return true; |
| } |
| |
| /** Executes a test iteration for user-specified query target. |
| * |
| * @param current_query_target Pipeline statistics query target to execute the iteration |
| * for. |
| * |
| * @return true if the test passed for the iteration, false otherwise. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional2::executeTest(glw::GLenum current_query_target) |
| { |
| bool result = true; |
| PipelineStatisticsQueryUtilities::_test_execution_result run_result; |
| const PipelineStatisticsQueryUtilities::PFNQUERYDRAWHANDLERPROC query_draw_handlers[] = { |
| executeBlitFramebufferTest, |
| executeBufferSubDataTest, |
| executeClearBufferfvColorBufferTest, |
| executeClearBufferfvDepthBufferTest, |
| executeClearBufferivStencilBufferTest, |
| executeClearBufferSubDataTest, |
| executeClearColorBufferTest, |
| executeClearDepthBufferTest, |
| executeClearStencilBufferTest, |
| executeClearTexSubImageTest, |
| executeCopyImageSubDataTest, |
| executeTexSubImageTest, |
| }; |
| const unsigned int n_query_draw_handlers = sizeof(query_draw_handlers) / sizeof(query_draw_handlers[0]); |
| |
| for (unsigned int n = 0; n < n_query_draw_handlers; ++n) |
| { |
| const PipelineStatisticsQueryUtilities::PFNQUERYDRAWHANDLERPROC& draw_handler_pfn = query_draw_handlers[n]; |
| |
| /* Query executors can return false if a given test cannot be executed, given |
| * work environment constraint (eg. insufficient GL version). In case of an error, |
| * they will throw an exception. |
| */ |
| if (draw_handler_pfn(this)) |
| { |
| if (!PipelineStatisticsQueryUtilities::executeQuery( |
| current_query_target, m_qo_id, m_bo_qo_id, DE_NULL, /* pfn_draw */ |
| DE_NULL, /* draw_user_arg */ |
| m_context.getRenderContext(), m_testCtx, m_context.getContextInfo(), &run_result)) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Query execution failed for query target " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(current_query_target) << "]" |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| else |
| { |
| const glw::GLuint64 expected_value = 0; |
| bool has_passed = true; |
| |
| has_passed = PipelineStatisticsQueryUtilities::verifyResultValues( |
| run_result, 1, &expected_value, m_bo_qo_id != 0, /* should_check_qo_bo_values */ |
| current_query_target, DE_NULL, DE_NULL, false, /* is_primitive_restart_enabled */ |
| m_testCtx, PipelineStatisticsQueryUtilities::VERIFICATION_TYPE_EXACT_MATCH); |
| |
| if (!has_passed) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message << "Test failed for iteration index [" << n << "]." |
| << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| } /* if (run results were obtained successfully) */ |
| } /* if (draw handler executed successfully) */ |
| } |
| |
| return result; |
| } |
| |
| /* Initializes all GL objects used by the test */ |
| void PipelineStatisticsQueryTestFunctional2::initObjects() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| /* Set up a buffer object we will use for one of the tests */ |
| gl.genBuffers(1, &m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed."); |
| |
| gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call(s) failed."); |
| |
| gl.bufferData(GL_ARRAY_BUFFER, bo_size, DE_NULL, /* data */ |
| GL_STATIC_DRAW); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed."); |
| |
| /* Set up texture objects we will use as color attachments for test FBOs */ |
| gl.genTextures(1, &m_to_draw_fbo_id); |
| gl.genTextures(1, &m_to_read_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call(s) failed"); |
| |
| gl.bindTexture(GL_TEXTURE_2D, m_to_draw_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed."); |
| |
| gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */ |
| GL_RGBA8, m_to_width, m_to_height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed."); |
| |
| gl.bindTexture(GL_TEXTURE_2D, m_to_read_fbo_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed."); |
| |
| gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */ |
| GL_RGBA8, m_to_width, m_to_height); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed."); |
| |
| /* Set up framebuffer objects */ |
| gl.genFramebuffers(1, &m_fbo_draw_id); |
| gl.genFramebuffers(1, &m_fbo_read_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call(s) failed."); |
| |
| gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_draw_id); |
| gl.bindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read_id); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call(s) failed."); |
| |
| gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_draw_fbo_id, 0); /* level */ |
| gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_read_fbo_id, 0); /* level */ |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call(s) failed."); |
| } |
| |
| /** Constructor. |
| * |
| * @param context Rendering context |
| */ |
| PipelineStatisticsQueryTestFunctional3::PipelineStatisticsQueryTestFunctional3(deqp::Context& context) |
| : PipelineStatisticsQueryTestFunctionalBase( |
| context, "functional_primitives_vertices_submitted_and_clipping_input_output_primitives", |
| "Verifies that GL_PRIMITIVES_SUBMITTED_ARB, GL_VERTICES_SUBMITTED_ARB, " |
| "GL_CLIPPING_INPUT_PRIMITIVES_ARB, and GL_CLIPPING_OUTPUT_PRIMITIVES_ARB " |
| "queries work correctly.") |
| , m_is_primitive_restart_enabled(false) |
| { |
| /* Left blank intentionally */ |
| } |
| |
| /** Deinitializes all GL objects that were created during test execution. */ |
| void PipelineStatisticsQueryTestFunctional3::deinitObjects() |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| |
| if (m_po_id != 0) |
| { |
| gl.deleteProgram(m_po_id); |
| |
| m_po_id = 0; |
| } |
| |
| /* Disable "primitive restart" functionality */ |
| gl.disable(GL_PRIMITIVE_RESTART); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable() call failed."); |
| } |
| |
| /** Executes a test iteration for user-specified query target. |
| * |
| * @param current_query_target Pipeline statistics query target to execute the iteration |
| * for. |
| * |
| * @return true if the test passed for the iteration, false otherwise. |
| **/ |
| bool PipelineStatisticsQueryTestFunctional3::executeTest(glw::GLenum current_query_target) |
| { |
| const glw::Functions& gl = m_context.getRenderContext().getFunctions(); |
| bool result = true; |
| PipelineStatisticsQueryUtilities::_test_execution_result run_result; |
| |
| /* Sanity check: This method should only be called for GL_VERTICES_SUBMITTED_ARB, |
| * GL_PRIMITIVES_SUBMITTED_ARB, GL_CLIPPING_INPUT_PRIMITIVES_ARB and |
| * GL_CLIPPING_OUTPUT_PRIMITIVES_ARB queries */ |
| DE_ASSERT(current_query_target == GL_VERTICES_SUBMITTED_ARB || |
| current_query_target == GL_PRIMITIVES_SUBMITTED_ARB || |
| current_query_target == GL_CLIPPING_INPUT_PRIMITIVES_ARB || |
| current_query_target == GL_CLIPPING_OUTPUT_PRIMITIVES_ARB); |
| |
| /* Set up VBO. We don't really care much about the visual outcome, |
| * so any data will do. |
| */ |
| const unsigned int n_vertex_components = 2; |
| const float vertex_data[] = { -0.1f, 0.2f, 0.3f, 0.1f, 0.2f, -0.7f, 0.5f, -0.5f, |
| 0.0f, 0.0f, -0.6f, -0.9f, -0.3f, 0.3f, -0.5f, -0.5f }; |
| const unsigned int index_data[] = { |
| 0, 6, 2, 1, 3, 5, 4, |
| }; |
| const unsigned int n_indices = sizeof(index_data) / sizeof(index_data[0]); |
| |
| m_indirect_draw_call_baseinstance_argument = 1; |
| m_indirect_draw_call_basevertex_argument = 0; |
| m_indirect_draw_call_count_argument = n_indices; |
| m_indirect_draw_call_first_argument = 0; |
| m_indirect_draw_call_primcount_argument = 3; |
| |
| initVBO(vertex_data, sizeof(vertex_data), index_data, sizeof(index_data), m_indirect_draw_call_count_argument, |
| m_indirect_draw_call_primcount_argument, m_indirect_draw_call_baseinstance_argument, |
| m_indirect_draw_call_first_argument, m_indirect_draw_call_basevertex_argument); |
| |
| initVAO(n_vertex_components); |
| |
| /* Verify that the query works correctly both when primitive restart functionality |
| * is disabled and enabled */ |
| const bool pr_statuses[] = { false, true }; |
| const unsigned int n_pr_statuses = sizeof(pr_statuses) / sizeof(pr_statuses[0]); |
| |
| for (unsigned int n_pr_status = 0; n_pr_status < n_pr_statuses; ++n_pr_status) |
| { |
| m_is_primitive_restart_enabled = pr_statuses[n_pr_status]; |
| |
| /* Primitive restart should never be enabled for GL_CLIPPING_INPUT_PRIMITIVES_ARB query. */ |
| if ((current_query_target == GL_CLIPPING_INPUT_PRIMITIVES_ARB || |
| current_query_target == GL_CLIPPING_OUTPUT_PRIMITIVES_ARB) && |
| m_is_primitive_restart_enabled) |
| { |
| continue; |
| } |
| |
| /* Configure 'primitive restart' functionality */ |
| if (!m_is_primitive_restart_enabled) |
| { |
| gl.disable(GL_PRIMITIVE_RESTART); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glDisable() call failed."); |
| } |
| else |
| { |
| gl.primitiveRestartIndex(0); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glPrimitiveRestartIndex() call failed."); |
| |
| gl.enable(GL_PRIMITIVE_RESTART); |
| GLU_EXPECT_NO_ERROR(gl.getError(), "glEnable() call failed."); |
| } |
| |
| /* Iterate through all primitive types */ |
| for (unsigned int n_primitive_type = 0; |
| n_primitive_type < PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_COUNT; ++n_primitive_type) |
| { |
| m_current_primitive_type = (PipelineStatisticsQueryUtilities::_primitive_type)n_primitive_type; |
| |
| /* Exclude patches from the test */ |
| if (m_current_primitive_type == PipelineStatisticsQueryUtilities::PRIMITIVE_TYPE_PATCHES) |
| { |
| continue; |
| } |
| |
| /* Iterate through all draw call types while the query is enabled (skip DrawArrays calls if primitive restart is enabled) */ |
| for (unsigned int n_draw_call_type = |
| (m_is_primitive_restart_enabled ? PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_GLDRAWELEMENTS : |
| 0); |
| n_draw_call_type < PipelineStatisticsQueryUtilities::DRAW_CALL_TYPE_COUNT; ++n_draw_call_type) |
| { |
| m_current_draw_call_type = (PipelineStatisticsQueryUtilities::_draw_call_type)n_draw_call_type; |
| |
| /* Only continue if the draw call is supported by the context */ |
| if (!PipelineStatisticsQueryUtilities::isDrawCallSupported(m_current_draw_call_type, gl)) |
| { |
| continue; |
| } |
| |
| if (!PipelineStatisticsQueryUtilities::executeQuery( |
| current_query_target, m_qo_id, m_bo_qo_id, queryCallbackDrawCallHandler, |
| (PipelineStatisticsQueryTestFunctionalBase*)this, m_context.getRenderContext(), m_testCtx, |
| m_context.getContextInfo(), &run_result)) |
| { |
| m_testCtx.getLog() << tcu::TestLog::Message |
| << "Could not retrieve test run results for query target " |
| "[" |
| << PipelineStatisticsQueryUtilities::getStringForEnum(current_query_target) |
| << "]" << tcu::TestLog::EndMessage; |
| |
| result = false; |
| } |
| else |
| { |
| glw::GLuint64 expected_values[4] = { 0 }; |
| unsigned int n_expected_values = 0; |
| PipelineStatisticsQueryUtilities::_verification_type verification_type = |
| PipelineStatisticsQueryUtilities::VERIFICATION_TYPE_EXACT_MATCH; |
| |
| if (current_query_target == GL_CLIPPING_OUTPUT_PRIMITIVES_ARB) |
| { |
| verification_type = PipelineStatisticsQueryUtilities::VERIFICATION_TYPE_EQUAL_OR_GREATER; |
| } |
| |
| if (current_query_target == GL_VERTICES_SUBMITTED_ARB) |
| { |
| getExpectedVerticesSubmittedQueryResult(m_current_primitive_type, &n_expected_values, |
| expected_values); |
| } |
| else |
| { |
| getExpectedPrimitivesSubmittedQueryResult(m_current_primitive_type, &n_expected_values, |
| expected_values); |
| } |
| |
| result &= PipelineStatisticsQueryUtilities::verifyResultValues( |
| run_result, n_expected_values, expected_values, m_bo_qo_id != 0, /* should_check_qo_bo_values */ |
| current_query_target, &m_current_draw_call_type, &m_current_primitive_type, |
| m_is_primitive_restart_enabled, m_testCtx, verification_type); |
| |
| } /* if (run results were obtained successfully) */ |
| } /* for (all draw call types) */ |
| } /* for (all primitive types) */ |
| } /* for (both when primitive restart is disabled and enabled) */ |
| |
| return result; |
| } |
| |
| /** Returns the expected result value(s) for a GL_PRIMITIVES_SUBMITTED_ARB query. There |
| * can be either one or two result values, depending on how the implementation handles |
| * incomplete primitives. |
| * |
| * @param current_primitive_type Primitive type used for the draw call, for which |
| * the query would be executed |
| * @param out_result1_written Deref will be set to true, if the first result value |
| * was written to @param out_result1. Otherwise, it will |
| * be set to false. |
| * @param out_result1 Deref will be set to the first of the acceptable |
| * result values, if @param out_result1_written was set |
| * to true. |
| * @param out_result2_written Deref will be set to true, if the second result value |
| * was written to @param out_result2. Otherwise, it will |
| * be set to false. |
| * @param out_result2 Deref will be set to the second of the acceptable |
| * result values, if @param out_result2_written was set |
| * to true. |
| * |
| **/ |
| void PipelineStatisticsQueryTestFunctional3::getExpectedPrimitivesSubmittedQueryResult( |
| PipelineStatisticsQuery
|