package gpu_core import "core:log" import vk "vendor:vulkan" Queue_Families :: struct { graphics: u32, present: u32, compute: u32, has_graphics: bool, has_present: bool, has_compute: bool, } Gpu_Device :: struct { physical_device: vk.PhysicalDevice, device: vk.Device, queue_families: Queue_Families, graphics_queue: vk.Queue, present_queue: vk.Queue, compute_queue: vk.Queue, properties: vk.PhysicalDeviceProperties, } @(private) required_device_extensions := [?]cstring{ vk.KHR_SWAPCHAIN_EXTENSION_NAME, } create_device :: proc(instance: vk.Instance, surface: vk.SurfaceKHR) -> (dev: Gpu_Device, ok: bool) { // Enumerate physical devices device_count: u32 vk.EnumeratePhysicalDevices(instance, &device_count, nil) if device_count == 0 { log.error("gpu/core: no Vulkan-capable GPU found") return {}, false } devices := make([]vk.PhysicalDevice, device_count, context.temp_allocator) vk.EnumeratePhysicalDevices(instance, &device_count, raw_data(devices)) // Pick best device (prefer discrete GPU) best_score := -1 for d in devices { score := rate_device(d, surface) if score > best_score { best_score = score dev.physical_device = d } } if best_score < 0 { log.error("gpu/core: no suitable GPU found") return {}, false } vk.GetPhysicalDeviceProperties(dev.physical_device, &dev.properties) log.infof("gpu/core: selected GPU: %s", dev.properties.deviceName) // Find queue families dev.queue_families = find_queue_families(dev.physical_device, surface) if !dev.queue_families.has_graphics || !dev.queue_families.has_present { log.error("gpu/core: GPU missing required queue families") return {}, false } // Create logical device unique_families: [3]u32 unique_count: u32 = 0 add_unique :: proc(arr: ^[3]u32, count: ^u32, val: u32) { for i in 0.. (dev: Gpu_Device, ok: bool) { device_count: u32 vk.EnumeratePhysicalDevices(instance, &device_count, nil) if device_count == 0 { log.error("gpu/core: no Vulkan-capable GPU found") return {}, false } devices := make([]vk.PhysicalDevice, device_count, context.temp_allocator) vk.EnumeratePhysicalDevices(instance, &device_count, raw_data(devices)) best_score := -1 for d in devices { score := rate_device_headless(d) if score > best_score { best_score = score dev.physical_device = d } } if best_score < 0 { log.error("gpu/core: no suitable headless GPU found") return {}, false } vk.GetPhysicalDeviceProperties(dev.physical_device, &dev.properties) log.infof("gpu/core: selected headless GPU: %s", dev.properties.deviceName) dev.queue_families = find_queue_families_headless(dev.physical_device) if !dev.queue_families.has_graphics || !dev.queue_families.has_compute { log.error("gpu/core: headless GPU missing graphics or compute queue") return {}, false } unique_families: [2]u32 unique_count: u32 = 0 add_unique :: proc(arr: ^[2]u32, count: ^u32, val: u32) { for i in 0.. Queue_Families { families: Queue_Families count: u32 vk.GetPhysicalDeviceQueueFamilyProperties(device, &count, nil) props := make([]vk.QueueFamilyProperties, count, context.temp_allocator) vk.GetPhysicalDeviceQueueFamilyProperties(device, &count, raw_data(props)) for p, i in props { idx := u32(i) if .GRAPHICS in p.queueFlags && !families.has_graphics { families.graphics = idx families.has_graphics = true } if .COMPUTE in p.queueFlags && !families.has_compute { families.compute = idx families.has_compute = true } present_support: b32 vk.GetPhysicalDeviceSurfaceSupportKHR(device, idx, surface, &present_support) if present_support && !families.has_present { families.present = idx families.has_present = true } } return families } @(private) find_queue_families_headless :: proc(device: vk.PhysicalDevice) -> Queue_Families { families: Queue_Families count: u32 vk.GetPhysicalDeviceQueueFamilyProperties(device, &count, nil) props := make([]vk.QueueFamilyProperties, count, context.temp_allocator) vk.GetPhysicalDeviceQueueFamilyProperties(device, &count, raw_data(props)) for p, i in props { idx := u32(i) if .GRAPHICS in p.queueFlags && !families.has_graphics { families.graphics = idx families.has_graphics = true } if .COMPUTE in p.queueFlags && !families.has_compute { families.compute = idx families.has_compute = true } } return families } @(private) rate_device :: proc(device: vk.PhysicalDevice, surface: vk.SurfaceKHR) -> int { props: vk.PhysicalDeviceProperties vk.GetPhysicalDeviceProperties(device, &props) // Check required extensions ext_count: u32 vk.EnumerateDeviceExtensionProperties(device, nil, &ext_count, nil) exts := make([]vk.ExtensionProperties, ext_count, context.temp_allocator) vk.EnumerateDeviceExtensionProperties(device, nil, &ext_count, raw_data(exts)) for required in required_device_extensions { found := false for &ext in exts { name := cstring(raw_data(&ext.extensionName)) if name == required { found = true break } } if !found { return -1 } } // Check queue families families := find_queue_families(device, surface) if !families.has_graphics || !families.has_present { return -1 } // Check swapchain support format_count: u32 vk.GetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, nil) present_count: u32 vk.GetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_count, nil) if format_count == 0 || present_count == 0 { return -1 } // Score score := 0 if props.deviceType == .DISCRETE_GPU { score += 1000 } else if props.deviceType == .INTEGRATED_GPU { score += 100 } return score } @(private) rate_device_headless :: proc(device: vk.PhysicalDevice) -> int { props: vk.PhysicalDeviceProperties vk.GetPhysicalDeviceProperties(device, &props) families := find_queue_families_headless(device) if !families.has_graphics || !families.has_compute { return -1 } ext_count: u32 vk.EnumerateDeviceExtensionProperties(device, nil, &ext_count, nil) exts := make([]vk.ExtensionProperties, ext_count, context.temp_allocator) vk.EnumerateDeviceExtensionProperties(device, nil, &ext_count, raw_data(exts)) for required in required_device_extensions { found := false for &ext in exts { name := cstring(raw_data(&ext.extensionName)) if name == required { found = true break } } if !found { return -1 } } score := 0 if props.deviceType == .DISCRETE_GPU { score += 1000 } else if props.deviceType == .INTEGRATED_GPU { score += 100 } return score }