1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2014 The Android Open Source Project
6 * Copyright (c) 2016 The Khronos Group Inc.
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
22 * \brief Tessellation Winding Tests
23 *//*--------------------------------------------------------------------*/
25 #include "vktTessellationWindingTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
30 #include "tcuTestLog.hpp"
31 #include "tcuRGBA.hpp"
32 #include "tcuMaybe.hpp"
35 #include "vkQueryUtil.hpp"
36 #include "vkBuilderUtil.hpp"
37 #include "vkImageUtil.hpp"
38 #include "vkTypeUtil.hpp"
39 #include "vkStrUtil.hpp"
41 #include "deUniquePtr.hpp"
45 namespace tessellation
53 std::string getCaseName (const TessPrimitiveType primitiveType, const Winding winding, bool yFlip)
55 std::ostringstream str;
56 str << getTessPrimitiveTypeShaderName(primitiveType) << "_" << getWindingShaderName(winding);
62 inline VkFrontFace mapFrontFace (const Winding winding)
66 case WINDING_CCW: return VK_FRONT_FACE_COUNTER_CLOCKWISE;
67 case WINDING_CW: return VK_FRONT_FACE_CLOCKWISE;
70 return VK_FRONT_FACE_LAST;
74 //! Returns true when the image passes the verification.
75 bool verifyResultImage (tcu::TestLog& log,
76 const tcu::ConstPixelBufferAccess image,
77 const TessPrimitiveType primitiveType,
78 const VkTessellationDomainOriginKHR domainOrigin,
79 const Winding winding,
81 const Winding frontFaceWinding)
83 const bool expectVisiblePrimitive = ((frontFaceWinding == winding) == (domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR)) != yFlip;
85 const int totalNumPixels = image.getWidth()*image.getHeight();
87 const tcu::Vec4 white = tcu::RGBA::white().toVec();
88 const tcu::Vec4 red = tcu::RGBA::red().toVec();
90 int numWhitePixels = 0;
93 // Count red and white pixels
94 for (int y = 0; y < image.getHeight(); y++)
95 for (int x = 0; x < image.getWidth(); x++)
97 numWhitePixels += image.getPixel(x, y) == white ? 1 : 0;
98 numRedPixels += image.getPixel(x, y) == red ? 1 : 0;
101 DE_ASSERT(numWhitePixels + numRedPixels <= totalNumPixels);
103 log << tcu::TestLog::Message << "Note: got " << numWhitePixels << " white and " << numRedPixels << " red pixels" << tcu::TestLog::EndMessage;
106 const int otherPixels = totalNumPixels - numWhitePixels - numRedPixels;
109 log << tcu::TestLog::Message
110 << "Failure: Got " << otherPixels << " other than white or red pixels"
111 << tcu::TestLog::EndMessage;
116 if (expectVisiblePrimitive)
118 if (primitiveType == TESSPRIMITIVETYPE_TRIANGLES)
120 const int badPixelTolerance = (primitiveType == TESSPRIMITIVETYPE_TRIANGLES ? 5*de::max(image.getWidth(), image.getHeight()) : 0);
122 if (de::abs(numWhitePixels - totalNumPixels/2) > badPixelTolerance)
124 log << tcu::TestLog::Message << "Failure: wrong number of white pixels; expected approximately " << totalNumPixels/2 << tcu::TestLog::EndMessage;
128 // Check number of filled pixels (from left) in top and bottom rows to
129 // determine if triangle is in right orientation.
131 const tcu::IVec2 expectedStart (0, 1);
132 const tcu::IVec2 expectedEnd (image.getWidth()-1, image.getWidth());
133 const tcu::IVec2 expectedTop = yFlip ? expectedStart : expectedEnd;
134 const tcu::IVec2 expectedBottom = yFlip ? expectedEnd : expectedStart;
135 int numTopFilled = 0;
136 int numBottomFilled = 0;
138 for (int x = 0; x < image.getWidth(); ++x)
140 if (image.getPixel(x, 0) == white)
146 for (int x = 0; x < image.getWidth(); ++x)
148 if (image.getPixel(x, image.getHeight()-1) == white)
149 numBottomFilled += 1;
154 if (!de::inBounds(numTopFilled, expectedTop[0], expectedTop[1]) ||
155 !de::inBounds(numBottomFilled, expectedBottom[0], expectedBottom[1]))
157 log << tcu::TestLog::Message << "Failure: triangle orientation is incorrect" << tcu::TestLog::EndMessage;
163 else if (primitiveType == TESSPRIMITIVETYPE_QUADS)
165 if (numWhitePixels != totalNumPixels)
167 log << tcu::TestLog::Message << "Failure: expected only white pixels (full-viewport quad)" << tcu::TestLog::EndMessage;
176 if (numWhitePixels != 0)
178 log << tcu::TestLog::Message << "Failure: expected only red pixels (everything culled)" << tcu::TestLog::EndMessage;
186 typedef tcu::Maybe<VkTessellationDomainOriginKHR> MaybeDomainOrigin;
188 class WindingTest : public TestCase
191 WindingTest (tcu::TestContext& testCtx,
192 const TessPrimitiveType primitiveType,
193 const MaybeDomainOrigin& domainOrigin,
194 const Winding winding,
197 void initPrograms (SourceCollections& programCollection) const;
198 TestInstance* createInstance (Context& context) const;
201 const TessPrimitiveType m_primitiveType;
202 const MaybeDomainOrigin m_domainOrigin;
203 const Winding m_winding;
207 WindingTest::WindingTest (tcu::TestContext& testCtx,
208 const TessPrimitiveType primitiveType,
209 const MaybeDomainOrigin& domainOrigin,
210 const Winding winding,
212 : TestCase (testCtx, getCaseName(primitiveType, winding, yFlip), "")
213 , m_primitiveType (primitiveType)
214 , m_domainOrigin (domainOrigin)
215 , m_winding (winding)
220 void WindingTest::initPrograms (SourceCollections& programCollection) const
222 // Vertex shader - no inputs
224 std::ostringstream src;
225 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
227 << "void main (void)\n"
231 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
234 // Tessellation control shader
236 std::ostringstream src;
237 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
238 << "#extension GL_EXT_tessellation_shader : require\n"
240 << "layout(vertices = 1) out;\n"
242 << "void main (void)\n"
244 << " gl_TessLevelInner[0] = 5.0;\n"
245 << " gl_TessLevelInner[1] = 5.0;\n"
247 << " gl_TessLevelOuter[0] = 5.0;\n"
248 << " gl_TessLevelOuter[1] = 5.0;\n"
249 << " gl_TessLevelOuter[2] = 5.0;\n"
250 << " gl_TessLevelOuter[3] = 5.0;\n"
253 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
256 // Tessellation evaluation shader
258 std::ostringstream src;
259 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
260 << "#extension GL_EXT_tessellation_shader : require\n"
262 << "layout(" << getTessPrimitiveTypeShaderName(m_primitiveType) << ", "
263 << getWindingShaderName(m_winding) << ") in;\n"
265 << "void main (void)\n"
267 << " gl_Position = vec4(gl_TessCoord.xy*2.0 - 1.0, 0.0, 1.0);\n"
270 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
275 std::ostringstream src;
276 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
278 << "layout(location = 0) out mediump vec4 o_color;\n"
280 << "void main (void)\n"
282 << " o_color = vec4(1.0);\n"
285 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
289 class WindingTestInstance : public TestInstance
292 WindingTestInstance (Context& context,
293 const TessPrimitiveType primitiveType,
294 const MaybeDomainOrigin& domainOrigin,
295 const Winding winding,
298 tcu::TestStatus iterate (void);
301 void requireExtension (const char* name) const;
303 const TessPrimitiveType m_primitiveType;
304 const MaybeDomainOrigin m_domainOrigin;
305 const Winding m_winding;
309 WindingTestInstance::WindingTestInstance (Context& context,
310 const TessPrimitiveType primitiveType,
311 const MaybeDomainOrigin& domainOrigin,
312 const Winding winding,
314 : TestInstance (context)
315 , m_primitiveType (primitiveType)
316 , m_domainOrigin (domainOrigin)
317 , m_winding (winding)
321 requireExtension("VK_KHR_maintenance1");
323 if ((bool)m_domainOrigin)
324 requireExtension("VK_KHR_maintenance2");
327 void WindingTestInstance::requireExtension (const char* name) const
329 if (!de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), name))
330 TCU_THROW(NotSupportedError, (std::string(name) + " is not supported").c_str());
333 tcu::TestStatus WindingTestInstance::iterate (void)
335 const DeviceInterface& vk = m_context.getDeviceInterface();
336 const VkDevice device = m_context.getDevice();
337 const VkQueue queue = m_context.getUniversalQueue();
338 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
339 Allocator& allocator = m_context.getDefaultAllocator();
343 const tcu::IVec2 renderSize = tcu::IVec2(64, 64);
344 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
345 const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
346 const Image colorAttachmentImage (vk, device, allocator,
347 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
348 MemoryRequirement::Any);
350 // Color output buffer: image will be copied here for verification
352 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
353 const Buffer colorBuffer (vk, device, allocator, makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
357 const Unique<VkImageView> colorAttachmentView(makeImageView (vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
358 const Unique<VkRenderPass> renderPass (makeRenderPass (vk, device, colorFormat));
359 const Unique<VkFramebuffer> framebuffer (makeFramebuffer (vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y(), 1u));
360 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(vk, device));
362 const VkCullModeFlags cullMode = VK_CULL_MODE_BACK_BIT;
364 // Front face is static state, so we have to create two pipelines.
366 const Unique<VkPipeline> pipelineCounterClockwise(GraphicsPipelineBuilder()
367 .setCullModeFlags (cullMode)
368 .setFrontFace (VK_FRONT_FACE_COUNTER_CLOCKWISE)
369 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
370 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
371 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
372 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
373 .setTessellationDomainOrigin (m_domainOrigin)
374 .build (vk, device, *pipelineLayout, *renderPass));
376 const Unique<VkPipeline> pipelineClockwise(GraphicsPipelineBuilder()
377 .setCullModeFlags (cullMode)
378 .setFrontFace (VK_FRONT_FACE_CLOCKWISE)
379 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("vert"), DE_NULL)
380 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, m_context.getBinaryCollection().get("tesc"), DE_NULL)
381 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, m_context.getBinaryCollection().get("tese"), DE_NULL)
382 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("frag"), DE_NULL)
383 .setTessellationDomainOrigin (m_domainOrigin)
384 .build (vk, device, *pipelineLayout, *renderPass));
386 const struct // not static
388 Winding frontFaceWinding;
392 { WINDING_CCW, *pipelineCounterClockwise },
393 { WINDING_CW, *pipelineClockwise },
396 tcu::TestLog& log = m_context.getTestContext().getLog();
397 log << tcu::TestLog::Message << "Pipeline uses " << getCullModeFlagsStr(cullMode) << tcu::TestLog::EndMessage;
403 const Unique<VkCommandPool> cmdPool (makeCommandPool (vk, device, queueFamilyIndex));
404 const Unique<VkCommandBuffer> cmdBuffer(allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
406 for (int caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(testCases); ++caseNdx)
408 const Winding frontFaceWinding = testCases[caseNdx].frontFaceWinding;
410 log << tcu::TestLog::Message << "Setting " << getFrontFaceName(mapFrontFace(frontFaceWinding)) << tcu::TestLog::EndMessage;
412 // Reset the command buffer and begin.
413 beginCommandBuffer(vk, *cmdBuffer);
415 // Change color attachment image layout
417 // State is slightly different on the first iteration.
418 const VkImageLayout currentLayout = (caseNdx == 0 ? VK_IMAGE_LAYOUT_UNDEFINED : VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
419 const VkAccessFlags srcFlags = (caseNdx == 0 ? (VkAccessFlags)0 : (VkAccessFlags)VK_ACCESS_TRANSFER_READ_BIT);
421 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
422 srcFlags, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
423 currentLayout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
424 *colorAttachmentImage, colorImageSubresourceRange);
426 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0u,
427 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
432 const VkRect2D renderArea = {
434 makeExtent2D(renderSize.x(), renderSize.y()),
436 const tcu::Vec4 clearColor = tcu::RGBA::red().toVec();
438 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
441 const VkViewport viewport =
444 m_yFlip ? static_cast<float>(renderSize.y()) : 0.0f, // float y;
445 static_cast<float>(renderSize.x()), // float width;
446 static_cast<float>(m_yFlip ? -renderSize.y() : renderSize.y()), // float height;
447 0.0f, // float minDepth;
448 1.0f, // float maxDepth;
450 vk.cmdSetViewport(*cmdBuffer, 0, 1, &viewport);
452 const VkRect2D scissor =
455 makeExtent2D(renderSize.x(), renderSize.y()),
457 vk.cmdSetScissor(*cmdBuffer, 0, 1, &scissor);
459 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, testCases[caseNdx].pipeline);
461 // Process a single abstract vertex.
462 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
463 endRenderPass(vk, *cmdBuffer);
465 // Copy render result to a host-visible buffer
467 const VkImageMemoryBarrier colorAttachmentPreCopyBarrier = makeImageMemoryBarrier(
468 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
469 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
470 *colorAttachmentImage, colorImageSubresourceRange);
472 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0u,
473 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentPreCopyBarrier);
476 const VkBufferImageCopy copyRegion = makeBufferImageCopy(makeExtent3D(renderSize.x(), renderSize.y(), 1), makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
477 vk.cmdCopyImageToBuffer(*cmdBuffer, *colorAttachmentImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *colorBuffer, 1u, ©Region);
480 const VkBufferMemoryBarrier postCopyBarrier = makeBufferMemoryBarrier(
481 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *colorBuffer, 0ull, colorBufferSizeBytes);
483 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
484 0u, DE_NULL, 1u, &postCopyBarrier, 0u, DE_NULL);
487 endCommandBuffer(vk, *cmdBuffer);
488 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
491 // Log rendered image
492 const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
493 invalidateMappedMemoryRange(vk, device, colorBufferAlloc.getMemory(), colorBufferAlloc.getOffset(), colorBufferSizeBytes);
495 const tcu::ConstPixelBufferAccess imagePixelAccess(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
496 log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
498 // Verify case result
499 success = verifyResultImage(log,
502 !m_domainOrigin ? VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR : *m_domainOrigin,
505 frontFaceWinding) && success;
509 return (success ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Failure"));
512 TestInstance* WindingTest::createInstance (Context& context) const
514 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER);
516 return new WindingTestInstance(context, m_primitiveType, m_domainOrigin, m_winding, m_yFlip);
519 void populateWindingGroup (tcu::TestCaseGroup* group, tcu::Maybe<VkTessellationDomainOriginKHR> domainOrigin)
521 static const TessPrimitiveType primitivesNoIsolines[] =
523 TESSPRIMITIVETYPE_TRIANGLES,
524 TESSPRIMITIVETYPE_QUADS,
527 for (int primitiveTypeNdx = 0; primitiveTypeNdx < DE_LENGTH_OF_ARRAY(primitivesNoIsolines); ++primitiveTypeNdx)
528 for (int windingNdx = 0; windingNdx < WINDING_LAST; ++windingNdx)
530 group->addChild(new WindingTest(group->getTestContext(), primitivesNoIsolines[primitiveTypeNdx], domainOrigin, (Winding)windingNdx, false));
531 group->addChild(new WindingTest(group->getTestContext(), primitivesNoIsolines[primitiveTypeNdx], domainOrigin, (Winding)windingNdx, true));
537 //! These tests correspond to dEQP-GLES31.functional.tessellation.winding.*
538 tcu::TestCaseGroup* createWindingTests (tcu::TestContext& testCtx)
540 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "winding", "Test the cw and ccw input layout qualifiers"));
542 addTestGroup(group.get(), "default_domain", "No tessellation domain specified", populateWindingGroup, tcu::nothing<VkTessellationDomainOriginKHR>());
543 addTestGroup(group.get(), "lower_left_domain", "Lower left tessellation domain", populateWindingGroup, tcu::just(VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR));
544 addTestGroup(group.get(), "upper_left_domain", "Upper left tessellation domain", populateWindingGroup, tcu::just(VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR));
546 return group.release();