MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKDevice.cpp
Go to the documentation of this file.
1#include "VKDevice.hpp"
4
5#include "set"
6
7namespace MayaFlux::Core {
8
13
15 : m_physical_device(other.m_physical_device)
16 , m_logical_device(other.m_logical_device)
17 , m_graphics_queue(other.m_graphics_queue)
18 , m_compute_queue(other.m_compute_queue)
19 , m_transfer_queue(other.m_transfer_queue)
20 , m_queue_families(other.m_queue_families)
21{
22 other.m_physical_device = VK_NULL_HANDLE;
23 other.m_logical_device = VK_NULL_HANDLE;
24 other.m_graphics_queue = VK_NULL_HANDLE;
25 other.m_compute_queue = VK_NULL_HANDLE;
26 other.m_transfer_queue = VK_NULL_HANDLE;
27}
28
30{
31 if (this != &other) {
32 cleanup();
33 m_physical_device = other.m_physical_device;
34 m_logical_device = other.m_logical_device;
35 m_graphics_queue = other.m_graphics_queue;
36 m_compute_queue = other.m_compute_queue;
37 m_transfer_queue = other.m_transfer_queue;
38 m_queue_families = other.m_queue_families;
39
40 other.m_physical_device = VK_NULL_HANDLE;
41 other.m_logical_device = VK_NULL_HANDLE;
42 other.m_graphics_queue = VK_NULL_HANDLE;
43 other.m_compute_queue = VK_NULL_HANDLE;
44 other.m_transfer_queue = VK_NULL_HANDLE;
45 }
46 return *this;
47}
48
49bool VKDevice::initialize(vk::Instance instance, vk::SurfaceKHR /*temp_surface*/, const GraphicsBackendInfo& backend_info)
50{
51 if (!pick_physical_device(instance, nullptr)) {
52 return false;
53 }
54
55 return create_logical_device(instance, backend_info);
56}
57
59{
60 if (m_logical_device) {
61 m_logical_device.destroy();
62 m_logical_device = nullptr;
63 MF_INFO(Journal::Component::Core, Journal::Context::GraphicsBackend, "Vulkan logical device destroyed.");
64 }
65 m_physical_device = nullptr;
66 m_graphics_queue = nullptr;
67 m_compute_queue = nullptr;
68 m_transfer_queue = nullptr;
70}
71
72bool VKDevice::pick_physical_device(vk::Instance instance, vk::SurfaceKHR /*temp_surface*/)
73{
74 vk::Instance vk_instance(instance);
75 auto devices = vk_instance.enumeratePhysicalDevices();
76
77 if (devices.empty()) {
79 std::source_location::current(),
80 "Failed to find GPUs with Vulkan support!");
81 }
82
83 for (const auto& device : devices) {
84 QueueFamilyIndices indices = find_queue_families(device, nullptr);
85
86 if (indices.graphics_family.has_value()) {
87 auto available_extensions = device.enumerateDeviceExtensionProperties();
88 bool supports_swapchain = false;
89 bool supports_mesh_shader = false;
90
91 for (const auto& ext : available_extensions) {
92 if (strcmp(ext.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
93 supports_swapchain = true;
94 }
95 if (strcmp(ext.extensionName, VK_EXT_MESH_SHADER_EXTENSION_NAME) == 0) {
96 supports_mesh_shader = true;
97 }
98 }
99
100 if (!supports_swapchain) {
102 "Physical device {} does not support VK_KHR_swapchain - skipping",
103 device.getProperties().deviceName.data());
104 continue;
105 }
106
107 if (!supports_mesh_shader) {
109 "Physical device {} does not support VK_EXT_mesh_shader - "
110 "mesh shading features will be unavailable",
111 device.getProperties().deviceName.data());
112 } else {
113 vk::PhysicalDeviceMeshShaderFeaturesEXT mesh_features;
114 vk::PhysicalDeviceFeatures2 temp_features;
115 temp_features.pNext = &mesh_features;
116 device.getFeatures2(&temp_features);
117
118 if (mesh_features.meshShader == VK_FALSE || mesh_features.taskShader == VK_FALSE) {
120 "Physical device {} supports VK_EXT_mesh_shader extension but features disabled",
121 device.getProperties().deviceName.data());
122 supports_mesh_shader = false;
123 }
124 }
125
126 m_physical_device = device;
127 m_queue_families = indices;
128 m_supports_mesh_shaders = supports_mesh_shader;
129
130 vk::PhysicalDeviceProperties props = device.getProperties();
132 "Selected GPU: {}", props.deviceName.data());
133 return true;
134 }
135 }
136
138 std::source_location::current(),
139 "Failed to find a suitable GPU!");
140 return false;
141}
142
143QueueFamilyIndices VKDevice::find_queue_families(vk::PhysicalDevice device, vk::SurfaceKHR surface)
144{
145 QueueFamilyIndices indices;
146 auto queue_families = device.getQueueFamilyProperties();
147
148 int i = 0;
149 for (const auto& queue_family : queue_families) {
150 if (queue_family.queueCount > 0 && queue_family.queueFlags & vk::QueueFlagBits::eGraphics) {
151 indices.graphics_family = i;
152 }
153
154 if (queue_family.queueCount > 0 && queue_family.queueFlags & vk::QueueFlagBits::eCompute && !(queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) {
155 indices.compute_family = i;
156 }
157
158 if (queue_family.queueCount > 0 && queue_family.queueFlags & vk::QueueFlagBits::eTransfer && !(queue_family.queueFlags & vk::QueueFlagBits::eGraphics) && !(queue_family.queueFlags & vk::QueueFlagBits::eCompute)) {
159 indices.transfer_family = i;
160 }
161
162 if (surface && queue_family.queueCount > 0) {
163 vk::Bool32 presentSupport = device.getSurfaceSupportKHR(i, surface);
164 if (presentSupport) {
165 indices.present_family = i;
167 "Found presentation support in queue family {}", i);
168 }
169 }
170
171 i++;
172 }
173
174 if (indices.graphics_family.has_value()) {
175 if (!indices.compute_family.has_value()) {
176 indices.compute_family = indices.graphics_family;
177 }
178 if (!indices.transfer_family.has_value()) {
179 indices.transfer_family = indices.graphics_family;
180 }
181 }
182
183 return indices;
184}
185
186bool VKDevice::update_presentation_queue(vk::SurfaceKHR surface)
187{
188 if (!surface) {
190 "No surface provided for presentation support check");
191 return false;
192 }
193
194 auto queue_families = m_physical_device.getQueueFamilyProperties();
195
196 for (uint32_t i = 0; i < queue_families.size(); i++) {
197 if (queue_families[i].queueCount > 0) {
198 vk::Bool32 presentSupport = m_physical_device.getSurfaceSupportKHR(i, surface);
199 if (presentSupport) {
200 if (i == m_queue_families.graphics_family.value()) {
204 "Graphics queue family {} supports presentation", i);
205 return true;
206 }
207 }
208 }
209 }
210
211 for (uint32_t i = 0; i < queue_families.size(); i++) {
212 if (queue_families[i].queueCount > 0) {
213 vk::Bool32 presentSupport = m_physical_device.getSurfaceSupportKHR(i, surface);
214 if (presentSupport) {
218 "Found presentation support in queue family {}", i);
219 return true;
220 }
221 }
222 }
223
225 "No queue family with presentation support found!");
226 return false;
227}
228
230{
231 std::vector<vk::ExtensionProperties> availableExtensions = m_physical_device.enumerateDeviceExtensionProperties();
232
233 MF_PRINT(Journal::Component::Core, Journal::Context::GraphicsBackend, "Available physical device extensions:");
234 for (const auto& extension : availableExtensions) {
235 std::cout << "\t- " << extension.extensionName << " (Version: " << extension.specVersion << ")\n";
236 }
238}
239
240bool VKDevice::create_logical_device(vk::Instance instance, const GraphicsBackendInfo& backend_info)
241{
242 if (!m_queue_families.graphics_family.has_value()) {
244 std::source_location::current(),
245 "No graphics queue family found!");
246 }
247
248 std::set<uint32_t> unique_queue_families;
249 unique_queue_families.insert(m_queue_families.graphics_family.value());
250
251 if (m_queue_families.compute_family.has_value()) {
252 unique_queue_families.insert(m_queue_families.compute_family.value());
253 }
254
256 unique_queue_families.insert(m_queue_families.transfer_family.value());
257 }
258
259 std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
260 float queue_priority = 1.0F;
261
262 for (uint32_t queue_family : unique_queue_families) {
263 vk::DeviceQueueCreateInfo queue_create_info {};
264 queue_create_info.queueFamilyIndex = queue_family;
265 queue_create_info.queueCount = 1;
266 queue_create_info.pQueuePriorities = &queue_priority;
267 queue_create_infos.push_back(queue_create_info);
268 }
269
270 vk::PhysicalDeviceFeatures device_features {};
271 device_features.samplerAnisotropy = backend_info.required_features.sampler_anisotropy;
272 device_features.geometryShader = backend_info.required_features.geometry_shaders;
273 device_features.tessellationShader = backend_info.required_features.tessellation_shaders;
274 device_features.multiViewport = backend_info.required_features.multi_viewport;
275 device_features.fillModeNonSolid = backend_info.required_features.fill_mode_non_solid;
276
277 vk::PhysicalDeviceFeatures2 features2 {};
278 features2.features = device_features;
279
280 vk::PhysicalDeviceVulkan13Features vulkan_13_features {};
281 vulkan_13_features.dynamicRendering = VK_TRUE;
282 vulkan_13_features.synchronization2 = VK_TRUE;
283
284 std::vector<const char*> device_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };
285
286#ifdef MAYAFLUX_PLATFORM_MACOS
287 auto available_exts = m_physical_device.enumerateDeviceExtensionProperties();
288 bool has_portability = std::ranges::any_of(available_exts,
289 [](const auto& ext) {
290 return strcmp(ext.extensionName, "VK_KHR_portability_subset") == 0;
291 });
292
293 if (has_portability) {
294 device_extensions.push_back("VK_KHR_portability_subset");
295 }
296#else
297 vk::PhysicalDeviceMeshShaderFeaturesEXT mesh_shader_features {};
299 device_extensions.push_back(VK_EXT_MESH_SHADER_EXTENSION_NAME);
300
301 mesh_shader_features.taskShader = VK_TRUE;
302 mesh_shader_features.meshShader = VK_TRUE;
303
304 vulkan_13_features.pNext = &mesh_shader_features;
305 }
306#endif
307
308 features2.pNext = &vulkan_13_features;
309
310 for (const auto& ext : backend_info.required_extensions) {
311 device_extensions.push_back(ext.c_str());
312 }
313
314 vk::DeviceCreateInfo create_info {};
315 create_info.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size());
316 create_info.pQueueCreateInfos = queue_create_infos.data();
317 create_info.pNext = &features2;
318 create_info.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size());
319 create_info.ppEnabledExtensionNames = device_extensions.data();
320
321 try {
322 m_logical_device = m_physical_device.createDevice(create_info);
323 VULKAN_HPP_DEFAULT_DISPATCHER.init(m_logical_device);
324
325 } catch (const std::exception& e) {
327 std::source_location::current(),
328 "Failed to create logical device: {}", e.what());
329 }
330
332
333 if (backend_info.enable_compute_queue && m_queue_families.compute_family.has_value()) {
335 } else {
337 }
338
339 if (backend_info.enable_transfer_queue && m_queue_families.transfer_family.has_value()) {
341 } else {
343 }
344
345 return true;
346}
347
348}
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_PRINT(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
void query_supported_extensions()
Query and log supported device extensions.
Definition VKDevice.cpp:229
bool pick_physical_device(vk::Instance instance, vk::SurfaceKHR temp_surface)
Pick a suitable physical device (GPU)
Definition VKDevice.cpp:72
void cleanup()
Cleanup device resources.
Definition VKDevice.cpp:58
bool m_supports_mesh_shaders
Whether the device supports mesh shaders.
Definition VKDevice.hpp:151
VKDevice & operator=(const VKDevice &)=delete
vk::PhysicalDevice m_physical_device
Selected physical device (GPU)
Definition VKDevice.hpp:115
vk::Queue m_compute_queue
Compute queue handle.
Definition VKDevice.hpp:119
bool create_logical_device(vk::Instance instance, const GraphicsBackendInfo &backend_info)
Create the logical device and retrieve queue handles.
Definition VKDevice.cpp:240
vk::Queue m_transfer_queue
Transfer queue handle.
Definition VKDevice.hpp:120
vk::Device m_logical_device
Logical device handle.
Definition VKDevice.hpp:116
QueueFamilyIndices find_queue_families(vk::PhysicalDevice device, vk::SurfaceKHR surface=nullptr)
Find queue families on the given physical device.
Definition VKDevice.cpp:143
bool update_presentation_queue(vk::SurfaceKHR surface)
Update presentation queue family for a specific surface.
Definition VKDevice.cpp:186
bool m_presentation_initialized
Whether presentation support has been initialized.
Definition VKDevice.hpp:149
vk::Queue m_graphics_queue
Graphics queue handle.
Definition VKDevice.hpp:118
bool initialize(vk::Instance instance, vk::SurfaceKHR temp_surface, const GraphicsBackendInfo &backend_info)
Initialize device (pick physical device and create logical device)
Definition VKDevice.cpp:49
QueueFamilyIndices m_queue_families
Indices of required queue families.
Definition VKDevice.hpp:122
Manages Vulkan physical device selection and logical device creation.
Definition VKDevice.hpp:32
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
bool enable_compute_queue
Enable compute queue (separate from graphics)
std::vector< std::string > required_extensions
Backend-specific extensions to request.
bool enable_transfer_queue
Enable transfer queue (separate from graphics)
struct MayaFlux::Core::GraphicsBackendInfo::@0 required_features
Required device features (Vulkan-specific)
Configuration for graphics API backend (Vulkan/OpenGL/etc.)
std::optional< uint32_t > transfer_family
Definition VKDevice.hpp:16
std::optional< uint32_t > graphics_family
Definition VKDevice.hpp:14
std::optional< uint32_t > present_family
Definition VKDevice.hpp:17
std::optional< uint32_t > compute_family
Definition VKDevice.hpp:15
Stores indices of queue families we need.
Definition VKDevice.hpp:13