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 VK_KHR_shader_draw_parameters tests 22 *//*--------------------------------------------------------------------*/ 23 24 #include "vktDrawShaderDrawParametersTests.hpp" 25 26 #include "vktTestCaseUtil.hpp" 27 #include "vktDrawTestCaseUtil.hpp" 28 #include "vktDrawBaseClass.hpp" 29 30 #include "vkQueryUtil.hpp" 31 32 #include "tcuTestLog.hpp" 33 #include "tcuImageCompare.hpp" 34 #include "tcuTextureUtil.hpp" 35 36 namespace vkt 37 { 38 namespace Draw 39 { 40 namespace 41 { 42 43 enum TestFlagBits 44 { 45 TEST_FLAG_INSTANCED = 1u << 0, 46 TEST_FLAG_INDEXED = 1u << 1, 47 TEST_FLAG_INDIRECT = 1u << 2, 48 TEST_FLAG_MULTIDRAW = 1u << 3, //!< multiDrawIndirect 49 TEST_FLAG_FIRST_INSTANCE = 1u << 4, //!< drawIndirectFirstInstance 50 }; 51 typedef deUint32 TestFlags; 52 53 struct FlagsTestSpec : public TestSpecBase 54 { 55 TestFlags flags; 56 }; 57 58 inline FlagsTestSpec addFlags (FlagsTestSpec spec, const TestFlags flags) 59 { 60 spec.flags |= flags; 61 return spec; 62 } 63 64 enum Constants 65 { 66 // \note Data layout in buffers (junk data and good data is intertwined). 67 // Values are largely arbitrary, but we try to avoid "nice" numbers to make sure the test doesn't pass by accident. 68 NUM_VERTICES = 4, //!< number of consecutive good vertices 69 NDX_FIRST_VERTEX = 2, //!< index of first good vertex data 70 NDX_SECOND_VERTEX = 9, //!< index of second good vertex data 71 NDX_FIRST_INDEX = 11, //!< index of a first good index (in index data) 72 NDX_SECOND_INDEX = 17, //!< index of a second good index 73 OFFSET_FIRST_INDEX = 1, //!< offset added to the first index 74 OFFSET_SECOND_INDEX = 4, //!< offset added to the second index 75 MAX_INSTANCE_COUNT = 3, //!< max number of draw instances 76 MAX_INDIRECT_DRAW_COUNT = 3, //!< max drawCount of indirect calls 77 }; 78 79 class DrawTest : public DrawTestsBaseClass 80 { 81 public: 82 typedef FlagsTestSpec TestSpec; 83 DrawTest (Context &context, TestSpec testSpec); 84 tcu::TestStatus iterate (void); 85 86 private: 87 template<typename T, std::size_t N> 88 void setIndirectCommand (const T (&pCmdData)[N]); 89 90 void drawReferenceImage (const tcu::PixelBufferAccess& refImage) const; 91 92 bool isInstanced (void) const { return (m_flags & TEST_FLAG_INSTANCED) != 0; } 93 bool isIndexed (void) const { return (m_flags & TEST_FLAG_INDEXED) != 0; } 94 bool isIndirect (void) const { return (m_flags & TEST_FLAG_INDIRECT) != 0; } 95 bool isMultiDraw (void) const { return (m_flags & TEST_FLAG_MULTIDRAW) != 0; } 96 bool isFirstInstance (void) const { return (m_flags & TEST_FLAG_FIRST_INSTANCE) != 0; } 97 98 const TestFlags m_flags; 99 de::SharedPtr<Buffer> m_indexBuffer; 100 de::SharedPtr<Buffer> m_indirectBuffer; 101 }; 102 103 DrawTest::DrawTest (Context &context, TestSpec testSpec) 104 : DrawTestsBaseClass(context, testSpec.shaders[glu::SHADERTYPE_VERTEX], testSpec.shaders[glu::SHADERTYPE_FRAGMENT], testSpec.topology) 105 , m_flags (testSpec.flags) 106 { 107 DE_ASSERT(m_topology == vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); 108 DE_ASSERT(!isMultiDraw() || isIndirect()); 109 DE_ASSERT(!isFirstInstance() || (isIndirect() && isInstanced())); 110 111 // Requirements 112 { 113 if (!vk::isDeviceExtensionSupported(context.getUsedApiVersion(), context.getDeviceExtensions(), "VK_KHR_shader_draw_parameters")) 114 TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_shader_draw_parameters"); 115 116 // Shader draw parameters is part of Vulkan 1.1 but is optional 117 if ( context.contextSupports(vk::ApiVersion(1, 1, 0)) ) 118 { 119 // Check if shader draw parameters is supported on the physical device. 120 vk::VkPhysicalDeviceShaderDrawParameterFeatures drawParameters = 121 { 122 vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES, // sType 123 DE_NULL, // pNext 124 VK_FALSE // shaderDrawParameters 125 }; 126 vk::VkPhysicalDeviceFeatures features; 127 deMemset(&features, 0, sizeof(vk::VkPhysicalDeviceFeatures)); 128 129 vk::VkPhysicalDeviceFeatures2 featuresExt = 130 { 131 vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, // sType 132 &drawParameters, // pNext 133 features 134 }; 135 136 context.getInstanceInterface().getPhysicalDeviceFeatures2(context.getPhysicalDevice(), &featuresExt); 137 138 if (drawParameters.shaderDrawParameters == VK_FALSE) 139 TCU_THROW(NotSupportedError, "shaderDrawParameters feature not supported by the device"); 140 } 141 142 if (isMultiDraw() && !m_context.getDeviceFeatures().multiDrawIndirect) 143 TCU_THROW(NotSupportedError, "Missing feature: multiDrawIndirect"); 144 145 if (isFirstInstance() && !m_context.getDeviceFeatures().drawIndirectFirstInstance) 146 TCU_THROW(NotSupportedError, "Missing feature: drawIndirectFirstInstance"); 147 } 148 149 // Vertex data 150 { 151 int refIndex = NDX_FIRST_VERTEX - OFFSET_FIRST_INDEX; 152 153 m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 154 m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 155 156 if (!isIndexed()) 157 refIndex = 0; 158 159 m_data.push_back(VertexElementData(tcu::Vec4(-0.3f, -0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 160 m_data.push_back(VertexElementData(tcu::Vec4(-0.3f, 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 161 m_data.push_back(VertexElementData(tcu::Vec4( 0.3f, -0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 162 m_data.push_back(VertexElementData(tcu::Vec4( 0.3f, 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 163 164 m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 165 m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 166 m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 167 168 if (!isIndexed()) 169 refIndex = 0; 170 171 m_data.push_back(VertexElementData(tcu::Vec4(-0.3f, -0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 172 m_data.push_back(VertexElementData(tcu::Vec4(-0.3f, 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 173 m_data.push_back(VertexElementData(tcu::Vec4( 0.3f, -0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 174 m_data.push_back(VertexElementData(tcu::Vec4( 0.3f, 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++)); 175 176 m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 177 m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1)); 178 179 // Make sure constants are up to date 180 DE_ASSERT(m_data.size() == NDX_SECOND_VERTEX + NUM_VERTICES + 2); 181 DE_ASSERT(NDX_SECOND_VERTEX - NDX_FIRST_VERTEX - NUM_VERTICES == 3); 182 } 183 184 if (isIndirect()) 185 { 186 const std::size_t indirectBufferSize = MAX_INDIRECT_DRAW_COUNT * 32; // space for COUNT commands plus some gratuitous padding 187 m_indirectBuffer = Buffer::createAndAlloc(m_vk, m_context.getDevice(), BufferCreateInfo(indirectBufferSize, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT), 188 m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible); 189 190 deMemset(m_indirectBuffer->getBoundMemory().getHostPtr(), 0, indirectBufferSize); 191 vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indirectBuffer->getBoundMemory().getMemory(), m_indirectBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE); 192 } 193 194 if (isIndexed()) 195 { 196 DE_ASSERT(NDX_FIRST_INDEX + NUM_VERTICES <= NDX_SECOND_INDEX); 197 const std::size_t indexBufferSize = sizeof(deUint32) * (NDX_SECOND_INDEX + NUM_VERTICES); 198 m_indexBuffer = Buffer::createAndAlloc(m_vk, m_context.getDevice(), BufferCreateInfo(indexBufferSize, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT), 199 m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible); 200 deUint32* indices = static_cast<deUint32*>(m_indexBuffer->getBoundMemory().getHostPtr()); 201 202 deMemset(indices, 0, indexBufferSize); 203 204 for (int i = 0; i < NUM_VERTICES; i++) 205 { 206 indices[NDX_FIRST_INDEX + i] = static_cast<deUint32>(NDX_FIRST_VERTEX + i) - OFFSET_FIRST_INDEX; 207 indices[NDX_SECOND_INDEX + i] = static_cast<deUint32>(NDX_SECOND_VERTEX + i) - OFFSET_SECOND_INDEX; 208 } 209 210 vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indexBuffer->getBoundMemory().getMemory(), m_indexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE); 211 } 212 213 initialize(); 214 } 215 216 template<typename T, std::size_t N> 217 void DrawTest::setIndirectCommand (const T (&pCmdData)[N]) 218 { 219 DE_ASSERT(N != 0 && N <= MAX_INDIRECT_DRAW_COUNT); 220 221 const std::size_t dataSize = N * sizeof(T); 222 223 deMemcpy(m_indirectBuffer->getBoundMemory().getHostPtr(), pCmdData, dataSize); 224 vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indirectBuffer->getBoundMemory().getMemory(), m_indirectBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE); 225 } 226 227 //! This function must be kept in sync with the shader. 228 void DrawTest::drawReferenceImage (const tcu::PixelBufferAccess& refImage) const 229 { 230 using tcu::Vec2; 231 using tcu::Vec4; 232 using tcu::IVec4; 233 234 const Vec2 perInstanceOffset[] = { Vec2(0.0f, 0.0f), Vec2(-0.3f, 0.0f), Vec2(0.0f, 0.3f) }; 235 const Vec2 perDrawOffset[] = { Vec2(0.0f, 0.0f), Vec2(-0.3f, -0.3f), Vec2(0.3f, 0.3f) }; 236 const Vec4 allColors[] = { Vec4(1.0f), Vec4(0.0f, 0.0f, 1.0f, 1.0f), Vec4(0.0f, 1.0f, 0.0f, 1.0f) }; 237 const int numInstances = isInstanced() ? MAX_INSTANCE_COUNT : 1; 238 const int numIndirectDraws = isMultiDraw() ? MAX_INDIRECT_DRAW_COUNT : 1; 239 const int rectWidth = static_cast<int>(WIDTH * 0.6f / 2.0f); 240 const int rectHeight = static_cast<int>(HEIGHT * 0.6f / 2.0f); 241 242 DE_ASSERT(DE_LENGTH_OF_ARRAY(perInstanceOffset) >= numInstances); 243 DE_ASSERT(DE_LENGTH_OF_ARRAY(allColors) >= numInstances && DE_LENGTH_OF_ARRAY(allColors) >= numIndirectDraws); 244 DE_ASSERT(DE_LENGTH_OF_ARRAY(perDrawOffset) >= numIndirectDraws); 245 246 tcu::clear(refImage, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f)); 247 248 for (int drawNdx = 0; drawNdx < numIndirectDraws; ++drawNdx) 249 for (int instanceNdx = 0; instanceNdx < numInstances; ++instanceNdx) 250 { 251 const Vec2 offset = perInstanceOffset[instanceNdx] + perDrawOffset[drawNdx]; 252 const Vec4& color = allColors[isMultiDraw() ? drawNdx : instanceNdx]; 253 int x = static_cast<int>(WIDTH * (1.0f - 0.3f + offset.x()) / 2.0f); 254 int y = static_cast<int>(HEIGHT * (1.0f - 0.3f + offset.y()) / 2.0f); 255 256 tcu::clear(tcu::getSubregion(refImage, x, y, rectWidth, rectHeight), color); 257 } 258 } 259 260 tcu::TestStatus DrawTest::iterate (void) 261 { 262 // Draw 263 { 264 beginRenderPass(); 265 266 const vk::VkDeviceSize vertexBufferOffset = 0; 267 const vk::VkBuffer vertexBuffer = m_vertexBuffer->object(); 268 269 m_vk.cmdBindVertexBuffers (*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset); 270 m_vk.cmdBindPipeline (*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline); 271 272 if (isIndexed()) 273 m_vk.cmdBindIndexBuffer(*m_cmdBuffer, m_indexBuffer->object(), 0ull, vk::VK_INDEX_TYPE_UINT32); 274 275 const deUint32 numInstances = isInstanced() ? MAX_INSTANCE_COUNT : 1; 276 277 if (isIndirect()) 278 { 279 if (isIndexed()) 280 { 281 const vk::VkDrawIndexedIndirectCommand commands[] = 282 { 283 // indexCount, instanceCount, firstIndex, vertexOffset, firstInstance 284 { NUM_VERTICES, numInstances, NDX_FIRST_INDEX, OFFSET_FIRST_INDEX, (isFirstInstance() ? 2u : 0u) }, 285 { NUM_VERTICES, numInstances, NDX_SECOND_INDEX, OFFSET_SECOND_INDEX, (isFirstInstance() ? 1u : 0u) }, 286 { NUM_VERTICES, numInstances, NDX_FIRST_INDEX, OFFSET_FIRST_INDEX, (isFirstInstance() ? 3u : 0u) }, 287 }; 288 setIndirectCommand(commands); 289 } 290 else 291 { 292 const vk::VkDrawIndirectCommand commands[] = 293 { 294 // vertexCount, instanceCount, firstVertex, firstInstance 295 { NUM_VERTICES, numInstances, NDX_FIRST_VERTEX, (isFirstInstance() ? 2u : 0u) }, 296 { NUM_VERTICES, numInstances, NDX_SECOND_VERTEX, (isFirstInstance() ? 1u : 0u) }, 297 { NUM_VERTICES, numInstances, NDX_FIRST_VERTEX, (isFirstInstance() ? 3u : 0u) }, 298 }; 299 setIndirectCommand(commands); 300 } 301 } 302 303 if (isIndirect()) 304 { 305 const deUint32 numIndirectDraws = isMultiDraw() ? MAX_INDIRECT_DRAW_COUNT : 1; 306 307 if (isIndexed()) 308 m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, m_indirectBuffer->object(), 0ull, numIndirectDraws, sizeof(vk::VkDrawIndexedIndirectCommand)); 309 else 310 m_vk.cmdDrawIndirect(*m_cmdBuffer, m_indirectBuffer->object(), 0ull, numIndirectDraws, sizeof(vk::VkDrawIndirectCommand)); 311 } 312 else 313 { 314 const deUint32 firstInstance = 2; 315 316 if (isIndexed()) 317 m_vk.cmdDrawIndexed(*m_cmdBuffer, NUM_VERTICES, numInstances, NDX_FIRST_INDEX, OFFSET_FIRST_INDEX, firstInstance); 318 else 319 m_vk.cmdDraw(*m_cmdBuffer, NUM_VERTICES, numInstances, NDX_FIRST_VERTEX, firstInstance); 320 } 321 322 m_vk.cmdEndRenderPass(*m_cmdBuffer); 323 m_vk.endCommandBuffer(*m_cmdBuffer); 324 } 325 326 // Submit 327 { 328 const vk::VkQueue queue = m_context.getUniversalQueue(); 329 const vk::VkSubmitInfo submitInfo = 330 { 331 vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; 332 DE_NULL, // const void* pNext; 333 0, // deUint32 waitSemaphoreCount; 334 DE_NULL, // const VkSemaphore* pWaitSemaphores; 335 (const vk::VkPipelineStageFlags*)DE_NULL, 336 1, // deUint32 commandBufferCount; 337 &m_cmdBuffer.get(), // const VkCommandBuffer* pCommandBuffers; 338 0, // deUint32 signalSemaphoreCount; 339 DE_NULL // const VkSemaphore* pSignalSemaphores; 340 }; 341 VK_CHECK(m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL)); 342 VK_CHECK(m_vk.queueWaitIdle(queue)); 343 } 344 345 // Validate 346 { 347 tcu::TextureLevel referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), static_cast<int>(0.5f + WIDTH), static_cast<int>(0.5f + HEIGHT)); 348 349 drawReferenceImage(referenceFrame.getAccess()); 350 351 const vk::VkOffset3D zeroOffset = { 0, 0, 0 }; 352 const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(m_context.getUniversalQueue(), m_context.getDefaultAllocator(), 353 vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT); 354 355 if (!tcu::fuzzyCompare(m_context.getTestContext().getLog(), "Result", "Image comparison result", referenceFrame.getAccess(), renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT)) 356 return tcu::TestStatus::fail("Rendered image is incorrect"); 357 else 358 return tcu::TestStatus::pass("OK"); 359 } 360 } 361 362 void addDrawCase (tcu::TestCaseGroup* group, const DrawTest::TestSpec testSpec, const TestFlags flags) 363 { 364 std::ostringstream name; 365 name << "draw"; 366 367 if (flags & TEST_FLAG_INDEXED) name << "_indexed"; 368 if (flags & TEST_FLAG_INDIRECT) name << "_indirect"; 369 if (flags & TEST_FLAG_INSTANCED) name << "_instanced"; 370 if (flags & TEST_FLAG_FIRST_INSTANCE) name << "_first_instance"; 371 372 group->addChild(new InstanceFactory<DrawTest>(group->getTestContext(), name.str(), "", addFlags(testSpec, flags))); 373 } 374 375 } // anonymous 376 377 ShaderDrawParametersTests::ShaderDrawParametersTests (tcu::TestContext &testCtx) 378 : TestCaseGroup (testCtx, "shader_draw_parameters", "VK_KHR_shader_draw_parameters") 379 { 380 } 381 382 void ShaderDrawParametersTests::init (void) 383 { 384 { 385 DrawTest::TestSpec testSpec; 386 testSpec.shaders[glu::SHADERTYPE_VERTEX] = "vulkan/draw/VertexFetchShaderDrawParameters.vert"; 387 testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/draw/VertexFetch.frag"; 388 testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; 389 testSpec.flags = 0; 390 391 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(getTestContext(), "base_vertex", "")); 392 addDrawCase(group.get(), testSpec, 0); 393 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED); 394 addDrawCase(group.get(), testSpec, TEST_FLAG_INDIRECT); 395 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED | TEST_FLAG_INDIRECT); 396 addChild(group.release()); 397 } 398 { 399 DrawTest::TestSpec testSpec; 400 testSpec.shaders[glu::SHADERTYPE_VERTEX] = "vulkan/draw/VertexFetchShaderDrawParameters.vert"; 401 testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/draw/VertexFetch.frag"; 402 testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; 403 testSpec.flags = TEST_FLAG_INSTANCED; 404 405 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(getTestContext(), "base_instance", "")); 406 addDrawCase(group.get(), testSpec, 0); 407 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED); 408 addDrawCase(group.get(), testSpec, TEST_FLAG_INDIRECT); 409 addDrawCase(group.get(), testSpec, TEST_FLAG_INDIRECT | TEST_FLAG_FIRST_INSTANCE); 410 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED | TEST_FLAG_INDIRECT); 411 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED | TEST_FLAG_INDIRECT | TEST_FLAG_FIRST_INSTANCE); 412 addChild(group.release()); 413 } 414 { 415 DrawTest::TestSpec testSpec; 416 testSpec.shaders[glu::SHADERTYPE_VERTEX] = "vulkan/draw/VertexFetchShaderDrawParametersDrawIndex.vert"; 417 testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/draw/VertexFetch.frag"; 418 testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; 419 testSpec.flags = TEST_FLAG_INDIRECT | TEST_FLAG_MULTIDRAW; 420 421 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(getTestContext(), "draw_index", "")); 422 addDrawCase(group.get(), testSpec, 0); 423 addDrawCase(group.get(), testSpec, TEST_FLAG_INSTANCED); 424 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED); 425 addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED | TEST_FLAG_INSTANCED); 426 addChild(group.release()); 427 } 428 } 429 430 } // DrawTests 431 } // vkt 432