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 35#include "tcuVector.hpp" 36#include "tcuTextureUtil.hpp" 37#include "tcuImageCompare.hpp" 38 39#include "deSharedPtr.hpp" 40 41namespace vkt 42{ 43namespace Draw 44{ 45namespace 46{ 47using namespace vk; 48using tcu::Vec4; 49using de::SharedPtr; 50using de::MovePtr; 51 52enum Constants 53{ 54 WIDTH = 256, 55 HEIGHT = WIDTH/2, 56}; 57 58struct TestParams 59{ 60 VkFrontFace frontFace; 61 VkCullModeFlagBits cullMode; 62}; 63 64class NegativeViewportHeightTestInstance : public TestInstance 65{ 66public: 67 NegativeViewportHeightTestInstance (Context& context, const TestParams& params); 68 tcu::TestStatus iterate (void); 69 tcu::ConstPixelBufferAccess draw (const VkViewport viewport); 70 MovePtr<tcu::TextureLevel> generateReferenceImage (void) const; 71 bool isCulled (const VkFrontFace triangleFace) const; 72 73private: 74 const TestParams m_params; 75 const VkFormat m_colorAttachmentFormat; 76 SharedPtr<Image> m_colorTargetImage; 77 Move<VkImageView> m_colorTargetView; 78 SharedPtr<Buffer> m_vertexBuffer; 79 Move<VkRenderPass> m_renderPass; 80 Move<VkFramebuffer> m_framebuffer; 81 Move<VkPipelineLayout> m_pipelineLayout; 82 Move<VkPipeline> m_pipeline; 83}; 84 85NegativeViewportHeightTestInstance::NegativeViewportHeightTestInstance (Context& context, const TestParams& params) 86 : TestInstance (context) 87 , m_params (params) 88 , m_colorAttachmentFormat (VK_FORMAT_R8G8B8A8_UNORM) 89{ 90 const DeviceInterface& vk = m_context.getDeviceInterface(); 91 const VkDevice device = m_context.getDevice(); 92 93 // Vertex data 94 { 95 std::vector<Vec4> vertexData; 96 97 // CCW triangle 98 vertexData.push_back(Vec4(-0.8f, -0.6f, 0.0f, 1.0f)); // 0-----2 99 vertexData.push_back(Vec4(-0.8f, 0.6f, 0.0f, 1.0f)); // | / 100 vertexData.push_back(Vec4(-0.2f, -0.6f, 0.0f, 1.0f)); // 1|/ 101 102 // CW triangle 103 vertexData.push_back(Vec4( 0.2f, -0.6f, 0.0f, 1.0f)); // 0-----1 104 vertexData.push_back(Vec4( 0.8f, -0.6f, 0.0f, 1.0f)); // \ | 105 vertexData.push_back(Vec4( 0.8f, 0.6f, 0.0f, 1.0f)); // \|2 106 107 const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4); 108 m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), 109 m_context.getDefaultAllocator(), MemoryRequirement::HostVisible); 110 111 deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize)); 112 flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(), m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE); 113 } 114 115 // Render pass 116 { 117 const VkExtent3D targetImageExtent = { WIDTH, HEIGHT, 1 }; 118 const VkImageUsageFlags targetImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; 119 120 const ImageCreateInfo targetImageCreateInfo( 121 VK_IMAGE_TYPE_2D, // imageType, 122 m_colorAttachmentFormat, // format, 123 targetImageExtent, // extent, 124 1u, // mipLevels, 125 1u, // arrayLayers, 126 VK_SAMPLE_COUNT_1_BIT, // samples, 127 VK_IMAGE_TILING_OPTIMAL, // tiling, 128 targetImageUsageFlags); // usage, 129 130 m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator()); 131 132 RenderPassCreateInfo renderPassCreateInfo; 133 renderPassCreateInfo.addAttachment(AttachmentDescription( 134 m_colorAttachmentFormat, // format 135 VK_SAMPLE_COUNT_1_BIT, // samples 136 VK_ATTACHMENT_LOAD_OP_LOAD, // loadOp 137 VK_ATTACHMENT_STORE_OP_STORE, // storeOp 138 VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp 139 VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp 140 VK_IMAGE_LAYOUT_GENERAL, // initialLayout 141 VK_IMAGE_LAYOUT_GENERAL)); // finalLayout 142 143 const VkAttachmentReference colorAttachmentReference = 144 { 145 0u, 146 VK_IMAGE_LAYOUT_GENERAL 147 }; 148 149 renderPassCreateInfo.addSubpass(SubpassDescription( 150 VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint 151 (VkSubpassDescriptionFlags)0, // flags 152 0u, // inputAttachmentCount 153 DE_NULL, // inputAttachments 154 1u, // colorAttachmentCount 155 &colorAttachmentReference, // colorAttachments 156 DE_NULL, // resolveAttachments 157 AttachmentReference(), // depthStencilAttachment 158 0u, // preserveAttachmentCount 159 DE_NULL)); // preserveAttachments 160 161 m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo); 162 } 163 164 // Framebuffer 165 { 166 const ImageViewCreateInfo colorTargetViewInfo (m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat); 167 m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo); 168 169 std::vector<VkImageView> colorAttachments(1); 170 colorAttachments[0] = *m_colorTargetView; 171 172 const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1); 173 m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo); 174 } 175 176 // Vertex input 177 178 const VkVertexInputBindingDescription vertexInputBindingDescription = 179 { 180 0u, // uint32_t binding; 181 sizeof(Vec4), // uint32_t stride; 182 VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate; 183 }; 184 185 const VkVertexInputAttributeDescription vertexInputAttributeDescription = 186 { 187 0u, // uint32_t location; 188 0u, // uint32_t binding; 189 VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; 190 0u // uint32_t offset; 191 }; 192 193 const PipelineCreateInfo::VertexInputState vertexInputState = PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription, 194 1, &vertexInputAttributeDescription); 195 196 // Graphics pipeline 197 198 const VkRect2D scissor = 199 { 200 { 0, 0 }, // x, y 201 { WIDTH, HEIGHT }, // width, height 202 }; 203 204 std::vector<VkDynamicState> dynamicStates; 205 dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT); 206 207 const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0)); 208 const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0)); 209 210 const PipelineLayoutCreateInfo pipelineLayoutCreateInfo; 211 m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo); 212 213 const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState; 214 215 PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0); 216 pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vertexModule, "main", VK_SHADER_STAGE_VERTEX_BIT)); 217 pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT)); 218 pipelineCreateInfo.addState (PipelineCreateInfo::VertexInputState (vertexInputState)); 219 pipelineCreateInfo.addState (PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)); 220 pipelineCreateInfo.addState (PipelineCreateInfo::ColorBlendState (1, &colorBlendAttachmentState)); 221 pipelineCreateInfo.addState (PipelineCreateInfo::ViewportState (1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor))); 222 pipelineCreateInfo.addState (PipelineCreateInfo::DepthStencilState ()); 223 pipelineCreateInfo.addState (PipelineCreateInfo::RasterizerState ( 224 VK_FALSE, // depthClampEnable 225 VK_FALSE, // rasterizerDiscardEnable 226 VK_POLYGON_MODE_FILL, // polygonMode 227 m_params.cullMode, // cullMode 228 m_params.frontFace, // frontFace 229 VK_FALSE, // depthBiasEnable 230 0.0f, // depthBiasConstantFactor 231 0.0f, // depthBiasClamp 232 0.0f, // depthBiasSlopeFactor 233 1.0f)); // lineWidth 234 pipelineCreateInfo.addState (PipelineCreateInfo::MultiSampleState ()); 235 pipelineCreateInfo.addState (PipelineCreateInfo::DynamicState (dynamicStates)); 236 237 m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo); 238} 239 240tcu::ConstPixelBufferAccess NegativeViewportHeightTestInstance::draw (const VkViewport viewport) 241{ 242 const DeviceInterface& vk = m_context.getDeviceInterface(); 243 const VkDevice device = m_context.getDevice(); 244 const VkQueue queue = m_context.getUniversalQueue(); 245 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); 246 247 // Command buffer 248 249 const CmdPoolCreateInfo cmdPoolCreateInfo (queueFamilyIndex); 250 const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, &cmdPoolCreateInfo)); 251 252 const VkCommandBufferAllocateInfo cmdBufferAllocateInfo = 253 { 254 VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, // VkStructureType sType; 255 DE_NULL, // const void* pNext; 256 *cmdPool, // VkCommandPool commandPool; 257 VK_COMMAND_BUFFER_LEVEL_PRIMARY, // VkCommandBufferLevel level; 258 1u, // deUint32 bufferCount; 259 }; 260 const Unique<VkCommandBuffer> cmdBuffer(allocateCommandBuffer(vk, device, &cmdBufferAllocateInfo)); 261 262 // Draw 263 264 { 265 const CmdBufferBeginInfo beginInfo; 266 vk.beginCommandBuffer(*cmdBuffer, &beginInfo); 267 } 268 269 vk.cmdSetViewport(*cmdBuffer, 0u, 1u, &viewport); 270 271 { 272 const VkClearColorValue clearColor = makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f).color; 273 const ImageSubresourceRange subresourceRange (VK_IMAGE_ASPECT_COLOR_BIT); 274 275 initialTransitionColor2DImage(vk, *cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL); 276 vk.cmdClearColorImage(*cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange); 277 } 278 { 279 const VkMemoryBarrier memBarrier = 280 { 281 VK_STRUCTURE_TYPE_MEMORY_BARRIER, // VkStructureType sType; 282 DE_NULL, // const void* pNext; 283 VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; 284 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT // VkAccessFlags dstAccessMask; 285 }; 286 287 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL); 288 } 289 { 290 const VkRect2D renderArea = { { 0, 0 }, { WIDTH, HEIGHT } }; 291 const RenderPassBeginInfo renderPassBegin (*m_renderPass, *m_framebuffer, renderArea); 292 293 vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBegin, VK_SUBPASS_CONTENTS_INLINE); 294 } 295 { 296 const VkDeviceSize offset = 0; 297 const VkBuffer buffer = m_vertexBuffer->object(); 298 299 vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &offset); 300 } 301 302 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline); 303 vk.cmdDraw(*cmdBuffer, 6, 1, 0, 0); 304 vk.cmdEndRenderPass(*cmdBuffer); 305 vk.endCommandBuffer(*cmdBuffer); 306 307 // Submit 308 { 309 const VkFenceCreateInfo fenceInfo = 310 { 311 VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, // VkStructureType sType; 312 DE_NULL, // const void* pNext; 313 (VkFenceCreateFlags)0, // VkFenceCreateFlags flags; 314 }; 315 const Unique<VkFence> fence (createFence(vk, device, &fenceInfo)); 316 const VkSubmitInfo submitInfo = 317 { 318 VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; 319 DE_NULL, // const void* pNext; 320 0, // uint32_t waitSemaphoreCount; 321 DE_NULL, // const VkSemaphore* pWaitSemaphores; 322 (const VkPipelineStageFlags*)DE_NULL, // const VkPipelineStageFlags* pWaitDstStageMask; 323 1, // uint32_t commandBufferCount; 324 &cmdBuffer.get(), // const VkCommandBuffer* pCommandBuffers; 325 0, // uint32_t signalSemaphoreCount; 326 DE_NULL // const VkSemaphore* pSignalSemaphores; 327 }; 328 329 VK_CHECK(vk.queueSubmit(queue, 1, &submitInfo, *fence)); 330 VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), VK_TRUE, ~0ull)); 331 } 332 333 // Get result 334 { 335 const VkOffset3D zeroOffset = { 0, 0, 0 }; 336 return m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, VK_IMAGE_ASPECT_COLOR_BIT); 337 } 338} 339 340//! Determine if a triangle with triangleFace orientation will be culled or not 341bool NegativeViewportHeightTestInstance::isCulled (const VkFrontFace triangleFace) const 342{ 343 const bool isFrontFacing = (triangleFace == m_params.frontFace); 344 345 if (m_params.cullMode == VK_CULL_MODE_FRONT_BIT && isFrontFacing) 346 return true; 347 if (m_params.cullMode == VK_CULL_MODE_BACK_BIT && !isFrontFacing) 348 return true; 349 350 return m_params.cullMode == VK_CULL_MODE_FRONT_AND_BACK; 351} 352 353MovePtr<tcu::TextureLevel> NegativeViewportHeightTestInstance::generateReferenceImage (void) const 354{ 355 DE_ASSERT(HEIGHT == WIDTH/2); 356 357 MovePtr<tcu::TextureLevel> image (new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), WIDTH, HEIGHT)); 358 const tcu::PixelBufferAccess access (image->getAccess()); 359 const Vec4 black (0.0f, 0.0f, 0.0f, 1.0f); 360 const Vec4 white (1.0f); 361 const Vec4 gray (0.5f, 0.5f, 0.5f, 1.0f); 362 363 tcu::clear(access, black); 364 365 const int p1 = static_cast<int>(static_cast<float>(HEIGHT) * (1.0f - 0.6f) / 2.0f); 366 const int p2 = p1 + static_cast<int>(static_cast<float>(HEIGHT) * (2.0f * 0.6f) / 2.0f); 367 368 // left triangle (CCW -> CW after y-flip) 369 if (!isCulled(VK_FRONT_FACE_CLOCKWISE)) 370 { 371 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_CLOCKWISE ? white : gray); 372 373 for (int y = p1; y <= p2; ++y) 374 for (int x = p1; x < y; ++x) 375 access.setPixel(color, x, y); 376 } 377 378 // right triangle (CW -> CCW after y-flip) 379 if (!isCulled(VK_FRONT_FACE_COUNTER_CLOCKWISE)) 380 { 381 const Vec4& color = (m_params.frontFace == VK_FRONT_FACE_COUNTER_CLOCKWISE ? white : gray); 382 383 for (int y = p1; y <= p2; ++y) 384 for (int x = WIDTH - y; x < p2 + HEIGHT; ++x) 385 access.setPixel(color, x, y); 386 } 387 388 return image; 389} 390 391std::string getCullModeStr (const VkCullModeFlagBits cullMode) 392{ 393 // Cull mode flags are a bit special, because there's a meaning to 0 and or'ed flags. 394 // The function getCullModeFlagsStr() doesn't work too well in this case. 395 396 switch (cullMode) 397 { 398 case VK_CULL_MODE_NONE: return "VK_CULL_MODE_NONE"; 399 case VK_CULL_MODE_FRONT_BIT: return "VK_CULL_MODE_FRONT_BIT"; 400 case VK_CULL_MODE_BACK_BIT: return "VK_CULL_MODE_BACK_BIT"; 401 case VK_CULL_MODE_FRONT_AND_BACK: return "VK_CULL_MODE_FRONT_AND_BACK"; 402 403 default: 404 DE_ASSERT(0); 405 return std::string(); 406 } 407} 408 409tcu::TestStatus NegativeViewportHeightTestInstance::iterate (void) 410{ 411 // Check requirements 412 413 if (!de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), std::string("VK_KHR_maintenance1"))) 414 TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_maintenance1"); 415 416 // Set up the viewport and draw 417 418 const VkViewport viewport = 419 { 420 0.0f, // float x; 421 static_cast<float>(HEIGHT), // float y; 422 static_cast<float>(WIDTH), // float width; 423 -static_cast<float>(HEIGHT), // float height; 424 0.0f, // float minDepth; 425 1.0f, // float maxDepth; 426 }; 427 428 const tcu::ConstPixelBufferAccess resultImage = draw(viewport); 429 430 // Verify the results 431 432 tcu::TestLog& log = m_context.getTestContext().getLog(); 433 MovePtr<tcu::TextureLevel> referenceImage = generateReferenceImage(); 434 435 log << tcu::TestLog::Message 436 << "Drawing two triangles with negative viewport height, which will cause a y-flip. This changes the sign of the triangle's area." 437 << tcu::TestLog::EndMessage; 438 log << tcu::TestLog::Message 439 << "After the flip, the triangle on the left is CW and the triangle on the right is CCW. Right angles of the both triangles should be at the bottom of the image." 440 << " Front face is white, back face is gray." 441 << tcu::TestLog::EndMessage; 442 log << tcu::TestLog::Message 443 << "Front face: " << getFrontFaceName(m_params.frontFace) << "\n" 444 << "Cull mode: " << getCullModeStr (m_params.cullMode) << "\n" 445 << tcu::TestLog::EndMessage; 446 447 if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT)) 448 return tcu::TestStatus::fail("Rendered image is incorrect"); 449 else 450 return tcu::TestStatus::pass("Pass"); 451} 452 453class NegativeViewportHeightTest : public TestCase 454{ 455public: 456 NegativeViewportHeightTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) 457 : TestCase (testCtx, name, description) 458 , m_params (params) 459 { 460 } 461 462 void initPrograms (SourceCollections& programCollection) const 463 { 464 // Vertex shader 465 { 466 std::ostringstream src; 467 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 468 << "\n" 469 << "layout(location = 0) in vec4 in_position;\n" 470 << "\n" 471 << "out gl_PerVertex {\n" 472 << " vec4 gl_Position;\n" 473 << "};\n" 474 << "\n" 475 << "void main(void)\n" 476 << "{\n" 477 << " gl_Position = in_position;\n" 478 << "}\n"; 479 480 programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); 481 } 482 483 // Fragment shader 484 { 485 std::ostringstream src; 486 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" 487 << "\n" 488 << "layout(location = 0) out vec4 out_color;\n" 489 << "\n" 490 << "void main(void)\n" 491 << "{\n" 492 << " if (gl_FrontFacing)\n" 493 << " out_color = vec4(1.0);\n" 494 << " else\n" 495 << " out_color = vec4(vec3(0.5), 1.0);\n" 496 << "}\n"; 497 498 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); 499 } 500 } 501 502 virtual TestInstance* createInstance (Context& context) const 503 { 504 return new NegativeViewportHeightTestInstance(context, m_params); 505 } 506 507private: 508 const TestParams m_params; 509}; 510 511void populateTestGroup (tcu::TestCaseGroup* testGroup) 512{ 513 const struct 514 { 515 const char* const name; 516 VkFrontFace frontFace; 517 } frontFace[] = 518 { 519 { "front_ccw", VK_FRONT_FACE_COUNTER_CLOCKWISE }, 520 { "front_cw", VK_FRONT_FACE_CLOCKWISE }, 521 }; 522 523 const struct 524 { 525 const char* const name; 526 VkCullModeFlagBits cullMode; 527 } cullMode[] = 528 { 529 { "cull_none", VK_CULL_MODE_NONE }, 530 { "cull_front", VK_CULL_MODE_FRONT_BIT }, 531 { "cull_back", VK_CULL_MODE_BACK_BIT }, 532 { "cull_both", VK_CULL_MODE_FRONT_AND_BACK }, 533 }; 534 535 for (int ndxFrontFace = 0; ndxFrontFace < DE_LENGTH_OF_ARRAY(frontFace); ++ndxFrontFace) 536 for (int ndxCullMode = 0; ndxCullMode < DE_LENGTH_OF_ARRAY(cullMode); ++ndxCullMode) 537 { 538 const TestParams params = 539 { 540 frontFace[ndxFrontFace].frontFace, 541 cullMode[ndxCullMode].cullMode, 542 }; 543 std::ostringstream name; 544 name << frontFace[ndxFrontFace].name << "_" << cullMode[ndxCullMode].name; 545 546 testGroup->addChild(new NegativeViewportHeightTest(testGroup->getTestContext(), name.str(), "", params)); 547 } 548} 549 550} // anonymous 551 552tcu::TestCaseGroup* createNegativeViewportHeightTests (tcu::TestContext& testCtx) 553{ 554 return createTestGroup(testCtx, "negative_viewport_height", "Negative viewport height (VK_KHR_maintenance1)", populateTestGroup); 555} 556 557} // Draw 558} // vkt 559