467 static const uint32_t probe_spirv[] = {
468 0x07230203, 0x00010000, 0x0008000b, 0x00000013,
469 0x00000000, 0x00020011, 0x00000001, 0x0006000b,
470 0x00000001, 0x4c534c47, 0x6474732e, 0x3035342e,
471 0x00000000, 0x0003000e, 0x00000000, 0x00000001,
472 0x0005000f, 0x00000005, 0x00000004, 0x6e69616d,
473 0x00000000, 0x00060010, 0x00000004, 0x00000011,
474 0x00000001, 0x00000001, 0x00000001, 0x00030003,
475 0x00000002, 0x000001c2, 0x00040005, 0x00000004,
476 0x6e69616d, 0x00000000, 0x00050005, 0x00000008,
477 0x626f7250, 0x66754265, 0x00726566, 0x00050006,
478 0x00000008, 0x00000000, 0x61746164, 0x00000000,
479 0x00030005, 0x0000000a, 0x00667562, 0x00040047,
480 0x00000007, 0x00000006, 0x00000004, 0x00030047,
481 0x00000008, 0x00000003, 0x00050048, 0x00000008,
482 0x00000000, 0x00000023, 0x00000000, 0x00040047,
483 0x0000000a, 0x00000021, 0x00000000, 0x00040047,
484 0x0000000a, 0x00000022, 0x00000000, 0x00040047,
485 0x00000012, 0x0000000b, 0x00000019, 0x00020013,
486 0x00000002, 0x00030021, 0x00000003, 0x00000002,
487 0x00040015, 0x00000006, 0x00000020, 0x00000000,
488 0x0003001d, 0x00000007, 0x00000006, 0x0003001e,
489 0x00000008, 0x00000007, 0x00040020, 0x00000009,
490 0x00000002, 0x00000008, 0x0004003b, 0x00000009,
491 0x0000000a, 0x00000002, 0x00040015, 0x0000000b,
492 0x00000020, 0x00000001, 0x0004002b, 0x0000000b,
493 0x0000000c, 0x00000000, 0x0004002b, 0x00000006,
494 0x0000000d, 0x0000002a, 0x00040020, 0x0000000e,
495 0x00000002, 0x00000006, 0x00040017, 0x00000010,
496 0x00000006, 0x00000003, 0x0004002b, 0x00000006,
497 0x00000011, 0x00000001, 0x0006002c, 0x00000010,
498 0x00000012, 0x00000011, 0x00000011, 0x00000011,
499 0x00050036, 0x00000002, 0x00000004, 0x00000000,
500 0x00000003, 0x000200f8, 0x00000005, 0x00060041,
501 0x0000000e, 0x0000000f, 0x0000000a, 0x0000000c,
502 0x0000000c, 0x0003003e, 0x0000000f, 0x0000000d,
503 0x000100fd, 0x00010038,
505 static const size_t probe_spirv_size =
sizeof(probe_spirv);
508 VkCommandPool probe_pool = VK_NULL_HANDLE;
509 VkCommandBuffer probe_cmd = VK_NULL_HANDLE;
510 VkFence probe_fence = VK_NULL_HANDLE;
511 VkShaderModule probe_shader = VK_NULL_HANDLE;
512 VkPipelineLayout probe_layout = VK_NULL_HANDLE;
513 VkPipeline probe_pipeline = VK_NULL_HANDLE;
514 VkDescriptorSetLayout probe_set_layout = VK_NULL_HANDLE;
515 VkDescriptorPool probe_desc_pool = VK_NULL_HANDLE;
516 VkDescriptorSet probe_desc_set = VK_NULL_HANDLE;
517 VkBuffer probe_buffer = VK_NULL_HANDLE;
518 VmaAllocation probe_alloc = VK_NULL_HANDLE;
520 auto cleanup = [&]() {
521 if (probe_pipeline != VK_NULL_HANDLE) vkDestroyPipeline(device, probe_pipeline,
nullptr);
522 if (probe_layout != VK_NULL_HANDLE) vkDestroyPipelineLayout(device, probe_layout,
nullptr);
523 if (probe_shader != VK_NULL_HANDLE) vkDestroyShaderModule(device, probe_shader,
nullptr);
524 if (probe_desc_pool != VK_NULL_HANDLE) vkDestroyDescriptorPool(device, probe_desc_pool,
nullptr);
525 if (probe_set_layout != VK_NULL_HANDLE) vkDestroyDescriptorSetLayout(device, probe_set_layout,
nullptr);
526 if (probe_buffer != VK_NULL_HANDLE) vmaDestroyBuffer(allocator, probe_buffer, probe_alloc);
527 if (probe_fence != VK_NULL_HANDLE) vkDestroyFence(device, probe_fence,
nullptr);
528 if (probe_pool != VK_NULL_HANDLE) vkDestroyCommandPool(device, probe_pool,
nullptr);
532 VkBufferCreateInfo buf_info{};
533 buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
534 buf_info.size =
sizeof(uint32_t);
535 buf_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
536 buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
538 VmaAllocationCreateInfo vma_info{};
539 vma_info.usage = VMA_MEMORY_USAGE_AUTO;
540 vma_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;
542 VmaAllocationInfo alloc_result{};
543 VkResult result = vmaCreateBuffer(allocator, &buf_info, &vma_info, &probe_buffer, &probe_alloc, &alloc_result);
544 if (result != VK_SUCCESS) {
546 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create probe buffer. "
547 "VkResult code: " + std::to_string(result));
551 *
static_cast<uint32_t *
>(alloc_result.pMappedData) = 0;
554 VkDescriptorSetLayoutBinding binding{};
556 binding.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
557 binding.descriptorCount = 1;
558 binding.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT;
560 VkDescriptorSetLayoutCreateInfo set_layout_info{};
561 set_layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
562 set_layout_info.bindingCount = 1;
563 set_layout_info.pBindings = &binding;
565 result = vkCreateDescriptorSetLayout(device, &set_layout_info,
nullptr, &probe_set_layout);
566 if (result != VK_SUCCESS) {
568 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create descriptor set layout. "
569 "VkResult code: " + std::to_string(result));
573 VkDescriptorPoolSize pool_size{};
574 pool_size.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
575 pool_size.descriptorCount = 1;
577 VkDescriptorPoolCreateInfo desc_pool_info{};
578 desc_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
579 desc_pool_info.maxSets = 1;
580 desc_pool_info.poolSizeCount = 1;
581 desc_pool_info.pPoolSizes = &pool_size;
583 result = vkCreateDescriptorPool(device, &desc_pool_info,
nullptr, &probe_desc_pool);
584 if (result != VK_SUCCESS) {
586 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create descriptor pool. "
587 "VkResult code: " + std::to_string(result));
590 VkDescriptorSetAllocateInfo desc_alloc_info{};
591 desc_alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
592 desc_alloc_info.descriptorPool = probe_desc_pool;
593 desc_alloc_info.descriptorSetCount = 1;
594 desc_alloc_info.pSetLayouts = &probe_set_layout;
596 result = vkAllocateDescriptorSets(device, &desc_alloc_info, &probe_desc_set);
597 if (result != VK_SUCCESS) {
599 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to allocate descriptor set. "
600 "VkResult code: " + std::to_string(result));
604 VkDescriptorBufferInfo desc_buf_info{};
605 desc_buf_info.buffer = probe_buffer;
606 desc_buf_info.offset = 0;
607 desc_buf_info.range = VK_WHOLE_SIZE;
609 VkWriteDescriptorSet write{};
610 write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
611 write.dstSet = probe_desc_set;
612 write.dstBinding = 0;
613 write.descriptorCount = 1;
614 write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
615 write.pBufferInfo = &desc_buf_info;
617 vkUpdateDescriptorSets(device, 1, &write, 0,
nullptr);
620 VkShaderModuleCreateInfo shader_info{};
621 shader_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
622 shader_info.codeSize = probe_spirv_size;
623 shader_info.pCode = probe_spirv;
625 result = vkCreateShaderModule(device, &shader_info,
nullptr, &probe_shader);
626 if (result != VK_SUCCESS) {
628 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create probe shader module. "
629 "VkResult code: " + std::to_string(result));
633 VkPipelineLayoutCreateInfo layout_create_info{};
634 layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
635 layout_create_info.setLayoutCount = 1;
636 layout_create_info.pSetLayouts = &probe_set_layout;
638 result = vkCreatePipelineLayout(device, &layout_create_info,
nullptr, &probe_layout);
639 if (result != VK_SUCCESS) {
641 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create probe pipeline layout. "
642 "VkResult code: " + std::to_string(result));
645 VkComputePipelineCreateInfo pipeline_info{};
646 pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
647 pipeline_info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
648 pipeline_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
649 pipeline_info.stage.module = probe_shader;
650 pipeline_info.stage.pName =
"main";
651 pipeline_info.layout = probe_layout;
653 result = vkCreateComputePipelines(device, VK_NULL_HANDLE, 1, &pipeline_info,
nullptr, &probe_pipeline);
654 if (result != VK_SUCCESS) {
656 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create probe compute pipeline. "
657 "VkResult code: " + std::to_string(result));
661 VkCommandPoolCreateInfo pool_create_info{};
662 pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
663 pool_create_info.queueFamilyIndex = compute_queue_family;
664 pool_create_info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;
666 result = vkCreateCommandPool(device, &pool_create_info,
nullptr, &probe_pool);
667 if (result != VK_SUCCESS) {
669 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to create command pool. "
670 "VkResult code: " + std::to_string(result));
673 VkCommandBufferAllocateInfo cmd_alloc_info{};
674 cmd_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
675 cmd_alloc_info.commandPool = probe_pool;
676 cmd_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
677 cmd_alloc_info.commandBufferCount = 1;
679 result = vkAllocateCommandBuffers(device, &cmd_alloc_info, &probe_cmd);
680 if (result != VK_SUCCESS) {
682 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Failed to allocate command buffer. "
683 "VkResult code: " + std::to_string(result));
686 VkCommandBufferBeginInfo begin_info{};
687 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
688 begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
690 vkBeginCommandBuffer(probe_cmd, &begin_info);
691 vkCmdBindPipeline(probe_cmd, VK_PIPELINE_BIND_POINT_COMPUTE, probe_pipeline);
692 vkCmdBindDescriptorSets(probe_cmd, VK_PIPELINE_BIND_POINT_COMPUTE, probe_layout, 0, 1, &probe_desc_set, 0,
nullptr);
693 vkCmdDispatch(probe_cmd, 1, 1, 1);
694 vkEndCommandBuffer(probe_cmd);
697 VkFenceCreateInfo fence_info{};
698 fence_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
700 result = vkCreateFence(device, &fence_info,
nullptr, &probe_fence);
701 if (result != VK_SUCCESS) {
704 "VkResult code: " + std::to_string(result));
707 VkSubmitInfo submit_info{};
708 submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
709 submit_info.commandBufferCount = 1;
710 submit_info.pCommandBuffers = &probe_cmd;
712 result = vkQueueSubmit(compute_queue, 1, &submit_info, probe_fence);
713 if (result != VK_SUCCESS) {
715 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): Compute dispatch submission failed. "
716 "VkResult code: " + std::to_string(result));
720 bool completed =
false;
721 for (
int attempt = 0; attempt < 50; ++attempt) {
722 result = vkWaitForFences(device, 1, &probe_fence, VK_TRUE, 100000000ULL);
723 if (result == VK_SUCCESS) {
726 }
else if (result != VK_TIMEOUT) {
729 "The Vulkan device was created but cannot execute compute shaders "
730 "(common on CI runners or systems without full GPU compute support). "
731 "VkResult code: " + std::to_string(result));
737 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): GPU compute probe timed out after 5 seconds.");
741 uint32_t readback = *
static_cast<uint32_t *
>(alloc_result.pMappedData);
744 if (readback != 42) {
745 helios_runtime_error(
"ERROR (VulkanDevice::probeComputeCapability): GPU compute probe produced wrong result "
746 "(expected 42, got " + std::to_string(readback) +
"). "
747 "The GPU may not support compute shader storage buffer writes.");