1 /// Vulkan implementation of GrAAL 2 module gfx.vulkan; 3 4 import gfx.bindings.vulkan; 5 import gfx.core.log : LogTag; 6 import gfx.graal; 7 8 enum gfxVkLogMask = 0x2000_0000; 9 package(gfx) immutable gfxVkLog = LogTag("GFX-VK", gfxVkLogMask); 10 11 // some standard layers 12 13 immutable lunarGValidationLayers = [ 14 "VK_LAYER_LUNARG_core_validation", 15 "VK_LAYER_LUNARG_standard_validation", 16 "VK_LAYER_LUNARG_parameter_validation", 17 ]; 18 19 immutable debugReportInstanceExtensions = [ 20 "VK_KHR_debug_report", "VK_EXT_debug_report" 21 ]; 22 23 @property ApiProps vulkanApiProps() 24 { 25 import gfx.math.proj : ndc, XYClip, ZClip; 26 27 return ApiProps( 28 "vulkan", ndc(XYClip.rightHand, ZClip.zeroToOne) 29 ); 30 } 31 32 /// Load global level vulkan functions, and instance level layers and extensions 33 /// This function must be called before any other in this module 34 void vulkanInit() 35 { 36 _globCmds = loadVulkanGlobalCmds(); 37 _instanceLayers = loadInstanceLayers(); 38 _instanceExtensions = loadInstanceExtensions(); 39 } 40 41 struct VulkanVersion 42 { 43 import std.bitmanip : bitfields; 44 mixin(bitfields!( 45 uint, "patch", 12, 46 uint, "minor", 10, 47 uint, "major", 10, 48 )); 49 50 this (in uint major, in uint minor, in uint patch) { 51 this.major = major; this.minor = minor; this.patch = patch; 52 } 53 54 this (in uint vkVer) { 55 this(VK_VERSION_MAJOR(vkVer), VK_VERSION_MINOR(vkVer), VK_VERSION_PATCH(vkVer)); 56 } 57 58 static VulkanVersion fromUint(in uint vkVer) { 59 return *cast(VulkanVersion*)(cast(void*)&vkVer); 60 } 61 62 uint toUint() const { 63 return *cast(uint*)(cast(void*)&this); 64 } 65 66 string toString() { 67 import std.format : format; 68 return format("VulkanVersion(%s, %s, %s)", this.major, this.minor, this.patch); 69 } 70 } 71 72 unittest { 73 const vkVer = VK_MAKE_VERSION(12, 7, 38); 74 auto vv = VulkanVersion.fromUint(vkVer); 75 assert(vv.major == 12); 76 assert(vv.minor == 7); 77 assert(vv.patch == 38); 78 assert(vv.toUint() == vkVer); 79 } 80 81 struct VulkanLayerProperties 82 { 83 string layerName; 84 VulkanVersion specVer; 85 VulkanVersion implVer; 86 string description; 87 88 @property VulkanExtensionProperties[] instanceExtensions() 89 { 90 return loadInstanceExtensions(layerName); 91 } 92 } 93 94 struct VulkanExtensionProperties 95 { 96 string extensionName; 97 VulkanVersion specVer; 98 } 99 100 /// Retrieve available instance level layer properties 101 @property VulkanLayerProperties[] vulkanInstanceLayers() { 102 return _instanceLayers; 103 } 104 /// Retrieve available instance level extensions properties 105 @property VulkanExtensionProperties[] vulkanInstanceExtensions() 106 { 107 return _instanceExtensions; 108 } 109 110 /// Options to create a Vulkan instance. 111 struct VulkanCreateInfo 112 { 113 /// Application name and version. 114 string appName; 115 /// ditto 116 VulkanVersion appVersion = VulkanVersion(0, 0, 0); 117 118 /// Mandatory layers that are needed by the application. 119 /// Instance creation will fail if one is not present. 120 const(string)[] mandatoryLayers; 121 /// Optional layers that will be enabled if present. 122 const(string)[] optionalLayers; 123 124 /// Mandatory extensions that are needed by the application. 125 /// Instance creation will fail if one is not present. 126 const(string)[] mandatoryExtensions; 127 /// Optional extensions that will be enabled if present. 128 const(string)[] optionalExtensions; 129 130 /// Build VulkanCreateInfo with default extensions, suitable for 131 /// 3D graphics on a surface. 132 /// Debug builds have by default Lunar-G validation layers and debug 133 /// extensions. 134 static VulkanCreateInfo defaultExts(string appName = "", 135 VulkanVersion appVersion = VulkanVersion(0, 0, 0)) 136 { 137 VulkanCreateInfo info; 138 info.appName = appName; 139 info.appVersion = appVersion; 140 debug 141 { 142 info.optionalLayers = lunarGValidationLayers; 143 info.optionalExtensions = debugReportInstanceExtensions; 144 } 145 info.mandatoryExtensions = surfaceInstanceExtensions; 146 return info; 147 } 148 } 149 150 /// Creates an Instance object with Vulkan backend with options 151 VulkanInstance createVulkanInstance(VulkanCreateInfo createInfo = VulkanCreateInfo.defaultExts()) 152 { 153 import gfx : gfxVersionMaj, gfxVersionMin, gfxVersionMic; 154 import std.algorithm : all, canFind, map; 155 import std.array : array; 156 import std.exception : enforce; 157 import std.range : chain; 158 import std.string : toStringz; 159 160 // throw if some requested layers or extensions are not available 161 // TODO: specific exception 162 foreach (l; createInfo.mandatoryLayers) { 163 enforce( 164 _instanceLayers.map!(il => il.layerName).canFind(l), 165 "Could not find layer " ~ l ~ " when creating Vulkan instance" 166 ); 167 } 168 foreach (e; createInfo.mandatoryExtensions) { 169 enforce( 170 _instanceExtensions.map!(ie => ie.extensionName).canFind(e), 171 "Could not find extension " ~ e ~ " when creating Vulkan instance" 172 ); 173 } 174 175 const(string)[] layers = createInfo.mandatoryLayers; 176 foreach (l; createInfo.optionalLayers) { 177 if (_instanceLayers.map!(il => il.layerName).canFind(l)) { 178 layers ~= l; 179 } 180 else { 181 gfxVkLog.warningf("Optional layer %s is not present and won't be enabled", l); 182 } 183 } 184 185 const(string)[] extensions = createInfo.mandatoryExtensions; 186 foreach (e; createInfo.optionalExtensions) { 187 if (_instanceExtensions.map!(ie => ie.extensionName).canFind(e)) { 188 extensions ~= e; 189 } 190 else { 191 gfxVkLog.warningf("Optional extension %s is not present and won't be enabled", e); 192 } 193 } 194 195 VkApplicationInfo ai; 196 ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; 197 if (createInfo.appName.length) { 198 ai.pApplicationName = toStringz(createInfo.appName); 199 } 200 ai.applicationVersion = createInfo.appVersion.toUint(); 201 ai.pEngineName = "gfx-d\0".ptr; 202 ai.engineVersion = VK_MAKE_VERSION(gfxVersionMaj, gfxVersionMin, gfxVersionMic); 203 ai.apiVersion = VK_API_VERSION_1_0; 204 205 const vkLayers = layers.map!toStringz.array; 206 const vkExts = extensions.map!toStringz.array; 207 208 gfxVkLog.info("Opening Vulkan instance."); 209 gfxVkLog.infof("Vulkan layers:%s", layers.length?"":" none"); 210 foreach (l; layers) { 211 gfxVkLog.infof(" %s", l); 212 } 213 gfxVkLog.infof("Vulkan extensions:%s", extensions.length?"":" none"); 214 import std.algorithm : canFind, filter, map; 215 import std.array : array; 216 import std.range : chain; 217 foreach (e; extensions) { 218 gfxVkLog.infof(" %s", e); 219 } 220 221 VkInstanceCreateInfo ici; 222 ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; 223 ici.pApplicationInfo = &ai; 224 ici.enabledLayerCount = cast(uint)vkLayers.length; 225 ici.ppEnabledLayerNames = vkLayers.ptr; 226 ici.enabledExtensionCount = cast(uint)vkExts.length; 227 ici.ppEnabledExtensionNames = vkExts.ptr; 228 229 auto vk = _globCmds; 230 VkInstance vkInst; 231 vulkanEnforce(vk.CreateInstance(&ici, null, &vkInst), "Could not create Vulkan instance"); 232 233 return new VulkanInstance(vkInst, layers, extensions); 234 } 235 236 /// Retrieve available device level layers 237 @property VulkanLayerProperties[] vulkanDeviceLayers(PhysicalDevice device) { 238 auto pd = cast(VulkanPhysicalDevice)device; 239 if (!pd) return []; 240 241 return pd._availableLayers; 242 } 243 /// Retrieve available instance level extensions properties 244 VulkanExtensionProperties[] vulkanDeviceExtensions(PhysicalDevice device, in string layerName=null) 245 { 246 auto pd = cast(VulkanPhysicalDevice)device; 247 if (!pd) return []; 248 249 if (!layerName) { 250 return pd._availableExtensions; 251 } 252 else { 253 return pd.loadDeviceExtensions(layerName); 254 } 255 } 256 257 void overrideDeviceOpenVulkanLayers(PhysicalDevice device, string[] layers) 258 { 259 auto pd = cast(VulkanPhysicalDevice)device; 260 if (!pd) return; 261 262 pd._openLayers = layers; 263 } 264 265 void overrideDeviceOpenVulkanExtensions(PhysicalDevice device, string[] extensions) 266 { 267 auto pd = cast(VulkanPhysicalDevice)device; 268 if (!pd) return; 269 270 pd._openExtensions = extensions; 271 } 272 273 274 package: 275 276 import gfx.core.rc; 277 import gfx.graal.device; 278 import gfx.graal.format; 279 import gfx.graal.memory; 280 import gfx.graal.presentation; 281 import gfx.graal.queue; 282 import gfx.vulkan.conv; 283 import gfx.vulkan.device; 284 import gfx.vulkan.error; 285 import gfx.vulkan.wsi : VulkanSurface; 286 287 import std.exception : enforce; 288 289 __gshared VkGlobalCmds _globCmds; 290 __gshared VulkanLayerProperties[] _instanceLayers; 291 __gshared VulkanExtensionProperties[] _instanceExtensions; 292 293 VulkanLayerProperties[] loadInstanceLayers() 294 { 295 auto vk = _globCmds; 296 uint count; 297 vulkanEnforce( 298 vk.EnumerateInstanceLayerProperties(&count, null), 299 "Could not retrieve Vulkan instance layers" 300 ); 301 if (!count) return[]; 302 303 auto vkLayers = new VkLayerProperties[count]; 304 vulkanEnforce( 305 vk.EnumerateInstanceLayerProperties(&count, &vkLayers[0]), 306 "Could not retrieve Vulkan instance layers" 307 ); 308 309 import std.algorithm : map; 310 import std.array : array; 311 import std.string : fromStringz; 312 313 return vkLayers 314 .map!((ref VkLayerProperties vkLp) { 315 return VulkanLayerProperties( 316 fromStringz(&vkLp.layerName[0]).idup, 317 VulkanVersion.fromUint(vkLp.specVersion), 318 VulkanVersion.fromUint(vkLp.implementationVersion), 319 fromStringz(&vkLp.description[0]).idup, 320 ); 321 }) 322 .array; 323 } 324 325 VulkanExtensionProperties[] loadInstanceExtensions(in string layerName=null) 326 { 327 import std.string : toStringz; 328 329 const(char)* layer; 330 if (layerName.length) { 331 layer = toStringz(layerName); 332 } 333 auto vk = _globCmds; 334 uint count; 335 vulkanEnforce( 336 vk.EnumerateInstanceExtensionProperties(layer, &count, null), 337 "Could not retrieve Vulkan instance extensions" 338 ); 339 if (!count) return[]; 340 341 auto vkExts = new VkExtensionProperties[count]; 342 vulkanEnforce( 343 vk.EnumerateInstanceExtensionProperties(layer, &count, &vkExts[0]), 344 "Could not retrieve Vulkan instance extensions" 345 ); 346 347 import std.algorithm : map; 348 import std.array : array; 349 import std.string : fromStringz; 350 351 return vkExts 352 .map!((ref VkExtensionProperties vkExt) { 353 return VulkanExtensionProperties( 354 fromStringz(&vkExt.extensionName[0]).idup, 355 VulkanVersion.fromUint(vkExt.specVersion) 356 ); 357 }) 358 .array; 359 } 360 361 VulkanLayerProperties[] loadDeviceLayers(VulkanPhysicalDevice pd) 362 { 363 auto vk = pd.vk; 364 uint count; 365 vulkanEnforce( 366 vk.EnumerateDeviceLayerProperties(pd.vkObj, &count, null), 367 "Could not retrieve Vulkan device layers" 368 ); 369 if (!count) return[]; 370 371 auto vkLayers = new VkLayerProperties[count]; 372 vulkanEnforce( 373 vk.EnumerateDeviceLayerProperties(pd.vkObj, &count, &vkLayers[0]), 374 "Could not retrieve Vulkan device layers" 375 ); 376 377 import std.algorithm : map; 378 import std.array : array; 379 import std.string : fromStringz; 380 381 return vkLayers 382 .map!((ref VkLayerProperties vkLp) { 383 return VulkanLayerProperties( 384 fromStringz(&vkLp.layerName[0]).idup, 385 VulkanVersion.fromUint(vkLp.specVersion), 386 VulkanVersion.fromUint(vkLp.implementationVersion), 387 fromStringz(&vkLp.description[0]).idup, 388 ); 389 }) 390 .array; 391 } 392 393 VulkanExtensionProperties[] loadDeviceExtensions(VulkanPhysicalDevice pd, in string layerName=null) 394 { 395 import std.string : toStringz; 396 397 const(char)* layer; 398 if (layerName.length) { 399 layer = toStringz(layerName); 400 } 401 402 auto vk = pd.vk; 403 uint count; 404 vulkanEnforce( 405 vk.EnumerateDeviceExtensionProperties(pd.vkObj, layer, &count, null), 406 "Could not retrieve Vulkan device extensions" 407 ); 408 if (!count) return[]; 409 410 auto vkExts = new VkExtensionProperties[count]; 411 vulkanEnforce( 412 vk.EnumerateDeviceExtensionProperties(pd.vkObj, layer, &count, &vkExts[0]), 413 "Could not retrieve Vulkan device extensions" 414 ); 415 416 import std.algorithm : map; 417 import std.array : array; 418 import std.string : fromStringz; 419 420 return vkExts 421 .map!((ref VkExtensionProperties vkExt) { 422 return VulkanExtensionProperties( 423 fromStringz(&vkExt.extensionName[0]).idup, 424 VulkanVersion.fromUint(vkExt.specVersion) 425 ); 426 }) 427 .array; 428 } 429 430 class VulkanObj(VkType) 431 { 432 this (VkType vkObj) { 433 _vkObj = vkObj; 434 } 435 436 final @property VkType vkObj() { 437 return _vkObj; 438 } 439 440 private VkType _vkObj; 441 } 442 443 class VulkanInstObj(VkType) : Disposable 444 { 445 this (VkType vkObj, VulkanInstance inst) 446 { 447 _vkObj = vkObj; 448 _inst = inst; 449 _inst.retain(); 450 } 451 452 override void dispose() { 453 _inst.release(); 454 _inst = null; 455 } 456 457 final @property VkType vkObj() { 458 return _vkObj; 459 } 460 461 final @property VulkanInstance inst() { 462 return _inst; 463 } 464 465 final @property VkInstance vkInst() { 466 return _inst.vkObj; 467 } 468 469 private VkType _vkObj; 470 private VulkanInstance _inst; 471 } 472 473 final class VulkanInstance : VulkanObj!(VkInstance), Instance 474 { 475 mixin(atomicRcCode); 476 477 this(VkInstance vkObj, in string[] layers, in string[] extensions) { 478 super(vkObj); 479 _vk = new VkInstanceCmds(vkObj, _globCmds); 480 this.layers = layers; 481 this.extensions = extensions; 482 } 483 484 override void dispose() { 485 if (_vkCb) { 486 vk.DestroyDebugReportCallbackEXT(vkObj, _vkCb, null); 487 _vkCb = VK_NULL_ND_HANDLE; 488 _callback = null; 489 } 490 vk.DestroyInstance(vkObj, null); 491 } 492 493 override @property Backend backend() { 494 return Backend.vulkan; 495 } 496 497 override @property ApiProps apiProps() { 498 return vulkanApiProps; 499 } 500 501 @property VkInstanceCmds vk() { 502 return _vk; 503 } 504 505 override PhysicalDevice[] devices() 506 { 507 import std.algorithm : map; 508 import std.array : array, uninitializedArray; 509 510 if (!_phDs.length) { 511 uint count; 512 vulkanEnforce(vk.EnumeratePhysicalDevices(vkObj, &count, null), 513 "Could not enumerate Vulkan devices"); 514 auto devices = uninitializedArray!(VkPhysicalDevice[])(count); 515 vulkanEnforce(vk.EnumeratePhysicalDevices(vkObj, &count, devices.ptr), 516 "Could not enumerate Vulkan devices"); 517 518 _phDs = devices 519 .map!(vkD => cast(PhysicalDevice)new VulkanPhysicalDevice(vkD, this)) 520 .array(); 521 } 522 return _phDs; 523 } 524 525 override void setDebugCallback(DebugCallback callback) { 526 VkDebugReportCallbackCreateInfoEXT ci; 527 ci.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; 528 ci.flags = 0x1f; 529 ci.pfnCallback = &gfxd_vk_DebugReportCallback; 530 ci.pUserData = cast(void*)this; 531 532 vk.CreateDebugReportCallbackEXT(vkObj, &ci, null, &_vkCb); 533 _callback = callback; 534 } 535 536 /// The layers enabled with this instance 537 public const(string[]) layers; 538 /// The extensions enabled with this instance 539 public const(string[]) extensions; 540 541 private VkInstanceCmds _vk; 542 private PhysicalDevice[] _phDs; 543 private VkDebugReportCallbackEXT _vkCb; 544 private DebugCallback _callback; 545 } 546 547 extern(C) nothrow { 548 VkBool32 gfxd_vk_DebugReportCallback(VkDebugReportFlagsEXT flags, 549 VkDebugReportObjectTypeEXT /+objectType+/, 550 ulong /+object+/, 551 size_t /+location+/, 552 int /+messageCode+/, 553 const(char)* pLayerPrefix, 554 const(char)* pMessage, 555 void* pUserData) 556 { 557 auto vkInst = cast(VulkanInstance)pUserData; 558 if (vkInst && vkInst._callback) { 559 import gfx.vulkan.conv : debugReportFlagsToGfx; 560 import std.string : fromStringz; 561 try { 562 vkInst._callback(debugReportFlagsToGfx(flags), fromStringz(pMessage).idup); 563 } 564 catch(Exception ex) { 565 import std.exception : collectException; 566 import std.stdio : stderr; 567 collectException( 568 stderr.writefln("Exception thrown in debug callback: %s", ex.msg) 569 ); 570 } 571 } 572 573 return VK_FALSE; 574 } 575 } 576 577 version(glfw) { 578 extern(C) @nogc nothrow int glfwGetPhysicalDevicePresentationSupport(VkInstance, VkPhysicalDevice, uint); 579 } 580 581 final class VulkanPhysicalDevice : PhysicalDevice 582 { 583 this(VkPhysicalDevice vkObj, VulkanInstance inst) { 584 _vkObj = vkObj; 585 _inst = inst; 586 _vk = _inst.vk; 587 588 vk.GetPhysicalDeviceProperties(_vkObj, &_vkProps); 589 590 _availableLayers = loadDeviceLayers(this); 591 _availableExtensions = loadDeviceExtensions(this); 592 593 import std.algorithm : canFind, map; 594 import std.exception : enforce; 595 debug { 596 foreach (l; lunarGValidationLayers) { 597 if (_availableLayers.map!"a.layerName".canFind(l)) { 598 _openLayers ~= l; 599 } 600 } 601 } 602 version(GfxOffscreen) {} 603 else { 604 import gfx.vulkan.wsi : swapChainDeviceExtension; 605 enforce(_availableExtensions.map!"a.extensionName".canFind(swapChainDeviceExtension)); 606 _openExtensions ~= swapChainDeviceExtension; 607 } 608 } 609 610 @property VkPhysicalDevice vkObj() { 611 return _vkObj; 612 } 613 614 @property VkInstanceCmds vk() { 615 return _vk; 616 } 617 618 override @property Instance instance() 619 { 620 auto inst = lockObj(_inst); 621 if (!inst) return null; 622 return giveAwayObj(inst); 623 } 624 625 override @property string name() { 626 import std.string : fromStringz; 627 return fromStringz(_vkProps.deviceName.ptr).idup; 628 } 629 override @property DeviceType type() { 630 return devTypeToGfx(_vkProps.deviceType); 631 } 632 override @property DeviceFeatures features() { 633 import std.algorithm : canFind, map; 634 import gfx.vulkan.wsi : swapChainDeviceExtension; 635 636 VkPhysicalDeviceFeatures vkFeats; 637 vk.GetPhysicalDeviceFeatures(vkObj, &vkFeats); 638 639 DeviceFeatures features; 640 features.anisotropy = vkFeats.samplerAnisotropy == VK_TRUE; 641 features.presentation = vulkanDeviceExtensions(this) 642 .map!(e => e.extensionName) 643 .canFind(swapChainDeviceExtension); 644 return features; 645 } 646 /// See_Also: <a href="https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceLimits.html">VkPhysicalDeviceLimits</a> 647 override @property DeviceLimits limits() 648 { 649 DeviceLimits limits; 650 limits.linearOptimalGranularity = 651 cast(size_t)_vkProps.limits.bufferImageGranularity; 652 limits.maxStorageBufferSize = 653 cast(size_t)_vkProps.limits.maxStorageBufferRange; 654 limits.maxDescriptorSetStorageBuffers = 655 cast(size_t)_vkProps.limits.maxDescriptorSetStorageBuffers; 656 limits.maxDescriptorSetStorageBuffersDynamic = 657 cast(size_t)_vkProps.limits.maxDescriptorSetStorageBuffersDynamic; 658 limits.minStorageBufferOffsetAlignment = 659 cast(size_t)_vkProps.limits.minStorageBufferOffsetAlignment; 660 limits.maxPushConstantsSize = 661 cast(size_t)_vkProps.limits.maxPushConstantsSize; 662 limits.maxUniformBufferSize = 663 cast(size_t)_vkProps.limits.maxUniformBufferRange; 664 limits.maxDescriptorSetUniformBuffers = 665 cast(size_t)_vkProps.limits.maxDescriptorSetUniformBuffers; 666 limits.maxDescriptorSetUniformBuffersDynamic = 667 cast(size_t)_vkProps.limits.maxDescriptorSetUniformBuffersDynamic; 668 limits.minUniformBufferOffsetAlignment = 669 cast(size_t)_vkProps.limits.minUniformBufferOffsetAlignment; 670 return limits; 671 } 672 673 override @property MemoryProperties memoryProperties() 674 { 675 VkPhysicalDeviceMemoryProperties vkProps=void; 676 vk.GetPhysicalDeviceMemoryProperties(_vkObj, &vkProps); 677 678 MemoryProperties props; 679 680 foreach(i; 0 .. vkProps.memoryHeapCount) { 681 const vkHeap = vkProps.memoryHeaps[i]; 682 props.heaps ~= MemoryHeap( 683 cast(size_t)vkHeap.size, (vkHeap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0 684 ); 685 } 686 foreach(i; 0 .. vkProps.memoryTypeCount) { 687 const vkMemType = vkProps.memoryTypes[i]; 688 props.types ~= MemoryType( 689 memPropsToGfx(vkMemType.propertyFlags), 690 vkMemType.heapIndex, 691 ); 692 } 693 694 return props; 695 } 696 697 override @property QueueFamily[] queueFamilies() 698 { 699 import std.array : array, uninitializedArray; 700 uint count; 701 vk.GetPhysicalDeviceQueueFamilyProperties(_vkObj, &count, null); 702 703 auto vkQueueFams = uninitializedArray!(VkQueueFamilyProperties[])(count); 704 vk.GetPhysicalDeviceQueueFamilyProperties(_vkObj, &count, vkQueueFams.ptr); 705 706 import std.algorithm : map; 707 return vkQueueFams.map!(vkObj => QueueFamily( 708 queueCapToGfx(vkObj.queueFlags), vkObj.queueCount 709 )).array; 710 } 711 712 override FormatProperties formatProperties(in Format format) 713 { 714 VkFormatProperties vkFp; 715 vk.GetPhysicalDeviceFormatProperties(_vkObj, format.toVk(), &vkFp); 716 717 return FormatProperties( 718 vkFp.linearTilingFeatures.toGfx(), 719 vkFp.optimalTilingFeatures.toGfx(), 720 vkFp.bufferFeatures.toGfx(), 721 ); 722 } 723 724 override bool supportsSurface(uint queueFamilyIndex, Surface graalSurface) { 725 auto surf = enforce( 726 cast(VulkanSurface)graalSurface, 727 "Did not pass a Vulkan surface" 728 ); 729 VkBool32 supported; 730 vulkanEnforce( 731 vk.GetPhysicalDeviceSurfaceSupportKHR(vkObj, queueFamilyIndex, surf.vkObj, &supported), 732 "Could not query vulkan surface support" 733 ); 734 735 version(glfw) { 736 import bindbc.glfw : GLFW_FALSE; 737 738 const supportsGlfw = glfwGetPhysicalDevicePresentationSupport(_inst.vkObj, _vkObj, queueFamilyIndex); 739 return supported != VK_FALSE && supportsGlfw != GLFW_FALSE; 740 } else { 741 return supported != VK_FALSE; 742 } 743 } 744 745 override SurfaceCaps surfaceCaps(Surface graalSurface) { 746 auto surf = enforce( 747 cast(VulkanSurface)graalSurface, 748 "Did not pass a Vulkan surface" 749 ); 750 VkSurfaceCapabilitiesKHR vkSc; 751 vulkanEnforce( 752 vk.GetPhysicalDeviceSurfaceCapabilitiesKHR(vkObj, surf.vkObj, &vkSc), 753 "Could not query vulkan surface capabilities" 754 ); 755 return vkSc.toGfx(); 756 } 757 758 override Format[] surfaceFormats(Surface graalSurface) { 759 auto surf = enforce( 760 cast(VulkanSurface)graalSurface, 761 "Did not pass a Vulkan surface" 762 ); 763 764 uint count; 765 vulkanEnforce( 766 vk.GetPhysicalDeviceSurfaceFormatsKHR(vkObj, surf.vkObj, &count, null), 767 "Could not query vulkan surface formats" 768 ); 769 auto vkSf = new VkSurfaceFormatKHR[count]; 770 vulkanEnforce( 771 vk.GetPhysicalDeviceSurfaceFormatsKHR(vkObj, surf.vkObj, &count, &vkSf[0]), 772 "Could not query vulkan surface formats" 773 ); 774 775 import std.algorithm : filter, map; 776 import std.array : array; 777 return vkSf 778 .filter!(sf => sf.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) 779 .map!(sf => sf.format.toGfx()) 780 .array; 781 } 782 783 override PresentMode[] surfacePresentModes(Surface graalSurface) { 784 auto surf = enforce( 785 cast(VulkanSurface)graalSurface, 786 "Did not pass a Vulkan surface" 787 ); 788 789 uint count; 790 vulkanEnforce( 791 vk.GetPhysicalDeviceSurfacePresentModesKHR(vkObj, surf.vkObj, &count, null), 792 "Could not query vulkan surface present modes" 793 ); 794 auto vkPms = new VkPresentModeKHR[count]; 795 vulkanEnforce( 796 vk.GetPhysicalDeviceSurfacePresentModesKHR(vkObj, surf.vkObj, &count, &vkPms[0]), 797 "Could not query vulkan surface present modes" 798 ); 799 800 import std.algorithm : filter, map; 801 import std.array : array; 802 return vkPms 803 .filter!(pm => pm.hasGfxSupport) 804 .map!(pm => pm.toGfx()) 805 .array; 806 } 807 808 override Device open(in QueueRequest[] queues, in DeviceFeatures features=DeviceFeatures.all) 809 { 810 import std.algorithm : filter, map, sort; 811 import std.array : array; 812 import std.exception : enforce; 813 import std.string : toStringz; 814 import gfx.vulkan.wsi : swapChainDeviceExtension; 815 816 if (!queues.length) { 817 return null; 818 } 819 820 const qcis = queues.map!((const(QueueRequest) r) { 821 VkDeviceQueueCreateInfo qci; 822 qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 823 qci.queueFamilyIndex = r.familyIndex; 824 qci.queueCount = cast(uint)r.priorities.length; 825 qci.pQueuePriorities = r.priorities.ptr; 826 return qci; 827 }).array; 828 829 const layers = _openLayers.map!toStringz.array; 830 const extensions = _openExtensions 831 .filter!(e => e != swapChainDeviceExtension || features.presentation) 832 .map!toStringz.array; 833 VkPhysicalDeviceFeatures vkFeats; 834 vkFeats.samplerAnisotropy = features.anisotropy ? VK_TRUE : VK_FALSE; 835 836 VkDeviceCreateInfo ci; 837 ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; 838 ci.queueCreateInfoCount = cast(uint)qcis.length; 839 ci.pQueueCreateInfos = qcis.ptr; 840 ci.enabledLayerCount = cast(uint)layers.length; 841 ci.ppEnabledLayerNames = layers.ptr; 842 ci.enabledExtensionCount = cast(uint)extensions.length; 843 ci.ppEnabledExtensionNames = extensions.ptr; 844 ci.pEnabledFeatures = &vkFeats; 845 846 VkDevice vkDev; 847 vulkanEnforce(vk.CreateDevice(_vkObj, &ci, null, &vkDev), 848 "Vulkan device creation failed"); 849 850 return new VulkanDevice(vkDev, this, _inst); 851 } 852 853 private VkPhysicalDevice _vkObj; 854 private VkPhysicalDeviceProperties _vkProps; 855 private VulkanInstance _inst; 856 857 private VkInstanceCmds _vk; 858 859 private VulkanLayerProperties[] _availableLayers; 860 private VulkanExtensionProperties[] _availableExtensions; 861 862 private string[] _openLayers; 863 private string[] _openExtensions; 864 } 865 866 DeviceType devTypeToGfx(in VkPhysicalDeviceType vkType) 867 { 868 switch (vkType) { 869 case VK_PHYSICAL_DEVICE_TYPE_OTHER: 870 return DeviceType.other; 871 case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: 872 return DeviceType.integratedGpu; 873 case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: 874 return DeviceType.discreteGpu; 875 case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: 876 return DeviceType.virtualGpu; 877 case VK_PHYSICAL_DEVICE_TYPE_CPU: 878 return DeviceType.cpu; 879 default: 880 assert(false, "unexpected vulkan device type constant"); 881 } 882 }