yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
43d0c2100
master
1// main.cpp 2 3// This file provides the application code for the `hello-world` example. 4// 5 6// This example uses Vulkan to run a simple compute shader written in Slang. 7// The goal is to demonstrate how to use the Slang API to cross compile 8// shader code. 9// 10#include "core/slang-string-util.h" 11#include "examples/example-base/example-base.h" 12#include "examples/example-base/test-base.h" 13#include "slang-com-ptr.h" 14#include "slang.h" 15#include "vulkan-api.h" 16 17using Slang ::ComPtr ; 18 19static const ExampleResources resourceBase ("hello-world" ); 20 21struct HelloWorldExample :public TestBase 22{ 23// The Vulkan functions pointers result from loading the vulkan library. 24VulkanAPI vkAPI ; 25 26// Vulkan objects used in this example. 27VkQueue queue ; 28VkCommandPool commandPool = VK_NULL_HANDLE ; 29 30// Input and output buffers. 31VkBuffer inOutBuffers [3 ]= {}; 32VkDeviceMemory bufferMemories [3 ]= {}; 33 34const size_t inputElementCount = 16 ; 35const size_t bufferSize = sizeof (float )* inputElementCount ; 36 37// We use a staging buffer allocated on host-visible memory to 38// upload/download data from GPU. 39VkBuffer stagingBuffer = VK_NULL_HANDLE ; 40VkDeviceMemory stagingMemory = VK_NULL_HANDLE ; 41 42VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE ; 43VkPipelineLayout pipelineLayout = VK_NULL_HANDLE ; 44VkPipeline pipeline = VK_NULL_HANDLE ; 45 46// Initializes the Vulkan instance and device. 47int initVulkanInstanceAndDevice (); 48 49// This function contains the most interesting part of this example. 50// It loads the `hello-world.slang` shader and compile it using the Slang API 51// into a SPIRV module, then create a Vulkan pipeline from the compiled shader. 52int createComputePipelineFromShader (); 53 54// Creates the input and output buffers. 55int createInOutBuffers (); 56 57// Sets up descriptor set bindings and dispatches the compute task. 58int dispatchCompute (); 59 60// Reads back and prints the result of the compute task. 61int printComputeResults (); 62 63// Main logic of this example. 64int run (); 65 66 ~HelloWorldExample (); 67}; 68 69 70int exampleMain (int argc ,char ** argv ) 71{ 72HelloWorldExample example ; 73example .parseOption (argc ,argv ); 74return example .run (); 75} 76 77/************************************************************/ 78/* HelloWorldExample Implementation */ 79/************************************************************/ 80 81int HelloWorldExample ::run () 82{ 83// If VK failed to initialize, skip running but return success anyway. 84// This allows our automated testing to distinguish between essential failures and the 85// case where the application is just not supported. 86if (int result = initVulkanInstanceAndDevice ()) 87return (vkAPI .device == VK_NULL_HANDLE ) ?0 :result ; 88RETURN_ON_FAIL (createComputePipelineFromShader ()); 89RETURN_ON_FAIL (createInOutBuffers ()); 90RETURN_ON_FAIL (dispatchCompute ()); 91RETURN_ON_FAIL (printComputeResults ()); 92return 0 ; 93} 94 95int HelloWorldExample ::initVulkanInstanceAndDevice () 96{ 97if (initializeVulkanDevice (vkAPI )!= 0 ) 98 { 99printf ("Failed to load Vulkan.\n" ); 100return -1 ; 101 } 102 103VkCommandPoolCreateInfo poolCreateInfo = {}; 104poolCreateInfo .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO ; 105poolCreateInfo .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT ; 106poolCreateInfo .queueFamilyIndex = vkAPI .queueFamilyIndex ; 107RETURN_ON_FAIL (vkAPI .vkCreateCommandPool (vkAPI .device ,& poolCreateInfo ,nullptr ,& commandPool )); 108 109vkAPI .vkGetDeviceQueue (vkAPI .device ,vkAPI .queueFamilyIndex ,0 ,& queue ); 110return 0 ; 111} 112 113int HelloWorldExample ::createComputePipelineFromShader () 114{ 115// First we need to create slang global session with work with the Slang API. 116ComPtr < slang::IGlobalSession > slangGlobalSession ; 117RETURN_ON_FAIL (slang::createGlobalSession (slangGlobalSession .writeRef ())); 118 119// Next we create a compilation session to generate SPIRV code from Slang source. 120 slang::SessionDesc sessionDesc = {}; 121 slang::TargetDesc targetDesc = {}; 122targetDesc .format = SLANG_SPIRV ; 123targetDesc .profile = slangGlobalSession -> findProfile ("spirv_1_5" ); 124targetDesc .flags = 0 ; 125 126 127sessionDesc .targets = & targetDesc ; 128sessionDesc .targetCount = 1 ; 129sessionDesc .compilerOptionEntryCount = 0 ; 130 131ComPtr < slang::ISession > session ; 132RETURN_ON_FAIL (slangGlobalSession -> createSession (sessionDesc ,session .writeRef ())); 133 134// Once the session has been obtained, we can start loading code into it. 135// 136// The simplest way to load code is by calling `loadModule` with the name of a Slang 137// module. A call to `loadModule("hello-world")` will behave more or less as if you 138// wrote: 139// 140// import hello_world; 141// 142// In a Slang shader file. The compiler will use its search paths to try to locate 143// `hello-world.slang`, then compile and load that file. If a matching module had 144// already been loaded previously, that would be used directly. 145 slang::IModule * slangModule = nullptr ; 146 { 147ComPtr < slang::IBlob > diagnosticBlob ; 148Slang ::String path = resourceBase .resolveResource ("hello-world.slang" ); 149slangModule = session -> loadModule (path .getBuffer (),diagnosticBlob .writeRef ()); 150diagnoseIfNeeded (diagnosticBlob ); 151if (!slangModule ) 152return -1 ; 153 } 154 155// Loading the `hello-world` module will compile and check all the shader code in it, 156// including the shader entry points we want to use. Now that the module is loaded 157// we can look up those entry points by name. 158// 159// Note: If you are using this `loadModule` approach to load your shader code it is 160// important to tag your entry point functions with the `[shader("...")]` attribute 161// (e.g., `[shader("compute")] void computeMain(...)`). Without that information there 162// is no umambiguous way for the compiler to know which functions represent entry 163// points when it parses your code via `loadModule()`. 164// 165ComPtr < slang::IEntryPoint > entryPoint ; 166slangModule -> findEntryPointByName ("computeMain" ,entryPoint .writeRef ()); 167 168// At this point we have a few different Slang API objects that represent 169// pieces of our code: `module`, `vertexEntryPoint`, and `fragmentEntryPoint`. 170// 171// A single Slang module could contain many different entry points (e.g., 172// four vertex entry points, three fragment entry points, and two compute 173// shaders), and before we try to generate output code for our target API 174// we need to identify which entry points we plan to use together. 175// 176// Modules and entry points are both examples of *component types* in the 177// Slang API. The API also provides a way to build a *composite* out of 178// other pieces, and that is what we are going to do with our module 179// and entry points. 180// 181Slang ::List < slang::IComponentType *> componentTypes ; 182componentTypes .add (slangModule ); 183componentTypes .add (entryPoint ); 184 185// Actually creating the composite component type is a single operation 186// on the Slang session, but the operation could potentially fail if 187// something about the composite was invalid (e.g., you are trying to 188// combine multiple copies of the same module), so we need to deal 189// with the possibility of diagnostic output. 190// 191ComPtr < slang::IComponentType > composedProgram ; 192 { 193ComPtr < slang::IBlob > diagnosticsBlob ; 194SlangResult result = session -> createCompositeComponentType ( 195componentTypes .getBuffer (), 196componentTypes .getCount (), 197composedProgram .writeRef (), 198diagnosticsBlob .writeRef ()); 199diagnoseIfNeeded (diagnosticsBlob ); 200RETURN_ON_FAIL (result ); 201 } 202 203// Now we can call `composedProgram->getEntryPointCode()` to retrieve the 204// compiled SPIRV code that we will use to create a vulkan compute pipeline. 205// This will trigger the final Slang compilation and spirv code generation. 206ComPtr < slang::IBlob > spirvCode ; 207 { 208ComPtr < slang::IBlob > diagnosticsBlob ; 209SlangResult result = composedProgram -> getEntryPointCode ( 2100 , 2110 , 212spirvCode .writeRef (), 213diagnosticsBlob .writeRef ()); 214diagnoseIfNeeded (diagnosticsBlob ); 215RETURN_ON_FAIL (result ); 216 217if (isTestMode ()) 218 { 219printEntrypointHashes (1 ,1 ,composedProgram ); 220 } 221 } 222 223// The following steps are all Vulkan API calls to create a pipeline. 224 225// First we need to create a descriptor set layout and a pipeline layout. 226// In this example, the pipeline layout is simple: we have a single descriptor 227// set with three buffer descriptors for our input/output storage buffers. 228// General applications typically has much more complicated pipeline layouts, 229// and should consider using Slang's reflection API to learn about the shader 230// parameter layout of a shader program. However, Slang's reflection API is 231// out of scope of this example. 232VkDescriptorSetLayoutCreateInfo descSetLayoutCreateInfo = { 233VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; 234descSetLayoutCreateInfo .bindingCount = 3 ; 235VkDescriptorSetLayoutBinding bindings [3 ]; 236for (int i = 0 ;i < 3 ;i ++ ) 237 { 238auto & binding = bindings [i ]; 239binding .binding = i ; 240binding .descriptorCount = 1 ; 241binding .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ; 242binding .stageFlags = VK_SHADER_STAGE_ALL ; 243binding .pImmutableSamplers = nullptr ; 244 } 245descSetLayoutCreateInfo .pBindings = bindings ; 246RETURN_ON_FAIL (vkAPI .vkCreateDescriptorSetLayout ( 247vkAPI .device , 248& descSetLayoutCreateInfo , 249nullptr , 250& descriptorSetLayout )); 251VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { 252VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; 253pipelineLayoutCreateInfo .setLayoutCount = 1 ; 254pipelineLayoutCreateInfo .pSetLayouts = & descriptorSetLayout ; 255RETURN_ON_FAIL (vkAPI .vkCreatePipelineLayout ( 256vkAPI .device , 257& pipelineLayoutCreateInfo , 258nullptr , 259& pipelineLayout )); 260 261// Next we create a shader module from the compiled SPIRV code. 262VkShaderModuleCreateInfo shaderCreateInfo = {VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; 263shaderCreateInfo .codeSize = spirvCode -> getBufferSize (); 264shaderCreateInfo .pCode = static_cast < const uint32_t *> (spirvCode -> getBufferPointer ()); 265VkShaderModule vkShaderModule ; 266RETURN_ON_FAIL ( 267vkAPI .vkCreateShaderModule (vkAPI .device ,& shaderCreateInfo ,nullptr ,& vkShaderModule )); 268 269// Now we have all we need to create a compute pipeline. 270VkComputePipelineCreateInfo pipelineCreateInfo = { 271VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO }; 272pipelineCreateInfo .stage .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO ; 273pipelineCreateInfo .stage .module = vkShaderModule ; 274pipelineCreateInfo .stage .stage = VK_SHADER_STAGE_COMPUTE_BIT ; 275pipelineCreateInfo .stage .pName = "main" ; 276pipelineCreateInfo .layout = pipelineLayout ; 277RETURN_ON_FAIL (vkAPI .vkCreateComputePipelines ( 278vkAPI .device , 279VK_NULL_HANDLE , 2801 , 281& pipelineCreateInfo , 282nullptr , 283& pipeline )); 284 285// We can destroy shader module now since it will no longer be used. 286vkAPI .vkDestroyShaderModule (vkAPI .device ,vkShaderModule ,nullptr ); 287 288return 0 ; 289} 290 291int HelloWorldExample ::createInOutBuffers () 292{ 293// Create input and output buffers that resides in device-local memory. 294for (int i = 0 ;i < 3 ;i ++ ) 295 { 296VkBufferCreateInfo bufferCreateInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; 297bufferCreateInfo .size = bufferSize ; 298bufferCreateInfo .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | 299VK_BUFFER_USAGE_TRANSFER_SRC_BIT | 300VK_BUFFER_USAGE_TRANSFER_DST_BIT ; 301RETURN_ON_FAIL ( 302vkAPI .vkCreateBuffer (vkAPI .device ,& bufferCreateInfo ,nullptr ,& inOutBuffers [i ])); 303VkMemoryRequirements memoryReqs = {}; 304vkAPI .vkGetBufferMemoryRequirements (vkAPI .device ,inOutBuffers [i ],& memoryReqs ); 305 306int memoryTypeIndex = vkAPI .findMemoryTypeIndex ( 307memoryReqs .memoryTypeBits , 308VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT ); 309assert (memoryTypeIndex >=0 ); 310 311VkMemoryPropertyFlags actualMemoryProperites = 312vkAPI .deviceMemoryProperties .memoryTypes [memoryTypeIndex ].propertyFlags ; 313 314VkMemoryAllocateInfo allocateInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; 315allocateInfo .allocationSize = memoryReqs .size ; 316allocateInfo .memoryTypeIndex = memoryTypeIndex ; 317RETURN_ON_FAIL ( 318vkAPI .vkAllocateMemory (vkAPI .device ,& allocateInfo ,nullptr ,& bufferMemories [i ])); 319RETURN_ON_FAIL ( 320vkAPI .vkBindBufferMemory (vkAPI .device ,inOutBuffers [i ],bufferMemories [i ],0 )); 321 } 322 323// Create the device memory and buffer object used for reading/writing 324// data to/from the device local buffers. 325 { 326VkBufferCreateInfo bufferCreateInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; 327bufferCreateInfo .size = bufferSize ; 328bufferCreateInfo .usage = 329VK_BUFFER_USAGE_TRANSFER_SRC_BIT |VK_BUFFER_USAGE_TRANSFER_DST_BIT ; 330RETURN_ON_FAIL ( 331vkAPI .vkCreateBuffer (vkAPI .device ,& bufferCreateInfo ,nullptr ,& stagingBuffer )); 332VkMemoryRequirements memoryReqs = {}; 333vkAPI .vkGetBufferMemoryRequirements (vkAPI .device ,stagingBuffer ,& memoryReqs ); 334 335int memoryTypeIndex = vkAPI .findMemoryTypeIndex ( 336memoryReqs .memoryTypeBits , 337VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |VK_MEMORY_PROPERTY_HOST_COHERENT_BIT ); 338assert (memoryTypeIndex >=0 ); 339 340VkMemoryPropertyFlags actualMemoryProperites = 341vkAPI .deviceMemoryProperties .memoryTypes [memoryTypeIndex ].propertyFlags ; 342 343VkMemoryAllocateInfo allocateInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; 344allocateInfo .allocationSize = memoryReqs .size ; 345allocateInfo .memoryTypeIndex = memoryTypeIndex ; 346RETURN_ON_FAIL ( 347vkAPI .vkAllocateMemory (vkAPI .device ,& allocateInfo ,nullptr ,& stagingMemory )); 348RETURN_ON_FAIL (vkAPI .vkBindBufferMemory (vkAPI .device ,stagingBuffer ,stagingMemory ,0 )); 349 } 350 351// Map staging buffer and writes in the initial input content. 352float * stagingBufferData = nullptr ; 353vkAPI .vkMapMemory (vkAPI .device ,stagingMemory ,0 ,bufferSize ,0 , (void ** )& stagingBufferData ); 354if (!stagingBufferData ) 355return -1 ; 356for (size_t i = 0 ;i < inputElementCount ;i ++ ) 357stagingBufferData [i ]= static_cast < float > (i ); 358vkAPI .vkUnmapMemory (vkAPI .device ,stagingMemory ); 359 360// Create a temporary command buffer for recording commands that writes initial 361// data into the input buffers. 362VkCommandBuffer uploadCommandBuffer ; 363VkCommandBufferAllocateInfo commandBufferAllocInfo = { 364VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; 365commandBufferAllocInfo .commandBufferCount = 1 ; 366commandBufferAllocInfo .commandPool = commandPool ; 367commandBufferAllocInfo .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ; 368RETURN_ON_FAIL (vkAPI .vkAllocateCommandBuffers ( 369vkAPI .device , 370& commandBufferAllocInfo , 371& uploadCommandBuffer )); 372 373VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; 374vkAPI .vkBeginCommandBuffer (uploadCommandBuffer ,& beginInfo ); 375VkBufferCopy bufferCopy = {}; 376bufferCopy .size = bufferSize ; 377vkAPI .vkCmdCopyBuffer (uploadCommandBuffer ,stagingBuffer ,inOutBuffers [0 ],1 ,& bufferCopy ); 378vkAPI .vkCmdCopyBuffer (uploadCommandBuffer ,stagingBuffer ,inOutBuffers [1 ],1 ,& bufferCopy ); 379vkAPI .vkEndCommandBuffer (uploadCommandBuffer ); 380VkSubmitInfo submitInfo = {VK_STRUCTURE_TYPE_SUBMIT_INFO }; 381submitInfo .commandBufferCount = 1 ; 382submitInfo .pCommandBuffers = & uploadCommandBuffer ; 383vkAPI .vkQueueSubmit (queue ,1 ,& submitInfo ,VK_NULL_HANDLE ); 384vkAPI .vkQueueWaitIdle (queue ); 385vkAPI .vkFreeCommandBuffers (vkAPI .device ,commandPool ,1 ,& uploadCommandBuffer ); 386return 0 ; 387} 388 389int HelloWorldExample ::dispatchCompute () 390{ 391// Create a descriptor pool. 392VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { 393VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; 394VkDescriptorPoolSize poolSizes []= { 395VkDescriptorPoolSize {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ,16 }}; 396descriptorPoolCreateInfo .maxSets = 4 ; 397descriptorPoolCreateInfo .poolSizeCount = sizeof (poolSizes ) /sizeof (VkDescriptorPoolSize ); 398descriptorPoolCreateInfo .pPoolSizes = poolSizes ; 399descriptorPoolCreateInfo .flags = 0 ; 400VkDescriptorPool descriptorPool = VK_NULL_HANDLE ; 401RETURN_ON_FAIL (vkAPI .vkCreateDescriptorPool ( 402vkAPI .device , 403& descriptorPoolCreateInfo , 404nullptr , 405& descriptorPool )); 406 407// Allocate descriptor set. 408VkDescriptorSetAllocateInfo descSetAllocInfo = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; 409descSetAllocInfo .descriptorPool = descriptorPool ; 410descSetAllocInfo .descriptorSetCount = 1 ; 411descSetAllocInfo .pSetLayouts = & descriptorSetLayout ; 412VkDescriptorSet descriptorSet = VK_NULL_HANDLE ; 413RETURN_ON_FAIL (vkAPI .vkAllocateDescriptorSets (vkAPI .device ,& descSetAllocInfo ,& descriptorSet )); 414 415// Write descriptor set. 416VkWriteDescriptorSet descriptorSetWrites [3 ]= {}; 417VkDescriptorBufferInfo bufferInfo [3 ]; 418for (int i = 0 ;i < 3 ;i ++ ) 419 { 420bufferInfo [i ].buffer = inOutBuffers [i ]; 421bufferInfo [i ].offset = 0 ; 422bufferInfo [i ].range = bufferSize ; 423 424descriptorSetWrites [i ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 425descriptorSetWrites [i ].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ; 426descriptorSetWrites [i ].descriptorCount = 1 ; 427descriptorSetWrites [i ].dstBinding = i ; 428descriptorSetWrites [i ].dstSet = descriptorSet ; 429descriptorSetWrites [i ].pBufferInfo = & bufferInfo [i ]; 430 } 431vkAPI .vkUpdateDescriptorSets (vkAPI .device ,3 ,descriptorSetWrites ,0 ,nullptr ); 432 433// Allocate command buffer and record dispatch commands. 434VkCommandBuffer commandBuffer ; 435VkCommandBufferAllocateInfo commandBufferAllocInfo = { 436VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; 437commandBufferAllocInfo .commandBufferCount = 1 ; 438commandBufferAllocInfo .commandPool = commandPool ; 439commandBufferAllocInfo .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ; 440RETURN_ON_FAIL ( 441vkAPI .vkAllocateCommandBuffers (vkAPI .device ,& commandBufferAllocInfo ,& commandBuffer )); 442VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; 443vkAPI .vkBeginCommandBuffer (commandBuffer ,& beginInfo ); 444vkAPI .vkCmdBindPipeline (commandBuffer ,VK_PIPELINE_BIND_POINT_COMPUTE ,pipeline ); 445vkAPI .vkCmdBindDescriptorSets ( 446commandBuffer , 447VK_PIPELINE_BIND_POINT_COMPUTE , 448pipelineLayout , 4490 , 4501 , 451& descriptorSet , 4520 , 453nullptr ); 454vkAPI .vkCmdDispatch (commandBuffer , (uint32_t )inputElementCount ,1 ,1 ); 455vkAPI .vkEndCommandBuffer (commandBuffer ); 456 457// Submit command buffer and wait. 458VkSubmitInfo submitInfo = {VK_STRUCTURE_TYPE_SUBMIT_INFO }; 459submitInfo .commandBufferCount = 1 ; 460submitInfo .pCommandBuffers = & commandBuffer ; 461vkAPI .vkQueueSubmit (queue ,1 ,& submitInfo ,VK_NULL_HANDLE ); 462vkAPI .vkQueueWaitIdle (queue ); 463vkAPI .vkFreeCommandBuffers (vkAPI .device ,commandPool ,1 ,& commandBuffer ); 464 465// Clean up. 466vkAPI .vkDestroyDescriptorPool (vkAPI .device ,descriptorPool ,nullptr ); 467return 0 ; 468} 469 470int HelloWorldExample ::printComputeResults () 471{ 472// Allocate command buffer to read back data. 473VkCommandBuffer commandBuffer ; 474VkCommandBufferAllocateInfo commandBufferAllocInfo = { 475VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; 476commandBufferAllocInfo .commandBufferCount = 1 ; 477commandBufferAllocInfo .commandPool = commandPool ; 478commandBufferAllocInfo .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY ; 479RETURN_ON_FAIL ( 480vkAPI .vkAllocateCommandBuffers (vkAPI .device ,& commandBufferAllocInfo ,& commandBuffer )); 481 482// Record commands to copy output buffer into staging buffer. 483VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; 484vkAPI .vkBeginCommandBuffer (commandBuffer ,& beginInfo ); 485VkBufferCopy bufferCopy = {}; 486bufferCopy .size = bufferSize ; 487vkAPI .vkCmdCopyBuffer (commandBuffer ,inOutBuffers [2 ],stagingBuffer ,1 ,& bufferCopy ); 488vkAPI .vkEndCommandBuffer (commandBuffer ); 489 490// Execute command buffer and wait. 491VkSubmitInfo submitInfo = {VK_STRUCTURE_TYPE_SUBMIT_INFO }; 492submitInfo .commandBufferCount = 1 ; 493submitInfo .pCommandBuffers = & commandBuffer ; 494vkAPI .vkQueueSubmit (queue ,1 ,& submitInfo ,VK_NULL_HANDLE ); 495vkAPI .vkQueueWaitIdle (queue ); 496vkAPI .vkFreeCommandBuffers (vkAPI .device ,commandPool ,1 ,& commandBuffer ); 497 498// Map and read back staging buffer. 499float * stagingBufferData = nullptr ; 500vkAPI .vkMapMemory (vkAPI .device ,stagingMemory ,0 ,bufferSize ,0 , (void ** )& stagingBufferData ); 501if (!stagingBufferData ) 502return -1 ; 503for (size_t i = 0 ;i < inputElementCount ;i ++ ) 504 { 505printf ("%f\n" ,stagingBufferData [i ]); 506 } 507return 0 ; 508} 509 510HelloWorldExample ::~HelloWorldExample () 511{ 512if (vkAPI .device == VK_NULL_HANDLE ) 513return ; 514 515vkAPI .vkDestroyPipeline (vkAPI .device ,pipeline ,nullptr ); 516for (int i = 0 ;i < 3 ;i ++ ) 517 { 518vkAPI .vkDestroyBuffer (vkAPI .device ,inOutBuffers [i ],nullptr ); 519vkAPI .vkFreeMemory (vkAPI .device ,bufferMemories [i ],nullptr ); 520 } 521vkAPI .vkDestroyBuffer (vkAPI .device ,stagingBuffer ,nullptr ); 522vkAPI .vkFreeMemory (vkAPI .device ,stagingMemory ,nullptr ); 523vkAPI .vkDestroyPipelineLayout (vkAPI .device ,pipelineLayout ,nullptr ); 524vkAPI .vkDestroyDescriptorSetLayout (vkAPI .device ,descriptorSetLayout ,nullptr ); 525vkAPI .vkDestroyCommandPool (vkAPI .device ,commandPool ,nullptr ); 526}