| /*------------------------------------------------------------------------ |
| * 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 "vkBarrierUtil.hpp" |
| #include "vkQueryUtil.hpp" |
| #include "vkBuilderUtil.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkStrUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| #include "vkObjUtil.hpp" |
| |
| #include "deUniquePtr.hpp" |
| #include "deStringUtil.hpp" |
| |
| #include <string> |
| #include <vector> |
| |
| namespace vkt |
| { |
| namespace tessellation |
| { |
| |
| using namespace vk; |
| |
| namespace |
| { |
| |
| 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 (allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| 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 : (VkAccessFlags)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_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, |
| 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier); |
| } |
| |
| // Begin render pass |
| { |
| const VkRect2D renderArea = makeRect2D(renderSize); |
| 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 |
| copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize); |
| |
| 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 |