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 }