1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
5 * Copyright (c) 2016 The Khronos Group Inc.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
21 * \brief Negative viewport height (part of VK_KHR_maintenance1)
22 *//*--------------------------------------------------------------------*/
24 #include "vktDrawNegativeViewportHeightTests.hpp"
25 #include "vktDrawCreateInfoUtil.hpp"
26 #include "vktDrawImageObjectUtil.hpp"
27 #include "vktDrawBufferObjectUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
29 #include "vktTestCaseUtil.hpp"
31 #include "vkPrograms.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkImageUtil.hpp"
35 #include "tcuVector.hpp"
36 #include "tcuTextureUtil.hpp"
37 #include "tcuImageCompare.hpp"
38 #include "tcuTestLog.hpp"
40 #include "deSharedPtr.hpp"
61 VkFrontFace frontFace;
62 VkCullModeFlagBits cullMode;
65 class NegativeViewportHeightTestInstance : public TestInstance
68 NegativeViewportHeightTestInstance (Context& context, const TestParams& params);
69 tcu::TestStatus iterate (void);
70 tcu::ConstPixelBufferAccess draw (const VkViewport viewport);
71 MovePtr<tcu::TextureLevel> generateReferenceImage (void) const;
72 bool isCulled (const VkFrontFace triangleFace) const;
75 const TestParams m_params;
76 const VkFormat m_colorAttachmentFormat;
77 SharedPtr<Image> m_colorTargetImage;
78 Move<VkImageView> m_colorTargetView;
79 SharedPtr<Buffer> m_vertexBuffer;
80 Move<VkRenderPass> m_renderPass;
81 Move<VkFramebuffer> m_framebuffer;
82 Move<VkPipelineLayout> m_pipelineLayout;
83 Move<VkPipeline> m_pipeline;
86 NegativeViewportHeightTestInstance::NegativeViewportHeightTestInstance (Context& context, const TestParams& params)
87 : TestInstance (context)
89 , m_colorAttachmentFormat (VK_FORMAT_R8G8B8A8_UNORM)
91 const DeviceInterface& vk = m_context.getDeviceInterface();
92 const VkDevice device = m_context.getDevice();
96 std::vector<Vec4> vertexData;
99 vertexData.push_back(Vec4(-0.8f, -0.6f, 0.0f, 1.0f)); // 0-----2
100 vertexData.push_back(Vec4(-0.8f, 0.6f, 0.0f, 1.0f)); // | /
101 vertexData.push_back(Vec4(-0.2f, -0.6f, 0.0f, 1.0f)); // 1|/
104 vertexData.push_back(Vec4( 0.2f, -0.6f, 0.0f, 1.0f)); // 0-----1
105 vertexData.push_back(Vec4( 0.8f, -0.6f, 0.0f, 1.0f)); // \ |
106 vertexData.push_back(Vec4( 0.8f, 0.6f, 0.0f, 1.0f)); // \|2
108 const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4);
109 m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
110 m_context.getDefaultAllocator(), MemoryRequirement::HostVisible);
112 deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize));
113 flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(), m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
118 const VkExtent3D targetImageExtent = { WIDTH, HEIGHT, 1 };
119 const VkImageUsageFlags targetImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
121 const ImageCreateInfo targetImageCreateInfo(
122 VK_IMAGE_TYPE_2D, // imageType,
123 m_colorAttachmentFormat, // format,
124 targetImageExtent, // extent,
127 VK_SAMPLE_COUNT_1_BIT, // samples,
128 VK_IMAGE_TILING_OPTIMAL, // tiling,
129 targetImageUsageFlags); // usage,
131 m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
133 RenderPassCreateInfo renderPassCreateInfo;
134 renderPassCreateInfo.addAttachment(AttachmentDescription(
135 m_colorAttachmentFormat, // format
136 VK_SAMPLE_COUNT_1_BIT, // samples
137 VK_ATTACHMENT_LOAD_OP_LOAD, // loadOp
138 VK_ATTACHMENT_STORE_OP_STORE, // storeOp
139 VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp
140 VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp
141 VK_IMAGE_LAYOUT_GENERAL, // initialLayout
142 VK_IMAGE_LAYOUT_GENERAL)); // finalLayout
144 const VkAttachmentReference colorAttachmentReference =
147 VK_IMAGE_LAYOUT_GENERAL
150 renderPassCreateInfo.addSubpass(SubpassDescription(
151 VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
152 (VkSubpassDescriptionFlags)0, // flags
153 0u, // inputAttachmentCount
154 DE_NULL, // inputAttachments
155 1u, // colorAttachmentCount
156 &colorAttachmentReference, // colorAttachments
157 DE_NULL, // resolveAttachments
158 AttachmentReference(), // depthStencilAttachment
159 0u, // preserveAttachmentCount
160 DE_NULL)); // preserveAttachments
162 m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
167 const ImageViewCreateInfo colorTargetViewInfo (m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat);
168 m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo);
170 std::vector<VkImageView> colorAttachments(1);
171 colorAttachments[0] = *m_colorTargetView;
173 const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
174 m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
179 const VkVertexInputBindingDescription vertexInputBindingDescription =
181 0u, // uint32_t binding;
182 sizeof(Vec4), // uint32_t stride;
183 VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
186 const VkVertexInputAttributeDescription vertexInputAttributeDescription =
188 0u, // uint32_t location;
189 0u, // uint32_t binding;
190 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
191 0u // uint32_t offset;
194 const PipelineCreateInfo::VertexInputState vertexInputState = PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription,
195 1, &vertexInputAttributeDescription);
199 const VkRect2D scissor =
202 { WIDTH, HEIGHT }, // width, height
205 std::vector<VkDynamicState> dynamicStates;
206 dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
208 const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
209 const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
211 const PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
212 m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
214 const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState;
216 PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0);
217 pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vertexModule, "main", VK_SHADER_STAGE_VERTEX_BIT));
218 pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT));
219 pipelineCreateInfo.addState (PipelineCreateInfo::VertexInputState (vertexInputState));
220 pipelineCreateInfo.addState (PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
221 pipelineCreateInfo.addState (PipelineCreateInfo::ColorBlendState (1, &colorBlendAttachmentState));
222 pipelineCreateInfo.addState (PipelineCreateInfo::ViewportState (1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor)));
223 pipelineCreateInfo.addState (PipelineCreateInfo::DepthStencilState ());
224 pipelineCreateInfo.addState (PipelineCreateInfo::RasterizerState (
225 VK_FALSE, // depthClampEnable
226 VK_FALSE, // rasterizerDiscardEnable
227 VK_POLYGON_MODE_FILL, // polygonMode
228 m_params.cullMode, // cullMode
229 m_params.frontFace, // frontFace
230 VK_FALSE, // depthBiasEnable
231 0.0f, // depthBiasConstantFactor
232 0.0f, // depthBiasClamp
233 0.0f, // depthBiasSlopeFactor
235 pipelineCreateInfo.addState (PipelineCreateInfo::MultiSampleState ());
236 pipelineCreateInfo.addState (PipelineCreateInfo::DynamicState (dynamicStates));
238 m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
241 tcu::ConstPixelBufferAccess NegativeViewportHeightTestInstance::draw (const VkViewport viewport)
243 const DeviceInterface& vk = m_context.getDeviceInterface();
244 const VkDevice device = m_context.getDevice();
245 const VkQueue queue = m_context.getUniversalQueue();
246 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
250 const CmdPoolCreateInfo cmdPoolCreateInfo (queueFamilyIndex);
251 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, &cmdPoolCreateInfo));
252 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
257 const CmdBufferBeginInfo beginInfo;
258 vk.beginCommandBuffer(*cmdBuffer, &beginInfo);
261 vk.cmdSetViewport(*cmdBuffer, 0u, 1u, &viewport);
264 const VkClearColorValue clearColor = makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f).color;
265 const ImageSubresourceRange subresourceRange (VK_IMAGE_ASPECT_COLOR_BIT);
267 initialTransitionColor2DImage(vk, *cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL,
268 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
269 vk.cmdClearColorImage(*cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
272 const VkMemoryBarrier memBarrier =
274 VK_STRUCTURE_TYPE_MEMORY_BARRIER, // VkStructureType sType;
275 DE_NULL, // const void* pNext;
276 VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask;
277 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT // VkAccessFlags dstAccessMask;
280 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
283 const VkRect2D renderArea = { { 0, 0 }, { WIDTH, HEIGHT } };
284 const RenderPassBeginInfo renderPassBegin (*m_renderPass, *m_framebuffer, renderArea);
286 vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE);
289 const VkDeviceSize offset = 0;
290 const VkBuffer buffer = m_vertexBuffer->object();
292 vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &offset);
295 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
296 vk.cmdDraw(*cmdBuffer, 6, 1, 0, 0);
297 vk.cmdEndRenderPass(*cmdBuffer);
298 vk.endCommandBuffer(*cmdBuffer);
302 const Unique<VkFence> fence (createFence(vk, device));
303 const VkSubmitInfo submitInfo =
305 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
306 DE_NULL, // const void* pNext;
307 0, // uint32_t waitSemaphoreCount;
308 DE_NULL, // const VkSemaphore* pWaitSemaphores;
309 (const VkPipelineStageFlags*)DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
310 1, // uint32_t commandBufferCount;
311 &cmdBuffer.get(), // const VkCommandBuffer* pCommandBuffers;
312 0, // uint32_t signalSemaphoreCount;
313 DE_NULL // const VkSemaphore* pSignalSemaphores;
316 VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
317 VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), VK_TRUE, ~0ull));
322 const VkOffset3D zeroOffset = { 0, 0, 0 };
323 return m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
327 //! Determine if a triangle with triangleFace orientation will be culled or not
328 bool NegativeViewportHeightTestInstance::isCulled (const VkFrontFace triangleFace) const
330 const bool isFrontFacing = (triangleFace == m_params.frontFace);
332 if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing)
334 if (m_params.cullMode == VK_CULL_MODE_BACK_BIT && !isFrontFacing)
337 return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
340 MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage (void) const
342 DE_ASSERT(HEIGHT == WIDTH/2);
344 MovePtr<tcu::TextureLevel> image (new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT));
345 const tcu::PixelBufferAccess access (image->getAccess());
346 const Vec4 black (0.0f, 0.0f, 0.0f, 1.0f);
347 const Vec4 white (1.0f);
348 const Vec4 gray (0.5f, 0.5f, 0.5f, 1.0f);
350 tcu::clear(access, black);
352 const int p1 = static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f);
353 const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f);
355 // left triangle (CCW -> CW after y-flip)
356 if (!isCulled(VK_FRONT_FACE_CLOCKWISE))
358 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray);
360 for (int y = p1; y <= p2; ++y)
361 for (int x = p1; x < y; ++x)
362 access.setPixel(color, x, y);
365 // right triangle (CW -> CCW after y-flip)
366 if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE))
368 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray);
370 for (int y = p1; y <= p2; ++y)
371 for (int x = WIDTH - y; x < p2 + HEIGHT; ++x)
372 access.setPixel(color, x, y);
378 std::string getCullModeStr (const VkCullModeFlagBits cullMode)
380 // Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags.
381 // The function getCullModeFlagsStr() doesn't work too well in this case.
385 case VK_CULL_MODE_NONE: return "VK_CULL_MODE_NONE";
386 case VK_CULL_MODE_FRONT_BIT: return "VK_CULL_MODE_FRONT_BIT";
387 case VK_CULL_MODE_BACK_BIT: return "VK_CULL_MODE_BACK_BIT";
388 case VK_CULL_MODE_FRONT_AND_BACK: return "VK_CULL_MODE_FRONT_AND_BACK";
392 return std::string();
396 tcu::TestStatus NegativeViewportHeightTestInstance::iterate (void)
398 // Check requirements
400 if (!de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), std::string("VK_KHR_maintenance1")))
401 TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_maintenance1");
403 // Set up the viewport and draw
405 const VkViewport viewport =
408 static_cast<float>(HEIGHT), // float y;
409 static_cast<float>(WIDTH), // float width;
410 -static_cast<float>(HEIGHT), // float height;
411 0.0f, // float minDepth;
412 1.0f, // float maxDepth;
415 const tcu::ConstPixelBufferAccess resultImage = draw(viewport);
417 // Verify the results
419 tcu::TestLog& log = m_context.getTestContext().getLog();
420 MovePtr<tcu::TextureLevel> referenceImage = generateReferenceImage();
422 log << tcu::TestLog::Message
423 << "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign of the triangle's area."
424 << tcu::TestLog::EndMessage;
425 log << tcu::TestLog::Message
426 << "After the flip, the triangle on the left is CW and the triangle on the right is CCW. Right angles of the both triangles should be at the bottom of the image."
427 << " Front face is white, back face is gray."
428 << tcu::TestLog::EndMessage;
429 log << tcu::TestLog::Message
430 << "Front face: " << getFrontFaceName(m_params.frontFace) << "\n"
431 << "Cull mode: " << getCullModeStr (m_params.cullMode) << "\n"
432 << tcu::TestLog::EndMessage;
434 if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT))
435 return tcu::TestStatus::fail("Rendered image is incorrect");
437 return tcu::TestStatus::pass("Pass");
440 class NegativeViewportHeightTest : public TestCase
443 NegativeViewportHeightTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
444 : TestCase (testCtx, name, description)
449 void initPrograms (SourceCollections& programCollection) const
453 std::ostringstream src;
454 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
456 << "layout(location = 0) in vec4 in_position;\n"
458 << "out gl_PerVertex {\n"
459 << " vec4 gl_Position;\n"
462 << "void main(void)\n"
464 << " gl_Position = in_position;\n"
467 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
472 std::ostringstream src;
473 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
475 << "layout(location = 0) out vec4 out_color;\n"
477 << "void main(void)\n"
479 << " if (gl_FrontFacing)\n"
480 << " out_color = vec4(1.0);\n"
482 << " out_color = vec4(vec3(0.5), 1.0);\n"
485 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
489 virtual TestInstance* createInstance (Context& context) const
491 return new NegativeViewportHeightTestInstance(context, m_params);
495 const TestParams m_params;
498 void populateTestGroup (tcu::TestCaseGroup* testGroup)
502 const char* const name;
503 VkFrontFace frontFace;
506 { "front_ccw", VK_FRONT_FACE_COUNTER_CLOCKWISE },
507 { "front_cw", VK_FRONT_FACE_CLOCKWISE },
512 const char* const name;
513 VkCullModeFlagBits cullMode;
516 { "cull_none", VK_CULL_MODE_NONE },
517 { "cull_front", VK_CULL_MODE_FRONT_BIT },
518 { "cull_back", VK_CULL_MODE_BACK_BIT },
519 { "cull_both", VK_CULL_MODE_FRONT_AND_BACK },
522 for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace)
523 for (int ndxCullMode = 0; ndxCullMode < DE_LENGTH_OF_ARRAY(cullMode); ++ndxCullMode)
525 const TestParams params =
527 frontFace[ndxFrontFace].frontFace,
528 cullMode[ndxCullMode].cullMode,
530 std::ostringstream name;
531 name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name;
533 testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), "", params));
539 tcu::TestCaseGroup* createNegativeViewportHeightTests (tcu::TestContext& testCtx)
541 return createTestGroup(testCtx, "negative_viewport_height", "Negative viewport height (VK_KHR_maintenance1)", populateTestGroup);