1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 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 Tessellation and geometry shader interaction tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fTessellationGeometryInteractionTests.hpp" 25 26 #include "tcuTestLog.hpp" 27 #include "tcuRenderTarget.hpp" 28 #include "tcuSurface.hpp" 29 #include "tcuImageCompare.hpp" 30 #include "tcuVectorUtil.hpp" 31 #include "tcuTextureUtil.hpp" 32 #include "gluRenderContext.hpp" 33 #include "gluShaderProgram.hpp" 34 #include "gluStrUtil.hpp" 35 #include "gluContextInfo.hpp" 36 #include "gluObjectWrapper.hpp" 37 #include "gluPixelTransfer.hpp" 38 #include "glwFunctions.hpp" 39 #include "glwEnums.hpp" 40 #include "deStringUtil.hpp" 41 #include "deUniquePtr.hpp" 42 43 #include <sstream> 44 #include <algorithm> 45 #include <iterator> 46 47 namespace deqp 48 { 49 namespace gles31 50 { 51 namespace Functional 52 { 53 namespace 54 { 55 56 static const char* const s_positionVertexShader = "#version 310 es\n" 57 "in highp vec4 a_position;\n" 58 "void main (void)\n" 59 "{\n" 60 " gl_Position = a_position;\n" 61 "}\n"; 62 static const char* const s_whiteOutputFragmentShader = "#version 310 es\n" 63 "layout(location = 0) out mediump vec4 fragColor;\n" 64 "void main (void)\n" 65 "{\n" 66 " fragColor = vec4(1.0);\n" 67 "}\n"; 68 69 static bool isBlack (const tcu::RGBA& c) 70 { 71 return c.getRed() == 0 && c.getGreen() == 0 && c.getBlue() == 0; 72 } 73 74 class IdentityShaderCase : public TestCase 75 { 76 public: 77 IdentityShaderCase (Context& context, const char* name, const char* description); 78 79 protected: 80 const char* getVertexSource (void) const; 81 const char* getFragmentSource (void) const; 82 }; 83 84 IdentityShaderCase::IdentityShaderCase (Context& context, const char* name, const char* description) 85 : TestCase(context, name, description) 86 { 87 } 88 89 const char* IdentityShaderCase::getVertexSource (void) const 90 { 91 return "#version 310 es\n" 92 "in highp vec4 a_position;\n" 93 "out highp vec4 v_vertex_color;\n" 94 "void main (void)\n" 95 "{\n" 96 " gl_Position = a_position;\n" 97 " v_vertex_color = vec4(a_position.x * 0.5 + 0.5, a_position.y * 0.5 + 0.5, 1.0, 0.4);\n" 98 "}\n"; 99 } 100 101 const char* IdentityShaderCase::getFragmentSource (void) const 102 { 103 return "#version 310 es\n" 104 "in mediump vec4 v_fragment_color;\n" 105 "layout(location = 0) out mediump vec4 fragColor;\n" 106 "void main (void)\n" 107 "{\n" 108 " fragColor = v_fragment_color;\n" 109 "}\n"; 110 } 111 112 class IdentityGeometryShaderCase : public IdentityShaderCase 113 { 114 public: 115 enum CaseType 116 { 117 CASE_TRIANGLES = 0, 118 CASE_QUADS, 119 CASE_ISOLINES, 120 }; 121 122 IdentityGeometryShaderCase (Context& context, const char* name, const char* description, CaseType caseType); 123 ~IdentityGeometryShaderCase (void); 124 125 private: 126 void init (void); 127 void deinit (void); 128 IterateResult iterate (void); 129 130 std::string getTessellationControlSource (void) const; 131 std::string getTessellationEvaluationSource (bool geometryActive) const; 132 std::string getGeometrySource (void) const; 133 134 enum 135 { 136 RENDER_SIZE = 128, 137 }; 138 139 const CaseType m_case; 140 deUint32 m_patchBuffer; 141 }; 142 143 IdentityGeometryShaderCase::IdentityGeometryShaderCase (Context& context, const char* name, const char* description, CaseType caseType) 144 : IdentityShaderCase (context, name, description) 145 , m_case (caseType) 146 , m_patchBuffer (0) 147 { 148 } 149 150 IdentityGeometryShaderCase::~IdentityGeometryShaderCase (void) 151 { 152 deinit(); 153 } 154 155 void IdentityGeometryShaderCase::init (void) 156 { 157 // Requirements 158 159 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") || 160 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) 161 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions"); 162 163 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || 164 m_context.getRenderTarget().getHeight() < RENDER_SIZE) 165 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target."); 166 167 // Log 168 169 m_testCtx.getLog() 170 << tcu::TestLog::Message 171 << "Testing tessellating shader program output does not change when a passthrough geometry shader is attached.\n" 172 << "Rendering two images, first with and second without a geometry shader. Expecting similar results.\n" 173 << "Using additive blending to detect overlap.\n" 174 << tcu::TestLog::EndMessage; 175 176 // Resources 177 178 { 179 static const tcu::Vec4 patchBufferData[4] = 180 { 181 tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f ), 182 tcu::Vec4( -0.9f, 0.9f, 0.0f, 1.0f ), 183 tcu::Vec4( 0.9f, -0.9f, 0.0f, 1.0f ), 184 tcu::Vec4( 0.9f, 0.9f, 0.0f, 1.0f ), 185 }; 186 187 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 188 189 gl.genBuffers(1, &m_patchBuffer); 190 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer); 191 gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW); 192 GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer"); 193 } 194 } 195 196 void IdentityGeometryShaderCase::deinit (void) 197 { 198 if (m_patchBuffer) 199 { 200 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer); 201 m_patchBuffer = 0; 202 } 203 } 204 205 IdentityGeometryShaderCase::IterateResult IdentityGeometryShaderCase::iterate (void) 206 { 207 const float innerTessellationLevel = 14.0f; 208 const float outerTessellationLevel = 14.0f; 209 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 210 tcu::Surface resultWithGeometry (RENDER_SIZE, RENDER_SIZE); 211 tcu::Surface resultWithoutGeometry (RENDER_SIZE, RENDER_SIZE); 212 213 const struct 214 { 215 const char* name; 216 const char* description; 217 bool containsGeometryShader; 218 tcu::PixelBufferAccess surfaceAccess; 219 } renderTargets[] = 220 { 221 { "RenderWithGeometryShader", "Render with geometry shader", true, resultWithGeometry.getAccess() }, 222 { "RenderWithoutGeometryShader", "Render without geometry shader", false, resultWithoutGeometry.getAccess() }, 223 }; 224 225 gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE); 226 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 227 GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport"); 228 229 gl.enable(GL_BLEND); 230 gl.blendFunc(GL_SRC_ALPHA, GL_ONE); 231 gl.blendEquation(GL_FUNC_ADD); 232 GLU_EXPECT_NO_ERROR(gl.getError(), "set blend"); 233 234 m_testCtx.getLog() << tcu::TestLog::Message << "Tessellation level: inner " << innerTessellationLevel << ", outer " << outerTessellationLevel << tcu::TestLog::EndMessage; 235 236 // render with and without geometry shader 237 for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx) 238 { 239 const tcu::ScopedLogSection section (m_testCtx.getLog(), renderTargets[renderNdx].name, renderTargets[renderNdx].description); 240 glu::ProgramSources sources; 241 242 sources << glu::VertexSource(getVertexSource()) 243 << glu::FragmentSource(getFragmentSource()) 244 << glu::TessellationControlSource(getTessellationControlSource()) 245 << glu::TessellationEvaluationSource(getTessellationEvaluationSource(renderTargets[renderNdx].containsGeometryShader)); 246 247 if (renderTargets[renderNdx].containsGeometryShader) 248 sources << glu::GeometrySource(getGeometrySource()); 249 250 { 251 const glu::ShaderProgram program (m_context.getRenderContext(), sources); 252 const glu::VertexArray vao (m_context.getRenderContext()); 253 const int posLocation = gl.getAttribLocation(program.getProgram(), "a_position"); 254 const int innerTessellationLoc = gl.getUniformLocation(program.getProgram(), "u_innerTessellationLevel"); 255 const int outerTessellationLoc = gl.getUniformLocation(program.getProgram(), "u_outerTessellationLevel"); 256 257 m_testCtx.getLog() << program; 258 259 if (!program.isOk()) 260 throw tcu::TestError("could not build program"); 261 if (posLocation == -1) 262 throw tcu::TestError("a_position location was -1"); 263 if (outerTessellationLoc == -1) 264 throw tcu::TestError("u_outerTessellationLevel location was -1"); 265 266 gl.bindVertexArray(*vao); 267 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer); 268 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); 269 gl.enableVertexAttribArray(posLocation); 270 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs"); 271 272 gl.useProgram(program.getProgram()); 273 gl.uniform1f(outerTessellationLoc, outerTessellationLevel); 274 275 if (innerTessellationLoc == -1) 276 gl.uniform1f(innerTessellationLoc, innerTessellationLevel); 277 278 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 279 280 gl.patchParameteri(GL_PATCH_VERTICES, (m_case == CASE_TRIANGLES) ? (3): (4)); 281 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 282 283 gl.clear(GL_COLOR_BUFFER_BIT); 284 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 285 286 gl.drawArrays(GL_PATCHES, 0, 4); 287 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches"); 288 289 glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess); 290 } 291 } 292 293 if (tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(), 294 "ImageCompare", 295 "Image comparison", 296 resultWithoutGeometry.getAccess(), 297 resultWithGeometry.getAccess(), 298 tcu::UVec4(8, 8, 8, 255), 299 tcu::IVec3(1, 1, 0), 300 true, 301 tcu::COMPARE_LOG_RESULT)) 302 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 303 else 304 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 305 306 return STOP; 307 } 308 309 std::string IdentityGeometryShaderCase::getTessellationControlSource (void) const 310 { 311 std::ostringstream buf; 312 313 buf << "#version 310 es\n" 314 "#extension GL_EXT_tessellation_shader : require\n" 315 "layout(vertices = 4) out;\n" 316 "\n" 317 "uniform highp float u_innerTessellationLevel;\n" 318 "uniform highp float u_outerTessellationLevel;\n" 319 "in highp vec4 v_vertex_color[];\n" 320 "out highp vec4 v_patch_color[];\n" 321 "\n" 322 "void main (void)\n" 323 "{\n" 324 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 325 " v_patch_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 326 "\n"; 327 328 if (m_case == CASE_TRIANGLES) 329 buf << " gl_TessLevelOuter[0] = u_outerTessellationLevel;\n" 330 " gl_TessLevelOuter[1] = u_outerTessellationLevel;\n" 331 " gl_TessLevelOuter[2] = u_outerTessellationLevel;\n" 332 " gl_TessLevelInner[0] = u_innerTessellationLevel;\n"; 333 else if (m_case == CASE_QUADS) 334 buf << " gl_TessLevelOuter[0] = u_outerTessellationLevel;\n" 335 " gl_TessLevelOuter[1] = u_outerTessellationLevel;\n" 336 " gl_TessLevelOuter[2] = u_outerTessellationLevel;\n" 337 " gl_TessLevelOuter[3] = u_outerTessellationLevel;\n" 338 " gl_TessLevelInner[0] = u_innerTessellationLevel;\n" 339 " gl_TessLevelInner[1] = u_innerTessellationLevel;\n"; 340 else if (m_case == CASE_ISOLINES) 341 buf << " gl_TessLevelOuter[0] = u_outerTessellationLevel;\n" 342 " gl_TessLevelOuter[1] = u_outerTessellationLevel;\n"; 343 else 344 DE_ASSERT(false); 345 346 buf << "}\n"; 347 348 return buf.str(); 349 } 350 351 std::string IdentityGeometryShaderCase::getTessellationEvaluationSource (bool geometryActive) const 352 { 353 const char* const colorOutputName = ((geometryActive) ? ("v_evaluated_color") : ("v_fragment_color")); 354 std::ostringstream buf; 355 356 buf << "#version 310 es\n" 357 "#extension GL_EXT_tessellation_shader : require\n" 358 "layout(" 359 << ((m_case == CASE_TRIANGLES) ? ("triangles") : (m_case == CASE_QUADS) ? ("quads") : ("isolines")) 360 << ") in;\n" 361 "\n" 362 "in highp vec4 v_patch_color[];\n" 363 "out highp vec4 " << colorOutputName << ";\n" 364 "\n" 365 "// note: No need to use precise gl_Position since we do not require gapless geometry\n" 366 "void main (void)\n" 367 "{\n"; 368 369 if (m_case == CASE_TRIANGLES) 370 buf << " vec3 weights = vec3(pow(gl_TessCoord.x, 1.3), pow(gl_TessCoord.y, 1.3), pow(gl_TessCoord.z, 1.3));\n" 371 " vec3 cweights = gl_TessCoord;\n" 372 " gl_Position = vec4(weights.x * gl_in[0].gl_Position.xyz + weights.y * gl_in[1].gl_Position.xyz + weights.z * gl_in[2].gl_Position.xyz, 1.0);\n" 373 " " << colorOutputName << " = cweights.x * v_patch_color[0] + cweights.y * v_patch_color[1] + cweights.z * v_patch_color[2];\n"; 374 else if (m_case == CASE_QUADS || m_case == CASE_ISOLINES) 375 buf << " vec2 normalizedCoord = (gl_TessCoord.xy * 2.0 - vec2(1.0));\n" 376 " vec2 normalizedWeights = normalizedCoord * (vec2(1.0) - 0.3 * cos(normalizedCoord.yx * 1.57));\n" 377 " vec2 weights = normalizedWeights * 0.5 + vec2(0.5);\n" 378 " vec2 cweights = gl_TessCoord.xy;\n" 379 " gl_Position = mix(mix(gl_in[0].gl_Position, gl_in[1].gl_Position, weights.y), mix(gl_in[2].gl_Position, gl_in[3].gl_Position, weights.y), weights.x);\n" 380 " " << colorOutputName << " = mix(mix(v_patch_color[0], v_patch_color[1], cweights.y), mix(v_patch_color[2], v_patch_color[3], cweights.y), cweights.x);\n"; 381 else 382 DE_ASSERT(false); 383 384 buf << "}\n"; 385 386 return buf.str(); 387 } 388 389 std::string IdentityGeometryShaderCase::getGeometrySource (void) const 390 { 391 const char* const geometryInputPrimitive = (m_case == CASE_ISOLINES) ? ("lines") : ("triangles"); 392 const char* const geometryOutputPrimitive = (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip"); 393 const int numEmitVertices = (m_case == CASE_ISOLINES) ? (2) : (3); 394 std::ostringstream buf; 395 396 buf << "#version 310 es\n" 397 "#extension GL_EXT_geometry_shader : require\n" 398 "layout(" << geometryInputPrimitive << ") in;\n" 399 "layout(" << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices <<") out;\n" 400 "\n" 401 "in highp vec4 v_evaluated_color[];\n" 402 "out highp vec4 v_fragment_color;\n" 403 "\n" 404 "void main (void)\n" 405 "{\n" 406 " for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n" 407 " {\n" 408 " gl_Position = gl_in[ndx].gl_Position;\n" 409 " v_fragment_color = v_evaluated_color[ndx];\n" 410 " EmitVertex();\n" 411 " }\n" 412 "}\n"; 413 414 return buf.str(); 415 } 416 417 class IdentityTessellationShaderCase : public IdentityShaderCase 418 { 419 public: 420 enum CaseType 421 { 422 CASE_TRIANGLES = 0, 423 CASE_ISOLINES, 424 }; 425 426 IdentityTessellationShaderCase (Context& context, const char* name, const char* description, CaseType caseType); 427 ~IdentityTessellationShaderCase (void); 428 429 private: 430 void init (void); 431 void deinit (void); 432 IterateResult iterate (void); 433 434 std::string getTessellationControlSource (void) const; 435 std::string getTessellationEvaluationSource (void) const; 436 std::string getGeometrySource (bool tessellationActive) const; 437 438 enum 439 { 440 RENDER_SIZE = 256, 441 }; 442 443 const CaseType m_case; 444 deUint32 m_dataBuffer; 445 }; 446 447 IdentityTessellationShaderCase::IdentityTessellationShaderCase (Context& context, const char* name, const char* description, CaseType caseType) 448 : IdentityShaderCase (context, name, description) 449 , m_case (caseType) 450 , m_dataBuffer (0) 451 { 452 } 453 454 IdentityTessellationShaderCase::~IdentityTessellationShaderCase (void) 455 { 456 deinit(); 457 } 458 459 void IdentityTessellationShaderCase::init (void) 460 { 461 // Requirements 462 463 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") || 464 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) 465 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions"); 466 467 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || 468 m_context.getRenderTarget().getHeight() < RENDER_SIZE) 469 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target."); 470 471 // Log 472 473 m_testCtx.getLog() 474 << tcu::TestLog::Message 475 << "Testing geometry shading shader program output does not change when a passthrough tessellation shader is attached.\n" 476 << "Rendering two images, first with and second without a tessellation shader. Expecting similar results.\n" 477 << "Using additive blending to detect overlap.\n" 478 << tcu::TestLog::EndMessage; 479 480 // Resources 481 482 { 483 static const tcu::Vec4 pointData[] = 484 { 485 tcu::Vec4( -0.4f, 0.4f, 0.0f, 1.0f ), 486 tcu::Vec4( 0.0f, -0.5f, 0.0f, 1.0f ), 487 tcu::Vec4( 0.4f, 0.4f, 0.0f, 1.0f ), 488 }; 489 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 490 491 gl.genBuffers(1, &m_dataBuffer); 492 gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer); 493 gl.bufferData(GL_ARRAY_BUFFER, sizeof(pointData), pointData, GL_STATIC_DRAW); 494 GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer"); 495 } 496 } 497 498 void IdentityTessellationShaderCase::deinit (void) 499 { 500 if (m_dataBuffer) 501 { 502 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_dataBuffer); 503 m_dataBuffer = 0; 504 } 505 } 506 507 IdentityTessellationShaderCase::IterateResult IdentityTessellationShaderCase::iterate (void) 508 { 509 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 510 tcu::Surface resultWithTessellation (RENDER_SIZE, RENDER_SIZE); 511 tcu::Surface resultWithoutTessellation (RENDER_SIZE, RENDER_SIZE); 512 const int numPrimitiveVertices = (m_case == CASE_TRIANGLES) ? (3) : (2); 513 514 const struct 515 { 516 const char* name; 517 const char* description; 518 bool containsTessellationShaders; 519 tcu::PixelBufferAccess surfaceAccess; 520 } renderTargets[] = 521 { 522 { "RenderWithTessellationShader", "Render with tessellation shader", true, resultWithTessellation.getAccess() }, 523 { "RenderWithoutTessellationShader", "Render without tessellation shader", false, resultWithoutTessellation.getAccess() }, 524 }; 525 526 gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE); 527 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 528 GLU_EXPECT_NO_ERROR(gl.getError(), "set viewport"); 529 530 gl.enable(GL_BLEND); 531 gl.blendFunc(GL_SRC_ALPHA, GL_ONE); 532 gl.blendEquation(GL_FUNC_ADD); 533 GLU_EXPECT_NO_ERROR(gl.getError(), "set blend"); 534 535 // render with and without tessellation shader 536 for (int renderNdx = 0; renderNdx < DE_LENGTH_OF_ARRAY(renderTargets); ++renderNdx) 537 { 538 const tcu::ScopedLogSection section (m_testCtx.getLog(), renderTargets[renderNdx].name, renderTargets[renderNdx].description); 539 glu::ProgramSources sources; 540 541 sources << glu::VertexSource(getVertexSource()) 542 << glu::FragmentSource(getFragmentSource()) 543 << glu::GeometrySource(getGeometrySource(renderTargets[renderNdx].containsTessellationShaders)); 544 545 if (renderTargets[renderNdx].containsTessellationShaders) 546 sources << glu::TessellationControlSource(getTessellationControlSource()) 547 << glu::TessellationEvaluationSource(getTessellationEvaluationSource()); 548 549 { 550 const glu::ShaderProgram program (m_context.getRenderContext(), sources); 551 const glu::VertexArray vao (m_context.getRenderContext()); 552 const int posLocation = gl.getAttribLocation(program.getProgram(), "a_position"); 553 554 m_testCtx.getLog() << program; 555 556 if (!program.isOk()) 557 throw tcu::TestError("could not build program"); 558 if (posLocation == -1) 559 throw tcu::TestError("a_position location was -1"); 560 561 gl.bindVertexArray(*vao); 562 gl.bindBuffer(GL_ARRAY_BUFFER, m_dataBuffer); 563 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); 564 gl.enableVertexAttribArray(posLocation); 565 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs"); 566 567 gl.useProgram(program.getProgram()); 568 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 569 570 gl.clear(GL_COLOR_BUFFER_BIT); 571 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 572 573 if (renderTargets[renderNdx].containsTessellationShaders) 574 { 575 gl.patchParameteri(GL_PATCH_VERTICES, numPrimitiveVertices); 576 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 577 578 gl.drawArrays(GL_PATCHES, 0, numPrimitiveVertices); 579 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches"); 580 } 581 else 582 { 583 gl.drawArrays((m_case == CASE_TRIANGLES) ? (GL_TRIANGLES) : (GL_LINES), 0, numPrimitiveVertices); 584 GLU_EXPECT_NO_ERROR(gl.getError(), "draw primitives"); 585 } 586 587 glu::readPixels(m_context.getRenderContext(), 0, 0, renderTargets[renderNdx].surfaceAccess); 588 } 589 } 590 591 // compare 592 { 593 bool imageOk; 594 595 if (m_context.getRenderTarget().getNumSamples() > 1) 596 imageOk = tcu::fuzzyCompare(m_testCtx.getLog(), 597 "ImageCompare", 598 "Image comparison", 599 resultWithoutTessellation.getAccess(), 600 resultWithTessellation.getAccess(), 601 0.03f, 602 tcu::COMPARE_LOG_RESULT); 603 else 604 imageOk = tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(), 605 "ImageCompare", 606 "Image comparison", 607 resultWithoutTessellation.getAccess(), 608 resultWithTessellation.getAccess(), 609 tcu::UVec4(8, 8, 8, 255), //!< threshold 610 tcu::IVec3(1, 1, 0), //!< 3x3 search kernel 611 true, //!< fragments may end up over the viewport, just ignore them 612 tcu::COMPARE_LOG_RESULT); 613 614 if (imageOk) 615 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 616 else 617 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 618 } 619 620 return STOP; 621 } 622 623 std::string IdentityTessellationShaderCase::getTessellationControlSource (void) const 624 { 625 std::ostringstream buf; 626 627 buf << "#version 310 es\n" 628 "#extension GL_EXT_tessellation_shader : require\n" 629 "layout(vertices = " << ((m_case == CASE_TRIANGLES) ? (3) : (2)) << ") out;\n" 630 "\n" 631 "in highp vec4 v_vertex_color[];\n" 632 "out highp vec4 v_control_color[];\n" 633 "\n" 634 "void main (void)\n" 635 "{\n" 636 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 637 " v_control_color[gl_InvocationID] = v_vertex_color[gl_InvocationID];\n" 638 "\n"; 639 640 if (m_case == CASE_TRIANGLES) 641 buf << " gl_TessLevelOuter[0] = 1.0;\n" 642 " gl_TessLevelOuter[1] = 1.0;\n" 643 " gl_TessLevelOuter[2] = 1.0;\n" 644 " gl_TessLevelInner[0] = 1.0;\n"; 645 else if (m_case == CASE_ISOLINES) 646 buf << " gl_TessLevelOuter[0] = 1.0;\n" 647 " gl_TessLevelOuter[1] = 1.0;\n"; 648 else 649 DE_ASSERT(false); 650 651 buf << "}\n"; 652 653 return buf.str(); 654 } 655 656 std::string IdentityTessellationShaderCase::getTessellationEvaluationSource (void) const 657 { 658 std::ostringstream buf; 659 660 buf << "#version 310 es\n" 661 "#extension GL_EXT_tessellation_shader : require\n" 662 "layout(" 663 << ((m_case == CASE_TRIANGLES) ? ("triangles") : ("isolines")) 664 << ") in;\n" 665 "\n" 666 "in highp vec4 v_control_color[];\n" 667 "out highp vec4 v_evaluated_color;\n" 668 "\n" 669 "// note: No need to use precise gl_Position since we do not require gapless geometry\n" 670 "void main (void)\n" 671 "{\n"; 672 673 if (m_case == CASE_TRIANGLES) 674 buf << " gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n" 675 " v_evaluated_color = gl_TessCoord.x * v_control_color[0] + gl_TessCoord.y * v_control_color[1] + gl_TessCoord.z * v_control_color[2];\n"; 676 else if (m_case == CASE_ISOLINES) 677 buf << " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x);\n" 678 " v_evaluated_color = mix(v_control_color[0], v_control_color[1], gl_TessCoord.x);\n"; 679 else 680 DE_ASSERT(false); 681 682 buf << "}\n"; 683 684 return buf.str(); 685 } 686 687 std::string IdentityTessellationShaderCase::getGeometrySource (bool tessellationActive) const 688 { 689 const char* const colorSourceName = (tessellationActive) ? ("v_evaluated_color") : ("v_vertex_color"); 690 const char* const geometryInputPrimitive = (m_case == CASE_ISOLINES) ? ("lines") : ("triangles"); 691 const char* const geometryOutputPrimitive = (m_case == CASE_ISOLINES) ? ("line_strip") : ("triangle_strip"); 692 const int numEmitVertices = (m_case == CASE_ISOLINES) ? (11) : (8); 693 std::ostringstream buf; 694 695 buf << "#version 310 es\n" 696 "#extension GL_EXT_geometry_shader : require\n" 697 "layout(" << geometryInputPrimitive << ") in;\n" 698 "layout(" << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices <<") out;\n" 699 "\n" 700 "in highp vec4 " << colorSourceName << "[];\n" 701 "out highp vec4 v_fragment_color;\n" 702 "\n" 703 "void main (void)\n" 704 "{\n"; 705 706 if (m_case == CASE_TRIANGLES) 707 { 708 buf << " vec4 centerPos = (gl_in[0].gl_Position + gl_in[1].gl_Position + gl_in[2].gl_Position) / 3.0f;\n" 709 "\n" 710 " for (int ndx = 0; ndx < 4; ++ndx)\n" 711 " {\n" 712 " gl_Position = centerPos + (centerPos - gl_in[ndx % 3].gl_Position);\n" 713 " v_fragment_color = " << colorSourceName << "[ndx % 3];\n" 714 " EmitVertex();\n" 715 "\n" 716 " gl_Position = centerPos + 0.7 * (centerPos - gl_in[ndx % 3].gl_Position);\n" 717 " v_fragment_color = " << colorSourceName << "[ndx % 3];\n" 718 " EmitVertex();\n" 719 " }\n"; 720 721 } 722 else if (m_case == CASE_ISOLINES) 723 { 724 buf << " vec4 mdir = vec4(gl_in[0].gl_Position.y - gl_in[1].gl_Position.y, gl_in[1].gl_Position.x - gl_in[0].gl_Position.x, 0.0, 0.0);\n" 725 " for (int i = 0; i <= 10; ++i)\n" 726 " {\n" 727 " float xweight = cos(float(i) / 10.0 * 6.28) * 0.5 + 0.5;\n" 728 " float mweight = sin(float(i) / 10.0 * 6.28) * 0.1 + 0.1;\n" 729 " gl_Position = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, xweight) + mweight * mdir;\n" 730 " v_fragment_color = mix(" << colorSourceName << "[0], " << colorSourceName << "[1], xweight);\n" 731 " EmitVertex();\n" 732 " }\n"; 733 } 734 else 735 DE_ASSERT(false); 736 737 buf << "}\n"; 738 739 return buf.str(); 740 } 741 742 class FeedbackPrimitiveTypeCase : public TestCase 743 { 744 public: 745 enum TessellationOutputType 746 { 747 TESSELLATION_OUT_TRIANGLES = 0, 748 TESSELLATION_OUT_QUADS, 749 TESSELLATION_OUT_ISOLINES, 750 751 TESSELLATION_OUT_LAST 752 }; 753 enum TessellationPointMode 754 { 755 TESSELLATION_POINTMODE_OFF = 0, 756 TESSELLATION_POINTMODE_ON, 757 758 TESSELLATION_POINTMODE_LAST 759 }; 760 enum GeometryOutputType 761 { 762 GEOMETRY_OUTPUT_POINTS = 0, 763 GEOMETRY_OUTPUT_LINES, 764 GEOMETRY_OUTPUT_TRIANGLES, 765 766 GEOMETRY_OUTPUT_LAST 767 }; 768 769 FeedbackPrimitiveTypeCase (Context& context, 770 const char* name, 771 const char* description, 772 TessellationOutputType tessellationOutput, 773 TessellationPointMode tessellationPointMode, 774 GeometryOutputType geometryOutputType); 775 ~FeedbackPrimitiveTypeCase (void); 776 777 private: 778 void init (void); 779 void deinit (void); 780 IterateResult iterate (void); 781 782 void renderWithFeedback (tcu::Surface& dst); 783 void renderWithoutFeedback (tcu::Surface& dst); 784 void verifyFeedbackResults (const std::vector<tcu::Vec4>& feedbackResult); 785 void verifyRenderedImage (const tcu::Surface& image, const std::vector<tcu::Vec4>& vertices); 786 787 void genTransformFeedback (void); 788 int getNumGeneratedElementsPerPrimitive (void) const; 789 int getNumGeneratedPrimitives (void) const; 790 int getNumTessellatedPrimitives (void) const; 791 int getGeometryAmplification (void) const; 792 793 const char* getVertexSource (void) const; 794 const char* getFragmentSource (void) const; 795 std::string getTessellationControlSource (void) const; 796 std::string getTessellationEvaluationSource (void) const; 797 std::string getGeometrySource (void) const; 798 799 static const char* getTessellationOutputDescription (TessellationOutputType tessellationOutput, 800 TessellationPointMode tessellationPointMode); 801 static const char* getGeometryInputDescription (TessellationOutputType tessellationOutput, 802 TessellationPointMode tessellationPointMode); 803 static const char* getGeometryOutputDescription (GeometryOutputType geometryOutput); 804 glw::GLenum getOutputPrimitiveGLType (void) const; 805 806 enum 807 { 808 RENDER_SIZE = 128, 809 }; 810 811 const TessellationOutputType m_tessellationOutput; 812 const TessellationPointMode m_tessellationPointMode; 813 const GeometryOutputType m_geometryOutputType; 814 815 glu::ShaderProgram* m_feedbackProgram; 816 glu::ShaderProgram* m_nonFeedbackProgram; 817 deUint32 m_patchBuffer; 818 deUint32 m_feedbackID; 819 deUint32 m_feedbackBuffer; 820 }; 821 822 FeedbackPrimitiveTypeCase::FeedbackPrimitiveTypeCase (Context& context, 823 const char* name, 824 const char* description, 825 TessellationOutputType tessellationOutput, 826 TessellationPointMode tessellationPointMode, 827 GeometryOutputType geometryOutputType) 828 : TestCase (context, name, description) 829 , m_tessellationOutput (tessellationOutput) 830 , m_tessellationPointMode (tessellationPointMode) 831 , m_geometryOutputType (geometryOutputType) 832 , m_feedbackProgram (DE_NULL) 833 , m_nonFeedbackProgram (DE_NULL) 834 , m_patchBuffer (0) 835 , m_feedbackID (0) 836 , m_feedbackBuffer (0) 837 { 838 DE_ASSERT(tessellationOutput < TESSELLATION_OUT_LAST); 839 DE_ASSERT(tessellationPointMode < TESSELLATION_POINTMODE_LAST); 840 DE_ASSERT(geometryOutputType < GEOMETRY_OUTPUT_LAST); 841 } 842 843 FeedbackPrimitiveTypeCase::~FeedbackPrimitiveTypeCase (void) 844 { 845 deinit(); 846 } 847 848 void FeedbackPrimitiveTypeCase::init (void) 849 { 850 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 851 852 // Requirements 853 854 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") || 855 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) 856 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions"); 857 858 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || 859 m_context.getRenderTarget().getHeight() < RENDER_SIZE) 860 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target."); 861 862 // Log 863 864 m_testCtx.getLog() 865 << tcu::TestLog::Message 866 << "Testing " 867 << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) 868 << "->" 869 << getGeometryInputDescription(m_tessellationOutput, m_tessellationPointMode) 870 << " primitive conversion with and without transform feedback.\n" 871 << "Sending a patch of 4 vertices (2x2 uniform grid) to tessellation control shader.\n" 872 << "Control shader emits a patch of 9 vertices (3x3 uniform grid).\n" 873 << "Setting outer tessellation level = 3, inner = 3.\n" 874 << "Primitive generator emits " << getTessellationOutputDescription(m_tessellationOutput, m_tessellationPointMode) << "\n" 875 << "Geometry shader transforms emitted primitives to " << getGeometryOutputDescription(m_geometryOutputType) << "\n" 876 << "Reading back vertex positions of generated primitives using transform feedback.\n" 877 << "Verifying rendered image and feedback vertices are consistent.\n" 878 << "Rendering scene again with identical shader program, but without setting feedback varying. Expecting similar output image." 879 << tcu::TestLog::EndMessage; 880 881 // Resources 882 883 { 884 static const tcu::Vec4 patchBufferData[4] = 885 { 886 tcu::Vec4( -0.9f, -0.9f, 0.0f, 1.0f ), 887 tcu::Vec4( -0.9f, 0.9f, 0.0f, 1.0f ), 888 tcu::Vec4( 0.9f, -0.9f, 0.0f, 1.0f ), 889 tcu::Vec4( 0.9f, 0.9f, 0.0f, 1.0f ), 890 }; 891 892 gl.genBuffers(1, &m_patchBuffer); 893 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer); 894 gl.bufferData(GL_ARRAY_BUFFER, sizeof(patchBufferData), patchBufferData, GL_STATIC_DRAW); 895 GLU_EXPECT_NO_ERROR(gl.getError(), "gen buffer"); 896 } 897 898 m_feedbackProgram = new glu::ShaderProgram(m_context.getRenderContext(), 899 glu::ProgramSources() 900 << glu::VertexSource(getVertexSource()) 901 << glu::FragmentSource(getFragmentSource()) 902 << glu::TessellationControlSource(getTessellationControlSource()) 903 << glu::TessellationEvaluationSource(getTessellationEvaluationSource()) 904 << glu::GeometrySource(getGeometrySource()) 905 << glu::TransformFeedbackVarying("tf_someVertexPosition") 906 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS)); 907 m_testCtx.getLog() << *m_feedbackProgram; 908 if (!m_feedbackProgram->isOk()) 909 throw tcu::TestError("failed to build program"); 910 911 m_nonFeedbackProgram = new glu::ShaderProgram(m_context.getRenderContext(), 912 glu::ProgramSources() 913 << glu::VertexSource(getVertexSource()) 914 << glu::FragmentSource(getFragmentSource()) 915 << glu::TessellationControlSource(getTessellationControlSource()) 916 << glu::TessellationEvaluationSource(getTessellationEvaluationSource()) 917 << glu::GeometrySource(getGeometrySource())); 918 if (!m_nonFeedbackProgram->isOk()) 919 { 920 m_testCtx.getLog() << *m_nonFeedbackProgram; 921 throw tcu::TestError("failed to build program"); 922 } 923 924 genTransformFeedback(); 925 } 926 927 void FeedbackPrimitiveTypeCase::deinit (void) 928 { 929 if (m_patchBuffer) 930 { 931 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_patchBuffer); 932 m_patchBuffer = 0; 933 } 934 935 if (m_feedbackBuffer) 936 { 937 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_feedbackBuffer); 938 m_feedbackBuffer = 0; 939 } 940 941 if (m_feedbackID) 942 { 943 m_context.getRenderContext().getFunctions().deleteTransformFeedbacks(1, &m_feedbackID); 944 m_feedbackID = 0; 945 } 946 947 if (m_feedbackProgram) 948 { 949 delete m_feedbackProgram; 950 m_feedbackProgram = DE_NULL; 951 } 952 953 if (m_nonFeedbackProgram) 954 { 955 delete m_nonFeedbackProgram; 956 m_nonFeedbackProgram = DE_NULL; 957 } 958 } 959 960 FeedbackPrimitiveTypeCase::IterateResult FeedbackPrimitiveTypeCase::iterate (void) 961 { 962 tcu::Surface feedbackResult (RENDER_SIZE, RENDER_SIZE); 963 tcu::Surface nonFeedbackResult (RENDER_SIZE, RENDER_SIZE); 964 965 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 966 967 // render with and without XFB 968 renderWithFeedback(feedbackResult); 969 renderWithoutFeedback(nonFeedbackResult); 970 971 // compare 972 { 973 bool imageOk; 974 975 m_testCtx.getLog() << tcu::TestLog::Message << "Comparing the image rendered with no transform feedback against the image rendered with enabled transform feedback." << tcu::TestLog::EndMessage; 976 977 if (m_context.getRenderTarget().getNumSamples() > 1) 978 imageOk = tcu::fuzzyCompare(m_testCtx.getLog(), 979 "ImageCompare", 980 "Image comparison", 981 feedbackResult.getAccess(), 982 nonFeedbackResult.getAccess(), 983 0.03f, 984 tcu::COMPARE_LOG_RESULT); 985 else 986 imageOk = tcu::intThresholdPositionDeviationCompare(m_testCtx.getLog(), 987 "ImageCompare", 988 "Image comparison", 989 feedbackResult.getAccess(), 990 nonFeedbackResult.getAccess(), 991 tcu::UVec4(8, 8, 8, 255), //!< threshold 992 tcu::IVec3(1, 1, 0), //!< 3x3 search kernel 993 true, //!< fragments may end up over the viewport, just ignore them 994 tcu::COMPARE_LOG_RESULT); 995 996 if (!imageOk) 997 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image comparison failed"); 998 } 999 1000 return STOP; 1001 } 1002 1003 void FeedbackPrimitiveTypeCase::renderWithFeedback(tcu::Surface& dst) 1004 { 1005 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1006 const glu::VertexArray vao (m_context.getRenderContext()); 1007 const glu::Query primitivesGeneratedQuery (m_context.getRenderContext()); 1008 const int posLocation = gl.getAttribLocation(m_feedbackProgram->getProgram(), "a_position"); 1009 const glw::GLenum feedbackPrimitiveMode = getOutputPrimitiveGLType(); 1010 1011 if (posLocation == -1) 1012 throw tcu::TestError("a_position was -1"); 1013 1014 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering with transform feedback" << tcu::TestLog::EndMessage; 1015 1016 gl.viewport(0, 0, dst.getWidth(), dst.getHeight()); 1017 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 1018 gl.clear(GL_COLOR_BUFFER_BIT); 1019 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 1020 1021 gl.bindVertexArray(*vao); 1022 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer); 1023 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); 1024 gl.enableVertexAttribArray(posLocation); 1025 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs"); 1026 1027 gl.useProgram(m_feedbackProgram->getProgram()); 1028 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 1029 1030 gl.patchParameteri(GL_PATCH_VERTICES, 4); 1031 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 1032 1033 gl.beginQuery(GL_PRIMITIVES_GENERATED, *primitivesGeneratedQuery); 1034 GLU_EXPECT_NO_ERROR(gl.getError(), "begin GL_PRIMITIVES_GENERATED query"); 1035 1036 m_testCtx.getLog() << tcu::TestLog::Message << "Begin transform feedback with mode " << glu::getPrimitiveTypeStr(feedbackPrimitiveMode) << tcu::TestLog::EndMessage; 1037 1038 gl.beginTransformFeedback(feedbackPrimitiveMode); 1039 GLU_EXPECT_NO_ERROR(gl.getError(), "begin xfb"); 1040 1041 m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES" << tcu::TestLog::EndMessage; 1042 1043 gl.drawArrays(GL_PATCHES, 0, 4); 1044 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches"); 1045 1046 gl.endTransformFeedback(); 1047 GLU_EXPECT_NO_ERROR(gl.getError(), "end xfb"); 1048 1049 gl.endQuery(GL_PRIMITIVES_GENERATED); 1050 GLU_EXPECT_NO_ERROR(gl.getError(), "end GL_PRIMITIVES_GENERATED query"); 1051 1052 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); 1053 GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels"); 1054 1055 // verify GL_PRIMITIVES_GENERATED 1056 { 1057 glw::GLuint primitivesGeneratedResult = 0; 1058 gl.getQueryObjectuiv(*primitivesGeneratedQuery, GL_QUERY_RESULT, &primitivesGeneratedResult); 1059 GLU_EXPECT_NO_ERROR(gl.getError(), "get GL_PRIMITIVES_GENERATED value"); 1060 1061 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying GL_PRIMITIVES_GENERATED, expecting " << getNumGeneratedPrimitives() << tcu::TestLog::EndMessage; 1062 1063 if ((int)primitivesGeneratedResult != getNumGeneratedPrimitives()) 1064 { 1065 m_testCtx.getLog() << tcu::TestLog::Message << "Error, GL_PRIMITIVES_GENERATED was " << primitivesGeneratedResult << tcu::TestLog::EndMessage; 1066 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Got unexpected GL_PRIMITIVES_GENERATED"); 1067 } 1068 else 1069 m_testCtx.getLog() << tcu::TestLog::Message << "GL_PRIMITIVES_GENERATED valid." << tcu::TestLog::EndMessage; 1070 } 1071 1072 // feedback 1073 { 1074 std::vector<tcu::Vec4> feedbackResults (getNumGeneratedElementsPerPrimitive() * getNumGeneratedPrimitives()); 1075 const void* mappedPtr = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (glw::GLsizeiptr)(feedbackResults.size() * sizeof(tcu::Vec4)), GL_MAP_READ_BIT); 1076 glw::GLboolean unmapResult; 1077 1078 GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange"); 1079 1080 m_testCtx.getLog() << tcu::TestLog::Message << "Reading transform feedback buffer." << tcu::TestLog::EndMessage; 1081 if (!mappedPtr) 1082 throw tcu::TestError("mapBufferRange returned null"); 1083 1084 deMemcpy(feedbackResults[0].getPtr(), mappedPtr, (int)(feedbackResults.size() * sizeof(tcu::Vec4))); 1085 1086 unmapResult = gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER); 1087 GLU_EXPECT_NO_ERROR(gl.getError(), "unmapBuffer"); 1088 1089 if (unmapResult != GL_TRUE) 1090 throw tcu::TestError("unmapBuffer failed, did not return true"); 1091 1092 // verify transform results 1093 verifyFeedbackResults(feedbackResults); 1094 1095 // verify feedback results are consistent with rendered image 1096 verifyRenderedImage(dst, feedbackResults); 1097 } 1098 } 1099 1100 void FeedbackPrimitiveTypeCase::renderWithoutFeedback (tcu::Surface& dst) 1101 { 1102 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1103 const glu::VertexArray vao (m_context.getRenderContext()); 1104 const int posLocation = gl.getAttribLocation(m_nonFeedbackProgram->getProgram(), "a_position"); 1105 1106 if (posLocation == -1) 1107 throw tcu::TestError("a_position was -1"); 1108 1109 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering without transform feedback" << tcu::TestLog::EndMessage; 1110 1111 gl.viewport(0, 0, dst.getWidth(), dst.getHeight()); 1112 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 1113 gl.clear(GL_COLOR_BUFFER_BIT); 1114 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 1115 1116 gl.bindVertexArray(*vao); 1117 gl.bindBuffer(GL_ARRAY_BUFFER, m_patchBuffer); 1118 gl.vertexAttribPointer(posLocation, 4, GL_FLOAT, GL_FALSE, 0, DE_NULL); 1119 gl.enableVertexAttribArray(posLocation); 1120 GLU_EXPECT_NO_ERROR(gl.getError(), "setup attribs"); 1121 1122 gl.useProgram(m_nonFeedbackProgram->getProgram()); 1123 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 1124 1125 gl.patchParameteri(GL_PATCH_VERTICES, 4); 1126 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 1127 1128 m_testCtx.getLog() << tcu::TestLog::Message << "Calling drawArrays with mode GL_PATCHES" << tcu::TestLog::EndMessage; 1129 1130 gl.drawArrays(GL_PATCHES, 0, 4); 1131 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches"); 1132 1133 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); 1134 GLU_EXPECT_NO_ERROR(gl.getError(), "readPixels"); 1135 } 1136 1137 void FeedbackPrimitiveTypeCase::verifyFeedbackResults (const std::vector<tcu::Vec4>& feedbackResult) 1138 { 1139 const int geometryAmplification = getGeometryAmplification(); 1140 const int elementsPerPrimitive = getNumGeneratedElementsPerPrimitive(); 1141 const int errorFloodThreshold = 8; 1142 int readNdx = 0; 1143 int numErrors = 0; 1144 1145 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying feedback results." << tcu::TestLog::EndMessage; 1146 1147 for (int tessellatedPrimitiveNdx = 0; tessellatedPrimitiveNdx < getNumTessellatedPrimitives(); ++tessellatedPrimitiveNdx) 1148 { 1149 const tcu::Vec4 primitiveVertex = feedbackResult[readNdx]; 1150 1151 // check the generated vertices are in the proper range (range: -0.4 <-> 0.4) 1152 { 1153 const float equalThreshold = 1.0e-6f; 1154 const bool centroidOk = (primitiveVertex.x() >= -0.4f - equalThreshold) && 1155 (primitiveVertex.x() <= 0.4f + equalThreshold) && 1156 (primitiveVertex.y() >= -0.4f - equalThreshold) && 1157 (primitiveVertex.y() <= 0.4f + equalThreshold) && 1158 (de::abs(primitiveVertex.z()) < equalThreshold) && 1159 (de::abs(primitiveVertex.w() - 1.0f) < equalThreshold); 1160 1161 if (!centroidOk && numErrors++ < errorFloodThreshold) 1162 { 1163 m_testCtx.getLog() 1164 << tcu::TestLog::Message 1165 << "Element at index " << (readNdx) << " (tessellation invocation " << tessellatedPrimitiveNdx << ")\n" 1166 << "\texpected vertex in range: ( [-0.4, 0.4], [-0.4, 0.4], 0.0, 1.0 )\n" 1167 << "\tgot: " << primitiveVertex 1168 << tcu::TestLog::EndMessage; 1169 1170 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid feedback output"); 1171 1172 ++readNdx; 1173 continue; 1174 } 1175 } 1176 1177 // check all other primitives generated from this tessellated primitive have the same feedback value 1178 for (int generatedPrimitiveNdx = 0; generatedPrimitiveNdx < geometryAmplification; ++generatedPrimitiveNdx) 1179 for (int primitiveVertexNdx = 0; primitiveVertexNdx < elementsPerPrimitive; ++primitiveVertexNdx) 1180 { 1181 const tcu::Vec4 generatedElementVertex = feedbackResult[readNdx]; 1182 const tcu::Vec4 equalThreshold (1.0e-6f); 1183 1184 if (tcu::boolAny(tcu::greaterThan(tcu::abs(primitiveVertex - generatedElementVertex), equalThreshold))) 1185 { 1186 if (numErrors++ < errorFloodThreshold) 1187 { 1188 m_testCtx.getLog() 1189 << tcu::TestLog::Message 1190 << "Element at index " << (readNdx) << " (tessellation invocation " << tessellatedPrimitiveNdx << ", geometry primitive " << generatedPrimitiveNdx << ", emitted vertex " << primitiveVertexNdx << "):\n" 1191 << "\tfeedback result was not contant over whole primitive.\n" 1192 << "\tfirst emitted value: " << primitiveVertex << "\n" 1193 << "\tcurrent emitted value:" << generatedElementVertex << "\n" 1194 << tcu::TestLog::EndMessage; 1195 } 1196 1197 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got multiple different feedback values for a single primitive"); 1198 } 1199 1200 readNdx++; 1201 } 1202 } 1203 1204 if (numErrors > errorFloodThreshold) 1205 m_testCtx.getLog() << tcu::TestLog::Message << "Omitted " << (numErrors - errorFloodThreshold) << " error(s)." << tcu::TestLog::EndMessage; 1206 } 1207 1208 static bool feedbackResultCompare (const tcu::Vec4& a, const tcu::Vec4& b) 1209 { 1210 if (a.x() < b.x()) 1211 return true; 1212 if (a.x() > b.x()) 1213 return false; 1214 1215 return a.y() < b.y(); 1216 } 1217 1218 void FeedbackPrimitiveTypeCase::verifyRenderedImage (const tcu::Surface& image, const std::vector<tcu::Vec4>& tfVertices) 1219 { 1220 std::vector<tcu::Vec4> vertices; 1221 1222 m_testCtx.getLog() << tcu::TestLog::Message << "Comparing result image against feedback results." << tcu::TestLog::EndMessage; 1223 1224 // Check only unique vertices 1225 std::unique_copy(tfVertices.begin(), tfVertices.end(), std::back_insert_iterator<std::vector<tcu::Vec4> >(vertices)); 1226 std::sort(vertices.begin(), vertices.end(), feedbackResultCompare); 1227 vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end()); 1228 1229 // Verifying vertices recorded with feedback actually ended up on the result image 1230 for (int ndx = 0; ndx < (int)vertices.size(); ++ndx) 1231 { 1232 // Rasterization (of lines) may deviate by one pixel. In addition to that, allow minimal errors in rasterized position vs. feedback result. 1233 // This minimal error could result in a difference in rounding => allow one additional pixel in deviation 1234 1235 const int rasterDeviation = 2; 1236 const tcu::IVec2 rasterPos ((int)deFloatRound((vertices[ndx].x() * 0.5f + 0.5f) * image.getWidth()), (int)deFloatRound((vertices[ndx].y() * 0.5f + 0.5f) * image.getHeight())); 1237 1238 // Find produced rasterization results 1239 bool found = false; 1240 1241 for (int dy = -rasterDeviation; dy <= rasterDeviation && !found; ++dy) 1242 for (int dx = -rasterDeviation; dx <= rasterDeviation && !found; ++dx) 1243 { 1244 // Raster result could end up outside the viewport 1245 if (rasterPos.x() + dx < 0 || rasterPos.x() + dx >= image.getWidth() || 1246 rasterPos.y() + dy < 0 || rasterPos.y() + dy >= image.getHeight()) 1247 found = true; 1248 else 1249 { 1250 const tcu::RGBA result = image.getPixel(rasterPos.x() + dx, rasterPos.y() + dy); 1251 1252 if(!isBlack(result)) 1253 found = true; 1254 } 1255 } 1256 1257 if (!found) 1258 { 1259 m_testCtx.getLog() 1260 << tcu::TestLog::Message 1261 << "Vertex " << vertices[ndx] << "\n" 1262 << "\tCould not find rasterization output for vertex.\n" 1263 << "\tExpected non-black pixels near " << rasterPos 1264 << tcu::TestLog::EndMessage; 1265 1266 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "invalid result image"); 1267 } 1268 } 1269 } 1270 1271 void FeedbackPrimitiveTypeCase::genTransformFeedback (void) 1272 { 1273 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1274 const int elementsPerPrimitive = getNumGeneratedElementsPerPrimitive(); 1275 const int feedbackPrimitives = getNumGeneratedPrimitives(); 1276 const int feedbackElements = elementsPerPrimitive * feedbackPrimitives; 1277 const std::vector<tcu::Vec4> initialBuffer (feedbackElements, tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f)); 1278 1279 gl.genTransformFeedbacks(1, &m_feedbackID); 1280 gl.bindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_feedbackID); 1281 GLU_EXPECT_NO_ERROR(gl.getError(), "gen transform feedback"); 1282 1283 gl.genBuffers(1, &m_feedbackBuffer); 1284 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_feedbackBuffer); 1285 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(tcu::Vec4) * initialBuffer.size(), initialBuffer[0].getPtr(), GL_STATIC_COPY); 1286 GLU_EXPECT_NO_ERROR(gl.getError(), "gen feedback buffer"); 1287 1288 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_feedbackBuffer); 1289 GLU_EXPECT_NO_ERROR(gl.getError(), "bind feedback buffer"); 1290 } 1291 1292 static int getTriangleNumOutputPrimitives (int tessellationLevel) 1293 { 1294 if (tessellationLevel == 1) 1295 return 1; 1296 else if (tessellationLevel == 2) 1297 return 6; 1298 else 1299 return 3 * (2 + 2 * (tessellationLevel - 2)) + getTriangleNumOutputPrimitives(tessellationLevel - 2); 1300 } 1301 1302 static int getTriangleNumOutputPrimitivesPoints (int tessellationLevel) 1303 { 1304 if (tessellationLevel == 0) 1305 return 1; 1306 else if (tessellationLevel == 1) 1307 return 3; 1308 else 1309 return 3 + 3 * (tessellationLevel - 1) + getTriangleNumOutputPrimitivesPoints(tessellationLevel - 2); 1310 } 1311 1312 int FeedbackPrimitiveTypeCase::getNumGeneratedElementsPerPrimitive (void) const 1313 { 1314 if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES) 1315 return 3; 1316 else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) 1317 return 2; 1318 else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) 1319 return 1; 1320 else 1321 { 1322 DE_ASSERT(false); 1323 return -1; 1324 } 1325 } 1326 1327 int FeedbackPrimitiveTypeCase::getNumGeneratedPrimitives (void) const 1328 { 1329 return getNumTessellatedPrimitives() * getGeometryAmplification(); 1330 } 1331 1332 int FeedbackPrimitiveTypeCase::getNumTessellatedPrimitives (void) const 1333 { 1334 const int tessellationLevel = 3; 1335 1336 if (m_tessellationPointMode == TESSELLATION_POINTMODE_OFF) 1337 { 1338 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) 1339 return getTriangleNumOutputPrimitives(tessellationLevel); 1340 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS) 1341 return tessellationLevel * tessellationLevel * 2; // tessellated as triangles 1342 else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) 1343 return tessellationLevel * tessellationLevel; 1344 } 1345 else if (m_tessellationPointMode == TESSELLATION_POINTMODE_ON) 1346 { 1347 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) 1348 return getTriangleNumOutputPrimitivesPoints(tessellationLevel); 1349 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS) 1350 return (tessellationLevel + 1) * (tessellationLevel + 1); 1351 else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) 1352 return tessellationLevel * (tessellationLevel + 1); 1353 } 1354 1355 DE_ASSERT(false); 1356 return -1; 1357 } 1358 1359 int FeedbackPrimitiveTypeCase::getGeometryAmplification (void) const 1360 { 1361 const int outputAmplification = (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (2) : (1); 1362 const int numInputVertices = (m_tessellationPointMode) ? (1) : (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) : (3); 1363 1364 return outputAmplification * numInputVertices; 1365 } 1366 1367 glw::GLenum FeedbackPrimitiveTypeCase::getOutputPrimitiveGLType (void) const 1368 { 1369 if (m_geometryOutputType == GEOMETRY_OUTPUT_TRIANGLES) 1370 return GL_TRIANGLES; 1371 else if (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) 1372 return GL_LINES; 1373 else if (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) 1374 return GL_POINTS; 1375 else 1376 { 1377 DE_ASSERT(false); 1378 return -1; 1379 } 1380 } 1381 1382 const char* FeedbackPrimitiveTypeCase::getVertexSource (void) const 1383 { 1384 return s_positionVertexShader; 1385 } 1386 1387 const char* FeedbackPrimitiveTypeCase::getFragmentSource (void) const 1388 { 1389 return s_whiteOutputFragmentShader; 1390 } 1391 1392 std::string FeedbackPrimitiveTypeCase::getTessellationControlSource (void) const 1393 { 1394 std::ostringstream buf; 1395 1396 buf << "#version 310 es\n" 1397 "#extension GL_EXT_tessellation_shader : require\n" 1398 "layout(vertices = 9) out;\n" 1399 "\n" 1400 "uniform highp float u_innerTessellationLevel;\n" 1401 "uniform highp float u_outerTessellationLevel;\n" 1402 "\n" 1403 "void main (void)\n" 1404 "{\n" 1405 " if (gl_PatchVerticesIn != 4)\n" 1406 " return;\n" 1407 "\n" 1408 " // Convert input 2x2 grid to 3x3 grid\n" 1409 " float xweight = float(gl_InvocationID % 3) / 2.0f;\n" 1410 " float yweight = float(gl_InvocationID / 3) / 2.0f;\n" 1411 "\n" 1412 " vec4 y0 = mix(gl_in[0].gl_Position, gl_in[1].gl_Position, yweight);\n" 1413 " vec4 y1 = mix(gl_in[2].gl_Position, gl_in[3].gl_Position, yweight);\n" 1414 "\n" 1415 " gl_out[gl_InvocationID].gl_Position = mix(y0, y1, xweight);\n" 1416 "\n"; 1417 1418 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) 1419 buf << " gl_TessLevelOuter[0] = 3.0;\n" 1420 " gl_TessLevelOuter[1] = 3.0;\n" 1421 " gl_TessLevelOuter[2] = 3.0;\n" 1422 " gl_TessLevelInner[0] = 3.0;\n"; 1423 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS) 1424 buf << " gl_TessLevelOuter[0] = 3.0;\n" 1425 " gl_TessLevelOuter[1] = 3.0;\n" 1426 " gl_TessLevelOuter[2] = 3.0;\n" 1427 " gl_TessLevelOuter[3] = 3.0;\n" 1428 " gl_TessLevelInner[0] = 3.0;\n" 1429 " gl_TessLevelInner[1] = 3.0;\n"; 1430 else if (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) 1431 buf << " gl_TessLevelOuter[0] = 3.0;\n" 1432 " gl_TessLevelOuter[1] = 3.0;\n"; 1433 else 1434 DE_ASSERT(false); 1435 1436 buf << "}\n"; 1437 1438 return buf.str(); 1439 } 1440 1441 std::string FeedbackPrimitiveTypeCase::getTessellationEvaluationSource (void) const 1442 { 1443 std::ostringstream buf; 1444 1445 buf << "#version 310 es\n" 1446 "#extension GL_EXT_tessellation_shader : require\n" 1447 "layout(" 1448 << ((m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) ? ("triangles") : (m_tessellationOutput == TESSELLATION_OUT_QUADS) ? ("quads") : ("isolines")) 1449 << ((m_tessellationPointMode) ? (", point_mode") : ("")) 1450 << ") in;\n" 1451 "\n" 1452 "out highp vec4 v_tessellationCoords;\n" 1453 "\n" 1454 "// note: No need to use precise gl_Position since we do not require gapless geometry\n" 1455 "void main (void)\n" 1456 "{\n" 1457 " if (gl_PatchVerticesIn != 9)\n" 1458 " return;\n" 1459 "\n" 1460 " vec4 patchCentroid = vec4(0.0);\n" 1461 " for (int ndx = 0; ndx < gl_PatchVerticesIn; ++ndx)\n" 1462 " patchCentroid += gl_in[ndx].gl_Position;\n" 1463 " patchCentroid /= patchCentroid.w;\n" 1464 "\n"; 1465 1466 if (m_tessellationOutput == TESSELLATION_OUT_TRIANGLES) 1467 buf << " // map barycentric coords to 2d coords\n" 1468 " const vec3 tessDirX = vec3( 0.4, 0.4, 0.0);\n" 1469 " const vec3 tessDirY = vec3( 0.0, -0.4, 0.0);\n" 1470 " const vec3 tessDirZ = vec3(-0.4, 0.4, 0.0);\n" 1471 " gl_Position = patchCentroid + vec4(gl_TessCoord.x * tessDirX + gl_TessCoord.y * tessDirY + gl_TessCoord.z * tessDirZ, 0.0);\n"; 1472 else if (m_tessellationOutput == TESSELLATION_OUT_QUADS || m_tessellationOutput == TESSELLATION_OUT_ISOLINES) 1473 buf << " gl_Position = patchCentroid + vec4(gl_TessCoord.x * 0.8 - 0.4, gl_TessCoord.y * 0.8 - 0.4, 0.0, 0.0);\n"; 1474 else 1475 DE_ASSERT(false); 1476 1477 buf << " v_tessellationCoords = vec4(gl_TessCoord, 0.0);\n" 1478 "}\n"; 1479 1480 return buf.str(); 1481 } 1482 1483 std::string FeedbackPrimitiveTypeCase::getGeometrySource (void) const 1484 { 1485 const char* const geometryInputPrimitive = (m_tessellationPointMode) ? ("points") : (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? ("lines") : ("triangles"); 1486 const char* const geometryOutputPrimitive = (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? ("points") : (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? ("line_strip") : ("triangle_strip"); 1487 const int numInputVertices = (m_tessellationPointMode) ? (1) : (m_tessellationOutput == TESSELLATION_OUT_ISOLINES) ? (2) : (3); 1488 const int numSingleVertexOutputVertices = (m_geometryOutputType == GEOMETRY_OUTPUT_POINTS) ? (1) : (m_geometryOutputType == GEOMETRY_OUTPUT_LINES) ? (4) : (3); 1489 const int numEmitVertices = numInputVertices * numSingleVertexOutputVertices; 1490 std::ostringstream buf; 1491 1492 buf << "#version 310 es\n" 1493 "#extension GL_EXT_geometry_shader : require\n" 1494 "layout(" << geometryInputPrimitive << ") in;\n" 1495 "layout(" << geometryOutputPrimitive << ", max_vertices=" << numEmitVertices <<") out;\n" 1496 "\n" 1497 "in highp vec4 v_tessellationCoords[];\n" 1498 "out highp vec4 tf_someVertexPosition;\n" 1499 "\n" 1500 "void main (void)\n" 1501 "{\n" 1502 " // Emit primitive\n" 1503 " for (int ndx = 0; ndx < gl_in.length(); ++ndx)\n" 1504 " {\n"; 1505 1506 switch (m_geometryOutputType) 1507 { 1508 case GEOMETRY_OUTPUT_POINTS: 1509 buf << " // Draw point on vertex\n" 1510 " gl_Position = gl_in[ndx].gl_Position;\n" 1511 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1512 " EmitVertex();\n"; 1513 break; 1514 1515 case GEOMETRY_OUTPUT_LINES: 1516 buf << " // Draw cross on vertex\n" 1517 " gl_Position = gl_in[ndx].gl_Position + vec4(-0.02, -0.02, 0.0, 0.0);\n" 1518 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1519 " EmitVertex();\n" 1520 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, 0.02, 0.0, 0.0);\n" 1521 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1522 " EmitVertex();\n" 1523 " EndPrimitive();\n" 1524 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, -0.02, 0.0, 0.0);\n" 1525 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1526 " EmitVertex();\n" 1527 " gl_Position = gl_in[ndx].gl_Position + vec4(-0.02, 0.02, 0.0, 0.0);\n" 1528 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1529 " EmitVertex();\n" 1530 " EndPrimitive();\n"; 1531 break; 1532 1533 case GEOMETRY_OUTPUT_TRIANGLES: 1534 buf << " // Draw triangle on vertex\n" 1535 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.00, -0.02, 0.0, 0.0);\n" 1536 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1537 " EmitVertex();\n" 1538 " gl_Position = gl_in[ndx].gl_Position + vec4( 0.02, 0.00, 0.0, 0.0);\n" 1539 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1540 " EmitVertex();\n" 1541 " gl_Position = gl_in[ndx].gl_Position + vec4( -0.02, 0.00, 0.0, 0.0);\n" 1542 " tf_someVertexPosition = gl_in[gl_in.length() - 1].gl_Position;\n" 1543 " EmitVertex();\n" 1544 " EndPrimitive();\n"; 1545 break; 1546 1547 default: 1548 DE_ASSERT(false); 1549 return ""; 1550 } 1551 1552 buf << " }\n" 1553 "}\n"; 1554 1555 return buf.str(); 1556 } 1557 1558 const char* FeedbackPrimitiveTypeCase::getTessellationOutputDescription (TessellationOutputType tessellationOutput, TessellationPointMode pointMode) 1559 { 1560 switch (tessellationOutput) 1561 { 1562 case TESSELLATION_OUT_TRIANGLES: return (pointMode) ? ("points (triangles in point mode)") : ("triangles"); 1563 case TESSELLATION_OUT_QUADS: return (pointMode) ? ("points (quads in point mode)") : ("quads"); 1564 case TESSELLATION_OUT_ISOLINES: return (pointMode) ? ("points (isolines in point mode)") : ("isolines"); 1565 default: 1566 DE_ASSERT(false); 1567 return DE_NULL; 1568 } 1569 } 1570 1571 const char* FeedbackPrimitiveTypeCase::getGeometryInputDescription (TessellationOutputType tessellationOutput, TessellationPointMode pointMode) 1572 { 1573 switch (tessellationOutput) 1574 { 1575 case TESSELLATION_OUT_TRIANGLES: return (pointMode) ? ("points") : ("triangles"); 1576 case TESSELLATION_OUT_QUADS: return (pointMode) ? ("points") : ("triangles"); 1577 case TESSELLATION_OUT_ISOLINES: return (pointMode) ? ("points") : ("lines"); 1578 default: 1579 DE_ASSERT(false); 1580 return DE_NULL; 1581 } 1582 } 1583 1584 const char* FeedbackPrimitiveTypeCase::getGeometryOutputDescription (GeometryOutputType geometryOutput) 1585 { 1586 switch (geometryOutput) 1587 { 1588 case GEOMETRY_OUTPUT_POINTS: return "points"; 1589 case GEOMETRY_OUTPUT_LINES: return "lines"; 1590 case GEOMETRY_OUTPUT_TRIANGLES: return "triangles"; 1591 default: 1592 DE_ASSERT(false); 1593 return DE_NULL; 1594 } 1595 } 1596 1597 class PointSizeCase : public TestCase 1598 { 1599 public: 1600 enum Flags 1601 { 1602 FLAG_VERTEX_SET = 0x01, // !< set gl_PointSize in vertex shader 1603 FLAG_TESSELLATION_CONTROL_SET = 0x02, // !< set gl_PointSize in tessellation evaluation shader 1604 FLAG_TESSELLATION_EVALUATION_SET = 0x04, // !< set gl_PointSize in tessellation control shader 1605 FLAG_TESSELLATION_ADD = 0x08, // !< read and add to gl_PointSize in tessellation shader pair 1606 FLAG_TESSELLATION_DONT_SET = 0x10, // !< don't set gl_PointSize in tessellation shader 1607 FLAG_GEOMETRY_SET = 0x20, // !< set gl_PointSize in geometry shader 1608 FLAG_GEOMETRY_ADD = 0x40, // !< read and add to gl_PointSize in geometry shader 1609 FLAG_GEOMETRY_DONT_SET = 0x80, // !< don't set gl_PointSize in geometry shader 1610 }; 1611 1612 PointSizeCase (Context& context, const char* name, const char* description, int flags); 1613 ~PointSizeCase (void); 1614 1615 static std::string genTestCaseName (int flags); 1616 static std::string genTestCaseDescription (int flags); 1617 1618 private: 1619 void init (void); 1620 void deinit (void); 1621 IterateResult iterate (void); 1622 1623 void checkExtensions (void) const; 1624 void checkPointSizeRequirements (void) const; 1625 1626 void renderTo (tcu::Surface& dst); 1627 bool verifyImage (const tcu::Surface& src); 1628 int getExpectedPointSize (void) const; 1629 1630 std::string genVertexSource (void) const; 1631 const char* genFragmentSource (void) const; 1632 std::string genTessellationControlSource (void) const; 1633 std::string genTessellationEvaluationSource (void) const; 1634 std::string genGeometrySource (void) const; 1635 1636 enum 1637 { 1638 RENDER_SIZE = 32, 1639 }; 1640 1641 const int m_flags; 1642 glu::ShaderProgram* m_program; 1643 }; 1644 1645 PointSizeCase::PointSizeCase (Context& context, const char* name, const char* description, int flags) 1646 : TestCase (context, name, description) 1647 , m_flags (flags) 1648 , m_program (DE_NULL) 1649 { 1650 } 1651 1652 PointSizeCase::~PointSizeCase (void) 1653 { 1654 deinit(); 1655 } 1656 1657 std::string PointSizeCase::genTestCaseName (int flags) 1658 { 1659 std::ostringstream buf; 1660 1661 // join per-bit descriptions into a single string with '_' separator 1662 if (flags & FLAG_VERTEX_SET) buf << "vertex_set"; 1663 if (flags & FLAG_TESSELLATION_CONTROL_SET) buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET-1)) ? ("_") : ("")) << "control_set"; 1664 if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? ("_") : ("")) << "evaluation_set"; 1665 if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? ("_") : ("")) << "control_pass_eval_add"; 1666 if (flags & FLAG_TESSELLATION_DONT_SET) buf << ((flags & (FLAG_TESSELLATION_DONT_SET-1)) ? ("_") : ("")) << "eval_default"; 1667 if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? ("_") : ("")) << "geometry_set"; 1668 if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? ("_") : ("")) << "geometry_add"; 1669 if (flags & FLAG_GEOMETRY_DONT_SET) buf << ((flags & (FLAG_GEOMETRY_DONT_SET-1)) ? ("_") : ("")) << "geometry_default"; 1670 1671 return buf.str(); 1672 } 1673 1674 std::string PointSizeCase::genTestCaseDescription (int flags) 1675 { 1676 std::ostringstream buf; 1677 1678 // join per-bit descriptions into a single string with ", " separator 1679 if (flags & FLAG_VERTEX_SET) buf << "set point size in vertex shader"; 1680 if (flags & FLAG_TESSELLATION_CONTROL_SET) buf << ((flags & (FLAG_TESSELLATION_CONTROL_SET-1)) ? (", ") : ("")) << "set point size in tessellation control shader"; 1681 if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? (", ") : ("")) << "set point size in tessellation evaluation shader"; 1682 if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? (", ") : ("")) << "add to point size in tessellation shader"; 1683 if (flags & FLAG_TESSELLATION_DONT_SET) buf << ((flags & (FLAG_TESSELLATION_DONT_SET-1)) ? (", ") : ("")) << "don't set point size in tessellation evaluation shader"; 1684 if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? (", ") : ("")) << "set point size in geometry shader"; 1685 if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? (", ") : ("")) << "add to point size in geometry shader"; 1686 if (flags & FLAG_GEOMETRY_DONT_SET) buf << ((flags & (FLAG_GEOMETRY_DONT_SET-1)) ? (", ") : ("")) << "don't set point size in geometry shader"; 1687 1688 return buf.str(); 1689 } 1690 1691 void PointSizeCase::init (void) 1692 { 1693 checkExtensions(); 1694 checkPointSizeRequirements(); 1695 1696 // log 1697 1698 if (m_flags & FLAG_VERTEX_SET) 1699 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage; 1700 if (m_flags & FLAG_TESSELLATION_CONTROL_SET) 1701 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in tessellation control shader to 4.0. (And ignoring it in evaluation)." << tcu::TestLog::EndMessage; 1702 if (m_flags & FLAG_TESSELLATION_EVALUATION_SET) 1703 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0." << tcu::TestLog::EndMessage; 1704 if (m_flags & FLAG_TESSELLATION_ADD) 1705 m_testCtx.getLog() << tcu::TestLog::Message << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation." << tcu::TestLog::EndMessage; 1706 if (m_flags & FLAG_TESSELLATION_DONT_SET) 1707 m_testCtx.getLog() << tcu::TestLog::Message << "Not setting point size in tessellation evaluation shader (resulting in the default point size)." << tcu::TestLog::EndMessage; 1708 if (m_flags & FLAG_GEOMETRY_SET) 1709 m_testCtx.getLog() << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage; 1710 if (m_flags & FLAG_GEOMETRY_ADD) 1711 m_testCtx.getLog() << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0." << tcu::TestLog::EndMessage; 1712 if (m_flags & FLAG_GEOMETRY_DONT_SET) 1713 m_testCtx.getLog() << tcu::TestLog::Message << "Not setting point size in geometry shader (resulting in the default point size)." << tcu::TestLog::EndMessage; 1714 1715 // program 1716 1717 { 1718 glu::ProgramSources sources; 1719 sources << glu::VertexSource(genVertexSource()) 1720 << glu::FragmentSource(genFragmentSource()); 1721 1722 if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) 1723 sources << glu::TessellationControlSource(genTessellationControlSource()) 1724 << glu::TessellationEvaluationSource(genTessellationEvaluationSource()); 1725 1726 if (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET)) 1727 sources << glu::GeometrySource(genGeometrySource()); 1728 1729 m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources); 1730 1731 m_testCtx.getLog() << *m_program; 1732 if (!m_program->isOk()) 1733 throw tcu::TestError("failed to build program"); 1734 } 1735 } 1736 1737 void PointSizeCase::deinit (void) 1738 { 1739 delete m_program; 1740 m_program = DE_NULL; 1741 } 1742 1743 PointSizeCase::IterateResult PointSizeCase::iterate (void) 1744 { 1745 tcu::Surface resultImage(RENDER_SIZE, RENDER_SIZE); 1746 1747 renderTo(resultImage); 1748 1749 if (verifyImage(resultImage)) 1750 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 1751 else 1752 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 1753 1754 return STOP; 1755 } 1756 1757 void PointSizeCase::checkExtensions (void) const 1758 { 1759 std::vector<std::string> requiredExtensions; 1760 bool allOk = true; 1761 1762 if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) 1763 requiredExtensions.push_back("GL_EXT_tessellation_shader"); 1764 1765 if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD)) 1766 requiredExtensions.push_back("GL_EXT_tessellation_point_size"); 1767 1768 if (m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD | FLAG_GEOMETRY_DONT_SET))) 1769 requiredExtensions.push_back("GL_EXT_geometry_shader"); 1770 1771 if (m_flags & (m_flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD))) 1772 requiredExtensions.push_back("GL_EXT_geometry_point_size"); 1773 1774 for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx) 1775 if (!m_context.getContextInfo().isExtensionSupported(requiredExtensions[ndx].c_str())) 1776 allOk = false; 1777 1778 if (!allOk) 1779 { 1780 std::ostringstream extensionList; 1781 1782 for (int ndx = 0; ndx < (int)requiredExtensions.size(); ++ndx) 1783 { 1784 if (ndx != 0) 1785 extensionList << ", "; 1786 extensionList << requiredExtensions[ndx]; 1787 } 1788 1789 throw tcu::NotSupportedError("Test requires {" + extensionList.str() + "} extension(s)"); 1790 } 1791 } 1792 1793 void PointSizeCase::checkPointSizeRequirements (void) const 1794 { 1795 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1796 float aliasedSizeRange[2] = { 0.0f, 0.0f }; 1797 const int requiredSize = getExpectedPointSize(); 1798 1799 gl.getFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedSizeRange); 1800 1801 if (float(requiredSize) > aliasedSizeRange[1]) 1802 throw tcu::NotSupportedError("Test requires point size " + de::toString(requiredSize)); 1803 } 1804 1805 void PointSizeCase::renderTo (tcu::Surface& dst) 1806 { 1807 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1808 const bool tessellationActive = (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD | FLAG_TESSELLATION_DONT_SET)) != 0; 1809 const int positionLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 1810 const glu::VertexArray vao (m_context.getRenderContext()); 1811 1812 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering single point." << tcu::TestLog::EndMessage; 1813 1814 if (positionLocation == -1) 1815 throw tcu::TestError("Attribute a_position location was -1"); 1816 1817 gl.viewport(0, 0, RENDER_SIZE, RENDER_SIZE); 1818 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 1819 gl.clear(GL_COLOR_BUFFER_BIT); 1820 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 1821 1822 gl.bindVertexArray(*vao); 1823 GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao"); 1824 1825 gl.useProgram(m_program->getProgram()); 1826 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 1827 1828 gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f); 1829 1830 if (tessellationActive) 1831 { 1832 gl.patchParameteri(GL_PATCH_VERTICES, 1); 1833 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 1834 1835 gl.drawArrays(GL_PATCHES, 0, 1); 1836 GLU_EXPECT_NO_ERROR(gl.getError(), "draw patches"); 1837 } 1838 else 1839 { 1840 gl.drawArrays(GL_POINTS, 0, 1); 1841 GLU_EXPECT_NO_ERROR(gl.getError(), "draw points"); 1842 } 1843 1844 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.getAccess()); 1845 } 1846 1847 bool PointSizeCase::verifyImage (const tcu::Surface& src) 1848 { 1849 const bool MSAATarget = (m_context.getRenderTarget().getNumSamples() > 1); 1850 const int expectedSize = getExpectedPointSize(); 1851 1852 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels." << tcu::TestLog::EndMessage; 1853 m_testCtx.getLog() << tcu::TestLog::Image("RenderImage", "Rendered image", src.getAccess()); 1854 1855 { 1856 bool resultAreaFound = false; 1857 tcu::IVec4 resultArea; 1858 1859 // Find rasterization output area 1860 1861 for (int y = 0; y < src.getHeight(); ++y) 1862 for (int x = 0; x < src.getWidth(); ++x) 1863 { 1864 if (!isBlack(src.getPixel(x, y))) 1865 { 1866 if (!resultAreaFound) 1867 { 1868 // first fragment 1869 resultArea = tcu::IVec4(x, y, x + 1, y + 1); 1870 resultAreaFound = true; 1871 } 1872 else 1873 { 1874 // union area 1875 resultArea.x() = de::min(resultArea.x(), x); 1876 resultArea.y() = de::min(resultArea.y(), y); 1877 resultArea.z() = de::max(resultArea.z(), x+1); 1878 resultArea.w() = de::max(resultArea.w(), y+1); 1879 } 1880 } 1881 } 1882 1883 if (!resultAreaFound) 1884 { 1885 m_testCtx.getLog() << tcu::TestLog::Message << "Verification failed, could not find any point fragments." << tcu::TestLog::EndMessage; 1886 return false; 1887 } 1888 1889 // verify area size 1890 if (MSAATarget) 1891 { 1892 const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1); 1893 1894 // MSAA: edges may be a little fuzzy 1895 if (de::abs(pointSize.x() - pointSize.y()) > 1) 1896 { 1897 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Detected point size was " << pointSize << tcu::TestLog::EndMessage; 1898 return false; 1899 } 1900 1901 // MSAA may produce larger areas, allow one pixel larger 1902 if (expectedSize != de::max(pointSize.x(), pointSize.y()) && (expectedSize+1) != de::max(pointSize.x(), pointSize.y())) 1903 { 1904 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << de::max(pointSize.x(), pointSize.y()) << tcu::TestLog::EndMessage; 1905 return false; 1906 } 1907 } 1908 else 1909 { 1910 const tcu::IVec2 pointSize = resultArea.swizzle(2,3) - resultArea.swizzle(0, 1); 1911 1912 if (pointSize.x() != pointSize.y()) 1913 { 1914 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize << tcu::TestLog::EndMessage; 1915 return false; 1916 } 1917 1918 if (pointSize.x() != expectedSize) 1919 { 1920 m_testCtx.getLog() << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got " << pointSize.x() << tcu::TestLog::EndMessage; 1921 return false; 1922 } 1923 } 1924 } 1925 1926 return true; 1927 } 1928 1929 int PointSizeCase::getExpectedPointSize (void) const 1930 { 1931 int addition = 0; 1932 1933 // geometry 1934 if (m_flags & FLAG_GEOMETRY_DONT_SET) 1935 return 1; 1936 else if (m_flags & FLAG_GEOMETRY_SET) 1937 return 6; 1938 else if (m_flags & FLAG_GEOMETRY_ADD) 1939 addition += 2; 1940 1941 // tessellation 1942 if (m_flags & FLAG_TESSELLATION_EVALUATION_SET) 1943 return 4 + addition; 1944 else if (m_flags & FLAG_TESSELLATION_ADD) 1945 addition += 2; 1946 else if (m_flags & (FLAG_TESSELLATION_CONTROL_SET | FLAG_TESSELLATION_DONT_SET)) 1947 { 1948 DE_ASSERT((m_flags & FLAG_GEOMETRY_ADD) == 0); // reading pointSize undefined 1949 return 1; 1950 } 1951 1952 // vertex 1953 if (m_flags & FLAG_VERTEX_SET) 1954 return 2 + addition; 1955 1956 // undefined 1957 DE_ASSERT(false); 1958 return -1; 1959 } 1960 1961 std::string PointSizeCase::genVertexSource (void) const 1962 { 1963 std::ostringstream buf; 1964 1965 buf << "#version 310 es\n" 1966 << "in highp vec4 a_position;\n" 1967 << "void main ()\n" 1968 << "{\n" 1969 << " gl_Position = a_position;\n"; 1970 1971 if (m_flags & FLAG_VERTEX_SET) 1972 buf << " gl_PointSize = 2.0;\n"; 1973 1974 buf << "}\n"; 1975 1976 return buf.str(); 1977 } 1978 1979 const char* PointSizeCase::genFragmentSource (void) const 1980 { 1981 return s_whiteOutputFragmentShader; 1982 } 1983 1984 std::string PointSizeCase::genTessellationControlSource (void) const 1985 { 1986 std::ostringstream buf; 1987 1988 buf << "#version 310 es\n" 1989 << "#extension GL_EXT_tessellation_shader : require\n" 1990 << ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("#extension GL_EXT_tessellation_point_size : require\n")) 1991 << "layout(vertices = 1) out;\n" 1992 << "void main ()\n" 1993 << "{\n" 1994 << " gl_TessLevelOuter[0] = 3.0;\n" 1995 << " gl_TessLevelOuter[1] = 3.0;\n" 1996 << " gl_TessLevelOuter[2] = 3.0;\n" 1997 << " gl_TessLevelInner[0] = 3.0;\n" 1998 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"; 1999 2000 if (m_flags & FLAG_TESSELLATION_ADD) 2001 buf << " // pass as is to eval\n" 2002 << " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n"; 2003 else if (m_flags & FLAG_TESSELLATION_CONTROL_SET) 2004 buf << " // thrown away\n" 2005 << " gl_out[gl_InvocationID].gl_PointSize = 4.0;\n"; 2006 2007 buf << "}\n"; 2008 2009 return buf.str(); 2010 } 2011 2012 std::string PointSizeCase::genTessellationEvaluationSource (void) const 2013 { 2014 std::ostringstream buf; 2015 2016 buf << "#version 310 es\n" 2017 << "#extension GL_EXT_tessellation_shader : require\n" 2018 << ((m_flags & FLAG_TESSELLATION_DONT_SET) ? ("") : ("#extension GL_EXT_tessellation_point_size : require\n")) 2019 << "layout(triangles, point_mode) in;\n" 2020 << "void main ()\n" 2021 << "{\n" 2022 << " // hide all but one vertex\n" 2023 << " if (gl_TessCoord.x < 0.99)\n" 2024 << " gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n" 2025 << " else\n" 2026 << " gl_Position = gl_in[0].gl_Position;\n"; 2027 2028 if (m_flags & FLAG_TESSELLATION_ADD) 2029 buf << "\n" 2030 << " // add to point size\n" 2031 << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n"; 2032 else if (m_flags & FLAG_TESSELLATION_EVALUATION_SET) 2033 buf << "\n" 2034 << " // set point size\n" 2035 << " gl_PointSize = 4.0;\n"; 2036 2037 buf << "}\n"; 2038 2039 return buf.str(); 2040 } 2041 2042 std::string PointSizeCase::genGeometrySource (void) const 2043 { 2044 std::ostringstream buf; 2045 2046 buf << "#version 310 es\n" 2047 << "#extension GL_EXT_geometry_shader : require\n" 2048 << ((m_flags & FLAG_GEOMETRY_DONT_SET) ? ("") : ("#extension GL_EXT_geometry_point_size : require\n")) 2049 << "layout (points) in;\n" 2050 << "layout (points, max_vertices=1) out;\n" 2051 << "\n" 2052 << "void main ()\n" 2053 << "{\n"; 2054 2055 if (m_flags & FLAG_GEOMETRY_SET) 2056 buf << " gl_Position = gl_in[0].gl_Position;\n" 2057 << " gl_PointSize = 6.0;\n"; 2058 else if (m_flags & FLAG_GEOMETRY_ADD) 2059 buf << " gl_Position = gl_in[0].gl_Position;\n" 2060 << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n"; 2061 else if (m_flags & FLAG_GEOMETRY_DONT_SET) 2062 buf << " gl_Position = gl_in[0].gl_Position;\n"; 2063 2064 buf << " EmitVertex();\n" 2065 << "}\n"; 2066 2067 return buf.str(); 2068 } 2069 2070 class AllowedRenderFailureException : public std::runtime_error 2071 { 2072 public: 2073 AllowedRenderFailureException (const char* message) : std::runtime_error(message) { } 2074 }; 2075 2076 class GridRenderCase : public TestCase 2077 { 2078 public: 2079 enum Flags 2080 { 2081 FLAG_TESSELLATION_MAX_SPEC = 0x0001, 2082 FLAG_TESSELLATION_MAX_IMPLEMENTATION = 0x0002, 2083 FLAG_GEOMETRY_MAX_SPEC = 0x0004, 2084 FLAG_GEOMETRY_MAX_IMPLEMENTATION = 0x0008, 2085 FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC = 0x0010, 2086 FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION = 0x0020, 2087 2088 FLAG_GEOMETRY_SCATTER_INSTANCES = 0x0040, 2089 FLAG_GEOMETRY_SCATTER_PRIMITIVES = 0x0080, 2090 FLAG_GEOMETRY_SEPARATE_PRIMITIVES = 0x0100, //!< if set, geometry shader outputs separate grid cells and not continuous slices 2091 FLAG_GEOMETRY_SCATTER_LAYERS = 0x0200, 2092 2093 FLAG_ALLOW_OUT_OF_MEMORY = 0x0400, //!< allow draw command to set GL_OUT_OF_MEMORY 2094 }; 2095 2096 GridRenderCase (Context& context, const char* name, const char* description, int flags); 2097 ~GridRenderCase (void); 2098 2099 private: 2100 void init (void); 2101 void deinit (void); 2102 IterateResult iterate (void); 2103 2104 void renderTo (std::vector<tcu::Surface>& dst); 2105 bool verifyResultLayer (int layerNdx, const tcu::Surface& dst); 2106 2107 const char* getVertexSource (void); 2108 const char* getFragmentSource (void); 2109 std::string getTessellationControlSource (int tessLevel); 2110 std::string getTessellationEvaluationSource (int tessLevel); 2111 std::string getGeometryShaderSource (int numPrimitives, int numInstances, int tessLevel); 2112 2113 enum 2114 { 2115 RENDER_SIZE = 256 2116 }; 2117 2118 const int m_flags; 2119 2120 glu::ShaderProgram* m_program; 2121 deUint32 m_texture; 2122 int m_numLayers; 2123 }; 2124 2125 GridRenderCase::GridRenderCase (Context& context, const char* name, const char* description, int flags) 2126 : TestCase (context, name, description) 2127 , m_flags (flags) 2128 , m_program (DE_NULL) 2129 , m_texture (0) 2130 , m_numLayers (1) 2131 { 2132 DE_ASSERT(((m_flags & FLAG_TESSELLATION_MAX_SPEC) == 0) || ((m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) == 0)); 2133 DE_ASSERT(((m_flags & FLAG_GEOMETRY_MAX_SPEC) == 0) || ((m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) == 0)); 2134 DE_ASSERT(((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) == 0) || ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) == 0)); 2135 DE_ASSERT(((m_flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) == ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0)); 2136 } 2137 2138 GridRenderCase::~GridRenderCase (void) 2139 { 2140 deinit(); 2141 } 2142 2143 void GridRenderCase::init (void) 2144 { 2145 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2146 2147 // Requirements 2148 2149 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") || 2150 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) 2151 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions"); 2152 2153 if ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) == 0) 2154 { 2155 if (m_context.getRenderTarget().getWidth() < RENDER_SIZE || 2156 m_context.getRenderTarget().getHeight() < RENDER_SIZE) 2157 throw tcu::NotSupportedError("Test requires " + de::toString<int>(RENDER_SIZE) + "x" + de::toString<int>(RENDER_SIZE) + " or larger render target."); 2158 } 2159 2160 // Log 2161 2162 m_testCtx.getLog() 2163 << tcu::TestLog::Message 2164 << "Testing tessellation and geometry shaders that output a large number of primitives.\n" 2165 << getDescription() 2166 << tcu::TestLog::EndMessage; 2167 2168 // Render target 2169 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) 2170 { 2171 // set limits 2172 m_numLayers = 8; 2173 2174 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers << tcu::TestLog::EndMessage; 2175 2176 gl.genTextures(1, &m_texture); 2177 gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_texture); 2178 gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, RENDER_SIZE, RENDER_SIZE, m_numLayers); 2179 2180 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 2181 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 2182 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 2183 gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 2184 2185 GLU_EXPECT_NO_ERROR(gl.getError(), "gen texture"); 2186 } 2187 2188 // Gen program 2189 { 2190 glu::ProgramSources sources; 2191 int tessGenLevel = -1; 2192 2193 sources << glu::VertexSource(getVertexSource()) 2194 << glu::FragmentSource(getFragmentSource()); 2195 2196 // Tessellation limits 2197 { 2198 if (m_flags & FLAG_TESSELLATION_MAX_IMPLEMENTATION) 2199 { 2200 gl.getIntegerv(GL_MAX_TESS_GEN_LEVEL, &tessGenLevel); 2201 GLU_EXPECT_NO_ERROR(gl.getError(), "query tessellation limits"); 2202 } 2203 else if (m_flags & FLAG_TESSELLATION_MAX_SPEC) 2204 { 2205 tessGenLevel = 64; 2206 } 2207 else 2208 { 2209 tessGenLevel = 5; 2210 } 2211 2212 m_testCtx.getLog() 2213 << tcu::TestLog::Message 2214 << "Tessellation level: " << tessGenLevel << ", mode = quad.\n" 2215 << "\tEach input patch produces " << (tessGenLevel*tessGenLevel) << " (" << (tessGenLevel*tessGenLevel*2) << " triangles)\n" 2216 << tcu::TestLog::EndMessage; 2217 2218 sources << glu::TessellationControlSource(getTessellationControlSource(tessGenLevel)) 2219 << glu::TessellationEvaluationSource(getTessellationEvaluationSource(tessGenLevel)); 2220 } 2221 2222 // Geometry limits 2223 { 2224 int geometryOutputComponents = -1; 2225 int geometryOutputVertices = -1; 2226 int geometryTotalOutputComponents = -1; 2227 int geometryShaderInvocations = -1; 2228 bool logGeometryLimits = false; 2229 bool logInvocationLimits = false; 2230 2231 if (m_flags & FLAG_GEOMETRY_MAX_IMPLEMENTATION) 2232 { 2233 m_testCtx.getLog() << tcu::TestLog::Message << "Using implementation maximum geometry shader output limits." << tcu::TestLog::EndMessage; 2234 2235 gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_COMPONENTS, &geometryOutputComponents); 2236 gl.getIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &geometryOutputVertices); 2237 gl.getIntegerv(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS, &geometryTotalOutputComponents); 2238 GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry limits"); 2239 2240 logGeometryLimits = true; 2241 } 2242 else if (m_flags & FLAG_GEOMETRY_MAX_SPEC) 2243 { 2244 m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader extension minimum maximum output limits." << tcu::TestLog::EndMessage; 2245 2246 geometryOutputComponents = 128; 2247 geometryOutputVertices = 256; 2248 geometryTotalOutputComponents = 1024; 2249 logGeometryLimits = true; 2250 } 2251 else 2252 { 2253 geometryOutputComponents = 128; 2254 geometryOutputVertices = 16; 2255 geometryTotalOutputComponents = 1024; 2256 } 2257 2258 if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION) 2259 { 2260 gl.getIntegerv(GL_MAX_GEOMETRY_SHADER_INVOCATIONS, &geometryShaderInvocations); 2261 GLU_EXPECT_NO_ERROR(gl.getError(), "query geometry invocation limits"); 2262 2263 logInvocationLimits = true; 2264 } 2265 else if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) 2266 { 2267 geometryShaderInvocations = 32; 2268 logInvocationLimits = true; 2269 } 2270 else 2271 { 2272 geometryShaderInvocations = 4; 2273 } 2274 2275 if (logGeometryLimits || logInvocationLimits) 2276 { 2277 tcu::MessageBuilder msg(&m_testCtx.getLog()); 2278 2279 msg << "Geometry shader, targeting following limits:\n"; 2280 2281 if (logGeometryLimits) 2282 msg << "\tGL_MAX_GEOMETRY_OUTPUT_COMPONENTS = " << geometryOutputComponents << "\n" 2283 << "\tGL_MAX_GEOMETRY_OUTPUT_VERTICES = " << geometryOutputVertices << "\n" 2284 << "\tGL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS = " << geometryTotalOutputComponents << "\n"; 2285 2286 if (logInvocationLimits) 2287 msg << "\tGL_MAX_GEOMETRY_SHADER_INVOCATIONS = " << geometryShaderInvocations; 2288 2289 msg << tcu::TestLog::EndMessage; 2290 } 2291 2292 { 2293 const bool separatePrimitives = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0; 2294 const int numComponentsPerVertex = 8; // vec4 pos, vec4 color 2295 int numVerticesPerInvocation; 2296 int numPrimitivesPerInvocation; 2297 int geometryVerticesPerPrimitive; 2298 int geometryPrimitivesOutPerPrimitive; 2299 2300 if (separatePrimitives) 2301 { 2302 const int numComponentLimit = geometryTotalOutputComponents / (4 * numComponentsPerVertex); 2303 const int numOutputLimit = geometryOutputVertices / 4; 2304 2305 numPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit); 2306 numVerticesPerInvocation = numPrimitivesPerInvocation * 4; 2307 } 2308 else 2309 { 2310 // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices. 2311 // Each slice is a triangle strip and is generated by a single shader invocation. 2312 // One slice with 4 segment ends (nodes) and 3 segments: 2313 // .__.__.__. 2314 // |\ |\ |\ | 2315 // |_\|_\|_\| 2316 2317 const int numSliceNodesComponentLimit = geometryTotalOutputComponents / (2 * numComponentsPerVertex); // each node 2 vertices 2318 const int numSliceNodesOutputLimit = geometryOutputVertices / 2; // each node 2 vertices 2319 const int numSliceNodes = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit); 2320 2321 numVerticesPerInvocation = numSliceNodes * 2; 2322 numPrimitivesPerInvocation = (numSliceNodes - 1) * 2; 2323 } 2324 2325 geometryVerticesPerPrimitive = numVerticesPerInvocation * geometryShaderInvocations; 2326 geometryPrimitivesOutPerPrimitive = numPrimitivesPerInvocation * geometryShaderInvocations; 2327 2328 m_testCtx.getLog() 2329 << tcu::TestLog::Message 2330 << "Geometry shader:\n" 2331 << "\tTotal output vertex count per invocation: " << (numVerticesPerInvocation) << "\n" 2332 << "\tTotal output primitive count per invocation: " << (numPrimitivesPerInvocation) << "\n" 2333 << "\tNumber of invocations per primitive: " << geometryShaderInvocations << "\n" 2334 << "\tTotal output vertex count per input primitive: " << (geometryVerticesPerPrimitive) << "\n" 2335 << "\tTotal output primitive count per input primitive: " << (geometryPrimitivesOutPerPrimitive) << "\n" 2336 << tcu::TestLog::EndMessage; 2337 2338 sources << glu::GeometrySource(getGeometryShaderSource(numPrimitivesPerInvocation, geometryShaderInvocations, tessGenLevel)); 2339 2340 m_testCtx.getLog() 2341 << tcu::TestLog::Message 2342 << "Program:\n" 2343 << "\tTotal program output vertices count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n" 2344 << "\tTotal program output primitive count per input patch: " << (tessGenLevel*tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n" 2345 << tcu::TestLog::EndMessage; 2346 } 2347 } 2348 2349 m_program = new glu::ShaderProgram(m_context.getRenderContext(), sources); 2350 m_testCtx.getLog() << *m_program; 2351 if (!m_program->isOk()) 2352 throw tcu::TestError("failed to build program"); 2353 } 2354 } 2355 2356 void GridRenderCase::deinit (void) 2357 { 2358 delete m_program; 2359 m_program = DE_NULL; 2360 2361 if (m_texture) 2362 { 2363 m_context.getRenderContext().getFunctions().deleteTextures(1, &m_texture); 2364 m_texture = 0; 2365 } 2366 } 2367 2368 GridRenderCase::IterateResult GridRenderCase::iterate (void) 2369 { 2370 std::vector<tcu::Surface> renderedLayers (m_numLayers); 2371 bool allLayersOk = true; 2372 2373 for (int ndx = 0; ndx < m_numLayers; ++ndx) 2374 renderedLayers[ndx].setSize(RENDER_SIZE, RENDER_SIZE); 2375 2376 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering single point at the origin. Expecting yellow and green colored grid-like image. (High-frequency grid may appear unicolored)." << tcu::TestLog::EndMessage; 2377 2378 try 2379 { 2380 renderTo(renderedLayers); 2381 } 2382 catch (const AllowedRenderFailureException& ex) 2383 { 2384 // Got accepted failure 2385 m_testCtx.getLog() 2386 << tcu::TestLog::Message 2387 << "Could not render, reason: " << ex.what() << "\n" 2388 << "Failure is allowed." 2389 << tcu::TestLog::EndMessage; 2390 2391 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 2392 return STOP; 2393 } 2394 2395 for (int ndx = 0; ndx < m_numLayers; ++ndx) 2396 allLayersOk &= verifyResultLayer(ndx, renderedLayers[ndx]); 2397 2398 if (allLayersOk) 2399 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 2400 else 2401 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image verification failed"); 2402 return STOP; 2403 } 2404 2405 void GridRenderCase::renderTo (std::vector<tcu::Surface>& dst) 2406 { 2407 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2408 const int positionLocation = gl.getAttribLocation(m_program->getProgram(), "a_position"); 2409 const glu::VertexArray vao (m_context.getRenderContext()); 2410 de::MovePtr<glu::Framebuffer> fbo; 2411 2412 if (positionLocation == -1) 2413 throw tcu::TestError("Attribute a_position location was -1"); 2414 2415 gl.viewport(0, 0, dst.front().getWidth(), dst.front().getHeight()); 2416 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 2417 GLU_EXPECT_NO_ERROR(gl.getError(), "viewport"); 2418 2419 gl.bindVertexArray(*vao); 2420 GLU_EXPECT_NO_ERROR(gl.getError(), "bind vao"); 2421 2422 gl.useProgram(m_program->getProgram()); 2423 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 2424 2425 gl.patchParameteri(GL_PATCH_VERTICES, 1); 2426 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 2427 2428 gl.vertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f); 2429 2430 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) 2431 { 2432 // clear texture contents 2433 { 2434 glu::Framebuffer clearFbo(m_context.getRenderContext()); 2435 gl.bindFramebuffer(GL_FRAMEBUFFER, *clearFbo); 2436 2437 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx) 2438 { 2439 gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx); 2440 gl.clear(GL_COLOR_BUFFER_BIT); 2441 } 2442 2443 GLU_EXPECT_NO_ERROR(gl.getError(), "clear tex contents"); 2444 } 2445 2446 // create and bind layered fbo 2447 2448 fbo = de::MovePtr<glu::Framebuffer>(new glu::Framebuffer(m_context.getRenderContext())); 2449 2450 gl.bindFramebuffer(GL_FRAMEBUFFER, **fbo); 2451 gl.framebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0); 2452 GLU_EXPECT_NO_ERROR(gl.getError(), "gen fbo"); 2453 } 2454 else 2455 { 2456 // clear viewport 2457 gl.clear(GL_COLOR_BUFFER_BIT); 2458 } 2459 2460 // draw 2461 { 2462 glw::GLenum glerror; 2463 2464 gl.drawArrays(GL_PATCHES, 0, 1); 2465 2466 glerror = gl.getError(); 2467 if (glerror == GL_OUT_OF_MEMORY && (m_flags & FLAG_ALLOW_OUT_OF_MEMORY)) 2468 throw AllowedRenderFailureException("got GL_OUT_OF_MEMORY while drawing"); 2469 2470 GLU_EXPECT_NO_ERROR(glerror, "draw patches"); 2471 } 2472 2473 // Read layers 2474 2475 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) 2476 { 2477 glu::Framebuffer readFbo(m_context.getRenderContext()); 2478 gl.bindFramebuffer(GL_FRAMEBUFFER, *readFbo); 2479 2480 for (int layerNdx = 0; layerNdx < m_numLayers; ++layerNdx) 2481 { 2482 gl.framebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_texture, 0, layerNdx); 2483 glu::readPixels(m_context.getRenderContext(), 0, 0, dst[layerNdx].getAccess()); 2484 GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels"); 2485 } 2486 } 2487 else 2488 { 2489 glu::readPixels(m_context.getRenderContext(), 0, 0, dst.front().getAccess()); 2490 GLU_EXPECT_NO_ERROR(gl.getError(), "read pixels"); 2491 } 2492 } 2493 2494 bool GridRenderCase::verifyResultLayer (int layerNdx, const tcu::Surface& image) 2495 { 2496 tcu::Surface errorMask (image.getWidth(), image.getHeight()); 2497 bool foundError = false; 2498 2499 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); 2500 2501 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output layer " << layerNdx << tcu::TestLog::EndMessage; 2502 2503 for (int y = 0; y < image.getHeight(); ++y) 2504 for (int x = 0; x < image.getWidth(); ++x) 2505 { 2506 const int threshold = 8; 2507 const tcu::RGBA color = image.getPixel(x, y); 2508 2509 // Color must be a linear combination of green and yellow 2510 if (color.getGreen() < 255 - threshold || color.getBlue() > threshold) 2511 { 2512 errorMask.setPixel(x, y, tcu::RGBA::red); 2513 foundError = true; 2514 } 2515 } 2516 2517 if (!foundError) 2518 { 2519 m_testCtx.getLog() 2520 << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage 2521 << tcu::TestLog::ImageSet("ImageVerification", "Image verification") 2522 << tcu::TestLog::Image("Result", "Rendered result", image.getAccess()) 2523 << tcu::TestLog::EndImageSet; 2524 return true; 2525 } 2526 else 2527 { 2528 m_testCtx.getLog() 2529 << tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage 2530 << tcu::TestLog::ImageSet("ImageVerification", "Image verification") 2531 << tcu::TestLog::Image("Result", "Rendered result", image.getAccess()) 2532 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 2533 << tcu::TestLog::EndImageSet; 2534 return false; 2535 } 2536 } 2537 2538 const char* GridRenderCase::getVertexSource (void) 2539 { 2540 return s_positionVertexShader; 2541 } 2542 2543 const char* GridRenderCase::getFragmentSource (void) 2544 { 2545 return "#version 310 es\n" 2546 "flat in mediump vec4 v_color;\n" 2547 "layout(location = 0) out mediump vec4 fragColor;\n" 2548 "void main (void)\n" 2549 "{\n" 2550 " fragColor = v_color;\n" 2551 "}\n"; 2552 } 2553 2554 std::string GridRenderCase::getTessellationControlSource (int tessLevel) 2555 { 2556 std::ostringstream buf; 2557 2558 buf << "#version 310 es\n" 2559 "#extension GL_EXT_tessellation_shader : require\n" 2560 "layout(vertices=1) out;\n" 2561 "\n" 2562 "void main()\n" 2563 "{\n" 2564 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 2565 " gl_TessLevelOuter[0] = " << tessLevel << ".0;\n" 2566 " gl_TessLevelOuter[1] = " << tessLevel << ".0;\n" 2567 " gl_TessLevelOuter[2] = " << tessLevel << ".0;\n" 2568 " gl_TessLevelOuter[3] = " << tessLevel << ".0;\n" 2569 " gl_TessLevelInner[0] = " << tessLevel << ".0;\n" 2570 " gl_TessLevelInner[1] = " << tessLevel << ".0;\n" 2571 "}\n"; 2572 2573 return buf.str(); 2574 } 2575 2576 std::string GridRenderCase::getTessellationEvaluationSource (int tessLevel) 2577 { 2578 std::ostringstream buf; 2579 2580 buf << "#version 310 es\n" 2581 "#extension GL_EXT_tessellation_shader : require\n" 2582 "layout(quads) in;\n" 2583 "\n" 2584 "out mediump ivec2 v_tessellationGridPosition;\n" 2585 "\n" 2586 "// note: No need to use precise gl_Position since position does not depend on order\n" 2587 "void main (void)\n" 2588 "{\n"; 2589 2590 if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) 2591 buf << " // Cover only a small area in a corner. The area will be expanded in geometry shader to cover whole viewport\n" 2592 " gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n"; 2593 else 2594 buf << " // Fill the whole viewport\n" 2595 " gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n"; 2596 2597 buf << " // Calculate position in tessellation grid\n" 2598 " v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << tessLevel << ")));\n" 2599 "}\n"; 2600 2601 return buf.str(); 2602 } 2603 2604 std::string GridRenderCase::getGeometryShaderSource (int numPrimitives, int numInstances, int tessLevel) 2605 { 2606 std::ostringstream buf; 2607 2608 buf << "#version 310 es\n" 2609 "#extension GL_EXT_geometry_shader : require\n" 2610 "layout(triangles, invocations=" << numInstances << ") in;\n" 2611 "layout(triangle_strip, max_vertices=" << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n" 2612 "\n" 2613 "in mediump ivec2 v_tessellationGridPosition[];\n" 2614 "flat out highp vec4 v_color;\n" 2615 "\n" 2616 "void main ()\n" 2617 "{\n" 2618 " const float equalThreshold = 0.001;\n" 2619 " const float gapOffset = 0.0001; // subdivision performed by the geometry shader might produce gaps. Fill potential gaps by enlarging the output slice a little.\n" 2620 "\n" 2621 " // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n" 2622 " // Original rectangle can be found by finding the bounding AABB of the triangle\n" 2623 " vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n" 2624 " min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n" 2625 " max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n" 2626 " max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n" 2627 "\n" 2628 " // Location in tessellation grid\n" 2629 " ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n" 2630 "\n" 2631 " // Which triangle of the two that split the grid cell\n" 2632 " int numVerticesOnBottomEdge = 0;\n" 2633 " for (int ndx = 0; ndx < 3; ++ndx)\n" 2634 " if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n" 2635 " ++numVerticesOnBottomEdge;\n" 2636 " bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n" 2637 "\n"; 2638 2639 if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES) 2640 { 2641 // scatter primitives 2642 buf << " // Draw grid cells\n" 2643 " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n" 2644 " for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n" 2645 " {\n" 2646 " ivec2 dstGridSize = ivec2(" << tessLevel << " * " << numPrimitives << ", 2 * " << tessLevel << " * " << numInstances << ");\n" 2647 " ivec2 dstGridNdx = ivec2(" << tessLevel << " * ndx + gridPosition.x, " << tessLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n" 2648 " vec4 dstArea;\n" 2649 " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n" 2650 " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n" 2651 " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n" 2652 " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n" 2653 "\n" 2654 " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 2655 " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 2656 " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n" 2657 "\n" 2658 " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n" 2659 " v_color = outputColor;\n" 2660 " EmitVertex();\n" 2661 "\n" 2662 " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n" 2663 " v_color = outputColor;\n" 2664 " EmitVertex();\n" 2665 "\n" 2666 " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n" 2667 " v_color = outputColor;\n" 2668 " EmitVertex();\n" 2669 "\n" 2670 " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n" 2671 " v_color = outputColor;\n" 2672 " EmitVertex();\n" 2673 " EndPrimitive();\n" 2674 " }\n"; 2675 } 2676 else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) 2677 { 2678 // Number of subrectangle instances = num layers 2679 DE_ASSERT(m_numLayers == numInstances * 2); 2680 2681 buf << " // Draw grid cells, send each primitive to a separate layer\n" 2682 " int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n" 2683 " for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n" 2684 " {\n" 2685 " ivec2 dstGridSize = ivec2(" << tessLevel << " * " << numPrimitives << ", " << tessLevel << ");\n" 2686 " ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n" 2687 " vec4 dstArea;\n" 2688 " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n" 2689 " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n" 2690 " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n" 2691 " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n" 2692 "\n" 2693 " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 2694 " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 2695 " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n" 2696 "\n" 2697 " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n" 2698 " v_color = outputColor;\n" 2699 " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 2700 " EmitVertex();\n" 2701 "\n" 2702 " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n" 2703 " v_color = outputColor;\n" 2704 " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 2705 " EmitVertex();\n" 2706 "\n" 2707 " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n" 2708 " v_color = outputColor;\n" 2709 " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 2710 " EmitVertex();\n" 2711 "\n" 2712 " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n" 2713 " v_color = outputColor;\n" 2714 " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 2715 " EmitVertex();\n" 2716 " EndPrimitive();\n" 2717 " }\n"; 2718 } 2719 else 2720 { 2721 if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES) 2722 { 2723 buf << " // Scatter slices\n" 2724 " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n" 2725 " ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInstances*2) << " + inputTriangleNdx);\n" 2726 " ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2(" << tessLevel << ", " << tessLevel << " * " << (numInstances*2) << ");\n" 2727 "\n" 2728 " // Draw slice to the dstSlice slot\n" 2729 " vec4 outputSliceArea;\n" 2730 " outputSliceArea.x = float(dstSliceNdx.x) / float(" << tessLevel << ") * 2.0 - 1.0 - gapOffset;\n" 2731 " outputSliceArea.y = float(dstSliceNdx.y) / float(" << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 - gapOffset;\n" 2732 " outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << tessLevel << ") * 2.0 - 1.0 + gapOffset;\n" 2733 " outputSliceArea.w = float(dstSliceNdx.y+1) / float(" << (tessLevel * numInstances * 2) << ") * 2.0 - 1.0 + gapOffset;\n"; 2734 } 2735 else 2736 { 2737 buf << " // Fill the input area with slices\n" 2738 " // Upper triangle produces slices only to the upper half of the quad and vice-versa\n" 2739 " float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n" 2740 " // Each slice is a invocation\n" 2741 " float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInstances << ");\n" 2742 " float invocationOffset = float(gl_InvocationID) * sliceHeight;\n" 2743 "\n" 2744 " vec4 outputSliceArea;\n" 2745 " outputSliceArea.x = aabb.x - gapOffset;\n" 2746 " outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n" 2747 " outputSliceArea.z = aabb.z + gapOffset;\n" 2748 " outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n"; 2749 } 2750 2751 buf << "\n" 2752 " // Draw slice\n" 2753 " for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n" 2754 " {\n" 2755 " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 2756 " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 2757 " vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n" 2758 " float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n" 2759 "\n" 2760 " gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n" 2761 " v_color = outputColor;\n" 2762 " EmitVertex();\n" 2763 "\n" 2764 " gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n" 2765 " v_color = outputColor;\n" 2766 " EmitVertex();\n" 2767 " }\n"; 2768 } 2769 2770 buf << "}\n"; 2771 2772 return buf.str(); 2773 } 2774 2775 class FeedbackRecordVariableSelectionCase : public TestCase 2776 { 2777 public: 2778 FeedbackRecordVariableSelectionCase (Context& context, const char* name, const char* description); 2779 ~FeedbackRecordVariableSelectionCase (void); 2780 2781 private: 2782 void init (void); 2783 void deinit (void); 2784 IterateResult iterate (void); 2785 2786 const char* getVertexSource (void); 2787 const char* getFragmentSource (void); 2788 const char* getTessellationControlSource (void); 2789 const char* getTessellationEvaluationSource (void); 2790 const char* getGeometrySource (void); 2791 2792 glu::ShaderProgram* m_program; 2793 deUint32 m_xfbBuf; 2794 }; 2795 2796 FeedbackRecordVariableSelectionCase::FeedbackRecordVariableSelectionCase (Context& context, const char* name, const char* description) 2797 : TestCase (context, name, description) 2798 , m_program (DE_NULL) 2799 , m_xfbBuf (0) 2800 { 2801 } 2802 2803 FeedbackRecordVariableSelectionCase::~FeedbackRecordVariableSelectionCase (void) 2804 { 2805 deinit(); 2806 } 2807 2808 void FeedbackRecordVariableSelectionCase::init (void) 2809 { 2810 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") || 2811 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader")) 2812 throw tcu::NotSupportedError("Test requires GL_EXT_tessellation_shader and GL_EXT_geometry_shader extensions"); 2813 2814 m_testCtx.getLog() << tcu::TestLog::Message << "Declaring multiple output variables with the same name in multiple shader stages. Capturing the value of the varying using transform feedback." << tcu::TestLog::EndMessage; 2815 2816 // gen feedback buffer fit for 1 triangle (4 components) 2817 { 2818 static const tcu::Vec4 initialData[3] = 2819 { 2820 tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f), 2821 tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f), 2822 tcu::Vec4(-1.0f, -1.0f, -1.0f, -1.0f), 2823 }; 2824 2825 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2826 2827 m_testCtx.getLog() << tcu::TestLog::Message << "Creating buffer for transform feedback. Allocating storage for one triangle. Filling with -1.0" << tcu::TestLog::EndMessage; 2828 2829 gl.genBuffers(1, &m_xfbBuf); 2830 gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_xfbBuf); 2831 gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, (int)(sizeof(tcu::Vec4[3])), initialData, GL_DYNAMIC_READ); 2832 GLU_EXPECT_NO_ERROR(gl.getError(), "gen xfb buf"); 2833 } 2834 2835 // gen shader 2836 m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() 2837 << glu::VertexSource(getVertexSource()) 2838 << glu::FragmentSource(getFragmentSource()) 2839 << glu::TessellationControlSource(getTessellationControlSource()) 2840 << glu::TessellationEvaluationSource(getTessellationEvaluationSource()) 2841 << glu::GeometrySource(getGeometrySource()) 2842 << glu::TransformFeedbackMode(GL_INTERLEAVED_ATTRIBS) 2843 << glu::TransformFeedbackVarying("tf_feedback")); 2844 m_testCtx.getLog() << *m_program; 2845 2846 if (!m_program->isOk()) 2847 throw tcu::TestError("could not build program"); 2848 } 2849 2850 void FeedbackRecordVariableSelectionCase::deinit (void) 2851 { 2852 delete m_program; 2853 m_program = DE_NULL; 2854 2855 if (m_xfbBuf) 2856 { 2857 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_xfbBuf); 2858 m_xfbBuf = 0; 2859 } 2860 } 2861 2862 FeedbackRecordVariableSelectionCase::IterateResult FeedbackRecordVariableSelectionCase::iterate (void) 2863 { 2864 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 2865 const int posLoc = gl.getAttribLocation(m_program->getProgram(), "a_position"); 2866 const glu::VertexArray vao (m_context.getRenderContext()); 2867 2868 if (posLoc == -1) 2869 throw tcu::TestError("a_position attribute location was -1"); 2870 2871 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 2872 2873 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering a patch of size 3." << tcu::TestLog::EndMessage; 2874 2875 // Render and feed back 2876 2877 gl.viewport(0, 0, 1, 1); 2878 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 2879 gl.clear(GL_COLOR_BUFFER_BIT); 2880 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 2881 2882 gl.bindVertexArray(*vao); 2883 GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray"); 2884 2885 gl.vertexAttrib4f(posLoc, 0.0f, 0.0f, 0.0f, 1.0f); 2886 GLU_EXPECT_NO_ERROR(gl.getError(), "vertexAttrib4f"); 2887 2888 gl.useProgram(m_program->getProgram()); 2889 GLU_EXPECT_NO_ERROR(gl.getError(), "use program"); 2890 2891 gl.patchParameteri(GL_PATCH_VERTICES, 3); 2892 GLU_EXPECT_NO_ERROR(gl.getError(), "set patch param"); 2893 2894 gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_xfbBuf); 2895 GLU_EXPECT_NO_ERROR(gl.getError(), "bind xfb buf"); 2896 2897 gl.beginTransformFeedback(GL_TRIANGLES); 2898 GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback"); 2899 2900 gl.drawArrays(GL_PATCHES, 0, 3); 2901 GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays"); 2902 2903 gl.endTransformFeedback(); 2904 GLU_EXPECT_NO_ERROR(gl.getError(), "beginTransformFeedback"); 2905 2906 m_testCtx.getLog() << tcu::TestLog::Message << "Verifying the value of tf_feedback using transform feedback, expecting (3.0, 3.0, 3.0, 3.0)." << tcu::TestLog::EndMessage; 2907 2908 // Read back result (one triangle) 2909 { 2910 tcu::Vec4 feedbackValues[3]; 2911 const void* mapPtr = gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, (int)sizeof(feedbackValues), GL_MAP_READ_BIT); 2912 GLU_EXPECT_NO_ERROR(gl.getError(), "mapBufferRange"); 2913 2914 if (mapPtr == DE_NULL) 2915 throw tcu::TestError("mapBufferRange returned null"); 2916 2917 deMemcpy(feedbackValues, mapPtr, sizeof(feedbackValues)); 2918 2919 if (gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER) != GL_TRUE) 2920 throw tcu::TestError("unmapBuffer did not return TRUE"); 2921 2922 for (int ndx = 0; ndx < 3; ++ndx) 2923 { 2924 if (!tcu::boolAll(tcu::lessThan(tcu::abs(feedbackValues[ndx] - tcu::Vec4(3.0f)), tcu::Vec4(0.001f)))) 2925 { 2926 m_testCtx.getLog() << tcu::TestLog::Message << "Feedback vertex " << ndx << ": expected (3.0, 3.0, 3.0, 3.0), got " << feedbackValues[ndx] << tcu::TestLog::EndMessage; 2927 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "got unexpected feedback results"); 2928 } 2929 } 2930 } 2931 2932 return STOP; 2933 } 2934 2935 const char* FeedbackRecordVariableSelectionCase::getVertexSource (void) 2936 { 2937 return "#version 310 es\n" 2938 "in highp vec4 a_position;\n" 2939 "out highp vec4 tf_feedback;\n" 2940 "void main()\n" 2941 "{\n" 2942 " gl_Position = a_position;\n" 2943 " tf_feedback = vec4(1.0, 1.0, 1.0, 1.0);\n" 2944 "}\n"; 2945 } 2946 2947 const char* FeedbackRecordVariableSelectionCase::getFragmentSource (void) 2948 { 2949 return s_whiteOutputFragmentShader; 2950 } 2951 2952 const char* FeedbackRecordVariableSelectionCase::getTessellationControlSource (void) 2953 { 2954 return "#version 310 es\n" 2955 "#extension GL_EXT_tessellation_shader : require\n" 2956 "layout(vertices=3) out;\n" 2957 "void main()\n" 2958 "{\n" 2959 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 2960 " gl_TessLevelOuter[0] = 1.0;\n" 2961 " gl_TessLevelOuter[1] = 1.0;\n" 2962 " gl_TessLevelOuter[2] = 1.0;\n" 2963 " gl_TessLevelInner[0] = 1.0;\n" 2964 "}\n"; 2965 } 2966 2967 const char* FeedbackRecordVariableSelectionCase::getTessellationEvaluationSource (void) 2968 { 2969 return "#version 310 es\n" 2970 "#extension GL_EXT_tessellation_shader : require\n" 2971 "layout(triangles) in;\n" 2972 "out highp vec4 tf_feedback;\n" 2973 "void main()\n" 2974 "{\n" 2975 " gl_Position = gl_in[0].gl_Position * gl_TessCoord.x + gl_in[1].gl_Position * gl_TessCoord.y + gl_in[2].gl_Position * gl_TessCoord.z;\n" 2976 " tf_feedback = vec4(2.0, 2.0, 2.0, 2.0);\n" 2977 "}\n"; 2978 } 2979 2980 const char* FeedbackRecordVariableSelectionCase::getGeometrySource (void) 2981 { 2982 return "#version 310 es\n" 2983 "#extension GL_EXT_geometry_shader : require\n" 2984 "layout (triangles) in;\n" 2985 "layout (triangle_strip, max_vertices=3) out;\n" 2986 "out highp vec4 tf_feedback;\n" 2987 "void main()\n" 2988 "{\n" 2989 " for (int ndx = 0; ndx < 3; ++ndx)\n" 2990 " {\n" 2991 " gl_Position = gl_in[ndx].gl_Position + vec4(float(ndx), float(ndx)*float(ndx), 0.0, 0.0);\n" 2992 " tf_feedback = vec4(3.0, 3.0, 3.0, 3.0);\n" 2993 " EmitVertex();\n" 2994 " }\n" 2995 " EndPrimitive();\n" 2996 "}\n"; 2997 } 2998 2999 } // anonymous 3000 3001 TessellationGeometryInteractionTests::TessellationGeometryInteractionTests (Context& context) 3002 : TestCaseGroup(context, "tessellation_geometry_interaction", "Tessellation and geometry shader interaction tests") 3003 { 3004 } 3005 3006 TessellationGeometryInteractionTests::~TessellationGeometryInteractionTests (void) 3007 { 3008 } 3009 3010 void TessellationGeometryInteractionTests::init (void) 3011 { 3012 tcu::TestCaseGroup* const renderGroup = new tcu::TestCaseGroup(m_testCtx, "render", "Various render tests"); 3013 tcu::TestCaseGroup* const feedbackGroup = new tcu::TestCaseGroup(m_testCtx, "feedback", "Test transform feedback"); 3014 tcu::TestCaseGroup* const pointSizeGroup = new tcu::TestCaseGroup(m_testCtx, "point_size", "Test point size"); 3015 3016 addChild(renderGroup); 3017 addChild(feedbackGroup); 3018 addChild(pointSizeGroup); 3019 3020 // .render 3021 { 3022 tcu::TestCaseGroup* const passthroughGroup = new tcu::TestCaseGroup(m_testCtx, "passthrough", "Render various types with either passthrough geometry or tessellation shader"); 3023 tcu::TestCaseGroup* const limitGroup = new tcu::TestCaseGroup(m_testCtx, "limits", "Render with properties near their limits"); 3024 tcu::TestCaseGroup* const scatterGroup = new tcu::TestCaseGroup(m_testCtx, "scatter", "Scatter output primitives"); 3025 3026 renderGroup->addChild(passthroughGroup); 3027 renderGroup->addChild(limitGroup); 3028 renderGroup->addChild(scatterGroup); 3029 3030 // .passthrough 3031 { 3032 // tessellate_tris_passthrough_geometry_no_change 3033 // tessellate_quads_passthrough_geometry_no_change 3034 // tessellate_isolines_passthrough_geometry_no_change 3035 passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_tris_passthrough_geometry_no_change", "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_TRIANGLES)); 3036 passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_quads_passthrough_geometry_no_change", "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_QUADS)); 3037 passthroughGroup->addChild(new IdentityGeometryShaderCase(m_context, "tessellate_isolines_passthrough_geometry_no_change", "Passthrough geometry shader has no effect", IdentityGeometryShaderCase::CASE_ISOLINES)); 3038 3039 // passthrough_tessellation_geometry_shade_triangles_no_change 3040 // passthrough_tessellation_geometry_shade_lines_no_change 3041 passthroughGroup->addChild(new IdentityTessellationShaderCase(m_context, "passthrough_tessellation_geometry_shade_triangles_no_change", "Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_TRIANGLES)); 3042 passthroughGroup->addChild(new IdentityTessellationShaderCase(m_context, "passthrough_tessellation_geometry_shade_lines_no_change", "Passthrough tessellation shader has no effect", IdentityTessellationShaderCase::CASE_ISOLINES)); 3043 } 3044 3045 // .limits 3046 { 3047 static const struct LimitCaseDef 3048 { 3049 const char* name; 3050 const char* desc; 3051 int flags; 3052 } cases[] = 3053 { 3054 // Test single limit 3055 { 3056 "output_required_max_tessellation", 3057 "Minimum maximum tessellation level", 3058 GridRenderCase::FLAG_TESSELLATION_MAX_SPEC 3059 }, 3060 { 3061 "output_implementation_max_tessellation", 3062 "Maximum tessellation level supported by the implementation", 3063 GridRenderCase::FLAG_TESSELLATION_MAX_IMPLEMENTATION 3064 }, 3065 { 3066 "output_required_max_geometry", 3067 "Output minimum maximum number of vertices the geometry shader", 3068 GridRenderCase::FLAG_GEOMETRY_MAX_SPEC 3069 }, 3070 { 3071 "output_implementation_max_geometry", 3072 "Output maximum number of vertices in the geometry shader supported by the implementation", 3073 GridRenderCase::FLAG_GEOMETRY_MAX_IMPLEMENTATION 3074 }, 3075 { 3076 "output_required_max_invocations", 3077 "Minimum maximum number of geometry shader invocations", 3078 GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC 3079 }, 3080 { 3081 "output_implementation_max_invocations", 3082 "Maximum number of geometry shader invocations supported by the implementation", 3083 GridRenderCase::FLAG_GEOMETRY_INVOCATIONS_MAX_IMPLEMENTATION 3084 }, 3085 }; 3086 3087 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx) 3088 limitGroup->addChild(new GridRenderCase(m_context, cases[ndx].name, cases[ndx].desc, cases[ndx].flags)); 3089 } 3090 3091 // .scatter 3092 { 3093 scatterGroup->addChild(new GridRenderCase(m_context, 3094 "geometry_scatter_instances", 3095 "Each geometry shader instance outputs its primitives far from other instances of the same execution", 3096 GridRenderCase::FLAG_GEOMETRY_SCATTER_INSTANCES)); 3097 scatterGroup->addChild(new GridRenderCase(m_context, 3098 "geometry_scatter_primitives", 3099 "Each geometry shader instance outputs its primitives far from other primitives of the same instance", 3100 GridRenderCase::FLAG_GEOMETRY_SCATTER_PRIMITIVES | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES)); 3101 scatterGroup->addChild(new GridRenderCase(m_context, 3102 "geometry_scatter_layers", 3103 "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance", 3104 GridRenderCase::FLAG_GEOMETRY_SCATTER_LAYERS | GridRenderCase::FLAG_GEOMETRY_SEPARATE_PRIMITIVES)); 3105 } 3106 } 3107 3108 // .feedback 3109 { 3110 static const struct PrimitiveCaseConfig 3111 { 3112 const char* name; 3113 const char* description; 3114 FeedbackPrimitiveTypeCase::TessellationOutputType tessellationOutput; 3115 FeedbackPrimitiveTypeCase::TessellationPointMode tessellationPointMode; 3116 FeedbackPrimitiveTypeCase::GeometryOutputType geometryOutputType; 3117 } caseConfigs[] = 3118 { 3119 // tess output triangles -> geo input triangles, output points 3120 { 3121 "tessellation_output_triangles_geometry_output_points", 3122 "Tessellation outputs triangles, geometry outputs points", 3123 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES, 3124 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, 3125 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS 3126 }, 3127 3128 // tess output quads <-> geo input triangles, output points 3129 { 3130 "tessellation_output_quads_geometry_output_points", 3131 "Tessellation outputs quads, geometry outputs points", 3132 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS, 3133 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, 3134 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS 3135 }, 3136 3137 // tess output isolines <-> geo input lines, output points 3138 { 3139 "tessellation_output_isolines_geometry_output_points", 3140 "Tessellation outputs isolines, geometry outputs points", 3141 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES, 3142 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_OFF, 3143 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_POINTS 3144 }, 3145 3146 // tess output triangles, point_mode <-> geo input points, output lines 3147 { 3148 "tessellation_output_triangles_point_mode_geometry_output_lines", 3149 "Tessellation outputs triangles in point mode, geometry outputs lines", 3150 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_TRIANGLES, 3151 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON, 3152 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES 3153 }, 3154 3155 // tess output quads, point_mode <-> geo input points, output lines 3156 { 3157 "tessellation_output_quads_point_mode_geometry_output_lines", 3158 "Tessellation outputs quads in point mode, geometry outputs lines", 3159 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_QUADS, 3160 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON, 3161 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_LINES 3162 }, 3163 3164 // tess output isolines, point_mode <-> geo input points, output triangles 3165 { 3166 "tessellation_output_isolines_point_mode_geometry_output_triangles", 3167 "Tessellation outputs isolines in point mode, geometry outputs triangles", 3168 FeedbackPrimitiveTypeCase::TESSELLATION_OUT_ISOLINES, 3169 FeedbackPrimitiveTypeCase::TESSELLATION_POINTMODE_ON, 3170 FeedbackPrimitiveTypeCase::GEOMETRY_OUTPUT_TRIANGLES 3171 }, 3172 }; 3173 3174 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseConfigs); ++ndx) 3175 { 3176 feedbackGroup->addChild(new FeedbackPrimitiveTypeCase(m_context, 3177 caseConfigs[ndx].name, 3178 caseConfigs[ndx].description, 3179 caseConfigs[ndx].tessellationOutput, 3180 caseConfigs[ndx].tessellationPointMode, 3181 caseConfigs[ndx].geometryOutputType)); 3182 } 3183 3184 feedbackGroup->addChild(new FeedbackRecordVariableSelectionCase(m_context, "record_variable_selection", "Record a variable that has been declared as an output variable in multiple shader stages")); 3185 } 3186 3187 // .point_size 3188 { 3189 static const int caseFlags[] = 3190 { 3191 PointSizeCase::FLAG_VERTEX_SET, 3192 PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET, 3193 PointSizeCase::FLAG_GEOMETRY_SET, 3194 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_CONTROL_SET, 3195 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET, 3196 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_DONT_SET, 3197 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_GEOMETRY_SET, 3198 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET | PointSizeCase::FLAG_GEOMETRY_SET, 3199 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_ADD | PointSizeCase::FLAG_GEOMETRY_ADD, 3200 PointSizeCase::FLAG_VERTEX_SET | PointSizeCase::FLAG_TESSELLATION_EVALUATION_SET | PointSizeCase::FLAG_GEOMETRY_DONT_SET, 3201 }; 3202 3203 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx) 3204 { 3205 const std::string name = PointSizeCase::genTestCaseName(caseFlags[ndx]); 3206 const std::string desc = PointSizeCase::genTestCaseDescription(caseFlags[ndx]); 3207 3208 pointSizeGroup->addChild(new PointSizeCase(m_context, name.c_str(), desc.c_str(), caseFlags[ndx])); 3209 } 3210 } 3211 } 3212 3213 } // Functional 3214 } // gles31 3215 } // deqp 3216