1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
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
}