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