Harbor

branch main
showing the latest snapshot on main
instance.odin 4.4 KB · Plain text
gpu/core/instance.odin 0644 Raw
package gpu_core

import "core:log"
import "base:runtime"
import vk "vendor:vulkan"

Gpu_Instance :: struct {
	instance:       vk.Instance,
	debug_messenger: vk.DebugUtilsMessengerEXT,
	validation:     bool,
	logger_data:    rawptr,
}

VALIDATION_LAYER :: "VK_LAYER_KHRONOS_validation"

create_instance :: proc(app_name: cstring, enable_validation: bool) -> (inst: Gpu_Instance, ok: bool) {
	// Load Vulkan library and global procedures
	if !load_vulkan() {
		return {}, false
	}

	// Application info
	app_info := vk.ApplicationInfo{
		sType              = .APPLICATION_INFO,
		pApplicationName   = app_name,
		applicationVersion = vk.MAKE_VERSION(1, 0, 0),
		pEngineName        = "GPU",
		engineVersion      = vk.MAKE_VERSION(1, 0, 0),
		apiVersion         = vk.API_VERSION_1_2,
	}

	// Required extensions
	extensions := make([dynamic]cstring, context.temp_allocator)
	append(&extensions, vk.KHR_SURFACE_EXTENSION_NAME)

	// Platform surface extensions
	when ODIN_OS == .Linux {
		append(&extensions, vk.KHR_XLIB_SURFACE_EXTENSION_NAME)
		append(&extensions, vk.KHR_WAYLAND_SURFACE_EXTENSION_NAME)
	} else when ODIN_OS == .Windows {
		append(&extensions, vk.KHR_WIN32_SURFACE_EXTENSION_NAME)
	}

	// Validation layers -- only enable if the layer is actually available
	layer_count: u32 = 0
	layers: [1]cstring
	validation_available := false
	if enable_validation {
		validation_available = check_validation_layer_support()
		if validation_available {
			layers[0] = VALIDATION_LAYER
			layer_count = 1
			append(&extensions, vk.EXT_DEBUG_UTILS_EXTENSION_NAME)
		} else {
			log.warn("gpu/core: validation layer not available, continuing without it")
		}
	}
	inst.validation = validation_available

	create_info := vk.InstanceCreateInfo{
		sType                   = .INSTANCE_CREATE_INFO,
		pApplicationInfo        = &app_info,
		enabledExtensionCount   = u32(len(extensions)),
		ppEnabledExtensionNames = raw_data(extensions[:]),
		enabledLayerCount       = layer_count,
		ppEnabledLayerNames     = &layers[0] if layer_count > 0 else nil,
	}

	result := vk.CreateInstance(&create_info, nil, &inst.instance)
	if result != .SUCCESS {
		log.errorf("gpu/core: vkCreateInstance failed: %v", result)
		return {}, false
	}

	// Load instance-level procedures
	vk.load_proc_addresses_instance(inst.instance)

	// Setup debug messenger
	if inst.validation {
		// Capture the current logger so the debug callback can use it.
		// Vulkan callbacks use "system" calling convention (no Odin context),
		// so we pass the logger through pUserData.
		logger_ptr := new(log.Logger)
		logger_ptr^ = context.logger
		inst.logger_data = logger_ptr

		debug_info := vk.DebugUtilsMessengerCreateInfoEXT{
			sType           = .DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
			messageSeverity = {.WARNING, .ERROR},
			messageType     = {.GENERAL, .VALIDATION, .PERFORMANCE},
			pfnUserCallback = debug_callback,
			pUserData       = logger_ptr,
		}

		result = vk.CreateDebugUtilsMessengerEXT(inst.instance, &debug_info, nil, &inst.debug_messenger)
		if result != .SUCCESS {
			log.warn("gpu/core: failed to create debug messenger")
		}
	}

	return inst, true
}

destroy_instance :: proc(inst: ^Gpu_Instance) {
	if inst.debug_messenger != 0 {
		vk.DestroyDebugUtilsMessengerEXT(inst.instance, inst.debug_messenger, nil)
		inst.debug_messenger = 0
	}
	if inst.logger_data != nil {
		free(inst.logger_data)
		inst.logger_data = nil
	}
	if inst.instance != nil {
		vk.DestroyInstance(inst.instance, nil)
		inst.instance = nil
	}
}

@(private)
check_validation_layer_support :: proc() -> bool {
	count: u32
	vk.EnumerateInstanceLayerProperties(&count, nil)
	if count == 0 {
		return false
	}

	layers := make([]vk.LayerProperties, count, context.temp_allocator)
	vk.EnumerateInstanceLayerProperties(&count, raw_data(layers))

	for &layer in layers {
		name := cstring(&layer.layerName[0])
		if name == VALIDATION_LAYER {
			return true
		}
	}

	return false
}

@(private)
debug_callback :: proc "system" (
	severity: vk.DebugUtilsMessageSeverityFlagsEXT,
	msg_type: vk.DebugUtilsMessageTypeFlagsEXT,
	callback_data: ^vk.DebugUtilsMessengerCallbackDataEXT,
	user_data: rawptr,
) -> b32 {
	context = runtime.default_context()
	if user_data != nil {
		context.logger = (cast(^log.Logger)user_data)^
	}
	if callback_data != nil && callback_data.pMessage != nil {
		if .ERROR in severity {
			log.errorf("gpu/vulkan: %s", callback_data.pMessage)
		} else if .WARNING in severity {
			log.warnf("gpu/vulkan: %s", callback_data.pMessage)
		}
	}
	return false
}