| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2014 The Android Open Source Project |
| * Copyright (c) 2016 The Khronos Group Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| *//*! |
| * \file |
| * \brief Tessellation Fractional Spacing Tests |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktTessellationFractionalSpacingTests.hpp" |
| #include "vktTestCaseUtil.hpp" |
| #include "vktTessellationUtil.hpp" |
| |
| #include "tcuTestLog.hpp" |
| |
| #include "vkDefs.hpp" |
| #include "vkBarrierUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkObjUtil.hpp" |
| |
| #include "deUniquePtr.hpp" |
| #include "deStringUtil.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| namespace vkt |
| { |
| namespace tessellation |
| { |
| |
| using namespace vk; |
| |
| namespace |
| { |
| |
| template <typename T, typename MembT> |
| std::vector<MembT> members (const std::vector<T>& objs, MembT T::* membP) |
| { |
| std::vector<MembT> result(objs.size()); |
| for (int i = 0; i < static_cast<int>(objs.size()); ++i) |
| result[i] = objs[i].*membP; |
| return result; |
| } |
| |
| //! Predicate functor for comparing structs by their members. |
| template <typename Pred, typename T, typename MembT> |
| class MemberPred |
| { |
| public: |
| MemberPred (MembT T::* membP) : m_membP(membP), m_pred(Pred()) {} |
| bool operator() (const T& a, const T& b) const { return m_pred(a.*m_membP, b.*m_membP); } |
| |
| private: |
| MembT T::* m_membP; |
| Pred m_pred; |
| }; |
| |
| //! Convenience wrapper for MemberPred, because class template arguments aren't deduced based on constructor arguments. |
| template <template <typename> class Pred, typename T, typename MembT> |
| inline MemberPred<Pred<MembT>, T, MembT> memberPred (MembT T::* membP) { return MemberPred<Pred<MembT>, T, MembT>(membP); } |
| |
| struct Segment |
| { |
| int index; //!< Index of left coordinate in sortedXCoords. |
| float length; |
| |
| Segment (void) : index(-1), length(-1.0f) {} |
| Segment (int index_, float length_) : index(index_), length(length_) {} |
| }; |
| |
| inline std::vector<float> lengths (const std::vector<Segment>& segments) { return members(segments, &Segment::length); } |
| |
| struct LineData |
| { |
| float tessLevel; |
| float additionalSegmentLength; |
| int additionalSegmentLocation; |
| |
| LineData (float lev, float len, int loc) : tessLevel(lev), additionalSegmentLength(len), additionalSegmentLocation(loc) {} |
| }; |
| |
| struct TestParams |
| { |
| ShaderLanguage shaderLanguage; |
| SpacingMode spacingMode; |
| |
| TestParams(ShaderLanguage sl, SpacingMode sm) : shaderLanguage(sl), spacingMode(sm) {} |
| }; |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Verify fractional spacing conditions for a single line |
| * |
| * Verify that the splitting of an edge (resulting from e.g. an isoline |
| * with outer levels { 1.0, tessLevel }) with a given fractional spacing |
| * mode fulfills certain conditions given in the spec. |
| * |
| * Note that some conditions can't be checked from just one line |
| * (specifically, that the additional segment decreases monotonically |
| * length and the requirement that the additional segments be placed |
| * identically for identical values of clamped level). |
| * |
| * Therefore, the function stores some values to additionalSegmentLengthDst |
| * and additionalSegmentLocationDst that can later be given to |
| * verifyFractionalSpacingMultiple(). A negative value in length means that |
| * no additional segments are present, i.e. there's just one segment. |
| * A negative value in location means that the value wasn't determinable, |
| * i.e. all segments had same length. |
| * The values are not stored if false is returned. |
| *//*--------------------------------------------------------------------*/ |
| bool verifyFractionalSpacingSingle (tcu::TestLog& log, |
| const SpacingMode spacingMode, |
| const float tessLevel, |
| const std::vector<float>& coords, |
| float* const pOutAdditionalSegmentLength, |
| int* const pOutAdditionalSegmentLocation) |
| { |
| DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN); |
| |
| const float clampedLevel = getClampedTessLevel(spacingMode, tessLevel); |
| const int finalLevel = getRoundedTessLevel(spacingMode, clampedLevel); |
| const std::vector<float> sortedCoords = sorted(coords); |
| std::string failNote = "Note: tessellation level is " + de::toString(tessLevel) + "\nNote: sorted coordinates are:\n " + containerStr(sortedCoords); |
| |
| if (static_cast<int>(coords.size()) != finalLevel + 1) |
| { |
| log << tcu::TestLog::Message << "Failure: number of vertices is " << coords.size() << "; expected " << finalLevel + 1 |
| << " (clamped tessellation level is " << clampedLevel << ")" |
| << "; final level (clamped level rounded up to " << (spacingMode == SPACINGMODE_FRACTIONAL_EVEN ? "even" : "odd") << ") is " << finalLevel |
| << " and should equal the number of segments, i.e. number of vertices minus 1" << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| if (sortedCoords[0] != 0.0f || sortedCoords.back() != 1.0f) |
| { |
| log << tcu::TestLog::Message << "Failure: smallest coordinate should be 0.0 and biggest should be 1.0" << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| { |
| std::vector<Segment> segments(finalLevel); |
| for (int i = 0; i < finalLevel; ++i) |
| segments[i] = Segment(i, sortedCoords[i+1] - sortedCoords[i]); |
| |
| failNote += "\nNote: segment lengths are, from left to right:\n " + containerStr(lengths(segments)); |
| |
| { |
| // Divide segments to two different groups based on length. |
| |
| std::vector<Segment> segmentsA; |
| std::vector<Segment> segmentsB; |
| segmentsA.push_back(segments[0]); |
| |
| for (int segNdx = 1; segNdx < static_cast<int>(segments.size()); ++segNdx) |
| { |
| const float epsilon = 0.001f; |
| const Segment& seg = segments[segNdx]; |
| |
| if (de::abs(seg.length - segmentsA[0].length) < epsilon) |
| segmentsA.push_back(seg); |
| else if (segmentsB.empty() || de::abs(seg.length - segmentsB[0].length) < epsilon) |
| segmentsB.push_back(seg); |
| else |
| { |
| log << tcu::TestLog::Message << "Failure: couldn't divide segments to 2 groups by length; " |
| << "e.g. segment of length " << seg.length << " isn't approximately equal to either " |
| << segmentsA[0].length << " or " << segmentsB[0].length << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| |
| if (clampedLevel == static_cast<float>(finalLevel)) |
| { |
| // All segments should be of equal length. |
| if (!segmentsA.empty() && !segmentsB.empty()) |
| { |
| log << tcu::TestLog::Message << "Failure: clamped and final tessellation level are equal, but not all segments are of equal length." << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| |
| if (segmentsA.empty() || segmentsB.empty()) // All segments have same length. This is ok. |
| { |
| *pOutAdditionalSegmentLength = (segments.size() == 1 ? -1.0f : segments[0].length); |
| *pOutAdditionalSegmentLocation = -1; |
| return true; |
| } |
| |
| if (segmentsA.size() != 2 && segmentsB.size() != 2) |
| { |
| 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 |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| // For convenience, arrange so that the 2-segment group is segmentsB. |
| if (segmentsB.size() != 2) |
| std::swap(segmentsA, segmentsB); |
| |
| // \note For 4-segment lines both segmentsA and segmentsB have 2 segments each. |
| // Thus, we can't be sure which ones were meant as the additional segments. |
| // We give the benefit of the doubt by assuming that they're the shorter |
| // ones (as they should). |
| |
| if (segmentsA.size() != 2) |
| { |
| if (segmentsB[0].length > segmentsA[0].length + 0.001f) |
| { |
| log << tcu::TestLog::Message << "Failure: the two additional segments are longer than the other segments" << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| else |
| { |
| // We have 2 segmentsA and 2 segmentsB, ensure segmentsB has the shorter lengths |
| if (segmentsB[0].length > segmentsA[0].length) |
| std::swap(segmentsA, segmentsB); |
| } |
| |
| // Check that the additional segments are placed symmetrically. |
| if (segmentsB[0].index + segmentsB[1].index + 1 != static_cast<int>(segments.size())) |
| { |
| log << tcu::TestLog::Message << "Failure: the two additional segments aren't placed symmetrically; " |
| << "one is at index " << segmentsB[0].index << " and other is at index " << segmentsB[1].index |
| << " (note: the two indexes should sum to " << static_cast<int>(segments.size())-1 << ", i.e. numberOfSegments-1)" << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << failNote << tcu::TestLog::EndMessage; |
| return false; |
| } |
| |
| *pOutAdditionalSegmentLength = segmentsB[0].length; |
| if (segmentsA.size() != 2) |
| *pOutAdditionalSegmentLocation = de::min(segmentsB[0].index, segmentsB[1].index); |
| else |
| *pOutAdditionalSegmentLocation = segmentsB[0].length < segmentsA[0].length - 0.001f ? de::min(segmentsB[0].index, segmentsB[1].index) |
| : -1; // \note -1 when can't reliably decide which ones are the additional segments, a or b. |
| |
| return true; |
| } |
| } |
| } |
| |
| /*--------------------------------------------------------------------*//*! |
| * \brief Verify fractional spacing conditions between multiple lines |
| * |
| * Verify the fractional spacing conditions that are not checked in |
| * verifyFractionalSpacingSingle(). Uses values given by said function |
| * as parameters, in addition to the spacing mode and tessellation level. |
| *//*--------------------------------------------------------------------*/ |
| static bool verifyFractionalSpacingMultiple (tcu::TestLog& log, |
| const SpacingMode spacingMode, |
| const std::vector<float>& tessLevels, |
| const std::vector<float>& additionalSegmentLengths, |
| const std::vector<int>& additionalSegmentLocations) |
| { |
| DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN); |
| DE_ASSERT(tessLevels.size() == additionalSegmentLengths.size() && tessLevels.size() == additionalSegmentLocations.size()); |
| |
| std::vector<LineData> lineDatas; |
| |
| for (int i = 0; i < static_cast<int>(tessLevels.size()); ++i) |
| lineDatas.push_back(LineData(tessLevels[i], additionalSegmentLengths[i], additionalSegmentLocations[i])); |
| |
| { |
| const std::vector<LineData> lineDatasSortedByLevel = sorted(lineDatas, memberPred<std::less>(&LineData::tessLevel)); |
| |
| // Check that lines with identical clamped tessellation levels have identical additionalSegmentLocation. |
| |
| for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx) |
| { |
| const LineData& curData = lineDatasSortedByLevel[lineNdx]; |
| const LineData& prevData = lineDatasSortedByLevel[lineNdx-1]; |
| |
| if (curData.additionalSegmentLocation < 0 || prevData.additionalSegmentLocation < 0) |
| continue; // Unknown locations, skip. |
| |
| if (getClampedTessLevel(spacingMode, curData.tessLevel) == getClampedTessLevel(spacingMode, prevData.tessLevel) && |
| curData.additionalSegmentLocation != prevData.additionalSegmentLocation) |
| { |
| log << tcu::TestLog::Message << "Failure: additional segments not located identically for two edges with identical clamped tessellation levels" << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << "Note: tessellation levels are " << curData.tessLevel << " and " << prevData.tessLevel |
| << " (clamped level " << getClampedTessLevel(spacingMode, curData.tessLevel) << ")" |
| << "; but first additional segments located at indices " |
| << curData.additionalSegmentLocation << " and " << prevData.additionalSegmentLocation << ", respectively" << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| |
| // Check that, among lines with same clamped rounded tessellation level, additionalSegmentLength is monotonically decreasing with "clampedRoundedTessLevel - clampedTessLevel" (the "fraction"). |
| |
| for (int lineNdx = 1; lineNdx < static_cast<int>(lineDatasSortedByLevel.size()); ++lineNdx) |
| { |
| const LineData& curData = lineDatasSortedByLevel[lineNdx]; |
| const LineData& prevData = lineDatasSortedByLevel[lineNdx-1]; |
| |
| if (curData.additionalSegmentLength < 0.0f || prevData.additionalSegmentLength < 0.0f) |
| continue; // Unknown segment lengths, skip. |
| |
| const float curClampedLevel = getClampedTessLevel(spacingMode, curData.tessLevel); |
| const float prevClampedLevel = getClampedTessLevel(spacingMode, prevData.tessLevel); |
| const int curFinalLevel = getRoundedTessLevel(spacingMode, curClampedLevel); |
| const int prevFinalLevel = getRoundedTessLevel(spacingMode, prevClampedLevel); |
| |
| if (curFinalLevel != prevFinalLevel) |
| continue; |
| |
| const float curFraction = static_cast<float>(curFinalLevel) - curClampedLevel; |
| const float prevFraction = static_cast<float>(prevFinalLevel) - prevClampedLevel; |
| |
| if (curData.additionalSegmentLength < prevData.additionalSegmentLength || |
| (curClampedLevel == prevClampedLevel && curData.additionalSegmentLength != prevData.additionalSegmentLength)) |
| { |
| 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 |
| << tcu::TestLog::Message << "Note: <f> stands for the clamped tessellation level and <n> for the final (rounded and clamped) tessellation level" << tcu::TestLog::EndMessage |
| << tcu::TestLog::Message << "Note: two edges have tessellation levels " << prevData.tessLevel << " and " << curData.tessLevel << " respectively" |
| << ", clamped " << prevClampedLevel << " and " << curClampedLevel << ", final " << prevFinalLevel << " and " << curFinalLevel |
| << "; fractions are " << prevFraction << " and " << curFraction |
| << ", but resulted in segment lengths " << prevData.additionalSegmentLength << " and " << curData.additionalSegmentLength << tcu::TestLog::EndMessage; |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| std::vector<float> genTessLevelCases (void) |
| { |
| std::vector<float> result; |
| |
| // Ranges [7.0 .. 8.0), [8.0 .. 9.0) and [9.0 .. 10.0) |
| { |
| static const float rangeStarts[] = { 7.0f, 8.0f, 9.0f }; |
| const int numSamplesPerRange = 10; |
| |
| for (int rangeNdx = 0; rangeNdx < DE_LENGTH_OF_ARRAY(rangeStarts); ++rangeNdx) |
| for (int i = 0; i < numSamplesPerRange; ++i) |
| result.push_back(rangeStarts[rangeNdx] + static_cast<float>(i)/numSamplesPerRange); |
| } |
| |
| // 0.3, 1.3, 2.3, ... , 62.3 |
| for (int i = 0; i <= 62; ++i) |
| result.push_back(static_cast<float>(i) + 0.3f); |
| |
| return result; |
| } |
| |
| //! Create a vector of floats from an array of floats. Offset is in bytes. |
| std::vector<float> readFloatArray(const int count, const void* memory, const int offset) |
| { |
| std::vector<float> results(count); |
| |
| if (count != 0) |
| { |
| const float* pFloatData = reinterpret_cast<const float*>(static_cast<const deUint8*>(memory) + offset); |
| deMemcpy(&results[0], pFloatData, sizeof(float) * count); |
| } |
| |
| return results; |
| } |
| |
| void initPrograms (vk::SourceCollections& programCollection, TestParams testParams) |
| { |
| if (testParams.shaderLanguage == SHADER_LANGUAGE_GLSL) |
| { |
| // Vertex shader: no inputs |
| { |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" |
| << "\n" |
| << "void main (void)\n" |
| << "{\n" |
| << "}\n"; |
| |
| programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); |
| } |
| |
| // Tessellation control shader |
| { |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" |
| << "#extension GL_EXT_tessellation_shader : require\n" |
| << "\n" |
| << "layout(vertices = 1) out;\n" |
| << "\n" |
| << "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n" |
| << " float outer1;\n" |
| << "} sb_levels;\n" |
| << "\n" |
| << "void main (void)\n" |
| << "{\n" |
| << " gl_TessLevelOuter[0] = 1.0;\n" |
| << " gl_TessLevelOuter[1] = sb_levels.outer1;\n" |
| << "}\n"; |
| |
| programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str()); |
| } |
| |
| // Tessellation evaluation shader |
| { |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n" |
| << "#extension GL_EXT_tessellation_shader : require\n" |
| << "\n" |
| << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_ISOLINES) << ", " |
| << getSpacingModeShaderName(testParams.spacingMode) << ", point_mode) in;\n" |
| << "\n" |
| << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n" |
| << " int numInvocations;\n" |
| << " float tessCoord[];\n" |
| << "} sb_out;\n" |
| << "\n" |
| << "void main (void)\n" |
| << "{\n" |
| << " int index = atomicAdd(sb_out.numInvocations, 1);\n" |
| << " sb_out.tessCoord[index] = gl_TessCoord.x;\n" |
| << "}\n"; |
| |
| programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); |
| } |
| } |
| else |
| { |
| // Vertex shader - no inputs |
| { |
| std::ostringstream src; |
| src << "void main (void)\n" |
| << "{\n" |
| << "}\n"; |
| |
| programCollection.hlslSources.add("vert") << glu::VertexSource(src.str()); |
| } |
| |
| // Tessellation control shader |
| { |
| std::ostringstream src; |
| src << "struct HS_CONSTANT_OUT\n" |
| << "{\n" |
| << " float tessLevelsOuter[2] : SV_TessFactor;\n" |
| << "};\n" |
| << "\n" |
| << "tbuffer TessLevels : register(b0)\n" |
| << "{\n" |
| << " float outer1;\n" |
| << "}\n" |
| << "\n" |
| << "[domain(\"isoline\")]\n" |
| << "[partitioning(\"" << getPartitioningShaderName(testParams.spacingMode) << "\")]\n" |
| << "[outputtopology(\"point\")]\n" |
| << "[outputcontrolpoints(1)]\n" |
| << "[patchconstantfunc(\"PCF\")]\n" |
| << "void main()\n" |
| << "{\n" |
| << "}\n" |
| << "\n" |
| << "HS_CONSTANT_OUT PCF()\n" |
| << "{\n" |
| << " HS_CONSTANT_OUT output;\n" |
| << " output.tessLevelsOuter[0] = 1.0;\n" |
| << " output.tessLevelsOuter[1] = outer1;\n" |
| << " return output;\n" |
| << "}\n"; |
| |
| programCollection.hlslSources.add("tesc") << glu::TessellationControlSource(src.str()); |
| } |
| |
| // Tessellation evaluation shader |
| { |
| std::ostringstream src; |
| |
| src << "struct OutputStruct\n" |
| << "{\n" |
| << " int numInvocations;\n" |
| << " float tessCoord[];\n" |
| << "};\n" |
| << "globallycoherent RWStructuredBuffer <OutputStruct> Output : register(b1);\n" |
| << "\n" |
| << "void main(float2 tessCoords : SV_DOMAINLOCATION)\n" |
| << "{\n" |
| << " int index;\n" |
| << " InterlockedAdd(Output[0].numInvocations, 1, index);\n" |
| << " Output[0].tessCoord[index] = tessCoords.x;\n" |
| << "}\n"; |
| |
| programCollection.hlslSources.add("tese") << glu::TessellationEvaluationSource(src.str()); |
| } |
| } |
| } |
| |
| tcu::TestStatus test (Context& context, TestParams testParams) |
| { |
| DE_ASSERT(testParams.spacingMode == SPACINGMODE_FRACTIONAL_ODD || testParams.spacingMode == SPACINGMODE_FRACTIONAL_EVEN); |
| DE_ASSERT(testParams.shaderLanguage == SHADER_LANGUAGE_GLSL || testParams.shaderLanguage == SHADER_LANGUAGE_HLSL); |
| |
| requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS); |
| |
| const DeviceInterface& vk = context.getDeviceInterface(); |
| const VkDevice device = context.getDevice(); |
| const VkQueue queue = context.getUniversalQueue(); |
| const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex(); |
| Allocator& allocator = context.getDefaultAllocator(); |
| |
| const std::vector<float> tessLevelCases = genTessLevelCases(); |
| const int maxNumVertices = 1 + getClampedRoundedTessLevel(testParams.spacingMode, *std::max_element(tessLevelCases.begin(), tessLevelCases.end())); |
| |
| // Result buffer: generated tess coords go here. |
| |
| const VkDeviceSize resultBufferSizeBytes = sizeof(int) + sizeof(float) * maxNumVertices; |
| const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); |
| |
| // Outer1 tessellation level constant buffer. |
| |
| const VkDeviceSize tessLevelsBufferSizeBytes = sizeof(float); // we pass only outer1 |
| const Buffer tessLevelsBuffer (vk, device, allocator, makeBufferCreateInfo(tessLevelsBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible); |
| |
| // Descriptors |
| |
| const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder() |
| .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) |
| .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) |
| .build(vk, device)); |
| |
| const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder() |
| .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) |
| .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER) |
| .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u)); |
| |
| const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout)); |
| const VkDescriptorBufferInfo tessLevelsBufferInfo = makeDescriptorBufferInfo(tessLevelsBuffer.get(), 0ull, tessLevelsBufferSizeBytes); |
| const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes); |
| |
| DescriptorSetUpdateBuilder() |
| .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo) |
| .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo) |
| .update(vk, device); |
| |
| // Pipeline |
| |
| const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device)); |
| const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, 0u, DE_NULL, 1u, 1u)); |
| const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout (vk, device, *descriptorSetLayout)); |
| const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); |
| const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder() |
| .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL) |
| .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"), DE_NULL) |
| .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get("tese"), DE_NULL) |
| .build(vk, device, *pipelineLayout, *renderPass)); |
| |
| // Data that will be verified across all cases |
| std::vector<float> additionalSegmentLengths; |
| std::vector<int> additionalSegmentLocations; |
| |
| bool success = false; |
| |
| // Repeat the test for all tessellation coords cases |
| for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx) |
| { |
| // Upload tessellation levels data to the input buffer |
| { |
| const Allocation& alloc = tessLevelsBuffer.getAllocation(); |
| float* const tessLevelOuter1 = static_cast<float*>(alloc.getHostPtr()); |
| |
| *tessLevelOuter1 = tessLevelCases[tessLevelCaseNdx]; |
| flushAlloc(vk, device, alloc); |
| } |
| |
| // Clear the results buffer |
| { |
| const Allocation& alloc = resultBuffer.getAllocation(); |
| |
| deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes)); |
| flushAlloc(vk, device, alloc); |
| } |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| |
| // Begin render pass |
| beginRenderPassWithRasterizationDisabled(vk, *cmdBuffer, *renderPass, *framebuffer); |
| |
| vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); |
| vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL); |
| |
| vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u); |
| endRenderPass(vk, *cmdBuffer); |
| |
| { |
| const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier( |
| VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes); |
| |
| vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u, |
| 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL); |
| } |
| |
| endCommandBuffer(vk, *cmdBuffer); |
| submitCommandsAndWait(vk, device, queue, *cmdBuffer); |
| |
| // Verify the result. |
| { |
| tcu::TestLog& log = context.getTestContext().getLog(); |
| const Allocation& resultAlloc = resultBuffer.getAllocation(); |
| |
| invalidateAlloc(vk, device, resultAlloc); |
| |
| const deInt32 numResults = *static_cast<deInt32*>(resultAlloc.getHostPtr()); |
| const std::vector<float> resultTessCoords = readFloatArray(numResults, resultAlloc.getHostPtr(), sizeof(deInt32)); |
| |
| // Outputs |
| float additionalSegmentLength; |
| int additionalSegmentLocation; |
| |
| success = verifyFractionalSpacingSingle(log, testParams.spacingMode, tessLevelCases[tessLevelCaseNdx], resultTessCoords, |
| &additionalSegmentLength, &additionalSegmentLocation); |
| |
| if (!success) |
| break; |
| |
| additionalSegmentLengths.push_back(additionalSegmentLength); |
| additionalSegmentLocations.push_back(additionalSegmentLocation); |
| } |
| } // for tessLevelCaseNdx |
| |
| if (success) |
| success = verifyFractionalSpacingMultiple(context.getTestContext().getLog(), testParams.spacingMode, tessLevelCases, additionalSegmentLengths, additionalSegmentLocations); |
| |
| return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure")); |
| } |
| |
| void checkSupportTess(Context& context, const TestParams) |
| { |
| if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR* const features = getPortability(context)) |
| { |
| checkPointMode(*features); |
| checkIsolines(*features); |
| } |
| } |
| |
| } // anonymous |
| |
| //! These tests correspond to dEQP-GLES31.functional.tessellation.fractional_spacing.* |
| //! Check validity of fractional spacing modes. Draws a single isoline, reads tess coords with SSBO. |
| tcu::TestCaseGroup* createFractionalSpacingTests (tcu::TestContext& testCtx) |
| { |
| de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "fractional_spacing", "Test fractional spacing modes")); |
| |
| addFunctionCaseWithPrograms(group.get(), "glsl_odd", "", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_ODD)); |
| addFunctionCaseWithPrograms(group.get(), "glsl_even", "", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_EVEN)); |
| addFunctionCaseWithPrograms(group.get(), "hlsl_odd", "", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_ODD)); |
| addFunctionCaseWithPrograms(group.get(), "hlsl_even", "", checkSupportTess, initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_EVEN)); |
| |
| return group.release(); |
| } |
| |
| } // tessellation |
| } // vkt |