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 Coordinates Tests 23 *//*--------------------------------------------------------------------*/ 24 25 #include "vktTessellationCoordinatesTests.hpp" 26 #include "vktTestCaseUtil.hpp" 27 #include "vktTessellationUtil.hpp" 28 29 #include "tcuTestLog.hpp" 30 #include "tcuRGBA.hpp" 31 #include "tcuSurface.hpp" 32 #include "tcuTextureUtil.hpp" 33 #include "tcuVectorUtil.hpp" 34 35 #include "vkDefs.hpp" 36 #include "vkQueryUtil.hpp" 37 #include "vkBuilderUtil.hpp" 38 #include "vkTypeUtil.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 template <typename T> 56 class SizeLessThan 57 { 58 public: 59 bool operator() (const T& a, const T& b) const { return a.size() < b.size(); } 60 }; 61 62 std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode) 63 { 64 std::ostringstream str; 65 str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode); 66 return str.str(); 67 } 68 69 std::vector<TessLevels> genTessLevelCases (const TessPrimitiveType primitiveType, 70 const SpacingMode spacingMode) 71 { 72 static const TessLevels rawTessLevelCases[] = 73 { 74 { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 75 { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } }, 76 { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 77 { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 78 { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } }, 79 { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } }, 80 { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 81 { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } }, 82 { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } } 83 }; 84 85 if (spacingMode == SPACINGMODE_EQUAL) 86 return std::vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases)); 87 else 88 { 89 std::vector<TessLevels> result; 90 result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases)); 91 92 for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); ++tessLevelCaseNdx) 93 { 94 TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx]; 95 96 float* const inner = &curTessLevelCase.inner[0]; 97 float* const outer = &curTessLevelCase.outer[0]; 98 99 for (int j = 0; j < 2; ++j) inner[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[j])); 100 for (int j = 0; j < 4; ++j) outer[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, outer[j])); 101 102 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 103 { 104 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f) 105 { 106 if (inner[0] == 1.0f) 107 inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f)); 108 } 109 } 110 else if (primitiveType == TESSPRIMITIVETYPE_QUADS) 111 { 112 if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f) 113 { 114 if (inner[0] == 1.0f) inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f)); 115 if (inner[1] == 1.0f) inner[1] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[1] + 0.1f)); 116 } 117 } 118 119 result.push_back(curTessLevelCase); 120 } 121 122 DE_ASSERT(static_cast<int>(result.size()) == DE_LENGTH_OF_ARRAY(rawTessLevelCases)); 123 return result; 124 } 125 } 126 127 std::vector<tcu::Vec3> generateReferenceTessCoords (const TessPrimitiveType primitiveType, 128 const SpacingMode spacingMode, 129 const float* innerLevels, 130 const float* outerLevels) 131 { 132 if (isPatchDiscarded(primitiveType, outerLevels)) 133 return std::vector<tcu::Vec3>(); 134 135 switch (primitiveType) 136 { 137 case TESSPRIMITIVETYPE_TRIANGLES: 138 { 139 int inner; 140 int outer[3]; 141 getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]); 142 143 if (spacingMode != SPACINGMODE_EQUAL) 144 { 145 // \note For fractional spacing modes, exact results are implementation-defined except in special cases. 146 DE_ASSERT(de::abs(innerLevels[0] - static_cast<float>(inner)) < 0.001f); 147 for (int i = 0; i < 3; ++i) 148 DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f); 149 DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1)); 150 } 151 152 return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]); 153 } 154 155 case TESSPRIMITIVETYPE_QUADS: 156 { 157 int inner[2]; 158 int outer[4]; 159 getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]); 160 161 if (spacingMode != SPACINGMODE_EQUAL) 162 { 163 // \note For fractional spacing modes, exact results are implementation-defined except in special cases. 164 for (int i = 0; i < 2; ++i) 165 DE_ASSERT(de::abs(innerLevels[i] - static_cast<float>(inner[i])) < 0.001f); 166 for (int i = 0; i < 4; ++i) 167 DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f); 168 169 DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1)); 170 } 171 172 return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]); 173 } 174 175 case TESSPRIMITIVETYPE_ISOLINES: 176 { 177 int outer[2]; 178 getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]); 179 180 if (spacingMode != SPACINGMODE_EQUAL) 181 { 182 // \note For fractional spacing modes, exact results are implementation-defined except in special cases. 183 DE_ASSERT(de::abs(outerLevels[1] - static_cast<float>(outer[1])) < 0.001f); 184 } 185 186 return generateReferenceIsolineTessCoords(outer[0], outer[1]); 187 } 188 189 default: 190 DE_ASSERT(false); 191 return std::vector<tcu::Vec3>(); 192 } 193 } 194 195 void drawPoint (tcu::Surface& dst, const int centerX, const int centerY, const tcu::RGBA& color, const int size) 196 { 197 const int width = dst.getWidth(); 198 const int height = dst.getHeight(); 199 DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height)); 200 DE_ASSERT(size > 0); 201 202 for (int yOff = -((size-1)/2); yOff <= size/2; ++yOff) 203 for (int xOff = -((size-1)/2); xOff <= size/2; ++xOff) 204 { 205 const int pixX = centerX + xOff; 206 const int pixY = centerY + yOff; 207 if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height)) 208 dst.setPixel(pixX, pixY, color); 209 } 210 } 211 212 void drawTessCoordPoint (tcu::Surface& dst, const TessPrimitiveType primitiveType, const tcu::Vec3& pt, const tcu::RGBA& color, const int size) 213 { 214 // \note These coordinates should match the description in the log message in TessCoordTestInstance::iterate. 215 216 static const tcu::Vec2 triangleCorners[3] = 217 { 218 tcu::Vec2(0.95f, 0.95f), 219 tcu::Vec2(0.5f, 0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)), 220 tcu::Vec2(0.05f, 0.95f) 221 }; 222 223 static const float quadIsolineLDRU[4] = 224 { 225 0.1f, 0.9f, 0.9f, 0.1f 226 }; 227 228 const tcu::Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0] 229 + pt.y()*triangleCorners[1] 230 + pt.z()*triangleCorners[2] 231 232 : primitiveType == TESSPRIMITIVETYPE_QUADS || 233 primitiveType == TESSPRIMITIVETYPE_ISOLINES ? tcu::Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2], 234 (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3]) 235 236 : tcu::Vec2(-1.0f); 237 238 drawPoint(dst, 239 static_cast<int>(dstPos.x() * (float)dst.getWidth()), 240 static_cast<int>(dstPos.y() * (float)dst.getHeight()), 241 color, 242 size); 243 } 244 245 void drawTessCoordVisualization (tcu::Surface& dst, const TessPrimitiveType primitiveType, const std::vector<tcu::Vec3>& coords) 246 { 247 const int imageWidth = 256; 248 const int imageHeight = 256; 249 dst.setSize(imageWidth, imageHeight); 250 251 tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); 252 253 for (int i = 0; i < static_cast<int>(coords.size()); ++i) 254 drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2); 255 } 256 257 inline bool vec3XLessThan (const tcu::Vec3& a, const tcu::Vec3& b) 258 { 259 return a.x() < b.x(); 260 } 261 262 int binarySearchFirstVec3WithXAtLeast (const std::vector<tcu::Vec3>& sorted, float x) 263 { 264 const tcu::Vec3 ref(x, 0.0f, 0.0f); 265 const std::vector<tcu::Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan); 266 if (first == sorted.end()) 267 return -1; 268 return static_cast<int>(std::distance(sorted.begin(), first)); 269 } 270 271 // Check that all points in subset are (approximately) present also in superset. 272 bool oneWayComparePointSets (tcu::TestLog& log, 273 tcu::Surface& errorDst, 274 const TessPrimitiveType primitiveType, 275 const std::vector<tcu::Vec3>& subset, 276 const std::vector<tcu::Vec3>& superset, 277 const char* subsetName, 278 const char* supersetName, 279 const tcu::RGBA& errorColor) 280 { 281 const std::vector<tcu::Vec3> supersetSorted = sorted(superset, vec3XLessThan); 282 const float epsilon = 0.01f; 283 const int maxNumFailurePrints = 5; 284 int numFailuresDetected = 0; 285 286 for (int subNdx = 0; subNdx < static_cast<int>(subset.size()); ++subNdx) 287 { 288 const tcu::Vec3& subPt = subset[subNdx]; 289 290 bool matchFound = false; 291 292 { 293 // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range. 294 const tcu::Vec3 matchMin = subPt - epsilon; 295 const tcu::Vec3 matchMax = subPt + epsilon; 296 const int firstCandidateNdx = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x()); 297 298 if (firstCandidateNdx >= 0) 299 { 300 // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range. 301 for (int superNdx = firstCandidateNdx; superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x(); ++superNdx) 302 { 303 const tcu::Vec3& superPt = supersetSorted[superNdx]; 304 305 if (tcu::boolAll(tcu::greaterThanEqual (superPt, matchMin)) && 306 tcu::boolAll(tcu::lessThanEqual (superPt, matchMax))) 307 { 308 matchFound = true; 309 break; 310 } 311 } 312 } 313 } 314 315 if (!matchFound) 316 { 317 ++numFailuresDetected; 318 if (numFailuresDetected < maxNumFailurePrints) 319 log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << tcu::TestLog::EndMessage; 320 else if (numFailuresDetected == maxNumFailurePrints) 321 log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage; 322 323 drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4); 324 } 325 } 326 327 return numFailuresDetected == 0; 328 } 329 330 //! Returns true on matching coordinate sets. 331 bool compareTessCoords (tcu::TestLog& log, 332 TessPrimitiveType primitiveType, 333 const std::vector<tcu::Vec3>& refCoords, 334 const std::vector<tcu::Vec3>& resCoords) 335 { 336 tcu::Surface refVisual; 337 tcu::Surface resVisual; 338 bool success = true; 339 340 drawTessCoordVisualization(refVisual, primitiveType, refCoords); 341 drawTessCoordVisualization(resVisual, primitiveType, resCoords); 342 343 // Check that all points in reference also exist in result. 344 success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success; 345 // Check that all points in result also exist in reference. 346 success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success; 347 348 if (!success) 349 { 350 log << tcu::TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << tcu::TestLog::EndMessage 351 << tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual) 352 << tcu::TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << tcu::TestLog::EndMessage; 353 } 354 355 log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual); 356 357 return success; 358 } 359 360 class TessCoordTest : public TestCase 361 { 362 public: 363 TessCoordTest (tcu::TestContext& testCtx, 364 const TessPrimitiveType primitiveType, 365 const SpacingMode spacingMode); 366 367 void initPrograms (SourceCollections& programCollection) const; 368 TestInstance* createInstance (Context& context) const; 369 370 private: 371 const TessPrimitiveType m_primitiveType; 372 const SpacingMode m_spacingMode; 373 }; 374 375 TessCoordTest::TessCoordTest (tcu::TestContext& testCtx, 376 const TessPrimitiveType primitiveType, 377 const SpacingMode spacingMode) 378 : TestCase (testCtx, getCaseName(primitiveType, spacingMode), "") 379 , m_primitiveType (primitiveType) 380 , m_spacingMode (spacingMode) 381 { 382 } 383 384 void TessCoordTest::initPrograms (SourceCollections& programCollection) const 385 { 386 // Vertex shader - no inputs 387 { 388 std::ostringstream src; 389 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 390 << "\n" 391 << "void main (void)\n" 392 << "{\n" 393 << "}\n"; 394 395 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 396 } 397 398 // Tessellation control shader 399 { 400 std::ostringstream src; 401 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 402 << "#extension GL_EXT_tessellation_shader : require\n" 403 << "\n" 404 << "layout(vertices = 1) out;\n" 405 << "\n" 406 << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n" 407 << " float inner0;\n" 408 << " float inner1;\n" 409 << " float outer0;\n" 410 << " float outer1;\n" 411 << " float outer2;\n" 412 << " float outer3;\n" 413 << "} sb_levels;\n" 414 << "\n" 415 << "void main (void)\n" 416 << "{\n" 417 << " gl_TessLevelInner[0] = sb_levels.inner0;\n" 418 << " gl_TessLevelInner[1] = sb_levels.inner1;\n" 419 << "\n" 420 << " gl_TessLevelOuter[0] = sb_levels.outer0;\n" 421 << " gl_TessLevelOuter[1] = sb_levels.outer1;\n" 422 << " gl_TessLevelOuter[2] = sb_levels.outer2;\n" 423 << " gl_TessLevelOuter[3] = sb_levels.outer3;\n" 424 << "}\n"; 425 426 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 427 } 428 429 // Tessellation evaluation shader 430 { 431 std::ostringstream src; 432 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 433 << "#extension GL_EXT_tessellation_shader : require\n" 434 << "\n" 435 << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", " 436 << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n" 437 << "\n" 438 << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n" 439 << " int numInvocations;\n" 440 << " vec3 tessCoord[];\n" // alignment is 16 bytes, same as vec4 441 << "} sb_out;\n" 442 << "\n" 443 << "void main (void)\n" 444 << "{\n" 445 << " int index = atomicAdd(sb_out.numInvocations, 1);\n" 446 << " sb_out.tessCoord[index] = gl_TessCoord;\n" 447 << "}\n"; 448 449 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 450 } 451 } 452 453 class TessCoordTestInstance : public TestInstance 454 { 455 public: 456 TessCoordTestInstance (Context& context, 457 const TessPrimitiveType primitiveType, 458 const SpacingMode spacingMode); 459 460 tcu::TestStatus iterate (void); 461 462 private: 463 const TessPrimitiveType m_primitiveType; 464 const SpacingMode m_spacingMode; 465 }; 466 467 TessCoordTestInstance::TessCoordTestInstance (Context& context, 468 const TessPrimitiveType primitiveType, 469 const SpacingMode spacingMode) 470 : TestInstance (context) 471 , m_primitiveType (primitiveType) 472 , m_spacingMode (spacingMode) 473 { 474 } 475 476 tcu::TestStatus TessCoordTestInstance::iterate (void) 477 { 478 const DeviceInterface& vk = m_context.getDeviceInterface(); 479 const VkDevice device = m_context.getDevice(); 480 const VkQueue queue = m_context.getUniversalQueue(); 481 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); 482 Allocator& allocator = m_context.getDefaultAllocator(); 483 484 // Test data 485 486 const std::vector<TessLevels> tessLevelCases = genTessLevelCases(m_primitiveType, m_spacingMode); 487 std::vector<std::vector<tcu::Vec3> > allReferenceTessCoords (tessLevelCases.size()); 488 489 for (deUint32 i = 0; i < tessLevelCases.size(); ++i) 490 allReferenceTessCoords[i] = generateReferenceTessCoords(m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]); 491 492 const size_t maxNumVertices = static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(), SizeLessThan<std::vector<tcu::Vec3> >())->size()); 493 494 // Input buffer: tessellation levels. Data is filled in later. 495 496 const Buffer tessLevelsBuffer(vk, device, allocator, 497 makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 498 499 // Output buffer: number of invocations + padding + tessellation coordinates. Initialized later. 500 501 const int resultBufferTessCoordsOffset = 4 * (int)sizeof(deInt32); 502 const int extraneousVertices = 16; // allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary) 503 const VkDeviceSize resultBufferSizeBytes = resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices)*sizeof(tcu::Vec4); 504 const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 505 506 // Descriptors 507 508 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder() 509 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) 510 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) 511 .build(vk, device)); 512 513 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder() 514 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 515 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 516 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); 517 518 const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); 519 520 const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, sizeof(TessLevels)); 521 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes); 522 523 DescriptorSetUpdateBuilder() 524 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo) 525 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo) 526 .update(vk, device); 527 528 // Pipeline: set up vertex processing without rasterization 529 530 const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device)); 531 const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass)); 532 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout)); 533 const Unique<VkCommandPool> cmdPool (makeCommandPool(vk, device, queueFamilyIndex)); 534 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 535 536 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder() 537 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL) 538 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL) 539 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL) 540 .build (vk, device, *pipelineLayout, *renderPass)); 541 542 deUint32 numPassedCases = 0; 543 544 // Repeat the test for all tessellation coords cases 545 for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx) 546 { 547 m_context.getTestContext().getLog() 548 << tcu::TestLog::Message 549 << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType) 550 << tcu::TestLog::EndMessage; 551 552 // Upload tessellation levels data to the input buffer 553 { 554 const Allocation& alloc = tessLevelsBuffer.getAllocation(); 555 TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr()); 556 *bufferTessLevels = tessLevelCases[tessLevelCaseNdx]; 557 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels)); 558 } 559 560 // Clear the results buffer 561 { 562 const Allocation& alloc = resultBuffer.getAllocation(); 563 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes)); 564 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes); 565 } 566 567 // Reset the command buffer and begin recording. 568 beginCommandBuffer(vk, *cmdBuffer); 569 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer); 570 571 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 572 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); 573 574 // Process a single abstract vertex. 575 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u); 576 endRenderPass(vk, *cmdBuffer); 577 578 { 579 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( 580 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes); 581 582 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 583 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL); 584 } 585 586 endCommandBuffer(vk, *cmdBuffer); 587 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 588 589 // Verify results 590 { 591 const Allocation& resultAlloc = resultBuffer.getAllocation(); 592 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes); 593 594 const deInt32 numResults = *static_cast<deInt32*>(resultAlloc.getHostPtr()); 595 const std::vector<tcu::Vec3> resultTessCoords = readInterleavedData<tcu::Vec3>(numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4)); 596 const std::vector<tcu::Vec3>& referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx]; 597 const int numExpectedResults = static_cast<int>(referenceTessCoords.size()); 598 tcu::TestLog& log = m_context.getTestContext().getLog(); 599 600 if (numResults < numExpectedResults) 601 { 602 log << tcu::TestLog::Message 603 << "Failure: generated " << numResults << " coordinates, but the expected reference value is " << numExpectedResults 604 << tcu::TestLog::EndMessage; 605 } 606 else if (numResults == numExpectedResults) 607 log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates" << tcu::TestLog::EndMessage; 608 else 609 { 610 log << tcu::TestLog::Message 611 << "Note: generated " << numResults << " coordinates (out of which " << numExpectedResults << " must be unique)" 612 << tcu::TestLog::EndMessage; 613 } 614 615 if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES) 616 log << tcu::TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << tcu::TestLog::EndMessage; 617 else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES) 618 log << tcu::TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << tcu::TestLog::EndMessage; 619 else 620 DE_ASSERT(false); 621 622 if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) && (numResults >= numExpectedResults)) 623 ++numPassedCases; 624 } 625 } // for tessLevelCaseNdx 626 627 return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Some cases have failed")); 628 } 629 630 TestInstance* TessCoordTest::createInstance (Context& context) const 631 { 632 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); 633 634 return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode); 635 } 636 637 } // anonymous 638 639 //! Based on dEQP-GLES31.functional.tessellation.tesscoord.* 640 //! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations. 641 //! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data. 642 tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx) 643 { 644 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "tesscoord", "Tessellation coordinates tests")); 645 646 for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx) 647 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx) 648 group->addChild(new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx)); 649 650 return group.release(); 651 } 652 653 } // tessellation 654 } // vkt 655