| /*------------------------------------------------------------------------ |
| * 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 "vkBarrierUtil.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 |
| { |
| |
| 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 * (float)image.getWidth()); |
| const int endX = static_cast<int>(0.85f * (float)image.getWidth()); |
| const int startY = static_cast<int>(0.15f * (float)image.getHeight()); |
| const int endY = static_cast<int>(0.85f * (float)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))); |
| |
| flushAlloc(vk, device, alloc); |
| // 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())); |
| const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex)); |
| const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer (vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout (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_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); |
| { |
| 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 |
| copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize); |
| |
| endCommandBuffer(vk, *cmdBuffer); |
| submitCommandsAndWait(vk, device, queue, *cmdBuffer); |
| |
| { |
| // Log the result image. |
| |
| const Allocation& colorBufferAlloc = colorBuffer.getAllocation(); |
| |
| invalidateAlloc(vk, device, colorBufferAlloc); |
| |
| 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 |