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 ¶ms);
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 ¶ms)
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 ¶ms)
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 ¶ms)
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 ¶ms)
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