xref: /aosp_15_r20/external/mesa3d/src/vulkan/screenshot-layer/screenshot.cpp (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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, &copyDone);
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