1 module evael.renderer.vk.vk_device; 2 3 import erupted.vulkan_lib_loader; 4 import bindbc.glfw; 5 6 mixin(bindGLFW_Vulkan); 7 8 import evael.renderer.graphics_device; 9 import evael.renderer.vk.vk_command; 10 import evael.renderer.vk.vk_wrapper; 11 12 import evael.lib.memory; 13 import evael.lib.containers.array; 14 15 import evael.utils.functions : assumeNoGC; 16 17 import evael.system.window; 18 19 debug 20 { 21 import evael.renderer.vk.vk_debugger; 22 import std..string : fromStringz; 23 import std.experimental.logger: info, infof; 24 } 25 26 class VulkanDevice : GraphicsDevice 27 { 28 private VkInstance m_instance; 29 private VkPhysicalDevice m_physicalDevice; 30 private VkDevice m_logicalDevice; 31 private VkSurfaceKHR m_surface; 32 33 private VkQueue m_graphicsQueue; 34 private VkQueue m_presentQueue; 35 36 private uint m_graphicsFamilyIndex; 37 private uint m_presentFamilyIndex; 38 39 debug private VulkanDebugger m_debugger; 40 41 /** 42 * VkDevice constructor. 43 */ 44 @nogc 45 public this(in ref GraphicsSettings graphicsSettings) 46 { 47 super(graphicsSettings); 48 49 this.initializeGLFW(); 50 this.createInstance(); 51 this.selectPhysicalDevice(); 52 this.createLogicalDevice(); 53 } 54 55 /** 56 * VkDevice destructor. 57 */ 58 @nogc 59 public ~this() 60 { 61 vkDestroySurfaceKHR(this.m_instance, this.m_surface, null); 62 vkDestroyDevice(this.m_logicalDevice, null); 63 vkDestroyInstance(this.m_instance, null); 64 } 65 66 @nogc 67 @property 68 public override void window(GLFWwindow* win) 69 { 70 super.window = win; 71 this.createSurface(); 72 } 73 74 75 @nogc 76 public override void beginFrame(in Color color = Color.LightGrey) 77 { 78 79 } 80 81 @nogc 82 public override void endFrame() 83 { 84 85 } 86 87 /* 88 * Initializes GLFW. 89 */ 90 @nogc 91 private void initializeGLFW() 92 { 93 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 94 } 95 96 /* 97 * Creates a vulkan instance. 98 */ 99 @nogc 100 private void createInstance() 101 { 102 /** 103 * Loading functions for vulkan 104 */ 105 loadGlobalLevelFunctions(); 106 loadGLFW_Vulkan(); 107 108 debug this.m_debugger = new VulkanDebugger(); 109 110 VkApplicationInfo appInfo = { 111 sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, 112 pApplicationName: "D Game", 113 applicationVersion: VK_MAKE_VERSION(1, 0, 0), 114 pEngineName: "No Engine", 115 engineVersion: VK_MAKE_VERSION(1, 0, 0), 116 apiVersion: VK_API_VERSION_1_0 117 }; 118 119 uint glfwExtensionCount = 0; 120 auto glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); 121 122 VkInstanceCreateInfo createInfo = 123 { 124 sType: VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, 125 pApplicationInfo: &appInfo, 126 enabledExtensionCount: glfwExtensionCount, 127 ppEnabledExtensionNames: glfwExtensions, 128 enabledLayerCount: 0 129 }; 130 131 debug 132 { 133 VkDebugUtilsMessengerCreateInfoEXT debuggerInfo = this.m_debugger.info; 134 createInfo.pNext = cast(VkDebugUtilsMessengerCreateInfoEXT*) &debuggerInfo; 135 136 this.m_debugger.addDebugExtensions(createInfo, glfwExtensions, glfwExtensionCount); 137 this.m_debugger.addValidationLayers(createInfo); 138 } 139 140 enforce(vk.CreateInstance(&createInfo, null, &this.m_instance), "Error when trying to create Vulkan instance."); 141 142 debug this.m_debugger.setupCallback(this.m_instance); 143 144 loadInstanceLevelFunctions(this.m_instance); 145 } 146 147 @nogc 148 private void createSurface() 149 { 150 auto result = glfwCreateWindowSurface(this.m_instance, this.m_window, null, &this.m_surface); 151 152 // TODO: remove this trick when std.string.format is nogc 153 assumeNoGC((VkResult r) 154 { 155 import std..string : format; 156 enforce(r == VK_SUCCESS, "Error when trying to create window surface: %d.".format(r)); 157 })(result); 158 } 159 160 /* 161 * Selects a physical device. 162 */ 163 @nogc 164 private void selectPhysicalDevice() 165 { 166 uint deviceCount = 0; 167 vk.EnumeratePhysicalDevices(this.m_instance, &deviceCount, null); 168 169 enforce(deviceCount > 0, "No graphics card that can handle Vulkan."); 170 171 auto devices = Array!VkPhysicalDevice(deviceCount); 172 devices.length = deviceCount; 173 vk.EnumeratePhysicalDevices(this.m_instance, &deviceCount, devices.data.ptr); 174 175 // We select the best suitable device 176 foreach (ref device; devices) 177 { 178 if (this.isDeviceSuitable(device)) 179 { 180 this.m_physicalDevice = device; 181 } 182 } 183 184 enforce(this.m_physicalDevice !is null, "No graphics card that can handle specific Vulkan features."); 185 } 186 187 @nogc 188 private bool isDeviceSuitable(ref VkPhysicalDevice device) 189 { 190 VkPhysicalDeviceProperties deviceProperties; 191 VkPhysicalDeviceFeatures deviceFeatures; 192 193 vkGetPhysicalDeviceProperties(device, &deviceProperties); 194 vkGetPhysicalDeviceFeatures(device, &deviceFeatures); 195 196 if (deviceProperties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) 197 return false; 198 199 if (deviceFeatures.geometryShader == false) 200 return false; 201 202 uint queueFamilyCount = 0; 203 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, null); 204 205 auto queueFamilies = Array!VkQueueFamilyProperties(queueFamilyCount); 206 queueFamilies.length = queueFamilyCount; 207 208 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data.ptr); 209 210 foreach (i, ref queueFamily; queueFamilies) 211 { 212 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) 213 { 214 graphicsFamilyIndex = cast(uint) i; 215 presentFamilyIndex = cast(uint) i; 216 217 return true; 218 this.m_physicalDevice = device; 219 this.m_graphicsFamilyIndex = cast(uint) i; 220 this.m_presentFamilyIndex = cast(uint) i; 221 222 debug 223 { 224 VkPhysicalDeviceProperties properties; 225 vkGetPhysicalDeviceProperties(device, &properties); 226 info("The following physical device has been selected: "); 227 info("\tPhysical device: ", properties.deviceName.ptr.fromStringz); 228 info("\tAPI Version: ", VK_VERSION_MAJOR(properties.apiVersion), ".", VK_VERSION_MINOR(properties.apiVersion), ".", VK_VERSION_PATCH(properties.apiVersion)); 229 info("\tDriver Version: ", properties.driverVersion); 230 info("\tDevice type: ", properties.deviceType); 231 } 232 233 break; 234 } 235 } 236 } 237 238 @nogc 239 public void createLogicalDevice() 240 { 241 VkDeviceQueueCreateInfo queueCreateInfo; 242 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; 243 queueCreateInfo.queueFamilyIndex = this.m_graphicsFamilyIndex; 244 queueCreateInfo.queueCount = 1; 245 246 immutable float queuePriority = 1.0f; 247 queueCreateInfo.pQueuePriorities = &queuePriority; 248 249 VkPhysicalDeviceFeatures deviceFeatures; 250 251 VkDeviceCreateInfo createInfo = { 252 sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, 253 pQueueCreateInfos: &queueCreateInfo, 254 queueCreateInfoCount: 1, 255 pEnabledFeatures: &deviceFeatures, 256 enabledExtensionCount 257 }; 258 259 enforce( 260 vk.CreateDevice(this.m_physicalDevice, &createInfo, null, &this.m_logicalDevice), 261 "Error when trying to create the logical device." 262 ); 263 264 loadDeviceLevelFunctions(this.m_logicalDevice); 265 266 vkGetDeviceQueue(this.m_logicalDevice, this.m_graphicsFamilyIndex, 0, &this.m_graphicsQueue); 267 vkGetDeviceQueue(this.m_logicalDevice, this.m_presentFamilyIndex, 0, &this.m_presentQueue); 268 } 269 270 public uint findQueueFamilies() 271 { 272 return 0; 273 } 274 }