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"
39 #include "deSharedPtr.hpp"
60 VkFrontFace frontFace;
61 VkCullModeFlagBits cullMode;
64 class NegativeViewportHeightTestInstance : public TestInstance
67 NegativeViewportHeightTestInstance (Context& context, const TestParams& params);
68 tcu::TestStatus iterate (void);
69 tcu::ConstPixelBufferAccess draw (const VkViewport viewport);
70 MovePtr<tcu::TextureLevel> generateReferenceImage (void) const;
71 bool isCulled (const VkFrontFace triangleFace) const;
74 const TestParams m_params;
75 const VkFormat m_colorAttachmentFormat;
76 SharedPtr<Image> m_colorTargetImage;
77 Move<VkImageView> m_colorTargetView;
78 SharedPtr<Buffer> m_vertexBuffer;
79 Move<VkRenderPass> m_renderPass;
80 Move<VkFramebuffer> m_framebuffer;
81 Move<VkPipelineLayout> m_pipelineLayout;
82 Move<VkPipeline> m_pipeline;
85 NegativeViewportHeightTestInstance::NegativeViewportHeightTestInstance (Context& context, const TestParams& params)
86 : TestInstance (context)
88 , m_colorAttachmentFormat (VK_FORMAT_R8G8B8A8_UNORM)
90 const DeviceInterface& vk = m_context.getDeviceInterface();
91 const VkDevice device = m_context.getDevice();
95 std::vector<Vec4> vertexData;
98 vertexData.push_back(Vec4(-0.8f, -0.6f, 0.0f, 1.0f)); // 0-----2
99 vertexData.push_back(Vec4(-0.8f, 0.6f, 0.0f, 1.0f)); // | /
100 vertexData.push_back(Vec4(-0.2f, -0.6f, 0.0f, 1.0f)); // 1|/
103 vertexData.push_back(Vec4( 0.2f, -0.6f, 0.0f, 1.0f)); // 0-----1
104 vertexData.push_back(Vec4( 0.8f, -0.6f, 0.0f, 1.0f)); // \ |
105 vertexData.push_back(Vec4( 0.8f, 0.6f, 0.0f, 1.0f)); // \|2
107 const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4);
108 m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
109 m_context.getDefaultAllocator(), MemoryRequirement::HostVisible);
111 deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize));
112 flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(), m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
117 const VkExtent3D targetImageExtent = { WIDTH, HEIGHT, 1 };
118 const VkImageUsageFlags targetImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
120 const ImageCreateInfo targetImageCreateInfo(
121 VK_IMAGE_TYPE_2D, // imageType,
122 m_colorAttachmentFormat, // format,
123 targetImageExtent, // extent,
126 VK_SAMPLE_COUNT_1_BIT, // samples,
127 VK_IMAGE_TILING_OPTIMAL, // tiling,
128 targetImageUsageFlags); // usage,
130 m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator());
132 RenderPassCreateInfo renderPassCreateInfo;
133 renderPassCreateInfo.addAttachment(AttachmentDescription(
134 m_colorAttachmentFormat, // format
135 VK_SAMPLE_COUNT_1_BIT, // samples
136 VK_ATTACHMENT_LOAD_OP_LOAD, // loadOp
137 VK_ATTACHMENT_STORE_OP_STORE, // storeOp
138 VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp
139 VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp
140 VK_IMAGE_LAYOUT_GENERAL, // initialLayout
141 VK_IMAGE_LAYOUT_GENERAL)); // finalLayout
143 const VkAttachmentReference colorAttachmentReference =
146 VK_IMAGE_LAYOUT_GENERAL
149 renderPassCreateInfo.addSubpass(SubpassDescription(
150 VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
151 (VkSubpassDescriptionFlags)0, // flags
152 0u, // inputAttachmentCount
153 DE_NULL, // inputAttachments
154 1u, // colorAttachmentCount
155 &colorAttachmentReference, // colorAttachments
156 DE_NULL, // resolveAttachments
157 AttachmentReference(), // depthStencilAttachment
158 0u, // preserveAttachmentCount
159 DE_NULL)); // preserveAttachments
161 m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
166 const ImageViewCreateInfo colorTargetViewInfo (m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat);
167 m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo);
169 std::vector<VkImageView> colorAttachments(1);
170 colorAttachments[0] = *m_colorTargetView;
172 const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
173 m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
178 const VkVertexInputBindingDescription vertexInputBindingDescription =
180 0u, // uint32_t binding;
181 sizeof(Vec4), // uint32_t stride;
182 VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
185 const VkVertexInputAttributeDescription vertexInputAttributeDescription =
187 0u, // uint32_t location;
188 0u, // uint32_t binding;
189 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
190 0u // uint32_t offset;
193 const PipelineCreateInfo::VertexInputState vertexInputState = PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription,
194 1, &vertexInputAttributeDescription);
198 const VkRect2D scissor =
201 { WIDTH, HEIGHT }, // width, height
204 std::vector<VkDynamicState> dynamicStates;
205 dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
207 const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
208 const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
210 const PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
211 m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
213 const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState;
215 PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0);
216 pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vertexModule, "main", VK_SHADER_STAGE_VERTEX_BIT));
217 pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT));
218 pipelineCreateInfo.addState (PipelineCreateInfo::VertexInputState (vertexInputState));
219 pipelineCreateInfo.addState (PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
220 pipelineCreateInfo.addState (PipelineCreateInfo::ColorBlendState (1, &colorBlendAttachmentState));
221 pipelineCreateInfo.addState (PipelineCreateInfo::ViewportState (1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor)));
222 pipelineCreateInfo.addState (PipelineCreateInfo::DepthStencilState ());
223 pipelineCreateInfo.addState (PipelineCreateInfo::RasterizerState (
224 VK_FALSE, // depthClampEnable
225 VK_FALSE, // rasterizerDiscardEnable
226 VK_POLYGON_MODE_FILL, // polygonMode
227 m_params.cullMode, // cullMode
228 m_params.frontFace, // frontFace
229 VK_FALSE, // depthBiasEnable
230 0.0f, // depthBiasConstantFactor
231 0.0f, // depthBiasClamp
232 0.0f, // depthBiasSlopeFactor
234 pipelineCreateInfo.addState (PipelineCreateInfo::MultiSampleState ());
235 pipelineCreateInfo.addState (PipelineCreateInfo::DynamicState (dynamicStates));
237 m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
240 tcu::ConstPixelBufferAccess NegativeViewportHeightTestInstance::draw (const VkViewport viewport)
242 const DeviceInterface& vk = m_context.getDeviceInterface();
243 const VkDevice device = m_context.getDevice();
244 const VkQueue queue = m_context.getUniversalQueue();
245 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
249 const CmdPoolCreateInfo cmdPoolCreateInfo (queueFamilyIndex);
250 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, &cmdPoolCreateInfo));
252 const VkCommandBufferAllocateInfo cmdBufferAllocateInfo =
254 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType;
255 DE_NULL, // const void* pNext;
256 *cmdPool, // VkCommandPool commandPool;
257 VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCommandBufferLevel level;
258 1u, // deUint32 bufferCount;
260 const Unique<VkCommandBuffer> cmdBuffer(allocateCommandBuffer(vk, device, &cmdBufferAllocateInfo));
265 const CmdBufferBeginInfo beginInfo;
266 vk.beginCommandBuffer(*cmdBuffer, &beginInfo);
269 vk.cmdSetViewport(*cmdBuffer, 0u, 1u, &viewport);
272 const VkClearColorValue clearColor = makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f).color;
273 const ImageSubresourceRange subresourceRange (VK_IMAGE_ASPECT_COLOR_BIT);
275 initialTransitionColor2DImage(vk, *cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL);
276 vk.cmdClearColorImage(*cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
279 const VkMemoryBarrier memBarrier =
281 VK_STRUCTURE_TYPE_MEMORY_BARRIER, // VkStructureType sType;
282 DE_NULL, // const void* pNext;
283 VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask;
284 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT // VkAccessFlags dstAccessMask;
287 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
290 const VkRect2D renderArea = { { 0, 0 }, { WIDTH, HEIGHT } };
291 const RenderPassBeginInfo renderPassBegin (*m_renderPass, *m_framebuffer, renderArea);
293 vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE);
296 const VkDeviceSize offset = 0;
297 const VkBuffer buffer = m_vertexBuffer->object();
299 vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &offset);
302 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
303 vk.cmdDraw(*cmdBuffer, 6, 1, 0, 0);
304 vk.cmdEndRenderPass(*cmdBuffer);
305 vk.endCommandBuffer(*cmdBuffer);
309 const VkFenceCreateInfo fenceInfo =
311 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType;
312 DE_NULL, // const void* pNext;
313 (VkFenceCreateFlags)0, // VkFenceCreateFlags flags;
315 const Unique<VkFence> fence (createFence(vk, device, &fenceInfo));
316 const VkSubmitInfo submitInfo =
318 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
319 DE_NULL, // const void* pNext;
320 0, // uint32_t waitSemaphoreCount;
321 DE_NULL, // const VkSemaphore* pWaitSemaphores;
322 (const VkPipelineStageFlags*)DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
323 1, // uint32_t commandBufferCount;
324 &cmdBuffer.get(), // const VkCommandBuffer* pCommandBuffers;
325 0, // uint32_t signalSemaphoreCount;
326 DE_NULL // const VkSemaphore* pSignalSemaphores;
329 VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
330 VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), VK_TRUE, ~0ull));
335 const VkOffset3D zeroOffset = { 0, 0, 0 };
336 return m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
340 //! Determine if a triangle with triangleFace orientation will be culled or not
341 bool NegativeViewportHeightTestInstance::isCulled (const VkFrontFace triangleFace) const
343 const bool isFrontFacing = (triangleFace == m_params.frontFace);
345 if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing)
347 if (m_params.cullMode == VK_CULL_MODE_BACK_BIT && !isFrontFacing)
350 return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
353 MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage (void) const
355 DE_ASSERT(HEIGHT == WIDTH/2);
357 MovePtr<tcu::TextureLevel> image (new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT));
358 const tcu::PixelBufferAccess access (image->getAccess());
359 const Vec4 black (0.0f, 0.0f, 0.0f, 1.0f);
360 const Vec4 white (1.0f);
361 const Vec4 gray (0.5f, 0.5f, 0.5f, 1.0f);
363 tcu::clear(access, black);
365 const int p1 = static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f);
366 const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f);
368 // left triangle (CCW -> CW after y-flip)
369 if (!isCulled(VK_FRONT_FACE_CLOCKWISE))
371 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray);
373 for (int y = p1; y <= p2; ++y)
374 for (int x = p1; x < y; ++x)
375 access.setPixel(color, x, y);
378 // right triangle (CW -> CCW after y-flip)
379 if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE))
381 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray);
383 for (int y = p1; y <= p2; ++y)
384 for (int x = WIDTH - y; x < p2 + HEIGHT; ++x)
385 access.setPixel(color, x, y);
391 std::string getCullModeStr (const VkCullModeFlagBits cullMode)
393 // Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags.
394 // The function getCullModeFlagsStr() doesn't work too well in this case.
398 case VK_CULL_MODE_NONE: return "VK_CULL_MODE_NONE";
399 case VK_CULL_MODE_FRONT_BIT: return "VK_CULL_MODE_FRONT_BIT";
400 case VK_CULL_MODE_BACK_BIT: return "VK_CULL_MODE_BACK_BIT";
401 case VK_CULL_MODE_FRONT_AND_BACK: return "VK_CULL_MODE_FRONT_AND_BACK";
405 return std::string();
409 tcu::TestStatus NegativeViewportHeightTestInstance::iterate (void)
411 // Check requirements
413 if (!de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), std::string("VK_KHR_maintenance1")))
414 TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_maintenance1");
416 // Set up the viewport and draw
418 const VkViewport viewport =
421 static_cast<float>(HEIGHT), // float y;
422 static_cast<float>(WIDTH), // float width;
423 -static_cast<float>(HEIGHT), // float height;
424 0.0f, // float minDepth;
425 1.0f, // float maxDepth;
428 const tcu::ConstPixelBufferAccess resultImage = draw(viewport);
430 // Verify the results
432 tcu::TestLog& log = m_context.getTestContext().getLog();
433 MovePtr<tcu::TextureLevel> referenceImage = generateReferenceImage();
435 log << tcu::TestLog::Message
436 << "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign of the triangle's area."
437 << tcu::TestLog::EndMessage;
438 log << tcu::TestLog::Message
439 << "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."
440 << " Front face is white, back face is gray."
441 << tcu::TestLog::EndMessage;
442 log << tcu::TestLog::Message
443 << "Front face: " << getFrontFaceName(m_params.frontFace) << "\n"
444 << "Cull mode: " << getCullModeStr (m_params.cullMode) << "\n"
445 << tcu::TestLog::EndMessage;
447 if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT))
448 return tcu::TestStatus::fail("Rendered image is incorrect");
450 return tcu::TestStatus::pass("Pass");
453 class NegativeViewportHeightTest : public TestCase
456 NegativeViewportHeightTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
457 : TestCase (testCtx, name, description)
462 void initPrograms (SourceCollections& programCollection) const
466 std::ostringstream src;
467 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
469 << "layout(location = 0) in vec4 in_position;\n"
471 << "out gl_PerVertex {\n"
472 << " vec4 gl_Position;\n"
475 << "void main(void)\n"
477 << " gl_Position = in_position;\n"
480 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
485 std::ostringstream src;
486 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
488 << "layout(location = 0) out vec4 out_color;\n"
490 << "void main(void)\n"
492 << " if (gl_FrontFacing)\n"
493 << " out_color = vec4(1.0);\n"
495 << " out_color = vec4(vec3(0.5), 1.0);\n"
498 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
502 virtual TestInstance* createInstance (Context& context) const
504 return new NegativeViewportHeightTestInstance(context, m_params);
508 const TestParams m_params;
511 void populateTestGroup (tcu::TestCaseGroup* testGroup)
515 const char* const name;
516 VkFrontFace frontFace;
519 { "front_ccw", VK_FRONT_FACE_COUNTER_CLOCKWISE },
520 { "front_cw", VK_FRONT_FACE_CLOCKWISE },
525 const char* const name;
526 VkCullModeFlagBits cullMode;
529 { "cull_none", VK_CULL_MODE_NONE },
530 { "cull_front", VK_CULL_MODE_FRONT_BIT },
531 { "cull_back", VK_CULL_MODE_BACK_BIT },
532 { "cull_both", VK_CULL_MODE_FRONT_AND_BACK },
535 for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace)
536 for (int ndxCullMode = 0; ndxCullMode < DE_LENGTH_OF_ARRAY(cullMode); ++ndxCullMode)
538 const TestParams params =
540 frontFace[ndxFrontFace].frontFace,
541 cullMode[ndxCullMode].cullMode,
543 std::ostringstream name;
544 name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name;
546 testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), "", params));
552 tcu::TestCaseGroup* createNegativeViewportHeightTests (tcu::TestContext& testCtx)
554 return createTestGroup(testCtx, "negative_viewport_height", "Negative viewport height (VK_KHR_maintenance1)", populateTestGroup);