1 /// Vulkan implementation of GrAAL 2 module gfx.vulkan; 3 4 import gfx.bindings.vulkan; 5 import gfx.graal; 6 7 8 // some standard layers 9 10 enum lunarGValidationLayers = [ 11 "VK_LAYER_LUNARG_core_validation", 12 "VK_LAYER_LUNARG_standard_validation", 13 "VK_LAYER_LUNARG_parameter_validation", 14 ]; 15 16 @property ApiProps vulkanApiProps() { 17 return ApiProps( 18 "vulkan", CoordSystem.rightHanded 19 ); 20 } 21 22 /// Load global level vulkan functions, and instance level layers and extensions 23 /// This function must be called before any other in this module 24 void vulkanInit() 25 { 26 synchronized { 27 _globCmds = loadVulkanGlobalCmds(); 28 _instanceLayers = loadInstanceLayers(); 29 _instanceExtensions = loadInstanceExtensions(); 30 } 31 } 32 33 struct VulkanVersion 34 { 35 import std.bitmanip : bitfields; 36 mixin(bitfields!( 37 uint, "patch", 12, 38 uint, "minor", 10, 39 uint, "major", 10, 40 )); 41 42 this (in uint major, in uint minor, in uint patch) { 43 this.major = major; this.minor = minor; this.patch = patch; 44 } 45 46 this (in uint vkVer) { 47 this(VK_VERSION_MAJOR(vkVer), VK_VERSION_MINOR(vkVer), VK_VERSION_PATCH(vkVer)); 48 } 49 50 static VulkanVersion fromUint(in uint vkVer) { 51 return *cast(VulkanVersion*)(cast(void*)&vkVer); 52 } 53 54 uint toUint() const { 55 return *cast(uint*)(cast(void*)&this); 56 } 57 58 string toString() { 59 import std.format : format; 60 return format("VulkanVersion(%s, %s, %s)", this.major, this.minor, this.patch); 61 } 62 } 63 64 unittest { 65 const vkVer = VK_MAKE_VERSION(12, 7, 38); 66 auto vv = VulkanVersion.fromUint(vkVer); 67 assert(vv.major == 12); 68 assert(vv.minor == 7); 69 assert(vv.patch == 38); 70 assert(vv.toUint() == vkVer); 71 } 72 73 struct VulkanLayerProperties 74 { 75 string layerName; 76 VulkanVersion specVer; 77 VulkanVersion implVer; 78 string description; 79 80 @property VulkanExtensionProperties[] instanceExtensions() 81 { 82 return loadInstanceExtensions(layerName); 83 } 84 } 85 86 struct VulkanExtensionProperties 87 { 88 string extensionName; 89 VulkanVersion specVer; 90 } 91 92 /// Retrieve available instance level layer properties 93 @property VulkanLayerProperties[] vulkanInstanceLayers() { 94 return _instanceLayers; 95 } 96 /// Retrieve available instance level extensions properties 97 @property VulkanExtensionProperties[] vulkanInstanceExtensions() 98 { 99 return _instanceExtensions; 100 } 101 102 /// Creates a vulkan instance with default layers and extensions 103 VulkanInstance createVulkanInstance(in string appName=null, 104 in VulkanVersion appVersion=VulkanVersion(0, 0, 0)) 105 { 106 debug { 107 const wantedLayers = lunarGValidationLayers; 108 const wantedExts = [ "VK_KHR_debug_report", "VK_EXT_debug_report" ]; 109 } 110 else { 111 const string[] wantedLayers = []; 112 const string[] wantedExts = []; 113 } 114 115 import gfx.vulkan.wsi : surfaceInstanceExtensions; 116 117 import std.algorithm : canFind, filter, map; 118 import std.array : array; 119 import std.range : chain; 120 121 const layers = wantedLayers 122 .filter!(l => _instanceLayers.map!(il => il.layerName).canFind(l)) 123 .array; 124 const exts = wantedExts 125 .filter!(e => _instanceExtensions.map!(ie => ie.extensionName).canFind(e)) 126 .array 127 ~ surfaceInstanceExtensions; 128 129 return createVulkanInstance(layers, exts, appName, appVersion); 130 } 131 132 /// Creates an Instance object with Vulkan backend with user specified layers and extensions 133 VulkanInstance createVulkanInstance(in string[] layers, in string[] extensions, 134 in string appName=null, 135 in VulkanVersion appVersion=VulkanVersion(0, 0, 0)) 136 { 137 import gfx : gfxVersionMaj, gfxVersionMin, gfxVersionMic; 138 import std.algorithm : all, canFind, map; 139 import std.array : array; 140 import std.exception : enforce; 141 import std.string : toStringz; 142 143 // throw if some requested layers or extensions are not available 144 // TODO: specific exception 145 foreach (l; layers) { 146 enforce( 147 _instanceLayers.map!(il => il.layerName).canFind(l), 148 "Could not find layer " ~ l ~ " when creating Vulkan instance" 149 ); 150 } 151 foreach (e; extensions) { 152 enforce( 153 _instanceExtensions.map!(ie => ie.extensionName).canFind(e), 154 "Could not find extension " ~ e ~ " when creating Vulkan instance" 155 ); 156 } 157 158 VkApplicationInfo ai; 159 ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 160 if (appName.length) { 161 ai.pApplicationName = toStringz(appName); 162 } 163 ai.applicationVersion = appVersion.toUint(); 164 ai.pEngineName = "gfx-d\n".ptr; 165 ai.engineVersion = VK_MAKE_VERSION(gfxVersionMaj, gfxVersionMin, gfxVersionMic); 166 167 auto vkLayers = layers.map!toStringz.array; 168 auto vkExts = extensions.map!toStringz.array; 169 170 VkInstanceCreateInfo ici; 171 ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 172 ici.pApplicationInfo = &ai; 173 ici.enabledLayerCount = cast(uint)vkLayers.length; 174 ici.ppEnabledLayerNames = &vkLayers[0]; 175 ici.enabledExtensionCount = cast(uint)vkExts.length; 176 ici.ppEnabledExtensionNames = &vkExts[0]; 177 178 VkInstance vkInst; 179 vulkanEnforce(_globCmds.createInstance(&ici, null, &vkInst), "Could not create Vulkan instance"); 180 181 return new VulkanInstance(vkInst); 182 } 183 184 /// Retrieve available device level layers 185 @property VulkanLayerProperties[] vulkanDeviceLayers(PhysicalDevice device) { 186 auto pd = cast(VulkanPhysicalDevice)device; 187 if (!pd) return []; 188 189 return pd._availableLayers; 190 } 191 /// Retrieve available instance level extensions properties 192 VulkanExtensionProperties[] vulkanDeviceExtensions(PhysicalDevice device, in string layerName=null) 193 { 194 auto pd = cast(VulkanPhysicalDevice)device; 195 if (!pd) return []; 196 197 if (!layerName) { 198 return pd._availableExtensions; 199 } 200 else { 201 return pd.loadDeviceExtensions(layerName); 202 } 203 } 204 205 void overrideDeviceOpenVulkanLayers(PhysicalDevice device, string[] layers) 206 { 207 auto pd = cast(VulkanPhysicalDevice)device; 208 if (!pd) return; 209 210 pd._openLayers = layers; 211 } 212 213 void overrideDeviceOpenVulkanExtensions(PhysicalDevice device, string[] extensions) 214 { 215 auto pd = cast(VulkanPhysicalDevice)device; 216 if (!pd) return; 217 218 pd._openExtensions = extensions; 219 } 220 221 222 package: 223 224 import gfx.core.rc; 225 import gfx.graal.device; 226 import gfx.graal.format; 227 import gfx.graal.memory; 228 import gfx.graal.presentation; 229 import gfx.graal.queue; 230 import gfx.vulkan.conv; 231 import gfx.vulkan.device; 232 import gfx.vulkan.error; 233 import gfx.vulkan.wsi : VulkanSurface; 234 235 import std.exception : enforce; 236 237 __gshared VkGlobalCmds _globCmds; 238 __gshared VulkanLayerProperties[] _instanceLayers; 239 __gshared VulkanExtensionProperties[] _instanceExtensions; 240 241 VulkanLayerProperties[] loadInstanceLayers() 242 { 243 uint count; 244 vulkanEnforce( 245 _globCmds.enumerateInstanceLayerProperties(&count, null), 246 "Could not retrieve Vulkan instance layers" 247 ); 248 if (!count) return[]; 249 250 auto vkLayers = new VkLayerProperties[count]; 251 vulkanEnforce( 252 _globCmds.enumerateInstanceLayerProperties(&count, &vkLayers[0]), 253 "Could not retrieve Vulkan instance layers" 254 ); 255 256 import std.algorithm : map; 257 import std.array : array; 258 import std.string : fromStringz; 259 260 return vkLayers 261 .map!((ref VkLayerProperties vkLp) { 262 return VulkanLayerProperties( 263 fromStringz(&vkLp.layerName[0]).idup, 264 VulkanVersion.fromUint(vkLp.specVersion), 265 VulkanVersion.fromUint(vkLp.implementationVersion), 266 fromStringz(&vkLp.description[0]).idup, 267 ); 268 }) 269 .array; 270 } 271 272 VulkanExtensionProperties[] loadInstanceExtensions(in string layerName=null) 273 { 274 import std.string : toStringz; 275 276 const(char)* layer; 277 if (layerName.length) { 278 layer = toStringz(layerName); 279 } 280 uint count; 281 vulkanEnforce( 282 _globCmds.enumerateInstanceExtensionProperties(layer, &count, null), 283 "Could not retrieve Vulkan instance extensions" 284 ); 285 if (!count) return[]; 286 287 auto vkExts = new VkExtensionProperties[count]; 288 vulkanEnforce( 289 _globCmds.enumerateInstanceExtensionProperties(layer, &count, &vkExts[0]), 290 "Could not retrieve Vulkan instance extensions" 291 ); 292 293 import std.algorithm : map; 294 import std.array : array; 295 import std.string : fromStringz; 296 297 return vkExts 298 .map!((ref VkExtensionProperties vkExt) { 299 return VulkanExtensionProperties( 300 fromStringz(&vkExt.extensionName[0]).idup, 301 VulkanVersion.fromUint(vkExt.specVersion) 302 ); 303 }) 304 .array; 305 } 306 307 VulkanLayerProperties[] loadDeviceLayers(VulkanPhysicalDevice pd) 308 { 309 uint count; 310 vulkanEnforce( 311 pd.cmds.enumerateDeviceLayerProperties(pd.vk, &count, null), 312 "Could not retrieve Vulkan device layers" 313 ); 314 if (!count) return[]; 315 316 auto vkLayers = new VkLayerProperties[count]; 317 vulkanEnforce( 318 pd.cmds.enumerateDeviceLayerProperties(pd.vk, &count, &vkLayers[0]), 319 "Could not retrieve Vulkan device layers" 320 ); 321 322 import std.algorithm : map; 323 import std.array : array; 324 import std.string : fromStringz; 325 326 return vkLayers 327 .map!((ref VkLayerProperties vkLp) { 328 return VulkanLayerProperties( 329 fromStringz(&vkLp.layerName[0]).idup, 330 VulkanVersion.fromUint(vkLp.specVersion), 331 VulkanVersion.fromUint(vkLp.implementationVersion), 332 fromStringz(&vkLp.description[0]).idup, 333 ); 334 }) 335 .array; 336 } 337 338 VulkanExtensionProperties[] loadDeviceExtensions(VulkanPhysicalDevice pd, in string layerName=null) 339 { 340 import std.string : toStringz; 341 342 const(char)* layer; 343 if (layerName.length) { 344 layer = toStringz(layerName); 345 } 346 347 uint count; 348 vulkanEnforce( 349 pd.cmds.enumerateDeviceExtensionProperties(pd.vk, layer, &count, null), 350 "Could not retrieve Vulkan device extensions" 351 ); 352 if (!count) return[]; 353 354 auto vkExts = new VkExtensionProperties[count]; 355 vulkanEnforce( 356 pd.cmds.enumerateDeviceExtensionProperties(pd.vk, layer, &count, &vkExts[0]), 357 "Could not retrieve Vulkan device extensions" 358 ); 359 360 import std.algorithm : map; 361 import std.array : array; 362 import std.string : fromStringz; 363 364 return vkExts 365 .map!((ref VkExtensionProperties vkExt) { 366 return VulkanExtensionProperties( 367 fromStringz(&vkExt.extensionName[0]).idup, 368 VulkanVersion.fromUint(vkExt.specVersion) 369 ); 370 }) 371 .array; 372 } 373 374 class VulkanObj(VkType) 375 { 376 this (VkType vk) { 377 _vk = vk; 378 } 379 380 final @property VkType vk() { 381 return _vk; 382 } 383 384 private VkType _vk; 385 } 386 387 class VulkanInstObj(VkType) : Disposable 388 { 389 this (VkType vk, VulkanInstance inst) 390 { 391 _vk = vk; 392 _inst = inst; 393 _inst.retain(); 394 } 395 396 override void dispose() { 397 _inst.release(); 398 _inst = null; 399 } 400 401 final @property VkType vk() { 402 return _vk; 403 } 404 405 final @property VulkanInstance inst() { 406 return _inst; 407 } 408 409 final @property VkInstance vkInst() { 410 return _inst.vk; 411 } 412 413 private VkType _vk; 414 private VulkanInstance _inst; 415 } 416 417 final class VulkanInstance : VulkanObj!(VkInstance), Instance 418 { 419 mixin(atomicRcCode); 420 421 this(VkInstance vk) { 422 super(vk); 423 _cmds = new VkInstanceCmds(vk, _globCmds); 424 } 425 426 override void dispose() { 427 cmds.destroyInstance(vk, null); 428 } 429 430 override @property ApiProps apiProps() { 431 return vulkanApiProps; 432 } 433 434 @property VkInstanceCmds cmds() { 435 return _cmds; 436 } 437 438 override PhysicalDevice[] devices() 439 { 440 import std.array : array, uninitializedArray; 441 uint count; 442 vulkanEnforce(cmds.enumeratePhysicalDevices(vk, &count, null), 443 "Could not enumerate Vulkan devices"); 444 auto devices = uninitializedArray!(VkPhysicalDevice[])(count); 445 vulkanEnforce(cmds.enumeratePhysicalDevices(vk, &count, devices.ptr), 446 "Could not enumerate Vulkan devices"); 447 448 import std.algorithm : map; 449 return devices 450 .map!(d => cast(PhysicalDevice)(new VulkanPhysicalDevice(d, this))) 451 .array; 452 } 453 454 VkInstanceCmds _cmds; 455 } 456 457 final class VulkanPhysicalDevice : PhysicalDevice 458 { 459 mixin(atomicRcCode); 460 461 this(VkPhysicalDevice vk, VulkanInstance inst) { 462 _vk = vk; 463 _inst = inst; 464 _inst.retain(); 465 _cmds = _inst.cmds; 466 467 cmds.getPhysicalDeviceProperties(_vk, &_vkProps); 468 469 _availableLayers = loadDeviceLayers(this); 470 _availableExtensions = loadDeviceExtensions(this); 471 472 import std.algorithm : canFind, map; 473 import std.exception : enforce; 474 debug { 475 foreach (l; lunarGValidationLayers) { 476 if (_availableLayers.map!"a.layerName".canFind(l)) { 477 _openLayers ~= l; 478 } 479 } 480 } 481 version(GfxOffscreen) {} 482 else { 483 import gfx.vulkan.wsi : swapChainExtension; 484 enforce(_availableExtensions.map!"a.extensionName".canFind(swapChainExtension)); 485 _openExtensions ~= swapChainExtension; 486 } 487 } 488 489 override void dispose() { 490 _inst.release(); 491 _inst = null; 492 } 493 494 495 @property VkPhysicalDevice vk() { 496 return _vk; 497 } 498 499 @property VkInstanceCmds cmds() { 500 return _cmds; 501 } 502 503 override @property uint apiVersion() { 504 return _vkProps.apiVersion; 505 } 506 override @property uint driverVersion() { 507 return _vkProps.driverVersion; 508 } 509 override @property uint vendorId() { 510 return _vkProps.vendorID; 511 } 512 override @property uint deviceId() { 513 return _vkProps.deviceID; 514 } 515 override @property string name() { 516 import std.string : fromStringz; 517 return fromStringz(_vkProps.deviceName.ptr).idup; 518 } 519 override @property DeviceType type() { 520 return devTypeToGfx(_vkProps.deviceType); 521 } 522 override @property DeviceFeatures features() { 523 import std.algorithm : canFind, map; 524 import gfx.vulkan.wsi : swapChainExtension; 525 526 auto exts = vulkanDeviceExtensions(this); 527 528 DeviceFeatures features; 529 features.presentation = exts 530 .map!(e => e.extensionName) 531 .canFind(swapChainExtension); 532 return features; 533 } 534 override @property DeviceLimits limits() { 535 import gfx.graal.pipeline : ShaderLanguage; 536 return DeviceLimits(ShaderLanguage.spirV); 537 } 538 539 override @property MemoryProperties memoryProperties() 540 { 541 VkPhysicalDeviceMemoryProperties vkProps=void; 542 cmds.getPhysicalDeviceMemoryProperties(_vk, &vkProps); 543 544 MemoryProperties props; 545 546 foreach(i; 0 .. vkProps.memoryHeapCount) { 547 const vkHeap = vkProps.memoryHeaps[i]; 548 props.heaps ~= MemoryHeap( 549 vkHeap.size, cast(MemProps)0, (vkHeap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0 550 ); 551 } 552 foreach(i; 0 .. vkProps.memoryTypeCount) { 553 const vkMemType = vkProps.memoryTypes[i]; 554 const type = MemoryType( 555 i, vkMemType.heapIndex, props.heaps[vkMemType.heapIndex].size, 556 memPropsToGfx(vkMemType.propertyFlags) 557 ); 558 props.types ~= type; 559 props.heaps[i].props |= type.props; 560 } 561 562 return props; 563 } 564 565 override @property QueueFamily[] queueFamilies() 566 { 567 import std.array : array, uninitializedArray; 568 uint count; 569 cmds.getPhysicalDeviceQueueFamilyProperties(_vk, &count, null); 570 571 auto vkQueueFams = uninitializedArray!(VkQueueFamilyProperties[])(count); 572 cmds.getPhysicalDeviceQueueFamilyProperties(_vk, &count, vkQueueFams.ptr); 573 574 import std.algorithm : map; 575 return vkQueueFams.map!(vk => QueueFamily( 576 queueCapToGfx(vk.queueFlags), vk.queueCount 577 )).array; 578 } 579 580 override FormatProperties formatProperties(in Format format) 581 { 582 VkFormatProperties vkFp; 583 cmds.getPhysicalDeviceFormatProperties(_vk, format.toVk(), &vkFp); 584 585 return FormatProperties( 586 vkFp.linearTilingFeatures.toGfx(), 587 vkFp.optimalTilingFeatures.toGfx(), 588 vkFp.bufferFeatures.toGfx(), 589 ); 590 } 591 592 override bool supportsSurface(uint queueFamilyIndex, Surface graalSurface) { 593 auto surf = enforce( 594 cast(VulkanSurface)graalSurface, 595 "Did not pass a Vulkan surface" 596 ); 597 VkBool32 supported; 598 vulkanEnforce( 599 cmds.getPhysicalDeviceSurfaceSupportKHR(vk, queueFamilyIndex, surf.vk, &supported), 600 "Could not query vulkan surface support" 601 ); 602 return supported != VK_FALSE; 603 } 604 605 override SurfaceCaps surfaceCaps(Surface graalSurface) { 606 auto surf = enforce( 607 cast(VulkanSurface)graalSurface, 608 "Did not pass a Vulkan surface" 609 ); 610 VkSurfaceCapabilitiesKHR vkSc; 611 vulkanEnforce( 612 cmds.getPhysicalDeviceSurfaceCapabilitiesKHR(vk, surf.vk, &vkSc), 613 "Could not query vulkan surface capabilities" 614 ); 615 return vkSc.toGfx(); 616 } 617 618 override Format[] surfaceFormats(Surface graalSurface) { 619 auto surf = enforce( 620 cast(VulkanSurface)graalSurface, 621 "Did not pass a Vulkan surface" 622 ); 623 624 uint count; 625 vulkanEnforce( 626 cmds.getPhysicalDeviceSurfaceFormatsKHR(vk, surf.vk, &count, null), 627 "Could not query vulkan surface formats" 628 ); 629 auto vkSf = new VkSurfaceFormatKHR[count]; 630 vulkanEnforce( 631 cmds.getPhysicalDeviceSurfaceFormatsKHR(vk, surf.vk, &count, &vkSf[0]), 632 "Could not query vulkan surface formats" 633 ); 634 635 import std.algorithm : filter, map; 636 import std.array : array; 637 return vkSf 638 .filter!(sf => sf.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) 639 .map!(sf => sf.format.toGfx()) 640 .array; 641 } 642 643 override PresentMode[] surfacePresentModes(Surface graalSurface) { 644 auto surf = enforce( 645 cast(VulkanSurface)graalSurface, 646 "Did not pass a Vulkan surface" 647 ); 648 649 uint count; 650 vulkanEnforce( 651 cmds.getPhysicalDeviceSurfacePresentModesKHR(vk, surf.vk, &count, null), 652 "Could not query vulkan surface present modes" 653 ); 654 auto vkPms = new VkPresentModeKHR[count]; 655 vulkanEnforce( 656 cmds.getPhysicalDeviceSurfacePresentModesKHR(vk, surf.vk, &count, &vkPms[0]), 657 "Could not query vulkan surface present modes" 658 ); 659 660 import std.algorithm : filter, map; 661 import std.array : array; 662 return vkPms 663 .filter!(pm => pm.hasGfxSupport) 664 .map!(pm => pm.toGfx()) 665 .array; 666 } 667 668 override Device open(in QueueRequest[] queues) 669 { 670 import std.algorithm : map, sort; 671 import std.array : array; 672 import std.exception : enforce; 673 import std.string : toStringz; 674 675 if (!queues.length) { 676 return null; 677 } 678 679 const qcis = queues.map!((const(QueueRequest) r) { 680 VkDeviceQueueCreateInfo qci; 681 qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 682 qci.queueFamilyIndex = r.familyIndex; 683 qci.queueCount = cast(uint)r.priorities.length; 684 qci.pQueuePriorities = r.priorities.ptr; 685 return qci; 686 }).array; 687 688 const layers = _openLayers.map!toStringz.array; 689 const extensions = _openExtensions.map!toStringz.array; 690 691 VkDeviceCreateInfo ci; 692 ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 693 ci.queueCreateInfoCount = cast(uint)qcis.length; 694 ci.pQueueCreateInfos = qcis.ptr; 695 ci.enabledLayerCount = cast(uint)layers.length; 696 ci.ppEnabledLayerNames = &layers[0]; 697 ci.enabledExtensionCount = cast(uint)extensions.length; 698 ci.ppEnabledExtensionNames = &extensions[0]; 699 700 VkDevice vkDev; 701 vulkanEnforce(cmds.createDevice(_vk, &ci, null, &vkDev), 702 "Vulkan device creation failed"); 703 704 return new VulkanDevice(vkDev, this); 705 } 706 707 private VkPhysicalDevice _vk; 708 private VkPhysicalDeviceProperties _vkProps; 709 private VulkanInstance _inst; 710 711 private VkInstanceCmds _cmds; 712 713 private VulkanLayerProperties[] _availableLayers; 714 private VulkanExtensionProperties[] _availableExtensions; 715 716 private string[] _openLayers; 717 private string[] _openExtensions; 718 } 719 720 DeviceType devTypeToGfx(in VkPhysicalDeviceType vkType) 721 { 722 switch (vkType) { 723 case VK_PHYSICAL_DEVICE_TYPE_OTHER: 724 return DeviceType.other; 725 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: 726 return DeviceType.integratedGpu; 727 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: 728 return DeviceType.discreteGpu; 729 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: 730 return DeviceType.virtualGpu; 731 case VK_PHYSICAL_DEVICE_TYPE_CPU: 732 return DeviceType.cpu; 733 default: 734 assert(false, "unexpected vulkan device type constant"); 735 } 736 }