"spirv-tools"),
GitRepo(
"https://github.com/KhronosGroup/glslang.git",
- "5639f3aca5b75cbe5419a623eecf5e3794fab917",
+ "3357d870e455005a3781743e77403572eefb60d7",
"glslang"),
]
${GLSLANG_ABS_PATH}/glslang/MachineIndependent
${GLSLANG_ABS_PATH}/glslang/GenericCodeGen
${GLSLANG_ABS_PATH}/glslang/OSDependent
+ ${GLSLANG_ABS_PATH}/hlsl
${GLSLANG_ABS_PATH}/OGLCompilersDLL
${GLSLANG_ABS_PATH}/SPIRV
)
${GLSLANG_ABS_PATH}/glslang/MachineIndependent/limits.cpp
${GLSLANG_ABS_PATH}/glslang/MachineIndependent/linkValidate.cpp
${GLSLANG_ABS_PATH}/glslang/MachineIndependent/parseConst.cpp
+ ${GLSLANG_ABS_PATH}/glslang/MachineIndependent/propagateNoContraction.cpp
${GLSLANG_ABS_PATH}/glslang/MachineIndependent/reflection.cpp
${GLSLANG_ABS_PATH}/glslang/MachineIndependent/preprocessor/Pp.cpp
${GLSLANG_ABS_PATH}/glslang/MachineIndependent/preprocessor/PpAtom.cpp
${GLSLANG_ABS_PATH}/glslang/GenericCodeGen/Link.cpp
${GLSLANG_ABS_PATH}/OGLCompilersDLL/InitializeDll.cpp
+ ${GLSLANG_ABS_PATH}/hlsl/hlslGrammar.cpp
+ ${GLSLANG_ABS_PATH}/hlsl/hlslOpMap.cpp
+ ${GLSLANG_ABS_PATH}/hlsl/hlslParseHelper.cpp
+ ${GLSLANG_ABS_PATH}/hlsl/hlslScanContext.cpp
+ ${GLSLANG_ABS_PATH}/hlsl/hlslTokenStream.cpp
+
${GLSLANG_ABS_PATH}/SPIRV/GlslangToSpv.cpp
${GLSLANG_ABS_PATH}/SPIRV/InReadableOrder.cpp
${GLSLANG_ABS_PATH}/SPIRV/SpvBuilder.cpp
${GLSLANG_ABS_PATH}/SPIRV/SPVRemapper.cpp
+ ${GLSLANG_ABS_PATH}/SPIRV/Logger.cpp
${GLSLANG_ABS_PATH}/SPIRV/doc.cpp
${GLSLANG_ABS_PATH}/SPIRV/disassemble.cpp
add_subdirectory(image)
add_subdirectory(wsi)
add_subdirectory(sparse_resources)
+add_subdirectory(tessellation)
include_directories(
api
image
wsi
sparse_resources
+ tessellation
)
set(DEQP_VK_COMMON_SRCS
deqp-vk-image
deqp-vk-wsi
deqp-vk-sparse-resources
+ deqp-vk-tessellation
)
add_library(deqp-vk-common STATIC ${DEQP_VK_COMMON_SRCS})
--- /dev/null
+include_directories(..)
+
+set(DEQP_VK_TESSELLATION_SRCS
+ vktTessellationTests.cpp
+ vktTessellationTests.hpp
+ vktTessellationUtil.cpp
+ vktTessellationUtil.hpp
+ vktTessellationLimitsTests.hpp
+ vktTessellationLimitsTests.cpp
+ vktTessellationCoordinatesTests.hpp
+ vktTessellationCoordinatesTests.cpp
+ vktTessellationWindingTests.hpp
+ vktTessellationWindingTests.cpp
+ vktTessellationShaderInputOutputTests.hpp
+ vktTessellationShaderInputOutputTests.cpp
+ vktTessellationMiscDrawTests.hpp
+ vktTessellationMiscDrawTests.cpp
+ vktTessellationCommonEdgeTests.hpp
+ vktTessellationCommonEdgeTests.cpp
+ vktTessellationFractionalSpacingTests.hpp
+ vktTessellationFractionalSpacingTests.cpp
+ vktTessellationPrimitiveDiscardTests.hpp
+ vktTessellationPrimitiveDiscardTests.cpp
+ vktTessellationInvarianceTests.hpp
+ vktTessellationInvarianceTests.cpp
+ vktTessellationUserDefinedIO.hpp
+ vktTessellationUserDefinedIO.cpp
+ )
+
+set(DEQP_VK_TESSELLATION_LIBS
+ deqp-vk-common
+ tcutil
+ vkutil
+ )
+
+add_library(deqp-vk-tessellation STATIC ${DEQP_VK_TESSELLATION_SRCS})
+target_link_libraries(deqp-vk-tessellation ${DEQP_VK_TESSELLATION_LIBS})
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Common Edge Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationCommonEdgeTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuTexture.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum CaseType
+{
+ CASETYPE_BASIC = 0, //!< Order patch vertices such that when two patches share a vertex, it's at the same index for both.
+ CASETYPE_PRECISE, //!< Vertex indices don't match like for CASETYPE_BASIC, but other measures are taken, using the 'precise' qualifier.
+
+ CASETYPE_LAST
+};
+
+struct CaseDefinition
+{
+ TessPrimitiveType primitiveType;
+ SpacingMode spacingMode;
+ CaseType caseType;
+};
+
+//! Check that a certain rectangle in the image contains no black pixels.
+//! Returns true if an image successfully passess the verification.
+bool verifyResult (tcu::TestLog& log, const tcu::ConstPixelBufferAccess image)
+{
+ const int startX = static_cast<int>(0.15f * image.getWidth());
+ const int endX = static_cast<int>(0.85f * image.getWidth());
+ const int startY = static_cast<int>(0.15f * image.getHeight());
+ const int endY = static_cast<int>(0.85f * image.getHeight());
+
+ for (int y = startY; y < endY; ++y)
+ for (int x = startX; x < endX; ++x)
+ {
+ const tcu::Vec4 pixel = image.getPixel(x, y);
+
+ if (pixel.x() == 0 && pixel.y() == 0 && pixel.z() == 0)
+ {
+ log << tcu::TestLog::Message << "Failure: there seem to be cracks in the rendered result" << tcu::TestLog::EndMessage
+ << tcu::TestLog::Message << "Note: pixel with zero r, g and b channels found at " << tcu::IVec2(x, y) << tcu::TestLog::EndMessage;
+
+ return false;
+ }
+ }
+
+ log << tcu::TestLog::Message << "Success: there seem to be no cracks in the rendered result" << tcu::TestLog::EndMessage;
+
+ return true;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_v_position;\n"
+ << "layout(location = 1) in highp float in_v_tessParam;\n"
+ << "\n"
+ << "layout(location = 0) out highp vec2 in_tc_position;\n"
+ << "layout(location = 1) out highp float in_tc_tessParam;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_position = in_v_position;\n"
+ << " in_tc_tessParam = in_v_tessParam;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+ }
+
+ // Tessellation control shader
+ {
+ const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "")
+ << "\n"
+ << "layout(vertices = " << numVertices << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_tc_position[];\n"
+ << "layout(location = 1) in highp float in_tc_tessParam[];\n"
+ << "\n"
+ << "layout(location = 0) out highp vec2 in_te_position[];\n"
+ << "\n"
+ << (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_TessLevelOuter;\n\n" : "")
+ << "void main (void)\n"
+ << "{\n"
+ << " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = 5.0;\n"
+ << " gl_TessLevelInner[1] = 5.0;\n"
+ << "\n"
+ << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
+ " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[2]);\n"
+ " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[0]);\n"
+ " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[1]);\n"
+ : caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
+ " gl_TessLevelOuter[0] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[0] + in_tc_tessParam[2]);\n"
+ " gl_TessLevelOuter[1] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[1] + in_tc_tessParam[0]);\n"
+ " gl_TessLevelOuter[2] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[3] + in_tc_tessParam[1]);\n"
+ " gl_TessLevelOuter[3] = 1.0 + 59.0 * 0.5 * (in_tc_tessParam[2] + in_tc_tessParam[3]);\n"
+ : "")
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ std::ostringstream primitiveSpecificCode;
+ if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ primitiveSpecificCode
+ << " highp vec2 pos = gl_TessCoord.x*in_te_position[0] + gl_TessCoord.y*in_te_position[1] + gl_TessCoord.z*in_te_position[2];\n"
+ << "\n"
+ << " highp float f = sqrt(3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z))) * 0.5 + 0.5;\n"
+ << " in_f_color = vec4(gl_TessCoord*f, 1.0);\n";
+ else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+ primitiveSpecificCode
+ << (caseDef.caseType == CASETYPE_BASIC ?
+ " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0]\n"
+ " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1]\n"
+ " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2]\n"
+ " + ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
+ : caseDef.caseType == CASETYPE_PRECISE ?
+ " highp vec2 a = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[0];\n"
+ " highp vec2 b = ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*in_te_position[1];\n"
+ " highp vec2 c = (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[2];\n"
+ " highp vec2 d = ( gl_TessCoord.x)*( gl_TessCoord.y)*in_te_position[3];\n"
+ " highp vec2 pos = a+b+c+d;\n"
+ : "")
+ << "\n"
+ << " highp float f = sqrt(1.0 - 2.0 * max(abs(gl_TessCoord.x - 0.5), abs(gl_TessCoord.y - 0.5)))*0.5 + 0.5;\n"
+ << " in_f_color = vec4(0.1, gl_TessCoord.xy*f, 1.0);\n";
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << (caseDef.caseType == CASETYPE_PRECISE ? "#extension GL_EXT_gpu_shader5 : require\n" : "")
+ << "\n"
+ << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+ << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_te_position[];\n"
+ << "\n"
+ << "layout(location = 0) out mediump vec4 in_f_color;\n"
+ << "\n"
+ << (caseDef.caseType == CASETYPE_PRECISE ? "precise gl_Position;\n\n" : "")
+ << "void main (void)\n"
+ << "{\n"
+ << primitiveSpecificCode.str()
+ << "\n"
+ << " // Offset the position slightly, based on the parity of the bits in the float representation.\n"
+ << " // This is done to detect possible small differences in edge vertex positions between patches.\n"
+ << " uvec2 bits = floatBitsToUint(pos);\n"
+ << " uint numBits = 0u;\n"
+ << " for (uint i = 0u; i < 32u; i++)\n"
+ << " numBits += ((bits[0] >> i) & 1u) + ((bits[1] >> i) & 1u);\n"
+ << " pos += float(numBits&1u)*0.04;\n"
+ << "\n"
+ << " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in mediump vec4 in_f_color;\n"
+ << "\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = in_f_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+//! Generic test code used by all test cases.
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+ DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+ DE_ASSERT(caseDef.caseType == CASETYPE_BASIC || caseDef.caseType == CASETYPE_PRECISE);
+
+ requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+ const DeviceInterface& vk = context.getDeviceInterface();
+ const VkDevice device = context.getDevice();
+ const VkQueue queue = context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = context.getDefaultAllocator();
+
+ // Prepare test data
+
+ std::vector<float> gridPosComps;
+ std::vector<float> gridTessParams;
+ std::vector<deUint16> gridIndices;
+
+ {
+ const int gridWidth = 4;
+ const int gridHeight = 4;
+ const int numVertices = (gridWidth+1)*(gridHeight+1);
+ const int numIndices = gridWidth*gridHeight * (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3*2 : 4);
+ const int numPosCompsPerVertex = 2;
+ const int totalNumPosComps = numPosCompsPerVertex*numVertices;
+
+ gridPosComps.reserve(totalNumPosComps);
+ gridTessParams.reserve(numVertices);
+ gridIndices.reserve(numIndices);
+
+ {
+ for (int i = 0; i < gridHeight+1; ++i)
+ for (int j = 0; j < gridWidth+1; ++j)
+ {
+ gridPosComps.push_back(-1.0f + 2.0f * ((float)j + 0.5f) / (float)(gridWidth+1));
+ gridPosComps.push_back(-1.0f + 2.0f * ((float)i + 0.5f) / (float)(gridHeight+1));
+ gridTessParams.push_back((float)(i*(gridWidth+1) + j) / (float)(numVertices-1));
+ }
+ }
+
+ // Generate patch vertex indices.
+ // \note If CASETYPE_BASIC, the vertices are ordered such that when multiple
+ // triangles/quads share a vertex, it's at the same index for everyone.
+
+ if (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ for (int i = 0; i < gridHeight; i++)
+ for (int j = 0; j < gridWidth; j++)
+ {
+ const deUint16 corners[4] =
+ {
+ (deUint16)((i+0)*(gridWidth+1) + j+0),
+ (deUint16)((i+0)*(gridWidth+1) + j+1),
+ (deUint16)((i+1)*(gridWidth+1) + j+0),
+ (deUint16)((i+1)*(gridWidth+1) + j+1)
+ };
+
+ const int secondTriangleVertexIndexOffset = caseDef.caseType == CASETYPE_BASIC ? 0 : 1;
+
+ for (int k = 0; k < 3; k++)
+ gridIndices.push_back(corners[(k+0 + i + (2-j%3)) % 3]);
+ for (int k = 0; k < 3; k++)
+ gridIndices.push_back(corners[(k+2 + i + (2-j%3) + secondTriangleVertexIndexOffset) % 3 + 1]);
+ }
+ }
+ else if (caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+ {
+ for (int i = 0; i < gridHeight; ++i)
+ for (int j = 0; j < gridWidth; ++j)
+ {
+ for (int m = 0; m < 2; m++)
+ for (int n = 0; n < 2; n++)
+ gridIndices.push_back((deUint16)((i+(i+m)%2)*(gridWidth+1) + j+(j+n)%2));
+
+ if (caseDef.caseType == CASETYPE_PRECISE && (i+j) % 2 == 0)
+ std::reverse(gridIndices.begin() + (gridIndices.size() - 4),
+ gridIndices.begin() + gridIndices.size());
+ }
+ }
+ else
+ DE_ASSERT(false);
+
+ DE_ASSERT(static_cast<int>(gridPosComps.size()) == totalNumPosComps);
+ DE_ASSERT(static_cast<int>(gridTessParams.size()) == numVertices);
+ DE_ASSERT(static_cast<int>(gridIndices.size()) == numIndices);
+ }
+
+ // Vertex input buffer: we put both attributes and indices in here.
+
+ const VkDeviceSize vertexDataSizeBytes = sizeInBytes(gridPosComps) + sizeInBytes(gridTessParams) + sizeInBytes(gridIndices);
+ const std::size_t vertexPositionsOffset = 0;
+ const std::size_t vertexTessParamsOffset = sizeInBytes(gridPosComps);
+ const std::size_t vertexIndicesOffset = vertexTessParamsOffset + sizeInBytes(gridTessParams);
+
+ const Buffer vertexBuffer(vk, device, allocator,
+ makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deUint8* const pData = static_cast<deUint8*>(alloc.getHostPtr());
+
+ deMemcpy(pData + vertexPositionsOffset, &gridPosComps[0], static_cast<std::size_t>(sizeInBytes(gridPosComps)));
+ deMemcpy(pData + vertexTessParamsOffset, &gridTessParams[0], static_cast<std::size_t>(sizeInBytes(gridTessParams)));
+ deMemcpy(pData + vertexIndicesOffset, &gridIndices[0], static_cast<std::size_t>(sizeInBytes(gridIndices)));
+
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+ // No barrier needed, flushed memory is automatically visible
+ }
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(256, 256);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView (makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device));
+
+ const int inPatchSize = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setPatchControlPoints(inPatchSize)
+ .addVertexBinding (makeVertexInputBindingDescription(0u, sizeof(tcu::Vec2), VK_VERTEX_INPUT_RATE_VERTEX))
+ .addVertexBinding (makeVertexInputBindingDescription(1u, sizeof(float), VK_VERTEX_INPUT_RATE_VERTEX))
+ .addVertexAttribute (makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32_SFLOAT, 0u))
+ .addVertexAttribute (makeVertexInputAttributeDescription(1u, 1u, VK_FORMAT_R32_SFLOAT, 0u))
+ .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)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ // Draw commands
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+ {
+ const VkBuffer buffers[] = { *vertexBuffer, *vertexBuffer };
+ const VkDeviceSize offsets[] = { vertexPositionsOffset, vertexTessParamsOffset, };
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, DE_LENGTH_OF_ARRAY(buffers), buffers, offsets);
+
+ vk.cmdBindIndexBuffer(*cmdBuffer, *vertexBuffer, vertexIndicesOffset, VK_INDEX_TYPE_UINT16);
+ }
+
+ vk.cmdDrawIndexed(*cmdBuffer, static_cast<deUint32>(gridIndices.size()), 1u, 0u, 0, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ {
+ // Log the result image.
+
+ const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+ const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+
+ tcu::TestLog& log = context.getTestContext().getLog();
+ log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess)
+ << tcu::TestLog::Message
+ << "Note: coloring is done to clarify the positioning and orientation of the "
+ << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? "triangles" :
+ caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ? "quads" : "")
+ << "; the color of a vertex corresponds to the index of that vertex in the patch"
+ << tcu::TestLog::EndMessage;
+
+ if (caseDef.caseType == CASETYPE_BASIC)
+ log << tcu::TestLog::Message << "Note: each shared vertex has the same index among the primitives it belongs to" << tcu::TestLog::EndMessage;
+ else if (caseDef.caseType == CASETYPE_PRECISE)
+ log << tcu::TestLog::Message << "Note: the 'precise' qualifier is used to avoid cracks between primitives" << tcu::TestLog::EndMessage;
+ else
+ DE_ASSERT(false);
+
+ // Verify the result.
+
+ const bool ok = verifyResult(log, imagePixelAccess);
+ return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+ }
+}
+
+std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const CaseType caseType)
+{
+ std::ostringstream str;
+ str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode)
+ << (caseType == CASETYPE_PRECISE ? "_precise" : "");
+ return str.str();
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.common_edge.*
+tcu::TestCaseGroup* createCommonEdgeTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "common_edge", "Draw multiple adjacent shapes and check that no cracks appear between them"));
+
+ static const TessPrimitiveType primitiveTypes[] =
+ {
+ TESSPRIMITIVETYPE_TRIANGLES,
+ TESSPRIMITIVETYPE_QUADS,
+ };
+
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitiveTypes); ++primitiveTypeNdx)
+ for (int caseTypeNdx = 0; caseTypeNdx < CASETYPE_LAST; ++caseTypeNdx)
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+ {
+ const TessPrimitiveType primitiveType = primitiveTypes[primitiveTypeNdx];
+ const CaseType caseType = static_cast<CaseType>(caseTypeNdx);
+ const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
+ const CaseDefinition caseDef = { primitiveType, spacingMode, caseType };
+
+ addFunctionCaseWithPrograms(group.get(), getCaseName(primitiveType, spacingMode, caseType), "", initPrograms, test, caseDef);
+ }
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONCOMMONEDGETESTS_HPP
+#define _VKTTESSELLATIONCOMMONEDGETESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Common Edge Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createCommonEdgeTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONCOMMONEDGETESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Coordinates Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationCoordinatesTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuRGBA.hpp"
+#include "tcuSurface.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+template <typename T>
+class SizeLessThan
+{
+public:
+ bool operator() (const T& a, const T& b) const { return a.size() < b.size(); }
+};
+
+std::string getCaseName (const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+ std::ostringstream str;
+ str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getSpacingModeShaderName(spacingMode);
+ return str.str();
+}
+
+std::vector<TessLevels> genTessLevelCases (const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode)
+{
+ static const TessLevels rawTessLevelCases[] =
+ {
+ { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
+ { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
+ { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
+ { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
+ { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
+ { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
+ { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
+ { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
+ { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
+ };
+
+ if (spacingMode == SPACINGMODE_EQUAL)
+ return std::vector<TessLevels>(DE_ARRAY_BEGIN(rawTessLevelCases), DE_ARRAY_END(rawTessLevelCases));
+ else
+ {
+ std::vector<TessLevels> result;
+ result.reserve(DE_LENGTH_OF_ARRAY(rawTessLevelCases));
+
+ for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(rawTessLevelCases); ++tessLevelCaseNdx)
+ {
+ TessLevels curTessLevelCase = rawTessLevelCases[tessLevelCaseNdx];
+
+ float* const inner = &curTessLevelCase.inner[0];
+ float* const outer = &curTessLevelCase.outer[0];
+
+ for (int j = 0; j < 2; ++j) inner[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[j]));
+ for (int j = 0; j < 4; ++j) outer[j] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, outer[j]));
+
+ if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f)
+ {
+ if (inner[0] == 1.0f)
+ inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
+ }
+ }
+ else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
+ {
+ if (outer[0] > 1.0f || outer[1] > 1.0f || outer[2] > 1.0f || outer[3] > 1.0f)
+ {
+ if (inner[0] == 1.0f) inner[0] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[0] + 0.1f));
+ if (inner[1] == 1.0f) inner[1] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, inner[1] + 0.1f));
+ }
+ }
+
+ result.push_back(curTessLevelCase);
+ }
+
+ DE_ASSERT(static_cast<int>(result.size()) == DE_LENGTH_OF_ARRAY(rawTessLevelCases));
+ return result;
+ }
+}
+
+std::vector<tcu::Vec3> generateReferenceTessCoords (const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode,
+ const float* innerLevels,
+ const float* outerLevels)
+{
+ if (isPatchDiscarded(primitiveType, outerLevels))
+ return std::vector<tcu::Vec3>();
+
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ {
+ int inner;
+ int outer[3];
+ getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
+
+ if (spacingMode != SPACINGMODE_EQUAL)
+ {
+ // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
+ DE_ASSERT(de::abs(innerLevels[0] - static_cast<float>(inner)) < 0.001f);
+ for (int i = 0; i < 3; ++i)
+ DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
+ DE_ASSERT(inner > 1 || (outer[0] == 1 && outer[1] == 1 && outer[2] == 1));
+ }
+
+ return generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]);
+ }
+
+ case TESSPRIMITIVETYPE_QUADS:
+ {
+ int inner[2];
+ int outer[4];
+ getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
+
+ if (spacingMode != SPACINGMODE_EQUAL)
+ {
+ // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
+ for (int i = 0; i < 2; ++i)
+ DE_ASSERT(de::abs(innerLevels[i] - static_cast<float>(inner[i])) < 0.001f);
+ for (int i = 0; i < 4; ++i)
+ DE_ASSERT(de::abs(outerLevels[i] - static_cast<float>(outer[i])) < 0.001f);
+
+ DE_ASSERT((inner[0] > 1 && inner[1] > 1) || (inner[0] == 1 && inner[1] == 1 && outer[0] == 1 && outer[1] == 1 && outer[2] == 1 && outer[3] == 1));
+ }
+
+ return generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
+ }
+
+ case TESSPRIMITIVETYPE_ISOLINES:
+ {
+ int outer[2];
+ getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
+
+ if (spacingMode != SPACINGMODE_EQUAL)
+ {
+ // \note For fractional spacing modes, exact results are implementation-defined except in special cases.
+ DE_ASSERT(de::abs(outerLevels[1] - static_cast<float>(outer[1])) < 0.001f);
+ }
+
+ return generateReferenceIsolineTessCoords(outer[0], outer[1]);
+ }
+
+ default:
+ DE_ASSERT(false);
+ return std::vector<tcu::Vec3>();
+ }
+}
+
+void drawPoint (tcu::Surface& dst, const int centerX, const int centerY, const tcu::RGBA& color, const int size)
+{
+ const int width = dst.getWidth();
+ const int height = dst.getHeight();
+ DE_ASSERT(de::inBounds(centerX, 0, width) && de::inBounds(centerY, 0, height));
+ DE_ASSERT(size > 0);
+
+ for (int yOff = -((size-1)/2); yOff <= size/2; ++yOff)
+ for (int xOff = -((size-1)/2); xOff <= size/2; ++xOff)
+ {
+ const int pixX = centerX + xOff;
+ const int pixY = centerY + yOff;
+ if (de::inBounds(pixX, 0, width) && de::inBounds(pixY, 0, height))
+ dst.setPixel(pixX, pixY, color);
+ }
+}
+
+void drawTessCoordPoint (tcu::Surface& dst, const TessPrimitiveType primitiveType, const tcu::Vec3& pt, const tcu::RGBA& color, const int size)
+{
+ // \note These coordinates should match the description in the log message in TessCoordTestInstance::iterate.
+
+ static const tcu::Vec2 triangleCorners[3] =
+ {
+ tcu::Vec2(0.95f, 0.95f),
+ tcu::Vec2(0.5f, 0.95f - 0.9f*deFloatSqrt(3.0f/4.0f)),
+ tcu::Vec2(0.05f, 0.95f)
+ };
+
+ static const float quadIsolineLDRU[4] =
+ {
+ 0.1f, 0.9f, 0.9f, 0.1f
+ };
+
+ const tcu::Vec2 dstPos = primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? pt.x()*triangleCorners[0]
+ + pt.y()*triangleCorners[1]
+ + pt.z()*triangleCorners[2]
+
+ : primitiveType == TESSPRIMITIVETYPE_QUADS ||
+ primitiveType == TESSPRIMITIVETYPE_ISOLINES ? tcu::Vec2((1.0f - pt.x())*quadIsolineLDRU[0] + pt.x()*quadIsolineLDRU[2],
+ (1.0f - pt.y())*quadIsolineLDRU[1] + pt.y()*quadIsolineLDRU[3])
+
+ : tcu::Vec2(-1.0f);
+
+ drawPoint(dst, static_cast<int>(dstPos.x() * dst.getWidth()), static_cast<int>(dstPos.y() * dst.getHeight()), color, size);
+}
+
+void drawTessCoordVisualization (tcu::Surface& dst, const TessPrimitiveType primitiveType, const std::vector<tcu::Vec3>& coords)
+{
+ const int imageWidth = 256;
+ const int imageHeight = 256;
+ dst.setSize(imageWidth, imageHeight);
+
+ tcu::clear(dst.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
+
+ for (int i = 0; i < static_cast<int>(coords.size()); ++i)
+ drawTessCoordPoint(dst, primitiveType, coords[i], tcu::RGBA::white(), 2);
+}
+
+inline bool vec3XLessThan (const tcu::Vec3& a, const tcu::Vec3& b)
+{
+ return a.x() < b.x();
+}
+
+int binarySearchFirstVec3WithXAtLeast (const std::vector<tcu::Vec3>& sorted, float x)
+{
+ const tcu::Vec3 ref(x, 0.0f, 0.0f);
+ const std::vector<tcu::Vec3>::const_iterator first = std::lower_bound(sorted.begin(), sorted.end(), ref, vec3XLessThan);
+ if (first == sorted.end())
+ return -1;
+ return static_cast<int>(std::distance(sorted.begin(), first));
+}
+
+// Check that all points in subset are (approximately) present also in superset.
+bool oneWayComparePointSets (tcu::TestLog& log,
+ tcu::Surface& errorDst,
+ const TessPrimitiveType primitiveType,
+ const std::vector<tcu::Vec3>& subset,
+ const std::vector<tcu::Vec3>& superset,
+ const char* subsetName,
+ const char* supersetName,
+ const tcu::RGBA& errorColor)
+{
+ const std::vector<tcu::Vec3> supersetSorted = sorted(superset, vec3XLessThan);
+ const float epsilon = 0.01f;
+ const int maxNumFailurePrints = 5;
+ int numFailuresDetected = 0;
+
+ for (int subNdx = 0; subNdx < static_cast<int>(subset.size()); ++subNdx)
+ {
+ const tcu::Vec3& subPt = subset[subNdx];
+
+ bool matchFound = false;
+
+ {
+ // Binary search the index of the first point in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
+ const tcu::Vec3 matchMin = subPt - epsilon;
+ const tcu::Vec3 matchMax = subPt + epsilon;
+ const int firstCandidateNdx = binarySearchFirstVec3WithXAtLeast(supersetSorted, matchMin.x());
+
+ if (firstCandidateNdx >= 0)
+ {
+ // Compare subPt to all points in supersetSorted with x in the [subPt.x() - epsilon, subPt.x() + epsilon] range.
+ for (int superNdx = firstCandidateNdx; superNdx < static_cast<int>(supersetSorted.size()) && supersetSorted[superNdx].x() <= matchMax.x(); ++superNdx)
+ {
+ const tcu::Vec3& superPt = supersetSorted[superNdx];
+
+ if (tcu::boolAll(tcu::greaterThanEqual (superPt, matchMin)) &&
+ tcu::boolAll(tcu::lessThanEqual (superPt, matchMax)))
+ {
+ matchFound = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!matchFound)
+ {
+ ++numFailuresDetected;
+ if (numFailuresDetected < maxNumFailurePrints)
+ log << tcu::TestLog::Message << "Failure: no matching " << supersetName << " point found for " << subsetName << " point " << subPt << tcu::TestLog::EndMessage;
+ else if (numFailuresDetected == maxNumFailurePrints)
+ log << tcu::TestLog::Message << "Note: More errors follow" << tcu::TestLog::EndMessage;
+
+ drawTessCoordPoint(errorDst, primitiveType, subPt, errorColor, 4);
+ }
+ }
+
+ return numFailuresDetected == 0;
+}
+
+//! Returns true on matching coordinate sets.
+bool compareTessCoords (tcu::TestLog& log,
+ TessPrimitiveType primitiveType,
+ const std::vector<tcu::Vec3>& refCoords,
+ const std::vector<tcu::Vec3>& resCoords)
+{
+ tcu::Surface refVisual;
+ tcu::Surface resVisual;
+ bool success = true;
+
+ drawTessCoordVisualization(refVisual, primitiveType, refCoords);
+ drawTessCoordVisualization(resVisual, primitiveType, resCoords);
+
+ // Check that all points in reference also exist in result.
+ success = oneWayComparePointSets(log, refVisual, primitiveType, refCoords, resCoords, "reference", "result", tcu::RGBA::blue()) && success;
+ // Check that all points in result also exist in reference.
+ success = oneWayComparePointSets(log, resVisual, primitiveType, resCoords, refCoords, "result", "reference", tcu::RGBA::red()) && success;
+
+ if (!success)
+ {
+ log << tcu::TestLog::Message << "Note: in the following reference visualization, points that are missing in result point set are blue (if any)" << tcu::TestLog::EndMessage
+ << tcu::TestLog::Image("RefTessCoordVisualization", "Reference tessCoord visualization", refVisual)
+ << tcu::TestLog::Message << "Note: in the following result visualization, points that are missing in reference point set are red (if any)" << tcu::TestLog::EndMessage;
+ }
+
+ log << tcu::TestLog::Image("ResTessCoordVisualization", "Result tessCoord visualization", resVisual);
+
+ return success;
+}
+
+class TessCoordTest : public TestCase
+{
+public:
+ TessCoordTest (tcu::TestContext& testCtx,
+ const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode);
+
+ void initPrograms (SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+private:
+ const TessPrimitiveType m_primitiveType;
+ const SpacingMode m_spacingMode;
+};
+
+TessCoordTest::TessCoordTest (tcu::TestContext& testCtx,
+ const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode)
+ : TestCase (testCtx, getCaseName(primitiveType, spacingMode), "")
+ , m_primitiveType (primitiveType)
+ , m_spacingMode (spacingMode)
+{
+}
+
+void TessCoordTest::initPrograms (SourceCollections& programCollection) const
+{
+ // 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 inner0;\n"
+ << " float inner1;\n"
+ << " float outer0;\n"
+ << " float outer1;\n"
+ << " float outer2;\n"
+ << " float outer3;\n"
+ << "} sb_levels;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_TessLevelInner[0] = sb_levels.inner0;\n"
+ << " gl_TessLevelInner[1] = sb_levels.inner1;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = sb_levels.outer0;\n"
+ << " gl_TessLevelOuter[1] = sb_levels.outer1;\n"
+ << " gl_TessLevelOuter[2] = sb_levels.outer2;\n"
+ << " gl_TessLevelOuter[3] = sb_levels.outer3;\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(m_primitiveType) << ", "
+ << getSpacingModeShaderName(m_spacingMode) << ", point_mode) in;\n"
+ << "\n"
+ << "layout(set = 0, binding = 1, std430) coherent restrict buffer Output {\n"
+ << " int numInvocations;\n"
+ << " vec3 tessCoord[];\n" // alignment is 16 bytes, same as vec4
+ << "} sb_out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " int index = atomicAdd(sb_out.numInvocations, 1);\n"
+ << " sb_out.tessCoord[index] = gl_TessCoord;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+}
+
+class TessCoordTestInstance : public TestInstance
+{
+public:
+ TessCoordTestInstance (Context& context,
+ const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode);
+
+ tcu::TestStatus iterate (void);
+
+private:
+ const TessPrimitiveType m_primitiveType;
+ const SpacingMode m_spacingMode;
+};
+
+TessCoordTestInstance::TessCoordTestInstance (Context& context,
+ const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode)
+ : TestInstance (context)
+ , m_primitiveType (primitiveType)
+ , m_spacingMode (spacingMode)
+{
+}
+
+tcu::TestStatus TessCoordTestInstance::iterate (void)
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = m_context.getDefaultAllocator();
+
+ // Test data
+
+ const std::vector<TessLevels> tessLevelCases = genTessLevelCases(m_primitiveType, m_spacingMode);
+ std::vector<std::vector<tcu::Vec3> > allReferenceTessCoords (tessLevelCases.size());
+
+ for (deUint32 i = 0; i < tessLevelCases.size(); ++i)
+ allReferenceTessCoords[i] = generateReferenceTessCoords(m_primitiveType, m_spacingMode, &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]);
+
+ const int maxNumVertices = static_cast<int>(std::max_element(allReferenceTessCoords.begin(), allReferenceTessCoords.end(), SizeLessThan<std::vector<tcu::Vec3> >())->size());
+
+ // Input buffer: tessellation levels. Data is filled in later.
+
+ const Buffer tessLevelsBuffer(vk, device, allocator,
+ makeBufferCreateInfo(sizeof(TessLevels), VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ // Output buffer: number of invocations + padding + tessellation coordinates. Initialized later.
+
+ const int resultBufferTessCoordsOffset = 4 * sizeof(deInt32);
+ const int extraneousVertices = 16; // allow some room for extraneous vertices from duplicate shader invocations (number is arbitrary)
+ const VkDeviceSize resultBufferSizeBytes = resultBufferTessCoordsOffset + (maxNumVertices + extraneousVertices)*sizeof(tcu::Vec4);
+ const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, 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, sizeof(TessLevels));
+ 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: set up vertex processing without rasterization
+
+ const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device));
+ const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+ const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ deUint32 numPassedCases = 0;
+
+ // Repeat the test for all tessellation coords cases
+ for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
+ {
+ m_context.getTestContext().getLog()
+ << tcu::TestLog::Message
+ << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], m_primitiveType)
+ << tcu::TestLog::EndMessage;
+
+ // Upload tessellation levels data to the input buffer
+ {
+ const Allocation& alloc = tessLevelsBuffer.getAllocation();
+ TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
+ *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+ }
+
+ // Clear the results buffer
+ {
+ const Allocation& alloc = resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+ }
+
+ // Reset the command buffer and begin recording.
+ beginCommandBuffer(vk, *cmdBuffer);
+ 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);
+
+ // Process a single abstract vertex.
+ 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 results
+ {
+ const Allocation& resultAlloc = resultBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+ const deInt32 numResults = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+ const std::vector<tcu::Vec3> resultTessCoords = readInterleavedData<tcu::Vec3>(numResults, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
+ const std::vector<tcu::Vec3>& referenceTessCoords = allReferenceTessCoords[tessLevelCaseNdx];
+ const int numExpectedResults = static_cast<int>(referenceTessCoords.size());
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+
+ if (numResults < numExpectedResults)
+ {
+ log << tcu::TestLog::Message
+ << "Failure: generated " << numResults << " coordinates, but the expected reference value is " << numExpectedResults
+ << tcu::TestLog::EndMessage;
+ }
+ else if (numResults == numExpectedResults)
+ log << tcu::TestLog::Message << "Note: generated " << numResults << " tessellation coordinates" << tcu::TestLog::EndMessage;
+ else
+ {
+ log << tcu::TestLog::Message
+ << "Note: generated " << numResults << " coordinates (out of which " << numExpectedResults << " must be unique)"
+ << tcu::TestLog::EndMessage;
+ }
+
+ if (m_primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ log << tcu::TestLog::Message << "Note: in the following visualization(s), the u=1, v=1, w=1 corners are at the right, top, and left corners, respectively" << tcu::TestLog::EndMessage;
+ else if (m_primitiveType == TESSPRIMITIVETYPE_QUADS || m_primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+ log << tcu::TestLog::Message << "Note: in the following visualization(s), u and v coordinate go left-to-right and bottom-to-top, respectively" << tcu::TestLog::EndMessage;
+ else
+ DE_ASSERT(false);
+
+ if (compareTessCoords(log, m_primitiveType, referenceTessCoords, resultTessCoords) && (numResults >= numExpectedResults))
+ ++numPassedCases;
+ }
+ } // for tessLevelCaseNdx
+
+ return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Some cases have failed"));
+}
+
+TestInstance* TessCoordTest::createInstance (Context& context) const
+{
+ requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+ return new TessCoordTestInstance(context, m_primitiveType, m_spacingMode);
+}
+
+} // anonymous
+
+//! Based on dEQP-GLES31.functional.tessellation.tesscoord.*
+//! \note Transform feedback is replaced with SSBO. Because of that, this version allows duplicate coordinates from shader invocations.
+//! The test still fails if not enough coordinates are generated, or if coordinates don't match the reference data.
+tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "tesscoord", "Tessellation coordinates tests"));
+
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+ group->addChild(new TessCoordTest(testCtx, (TessPrimitiveType)primitiveTypeNdx, (SpacingMode)spacingModeNdx));
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONCOORDINATESTESTS_HPP
+#define _VKTTESSELLATIONCOORDINATESTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Coordinates Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createCoordinatesTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONCOORDINATESTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.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) {}
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \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);
+
+ 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, const SpacingMode spacingMode)
+{
+ // 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(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());
+ }
+}
+
+tcu::TestStatus test (Context& context, const SpacingMode spacingMode)
+{
+ DE_ASSERT(spacingMode == SPACINGMODE_FRACTIONAL_ODD || spacingMode == SPACINGMODE_FRACTIONAL_EVEN);
+
+ 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(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 (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+ const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ 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];
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), tessLevelsBufferSizeBytes);
+ }
+
+ // Clear the results buffer
+ {
+ const Allocation& alloc = resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+ }
+
+ 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();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+ 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, 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(), 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(), "odd", "", initPrograms, test, SPACINGMODE_FRACTIONAL_ODD);
+ addFunctionCaseWithPrograms(group.get(), "even", "", initPrograms, test, SPACINGMODE_FRACTIONAL_EVEN);
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONFRACTIONALSPACINGTESTS_HPP
+#define _VKTTESSELLATIONFRACTIONALSPACINGTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createFractionalSpacingTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONFRACTIONALSPACINGTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Invariance Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationInvarianceTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuVectorUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+#include "deRandom.hpp"
+
+#include <string>
+#include <vector>
+#include <set>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+ NUM_TESS_LEVELS = 6, // two inner and four outer levels
+};
+
+enum WindingUsage
+{
+ WINDING_USAGE_CCW = 0,
+ WINDING_USAGE_CW,
+ WINDING_USAGE_VARY,
+
+ WINDING_USAGE_LAST,
+};
+
+inline WindingUsage getWindingUsage (const Winding winding)
+{
+ const WindingUsage usage = winding == WINDING_CCW ? WINDING_USAGE_CCW :
+ winding == WINDING_CW ? WINDING_USAGE_CW : WINDING_USAGE_LAST;
+ DE_ASSERT(usage != WINDING_USAGE_LAST);
+ return usage;
+}
+
+std::vector<Winding> getWindingCases (const WindingUsage windingUsage)
+{
+ std::vector<Winding> cases;
+ switch (windingUsage)
+ {
+ case WINDING_USAGE_CCW:
+ cases.push_back(WINDING_CCW);
+ break;
+ case WINDING_USAGE_CW:
+ cases.push_back(WINDING_CW);
+ break;
+ case WINDING_USAGE_VARY:
+ cases.push_back(WINDING_CCW);
+ cases.push_back(WINDING_CW);
+ break;
+ default:
+ DE_ASSERT(false);
+ break;
+ }
+ return cases;
+}
+
+enum PointModeUsage
+{
+ POINT_MODE_USAGE_DONT_USE = 0,
+ POINT_MODE_USAGE_USE,
+ POINT_MODE_USAGE_VARY,
+
+ POINT_MODE_USAGE_LAST,
+};
+
+inline PointModeUsage getPointModeUsage (const bool usePointMode)
+{
+ return usePointMode ? POINT_MODE_USAGE_USE : POINT_MODE_USAGE_DONT_USE;
+}
+
+std::vector<bool> getUsePointModeCases (const PointModeUsage pointModeUsage)
+{
+ std::vector<bool> cases;
+ switch (pointModeUsage)
+ {
+ case POINT_MODE_USAGE_DONT_USE:
+ cases.push_back(false);
+ break;
+ case POINT_MODE_USAGE_USE:
+ cases.push_back(true);
+ break;
+ case POINT_MODE_USAGE_VARY:
+ cases.push_back(false);
+ cases.push_back(true);
+ break;
+ default:
+ DE_ASSERT(false);
+ break;
+ }
+ return cases;
+}
+
+//! Data captured in the shader per output primitive (in geometry stage).
+struct PerPrimitive
+{
+ deInt32 patchPrimitiveID; //!< gl_PrimitiveID in tessellation evaluation shader
+ deInt32 primitiveID; //!< ID of an output primitive in geometry shader (user-defined)
+
+ deInt32 unused_padding[2];
+
+ tcu::Vec4 tessCoord[3]; //!< 3 coords for triangles/quads, 2 for isolines, 1 for point mode. Vec4 due to alignment.
+};
+
+typedef std::vector<PerPrimitive> PerPrimitiveVec;
+
+inline bool byPatchPrimitiveID (const PerPrimitive& a, const PerPrimitive& b)
+{
+ return a.patchPrimitiveID < b.patchPrimitiveID;
+}
+
+inline std::string getProgramName (const std::string& baseName, const Winding winding, const bool usePointMode)
+{
+ std::ostringstream str;
+ str << baseName << "_" << getWindingShaderName(winding) << (usePointMode ? "_point_mode" : "");
+ return str.str();
+}
+
+inline std::string getProgramName (const std::string& baseName, const bool usePointMode)
+{
+ std::ostringstream str;
+ str << baseName << (usePointMode ? "_point_mode" : "");
+ return str.str();
+}
+
+inline std::string getProgramDescription (const Winding winding, const bool usePointMode)
+{
+ std::ostringstream str;
+ str << "winding mode " << getWindingShaderName(winding) << ", " << (usePointMode ? "" : "don't ") << "use point mode";
+ return str.str();
+};
+
+template <typename T, int N>
+std::vector<T> arrayToVector (const T (&arr)[N])
+{
+ return std::vector<T>(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
+}
+
+template <typename T, int N>
+T arrayMax (const T (&arr)[N])
+{
+ return *std::max_element(DE_ARRAY_BEGIN(arr), DE_ARRAY_END(arr));
+}
+
+template <int Size>
+inline tcu::Vector<bool, Size> singleTrueMask (int index)
+{
+ DE_ASSERT(de::inBounds(index, 0, Size));
+ tcu::Vector<bool, Size> result;
+ result[index] = true;
+ return result;
+}
+
+template <typename ContainerT, typename T>
+inline bool contains (const ContainerT& c, const T& key)
+{
+ return c.find(key) != c.end();
+}
+
+template <typename SeqT, int Size, typename Pred>
+class LexCompare
+{
+public:
+ LexCompare (void) : m_pred(Pred()) {}
+
+ bool operator() (const SeqT& a, const SeqT& b) const
+ {
+ for (int i = 0; i < Size; ++i)
+ {
+ if (m_pred(a[i], b[i]))
+ return true;
+ if (m_pred(b[i], a[i]))
+ return false;
+ }
+ return false;
+ }
+
+private:
+ Pred m_pred;
+};
+
+template <int Size>
+class VecLexLessThan : public LexCompare<tcu::Vector<float, Size>, Size, std::less<float> >
+{
+};
+
+//! Add default programs for invariance tests.
+//! Creates multiple shader programs for combinations of winding and point mode.
+//! mirrorCoords - special mode where some tessellation coordinates are mirrored in tessellation evaluation shader.
+//! This is used by symmetric outer edge test.
+void addDefaultPrograms (vk::SourceCollections& programCollection,
+ const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode,
+ const WindingUsage windingUsage,
+ const PointModeUsage pointModeUsage,
+ const bool mirrorCoords = false)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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(location = 0) in highp float in_tc_attr[];\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_TessLevelInner[0] = in_tc_attr[0];\n"
+ << " gl_TessLevelInner[1] = in_tc_attr[1];\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+ << " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+ << " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+ << " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ const std::string perVertexInterfaceBlock = \
+ "VertexData {\n" // no in/out qualifier
+ " vec4 in_gs_tessCoord;\n" // w component is used by mirroring test
+ " int in_gs_primitiveID;\n"
+ "}"; // no newline nor semicolon
+
+ // Alternative tess coordinates handling code
+ std::ostringstream tessEvalCoordSrc;
+ if (mirrorCoords)
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ tessEvalCoordSrc << " float x = gl_TessCoord.x;\n"
+ << " float y = gl_TessCoord.y;\n"
+ << " float z = gl_TessCoord.z;\n"
+ << "\n"
+ << " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
+ << " ib_out.in_gs_tessCoord = z == 0.0 && x > 0.5 && x != 1.0 ? vec4(1.0-x, 1.0-y, 0.0, 1.0)\n"
+ << " : y == 0.0 && z > 0.5 && z != 1.0 ? vec4(1.0-x, 0.0, 1.0-z, 1.0)\n"
+ << " : x == 0.0 && y > 0.5 && y != 1.0 ? vec4( 0.0, 1.0-y, 1.0-z, 1.0)\n"
+ << " : vec4(x, y, z, 0.0);\n";
+ break;
+ case TESSPRIMITIVETYPE_QUADS:
+ tessEvalCoordSrc << " float x = gl_TessCoord.x;\n"
+ << " float y = gl_TessCoord.y;\n"
+ << "\n"
+ << " // Mirror one half of each outer edge onto the other half, except the endpoints (because they belong to two edges)\n"
+ << " ib_out.in_gs_tessCoord = (x == 0.0 || x == 1.0) && y > 0.5 && y != 1.0 ? vec4( x, 1.0-y, 0.0, 1.0)\n"
+ << " : (y == 0.0 || y == 1.0) && x > 0.5 && x != 1.0 ? vec4(1.0-x, y, 0.0, 1.0)\n"
+ << " : vec4(x, y, 0.0, 0.0);\n";
+ break;
+ case TESSPRIMITIVETYPE_ISOLINES:
+ tessEvalCoordSrc << " float x = gl_TessCoord.x;\n"
+ << " float y = gl_TessCoord.y;\n"
+ << "\n"
+ << " // Mirror one half of each outer edge onto the other half\n"
+ << " ib_out.in_gs_tessCoord = (x == 0.0 || x == 1.0) && y > 0.5 ? vec4(x, 1.0-y, 0.0, 1.0)\n"
+ << " : vec4(x, y, 0.0, 0.0);\n";
+ break;
+ default:
+ DE_ASSERT(false);
+ return;
+ }
+ else
+ tessEvalCoordSrc << " ib_out.in_gs_tessCoord = vec4(gl_TessCoord, 0.0);\n";
+
+ const std::vector<Winding> windingCases = getWindingCases(windingUsage);
+ const std::vector<bool> usePointModeCases = getUsePointModeCases(pointModeUsage);
+
+ for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter)
+ for (std::vector<bool>::const_iterator usePointModeIter = usePointModeCases.begin(); usePointModeIter != usePointModeCases.end(); ++usePointModeIter)
+ {
+ // 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(primitiveType) << ", "
+ << getSpacingModeShaderName(spacingMode) << ", "
+ << getWindingShaderName(*windingIter)
+ << (*usePointModeIter ? ", point_mode" : "") << ") in;\n"
+ << "\n"
+ << "layout(location = 0) out " << perVertexInterfaceBlock << " ib_out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << tessEvalCoordSrc.str()
+ << " ib_out.in_gs_primitiveID = gl_PrimitiveID;\n"
+ << "}\n";
+
+ programCollection.glslSources.add(getProgramName("tese", *windingIter, *usePointModeIter)) << glu::TessellationEvaluationSource(src.str());
+ }
+ } // for windingNdx, usePointModeNdx
+
+ // Geometry shader: data is captured here.
+ {
+ for (std::vector<bool>::const_iterator usePointModeIter = usePointModeCases.begin(); usePointModeIter != usePointModeCases.end(); ++usePointModeIter)
+ {
+ const int numVertices = numVerticesPerPrimitive(primitiveType, *usePointModeIter); // Primitives that the tessellated patch comprises of.
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_geometry_shader : require\n"
+ << "\n"
+ << "layout(" << getGeometryShaderInputPrimitiveTypeShaderName(primitiveType, *usePointModeIter) << ") in;\n"
+ << "layout(" << getGeometryShaderOutputPrimitiveTypeShaderName(primitiveType, *usePointModeIter) << ", max_vertices = " << numVertices << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in " << perVertexInterfaceBlock << " ib_in[];\n"
+ << "\n"
+ << "struct PerPrimitive {\n"
+ << " int patchPrimitiveID;\n"
+ << " int primitiveID;\n"
+ << " vec4 tessCoord[3];\n"
+ << "};\n"
+ << "\n"
+ << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+ << " int numPrimitives;\n"
+ << " PerPrimitive primitive[];\n"
+ << "} sb_out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " int index = atomicAdd(sb_out.numPrimitives, 1);\n"
+ << " sb_out.primitive[index].patchPrimitiveID = ib_in[0].in_gs_primitiveID;\n"
+ << " sb_out.primitive[index].primitiveID = index;\n";
+ for (int i = 0; i < numVertices; ++i)
+ src << " sb_out.primitive[index].tessCoord[" << i << "] = ib_in[" << i << "].in_gs_tessCoord;\n";
+ for (int i = 0; i < numVertices; ++i)
+ src << "\n"
+ << " gl_Position = vec4(0.0);\n"
+ << " EmitVertex();\n";
+ src << "}\n";
+
+ programCollection.glslSources.add(getProgramName("geom", *usePointModeIter)) << glu::GeometrySource(src.str());
+ }
+ }
+}
+
+//! A description of an outer edge of a triangle, quad or isolines.
+//! An outer edge can be described by the index of a u/v/w coordinate
+//! and the coordinate's value along that edge.
+struct OuterEdgeDescription
+{
+ int constantCoordinateIndex;
+ float constantCoordinateValueChoices[2];
+ int numConstantCoordinateValueChoices;
+
+ OuterEdgeDescription (const int i, const float c0) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(1) { constantCoordinateValueChoices[0] = c0; }
+ OuterEdgeDescription (const int i, const float c0, const float c1) : constantCoordinateIndex(i), numConstantCoordinateValueChoices(2) { constantCoordinateValueChoices[0] = c0; constantCoordinateValueChoices[1] = c1; }
+
+ std::string description (void) const
+ {
+ static const char* const coordinateNames[] = { "u", "v", "w" };
+ std::string result;
+ for (int i = 0; i < numConstantCoordinateValueChoices; ++i)
+ result += std::string() + (i > 0 ? " or " : "") + coordinateNames[constantCoordinateIndex] + "=" + de::toString(constantCoordinateValueChoices[i]);
+ return result;
+ }
+
+ bool contains (const tcu::Vec3& v) const
+ {
+ for (int i = 0; i < numConstantCoordinateValueChoices; ++i)
+ if (v[constantCoordinateIndex] == constantCoordinateValueChoices[i])
+ return true;
+ return false;
+ }
+};
+
+std::vector<OuterEdgeDescription> outerEdgeDescriptions (const TessPrimitiveType primType)
+{
+ static const OuterEdgeDescription triangleOuterEdgeDescriptions[3] =
+ {
+ OuterEdgeDescription(0, 0.0f),
+ OuterEdgeDescription(1, 0.0f),
+ OuterEdgeDescription(2, 0.0f)
+ };
+
+ static const OuterEdgeDescription quadOuterEdgeDescriptions[4] =
+ {
+ OuterEdgeDescription(0, 0.0f),
+ OuterEdgeDescription(1, 0.0f),
+ OuterEdgeDescription(0, 1.0f),
+ OuterEdgeDescription(1, 1.0f)
+ };
+
+ static const OuterEdgeDescription isolinesOuterEdgeDescriptions[1] =
+ {
+ OuterEdgeDescription(0, 0.0f, 1.0f),
+ };
+
+ switch (primType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES: return arrayToVector(triangleOuterEdgeDescriptions);
+ case TESSPRIMITIVETYPE_QUADS: return arrayToVector(quadOuterEdgeDescriptions);
+ case TESSPRIMITIVETYPE_ISOLINES: return arrayToVector(isolinesOuterEdgeDescriptions);
+
+ default:
+ DE_ASSERT(false);
+ return std::vector<OuterEdgeDescription>();
+ }
+}
+
+namespace InvariantOuterEdge
+{
+
+struct CaseDefinition
+{
+ TessPrimitiveType primitiveType;
+ SpacingMode spacingMode;
+ Winding winding;
+ bool usePointMode;
+};
+
+typedef std::set<tcu::Vec3, VecLexLessThan<3> > Vec3Set;
+
+std::vector<float> generateRandomPatchTessLevels (const int numPatches, const int constantOuterLevelIndex, const float constantOuterLevel, de::Random& rnd)
+{
+ std::vector<float> tessLevels(numPatches*NUM_TESS_LEVELS);
+
+ for (int patchNdx = 0; patchNdx < numPatches; ++patchNdx)
+ {
+ float* const inner = &tessLevels[patchNdx*NUM_TESS_LEVELS + 0];
+ float* const outer = &tessLevels[patchNdx*NUM_TESS_LEVELS + 2];
+
+ for (int j = 0; j < 2; ++j)
+ inner[j] = rnd.getFloat(1.0f, 62.0f);
+ for (int j = 0; j < 4; ++j)
+ outer[j] = j == constantOuterLevelIndex ? constantOuterLevel : rnd.getFloat(1.0f, 62.0f);
+ }
+
+ return tessLevels;
+}
+
+std::vector<float> generatePatchTessLevels (const int numPatches, const int constantOuterLevelIndex, const float constantOuterLevel)
+{
+ de::Random rnd(123);
+ return generateRandomPatchTessLevels(numPatches, constantOuterLevelIndex, constantOuterLevel, rnd);
+}
+
+int multiplePatchReferencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* levels, int numPatches)
+{
+ int result = 0;
+ for (int patchNdx = 0; patchNdx < numPatches; ++patchNdx)
+ result += referencePrimitiveCount(primitiveType, spacingMode, usePointMode, &levels[NUM_TESS_LEVELS*patchNdx + 0], &levels[NUM_TESS_LEVELS*patchNdx + 2]);
+ return result;
+}
+
+template<std::size_t N>
+int computeMaxPrimitiveCount (const int numPatchesToDraw, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float (&singleOuterEdgeLevels)[N])
+{
+ const int outerEdgeIndex = 0; // outer-edge index doesn't affect vertex count
+ const std::vector<float> patchTessLevels = generatePatchTessLevels(numPatchesToDraw, outerEdgeIndex, arrayMax(singleOuterEdgeLevels));
+ return multiplePatchReferencePrimitiveCount(primitiveType, spacingMode, usePointMode, &patchTessLevels[0], numPatchesToDraw);
+}
+
+void logOuterTessellationLevel (tcu::TestLog& log, const float tessLevel, const OuterEdgeDescription& edgeDesc)
+{
+ log << tcu::TestLog::Message
+ << "Testing with outer tessellation level " << tessLevel << " for the " << edgeDesc.description() << " edge, and with various levels for other edges, and with all programs"
+ << tcu::TestLog::EndMessage;
+}
+
+void logPrimitiveCountError (tcu::TestLog& log, const int numPatchesToDraw, int numPrimitives, const int refNumPrimitives, const std::vector<float>& patchTessLevels)
+{
+ log << tcu::TestLog::Message
+ << "Failure: the number of generated primitives is " << numPrimitives << ", expected at least " << refNumPrimitives
+ << tcu::TestLog::EndMessage;
+
+ if (numPatchesToDraw == 1)
+ log << tcu::TestLog::Message
+ << "Note: rendered one patch; tessellation levels are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
+ << containerStr(patchTessLevels, NUM_TESS_LEVELS)
+ << tcu::TestLog::EndMessage;
+ else
+ log << tcu::TestLog::Message
+ << "Note: rendered " << numPatchesToDraw << " patches in one draw call; "
+ << "tessellation levels for each patch are (in order [inner0, inner1, outer0, outer1, outer2, outer3]):\n"
+ << containerStr(patchTessLevels, NUM_TESS_LEVELS)
+ << tcu::TestLog::EndMessage;
+}
+
+class BaseTestInstance : public TestInstance
+{
+public:
+ struct DrawResult
+ {
+ bool success;
+ int refNumPrimitives;
+ int numPrimitiveVertices;
+ deInt32 numPrimitives;
+ PerPrimitiveVec primitives;
+ };
+
+ BaseTestInstance (Context& context, const CaseDefinition caseDef, const int numPatchesToDraw);
+ DrawResult draw (const deUint32 vertexCount, const std::vector<float>& patchTessLevels, const Winding winding, const bool usePointMode);
+ void uploadVertexAttributes (const std::vector<float>& vertexData);
+
+protected:
+ static const float m_singleOuterEdgeLevels[];
+
+ const CaseDefinition m_caseDef;
+ const int m_numPatchesToDraw;
+ const VkFormat m_vertexFormat;
+ const deUint32 m_vertexStride;
+ const std::vector<OuterEdgeDescription> m_edgeDescriptions;
+ const int m_maxNumPrimitivesInDrawCall;
+ const VkDeviceSize m_vertexDataSizeBytes;
+ const Buffer m_vertexBuffer;
+ const int m_resultBufferPrimitiveDataOffset;
+ const VkDeviceSize m_resultBufferSizeBytes;
+ const Buffer m_resultBuffer;
+ Unique<VkDescriptorSetLayout> m_descriptorSetLayout;
+ Unique<VkDescriptorPool> m_descriptorPool;
+ Unique<VkDescriptorSet> m_descriptorSet;
+ Unique<VkRenderPass> m_renderPass;
+ Unique<VkFramebuffer> m_framebuffer;
+ Unique<VkPipelineLayout> m_pipelineLayout;
+ Unique<VkCommandPool> m_cmdPool;
+ Unique<VkCommandBuffer> m_cmdBuffer;
+};
+
+const float BaseTestInstance::m_singleOuterEdgeLevels[] = { 1.0f, 1.2f, 1.9f, 2.3f, 2.8f, 3.3f, 3.8f, 10.2f, 1.6f, 24.4f, 24.7f, 63.0f };
+
+BaseTestInstance::BaseTestInstance (Context& context, const CaseDefinition caseDef, const int numPatchesToDraw)
+ : TestInstance (context)
+ , m_caseDef (caseDef)
+ , m_numPatchesToDraw (numPatchesToDraw)
+ , m_vertexFormat (VK_FORMAT_R32_SFLOAT)
+ , m_vertexStride (tcu::getPixelSize(mapVkFormat(m_vertexFormat)))
+ , m_edgeDescriptions (outerEdgeDescriptions(m_caseDef.primitiveType))
+ , m_maxNumPrimitivesInDrawCall (computeMaxPrimitiveCount(m_numPatchesToDraw, caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode, m_singleOuterEdgeLevels))
+ , m_vertexDataSizeBytes (NUM_TESS_LEVELS * m_numPatchesToDraw * m_vertexStride)
+ , m_vertexBuffer (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(),
+ makeBufferCreateInfo(m_vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible)
+ , m_resultBufferPrimitiveDataOffset (sizeof(deInt32) * 4)
+ , m_resultBufferSizeBytes (m_resultBufferPrimitiveDataOffset + m_maxNumPrimitivesInDrawCall * sizeof(PerPrimitive))
+ , m_resultBuffer (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getDefaultAllocator(),
+ makeBufferCreateInfo(m_resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible)
+ , m_descriptorSetLayout (DescriptorSetLayoutBuilder()
+ .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT)
+ .build(m_context.getDeviceInterface(), m_context.getDevice()))
+ , m_descriptorPool (DescriptorPoolBuilder()
+ .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+ .build(m_context.getDeviceInterface(), m_context.getDevice(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u))
+ , m_descriptorSet (makeDescriptorSet(m_context.getDeviceInterface(), m_context.getDevice(), *m_descriptorPool, *m_descriptorSetLayout))
+ , m_renderPass (makeRenderPassWithoutAttachments (m_context.getDeviceInterface(), m_context.getDevice()))
+ , m_framebuffer (makeFramebufferWithoutAttachments(m_context.getDeviceInterface(), m_context.getDevice(), *m_renderPass))
+ , m_pipelineLayout (makePipelineLayout (m_context.getDeviceInterface(), m_context.getDevice(), *m_descriptorSetLayout))
+ , m_cmdPool (makeCommandPool (m_context.getDeviceInterface(), m_context.getDevice(), m_context.getUniversalQueueFamilyIndex()))
+ , m_cmdBuffer (makeCommandBuffer (m_context.getDeviceInterface(), m_context.getDevice(), *m_cmdPool))
+{
+ requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(),
+ FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+ const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(m_resultBuffer.get(), 0ull, m_resultBufferSizeBytes);
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*m_descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+ .update(m_context.getDeviceInterface(), m_context.getDevice());
+}
+
+//! patchTessLevels are tessellation levels for all drawn patches.
+BaseTestInstance::DrawResult BaseTestInstance::draw (const deUint32 vertexCount, const std::vector<float>& patchTessLevels, const Winding winding, const bool usePointMode)
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setPatchControlPoints (NUM_TESS_LEVELS)
+ .setVertexInputSingleAttribute(m_vertexFormat, m_vertexStride)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(getProgramName("tese", winding, usePointMode)), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get(getProgramName("geom", usePointMode)), DE_NULL)
+ .build (vk, device, *m_pipelineLayout, *m_renderPass));
+
+ {
+ const Allocation& alloc = m_resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(m_resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), m_resultBufferSizeBytes);
+ }
+
+ beginCommandBuffer(vk, *m_cmdBuffer);
+ beginRenderPassWithRasterizationDisabled(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer);
+
+ vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+ vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayout, 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ vk.cmdDraw(*m_cmdBuffer, vertexCount, 1u, 0u, 0u);
+ endRenderPass(vk, *m_cmdBuffer);
+
+ {
+ const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *m_resultBuffer, 0ull, m_resultBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *m_cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
+
+ // Read back and check results
+
+ const Allocation& resultAlloc = m_resultBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), m_resultBufferSizeBytes);
+
+ DrawResult result;
+ result.success = true;
+ result.refNumPrimitives = multiplePatchReferencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, usePointMode, &patchTessLevels[0], m_numPatchesToDraw);
+ result.numPrimitiveVertices = numVerticesPerPrimitive(m_caseDef.primitiveType, usePointMode);
+ result.numPrimitives = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+ result.primitives = sorted(readInterleavedData<PerPrimitive>(result.numPrimitives, resultAlloc.getHostPtr(), m_resultBufferPrimitiveDataOffset, sizeof(PerPrimitive)),
+ byPatchPrimitiveID);
+
+ // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+ DE_ASSERT(result.numPrimitives <= m_maxNumPrimitivesInDrawCall);
+
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ if (result.numPrimitives != result.refNumPrimitives)
+ {
+ logPrimitiveCountError(log, m_numPatchesToDraw, result.numPrimitives, result.refNumPrimitives, patchTessLevels);
+ result.success = false;
+ }
+ return result;
+}
+
+void BaseTestInstance::uploadVertexAttributes (const std::vector<float>& vertexData)
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+
+ const Allocation& alloc = m_vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), &vertexData[0], sizeInBytes(vertexData));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeInBytes(vertexData));
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #2
+ *
+ * Test that the set of vertices along an outer edge of a quad or triangle
+ * only depends on that edge's tessellation level, and spacing.
+ *
+ * For each (outer) edge in the quad or triangle, draw multiple patches
+ * with identical tessellation levels for that outer edge but with
+ * different values for the other outer edges; compare, among the
+ * primitives, the vertices generated for that outer edge. Repeat with
+ * different programs, using different winding etc. settings. Compare
+ * the edge's vertices between different programs.
+ *//*--------------------------------------------------------------------*/
+class OuterEdgeDivisionTestInstance : public BaseTestInstance
+{
+public:
+ OuterEdgeDivisionTestInstance (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 10) {}
+ tcu::TestStatus iterate (void);
+};
+
+tcu::TestStatus OuterEdgeDivisionTestInstance::iterate (void)
+{
+ for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex)
+ for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx)
+ {
+ const OuterEdgeDescription& edgeDesc = m_edgeDescriptions[outerEdgeIndex];
+ const std::vector<float> patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
+
+ Vec3Set firstOuterEdgeVertices; // Vertices of the outer edge of the first patch of the first program's draw call; used for comparison with other patches.
+
+ uploadVertexAttributes(patchTessLevels);
+ logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc);
+
+ for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
+ for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx)
+ {
+ const Winding winding = static_cast<Winding>(windingNdx);
+ const bool usePointMode = (usePointModeNdx != 0);
+ const bool isFirstProgram = (windingNdx == 0 && usePointModeNdx == 0);
+
+ const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, winding, usePointMode);
+
+ if (!result.success)
+ return tcu::TestStatus::fail("Invalid set of vertices");
+
+ // Check the vertices of each patch.
+
+ int primitiveNdx = 0;
+ for (int patchNdx = 0; patchNdx < m_numPatchesToDraw; ++patchNdx)
+ {
+ DE_ASSERT(primitiveNdx < result.numPrimitives);
+
+ const float* const innerLevels = &patchTessLevels[NUM_TESS_LEVELS*patchNdx + 0];
+ const float* const outerLevels = &patchTessLevels[NUM_TESS_LEVELS*patchNdx + 2];
+
+ Vec3Set outerEdgeVertices;
+
+ // We're interested in just the vertices on the current outer edge.
+ for (; primitiveNdx < result.numPrimitives && result.primitives[primitiveNdx].patchPrimitiveID == patchNdx; ++primitiveNdx)
+ for (int i = 0; i < result.numPrimitiveVertices; ++i)
+ {
+ const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2);
+ if (edgeDesc.contains(coord))
+ outerEdgeVertices.insert(coord);
+ }
+
+ // Compare the vertices to those of the first patch (unless this is the first patch).
+
+ if (isFirstProgram && patchNdx == 0)
+ firstOuterEdgeVertices = outerEdgeVertices;
+ else if (firstOuterEdgeVertices != outerEdgeVertices)
+ {
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+
+ log << tcu::TestLog::Message
+ << "Failure: vertices generated for the edge differ between the following cases:\n"
+ << " - case A: " << getProgramDescription((Winding)0, (bool)0) << ", tessellation levels: "
+ << getTessellationLevelsString(&patchTessLevels[0], &patchTessLevels[2]) << "\n"
+ << " - case B: " << getProgramDescription(winding, usePointMode) << ", tessellation levels: "
+ << getTessellationLevelsString(innerLevels, outerLevels)
+ << tcu::TestLog::EndMessage;
+
+ log << tcu::TestLog::Message
+ << "Note: resulting vertices for the edge for the cases were:\n"
+ << " - case A: " << containerStr(firstOuterEdgeVertices, 5, 14) << "\n"
+ << " - case B: " << containerStr(outerEdgeVertices, 5, 14)
+ << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of vertices");
+ }
+ }
+ DE_ASSERT(primitiveNdx == result.numPrimitives);
+ } // for windingNdx, usePointModeNdx
+ } // for outerEdgeIndex, outerEdgeLevelCaseNdx
+
+ return tcu::TestStatus::pass("OK");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #4
+ *
+ * Test that the vertices on an outer edge don't depend on which of the
+ * edges it is, other than with respect to component order.
+ *//*--------------------------------------------------------------------*/
+class OuterEdgeIndexIndependenceTestInstance : public BaseTestInstance
+{
+public:
+ OuterEdgeIndexIndependenceTestInstance (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {}
+ tcu::TestStatus iterate (void);
+};
+
+tcu::TestStatus OuterEdgeIndexIndependenceTestInstance::iterate (void)
+{
+ for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx)
+ {
+ Vec3Set firstEdgeVertices;
+
+ for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex)
+ {
+ const OuterEdgeDescription& edgeDesc = m_edgeDescriptions[outerEdgeIndex];
+ const std::vector<float> patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
+
+ uploadVertexAttributes(patchTessLevels);
+ logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc);
+ const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, m_caseDef.winding, m_caseDef.usePointMode);
+
+ // Verify case result
+
+ if (!result.success)
+ return tcu::TestStatus::fail("Invalid set of vertices");
+
+ Vec3Set currentEdgeVertices;
+
+ // Get the vertices on the current outer edge.
+ for (int primitiveNdx = 0; primitiveNdx < result.numPrimitives; ++primitiveNdx)
+ for (int i = 0; i < result.numPrimitiveVertices; ++i)
+ {
+ const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2);
+ if (edgeDesc.contains(coord))
+ {
+ // Swizzle components to match the order of the first edge.
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ currentEdgeVertices.insert(outerEdgeIndex == 0 ? coord :
+ outerEdgeIndex == 1 ? coord.swizzle(1, 0, 2) :
+ outerEdgeIndex == 2 ? coord.swizzle(2, 1, 0) : tcu::Vec3(-1.0f));
+ else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+ currentEdgeVertices.insert(tcu::Vec3(outerEdgeIndex == 0 ? coord.y() :
+ outerEdgeIndex == 1 ? coord.x() :
+ outerEdgeIndex == 2 ? coord.y() :
+ outerEdgeIndex == 3 ? coord.x() : -1.0f,
+ 0.0f, 0.0f));
+ else
+ DE_ASSERT(false);
+ }
+ }
+
+ if (outerEdgeIndex == 0)
+ firstEdgeVertices = currentEdgeVertices;
+ else
+ {
+ // Compare vertices of this edge to those of the first edge.
+ if (currentEdgeVertices != firstEdgeVertices)
+ {
+ const char* const swizzleDesc =
+ m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? (outerEdgeIndex == 1 ? "(y, x, z)" :
+ outerEdgeIndex == 2 ? "(z, y, x)" : DE_NULL) :
+ m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ? (outerEdgeIndex == 1 ? "(x, 0)" :
+ outerEdgeIndex == 2 ? "(y, 0)" :
+ outerEdgeIndex == 3 ? "(x, 0)" : DE_NULL)
+ : DE_NULL;
+
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ log << tcu::TestLog::Message
+ << "Failure: the set of vertices on the " << edgeDesc.description() << " edge"
+ << " doesn't match the set of vertices on the " << m_edgeDescriptions[0].description() << " edge"
+ << tcu::TestLog::EndMessage;
+
+ log << tcu::TestLog::Message
+ << "Note: set of vertices on " << edgeDesc.description() << " edge, components swizzled like " << swizzleDesc
+ << " to match component order on first edge:\n" << containerStr(currentEdgeVertices, 5)
+ << "\non " << m_edgeDescriptions[0].description() << " edge:\n" << containerStr(firstEdgeVertices, 5)
+ << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of vertices");
+ }
+ }
+ }
+ }
+ return tcu::TestStatus::pass("OK");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #3
+ *
+ * Test that the vertices along an outer edge are placed symmetrically.
+ *
+ * Draw multiple patches with different tessellation levels and different
+ * point_mode, winding etc. Before outputting tesscoords from shader, mirror
+ * the vertices in the TES such that every vertex on an outer edge -
+ * except the possible middle vertex - should be duplicated in the output.
+ * Check that appropriate duplicates exist.
+ *//*--------------------------------------------------------------------*/
+class SymmetricOuterEdgeTestInstance : public BaseTestInstance
+{
+public:
+ SymmetricOuterEdgeTestInstance (Context& context, const CaseDefinition caseDef) : BaseTestInstance (context, caseDef, 1) {}
+ tcu::TestStatus iterate (void);
+};
+
+tcu::TestStatus SymmetricOuterEdgeTestInstance::iterate (void)
+{
+ for (int outerEdgeIndex = 0; outerEdgeIndex < static_cast<int>(m_edgeDescriptions.size()); ++outerEdgeIndex)
+ for (int outerEdgeLevelCaseNdx = 0; outerEdgeLevelCaseNdx < DE_LENGTH_OF_ARRAY(m_singleOuterEdgeLevels); ++outerEdgeLevelCaseNdx)
+ {
+ const OuterEdgeDescription& edgeDesc = m_edgeDescriptions[outerEdgeIndex];
+ const std::vector<float> patchTessLevels = generatePatchTessLevels(m_numPatchesToDraw, outerEdgeIndex, m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx]);
+
+ uploadVertexAttributes(patchTessLevels);
+ logOuterTessellationLevel(m_context.getTestContext().getLog(), m_singleOuterEdgeLevels[outerEdgeLevelCaseNdx], edgeDesc);
+ const DrawResult result = draw(static_cast<deUint32>(patchTessLevels.size()), patchTessLevels, m_caseDef.winding, m_caseDef.usePointMode);
+
+ // Verify case result
+
+ if (!result.success)
+ return tcu::TestStatus::fail("Invalid set of vertices");
+
+ Vec3Set nonMirroredEdgeVertices;
+ Vec3Set mirroredEdgeVertices;
+
+ // Get the vertices on the current outer edge.
+ for (int primitiveNdx = 0; primitiveNdx < result.numPrimitives; ++primitiveNdx)
+ for (int i = 0; i < result.numPrimitiveVertices; ++i)
+ {
+ const tcu::Vec3& coord = result.primitives[primitiveNdx].tessCoord[i].swizzle(0, 1, 2);
+ if (edgeDesc.contains(coord))
+ {
+ // Ignore the middle vertex of the outer edge, as it's exactly at the mirroring point;
+ // for isolines, also ignore (0, 0) and (1, 0) because there's no mirrored counterpart for them.
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES &&
+ coord == tcu::select(tcu::Vec3(0.0f), tcu::Vec3(0.5f), singleTrueMask<3>(edgeDesc.constantCoordinateIndex)))
+ continue;
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS &&
+ coord.swizzle(0,1) == tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(0.5f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex)))
+ continue;
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_ISOLINES &&
+ (coord == tcu::Vec3(0.0f, 0.5f, 0.0f) || coord == tcu::Vec3(1.0f, 0.5f, 0.0f) || coord == tcu::Vec3(0.0f, 0.0f, 0.0f) || coord == tcu::Vec3(1.0f, 0.0f, 0.0f)))
+ continue;
+
+ const bool isMirrored = result.primitives[primitiveNdx].tessCoord[i].w() > 0.5f;
+ if (isMirrored)
+ mirroredEdgeVertices.insert(coord);
+ else
+ nonMirroredEdgeVertices.insert(coord);
+ }
+ }
+
+ if (m_caseDef.primitiveType != TESSPRIMITIVETYPE_ISOLINES)
+ {
+ // Check that both endpoints are present. Note that endpoints aren't mirrored by the shader, since they belong to more than one edge.
+
+ tcu::Vec3 endpointA;
+ tcu::Vec3 endpointB;
+
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ endpointA = tcu::select(tcu::Vec3(1.0f), tcu::Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 1) % 3));
+ endpointB = tcu::select(tcu::Vec3(1.0f), tcu::Vec3(0.0f), singleTrueMask<3>((edgeDesc.constantCoordinateIndex + 2) % 3));
+ }
+ else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+ {
+ endpointA.xy() = tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(0.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
+ endpointB.xy() = tcu::select(tcu::Vec2(edgeDesc.constantCoordinateValueChoices[0]), tcu::Vec2(1.0f), singleTrueMask<2>(edgeDesc.constantCoordinateIndex));
+ }
+ else
+ DE_ASSERT(false);
+
+ if (!contains(nonMirroredEdgeVertices, endpointA) ||
+ !contains(nonMirroredEdgeVertices, endpointB))
+ {
+ m_context.getTestContext().getLog()
+ << tcu::TestLog::Message << "Failure: edge doesn't contain both endpoints, " << endpointA << " and " << endpointB << tcu::TestLog::EndMessage
+ << tcu::TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
+ << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of vertices");
+ }
+ nonMirroredEdgeVertices.erase(endpointA);
+ nonMirroredEdgeVertices.erase(endpointB);
+ }
+
+ if (nonMirroredEdgeVertices != mirroredEdgeVertices)
+ {
+ m_context.getTestContext().getLog()
+ << tcu::TestLog::Message << "Failure: the set of mirrored edges isn't equal to the set of non-mirrored edges (ignoring endpoints and possible middle)" << tcu::TestLog::EndMessage
+ << tcu::TestLog::Message << "Note: non-mirrored vertices:\n" << containerStr(nonMirroredEdgeVertices, 5)
+ << "\nmirrored vertices:\n" << containerStr(mirroredEdgeVertices, 5) << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of vertices");
+ }
+ }
+ return tcu::TestStatus::pass("OK");
+}
+
+class OuterEdgeDivisionTest : public TestCase
+{
+public:
+ OuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+ : TestCase (testCtx, name, description)
+ , m_caseDef (caseDef)
+ {
+ }
+
+ void initPrograms (vk::SourceCollections& programCollection) const
+ {
+ addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, WINDING_USAGE_VARY, POINT_MODE_USAGE_VARY);
+ }
+
+ TestInstance* createInstance (Context& context) const
+ {
+ return new OuterEdgeDivisionTestInstance(context, m_caseDef);
+ };
+
+private:
+ const CaseDefinition m_caseDef;
+};
+
+class OuterEdgeIndexIndependenceTest : public TestCase
+{
+public:
+ OuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+ : TestCase (testCtx, name, description)
+ , m_caseDef (caseDef)
+ {
+ DE_ASSERT(m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+ }
+
+ void initPrograms (vk::SourceCollections& programCollection) const
+ {
+ addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode));
+ }
+
+ TestInstance* createInstance (Context& context) const
+ {
+ return new OuterEdgeIndexIndependenceTestInstance(context, m_caseDef);
+ };
+
+private:
+ const CaseDefinition m_caseDef;
+};
+
+class SymmetricOuterEdgeTest : public TestCase
+{
+public:
+ SymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+ : TestCase (testCtx, name, description)
+ , m_caseDef (caseDef)
+ {
+ }
+
+ void initPrograms (vk::SourceCollections& programCollection) const
+ {
+ const bool mirrorCoords = true;
+ addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, getWindingUsage(m_caseDef.winding), getPointModeUsage(m_caseDef.usePointMode), mirrorCoords);
+ }
+
+ TestInstance* createInstance (Context& context) const
+ {
+ return new SymmetricOuterEdgeTestInstance(context, m_caseDef);
+ };
+
+private:
+ const CaseDefinition m_caseDef;
+};
+
+tcu::TestCase* makeOuterEdgeDivisionTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+ const CaseDefinition caseDef = { primitiveType, spacingMode, WINDING_LAST, false }; // winding is ignored by this test
+ return new OuterEdgeDivisionTest(testCtx, name, description, caseDef);
+}
+
+tcu::TestCase* makeOuterEdgeIndexIndependenceTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+ const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
+ return new OuterEdgeIndexIndependenceTest(testCtx, name, description, caseDef);
+}
+
+tcu::TestCase* makeSymmetricOuterEdgeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+ const CaseDefinition caseDef = { primitiveType, spacingMode, winding, usePointMode };
+ return new SymmetricOuterEdgeTest(testCtx, name, description, caseDef);
+}
+
+} // InvariantOuterEdge ns
+
+namespace PrimitiveSetInvariance
+{
+
+enum CaseType
+{
+ CASETYPE_INVARIANT_PRIMITIVE_SET,
+ CASETYPE_INVARIANT_TRIANGLE_SET,
+ CASETYPE_INVARIANT_OUTER_TRIANGLE_SET,
+ CASETYPE_INVARIANT_INNER_TRIANGLE_SET,
+};
+
+struct CaseDefinition
+{
+ CaseType caseType;
+ TessPrimitiveType primitiveType;
+ SpacingMode spacingMode;
+ WindingUsage windingUsage;
+ bool usePointMode;
+};
+
+struct LevelCase
+{
+ std::vector<TessLevels> levels;
+ int mem; //!< Subclass-defined arbitrary piece of data, for type of the levelcase, if needed.
+
+ LevelCase (const TessLevels& lev) : levels(std::vector<TessLevels>(1, lev)), mem(0) {}
+ LevelCase (void) : mem(0) {}
+};
+
+typedef tcu::Vector<tcu::Vec3, 3> Triangle;
+
+inline Triangle makeTriangle (const PerPrimitive& primitive)
+{
+ return Triangle(primitive.tessCoord[0].swizzle(0, 1, 2),
+ primitive.tessCoord[1].swizzle(0, 1, 2),
+ primitive.tessCoord[2].swizzle(0, 1, 2));
+}
+
+//! Compare triangle sets, ignoring triangle order and vertex order within triangle, and possibly exclude some triangles too.
+template <typename IsTriangleRelevantT>
+bool compareTriangleSets (const PerPrimitiveVec& primitivesA,
+ const PerPrimitiveVec& primitivesB,
+ tcu::TestLog& log,
+ const IsTriangleRelevantT& isTriangleRelevant,
+ const char* ignoredTriangleDescription = DE_NULL)
+{
+ typedef LexCompare<Triangle, 3, VecLexLessThan<3> > TriangleLexLessThan;
+ typedef std::set<Triangle, TriangleLexLessThan> TriangleSet;
+
+ const int numTrianglesA = static_cast<int>(primitivesA.size());
+ const int numTrianglesB = static_cast<int>(primitivesB.size());
+ TriangleSet trianglesA;
+ TriangleSet trianglesB;
+
+ for (int aOrB = 0; aOrB < 2; ++aOrB)
+ {
+ const PerPrimitiveVec& primitives = aOrB == 0 ? primitivesA : primitivesB;
+ const int numTriangles = aOrB == 0 ? numTrianglesA : numTrianglesB;
+ TriangleSet& triangles = aOrB == 0 ? trianglesA : trianglesB;
+
+ for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
+ {
+ Triangle triangle = makeTriangle(primitives[triNdx]);
+
+ if (isTriangleRelevant(triangle.getPtr()))
+ {
+ std::sort(triangle.getPtr(), triangle.getPtr()+3, VecLexLessThan<3>());
+ triangles.insert(triangle);
+ }
+ }
+ }
+ {
+ TriangleSet::const_iterator aIt = trianglesA.begin();
+ TriangleSet::const_iterator bIt = trianglesB.begin();
+
+ while (aIt != trianglesA.end() || bIt != trianglesB.end())
+ {
+ const bool aEnd = aIt == trianglesA.end();
+ const bool bEnd = bIt == trianglesB.end();
+
+ if (aEnd || bEnd || *aIt != *bIt)
+ {
+ log << tcu::TestLog::Message << "Failure: triangle sets in two cases are not equal (when ignoring triangle and vertex order"
+ << (ignoredTriangleDescription == DE_NULL ? "" : std::string() + ", and " + ignoredTriangleDescription) << ")" << tcu::TestLog::EndMessage;
+
+ if (!aEnd && (bEnd || TriangleLexLessThan()(*aIt, *bIt)))
+ log << tcu::TestLog::Message << "Note: e.g. triangle " << *aIt << " exists for first case but not for second" << tcu::TestLog::EndMessage;
+ else
+ log << tcu::TestLog::Message << "Note: e.g. triangle " << *bIt << " exists for second case but not for first" << tcu::TestLog::EndMessage;
+
+ return false;
+ }
+
+ ++aIt;
+ ++bIt;
+ }
+
+ return true;
+ }
+}
+
+template <typename ArgT, bool res>
+struct ConstantUnaryPredicate
+{
+ bool operator() (const ArgT&) const { return res; }
+};
+
+bool compareTriangleSets (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, tcu::TestLog& log)
+{
+ return compareTriangleSets(primitivesA, primitivesB, log, ConstantUnaryPredicate<const tcu::Vec3*, true>());
+}
+
+//! Compare two sets of primitives. Order of primitives in each set is undefined, but within each primitive
+//! vertex order and coordinates are expected to match exactly.
+bool comparePrimitivesExact (const PerPrimitive* const primitivesA, const PerPrimitive* const primitivesB, const int numPrimitivesPerPatch)
+{
+ int ndxB = 0;
+ for (int ndxA = 0; ndxA < numPrimitivesPerPatch; ++ndxA)
+ {
+ const tcu::Vec4 (&coordsA)[3] = primitivesA[ndxA].tessCoord;
+ bool match = false;
+
+ // Actually both sets are usually somewhat sorted, so don't reset ndxB after each match. Instead, continue from the next index.
+ for (int i = 0; i < numPrimitivesPerPatch; ++i)
+ {
+ const tcu::Vec4 (&coordsB)[3] = primitivesB[ndxB].tessCoord;
+ ndxB = (ndxB + 1) % numPrimitivesPerPatch;
+
+ if (coordsA[0] == coordsB[0] && coordsA[1] == coordsB[1] && coordsA[2] == coordsB[2])
+ {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match)
+ return false;
+ }
+ return true;
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Base class for testing invariance of entire primitive set
+ *
+ * Draws two patches with identical tessellation levels and compares the
+ * results. Repeats the same with other programs that are only different
+ * in irrelevant ways; compares the results between these two programs.
+ * Also potentially compares to results produced by different tessellation
+ * levels (see e.g. invariance rule #6).
+ * Furthermore, repeats the above with multiple different tessellation
+ * value sets.
+ *
+ * The manner of primitive set comparison is defined by subclass. E.g.
+ * case for invariance rule #1 tests that same vertices come out, in same
+ * order; rule #5 only requires that the same triangles are output, but
+ * not necessarily in the same order.
+ *//*--------------------------------------------------------------------*/
+class InvarianceTestCase : public TestCase
+{
+public:
+ InvarianceTestCase (tcu::TestContext& context, const std::string& name, const std::string& description, const CaseDefinition& caseDef)
+ : TestCase (context, name, description)
+ , m_caseDef (caseDef) {}
+
+ virtual ~InvarianceTestCase (void) {}
+
+ void initPrograms (SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+private:
+ const CaseDefinition m_caseDef;
+};
+
+void InvarianceTestCase::initPrograms (SourceCollections& programCollection) const
+{
+ addDefaultPrograms(programCollection, m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.windingUsage, getPointModeUsage(m_caseDef.usePointMode));
+}
+
+class InvarianceTestInstance : public TestInstance
+{
+public:
+ InvarianceTestInstance (Context& context, const CaseDefinition& caseDef) : TestInstance(context), m_caseDef(caseDef) {}
+ virtual ~InvarianceTestInstance (void) {}
+
+ tcu::TestStatus iterate (void);
+
+protected:
+ virtual std::vector<LevelCase> genTessLevelCases (void) const;
+ virtual bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int levelCaseMem) const = 0;
+
+ const CaseDefinition m_caseDef;
+};
+
+std::vector<LevelCase> InvarianceTestInstance::genTessLevelCases (void) const
+{
+ static const TessLevels basicTessLevelCases[] =
+ {
+ { { 1.0f, 1.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
+ { { 63.0f, 24.0f }, { 15.0f, 42.0f, 10.0f, 12.0f } },
+ { { 3.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
+ { { 4.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
+ { { 2.0f, 2.0f }, { 6.0f, 8.0f, 7.0f, 9.0f } },
+ { { 5.0f, 6.0f }, { 1.0f, 1.0f, 1.0f, 1.0f } },
+ { { 1.0f, 6.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
+ { { 5.0f, 1.0f }, { 2.0f, 3.0f, 1.0f, 4.0f } },
+ { { 5.2f, 1.6f }, { 2.9f, 3.4f, 1.5f, 4.1f } }
+ };
+
+ std::vector<LevelCase> result;
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(basicTessLevelCases); ++i)
+ result.push_back(LevelCase(basicTessLevelCases[i]));
+
+ {
+ de::Random rnd(123);
+ for (int i = 0; i < 10; ++i)
+ {
+ TessLevels levels;
+ for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.inner); ++j)
+ levels.inner[j] = rnd.getFloat(1.0f, 16.0f);
+ for (int j = 0; j < DE_LENGTH_OF_ARRAY(levels.outer); ++j)
+ levels.outer[j] = rnd.getFloat(1.0f, 16.0f);
+ result.push_back(LevelCase(levels));
+ }
+ }
+
+ return result;
+}
+
+tcu::TestStatus InvarianceTestInstance::iterate (void)
+{
+ requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(),
+ FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = m_context.getDefaultAllocator();
+
+ const std::vector<LevelCase> tessLevelCases = genTessLevelCases();
+ const int numPatchesPerDrawCall = 2;
+ int maxNumPrimitivesPerPatch = 0; // computed below
+ std::vector<std::vector<int> > primitiveCounts;
+
+ for (int caseNdx = 0; caseNdx < static_cast<int>(tessLevelCases.size()); ++caseNdx)
+ {
+ primitiveCounts.push_back(std::vector<int>());
+ for (int levelNdx = 0; levelNdx < static_cast<int>(tessLevelCases[caseNdx].levels.size()); ++levelNdx)
+ {
+ const int primitiveCount = referencePrimitiveCount(m_caseDef.primitiveType, m_caseDef.spacingMode, m_caseDef.usePointMode,
+ &tessLevelCases[caseNdx].levels[levelNdx].inner[0], &tessLevelCases[caseNdx].levels[levelNdx].outer[0]);
+ primitiveCounts.back().push_back(primitiveCount);
+ maxNumPrimitivesPerPatch = de::max(maxNumPrimitivesPerPatch, primitiveCount);
+ }
+ }
+
+ // Vertex input attributes buffer: to pass tessellation levels
+
+ const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT;
+ const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
+ const VkDeviceSize vertexDataSizeBytes = NUM_TESS_LEVELS * numPatchesPerDrawCall * vertexStride;
+ const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ // Output buffer: number of primitives and an array of PerPrimitive structures
+
+ const int resultBufferMaxVertices = numPatchesPerDrawCall * maxNumPrimitivesPerPatch * numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode);
+ const int resultBufferTessCoordsOffset = sizeof(deInt32) * 4;
+ const VkDeviceSize resultBufferSizeBytes = resultBufferTessCoordsOffset + resultBufferMaxVertices * sizeof(PerPrimitive);
+ const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ // Descriptors
+
+ const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+ .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_GEOMETRY_BIT)
+ .build(vk, device));
+
+ const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+ .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 resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+ .update(vk, device);
+
+ const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device));
+ const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+ const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < static_cast<int>(tessLevelCases.size()); ++tessLevelCaseNdx)
+ {
+ const LevelCase& levelCase = tessLevelCases[tessLevelCaseNdx];
+ PerPrimitiveVec firstPrim;
+
+ {
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ std::ostringstream tessLevelsStr;
+
+ for (int i = 0; i < static_cast<int>(levelCase.levels.size()); ++i)
+ tessLevelsStr << (levelCase.levels.size() > 1u ? "\n" : "") << getTessellationLevelsString(levelCase.levels[i], m_caseDef.primitiveType);
+
+ log << tcu::TestLog::Message << "Tessellation level sets: " << tessLevelsStr.str() << tcu::TestLog::EndMessage;
+ }
+
+ for (int subTessLevelCaseNdx = 0; subTessLevelCaseNdx < static_cast<int>(levelCase.levels.size()); ++subTessLevelCaseNdx)
+ {
+ const TessLevels& tessLevels = levelCase.levels[subTessLevelCaseNdx];
+ {
+ TessLevels data[2];
+ data[0] = tessLevels;
+ data[1] = tessLevels;
+
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), data, sizeof(data));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(data));
+ }
+
+ int programNdx = 0;
+ const std::vector<Winding> windingCases = getWindingCases(m_caseDef.windingUsage);
+ for (std::vector<Winding>::const_iterator windingIter = windingCases.begin(); windingIter != windingCases.end(); ++windingIter)
+ {
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setPatchControlPoints (NUM_TESS_LEVELS)
+ .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get(getProgramName("tese", *windingIter, m_caseDef.usePointMode)), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, m_context.getBinaryCollection().get(getProgramName("geom", m_caseDef.usePointMode)), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ {
+ const Allocation& alloc = resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+ }
+
+ beginCommandBuffer(vk, *cmdBuffer);
+ 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);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ vk.cmdDraw(*cmdBuffer, numPatchesPerDrawCall * NUM_TESS_LEVELS, 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 case result
+ {
+ const Allocation& resultAlloc = resultBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+ const int refNumPrimitives = numPatchesPerDrawCall * primitiveCounts[tessLevelCaseNdx][subTessLevelCaseNdx];
+ const int numPrimitiveVertices = numVerticesPerPrimitive(m_caseDef.primitiveType, m_caseDef.usePointMode);
+ const deInt32 numPrimitives = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+ const PerPrimitiveVec primitives = sorted(readInterleavedData<PerPrimitive>(numPrimitives, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(PerPrimitive)),
+ byPatchPrimitiveID);
+
+ // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+ DE_ASSERT(numPrimitiveVertices * numPrimitives <= resultBufferMaxVertices);
+
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+
+ if (numPrimitives != refNumPrimitives)
+ {
+ log << tcu::TestLog::Message << "Failure: got " << numPrimitives << " primitives, but expected " << refNumPrimitives << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of primitives");
+ }
+
+ const int half = static_cast<int>(primitives.size() / 2);
+ const PerPrimitiveVec prim0 = PerPrimitiveVec(primitives.begin(), primitives.begin() + half);
+ const PerPrimitive* const prim1 = &primitives[half];
+
+ if (!comparePrimitivesExact(&prim0[0], prim1, half))
+ {
+ log << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two primitives drawn in one draw call" << tcu::TestLog::EndMessage
+ << tcu::TestLog::Message << "Note: tessellation levels for both primitives were: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType) << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of primitives");
+ }
+
+ if (programNdx == 0 && subTessLevelCaseNdx == 0)
+ firstPrim = prim0;
+ else
+ {
+ const bool compareOk = compare(firstPrim, prim0, levelCase.mem);
+ if (!compareOk)
+ {
+ log << tcu::TestLog::Message
+ << "Note: comparison of tessellation coordinates failed; comparison was made between following cases:\n"
+ << " - case A: program 0, tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx].levels[0], m_caseDef.primitiveType) << "\n"
+ << " - case B: program " << programNdx << ", tessellation levels: " << getTessellationLevelsString(tessLevels, m_caseDef.primitiveType)
+ << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid set of primitives");
+ }
+ }
+ }
+ ++programNdx;
+ }
+ }
+ }
+ return tcu::TestStatus::pass("OK");
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #1
+ *
+ * Test that the sequence of primitives input to the TES only depends on
+ * the tessellation levels, tessellation mode, spacing mode, winding, and
+ * point mode.
+ *//*--------------------------------------------------------------------*/
+class InvariantPrimitiveSetTestInstance : public InvarianceTestInstance
+{
+public:
+ InvariantPrimitiveSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+ bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
+ {
+ if (!comparePrimitivesExact(&primitivesA[0], &primitivesB[0], static_cast<int>(primitivesA.size())))
+ {
+ m_context.getTestContext().getLog()
+ << tcu::TestLog::Message << "Failure: tessellation coordinates differ between two programs" << tcu::TestLog::EndMessage;
+
+ return false;
+ }
+ return true;
+ }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #5
+ *
+ * Test that the set of triangles input to the TES only depends on the
+ * tessellation levels, tessellation mode and spacing mode. Specifically,
+ * winding doesn't change the set of triangles, though it can change the
+ * order in which they are input to TES, and can (and will) change the
+ * vertex order within a triangle.
+ *//*--------------------------------------------------------------------*/
+class InvariantTriangleSetTestInstance : public InvarianceTestInstance
+{
+public:
+ InvariantTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+ bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
+ {
+ return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog());
+ }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #6
+ *
+ * Test that the set of inner triangles input to the TES only depends on
+ * the inner tessellation levels, tessellation mode and spacing mode.
+ *//*--------------------------------------------------------------------*/
+class InvariantInnerTriangleSetTestInstance : public InvarianceTestInstance
+{
+public:
+ InvariantInnerTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+ std::vector<LevelCase> genTessLevelCases (void) const
+ {
+ const int numSubCases = 4;
+ const std::vector<LevelCase> baseResults = InvarianceTestInstance::genTessLevelCases();
+ std::vector<LevelCase> result;
+ de::Random rnd (123);
+
+ // Generate variants with different values for irrelevant levels.
+ for (int baseNdx = 0; baseNdx < static_cast<int>(baseResults.size()); ++baseNdx)
+ {
+ const TessLevels& base = baseResults[baseNdx].levels[0];
+ TessLevels levels = base;
+ LevelCase levelCase;
+
+ for (int subNdx = 0; subNdx < numSubCases; ++subNdx)
+ {
+ levelCase.levels.push_back(levels);
+
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
+ levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
+ }
+
+ result.push_back(levelCase);
+ }
+
+ return result;
+ }
+
+ struct IsInnerTriangleTriangle
+ {
+ bool operator() (const tcu::Vec3* vertices) const
+ {
+ for (int v = 0; v < 3; ++v)
+ for (int c = 0; c < 3; ++c)
+ if (vertices[v][c] == 0.0f)
+ return false;
+ return true;
+ }
+ };
+
+ struct IsInnerQuadTriangle
+ {
+ bool operator() (const tcu::Vec3* vertices) const
+ {
+ for (int v = 0; v < 3; ++v)
+ for (int c = 0; c < 2; ++c)
+ if (vertices[v][c] == 0.0f || vertices[v][c] == 1.0f)
+ return false;
+ return true;
+ }
+ };
+
+ bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int) const
+ {
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerTriangleTriangle(), "outer triangles");
+ else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+ return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(), IsInnerQuadTriangle(), "outer triangles");
+ else
+ {
+ DE_ASSERT(false);
+ return false;
+ }
+ }
+};
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test invariance rule #7
+ *
+ * Test that the set of outer triangles input to the TES only depends on
+ * tessellation mode, spacing mode and the inner and outer tessellation
+ * levels corresponding to the inner and outer edges relevant to that
+ * triangle.
+ *//*--------------------------------------------------------------------*/
+class InvariantOuterTriangleSetTestInstance : public InvarianceTestInstance
+{
+public:
+ InvariantOuterTriangleSetTestInstance (Context& context, const CaseDefinition& caseDef) : InvarianceTestInstance(context, caseDef) {}
+
+protected:
+ std::vector<LevelCase> genTessLevelCases (void) const
+ {
+ const int numSubCasesPerEdge = 4;
+ const int numEdges = m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3
+ : m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ? 4 : 0;
+ const std::vector<LevelCase> baseResult = InvarianceTestInstance::genTessLevelCases();
+ std::vector<LevelCase> result;
+ de::Random rnd (123);
+
+ // Generate variants with different values for irrelevant levels.
+ for (int baseNdx = 0; baseNdx < static_cast<int>(baseResult.size()); ++baseNdx)
+ {
+ const TessLevels& base = baseResult[baseNdx].levels[0];
+ if (base.inner[0] == 1.0f || (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS && base.inner[1] == 1.0f))
+ continue;
+
+ for (int edgeNdx = 0; edgeNdx < numEdges; ++edgeNdx)
+ {
+ TessLevels levels = base;
+ LevelCase levelCase;
+ levelCase.mem = edgeNdx;
+
+ for (int subCaseNdx = 0; subCaseNdx < numSubCasesPerEdge; ++subCaseNdx)
+ {
+ levelCase.levels.push_back(levels);
+
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(levels.outer); ++i)
+ {
+ if (i != edgeNdx)
+ levels.outer[i] = rnd.getFloat(2.0f, 16.0f);
+ }
+
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ levels.inner[1] = rnd.getFloat(2.0f, 16.0f);
+ }
+
+ result.push_back(levelCase);
+ }
+ }
+
+ return result;
+ }
+
+ class IsTriangleTriangleOnOuterEdge
+ {
+ public:
+ IsTriangleTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
+ bool operator() (const tcu::Vec3* vertices) const
+ {
+ bool touchesAppropriateEdge = false;
+ for (int v = 0; v < 3; ++v)
+ if (vertices[v][m_edgeNdx] == 0.0f)
+ touchesAppropriateEdge = true;
+
+ if (touchesAppropriateEdge)
+ {
+ const tcu::Vec3 avg = (vertices[0] + vertices[1] + vertices[2]) / 3.0f;
+ return avg[m_edgeNdx] < avg[(m_edgeNdx+1)%3] &&
+ avg[m_edgeNdx] < avg[(m_edgeNdx+2)%3];
+ }
+ return false;
+ }
+
+ private:
+ const int m_edgeNdx;
+ };
+
+ class IsQuadTriangleOnOuterEdge
+ {
+ public:
+ IsQuadTriangleOnOuterEdge (int edgeNdx) : m_edgeNdx(edgeNdx) {}
+
+ bool onEdge (const tcu::Vec3& v) const
+ {
+ return v[m_edgeNdx%2] == (m_edgeNdx <= 1 ? 0.0f : 1.0f);
+ }
+
+ static inline bool onAnyEdge (const tcu::Vec3& v)
+ {
+ return v[0] == 0.0f || v[0] == 1.0f || v[1] == 0.0f || v[1] == 1.0f;
+ }
+
+ bool operator() (const tcu::Vec3* vertices) const
+ {
+ for (int v = 0; v < 3; ++v)
+ {
+ const tcu::Vec3& a = vertices[v];
+ const tcu::Vec3& b = vertices[(v+1)%3];
+ const tcu::Vec3& c = vertices[(v+2)%3];
+ if (onEdge(a) && onEdge(b))
+ return true;
+ if (onEdge(c) && !onAnyEdge(a) && !onAnyEdge(b) && a[m_edgeNdx%2] == b[m_edgeNdx%2])
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ const int m_edgeNdx;
+ };
+
+ bool compare (const PerPrimitiveVec& primitivesA, const PerPrimitiveVec& primitivesB, const int outerEdgeNdx) const
+ {
+ if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
+ IsTriangleTriangleOnOuterEdge(outerEdgeNdx),
+ ("inner triangles, and outer triangles corresponding to other edge than edge "
+ + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
+ }
+ else if (m_caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS)
+ {
+ return compareTriangleSets(primitivesA, primitivesB, m_context.getTestContext().getLog(),
+ IsQuadTriangleOnOuterEdge(outerEdgeNdx),
+ ("inner triangles, and outer triangles corresponding to other edge than edge "
+ + outerEdgeDescriptions(m_caseDef.primitiveType)[outerEdgeNdx].description()).c_str());
+ }
+ else
+ DE_ASSERT(false);
+
+ return true;
+ }
+};
+
+TestInstance* InvarianceTestCase::createInstance (Context& context) const
+{
+ switch (m_caseDef.caseType)
+ {
+ case CASETYPE_INVARIANT_PRIMITIVE_SET: return new InvariantPrimitiveSetTestInstance (context, m_caseDef);
+ case CASETYPE_INVARIANT_TRIANGLE_SET: return new InvariantTriangleSetTestInstance (context, m_caseDef);
+ case CASETYPE_INVARIANT_OUTER_TRIANGLE_SET: return new InvariantOuterTriangleSetTestInstance(context, m_caseDef);
+ case CASETYPE_INVARIANT_INNER_TRIANGLE_SET: return new InvariantInnerTriangleSetTestInstance(context, m_caseDef);
+ default:
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+TestCase* makeInvariantPrimitiveSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+ const CaseDefinition caseDef = { CASETYPE_INVARIANT_PRIMITIVE_SET, primitiveType, spacingMode, getWindingUsage(winding), usePointMode };
+ return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+TestCase* makeInvariantTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+ DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
+ const CaseDefinition caseDef = { CASETYPE_INVARIANT_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
+ return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+TestCase* makeInvariantInnerTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+ DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
+ const CaseDefinition caseDef = { CASETYPE_INVARIANT_INNER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
+ return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+TestCase* makeInvariantOuterTriangleSetTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode)
+{
+ DE_ASSERT(primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS);
+ const CaseDefinition caseDef = { CASETYPE_INVARIANT_OUTER_TRIANGLE_SET, primitiveType, spacingMode, WINDING_USAGE_VARY, false };
+ return new InvarianceTestCase(testCtx, name, description, caseDef);
+}
+
+} // PrimitiveSetInvariance ns
+
+namespace TessCoordComponent
+{
+
+enum CaseType
+{
+ CASETYPE_TESS_COORD_RANGE = 0, //!< Test that all (relevant) components of tess coord are in [0,1].
+ CASETYPE_ONE_MINUS_TESS_COORD, //!< Test that for every (relevant) component c of a tess coord, 1.0-c is exact.
+
+ CASETYPE_LAST
+};
+
+struct CaseDefinition
+{
+ CaseType caseType;
+ TessPrimitiveType primitiveType;
+ SpacingMode spacingMode;
+ Winding winding;
+ bool usePointMode;
+};
+
+std::vector<TessLevels> genTessLevelCases (const int numCases)
+{
+ de::Random rnd(123);
+ std::vector<TessLevels> result;
+
+ for (int i = 0; i < numCases; ++i)
+ {
+ TessLevels levels;
+ levels.inner[0] = rnd.getFloat(1.0f, 63.0f);
+ levels.inner[1] = rnd.getFloat(1.0f, 63.0f);
+ levels.outer[0] = rnd.getFloat(1.0f, 63.0f);
+ levels.outer[1] = rnd.getFloat(1.0f, 63.0f);
+ levels.outer[2] = rnd.getFloat(1.0f, 63.0f);
+ levels.outer[3] = rnd.getFloat(1.0f, 63.0f);
+ result.push_back(levels);
+ }
+
+ return result;
+}
+
+typedef bool (*CompareFunc)(tcu::TestLog& log, const float value);
+
+bool compareTessCoordRange (tcu::TestLog& log, const float value)
+{
+ if (!de::inRange(value, 0.0f, 1.0f))
+ {
+ log << tcu::TestLog::Message << "Failure: tess coord component isn't in range [0,1]" << tcu::TestLog::EndMessage;
+ return false;
+ }
+ return true;
+}
+
+bool compareOneMinusTessCoord (tcu::TestLog& log, const float value)
+{
+ if (value != 1.0f)
+ {
+ log << tcu::TestLog::Message << "Failure: comp + (1.0-comp) doesn't equal 1.0 for some component of tessellation coordinate" << tcu::TestLog::EndMessage;
+ return false;
+ }
+ return true;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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(location = 0) in highp float in_tc_attr[];\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_TessLevelInner[0] = in_tc_attr[0];\n"
+ << " gl_TessLevelInner[1] = in_tc_attr[1];\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+ << " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+ << " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+ << " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ std::ostringstream tessCoordSrc;
+
+ if (caseDef.caseType == CASETYPE_TESS_COORD_RANGE)
+ tessCoordSrc << " sb_out.tessCoord[index] = gl_TessCoord;\n";
+ else if (caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD)
+ {
+ const char* components[] = { "x" , "y", "z" };
+ const int numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
+
+ for (int i = 0; i < numComponents; ++i)
+ tessCoordSrc << " {\n"
+ << " float oneMinusComp = 1.0 - gl_TessCoord." << components[i] << ";\n"
+ << " sb_out.tessCoord[index]." << components[i] << " = gl_TessCoord." << components[i] << " + oneMinusComp;\n"
+ << " }\n";
+ }
+ else
+ {
+ DE_ASSERT(false);
+ return;
+ }
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "\n"
+ << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
+ << getSpacingModeShaderName(caseDef.spacingMode) << ", "
+ << getWindingShaderName(caseDef.winding)
+ << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
+ << "\n"
+ << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+ << " int numInvocations;\n"
+ << " vec3 tessCoord[];\n"
+ << "} sb_out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " int index = atomicAdd(sb_out.numInvocations, 1);\n"
+ << tessCoordSrc.str()
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+}
+
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+ 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 int numTessLevelCases = 32;
+ const std::vector<TessLevels> tessLevelCases = genTessLevelCases(numTessLevelCases);
+
+ int maxNumVerticesInDrawCall = 0;
+ for (int i = 0; i < numTessLevelCases; ++i)
+ maxNumVerticesInDrawCall = de::max(maxNumVerticesInDrawCall, referenceVertexCount(caseDef.primitiveType, caseDef.spacingMode, caseDef.usePointMode,
+ &tessLevelCases[i].inner[0], &tessLevelCases[i].outer[0]));
+
+ // We may get more invocations than expected, so add some more space (arbitrary number).
+ maxNumVerticesInDrawCall += 4;
+
+ // Vertex input attributes buffer: to pass tessellation levels
+
+ const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT;
+ const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
+ const VkDeviceSize vertexDataSizeBytes = NUM_TESS_LEVELS * vertexStride;
+ const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ DE_ASSERT(vertexDataSizeBytes == sizeof(TessLevels));
+
+ // Output buffer: number of invocations and array of tess coords
+
+ const int resultBufferTessCoordsOffset = sizeof(deInt32) * 4;
+ const VkDeviceSize resultBufferSizeBytes = resultBufferTessCoordsOffset + maxNumVerticesInDrawCall * sizeof(tcu::Vec4);
+ const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ // Descriptors
+
+ const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+ .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)
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+ const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+ const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+ .update(vk, device);
+
+ const Unique<VkRenderPass> renderPass (makeRenderPassWithoutAttachments (vk, device));
+ const Unique<VkFramebuffer> framebuffer (makeFramebufferWithoutAttachments(vk, device, *renderPass));
+ const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout (vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setPatchControlPoints (NUM_TESS_LEVELS)
+ .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+ .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));
+
+ for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < numTessLevelCases; ++tessLevelCaseNdx)
+ {
+ context.getTestContext().getLog()
+ << tcu::TestLog::Message
+ << "Testing with tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
+ << tcu::TestLog::EndMessage;
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), &tessLevelCases[tessLevelCaseNdx], sizeof(TessLevels));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+ }
+ {
+ const Allocation& alloc = resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+ }
+
+ beginCommandBuffer(vk, *cmdBuffer);
+ 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);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ vk.cmdDraw(*cmdBuffer, NUM_TESS_LEVELS, 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 case result
+ {
+ const Allocation& resultAlloc = resultBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+ const deInt32 numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+ const std::vector<tcu::Vec3> vertices = readInterleavedData<tcu::Vec3>(numVertices, resultAlloc.getHostPtr(), resultBufferTessCoordsOffset, sizeof(tcu::Vec4));
+
+ // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+ DE_ASSERT(numVertices <= maxNumVerticesInDrawCall);
+
+ tcu::TestLog& log = context.getTestContext().getLog();
+ const int numComponents = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 2);
+
+ CompareFunc compare = (caseDef.caseType == CASETYPE_TESS_COORD_RANGE ? compareTessCoordRange :
+ caseDef.caseType == CASETYPE_ONE_MINUS_TESS_COORD ? compareOneMinusTessCoord : DE_NULL);
+
+ DE_ASSERT(compare != DE_NULL);
+
+ for (std::vector<tcu::Vec3>::const_iterator vertexIter = vertices.begin(); vertexIter != vertices.end(); ++vertexIter)
+ for (int i = 0; i < numComponents; ++i)
+ if (!compare(log, (*vertexIter)[i]))
+ {
+ log << tcu::TestLog::Message << "Note: got a wrong tessellation coordinate "
+ << (numComponents == 3 ? de::toString(*vertexIter) : de::toString(vertexIter->swizzle(0,1))) << tcu::TestLog::EndMessage;
+
+ tcu::TestStatus::fail("Invalid tessellation coordinate component");
+ }
+ }
+ }
+ return tcu::TestStatus::pass("OK");
+}
+
+tcu::TestCase* makeTessCoordRangeTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+ const CaseDefinition caseDef = { CASETYPE_TESS_COORD_RANGE, primitiveType, spacingMode, winding, usePointMode };
+ return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
+}
+
+tcu::TestCase* makeOneMinusTessCoordTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const Winding winding, const bool usePointMode)
+{
+ const CaseDefinition caseDef = { CASETYPE_ONE_MINUS_TESS_COORD, primitiveType, spacingMode, winding, usePointMode };
+ return createFunctionCaseWithPrograms(testCtx, tcu::NODETYPE_SELF_VALIDATE, name, description, initPrograms, test, caseDef);
+}
+
+} // TessCoordComponent ns
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.invariance.*
+//! Original OpenGL ES tests used transform feedback to get vertices in primitive order. To emulate this behavior we have to use geometry shader,
+//! which allows us to intercept verticess of final output primitives. This can't be done with tessellation shaders alone as number and order of
+//! invocation is undefined.
+tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "invariance", "Test tessellation invariance rules"));
+
+ de::MovePtr<tcu::TestCaseGroup> invariantPrimitiveSetGroup (new tcu::TestCaseGroup(testCtx, "primitive_set", "Test invariance rule #1"));
+ de::MovePtr<tcu::TestCaseGroup> invariantOuterEdgeGroup (new tcu::TestCaseGroup(testCtx, "outer_edge_division", "Test invariance rule #2"));
+ de::MovePtr<tcu::TestCaseGroup> symmetricOuterEdgeGroup (new tcu::TestCaseGroup(testCtx, "outer_edge_symmetry", "Test invariance rule #3"));
+ de::MovePtr<tcu::TestCaseGroup> outerEdgeVertexSetIndexIndependenceGroup(new tcu::TestCaseGroup(testCtx, "outer_edge_index_independence", "Test invariance rule #4"));
+ de::MovePtr<tcu::TestCaseGroup> invariantTriangleSetGroup (new tcu::TestCaseGroup(testCtx, "triangle_set", "Test invariance rule #5"));
+ de::MovePtr<tcu::TestCaseGroup> invariantInnerTriangleSetGroup (new tcu::TestCaseGroup(testCtx, "inner_triangle_set", "Test invariance rule #6"));
+ de::MovePtr<tcu::TestCaseGroup> invariantOuterTriangleSetGroup (new tcu::TestCaseGroup(testCtx, "outer_triangle_set", "Test invariance rule #7"));
+ de::MovePtr<tcu::TestCaseGroup> tessCoordComponentRangeGroup (new tcu::TestCaseGroup(testCtx, "tess_coord_component_range", "Test invariance rule #8, first part"));
+ de::MovePtr<tcu::TestCaseGroup> oneMinusTessCoordComponentGroup (new tcu::TestCaseGroup(testCtx, "one_minus_tess_coord_component", "Test invariance rule #8, second part"));
+
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+ {
+ const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
+ const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
+ const bool triOrQuad = primitiveType == TESSPRIMITIVETYPE_TRIANGLES || primitiveType == TESSPRIMITIVETYPE_QUADS;
+ const std::string primName = getTessPrimitiveTypeShaderName(primitiveType);
+ const std::string primSpacName = primName + "_" + getSpacingModeShaderName(spacingMode);
+
+ if (triOrQuad)
+ {
+ invariantOuterEdgeGroup->addChild ( InvariantOuterEdge::makeOuterEdgeDivisionTest (testCtx, primSpacName, "", primitiveType, spacingMode));
+ invariantTriangleSetGroup->addChild (PrimitiveSetInvariance::makeInvariantTriangleSetTest (testCtx, primSpacName, "", primitiveType, spacingMode));
+ invariantInnerTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantInnerTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode));
+ invariantOuterTriangleSetGroup->addChild(PrimitiveSetInvariance::makeInvariantOuterTriangleSetTest(testCtx, primSpacName, "", primitiveType, spacingMode));
+ }
+
+ for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
+ for (int usePointModeNdx = 0; usePointModeNdx <= 1; ++usePointModeNdx)
+ {
+ const Winding winding = static_cast<Winding>(windingNdx);
+ const bool usePointMode = (usePointModeNdx != 0);
+ const std::string primSpacWindPointName = primSpacName + "_" + getWindingShaderName(winding) + (usePointMode ? "_point_mode" : "");
+
+ invariantPrimitiveSetGroup->addChild (PrimitiveSetInvariance::makeInvariantPrimitiveSetTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+ tessCoordComponentRangeGroup->addChild ( TessCoordComponent::makeTessCoordRangeTest (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+ oneMinusTessCoordComponentGroup->addChild( TessCoordComponent::makeOneMinusTessCoordTest (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+ symmetricOuterEdgeGroup->addChild ( InvariantOuterEdge::makeSymmetricOuterEdgeTest (testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+
+ if (triOrQuad)
+ outerEdgeVertexSetIndexIndependenceGroup->addChild(InvariantOuterEdge::makeOuterEdgeIndexIndependenceTest(testCtx, primSpacWindPointName, "", primitiveType, spacingMode, winding, usePointMode));
+ }
+ }
+
+ group->addChild(invariantPrimitiveSetGroup.release());
+ group->addChild(invariantOuterEdgeGroup.release());
+ group->addChild(symmetricOuterEdgeGroup.release());
+ group->addChild(outerEdgeVertexSetIndexIndependenceGroup.release());
+ group->addChild(invariantTriangleSetGroup.release());
+ group->addChild(invariantInnerTriangleSetGroup.release());
+ group->addChild(invariantOuterTriangleSetGroup.release());
+ group->addChild(tessCoordComponentRangeGroup.release());
+ group->addChild(oneMinusTessCoordComponentGroup.release());
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONINVARIANCETESTS_HPP
+#define _VKTTESSELLATIONINVARIANCETESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Invariance Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createInvarianceTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONINVARIANCETESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Limits Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationLimitsTests.hpp"
+#include "vktTestCaseUtil.hpp"
+
+#include "tcuTestLog.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum TessellationLimits
+{
+ LIMIT_MAX_TESSELLATION_GENERATION_LEVEL,
+ LIMIT_MAX_TESSELLATION_PATCH_SIZE,
+ LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_INPUT_COMPONENTS,
+ LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_OUTPUT_COMPONENTS,
+ LIMIT_MAX_TESSELLATION_CONTROL_PER_PATCH_OUTPUT_COMPONENTS,
+ LIMIT_MAX_TESSELLATION_CONTROL_TOTAL_OUTPUT_COMPONENTS,
+ LIMIT_MAX_TESSELLATION_EVALUATION_INPUT_COMPONENTS,
+ LIMIT_MAX_TESSELLATION_EVALUATION_OUTPUT_COMPONENTS,
+};
+
+struct LimitsCaseDefinition
+{
+ TessellationLimits limitType;
+ deUint32 minimum; //!< Implementation must provide at least this value
+};
+
+tcu::TestStatus expectGreaterOrEqual(tcu::TestLog& log, const deUint32 expected, const deUint32 actual)
+{
+ log << tcu::TestLog::Message << "Expected: " << expected << ", got: " << actual << tcu::TestLog::EndMessage;
+
+ if (actual >= expected)
+ return tcu::TestStatus::pass("OK");
+ else
+ return tcu::TestStatus::fail("Value doesn't meet minimal spec requirements");
+}
+
+tcu::TestStatus deviceLimitsTestCase(Context& context, const LimitsCaseDefinition caseDef)
+{
+ const InstanceInterface& vki = context.getInstanceInterface();
+ const VkPhysicalDevice physDevice = context.getPhysicalDevice();
+ const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
+
+ if (!features.tessellationShader)
+ throw tcu::NotSupportedError("Tessellation shader not supported");
+
+ const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice);
+ tcu::TestLog& log = context.getTestContext().getLog();
+
+ switch (caseDef.limitType)
+ {
+ case LIMIT_MAX_TESSELLATION_GENERATION_LEVEL:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationGenerationLevel);
+ case LIMIT_MAX_TESSELLATION_PATCH_SIZE:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationPatchSize);
+ case LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_INPUT_COMPONENTS:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlPerVertexInputComponents);
+ case LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_OUTPUT_COMPONENTS:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlPerVertexOutputComponents);
+ case LIMIT_MAX_TESSELLATION_CONTROL_PER_PATCH_OUTPUT_COMPONENTS:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlPerPatchOutputComponents);
+ case LIMIT_MAX_TESSELLATION_CONTROL_TOTAL_OUTPUT_COMPONENTS:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationControlTotalOutputComponents);
+ case LIMIT_MAX_TESSELLATION_EVALUATION_INPUT_COMPONENTS:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationEvaluationInputComponents);
+ case LIMIT_MAX_TESSELLATION_EVALUATION_OUTPUT_COMPONENTS:
+ return expectGreaterOrEqual(log, caseDef.minimum, properties.limits.maxTessellationEvaluationOutputComponents);
+ }
+
+ // Control should never get here.
+ DE_FATAL("Internal test error");
+ return tcu::TestStatus::fail("Test error");
+}
+
+} // anonymous
+
+//! These tests correspond roughly to dEQP-GLES31.functional.tessellation.state_query.*
+tcu::TestCaseGroup* createLimitsTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "limits", "Tessellation limits tests"));
+
+ static const struct
+ {
+ std::string caseName;
+ LimitsCaseDefinition caseDef;
+ } cases[] =
+ {
+ { "max_tessellation_generation_level", { LIMIT_MAX_TESSELLATION_GENERATION_LEVEL, 64 } },
+ { "max_tessellation_patch_size", { LIMIT_MAX_TESSELLATION_PATCH_SIZE, 32 } },
+ { "max_tessellation_control_per_vertex_input_components", { LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_INPUT_COMPONENTS, 64 } },
+ { "max_tessellation_control_per_vertex_output_components", { LIMIT_MAX_TESSELLATION_CONTROL_PER_VERTEX_OUTPUT_COMPONENTS, 64 } },
+ { "max_tessellation_control_per_patch_output_components", { LIMIT_MAX_TESSELLATION_CONTROL_PER_PATCH_OUTPUT_COMPONENTS, 120 } },
+ { "max_tessellation_control_total_output_components", { LIMIT_MAX_TESSELLATION_CONTROL_TOTAL_OUTPUT_COMPONENTS, 2048 } },
+ { "max_tessellation_evaluation_input_components", { LIMIT_MAX_TESSELLATION_EVALUATION_INPUT_COMPONENTS, 64 } },
+ { "max_tessellation_evaluation_output_components", { LIMIT_MAX_TESSELLATION_EVALUATION_OUTPUT_COMPONENTS, 64 } },
+ };
+
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(cases); ++i)
+ addFunctionCase<LimitsCaseDefinition>(group.get(), cases[i].caseName, "", deviceLimitsTestCase, cases[i].caseDef);
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONLIMITSTESTS_HPP
+#define _VKTTESSELLATIONLIMITSTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Limits Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createLimitsTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONLIMITSTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Miscellaneous Draw Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationMiscDrawTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageIO.hpp"
+#include "tcuTexture.hpp"
+#include "tcuImageCompare.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+struct CaseDefinition
+{
+ TessPrimitiveType primitiveType;
+ SpacingMode spacingMode;
+ std::string referenceImagePathPrefix; //!< without case suffix and extension (e.g. "_1.png")
+};
+
+inline CaseDefinition makeCaseDefinition (const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode,
+ const std::string& referenceImagePathPrefix)
+{
+ CaseDefinition caseDef;
+ caseDef.primitiveType = primitiveType;
+ caseDef.spacingMode = spacingMode;
+ caseDef.referenceImagePathPrefix = referenceImagePathPrefix;
+ return caseDef;
+}
+
+std::vector<TessLevels> genTessLevelCases (const SpacingMode spacingMode)
+{
+ static const TessLevels tessLevelCases[] =
+ {
+ { { 9.0f, 9.0f }, { 9.0f, 9.0f, 9.0f, 9.0f } },
+ { { 8.0f, 11.0f }, { 13.0f, 15.0f, 18.0f, 21.0f } },
+ { { 17.0f, 14.0f }, { 3.0f, 6.0f, 9.0f, 12.0f } },
+ };
+
+ std::vector<TessLevels> resultTessLevels(DE_LENGTH_OF_ARRAY(tessLevelCases));
+
+ for (int tessLevelCaseNdx = 0; tessLevelCaseNdx < DE_LENGTH_OF_ARRAY(tessLevelCases); ++tessLevelCaseNdx)
+ {
+ TessLevels& tessLevels = resultTessLevels[tessLevelCaseNdx];
+
+ for (int i = 0; i < 2; ++i)
+ tessLevels.inner[i] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, tessLevelCases[tessLevelCaseNdx].inner[i]));
+
+ for (int i = 0; i < 4; ++i)
+ tessLevels.outer[i] = static_cast<float>(getClampedRoundedTessLevel(spacingMode, tessLevelCases[tessLevelCaseNdx].outer[i]));
+ }
+
+ return resultTessLevels;
+}
+
+std::vector<tcu::Vec2> genVertexPositions (const TessPrimitiveType primitiveType)
+{
+ std::vector<tcu::Vec2> positions;
+ positions.reserve(4);
+
+ if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ positions.push_back(tcu::Vec2( 0.8f, 0.6f));
+ positions.push_back(tcu::Vec2( 0.0f, -0.786f));
+ positions.push_back(tcu::Vec2(-0.8f, 0.6f));
+ }
+ else if (primitiveType == TESSPRIMITIVETYPE_QUADS || primitiveType == TESSPRIMITIVETYPE_ISOLINES)
+ {
+ positions.push_back(tcu::Vec2(-0.8f, -0.8f));
+ positions.push_back(tcu::Vec2( 0.8f, -0.8f));
+ positions.push_back(tcu::Vec2(-0.8f, 0.8f));
+ positions.push_back(tcu::Vec2( 0.8f, 0.8f));
+ }
+ else
+ DE_ASSERT(false);
+
+ return positions;
+}
+
+//! Common test function used by all test cases.
+tcu::TestStatus runTest (Context& context, const CaseDefinition caseDef)
+{
+ requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+ 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<TessLevels> tessLevelCases = genTessLevelCases(caseDef.spacingMode);
+ const std::vector<tcu::Vec2> vertexData = genVertexPositions(caseDef.primitiveType);
+ const deUint32 inPatchSize = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+ // Vertex input: positions
+
+ const VkFormat vertexFormat = VK_FORMAT_R32G32_SFLOAT;
+ const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
+ const VkDeviceSize vertexDataSizeBytes = sizeInBytes(vertexData);
+
+ const Buffer vertexBuffer(vk, device, allocator,
+ makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ DE_ASSERT(inPatchSize == vertexData.size());
+ DE_ASSERT(sizeof(vertexData[0]) == vertexStride);
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), &vertexData[0], static_cast<std::size_t>(vertexDataSizeBytes));
+
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+ // No barrier needed, flushed memory is automatically visible
+ }
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(256, 256);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Input buffer: tessellation levels. Data is filled in later.
+
+ const Buffer tessLevelsBuffer(vk, device, allocator,
+ makeBufferCreateInfo(sizeof(TessLevels), 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 | VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
+ .build(vk, device));
+
+ const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
+ .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, sizeof(TessLevels));
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &tessLevelsBufferInfo)
+ .update(vk, device);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView (makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout (vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+ .setPatchControlPoints (inPatchSize)
+ .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)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ // Draw commands
+
+ deUint32 numPassedCases = 0;
+
+ for (deUint32 tessLevelCaseNdx = 0; tessLevelCaseNdx < tessLevelCases.size(); ++tessLevelCaseNdx)
+ {
+ context.getTestContext().getLog()
+ << tcu::TestLog::Message
+ << "Tessellation levels: " << getTessellationLevelsString(tessLevelCases[tessLevelCaseNdx], caseDef.primitiveType)
+ << tcu::TestLog::EndMessage;
+
+ // Upload tessellation levels data to the input buffer
+ {
+ const Allocation& alloc = tessLevelsBuffer.getAllocation();
+ TessLevels* const bufferTessLevels = static_cast<TessLevels*>(alloc.getHostPtr());
+ *bufferTessLevels = tessLevelCases[tessLevelCaseNdx];
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), sizeof(TessLevels));
+ }
+
+ // Reset the command buffer and begin recording.
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ // State is slightly different on the first iteration.
+ const VkImageLayout currentLayout = (tessLevelCaseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+ const VkAccessFlags srcFlags = (tessLevelCaseNdx == 0 ? (VkAccessFlags)0 : VK_ACCESS_TRANSFER_READ_BIT);
+
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ 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);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ // Process enough vertices to make a patch.
+ vk.cmdDraw(*cmdBuffer, inPatchSize, 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ {
+ const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+ // Verify case result
+ const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+
+ // Load reference image
+ const std::string referenceImagePath = caseDef.referenceImagePathPrefix + "_" + de::toString(tessLevelCaseNdx) + ".png";
+ tcu::TextureLevel referenceImage;
+ tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), referenceImagePath.c_str());
+
+ if (tcu::fuzzyCompare(context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
+ referenceImage.getAccess(), resultImageAccess, 0.002f, tcu::COMPARE_LOG_RESULT))
+ ++numPassedCases;
+ }
+ } // tessLevelCaseNdx
+
+ return (numPassedCases == tessLevelCases.size() ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+}
+
+inline const char* getTessLevelsSSBODeclaration (void)
+{
+ return "layout(set = 0, binding = 0, std430) readonly restrict buffer TessLevels {\n"
+ " float inner0;\n"
+ " float inner1;\n"
+ " float outer0;\n"
+ " float outer1;\n"
+ " float outer2;\n"
+ " float outer3;\n"
+ "} sb_levels;\n";
+}
+
+//! Add vertex, fragment, and tessellation control shaders.
+void initCommonPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ DE_ASSERT(!programCollection.glslSources.contains("vert"));
+ DE_ASSERT(!programCollection.glslSources.contains("tesc"));
+ DE_ASSERT(!programCollection.glslSources.contains("frag"));
+
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_v_position;\n"
+ << "layout(location = 0) out highp vec2 in_tc_position;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_position = in_v_position;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
+ }
+
+ // Tessellation control shader
+ {
+ const int numVertices = (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 3 : 4);
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "\n"
+ << "layout(vertices = " << numVertices << ") out;\n"
+ << "\n"
+ << getTessLevelsSSBODeclaration()
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_tc_position[];\n"
+ << "layout(location = 0) out highp vec2 in_te_position[];\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_te_position[gl_InvocationID] = in_tc_position[gl_InvocationID];\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = sb_levels.inner0;\n"
+ << " gl_TessLevelInner[1] = sb_levels.inner1;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = sb_levels.outer0;\n"
+ << " gl_TessLevelOuter[1] = sb_levels.outer1;\n"
+ << " gl_TessLevelOuter[2] = sb_levels.outer2;\n"
+ << " gl_TessLevelOuter[3] = sb_levels.outer3;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 in_f_color;\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = in_f_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+void initProgramsFillCoverCase (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+
+ initCommonPrograms(programCollection, caseDef);
+
+ // 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(caseDef.primitiveType) << ", "
+ << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_te_position[];\n"
+ << "layout(location = 0) out highp vec4 in_f_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
+ " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
+ " highp vec2 corner0 = in_te_position[0];\n"
+ " highp vec2 corner1 = in_te_position[1];\n"
+ " highp vec2 corner2 = in_te_position[2];\n"
+ " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
+ " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2) / 3.0;\n"
+ " highp float f = (1.0 - length(fromCenter)) * (1.5 - d);\n"
+ " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
+ " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ : caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
+ " highp vec2 corner0 = in_te_position[0];\n"
+ " highp vec2 corner1 = in_te_position[1];\n"
+ " highp vec2 corner2 = in_te_position[2];\n"
+ " highp vec2 corner3 = in_te_position[3];\n"
+ " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
+ " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
+ " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
+ " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
+ " highp float d = 2.0 * min(abs(gl_TessCoord.x-0.5), abs(gl_TessCoord.y-0.5));\n"
+ " highp vec2 fromCenter = pos - (corner0 + corner1 + corner2 + corner3) / 4.0;\n"
+ " highp float f = (1.0 - length(fromCenter)) * sqrt(1.7 - d);\n"
+ " pos += 0.75 * f * fromCenter / (length(fromCenter) + 0.3);\n"
+ " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ : "")
+ << " in_f_color = vec4(1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+}
+
+void initProgramsFillNonOverlapCase (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES || caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS);
+
+ initCommonPrograms(programCollection, caseDef);
+
+ // 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(caseDef.primitiveType) << ", "
+ << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+ << "\n"
+ << getTessLevelsSSBODeclaration()
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_te_position[];\n"
+ << "layout(location = 0) out highp vec4 in_f_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << (caseDef.primitiveType == TESSPRIMITIVETYPE_TRIANGLES ?
+ " highp vec2 corner0 = in_te_position[0];\n"
+ " highp vec2 corner1 = in_te_position[1];\n"
+ " highp vec2 corner2 = in_te_position[2];\n"
+ " highp vec2 pos = corner0*gl_TessCoord.x + corner1*gl_TessCoord.y + corner2*gl_TessCoord.z;\n"
+ " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ " highp int numConcentricTriangles = int(round(sb_levels.inner0)) / 2 + 1;\n"
+ " highp float d = 3.0 * min(gl_TessCoord.x, min(gl_TessCoord.y, gl_TessCoord.z));\n"
+ " highp int phase = int(d*float(numConcentricTriangles)) % 3;\n"
+ " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
+ " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+ " : vec4(0.0, 0.0, 1.0, 1.0);\n"
+ : caseDef.primitiveType == TESSPRIMITIVETYPE_QUADS ?
+ " highp vec2 corner0 = in_te_position[0];\n"
+ " highp vec2 corner1 = in_te_position[1];\n"
+ " highp vec2 corner2 = in_te_position[2];\n"
+ " highp vec2 corner3 = in_te_position[3];\n"
+ " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
+ " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
+ " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
+ " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
+ " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ " highp int phaseX = int(round((0.5 - abs(gl_TessCoord.x-0.5)) * sb_levels.inner0));\n"
+ " highp int phaseY = int(round((0.5 - abs(gl_TessCoord.y-0.5)) * sb_levels.inner1));\n"
+ " highp int phase = min(phaseX, phaseY) % 3;\n"
+ " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
+ " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+ " : vec4(0.0, 0.0, 1.0, 1.0);\n"
+ : "")
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+}
+
+void initProgramsIsolinesCase (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ DE_ASSERT(caseDef.primitiveType == TESSPRIMITIVETYPE_ISOLINES);
+
+ initCommonPrograms(programCollection, caseDef);
+
+ // 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(caseDef.primitiveType) << ", "
+ << getSpacingModeShaderName(caseDef.spacingMode) << ") in;\n"
+ << "\n"
+ << getTessLevelsSSBODeclaration()
+ << "\n"
+ << "layout(location = 0) in highp vec2 in_te_position[];\n"
+ << "layout(location = 0) out highp vec4 in_f_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " highp vec2 corner0 = in_te_position[0];\n"
+ << " highp vec2 corner1 = in_te_position[1];\n"
+ << " highp vec2 corner2 = in_te_position[2];\n"
+ << " highp vec2 corner3 = in_te_position[3];\n"
+ << " highp vec2 pos = (1.0-gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner0\n"
+ << " + ( gl_TessCoord.x)*(1.0-gl_TessCoord.y)*corner1\n"
+ << " + (1.0-gl_TessCoord.x)*( gl_TessCoord.y)*corner2\n"
+ << " + ( gl_TessCoord.x)*( gl_TessCoord.y)*corner3;\n"
+ << " pos.y += 0.15*sin(gl_TessCoord.x*10.0);\n"
+ << " gl_Position = vec4(pos, 0.0, 1.0);\n"
+ << " highp int phaseX = int(round(gl_TessCoord.x*sb_levels.outer1));\n"
+ << " highp int phaseY = int(round(gl_TessCoord.y*sb_levels.outer0));\n"
+ << " highp int phase = (phaseX + phaseY) % 3;\n"
+ << " in_f_color = phase == 0 ? vec4(1.0, 0.0, 0.0, 1.0)\n"
+ << " : phase == 1 ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+ << " : vec4(0.0, 0.0, 1.0, 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+}
+
+inline std::string getReferenceImagePathPrefix (const std::string& caseName)
+{
+ return "vulkan/data/tessellation/" + caseName + "_ref";
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.misc_draw.*
+tcu::TestCaseGroup* createMiscDrawTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "misc_draw", "Miscellaneous draw-result-verifying cases"));
+
+ static const TessPrimitiveType primitivesNoIsolines[] =
+ {
+ TESSPRIMITIVETYPE_TRIANGLES,
+ TESSPRIMITIVETYPE_QUADS,
+ };
+
+ // Triangle fill case
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+ {
+ const TessPrimitiveType primitiveType = primitivesNoIsolines[primitiveTypeNdx];
+ const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
+ const std::string caseName = std::string() + "fill_cover_" + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName(spacingMode);
+
+ addFunctionCaseWithPrograms(group.get(), caseName, "Check that there are no obvious gaps in the triangle-filled area of a tessellated shape",
+ initProgramsFillCoverCase, runTest, makeCaseDefinition(primitiveType, spacingMode, getReferenceImagePathPrefix(caseName)));
+ }
+
+ // Triangle non-overlap case
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+ {
+ const TessPrimitiveType primitiveType = primitivesNoIsolines[primitiveTypeNdx];
+ const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
+ const std::string caseName = std::string() + "fill_overlap_" + getTessPrimitiveTypeShaderName(primitiveType) + "_" + getSpacingModeShaderName(spacingMode);
+
+ addFunctionCaseWithPrograms(group.get(), caseName, "Check that there are no obvious triangle overlaps in the triangle-filled area of a tessellated shape",
+ initProgramsFillNonOverlapCase, runTest, makeCaseDefinition(primitiveType, spacingMode, getReferenceImagePathPrefix(caseName)));
+ }
+
+ // Isolines
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; ++spacingModeNdx)
+ {
+ const SpacingMode spacingMode = static_cast<SpacingMode>(spacingModeNdx);
+ const std::string caseName = std::string() + "isolines_" + getSpacingModeShaderName(spacingMode);
+
+ addFunctionCaseWithPrograms(group.get(), caseName, "Basic isolines render test",
+ initProgramsIsolinesCase, runTest, makeCaseDefinition(TESSPRIMITIVETYPE_ISOLINES, spacingMode, getReferenceImagePathPrefix(caseName)));
+ }
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONMISCDRAWTESTS_HPP
+#define _VKTTESSELLATIONMISCDRAWTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Miscellaneous Draw Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createMiscDrawTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONMISCDRAWTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Primitive Discard Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationPrimitiveDiscardTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+struct CaseDefinition
+{
+ TessPrimitiveType primitiveType;
+ SpacingMode spacingMode;
+ Winding winding;
+ bool usePointMode;
+};
+
+int intPow (int base, int exp)
+{
+ DE_ASSERT(exp >= 0);
+ if (exp == 0)
+ return 1;
+ else
+ {
+ const int sub = intPow(base, exp/2);
+ if (exp % 2 == 0)
+ return sub*sub;
+ else
+ return sub*sub*base;
+ }
+}
+
+std::vector<float> genAttributes (void)
+{
+ // Generate input attributes (tessellation levels, and position scale and
+ // offset) for a number of primitives. Each primitive has a different
+ // combination of tessellatio levels; each level is either a valid
+ // value or an "invalid" value (negative or zero, chosen from
+ // invalidTessLevelChoices).
+
+ // \note The attributes are generated in such an order that all of the
+ // valid attribute tuples come before the first invalid one both
+ // in the result vector, and when scanning the resulting 2d grid
+ // of primitives is scanned in y-major order. This makes
+ // verification somewhat simpler.
+
+ static const float baseTessLevels[6] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
+ static const float invalidTessLevelChoices[] = { -0.42f, 0.0f };
+ const int numChoices = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
+ float choices[6][numChoices];
+ std::vector<float> result;
+
+ for (int levelNdx = 0; levelNdx < 6; levelNdx++)
+ for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
+ choices[levelNdx][choiceNdx] = choiceNdx == 0 ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
+
+ {
+ const int numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
+ const int numRows = numCols;
+ int index = 0;
+ int i[6];
+ // We could do this with some generic combination-generation function, but meh, it's not that bad.
+ for (i[2] = 0; i[2] < numChoices; i[2]++) // First outer
+ for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
+ for (i[4] = 0; i[4] < numChoices; i[4]++) // Third outer
+ for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
+ for (i[0] = 0; i[0] < numChoices; i[0]++) // First inner
+ for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
+ {
+ for (int j = 0; j < 6; j++)
+ result.push_back(choices[j][i[j]]);
+
+ {
+ const int col = index % numCols;
+ const int row = index / numCols;
+ // Position scale.
+ result.push_back((float)2.0f / (float)numCols);
+ result.push_back((float)2.0f / (float)numRows);
+ // Position offset.
+ result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
+ result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
+ }
+
+ index++;
+ }
+ }
+
+ return result;
+}
+
+//! Check that white pixels are found around every non-discarded patch,
+//! and that only black pixels are found after the last non-discarded patch.
+//! Returns true on successful comparison.
+bool verifyResultImage (tcu::TestLog& log,
+ const int numPrimitives,
+ const int numAttribsPerPrimitive,
+ const TessPrimitiveType primitiveType,
+ const std::vector<float>& attributes,
+ const tcu::ConstPixelBufferAccess pixels)
+{
+ const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
+ const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
+
+ int lastWhitePixelRow = 0;
+ int secondToLastWhitePixelRow = 0;
+ int lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
+
+ for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
+ {
+ const float* const attr = &attributes[numAttribsPerPrimitive*patchNdx];
+ const bool validLevels = !isPatchDiscarded(primitiveType, &attr[2]);
+
+ if (validLevels)
+ {
+ // Not a discarded patch; check that at least one white pixel is found in its area.
+
+ const float* const scale = &attr[6];
+ const float* const offset = &attr[8];
+ const int x0 = (int)(( offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) - 1;
+ const int x1 = (int)((scale[0] + offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) + 1;
+ const int y0 = (int)(( offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) - 1;
+ const int y1 = (int)((scale[1] + offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) + 1;
+ bool pixelOk = false;
+
+ if (y1 > lastWhitePixelRow)
+ {
+ secondToLastWhitePixelRow = lastWhitePixelRow;
+ lastWhitePixelRow = y1;
+ }
+ lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
+
+ for (int y = y0; y <= y1 && !pixelOk; y++)
+ for (int x = x0; x <= x1 && !pixelOk; x++)
+ {
+ if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
+ continue;
+
+ if (pixels.getPixel(x, y) == white)
+ pixelOk = true;
+ }
+
+ if (!pixelOk)
+ {
+ log << tcu::TestLog::Message
+ << "Failure: expected at least one white pixel in the rectangle "
+ << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]"
+ << tcu::TestLog::EndMessage
+ << tcu::TestLog::Message
+ << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
+ << getTessellationLevelsString(&attr[0], &attr[1])
+ << tcu::TestLog::EndMessage;
+
+ return false;
+ }
+ }
+ else
+ {
+ // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
+
+ for (int y = 0; y < pixels.getHeight(); y++)
+ for (int x = 0; x < pixels.getWidth(); x++)
+ {
+ if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
+ {
+ if (pixels.getPixel(x, y) != black)
+ {
+ log << tcu::TestLog::Message
+ << "Failure: expected all pixels to be black in the area "
+ << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
+ ? std::string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
+ + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
+ : std::string() + "y > " + de::toString(lastWhitePixelRow))
+ << " (they all correspond to patches that should be discarded)"
+ << tcu::TestLog::EndMessage
+ << tcu::TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << tcu::TestLog::EndMessage;
+
+ return false;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+int expectedVertexCount (const int numPrimitives,
+ const int numAttribsPerPrimitive,
+ const TessPrimitiveType primitiveType,
+ const SpacingMode spacingMode,
+ const std::vector<float>& attributes)
+{
+ int count = 0;
+ for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
+ count += referenceVertexCount(primitiveType, spacingMode, true, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
+ return count;
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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(location = 0) in highp float in_tc_attr[];\n"
+ << "\n"
+ << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
+ << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
+ << " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = in_tc_attr[0];\n"
+ << " gl_TessLevelInner[1] = in_tc_attr[1];\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+ << " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+ << " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+ << " gl_TessLevelOuter[3] = in_tc_attr[5];\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(caseDef.primitiveType) << ", "
+ << getSpacingModeShaderName(caseDef.spacingMode) << ", "
+ << getWindingShaderName(caseDef.winding)
+ << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
+ << "\n"
+ << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
+ << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
+ << "\n"
+ << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+ << " int numInvocations;\n"
+ << "} sb_out;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " atomicAdd(sb_out.numInvocations, 1);\n"
+ << "\n"
+ << " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = vec4(1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+/*--------------------------------------------------------------------*//*!
+ * \brief Test that patch is discarded if relevant outer level <= 0.0
+ *
+ * Draws patches with different combinations of tessellation levels,
+ * varying which levels are negative. Verifies by checking that white
+ * pixels exist inside the area of valid primitives, and only black pixels
+ * exist inside the area of discarded primitives. An additional sanity
+ * test is done, checking that the number of primitives written by shader is
+ * correct.
+ *//*--------------------------------------------------------------------*/
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+ 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> attributes = genAttributes();
+ const int numAttribsPerPrimitive = 6 + 2 + 2; // Tess levels, scale, offset.
+ const int numPrimitives = static_cast<int>(attributes.size() / numAttribsPerPrimitive);
+ const int numExpectedVertices = expectedVertexCount(numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, caseDef.spacingMode, attributes);
+
+ // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
+ {
+ bool discardedPatchEncountered = false;
+ for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
+ {
+ const bool discard = isPatchDiscarded(caseDef.primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
+ DE_ASSERT(discard || !discardedPatchEncountered);
+ discardedPatchEncountered = discard;
+ }
+ DE_UNREF(discardedPatchEncountered);
+ }
+
+ // Vertex input attributes buffer
+
+ const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT;
+ const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
+ const VkDeviceSize vertexDataSizeBytes = sizeInBytes(attributes);
+ const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ DE_ASSERT(static_cast<int>(attributes.size()) == numPrimitives * numAttribsPerPrimitive);
+ DE_ASSERT(sizeof(attributes[0]) == vertexStride);
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+ // No barrier needed, flushed memory is automatically visible
+ }
+
+ // Output buffer: number of invocations
+
+ const VkDeviceSize resultBufferSizeBytes = sizeof(deInt32);
+ const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ {
+ const Allocation& alloc = resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+ }
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(256, 256);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer(vk, device, allocator,
+ makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Descriptors
+
+ const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+ .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)
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+ const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+ const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+ .update(vk, device);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setPatchControlPoints (numAttribsPerPrimitive)
+ .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+ .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)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ context.getTestContext().getLog()
+ << tcu::TestLog::Message
+ << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
+ << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels"
+ << tcu::TestLog::EndMessage;
+
+ // Draw commands
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ 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);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(attributes.size()), 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+ {
+ 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);
+
+ {
+ // Log rendered image
+ const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+ const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+
+ tcu::TestLog& log = context.getTestContext().getLog();
+ log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
+
+ // Verify case result
+ const Allocation& resultAlloc = resultBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+ const deInt32 numResultVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+
+ if (numResultVertices < numExpectedVertices)
+ {
+ log << tcu::TestLog::Message
+ << "Failure: expected " << numExpectedVertices << " vertices from shader invocations, but got only " << numResultVertices
+ << tcu::TestLog::EndMessage;
+ return tcu::TestStatus::fail("Wrong number of tessellation coordinates");
+ }
+ else if (numResultVertices == numExpectedVertices)
+ {
+ log << tcu::TestLog::Message
+ << "Note: shader invocations generated " << numResultVertices << " vertices"
+ << tcu::TestLog::EndMessage;
+ }
+ else
+ {
+ log << tcu::TestLog::Message
+ << "Note: shader invocations generated " << numResultVertices << " vertices (expected " << numExpectedVertices << ", got "
+ << (numResultVertices - numExpectedVertices) << " extra)"
+ << tcu::TestLog::EndMessage;
+ }
+
+ return (verifyResultImage(log, numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, attributes, imagePixelAccess)
+ ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image verification failed"));
+ }
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.primitive_discard.*
+//! \note Original test used transform feedback (TF) to capture the number of output vertices. The behavior of TF differs significantly from SSBO approach,
+//! especially for non-point_mode rendering. TF returned all coordinates, while SSBO computes the count based on the number of shader invocations
+//! which yields a much smaller number because invocations for duplicate coordinates are often eliminated.
+//! Because of this, the test was changed to:
+//! - always compute the number of expected coordinates as if point_mode was enabled
+//! - not fail if implementation returned more coordinates than expected
+tcu::TestCaseGroup* createPrimitiveDiscardTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0"));
+
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; primitiveTypeNdx++)
+ for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; spacingModeNdx++)
+ for (int windingNdx = 0; windingNdx < WINDING_LAST; windingNdx++)
+ for (int usePointModeNdx = 0; usePointModeNdx <= 1; usePointModeNdx++)
+ {
+ const CaseDefinition caseDef =
+ {
+ (TessPrimitiveType)primitiveTypeNdx,
+ (SpacingMode)spacingModeNdx,
+ (Winding)windingNdx,
+ (usePointModeNdx != 0),
+ };
+
+ const std::string caseName = std::string() + getTessPrimitiveTypeShaderName(caseDef.primitiveType)
+ + "_" + getSpacingModeShaderName(caseDef.spacingMode)
+ + "_" + getWindingShaderName(caseDef.winding)
+ + (caseDef.usePointMode ? "_point_mode" : "");
+
+ addFunctionCaseWithPrograms(group.get(), caseName, "", initPrograms, test, caseDef);
+ }
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONPRIMITIVEDISCARDTESTS_HPP
+#define _VKTTESSELLATIONPRIMITIVEDISCARDTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Primitive Discard Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createPrimitiveDiscardTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONPRIMITIVEDISCARDTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Shader Input/Output Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationShaderInputOutputTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageIO.hpp"
+#include "tcuTexture.hpp"
+#include "tcuImageCompare.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deStringUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+ RENDER_SIZE = 256,
+};
+
+//! Generic test code used by all test cases.
+tcu::TestStatus runTest (Context& context,
+ const int numPrimitives,
+ const int inPatchSize,
+ const int outPatchSize,
+ const VkFormat vertexFormat,
+ const void* vertexData,
+ const VkDeviceSize vertexDataSizeBytes,
+ const tcu::ConstPixelBufferAccess& referenceImageAccess)
+{
+ requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+ const DeviceInterface& vk = context.getDeviceInterface();
+ const VkDevice device = context.getDevice();
+ const VkQueue queue = context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = context.getDefaultAllocator();
+
+ // Vertex input: may be just some abstract numbers
+
+ const Buffer vertexBuffer(vk, device, allocator,
+ makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), vertexData, static_cast<std::size_t>(vertexDataSizeBytes));
+
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+ // No barrier needed, flushed memory is automatically visible
+ }
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setVertexInputSingleAttribute(vertexFormat, tcu::getPixelSize(mapVkFormat(vertexFormat)))
+ .setPatchControlPoints (inPatchSize)
+ .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)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ {
+ tcu::TestLog& log = context.getTestContext().getLog();
+ log << tcu::TestLog::Message
+ << "Note: input patch size is " << inPatchSize << ", output patch size is " << outPatchSize
+ << tcu::TestLog::EndMessage;
+ }
+
+ // Draw commands
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ // Process enough vertices to make a patch.
+ vk.cmdDraw(*cmdBuffer, numPrimitives * inPatchSize, 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ {
+ const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+ // Verify case result
+ const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+ tcu::TestLog& log = context.getTestContext().getLog();
+ const bool ok = tcu::fuzzyCompare(log, "ImageComparison", "Image Comparison", referenceImageAccess, resultImageAccess, 0.002f, tcu::COMPARE_LOG_RESULT);
+
+ return (ok ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+ }
+}
+
+namespace PatchVertexCount
+{
+
+struct CaseDefinition
+{
+ int inPatchSize;
+ int outPatchSize;
+ std::string referenceImagePath;
+};
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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 = " << caseDef.outPatchSize << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_tc_attr[];\n"
+ << "layout(location = 0) out highp float in_te_attr[];\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID*" << caseDef.inPatchSize << "/" << caseDef.outPatchSize << "];\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = 5.0;\n"
+ << " gl_TessLevelInner[1] = 5.0;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = 5.0;\n"
+ << " gl_TessLevelOuter[1] = 5.0;\n"
+ << " gl_TessLevelOuter[2] = 5.0;\n"
+ << " gl_TessLevelOuter[3] = 5.0;\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_QUADS) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_te_attr[];\n"
+ << "layout(location = 0) out mediump vec4 in_f_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
+ << " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" << caseDef.outPatchSize << "-1)))];\n"
+ << " gl_Position = vec4(x, y, 0.0, 1.0);\n"
+ << " in_f_color = vec4(1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in mediump vec4 in_f_color;\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = in_f_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+ // Input vertex attribute data
+ std::vector<float> vertexData;
+ vertexData.reserve(caseDef.inPatchSize);
+ for (int i = 0; i < caseDef.inPatchSize; ++i)
+ {
+ const float f = static_cast<float>(i) / (caseDef.inPatchSize - 1);
+ vertexData.push_back(f*f);
+ }
+ const VkDeviceSize vertexBufferSize = sizeof(float) * vertexData.size();
+
+ // Load reference image
+ tcu::TextureLevel referenceImage;
+ tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), caseDef.referenceImagePath.c_str());
+
+ const int numPrimitives = 1;
+
+ return runTest(context, numPrimitives, caseDef.inPatchSize, caseDef.outPatchSize,
+ VK_FORMAT_R32_SFLOAT, &vertexData[0], vertexBufferSize, referenceImage.getAccess());
+}
+
+} // PatchVertexCountTest ns
+
+namespace PerPatchData
+{
+
+enum CaseType
+{
+ CASETYPE_PRIMITIVE_ID_TCS,
+ CASETYPE_PRIMITIVE_ID_TES,
+ CASETYPE_PATCH_VERTICES_IN_TCS,
+ CASETYPE_PATCH_VERTICES_IN_TES,
+ CASETYPE_TESS_LEVEL_INNER0_TES,
+ CASETYPE_TESS_LEVEL_INNER1_TES,
+ CASETYPE_TESS_LEVEL_OUTER0_TES,
+ CASETYPE_TESS_LEVEL_OUTER1_TES,
+ CASETYPE_TESS_LEVEL_OUTER2_TES,
+ CASETYPE_TESS_LEVEL_OUTER3_TES,
+};
+
+enum Constants
+{
+ OUTPUT_PATCH_SIZE = 5,
+ INPUT_PATCH_SIZE = 10,
+};
+
+struct CaseDefinition
+{
+ CaseType caseType;
+ std::string caseName;
+ bool usesReferenceImageFromFile;
+ std::string referenceImagePath;
+ std::string caseDescription;
+};
+
+int getNumPrimitives (const CaseType type)
+{
+ return (type == CASETYPE_PRIMITIVE_ID_TCS || type == CASETYPE_PRIMITIVE_ID_TES ? 8 : 1);
+}
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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 = " << OUTPUT_PATCH_SIZE << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_tc_attr[];\n"
+ << "layout(location = 0) out highp float in_te_attr[];\n"
+ << "\n"
+ << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS ? "layout(location = 1) patch out mediump int in_te_primitiveIDFromTCS;\n" :
+ caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "layout(location = 1) patch out mediump int in_te_patchVerticesInFromTCS;\n" : "")
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
+ << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS ? " in_te_primitiveIDFromTCS = gl_PrimitiveID;\n" :
+ caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? " in_te_patchVerticesInFromTCS = gl_PatchVerticesIn;\n" : "")
+ << "\n"
+ << " gl_TessLevelInner[0] = 9.0;\n"
+ << " gl_TessLevelInner[1] = 8.0;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = 7.0;\n"
+ << " gl_TessLevelOuter[1] = 6.0;\n"
+ << " gl_TessLevelOuter[2] = 5.0;\n"
+ << " gl_TessLevelOuter[3] = 4.0;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ const float xScale = 1.0f / getNumPrimitives(caseDef.caseType);
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "\n"
+ << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_QUADS) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_te_attr[];\n"
+ << "layout(location = 0) out mediump vec4 in_f_color;\n"
+ << "\n"
+ << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS ? "layout(location = 1) patch in mediump int in_te_primitiveIDFromTCS;\n" :
+ caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? "layout(location = 1) patch in mediump int in_te_patchVerticesInFromTCS;\n" : "")
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " highp float x = (gl_TessCoord.x*float(" << xScale << ") + in_te_attr[0]) * 2.0 - 1.0;\n"
+ << " highp float y = gl_TessCoord.y*2.0 - 1.0;\n"
+ << " gl_Position = vec4(x, y, 0.0, 1.0);\n"
+ << (caseDef.caseType == CASETYPE_PRIMITIVE_ID_TCS ? " bool ok = in_te_primitiveIDFromTCS == 3;\n" :
+ caseDef.caseType == CASETYPE_PRIMITIVE_ID_TES ? " bool ok = gl_PrimitiveID == 3;\n" :
+ caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TCS ? " bool ok = in_te_patchVerticesInFromTCS == " + de::toString(INPUT_PATCH_SIZE) + ";\n" :
+ caseDef.caseType == CASETYPE_PATCH_VERTICES_IN_TES ? " bool ok = gl_PatchVerticesIn == " + de::toString(OUTPUT_PATCH_SIZE) + ";\n" :
+ caseDef.caseType == CASETYPE_TESS_LEVEL_INNER0_TES ? " bool ok = abs(gl_TessLevelInner[0] - 9.0) < 0.1f;\n" :
+ caseDef.caseType == CASETYPE_TESS_LEVEL_INNER1_TES ? " bool ok = abs(gl_TessLevelInner[1] - 8.0) < 0.1f;\n" :
+ caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER0_TES ? " bool ok = abs(gl_TessLevelOuter[0] - 7.0) < 0.1f;\n" :
+ caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER1_TES ? " bool ok = abs(gl_TessLevelOuter[1] - 6.0) < 0.1f;\n" :
+ caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER2_TES ? " bool ok = abs(gl_TessLevelOuter[2] - 5.0) < 0.1f;\n" :
+ caseDef.caseType == CASETYPE_TESS_LEVEL_OUTER3_TES ? " bool ok = abs(gl_TessLevelOuter[3] - 4.0) < 0.1f;\n" : "")
+ << " in_f_color = ok ? vec4(1.0) : vec4(vec3(0.0), 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in mediump vec4 in_f_color;\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = in_f_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+//! Resize an image and fill with white color.
+void initializeWhiteReferenceImage (tcu::TextureLevel& image, const int width, const int height)
+{
+ DE_ASSERT(width > 0 && height > 0);
+
+ image.setStorage(mapVkFormat(VK_FORMAT_R8G8B8A8_UNORM), width, height);
+ tcu::PixelBufferAccess access = image.getAccess();
+
+ const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
+
+ for (int y = 0; y < height; ++y)
+ for (int x = 0; x < width; ++x)
+ access.setPixel(white, x, y);
+}
+
+tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
+{
+ DE_ASSERT(!caseDef.usesReferenceImageFromFile || !caseDef.referenceImagePath.empty());
+
+ // Input vertex attribute data
+ const int numPrimitives = getNumPrimitives(caseDef.caseType);
+ std::vector<float> vertexData (INPUT_PATCH_SIZE * numPrimitives, 0.0f);
+ const VkDeviceSize vertexBufferSize = sizeof(float) * vertexData.size();
+
+ for (int i = 0; i < numPrimitives; ++i)
+ vertexData[INPUT_PATCH_SIZE * i] = static_cast<float>(i) / numPrimitives;
+
+ tcu::TextureLevel referenceImage;
+ if (caseDef.usesReferenceImageFromFile)
+ tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), caseDef.referenceImagePath.c_str());
+ else
+ initializeWhiteReferenceImage(referenceImage, RENDER_SIZE, RENDER_SIZE);
+
+ return runTest(context, numPrimitives, INPUT_PATCH_SIZE, OUTPUT_PATCH_SIZE,
+ VK_FORMAT_R32_SFLOAT, &vertexData[0], vertexBufferSize, referenceImage.getAccess());
+}
+
+} // PerPatchData ns
+
+namespace GLPosition
+{
+
+enum CaseType
+{
+ CASETYPE_VS_TO_TCS = 0,
+ CASETYPE_TCS_TO_TES,
+ CASETYPE_VS_TO_TCS_TO_TES,
+};
+
+void initPrograms (vk::SourceCollections& programCollection, const CaseType caseType)
+{
+ const bool vsToTCS = caseType == CASETYPE_VS_TO_TCS || caseType == CASETYPE_VS_TO_TCS_TO_TES;
+ const bool tcsToTES = caseType == CASETYPE_TCS_TO_TES || caseType == CASETYPE_VS_TO_TCS_TO_TES;
+
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 in_v_attr;\n"
+ << (!vsToTCS ? "layout(location = 0) out highp vec4 in_tc_attr;\n" : "")
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " " << (vsToTCS ? "gl_Position" : "in_tc_attr") << " = in_v_attr;\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 = 3) out;\n"
+ << "\n"
+ << (!vsToTCS ? "layout(location = 0) in highp vec4 in_tc_attr[];\n" : "")
+ << (!tcsToTES ? "layout(location = 0) out highp vec4 in_te_attr[];\n" : "")
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " " << (tcsToTES ? "gl_out[gl_InvocationID].gl_Position" : "in_te_attr[gl_InvocationID]") << " = "
+ << (vsToTCS ? "gl_in[gl_InvocationID].gl_Position" : "in_tc_attr[gl_InvocationID]") << ";\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = 2.0;\n"
+ << " gl_TessLevelInner[1] = 3.0;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = 4.0;\n"
+ << " gl_TessLevelOuter[1] = 5.0;\n"
+ << " gl_TessLevelOuter[2] = 6.0;\n"
+ << " gl_TessLevelOuter[3] = 7.0;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
+ }
+
+ // Tessellation evaluation shader
+ {
+ const std::string tesIn0 = tcsToTES ? "gl_in[0].gl_Position" : "in_te_attr[0]";
+ const std::string tesIn1 = tcsToTES ? "gl_in[1].gl_Position" : "in_te_attr[1]";
+ const std::string tesIn2 = tcsToTES ? "gl_in[2].gl_Position" : "in_te_attr[2]";
+
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "#extension GL_EXT_tessellation_shader : require\n"
+ << "\n"
+ << "layout(" << getTessPrimitiveTypeShaderName(TESSPRIMITIVETYPE_TRIANGLES) << ") in;\n"
+ << "\n"
+ << (!tcsToTES ? "layout(location = 0) in highp vec4 in_te_attr[];\n" : "")
+ << "layout(location = 0) out highp vec4 in_f_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " highp vec2 xy = gl_TessCoord.x * " << tesIn0 << ".xy\n"
+ << " + gl_TessCoord.y * " << tesIn1 << ".xy\n"
+ << " + gl_TessCoord.z * " << tesIn2 << ".xy;\n"
+ << " gl_Position = vec4(xy, 0.0, 1.0);\n"
+ << " in_f_color = vec4(" << tesIn0 << ".z + " << tesIn1 << ".w,\n"
+ << " " << tesIn2 << ".z + " << tesIn0 << ".w,\n"
+ << " " << tesIn1 << ".z + " << tesIn2 << ".w,\n"
+ << " 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 in_f_color;\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = in_f_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+tcu::TestStatus test (Context& context, const CaseType caseType)
+{
+ DE_UNREF(caseType);
+
+ // Input vertex attribute data
+ static const float vertexData[3*4] =
+ {
+ -0.8f, -0.7f, 0.1f, 0.7f,
+ -0.5f, 0.4f, 0.2f, 0.5f,
+ 0.3f, 0.2f, 0.3f, 0.45f
+ };
+
+ tcu::TextureLevel referenceImage;
+ tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), "vulkan/data/tessellation/gl_position_ref.png");
+
+ const int numPrimitives = 1;
+ const int inPatchSize = 3;
+ const int outPatchSize = 3;
+
+ return runTest(context, numPrimitives, inPatchSize, outPatchSize,
+ VK_FORMAT_R32G32B32A32_SFLOAT, vertexData, sizeof(vertexData), referenceImage.getAccess());
+}
+
+} // GLPosition ns
+
+namespace Barrier
+{
+
+enum Constants
+{
+ NUM_VERTICES = 32,
+};
+
+void initPrograms (vk::SourceCollections& programCollection)
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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 = " << NUM_VERTICES << ") out;\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_tc_attr[];\n"
+ << "layout(location = 0) out highp float in_te_attr[];\n"
+ << "\n"
+ << "layout(location = 1) patch out highp float in_te_patchAttr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_te_attr[gl_InvocationID] = in_tc_attr[gl_InvocationID];\n"
+ << " in_te_patchAttr = 0.0f;\n"
+ << "\n"
+ << " barrier();\n"
+ << "\n"
+ << " if (gl_InvocationID == 5)\n"
+ << " in_te_patchAttr = float(gl_InvocationID)*0.1;\n"
+ << "\n"
+ << " barrier();\n"
+ << "\n"
+ << " highp float temp = in_te_patchAttr + in_te_attr[gl_InvocationID];\n"
+ << "\n"
+ << " barrier();\n"
+ << "\n"
+ << " if (gl_InvocationID == " << NUM_VERTICES << "-1)\n"
+ << " in_te_patchAttr = float(gl_InvocationID);\n"
+ << "\n"
+ << " barrier();\n"
+ << "\n"
+ << " in_te_attr[gl_InvocationID] = temp;\n"
+ << "\n"
+ << " barrier();\n"
+ << "\n"
+ << " temp = temp + in_te_attr[(gl_InvocationID+1) % " << NUM_VERTICES << "];\n"
+ << "\n"
+ << " barrier();\n"
+ << "\n"
+ << " in_te_attr[gl_InvocationID] = 0.25*temp;\n"
+ << "\n"
+ << " gl_TessLevelInner[0] = 32.0;\n"
+ << " gl_TessLevelInner[1] = 32.0;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = 32.0;\n"
+ << " gl_TessLevelOuter[1] = 32.0;\n"
+ << " gl_TessLevelOuter[2] = 32.0;\n"
+ << " gl_TessLevelOuter[3] = 32.0;\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_QUADS) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_te_attr[];\n"
+ << "layout(location = 1) patch in highp float in_te_patchAttr;\n"
+ << "\n"
+ << "layout(location = 0) out highp float in_f_blue;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " highp float x = gl_TessCoord.x*2.0 - 1.0;\n"
+ << " highp float y = gl_TessCoord.y - in_te_attr[int(round(gl_TessCoord.x*float(" << NUM_VERTICES << "-1)))];\n"
+ << " gl_Position = vec4(x, y, 0.0, 1.0);\n"
+ << " in_f_blue = abs(in_te_patchAttr - float(" << NUM_VERTICES << "-1));\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_f_blue;\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = vec4(1.0, 0.0, in_f_blue, 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+tcu::TestStatus test (Context& context)
+{
+ // Input vertex attribute data
+ std::vector<float> vertexData (NUM_VERTICES);
+ const VkDeviceSize vertexBufferSize = sizeof(float) * vertexData.size();
+
+ for (int i = 0; i < NUM_VERTICES; ++i)
+ vertexData[i] = static_cast<float>(i) / (NUM_VERTICES - 1);
+
+ tcu::TextureLevel referenceImage;
+ tcu::ImageIO::loadPNG(referenceImage, context.getTestContext().getArchive(), "vulkan/data/tessellation/barrier_ref.png");
+
+ const int numPrimitives = 1;
+ const int inPatchSize = NUM_VERTICES;
+ const int outPatchSize = NUM_VERTICES;
+
+ return runTest(context, numPrimitives, inPatchSize, outPatchSize,
+ VK_FORMAT_R32_SFLOAT, &vertexData[0], vertexBufferSize, referenceImage.getAccess());
+}
+
+} // Barrier ns
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.shader_input_output.*
+tcu::TestCaseGroup* createShaderInputOutputTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "shader_input_output", "Test tessellation control and evaluation shader inputs and outputs"));
+
+ // Patch vertex counts
+ {
+ static const struct
+ {
+ int inPatchSize;
+ int outPatchSize;
+ } patchVertexCountCases[] =
+ {
+ { 5, 10 },
+ { 10, 5 }
+ };
+
+ for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(patchVertexCountCases); caseNdx++)
+ {
+ const int inSize = patchVertexCountCases[caseNdx].inPatchSize;
+ const int outSize = patchVertexCountCases[caseNdx].outPatchSize;
+
+ const std::string caseName = "patch_vertices_" + de::toString(inSize) + "_in_" + de::toString(outSize) + "_out";
+ const PatchVertexCount::CaseDefinition caseDef =
+ {
+ inSize, outSize, "vulkan/data/tessellation/" + caseName + "_ref.png"
+ };
+
+ addFunctionCaseWithPrograms(group.get(), caseName, "Test input and output patch vertex counts",
+ PatchVertexCount::initPrograms, PatchVertexCount::test, caseDef);
+ }
+ }
+
+ // Per patch data
+ {
+ static const PerPatchData::CaseDefinition cases[] =
+ {
+ { PerPatchData::CASETYPE_PRIMITIVE_ID_TCS, "primitive_id_tcs", true, "vulkan/data/tessellation/primitive_id_tcs_ref.png", "Read gl_PrimitiveID in TCS and pass it as patch output to TES" },
+ { PerPatchData::CASETYPE_PRIMITIVE_ID_TES, "primitive_id_tes", true, "vulkan/data/tessellation/primitive_id_tes_ref.png", "Read gl_PrimitiveID in TES" },
+ { PerPatchData::CASETYPE_PATCH_VERTICES_IN_TCS, "patch_vertices_in_tcs", false, "", "Read gl_PatchVerticesIn in TCS and pass it as patch output to TES" },
+ { PerPatchData::CASETYPE_PATCH_VERTICES_IN_TES, "patch_vertices_in_tes", false, "", "Read gl_PatchVerticesIn in TES" },
+ { PerPatchData::CASETYPE_TESS_LEVEL_INNER0_TES, "tess_level_inner_0_tes", false, "", "Read gl_TessLevelInner[0] in TES" },
+ { PerPatchData::CASETYPE_TESS_LEVEL_INNER1_TES, "tess_level_inner_1_tes", false, "", "Read gl_TessLevelInner[1] in TES" },
+ { PerPatchData::CASETYPE_TESS_LEVEL_OUTER0_TES, "tess_level_outer_0_tes", false, "", "Read gl_TessLevelOuter[0] in TES" },
+ { PerPatchData::CASETYPE_TESS_LEVEL_OUTER1_TES, "tess_level_outer_1_tes", false, "", "Read gl_TessLevelOuter[1] in TES" },
+ { PerPatchData::CASETYPE_TESS_LEVEL_OUTER2_TES, "tess_level_outer_2_tes", false, "", "Read gl_TessLevelOuter[2] in TES" },
+ { PerPatchData::CASETYPE_TESS_LEVEL_OUTER3_TES, "tess_level_outer_3_tes", false, "", "Read gl_TessLevelOuter[3] in TES" },
+ };
+
+ for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
+ addFunctionCaseWithPrograms(group.get(), cases[caseNdx].caseName, cases[caseNdx].caseDescription,
+ PerPatchData::initPrograms, PerPatchData::test, cases[caseNdx]);
+ }
+
+ // gl_Position
+ {
+ static const struct
+ {
+ GLPosition::CaseType type;
+ std::string caseName;
+ } cases[] =
+ {
+ { GLPosition::CASETYPE_VS_TO_TCS, "gl_position_vs_to_tcs" },
+ { GLPosition::CASETYPE_TCS_TO_TES, "gl_position_tcs_to_tes" },
+ { GLPosition::CASETYPE_VS_TO_TCS_TO_TES, "gl_position_vs_to_tcs_to_tes" },
+ };
+
+ for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(cases); ++caseNdx)
+ addFunctionCaseWithPrograms(group.get(), cases[caseNdx].caseName, "Pass gl_Position between VS and TCS, or between TCS and TES",
+ GLPosition::initPrograms, GLPosition::test, cases[caseNdx].type);
+ }
+
+ // Barrier
+ addFunctionCaseWithPrograms(group.get(), "barrier", "Basic barrier usage", Barrier::initPrograms, Barrier::test);
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONSHADERINPUTOUTPUTTESTS_HPP
+#define _VKTTESSELLATIONSHADERINPUTOUTPUTTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Shader Input/Output Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createShaderInputOutputTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONSHADERINPUTOUTPUTTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationTests.hpp"
+#include "vktTestGroupUtil.hpp"
+#include "vktTessellationLimitsTests.hpp"
+#include "vktTessellationCoordinatesTests.hpp"
+#include "vktTessellationWindingTests.hpp"
+#include "vktTessellationShaderInputOutputTests.hpp"
+#include "vktTessellationMiscDrawTests.hpp"
+#include "vktTessellationCommonEdgeTests.hpp"
+#include "vktTessellationFractionalSpacingTests.hpp"
+#include "vktTessellationPrimitiveDiscardTests.hpp"
+#include "vktTessellationInvarianceTests.hpp"
+#include "vktTessellationUserDefinedIO.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+namespace
+{
+
+void createChildren (tcu::TestCaseGroup* tessellationTests)
+{
+ tcu::TestContext& testCtx = tessellationTests->getTestContext();
+
+ tessellationTests->addChild(createLimitsTests (testCtx));
+ tessellationTests->addChild(createCoordinatesTests (testCtx));
+ tessellationTests->addChild(createWindingTests (testCtx));
+ tessellationTests->addChild(createShaderInputOutputTests(testCtx));
+ tessellationTests->addChild(createMiscDrawTests (testCtx));
+ tessellationTests->addChild(createCommonEdgeTests (testCtx));
+ tessellationTests->addChild(createFractionalSpacingTests(testCtx));
+ tessellationTests->addChild(createPrimitiveDiscardTests (testCtx));
+ tessellationTests->addChild(createInvarianceTests (testCtx));
+ tessellationTests->addChild(createUserDefinedIOTests (testCtx));
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
+{
+ return createTestGroup(testCtx, "tessellation", "Tessellation tests", createChildren);
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONTESTS_HPP
+#define _VKTTESSELLATIONTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 User Defined IO Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationUserDefinedIO.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuImageIO.hpp"
+
+#include "gluVarType.hpp"
+#include "gluVarTypeUtil.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deSharedPtr.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+enum Constants
+{
+ NUM_PER_PATCH_BLOCKS = 2,
+ NUM_PER_PATCH_ARRAY_ELEMS = 3,
+ NUM_OUTPUT_VERTICES = 5,
+ NUM_TESS_LEVELS = 6,
+ MAX_TESSELLATION_PATCH_SIZE = 32,
+ RENDER_SIZE = 256,
+};
+
+enum IOType
+{
+ IO_TYPE_PER_PATCH = 0,
+ IO_TYPE_PER_PATCH_ARRAY,
+ IO_TYPE_PER_PATCH_BLOCK,
+ IO_TYPE_PER_PATCH_BLOCK_ARRAY,
+ IO_TYPE_PER_VERTEX,
+ IO_TYPE_PER_VERTEX_BLOCK,
+
+ IO_TYPE_LAST
+};
+
+enum VertexIOArraySize
+{
+ VERTEX_IO_ARRAY_SIZE_IMPLICIT = 0,
+ VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN, //!< Use gl_MaxPatchVertices as size for per-vertex input array.
+ VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN, //!< Minimum maxTessellationPatchSize required by the spec.
+
+ VERTEX_IO_ARRAY_SIZE_LAST
+};
+
+struct CaseDefinition
+{
+ TessPrimitiveType primitiveType;
+ IOType ioType;
+ VertexIOArraySize vertexIOArraySize;
+ std::string referenceImagePath;
+};
+
+typedef std::string (*BasicTypeVisitFunc)(const std::string& name, glu::DataType type, int indentationDepth); //!< See glslTraverseBasicTypes below.
+
+class TopLevelObject
+{
+public:
+ virtual ~TopLevelObject (void) {}
+
+ virtual std::string name (void) const = 0;
+ virtual std::string declare (void) const = 0;
+ virtual std::string declareArray (const std::string& arraySizeExpr) const = 0;
+ virtual std::string glslTraverseBasicTypeArray (const int numArrayElements, //!< If negative, traverse just array[gl_InvocationID], not all indices.
+ const int indentationDepth,
+ BasicTypeVisitFunc) const = 0;
+ virtual std::string glslTraverseBasicType (const int indentationDepth,
+ BasicTypeVisitFunc) const = 0;
+ virtual int numBasicSubobjectsInElementType (void) const = 0;
+ virtual std::string basicSubobjectAtIndex (const int index, const int arraySize) const = 0;
+};
+
+std::string glslTraverseBasicTypes (const std::string& rootName,
+ const glu::VarType& rootType,
+ const int arrayNestingDepth,
+ const int indentationDepth,
+ const BasicTypeVisitFunc visit)
+{
+ if (rootType.isBasicType())
+ return visit(rootName, rootType.getBasicType(), indentationDepth);
+ else if (rootType.isArrayType())
+ {
+ const std::string indentation = std::string(indentationDepth, '\t');
+ const std::string loopIndexName = "i" + de::toString(arrayNestingDepth);
+ const std::string arrayLength = de::toString(rootType.getArraySize());
+ return indentation + "for (int " + loopIndexName + " = 0; " + loopIndexName + " < " + de::toString(rootType.getArraySize()) + "; ++" + loopIndexName + ")\n" +
+ indentation + "{\n" +
+ glslTraverseBasicTypes(rootName + "[" + loopIndexName + "]", rootType.getElementType(), arrayNestingDepth+1, indentationDepth+1, visit) +
+ indentation + "}\n";
+ }
+ else if (rootType.isStructType())
+ {
+ const glu::StructType& structType = *rootType.getStructPtr();
+ const int numMembers = structType.getNumMembers();
+ std::string result;
+
+ for (int membNdx = 0; membNdx < numMembers; ++membNdx)
+ {
+ const glu::StructMember& member = structType.getMember(membNdx);
+ result += glslTraverseBasicTypes(rootName + "." + member.getName(), member.getType(), arrayNestingDepth, indentationDepth, visit);
+ }
+
+ return result;
+ }
+ else
+ {
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+//! Used as the 'visit' argument for glslTraverseBasicTypes.
+std::string glslAssignBasicTypeObject (const std::string& name, const glu::DataType type, const int indentationDepth)
+{
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const std::string indentation = std::string(indentationDepth, '\t');
+ std::ostringstream result;
+
+ result << indentation << name << " = ";
+
+ if (type != glu::TYPE_FLOAT)
+ result << std::string() << glu::getDataTypeName(type) << "(";
+ for (int i = 0; i < scalarSize; ++i)
+ result << (i > 0 ? ", v+" + de::floatToString(0.8f*i, 1) : "v");
+ if (type != glu::TYPE_FLOAT)
+ result << ")";
+ result << ";\n"
+ << indentation << "v += 0.4;\n";
+ return result.str();
+}
+
+//! Used as the 'visit' argument for glslTraverseBasicTypes.
+std::string glslCheckBasicTypeObject (const std::string& name, const glu::DataType type, const int indentationDepth)
+{
+ const int scalarSize = glu::getDataTypeScalarSize(type);
+ const std::string indentation = std::string(indentationDepth, '\t');
+ std::ostringstream result;
+
+ result << indentation << "allOk = allOk && compare_" << glu::getDataTypeName(type) << "(" << name << ", ";
+
+ if (type != glu::TYPE_FLOAT)
+ result << std::string() << glu::getDataTypeName(type) << "(";
+ for (int i = 0; i < scalarSize; ++i)
+ result << (i > 0 ? ", v+" + de::floatToString(0.8f*i, 1) : "v");
+ if (type != glu::TYPE_FLOAT)
+ result << ")";
+ result << ");\n"
+ << indentation << "v += 0.4;\n"
+ << indentation << "if (allOk) ++firstFailedInputIndex;\n";
+
+ return result.str();
+}
+
+int numBasicSubobjectsInElementType (const std::vector<de::SharedPtr<TopLevelObject> >& objects)
+{
+ int result = 0;
+ for (int i = 0; i < static_cast<int>(objects.size()); ++i)
+ result += objects[i]->numBasicSubobjectsInElementType();
+ return result;
+}
+
+std::string basicSubobjectAtIndex (const int subobjectIndex, const std::vector<de::SharedPtr<TopLevelObject> >& objects, const int topLevelArraySize)
+{
+ int currentIndex = 0;
+ int objectIndex = 0;
+
+ for (; currentIndex < subobjectIndex; ++objectIndex)
+ currentIndex += objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
+
+ if (currentIndex > subobjectIndex)
+ {
+ --objectIndex;
+ currentIndex -= objects[objectIndex]->numBasicSubobjectsInElementType() * topLevelArraySize;
+ }
+
+ return objects[objectIndex]->basicSubobjectAtIndex(subobjectIndex - currentIndex, topLevelArraySize);
+}
+
+int numBasicSubobjects (const glu::VarType& type)
+{
+ if (type.isBasicType())
+ return 1;
+ else if (type.isArrayType())
+ return type.getArraySize()*numBasicSubobjects(type.getElementType());
+ else if (type.isStructType())
+ {
+ const glu::StructType& structType = *type.getStructPtr();
+ int result = 0;
+ for (int i = 0; i < structType.getNumMembers(); ++i)
+ result += numBasicSubobjects(structType.getMember(i).getType());
+ return result;
+ }
+ else
+ {
+ DE_ASSERT(false);
+ return -1;
+ }
+}
+
+class Variable : public TopLevelObject
+{
+public:
+ Variable (const std::string& name_, const glu::VarType& type, const bool isArray)
+ : m_name (name_)
+ , m_type (type)
+ , m_isArray (isArray)
+ {
+ DE_ASSERT(!type.isArrayType());
+ }
+
+ std::string name (void) const { return m_name; }
+ std::string declare (void) const;
+ std::string declareArray (const std::string& arraySizeExpr) const;
+ std::string glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc) const;
+ std::string glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc) const;
+ int numBasicSubobjectsInElementType (void) const;
+ std::string basicSubobjectAtIndex (const int index, const int arraySize) const;
+
+private:
+ std::string m_name;
+ glu::VarType m_type; //!< If this Variable is an array element, m_type is the element type; otherwise just the variable type.
+ const bool m_isArray;
+};
+
+std::string Variable::declare (void) const
+{
+ DE_ASSERT(!m_isArray);
+ return de::toString(glu::declare(m_type, m_name)) + ";\n";
+}
+
+std::string Variable::declareArray (const std::string& sizeExpr) const
+{
+ DE_ASSERT(m_isArray);
+ return de::toString(glu::declare(m_type, m_name)) + "[" + sizeExpr + "];\n";
+}
+
+std::string Variable::glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+ DE_ASSERT(m_isArray);
+
+ const bool traverseAsArray = numArrayElements >= 0;
+ const std::string traversedName = m_name + (!traverseAsArray ? "[gl_InvocationID]" : "");
+ const glu::VarType type = traverseAsArray ? glu::VarType(m_type, numArrayElements) : m_type;
+
+ return glslTraverseBasicTypes(traversedName, type, 0, indentationDepth, visit);
+}
+
+std::string Variable::glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+ DE_ASSERT(!m_isArray);
+ return glslTraverseBasicTypes(m_name, m_type, 0, indentationDepth, visit);
+}
+
+int Variable::numBasicSubobjectsInElementType (void) const
+{
+ return numBasicSubobjects(m_type);
+}
+
+std::string Variable::basicSubobjectAtIndex (const int subobjectIndex, const int arraySize) const
+{
+ const glu::VarType type = m_isArray ? glu::VarType(m_type, arraySize) : m_type;
+ int currentIndex = 0;
+
+ for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&type); basicIt != glu::BasicTypeIterator::end(&type); ++basicIt)
+ {
+ if (currentIndex == subobjectIndex)
+ return m_name + de::toString(glu::TypeAccessFormat(type, basicIt.getPath()));
+ ++currentIndex;
+ }
+ DE_ASSERT(false);
+ return DE_NULL;
+}
+
+class IOBlock : public TopLevelObject
+{
+public:
+ struct Member
+ {
+ std::string name;
+ glu::VarType type;
+
+ Member (const std::string& n, const glu::VarType& t) : name(n), type(t) {}
+ };
+
+ IOBlock (const std::string& blockName, const std::string& interfaceName, const std::vector<Member>& members)
+ : m_blockName (blockName)
+ , m_interfaceName (interfaceName)
+ , m_members (members)
+ {
+ }
+
+ std::string name (void) const { return m_interfaceName; }
+ std::string declare (void) const;
+ std::string declareArray (const std::string& arraySizeExpr) const;
+ std::string glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc) const;
+ std::string glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc) const;
+ int numBasicSubobjectsInElementType (void) const;
+ std::string basicSubobjectAtIndex (const int index, const int arraySize) const;
+
+private:
+ std::string m_blockName;
+ std::string m_interfaceName;
+ std::vector<Member> m_members;
+};
+
+std::string IOBlock::declare (void) const
+{
+ std::ostringstream buf;
+
+ buf << m_blockName << "\n"
+ << "{\n";
+
+ for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+ buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
+
+ buf << "} " << m_interfaceName << ";\n";
+ return buf.str();
+}
+
+std::string IOBlock::declareArray (const std::string& sizeExpr) const
+{
+ std::ostringstream buf;
+
+ buf << m_blockName << "\n"
+ << "{\n";
+
+ for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+ buf << "\t" << glu::declare(m_members[i].type, m_members[i].name) << ";\n";
+
+ buf << "} " << m_interfaceName << "[" << sizeExpr << "];\n";
+ return buf.str();
+}
+
+std::string IOBlock::glslTraverseBasicTypeArray (const int numArrayElements, const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+ if (numArrayElements >= 0)
+ {
+ const std::string indentation = std::string(indentationDepth, '\t');
+ std::ostringstream result;
+
+ result << indentation << "for (int i0 = 0; i0 < " << numArrayElements << "; ++i0)\n"
+ << indentation << "{\n";
+ for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+ result << glslTraverseBasicTypes(m_interfaceName + "[i0]." + m_members[i].name, m_members[i].type, 1, indentationDepth + 1, visit);
+ result << indentation + "}\n";
+ return result.str();
+ }
+ else
+ {
+ std::ostringstream result;
+ for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+ result << glslTraverseBasicTypes(m_interfaceName + "[gl_InvocationID]." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
+ return result.str();
+ }
+}
+
+std::string IOBlock::glslTraverseBasicType (const int indentationDepth, BasicTypeVisitFunc visit) const
+{
+ std::ostringstream result;
+ for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+ result << glslTraverseBasicTypes(m_interfaceName + "." + m_members[i].name, m_members[i].type, 0, indentationDepth, visit);
+ return result.str();
+}
+
+int IOBlock::numBasicSubobjectsInElementType (void) const
+{
+ int result = 0;
+ for (int i = 0; i < static_cast<int>(m_members.size()); ++i)
+ result += numBasicSubobjects(m_members[i].type);
+ return result;
+}
+
+std::string IOBlock::basicSubobjectAtIndex (const int subobjectIndex, const int arraySize) const
+{
+ int currentIndex = 0;
+ for (int arrayNdx = 0; arrayNdx < arraySize; ++arrayNdx)
+ for (int memberNdx = 0; memberNdx < static_cast<int>(m_members.size()); ++memberNdx)
+ {
+ const glu::VarType& membType = m_members[memberNdx].type;
+ for (glu::BasicTypeIterator basicIt = glu::BasicTypeIterator::begin(&membType); basicIt != glu::BasicTypeIterator::end(&membType); ++basicIt)
+ {
+ if (currentIndex == subobjectIndex)
+ return m_interfaceName + "[" + de::toString(arrayNdx) + "]." + m_members[memberNdx].name + de::toString(glu::TypeAccessFormat(membType, basicIt.getPath()));
+ currentIndex++;
+ }
+ }
+ DE_ASSERT(false);
+ return DE_NULL;
+}
+
+class UserDefinedIOTest : public TestCase
+{
+public:
+ UserDefinedIOTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef);
+ void initPrograms (vk::SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+private:
+ const CaseDefinition m_caseDef;
+ std::vector<glu::StructType> m_structTypes;
+ std::vector<de::SharedPtr<TopLevelObject> > m_tcsOutputs;
+ std::vector<de::SharedPtr<TopLevelObject> > m_tesInputs;
+ std::string m_tcsDeclarations;
+ std::string m_tcsStatements;
+ std::string m_tesDeclarations;
+ std::string m_tesStatements;
+};
+
+UserDefinedIOTest::UserDefinedIOTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const CaseDefinition caseDef)
+ : TestCase (testCtx, name, description)
+ , m_caseDef (caseDef)
+{
+ const bool isPerPatchIO = m_caseDef.ioType == IO_TYPE_PER_PATCH ||
+ m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ||
+ m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ||
+ m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
+
+ const bool isExplicitVertexArraySize = m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ||
+ m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN;
+
+ const std::string vertexAttrArrayInputSize = m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_IMPLICIT ? ""
+ : m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN ? "gl_MaxPatchVertices"
+ : m_caseDef.vertexIOArraySize == VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN ? de::toString(MAX_TESSELLATION_PATCH_SIZE)
+ : DE_NULL;
+
+ const char* const maybePatch = isPerPatchIO ? "patch " : "";
+ const std::string outMaybePatch = std::string() + maybePatch + "out ";
+ const std::string inMaybePatch = std::string() + maybePatch + "in ";
+ const bool useBlock = m_caseDef.ioType == IO_TYPE_PER_VERTEX_BLOCK ||
+ m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ||
+ m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY;
+ const int wrongNumElements = -2;
+
+ std::ostringstream tcsDeclarations;
+ std::ostringstream tcsStatements;
+ std::ostringstream tesDeclarations;
+ std::ostringstream tesStatements;
+
+ // Indices 0 and 1 are taken, see initPrograms()
+ int tcsNextOutputLocation = 2;
+ int tesNextInputLocation = 2;
+
+ m_structTypes.push_back(glu::StructType("S"));
+
+ const glu::VarType highpFloat (glu::TYPE_FLOAT, glu::PRECISION_HIGHP);
+ glu::StructType& structType = m_structTypes.back();
+ const glu::VarType structVarType (&structType);
+ bool usedStruct = false;
+
+ structType.addMember("x", glu::VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
+ structType.addMember("y", glu::VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
+
+ // It is illegal to have a structure containing an array as an output variable
+ if (useBlock)
+ structType.addMember("z", glu::VarType(highpFloat, 2));
+
+ if (useBlock)
+ {
+ std::vector<IOBlock::Member> blockMembers;
+
+ // use leaner block to make sure it is not larger than allowed (per-patch storage is very limited)
+ const bool useLightweightBlock = (m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY);
+
+ if (!useLightweightBlock)
+ blockMembers.push_back(IOBlock::Member("blockS", structVarType));
+
+ blockMembers.push_back(IOBlock::Member("blockFa", glu::VarType(highpFloat, 3)));
+ blockMembers.push_back(IOBlock::Member("blockSa", glu::VarType(structVarType, 2)));
+ blockMembers.push_back(IOBlock::Member("blockF", highpFloat));
+
+ m_tcsOutputs.push_back (de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "tcBlock", blockMembers)));
+ m_tesInputs.push_back (de::SharedPtr<TopLevelObject>(new IOBlock("TheBlock", "teBlock", blockMembers)));
+
+ usedStruct = true;
+ }
+ else
+ {
+ const Variable var0("in_te_s", structVarType, m_caseDef.ioType != IO_TYPE_PER_PATCH);
+ const Variable var1("in_te_f", highpFloat, m_caseDef.ioType != IO_TYPE_PER_PATCH);
+
+ if (m_caseDef.ioType != IO_TYPE_PER_PATCH_ARRAY)
+ {
+ // Arrays of structures are disallowed, add struct cases only if not arrayed variable
+ m_tcsOutputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var0)));
+ m_tesInputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var0)));
+
+ usedStruct = true;
+ }
+
+ m_tcsOutputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var1)));
+ m_tesInputs.push_back (de::SharedPtr<TopLevelObject>(new Variable(var1)));
+ }
+
+ if (usedStruct)
+ tcsDeclarations << de::toString(glu::declare(structType)) + ";\n";
+
+ tcsStatements << "\t{\n"
+ << "\t\thighp float v = 1.3;\n";
+
+ for (int tcsOutputNdx = 0; tcsOutputNdx < static_cast<int>(m_tcsOutputs.size()); ++tcsOutputNdx)
+ {
+ const TopLevelObject& output = *m_tcsOutputs[tcsOutputNdx];
+ const int numElements = !isPerPatchIO ? -1 //!< \note -1 means indexing with gl_InstanceID
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH ? 1
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
+ : wrongNumElements;
+ const bool isArray = (numElements != 1);
+
+ DE_ASSERT(numElements != wrongNumElements);
+
+ // \note: TCS output arrays are always implicitly-sized
+ tcsDeclarations << "layout(location = " << tcsNextOutputLocation << ") ";
+ if (isArray)
+ tcsDeclarations << outMaybePatch << output.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(NUM_PER_PATCH_BLOCKS)
+ : "");
+ else
+ tcsDeclarations << outMaybePatch << output.declare();
+
+ tcsNextOutputLocation += output.numBasicSubobjectsInElementType();
+
+ if (!isPerPatchIO)
+ tcsStatements << "\t\tv += float(gl_InvocationID)*" << de::floatToString(0.4f * output.numBasicSubobjectsInElementType(), 1) << ";\n";
+
+ tcsStatements << "\n\t\t// Assign values to output " << output.name() << "\n";
+ if (isArray)
+ tcsStatements << output.glslTraverseBasicTypeArray(numElements, 2, glslAssignBasicTypeObject);
+ else
+ tcsStatements << output.glslTraverseBasicType(2, glslAssignBasicTypeObject);
+
+ if (!isPerPatchIO)
+ tcsStatements << "\t\tv += float(" << de::toString(NUM_OUTPUT_VERTICES) << "-gl_InvocationID-1)*" << de::floatToString(0.4f * output.numBasicSubobjectsInElementType(), 1) << ";\n";
+ }
+ tcsStatements << "\t}\n";
+
+ tcsDeclarations << "\n"
+ << "layout(location = 0) in " + Variable("in_tc_attr", highpFloat, true).declareArray(vertexAttrArrayInputSize);
+
+ if (usedStruct)
+ tesDeclarations << de::toString(glu::declare(structType)) << ";\n";
+
+ tesStatements << "\tbool allOk = true;\n"
+ << "\thighp uint firstFailedInputIndex = 0u;\n"
+ << "\t{\n"
+ << "\t\thighp float v = 1.3;\n";
+
+ for (int tesInputNdx = 0; tesInputNdx < static_cast<int>(m_tesInputs.size()); ++tesInputNdx)
+ {
+ const TopLevelObject& input = *m_tesInputs[tesInputNdx];
+ const int numElements = !isPerPatchIO ? NUM_OUTPUT_VERTICES
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH ? 1
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
+ : wrongNumElements;
+ const bool isArray = (numElements != 1);
+
+ DE_ASSERT(numElements != wrongNumElements);
+
+ tesDeclarations << "layout(location = " << tesNextInputLocation << ") ";
+ if (isArray)
+ tesDeclarations << inMaybePatch << input.declareArray(m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? de::toString(NUM_PER_PATCH_ARRAY_ELEMS)
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? de::toString(NUM_PER_PATCH_BLOCKS)
+ : isExplicitVertexArraySize ? de::toString(vertexAttrArrayInputSize)
+ : "");
+ else
+ tesDeclarations << inMaybePatch + input.declare();
+
+ tesNextInputLocation += input.numBasicSubobjectsInElementType();
+
+ tesStatements << "\n\t\t// Check values in input " << input.name() << "\n";
+ if (isArray)
+ tesStatements << input.glslTraverseBasicTypeArray(numElements, 2, glslCheckBasicTypeObject);
+ else
+ tesStatements << input.glslTraverseBasicType(2, glslCheckBasicTypeObject);
+ }
+ tesStatements << "\t}\n";
+
+ m_tcsDeclarations = tcsDeclarations.str();
+ m_tcsStatements = tcsStatements.str();
+ m_tesDeclarations = tesDeclarations.str();
+ m_tesStatements = tesStatements.str();
+}
+
+void UserDefinedIOTest::initPrograms (vk::SourceCollections& programCollection) const
+{
+ // Vertex shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp float in_v_attr;\n"
+ << "layout(location = 0) out highp float in_tc_attr;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " in_tc_attr = in_v_attr;\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 = " << NUM_OUTPUT_VERTICES << ") out;\n"
+ << "\n"
+ << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
+ << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
+ << "\n"
+ << m_tcsDeclarations
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << m_tcsStatements
+ << "\n"
+ << " gl_TessLevelInner[0] = in_tc_attr[0];\n"
+ << " gl_TessLevelInner[1] = in_tc_attr[1];\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
+ << " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
+ << " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
+ << " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
+ << "\n"
+ << " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
+ << " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\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(m_caseDef.primitiveType) << ") in;\n"
+ << "\n"
+ << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
+ << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
+ << "\n"
+ << m_tesDeclarations
+ << "\n"
+ << "layout(location = 0) out highp vec4 in_f_color;\n"
+ << "\n"
+ << "// Will contain the index of the first incorrect input,\n"
+ << "// or the number of inputs if all are correct\n"
+ << "layout (set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
+ << " int numInvocations;\n"
+ << " uint firstFailedInputIndex[];\n"
+ << "} sb_out;\n"
+ << "\n"
+ << "bool compare_int (int a, int b) { return a == b; }\n"
+ << "bool compare_float (float a, float b) { return abs(a - b) < 0.01f; }\n"
+ << "bool compare_vec4 (vec4 a, vec4 b) { return all(lessThan(abs(a - b), vec4(0.01f))); }\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << m_tesStatements
+ << "\n"
+ << " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n"
+ << " in_f_color = allOk ? vec4(0.0, 1.0, 0.0, 1.0)\n"
+ << " : vec4(1.0, 0.0, 0.0, 1.0);\n"
+ << "\n"
+ << " int index = atomicAdd(sb_out.numInvocations, 1);\n"
+ << " sb_out.firstFailedInputIndex[index] = firstFailedInputIndex;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) in highp vec4 in_f_color;\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = in_f_color;\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+class UserDefinedIOTestInstance : public TestInstance
+{
+public:
+ UserDefinedIOTestInstance (Context& context,
+ const CaseDefinition caseDef,
+ const std::vector<de::SharedPtr<TopLevelObject> >& tesInputs);
+ tcu::TestStatus iterate (void);
+
+private:
+ const CaseDefinition m_caseDef;
+ const std::vector<de::SharedPtr<TopLevelObject> > m_tesInputs;
+};
+
+UserDefinedIOTestInstance::UserDefinedIOTestInstance (Context& context, const CaseDefinition caseDef, const std::vector<de::SharedPtr<TopLevelObject> >& tesInputs)
+ : TestInstance (context)
+ , m_caseDef (caseDef)
+ , m_tesInputs (tesInputs)
+{
+}
+
+tcu::TestStatus UserDefinedIOTestInstance::iterate (void)
+{
+ requireFeatures(m_context.getInstanceInterface(), m_context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
+
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = m_context.getDefaultAllocator();
+
+ const int numAttributes = NUM_TESS_LEVELS + 2 + 2;
+ static const float attributes[numAttributes] = { /* inner */ 3.0f, 4.0f, /* outer */ 5.0f, 6.0f, 7.0f, 8.0f, /* pos. scale */ 1.2f, 1.3f, /* pos. offset */ -0.3f, -0.4f };
+ const int refNumVertices = referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, false, &attributes[0], &attributes[2]);
+ const int refNumUniqueVertices = referenceVertexCount(m_caseDef.primitiveType, SPACINGMODE_EQUAL, true, &attributes[0], &attributes[2]);
+
+ // Vertex input attributes buffer: to pass tessellation levels
+
+ const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT;
+ const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
+ const VkDeviceSize vertexDataSizeBytes = numAttributes * vertexStride;
+ const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ {
+ const Allocation& alloc = vertexBuffer.getAllocation();
+ deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), vertexDataSizeBytes);
+ }
+
+ // Output buffer: number of invocations and verification indices
+
+ const int resultBufferMaxVertices = refNumVertices;
+ const VkDeviceSize resultBufferSizeBytes = sizeof(deInt32) + resultBufferMaxVertices * sizeof(deUint32);
+ const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
+
+ {
+ const Allocation& alloc = resultBuffer.getAllocation();
+ deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
+ flushMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), resultBufferSizeBytes);
+ }
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Descriptors
+
+ const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
+ .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)
+ .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
+
+ const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
+ const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
+
+ DescriptorSetUpdateBuilder()
+ .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
+ .update(vk, device);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer (makeCommandBuffer (vk, device, *cmdPool));
+
+ const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setPatchControlPoints (numAttributes)
+ .setVertexInputSingleAttribute(vertexFormat, vertexStride)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ // Begin draw
+
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ 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);
+ {
+ const VkDeviceSize vertexBufferOffset = 0ull;
+ vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ }
+
+ vk.cmdDraw(*cmdBuffer, numAttributes, 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ // Verification
+
+ bool isImageCompareOK = false;
+ {
+ const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+ // Load reference image
+ tcu::TextureLevel referenceImage;
+ tcu::ImageIO::loadPNG(referenceImage, m_context.getTestContext().getArchive(), m_caseDef.referenceImagePath.c_str());
+
+ // Verify case result
+ const tcu::ConstPixelBufferAccess resultImageAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+ isImageCompareOK = tcu::fuzzyCompare(m_context.getTestContext().getLog(), "ImageComparison", "Image Comparison",
+ referenceImage.getAccess(), resultImageAccess, 0.02f, tcu::COMPARE_LOG_RESULT);
+ }
+ {
+ const Allocation& resultAlloc = resultBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), resultBufferSizeBytes);
+
+ const deInt32 numVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
+ const deUint32* const vertices = reinterpret_cast<deUint32*>(static_cast<deUint8*>(resultAlloc.getHostPtr()) + sizeof(deInt32));
+
+ // If this fails then we didn't read all vertices from shader and test must be changed to allow more.
+ DE_ASSERT(numVertices <= refNumVertices);
+
+ if (numVertices < refNumUniqueVertices)
+ {
+ m_context.getTestContext().getLog()
+ << tcu::TestLog::Message << "Failure: got " << numVertices << " vertices, but expected at least " << refNumUniqueVertices << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Wrong number of vertices");
+ }
+ else
+ {
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ const int topLevelArraySize = (m_caseDef.ioType == IO_TYPE_PER_PATCH ? 1
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_ARRAY ? NUM_PER_PATCH_ARRAY_ELEMS
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK ? 1
+ : m_caseDef.ioType == IO_TYPE_PER_PATCH_BLOCK_ARRAY ? NUM_PER_PATCH_BLOCKS
+ : NUM_OUTPUT_VERTICES);
+ const deUint32 numTEInputs = numBasicSubobjectsInElementType(m_tesInputs) * topLevelArraySize;
+
+ for (int vertexNdx = 0; vertexNdx < numVertices; ++vertexNdx)
+ if (vertices[vertexNdx] > numTEInputs)
+ {
+ log << tcu::TestLog::Message
+ << "Failure: out_te_firstFailedInputIndex has value " << vertices[vertexNdx]
+ << ", but should be in range [0, " << numTEInputs << "]" << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid values returned from shader");
+ }
+ else if (vertices[vertexNdx] != numTEInputs)
+ {
+ log << tcu::TestLog::Message << "Failure: in tessellation evaluation shader, check for input "
+ << basicSubobjectAtIndex(vertices[vertexNdx], m_tesInputs, topLevelArraySize) << " failed" << tcu::TestLog::EndMessage;
+
+ return tcu::TestStatus::fail("Invalid input value in tessellation evaluation shader");
+ }
+ }
+ }
+ return (isImageCompareOK ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image comparison failed"));
+}
+
+TestInstance* UserDefinedIOTest::createInstance (Context& context) const
+{
+ return new UserDefinedIOTestInstance(context, m_caseDef, m_tesInputs);
+}
+
+} // anonymous
+
+//! These tests correspond roughly to dEQP-GLES31.functional.tessellation.user_defined_io.*
+//! Original GLES test queried maxTessellationPatchSize, but this can't be done at the stage the shader source is prepared.
+//! Instead, we use minimum supported value.
+//! Negative tests weren't ported because vktShaderLibrary doesn't support tests that are expected to fail shader compilation.
+tcu::TestCaseGroup* createUserDefinedIOTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "user_defined_io", "Test non-built-in per-patch and per-vertex inputs and outputs"));
+
+ static const struct
+ {
+ const char* name;
+ const char* description;
+ IOType ioType;
+ } ioCases[] =
+ {
+ { "per_patch", "Per-patch TCS outputs", IO_TYPE_PER_PATCH },
+ { "per_patch_array", "Per-patch array TCS outputs", IO_TYPE_PER_PATCH_ARRAY },
+ { "per_patch_block", "Per-patch TCS outputs in IO block", IO_TYPE_PER_PATCH_BLOCK },
+ { "per_patch_block_array", "Per-patch TCS outputs in IO block array", IO_TYPE_PER_PATCH_BLOCK_ARRAY },
+ { "per_vertex", "Per-vertex TCS outputs", IO_TYPE_PER_VERTEX },
+ { "per_vertex_block", "Per-vertex TCS outputs in IO block", IO_TYPE_PER_VERTEX_BLOCK },
+ };
+
+ static const struct
+ {
+ const char* name;
+ VertexIOArraySize vertexIOArraySize;
+ } vertexArraySizeCases[] =
+ {
+ { "vertex_io_array_size_implicit", VERTEX_IO_ARRAY_SIZE_IMPLICIT },
+ { "vertex_io_array_size_shader_builtin", VERTEX_IO_ARRAY_SIZE_EXPLICIT_SHADER_BUILTIN },
+ { "vertex_io_array_size_spec_min", VERTEX_IO_ARRAY_SIZE_EXPLICIT_SPEC_MIN },
+ };
+
+ for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(ioCases); ++caseNdx)
+ {
+ de::MovePtr<tcu::TestCaseGroup> ioTypeGroup (new tcu::TestCaseGroup(testCtx, ioCases[caseNdx].name, ioCases[caseNdx].description));
+ for (int arrayCaseNdx = 0; arrayCaseNdx < DE_LENGTH_OF_ARRAY(vertexArraySizeCases); ++arrayCaseNdx)
+ {
+ de::MovePtr<tcu::TestCaseGroup> vertexArraySizeGroup (new tcu::TestCaseGroup(testCtx, vertexArraySizeCases[arrayCaseNdx].name, ""));
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; ++primitiveTypeNdx)
+ {
+ const TessPrimitiveType primitiveType = static_cast<TessPrimitiveType>(primitiveTypeNdx);
+ const std::string primitiveName = getTessPrimitiveTypeShaderName(primitiveType);
+ const CaseDefinition caseDef = { primitiveType, ioCases[caseNdx].ioType, vertexArraySizeCases[arrayCaseNdx].vertexIOArraySize,
+ std::string() + "vulkan/data/tessellation/user_defined_io_" + primitiveName + "_ref.png" };
+
+ vertexArraySizeGroup->addChild(new UserDefinedIOTest(testCtx, primitiveName, "", caseDef));
+ }
+ ioTypeGroup->addChild(vertexArraySizeGroup.release());
+ }
+ group->addChild(ioTypeGroup.release());
+ }
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONUSERDEFINEDIOTESTS_HPP
+#define _VKTTESSELLATIONUSERDEFINEDIOTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 User Defined IO Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createUserDefinedIOTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONUSERDEFINEDIOTESTS_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Utilities
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "deMath.h"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+VkBufferCreateInfo makeBufferCreateInfo (const VkDeviceSize bufferSize,
+ const VkBufferUsageFlags usage)
+{
+ const VkBufferCreateInfo bufferCreateInfo =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkBufferCreateFlags)0, // VkBufferCreateFlags flags;
+ bufferSize, // VkDeviceSize size;
+ usage, // VkBufferUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // deUint32 queueFamilyIndexCount;
+ DE_NULL, // const deUint32* pQueueFamilyIndices;
+ };
+ return bufferCreateInfo;
+}
+
+VkBufferMemoryBarrier makeBufferMemoryBarrier (const VkAccessFlags srcAccessMask,
+ const VkAccessFlags dstAccessMask,
+ const VkBuffer buffer,
+ const VkDeviceSize offset,
+ const VkDeviceSize bufferSizeBytes)
+{
+ const VkBufferMemoryBarrier barrier =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ srcAccessMask, // VkAccessFlags srcAccessMask;
+ dstAccessMask, // VkAccessFlags dstAccessMask;
+ VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
+ VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
+ buffer, // VkBuffer buffer;
+ offset, // VkDeviceSize offset;
+ bufferSizeBytes, // VkDeviceSize size;
+ };
+ return barrier;
+}
+
+VkImageMemoryBarrier makeImageMemoryBarrier (const VkAccessFlags srcAccessMask,
+ const VkAccessFlags dstAccessMask,
+ const VkImageLayout oldLayout,
+ const VkImageLayout newLayout,
+ const VkImage image,
+ const VkImageSubresourceRange subresourceRange)
+{
+ const VkImageMemoryBarrier barrier =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ srcAccessMask, // VkAccessFlags outputMask;
+ dstAccessMask, // VkAccessFlags inputMask;
+ oldLayout, // VkImageLayout oldLayout;
+ newLayout, // VkImageLayout newLayout;
+ VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
+ VK_QUEUE_FAMILY_IGNORED, // deUint32 destQueueFamilyIndex;
+ image, // VkImage image;
+ subresourceRange, // VkImageSubresourceRange subresourceRange;
+ };
+ return barrier;
+}
+
+Move<VkCommandPool> makeCommandPool (const DeviceInterface& vk, const VkDevice device, const deUint32 queueFamilyIndex)
+{
+ const VkCommandPoolCreateInfo info =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, // VkCommandPoolCreateFlags flags;
+ queueFamilyIndex, // deUint32 queueFamilyIndex;
+ };
+ return createCommandPool(vk, device, &info);
+}
+
+Move<VkCommandBuffer> makeCommandBuffer (const DeviceInterface& vk, const VkDevice device, const VkCommandPool commandPool)
+{
+ const VkCommandBufferAllocateInfo info =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ commandPool, // VkCommandPool commandPool;
+ VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCommandBufferLevel level;
+ 1u, // deUint32 commandBufferCount;
+ };
+ return allocateCommandBuffer(vk, device, &info);
+}
+
+Move<VkDescriptorSet> makeDescriptorSet (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkDescriptorPool descriptorPool,
+ const VkDescriptorSetLayout setLayout)
+{
+ const VkDescriptorSetAllocateInfo info =
+ {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ descriptorPool, // VkDescriptorPool descriptorPool;
+ 1u, // deUint32 descriptorSetCount;
+ &setLayout, // const VkDescriptorSetLayout* pSetLayouts;
+ };
+ return allocateDescriptorSet(vk, device, &info);
+}
+
+Move<VkPipelineLayout> makePipelineLayout (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkDescriptorSetLayout descriptorSetLayout)
+{
+ const VkPipelineLayoutCreateInfo info =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
+ 1u, // deUint32 setLayoutCount;
+ &descriptorSetLayout, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL, // const VkPushConstantRange* pPushConstantRanges;
+ };
+ return createPipelineLayout(vk, device, &info);
+}
+
+Move<VkPipelineLayout> makePipelineLayoutWithoutDescriptors (const DeviceInterface& vk,
+ const VkDevice device)
+{
+ const VkPipelineLayoutCreateInfo info =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineLayoutCreateFlags)0, // VkPipelineLayoutCreateFlags flags;
+ 0u, // deUint32 setLayoutCount;
+ DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
+ 0u, // deUint32 pushConstantRangeCount;
+ DE_NULL, // const VkPushConstantRange* pPushConstantRanges;
+ };
+ return createPipelineLayout(vk, device, &info);
+}
+
+Move<VkPipeline> makeComputePipeline (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkPipelineLayout pipelineLayout,
+ const VkShaderModule shaderModule,
+ const VkSpecializationInfo* specInfo)
+{
+ const VkPipelineShaderStageCreateInfo shaderStageInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ VK_SHADER_STAGE_COMPUTE_BIT, // VkShaderStageFlagBits stage;
+ shaderModule, // VkShaderModule module;
+ "main", // const char* pName;
+ specInfo, // const VkSpecializationInfo* pSpecializationInfo;
+ };
+ const VkComputePipelineCreateInfo pipelineInfo =
+ {
+ VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags;
+ shaderStageInfo, // VkPipelineShaderStageCreateInfo stage;
+ pipelineLayout, // VkPipelineLayout layout;
+ DE_NULL, // VkPipeline basePipelineHandle;
+ 0, // deInt32 basePipelineIndex;
+ };
+ return createComputePipeline(vk, device, DE_NULL , &pipelineInfo);
+}
+
+VkImageCreateInfo makeImageCreateInfo (const tcu::IVec2& size, const VkFormat format, const VkImageUsageFlags usage, const deUint32 numArrayLayers)
+{
+ const VkImageCreateInfo imageInfo =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkImageCreateFlags)0, // VkImageCreateFlags flags;
+ VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ format, // VkFormat format;
+ makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
+ 1u, // uint32_t mipLevels;
+ numArrayLayers, // uint32_t arrayLayers;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ usage, // VkImageUsageFlags usage;
+ VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // uint32_t queueFamilyIndexCount;
+ DE_NULL, // const uint32_t* pQueueFamilyIndices;
+ VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+ return imageInfo;
+}
+
+Move<VkImageView> makeImageView (const DeviceInterface& vk,
+ const VkDevice vkDevice,
+ const VkImage image,
+ const VkImageViewType viewType,
+ const VkFormat format,
+ const VkImageSubresourceRange subresourceRange)
+{
+ const VkImageViewCreateInfo imageViewParams =
+ {
+ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkImageViewCreateFlags)0, // VkImageViewCreateFlags flags;
+ image, // VkImage image;
+ viewType, // VkImageViewType viewType;
+ format, // VkFormat format;
+ makeComponentMappingRGBA(), // VkComponentMapping components;
+ subresourceRange, // VkImageSubresourceRange subresourceRange;
+ };
+ return createImageView(vk, vkDevice, &imageViewParams);
+}
+
+VkBufferImageCopy makeBufferImageCopy (const VkExtent3D extent,
+ const VkImageSubresourceLayers subresourceLayers)
+{
+ const VkBufferImageCopy copyParams =
+ {
+ 0ull, // VkDeviceSize bufferOffset;
+ 0u, // deUint32 bufferRowLength;
+ 0u, // deUint32 bufferImageHeight;
+ subresourceLayers, // VkImageSubresourceLayers imageSubresource;
+ makeOffset3D(0, 0, 0), // VkOffset3D imageOffset;
+ extent, // VkExtent3D imageExtent;
+ };
+ return copyParams;
+}
+
+void beginCommandBuffer (const DeviceInterface& vk, const VkCommandBuffer commandBuffer)
+{
+ const VkCommandBufferBeginInfo info =
+ {
+ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, // VkCommandBufferUsageFlags flags;
+ DE_NULL, // const VkCommandBufferInheritanceInfo* pInheritanceInfo;
+ };
+ VK_CHECK(vk.beginCommandBuffer(commandBuffer, &info));
+}
+
+void endCommandBuffer (const DeviceInterface& vk, const VkCommandBuffer commandBuffer)
+{
+ VK_CHECK(vk.endCommandBuffer(commandBuffer));
+}
+
+void submitCommandsAndWait (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkQueue queue,
+ const VkCommandBuffer commandBuffer)
+{
+ const VkFenceCreateInfo fenceInfo =
+ {
+ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkFenceCreateFlags)0, // VkFenceCreateFlags flags;
+ };
+ const Unique<VkFence> fence(createFence(vk, device, &fenceInfo));
+
+ const VkSubmitInfo submitInfo =
+ {
+ VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ 0u, // uint32_t waitSemaphoreCount;
+ DE_NULL, // const VkSemaphore* pWaitSemaphores;
+ DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
+ 1u, // uint32_t commandBufferCount;
+ &commandBuffer, // const VkCommandBuffer* pCommandBuffers;
+ 0u, // uint32_t signalSemaphoreCount;
+ DE_NULL, // const VkSemaphore* pSignalSemaphores;
+ };
+ VK_CHECK(vk.queueSubmit(queue, 1u, &submitInfo, *fence));
+ VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), DE_TRUE, ~0ull));
+}
+
+void beginRenderPass (const DeviceInterface& vk,
+ const VkCommandBuffer commandBuffer,
+ const VkRenderPass renderPass,
+ const VkFramebuffer framebuffer,
+ const VkRect2D& renderArea,
+ const tcu::Vec4& clearColor)
+{
+ const VkClearValue clearValue = makeClearValueColor(clearColor);
+
+ const VkRenderPassBeginInfo renderPassBeginInfo = {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ renderPass, // VkRenderPass renderPass;
+ framebuffer, // VkFramebuffer framebuffer;
+ renderArea, // VkRect2D renderArea;
+ 1u, // uint32_t clearValueCount;
+ &clearValue, // const VkClearValue* pClearValues;
+ };
+
+ vk.cmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+}
+
+void beginRenderPassWithRasterizationDisabled (const DeviceInterface& vk,
+ const VkCommandBuffer commandBuffer,
+ const VkRenderPass renderPass,
+ const VkFramebuffer framebuffer)
+{
+ const VkRect2D renderArea = {{ 0, 0 }, { 0, 0 }};
+
+ const VkRenderPassBeginInfo renderPassBeginInfo = {
+ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ renderPass, // VkRenderPass renderPass;
+ framebuffer, // VkFramebuffer framebuffer;
+ renderArea, // VkRect2D renderArea;
+ 0u, // uint32_t clearValueCount;
+ DE_NULL, // const VkClearValue* pClearValues;
+ };
+
+ vk.cmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+}
+
+void endRenderPass (const DeviceInterface& vk,
+ const VkCommandBuffer commandBuffer)
+{
+ vk.cmdEndRenderPass(commandBuffer);
+}
+
+Move<VkRenderPass> makeRenderPass (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkFormat colorFormat)
+{
+ const VkAttachmentDescription colorAttachmentDescription =
+ {
+ (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags;
+ colorFormat, // VkFormat format;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ VK_ATTACHMENT_LOAD_OP_CLEAR, // VkAttachmentLoadOp loadOp;
+ VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp;
+ VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp;
+ VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout;
+ };
+
+ const VkAttachmentReference colorAttachmentReference =
+ {
+ 0u, // deUint32 attachment;
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout;
+ };
+
+ const VkAttachmentReference depthAttachmentReference =
+ {
+ VK_ATTACHMENT_UNUSED, // deUint32 attachment;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout layout;
+ };
+
+ const VkSubpassDescription subpassDescription =
+ {
+ (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags;
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
+ 0u, // deUint32 inputAttachmentCount;
+ DE_NULL, // const VkAttachmentReference* pInputAttachments;
+ 1u, // deUint32 colorAttachmentCount;
+ &colorAttachmentReference, // const VkAttachmentReference* pColorAttachments;
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments;
+ &depthAttachmentReference, // const VkAttachmentReference* pDepthStencilAttachment;
+ 0u, // deUint32 preserveAttachmentCount;
+ DE_NULL // const deUint32* pPreserveAttachments;
+ };
+
+ const VkRenderPassCreateInfo renderPassInfo =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags;
+ 1u, // deUint32 attachmentCount;
+ &colorAttachmentDescription, // const VkAttachmentDescription* pAttachments;
+ 1u, // deUint32 subpassCount;
+ &subpassDescription, // const VkSubpassDescription* pSubpasses;
+ 0u, // deUint32 dependencyCount;
+ DE_NULL // const VkSubpassDependency* pDependencies;
+ };
+
+ return createRenderPass(vk, device, &renderPassInfo);
+}
+
+Move<VkRenderPass> makeRenderPassWithoutAttachments (const DeviceInterface& vk,
+ const VkDevice device)
+{
+ const VkAttachmentReference unusedAttachment =
+ {
+ VK_ATTACHMENT_UNUSED, // deUint32 attachment;
+ VK_IMAGE_LAYOUT_UNDEFINED // VkImageLayout layout;
+ };
+
+ const VkSubpassDescription subpassDescription =
+ {
+ (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags;
+ VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint;
+ 0u, // deUint32 inputAttachmentCount;
+ DE_NULL, // const VkAttachmentReference* pInputAttachments;
+ 0u, // deUint32 colorAttachmentCount;
+ DE_NULL, // const VkAttachmentReference* pColorAttachments;
+ DE_NULL, // const VkAttachmentReference* pResolveAttachments;
+ &unusedAttachment, // const VkAttachmentReference* pDepthStencilAttachment;
+ 0u, // deUint32 preserveAttachmentCount;
+ DE_NULL // const deUint32* pPreserveAttachments;
+ };
+
+ const VkRenderPassCreateInfo renderPassInfo =
+ {
+ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags;
+ 0u, // deUint32 attachmentCount;
+ DE_NULL, // const VkAttachmentDescription* pAttachments;
+ 1u, // deUint32 subpassCount;
+ &subpassDescription, // const VkSubpassDescription* pSubpasses;
+ 0u, // deUint32 dependencyCount;
+ DE_NULL // const VkSubpassDependency* pDependencies;
+ };
+
+ return createRenderPass(vk, device, &renderPassInfo);
+}
+
+Move<VkFramebuffer> makeFramebuffer (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkRenderPass renderPass,
+ const VkImageView colorAttachment,
+ const deUint32 width,
+ const deUint32 height,
+ const deUint32 layers)
+{
+ const VkFramebufferCreateInfo framebufferInfo = {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkFramebufferCreateFlags)0, // VkFramebufferCreateFlags flags;
+ renderPass, // VkRenderPass renderPass;
+ 1u, // uint32_t attachmentCount;
+ &colorAttachment, // const VkImageView* pAttachments;
+ width, // uint32_t width;
+ height, // uint32_t height;
+ layers, // uint32_t layers;
+ };
+
+ return createFramebuffer(vk, device, &framebufferInfo);
+}
+
+Move<VkFramebuffer> makeFramebufferWithoutAttachments (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkRenderPass renderPass)
+{
+ const VkFramebufferCreateInfo framebufferInfo = {
+ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkFramebufferCreateFlags)0, // VkFramebufferCreateFlags flags;
+ renderPass, // VkRenderPass renderPass;
+ 0u, // uint32_t attachmentCount;
+ DE_NULL, // const VkImageView* pAttachments;
+ 0u, // uint32_t width;
+ 0u, // uint32_t height;
+ 0u, // uint32_t layers;
+ };
+
+ return createFramebuffer(vk, device, &framebufferInfo);
+}
+
+GraphicsPipelineBuilder& GraphicsPipelineBuilder::setShader (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkShaderStageFlagBits stage,
+ const ProgramBinary& binary,
+ const VkSpecializationInfo* specInfo)
+{
+ VkShaderModule module;
+ switch (stage)
+ {
+ case (VK_SHADER_STAGE_VERTEX_BIT):
+ DE_ASSERT(m_vertexShaderModule.get() == DE_NULL);
+ m_vertexShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+ module = *m_vertexShaderModule;
+ break;
+
+ case (VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT):
+ DE_ASSERT(m_tessControlShaderModule.get() == DE_NULL);
+ m_tessControlShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+ module = *m_tessControlShaderModule;
+ break;
+
+ case (VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT):
+ DE_ASSERT(m_tessEvaluationShaderModule.get() == DE_NULL);
+ m_tessEvaluationShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+ module = *m_tessEvaluationShaderModule;
+ break;
+
+ case (VK_SHADER_STAGE_GEOMETRY_BIT):
+ DE_ASSERT(m_geometryShaderModule.get() == DE_NULL);
+ m_geometryShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+ module = *m_geometryShaderModule;
+ break;
+
+ case (VK_SHADER_STAGE_FRAGMENT_BIT):
+ DE_ASSERT(m_fragmentShaderModule.get() == DE_NULL);
+ m_fragmentShaderModule = createShaderModule(vk, device, binary, (VkShaderModuleCreateFlags)0);
+ module = *m_fragmentShaderModule;
+ break;
+
+ default:
+ DE_FATAL("Invalid shader stage");
+ return *this;
+ }
+
+ const VkPipelineShaderStageCreateInfo pipelineShaderStageInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineShaderStageCreateFlags)0, // VkPipelineShaderStageCreateFlags flags;
+ stage, // VkShaderStageFlagBits stage;
+ module, // VkShaderModule module;
+ "main", // const char* pName;
+ specInfo, // const VkSpecializationInfo* pSpecializationInfo;
+ };
+
+ m_shaderStageFlags |= stage;
+ m_shaderStages.push_back(pipelineShaderStageInfo);
+
+ return *this;
+}
+
+GraphicsPipelineBuilder& GraphicsPipelineBuilder::setVertexInputSingleAttribute (const VkFormat vertexFormat, const deUint32 stride)
+{
+ const VkVertexInputBindingDescription bindingDesc =
+ {
+ 0u, // uint32_t binding;
+ stride, // uint32_t stride;
+ VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
+ };
+ const VkVertexInputAttributeDescription attributeDesc =
+ {
+ 0u, // uint32_t location;
+ 0u, // uint32_t binding;
+ vertexFormat, // VkFormat format;
+ 0u, // uint32_t offset;
+ };
+
+ m_vertexInputBindings.clear();
+ m_vertexInputBindings.push_back(bindingDesc);
+
+ m_vertexInputAttributes.clear();
+ m_vertexInputAttributes.push_back(attributeDesc);
+
+ return *this;
+}
+
+template<typename T>
+inline const T* dataPointer (const std::vector<T>& vec)
+{
+ return (vec.size() != 0 ? &vec[0] : DE_NULL);
+}
+
+Move<VkPipeline> GraphicsPipelineBuilder::build (const DeviceInterface& vk,
+ const VkDevice device,
+ const VkPipelineLayout pipelineLayout,
+ const VkRenderPass renderPass)
+{
+ const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineVertexInputStateCreateFlags)0, // VkPipelineVertexInputStateCreateFlags flags;
+ static_cast<deUint32>(m_vertexInputBindings.size()), // uint32_t vertexBindingDescriptionCount;
+ dataPointer(m_vertexInputBindings), // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
+ static_cast<deUint32>(m_vertexInputAttributes.size()), // uint32_t vertexAttributeDescriptionCount;
+ dataPointer(m_vertexInputAttributes), // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
+ };
+
+ const VkPrimitiveTopology topology = (m_shaderStageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
+ : m_primitiveTopology;
+ const VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineInputAssemblyStateCreateFlags)0, // VkPipelineInputAssemblyStateCreateFlags flags;
+ topology, // VkPrimitiveTopology topology;
+ VK_FALSE, // VkBool32 primitiveRestartEnable;
+ };
+
+ const VkPipelineTessellationStateCreateInfo pipelineTessellationStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineTessellationStateCreateFlags)0, // VkPipelineTessellationStateCreateFlags flags;
+ m_patchControlPoints, // uint32_t patchControlPoints;
+ };
+
+ const VkViewport viewport = makeViewport(
+ 0.0f, 0.0f,
+ static_cast<float>(m_renderSize.x()), static_cast<float>(m_renderSize.y()),
+ 0.0f, 1.0f);
+
+ const VkRect2D scissor = {
+ makeOffset2D(0, 0),
+ makeExtent2D(m_renderSize.x(), m_renderSize.y()),
+ };
+
+ const VkPipelineViewportStateCreateInfo pipelineViewportStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineViewportStateCreateFlags)0, // VkPipelineViewportStateCreateFlags flags;
+ 1u, // uint32_t viewportCount;
+ &viewport, // const VkViewport* pViewports;
+ 1u, // uint32_t scissorCount;
+ &scissor, // const VkRect2D* pScissors;
+ };
+
+ const bool isRasterizationDisabled = ((m_shaderStageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) == 0);
+ const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineRasterizationStateCreateFlags)0, // VkPipelineRasterizationStateCreateFlags flags;
+ VK_FALSE, // VkBool32 depthClampEnable;
+ isRasterizationDisabled, // VkBool32 rasterizerDiscardEnable;
+ VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
+ m_cullModeFlags, // VkCullModeFlags cullMode;
+ m_frontFace, // VkFrontFace frontFace;
+ VK_FALSE, // VkBool32 depthBiasEnable;
+ 0.0f, // float depthBiasConstantFactor;
+ 0.0f, // float depthBiasClamp;
+ 0.0f, // float depthBiasSlopeFactor;
+ 1.0f, // float lineWidth;
+ };
+
+ const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineMultisampleStateCreateFlags)0, // VkPipelineMultisampleStateCreateFlags flags;
+ VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
+ VK_FALSE, // VkBool32 sampleShadingEnable;
+ 0.0f, // float minSampleShading;
+ DE_NULL, // const VkSampleMask* pSampleMask;
+ VK_FALSE, // VkBool32 alphaToCoverageEnable;
+ VK_FALSE // VkBool32 alphaToOneEnable;
+ };
+
+ const VkStencilOpState stencilOpState = makeStencilOpState(
+ VK_STENCIL_OP_KEEP, // stencil fail
+ VK_STENCIL_OP_KEEP, // depth & stencil pass
+ VK_STENCIL_OP_KEEP, // depth only fail
+ VK_COMPARE_OP_NEVER, // compare op
+ 0u, // compare mask
+ 0u, // write mask
+ 0u); // reference
+
+ const VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineDepthStencilStateCreateFlags)0, // VkPipelineDepthStencilStateCreateFlags flags;
+ VK_FALSE, // VkBool32 depthTestEnable;
+ VK_FALSE, // VkBool32 depthWriteEnable;
+ VK_COMPARE_OP_LESS, // VkCompareOp depthCompareOp;
+ VK_FALSE, // VkBool32 depthBoundsTestEnable;
+ VK_FALSE, // VkBool32 stencilTestEnable;
+ stencilOpState, // VkStencilOpState front;
+ stencilOpState, // VkStencilOpState back;
+ 0.0f, // float minDepthBounds;
+ 1.0f, // float maxDepthBounds;
+ };
+
+ const VkColorComponentFlags colorComponentsAll = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ const VkPipelineColorBlendAttachmentState pipelineColorBlendAttachmentState =
+ {
+ m_blendEnable, // VkBool32 blendEnable;
+ VK_BLEND_FACTOR_SRC_ALPHA, // VkBlendFactor srcColorBlendFactor;
+ VK_BLEND_FACTOR_ONE, // VkBlendFactor dstColorBlendFactor;
+ VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp;
+ VK_BLEND_FACTOR_SRC_ALPHA, // VkBlendFactor srcAlphaBlendFactor;
+ VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
+ VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp;
+ colorComponentsAll, // VkColorComponentFlags colorWriteMask;
+ };
+
+ const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo =
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineColorBlendStateCreateFlags)0, // VkPipelineColorBlendStateCreateFlags flags;
+ VK_FALSE, // VkBool32 logicOpEnable;
+ VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
+ 1u, // deUint32 attachmentCount;
+ &pipelineColorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
+ { 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConstants[4];
+ };
+
+ const VkGraphicsPipelineCreateInfo graphicsPipelineInfo =
+ {
+ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
+ DE_NULL, // const void* pNext;
+ (VkPipelineCreateFlags)0, // VkPipelineCreateFlags flags;
+ static_cast<deUint32>(m_shaderStages.size()), // deUint32 stageCount;
+ &m_shaderStages[0], // const VkPipelineShaderStageCreateInfo* pStages;
+ &vertexInputStateInfo, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
+ &pipelineInputAssemblyStateInfo, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
+ (m_shaderStageFlags & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ? &pipelineTessellationStateInfo : DE_NULL), // const VkPipelineTessellationStateCreateInfo* pTessellationState;
+ (isRasterizationDisabled ? DE_NULL : &pipelineViewportStateInfo), // const VkPipelineViewportStateCreateInfo* pViewportState;
+ &pipelineRasterizationStateInfo, // const VkPipelineRasterizationStateCreateInfo* pRasterizationState;
+ (isRasterizationDisabled ? DE_NULL : &pipelineMultisampleStateInfo), // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
+ (isRasterizationDisabled ? DE_NULL : &pipelineDepthStencilStateInfo), // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
+ (isRasterizationDisabled ? DE_NULL : &pipelineColorBlendStateInfo), // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
+ DE_NULL, // const VkPipelineDynamicStateCreateInfo* pDynamicState;
+ pipelineLayout, // VkPipelineLayout layout;
+ renderPass, // VkRenderPass renderPass;
+ 0u, // deUint32 subpass;
+ DE_NULL, // VkPipeline basePipelineHandle;
+ 0, // deInt32 basePipelineIndex;
+ };
+
+ return createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineInfo);
+}
+
+float getClampedTessLevel (const SpacingMode mode, const float tessLevel)
+{
+ switch (mode)
+ {
+ case SPACINGMODE_EQUAL: return de::max(1.0f, tessLevel);
+ case SPACINGMODE_FRACTIONAL_ODD: return de::max(1.0f, tessLevel);
+ case SPACINGMODE_FRACTIONAL_EVEN: return de::max(2.0f, tessLevel);
+ default:
+ DE_ASSERT(false);
+ return 0.0f;
+ }
+}
+
+int getRoundedTessLevel (const SpacingMode mode, const float clampedTessLevel)
+{
+ static const int MINIMUM_MAX_TESS_GEN_LEVEL = 64; //!< Minimum maxTessellationGenerationLevel defined by the spec.
+
+ int result = (int)deFloatCeil(clampedTessLevel);
+
+ switch (mode)
+ {
+ case SPACINGMODE_EQUAL: break;
+ case SPACINGMODE_FRACTIONAL_ODD: result += 1 - result % 2; break;
+ case SPACINGMODE_FRACTIONAL_EVEN: result += result % 2; break;
+ default:
+ DE_ASSERT(false);
+ }
+ DE_ASSERT(de::inRange<int>(result, 1, MINIMUM_MAX_TESS_GEN_LEVEL));
+
+ return result;
+}
+
+int getClampedRoundedTessLevel (const SpacingMode mode, const float tessLevel)
+{
+ return getRoundedTessLevel(mode, getClampedTessLevel(mode, tessLevel));
+}
+
+void getClampedRoundedTriangleTessLevels (const SpacingMode spacingMode,
+ const float* innerSrc,
+ const float* outerSrc,
+ int* innerDst,
+ int* outerDst)
+{
+ innerDst[0] = getClampedRoundedTessLevel(spacingMode, innerSrc[0]);
+ for (int i = 0; i < 3; i++)
+ outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
+}
+
+void getClampedRoundedQuadTessLevels (const SpacingMode spacingMode,
+ const float* innerSrc,
+ const float* outerSrc,
+ int* innerDst,
+ int* outerDst)
+{
+ for (int i = 0; i < 2; i++)
+ innerDst[i] = getClampedRoundedTessLevel(spacingMode, innerSrc[i]);
+ for (int i = 0; i < 4; i++)
+ outerDst[i] = getClampedRoundedTessLevel(spacingMode, outerSrc[i]);
+}
+
+void getClampedRoundedIsolineTessLevels (const SpacingMode spacingMode,
+ const float* outerSrc,
+ int* outerDst)
+{
+ outerDst[0] = getClampedRoundedTessLevel(SPACINGMODE_EQUAL, outerSrc[0]);
+ outerDst[1] = getClampedRoundedTessLevel(spacingMode, outerSrc[1]);
+}
+
+int numOuterTessellationLevels (const TessPrimitiveType primType)
+{
+ switch (primType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES: return 3;
+ case TESSPRIMITIVETYPE_QUADS: return 4;
+ case TESSPRIMITIVETYPE_ISOLINES: return 2;
+ default:
+ DE_ASSERT(false);
+ return 0;
+ }
+}
+
+bool isPatchDiscarded (const TessPrimitiveType primitiveType, const float* outerLevels)
+{
+ const int numOuterLevels = numOuterTessellationLevels(primitiveType);
+ for (int i = 0; i < numOuterLevels; i++)
+ if (outerLevels[i] <= 0.0f)
+ return true;
+ return false;
+}
+
+std::string getTessellationLevelsString (const TessLevels& tessLevels, const TessPrimitiveType primitiveType)
+{
+ std::ostringstream str;
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_ISOLINES:
+ str << "inner: { }, "
+ << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << " }";
+ break;
+
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ str << "inner: { " << tessLevels.inner[0] << " }, "
+ << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << ", " << tessLevels.outer[2] << " }";
+ break;
+
+ case TESSPRIMITIVETYPE_QUADS:
+ str << "inner: { " << tessLevels.inner[0] << ", " << tessLevels.inner[1] << " }, "
+ << "outer: { " << tessLevels.outer[0] << ", " << tessLevels.outer[1] << ", " << tessLevels.outer[2] << ", " << tessLevels.outer[3] << " }";
+ break;
+
+ default:
+ DE_ASSERT(false);
+ }
+
+ return str.str();
+}
+
+//! Assumes array sizes inner[2] and outer[4].
+std::string getTessellationLevelsString (const float* inner, const float* outer)
+{
+ const TessLevels tessLevels =
+ {
+ { inner[0], inner[1] },
+ { outer[0], outer[1], outer[2], outer[3] }
+ };
+ return getTessellationLevelsString(tessLevels, TESSPRIMITIVETYPE_QUADS);
+}
+
+// \note The tessellation coordinates generated by this function could break some of the rules given in the spec
+// (e.g. it may not exactly hold that u+v+w == 1.0f, or [uvw] + (1.0f-[uvw]) == 1.0f).
+std::vector<tcu::Vec3> generateReferenceTriangleTessCoords (const SpacingMode spacingMode,
+ const int inner,
+ const int outer0,
+ const int outer1,
+ const int outer2)
+{
+ std::vector<tcu::Vec3> tessCoords;
+
+ if (inner == 1)
+ {
+ if (outer0 == 1 && outer1 == 1 && outer2 == 1)
+ {
+ tessCoords.push_back(tcu::Vec3(1.0f, 0.0f, 0.0f));
+ tessCoords.push_back(tcu::Vec3(0.0f, 1.0f, 0.0f));
+ tessCoords.push_back(tcu::Vec3(0.0f, 0.0f, 1.0f));
+ return tessCoords;
+ }
+ else
+ return generateReferenceTriangleTessCoords(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+ outer0, outer1, outer2);
+ }
+ else
+ {
+ for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(tcu::Vec3( 0.0f, v, 1.0f - v)); }
+ for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(tcu::Vec3(1.0f - v, 0.0f, v)); }
+ for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(tcu::Vec3( v, 1.0f - v, 0.0f)); }
+
+ const int numInnerTriangles = inner/2;
+ for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
+ {
+ const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
+
+ if (curInnerTriangleLevel == 0)
+ tessCoords.push_back(tcu::Vec3(1.0f/3.0f));
+ else
+ {
+ const float minUVW = (float)(2 * (innerTriangleNdx + 1)) / (float)(3 * inner);
+ const float maxUVW = 1.0f - 2.0f*minUVW;
+ const tcu::Vec3 corners[3] =
+ {
+ tcu::Vec3(maxUVW, minUVW, minUVW),
+ tcu::Vec3(minUVW, maxUVW, minUVW),
+ tcu::Vec3(minUVW, minUVW, maxUVW)
+ };
+
+ for (int i = 0; i < curInnerTriangleLevel; i++)
+ {
+ const float f = (float)i / (float)curInnerTriangleLevel;
+ for (int j = 0; j < 3; j++)
+ tessCoords.push_back((1.0f - f)*corners[j] + f*corners[(j+1)%3]);
+ }
+ }
+ }
+
+ return tessCoords;
+ }
+}
+
+// \note The tessellation coordinates generated by this function could break some of the rules given in the spec
+// (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
+std::vector<tcu::Vec3> generateReferenceQuadTessCoords (const SpacingMode spacingMode,
+ const int inner0,
+ const int inner1,
+ const int outer0,
+ const int outer1,
+ const int outer2,
+ const int outer3)
+{
+ std::vector<tcu::Vec3> tessCoords;
+
+ if (inner0 == 1 || inner1 == 1)
+ {
+ if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
+ {
+ tessCoords.push_back(tcu::Vec3(0.0f, 0.0f, 0.0f));
+ tessCoords.push_back(tcu::Vec3(1.0f, 0.0f, 0.0f));
+ tessCoords.push_back(tcu::Vec3(0.0f, 1.0f, 0.0f));
+ tessCoords.push_back(tcu::Vec3(1.0f, 1.0f, 0.0f));
+ return tessCoords;
+ }
+ else
+ return generateReferenceQuadTessCoords(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+ inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+ outer0, outer1, outer2, outer3);
+ }
+ else
+ {
+ for (int i = 0; i < outer0; i++) { const float v = (float)i / (float)outer0; tessCoords.push_back(tcu::Vec3( 0.0f, v, 0.0f)); }
+ for (int i = 0; i < outer1; i++) { const float v = (float)i / (float)outer1; tessCoords.push_back(tcu::Vec3(1.0f - v, 0.0f, 0.0f)); }
+ for (int i = 0; i < outer2; i++) { const float v = (float)i / (float)outer2; tessCoords.push_back(tcu::Vec3( 1.0f, 1.0f - v, 0.0f)); }
+ for (int i = 0; i < outer3; i++) { const float v = (float)i / (float)outer3; tessCoords.push_back(tcu::Vec3( v, 1.0f, 0.0f)); }
+
+ for (int innerVtxY = 0; innerVtxY < inner1-1; innerVtxY++)
+ for (int innerVtxX = 0; innerVtxX < inner0-1; innerVtxX++)
+ tessCoords.push_back(tcu::Vec3((float)(innerVtxX + 1) / (float)inner0,
+ (float)(innerVtxY + 1) / (float)inner1,
+ 0.0f));
+
+ return tessCoords;
+ }
+}
+
+// \note The tessellation coordinates generated by this function could break some of the rules given in the spec
+// (e.g. it may not exactly hold that [uv] + (1.0f-[uv]) == 1.0f).
+std::vector<tcu::Vec3> generateReferenceIsolineTessCoords (const int outer0, const int outer1)
+{
+ std::vector<tcu::Vec3> tessCoords;
+
+ for (int y = 0; y < outer0; y++)
+ for (int x = 0; x < outer1+1; x++)
+ tessCoords.push_back(tcu::Vec3((float)x / (float)outer1,
+ (float)y / (float)outer0,
+ 0.0f));
+
+ return tessCoords;
+}
+
+static int referencePointModePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
+{
+ if (isPatchDiscarded(primitiveType, outerLevels))
+ return 0;
+
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ {
+ int inner;
+ int outer[3];
+ getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
+ return static_cast<int>(generateReferenceTriangleTessCoords(spacingMode, inner, outer[0], outer[1], outer[2]).size());
+ }
+
+ case TESSPRIMITIVETYPE_QUADS:
+ {
+ int inner[2];
+ int outer[4];
+ getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
+ return static_cast<int>(generateReferenceQuadTessCoords(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]).size());
+ }
+
+ case TESSPRIMITIVETYPE_ISOLINES:
+ {
+ int outer[2];
+ getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
+ return static_cast<int>(generateReferenceIsolineTessCoords(outer[0], outer[1]).size());
+ }
+
+ default:
+ DE_ASSERT(false);
+ return 0;
+ }
+}
+
+static int referenceTriangleNonPointModePrimitiveCount (const SpacingMode spacingMode, const int inner, const int outer0, const int outer1, const int outer2)
+{
+ if (inner == 1)
+ {
+ if (outer0 == 1 && outer1 == 1 && outer2 == 1)
+ return 1;
+ else
+ return referenceTriangleNonPointModePrimitiveCount(spacingMode, spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+ outer0, outer1, outer2);
+ }
+ else
+ {
+ int result = outer0 + outer1 + outer2;
+
+ const int numInnerTriangles = inner/2;
+ for (int innerTriangleNdx = 0; innerTriangleNdx < numInnerTriangles; innerTriangleNdx++)
+ {
+ const int curInnerTriangleLevel = inner - 2*(innerTriangleNdx+1);
+
+ if (curInnerTriangleLevel == 1)
+ result += 4;
+ else
+ result += 2*3*curInnerTriangleLevel;
+ }
+
+ return result;
+ }
+}
+
+static int referenceQuadNonPointModePrimitiveCount (const SpacingMode spacingMode, const int inner0, const int inner1, const int outer0, const int outer1, const int outer2, const int outer3)
+{
+ if (inner0 == 1 || inner1 == 1)
+ {
+ if (inner0 == 1 && inner1 == 1 && outer0 == 1 && outer1 == 1 && outer2 == 1 && outer3 == 1)
+ return 2;
+ else
+ return referenceQuadNonPointModePrimitiveCount(spacingMode, inner0 > 1 ? inner0 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+ inner1 > 1 ? inner1 : spacingMode == SPACINGMODE_FRACTIONAL_ODD ? 3 : 2,
+ outer0, outer1, outer2, outer3);
+ }
+ else
+ return 2*(inner0-2)*(inner1-2) + 2*(inner0-2) + 2*(inner1-2) + outer0+outer1+outer2+outer3;
+}
+
+static inline int referenceIsolineNonPointModePrimitiveCount (const int outer0, const int outer1)
+{
+ return outer0*outer1;
+}
+
+static int referenceNonPointModePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const float* innerLevels, const float* outerLevels)
+{
+ if (isPatchDiscarded(primitiveType, outerLevels))
+ return 0;
+
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ {
+ int inner;
+ int outer[3];
+ getClampedRoundedTriangleTessLevels(spacingMode, innerLevels, outerLevels, &inner, &outer[0]);
+ return referenceTriangleNonPointModePrimitiveCount(spacingMode, inner, outer[0], outer[1], outer[2]);
+ }
+
+ case TESSPRIMITIVETYPE_QUADS:
+ {
+ int inner[2];
+ int outer[4];
+ getClampedRoundedQuadTessLevels(spacingMode, innerLevels, outerLevels, &inner[0], &outer[0]);
+ return referenceQuadNonPointModePrimitiveCount(spacingMode, inner[0], inner[1], outer[0], outer[1], outer[2], outer[3]);
+ }
+
+ case TESSPRIMITIVETYPE_ISOLINES:
+ {
+ int outer[2];
+ getClampedRoundedIsolineTessLevels(spacingMode, &outerLevels[0], &outer[0]);
+ return referenceIsolineNonPointModePrimitiveCount(outer[0], outer[1]);
+ }
+
+ default:
+ DE_ASSERT(false);
+ return 0;
+ }
+}
+
+int numVerticesPerPrimitive (const TessPrimitiveType primitiveType, const bool usePointMode)
+{
+ if (usePointMode)
+ return 1;
+
+ switch (primitiveType)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES: return 3;
+ case TESSPRIMITIVETYPE_QUADS: return 3; // quads are composed of two triangles
+ case TESSPRIMITIVETYPE_ISOLINES: return 2;
+ default:
+ DE_ASSERT(false);
+ return 0;
+ }
+}
+
+int referencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels)
+{
+ return usePointMode ? referencePointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels)
+ : referenceNonPointModePrimitiveCount (primitiveType, spacingMode, innerLevels, outerLevels);
+}
+
+//! In point mode this should return the number of unique vertices, while in non-point mode the maximum theoretical number of verticies.
+//! Actual implementation will likely return a much smaller number because the shader isn't required to be run for duplicate coordinates.
+int referenceVertexCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels)
+{
+ return referencePrimitiveCount(primitiveType, spacingMode, usePointMode, innerLevels, outerLevels)
+ * numVerticesPerPrimitive(primitiveType, usePointMode);
+}
+
+void requireFeatures (const InstanceInterface& vki, const VkPhysicalDevice physDevice, const FeatureFlags flags)
+{
+ const VkPhysicalDeviceFeatures features = getPhysicalDeviceFeatures(vki, physDevice);
+
+ if (((flags & FEATURE_TESSELLATION_SHADER) != 0) && !features.tessellationShader)
+ throw tcu::NotSupportedError("Tessellation shader not supported");
+
+ if (((flags & FEATURE_GEOMETRY_SHADER) != 0) && !features.geometryShader)
+ throw tcu::NotSupportedError("Geometry shader not supported");
+
+ if (((flags & FEATURE_SHADER_FLOAT_64) != 0) && !features.shaderFloat64)
+ throw tcu::NotSupportedError("Double-precision floats not supported");
+
+ if (((flags & FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS) != 0) && !features.vertexPipelineStoresAndAtomics)
+ throw tcu::NotSupportedError("SSBO and image writes not supported in vertex pipeline");
+
+ if (((flags & FEATURE_FRAGMENT_STORES_AND_ATOMICS) != 0) && !features.fragmentStoresAndAtomics)
+ throw tcu::NotSupportedError("SSBO and image writes not supported in fragment shader");
+
+ if (((flags & FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE) != 0) && !features.shaderTessellationAndGeometryPointSize)
+ throw tcu::NotSupportedError("Tessellation and geometry shaders don't support PointSize built-in");
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONUTIL_HPP
+#define _VKTTESSELLATIONUTIL_HPP
+/*------------------------------------------------------------------------
+ * 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 Utilities
+ *//*--------------------------------------------------------------------*/
+
+#include "vkDefs.hpp"
+#include "vkMemUtil.hpp"
+#include "vkRef.hpp"
+#include "vkPrograms.hpp"
+#include "vkRefUtil.hpp"
+#include "vkQueryUtil.hpp"
+
+#include "tcuVector.hpp"
+
+#include "deStringUtil.hpp"
+
+#include <algorithm> // sort
+#include <iterator> // distance
+
+namespace vkt
+{
+namespace tessellation
+{
+
+class Buffer
+{
+public:
+ Buffer (const vk::DeviceInterface& vk,
+ const vk::VkDevice device,
+ vk::Allocator& allocator,
+ const vk::VkBufferCreateInfo& bufferCreateInfo,
+ const vk::MemoryRequirement memoryRequirement)
+
+ : m_buffer (createBuffer(vk, device, &bufferCreateInfo))
+ , m_allocation (allocator.allocate(getBufferMemoryRequirements(vk, device, *m_buffer), memoryRequirement))
+ {
+ VK_CHECK(vk.bindBufferMemory(device, *m_buffer, m_allocation->getMemory(), m_allocation->getOffset()));
+ }
+
+ const vk::VkBuffer& get (void) const { return *m_buffer; }
+ const vk::VkBuffer& operator* (void) const { return get(); }
+ vk::Allocation& getAllocation (void) const { return *m_allocation; }
+
+private:
+ const vk::Unique<vk::VkBuffer> m_buffer;
+ const de::UniquePtr<vk::Allocation> m_allocation;
+
+ // "deleted"
+ Buffer (const Buffer&);
+ Buffer& operator= (const Buffer&);
+};
+
+class Image
+{
+public:
+ Image (const vk::DeviceInterface& vk,
+ const vk::VkDevice device,
+ vk::Allocator& allocator,
+ const vk::VkImageCreateInfo& imageCreateInfo,
+ const vk::MemoryRequirement memoryRequirement)
+
+ : m_image (createImage(vk, device, &imageCreateInfo))
+ , m_allocation (allocator.allocate(getImageMemoryRequirements(vk, device, *m_image), memoryRequirement))
+ {
+ VK_CHECK(vk.bindImageMemory(device, *m_image, m_allocation->getMemory(), m_allocation->getOffset()));
+ }
+
+ const vk::VkImage& get (void) const { return *m_image; }
+ const vk::VkImage& operator* (void) const { return get(); }
+ vk::Allocation& getAllocation (void) const { return *m_allocation; }
+
+private:
+ const vk::Unique<vk::VkImage> m_image;
+ const de::UniquePtr<vk::Allocation> m_allocation;
+
+ // "deleted"
+ Image (const Image&);
+ Image& operator= (const Image&);
+};
+
+class GraphicsPipelineBuilder
+{
+public:
+ GraphicsPipelineBuilder (void) : m_renderSize (0, 0)
+ , m_shaderStageFlags (0u)
+ , m_cullModeFlags (vk::VK_CULL_MODE_NONE)
+ , m_frontFace (vk::VK_FRONT_FACE_COUNTER_CLOCKWISE)
+ , m_patchControlPoints (1u)
+ , m_blendEnable (false)
+ , m_primitiveTopology (vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) {}
+
+ GraphicsPipelineBuilder& setRenderSize (const tcu::IVec2& size) { m_renderSize = size; return *this; }
+ GraphicsPipelineBuilder& setShader (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkShaderStageFlagBits stage, const vk::ProgramBinary& binary, const vk::VkSpecializationInfo* specInfo);
+ GraphicsPipelineBuilder& setPatchControlPoints (const deUint32 controlPoints) { m_patchControlPoints = controlPoints; return *this; }
+ GraphicsPipelineBuilder& setCullModeFlags (const vk::VkCullModeFlags cullModeFlags) { m_cullModeFlags = cullModeFlags; return *this; }
+ GraphicsPipelineBuilder& setFrontFace (const vk::VkFrontFace frontFace) { m_frontFace = frontFace; return *this; }
+ GraphicsPipelineBuilder& setBlend (const bool enable) { m_blendEnable = enable; return *this; }
+
+ //! Applies only to pipelines without tessellation shaders.
+ GraphicsPipelineBuilder& setPrimitiveTopology (const vk::VkPrimitiveTopology topology) { m_primitiveTopology = topology; return *this; }
+
+ GraphicsPipelineBuilder& addVertexBinding (const vk::VkVertexInputBindingDescription vertexBinding) { m_vertexInputBindings.push_back(vertexBinding); return *this; }
+ GraphicsPipelineBuilder& addVertexAttribute (const vk::VkVertexInputAttributeDescription vertexAttribute) { m_vertexInputAttributes.push_back(vertexAttribute); return *this; }
+
+ //! Basic vertex input configuration (uses biding 0, location 0, etc.)
+ GraphicsPipelineBuilder& setVertexInputSingleAttribute (const vk::VkFormat vertexFormat, const deUint32 stride);
+
+ vk::Move<vk::VkPipeline> build (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkRenderPass renderPass);
+
+private:
+ tcu::IVec2 m_renderSize;
+ vk::Move<vk::VkShaderModule> m_vertexShaderModule;
+ vk::Move<vk::VkShaderModule> m_fragmentShaderModule;
+ vk::Move<vk::VkShaderModule> m_geometryShaderModule;
+ vk::Move<vk::VkShaderModule> m_tessControlShaderModule;
+ vk::Move<vk::VkShaderModule> m_tessEvaluationShaderModule;
+ std::vector<vk::VkPipelineShaderStageCreateInfo> m_shaderStages;
+ std::vector<vk::VkVertexInputBindingDescription> m_vertexInputBindings;
+ std::vector<vk::VkVertexInputAttributeDescription> m_vertexInputAttributes;
+ vk::VkShaderStageFlags m_shaderStageFlags;
+ vk::VkCullModeFlags m_cullModeFlags;
+ vk::VkFrontFace m_frontFace;
+ deUint32 m_patchControlPoints;
+ bool m_blendEnable;
+ vk::VkPrimitiveTopology m_primitiveTopology;
+
+ GraphicsPipelineBuilder (const GraphicsPipelineBuilder&); // "deleted"
+ GraphicsPipelineBuilder& operator= (const GraphicsPipelineBuilder&);
+};
+
+struct TessLevels
+{
+ float inner[2];
+ float outer[4];
+};
+
+enum TessPrimitiveType
+{
+ TESSPRIMITIVETYPE_TRIANGLES = 0,
+ TESSPRIMITIVETYPE_QUADS,
+ TESSPRIMITIVETYPE_ISOLINES,
+
+ TESSPRIMITIVETYPE_LAST,
+};
+
+enum SpacingMode
+{
+ SPACINGMODE_EQUAL = 0,
+ SPACINGMODE_FRACTIONAL_ODD,
+ SPACINGMODE_FRACTIONAL_EVEN,
+
+ SPACINGMODE_LAST,
+};
+
+enum Winding
+{
+ WINDING_CCW = 0,
+ WINDING_CW,
+
+ WINDING_LAST,
+};
+
+enum FeatureFlagBits
+{
+ FEATURE_TESSELLATION_SHADER = 1u << 0,
+ FEATURE_GEOMETRY_SHADER = 1u << 1,
+ FEATURE_SHADER_FLOAT_64 = 1u << 2,
+ FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS = 1u << 3,
+ FEATURE_FRAGMENT_STORES_AND_ATOMICS = 1u << 4,
+ FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE = 1u << 5,
+};
+typedef deUint32 FeatureFlags;
+
+vk::VkBufferCreateInfo makeBufferCreateInfo (const vk::VkDeviceSize bufferSize, const vk::VkBufferUsageFlags usage);
+vk::VkImageCreateInfo makeImageCreateInfo (const tcu::IVec2& size, const vk::VkFormat format, const vk::VkImageUsageFlags usage, const deUint32 numArrayLayers);
+vk::Move<vk::VkCommandPool> makeCommandPool (const vk::DeviceInterface& vk, const vk::VkDevice device, const deUint32 queueFamilyIndex);
+vk::Move<vk::VkCommandBuffer> makeCommandBuffer (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkCommandPool commandPool);
+vk::Move<vk::VkDescriptorSet> makeDescriptorSet (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkDescriptorPool descriptorPool, const vk::VkDescriptorSetLayout setLayout);
+vk::Move<vk::VkPipelineLayout> makePipelineLayout (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkDescriptorSetLayout descriptorSetLayout);
+vk::Move<vk::VkPipelineLayout> makePipelineLayoutWithoutDescriptors (const vk::DeviceInterface& vk, const vk::VkDevice device);
+vk::Move<vk::VkPipeline> makeComputePipeline (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkPipelineLayout pipelineLayout, const vk::VkShaderModule shaderModule, const vk::VkSpecializationInfo* specInfo);
+vk::Move<vk::VkRenderPass> makeRenderPass (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkFormat colorFormat);
+vk::Move<vk::VkRenderPass> makeRenderPassWithoutAttachments (const vk::DeviceInterface& vk, const vk::VkDevice device);
+vk::Move<vk::VkFramebuffer> makeFramebuffer (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkRenderPass renderPass, const vk::VkImageView colorAttachment, const deUint32 width, const deUint32 height, const deUint32 layers);
+vk::Move<vk::VkFramebuffer> makeFramebufferWithoutAttachments (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkRenderPass renderPass);
+vk::Move<vk::VkImageView> makeImageView (const vk::DeviceInterface& vk, const vk::VkDevice vkDevice, const vk::VkImage image, const vk::VkImageViewType viewType, const vk::VkFormat format, const vk::VkImageSubresourceRange subresourceRange);
+vk::VkBufferImageCopy makeBufferImageCopy (const vk::VkExtent3D extent, const vk::VkImageSubresourceLayers subresourceLayers);
+vk::VkBufferMemoryBarrier makeBufferMemoryBarrier (const vk::VkAccessFlags srcAccessMask, const vk::VkAccessFlags dstAccessMask, const vk::VkBuffer buffer, const vk::VkDeviceSize offset, const vk::VkDeviceSize bufferSizeBytes);
+vk::VkImageMemoryBarrier makeImageMemoryBarrier (const vk::VkAccessFlags srcAccessMask, const vk::VkAccessFlags dstAccessMask, const vk::VkImageLayout oldLayout, const vk::VkImageLayout newLayout, const vk::VkImage image, const vk::VkImageSubresourceRange subresourceRange);
+
+void beginCommandBuffer (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer);
+void endCommandBuffer (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer);
+void submitCommandsAndWait (const vk::DeviceInterface& vk, const vk::VkDevice device, const vk::VkQueue queue, const vk::VkCommandBuffer commandBuffer);
+void beginRenderPass (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer, const vk::VkRenderPass renderPass, const vk::VkFramebuffer framebuffer, const vk::VkRect2D& renderArea, const tcu::Vec4& clearColor);
+void beginRenderPassWithRasterizationDisabled (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer, const vk::VkRenderPass renderPass, const vk::VkFramebuffer framebuffer);
+void endRenderPass (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer);
+void requireFeatures (const vk::InstanceInterface& vki, const vk::VkPhysicalDevice physDevice, const FeatureFlags flags);
+
+float getClampedTessLevel (const SpacingMode mode, const float tessLevel);
+int getRoundedTessLevel (const SpacingMode mode, const float clampedTessLevel);
+int getClampedRoundedTessLevel (const SpacingMode mode, const float tessLevel);
+void getClampedRoundedTriangleTessLevels (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst);
+void getClampedRoundedQuadTessLevels (const SpacingMode mode, const float* innerSrc, const float* outerSrc, int* innerDst, int* outerDst);
+void getClampedRoundedIsolineTessLevels (const SpacingMode mode, const float* outerSrc, int* outerDst);
+int numOuterTessellationLevels (const TessPrimitiveType primitiveType);
+std::string getTessellationLevelsString (const TessLevels& tessLevels, const TessPrimitiveType primitiveType);
+std::string getTessellationLevelsString (const float* inner, const float* outer);
+bool isPatchDiscarded (const TessPrimitiveType primitiveType, const float* outerLevels);
+std::vector<tcu::Vec3> generateReferenceTriangleTessCoords (const SpacingMode spacingMode, const int inner, const int outer0, const int outer1, const int outer2);
+std::vector<tcu::Vec3> generateReferenceQuadTessCoords (const SpacingMode spacingMode, const int inner0, const int inner1, const int outer0, const int outer1, const int outer2, const int outer3);
+std::vector<tcu::Vec3> generateReferenceIsolineTessCoords (const int outer0, const int outer1);
+int referenceVertexCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels);
+int referencePrimitiveCount (const TessPrimitiveType primitiveType, const SpacingMode spacingMode, const bool usePointMode, const float* innerLevels, const float* outerLevels);
+int numVerticesPerPrimitive (const TessPrimitiveType primitiveType, const bool usePointMode);
+
+static inline const char* getTessPrimitiveTypeShaderName (const TessPrimitiveType type)
+{
+ switch (type)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES: return "triangles";
+ case TESSPRIMITIVETYPE_QUADS: return "quads";
+ case TESSPRIMITIVETYPE_ISOLINES: return "isolines";
+ default:
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+static inline const char* getSpacingModeShaderName (SpacingMode mode)
+{
+ switch (mode)
+ {
+ case SPACINGMODE_EQUAL: return "equal_spacing";
+ case SPACINGMODE_FRACTIONAL_ODD: return "fractional_odd_spacing";
+ case SPACINGMODE_FRACTIONAL_EVEN: return "fractional_even_spacing";
+ default:
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+static inline const char* getWindingShaderName (const Winding winding)
+{
+ switch (winding)
+ {
+ case WINDING_CCW: return "ccw";
+ case WINDING_CW: return "cw";
+ default:
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+static inline const char* getGeometryShaderInputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode)
+{
+ if (usePointMode)
+ return "points";
+
+ switch (type)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ case TESSPRIMITIVETYPE_QUADS:
+ return "triangles";
+
+ case TESSPRIMITIVETYPE_ISOLINES:
+ return "lines";
+
+ default:
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+static inline const char* getGeometryShaderOutputPrimitiveTypeShaderName (const TessPrimitiveType type, const bool usePointMode)
+{
+ if (usePointMode)
+ return "points";
+
+ switch (type)
+ {
+ case TESSPRIMITIVETYPE_TRIANGLES:
+ case TESSPRIMITIVETYPE_QUADS:
+ return "triangle_strip";
+
+ case TESSPRIMITIVETYPE_ISOLINES:
+ return "line_strip";
+
+ default:
+ DE_ASSERT(false);
+ return DE_NULL;
+ }
+}
+
+template<typename T>
+inline std::size_t sizeInBytes (const std::vector<T>& vec)
+{
+ return vec.size() * sizeof(vec[0]);
+}
+
+template <typename T>
+static std::vector<T> sorted (const std::vector<T>& unsorted)
+{
+ std::vector<T> result = unsorted;
+ std::sort(result.begin(), result.end());
+ return result;
+}
+
+template <typename T, typename P>
+static std::vector<T> sorted (const std::vector<T>& unsorted, P pred)
+{
+ std::vector<T> result = unsorted;
+ std::sort(result.begin(), result.end(), pred);
+ return result;
+}
+
+template <typename IterT>
+std::string elemsStr (const IterT& begin, const IterT& end, int wrapLengthParam = 0, int numIndentationSpaces = 0)
+{
+ const int bigInt = ~0u/2;
+ const std::string baseIndentation = std::string(numIndentationSpaces, ' ');
+ const std::string deepIndentation = baseIndentation + std::string(4, ' ');
+ const int wrapLength = wrapLengthParam > 0 ? wrapLengthParam : bigInt;
+ const int length = static_cast<int>(std::distance(begin, end));
+ std::string result;
+
+ if (length > wrapLength)
+ result += "(amount: " + de::toString(length) + ") ";
+ result += std::string() + "{" + (length > wrapLength ? "\n"+deepIndentation : " ");
+
+ {
+ int index = 0;
+ for (IterT it = begin; it != end; ++it)
+ {
+ if (it != begin)
+ result += std::string() + ", " + (index % wrapLength == 0 ? "\n"+deepIndentation : "");
+ result += de::toString(*it);
+ index++;
+ }
+
+ result += length > wrapLength ? "\n"+baseIndentation : " ";
+ }
+
+ result += "}";
+ return result;
+}
+
+template <typename ContainerT>
+std::string containerStr (const ContainerT& c, int wrapLengthParam = 0, int numIndentationSpaces = 0)
+{
+ return elemsStr(c.begin(), c.end(), wrapLengthParam, numIndentationSpaces);
+}
+
+//! Copy 'count' objects of type T from 'memory' into a vector.
+//! 'offset' is the offset of first object in memory, and 'stride' is the distance between consecutive objects.
+template<typename T>
+std::vector<T> readInterleavedData (const int count, const void* memory, const int offset, const int stride)
+{
+ std::vector<T> results(count);
+ const deUint8* pData = static_cast<const deUint8*>(memory) + offset;
+
+ for (int i = 0; i < count; ++i)
+ {
+ deMemcpy(&results[i], pData, sizeof(T));
+ pData += stride;
+ }
+
+ return results;
+}
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONUTIL_HPP
--- /dev/null
+/*------------------------------------------------------------------------
+ * 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 Winding Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTessellationWindingTests.hpp"
+#include "vktTestCaseUtil.hpp"
+#include "vktTessellationUtil.hpp"
+
+#include "tcuTestLog.hpp"
+#include "tcuRGBA.hpp"
+
+#include "vkDefs.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageUtil.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkStrUtil.hpp"
+
+#include "deUniquePtr.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+using namespace vk;
+
+namespace
+{
+
+std::string getCaseName (const TessPrimitiveType primitiveType, const Winding winding)
+{
+ std::ostringstream str;
+ str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getWindingShaderName(winding);
+ return str.str();
+}
+
+inline VkFrontFace mapFrontFace (const Winding winding)
+{
+ switch (winding)
+ {
+ case WINDING_CCW: return VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ case WINDING_CW: return VK_FRONT_FACE_CLOCKWISE;
+ default:
+ DE_ASSERT(false);
+ return VK_FRONT_FACE_LAST;
+ }
+}
+
+//! Returns true when the image passes the verification.
+bool verifyResultImage (tcu::TestLog& log,
+ const tcu::ConstPixelBufferAccess image,
+ const TessPrimitiveType primitiveType,
+ const Winding winding,
+ const Winding frontFaceWinding)
+{
+ const int totalNumPixels = image.getWidth()*image.getHeight();
+ const int badPixelTolerance = (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(image.getWidth(), image.getHeight()) : 0);
+
+ const tcu::Vec4 white = tcu::RGBA::white().toVec();
+ const tcu::Vec4 red = tcu::RGBA::red().toVec();
+
+ int numWhitePixels = 0;
+ int numRedPixels = 0;
+ for (int y = 0; y < image.getHeight(); y++)
+ for (int x = 0; x < image.getWidth(); x++)
+ {
+ numWhitePixels += image.getPixel(x, y) == white ? 1 : 0;
+ numRedPixels += image.getPixel(x, y) == red ? 1 : 0;
+ }
+
+ DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
+
+ log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << tcu::TestLog::EndMessage;
+
+ const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels;
+ if (otherPixels > badPixelTolerance)
+ {
+ log << tcu::TestLog::Message
+ << "Failure: Got " << otherPixels << " other than white or red pixels (maximum tolerance " << badPixelTolerance << ")"
+ << tcu::TestLog::EndMessage;
+ return false;
+ }
+
+ if (frontFaceWinding == winding)
+ {
+ if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
+ {
+ if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
+ {
+ log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << tcu::TestLog::EndMessage;
+ return false;
+ }
+ }
+ else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
+ {
+ if (numWhitePixels != totalNumPixels)
+ {
+ log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << tcu::TestLog::EndMessage;
+ return false;
+ }
+ }
+ else
+ DE_ASSERT(false);
+ }
+ else
+ {
+ if (numWhitePixels != 0)
+ {
+ log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)" << tcu::TestLog::EndMessage;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+class WindingTest : public TestCase
+{
+public:
+ WindingTest (tcu::TestContext& testCtx,
+ const TessPrimitiveType primitiveType,
+ const Winding winding);
+
+ void initPrograms (SourceCollections& programCollection) const;
+ TestInstance* createInstance (Context& context) const;
+
+private:
+ const TessPrimitiveType m_primitiveType;
+ const Winding m_winding;
+};
+
+WindingTest::WindingTest (tcu::TestContext& testCtx,
+ const TessPrimitiveType primitiveType,
+ const Winding winding)
+ : TestCase (testCtx, getCaseName(primitiveType, winding), "")
+ , m_primitiveType (primitiveType)
+ , m_winding (winding)
+{
+}
+
+void WindingTest::initPrograms (SourceCollections& programCollection) const
+{
+ // 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"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_TessLevelInner[0] = 5.0;\n"
+ << " gl_TessLevelInner[1] = 5.0;\n"
+ << "\n"
+ << " gl_TessLevelOuter[0] = 5.0;\n"
+ << " gl_TessLevelOuter[1] = 5.0;\n"
+ << " gl_TessLevelOuter[2] = 5.0;\n"
+ << " gl_TessLevelOuter[3] = 5.0;\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(m_primitiveType) << ", "
+ << getWindingShaderName(m_winding) << ") in;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
+ }
+
+ // Fragment shader
+ {
+ std::ostringstream src;
+ src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
+ << "\n"
+ << "layout(location = 0) out mediump vec4 o_color;\n"
+ << "\n"
+ << "void main (void)\n"
+ << "{\n"
+ << " o_color = vec4(1.0);\n"
+ << "}\n";
+
+ programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
+ }
+}
+
+class WindingTestInstance : public TestInstance
+{
+public:
+ WindingTestInstance (Context& context,
+ const TessPrimitiveType primitiveType,
+ const Winding winding);
+
+ tcu::TestStatus iterate (void);
+
+private:
+ const TessPrimitiveType m_primitiveType;
+ const Winding m_winding;
+};
+
+WindingTestInstance::WindingTestInstance (Context& context,
+ const TessPrimitiveType primitiveType,
+ const Winding winding)
+ : TestInstance (context)
+ , m_primitiveType (primitiveType)
+ , m_winding (winding)
+{
+}
+
+tcu::TestStatus WindingTestInstance::iterate (void)
+{
+ const DeviceInterface& vk = m_context.getDeviceInterface();
+ const VkDevice device = m_context.getDevice();
+ const VkQueue queue = m_context.getUniversalQueue();
+ const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
+ Allocator& allocator = m_context.getDefaultAllocator();
+
+ // Color attachment
+
+ const tcu::IVec2 renderSize = tcu::IVec2(64, 64);
+ const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const Image colorAttachmentImage (vk, device, allocator,
+ makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
+ MemoryRequirement::Any);
+
+ // Color output buffer: image will be copied here for verification
+
+ const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
+ const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
+
+ // Pipeline
+
+ const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
+ const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
+ const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
+ const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device));
+
+ const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
+
+ // Front face is static state, so we have to create two pipelines.
+
+ const Unique<VkPipeline> pipelineCounterClockwise(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setCullModeFlags(cullMode)
+ .setFrontFace (VK_FRONT_FACE_COUNTER_CLOCKWISE)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ const Unique<VkPipeline> pipelineClockwise(GraphicsPipelineBuilder()
+ .setRenderSize (renderSize)
+ .setCullModeFlags(cullMode)
+ .setFrontFace (VK_FRONT_FACE_CLOCKWISE)
+ .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
+ .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
+ .build (vk, device, *pipelineLayout, *renderPass));
+
+ const struct // not static
+ {
+ Winding frontFaceWinding;
+ VkPipeline pipeline;
+ } testCases[] =
+ {
+ { WINDING_CCW, *pipelineCounterClockwise },
+ { WINDING_CW, *pipelineClockwise },
+ };
+
+ tcu::TestLog& log = m_context.getTestContext().getLog();
+ log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage;
+
+ bool success = true;
+
+ // Draw commands
+
+ const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
+ const Unique<VkCommandBuffer> cmdBuffer(makeCommandBuffer(vk, device, *cmdPool));
+
+ for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx)
+ {
+ const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding;
+
+ log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding)) << tcu::TestLog::EndMessage;
+
+ // Reset the command buffer and begin.
+ beginCommandBuffer(vk, *cmdBuffer);
+
+ // Change color attachment image layout
+ {
+ // State is slightly different on the first iteration.
+ const VkImageLayout currentLayout = (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
+ const VkAccessFlags srcFlags = (caseNdx == 0 ? (VkAccessFlags)0 : VK_ACCESS_TRANSFER_READ_BIT);
+
+ const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
+ srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
+ currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
+ }
+
+ // Begin render pass
+ {
+ const VkRect2D renderArea = {
+ makeOffset2D(0, 0),
+ makeExtent2D(renderSize.x(), renderSize.y()),
+ };
+ const tcu::Vec4 clearColor = tcu::RGBA::red().toVec();
+
+ beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
+ }
+
+ vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline);
+
+ // Process a single abstract vertex.
+ vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
+ endRenderPass(vk, *cmdBuffer);
+
+ // Copy render result to a host-visible buffer
+ {
+ const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ *colorAttachmentImage, colorImageSubresourceRange);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
+ 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
+ }
+ {
+ const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 0), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
+ }
+ {
+ const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
+ VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
+
+ vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
+ 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
+ }
+
+ endCommandBuffer(vk, *cmdBuffer);
+ submitCommandsAndWait(vk, device, queue, *cmdBuffer);
+
+ {
+ // Log rendered image
+ const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
+ invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
+
+ const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
+ log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
+
+ // Verify case result
+ success = success && verifyResultImage(log, imagePixelAccess, m_primitiveType, m_winding, frontFaceWinding);
+ }
+ } // for windingNdx
+
+ return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
+}
+
+TestInstance* WindingTest::createInstance (Context& context) const
+{
+ requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
+
+ return new WindingTestInstance(context, m_primitiveType, m_winding);
+}
+
+} // anonymous
+
+//! These tests correspond to dEQP-GLES31.functional.tessellation.winding.*
+tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "winding", "Test the cw and ccw input layout qualifiers"));
+
+ static const TessPrimitiveType primitivesNoIsolines[] =
+ {
+ TESSPRIMITIVETYPE_TRIANGLES,
+ TESSPRIMITIVETYPE_QUADS,
+ };
+
+ for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
+ for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
+ group->addChild(new WindingTest(testCtx, primitivesNoIsolines[primitiveTypeNdx], (Winding)windingNdx));
+
+ return group.release();
+}
+
+} // tessellation
+} // vkt
--- /dev/null
+#ifndef _VKTTESSELLATIONWINDINGTESTS_HPP
+#define _VKTTESSELLATIONWINDINGTESTS_HPP
+/*------------------------------------------------------------------------
+ * 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 Winding Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace tessellation
+{
+
+tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx);
+
+} // tessellation
+} // vkt
+
+#endif // _VKTTESSELLATIONWINDINGTESTS_HPP
#include "vktWsiTests.hpp"
#include "vktSynchronization.hpp"
#include "vktSparseResourcesTests.hpp"
+#include "vktTessellationTests.hpp"
#include <vector>
#include <sstream>
addChild(wsi::createTests (m_testCtx));
addChild(createSynchronizationTests (m_testCtx));
addChild(sparse::createTests (m_testCtx));
+ addChild(tessellation::createTests (m_testCtx));
}
} // vkt
# Issue 340: Cube map corner texel selection
dEQP-VK.pipeline.sampler.view_type.cube.format.r5g6b5_unorm_pack16.mag_filter.linear
+
+# Issue 384: glslang incorrect stuct member decorations
+dEQP-VK.tessellation.user_defined_io.*
dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_17
dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_20
dEQP-VK.sparse_resources.buffer_sparse_memory_aliasing.buffer_size_2_24
+dEQP-VK.tessellation.limits.max_tessellation_generation_level
+dEQP-VK.tessellation.limits.max_tessellation_patch_size
+dEQP-VK.tessellation.limits.max_tessellation_control_per_vertex_input_components
+dEQP-VK.tessellation.limits.max_tessellation_control_per_vertex_output_components
+dEQP-VK.tessellation.limits.max_tessellation_control_per_patch_output_components
+dEQP-VK.tessellation.limits.max_tessellation_control_total_output_components
+dEQP-VK.tessellation.limits.max_tessellation_evaluation_input_components
+dEQP-VK.tessellation.limits.max_tessellation_evaluation_output_components
+dEQP-VK.tessellation.tesscoord.triangles_equal_spacing
+dEQP-VK.tessellation.tesscoord.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.tesscoord.triangles_fractional_even_spacing
+dEQP-VK.tessellation.tesscoord.quads_equal_spacing
+dEQP-VK.tessellation.tesscoord.quads_fractional_odd_spacing
+dEQP-VK.tessellation.tesscoord.quads_fractional_even_spacing
+dEQP-VK.tessellation.tesscoord.isolines_equal_spacing
+dEQP-VK.tessellation.tesscoord.isolines_fractional_odd_spacing
+dEQP-VK.tessellation.tesscoord.isolines_fractional_even_spacing
+dEQP-VK.tessellation.winding.triangles_ccw
+dEQP-VK.tessellation.winding.triangles_cw
+dEQP-VK.tessellation.winding.quads_ccw
+dEQP-VK.tessellation.winding.quads_cw
+dEQP-VK.tessellation.shader_input_output.patch_vertices_5_in_10_out
+dEQP-VK.tessellation.shader_input_output.patch_vertices_10_in_5_out
+dEQP-VK.tessellation.shader_input_output.primitive_id_tcs
+dEQP-VK.tessellation.shader_input_output.primitive_id_tes
+dEQP-VK.tessellation.shader_input_output.patch_vertices_in_tcs
+dEQP-VK.tessellation.shader_input_output.patch_vertices_in_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_inner_0_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_inner_1_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_0_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_1_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_2_tes
+dEQP-VK.tessellation.shader_input_output.tess_level_outer_3_tes
+dEQP-VK.tessellation.shader_input_output.gl_position_vs_to_tcs
+dEQP-VK.tessellation.shader_input_output.gl_position_tcs_to_tes
+dEQP-VK.tessellation.shader_input_output.gl_position_vs_to_tcs_to_tes
+dEQP-VK.tessellation.shader_input_output.barrier
+dEQP-VK.tessellation.misc_draw.fill_cover_triangles_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_triangles_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_triangles_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_quads_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_quads_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_cover_quads_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_triangles_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_triangles_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_triangles_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_quads_equal_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_quads_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.fill_overlap_quads_fractional_even_spacing
+dEQP-VK.tessellation.misc_draw.isolines_equal_spacing
+dEQP-VK.tessellation.misc_draw.isolines_fractional_odd_spacing
+dEQP-VK.tessellation.misc_draw.isolines_fractional_even_spacing
+dEQP-VK.tessellation.common_edge.triangles_equal_spacing
+dEQP-VK.tessellation.common_edge.triangles_equal_spacing_precise
+dEQP-VK.tessellation.common_edge.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.common_edge.triangles_fractional_odd_spacing_precise
+dEQP-VK.tessellation.common_edge.triangles_fractional_even_spacing
+dEQP-VK.tessellation.common_edge.triangles_fractional_even_spacing_precise
+dEQP-VK.tessellation.common_edge.quads_equal_spacing
+dEQP-VK.tessellation.common_edge.quads_equal_spacing_precise
+dEQP-VK.tessellation.common_edge.quads_fractional_odd_spacing
+dEQP-VK.tessellation.common_edge.quads_fractional_odd_spacing_precise
+dEQP-VK.tessellation.common_edge.quads_fractional_even_spacing
+dEQP-VK.tessellation.common_edge.quads_fractional_even_spacing_precise
+dEQP-VK.tessellation.fractional_spacing.odd
+dEQP-VK.tessellation.fractional_spacing.even
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_cw
+dEQP-VK.tessellation.primitive_discard.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.primitive_discard.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_cw
+dEQP-VK.tessellation.primitive_discard.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.primitive_discard.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_cw
+dEQP-VK.tessellation.primitive_discard.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.primitive_discard.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.primitive_set.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_division.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.quads_equal_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_edge_division.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_symmetry.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.outer_edge_index_independence.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.triangle_set.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.triangle_set.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.triangle_set.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.triangle_set.quads_equal_spacing
+dEQP-VK.tessellation.invariance.triangle_set.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.triangle_set.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.quads_equal_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.inner_triangle_set.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.triangles_equal_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.triangles_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.triangles_fractional_even_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.quads_equal_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.quads_fractional_odd_spacing
+dEQP-VK.tessellation.invariance.outer_triangle_set.quads_fractional_even_spacing
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.tess_coord_component_range.isolines_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.triangles_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.quads_fractional_even_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_equal_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_odd_spacing_cw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_ccw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_ccw_point_mode
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_cw
+dEQP-VK.tessellation.invariance.one_minus_tess_coord_component.isolines_fractional_even_spacing_cw_point_mode