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 Fractional Spacing Tests 23 *//*--------------------------------------------------------------------*/ 24 25 #include "vktTessellationFractionalSpacingTests.hpp" 26 #include "vktTestCaseUtil.hpp" 27 #include "vktTessellationUtil.hpp" 28 29 #include "tcuTestLog.hpp" 30 31 #include "vkDefs.hpp" 32 #include "vkBarrierUtil.hpp" 33 #include "vkQueryUtil.hpp" 34 #include "vkBuilderUtil.hpp" 35 #include "vkTypeUtil.hpp" 36 #include "vkCmdUtil.hpp" 37 #include "vkObjUtil.hpp" 38 39 #include "deUniquePtr.hpp" 40 #include "deStringUtil.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, typename MembT> 56 std::vector<MembT> members (const std::vector<T>& objs, MembT T::* membP) 57 { 58 std::vector<MembT> result(objs.size()); 59 for (int i = 0; i < static_cast<int>(objs.size()); ++i) 60 result[i] = objs[i].*membP; 61 return result; 62 } 63 64 //! Predicate functor for comparing structs by their members. 65 template <typename Pred, typename T, typename MembT> 66 class MemberPred 67 { 68 public: 69 MemberPred (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {} 70 bool operator() (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); } 71 72 private: 73 MembT T::* m_membP; 74 Pred m_pred; 75 }; 76 77 //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments. 78 template <template <typename> class Pred, typename T, typename MembT> 79 inline MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); } 80 81 struct Segment 82 { 83 int index; //!< Index of left coordinate in sortedXCoords. 84 float length; 85 86 Segment (void) : index(-1), length(-1.0f) {} 87 Segment (int index_, float length_) : index(index_), length(length_) {} 88 }; 89 90 inline std::vector<float> lengths (const std::vector<Segment>& segments) { return members(segments, &Segment::length); } 91 92 struct LineData 93 { 94 float tessLevel; 95 float additionalSegmentLength; 96 int additionalSegmentLocation; 97 98 LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {} 99 }; 100 101 struct TestParams 102 { 103 ShaderLanguage shaderLanguage; 104 SpacingMode spacingMode; 105 106 TestParams(ShaderLanguage sl, SpacingMode sm) : shaderLanguage(sl), spacingMode(sm) {} 107 }; 108 109 /*--------------------------------------------------------------------*//*! 110 * \brief Verify fractional spacing conditions for a single line 111 * 112 * Verify that the splitting of an edge (resulting from e.g. an isoline 113 * with outer levels { 1.0, tessLevel }) with a given fractional spacing 114 * mode fulfills certain conditions given in the spec. 115 * 116 * Note that some conditions can't be checked from just one line 117 * (specifically, that the additional segment decreases monotonically 118 * length and the requirement that the additional segments be placed 119 * identically for identical values of clamped level). 120 * 121 * Therefore, the function stores some values to additionalSegmentLengthDst 122 * and additionalSegmentLocationDst that can later be given to 123 * verifyFractionalSpacingMultiple(). A negative value in length means that 124 * no additional segments are present, i.e. there's just one segment. 125 * A negative value in location means that the value wasn't determinable, 126 * i.e. all segments had same length. 127 * The values are not stored if false is returned. 128 *//*--------------------------------------------------------------------*/ 129 bool verifyFractionalSpacingSingle (tcu::TestLog& log, 130 const SpacingMode spacingMode, 131 const float tessLevel, 132 const std::vector<float>& coords, 133 float* const pOutAdditionalSegmentLength, 134 int* const pOutAdditionalSegmentLocation) 135 { 136 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN); 137 138 const float clampedLevel = getClampedTessLevel(spacingMode, tessLevel); 139 const int finalLevel = getRoundedTessLevel(spacingMode, clampedLevel); 140 const std::vector<float> sortedCoords = sorted(coords); 141 std::string failNote = "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n " + containerStr(sortedCoords); 142 143 if (static_cast<int>(coords.size()) != finalLevel + 1) 144 { 145 log << tcu::TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1 146 << " (clamped tessellation level is " << clampedLevel << ")" 147 << "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel 148 << " and should equal the number of segments, i.e. number of vertices minus 1" << tcu::TestLog::EndMessage 149 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 150 return false; 151 } 152 153 if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f) 154 { 155 log << tcu::TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << tcu::TestLog::EndMessage 156 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 157 return false; 158 } 159 160 { 161 std::vector<Segment> segments(finalLevel); 162 for (int i = 0; i < finalLevel; ++i) 163 segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]); 164 165 failNote += "\nNote: segment lengths are, from left to right:\n " + containerStr(lengths(segments)); 166 167 { 168 // Divide segments to two different groups based on length. 169 170 std::vector<Segment> segmentsA; 171 std::vector<Segment> segmentsB; 172 segmentsA.push_back(segments[0]); 173 174 for (int segNdx = 1; segNdx < static_cast<int>(segments.size()); ++segNdx) 175 { 176 const float epsilon = 0.001f; 177 const Segment& seg = segments[segNdx]; 178 179 if (de::abs(seg.length - segmentsA[0].length) < epsilon) 180 segmentsA.push_back(seg); 181 else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon) 182 segmentsB.push_back(seg); 183 else 184 { 185 log << tcu::TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; " 186 << "e.g. segment of length " << seg.length << " isn't approximately equal to either " 187 << segmentsA[0].length << " or " << segmentsB[0].length << tcu::TestLog::EndMessage 188 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 189 return false; 190 } 191 } 192 193 if (clampedLevel == static_cast<float>(finalLevel)) 194 { 195 // All segments should be of equal length. 196 if (!segmentsA.empty() && !segmentsB.empty()) 197 { 198 log << tcu::TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << tcu::TestLog::EndMessage 199 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 200 return false; 201 } 202 } 203 204 if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok. 205 { 206 *pOutAdditionalSegmentLength = (segments.size() == 1 ? -1.0f : segments[0].length); 207 *pOutAdditionalSegmentLocation = -1; 208 return true; 209 } 210 211 if (segmentsA.size() != 2 && segmentsB.size() != 2) 212 { 213 log << tcu::TestLog::Message << "Failure: when dividing the segments to 2 groups by length, neither of the two groups has exactly 2 or 0 segments in it" << tcu::TestLog::EndMessage 214 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 215 return false; 216 } 217 218 // For convenience, arrange so that the 2-segment group is segmentsB. 219 if (segmentsB.size() != 2) 220 std::swap(segmentsA, segmentsB); 221 222 // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each. 223 // Thus, we can't be sure which ones were meant as the additional segments. 224 // We give the benefit of the doubt by assuming that they're the shorter 225 // ones (as they should). 226 227 if (segmentsA.size() != 2) 228 { 229 if (segmentsB[0].length > segmentsA[0].length + 0.001f) 230 { 231 log << tcu::TestLog::Message << "Failure: the two additional segments are longer than the other segments" << tcu::TestLog::EndMessage 232 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 233 return false; 234 } 235 } 236 else 237 { 238 // We have 2 segmentsA and 2 segmentsB, ensure segmentsB has the shorter lengths 239 if (segmentsB[0].length > segmentsA[0].length) 240 std::swap(segmentsA, segmentsB); 241 } 242 243 // Check that the additional segments are placed symmetrically. 244 if (segmentsB[0].index + segmentsB[1].index + 1 != static_cast<int>(segments.size())) 245 { 246 log << tcu::TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; " 247 << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index 248 << " (note: the two indexes should sum to " << static_cast<int>(segments.size())-1 << ", i.e. numberOfSegments-1)" << tcu::TestLog::EndMessage 249 << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; 250 return false; 251 } 252 253 *pOutAdditionalSegmentLength = segmentsB[0].length; 254 if (segmentsA.size() != 2) 255 *pOutAdditionalSegmentLocation = de::min(segmentsB[0].index, segmentsB[1].index); 256 else 257 *pOutAdditionalSegmentLocation = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index) 258 : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b. 259 260 return true; 261 } 262 } 263 } 264 265 /*--------------------------------------------------------------------*//*! 266 * \brief Verify fractional spacing conditions between multiple lines 267 * 268 * Verify the fractional spacing conditions that are not checked in 269 * verifyFractionalSpacingSingle(). Uses values given by said function 270 * as parameters, in addition to the spacing mode and tessellation level. 271 *//*--------------------------------------------------------------------*/ 272 static bool verifyFractionalSpacingMultiple (tcu::TestLog& log, 273 const SpacingMode spacingMode, 274 const std::vector<float>& tessLevels, 275 const std::vector<float>& additionalSegmentLengths, 276 const std::vector<int>& additionalSegmentLocations) 277 { 278 DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN); 279 DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() && tessLevels.size() == additionalSegmentLocations.size()); 280 281 std::vector<LineData> lineDatas; 282 283 for (int i = 0; i < static_cast<int>(tessLevels.size()); ++i) 284 lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i])); 285 286 { 287 const std::vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel)); 288 289 // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation. 290 291 for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx) 292 { 293 const LineData& curData = lineDatasSortedByLevel[lineNdx]; 294 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1]; 295 296 if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0) 297 continue; // Unknown locations, skip. 298 299 if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) && 300 curData.additionalSegmentLocation != prevData.additionalSegmentLocation) 301 { 302 log << tcu::TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << tcu::TestLog::EndMessage 303 << tcu::TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel 304 << " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")" 305 << "; but first additional segments located at indices " 306 << curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << tcu::TestLog::EndMessage; 307 return false; 308 } 309 } 310 311 // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction"). 312 313 for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx) 314 { 315 const LineData& curData = lineDatasSortedByLevel[lineNdx]; 316 const LineData& prevData = lineDatasSortedByLevel[lineNdx-1]; 317 318 if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f) 319 continue; // Unknown segment lengths, skip. 320 321 const float curClampedLevel = getClampedTessLevel(spacingMode, curData.tessLevel); 322 const float prevClampedLevel = getClampedTessLevel(spacingMode, prevData.tessLevel); 323 const int curFinalLevel = getRoundedTessLevel(spacingMode, curClampedLevel); 324 const int prevFinalLevel = getRoundedTessLevel(spacingMode, prevClampedLevel); 325 326 if (curFinalLevel != prevFinalLevel) 327 continue; 328 329 const float curFraction = static_cast<float>(curFinalLevel) - curClampedLevel; 330 const float prevFraction = static_cast<float>(prevFinalLevel) - prevClampedLevel; 331 332 if (curData.additionalSegmentLength < prevData.additionalSegmentLength || 333 (curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength)) 334 { 335 log << tcu::TestLog::Message << "Failure: additional segment length isn't monotonically decreasing with the fraction <n> - <f>, among edges with same final tessellation level" << tcu::TestLog::EndMessage 336 << tcu::TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << tcu::TestLog::EndMessage 337 << tcu::TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively" 338 << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel 339 << "; fractions are " << prevFraction << " and " << curFraction 340 << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << tcu::TestLog::EndMessage; 341 return false; 342 } 343 } 344 } 345 346 return true; 347 } 348 349 std::vector<float> genTessLevelCases (void) 350 { 351 std::vector<float> result; 352 353 // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0) 354 { 355 static const float rangeStarts[] = { 7.0f, 8.0f, 9.0f }; 356 const int numSamplesPerRange = 10; 357 358 for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); ++rangeNdx) 359 for (int i = 0; i < numSamplesPerRange; ++i) 360 result.push_back(rangeStarts[rangeNdx] + static_cast<float>(i)/numSamplesPerRange); 361 } 362 363 // 0.3, 1.3, 2.3, ... , 62.3 364 for (int i = 0; i <= 62; ++i) 365 result.push_back(static_cast<float>(i) + 0.3f); 366 367 return result; 368 } 369 370 //! Create a vector of floats from an array of floats. Offset is in bytes. 371 std::vector<float> readFloatArray(const int count, const void* memory, const int offset) 372 { 373 std::vector<float> results(count); 374 375 if (count != 0) 376 { 377 const float* pFloatData = reinterpret_cast<const float*>(static_cast<const deUint8*>(memory) + offset); 378 deMemcpy(&results[0], pFloatData, sizeof(float) * count); 379 } 380 381 return results; 382 } 383 384 void initPrograms (vk::SourceCollections& programCollection, TestParams testParams) 385 { 386 if (testParams.shaderLanguage == SHADER_LANGUAGE_GLSL) 387 { 388 // Vertex shader: no inputs 389 { 390 std::ostringstream src; 391 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 392 << "\n" 393 << "void main (void)\n" 394 << "{\n" 395 << "}\n"; 396 397 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 398 } 399 400 // Tessellation control shader 401 { 402 std::ostringstream src; 403 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 404 << "#extension GL_EXT_tessellation_shader : require\n" 405 << "\n" 406 << "layout(vertices = 1) out;\n" 407 << "\n" 408 << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n" 409 << " float outer1;\n" 410 << "} sb_levels;\n" 411 << "\n" 412 << "void main (void)\n" 413 << "{\n" 414 << " gl_TessLevelOuter[0] = 1.0;\n" 415 << " gl_TessLevelOuter[1] = sb_levels.outer1;\n" 416 << "}\n"; 417 418 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); 419 } 420 421 // Tessellation evaluation shader 422 { 423 std::ostringstream src; 424 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" 425 << "#extension GL_EXT_tessellation_shader : require\n" 426 << "\n" 427 << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_ISOLINES) << ", " 428 << getSpacingModeShaderName(testParams.spacingMode) << ", point_mode) in;\n" 429 << "\n" 430 << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n" 431 << " int numInvocations;\n" 432 << " float tessCoord[];\n" 433 << "} sb_out;\n" 434 << "\n" 435 << "void main (void)\n" 436 << "{\n" 437 << " int index = atomicAdd(sb_out.numInvocations, 1);\n" 438 << " sb_out.tessCoord[index] = gl_TessCoord.x;\n" 439 << "}\n"; 440 441 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 442 } 443 } 444 else 445 { 446 // Vertex shader - no inputs 447 { 448 std::ostringstream src; 449 src << "void main (void)\n" 450 << "{\n" 451 << "}\n"; 452 453 programCollection.hlslSources.add("vert") << glu::VertexSource(src.str()); 454 } 455 456 // Tessellation control shader 457 { 458 std::ostringstream src; 459 src << "struct HS_CONSTANT_OUT\n" 460 << "{\n" 461 << " float tessLevelsOuter[2] : SV_TessFactor;\n" 462 << "};\n" 463 << "\n" 464 << "tbuffer TessLevels : register(b0)\n" 465 << "{\n" 466 << " float outer1;\n" 467 << "}\n" 468 << "\n" 469 << "[domain(\"isoline\")]\n" 470 << "[partitioning(\"" << getPartitioningShaderName(testParams.spacingMode) << "\")]\n" 471 << "[outputtopology(\"point\")]\n" 472 << "[outputcontrolpoints(1)]\n" 473 << "[patchconstantfunc(\"PCF\")]\n" 474 << "void main()\n" 475 << "{\n" 476 << "}\n" 477 << "\n" 478 << "HS_CONSTANT_OUT PCF()\n" 479 << "{\n" 480 << " HS_CONSTANT_OUT output;\n" 481 << " output.tessLevelsOuter[0] = 1.0;\n" 482 << " output.tessLevelsOuter[1] = outer1;\n" 483 << " return output;\n" 484 << "}\n"; 485 486 programCollection.hlslSources.add("tesc") << glu::TessellationControlSource(src.str()); 487 } 488 489 // Tessellation evaluation shader 490 { 491 std::ostringstream src; 492 493 src << "struct OutputStruct\n" 494 << "{\n" 495 << " int numInvocations;\n" 496 << " float tessCoord[];\n" 497 << "};\n" 498 << "globallycoherent RWStructuredBuffer <OutputStruct> Output : register(b1);\n" 499 << "\n" 500 << "void main(float2 tessCoords : SV_DOMAINLOCATION)\n" 501 << "{\n" 502 << " int index;\n" 503 << " InterlockedAdd(Output[0].numInvocations, 1, index);\n" 504 << " Output[0].tessCoord[index] = tessCoords.x;\n" 505 << "}\n"; 506 507 programCollection.hlslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); 508 } 509 } 510 } 511 512 tcu::TestStatus test (Context& context, TestParams testParams) 513 { 514 DE_ASSERT(testParams.spacingMode == SPACINGMODE_FRACTIONAL_ODD || testParams.spacingMode == SPACINGMODE_FRACTIONAL_EVEN); 515 DE_ASSERT(testParams.shaderLanguage == SHADER_LANGUAGE_GLSL || testParams.shaderLanguage == SHADER_LANGUAGE_HLSL); 516 517 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); 518 519 const DeviceInterface& vk = context.getDeviceInterface(); 520 const VkDevice device = context.getDevice(); 521 const VkQueue queue = context.getUniversalQueue(); 522 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); 523 Allocator& allocator = context.getDefaultAllocator(); 524 525 const std::vector<float> tessLevelCases = genTessLevelCases(); 526 const int maxNumVertices = 1 + getClampedRoundedTessLevel(testParams.spacingMode, *std::max_element(tessLevelCases.begin(), tessLevelCases.end())); 527 528 // Result buffer: generated tess coords go here. 529 530 const VkDeviceSize resultBufferSizeBytes = sizeof(int) + sizeof(float) * maxNumVertices; 531 const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 532 533 // Outer1 tessellation level constant buffer. 534 535 const VkDeviceSize tessLevelsBufferSizeBytes = sizeof(float); // we pass only outer1 536 const Buffer tessLevelsBuffer (vk, device, allocator, makeBufferCreateInfo(tessLevelsBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); 537 538 // Descriptors 539 540 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder() 541 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) 542 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) 543 .build(vk, device)); 544 545 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder() 546 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 547 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) 548 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); 549 550 const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); 551 const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, tessLevelsBufferSizeBytes); 552 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes); 553 554 DescriptorSetUpdateBuilder() 555 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo) 556 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo) 557 .update(vk, device); 558 559 // Pipeline 560 561 const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device)); 562 const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass)); 563 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout)); 564 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); 565 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); 566 567 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder() 568 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL) 569 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"), DE_NULL) 570 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL) 571 .build(vk, device, *pipelineLayout, *renderPass)); 572 573 // Data that will be verified across all cases 574 std::vector<float> additionalSegmentLengths; 575 std::vector<int> additionalSegmentLocations; 576 577 bool success = false; 578 579 // Repeat the test for all tessellation coords cases 580 for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx) 581 { 582 // Upload tessellation levels data to the input buffer 583 { 584 const Allocation& alloc = tessLevelsBuffer.getAllocation(); 585 float* const tessLevelOuter1 = static_cast<float*>(alloc.getHostPtr()); 586 587 *tessLevelOuter1 = tessLevelCases[tessLevelCaseNdx]; 588 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), tessLevelsBufferSizeBytes); 589 } 590 591 // Clear the results buffer 592 { 593 const Allocation& alloc = resultBuffer.getAllocation(); 594 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes)); 595 flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes); 596 } 597 598 beginCommandBuffer(vk, *cmdBuffer); 599 600 // Begin render pass 601 beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer); 602 603 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 604 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); 605 606 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u); 607 endRenderPass(vk, *cmdBuffer); 608 609 { 610 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( 611 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes); 612 613 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, 614 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL); 615 } 616 617 endCommandBuffer(vk, *cmdBuffer); 618 submitCommandsAndWait(vk, device, queue, *cmdBuffer); 619 620 // Verify the result. 621 { 622 tcu::TestLog& log = context.getTestContext().getLog(); 623 624 const Allocation& resultAlloc = resultBuffer.getAllocation(); 625 invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes); 626 627 const deInt32 numResults = *static_cast<deInt32*>(resultAlloc.getHostPtr()); 628 const std::vector<float> resultTessCoords = readFloatArray(numResults, resultAlloc.getHostPtr(), sizeof(deInt32)); 629 630 // Outputs 631 float additionalSegmentLength; 632 int additionalSegmentLocation; 633 634 success = verifyFractionalSpacingSingle(log, testParams.spacingMode, tessLevelCases[tessLevelCaseNdx], resultTessCoords, 635 &additionalSegmentLength, &additionalSegmentLocation); 636 637 if (!success) 638 break; 639 640 additionalSegmentLengths.push_back(additionalSegmentLength); 641 additionalSegmentLocations.push_back(additionalSegmentLocation); 642 } 643 } // for tessLevelCaseNdx 644 645 if (success) 646 success = verifyFractionalSpacingMultiple(context.getTestContext().getLog(), testParams.spacingMode, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations); 647 648 return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure")); 649 } 650 651 } // anonymous 652 653 //! These tests correspond to dEQP-GLES31.functional.tessellation.fractional_spacing.* 654 //! Check validity of fractional spacing modes. Draws a single isoline, reads tess coords with SSBO. 655 tcu::TestCaseGroup* createFractionalSpacingTests (tcu::TestContext& testCtx) 656 { 657 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "fractional_spacing", "Test fractional spacing modes")); 658 659 addFunctionCaseWithPrograms(group.get(), "glsl_odd", "", initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_ODD)); 660 addFunctionCaseWithPrograms(group.get(), "glsl_even", "", initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_EVEN)); 661 addFunctionCaseWithPrograms(group.get(), "hlsl_odd", "", initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_ODD)); 662 addFunctionCaseWithPrograms(group.get(), "hlsl_even", "", initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_EVEN)); 663 664 return group.release(); 665 } 666 667 } // tessellation 668 } // vkt 669