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