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