1 /*------------------------------------------------------------------------ 2 * Vulkan Conformance Tests 3 * ------------------------ 4 * 5 * Copyright (c) 2014 The Android Open Source Project 6 * Copyright (c) 2016 The Khronos Group Inc. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 *//*! 21 * \file 22 * \brief Tessellation Geometry Interaction - Grid render (limits, scatter) 23 *//*--------------------------------------------------------------------*/ 24 25 #include "vktTessellationGeometryGridRenderTests.hpp" 26 #include "vktTestCaseUtil.hpp" 27 #include "vktTessellationUtil.hpp" 28 29 #include "tcuTestLog.hpp" 30 #include "tcuTextureUtil.hpp" 31 #include "tcuSurface.hpp" 32 #include "tcuRGBA.hpp" 33 34 #include "vkDefs.hpp" 35 #include "vkQueryUtil.hpp" 36 #include "vkBuilderUtil.hpp" 37 #include "vkTypeUtil.hpp" 38 #include "vkImageUtil.hpp" 39 40 #include "deUniquePtr.hpp" 41 42 #include <string> 43 #include <vector> 44 45 namespace vkt 46 { 47 namespace tessellation 48 { 49 50 using namespace vk; 51 52 namespace 53 { 54 55 enum Constants 56 { 57 RENDER_SIZE = 256, 58 }; 59 60 enum FlagBits 61 { 62 FLAG_TESSELLATION_MAX_SPEC = 1u << 0, 63 FLAG_GEOMETRY_MAX_SPEC = 1u << 1, 64 FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC = 1u << 2, 65 66 FLAG_GEOMETRY_SCATTER_INSTANCES = 1u << 3, 67 FLAG_GEOMETRY_SCATTER_PRIMITIVES = 1u << 4, 68 FLAG_GEOMETRY_SEPARATE_PRIMITIVES = 1u << 5, //!< if set, geometry shader outputs separate grid cells and not continuous slices 69 FLAG_GEOMETRY_SCATTER_LAYERS = 1u << 6, 70 }; 71 typedef deUint32 Flags; 72 73 class GridRenderTestCase : public TestCase 74 { 75 public: 76 void initPrograms (vk::SourceCollections& programCollection) const; 77 TestInstance* createInstance (Context& context) const; 78 79 GridRenderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Flags flags); 80 81 private: 82 const Flags m_flags; 83 const int m_tessGenLevel; 84 const int m_numGeometryInvocations; 85 const int m_numLayers; 86 int m_numGeometryPrimitivesPerInvocation; 87 }; 88 89 GridRenderTestCase::GridRenderTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const Flags flags) 90 : TestCase (testCtx, name, description) 91 , m_flags (flags) 92 , m_tessGenLevel ((m_flags & FLAG_TESSELLATION_MAX_SPEC) ? 64 : 5) 93 , m_numGeometryInvocations ((m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) ? 32 : 4) 94 , m_numLayers ((m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) ? 8 : 1) 95 { 96 DE_ASSERT(((flags & (FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) != 0) == ((flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0)); 97 98 testCtx.getLog() 99 << tcu::TestLog::Message 100 << "Testing tessellation and geometry shaders that output a large number of primitives.\n" 101 << getDescription() 102 << tcu::TestLog::EndMessage; 103 104 if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) 105 m_testCtx.getLog() << tcu::TestLog::Message << "Rendering to 2d texture array, numLayers = " << m_numLayers << tcu::TestLog::EndMessage; 106 107 m_testCtx.getLog() 108 << tcu::TestLog::Message 109 << "Tessellation level: " << m_tessGenLevel << ", mode = quad.\n" 110 << "\tEach input patch produces " << (m_tessGenLevel*m_tessGenLevel) << " (" << (m_tessGenLevel*m_tessGenLevel*2) << " triangles)\n" 111 << tcu::TestLog::EndMessage; 112 113 int geometryOutputComponents = 0; 114 int geometryOutputVertices = 0; 115 int geometryTotalOutputComponents = 0; 116 117 if (m_flags & FLAG_GEOMETRY_MAX_SPEC) 118 { 119 m_testCtx.getLog() << tcu::TestLog::Message << "Using geometry shader minimum maximum output limits." << tcu::TestLog::EndMessage; 120 121 geometryOutputComponents = 64; 122 geometryOutputVertices = 256; 123 geometryTotalOutputComponents = 1024; 124 } 125 else 126 { 127 geometryOutputComponents = 64; 128 geometryOutputVertices = 16; 129 geometryTotalOutputComponents = 1024; 130 } 131 132 if ((m_flags & FLAG_GEOMETRY_MAX_SPEC) || (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC)) 133 { 134 tcu::MessageBuilder msg(&m_testCtx.getLog()); 135 136 msg << "Geometry shader, targeting following limits:\n"; 137 138 if (m_flags & FLAG_GEOMETRY_MAX_SPEC) 139 msg << "\tmaxGeometryOutputComponents = " << geometryOutputComponents << "\n" 140 << "\tmaxGeometryOutputVertices = " << geometryOutputVertices << "\n" 141 << "\tmaxGeometryTotalOutputComponents = " << geometryTotalOutputComponents << "\n"; 142 143 if (m_flags & FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC) 144 msg << "\tmaxGeometryShaderInvocations = " << m_numGeometryInvocations; 145 146 msg << tcu::TestLog::EndMessage; 147 } 148 149 const bool separatePrimitives = (m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) != 0; 150 const int numComponentsPerVertex = 8; // vec4 pos, vec4 color 151 int numVerticesPerInvocation = 0; 152 int geometryVerticesPerPrimitive = 0; 153 int geometryPrimitivesOutPerPrimitive = 0; 154 155 if (separatePrimitives) 156 { 157 const int numComponentLimit = geometryTotalOutputComponents / (4 * numComponentsPerVertex); 158 const int numOutputLimit = geometryOutputVertices / 4; 159 160 m_numGeometryPrimitivesPerInvocation = de::min(numComponentLimit, numOutputLimit); 161 numVerticesPerInvocation = m_numGeometryPrimitivesPerInvocation * 4; 162 } 163 else 164 { 165 // If FLAG_GEOMETRY_SEPARATE_PRIMITIVES is not set, geometry shader fills a rectangle area in slices. 166 // Each slice is a triangle strip and is generated by a single shader invocation. 167 // One slice with 4 segment ends (nodes) and 3 segments: 168 // .__.__.__. 169 // |\ |\ |\ | 170 // |_\|_\|_\| 171 172 const int numSliceNodesComponentLimit = geometryTotalOutputComponents / (2 * numComponentsPerVertex); // each node 2 vertices 173 const int numSliceNodesOutputLimit = geometryOutputVertices / 2; // each node 2 vertices 174 const int numSliceNodes = de::min(numSliceNodesComponentLimit, numSliceNodesOutputLimit); 175 176 numVerticesPerInvocation = numSliceNodes * 2; 177 m_numGeometryPrimitivesPerInvocation = (numSliceNodes - 1) * 2; 178 } 179 180 geometryVerticesPerPrimitive = numVerticesPerInvocation * m_numGeometryInvocations; 181 geometryPrimitivesOutPerPrimitive = m_numGeometryPrimitivesPerInvocation * m_numGeometryInvocations; 182 183 m_testCtx.getLog() 184 << tcu::TestLog::Message 185 << "Geometry shader:\n" 186 << "\tTotal output vertex count per invocation: " << numVerticesPerInvocation << "\n" 187 << "\tTotal output primitive count per invocation: " << m_numGeometryPrimitivesPerInvocation << "\n" 188 << "\tNumber of invocations per primitive: " << m_numGeometryInvocations << "\n" 189 << "\tTotal output vertex count per input primitive: " << geometryVerticesPerPrimitive << "\n" 190 << "\tTotal output primitive count per input primitive: " << geometryPrimitivesOutPerPrimitive << "\n" 191 << tcu::TestLog::EndMessage; 192 193 m_testCtx.getLog() 194 << tcu::TestLog::Message 195 << "Program:\n" 196 << "\tTotal program output vertices count per input patch: " << (m_tessGenLevel*m_tessGenLevel*2 * geometryVerticesPerPrimitive) << "\n" 197 << "\tTotal program output primitive count per input patch: " << (m_tessGenLevel*m_tessGenLevel*2 * geometryPrimitivesOutPerPrimitive) << "\n" 198 << tcu::TestLog::EndMessage; 199 } 200 201 void GridRenderTestCase::initPrograms (SourceCollections& programCollection) const 202 { 203 // Vertex shader 204 { 205 std::ostringstream src; 206 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 207 << "\n" 208 << "void main (void)\n" 209 << "{\n" 210 << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" 211 << "}\n"; 212 213 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 214 } 215 216 // Fragment shader 217 { 218 std::ostringstream src; 219 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 220 << "layout(location = 0) flat in highp vec4 v_color;\n" 221 << "layout(location = 0) out mediump vec4 fragColor;\n" 222 << "\n" 223 << "void main (void)\n" 224 << "{\n" 225 << " fragColor = v_color;\n" 226 << "}\n"; 227 228 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 229 } 230 231 // Tessellation control 232 { 233 std::ostringstream src; 234 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 235 "#extension GL_EXT_tessellation_shader : require\n" 236 "layout(vertices = 1) out;\n" 237 "\n" 238 "void main (void)\n" 239 "{\n" 240 " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" 241 " gl_TessLevelInner[0] = float(" << m_tessGenLevel << ");\n" 242 " gl_TessLevelInner[1] = float(" << m_tessGenLevel << ");\n" 243 " gl_TessLevelOuter[0] = float(" << m_tessGenLevel << ");\n" 244 " gl_TessLevelOuter[1] = float(" << m_tessGenLevel << ");\n" 245 " gl_TessLevelOuter[2] = float(" << m_tessGenLevel << ");\n" 246 " gl_TessLevelOuter[3] = float(" << m_tessGenLevel << ");\n" 247 "}\n"; 248 249 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 250 } 251 252 // Tessellation evaluation 253 { 254 std::ostringstream src; 255 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 256 << "#extension GL_EXT_tessellation_shader : require\n" 257 << "layout(quads) in;\n" 258 << "\n" 259 << "layout(location = 0) out mediump ivec2 v_tessellationGridPosition;\n" 260 << "\n" 261 << "// note: No need to use precise gl_Position since position does not depend on order\n" 262 << "void main (void)\n" 263 << "{\n"; 264 265 if (m_flags & (FLAG_GEOMETRY_SCATTER_INSTANCES | FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SCATTER_LAYERS)) 266 src << " // Cover only a small area in a corner. The area will be expanded in geometry shader to cover whole viewport\n" 267 << " gl_Position = vec4(gl_TessCoord.x * 0.3 - 1.0, gl_TessCoord.y * 0.3 - 1.0, 0.0, 1.0);\n"; 268 else 269 src << " // Fill the whole viewport\n" 270 << " gl_Position = vec4(gl_TessCoord.x * 2.0 - 1.0, gl_TessCoord.y * 2.0 - 1.0, 0.0, 1.0);\n"; 271 272 src << " // Calculate position in tessellation grid\n" 273 << " v_tessellationGridPosition = ivec2(round(gl_TessCoord.xy * float(" << m_tessGenLevel << ")));\n" 274 << "}\n"; 275 276 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 277 } 278 279 // Geometry shader 280 { 281 const int numInvocations = m_numGeometryInvocations; 282 const int numPrimitives = m_numGeometryPrimitivesPerInvocation; 283 284 std::ostringstream src; 285 286 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 287 << "#extension GL_EXT_geometry_shader : require\n" 288 << "layout(triangles, invocations = " << numInvocations << ") in;\n" 289 << "layout(triangle_strip, max_vertices = " << ((m_flags & FLAG_GEOMETRY_SEPARATE_PRIMITIVES) ? (4 * numPrimitives) : (numPrimitives + 2)) << ") out;\n" 290 << "\n" 291 << "layout(location = 0) in mediump ivec2 v_tessellationGridPosition[];\n" 292 << "layout(location = 0) flat out highp vec4 v_color;\n" 293 << "\n" 294 << "void main (void)\n" 295 << "{\n" 296 << " const float equalThreshold = 0.001;\n" 297 << " 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" 298 << "\n" 299 << " // Input triangle is generated from an axis-aligned rectangle by splitting it in half\n" 300 << " // Original rectangle can be found by finding the bounding AABB of the triangle\n" 301 << " vec4 aabb = vec4(min(gl_in[0].gl_Position.x, min(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n" 302 << " min(gl_in[0].gl_Position.y, min(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)),\n" 303 << " max(gl_in[0].gl_Position.x, max(gl_in[1].gl_Position.x, gl_in[2].gl_Position.x)),\n" 304 << " max(gl_in[0].gl_Position.y, max(gl_in[1].gl_Position.y, gl_in[2].gl_Position.y)));\n" 305 << "\n" 306 << " // Location in tessellation grid\n" 307 << " ivec2 gridPosition = ivec2(min(v_tessellationGridPosition[0], min(v_tessellationGridPosition[1], v_tessellationGridPosition[2])));\n" 308 << "\n" 309 << " // Which triangle of the two that split the grid cell\n" 310 << " int numVerticesOnBottomEdge = 0;\n" 311 << " for (int ndx = 0; ndx < 3; ++ndx)\n" 312 << " if (abs(gl_in[ndx].gl_Position.y - aabb.w) < equalThreshold)\n" 313 << " ++numVerticesOnBottomEdge;\n" 314 << " bool isBottomTriangle = numVerticesOnBottomEdge == 2;\n" 315 << "\n"; 316 317 if (m_flags & FLAG_GEOMETRY_SCATTER_PRIMITIVES) 318 { 319 // scatter primitives 320 src << " // Draw grid cells\n" 321 << " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n" 322 << " for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n" 323 << " {\n" 324 << " ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", 2 * " << m_tessGenLevel << " * " << numInvocations << ");\n" 325 << " ivec2 dstGridNdx = ivec2(" << m_tessGenLevel << " * ndx + gridPosition.x, " << m_tessGenLevel << " * inputTriangleNdx + 2 * gridPosition.y + ndx * 127) % dstGridSize;\n" 326 << " vec4 dstArea;\n" 327 << " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n" 328 << " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n" 329 << " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n" 330 << " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n" 331 << "\n" 332 << " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 333 << " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 334 << " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n" 335 << "\n" 336 << " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n" 337 << " v_color = outputColor;\n" 338 << " EmitVertex();\n" 339 << "\n" 340 << " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n" 341 << " v_color = outputColor;\n" 342 << " EmitVertex();\n" 343 << "\n" 344 << " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n" 345 << " v_color = outputColor;\n" 346 << " EmitVertex();\n" 347 << "\n" 348 << " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n" 349 << " v_color = outputColor;\n" 350 << " EmitVertex();\n" 351 << " EndPrimitive();\n" 352 << " }\n"; 353 } 354 else if (m_flags & FLAG_GEOMETRY_SCATTER_LAYERS) 355 { 356 // Number of subrectangle instances = num layers 357 DE_ASSERT(m_numLayers == numInvocations * 2); 358 359 src << " // Draw grid cells, send each primitive to a separate layer\n" 360 << " int baseLayer = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n" 361 << " for (int ndx = 0; ndx < " << numPrimitives << "; ++ndx)\n" 362 << " {\n" 363 << " ivec2 dstGridSize = ivec2(" << m_tessGenLevel << " * " << numPrimitives << ", " << m_tessGenLevel << ");\n" 364 << " ivec2 dstGridNdx = ivec2((gridPosition.x * " << numPrimitives << " * 7 + ndx)*13, (gridPosition.y * 127 + ndx) * 19) % dstGridSize;\n" 365 << " vec4 dstArea;\n" 366 << " dstArea.x = float(dstGridNdx.x) / float(dstGridSize.x) * 2.0 - 1.0 - gapOffset;\n" 367 << " dstArea.y = float(dstGridNdx.y) / float(dstGridSize.y) * 2.0 - 1.0 - gapOffset;\n" 368 << " dstArea.z = float(dstGridNdx.x+1) / float(dstGridSize.x) * 2.0 - 1.0 + gapOffset;\n" 369 << " dstArea.w = float(dstGridNdx.y+1) / float(dstGridSize.y) * 2.0 - 1.0 + gapOffset;\n" 370 << "\n" 371 << " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 372 << " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 373 << " vec4 outputColor = (((dstGridNdx.y + dstGridNdx.x) % 2) == 0) ? (green) : (yellow);\n" 374 << "\n" 375 << " gl_Position = vec4(dstArea.x, dstArea.y, 0.0, 1.0);\n" 376 << " v_color = outputColor;\n" 377 << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 378 << " EmitVertex();\n" 379 << "\n" 380 << " gl_Position = vec4(dstArea.x, dstArea.w, 0.0, 1.0);\n" 381 << " v_color = outputColor;\n" 382 << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 383 << " EmitVertex();\n" 384 << "\n" 385 << " gl_Position = vec4(dstArea.z, dstArea.y, 0.0, 1.0);\n" 386 << " v_color = outputColor;\n" 387 << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 388 << " EmitVertex();\n" 389 << "\n" 390 << " gl_Position = vec4(dstArea.z, dstArea.w, 0.0, 1.0);\n" 391 << " v_color = outputColor;\n" 392 << " gl_Layer = ((baseLayer + ndx) * 11) % " << m_numLayers << ";\n" 393 << " EmitVertex();\n" 394 << " EndPrimitive();\n" 395 << " }\n"; 396 } 397 else 398 { 399 if (m_flags & FLAG_GEOMETRY_SCATTER_INSTANCES) 400 { 401 src << " // Scatter slices\n" 402 << " int inputTriangleNdx = gl_InvocationID * 2 + ((isBottomTriangle) ? (1) : (0));\n" 403 << " ivec2 srcSliceNdx = ivec2(gridPosition.x, gridPosition.y * " << (numInvocations*2) << " + inputTriangleNdx);\n" 404 << " ivec2 dstSliceNdx = ivec2(7 * srcSliceNdx.x, 127 * srcSliceNdx.y) % ivec2(" << m_tessGenLevel << ", " << m_tessGenLevel << " * " << (numInvocations*2) << ");\n" 405 << "\n" 406 << " // Draw slice to the dstSlice slot\n" 407 << " vec4 outputSliceArea;\n" 408 << " outputSliceArea.x = float(dstSliceNdx.x) / float(" << m_tessGenLevel << ") * 2.0 - 1.0 - gapOffset;\n" 409 << " outputSliceArea.y = float(dstSliceNdx.y) / float(" << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 - gapOffset;\n" 410 << " outputSliceArea.z = float(dstSliceNdx.x+1) / float(" << m_tessGenLevel << ") * 2.0 - 1.0 + gapOffset;\n" 411 << " outputSliceArea.w = float(dstSliceNdx.y+1) / float(" << (m_tessGenLevel * numInvocations * 2) << ") * 2.0 - 1.0 + gapOffset;\n"; 412 } 413 else 414 { 415 src << " // Fill the input area with slices\n" 416 << " // Upper triangle produces slices only to the upper half of the quad and vice-versa\n" 417 << " float triangleOffset = (isBottomTriangle) ? ((aabb.w + aabb.y) / 2.0) : (aabb.y);\n" 418 << " // Each slice is a invocation\n" 419 << " float sliceHeight = (aabb.w - aabb.y) / float(2 * " << numInvocations << ");\n" 420 << " float invocationOffset = float(gl_InvocationID) * sliceHeight;\n" 421 << "\n" 422 << " vec4 outputSliceArea;\n" 423 << " outputSliceArea.x = aabb.x - gapOffset;\n" 424 << " outputSliceArea.y = triangleOffset + invocationOffset - gapOffset;\n" 425 << " outputSliceArea.z = aabb.z + gapOffset;\n" 426 << " outputSliceArea.w = triangleOffset + invocationOffset + sliceHeight + gapOffset;\n"; 427 } 428 429 src << "\n" 430 << " // Draw slice\n" 431 << " for (int ndx = 0; ndx < " << ((numPrimitives+2)/2) << "; ++ndx)\n" 432 << " {\n" 433 << " vec4 green = vec4(0.0, 1.0, 0.0, 1.0);\n" 434 << " vec4 yellow = vec4(1.0, 1.0, 0.0, 1.0);\n" 435 << " vec4 outputColor = (((gl_InvocationID + ndx) % 2) == 0) ? (green) : (yellow);\n" 436 << " float xpos = mix(outputSliceArea.x, outputSliceArea.z, float(ndx) / float(" << (numPrimitives/2) << "));\n" 437 << "\n" 438 << " gl_Position = vec4(xpos, outputSliceArea.y, 0.0, 1.0);\n" 439 << " v_color = outputColor;\n" 440 << " EmitVertex();\n" 441 << "\n" 442 << " gl_Position = vec4(xpos, outputSliceArea.w, 0.0, 1.0);\n" 443 << " v_color = outputColor;\n" 444 << " EmitVertex();\n" 445 << " }\n"; 446 } 447 448 src << "}\n"; 449 450 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str()); 451 } 452 } 453 454 class GridRenderTestInstance : public TestInstance 455 { 456 public: 457 struct Params 458 { 459 Flags flags; 460 int numLayers; 461 462 Params (void) : flags(), numLayers() {} 463 }; 464 GridRenderTestInstance (Context& context, const Params& params) : TestInstance(context), m_params(params) {} 465 tcu::TestStatus iterate (void); 466 467 private: 468 Params m_params; 469 }; 470 471 TestInstance* GridRenderTestCase::createInstance (Context& context) const 472 { 473 GridRenderTestInstance::Params params; 474 475 params.flags = m_flags; 476 params.numLayers = m_numLayers; 477 478 return new GridRenderTestInstance(context, params); 479 } 480 481 bool verifyResultLayer (tcu::TestLog& log, const tcu::ConstPixelBufferAccess& image, const int layerNdx) 482 { 483 tcu::Surface errorMask (image.getWidth(), image.getHeight()); 484 bool foundError = false; 485 486 tcu::clear(errorMask.getAccess(), tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); 487 488 log << tcu::TestLog::Message << "Verifying output layer " << layerNdx << tcu::TestLog::EndMessage; 489 490 for (int y = 0; y < image.getHeight(); ++y) 491 for (int x = 0; x < image.getWidth(); ++x) 492 { 493 const int threshold = 8; 494 const tcu::RGBA color (image.getPixel(x, y)); 495 496 // Color must be a linear combination of green and yellow 497 if (color.getGreen() < 255 - threshold || color.getBlue() > threshold) 498 { 499 errorMask.setPixel(x, y, tcu::RGBA::red()); 500 foundError = true; 501 } 502 } 503 504 if (!foundError) 505 { 506 log << tcu::TestLog::Message << "Image valid." << tcu::TestLog::EndMessage 507 << tcu::TestLog::ImageSet("ImageVerification", "Image verification") 508 << tcu::TestLog::Image("Result", "Rendered result", image) 509 << tcu::TestLog::EndImageSet; 510 return true; 511 } 512 else 513 { 514 log << tcu::TestLog::Message << "Image verification failed, found invalid pixels." << tcu::TestLog::EndMessage 515 << tcu::TestLog::ImageSet("ImageVerification", "Image verification") 516 << tcu::TestLog::Image("Result", "Rendered result", image) 517 << tcu::TestLog::Image("ErrorMask", "Error mask", errorMask.getAccess()) 518 << tcu::TestLog::EndImageSet; 519 return false; 520 } 521 } 522 523 tcu::TestStatus GridRenderTestInstance::iterate (void) 524 { 525 requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER); 526 527 m_context.getTestContext().getLog() 528 << tcu::TestLog::Message 529 << "Rendering single point at the origin. Expecting yellow and green colored grid-like image. (High-frequency grid may appear unicolored)." 530 << tcu::TestLog::EndMessage; 531 532 const DeviceInterface& vk = m_context.getDeviceInterface(); 533 const VkDevice device = m_context.getDevice(); 534 const VkQueue queue = m_context.getUniversalQueue(); 535 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); 536 Allocator& allocator = m_context.getDefaultAllocator(); 537 538 // Color attachment 539 540 const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE); 541 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM; 542 const VkImageSubresourceRange colorImageAllLayersRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, m_params.numLayers); 543 const VkImageCreateInfo colorImageCreateInfo = makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, m_params.numLayers); 544 const VkImageViewType colorAttachmentViewType = (m_params.numLayers == 1 ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY); 545 const Image colorAttachmentImage (vk, device, allocator, colorImageCreateInfo, MemoryRequirement::Any); 546 547 // Color output buffer: image will be copied here for verification (big enough for all layers). 548 549 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * m_params.numLayers * tcu::getPixelSize(mapVkFormat(colorFormat)); 550 const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible); 551 552 // Pipeline: no vertex input attributes nor descriptors. 553 554 const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, colorAttachmentViewType, colorFormat, colorImageAllLayersRange)); 555 const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat)); 556 const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), m_params.numLayers)); 557 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device)); 558 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 559 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 560 561 const Unique<VkPipeline> pipeline (GraphicsPipelineBuilder() 562 .setRenderSize (renderSize) 563 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 564 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL) 565 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 566 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL) 567 .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get("geom"), DE_NULL) 568 .build (vk, device, *pipelineLayout, *renderPass)); 569 570 beginCommandBuffer(vk, *cmdBuffer); 571 572 // Change color attachment image layout 573 { 574 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier( 575 (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, 576 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 577 *colorAttachmentImage, colorImageAllLayersRange); 578 579 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u, 580 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier); 581 } 582 583 // Begin render pass 584 { 585 const VkRect2D renderArea = { 586 makeOffset2D(0, 0), 587 makeExtent2D(renderSize.x(), renderSize.y()), 588 }; 589 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f); 590 591 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor); 592 } 593 594 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 595 596 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u); 597 endRenderPass(vk, *cmdBuffer); 598 599 // Copy render result to a host-visible buffer 600 { 601 const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier( 602 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT, 603 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 604 *colorAttachmentImage, colorImageAllLayersRange); 605 606 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u, 607 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier); 608 } 609 { 610 const VkImageSubresourceLayers subresourceLayers = makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, m_params.numLayers); 611 const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), subresourceLayers); 612 vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region); 613 } 614 { 615 const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier( 616 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes); 617 618 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 619 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL); 620 } 621 622 endCommandBuffer(vk, *cmdBuffer); 623 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 624 625 // Verify results 626 { 627 const Allocation& alloc = colorBuffer.getAllocation(); 628 invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes); 629 630 const tcu::ConstPixelBufferAccess imageAllLayers(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), m_params.numLayers, alloc.getHostPtr()); 631 632 bool allOk = true; 633 for (int ndx = 0; ndx < m_params.numLayers; ++ndx) 634 allOk = allOk && verifyResultLayer(m_context.getTestContext().getLog(), 635 tcu::getSubregion(imageAllLayers, 0, 0, ndx, renderSize.x(), renderSize.y(), 1), 636 ndx); 637 638 return (allOk ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed")); 639 } 640 } 641 642 struct TestCaseDescription 643 { 644 const char* name; 645 const char* desc; 646 Flags flags; 647 }; 648 649 } // anonymous 650 651 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.limits.* 652 //! \note Tests that check implementation defined limits were omitted, because they rely on runtime shader source generation 653 //! (e.g. changing the number of vertices output from geometry shader). CTS currently doesn't support that, 654 //! because some platforms require precompiled shaders. 655 tcu::TestCaseGroup* createGeometryGridRenderLimitsTests (tcu::TestContext& testCtx) 656 { 657 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "limits", "Render with properties near their limits")); 658 659 static const TestCaseDescription cases[] = 660 { 661 { 662 "output_required_max_tessellation", 663 "Minimum maximum tessellation level", 664 FLAG_TESSELLATION_MAX_SPEC 665 }, 666 { 667 "output_required_max_geometry", 668 "Output minimum maximum number of vertices the geometry shader", 669 FLAG_GEOMETRY_MAX_SPEC 670 }, 671 { 672 "output_required_max_invocations", 673 "Minimum maximum number of geometry shader invocations", 674 FLAG_GEOMETRY_INVOCATIONS_MAX_SPEC 675 }, 676 }; 677 678 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx) 679 group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].desc, cases[ndx].flags)); 680 681 return group.release(); 682 } 683 684 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.render.scatter.* 685 tcu::TestCaseGroup* createGeometryGridRenderScatterTests (tcu::TestContext& testCtx) 686 { 687 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "scatter", "Scatter output primitives")); 688 689 static const TestCaseDescription cases[] = 690 { 691 { 692 "geometry_scatter_instances", 693 "Each geometry shader instance outputs its primitives far from other instances of the same execution", 694 FLAG_GEOMETRY_SCATTER_INSTANCES 695 }, 696 { 697 "geometry_scatter_primitives", 698 "Each geometry shader instance outputs its primitives far from other primitives of the same instance", 699 FLAG_GEOMETRY_SCATTER_PRIMITIVES | FLAG_GEOMETRY_SEPARATE_PRIMITIVES 700 }, 701 { 702 "geometry_scatter_layers", 703 "Each geometry shader instance outputs its primitives to multiple layers and far from other primitives of the same instance", 704 FLAG_GEOMETRY_SCATTER_LAYERS | FLAG_GEOMETRY_SEPARATE_PRIMITIVES 705 }, 706 }; 707 708 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(cases); ++ndx) 709 group->addChild(new GridRenderTestCase(testCtx, cases[ndx].name, cases[ndx].desc, cases[ndx].flags)); 710 711 return group.release(); 712 } 713 714 } // tessellation 715 } // vkt 716