1 /*
2 * Copyright © 2024 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /*
25 * Copyright (C) 2015-2021 Valve Corporation
26 * Copyright (C) 2015-2021 LunarG, Inc.
27 *
28 * Licensed under the Apache License, Version 2.0 (the "License");
29 * you may not use this file except in compliance with the License.
30 * You may obtain a copy of the License at
31 *
32 * http://www.apache.org/licenses/LICENSE-2.0
33 *
34 * Unless required by applicable law or agreed to in writing, software
35 * distributed under the License is distributed on an "AS IS" BASIS,
36 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
37 * See the License for the specific language governing permissions and
38 * limitations under the License.
39 *
40 * Author: Cody Northrop <[email protected]>
41 * Author: David Pinedo <[email protected]>
42 * Author: Jon Ashburn <[email protected]>
43 * Author: Tony Barbour <[email protected]>
44 */
45
46 #include <string.h>
47 #include <stdlib.h>
48 #include <assert.h>
49 #include <pthread.h>
50 #include <png.h>
51 #include <time.h>
52
53 #include <vulkan/vulkan_core.h>
54 #include <vulkan/vk_layer.h>
55
56 #include "git_sha1.h"
57
58 #include "screenshot_params.h"
59
60 #include "util/u_debug.h"
61 #include "util/hash_table.h"
62 #include "util/list.h"
63 #include "util/ralloc.h"
64 #include "util/os_time.h"
65 #include "util/os_socket.h"
66 #include "util/simple_mtx.h"
67 #include "util/u_math.h"
68
69 #include "vk_enum_to_str.h"
70 #include "vk_dispatch_table.h"
71 #include "vk_util.h"
72
73 typedef pthread_mutex_t loader_platform_thread_mutex;
loader_platform_thread_create_mutex(loader_platform_thread_mutex * pMutex)74 static inline void loader_platform_thread_create_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_init(pMutex, NULL); }
loader_platform_thread_lock_mutex(loader_platform_thread_mutex * pMutex)75 static inline void loader_platform_thread_lock_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_lock(pMutex); }
loader_platform_thread_unlock_mutex(loader_platform_thread_mutex * pMutex)76 static inline void loader_platform_thread_unlock_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_unlock(pMutex); }
loader_platform_thread_delete_mutex(loader_platform_thread_mutex * pMutex)77 static inline void loader_platform_thread_delete_mutex(loader_platform_thread_mutex *pMutex) { pthread_mutex_destroy(pMutex); }
78
79 static int globalLockInitialized = 0;
80 static loader_platform_thread_mutex globalLock;
81
82 uint32_t MAX_PATH_SIZE = 512;
83
84 /* Mapped from VkInstace/VkPhysicalDevice */
85 struct instance_data {
86 struct vk_instance_dispatch_table vtable;
87 struct vk_physical_device_dispatch_table pd_vtable;
88 VkInstance instance;
89
90 struct screenshot_params params;
91
92 int control_client;
93 int socket_fd;
94
95 /* Enabling switch for taking screenshot */
96 bool screenshot_enabled;
97
98 /* Enabling switch for socket communications */
99 bool socket_enabled;
100 bool socket_setup;
101
102 const char *filename;
103 };
104
105 pthread_cond_t ptCondition = PTHREAD_COND_INITIALIZER;
106 pthread_mutex_t ptLock = PTHREAD_MUTEX_INITIALIZER;
107
108 VkFence copyDone;
109 const VkPipelineStageFlags dstStageWaitBeforeSubmission = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
110 const VkSemaphore *pSemaphoreWaitBeforePresent;
111 uint32_t semaphoreWaitBeforePresentCount;
112 VkSemaphore semaphoreWaitAfterSubmission;
113
114 /* Mapped from VkDevice */
115 struct queue_data;
116 struct device_data {
117 struct instance_data *instance;
118
119 PFN_vkSetDeviceLoaderData set_device_loader_data;
120
121 struct vk_device_dispatch_table vtable;
122 VkPhysicalDevice physical_device;
123 VkDevice device;
124
125 VkPhysicalDeviceProperties properties;
126
127 struct queue_data *graphic_queue;
128 struct queue_data **queues;
129 uint32_t n_queues;
130 };
131
132 /* Mapped from VkQueue */
133 struct queue_data {
134 struct device_data *device;
135 VkQueue queue;
136 VkQueueFlags flags;
137 uint32_t index;
138 };
139
140 /* Mapped from VkSwapchainKHR */
141 struct swapchain_data {
142 struct device_data *device;
143
144 VkSwapchainKHR swapchain;
145 VkExtent2D imageExtent;
146 VkFormat format;
147
148 VkImage image;
149 };
150
151 static struct hash_table_u64 *vk_object_to_data = NULL;
152 static simple_mtx_t vk_object_to_data_mutex = SIMPLE_MTX_INITIALIZER;
153
ensure_vk_object_map(void)154 static inline void ensure_vk_object_map(void)
155 {
156 if (!vk_object_to_data)
157 vk_object_to_data = _mesa_hash_table_u64_create(NULL);
158 }
159
160 #define HKEY(obj) ((uint64_t)(obj))
161 #define FIND(type, obj) ((type *)find_object_data(HKEY(obj)))
162
find_object_data(uint64_t obj)163 static void *find_object_data(uint64_t obj)
164 {
165 simple_mtx_lock(&vk_object_to_data_mutex);
166 ensure_vk_object_map();
167 void *data = _mesa_hash_table_u64_search(vk_object_to_data, obj);
168 simple_mtx_unlock(&vk_object_to_data_mutex);
169 return data;
170 }
171
map_object(uint64_t obj,void * data)172 static void map_object(uint64_t obj, void *data)
173 {
174 simple_mtx_lock(&vk_object_to_data_mutex);
175 ensure_vk_object_map();
176 _mesa_hash_table_u64_insert(vk_object_to_data, obj, data);
177 simple_mtx_unlock(&vk_object_to_data_mutex);
178 }
179
unmap_object(uint64_t obj)180 static void unmap_object(uint64_t obj)
181 {
182 simple_mtx_lock(&vk_object_to_data_mutex);
183 _mesa_hash_table_u64_remove(vk_object_to_data, obj);
184 simple_mtx_unlock(&vk_object_to_data_mutex);
185 }
186
187 #define VK_CHECK(expr) \
188 do { \
189 VkResult __result = (expr); \
190 if (__result != VK_SUCCESS) { \
191 LOG(ERROR, "'%s' line %i failed with %s\n", \
192 #expr, __LINE__, vk_Result_to_str(__result)); \
193 } \
194 } while (0)
195
get_instance_chain_info(const VkInstanceCreateInfo * pCreateInfo,VkLayerFunction func)196 static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo,
197 VkLayerFunction func)
198 {
199 vk_foreach_struct_const(item, pCreateInfo->pNext) {
200 if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
201 ((VkLayerInstanceCreateInfo *) item)->function == func)
202 return (VkLayerInstanceCreateInfo *) item;
203 }
204 unreachable("instance chain info not found");
205 return NULL;
206 }
207
get_device_chain_info(const VkDeviceCreateInfo * pCreateInfo,VkLayerFunction func)208 static VkLayerDeviceCreateInfo *get_device_chain_info(const VkDeviceCreateInfo *pCreateInfo,
209 VkLayerFunction func)
210 {
211 vk_foreach_struct_const(item, pCreateInfo->pNext) {
212 if (item->sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO &&
213 ((VkLayerDeviceCreateInfo *) item)->function == func)
214 return (VkLayerDeviceCreateInfo *)item;
215 }
216 unreachable("device chain info not found");
217 return NULL;
218 }
219
220 /**/
221
new_instance_data(VkInstance instance)222 static struct instance_data *new_instance_data(VkInstance instance)
223 {
224 struct instance_data *data = rzalloc(NULL, struct instance_data);
225 data->instance = instance;
226 data->control_client = -1;
227 data->socket_fd = -1;
228 map_object(HKEY(data->instance), data);
229 return data;
230 }
231
destroy_instance_data(struct instance_data * data)232 void destroy_instance_data(struct instance_data *data)
233 {
234 destroy_frame_list(data->params.frames);
235 if (data->socket_fd >= 0)
236 os_socket_close(data->socket_fd);
237 unmap_object(HKEY(data->instance));
238 ralloc_free(data);
239 }
240
instance_data_map_physical_devices(struct instance_data * instance_data,bool map)241 static void instance_data_map_physical_devices(struct instance_data *instance_data,
242 bool map)
243 {
244 uint32_t physicalDeviceCount = 0;
245 instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
246 &physicalDeviceCount,
247 NULL);
248
249 VkPhysicalDevice *physicalDevices = (VkPhysicalDevice *) malloc(sizeof(VkPhysicalDevice) * physicalDeviceCount);
250 instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
251 &physicalDeviceCount,
252 physicalDevices);
253
254 for (uint32_t i = 0; i < physicalDeviceCount; i++) {
255 if (map)
256 map_object(HKEY(physicalDevices[i]), instance_data);
257 else
258 unmap_object(HKEY(physicalDevices[i]));
259 }
260
261 free(physicalDevices);
262 }
263
264 /**/
new_device_data(VkDevice device,struct instance_data * instance)265 static struct device_data *new_device_data(VkDevice device, struct instance_data *instance)
266 {
267 struct device_data *data = rzalloc(NULL, struct device_data);
268 data->instance = instance;
269 data->device = device;
270 map_object(HKEY(data->device), data);
271 return data;
272 }
273
new_queue_data(VkQueue queue,const VkQueueFamilyProperties * family_props,struct device_data * device_data,uint32_t index)274 static struct queue_data *new_queue_data(VkQueue queue,
275 const VkQueueFamilyProperties *family_props,
276 struct device_data *device_data,
277 uint32_t index)
278 {
279 struct queue_data *data = rzalloc(device_data, struct queue_data);
280 data->device = device_data;
281 data->queue = queue;
282 data->flags = family_props->queueFlags;
283 data->index = index;
284 map_object(HKEY(data->queue), data);
285
286 if ((data->flags & VK_QUEUE_GRAPHICS_BIT) != 0) {
287 device_data->graphic_queue = data;
288 }
289 return data;
290 }
291
destroy_queue(struct queue_data * data)292 static void destroy_queue(struct queue_data *data)
293 {
294 struct device_data *device_data = data->device;
295 unmap_object(HKEY(data->queue));
296 ralloc_free(data);
297 }
298
device_map_queues(struct device_data * data,const VkDeviceCreateInfo * pCreateInfo)299 static void device_map_queues(struct device_data *data,
300 const VkDeviceCreateInfo *pCreateInfo)
301 {
302 loader_platform_thread_lock_mutex(&globalLock);
303 for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++)
304 data->n_queues += pCreateInfo->pQueueCreateInfos[i].queueCount;
305 data->queues = ralloc_array(data, struct queue_data *, data->n_queues);
306
307 struct instance_data *instance_data = data->instance;
308 uint32_t n_family_props;
309 instance_data->pd_vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device,
310 &n_family_props,
311 NULL);
312 VkQueueFamilyProperties *family_props =
313 (VkQueueFamilyProperties *)malloc(sizeof(VkQueueFamilyProperties) * n_family_props);
314 instance_data->pd_vtable.GetPhysicalDeviceQueueFamilyProperties(data->physical_device,
315 &n_family_props,
316 family_props);
317
318 uint32_t queue_index = 0;
319 for (uint32_t i = 0; i < pCreateInfo->queueCreateInfoCount; i++) {
320 for (uint32_t j = 0; j < pCreateInfo->pQueueCreateInfos[i].queueCount; j++) {
321 VkQueue queue;
322 data->vtable.GetDeviceQueue(data->device,
323 pCreateInfo->pQueueCreateInfos[i].queueFamilyIndex,
324 j, &queue);
325 VK_CHECK(data->set_device_loader_data(data->device, queue));
326
327 data->queues[queue_index] =
328 new_queue_data(queue, family_props, data, queue_index);
329 queue_index++;
330 }
331 }
332
333 free(family_props);
334 loader_platform_thread_unlock_mutex(&globalLock);
335 }
336
device_unmap_queues(struct device_data * data)337 static void device_unmap_queues(struct device_data *data)
338 {
339 for (uint32_t i = 0; i < data->n_queues; i++)
340 destroy_queue(data->queues[i]);
341 }
342
destroy_device_data(struct device_data * data)343 static void destroy_device_data(struct device_data *data)
344 {
345 loader_platform_thread_lock_mutex(&globalLock);
346 unmap_object(HKEY(data->device));
347 ralloc_free(data);
348 loader_platform_thread_unlock_mutex(&globalLock);
349 }
350
new_swapchain_data(VkSwapchainKHR swapchain,struct device_data * device_data)351 static struct swapchain_data *new_swapchain_data(VkSwapchainKHR swapchain,
352 struct device_data *device_data)
353 {
354 struct instance_data *instance_data = device_data->instance;
355 struct swapchain_data *data = rzalloc(NULL, struct swapchain_data);
356 data->device = device_data;
357 data->swapchain = swapchain;
358 map_object(HKEY(data->swapchain), data);
359 return data;
360 }
361
destroy_swapchain_data(struct swapchain_data * data)362 static void destroy_swapchain_data(struct swapchain_data *data)
363 {
364 unmap_object(HKEY(data->swapchain));
365 ralloc_free(data);
366 }
367
parse_command(struct instance_data * instance_data,const char * cmd,unsigned cmdlen,const char * param,unsigned paramlen)368 static void parse_command(struct instance_data *instance_data,
369 const char *cmd, unsigned cmdlen,
370 const char *param, unsigned paramlen)
371 {
372 /* parse string (if any) from capture command */
373 if (!strncmp(cmd, "capture", cmdlen)) {
374 instance_data->screenshot_enabled = true;
375 if (paramlen > 1) {
376 instance_data->filename = param;
377 } else {
378 instance_data->filename = NULL;
379 }
380 }
381 }
382
383 #define BUFSIZE 4096
384
385 /**
386 * This function will process commands through the control file.
387 *
388 * A command starts with a colon, followed by the command, and followed by an
389 * option '=' and a parameter. It has to end with a semi-colon. A full command
390 * + parameter looks like:
391 *
392 * :cmd=param;
393 */
process_char(struct instance_data * instance_data,char c)394 static void process_char(struct instance_data *instance_data, char c)
395 {
396 static char cmd[BUFSIZE];
397 static char param[BUFSIZE];
398
399 static unsigned cmdpos = 0;
400 static unsigned parampos = 0;
401 static bool reading_cmd = false;
402 static bool reading_param = false;
403
404 switch (c) {
405 case ':':
406 cmdpos = 0;
407 parampos = 0;
408 reading_cmd = true;
409 reading_param = false;
410 break;
411 case ';':
412 if (!reading_cmd)
413 break;
414 cmd[cmdpos++] = '\0';
415 param[parampos++] = '\0';
416 parse_command(instance_data, cmd, cmdpos, param, parampos);
417 reading_cmd = false;
418 reading_param = false;
419 break;
420 case '=':
421 if (!reading_cmd)
422 break;
423 reading_param = true;
424 break;
425 default:
426 if (!reading_cmd)
427 break;
428
429 if (reading_param) {
430 /* overflow means an invalid parameter */
431 if (parampos >= BUFSIZE - 1) {
432 reading_cmd = false;
433 reading_param = false;
434 break;
435 }
436
437 param[parampos++] = c;
438 } else {
439 /* overflow means an invalid command */
440 if (cmdpos >= BUFSIZE - 1) {
441 reading_cmd = false;
442 break;
443 }
444
445 cmd[cmdpos++] = c;
446 }
447 }
448 }
449
control_send(struct instance_data * instance_data,const char * cmd,unsigned cmdlen,const char * param,unsigned paramlen)450 static void control_send(struct instance_data *instance_data,
451 const char *cmd, unsigned cmdlen,
452 const char *param, unsigned paramlen)
453 {
454 unsigned msglen = 0;
455 char buffer[BUFSIZE];
456
457 assert(cmdlen + paramlen + 3 < BUFSIZE);
458
459 buffer[msglen++] = ':';
460
461 memcpy(&buffer[msglen], cmd, cmdlen);
462 msglen += cmdlen;
463
464 if (paramlen > 0) {
465 buffer[msglen++] = '=';
466 memcpy(&buffer[msglen], param, paramlen);
467 msglen += paramlen;
468 buffer[msglen++] = ';';
469 }
470
471 os_socket_send(instance_data->control_client, buffer, msglen, 0);
472 }
473
control_send_connection_string(struct device_data * device_data)474 static void control_send_connection_string(struct device_data *device_data)
475 {
476 struct instance_data *instance_data = device_data->instance;
477
478 const char *controlVersionCmd = "MesaScreenshotControlVersion";
479 const char *controlVersionString = "1";
480
481 control_send(instance_data, controlVersionCmd, strlen(controlVersionCmd),
482 controlVersionString, strlen(controlVersionString));
483
484 const char *deviceCmd = "DeviceName";
485 const char *deviceName = device_data->properties.deviceName;
486
487 control_send(instance_data, deviceCmd, strlen(deviceCmd),
488 deviceName, strlen(deviceName));
489
490 const char *mesaVersionCmd = "MesaVersion";
491 const char *mesaVersionString = "Mesa " PACKAGE_VERSION MESA_GIT_SHA1;
492
493 control_send(instance_data, mesaVersionCmd, strlen(mesaVersionCmd),
494 mesaVersionString, strlen(mesaVersionString));
495 }
496
control_client_check(struct device_data * device_data)497 static void control_client_check(struct device_data *device_data)
498 {
499 struct instance_data *instance_data = device_data->instance;
500
501 /* Already connected, just return. */
502 if (instance_data->control_client >= 0)
503 return;
504
505 int socket_fd = os_socket_accept(instance_data->socket_fd);
506 if (socket_fd == -1) {
507 if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ECONNABORTED)
508 LOG(ERROR, "socket error: %s\n", strerror(errno));
509 return;
510 }
511
512 if (socket_fd >= 0) {
513 os_socket_block(socket_fd, false);
514 instance_data->control_client = socket_fd;
515 control_send_connection_string(device_data);
516 }
517 }
518
control_client_disconnected(struct instance_data * instance_data)519 static void control_client_disconnected(struct instance_data *instance_data)
520 {
521 os_socket_close(instance_data->control_client);
522 instance_data->control_client = -1;
523 }
524
process_control_socket(struct instance_data * instance_data)525 static void process_control_socket(struct instance_data *instance_data)
526 {
527 const int client = instance_data->control_client;
528 if (client >= 0) {
529 char buf[BUFSIZE];
530
531 while (true) {
532 ssize_t n = os_socket_recv(client, buf, BUFSIZE, 0);
533
534 if (n == -1) {
535 if (errno == EAGAIN || errno == EWOULDBLOCK) {
536 /* nothing to read, try again later */
537 break;
538 }
539
540 if (errno != ECONNRESET)
541 LOG(ERROR, "Connection failed: %s\n", strerror(errno));
542
543 control_client_disconnected(instance_data);
544 } else if (n == 0) {
545 /* recv() returns 0 when the client disconnects */
546 control_client_disconnected(instance_data);
547 }
548
549 for (ssize_t i = 0; i < n; i++) {
550 process_char(instance_data, buf[i]);
551 }
552
553 /* If we try to read BUFSIZE and receive BUFSIZE bytes from the
554 * socket, there's a good chance that there's still more data to be
555 * read, so we will try again. Otherwise, simply be done for this
556 * iteration and try again on the next frame.
557 */
558 if (n < BUFSIZE)
559 break;
560 }
561 }
562 }
563
screenshot_CreateSwapchainKHR(VkDevice device,const VkSwapchainCreateInfoKHR * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkSwapchainKHR * pSwapchain)564 static VkResult screenshot_CreateSwapchainKHR(
565 VkDevice device,
566 const VkSwapchainCreateInfoKHR* pCreateInfo,
567 const VkAllocationCallbacks* pAllocator,
568 VkSwapchainKHR* pSwapchain)
569 {
570 struct device_data *device_data = FIND(struct device_data, device);
571
572 // Turn on transfer src bit for image copy later on.
573 VkSwapchainCreateInfoKHR createInfo = *pCreateInfo;
574 createInfo.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
575 VkResult result = device_data->vtable.CreateSwapchainKHR(device, &createInfo, pAllocator, pSwapchain);
576 if (result != VK_SUCCESS) return result;
577
578 loader_platform_thread_lock_mutex(&globalLock);
579
580 struct swapchain_data *swapchain_data = new_swapchain_data(*pSwapchain, device_data);
581 swapchain_data->imageExtent = pCreateInfo->imageExtent;
582 swapchain_data->format = pCreateInfo->imageFormat;
583 loader_platform_thread_unlock_mutex(&globalLock);
584 return result;
585 }
586
screenshot_GetSwapchainImagesKHR(VkDevice device,VkSwapchainKHR swapchain,uint32_t * pCount,VkImage * pSwapchainImages)587 static VkResult screenshot_GetSwapchainImagesKHR(
588 VkDevice device,
589 VkSwapchainKHR swapchain,
590 uint32_t* pCount,
591 VkImage* pSwapchainImages)
592 {
593 struct swapchain_data *swapchain_data = FIND(struct swapchain_data, swapchain);
594 struct vk_device_dispatch_table *vtable = &(swapchain_data->device->vtable);
595 VkResult result = vtable->GetSwapchainImagesKHR(device, swapchain, pCount, pSwapchainImages);
596
597 loader_platform_thread_lock_mutex(&globalLock);
598 if (result == VK_SUCCESS) {
599 // Save only the first image from the first swapchain
600 if (*pCount > 0) {
601 if(pSwapchainImages){
602 swapchain_data->image = pSwapchainImages[0];
603 }
604 }
605 }
606 loader_platform_thread_unlock_mutex(&globalLock);
607 return result;
608 }
609
screenshot_DestroySwapchainKHR(VkDevice device,VkSwapchainKHR swapchain,const VkAllocationCallbacks * pAllocator)610 static void screenshot_DestroySwapchainKHR(
611 VkDevice device,
612 VkSwapchainKHR swapchain,
613 const VkAllocationCallbacks* pAllocator)
614 {
615 if (swapchain == VK_NULL_HANDLE) {
616 struct device_data *device_data = FIND(struct device_data, device);
617 device_data->vtable.DestroySwapchainKHR(device, swapchain, pAllocator);
618 return;
619 }
620
621 struct swapchain_data *swapchain_data =
622 FIND(struct swapchain_data, swapchain);
623
624 swapchain_data->device->vtable.DestroySwapchainKHR(device, swapchain, pAllocator);
625 destroy_swapchain_data(swapchain_data);
626 }
627
628 /* Convert long int to string */
itoa(uint32_t integer,char * dest_str)629 static void itoa(uint32_t integer, char *dest_str)
630 {
631 // Our sizes are limited to uin32_t max value: 4,294,967,295 (10 digits)
632 sprintf(dest_str, "%u", integer);
633 }
634
get_mem_type_from_properties(VkPhysicalDeviceMemoryProperties * mem_properties,uint32_t bits_type,VkFlags requirements_mask,uint32_t * type_index)635 static bool get_mem_type_from_properties(
636 VkPhysicalDeviceMemoryProperties* mem_properties,
637 uint32_t bits_type,
638 VkFlags requirements_mask,
639 uint32_t* type_index)
640 {
641 for (uint32_t i = 0; i < 32; i++) {
642 if ((bits_type & 1) == 1) {
643 if ((mem_properties->memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {
644 *type_index = i;
645 return true;
646 }
647 }
648 bits_type >>= 1;
649 }
650 return false;
651 }
652
653 // Track allocated resources in writeFile()
654 // and clean them up when they go out of scope.
655 struct WriteFileCleanupData {
656 device_data *dev_data;
657 VkImage image2;
658 VkImage image3;
659 VkDeviceMemory mem2;
660 VkDeviceMemory mem3;
661 bool mem2mapped;
662 bool mem3mapped;
663 VkCommandBuffer commandBuffer;
664 VkCommandPool commandPool;
665 ~WriteFileCleanupData();
666 };
667
~WriteFileCleanupData()668 WriteFileCleanupData::~WriteFileCleanupData() {
669 if (mem2mapped) dev_data->vtable.UnmapMemory(dev_data->device, mem2);
670 if (mem2) dev_data->vtable.FreeMemory(dev_data->device, mem2, NULL);
671 if (image2) dev_data->vtable.DestroyImage(dev_data->device, image2, NULL);
672
673 if (mem3mapped) dev_data->vtable.UnmapMemory(dev_data->device, mem3);
674 if (mem3) dev_data->vtable.FreeMemory(dev_data->device, mem3, NULL);
675 if (image3) dev_data->vtable.DestroyImage(dev_data->device, image3, NULL);
676
677 if (commandBuffer) dev_data->vtable.FreeCommandBuffers(dev_data->device, commandPool, 1, &commandBuffer);
678 if (commandPool) dev_data->vtable.DestroyCommandPool(dev_data->device, commandPool, NULL);
679 }
680
get_time()681 static uint64_t get_time() {
682 if (LOG_TYPE == DEBUG) {
683 struct timespec tspec;
684 long BILLION = 1000000000;
685 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tspec);
686 uint64_t sec = tspec.tv_sec;
687 uint64_t nsec = tspec.tv_nsec;
688 return ((sec * BILLION) + nsec);
689 } else {
690 return 0;
691 }
692 }
693
print_time_difference(long int start_time,long int end_time)694 static void print_time_difference(long int start_time, long int end_time) {
695 if (end_time > 0) {
696 LOG(DEBUG, "Time to copy: %u nanoseconds\n", end_time - start_time);
697 }
698 }
699
700 // Store all data required for threading the saving to file functionality
701 struct ThreadSaveData {
702 struct device_data *device_data;
703 const char *filename;
704 const char *pFramebuffer;
705 VkSubresourceLayout srLayout;
706 VkFence fence;
707 uint32_t const width;
708 uint32_t const height;
709 };
710
711 /* Write the copied image to a PNG file */
writePNG(void * data)712 void *writePNG(void *data) {
713 struct ThreadSaveData *threadData = (struct ThreadSaveData*)data;
714 FILE *file;
715 size_t length = sizeof(char[MAX_PATH_SIZE]);
716 const char *tmpStr = ".tmp";
717 char *filename = (char *)malloc(length);
718 char *tmpFilename = (char *)malloc(length + 4); // Allow for ".tmp"
719 memcpy(filename, threadData->filename, length);
720 memcpy(tmpFilename, threadData->filename, length);
721 strcat(tmpFilename, tmpStr);
722 file = fopen(tmpFilename, "wb"); //create file for output
723 if (!file) {
724 LOG(ERROR, "Failed to open output file, '%s', error(%d): %s\n", tmpFilename, errno, strerror(errno));
725 pthread_cond_signal(&ptCondition);
726 return nullptr;
727 }
728 VkResult res;
729 png_byte **row_pointer;
730 threadData->pFramebuffer += threadData->srLayout.offset;
731 png_infop info;
732 int localHeight = threadData->height;
733 int localWidth = threadData->width;
734 bool checks_failed = false;
735 // TODO: Look into runtime version mismatch issue with some VK workloads
736 png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); //create structure for write PNG_LIBPNG_VER_STRING
737 if (!png) {
738 LOG(ERROR, "Create write struct failed. VER_STRING=%s\n", PNG_LIBPNG_VER_STRING);
739 checks_failed = true;
740 } else {
741 info = png_create_info_struct(png);
742 if (!info) {
743 LOG(ERROR, "Create info struct failed\n");
744 checks_failed = true;
745 } else if (setjmp(png_jmpbuf(png))) {
746 LOG(ERROR, "setjmp() failed\n");
747 png_destroy_write_struct(&png, &info);
748 checks_failed = true;
749 }
750 }
751 if (checks_failed) {
752 fclose(file);
753 pthread_cond_signal(&ptCondition);
754 return nullptr;
755 }
756 threadData->device_data->vtable.WaitForFences(threadData->device_data->device, 1, &threadData->fence, VK_TRUE, UINT64_MAX);
757 auto start_time = get_time();
758 const int RGB_NUM_CHANNELS = 3;
759 row_pointer = (png_byte **)malloc(sizeof(png_byte *) * localHeight);
760 for (int y = 0; y < localHeight; y++) {
761 int char_counter = 0;
762 row_pointer[y] = (png_byte *)malloc(sizeof(png_byte) * RGB_NUM_CHANNELS * localWidth);
763 for (int x = 0; x < localWidth * RGB_NUM_CHANNELS; x += RGB_NUM_CHANNELS) {
764 memcpy(&row_pointer[y][x], &threadData->pFramebuffer[char_counter], RGB_NUM_CHANNELS * sizeof(png_byte));
765 char_counter += RGB_NUM_CHANNELS;
766 }
767 threadData->pFramebuffer += threadData->srLayout.rowPitch;
768 }
769 auto end_time = get_time();
770 print_time_difference(start_time, end_time);
771 // We've created all local copies of data,
772 // so let's signal main thread to continue
773 pthread_cond_signal(&ptCondition);
774 png_init_io(png, file); // Initialize file output
775 png_set_IHDR( // Set image properties
776 png, // Pointer to png_struct
777 info, // Pointer to info_struct
778 localWidth, // Image width
779 localHeight, // Image height
780 8, // Color depth
781 PNG_COLOR_TYPE_RGB,
782 PNG_INTERLACE_NONE,
783 PNG_COMPRESSION_TYPE_DEFAULT,
784 PNG_FILTER_TYPE_DEFAULT
785 );
786 png_set_compression_level(png, 1); // Z_BEST_SPEED=1
787 png_set_compression_strategy(png, 2); // Z_HUFFMAN_ONLY=2
788 png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
789 png_set_compression_mem_level(png, 9);
790 png_set_compression_buffer_size(png, 65536);
791 png_write_info(png, info); // Write png image information to file
792 png_write_image(png, row_pointer); // Actually write image
793 png_write_end(png, NULL); // End image writing
794 free(row_pointer);
795 fclose(file);
796
797 // Rename file, indicating completion, client should be
798 // checking for the final file exists.
799 if (rename(tmpFilename, filename) != 0 )
800 LOG(ERROR, "Could not rename from '%s' to '%s'\n", tmpFilename, filename);
801 else
802 LOG(INFO, "Successfully renamed from '%s' to '%s'\n", tmpFilename, filename);
803
804 if(filename)
805 free(filename);
806 if(tmpFilename)
807 free(tmpFilename);
808 return nullptr;
809 }
810
811 /* Write an image to file. Upon encountering issues, do not impact the
812 Present operation, */
write_image(const char * filename,VkImage image,struct device_data * device_data,struct instance_data * instance_data,struct swapchain_data * swapchain_data)813 static bool write_image(
814 const char* filename,
815 VkImage image,
816 struct device_data* device_data,
817 struct instance_data* instance_data,
818 struct swapchain_data* swapchain_data)
819 {
820 VkDevice device = device_data->device;
821 VkPhysicalDevice physical_device = device_data->physical_device;
822 VkInstance instance = instance_data->instance;
823
824 uint32_t const width = swapchain_data->imageExtent.width;
825 uint32_t const height = swapchain_data->imageExtent.height;
826 VkFormat const format = swapchain_data->format;
827
828 queue_data* queue_data = device_data->graphic_queue;
829 VkQueue queue = queue_data->queue;
830
831 VkResult err;
832
833 /* Force destination format to be RGB to make writing to file much faster */
834 VkFormat destination_format = VK_FORMAT_R8G8B8_UNORM;
835
836 VkFormatProperties device_format_properties;
837 instance_data->pd_vtable.GetPhysicalDeviceFormatProperties(physical_device,
838 destination_format,
839 &device_format_properties);
840 /* If origin and destination formats are the same, no need to convert */
841 bool copyOnly = false;
842 bool needs_2_steps = false;
843 if (destination_format == format) {
844 copyOnly = true;
845 LOG(DEBUG, "Only copying since the src/dest formats are the same\n");
846 } else {
847 bool const blt_linear = device_format_properties.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ? true : false;
848 bool const blt_optimal = device_format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT ? true : false;
849 if (!blt_linear && !blt_optimal) {
850 return false;
851 } else if (!blt_linear && blt_optimal) {
852 // Can't blit to linear target, but can blit to optimal
853 needs_2_steps = true;
854 LOG(DEBUG, "Needs 2 steps\n");
855 }
856 }
857
858 WriteFileCleanupData data = {};
859 data.dev_data = device_data;
860
861 VkImageCreateInfo img_create_info2 = {
862 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
863 NULL,
864 0,
865 VK_IMAGE_TYPE_2D,
866 destination_format,
867 {width, height, 1},
868 1,
869 1,
870 VK_SAMPLE_COUNT_1_BIT,
871 VK_IMAGE_TILING_LINEAR,
872 VK_IMAGE_USAGE_TRANSFER_DST_BIT,
873 VK_SHARING_MODE_EXCLUSIVE,
874 0,
875 NULL,
876 VK_IMAGE_LAYOUT_UNDEFINED,
877 };
878 VkImageCreateInfo img_create_info3 = img_create_info2;
879
880 if (needs_2_steps) {
881 img_create_info2.tiling = VK_IMAGE_TILING_OPTIMAL;
882 img_create_info2.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
883 }
884 VkMemoryAllocateInfo mem_alloc_info = {
885 VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
886 NULL,
887 0,
888 0
889 };
890 VkMemoryRequirements mem_requirements;
891 VkPhysicalDeviceMemoryProperties mem_properties;
892
893 VK_CHECK(device_data->vtable.CreateImage(device, &img_create_info2, NULL, &data.image2));
894 device_data->vtable.GetImageMemoryRequirements(device, data.image2, &mem_requirements);
895 mem_alloc_info.allocationSize = mem_requirements.size;
896 instance_data->pd_vtable.GetPhysicalDeviceMemoryProperties(physical_device, &mem_properties);
897 if(!get_mem_type_from_properties(&mem_properties,
898 mem_requirements.memoryTypeBits,
899 needs_2_steps ? VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
900 &mem_alloc_info.memoryTypeIndex)) {
901 LOG(ERROR, "Unable to get memory type from the intermediate/final image properties.\n");
902 return false;
903 }
904
905 VK_CHECK(device_data->vtable.AllocateMemory(device, &mem_alloc_info, NULL, &data.mem2));
906 VK_CHECK(device_data->vtable.BindImageMemory(device, data.image2, data.mem2, 0));
907
908 if (needs_2_steps) {
909 VK_CHECK(device_data->vtable.CreateImage(device, &img_create_info3, NULL, &data.image3));
910 device_data->vtable.GetImageMemoryRequirements(device, data.image3, &mem_requirements);
911 mem_alloc_info.allocationSize = mem_requirements.size;
912 instance_data->pd_vtable.GetPhysicalDeviceMemoryProperties(physical_device, &mem_properties);
913
914 if(!get_mem_type_from_properties(&mem_properties,
915 mem_requirements.memoryTypeBits,
916 VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
917 &mem_alloc_info.memoryTypeIndex)) {
918 LOG(ERROR, "Unable to get memory type from the temporary image properties.\n");
919 return false;
920 }
921 VK_CHECK(device_data->vtable.AllocateMemory(device, &mem_alloc_info, NULL, &data.mem3));
922 VK_CHECK(device_data->vtable.BindImageMemory(device, data.image3, data.mem3, 0));
923 }
924
925 /* Setup command pool */
926 VkCommandPoolCreateInfo cmd_pool_info = {};
927 cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
928 cmd_pool_info.pNext = NULL;
929 cmd_pool_info.queueFamilyIndex = queue_data->index;
930 cmd_pool_info.flags = 0;
931
932 VK_CHECK(device_data->vtable.CreateCommandPool(device, &cmd_pool_info, NULL, &data.commandPool));
933
934 /* Set up command buffer */
935 const VkCommandBufferAllocateInfo cmd_buf_alloc_info = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, NULL,
936 data.commandPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1};
937 VK_CHECK(device_data->vtable.AllocateCommandBuffers(device, &cmd_buf_alloc_info, &data.commandBuffer));
938
939 if (device_data->set_device_loader_data) {
940 VK_CHECK(device_data->set_device_loader_data(device, (void *)data.commandBuffer));
941 } else {
942 *((const void **)data.commandBuffer) = *(void **)device;
943 }
944
945 const VkCommandBufferBeginInfo cmd_buf_begin_info = {
946 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
947 NULL,
948 VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
949 };
950 VK_CHECK(device_data->vtable.BeginCommandBuffer(data.commandBuffer, &cmd_buf_begin_info));
951
952 // This barrier is used to transition from/to present Layout
953 VkImageMemoryBarrier presentMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
954 NULL,
955 VK_ACCESS_MEMORY_WRITE_BIT,
956 VK_ACCESS_TRANSFER_READ_BIT,
957 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
958 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
959 VK_QUEUE_FAMILY_IGNORED,
960 VK_QUEUE_FAMILY_IGNORED,
961 image,
962 {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
963
964 // This barrier is used to transition from a newly-created layout to a blt
965 // or copy destination layout.
966 VkImageMemoryBarrier destMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
967 NULL,
968 0,
969 VK_ACCESS_TRANSFER_WRITE_BIT,
970 VK_IMAGE_LAYOUT_UNDEFINED,
971 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
972 VK_QUEUE_FAMILY_IGNORED,
973 VK_QUEUE_FAMILY_IGNORED,
974 data.image2,
975 {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
976
977 // This barrier is used to transition a dest layout to general layout.
978 VkImageMemoryBarrier generalMemoryBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
979 NULL,
980 VK_ACCESS_TRANSFER_WRITE_BIT,
981 VK_ACCESS_MEMORY_READ_BIT,
982 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
983 VK_IMAGE_LAYOUT_GENERAL,
984 VK_QUEUE_FAMILY_IGNORED,
985 VK_QUEUE_FAMILY_IGNORED,
986 data.image2,
987 {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
988
989 VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
990 VkPipelineStageFlags dstStages = VK_PIPELINE_STAGE_TRANSFER_BIT;
991
992 device_data->vtable.CmdPipelineBarrier(data.commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
993 dstStages, 0, 0, NULL, 0, NULL, 1, &presentMemoryBarrier);
994
995 device_data->vtable.CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier);
996
997 const VkImageCopy img_copy = {
998 {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
999 {0, 0, 0},
1000 {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1},
1001 {0, 0, 0},
1002 {width, height, 1}
1003 };
1004
1005 if (copyOnly) {
1006 device_data->vtable.CmdCopyImage(data.commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image2,
1007 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &img_copy);
1008 } else {
1009 VkImageBlit imageBlitRegion = {};
1010 imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1011 imageBlitRegion.srcSubresource.baseArrayLayer = 0;
1012 imageBlitRegion.srcSubresource.layerCount = 1;
1013 imageBlitRegion.srcSubresource.mipLevel = 0;
1014 imageBlitRegion.srcOffsets[1].x = width;
1015 imageBlitRegion.srcOffsets[1].y = height;
1016 imageBlitRegion.srcOffsets[1].z = 1;
1017 imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1018 imageBlitRegion.dstSubresource.baseArrayLayer = 0;
1019 imageBlitRegion.dstSubresource.layerCount = 1;
1020 imageBlitRegion.dstSubresource.mipLevel = 0;
1021 imageBlitRegion.dstOffsets[1].x = width;
1022 imageBlitRegion.dstOffsets[1].y = height;
1023 imageBlitRegion.dstOffsets[1].z = 1;
1024
1025 device_data->vtable.CmdBlitImage(data.commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image2,
1026 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST);
1027 if (needs_2_steps) {
1028 // image 3 needs to be transitioned from its undefined state to a
1029 // transfer destination.
1030 destMemoryBarrier.image = data.image3;
1031 device_data->vtable.CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &destMemoryBarrier);
1032
1033 // Transition image2 so that it can be read for the upcoming copy to
1034 // image 3.
1035 destMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
1036 destMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1037 destMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1038 destMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1039 destMemoryBarrier.image = data.image2;
1040 device_data->vtable.CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1,
1041 &destMemoryBarrier);
1042
1043 // This step essentially untiles the image.
1044 device_data->vtable.CmdCopyImage(data.commandBuffer, data.image2, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, data.image3,
1045 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &img_copy);
1046 generalMemoryBarrier.image = data.image3;
1047 }
1048 }
1049
1050 // The destination needs to be transitioned from the optimal copy format to
1051 // the format we can read with the CPU.
1052 device_data->vtable.CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1, &generalMemoryBarrier);
1053
1054 // Restore the swap chain image layout to what it was before.
1055 // This may not be strictly needed, but it is generally good to restore
1056 // things to original state.
1057 presentMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1058 presentMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1059 presentMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1060 presentMemoryBarrier.dstAccessMask = 0;
1061 device_data->vtable.CmdPipelineBarrier(data.commandBuffer, srcStages, dstStages, 0, 0, NULL, 0, NULL, 1,
1062 &presentMemoryBarrier);
1063 VK_CHECK(device_data->vtable.EndCommandBuffer(data.commandBuffer));
1064
1065 VkSubmitInfo submitInfo;
1066 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1067 submitInfo.pNext = NULL;
1068 submitInfo.waitSemaphoreCount = semaphoreWaitBeforePresentCount;
1069 submitInfo.pWaitSemaphores = pSemaphoreWaitBeforePresent;
1070 submitInfo.pWaitDstStageMask = &dstStageWaitBeforeSubmission;
1071 submitInfo.commandBufferCount = 1;
1072 submitInfo.pCommandBuffers = &data.commandBuffer;
1073 submitInfo.signalSemaphoreCount = 1;
1074 submitInfo.pSignalSemaphores = &semaphoreWaitAfterSubmission;
1075 VK_CHECK(device_data->vtable.QueueSubmit(queue, 1, &submitInfo, copyDone));
1076
1077 // Map the final image so that the CPU can read it.
1078 const VkImageSubresource img_subresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0};
1079 VkSubresourceLayout srLayout;
1080 const char *pFramebuffer;
1081 if (!needs_2_steps) {
1082 device_data->vtable.GetImageSubresourceLayout(device, data.image2, &img_subresource, &srLayout);
1083 VK_CHECK(device_data->vtable.MapMemory(device, data.mem2, 0, VK_WHOLE_SIZE, 0, (void **)&pFramebuffer));
1084 data.mem2mapped = true;
1085 } else {
1086 device_data->vtable.GetImageSubresourceLayout(device, data.image3, &img_subresource, &srLayout);
1087 VK_CHECK(device_data->vtable.MapMemory(device, data.mem3, 0, VK_WHOLE_SIZE, 0, (void **)&pFramebuffer));
1088 data.mem3mapped = true;
1089 }
1090
1091 // Thread off I/O operations
1092 pthread_t ioThread;
1093 pthread_mutex_lock(&ptLock); // Grab lock, we need to wait until thread has copied values of pointers
1094 struct ThreadSaveData threadData = {device_data, filename, pFramebuffer, srLayout, copyDone, width, height};
1095
1096 // Write the data to a PNG file.
1097 pthread_create(&ioThread, NULL, writePNG, (void *)&threadData);
1098 pthread_detach(ioThread); // Reclaim resources once thread terminates
1099 pthread_cond_wait(&ptCondition, &ptLock);
1100 pthread_mutex_unlock(&ptLock);
1101
1102 return true;
1103 }
1104
screenshot_QueuePresentKHR(VkQueue queue,const VkPresentInfoKHR * pPresentInfo)1105 static VkResult screenshot_QueuePresentKHR(
1106 VkQueue queue,
1107 const VkPresentInfoKHR* pPresentInfo)
1108 {
1109 struct queue_data *queue_data = FIND(struct queue_data, queue);
1110 struct device_data *device_data = queue_data->device;
1111 struct instance_data *instance_data = device_data->instance;
1112
1113 VkPresentInfoKHR present_info = *pPresentInfo;
1114
1115 static uint32_t frame_counter = 0;
1116
1117 VkResult result = VK_SUCCESS;
1118 loader_platform_thread_lock_mutex(&globalLock);
1119 if (pPresentInfo && pPresentInfo->swapchainCount > 0) {
1120 VkSwapchainKHR swapchain = pPresentInfo->pSwapchains[0];
1121
1122 struct swapchain_data *swapchain_data = FIND(struct swapchain_data, swapchain);
1123
1124 /* Run initial setup with client */
1125 if(instance_data->params.enabled[SCREENSHOT_PARAM_ENABLED_comms] && instance_data->socket_fd < 0) {
1126 int ret = os_socket_listen_abstract(instance_data->params.control, 1);
1127 if (ret >= 0) {
1128 os_socket_block(ret, false);
1129 instance_data->socket_fd = ret;
1130 }
1131 if (instance_data->socket_fd >= 0)
1132 LOG(INFO, "socket set! Waiting for client input...\n");
1133 }
1134
1135 if (instance_data->socket_fd >= 0) {
1136 /* Check for input from client */
1137 control_client_check(device_data);
1138 process_control_socket(instance_data);
1139 } else if (instance_data->params.frames) {
1140 /* Else check if the frame number is within the given frame list */
1141 if (instance_data->params.frames->size > 0) {
1142 struct frame_list *list = instance_data->params.frames;
1143 struct frame_node *prev = nullptr;
1144 for (struct frame_node *node = list->head; node!=nullptr; prev = node, node = node->next) {
1145 if (frame_counter < node->frame_num){
1146 break;
1147 } else if (frame_counter == node->frame_num) {
1148 instance_data->screenshot_enabled = true;
1149 remove_node(list, prev, node);
1150 break;
1151 } else {
1152 LOG(ERROR, "mesa-screenshot: Somehow encountered a higher number "
1153 "than what exists in the frame list. Won't capture frame!\n");
1154 destroy_frame_list(list);
1155 break;
1156 }
1157 }
1158 } else if(instance_data->params.frames->all_frames) {
1159 instance_data->screenshot_enabled = true;
1160 }
1161 }
1162
1163 if (instance_data->screenshot_enabled) {
1164 LOG(DEBUG, "Screenshot Authorized!\n");
1165 uint32_t SUFFIX_SIZE = 4; // strlen('.png') == 4;
1166 uint32_t path_size_used = 0;
1167 const char *SUFFIX = ".png";
1168 const char *TEMP_DIR = "/tmp/";
1169 char full_path[MAX_PATH_SIZE]; // Let's increase to 512 to account for large files/paths
1170 char filename[256] = "";
1171 char frame_counter_str[11];
1172 bool rename_file = true;
1173 itoa(frame_counter, frame_counter_str);
1174
1175 /* Check if we have an output directory given from the env options */
1176 if (instance_data->params.output_dir &&
1177 strlen(instance_data->params.output_dir) > 0) {
1178 strcat(full_path, instance_data->params.output_dir);
1179 } else {
1180 memcpy(full_path, TEMP_DIR, strlen(TEMP_DIR));
1181 }
1182 path_size_used += strlen(full_path);
1183 /* Check if we have a filename from the client */
1184 if (instance_data->filename && strlen(instance_data->filename) > SUFFIX_SIZE) {
1185 /* Confirm that filename is of form '<name>.png' */
1186 uint32_t name_len = strlen(instance_data->filename);
1187 const char *suffix_ptr = &instance_data->filename[name_len - SUFFIX_SIZE];
1188 if (!strcmp(suffix_ptr, SUFFIX)) {
1189 rename_file = false;
1190 strcpy(filename, instance_data->filename);
1191 }
1192 }
1193 if (rename_file) {
1194 strcat(filename, frame_counter_str);
1195 strcat(filename, SUFFIX);
1196 }
1197 path_size_used += strlen(filename);
1198 if(path_size_used <= MAX_PATH_SIZE) {
1199 strcat(full_path, filename);
1200 pSemaphoreWaitBeforePresent = pPresentInfo->pWaitSemaphores;
1201 semaphoreWaitBeforePresentCount = pPresentInfo->waitSemaphoreCount;
1202 VkSemaphoreCreateInfo semaphoreInfo = {};
1203 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1204 device_data->vtable.CreateSemaphore(device_data->device, &semaphoreInfo, nullptr, &semaphoreWaitAfterSubmission);
1205 VkFenceCreateInfo fenceInfo = {};
1206 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1207 device_data->vtable.CreateFence(device_data->device, &fenceInfo, nullptr, ©Done);
1208 if(write_image(full_path,
1209 swapchain_data->image,
1210 device_data,
1211 instance_data,
1212 swapchain_data)) {
1213 present_info.pWaitSemaphores = &semaphoreWaitAfterSubmission; // Make semaphore here
1214 present_info.waitSemaphoreCount = 1;
1215 }
1216 } else {
1217 LOG(DEBUG, "Cancelling screenshot due to excessive filepath size (max %u characters)\n", MAX_PATH_SIZE);
1218 }
1219 }
1220 }
1221 frame_counter++;
1222 instance_data->screenshot_enabled = false;
1223 loader_platform_thread_unlock_mutex(&globalLock);
1224 VkResult chain_result = queue_data->device->vtable.QueuePresentKHR(queue, &present_info);
1225 if (pPresentInfo->pResults)
1226 pPresentInfo->pResults[0] = chain_result;
1227 if (chain_result != VK_SUCCESS && result == VK_SUCCESS)
1228 result = chain_result;
1229
1230 if (semaphoreWaitAfterSubmission != VK_NULL_HANDLE) {
1231 device_data->vtable.DestroySemaphore(device_data->device, semaphoreWaitAfterSubmission, nullptr);
1232 semaphoreWaitAfterSubmission = VK_NULL_HANDLE;
1233 }
1234 if (copyDone != VK_NULL_HANDLE) {
1235 device_data->vtable.DestroyFence(device_data->device, copyDone, nullptr);
1236 copyDone = VK_NULL_HANDLE;
1237 }
1238 return result;
1239 }
1240
screenshot_CreateDevice(VkPhysicalDevice physicalDevice,const VkDeviceCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkDevice * pDevice)1241 static VkResult screenshot_CreateDevice(
1242 VkPhysicalDevice physicalDevice,
1243 const VkDeviceCreateInfo* pCreateInfo,
1244 const VkAllocationCallbacks* pAllocator,
1245 VkDevice* pDevice)
1246 {
1247 struct instance_data *instance_data =
1248 FIND(struct instance_data, physicalDevice);
1249 VkLayerDeviceCreateInfo *chain_info =
1250 get_device_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
1251 assert(chain_info->u.pLayerInfo);
1252 PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr = chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
1253 PFN_vkGetDeviceProcAddr fpGetDeviceProcAddr = chain_info->u.pLayerInfo->pfnNextGetDeviceProcAddr;
1254 PFN_vkCreateDevice fpCreateDevice = (PFN_vkCreateDevice)fpGetInstanceProcAddr(NULL, "vkCreateDevice");
1255 if (fpCreateDevice == NULL) {
1256 return VK_ERROR_INITIALIZATION_FAILED;
1257 }
1258
1259 // Advance the link info for the next element on the chain
1260 chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
1261
1262 VkDeviceCreateInfo create_info = *pCreateInfo;
1263
1264 VkResult result = fpCreateDevice(physicalDevice, &create_info, pAllocator, pDevice);
1265 if (result != VK_SUCCESS) return result;
1266
1267 struct device_data *device_data = new_device_data(*pDevice, instance_data);
1268 device_data->physical_device = physicalDevice;
1269 vk_device_dispatch_table_load(&device_data->vtable,
1270 fpGetDeviceProcAddr, *pDevice);
1271
1272 instance_data->pd_vtable.GetPhysicalDeviceProperties(device_data->physical_device,
1273 &device_data->properties);
1274
1275 VkLayerDeviceCreateInfo *load_data_info =
1276 get_device_chain_info(pCreateInfo, VK_LOADER_DATA_CALLBACK);
1277
1278 device_data->set_device_loader_data = load_data_info->u.pfnSetDeviceLoaderData;
1279
1280 device_map_queues(device_data, pCreateInfo);
1281 return result;
1282 }
1283
screenshot_DestroyDevice(VkDevice device,const VkAllocationCallbacks * pAllocator)1284 static void screenshot_DestroyDevice(
1285 VkDevice device,
1286 const VkAllocationCallbacks* pAllocator)
1287 {
1288 struct device_data *device_data = FIND(struct device_data, device);
1289 device_unmap_queues(device_data);
1290 device_data->vtable.DestroyDevice(device, pAllocator);
1291 destroy_device_data(device_data);
1292 }
1293
screenshot_CreateInstance(const VkInstanceCreateInfo * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkInstance * pInstance)1294 static VkResult screenshot_CreateInstance(
1295 const VkInstanceCreateInfo* pCreateInfo,
1296 const VkAllocationCallbacks* pAllocator,
1297 VkInstance* pInstance)
1298 {
1299 VkLayerInstanceCreateInfo *chain_info =
1300 get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
1301
1302 assert(chain_info->u.pLayerInfo);
1303 PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
1304 chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
1305 PFN_vkCreateInstance fpCreateInstance =
1306 (PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
1307 if (fpCreateInstance == NULL) {
1308 return VK_ERROR_INITIALIZATION_FAILED;
1309 }
1310
1311 // Advance the link info for the next element on the chain
1312 chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
1313
1314 VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
1315 if (result != VK_SUCCESS) return result;
1316
1317 struct instance_data *instance_data = new_instance_data(*pInstance);
1318 vk_instance_dispatch_table_load(&instance_data->vtable,
1319 fpGetInstanceProcAddr,
1320 instance_data->instance);
1321 vk_physical_device_dispatch_table_load(&instance_data->pd_vtable,
1322 fpGetInstanceProcAddr,
1323 instance_data->instance);
1324 instance_data_map_physical_devices(instance_data, true);
1325
1326 parse_screenshot_env(&instance_data->params, getenv("VK_LAYER_MESA_SCREENSHOT_CONFIG"));
1327
1328 if (!globalLockInitialized) {
1329 loader_platform_thread_create_mutex(&globalLock);
1330 globalLockInitialized = 1;
1331 }
1332
1333 return result;
1334 }
1335
screenshot_DestroyInstance(VkInstance instance,const VkAllocationCallbacks * pAllocator)1336 static void screenshot_DestroyInstance(
1337 VkInstance instance,
1338 const VkAllocationCallbacks* pAllocator)
1339 {
1340 struct instance_data *instance_data = FIND(struct instance_data, instance);
1341 instance_data_map_physical_devices(instance_data, false);
1342 instance_data->vtable.DestroyInstance(instance, pAllocator);
1343 destroy_instance_data(instance_data);
1344 }
1345
1346 static const struct {
1347 const char *name;
1348 void *ptr;
1349 } name_to_funcptr_map[] = {
1350 { "vkGetInstanceProcAddr", (void *) vkGetInstanceProcAddr },
1351 { "vkGetDeviceProcAddr", (void *) vkGetDeviceProcAddr },
1352 #define ADD_HOOK(fn) { "vk" # fn, (void *) screenshot_ ## fn }
1353 #define ADD_ALIAS_HOOK(alias, fn) { "vk" # alias, (void *) screenshot_ ## fn }
1354 ADD_HOOK(CreateSwapchainKHR),
1355 ADD_HOOK(GetSwapchainImagesKHR),
1356 ADD_HOOK(DestroySwapchainKHR),
1357 ADD_HOOK(QueuePresentKHR),
1358
1359 ADD_HOOK(CreateDevice),
1360 ADD_HOOK(DestroyDevice),
1361
1362 ADD_HOOK(CreateInstance),
1363 ADD_HOOK(DestroyInstance),
1364 #undef ADD_HOOK
1365 #undef ADD_ALIAS_HOOK
1366 };
1367
find_ptr(const char * name)1368 static void *find_ptr(const char *name)
1369 {
1370 for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) {
1371 if (strcmp(name, name_to_funcptr_map[i].name) == 0)
1372 return name_to_funcptr_map[i].ptr;
1373 }
1374
1375 return NULL;
1376 }
1377
vkGetDeviceProcAddr(VkDevice dev,const char * funcName)1378 PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr(VkDevice dev,
1379 const char *funcName)
1380 {
1381 void *ptr = find_ptr(funcName);
1382 if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
1383
1384 if (dev == NULL) return NULL;
1385
1386 struct device_data *device_data = FIND(struct device_data, dev);
1387 if (device_data->vtable.GetDeviceProcAddr == NULL) return NULL;
1388 return device_data->vtable.GetDeviceProcAddr(dev, funcName);
1389 }
1390
vkGetInstanceProcAddr(VkInstance instance,const char * funcName)1391 PUBLIC VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(VkInstance instance,
1392 const char *funcName)
1393 {
1394 void *ptr = find_ptr(funcName);
1395 if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
1396
1397 if (instance == NULL) return NULL;
1398
1399 struct instance_data *instance_data = FIND(struct instance_data, instance);
1400 if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL;
1401 return instance_data->vtable.GetInstanceProcAddr(instance, funcName);
1402 }
1403