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