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());
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.cmdClearColorImage(*cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
271 const VkMemoryBarrier memBarrier =
273 VK_STRUCTURE_TYPE_MEMORY_BARRIER, // VkStructureType sType;
274 DE_NULL, // const void* pNext;
275 VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask;
276 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT // VkAccessFlags dstAccessMask;
279 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
282 const VkRect2D renderArea = { { 0, 0 }, { WIDTH, HEIGHT } };
283 const RenderPassBeginInfo renderPassBegin (*m_renderPass, *m_framebuffer, renderArea);
285 vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE);
288 const VkDeviceSize offset = 0;
289 const VkBuffer buffer = m_vertexBuffer->object();
291 vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &offset);
294 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
295 vk.cmdDraw(*cmdBuffer, 6, 1, 0, 0);
296 vk.cmdEndRenderPass(*cmdBuffer);
297 vk.endCommandBuffer(*cmdBuffer);
301 const Unique<VkFence> fence (createFence(vk, device));
302 const VkSubmitInfo submitInfo =
304 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType;
305 DE_NULL, // const void* pNext;
306 0, // uint32_t waitSemaphoreCount;
307 DE_NULL, // const VkSemaphore* pWaitSemaphores;
308 (const VkPipelineStageFlags*)DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask;
309 1, // uint32_t commandBufferCount;
310 &cmdBuffer.get(), // const VkCommandBuffer* pCommandBuffers;
311 0, // uint32_t signalSemaphoreCount;
312 DE_NULL // const VkSemaphore* pSignalSemaphores;
315 VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence));
316 VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), VK_TRUE, ~0ull));
321 const VkOffset3D zeroOffset = { 0, 0, 0 };
322 return m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
326 //! Determine if a triangle with triangleFace orientation will be culled or not
327 bool NegativeViewportHeightTestInstance::isCulled (const VkFrontFace triangleFace) const
329 const bool isFrontFacing = (triangleFace == m_params.frontFace);
331 if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing)
333 if (m_params.cullMode == VK_CULL_MODE_BACK_BIT && !isFrontFacing)
336 return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
339 MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage (void) const
341 DE_ASSERT(HEIGHT == WIDTH/2);
343 MovePtr<tcu::TextureLevel> image (new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT));
344 const tcu::PixelBufferAccess access (image->getAccess());
345 const Vec4 black (0.0f, 0.0f, 0.0f, 1.0f);
346 const Vec4 white (1.0f);
347 const Vec4 gray (0.5f, 0.5f, 0.5f, 1.0f);
349 tcu::clear(access, black);
351 const int p1 = static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f);
352 const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f);
354 // left triangle (CCW -> CW after y-flip)
355 if (!isCulled(VK_FRONT_FACE_CLOCKWISE))
357 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray);
359 for (int y = p1; y <= p2; ++y)
360 for (int x = p1; x < y; ++x)
361 access.setPixel(color, x, y);
364 // right triangle (CW -> CCW after y-flip)
365 if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE))
367 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray);
369 for (int y = p1; y <= p2; ++y)
370 for (int x = WIDTH - y; x < p2 + HEIGHT; ++x)
371 access.setPixel(color, x, y);
377 std::string getCullModeStr (const VkCullModeFlagBits cullMode)
379 // Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags.
380 // The function getCullModeFlagsStr() doesn't work too well in this case.
384 case VK_CULL_MODE_NONE: return "VK_CULL_MODE_NONE";
385 case VK_CULL_MODE_FRONT_BIT: return "VK_CULL_MODE_FRONT_BIT";
386 case VK_CULL_MODE_BACK_BIT: return "VK_CULL_MODE_BACK_BIT";
387 case VK_CULL_MODE_FRONT_AND_BACK: return "VK_CULL_MODE_FRONT_AND_BACK";
391 return std::string();
395 tcu::TestStatus NegativeViewportHeightTestInstance::iterate (void)
397 // Check requirements
399 if (!de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), std::string("VK_KHR_maintenance1")))
400 TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_maintenance1");
402 // Set up the viewport and draw
404 const VkViewport viewport =
407 static_cast<float>(HEIGHT), // float y;
408 static_cast<float>(WIDTH), // float width;
409 -static_cast<float>(HEIGHT), // float height;
410 0.0f, // float minDepth;
411 1.0f, // float maxDepth;
414 const tcu::ConstPixelBufferAccess resultImage = draw(viewport);
416 // Verify the results
418 tcu::TestLog& log = m_context.getTestContext().getLog();
419 MovePtr<tcu::TextureLevel> referenceImage = generateReferenceImage();
421 log << tcu::TestLog::Message
422 << "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign of the triangle's area."
423 << tcu::TestLog::EndMessage;
424 log << tcu::TestLog::Message
425 << "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."
426 << " Front face is white, back face is gray."
427 << tcu::TestLog::EndMessage;
428 log << tcu::TestLog::Message
429 << "Front face: " << getFrontFaceName(m_params.frontFace) << "\n"
430 << "Cull mode: " << getCullModeStr (m_params.cullMode) << "\n"
431 << tcu::TestLog::EndMessage;
433 if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT))
434 return tcu::TestStatus::fail("Rendered image is incorrect");
436 return tcu::TestStatus::pass("Pass");
439 class NegativeViewportHeightTest : public TestCase
442 NegativeViewportHeightTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
443 : TestCase (testCtx, name, description)
448 void initPrograms (SourceCollections& programCollection) const
452 std::ostringstream src;
453 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
455 << "layout(location = 0) in vec4 in_position;\n"
457 << "out gl_PerVertex {\n"
458 << " vec4 gl_Position;\n"
461 << "void main(void)\n"
463 << " gl_Position = in_position;\n"
466 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
471 std::ostringstream src;
472 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
474 << "layout(location = 0) out vec4 out_color;\n"
476 << "void main(void)\n"
478 << " if (gl_FrontFacing)\n"
479 << " out_color = vec4(1.0);\n"
481 << " out_color = vec4(vec3(0.5), 1.0);\n"
484 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
488 virtual TestInstance* createInstance (Context& context) const
490 return new NegativeViewportHeightTestInstance(context, m_params);
494 const TestParams m_params;
497 void populateTestGroup (tcu::TestCaseGroup* testGroup)
501 const char* const name;
502 VkFrontFace frontFace;
505 { "front_ccw", VK_FRONT_FACE_COUNTER_CLOCKWISE },
506 { "front_cw", VK_FRONT_FACE_CLOCKWISE },
511 const char* const name;
512 VkCullModeFlagBits cullMode;
515 { "cull_none", VK_CULL_MODE_NONE },
516 { "cull_front", VK_CULL_MODE_FRONT_BIT },
517 { "cull_back", VK_CULL_MODE_BACK_BIT },
518 { "cull_both", VK_CULL_MODE_FRONT_AND_BACK },
521 for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace)
522 for (int ndxCullMode = 0; ndxCullMode < DE_LENGTH_OF_ARRAY(cullMode); ++ndxCullMode)
524 const TestParams params =
526 frontFace[ndxFrontFace].frontFace,
527 cullMode[ndxCullMode].cullMode,
529 std::ostringstream name;
530 name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name;
532 testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), "", params));
538 tcu::TestCaseGroup* createNegativeViewportHeightTests (tcu::TestContext& testCtx)
540 return createTestGroup(testCtx, "negative_viewport_height", "Negative viewport height (VK_KHR_maintenance1)", populateTestGroup);