• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef _VKTDGCUTILEXT_HPP
2 #define _VKTDGCUTILEXT_HPP
3 /*------------------------------------------------------------------------
4  * Vulkan Conformance Tests
5  * ------------------------
6  *
7  * Copyright (c) 2024 The Khronos Group Inc.
8  * Copyright (c) 2024 Valve Corporation.
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  *      http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  *//*!
23  * \file
24  * \brief Device Generated Commands EXT Utility Code
25  *//*--------------------------------------------------------------------*/
26 
27 #include "vkMemUtil.hpp"
28 #include "vkBufferWithMemory.hpp"
29 #include "vktTestCase.hpp"
30 
31 #include "tcuMaybe.hpp"
32 
33 #include <cstdint>
34 #include <vector>
35 #include <memory>
36 
37 namespace vkt
38 {
39 namespace DGC
40 {
41 
42 void checkDGCExtSupport(Context &context, vk::VkShaderStageFlags stages, vk::VkShaderStageFlags bindStagesPipeline = 0u,
43                         vk::VkShaderStageFlags bindStagesShaderObject          = 0u,
44                         vk::VkIndirectCommandsInputModeFlagsEXT inputModeFlags = 0u, bool transformFeedback = false);
45 
46 enum class DGCComputeSupportType
47 {
48     BASIC         = 0, // No binds.
49     BIND_PIPELINE = 1,
50     BIND_SHADER   = 2,
51 };
52 
53 void checkDGCExtComputeSupport(Context &context, DGCComputeSupportType supportType);
54 
55 vk::VkIndirectExecutionSetInfoEXT makeIndirectExecutionSetInfo(
56     const vk::VkIndirectExecutionSetPipelineInfoEXT &pipelineInfo);
57 vk::VkIndirectExecutionSetInfoEXT makeIndirectExecutionSetInfo(
58     const vk::VkIndirectExecutionSetShaderInfoEXT &shaderInfo);
59 
60 // Class that helps create and update an indirect execution set.
61 // You probably don't want to create these objects directly. Instead, use one of the makeExecutionSetManager* functions.
62 class ExecutionSetManager
63 {
64 public:
65     ExecutionSetManager(const vk::DeviceInterface &vkd, vk::VkDevice device,
66                         const vk::VkIndirectExecutionSetPipelineInfoEXT &pipelineInfo);
67     ExecutionSetManager(const vk::DeviceInterface &vkd, vk::VkDevice device,
68                         const vk::VkIndirectExecutionSetShaderInfoEXT &shaderInfo);
69 
70     // Add an element to be updated for pipeline-based indirect execution sets.
71     // See update().
72     void addPipeline(uint32_t index, vk::VkPipeline pipeline);
73 
74     // Add an element to be updated for shader-object-based indirect execution sets.
75     // See update().
76     void addShader(uint32_t index, vk::VkShaderEXT shader);
77 
78     // Run pending vkUpdateIndirectExecutionSet* calls. Clear queue of pending updates.
79     void update(void);
80 
81     // Get the indirect execution set handle.
82     vk::VkIndirectExecutionSetEXT get(bool requireNoPendingWrites = true) const;
operator *(void) const83     vk::VkIndirectExecutionSetEXT operator*(void) const
84     {
85         return get();
86     }
87 
88 protected:
89     const vk::DeviceInterface &m_vkd;
90     const vk::VkDevice m_device;
91     vk::Move<vk::VkIndirectExecutionSetEXT> m_executionSet;
92     bool m_pipelines;
93     bool m_shaderObjects;
94 
95     // Helps make sure we don't miss the update() call.
96     // Note we can still call get() at any moment but, by default, not while there are updates pending.
assertNoPendingWrites(void) const97     void assertNoPendingWrites(void) const
98     {
99         DE_ASSERT(m_pipelineWrites.empty());
100         DE_ASSERT(m_shaderWrites.empty());
101     }
102 
103     std::vector<vk::VkWriteIndirectExecutionSetPipelineEXT> m_pipelineWrites;
104     std::vector<vk::VkWriteIndirectExecutionSetShaderEXT> m_shaderWrites;
105 };
106 
107 using ExecutionSetManagerPtr = std::unique_ptr<ExecutionSetManager>;
108 
109 ExecutionSetManagerPtr makeExecutionSetManagerPipeline(const vk::DeviceInterface &vkd, vk::VkDevice device,
110                                                        vk::VkPipeline initialPipeline, uint32_t maxPipelineCount);
111 
112 // Info about a shader stage used when creating an indirect execution set with shader objects.
113 struct IESStageInfo
114 {
IESStageInfovkt::DGC::IESStageInfo115     IESStageInfo(const vk::VkShaderEXT &shader_, const std::vector<vk::VkDescriptorSetLayout> &setLayouts_)
116         : shader(shader_)
117         , setLayouts(setLayouts_)
118     {
119     }
120 
121     vk::VkShaderEXT shader;
122     std::vector<vk::VkDescriptorSetLayout> setLayouts;
123 };
124 
125 ExecutionSetManagerPtr makeExecutionSetManagerShader(const vk::DeviceInterface &vkd, vk::VkDevice device,
126                                                      const std::vector<IESStageInfo> &stages,
127                                                      const std::vector<vk::VkPushConstantRange> &pushConstantRanges,
128                                                      uint32_t maxShaderCount);
129 
130 // Handles creating a memory requirements info structure with the proper pNext chain.
131 class DGCMemReqsInfo
132 {
133 public:
134     DGCMemReqsInfo(vk::VkIndirectExecutionSetEXT ies, vk::VkIndirectCommandsLayoutEXT cmdsLayout, uint32_t maxSeqCount,
135                    uint32_t maxDrawCount, vk::VkPipeline pipeline = VK_NULL_HANDLE,
136                    const std::vector<vk::VkShaderEXT> *shaders = nullptr);
137 
setCommandsLayout(vk::VkIndirectCommandsLayoutEXT cmdsLayout)138     void setCommandsLayout(vk::VkIndirectCommandsLayoutEXT cmdsLayout)
139     {
140         m_memReqs.indirectCommandsLayout = cmdsLayout;
141     }
setMaxSequenceCount(uint32_t maxSeqCount)142     void setMaxSequenceCount(uint32_t maxSeqCount)
143     {
144         m_memReqs.maxSequenceCount = maxSeqCount;
145     }
146 
get(void) const147     const vk::VkGeneratedCommandsMemoryRequirementsInfoEXT &get(void) const
148     {
149         return m_memReqs;
150     }
operator *(void) const151     const vk::VkGeneratedCommandsMemoryRequirementsInfoEXT &operator*(void) const
152     {
153         return get();
154     }
155 
156     // pNext pointer would be wrong, do not bother.
157     DGCMemReqsInfo(const DGCMemReqsInfo &) = delete;
158 
159 protected:
160     vk::VkGeneratedCommandsMemoryRequirementsInfoEXT m_memReqs;
161     vk::VkGeneratedCommandsPipelineInfoEXT m_pipelineInfo;
162     vk::VkGeneratedCommandsShaderInfoEXT m_shadersInfo;
163     std::vector<vk::VkShaderEXT> m_shaders;
164 };
165 
166 // Handles creating a VkGeneratedCommandsInfoEXT structure with the proper pNext chain.
167 class DGCGenCmdsInfo
168 {
169 public:
170     DGCGenCmdsInfo(vk::VkShaderStageFlags shaderStages, vk::VkIndirectExecutionSetEXT ies,
171                    vk::VkIndirectCommandsLayoutEXT indirectCommandsLayout, vk::VkDeviceAddress indirectAddress,
172                    vk::VkDeviceSize indirectAddressSize, vk::VkDeviceAddress preprocessAddress,
173                    vk::VkDeviceSize preprocessSize, uint32_t maxSequenceCount, vk::VkDeviceAddress sequenceCountAddress,
174                    uint32_t maxDrawCount, vk::VkPipeline pipeline = VK_NULL_HANDLE,
175                    const std::vector<vk::VkShaderEXT> *shaders = nullptr);
176 
177     DGCGenCmdsInfo(const DGCGenCmdsInfo &); // Needed for some tests.
178 
get(void) const179     const vk::VkGeneratedCommandsInfoEXT &get(void) const
180     {
181         return m_genCmdsInfo;
182     }
operator *(void) const183     const vk::VkGeneratedCommandsInfoEXT &operator*(void) const
184     {
185         return get();
186     }
187 
188 protected:
189     vk::VkGeneratedCommandsInfoEXT m_genCmdsInfo;
190     vk::VkGeneratedCommandsPipelineInfoEXT m_pipelineInfo;
191     vk::VkGeneratedCommandsShaderInfoEXT m_shadersInfo;
192     std::vector<vk::VkShaderEXT> m_shaders;
193 };
194 
195 // Useful because it's easy to forget to initialize the sType and pNext members of VkMemoryRequirements2.
196 vk::VkMemoryRequirements getGeneratedCommandsMemoryRequirementsExt(
197     const vk::DeviceInterface &, vk::VkDevice, const vk::VkGeneratedCommandsMemoryRequirementsInfoEXT &);
198 
199 // Insert a memory barrier from the preprocessing stage to the execution stage.
200 void preprocessToExecuteBarrierExt(const vk::DeviceInterface &, vk::VkCommandBuffer);
201 
202 // Class to help build VkIndirectCommandsLayoutEXT objects.
203 class IndirectCommandsLayoutBuilderExt
204 {
205 public:
206     IndirectCommandsLayoutBuilderExt(vk::VkIndirectCommandsLayoutUsageFlagsEXT, vk::VkShaderStageFlags,
207                                      vk::VkPipelineLayout,
208                                      const vk::VkPipelineLayoutCreateInfo *pPipelineLayout = nullptr);
209     IndirectCommandsLayoutBuilderExt(const IndirectCommandsLayoutBuilderExt &)            = delete;
210     IndirectCommandsLayoutBuilderExt &operator=(const IndirectCommandsLayoutBuilderExt &) = delete;
211 
212     // Commands to add tokens to the layout.
213     void addPushConstantToken(uint32_t offset, const vk::VkPushConstantRange &pcRange);
214     void addSequenceIndexToken(uint32_t offset, const vk::VkPushConstantRange &pcRange);
215     void addVertexBufferToken(uint32_t offset, uint32_t bindingNumber);
216     void addIndexBufferToken(uint32_t offset, vk::VkIndirectCommandsInputModeFlagBitsEXT mode);
217     void addExecutionSetToken(uint32_t offset, vk::VkIndirectExecutionSetInfoTypeEXT setType,
218                               vk::VkShaderStageFlags stages);
219     void addComputePipelineToken(uint32_t offset);     // Shortcut for addExecutionSetToken
220     void addComputeShaderObjectToken(uint32_t offset); // Shortcut for addExecutionSetToken
221     void addDrawIndexedToken(uint32_t offset);
222     void addDrawToken(uint32_t offset);
223     void addDrawIndexedCountToken(uint32_t offset);
224     void addDrawCountToken(uint32_t offset);
225     void addDrawMeshTasksCountNvToken(uint32_t offset);
226     void addDrawMeshTasksCountToken(uint32_t offset);
227     void addDispatchToken(uint32_t offset);
228     void addDrawMeshTasksNvToken(uint32_t offset);
229     void addDrawMeshTasksToken(uint32_t offset);
230     void addTraceRays2Token(uint32_t offset);
231 
232     // Get the calculated range (amount of data so far) for the command stream, based on token data offsets and sizes.
233     uint32_t getStreamRange(void) const;
234 
235     // The stream stride is calculated automatically by default but it can also be set manually with this method.
236     // This can be useful for adding extra padding at the end of the items in a stream, or to do some more convoluted layouts.
237     void setStreamStride(uint32_t stride);
238 
239     // Returns the stream stride: the manual value if it's been set or the automatic value otherwise.
240     uint32_t getStreamStride(void) const;
241 
242     // Build the specified layout and return its handle.
243     vk::Move<vk::VkIndirectCommandsLayoutEXT> build(const vk::DeviceInterface &vkd, vk::VkDevice device,
244                                                     const vk::VkAllocationCallbacks *pAllocator = nullptr) const;
245 
246 protected:
247     const vk::VkIndirectCommandsLayoutUsageFlagsEXT m_layoutUsageFlags;
248     const vk::VkShaderStageFlags m_shaderStages;
249     const vk::VkPipelineLayout m_pipelineLayout;
250     const vk::VkPipelineLayoutCreateInfo *const m_layoutCreateInfoPtr;
251     tcu::Maybe<uint32_t> m_manualStride;
252 
253     // Similar to VkIndirectCommandsLayoutTokenEXT. Instead of having a union of pointers, it has a flat list of smart ones.
254     // This allows us to manage memory for each token easily and converting it to VkIndirectCommandsLayoutTokenEXT is very simple.
255     struct InternalToken
256     {
257         vk::VkIndirectCommandsTokenTypeEXT type;
258         uint32_t offset;
259         std::unique_ptr<vk::VkIndirectCommandsPushConstantTokenEXT> pPushConstant;
260         std::unique_ptr<vk::VkIndirectCommandsVertexBufferTokenEXT> pVertexBuffer;
261         std::unique_ptr<vk::VkIndirectCommandsIndexBufferTokenEXT> pIndexBuffer;
262         std::unique_ptr<vk::VkIndirectCommandsExecutionSetTokenEXT> pExecutionSet;
263 
264         // Builds an empty token.
265         InternalToken();
266 
267         // Converts internal token to a Vulkan token.
268         vk::VkIndirectCommandsLayoutTokenEXT asVkToken(void) const;
269     };
270 
271     std::vector<InternalToken> m_tokens;
272 
273     // Pushes back a new empty token and returns a reference to it.
274     InternalToken &pushBackEmptyToken(void);
275 
276     // Adds a simple token (token with no meta-data) given the offset and type.
277     void addSimpleToken(uint32_t offset, vk::VkIndirectCommandsTokenTypeEXT tokenType);
278 };
279 
280 class PreprocessBufferExt
281 {
282 public:
283     PreprocessBufferExt(const vk::DeviceInterface &vkd, vk::VkDevice device, vk::Allocator &alloc,
284                         vk::VkIndirectExecutionSetEXT indirectExecutionSet,
285                         vk::VkIndirectCommandsLayoutEXT indirectCommandsLayout, uint32_t maxSequenceCount,
286                         uint32_t maxDrawCount /*only used for COUNT-type tokens*/,
287                         vk::VkPipeline pipeline = VK_NULL_HANDLE, const std::vector<vk::VkShaderEXT> *shaders = nullptr,
288                         vk::VkDeviceSize offset = 0ull);
289 
get(void) const290     vk::VkBuffer get(void) const
291     {
292         return *m_buffer;
293     }
operator *(void) const294     vk::VkBuffer operator*(void) const
295     {
296         return get();
297     }
getAllocation(void) const298     const vk::Allocation &getAllocation(void) const
299     {
300         return *m_bufferAllocation;
301     }
getDeviceAddress(void) const302     vk::VkDeviceAddress getDeviceAddress(void) const
303     {
304         return m_deviceAddress;
305     }
getSize(void) const306     vk::VkDeviceSize getSize(void) const
307     {
308         return m_size;
309     }
needed(void) const310     bool needed(void) const
311     {
312         return (m_size > 0ull);
313     }
314 
315     // Forbid accidental copy and assignment.
316     PreprocessBufferExt(const PreprocessBufferExt &)            = delete;
317     PreprocessBufferExt &operator=(const PreprocessBufferExt &) = delete;
318 
319 protected:
320     vk::VkDeviceSize m_offset;
321     vk::Move<vk::VkBuffer> m_buffer;
322     de::MovePtr<vk::Allocation> m_bufferAllocation;
323     vk::VkDeviceSize m_size;
324     vk::VkDeviceAddress m_deviceAddress;
325 };
326 
327 vk::VkDeviceAddress getBufferDeviceAddress(const vk::DeviceInterface &vkd, vk::VkDevice device, vk::VkBuffer buffer);
328 
329 // General buffer with indirect usage and device-addressable memory.
330 class DGCBuffer
331 {
332 public:
333     DGCBuffer(const vk::DeviceInterface &vk, const vk::VkDevice device, vk::Allocator &allocator,
334               const vk::VkDeviceSize size, const vk::VkBufferUsageFlags extraUsageFlags = 0u,
335               const vk::MemoryRequirement extraMemReqs = vk::MemoryRequirement::HostVisible);
336 
get(void) const337     const vk::VkBuffer &get(void) const
338     {
339         return m_buffer.get();
340     }
operator *(void) const341     const vk::VkBuffer &operator*(void) const
342     {
343         return get();
344     }
getAllocation(void) const345     vk::Allocation &getAllocation(void) const
346     {
347         return m_buffer.getAllocation();
348     }
getDeviceAddress(void) const349     vk::VkDeviceAddress getDeviceAddress(void) const
350     {
351         return m_address;
352     }
getSize(void) const353     vk::VkDeviceSize getSize(void) const
354     {
355         return m_size;
356     }
357 
358 protected:
359     vk::VkDeviceSize m_size;
360     vk::BufferWithMemory m_buffer;
361     vk::VkDeviceAddress m_address;
362 };
363 
364 // Class that helps create compute pipelines to be used with DGC.
365 class DGCComputePipelineExt
366 {
367 public:
368     DGCComputePipelineExt(const vk::DeviceInterface &vkd, vk::VkDevice device,
369                           vk::VkPipelineCreateFlags2KHR pipelineFlags, vk::VkPipelineLayout pipelineLayout,
370                           vk::VkPipelineShaderStageCreateFlags shaderStageCreateFlags, vk::VkShaderModule module,
371                           const vk::VkSpecializationInfo *specializationInfo = nullptr,
372                           vk::VkPipeline basePipelineHandle = VK_NULL_HANDLE, int32_t basePipelineIndex = -1,
373                           uint32_t subgroupSize = 0u);
374 
375     vk::VkPipeline get(void) const;
376     vk::VkPipeline operator*(void) const;
377 
378     // Forbid accidental copy and assignment.
379     DGCComputePipelineExt(const DGCComputePipelineExt &other)       = delete;
380     DGCComputePipelineExt &operator=(const DGCComputePipelineExt &) = delete;
381 
382 protected:
383     vk::Move<vk::VkPipeline> m_pipeline;
384 };
385 
386 // Class that helps create shaders to be used with DGC.
387 class DGCShaderExt
388 {
389 public:
390     DGCShaderExt(const vk::DeviceInterface &vkd, vk::VkDevice device, vk::VkShaderStageFlagBits stage,
391                  vk::VkShaderCreateFlagsEXT shaderFlags, const vk::ProgramBinary &shaderBinary,
392                  const std::vector<vk::VkDescriptorSetLayout> &setLayouts,
393                  const std::vector<vk::VkPushConstantRange> &pushConstantRanges, bool tessellationFeature,
394                  bool geometryFeature, const vk::VkSpecializationInfo *specializationInfo = nullptr,
395                  const void *pNext = nullptr);
396 
~DGCShaderExt()397     virtual ~DGCShaderExt()
398     {
399     }
400 
get(void) const401     const vk::VkShaderEXT &get(void) const
402     {
403         return *m_shader;
404     }
operator *(void) const405     const vk::VkShaderEXT &operator*(void) const
406     {
407         return get();
408     }
409 
410     // Forbid accidental copy and assignment.
411     DGCShaderExt(const DGCShaderExt &other)       = delete;
412     DGCShaderExt &operator=(const DGCShaderExt &) = delete;
413 
414 protected:
415     // Default constructor, not public but available to derived classes so they can use it before preparing the init data.
416     DGCShaderExt(void);
417 
418     // Real constructor, used to be able to build the pNext structure before calling it.
419     void init(const vk::DeviceInterface &vkd, vk::VkDevice device, vk::VkShaderStageFlagBits stage,
420               vk::VkShaderCreateFlagsEXT shaderFlags, const vk::ProgramBinary &shaderBinary,
421               const std::vector<vk::VkDescriptorSetLayout> &setLayouts,
422               const std::vector<vk::VkPushConstantRange> &pushConstantRanges, bool tessellationFeature,
423               bool geometryFeature, const vk::VkSpecializationInfo *specializationInfo = nullptr,
424               const void *pNext = nullptr);
425 
426     vk::Move<vk::VkShaderEXT> m_shader;
427 };
428 
429 // Class that helps create compute shaders to be used with DGC.
430 class DGCComputeShaderExt : public DGCShaderExt
431 {
432 public:
433     DGCComputeShaderExt(const vk::DeviceInterface &vkd, vk::VkDevice device, vk::VkShaderCreateFlagsEXT shaderFlags,
434                         const vk::ProgramBinary &shaderBinary, const std::vector<vk::VkDescriptorSetLayout> &setLayouts,
435                         const std::vector<vk::VkPushConstantRange> &pushConstantRanges,
436                         const vk::VkSpecializationInfo *specializationInfo = nullptr, uint32_t subgroupSize = 0u);
437 
438     // Forbid accidental copy and assignment.
439     DGCComputeShaderExt(const DGCComputeShaderExt &other)       = delete;
440     DGCComputeShaderExt &operator=(const DGCComputeShaderExt &) = delete;
441 };
442 
443 // The original method from the context returns a vector of const char*. This transforms it into the preferred format.
444 std::vector<std::string> getDeviceCreationExtensions(Context &context);
445 
446 // Helper function for tests using shader objects.
447 void bindShaderObjectState(const vk::DeviceInterface &vkd, const std::vector<std::string> &extensions,
448                            const vk::VkCommandBuffer cmdBuffer, const std::vector<vk::VkViewport> &viewports,
449                            const std::vector<vk::VkRect2D> &scissors, const vk::VkPrimitiveTopology topology,
450                            const uint32_t patchControlPoints,
451                            const vk::VkPipelineVertexInputStateCreateInfo *vertexInputStateCreateInfo,
452                            const vk::VkPipelineRasterizationStateCreateInfo *rasterizationStateCreateInfo,
453                            const vk::VkPipelineMultisampleStateCreateInfo *multisampleStateCreateInfo,
454                            const vk::VkPipelineDepthStencilStateCreateInfo *depthStencilStateCreateInfo,
455                            const vk::VkPipelineColorBlendStateCreateInfo *colorBlendStateCreateInfo);
456 
457 // Helper struct for D3D12 compatibility, needed to test VK_INDIRECT_COMMANDS_INPUT_MODE_DXGI_INDEX_BUFFER_EXT.
458 struct IndexBufferViewD3D12
459 {
460     uint64_t bufferAddress;
461     uint32_t size;
462     int32_t indexType;
463 
464     IndexBufferViewD3D12(vk::VkDeviceAddress address, uint32_t size, vk::VkIndexType indexType);
465 };
466 
467 // Helper that allows us to submit a separate preprocess and a normal command buffer with proper sync.
468 void submitAndWaitWithPreprocess(const vk::DeviceInterface &vkd, vk::VkDevice device, vk::VkQueue queue,
469                                  vk::VkCommandBuffer cmdBuffer,
470                                  vk::VkCommandBuffer preprocessCmdBuffer = VK_NULL_HANDLE);
471 
472 } // namespace DGC
473 } // namespace vkt
474 
475 #endif // _VKTDGCUTILEXT_HPP
476