xref: /aosp_15_r20/external/deqp/external/vulkancts/modules/vulkan/draw/vktDrawSimpleTest.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2015 The Khronos Group Inc.
6  * Copyright (c) 2015 Intel Corporation
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Simple Draw Tests
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktDrawSimpleTest.hpp"
26 
27 #include "vktTestCaseUtil.hpp"
28 #include "vktDrawTestCaseUtil.hpp"
29 
30 #include "vktDrawBaseClass.hpp"
31 
32 #include "tcuTestLog.hpp"
33 #include "tcuResource.hpp"
34 #include "tcuImageCompare.hpp"
35 #include "tcuTextureUtil.hpp"
36 #include "tcuRGBA.hpp"
37 
38 #include "vkDefs.hpp"
39 #include "vkCmdUtil.hpp"
40 
41 namespace vkt
42 {
43 namespace Draw
44 {
45 namespace
46 {
47 class SimpleDraw : public DrawTestsBaseClass
48 {
49 public:
50     typedef TestSpecBase TestSpec;
51     SimpleDraw(Context &context, TestSpec testSpec);
52     virtual tcu::TestStatus iterate(void);
53     void draw(vk::VkCommandBuffer cmdBuffer, uint32_t instanceCount = 1u, uint32_t firstInstance = 0u);
54 };
55 
56 class SimpleDrawInstanced : public SimpleDraw
57 {
58 public:
59     typedef TestSpec TestSpec;
60     SimpleDrawInstanced(Context &context, TestSpec testSpec);
61     tcu::TestStatus iterate(void);
62 };
63 
SimpleDraw(Context & context,TestSpec testSpec)64 SimpleDraw::SimpleDraw(Context &context, TestSpec testSpec)
65     : DrawTestsBaseClass(context, testSpec.shaders[glu::SHADERTYPE_VERTEX], testSpec.shaders[glu::SHADERTYPE_FRAGMENT],
66                          testSpec.groupParams, testSpec.topology)
67 {
68     m_data.push_back(VertexElementData(tcu::Vec4(1.0f, -1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
69     m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
70 
71     int refVertexIndex = 2;
72 
73     switch (m_topology)
74     {
75     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
76         m_data.push_back(
77             VertexElementData(tcu::Vec4(-0.3f, -0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
78         m_data.push_back(
79             VertexElementData(tcu::Vec4(-0.3f, 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
80         m_data.push_back(
81             VertexElementData(tcu::Vec4(0.3f, -0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
82         m_data.push_back(
83             VertexElementData(tcu::Vec4(0.3f, -0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
84         m_data.push_back(
85             VertexElementData(tcu::Vec4(0.3f, 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
86         m_data.push_back(
87             VertexElementData(tcu::Vec4(-0.3f, 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
88         break;
89     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
90         m_data.push_back(
91             VertexElementData(tcu::Vec4(-0.3f, -0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
92         m_data.push_back(
93             VertexElementData(tcu::Vec4(-0.3f, 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
94         m_data.push_back(
95             VertexElementData(tcu::Vec4(0.3f, -0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
96         m_data.push_back(
97             VertexElementData(tcu::Vec4(0.3f, 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
98         m_data.push_back(
99             VertexElementData(tcu::Vec4(-0.3f, 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
100         break;
101     case vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
102     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
103     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
104     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
105     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
106     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
107     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
108     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
109     case vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
110     case vk::VK_PRIMITIVE_TOPOLOGY_LAST:
111         DE_FATAL("Topology not implemented");
112         break;
113     default:
114         DE_FATAL("Unknown topology");
115         break;
116     }
117 
118     m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
119 
120     initialize();
121 }
122 
iterate(void)123 tcu::TestStatus SimpleDraw::iterate(void)
124 {
125     tcu::TestLog &log                         = m_context.getTestContext().getLog();
126     const vk::VkQueue queue                   = m_context.getUniversalQueue();
127     const vk::VkDevice device                 = m_context.getDevice();
128     const vk::VkDeviceSize vertexBufferOffset = 0;
129     const vk::VkBuffer vertexBuffer           = m_vertexBuffer->object();
130 
131 #ifndef CTS_USES_VULKANSC
132     if (m_groupParams->useSecondaryCmdBuffer)
133     {
134         // record secondary command buffer
135         if (m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
136         {
137             beginSecondaryCmdBuffer(m_vk, vk::VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT);
138             beginDynamicRender(*m_secCmdBuffer);
139         }
140         else
141             beginSecondaryCmdBuffer(m_vk);
142 
143         m_vk.cmdBindVertexBuffers(*m_secCmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
144         m_vk.cmdBindPipeline(*m_secCmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
145         draw(*m_secCmdBuffer);
146 
147         if (m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
148             endDynamicRender(*m_secCmdBuffer);
149 
150         endCommandBuffer(m_vk, *m_secCmdBuffer);
151 
152         // record primary command buffer
153         beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
154         preRenderBarriers();
155 
156         if (!m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
157             beginDynamicRender(*m_cmdBuffer, vk::VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
158 
159         m_vk.cmdExecuteCommands(*m_cmdBuffer, 1u, &*m_secCmdBuffer);
160 
161         if (!m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
162             endDynamicRender(*m_cmdBuffer);
163 
164         endCommandBuffer(m_vk, *m_cmdBuffer);
165     }
166     else if (m_groupParams->useDynamicRendering)
167     {
168         beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
169         preRenderBarriers();
170         beginDynamicRender(*m_cmdBuffer);
171 
172         m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
173         m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
174         draw(*m_cmdBuffer);
175 
176         endDynamicRender(*m_cmdBuffer);
177         endCommandBuffer(m_vk, *m_cmdBuffer);
178     }
179 #endif // CTS_USES_VULKANSC
180 
181     if (!m_groupParams->useDynamicRendering)
182     {
183         beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
184         preRenderBarriers();
185         beginLegacyRender(*m_cmdBuffer);
186 
187         m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
188         m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
189         draw(*m_cmdBuffer);
190 
191         endLegacyRender(*m_cmdBuffer);
192         endCommandBuffer(m_vk, *m_cmdBuffer);
193     }
194 
195     submitCommandsAndWait(m_vk, device, queue, m_cmdBuffer.get());
196 
197     // Validation
198     tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5f + static_cast<float>(WIDTH)),
199                                   (int)(0.5f + static_cast<float>(HEIGHT)));
200 
201     referenceFrame.allocLevel(0);
202 
203     const int32_t frameWidth  = referenceFrame.getWidth();
204     const int32_t frameHeight = referenceFrame.getHeight();
205 
206     tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
207 
208     ReferenceImageCoordinates refCoords;
209 
210     for (int y = 0; y < frameHeight; y++)
211     {
212         const float yCoord = (float)(y / (0.5 * frameHeight)) - 1.0f;
213 
214         for (int x = 0; x < frameWidth; x++)
215         {
216             const float xCoord = (float)(x / (0.5 * frameWidth)) - 1.0f;
217 
218             if ((yCoord >= refCoords.bottom && yCoord <= refCoords.top && xCoord >= refCoords.left &&
219                  xCoord <= refCoords.right))
220                 referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), x, y);
221         }
222     }
223 
224     const vk::VkOffset3D zeroOffset = {0, 0, 0};
225     const tcu::ConstPixelBufferAccess renderedFrame =
226         m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset,
227                                         WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
228 
229     qpTestResult res = QP_TEST_RESULT_PASS;
230 
231     if (!tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame.getLevel(0), renderedFrame, 0.05f,
232                            tcu::COMPARE_LOG_RESULT))
233     {
234         res = QP_TEST_RESULT_FAIL;
235     }
236 
237     return tcu::TestStatus(res, qpGetTestResultName(res));
238 }
239 
draw(vk::VkCommandBuffer cmdBuffer,uint32_t instanceCount,uint32_t firstInstance)240 void SimpleDraw::draw(vk::VkCommandBuffer cmdBuffer, uint32_t instanceCount, uint32_t firstInstance)
241 {
242     switch (m_topology)
243     {
244     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
245         m_vk.cmdDraw(cmdBuffer, 6, instanceCount, 2, firstInstance);
246         break;
247     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
248         m_vk.cmdDraw(cmdBuffer, 4, instanceCount, 2, firstInstance);
249         break;
250     case vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
251     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
252     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
253     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
254     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:
255     case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:
256     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:
257     case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:
258     case vk::VK_PRIMITIVE_TOPOLOGY_PATCH_LIST:
259     case vk::VK_PRIMITIVE_TOPOLOGY_LAST:
260         DE_FATAL("Topology not implemented");
261         break;
262     default:
263         DE_FATAL("Unknown topology");
264         break;
265     }
266 }
267 
SimpleDrawInstanced(Context & context,TestSpec testSpec)268 SimpleDrawInstanced::SimpleDrawInstanced(Context &context, TestSpec testSpec) : SimpleDraw(context, testSpec)
269 {
270 }
271 
iterate(void)272 tcu::TestStatus SimpleDrawInstanced::iterate(void)
273 {
274     tcu::TestLog &log                         = m_context.getTestContext().getLog();
275     const vk::VkQueue queue                   = m_context.getUniversalQueue();
276     const vk::VkDevice device                 = m_context.getDevice();
277     const vk::VkDeviceSize vertexBufferOffset = 0;
278     const vk::VkBuffer vertexBuffer           = m_vertexBuffer->object();
279 
280 #ifndef CTS_USES_VULKANSC
281     if (m_groupParams->useSecondaryCmdBuffer)
282     {
283         // record secondary command buffer
284         if (m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
285         {
286             beginSecondaryCmdBuffer(m_vk, vk::VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT);
287             beginDynamicRender(*m_secCmdBuffer);
288         }
289         else
290             beginSecondaryCmdBuffer(m_vk);
291 
292         m_vk.cmdBindVertexBuffers(*m_secCmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
293         m_vk.cmdBindPipeline(*m_secCmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
294         draw(*m_secCmdBuffer, 4u, 2u);
295 
296         if (m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
297             endDynamicRender(*m_secCmdBuffer);
298 
299         endCommandBuffer(m_vk, *m_secCmdBuffer);
300 
301         // record primary command buffer
302         beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
303         preRenderBarriers();
304 
305         if (!m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
306             beginDynamicRender(*m_cmdBuffer, vk::VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
307 
308         m_vk.cmdExecuteCommands(*m_cmdBuffer, 1u, &*m_secCmdBuffer);
309 
310         if (!m_groupParams->secondaryCmdBufferCompletelyContainsDynamicRenderpass)
311             endDynamicRender(*m_cmdBuffer);
312 
313         endCommandBuffer(m_vk, *m_cmdBuffer);
314     }
315     else if (m_groupParams->useDynamicRendering)
316     {
317         beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
318         preRenderBarriers();
319 
320         beginDynamicRender(*m_cmdBuffer);
321         m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
322         m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
323         draw(*m_cmdBuffer, 4u, 2u);
324         endDynamicRender(*m_cmdBuffer);
325 
326         endCommandBuffer(m_vk, *m_cmdBuffer);
327     }
328 #endif // CTS_USES_VULKANSC
329 
330     if (!m_groupParams->useDynamicRendering)
331     {
332         beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
333         preRenderBarriers();
334 
335         beginLegacyRender(*m_cmdBuffer);
336         m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
337         m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
338         draw(*m_cmdBuffer, 4u, 2u);
339         endLegacyRender(*m_cmdBuffer);
340 
341         endCommandBuffer(m_vk, *m_cmdBuffer);
342     }
343 
344     submitCommandsAndWait(m_vk, device, queue, m_cmdBuffer.get());
345 
346     // Validation
347     VK_CHECK(m_vk.queueWaitIdle(queue));
348 
349     tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5f + static_cast<float>(WIDTH)),
350                                   (int)(0.5f + static_cast<float>(HEIGHT)));
351 
352     referenceFrame.allocLevel(0);
353 
354     const int32_t frameWidth  = referenceFrame.getWidth();
355     const int32_t frameHeight = referenceFrame.getHeight();
356 
357     tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
358 
359     ReferenceImageInstancedCoordinates refInstancedCoords;
360 
361     for (int y = 0; y < frameHeight; y++)
362     {
363         const float yCoord = (float)(y / (0.5 * frameHeight)) - 1.0f;
364 
365         for (int x = 0; x < frameWidth; x++)
366         {
367             const float xCoord = (float)(x / (0.5 * frameWidth)) - 1.0f;
368 
369             if ((yCoord >= refInstancedCoords.bottom && yCoord <= refInstancedCoords.top &&
370                  xCoord >= refInstancedCoords.left && xCoord <= refInstancedCoords.right))
371                 referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), x, y);
372         }
373     }
374 
375     const vk::VkOffset3D zeroOffset = {0, 0, 0};
376     const tcu::ConstPixelBufferAccess renderedFrame =
377         m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset,
378                                         WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
379 
380     qpTestResult res = QP_TEST_RESULT_PASS;
381 
382     if (!tcu::fuzzyCompare(log, "Result", "Image comparison result", referenceFrame.getLevel(0), renderedFrame, 0.05f,
383                            tcu::COMPARE_LOG_RESULT))
384     {
385         res = QP_TEST_RESULT_FAIL;
386     }
387 
388     return tcu::TestStatus(res, qpGetTestResultName(res));
389 }
390 
checkSupport(Context & context,SimpleDraw::TestSpec testSpec)391 void checkSupport(Context &context, SimpleDraw::TestSpec testSpec)
392 {
393     if (testSpec.groupParams->useDynamicRendering)
394         context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
395 }
396 
397 } // namespace
398 
SimpleDrawTests(tcu::TestContext & testCtx,const SharedGroupParams groupParams)399 SimpleDrawTests::SimpleDrawTests(tcu::TestContext &testCtx, const SharedGroupParams groupParams)
400     : TestCaseGroup(testCtx, "simple_draw")
401     , m_groupParams(groupParams)
402 {
403     /* Left blank on purpose */
404 }
405 
~SimpleDrawTests(void)406 SimpleDrawTests::~SimpleDrawTests(void)
407 {
408 }
409 
init(void)410 void SimpleDrawTests::init(void)
411 {
412     {
413         SimpleDraw::TestSpec testSpec{
414             {// ShaderMap shaders;
415              {glu::SHADERTYPE_VERTEX, "vulkan/draw/VertexFetch.vert"},
416              {glu::SHADERTYPE_FRAGMENT, "vulkan/draw/VertexFetch.frag"}},
417             vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // vk::VkPrimitiveTopology topology;
418             m_groupParams                            // const SharedGroupParams groupParams;
419         };
420 
421         addChild(new InstanceFactory<SimpleDraw, FunctionSupport1<SimpleDraw::TestSpec>>(
422             m_testCtx, "simple_draw_triangle_list", testSpec,
423             FunctionSupport1<SimpleDraw::TestSpec>::Args(checkSupport, testSpec)));
424         testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
425         addChild(new InstanceFactory<SimpleDraw, FunctionSupport1<SimpleDraw::TestSpec>>(
426             m_testCtx, "simple_draw_triangle_strip", testSpec,
427             FunctionSupport1<SimpleDraw::TestSpec>::Args(checkSupport, testSpec)));
428     }
429     {
430         SimpleDrawInstanced::TestSpec testSpec{
431             {{glu::SHADERTYPE_VERTEX, "vulkan/draw/VertexFetchInstancedFirstInstance.vert"},
432              {glu::SHADERTYPE_FRAGMENT, "vulkan/draw/VertexFetch.frag"}},
433             vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
434             m_groupParams};
435 
436         addChild(new InstanceFactory<SimpleDrawInstanced, FunctionSupport1<SimpleDrawInstanced::TestSpec>>(
437             m_testCtx, "simple_draw_instanced_triangle_list", testSpec,
438             FunctionSupport1<SimpleDrawInstanced::TestSpec>::Args(checkSupport, testSpec)));
439         testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
440         addChild(new InstanceFactory<SimpleDrawInstanced, FunctionSupport1<SimpleDrawInstanced::TestSpec>>(
441             m_testCtx, "simple_draw_instanced_triangle_strip", testSpec,
442             FunctionSupport1<SimpleDrawInstanced::TestSpec>::Args(checkSupport, testSpec)));
443     }
444 }
445 
446 } // namespace Draw
447 } // namespace vkt
448