• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &copyDone);
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