1/*------------------------------------------------------------------------- 2 * OpenGL Conformance Test Suite 3 * ----------------------------- 4 * 5 * Copyright (c) 2014-2016 The Khronos Group Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 */ /*! 20 * \file 21 * \brief 22 */ /*-------------------------------------------------------------------*/ 23 24#include "esextcTessellationShaderPoints.hpp" 25#include "gluContextInfo.hpp" 26#include "gluDefs.hpp" 27#include "glwEnums.hpp" 28#include "glwFunctions.hpp" 29#include "tcuTestLog.hpp" 30 31namespace glcts 32{ 33 34const unsigned int TessellationShaderPointsgl_PointSize::m_rt_height = 35 16; /* note: update shaders if you change this value */ 36const unsigned int TessellationShaderPointsgl_PointSize::m_rt_width = 37 16; /* note: update shaders if you change this value */ 38 39/** Constructor 40 * 41 * @param context Test context 42 **/ 43TessellationShaderPointsTests::TessellationShaderPointsTests(glcts::Context& context, const ExtParameters& extParams) 44 : TestCaseGroupBase(context, extParams, "tessellation_shader_point_mode", "Verifies point mode functionality") 45{ 46 /* No implementation needed */ 47} 48 49/** 50 * Initializes test groups for geometry shader tests 51 **/ 52void TessellationShaderPointsTests::init(void) 53{ 54 addChild(new glcts::TessellationShaderPointsgl_PointSize(m_context, m_extParams)); 55 addChild(new glcts::TessellationShaderPointsVerification(m_context, m_extParams)); 56} 57 58/** Constructor 59 * 60 * @param context Test context 61 * @param name Test case's name 62 * @param description Test case's desricption 63 **/ 64TessellationShaderPointsgl_PointSize::TessellationShaderPointsgl_PointSize(Context& context, 65 const ExtParameters& extParams) 66 : TestCaseBase(context, extParams, "point_rendering", "Verifies point size used to render points is taken from" 67 " the right stage") 68 , m_fbo_id(0) 69 , m_to_id(0) 70 , m_vao_id(0) 71{ 72 /* Left blank on purpose */ 73} 74 75/** Deinitializes all ES objects created for the test. */ 76void TessellationShaderPointsgl_PointSize::deinit() 77{ 78 /** Call base class' deinit() function */ 79 TestCaseBase::deinit(); 80 81 if (!m_is_tessellation_shader_supported) 82 { 83 return; 84 } 85 86 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 87 88 if (!glu::isContextTypeES(m_context.getRenderContext().getType())) 89 { 90 /* Disable point size */ 91 gl.disable(GL_PROGRAM_POINT_SIZE); 92 } 93 94 /* Reset the program object */ 95 gl.useProgram(0); 96 97 /* Revert GL_PATCH_VERTICES_EXT to default value */ 98 gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3); 99 100 /* Unbind vertex array object */ 101 gl.bindVertexArray(0); 102 103 /* Deinitialize test-specific objects */ 104 for (_tests_iterator it = m_tests.begin(); it != m_tests.end(); ++it) 105 { 106 const _test_descriptor& test = *it; 107 108 if (test.fs_id != 0) 109 { 110 gl.deleteShader(test.fs_id); 111 } 112 113 if (test.gs_id != 0) 114 { 115 gl.deleteShader(test.gs_id); 116 } 117 118 if (test.po_id != 0) 119 { 120 gl.deleteProgram(test.po_id); 121 } 122 123 if (test.tes_id != 0) 124 { 125 gl.deleteShader(test.tes_id); 126 } 127 128 if (test.tcs_id != 0) 129 { 130 gl.deleteShader(test.tcs_id); 131 } 132 133 if (test.vs_id != 0) 134 { 135 gl.deleteShader(test.vs_id); 136 } 137 } 138 m_tests.clear(); 139 140 if (m_fbo_id != 0) 141 { 142 gl.deleteFramebuffers(1, &m_fbo_id); 143 144 m_fbo_id = 0; 145 } 146 147 if (m_to_id != 0) 148 { 149 gl.deleteTextures(1, &m_to_id); 150 151 m_to_id = 0; 152 } 153 154 if (m_vao_id != 0) 155 { 156 gl.deleteVertexArrays(1, &m_vao_id); 157 158 m_vao_id = 0; 159 } 160} 161 162/** Initializes all ES objects that will be used for the test. */ 163void TessellationShaderPointsgl_PointSize::initTest() 164{ 165 /* The test should only execute if maximum point size is at least 2 */ 166 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 167 glw::GLint gl_max_point_size_value[2] = { 0 }; 168 const int min_max_point_size = 2; 169 170 if (!glu::isContextTypeES(m_context.getRenderContext().getType())) 171 { 172 gl.getIntegerv(GL_POINT_SIZE_RANGE, gl_max_point_size_value); 173 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_POINT_SIZE_RANGE failed."); 174 } 175 else 176 { 177 gl.getIntegerv(GL_ALIASED_POINT_SIZE_RANGE, gl_max_point_size_value); 178 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_ALIASED_POINT_SIZE_RANGE failed."); 179 } 180 181 if (gl_max_point_size_value[1] < min_max_point_size) 182 { 183 throw tcu::NotSupportedError("Maximum point size is lower than 2."); 184 } 185 186 /* The test requires EXT_tessellation_shader, EXT_tessellation_shader_point_size */ 187 if (!m_is_tessellation_shader_supported || !m_is_tessellation_shader_point_size_supported) 188 { 189 throw tcu::NotSupportedError("At least one of the required extensions is not supported."); 190 } 191 192 /* Initialize vertex array object */ 193 gl.genVertexArrays(1, &m_vao_id); 194 GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); 195 196 gl.bindVertexArray(m_vao_id); 197 GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); 198 199 /* Initialize fs+gs+vs test descriptor */ 200 if (m_is_geometry_shader_extension_supported) 201 { 202 _test_descriptor pass_fs_gs_tes_vs; 203 204 /* Configure shader bodies */ 205 pass_fs_gs_tes_vs.fs_body = "${VERSION}\n" 206 "\n" 207 "precision highp float;\n" 208 "\n" 209 "in vec4 color;\n" 210 "out vec4 result;\n" 211 "\n" 212 "void main()\n" 213 "{\n" 214 " result = color;\n" 215 "}\n"; 216 217 pass_fs_gs_tes_vs.gs_body = "${VERSION}\n" 218 "\n" 219 "${GEOMETRY_SHADER_REQUIRE}\n" 220 "${GEOMETRY_POINT_SIZE_REQUIRE}\n" 221 "\n" 222 "layout(points) in;\n" 223 "layout(points, max_vertices=5) out;\n" 224 "\n" 225 "out vec4 color;\n" 226 "\n" 227 "void main()\n" 228 "{\n" 229 " const float point_dx = 2.0 / 16.0 /* rendertarget width */;\n" 230 " const float point_dy = 2.0 / 16.0 /* rendertarget_height */;\n" 231 "\n" 232 /* Center */ 233 " color = vec4(0.1, 0.2, 0.3, 0.4);\n" 234 " gl_PointSize = 2.0;\n" 235 " gl_Position = vec4(point_dx + 0.0, point_dy + 0.0, 0.0, 1.0);\n" 236 " EmitVertex();\n" 237 "\n" 238 /* Top-left corner */ 239 " color = vec4(0.2, 0.3, 0.4, 0.5);\n" 240 " gl_PointSize = 2.0;\n" 241 " gl_Position = vec4(point_dx - 1.0, -point_dy + 1.0, 0.0, 1.0);\n" 242 " EmitVertex();\n" 243 "\n" 244 /* Top-right corner */ 245 " color = vec4(0.3, 0.4, 0.5, 0.6);\n" 246 " gl_PointSize = 2.0;\n" 247 " gl_Position = vec4(-point_dx + 1.0, -point_dy + 1.0, 0.0, 1.0);\n" 248 " EmitVertex();\n" 249 "\n" 250 /* Bottom-left corner */ 251 " color = vec4(0.4, 0.5, 0.6, 0.7);\n" 252 " gl_PointSize = 2.0;\n" 253 " gl_Position = vec4(point_dx - 1.0, point_dy - 1.0, 0.0, 1.0);\n" 254 " EmitVertex();\n" 255 "\n" 256 /* Bottom-right corner */ 257 " color = vec4(0.5, 0.6, 0.7, 0.8);\n" 258 " gl_PointSize = 2.0;\n" 259 " gl_Position = vec4(-point_dx + 1.0, point_dy - 1.0, 0.0, 1.0);\n" 260 " EmitVertex();\n" 261 "\n" 262 "}\n"; 263 264 pass_fs_gs_tes_vs.tes_body = "${VERSION}\n" 265 "\n" 266 "${TESSELLATION_SHADER_REQUIRE}\n" 267 "${TESSELLATION_POINT_SIZE_REQUIRE}\n" 268 "\n" 269 "layout(isolines, point_mode) in;\n" 270 "\n" 271 "void main()\n" 272 "{\n" 273 " gl_PointSize = 0.1;\n" 274 "}\n"; 275 276 pass_fs_gs_tes_vs.tcs_body = "${VERSION}\n" 277 "\n" 278 "${TESSELLATION_SHADER_REQUIRE}\n" 279 "${TESSELLATION_POINT_SIZE_REQUIRE}\n" 280 "\n" 281 "layout(vertices=1) out;\n" 282 "\n" 283 "void main()\n" 284 "{\n" 285 " gl_out[gl_InvocationID].gl_Position =\n" 286 " gl_in[gl_InvocationID].gl_Position;\n" 287 " gl_out[gl_InvocationID].gl_PointSize =\n" 288 " gl_in[gl_InvocationID].gl_PointSize;\n" 289 "\n" 290 " gl_TessLevelOuter[0] = 1.0;\n" 291 " gl_TessLevelOuter[1] = 1.0;\n" 292 " gl_TessLevelOuter[2] = 1.0;\n" 293 " gl_TessLevelOuter[3] = 1.0;\n" 294 " gl_TessLevelInner[0] = 1.0;\n" 295 " gl_TessLevelInner[1] = 1.0;\n" 296 "}\n"; 297 298 pass_fs_gs_tes_vs.vs_body = "${VERSION}\n" 299 "\n" 300 "void main()\n" 301 "{\n" 302 " gl_PointSize = 0.01;\n" 303 "}\n"; 304 305 pass_fs_gs_tes_vs.draw_call_count = 1; 306 307 /* Store the descriptor in a vector that will be used by iterate() */ 308 m_tests.push_back(pass_fs_gs_tes_vs); 309 } /* if (m_is_geometry_shader_extension_supported) */ 310 311 /* Initialize fs+te+vs test descriptor */ 312 if (m_is_tessellation_shader_supported) 313 { 314 _test_descriptor pass_fs_tes_vs; 315 316 /* Configure shader bodies */ 317 pass_fs_tes_vs.fs_body = "${VERSION}\n" 318 "\n" 319 "precision highp float;\n" 320 "\n" 321 "in vec4 result_color;\n" 322 "out vec4 result;\n" 323 "\n" 324 "void main()\n" 325 "{\n" 326 " result = result_color;\n" 327 "}\n"; 328 329 pass_fs_tes_vs.tes_body = "${VERSION}\n" 330 "\n" 331 "${TESSELLATION_SHADER_REQUIRE}\n" 332 "${TESSELLATION_POINT_SIZE_REQUIRE}\n" 333 "\n" 334 "layout(isolines, point_mode) in;\n" 335 "\n" 336 "in vec4 tcColor[];\n" 337 "out vec4 result_color;\n" 338 "\n" 339 "void main()\n" 340 "{\n" 341 " gl_PointSize = 2.0;\n" 342 " gl_Position = gl_in[0].gl_Position;\n" 343 " result_color = tcColor[0];\n" 344 "}\n"; 345 346 pass_fs_tes_vs.tcs_body = "${VERSION}\n" 347 "\n" 348 "${TESSELLATION_SHADER_REQUIRE}\n" 349 "${TESSELLATION_POINT_SIZE_REQUIRE}\n" 350 "\n" 351 "layout(vertices=1) out;\n" 352 "\n" 353 "in vec4 color[];\n" 354 "out vec4 tcColor[];\n" 355 "\n" 356 "void main()\n" 357 "{\n" 358 " tcColor[gl_InvocationID] = color[gl_InvocationID];\n" 359 " gl_out[gl_InvocationID].gl_Position =\n" 360 " gl_in[gl_InvocationID].gl_Position;\n" 361 " gl_out[gl_InvocationID].gl_PointSize =\n" 362 " gl_in[gl_InvocationID].gl_PointSize;\n" 363 "\n" 364 " gl_TessLevelOuter[0] = 1.0;\n" 365 " gl_TessLevelOuter[1] = 1.0;\n" 366 " gl_TessLevelOuter[2] = 1.0;\n" 367 " gl_TessLevelOuter[3] = 1.0;\n" 368 " gl_TessLevelInner[0] = 1.0;\n" 369 " gl_TessLevelInner[1] = 1.0;\n" 370 "}\n"; 371 372 pass_fs_tes_vs.vs_body = "${VERSION}\n" 373 "\n" 374 "out vec4 color;\n" 375 "\n" 376 "void main()\n" 377 "{\n" 378 " const float point_dx = 2.0 / 16.0 /* rendertarget width */;\n" 379 " const float point_dy = 2.0 / 16.0 /* rendertarget_height */;\n" 380 "\n" 381 " gl_PointSize = 0.1;\n" 382 "\n" 383 " switch (gl_VertexID)\n" 384 " {\n" 385 " case 0:\n" 386 " {\n" 387 /* Center */ 388 " color = vec4(0.1, 0.2, 0.3, 0.4);\n" 389 " gl_Position = vec4(point_dx + 0.0, point_dy + 0.0, 0.0, 1.0);\n" 390 "\n" 391 " break;\n" 392 " }\n" 393 "\n" 394 " case 1:\n" 395 " {\n" 396 /* Top-left corner */ 397 " color = vec4(0.2, 0.3, 0.4, 0.5);\n" 398 " gl_Position = vec4(point_dx - 1.0, -point_dy + 1.0, 0.0, 1.0);\n" 399 "\n" 400 " break;\n" 401 " }\n" 402 "\n" 403 " case 2:\n" 404 " {\n" 405 /* Top-right corner */ 406 " color = vec4(0.3, 0.4, 0.5, 0.6);\n" 407 " gl_Position = vec4(-point_dx + 1.0, -point_dy + 1.0, 0.0, 1.0);\n" 408 "\n" 409 " break;\n" 410 " }\n" 411 "\n" 412 " case 3:\n" 413 " {\n" 414 /* Bottom-left corner */ 415 " color = vec4(0.4, 0.5, 0.6, 0.7);\n" 416 " gl_Position = vec4(point_dx - 1.0, point_dy - 1.0, 0.0, 1.0);\n" 417 "\n" 418 " break;\n" 419 " }\n" 420 "\n" 421 " case 4:\n" 422 " {\n" 423 /* Bottom-right corner */ 424 " color = vec4(0.5, 0.6, 0.7, 0.8);\n" 425 " gl_Position = vec4(-point_dx + 1.0, point_dy - 1.0, 0.0, 1.0);\n" 426 "\n" 427 " break;\n" 428 " }\n" 429 " }\n" 430 "}\n"; 431 432 pass_fs_tes_vs.draw_call_count = 5; /* points in total */ 433 434 /* Store the descriptor in a vector that will be used by iterate() */ 435 m_tests.push_back(pass_fs_tes_vs); 436 } /* if (m_is_tessellation_shader_supported) */ 437 438 /* Set up a color texture we will be rendering to */ 439 gl.genTextures(1, &m_to_id); 440 GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed"); 441 442 gl.bindTexture(GL_TEXTURE_2D, m_to_id); 443 GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() failed"); 444 445 gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, GL_RGBA8, m_rt_width, m_rt_height); 446 GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() failed"); 447 448 /* Set up a FBO we'll use for rendering */ 449 gl.genFramebuffers(1, &m_fbo_id); 450 GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() failed"); 451 452 gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); 453 GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() failed"); 454 455 gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0 /* level */); 456 GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() failed"); 457 458 if (!glu::isContextTypeES(m_context.getRenderContext().getType())) 459 { 460 /* Enable point size */ 461 gl.enable(GL_PROGRAM_POINT_SIZE); 462 } 463 464 /* We're good to execute the test! */ 465} 466 467/** Executes the test. 468 * 469 * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. 470 * 471 * Note the function throws exception should an error occur! 472 * 473 * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. 474 **/ 475tcu::TestNode::IterateResult TessellationShaderPointsgl_PointSize::iterate(void) 476{ 477 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 478 479 initTest(); 480 481 gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1); 482 GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed"); 483 484 /* Iterate through all test descriptors.. */ 485 for (_tests_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++) 486 { 487 _test_descriptor& test = *test_iterator; 488 489 /* Generate all shader objects we'll need */ 490 if (test.fs_body != NULL) 491 { 492 test.fs_id = gl.createShader(GL_FRAGMENT_SHADER); 493 } 494 495 if (test.gs_body != NULL) 496 { 497 test.gs_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER); 498 } 499 500 if (test.tes_body != NULL) 501 { 502 test.tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER); 503 } 504 505 if (test.tcs_body != NULL) 506 { 507 test.tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER); 508 } 509 510 if (test.vs_body != NULL) 511 { 512 test.vs_id = gl.createShader(GL_VERTEX_SHADER); 513 } 514 GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed"); 515 516 /* Generate a test program object before we continue */ 517 test.po_id = gl.createProgram(); 518 GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed"); 519 520 bool link_success = 521 buildProgram(test.po_id, test.fs_id, test.fs_id ? 1 : 0, &test.fs_body, test.gs_id, test.gs_id ? 1 : 0, 522 &test.gs_body, test.tes_id, test.tes_id ? 1 : 0, &test.tes_body, test.tcs_id, 523 test.tcs_id ? 1 : 0, &test.tcs_body, test.vs_id, test.vs_id ? 1 : 0, &test.vs_body); 524 525 if (!link_success) 526 { 527 TCU_FAIL("Program linking failed"); 528 } 529 530 /* Prepare for rendering */ 531 gl.clearColor(0.0f /* red */, 0.0f /* green */, 0.0f /* blue */, 0.0f /* alpha */); 532 GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor() failed"); 533 534 gl.clear(GL_COLOR_BUFFER_BIT); 535 GLU_EXPECT_NO_ERROR(gl.getError(), "glClear(GL_COLOR_BUFFER_BIT) failed"); 536 537 gl.viewport(0 /* x */, 0 /* x */, m_rt_width, m_rt_height); 538 GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() failed"); 539 540 gl.useProgram(test.po_id); 541 GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed"); 542 543 /* Render */ 544 gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, test.draw_call_count); 545 GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed"); 546 547 /* Read back the rendered data */ 548 unsigned char buffer[m_rt_width * m_rt_height * 4 /* components */] = { 0 }; 549 550 gl.readPixels(0, /* x */ 551 0, /* y */ 552 m_rt_width, m_rt_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); 553 GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed"); 554 555 /* Verify all 5 points were rendered correctly */ 556 const float epsilon = (float)1.0f / 255.0f; 557 const unsigned int pixel_size = 4; /* components, GL_UNSIGNED_BYTE */ 558 const float point_data[] = { 559 /* x */ /* y */ /* r */ /* g */ /* b */ /* a */ 560 0.5f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f, /* center */ 561 0.0f, 1.0f, 0.2f, 0.3f, 0.4f, 0.5f, /* top-left */ 562 1.0f, 1.0f, 0.3f, 0.4f, 0.5f, 0.6f, /* top-right */ 563 0.0f, 0.0f, 0.4f, 0.5f, 0.6f, 0.7f, /* bottom-left */ 564 1.0f, 0.0f, 0.5f, 0.6f, 0.7f, 0.8f /* bottom-right */ 565 }; 566 const unsigned int row_size = pixel_size * m_rt_width; 567 const unsigned int n_fields_per_point = 6; 568 const unsigned int n_points = sizeof(point_data) / sizeof(point_data[0]) / n_fields_per_point; 569 570 for (unsigned int n_point = 0; n_point < n_points; ++n_point) 571 { 572 int x = (int)(point_data[n_point * n_fields_per_point + 0] * float(m_rt_width - 1) + 0.5f); 573 int y = (int)(point_data[n_point * n_fields_per_point + 1] * float(m_rt_height - 1) + 0.5f); 574 float expected_color_r = point_data[n_point * n_fields_per_point + 2]; 575 float expected_color_g = point_data[n_point * n_fields_per_point + 3]; 576 float expected_color_b = point_data[n_point * n_fields_per_point + 4]; 577 float expected_color_a = point_data[n_point * n_fields_per_point + 5]; 578 579 const unsigned char* rendered_color_ubyte_ptr = buffer + row_size * y + x * pixel_size; 580 const float rendered_color_r = float(rendered_color_ubyte_ptr[0]) / 255.0f; 581 const float rendered_color_g = float(rendered_color_ubyte_ptr[1]) / 255.0f; 582 const float rendered_color_b = float(rendered_color_ubyte_ptr[2]) / 255.0f; 583 const float rendered_color_a = float(rendered_color_ubyte_ptr[3]) / 255.0f; 584 585 /* Compare the pixels */ 586 if (de::abs(expected_color_r - rendered_color_r) > epsilon || 587 de::abs(expected_color_g - rendered_color_g) > epsilon || 588 de::abs(expected_color_b - rendered_color_b) > epsilon || 589 de::abs(expected_color_a - rendered_color_a) > epsilon) 590 { 591 m_testCtx.getLog() << tcu::TestLog::Message << "Pixel data comparison failed; expected: " 592 << "(" << expected_color_r << ", " << expected_color_g << ", " << expected_color_b 593 << ", " << expected_color_a << ") rendered: " 594 << "(" << rendered_color_r << ", " << rendered_color_g << ", " << rendered_color_b 595 << ", " << rendered_color_a << ") epsilon: " << epsilon << tcu::TestLog::EndMessage; 596 597 TCU_FAIL("Pixel data comparison failed"); 598 } 599 } /* for (all points) */ 600 } /* for (all tests) */ 601 602 /* All done */ 603 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 604 return STOP; 605} 606 607/** Constructor 608 * 609 * @param context Test context 610 **/ 611TessellationShaderPointsVerification::TessellationShaderPointsVerification(Context& context, 612 const ExtParameters& extParams) 613 : TestCaseBase(context, extParams, "points_verification", 614 "Verifies points generated by the tessellator unit do not duplicate " 615 "and that their amount is correct") 616 , m_utils(DE_NULL) 617 , m_vao_id(0) 618{ 619 /* Left blank on purpose */ 620} 621 622/* Deinitializes all ES Instances generated for the test */ 623void TessellationShaderPointsVerification::deinit() 624{ 625 /* Call base class' deinit() */ 626 TestCaseBase::deinit(); 627 628 if (!m_is_tessellation_shader_supported) 629 { 630 return; 631 } 632 633 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 634 635 /* Unbind vertex array object */ 636 gl.bindVertexArray(0); 637 638 /* Delete utils instances */ 639 if (m_utils != DE_NULL) 640 { 641 delete m_utils; 642 643 m_utils = DE_NULL; 644 } 645 646 /* Delete vertex array object */ 647 if (m_vao_id != 0) 648 { 649 gl.deleteVertexArrays(1, &m_vao_id); 650 651 m_vao_id = 0; 652 } 653} 654 655/** Initializes ES objects necessary to run the test. */ 656void TessellationShaderPointsVerification::initTest() 657{ 658 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 659 660 /* Skip if required extensions are not supported. */ 661 if (!m_is_tessellation_shader_supported) 662 { 663 throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); 664 } 665 666 /* Initialize and bind vertex array object */ 667 gl.genVertexArrays(1, &m_vao_id); 668 GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object"); 669 670 gl.bindVertexArray(m_vao_id); 671 GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!"); 672 673 /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */ 674 glw::GLint gl_max_tess_gen_level_value = 0; 675 676 gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value); 677 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); 678 679 /* Initialize all test iterations */ 680 const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, 681 TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES, 682 TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES }; 683 const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]); 684 685 const _tessellation_shader_vertex_spacing vertex_spacings[] = { 686 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, 687 TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN, 688 }; 689 const unsigned int n_vertex_spacings = sizeof(vertex_spacings) / sizeof(vertex_spacings[0]); 690 691 for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode) 692 { 693 _tessellation_levels_set levels_set; 694 _tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode]; 695 696 levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode( 697 primitive_mode, gl_max_tess_gen_level_value, 698 TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES); 699 700 for (unsigned int n_vertex_spacing = 0; n_vertex_spacing < n_vertex_spacings; ++n_vertex_spacing) 701 { 702 _tessellation_shader_vertex_spacing vertex_spacing = vertex_spacings[n_vertex_spacing]; 703 704 for (_tessellation_levels_set_const_iterator levels_set_iterator = levels_set.begin(); 705 levels_set_iterator != levels_set.end(); levels_set_iterator++) 706 { 707 const _tessellation_levels& levels = *levels_set_iterator; 708 709 /* Skip border cases that this test cannot handle */ 710 if ((primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS || 711 primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) && 712 vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD && 713 (levels.inner[0] <= 1 || levels.inner[1] <= 1)) 714 { 715 continue; 716 } 717 718 /* Initialize a test run descriptor for the iteration-specific properties */ 719 _run run; 720 721 memcpy(run.inner, levels.inner, sizeof(run.inner)); 722 memcpy(run.outer, levels.outer, sizeof(run.outer)); 723 724 run.primitive_mode = primitive_mode; 725 run.vertex_spacing = vertex_spacing; 726 727 m_runs.push_back(run); 728 } /* for (all level sets) */ 729 } /* for (all vertex spacing modes) */ 730 } /* for (all primitive modes) */ 731 732 /* Initialize utils instance. 733 */ 734 m_utils = new TessellationShaderUtils(gl, this); 735} 736 737/** Executes the test. 738 * 739 * Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise. 740 * 741 * Note the function throws exception should an error occur! 742 * 743 * @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again. 744 **/ 745tcu::TestNode::IterateResult TessellationShaderPointsVerification::iterate(void) 746{ 747 initTest(); 748 749 /* Do not execute if required extensions are not supported. */ 750 if (!m_is_tessellation_shader_supported) 751 { 752 throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED); 753 } 754 755 /* Iterate through all the test descriptors */ 756 for (std::vector<_run>::const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++) 757 { 758 const _run& run = *run_iterator; 759 std::vector<char> run_data; 760 unsigned int run_n_vertices = 0; 761 762 run_data = m_utils->getDataGeneratedByTessellator(run.inner, true, /* point_mode */ 763 run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW, 764 run.vertex_spacing, run.outer); 765 766 run_n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(run.primitive_mode, run.inner, run.outer, 767 run.vertex_spacing, true); /* point_mode */ 768 769 /* First, make sure a valid amount of duplicate vertices was found for a single data set */ 770 verifyCorrectAmountOfDuplicateVertices(run, &run_data[0], run_n_vertices); 771 772 /* Now, verify that amount of generated vertices is correct, given 773 * tessellation shader stage configuration */ 774 verifyCorrectAmountOfVertices(run, &run_data[0], run_n_vertices); 775 } /* for (all tests) */ 776 777 /* All done */ 778 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 779 return STOP; 780} 781 782/** Verifies that a correct amount of vertices was generated, given test iteration-specific properties. 783 * Throws a TestError exception in if an incorrect amount of vertices was generated by the tessellator. 784 * 785 * @param run Run descriptor. 786 * @param run_data Data generated for the run. 787 * @param run_n_vertices Amount of vertices present at @param run_data. 788 */ 789void TessellationShaderPointsVerification::verifyCorrectAmountOfVertices(const _run& run, const void* run_data, 790 unsigned int run_n_vertices) 791{ 792 (void)run_data; 793 794 const float epsilon = 1e-5f; 795 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 796 unsigned int n_expected_vertices = 0; 797 float post_vs_inner_tess_levels[2] = { 0.0f }; 798 float post_vs_outer_tess_levels[4] = { 0.0f }; 799 800 /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value*/ 801 glw::GLint gl_max_tess_gen_level_value = 0; 802 803 gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value); 804 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname"); 805 806 /* Determine vertex spacing that the tessellator should have used for current primitive mode */ 807 glw::GLfloat actual_inner_levels[2] = { 0.0f }; 808 _tessellation_shader_vertex_spacing actual_vs_mode = run.vertex_spacing; 809 glw::GLfloat clamped_inner_levels[2] = { 0.0f }; 810 811 memcpy(actual_inner_levels, run.inner, sizeof(run.inner)); 812 813 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 814 run.vertex_spacing, actual_inner_levels[0], gl_max_tess_gen_level_value, clamped_inner_levels + 0, 815 DE_NULL); /* out_clamped_and_rounded */ 816 817 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 818 run.vertex_spacing, actual_inner_levels[1], gl_max_tess_gen_level_value, clamped_inner_levels + 1, 819 DE_NULL); /* out_clamped_and_rounded */ 820 821 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) 822 { 823 /* For isolines tessellation, outer[1] is subdivided as per specified vertex spacing as specified. 824 * outer[0] should be subdivided using equal vertex spacing. 825 * 826 * This is owing to the following language in the spec (* marks important subtleties): 827 * 828 * The *u==0* and *u==1* edges of the rectangle are subdivided according to the first outer 829 * tessellation level. For the purposes of *this* subdivision, the tessellation spacing mode 830 * is ignored and treated as "equal_spacing". 831 */ 832 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL, 833 run.outer[0], gl_max_tess_gen_level_value, 834 DE_NULL, /* out_clamped */ 835 post_vs_outer_tess_levels); 836 837 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 838 actual_vs_mode, run.outer[1], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 839 post_vs_outer_tess_levels + 1); 840 } 841 842 if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS) 843 { 844 /* As per extension spec: 845 * 846 * if either clamped inner tessellation level is one, that tessellation level 847 * is treated as though it were originally specified as 1+epsilon, which would 848 * rounded up to result in a two- or three-segment subdivision according to the 849 * tessellation spacing. 850 * 851 **/ 852 if (de::abs(clamped_inner_levels[0] - 1.0f) < epsilon) 853 { 854 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 855 run.vertex_spacing, clamped_inner_levels[0] + 1.0f, /* epsilon */ 856 gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 857 actual_inner_levels + 0); 858 } 859 860 if (de::abs(clamped_inner_levels[1] - 1.0f) < epsilon) 861 { 862 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 863 run.vertex_spacing, clamped_inner_levels[1] + 1.0f, /* epsilon */ 864 gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 865 actual_inner_levels + 1); 866 } 867 } 868 869 /* Retrieve tessellation level values, taking vertex spacing setting into account */ 870 for (int n = 0; n < 2 /* inner tessellation level values */; ++n) 871 { 872 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 873 actual_vs_mode, actual_inner_levels[n], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 874 post_vs_inner_tess_levels + n); 875 } 876 877 if (run.primitive_mode != TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) 878 { 879 for (int n = 0; n < 4 /* outer tessellation level values */; ++n) 880 { 881 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 882 actual_vs_mode, run.outer[n], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 883 post_vs_outer_tess_levels + n); 884 } 885 } 886 887 /* Calculate amount of vertices that should be generated in point mode */ 888 switch (run.primitive_mode) 889 { 890 case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES: 891 { 892 n_expected_vertices = int(post_vs_outer_tess_levels[0]) * int(post_vs_outer_tess_levels[1] + 1); 893 894 break; 895 } 896 897 case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: 898 { 899 n_expected_vertices = /* outer quad */ 900 int(post_vs_outer_tess_levels[0]) + int(post_vs_outer_tess_levels[1]) + int(post_vs_outer_tess_levels[2]) + 901 int(post_vs_outer_tess_levels[3]) + 902 /* inner quad */ 903 (int(post_vs_inner_tess_levels[0]) - 1) * (int(post_vs_inner_tess_levels[1]) - 1); 904 905 break; 906 } 907 908 case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: 909 { 910 /* If the first inner tessellation level and all three outer tessellation 911 * levels are exactly one after clamping and rounding, only a single triangle 912 * with (u,v,w) coordinates of (0,0,1), (1,0,0), and (0,1,0) is generated. 913 */ 914 if (de::abs(run.inner[0] - 1.0f) < epsilon && de::abs(run.outer[0] - 1.0f) < epsilon && 915 de::abs(run.outer[1] - 1.0f) < epsilon && de::abs(run.outer[2] - 1.0f) < epsilon) 916 { 917 n_expected_vertices = 3; 918 } 919 else 920 { 921 /* If the inner tessellation level is one and any of the outer tessellation 922 * levels is greater than one, the inner tessellation level is treated as 923 * though it were originally specified as 1+epsilon and will be rounded up to 924 * result in a two- or three-segment subdivision according to the 925 * tessellation spacing. 926 */ 927 if (de::abs(run.inner[0] - 1.0f) < epsilon && 928 (run.outer[0] > 1.0f || run.outer[1] > 1.0f || run.outer[2] > 1.0f)) 929 { 930 TessellationShaderUtils::getTessellationLevelAfterVertexSpacing( 931 run.vertex_spacing, 2.0f, gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */ 932 post_vs_inner_tess_levels); 933 } 934 935 /* Count vertices making up concentric inner triangles */ 936 n_expected_vertices = (int)post_vs_outer_tess_levels[0] + (int)post_vs_outer_tess_levels[1] + 937 (int)post_vs_outer_tess_levels[2]; 938 939 for (int n = (int)post_vs_inner_tess_levels[0]; n >= 0; n -= 2) 940 { 941 /* For the outermost inner triangle, the inner triangle is degenerate - 942 * a single point at the center of the triangle -- if <n> is two. 943 */ 944 if (n == 2) 945 { 946 n_expected_vertices++; /* degenerate vertex */ 947 948 break; 949 } 950 951 /* If <n> is three, the edges of the inner triangle are not subdivided and is 952 * the final triangle in the set of concentric triangles. 953 */ 954 if (n == 3) 955 { 956 n_expected_vertices += 3 /* vertices per triangle */; 957 958 break; 959 } 960 961 /* Otherwise, each edge of the inner triangle is divided into <n>-2 segments, 962 * with the <n>-1 vertices of this subdivision produced by intersecting the 963 * inner edge with lines perpendicular to the edge running through the <n>-1 964 * innermost vertices of the subdivision of the outer edge. 965 */ 966 if (n >= 2) 967 { 968 n_expected_vertices += (n - 2) * 3 /* triangle edges */; 969 } 970 else 971 { 972 /* Count in the degenerate point instead */ 973 n_expected_vertices++; 974 } 975 } /* for (all inner triangles) */ 976 } 977 978 break; 979 } 980 981 default: 982 { 983 TCU_FAIL("Unrecognized primitive mode"); 984 } 985 } /* switch (test.primitive_mode) */ 986 987 /* Compare two values */ 988 if (run_n_vertices != n_expected_vertices) 989 { 990 std::string primitive_mode = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode); 991 std::string vertex_spacing = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); 992 993 m_testCtx.getLog() << tcu::TestLog::Message << run_n_vertices 994 << " vertices were generated by the tessellator instead of expected " << n_expected_vertices 995 << " for primitive mode [" << primitive_mode << "], vertex spacing mode [" << vertex_spacing 996 << "], inner tessellation levels:[" << run.inner[0] << ", " << run.inner[1] 997 << "], outer tessellation levels:[" << run.outer[0] << ", " << run.outer[1] << ", " 998 << run.outer[2] << ", " << run.outer[3] << "], point mode enabled." 999 << tcu::TestLog::EndMessage; 1000 1001 TCU_FAIL("Amount of vertices generated in point mode was incorrect"); 1002 } /* if (test.n_vertices != n_expected_vertices) */ 1003} 1004 1005/** Verifies a valid amount of duplicate vertices is present in the set of coordinates 1006 * generated by the tessellator, as described by user-provided test iteration descriptor. 1007 * Throws a TestError exception if the vertex set does not meet the requirements. 1008 * 1009 * @param test Test iteration descriptor. 1010 * @param run_data Data generated for the run. 1011 * @param run_n_vertices Amount of vertices present at @param run_data. 1012 **/ 1013void TessellationShaderPointsVerification::verifyCorrectAmountOfDuplicateVertices(const _run& run, const void* run_data, 1014 unsigned int run_n_vertices) 1015{ 1016 const float epsilon = 1e-5f; 1017 unsigned int n_duplicate_vertices = 0; 1018 1019 for (unsigned int n_vertex_a = 0; n_vertex_a < run_n_vertices; ++n_vertex_a) 1020 { 1021 const float* vertex_a = (const float*)run_data + n_vertex_a * 3; /* components */ 1022 1023 for (unsigned int n_vertex_b = n_vertex_a + 1; n_vertex_b < run_n_vertices; ++n_vertex_b) 1024 { 1025 const float* vertex_b = (const float*)run_data + n_vertex_b * 3; /* components */ 1026 1027 if (de::abs(vertex_a[0] - vertex_b[0]) < epsilon && de::abs(vertex_a[1] - vertex_b[1]) < epsilon && 1028 de::abs(vertex_a[2] - vertex_b[2]) < epsilon) 1029 { 1030 n_duplicate_vertices++; 1031 } 1032 } /* for (all vertices) */ 1033 } /* for (all vertices) */ 1034 1035 if (n_duplicate_vertices != 0) 1036 { 1037 std::string vertex_spacing = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing); 1038 1039 m_testCtx.getLog() << tcu::TestLog::Message << "Duplicate vertices found for the following tesselelation" 1040 " configuration: tessellation level:" 1041 "[" 1042 << run.inner[0] << ", " << run.inner[1] << "], " 1043 "outer tessellation level:" 1044 " [" 1045 << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " << run.outer[3] 1046 << "], " 1047 << "vertex spacing mode:[" << vertex_spacing.c_str() << "]" << tcu::TestLog::EndMessage; 1048 1049 TCU_FAIL("Duplicate vertex found"); 1050 } 1051} 1052 1053} /* namespace glcts */ 1054