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