1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  *
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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.
18  *
19  *//*!
20  * \file
21  * \brief Negative viewport height (part of VK_KHR_maintenance1)
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktDrawNegativeViewportHeightTests.hpp"
25 #include "vktDrawCreateInfoUtil.hpp"
26 #include "vktDrawImageObjectUtil.hpp"
27 #include "vktDrawBufferObjectUtil.hpp"
28 #include "vktTestGroupUtil.hpp"
29 #include "vktTestCaseUtil.hpp"
30 
31 #include "vkPrograms.hpp"
32 #include "vkTypeUtil.hpp"
33 #include "vkImageUtil.hpp"
34 #include "vkQueryUtil.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkObjUtil.hpp"
37 #include "vkBarrierUtil.hpp"
38 
39 #include "tcuVector.hpp"
40 #include "tcuTextureUtil.hpp"
41 #include "tcuImageCompare.hpp"
42 #include "tcuTestLog.hpp"
43 
44 #include "deSharedPtr.hpp"
45 #include "deRandom.hpp"
46 
47 namespace vkt
48 {
49 namespace Draw
50 {
51 namespace
52 {
53 using namespace vk;
54 using de::MovePtr;
55 using de::SharedPtr;
56 using tcu::Vec4;
57 
58 class DynRenderHelper
59 {
60 public:
DynRenderHelper(const SharedGroupParams params)61     DynRenderHelper(const SharedGroupParams params) : m_params(params)
62     {
63     }
64 
beginSecondaryCmdBuffer(const DeviceInterface & vkd,VkCommandBuffer cmdBuffer,const VkFormat & colorAttachmentFormat) const65     void beginSecondaryCmdBuffer(const DeviceInterface &vkd, VkCommandBuffer cmdBuffer,
66                                  const VkFormat &colorAttachmentFormat) const
67     {
68 #ifndef CTS_USES_VULKANSC
69         VkRenderingFlags renderingFlags = 0u;
70         if (m_params->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
71             renderingFlags |= VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;
72 
73         VkCommandBufferInheritanceRenderingInfoKHR inheritanceRenderingInfo{
74             VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO_KHR, // VkStructureType sType;
75             DE_NULL,                                                         // const void* pNext;
76             renderingFlags,                                                  // VkRenderingFlagsKHR flags;
77             0u,                                                              // uint32_t viewMask;
78             1u,                                                              // uint32_t colorAttachmentCount;
79             &colorAttachmentFormat,                                          // const VkFormat* pColorAttachmentFormats;
80             VK_FORMAT_UNDEFINED,                                             // VkFormat depthAttachmentFormat;
81             VK_FORMAT_UNDEFINED,                                             // VkFormat stencilAttachmentFormat;
82             VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
83         };
84         const VkCommandBufferInheritanceInfo bufferInheritanceInfo = initVulkanStructure(&inheritanceRenderingInfo);
85 
86         VkCommandBufferUsageFlags usageFlags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
87         if (!m_params->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
88             usageFlags |= VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
89 
90         const VkCommandBufferBeginInfo commandBufBeginParams{
91             VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
92             DE_NULL,                                     // const void* pNext;
93             usageFlags,                                  // VkCommandBufferUsageFlags flags;
94             &bufferInheritanceInfo};
95 
96         VK_CHECK(vkd.beginCommandBuffer(cmdBuffer, &commandBufBeginParams));
97 #else
98         DE_UNREF(vkd);
99         DE_UNREF(cmdBuffer);
100         DE_UNREF(colorAttachmentFormat);
101         DE_ASSERT(false);
102 #endif // CTS_USES_VULKANSC
103     }
104 
beginRendering(const DeviceInterface & vkd,const VkCommandBuffer cmdBuffer,const bool isPrimaryCmdBuffer,const VkImageView colorImageView,const VkRect2D & renderArea,const VkClearValue & clearValue,const VkImageLayout imageLayout) const105     void beginRendering(const DeviceInterface &vkd, const VkCommandBuffer cmdBuffer, const bool isPrimaryCmdBuffer,
106                         const VkImageView colorImageView, const VkRect2D &renderArea, const VkClearValue &clearValue,
107                         const VkImageLayout imageLayout) const
108     {
109 #ifndef CTS_USES_VULKANSC
110         VkRenderingFlagsKHR renderingFlags = 0u;
111         if (isPrimaryCmdBuffer && m_params->useSecondaryCmdBuffer &&
112             !m_params->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
113             renderingFlags |= VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT;
114 
115         vk::beginRendering(vkd, cmdBuffer, colorImageView, renderArea, clearValue, imageLayout,
116                            VK_ATTACHMENT_LOAD_OP_LOAD, renderingFlags);
117 #else
118         DE_UNREF(vkd);
119         DE_UNREF(cmdBuffer);
120         DE_UNREF(isPrimaryCmdBuffer);
121         DE_UNREF(colorImageView);
122         DE_UNREF(renderArea);
123         DE_UNREF(clearValue);
124         DE_UNREF(imageLayout);
125         DE_ASSERT(false);
126 #endif // CTS_USES_VULKANSC
127     }
128 
129 protected:
130     SharedGroupParams m_params;
131 };
132 
133 enum Constants
134 {
135     WIDTH  = 256,
136     HEIGHT = WIDTH / 2,
137 };
138 
139 struct TestParams
140 {
141     VkFrontFace frontFace;
142     VkCullModeFlagBits cullMode;
143     bool zeroViewportHeight;
144     const SharedGroupParams groupParams;
145 };
146 
147 class NegativeViewportHeightTestInstance : public TestInstance
148 {
149 public:
150     NegativeViewportHeightTestInstance(Context &context, const TestParams &params);
151     tcu::TestStatus iterate(void);
152     void preRenderCommands(VkCommandBuffer cmdBuffer, const VkClearValue &clearColor);
153     void draw(VkCommandBuffer cmdBuffer, const VkViewport &viewport);
154 
155     MovePtr<tcu::TextureLevel> generateReferenceImage(void) const;
156     bool isCulled(const VkFrontFace triangleFace) const;
157 
158 private:
159     const TestParams m_params;
160     const DynRenderHelper m_dynRenderHelper;
161     const VkFormat m_colorAttachmentFormat;
162     SharedPtr<Image> m_colorTargetImage;
163     Move<VkImageView> m_colorTargetView;
164     SharedPtr<Buffer> m_vertexBuffer;
165     Move<VkRenderPass> m_renderPass;
166     Move<VkFramebuffer> m_framebuffer;
167     Move<VkPipelineLayout> m_pipelineLayout;
168     Move<VkPipeline> m_pipeline;
169 };
170 
NegativeViewportHeightTestInstance(Context & context,const TestParams & params)171 NegativeViewportHeightTestInstance::NegativeViewportHeightTestInstance(Context &context, const TestParams &params)
172     : TestInstance(context)
173     , m_params(params)
174     , m_dynRenderHelper(params.groupParams)
175     , m_colorAttachmentFormat(VK_FORMAT_R8G8B8A8_UNORM)
176 {
177     const DeviceInterface &vk = m_context.getDeviceInterface();
178     const VkDevice device     = m_context.getDevice();
179 
180     // Vertex data
181     {
182         std::vector<Vec4> vertexData;
183 
184         // CCW triangle
185         vertexData.push_back(Vec4(-0.8f, -0.6f, 0.0f, 1.0f)); //  0-----2
186         vertexData.push_back(Vec4(-0.8f, 0.6f, 0.0f, 1.0f));  //   |  /
187         vertexData.push_back(Vec4(-0.2f, -0.6f, 0.0f, 1.0f)); //  1|/
188 
189         // CW triangle
190         vertexData.push_back(Vec4(0.2f, -0.6f, 0.0f, 1.0f)); //  0-----1
191         vertexData.push_back(Vec4(0.8f, -0.6f, 0.0f, 1.0f)); //    \  |
192         vertexData.push_back(Vec4(0.8f, 0.6f, 0.0f, 1.0f));  //      \|2
193 
194         const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4);
195         m_vertexBuffer =
196             Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT),
197                                    m_context.getDefaultAllocator(), MemoryRequirement::HostVisible);
198 
199         deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize));
200         flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(),
201                                m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
202     }
203 
204     const VkExtent3D targetImageExtent = {WIDTH, HEIGHT, 1};
205     const VkImageUsageFlags targetImageUsageFlags =
206         VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
207 
208     const ImageCreateInfo targetImageCreateInfo(VK_IMAGE_TYPE_2D,        // imageType,
209                                                 m_colorAttachmentFormat, // format,
210                                                 targetImageExtent,       // extent,
211                                                 1u,                      // mipLevels,
212                                                 1u,                      // arrayLayers,
213                                                 VK_SAMPLE_COUNT_1_BIT,   // samples,
214                                                 VK_IMAGE_TILING_OPTIMAL, // tiling,
215                                                 targetImageUsageFlags);  // usage,
216 
217     m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(),
218                                                m_context.getUniversalQueueFamilyIndex());
219 
220     const ImageViewCreateInfo colorTargetViewInfo(m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D,
221                                                   m_colorAttachmentFormat);
222     m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo);
223 
224     // Render pass and framebuffer
225     if (!m_params.groupParams->useDynamicRendering)
226     {
227         RenderPassCreateInfo renderPassCreateInfo;
228         renderPassCreateInfo.addAttachment(AttachmentDescription(m_colorAttachmentFormat,          // format
229                                                                  VK_SAMPLE_COUNT_1_BIT,            // samples
230                                                                  VK_ATTACHMENT_LOAD_OP_LOAD,       // loadOp
231                                                                  VK_ATTACHMENT_STORE_OP_STORE,     // storeOp
232                                                                  VK_ATTACHMENT_LOAD_OP_DONT_CARE,  // stencilLoadOp
233                                                                  VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp
234                                                                  VK_IMAGE_LAYOUT_GENERAL,          // initialLayout
235                                                                  VK_IMAGE_LAYOUT_GENERAL));        // finalLayout
236 
237         const VkAttachmentReference colorAttachmentReference = {0u, VK_IMAGE_LAYOUT_GENERAL};
238 
239         renderPassCreateInfo.addSubpass(SubpassDescription(VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
240                                                            (VkSubpassDescriptionFlags)0,    // flags
241                                                            0u,                              // inputAttachmentCount
242                                                            DE_NULL,                         // inputAttachments
243                                                            1u,                              // colorAttachmentCount
244                                                            &colorAttachmentReference,       // colorAttachments
245                                                            DE_NULL,                         // resolveAttachments
246                                                            AttachmentReference(),           // depthStencilAttachment
247                                                            0u,                              // preserveAttachmentCount
248                                                            DE_NULL));                       // preserveAttachments
249 
250         m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo);
251 
252         std::vector<VkImageView> colorAttachments{*m_colorTargetView};
253         const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
254         m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo);
255     }
256 
257     // Vertex input
258 
259     const VkVertexInputBindingDescription vertexInputBindingDescription = {
260         0u,                          // uint32_t             binding;
261         sizeof(Vec4),                // uint32_t             stride;
262         VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate    inputRate;
263     };
264 
265     const VkVertexInputAttributeDescription vertexInputAttributeDescription = {
266         0u,                            // uint32_t    location;
267         0u,                            // uint32_t    binding;
268         VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat    format;
269         0u                             // uint32_t    offset;
270     };
271 
272     const PipelineCreateInfo::VertexInputState vertexInputState =
273         PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription, 1, &vertexInputAttributeDescription);
274 
275     // Graphics pipeline
276 
277     const VkRect2D scissor = makeRect2D(WIDTH, HEIGHT);
278 
279     std::vector<VkDynamicState> dynamicStates;
280     dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT);
281 
282     const Unique<VkShaderModule> vertexModule(
283         createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
284     const Unique<VkShaderModule> fragmentModule(
285         createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
286 
287     const PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
288     m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
289 
290     const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState;
291 
292     PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0);
293     pipelineCreateInfo.addShader(
294         PipelineCreateInfo::PipelineShaderStage(*vertexModule, "main", VK_SHADER_STAGE_VERTEX_BIT));
295     pipelineCreateInfo.addShader(
296         PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT));
297     pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(vertexInputState));
298     pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
299     pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &colorBlendAttachmentState));
300     pipelineCreateInfo.addState(
301         PipelineCreateInfo::ViewportState(1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor)));
302     pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
303     pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState(VK_FALSE,             // depthClampEnable
304                                                                     VK_FALSE,             // rasterizerDiscardEnable
305                                                                     VK_POLYGON_MODE_FILL, // polygonMode
306                                                                     m_params.cullMode,    // cullMode
307                                                                     m_params.frontFace,   // frontFace
308                                                                     VK_FALSE,             // depthBiasEnable
309                                                                     0.0f,                 // depthBiasConstantFactor
310                                                                     0.0f,                 // depthBiasClamp
311                                                                     0.0f,                 // depthBiasSlopeFactor
312                                                                     1.0f));               // lineWidth
313     pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
314     pipelineCreateInfo.addState(PipelineCreateInfo::DynamicState(dynamicStates));
315 
316 #ifndef CTS_USES_VULKANSC
317     vk::VkPipelineRenderingCreateInfoKHR renderingCreateInfo{vk::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
318                                                              DE_NULL,
319                                                              0u,
320                                                              1u,
321                                                              &m_colorAttachmentFormat,
322                                                              vk::VK_FORMAT_UNDEFINED,
323                                                              vk::VK_FORMAT_UNDEFINED};
324 
325     if (m_params.groupParams->useDynamicRendering)
326         pipelineCreateInfo.pNext = &renderingCreateInfo;
327 #endif // CTS_USES_VULKANSC
328 
329     m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
330 }
331 
preRenderCommands(VkCommandBuffer cmdBuffer,const VkClearValue & clearColor)332 void NegativeViewportHeightTestInstance::preRenderCommands(VkCommandBuffer cmdBuffer, const VkClearValue &clearColor)
333 {
334     const DeviceInterface &vk = m_context.getDeviceInterface();
335     const ImageSubresourceRange subresourceRange(VK_IMAGE_ASPECT_COLOR_BIT);
336 
337     initialTransitionColor2DImage(vk, cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL,
338                                   VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
339     vk.cmdClearColorImage(cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor.color, 1,
340                           &subresourceRange);
341 
342     const VkMemoryBarrier memBarrier{
343         VK_STRUCTURE_TYPE_MEMORY_BARRIER,                                          // VkStructureType sType;
344         DE_NULL,                                                                   // const void* pNext;
345         VK_ACCESS_TRANSFER_WRITE_BIT,                                              // VkAccessFlags srcAccessMask;
346         VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT // VkAccessFlags dstAccessMask;
347     };
348 
349     vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0,
350                           1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
351 }
352 
draw(VkCommandBuffer cmdBuffer,const VkViewport & viewport)353 void NegativeViewportHeightTestInstance::draw(VkCommandBuffer cmdBuffer, const VkViewport &viewport)
354 {
355     const DeviceInterface &vk = m_context.getDeviceInterface();
356     const VkBuffer buffer     = m_vertexBuffer->object();
357     const VkDeviceSize offset = 0;
358 
359     if (m_params.zeroViewportHeight)
360     {
361         // Set zero viewport height
362         const VkViewport zeroViewportHeight{
363             viewport.x,        // float    x;
364             viewport.y / 2.0f, // float    y;
365             viewport.width,    // float    width;
366             0.0f,              // float    height;
367             viewport.minDepth, // float    minDepth;
368             viewport.maxDepth  // float    maxDepth;
369         };
370 
371         vk.cmdSetViewport(cmdBuffer, 0u, 1u, &zeroViewportHeight);
372     }
373     else
374         vk.cmdSetViewport(cmdBuffer, 0u, 1u, &viewport);
375 
376     vk.cmdBindVertexBuffers(cmdBuffer, 0, 1, &buffer, &offset);
377     vk.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
378     vk.cmdDraw(cmdBuffer, 6, 1, 0, 0);
379 }
380 
381 //! Determine if a triangle with triangleFace orientation will be culled or not
isCulled(const VkFrontFace triangleFace) const382 bool NegativeViewportHeightTestInstance::isCulled(const VkFrontFace triangleFace) const
383 {
384     const bool isFrontFacing = (triangleFace == m_params.frontFace);
385 
386     if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing)
387         return true;
388     if (m_params.cullMode == VK_CULL_MODE_BACK_BIT && !isFrontFacing)
389         return true;
390 
391     return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK;
392 }
393 
generateReferenceImage(void) const394 MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage(void) const
395 {
396     DE_ASSERT(HEIGHT == WIDTH / 2);
397 
398     MovePtr<tcu::TextureLevel> image(new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT));
399     const tcu::PixelBufferAccess access(image->getAccess());
400     const Vec4 blue(0.125f, 0.25f, 0.5f, 1.0f);
401     const Vec4 white(1.0f);
402     const Vec4 gray(0.5f, 0.5f, 0.5f, 1.0f);
403 
404     tcu::clear(access, blue);
405 
406     // Zero viewport height
407     if (m_params.zeroViewportHeight)
408     {
409         return image;
410     }
411     // Negative viewport height
412     else
413     {
414         const int p1 = static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f);
415         const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f);
416 
417         // left triangle (CCW -> CW after y-flip)
418         if (!isCulled(VK_FRONT_FACE_CLOCKWISE))
419         {
420             const Vec4 &color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray);
421 
422             for (int y = p1; y <= p2; ++y)
423                 for (int x = p1; x < y; ++x)
424                     access.setPixel(color, x, y);
425         }
426 
427         // right triangle (CW -> CCW after y-flip)
428         if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE))
429         {
430             const Vec4 &color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray);
431 
432             for (int y = p1; y <= p2; ++y)
433                 for (int x = WIDTH - y; x < p2 + HEIGHT; ++x)
434                     access.setPixel(color, x, y);
435         }
436 
437         return image;
438     }
439 }
440 
getCullModeStr(const VkCullModeFlagBits cullMode)441 std::string getCullModeStr(const VkCullModeFlagBits cullMode)
442 {
443     // Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags.
444     // The function getCullModeFlagsStr() doesn't work too well in this case.
445 
446     switch (cullMode)
447     {
448     case VK_CULL_MODE_NONE:
449         return "VK_CULL_MODE_NONE";
450     case VK_CULL_MODE_FRONT_BIT:
451         return "VK_CULL_MODE_FRONT_BIT";
452     case VK_CULL_MODE_BACK_BIT:
453         return "VK_CULL_MODE_BACK_BIT";
454     case VK_CULL_MODE_FRONT_AND_BACK:
455         return "VK_CULL_MODE_FRONT_AND_BACK";
456 
457     default:
458         DE_ASSERT(0);
459         return std::string();
460     }
461 }
462 
iterate(void)463 tcu::TestStatus NegativeViewportHeightTestInstance::iterate(void)
464 {
465     // Set up the viewport and draw
466 
467     const VkViewport viewport{
468         0.0f,                        // float    x;
469         static_cast<float>(HEIGHT),  // float    y;
470         static_cast<float>(WIDTH),   // float    width;
471         -static_cast<float>(HEIGHT), // float    height;
472         0.0f,                        // float    minDepth;
473         1.0f,                        // float    maxDepth;
474     };
475     VkRect2D rect = makeRect2D(0, 0, WIDTH, HEIGHT);
476 
477     const DeviceInterface &vk       = m_context.getDeviceInterface();
478     const VkDevice device           = m_context.getDevice();
479     const VkQueue queue             = m_context.getUniversalQueue();
480     const uint32_t queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
481     const VkClearValue clearColor   = makeClearValueColorF32(0.125f, 0.25f, 0.5f, 1.0f);
482     const CmdPoolCreateInfo cmdPoolCreateInfo(queueFamilyIndex);
483     const Unique<VkCommandPool> cmdPool(createCommandPool(vk, device, &cmdPoolCreateInfo));
484     const Unique<VkCommandBuffer> cmdBuffer(
485         allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
486     Move<VkCommandBuffer> secCmdBuffer;
487 
488 #ifndef CTS_USES_VULKANSC
489     if (m_params.groupParams->useSecondaryCmdBuffer)
490     {
491         secCmdBuffer = allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
492 
493         // record secondary command buffer
494         m_dynRenderHelper.beginSecondaryCmdBuffer(vk, *secCmdBuffer, m_colorAttachmentFormat);
495 
496         if (m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
497             m_dynRenderHelper.beginRendering(vk, *secCmdBuffer, false /*isPrimary*/, *m_colorTargetView, rect,
498                                              clearColor, VK_IMAGE_LAYOUT_GENERAL);
499 
500         draw(*secCmdBuffer, viewport);
501 
502         if (m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
503             endRendering(vk, *secCmdBuffer);
504 
505         endCommandBuffer(vk, *secCmdBuffer);
506 
507         // record primary command buffer
508         beginCommandBuffer(vk, *cmdBuffer, 0u);
509 
510         preRenderCommands(*cmdBuffer, clearColor);
511 
512         if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
513             m_dynRenderHelper.beginRendering(vk, *cmdBuffer, true /*isPrimary*/, *m_colorTargetView, rect, clearColor,
514                                              VK_IMAGE_LAYOUT_GENERAL);
515 
516         vk.cmdExecuteCommands(*cmdBuffer, 1u, &*secCmdBuffer);
517 
518         if (!m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
519             endRendering(vk, *cmdBuffer);
520 
521         endCommandBuffer(vk, *cmdBuffer);
522     }
523     else if (m_params.groupParams->useDynamicRendering)
524     {
525         beginCommandBuffer(vk, *cmdBuffer);
526 
527         preRenderCommands(*cmdBuffer, clearColor);
528         m_dynRenderHelper.beginRendering(vk, *cmdBuffer, true /*isPrimary*/, *m_colorTargetView, rect, clearColor,
529                                          VK_IMAGE_LAYOUT_GENERAL);
530         draw(*cmdBuffer, viewport);
531         endRendering(vk, *cmdBuffer);
532 
533         endCommandBuffer(vk, *cmdBuffer);
534     }
535 #endif // CTS_USES_VULKANSC
536 
537     if (!m_params.groupParams->useDynamicRendering)
538     {
539         beginCommandBuffer(vk, *cmdBuffer);
540 
541         preRenderCommands(*cmdBuffer, clearColor);
542         beginRenderPass(vk, *cmdBuffer, *m_renderPass, *m_framebuffer, rect);
543         draw(*cmdBuffer, viewport);
544         endRenderPass(vk, *cmdBuffer);
545 
546         endCommandBuffer(vk, *cmdBuffer);
547     }
548 
549     // Submit
550     submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
551 
552     // Get result
553     const VkOffset3D zeroOffset = {0, 0, 0};
554     const tcu::ConstPixelBufferAccess resultImage =
555         m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset,
556                                         WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT);
557 
558     // Verify the results
559 
560     tcu::TestLog &log                         = m_context.getTestContext().getLog();
561     MovePtr<tcu::TextureLevel> referenceImage = generateReferenceImage();
562 
563     // Zero viewport height
564     if (m_params.zeroViewportHeight)
565     {
566         log << tcu::TestLog::Message << "Drawing two triangles with zero viewport height." << tcu::TestLog::EndMessage;
567         log << tcu::TestLog::Message << "Result image should be empty." << tcu::TestLog::EndMessage;
568     }
569     // Negative viewport height
570     else
571     {
572         log << tcu::TestLog::Message
573             << "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign "
574                "of the triangle's area."
575             << tcu::TestLog::EndMessage;
576         log << tcu::TestLog::Message
577             << "After the flip, the triangle on the left is CW and the triangle on the right is CCW. Right angles of "
578                "the both triangles should be at the bottom of the image."
579             << " Front face is white, back face is gray." << tcu::TestLog::EndMessage;
580     }
581 
582     log << tcu::TestLog::Message << "Front face: " << getFrontFaceName(m_params.frontFace) << "\n"
583         << "Cull mode: " << getCullModeStr(m_params.cullMode) << "\n"
584         << tcu::TestLog::EndMessage;
585 
586     if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f,
587                            tcu::COMPARE_LOG_RESULT))
588         return tcu::TestStatus::fail("Rendered image is incorrect");
589     else
590         return tcu::TestStatus::pass("Pass");
591 }
592 
593 class NegativeViewportHeightTest : public TestCase
594 {
595 public:
NegativeViewportHeightTest(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)596     NegativeViewportHeightTest(tcu::TestContext &testCtx, const std::string &name, const TestParams &params)
597         : TestCase(testCtx, name)
598         , m_params(params)
599     {
600     }
601 
initPrograms(SourceCollections & programCollection) const602     void initPrograms(SourceCollections &programCollection) const
603     {
604         // Vertex shader
605         {
606             std::ostringstream src;
607             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
608                 << "\n"
609                 << "layout(location = 0) in vec4 in_position;\n"
610                 << "\n"
611                 << "out gl_PerVertex {\n"
612                 << "    vec4  gl_Position;\n"
613                 << "};\n"
614                 << "\n"
615                 << "void main(void)\n"
616                 << "{\n"
617                 << "    gl_Position = in_position;\n"
618                 << "}\n";
619 
620             programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
621         }
622 
623         // Fragment shader
624         {
625             std::ostringstream src;
626             src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
627                 << "\n"
628                 << "layout(location = 0) out vec4 out_color;\n"
629                 << "\n"
630                 << "void main(void)\n"
631                 << "{\n"
632                 << "    if (gl_FrontFacing)\n"
633                 << "        out_color = vec4(1.0);\n"
634                 << "    else\n"
635                 << "        out_color = vec4(vec3(0.5), 1.0);\n"
636                 << "}\n";
637 
638             programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
639         }
640     }
641 
checkSupport(Context & context) const642     virtual void checkSupport(Context &context) const
643     {
644         if (m_params.groupParams->useDynamicRendering)
645             context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
646 
647         context.requireDeviceFunctionality("VK_KHR_maintenance1");
648     }
649 
createInstance(Context & context) const650     virtual TestInstance *createInstance(Context &context) const
651     {
652         return new NegativeViewportHeightTestInstance(context, m_params);
653     }
654 
655 private:
656     const TestParams m_params;
657 };
658 
659 struct SubGroupParams
660 {
661     bool zeroViewportHeight;
662     const SharedGroupParams groupParams;
663 };
664 
populateTestGroup(tcu::TestCaseGroup * testGroup,SubGroupParams subGroupParams)665 void populateTestGroup(tcu::TestCaseGroup *testGroup, SubGroupParams subGroupParams)
666 {
667     const struct
668     {
669         const char *const name;
670         VkFrontFace frontFace;
671     } frontFace[] = {
672         {"front_ccw", VK_FRONT_FACE_COUNTER_CLOCKWISE},
673         {"front_cw", VK_FRONT_FACE_CLOCKWISE},
674     };
675 
676     const struct
677     {
678         const char *const name;
679         VkCullModeFlagBits cullMode;
680     } cullMode[] = {
681         {"cull_none", VK_CULL_MODE_NONE},
682         {"cull_front", VK_CULL_MODE_FRONT_BIT},
683         {"cull_back", VK_CULL_MODE_BACK_BIT},
684         {"cull_both", VK_CULL_MODE_FRONT_AND_BACK},
685     };
686 
687     for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace)
688         for (int ndxCullMode = 0; ndxCullMode < DE_LENGTH_OF_ARRAY(cullMode); ++ndxCullMode)
689         {
690             const TestParams params = {frontFace[ndxFrontFace].frontFace, cullMode[ndxCullMode].cullMode,
691                                        subGroupParams.zeroViewportHeight, subGroupParams.groupParams};
692             std::ostringstream name;
693             name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name;
694 
695             testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), params));
696         }
697 }
698 
699 enum class OffScreenAxisCase
700 {
701     ONSCREEN      = 0,
702     NEGATIVE_SIDE = 1,
703     POSITIVE_SIDE = 2,
704 };
705 
706 struct OffScreenParams
707 {
708     const uint32_t randomSeed;
709     const OffScreenAxisCase xAxis;
710     const OffScreenAxisCase yAxis;
711     const bool negativeHeight;
712     const SharedGroupParams groupParams;
713 
OffScreenParamsvkt::Draw::__anonfdcf89540111::OffScreenParams714     OffScreenParams(uint32_t seed, OffScreenAxisCase x, OffScreenAxisCase y, bool negH, const SharedGroupParams gp)
715         : randomSeed(seed)
716         , xAxis(x)
717         , yAxis(y)
718         , negativeHeight(negH)
719         , groupParams(gp)
720     {
721         // At least one of them must be offscreen.
722         DE_ASSERT(xAxis != OffScreenAxisCase::ONSCREEN || yAxis != OffScreenAxisCase::ONSCREEN);
723     }
724 };
725 
726 class OffScreenViewportCase : public vkt::TestCase
727 {
728 public:
729     static constexpr int32_t kFramebufferSize = 32; // Width and Height of framebuffer.
730     static constexpr int32_t kViewportMaxDim =
731         1024; // When generating offscreen coords, use this limit as the negative or positive max coord for X/Y.
732     static constexpr uint32_t kVertexCount = 4u;
733 
734     // Choose a couple of values for the Axis range (X or Y) according to the chosen Axis case.
genAxis(de::Random & rnd,OffScreenAxisCase axisCase)735     static tcu::IVec2 genAxis(de::Random &rnd, OffScreenAxisCase axisCase)
736     {
737         int32_t minVal = 0;
738         int32_t maxVal = 0;
739 
740         if (axisCase == OffScreenAxisCase::ONSCREEN)
741             maxVal = kFramebufferSize - 1;
742         else if (axisCase == OffScreenAxisCase::NEGATIVE_SIDE)
743         {
744             minVal = -kViewportMaxDim;
745             maxVal = -1;
746         }
747         else if (axisCase == OffScreenAxisCase::POSITIVE_SIDE)
748         {
749             minVal = kFramebufferSize + 1;
750             maxVal = kViewportMaxDim;
751         }
752 
753         const auto a = rnd.getInt(minVal, maxVal);
754         const auto b = rnd.getInt(minVal, maxVal);
755 
756         const tcu::IVec2 axisRange(de::min(a, b), de::max(a, b));
757         return axisRange;
758     }
759 
OffScreenViewportCase(tcu::TestContext & testCtx,const std::string & name,const OffScreenParams & params)760     OffScreenViewportCase(tcu::TestContext &testCtx, const std::string &name, const OffScreenParams &params)
761         : vkt::TestCase(testCtx, name)
762         , m_params(params)
763     {
764     }
765 
~OffScreenViewportCase(void)766     virtual ~OffScreenViewportCase(void)
767     {
768     }
769 
770     void initPrograms(vk::SourceCollections &programCollection) const override;
771     TestInstance *createInstance(Context &context) const override;
772     void checkSupport(Context &context) const override;
773 
774 protected:
775     const OffScreenParams m_params;
776 };
777 
778 class OffScreenViewportInstance : public vkt::TestInstance
779 {
780 public:
OffScreenViewportInstance(Context & context,const OffScreenParams & params)781     OffScreenViewportInstance(Context &context, const OffScreenParams &params)
782         : vkt::TestInstance(context)
783         , m_params(params)
784         , m_dynRenderHelper(params.groupParams)
785     {
786     }
787 
~OffScreenViewportInstance(void)788     virtual ~OffScreenViewportInstance(void)
789     {
790     }
791 
792     tcu::TestStatus iterate(void) override;
793 
794 protected:
795     const OffScreenParams m_params;
796     const DynRenderHelper m_dynRenderHelper;
797 };
798 
createInstance(Context & context) const799 TestInstance *OffScreenViewportCase::createInstance(Context &context) const
800 {
801     return new OffScreenViewportInstance(context, m_params);
802 }
803 
checkSupport(Context & context) const804 void OffScreenViewportCase::checkSupport(Context &context) const
805 {
806     if (m_params.groupParams->useDynamicRendering)
807         context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
808 
809     if (m_params.negativeHeight)
810         context.requireDeviceFunctionality("VK_KHR_maintenance1");
811 }
812 
initPrograms(vk::SourceCollections & programCollection) const813 void OffScreenViewportCase::initPrograms(vk::SourceCollections &programCollection) const
814 {
815     std::ostringstream vert;
816     vert << "#version 460\n"
817          << "const int vertexCount = " << kVertexCount << ";\n"
818          << "vec2 positions[vertexCount] = vec2[](\n"
819          << "    vec2(-1.0, -1.0),\n"
820          << "    vec2(-1.0,  1.0),\n"
821          << "    vec2( 1.0, -1.0),\n"
822          << "    vec2( 1.0,  1.0)\n"
823          << ");\n"
824          << "void main (void) { gl_Position = vec4(positions[gl_VertexIndex % vertexCount], 0.0, 1.0); }\n";
825     programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
826 
827     std::ostringstream frag;
828     frag << "#version 460\n"
829          << "layout (location=0) out vec4 outColor;\n"
830          << "void main (void) { outColor = vec4(0.0, 0.0, 1.0, 1.0); }\n";
831     programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
832 }
833 
iterate(void)834 tcu::TestStatus OffScreenViewportInstance::iterate(void)
835 {
836     de::Random rnd(m_params.randomSeed);
837 
838     // Pseudorandomly generate viewport data.
839     const auto xAxis = OffScreenViewportCase::genAxis(rnd, m_params.xAxis);
840     auto yAxis       = OffScreenViewportCase::genAxis(rnd, m_params.yAxis);
841     const auto width = xAxis.y() - xAxis.x() + 1;
842     auto height      = yAxis.y() - yAxis.x() + 1;
843 
844     if (m_params.negativeHeight)
845     {
846         height = -height;
847         std::swap(yAxis[0], yAxis[1]);
848     }
849 
850     const VkViewport testViewport = {
851         static_cast<float>(xAxis.x()), // float x;
852         static_cast<float>(yAxis.x()), // float y;
853         static_cast<float>(width),     // float width;
854         static_cast<float>(height),    // float height;
855         0.0f,                          // float minDepth;
856         1.0f,                          // float maxDepth;
857     };
858 
859     // Framebuffer parameters.
860     const auto kIFbSize = OffScreenViewportCase::kFramebufferSize;
861     const auto fbSize   = static_cast<uint32_t>(kIFbSize);
862     const auto fbExtent = makeExtent3D(fbSize, fbSize, 1u);
863     const auto fbFormat = VK_FORMAT_R8G8B8A8_UNORM;
864     const auto fbUsage =
865         (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
866 
867     const auto &ctx = m_context.getContextCommonData();
868     CommandPoolWithBuffer cmd(ctx.vkd, ctx.device, ctx.qfIndex);
869     ImageWithBuffer colorRes(ctx.vkd, ctx.device, ctx.allocator, fbExtent, fbFormat, fbUsage, VK_IMAGE_TYPE_2D);
870 
871     const auto &binaries  = m_context.getBinaryCollection();
872     const auto vertModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
873     const auto fragModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
874 
875     // Render pass and framebuffer.
876     const auto renderPass =
877         makeRenderPass(ctx.vkd, ctx.device, fbFormat, VK_FORMAT_UNDEFINED /* DS format */, VK_ATTACHMENT_LOAD_OP_LOAD);
878     const auto framebuffer = makeFramebuffer(ctx.vkd, ctx.device, renderPass.get(), colorRes.getImageView(),
879                                              fbExtent.width, fbExtent.height);
880 
881     // Pipeline.
882     const VkPipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = initVulkanStructure();
883 
884     const std::vector<VkViewport> viewports(1u, testViewport);
885     const std::vector<VkRect2D> scissors(1u, makeRect2D(fbExtent));
886 
887     void *pNext = DE_NULL;
888 #ifndef CTS_USES_VULKANSC
889     vk::VkPipelineRenderingCreateInfoKHR renderingCreateInfo{vk::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
890                                                              DE_NULL,
891                                                              0u,
892                                                              1u,
893                                                              &fbFormat,
894                                                              vk::VK_FORMAT_UNDEFINED,
895                                                              vk::VK_FORMAT_UNDEFINED};
896     if (m_params.groupParams->useDynamicRendering)
897         pNext = &renderingCreateInfo;
898 #endif
899 
900     const auto pipelineLayout = makePipelineLayout(ctx.vkd, ctx.device);
901     const auto pipelineRP     = (m_params.groupParams->useDynamicRendering ? VK_NULL_HANDLE : renderPass.get());
902     const auto pipeline       = makeGraphicsPipeline(
903         ctx.vkd, ctx.device, pipelineLayout.get(), vertModule.get(), VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE,
904         fragModule.get(), pipelineRP, viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 0u, 0u,
905         &vertexInputStateCreateInfo, DE_NULL, DE_NULL, DE_NULL, DE_NULL, DE_NULL, pNext);
906 
907     const auto cmdBuffer = cmd.cmdBuffer.get();
908     const auto secCmdBufferPtr =
909         (m_params.groupParams->useSecondaryCmdBuffer ?
910              allocateCommandBuffer(ctx.vkd, ctx.device, cmd.cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_SECONDARY) :
911              Move<VkCommandBuffer>());
912     const auto secCmdBuffer  = secCmdBufferPtr.get();
913     const auto clearColor    = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
914     const auto clearColorVal = makeClearValueColorVec4(clearColor);
915     const auto colorSRR      = makeDefaultImageSubresourceRange();
916 
917     // Draw (offscreen due to the viewport).
918     beginCommandBuffer(ctx.vkd, cmdBuffer);
919 
920     // Clear color image outside render pass.
921     const auto preClearBarrier =
922         makeImageMemoryBarrier(0u, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
923                                VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, colorRes.getImage(), colorSRR);
924     cmdPipelineImageMemoryBarrier(ctx.vkd, cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
925                                   &preClearBarrier);
926 
927     ctx.vkd.cmdClearColorImage(cmdBuffer, colorRes.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
928                                &clearColorVal.color, 1u, &colorSRR);
929 
930     const auto postClearBarrier = makeImageMemoryBarrier(
931         VK_ACCESS_TRANSFER_WRITE_BIT, (VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT),
932         VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, colorRes.getImage(), colorSRR);
933     cmdPipelineImageMemoryBarrier(ctx.vkd, cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
934                                   VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, &postClearBarrier);
935 
936     // Render pass.
937     if (!m_params.groupParams->useDynamicRendering)
938     {
939         beginRenderPass(ctx.vkd, cmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u));
940         ctx.vkd.cmdBindPipeline(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
941         ctx.vkd.cmdDraw(cmdBuffer, OffScreenViewportCase::kVertexCount, 1u, 0u, 0u);
942         endRenderPass(ctx.vkd, cmdBuffer);
943     }
944     else
945     {
946 #ifndef CTS_USES_VULKANSC
947         const bool secondary              = m_params.groupParams->useSecondaryCmdBuffer;
948         const bool allInSecondary         = m_params.groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass;
949         const auto beginEndCmdBuffer      = (allInSecondary ? secCmdBuffer : cmdBuffer);
950         const auto rpContentsCmdBuffer    = (secondary ? secCmdBuffer : cmdBuffer);
951         const auto endAndExecuteSecondary = [&cmdBuffer, &secCmdBuffer, &ctx](void)
952         {
953             endCommandBuffer(ctx.vkd, secCmdBuffer);
954             ctx.vkd.cmdExecuteCommands(cmdBuffer, 1u, &secCmdBuffer);
955         };
956 
957         if (secondary)
958             m_dynRenderHelper.beginSecondaryCmdBuffer(ctx.vkd, secCmdBuffer, fbFormat);
959 
960         m_dynRenderHelper.beginRendering(ctx.vkd, beginEndCmdBuffer, !allInSecondary /*isPrimary*/,
961                                          colorRes.getImageView(), scissors.at(0), clearColorVal,
962                                          VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
963         ctx.vkd.cmdBindPipeline(rpContentsCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.get());
964         ctx.vkd.cmdDraw(rpContentsCmdBuffer, OffScreenViewportCase::kVertexCount, 1u, 0u, 0u);
965         if (secondary && !allInSecondary)
966             endAndExecuteSecondary();
967         endRendering(ctx.vkd, beginEndCmdBuffer);
968 
969         if (secondary && allInSecondary)
970             endAndExecuteSecondary();
971 #else
972         DE_UNREF(secCmdBuffer);
973         DE_ASSERT(false);
974 #endif // CTS_USES_VULKANSC
975     }
976 
977     // Copy to results buffer.
978     copyImageToBuffer(ctx.vkd, cmdBuffer, colorRes.getImage(), colorRes.getBuffer(), tcu::IVec2(kIFbSize, kIFbSize),
979                       VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 1u,
980                       VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_COLOR_BIT,
981                       VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
982 
983     endCommandBuffer(ctx.vkd, cmdBuffer);
984     submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
985 
986     // Verify color buffer.
987     invalidateAlloc(ctx.vkd, ctx.device, colorRes.getBufferAllocation());
988 
989     const tcu::ConstPixelBufferAccess resultAccess(mapVkFormat(fbFormat), tcu::IVec3(kIFbSize, kIFbSize, 1),
990                                                    colorRes.getBufferAllocation().getHostPtr());
991     auto &log = m_context.getTestContext().getLog();
992     const tcu::Vec4 threshold(0.0f, 0.0f, 0.0f, 0.0f);
993 
994     if (!tcu::floatThresholdCompare(log, "Result", "", clearColor, resultAccess, threshold, tcu::COMPARE_LOG_ON_ERROR))
995         return tcu::TestStatus::fail("Unexpected color result; check log for details");
996 
997     return tcu::TestStatus::pass("Pass");
998 }
999 
1000 } // namespace
1001 
createNegativeViewportHeightTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)1002 tcu::TestCaseGroup *createNegativeViewportHeightTests(tcu::TestContext &testCtx, const SharedGroupParams groupParams)
1003 {
1004     SubGroupParams subGroupParams{false, groupParams};
1005     // Negative viewport height (VK_KHR_maintenance1)
1006     return createTestGroup(testCtx, "negative_viewport_height", populateTestGroup, subGroupParams);
1007 }
1008 
createZeroViewportHeightTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)1009 tcu::TestCaseGroup *createZeroViewportHeightTests(tcu::TestContext &testCtx, const SharedGroupParams groupParams)
1010 {
1011     SubGroupParams subGroupParams{false, groupParams};
1012     // Zero viewport height (VK_KHR_maintenance1)
1013     return createTestGroup(testCtx, "zero_viewport_height", populateTestGroup, subGroupParams);
1014 }
1015 
createOffScreenViewportTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)1016 tcu::TestCaseGroup *createOffScreenViewportTests(tcu::TestContext &testCtx, const SharedGroupParams groupParams)
1017 {
1018     using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
1019 
1020     const struct
1021     {
1022         const OffScreenAxisCase axisCase;
1023         const char *suffix;
1024     } axisCases[] = {
1025         {OffScreenAxisCase::ONSCREEN, "_on_screen"},
1026         {OffScreenAxisCase::NEGATIVE_SIDE, "_off_screen_negative"},
1027         {OffScreenAxisCase::POSITIVE_SIDE, "_off_screen_positive"},
1028     };
1029 
1030     const struct
1031     {
1032         const bool negativeHeight;
1033         const char *suffix;
1034     } negativeHeightCases[] = {
1035         {false, ""},
1036         {true, "_negative_height"},
1037     };
1038 
1039     uint32_t seed = 1674229780;
1040     // Test using off-screen viewports
1041     GroupPtr group(new tcu::TestCaseGroup(testCtx, "offscreen_viewport"));
1042 
1043     for (const auto &xCase : axisCases)
1044         for (const auto &yCase : axisCases)
1045         {
1046             // At least one of the axis has to be offscreen for the framebuffer to remain clear.
1047             if (xCase.axisCase == OffScreenAxisCase::ONSCREEN && yCase.axisCase == OffScreenAxisCase::ONSCREEN)
1048                 continue;
1049 
1050             for (const auto &negHeightCase : negativeHeightCases)
1051             {
1052                 OffScreenParams params(seed, xCase.axisCase, yCase.axisCase, negHeightCase.negativeHeight, groupParams);
1053                 ++seed;
1054 
1055                 const auto testName = std::string("x") + xCase.suffix + "_y" + yCase.suffix + negHeightCase.suffix;
1056                 group->addChild(new OffScreenViewportCase(testCtx, testName, params));
1057             }
1058         }
1059     return group.release();
1060 }
1061 
1062 } // namespace Draw
1063 } // namespace vkt
1064