Monad Engine da02fba2
> Undercity Codex_
Loading...
Searching...
No Matches
Context.cpp
Go to the documentation of this file.
2
3#include <cstring>
4#include <set>
5#include <stdexcept>
6#include <string>
7
8#define GLFW_INCLUDE_VULKAN
9#include <GLFW/glfw3.h>
10
11namespace mnd::vk
12{
13namespace
14{
15constexpr const char *kValidationLayerName = "VK_LAYER_KHRONOS_validation";
16
17const std::vector<const char *> kRequiredDeviceExtensions = {
18 VK_KHR_SWAPCHAIN_EXTENSION_NAME,
19};
20
21void Check(VkResult result, const char *what)
22{
23 if (result != VK_SUCCESS)
24 {
25 throw std::runtime_error(std::string("vk: ") + what + " failed (VkResult=" +
26 std::to_string(static_cast<int>(result)) + ")");
27 }
28}
29
30bool ValidationLayerAvailable()
31{
32 uint32_t count = 0;
33 vkEnumerateInstanceLayerProperties(&count, nullptr);
34 std::vector<VkLayerProperties> layers(count);
35 vkEnumerateInstanceLayerProperties(&count, layers.data());
36 for (const auto &layer : layers)
37 {
38 if (std::strcmp(layer.layerName, kValidationLayerName) == 0)
39 {
40 return true;
41 }
42 }
43 return false;
44}
45
46VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
47 VkDebugUtilsMessageTypeFlagsEXT /*type*/,
48 const VkDebugUtilsMessengerCallbackDataEXT *data,
49 void * /*userData*/)
50{
51 // Forward to stderr so the preview tool can run without the engine logger.
52 if (severity >= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
53 {
54 fprintf(stderr, "[vk] %s\n", data->pMessage);
55 }
56 return VK_FALSE;
57}
58
59QueueFamilies FindQueueFamilies(VkPhysicalDevice device, VkSurfaceKHR surface)
60{
61 QueueFamilies out;
62 uint32_t count = 0;
63 vkGetPhysicalDeviceQueueFamilyProperties(device, &count, nullptr);
64 std::vector<VkQueueFamilyProperties> families(count);
65 vkGetPhysicalDeviceQueueFamilyProperties(device, &count, families.data());
66
67 for (uint32_t i = 0; i < count; ++i)
68 {
69 if (families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
70 {
71 out.graphics = i;
72 }
73 VkBool32 presentSupport = VK_FALSE;
74 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
75 if (presentSupport)
76 {
77 out.present = i;
78 }
79 if (out.IsComplete())
80 {
81 break;
82 }
83 }
84 return out;
85}
86
87bool DeviceHasRequiredExtensions(VkPhysicalDevice device)
88{
89 uint32_t count = 0;
90 vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr);
91 std::vector<VkExtensionProperties> extensions(count);
92 vkEnumerateDeviceExtensionProperties(device, nullptr, &count, extensions.data());
93
94 for (const char *required : kRequiredDeviceExtensions)
95 {
96 bool found = false;
97 for (const auto &ext : extensions)
98 {
99 if (std::strcmp(ext.extensionName, required) == 0)
100 {
101 found = true;
102 break;
103 }
104 }
105 if (!found)
106 {
107 return false;
108 }
109 }
110 return true;
111}
112
113bool DeviceHasUsableSwapchain(VkPhysicalDevice device, VkSurfaceKHR surface)
114{
115 uint32_t formatCount = 0;
116 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
117 uint32_t presentModeCount = 0;
118 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
119 return formatCount > 0 && presentModeCount > 0;
120}
121} // namespace
122
123Context::Context(GLFWwindow *window, bool enableValidation)
124 : m_enableValidation(enableValidation && ValidationLayerAvailable())
125{
126 CreateInstance();
127 if (m_enableValidation)
128 {
129 CreateDebugMessenger();
130 }
131 CreateSurface(window);
132 PickPhysicalDevice();
133 CreateLogicalDevice();
134}
135
137{
138 if (m_device)
139 {
140 vkDestroyDevice(m_device, nullptr);
141 }
142 if (m_debugMessenger)
143 {
144 auto destroyFn = reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
145 vkGetInstanceProcAddr(m_instance, "vkDestroyDebugUtilsMessengerEXT"));
146 if (destroyFn)
147 {
148 destroyFn(m_instance, m_debugMessenger, nullptr);
149 }
150 }
151 if (m_surface)
152 {
153 vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
154 }
155 if (m_instance)
156 {
157 vkDestroyInstance(m_instance, nullptr);
158 }
159}
160
161void Context::CreateInstance()
162{
163 VkApplicationInfo app{};
164 app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
165 app.pApplicationName = "monad-engine vulkan preview";
166 app.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
167 app.pEngineName = "monad-engine";
168 app.engineVersion = VK_MAKE_VERSION(0, 1, 0);
169 app.apiVersion = VK_API_VERSION_1_2;
170
171 uint32_t glfwExtCount = 0;
172 const char **glfwExt = glfwGetRequiredInstanceExtensions(&glfwExtCount);
173
174 std::vector<const char *> extensions(glfwExt, glfwExt + glfwExtCount);
175 if (m_enableValidation)
176 {
177 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
178 }
179
180 VkInstanceCreateInfo info{};
181 info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
182 info.pApplicationInfo = &app;
183 info.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
184 info.ppEnabledExtensionNames = extensions.data();
185
186 const char *layers[] = { kValidationLayerName };
187 if (m_enableValidation)
188 {
189 info.enabledLayerCount = 1;
190 info.ppEnabledLayerNames = layers;
191 }
192
193 Check(vkCreateInstance(&info, nullptr, &m_instance), "vkCreateInstance");
194}
195
196void Context::CreateDebugMessenger()
197{
198 VkDebugUtilsMessengerCreateInfoEXT info{};
199 info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
200 info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
201 VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
202 info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
203 VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
204 VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
205 info.pfnUserCallback = DebugCallback;
206
207 auto createFn = reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
208 vkGetInstanceProcAddr(m_instance, "vkCreateDebugUtilsMessengerEXT"));
209 if (!createFn)
210 {
211 return;
212 }
213 Check(createFn(m_instance, &info, nullptr, &m_debugMessenger),
214 "vkCreateDebugUtilsMessengerEXT");
215}
216
217void Context::CreateSurface(GLFWwindow *window)
218{
219 Check(glfwCreateWindowSurface(m_instance, window, nullptr, &m_surface),
220 "glfwCreateWindowSurface");
221}
222
223void Context::PickPhysicalDevice()
224{
225 uint32_t count = 0;
226 vkEnumeratePhysicalDevices(m_instance, &count, nullptr);
227 if (count == 0)
228 {
229 throw std::runtime_error("vk: no physical devices found");
230 }
231 std::vector<VkPhysicalDevice> devices(count);
232 vkEnumeratePhysicalDevices(m_instance, &count, devices.data());
233
234 // Score: discrete GPU > integrated > anything else, gated on swapchain support.
235 int bestScore = -1;
236 VkPhysicalDevice bestDevice = VK_NULL_HANDLE;
237 QueueFamilies bestFamilies;
238
239 for (auto device : devices)
240 {
241 if (!DeviceHasRequiredExtensions(device)) continue;
242 if (!DeviceHasUsableSwapchain(device, m_surface)) continue;
243
244 QueueFamilies fam = FindQueueFamilies(device, m_surface);
245 if (!fam.IsComplete()) continue;
246
247 VkPhysicalDeviceProperties props;
248 vkGetPhysicalDeviceProperties(device, &props);
249 int score = 0;
250 if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) score = 1000;
251 if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) score = 500;
252 if (score > bestScore)
253 {
254 bestScore = score;
255 bestDevice = device;
256 bestFamilies = fam;
257 }
258 }
259
260 if (bestDevice == VK_NULL_HANDLE)
261 {
262 throw std::runtime_error("vk: no suitable physical device");
263 }
264 m_physicalDevice = bestDevice;
265 m_queueFamilies = bestFamilies;
266}
267
268void Context::CreateLogicalDevice()
269{
270 std::set<uint32_t> uniqueFamilies = { m_queueFamilies.graphics, m_queueFamilies.present };
271 std::vector<VkDeviceQueueCreateInfo> queueInfos;
272 queueInfos.reserve(uniqueFamilies.size());
273 float priority = 1.0f;
274 for (uint32_t family : uniqueFamilies)
275 {
276 VkDeviceQueueCreateInfo qci{};
277 qci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
278 qci.queueFamilyIndex = family;
279 qci.queueCount = 1;
280 qci.pQueuePriorities = &priority;
281 queueInfos.push_back(qci);
282 }
283
284 VkPhysicalDeviceFeatures features{};
285
286 VkDeviceCreateInfo info{};
287 info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
288 info.queueCreateInfoCount = static_cast<uint32_t>(queueInfos.size());
289 info.pQueueCreateInfos = queueInfos.data();
290 info.pEnabledFeatures = &features;
291 info.enabledExtensionCount = static_cast<uint32_t>(kRequiredDeviceExtensions.size());
292 info.ppEnabledExtensionNames = kRequiredDeviceExtensions.data();
293
294 Check(vkCreateDevice(m_physicalDevice, &info, nullptr, &m_device), "vkCreateDevice");
295
296 vkGetDeviceQueue(m_device, m_queueFamilies.graphics, 0, &m_graphicsQueue);
297 vkGetDeviceQueue(m_device, m_queueFamilies.present, 0, &m_presentQueue);
298}
299
300} // namespace mnd::vk
Vulkan instance + physical/logical device + queue selection.
Context(GLFWwindow *window, bool enableValidation)
Definition Context.cpp:123
bool IsComplete() const
Definition Context.h:27