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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
|
# gpu.folk --
#
# Sets up the GPU device.
if {[info exists this] && $::tcl_platform(os) eq "darwin"} {
# We hard-code gpu.folk into thread 0, so we should abort if not
# running that way.
return
}
fn defineVulkanHandleType {cc type} {
$cc argtype $type [format {
#ifdef VK_USE_64_BIT_PTR_DEFINES
%s $argname; sscanf(Jim_String($obj), "(%s) 0x%%p", &$argname);
#else
%s $argname; sscanf(Jim_String($obj), "(%s) 0x%%llx", &$argname);
#endif
} $type $type $type $type]
# Tcl_ObjPrintf doesn't work with %lld/%llx for some reason,
# so we do it by hand.
$cc rtype $type [format {
#ifdef VK_USE_64_BIT_PTR_DEFINES
$robj = Jim_ObjPrintf("(%s) 0x%%" PRIxPTR, (uintptr_t) $rvalue);
#else
{
char buf[100]; snprintf(buf, 100, "(%s) 0x%%llx", $rvalue);
$robj = Jim_NewStringObj(buf, -1);
}
#endif
} $type $type]
}
Claim the GPU Vulkan handle type definer is [fn defineVulkanHandleType]
When $::thisNode has had displays enumerated &\
/someone/ wishes $::thisNode uses display /display/ with /...any/ {
set useGlfw $($display eq "glfw")
# First, we build the C GPU library, which can make direct calls into
# Vulkan -- this library then exposes functions that we can call from
# Tcl.
set gpuc [C]
$gpuc cflags -I./vendor
$gpuc include <pthread.h>
$gpuc code {
#define VOLK_IMPLEMENTATION
#include "volk/volk.h"
}
if {$useGlfw} {
$gpuc code {
// This must be included _after_ volk.h (Vulkan).
#include <GLFW/glfw3.h>
}
}
set macos [expr {$::tcl_platform(os) eq "darwin"}]
if {$macos} {
$gpuc cflags -I/opt/homebrew/include -L/opt/homebrew/lib
}
if {$useGlfw} {
$gpuc endcflags -lglfw
} else {
foreach renderFile [glob -nocomplain "/dev/dri/render*"] {
if {![file readable $renderFile]} {
puts stderr "Gpu: Warning: $renderFile is not readable by current user; Vulkan device may not appear.
Try doing `sudo chmod 666 $renderFile`."
}
}
}
$gpuc argtype VkResult { long $argname; __ENSURE_OK(Jim_GetLong(interp, $obj, &$argname)); }
$gpuc proc VkResultToString {VkResult res} char* {
switch (res) {
#define CASE(x) case VK_##x: return #x;
CASE(SUCCESS) CASE(NOT_READY)
CASE(TIMEOUT) CASE(EVENT_SET)
CASE(EVENT_RESET) CASE(INCOMPLETE)
CASE(ERROR_OUT_OF_HOST_MEMORY) CASE(ERROR_OUT_OF_DEVICE_MEMORY)
CASE(ERROR_INITIALIZATION_FAILED) CASE(ERROR_DEVICE_LOST)
CASE(ERROR_MEMORY_MAP_FAILED) CASE(ERROR_LAYER_NOT_PRESENT)
CASE(ERROR_EXTENSION_NOT_PRESENT) CASE(ERROR_FEATURE_NOT_PRESENT)
CASE(ERROR_INCOMPATIBLE_DRIVER) CASE(ERROR_TOO_MANY_OBJECTS)
CASE(ERROR_FORMAT_NOT_SUPPORTED) CASE(ERROR_FRAGMENTED_POOL)
CASE(ERROR_UNKNOWN) CASE(ERROR_OUT_OF_POOL_MEMORY)
CASE(ERROR_INVALID_EXTERNAL_HANDLE) CASE(ERROR_FRAGMENTATION)
CASE(ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS)
CASE(PIPELINE_COMPILE_REQUIRED) CASE(ERROR_SURFACE_LOST_KHR)
CASE(ERROR_NATIVE_WINDOW_IN_USE_KHR) CASE(SUBOPTIMAL_KHR)
CASE(ERROR_OUT_OF_DATE_KHR) CASE(ERROR_INCOMPATIBLE_DISPLAY_KHR)
CASE(ERROR_VALIDATION_FAILED_EXT) CASE(ERROR_INVALID_SHADER_NV)
#ifdef VK_ENABLE_BETA_EXTENSIONS
CASE(ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR)
CASE(ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR)
CASE(ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR)
CASE(ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR)
CASE(ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR)
CASE(ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR)
#endif
CASE(ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT)
CASE(ERROR_NOT_PERMITTED_KHR)
CASE(ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
CASE(THREAD_IDLE_KHR) CASE(THREAD_DONE_KHR)
CASE(OPERATION_DEFERRED_KHR) CASE(OPERATION_NOT_DEFERRED_KHR)
default: return "unknown";
}
#undef CASE
}
local proc vktry {call} { string map {\n " "} [csubst {{
VkResult res = $call;
if (res != VK_SUCCESS) {
fprintf(stderr, "Failed $call: %s (%d)\n",
VkResultToString(res), res); exit(1);
}
}}] }
$gpuc define {
VkInstance instance;
VkPhysicalDevice physicalDevice;
VkPhysicalDeviceFeatures physicalDeviceFeatures;
VkPhysicalDeviceProperties physicalDeviceProperties;
VkDevice device;
uint32_t computeQueueFamilyIndex;
uint32_t graphicsQueueFamilyIndex = UINT32_MAX;
pthread_mutex_t graphicsQueueMutex;
VkQueue graphicsQueue;
VkQueue presentQueue;
VkQueue computeQueue;
}
defineVulkanHandleType $gpuc VkPhysicalDevice
defineVulkanHandleType $gpuc VkDevice
$gpuc proc getPhysicalDevice {} VkPhysicalDevice { return physicalDevice; }
$gpuc proc getDevice {} VkDevice { return device; }
$gpuc proc init {bool useGlfw} void {
$[vktry volkInitialize()]
$[if {$useGlfw} { expr {"glfwInit();"} }]
// Set up VkInstance instance:
{
VkInstanceCreateInfo createInfo = {0};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
const char* validationLayers[] = {
"VK_LAYER_KHRONOS_validation"
};
createInfo.enabledLayerCount = sizeof(validationLayers)/sizeof(validationLayers[0]);
createInfo.ppEnabledLayerNames = validationLayers;
$[if {$macos} {subst -nocommands {
const char* enabledExtensions[] = {
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
VK_KHR_SURFACE_EXTENSION_NAME,
"VK_EXT_metal_surface",
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
};
createInfo.enabledExtensionCount = sizeof(enabledExtensions)/sizeof(enabledExtensions[0]);
}} elseif {$useGlfw} {subst -nocommands {
const char** enabledExtensions = glfwGetRequiredInstanceExtensions(&createInfo.enabledExtensionCount);
}} else {subst -nocommands {
const char* enabledExtensions[] = {
// 2 extensions for non-X11/Wayland display
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_DISPLAY_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
};
createInfo.enabledExtensionCount = sizeof(enabledExtensions)/sizeof(enabledExtensions[0]);
}}]
createInfo.ppEnabledExtensionNames = enabledExtensions;
$[if {$macos} { expr {{
createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
}} }]
VkResult res = vkCreateInstance(&createInfo, NULL, &instance);
if (res != VK_SUCCESS) {
FOLK_ERROR("Failed vkCreateInstance: %s (%d)\n",
VkResultToString(res), res);
if (res == VK_ERROR_LAYER_NOT_PRESENT) {
FOLK_ERROR("It looks like a required layer is missing.\n"
"Did you install `vulkan-validationlayers`?\n");
}
}
}
volkLoadInstance(instance);
// Set up VkPhysicalDevice physicalDevice
{
uint32_t physicalDeviceCount = 0;
vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, NULL);
if (physicalDeviceCount == 0) {
FOLK_ERROR("Failed to find Vulkan physical device\n");
}
printf("gpu: Found %d Vulkan devices\n", physicalDeviceCount);
VkPhysicalDevice physicalDevices[physicalDeviceCount];
vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices);
physicalDevice = physicalDevices[0];
vkGetPhysicalDeviceFeatures(physicalDevice, &physicalDeviceFeatures);
vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
}
computeQueueFamilyIndex = UINT32_MAX; {
uint32_t queueFamilyCount = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, NULL);
VkQueueFamilyProperties queueFamilies[queueFamilyCount];
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies);
for (int i = 0; i < queueFamilyCount; i++) {
if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT) {
computeQueueFamilyIndex = i;
}
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphicsQueueFamilyIndex = i;
break;
}
}
if (graphicsQueueFamilyIndex == UINT32_MAX) {
fprintf(stderr, "Failed to find a Vulkan graphics queue family\n"); exit(1);
}
if (computeQueueFamilyIndex == UINT32_MAX) {
fprintf(stderr, "Failed to find a Vulkan compute queue family\n"); exit(1);
}
}
// Set up VkDevice device
{
VkDeviceQueueCreateInfo queueCreateInfo = {0};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
queueCreateInfo.queueCount = 1;
float queuePriority = 1.0f;
queueCreateInfo.pQueuePriorities = &queuePriority;
VkPhysicalDeviceFeatures deviceFeatures = {0};
$[if {$macos} {subst -nocommands {
const char *deviceExtensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
"VK_KHR_portability_subset",
VK_KHR_MAINTENANCE3_EXTENSION_NAME
};
}} elseif {$useGlfw} {subst -nocommands {
const char *deviceExtensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
}} else {subst -nocommands {
const char *deviceExtensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
VK_KHR_MAINTENANCE3_EXTENSION_NAME
};
}}]
VkDeviceCreateInfo createInfo = {0};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.pQueueCreateInfos = &queueCreateInfo;
createInfo.queueCreateInfoCount = 1;
createInfo.pEnabledFeatures = &deviceFeatures;
createInfo.enabledLayerCount = 0;
createInfo.enabledExtensionCount = sizeof(deviceExtensions)/sizeof(deviceExtensions[0]);
createInfo.ppEnabledExtensionNames = deviceExtensions;
/* VkPhysicalDeviceDescriptorIndexingFeatures descriptorIndexingFeatures = {0}; */
/* descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES; */
/* descriptorIndexingFeatures.descriptorBindingPartiallyBound = VK_TRUE; */
/* // TODO: Do we need more descriptor indexing features? */
/* createInfo.pNext = &descriptorIndexingFeatures; */
$[vktry {vkCreateDevice(physicalDevice, &createInfo, NULL, &device)}]
}
// Set up VkQueue graphicsQueue and VkQueue presentQueue and VkQueue computeQueue
{
pthread_mutex_init(&graphicsQueueMutex, NULL);
vkGetDeviceQueue(device, graphicsQueueFamilyIndex, 0, &graphicsQueue);
presentQueue = graphicsQueue;
computeQueue = graphicsQueue;
}
}
$gpuc proc getDoesSupportShaderSampledImageArrayDynamicIndexing {} bool {
return physicalDeviceFeatures.shaderSampledImageArrayDynamicIndexing;
}
$gpuc proc getMaxTextures {} int {
int maxTextures = physicalDeviceProperties.limits.maxPerStageDescriptorSamplers;
// COMBINED_IMAGE_SAMPLER counts against both sampler and sampled image limits.
int maxSampledImages = physicalDeviceProperties.limits.maxPerStageDescriptorSampledImages;
if (maxSampledImages < maxTextures) { maxTextures = maxSampledImages; }
// If it's actually a low cap (often 16), stick with that.
if (maxTextures < 128) { return maxTextures; }
// HACK: for now, I don't want to deal with all the other descriptor caps etc.
return 128;
}
set gpuLib [$gpuc compile]
$gpuLib init $useGlfw
Claim the GPU library is $gpuLib
Claim the GPU physical device is [$gpuLib getPhysicalDevice]
Claim the GPU device is [$gpuLib getDevice]
}
|