blob: 62d30b2bfbb7b4e88cd50fa1222f548e449e5716 [file] [log] [blame]
/*------------------------------------------------------------------------
* 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"));
}
} // 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", "", initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_ODD));
addFunctionCaseWithPrograms(group.get(), "glsl_even", "", initPrograms, test, TestParams(SHADER_LANGUAGE_GLSL, SPACINGMODE_FRACTIONAL_EVEN));
addFunctionCaseWithPrograms(group.get(), "hlsl_odd", "", initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_ODD));
addFunctionCaseWithPrograms(group.get(), "hlsl_even", "", initPrograms, test, TestParams(SHADER_LANGUAGE_HLSL, SPACINGMODE_FRACTIONAL_EVEN));
return group.release();
}
} // tessellation
} // vkt