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