• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 Red Hat, Inc.
3  * Copyright © 2021 Valve Corporation
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 
26 #include "zink_context.h"
27 #include "zink_screen.h"
28 #include "zink_resource.h"
29 #include "zink_kopper.h"
30 
31 static void
zink_kopper_set_present_mode_for_interval(struct kopper_displaytarget * cdt,int interval)32 zink_kopper_set_present_mode_for_interval(struct kopper_displaytarget *cdt, int interval)
33 {
34 #ifdef WIN32
35     // not hooked up yet so let's not sabotage benchmarks
36     cdt->present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
37 #else
38    assert(interval >= 0); /* TODO: VK_PRESENT_MODE_FIFO_RELAXED_KHR */
39    if (interval == 0) {
40       if (cdt->present_modes & BITFIELD_BIT(VK_PRESENT_MODE_IMMEDIATE_KHR))
41          cdt->present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
42       else
43          cdt->present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
44    } else if (interval > 0) {
45       cdt->present_mode = VK_PRESENT_MODE_FIFO_KHR;
46    }
47    assert(cdt->present_modes & BITFIELD_BIT(cdt->present_mode));
48 #endif
49 }
50 
51 static void
init_dt_type(struct kopper_displaytarget * cdt)52 init_dt_type(struct kopper_displaytarget *cdt)
53 {
54     VkStructureType type = cdt->info.bos.sType;
55     switch (type) {
56 #ifdef VK_USE_PLATFORM_XCB_KHR
57     case VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR:
58        cdt->type = KOPPER_X11;
59        break;
60 #endif
61 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
62     case VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR:
63        cdt->type = KOPPER_WAYLAND;
64        break;
65 #endif
66 #ifdef VK_USE_PLATFORM_WIN32_KHR
67     case VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR:
68        cdt->type = KOPPER_WIN32;
69        break;
70 #endif
71     default:
72        unreachable("unsupported!");
73     }
74 }
75 
76 static VkSurfaceKHR
kopper_CreateSurface(struct zink_screen * screen,struct kopper_displaytarget * cdt)77 kopper_CreateSurface(struct zink_screen *screen, struct kopper_displaytarget *cdt)
78 {
79     VkSurfaceKHR surface = VK_NULL_HANDLE;
80     VkResult error = VK_SUCCESS;
81 
82     init_dt_type(cdt);
83     VkStructureType type = cdt->info.bos.sType;
84     switch (type) {
85 #ifdef VK_USE_PLATFORM_XCB_KHR
86     case VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR:
87        error = VKSCR(CreateXcbSurfaceKHR)(screen->instance, &cdt->info.xcb, NULL, &surface);
88        break;
89 #endif
90 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
91     case VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR:
92        error = VKSCR(CreateWaylandSurfaceKHR)(screen->instance, &cdt->info.wl, NULL, &surface);
93        break;
94 #endif
95  #ifdef VK_USE_PLATFORM_WIN32_KHR
96     case VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR:
97        error = VKSCR(CreateWin32SurfaceKHR)(screen->instance, &cdt->info.win32, NULL, &surface);
98        break;
99 #endif
100     default:
101        unreachable("unsupported!");
102     }
103     if (error != VK_SUCCESS) {
104        return VK_NULL_HANDLE;
105     }
106 
107     VkBool32 supported;
108     error = VKSCR(GetPhysicalDeviceSurfaceSupportKHR)(screen->pdev, screen->gfx_queue, surface, &supported);
109     if (!zink_screen_handle_vkresult(screen, error) || !supported)
110        goto fail;
111 
112     unsigned count = 10;
113     VkPresentModeKHR modes[10];
114     error = VKSCR(GetPhysicalDeviceSurfacePresentModesKHR)(screen->pdev, surface, &count, modes);
115     if (!zink_screen_handle_vkresult(screen, error))
116        goto fail;
117 
118     for (unsigned i = 0; i < count; i++) {
119        /* VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR and VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR
120         * are not handled
121         */
122        assert(modes[i] <= VK_PRESENT_MODE_FIFO_RELAXED_KHR);
123        if (modes[i] <= VK_PRESENT_MODE_FIFO_RELAXED_KHR)
124           cdt->present_modes |= BITFIELD_BIT(modes[i]);
125     }
126 
127     zink_kopper_set_present_mode_for_interval(cdt, cdt->info.initial_swap_interval);
128 
129     return surface;
130 fail:
131    VKSCR(DestroySurfaceKHR)(screen->instance, surface, NULL);
132    return VK_NULL_HANDLE;
133 }
134 
135 static void
destroy_swapchain(struct zink_screen * screen,struct kopper_swapchain * cswap)136 destroy_swapchain(struct zink_screen *screen, struct kopper_swapchain *cswap)
137 {
138    if (!cswap)
139       return;
140    for (unsigned i = 0; i < cswap->num_images; i++) {
141       VKSCR(DestroySemaphore)(screen->dev, cswap->images[i].acquire, NULL);
142    }
143    free(cswap->images);
144    hash_table_foreach(cswap->presents, he) {
145       struct util_dynarray *arr = he->data;
146       while (util_dynarray_contains(arr, VkSemaphore))
147          VKSCR(DestroySemaphore)(screen->dev, util_dynarray_pop(arr, VkSemaphore), NULL);
148       util_dynarray_fini(arr);
149       free(arr);
150    }
151    _mesa_hash_table_destroy(cswap->presents, NULL);
152    VKSCR(DestroySwapchainKHR)(screen->dev, cswap->swapchain, NULL);
153    free(cswap);
154 }
155 
156 static void
prune_old_swapchains(struct zink_screen * screen,struct kopper_displaytarget * cdt,bool wait)157 prune_old_swapchains(struct zink_screen *screen, struct kopper_displaytarget *cdt, bool wait)
158 {
159    while (cdt->old_swapchain) {
160       struct kopper_swapchain *cswap = cdt->old_swapchain;
161       if (cswap->async_presents) {
162          if (wait)
163             continue;
164          return;
165       }
166       cdt->old_swapchain = cswap->next;
167       destroy_swapchain(screen, cswap);
168    }
169 }
170 
171 static struct hash_entry *
find_dt_entry(struct zink_screen * screen,const struct kopper_displaytarget * cdt)172 find_dt_entry(struct zink_screen *screen, const struct kopper_displaytarget *cdt)
173 {
174    struct hash_entry *he = NULL;
175    switch (cdt->type) {
176 #ifdef VK_USE_PLATFORM_XCB_KHR
177    case KOPPER_X11:
178       he = _mesa_hash_table_search_pre_hashed(&screen->dts, cdt->info.xcb.window, (void*)(uintptr_t)cdt->info.xcb.window);
179       break;
180 #endif
181 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
182    case KOPPER_WAYLAND:
183       he = _mesa_hash_table_search(&screen->dts, cdt->info.wl.surface);
184       break;
185 #endif
186 #ifdef VK_USE_PLATFORM_WIN32_KHR
187    case KOPPER_WIN32:
188       he = _mesa_hash_table_search(&screen->dts, cdt->info.win32.hwnd);
189       break;
190 #endif
191    default:
192       unreachable("unsupported!");
193    }
194    return he;
195 }
196 
197 void
zink_kopper_deinit_displaytarget(struct zink_screen * screen,struct kopper_displaytarget * cdt)198 zink_kopper_deinit_displaytarget(struct zink_screen *screen, struct kopper_displaytarget *cdt)
199 {
200    if (!cdt->surface)
201       return;
202    simple_mtx_lock(&screen->dt_lock);
203    struct hash_entry *he = find_dt_entry(screen, cdt);
204    assert(he);
205    /* this deinits the registered entry, which should always be the "right" entry */
206    cdt = he->data;
207    _mesa_hash_table_remove(&screen->dts, he);
208    simple_mtx_unlock(&screen->dt_lock);
209    destroy_swapchain(screen, cdt->swapchain);
210    prune_old_swapchains(screen, cdt, true);
211    VKSCR(DestroySurfaceKHR)(screen->instance, cdt->surface, NULL);
212    cdt->swapchain = cdt->old_swapchain = NULL;
213    cdt->surface = VK_NULL_HANDLE;
214    util_queue_fence_destroy(&cdt->present_fence);
215 }
216 
217 static struct kopper_swapchain *
kopper_CreateSwapchain(struct zink_screen * screen,struct kopper_displaytarget * cdt,unsigned w,unsigned h,VkResult * result)218 kopper_CreateSwapchain(struct zink_screen *screen, struct kopper_displaytarget *cdt, unsigned w, unsigned h, VkResult *result)
219 {
220    VkResult error = VK_SUCCESS;
221    struct kopper_swapchain *cswap = CALLOC_STRUCT(kopper_swapchain);
222    if (!cswap)
223       return NULL;
224    cswap->last_present_prune = 1;
225 
226    bool has_alpha = cdt->info.has_alpha && (cdt->caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR);
227    if (cdt->swapchain) {
228       cswap->scci = cdt->swapchain->scci;
229       cswap->scci.oldSwapchain = cdt->swapchain->swapchain;
230    } else {
231       cswap->scci.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
232       cswap->scci.pNext = NULL;
233       cswap->scci.surface = cdt->surface;
234       cswap->scci.flags = zink_kopper_has_srgb(cdt) ? VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR : 0;
235       cswap->scci.imageFormat = cdt->formats[0];
236       cswap->scci.imageColorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
237       // TODO: This is where you'd hook up stereo
238       cswap->scci.imageArrayLayers = 1;
239       cswap->scci.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
240                                VK_IMAGE_USAGE_SAMPLED_BIT |
241                                VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
242                                VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
243       cswap->scci.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
244       cswap->scci.queueFamilyIndexCount = 0;
245       cswap->scci.pQueueFamilyIndices = NULL;
246       cswap->scci.compositeAlpha = has_alpha ? VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
247       cswap->scci.clipped = VK_TRUE;
248    }
249    cswap->scci.presentMode = cdt->present_mode;
250    cswap->scci.minImageCount = cdt->caps.minImageCount;
251    cswap->scci.preTransform = cdt->caps.currentTransform;
252    if (cdt->formats[1])
253       cswap->scci.pNext = &cdt->format_list;
254 
255    /* different display platforms have, by vulkan spec, different sizing methodologies */
256    switch (cdt->type) {
257    case KOPPER_X11:
258    case KOPPER_WIN32:
259       /* With Xcb, minImageExtent, maxImageExtent, and currentExtent must always equal the window size.
260        * ...
261        * Due to above restrictions, it is only possible to create a new swapchain on this
262        * platform with imageExtent being equal to the current size of the window.
263        */
264       cswap->scci.imageExtent.width = cdt->caps.currentExtent.width;
265       cswap->scci.imageExtent.height = cdt->caps.currentExtent.height;
266       break;
267    case KOPPER_WAYLAND:
268       /* On Wayland, currentExtent is the special value (0xFFFFFFFF, 0xFFFFFFFF), indicating that the
269        * surface size will be determined by the extent of a swapchain targeting the surface. Whatever the
270        * application sets a swapchain’s imageExtent to will be the size of the window, after the first image is
271        * presented.
272        */
273       cswap->scci.imageExtent.width = w;
274       cswap->scci.imageExtent.height = h;
275       break;
276    default:
277       unreachable("unknown display platform");
278    }
279 
280    error = VKSCR(CreateSwapchainKHR)(screen->dev, &cswap->scci, NULL,
281                                 &cswap->swapchain);
282    if (error == VK_ERROR_NATIVE_WINDOW_IN_USE_KHR) {
283       if (util_queue_is_initialized(&screen->flush_queue))
284          util_queue_finish(&screen->flush_queue);
285       VkResult result = VKSCR(QueueWaitIdle)(screen->queue);
286       if (result != VK_SUCCESS)
287          mesa_loge("ZINK: vkQueueWaitIdle failed (%s)", vk_Result_to_str(result));
288       zink_kopper_deinit_displaytarget(screen, cdt);
289       error = VKSCR(CreateSwapchainKHR)(screen->dev, &cswap->scci, NULL,
290                                    &cswap->swapchain);
291    }
292    if (error != VK_SUCCESS) {
293        mesa_loge("CreateSwapchainKHR failed with %s\n", vk_Result_to_str(error));
294        free(cswap);
295        *result = error;
296        return NULL;
297    }
298    cswap->max_acquires = cswap->scci.minImageCount - cdt->caps.minImageCount;
299    cswap->last_present = UINT32_MAX;
300 
301    *result = VK_SUCCESS;
302    return cswap;
303 }
304 
305 static VkResult
kopper_GetSwapchainImages(struct zink_screen * screen,struct kopper_swapchain * cswap)306 kopper_GetSwapchainImages(struct zink_screen *screen, struct kopper_swapchain *cswap)
307 {
308    VkResult error = VKSCR(GetSwapchainImagesKHR)(screen->dev, cswap->swapchain, &cswap->num_images, NULL);
309    zink_screen_handle_vkresult(screen, error);
310    if (error != VK_SUCCESS)
311       return error;
312    cswap->images = calloc(cswap->num_images, sizeof(struct kopper_swapchain_image));
313    cswap->presents = _mesa_hash_table_create_u32_keys(NULL);
314    VkImage images[32];
315    error = VKSCR(GetSwapchainImagesKHR)(screen->dev, cswap->swapchain, &cswap->num_images, images);
316    assert(cswap->num_images <= ARRAY_SIZE(images));
317    if (zink_screen_handle_vkresult(screen, error)) {
318       for (unsigned i = 0; i < cswap->num_images; i++)
319          cswap->images[i].image = images[i];
320    }
321    return error;
322 }
323 
324 static VkResult
update_caps(struct zink_screen * screen,struct kopper_displaytarget * cdt)325 update_caps(struct zink_screen *screen, struct kopper_displaytarget *cdt)
326 {
327    VkResult error = VKSCR(GetPhysicalDeviceSurfaceCapabilitiesKHR)(screen->pdev, cdt->surface, &cdt->caps);
328    zink_screen_handle_vkresult(screen, error);
329    return error;
330 }
331 
332 static VkResult
update_swapchain(struct zink_screen * screen,struct kopper_displaytarget * cdt,unsigned w,unsigned h)333 update_swapchain(struct zink_screen *screen, struct kopper_displaytarget *cdt, unsigned w, unsigned h)
334 {
335    VkResult error = update_caps(screen, cdt);
336    if (error != VK_SUCCESS)
337       return error;
338    struct kopper_swapchain *cswap = kopper_CreateSwapchain(screen, cdt, w, h, &error);
339    if (!cswap)
340       return error;
341    prune_old_swapchains(screen, cdt, false);
342    struct kopper_swapchain **pswap = &cdt->old_swapchain;
343    while (*pswap)
344       *pswap = (*pswap)->next;
345    *pswap = cdt->swapchain;
346    cdt->swapchain = cswap;
347 
348    return kopper_GetSwapchainImages(screen, cdt->swapchain);
349 }
350 
351 struct kopper_displaytarget *
zink_kopper_displaytarget_create(struct zink_screen * screen,unsigned tex_usage,enum pipe_format format,unsigned width,unsigned height,unsigned alignment,const void * loader_private,unsigned * stride)352 zink_kopper_displaytarget_create(struct zink_screen *screen, unsigned tex_usage,
353                                  enum pipe_format format, unsigned width,
354                                  unsigned height, unsigned alignment,
355                                  const void *loader_private, unsigned *stride)
356 {
357    struct kopper_displaytarget *cdt;
358    const struct kopper_loader_info *info = loader_private;
359 
360    {
361       struct kopper_displaytarget k;
362       struct hash_entry *he = NULL;
363       k.info = *info;
364       init_dt_type(&k);
365       simple_mtx_lock(&screen->dt_lock);
366       if (unlikely(!screen->dts.table)) {
367          switch (k.type) {
368          case KOPPER_X11:
369             _mesa_hash_table_init(&screen->dts, screen, NULL, _mesa_key_pointer_equal);
370             break;
371          case KOPPER_WAYLAND:
372          case KOPPER_WIN32:
373             _mesa_hash_table_init(&screen->dts, screen, _mesa_hash_pointer, _mesa_key_pointer_equal);
374             break;
375          default:
376             unreachable("unknown kopper type");
377          }
378       } else {
379          he = find_dt_entry(screen, &k);
380       }
381       simple_mtx_unlock(&screen->dt_lock);
382       if (he) {
383          cdt = he->data;
384          p_atomic_inc(&cdt->refcount);
385          *stride = cdt->stride;
386          return cdt;
387       }
388    }
389 
390    cdt = CALLOC_STRUCT(kopper_displaytarget);
391    if (!cdt)
392       return NULL;
393 
394    cdt->refcount = 1;
395    cdt->loader_private = (void*)loader_private;
396    cdt->info = *info;
397    util_queue_fence_init(&cdt->present_fence);
398 
399    enum pipe_format srgb = PIPE_FORMAT_NONE;
400    if (screen->info.have_KHR_swapchain_mutable_format) {
401       srgb = util_format_is_srgb(format) ? util_format_linear(format) : util_format_srgb(format);
402       /* why do these helpers have different default return values? */
403       if (srgb == format)
404          srgb = PIPE_FORMAT_NONE;
405    }
406    cdt->formats[0] = zink_get_format(screen, format);
407    if (srgb) {
408       cdt->format_list.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO;
409       cdt->format_list.pNext = NULL;
410       cdt->format_list.viewFormatCount = 2;
411       cdt->format_list.pViewFormats = cdt->formats;
412 
413       cdt->formats[1] = zink_get_format(screen, srgb);
414    }
415 
416    cdt->surface = kopper_CreateSurface(screen, cdt);
417    if (!cdt->surface)
418       goto out;
419 
420    if (update_swapchain(screen, cdt, width, height) != VK_SUCCESS)
421       goto out;
422 
423    simple_mtx_lock(&screen->dt_lock);
424    switch (cdt->type) {
425 #ifdef VK_USE_PLATFORM_XCB_KHR
426    case KOPPER_X11:
427       _mesa_hash_table_insert_pre_hashed(&screen->dts, cdt->info.xcb.window, (void*)(uintptr_t)cdt->info.xcb.window, cdt);
428       break;
429 #endif
430 #ifdef VK_USE_PLATFORM_WAYLAND_KHR
431    case KOPPER_WAYLAND:
432       _mesa_hash_table_insert(&screen->dts, cdt->info.wl.surface, cdt);
433       break;
434 #endif
435 #ifdef VK_USE_PLATFORM_WIN32_KHR
436    case KOPPER_WIN32:
437       _mesa_hash_table_insert(&screen->dts, cdt->info.win32.hwnd, cdt);
438       break;
439 #endif
440    default:
441       unreachable("unsupported!");
442    }
443    simple_mtx_unlock(&screen->dt_lock);
444 
445    *stride = cdt->stride;
446    return cdt;
447 
448 //moar cleanup
449 out:
450    return NULL;
451 }
452 
453 void
zink_kopper_displaytarget_destroy(struct zink_screen * screen,struct kopper_displaytarget * cdt)454 zink_kopper_displaytarget_destroy(struct zink_screen *screen, struct kopper_displaytarget *cdt)
455 {
456    if (!p_atomic_dec_zero(&cdt->refcount))
457       return;
458    zink_kopper_deinit_displaytarget(screen, cdt);
459    FREE(cdt);
460 }
461 
462 static VkResult
kopper_acquire(struct zink_screen * screen,struct zink_resource * res,uint64_t timeout)463 kopper_acquire(struct zink_screen *screen, struct zink_resource *res, uint64_t timeout)
464 {
465    struct kopper_displaytarget *cdt = res->obj->dt;
466 
467    /* if:
468     * - we don't need a new image
469     * - we have a swapchain image
470     * - that image is either acquired or acquiring
471     *
472     * then this is a no-op
473     */
474    if (!res->obj->new_dt && res->obj->dt_idx != UINT32_MAX &&
475        (cdt->swapchain->images[res->obj->dt_idx].acquire || cdt->swapchain->images[res->obj->dt_idx].acquired))
476       return VK_SUCCESS;
477    VkSemaphore acquire = VK_NULL_HANDLE;
478 
479    while (true) {
480       if (res->obj->new_dt) {
481          VkResult error = update_swapchain(screen, cdt, res->base.b.width0, res->base.b.height0);
482          zink_screen_handle_vkresult(screen, error);
483          if (error != VK_SUCCESS)
484             return error;
485          res->obj->new_dt = false;
486          res->layout = VK_IMAGE_LAYOUT_UNDEFINED;
487          res->obj->access = 0;
488          res->obj->access_stage = 0;
489       }
490       if (timeout == UINT64_MAX && util_queue_is_initialized(&screen->flush_queue) &&
491           p_atomic_read_relaxed(&cdt->swapchain->num_acquires) > cdt->swapchain->max_acquires) {
492          util_queue_fence_wait(&cdt->present_fence);
493       }
494       VkSemaphoreCreateInfo sci = {
495          VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
496          NULL,
497          0
498       };
499       VkResult ret;
500       if (!acquire) {
501          ret = VKSCR(CreateSemaphore)(screen->dev, &sci, NULL, &acquire);
502          assert(acquire);
503          if (ret != VK_SUCCESS)
504             return ret;
505       }
506       ret = VKSCR(AcquireNextImageKHR)(screen->dev, cdt->swapchain->swapchain, timeout, acquire, VK_NULL_HANDLE, &res->obj->dt_idx);
507       if (ret != VK_SUCCESS && ret != VK_SUBOPTIMAL_KHR) {
508          if (ret == VK_ERROR_OUT_OF_DATE_KHR) {
509             res->obj->new_dt = true;
510             continue;
511          }
512          VKSCR(DestroySemaphore)(screen->dev, acquire, NULL);
513          return ret;
514       }
515       break;
516    }
517 
518    cdt->swapchain->images[res->obj->dt_idx].acquire = acquire;
519    res->obj->image = cdt->swapchain->images[res->obj->dt_idx].image;
520    cdt->swapchain->images[res->obj->dt_idx].acquired = false;
521    if (!cdt->swapchain->images[res->obj->dt_idx].init) {
522       /* swapchain images are initially in the UNDEFINED layout */
523       res->layout = VK_IMAGE_LAYOUT_UNDEFINED;
524       cdt->swapchain->images[res->obj->dt_idx].init = true;
525    }
526    if (timeout == UINT64_MAX) {
527       res->obj->indefinite_acquire = true;
528       p_atomic_inc(&cdt->swapchain->num_acquires);
529    }
530    cdt->swapchain->images[res->obj->dt_idx].dt_has_data = false;
531    return VK_SUCCESS;
532 }
533 
534 static void
kill_swapchain(struct zink_context * ctx,struct zink_resource * res)535 kill_swapchain(struct zink_context *ctx, struct zink_resource *res)
536 {
537    struct zink_screen *screen = zink_screen(ctx->base.screen);
538    /* dead swapchain */
539    mesa_loge("zink: swapchain killed %p\n", res);
540    zink_batch_reference_resource(&ctx->batch, res);
541    struct pipe_resource *pres = screen->base.resource_create(&screen->base, &res->base.b);
542    zink_resource_object_reference(screen, &res->obj, zink_resource(pres)->obj);
543    res->layout = VK_IMAGE_LAYOUT_UNDEFINED;
544    res->swapchain = false;
545    pipe_resource_reference(&pres, NULL);
546 }
547 
548 static bool
is_swapchain_kill(VkResult ret)549 is_swapchain_kill(VkResult ret)
550 {
551    return ret != VK_SUCCESS &&
552           ret != VK_TIMEOUT &&
553           ret != VK_NOT_READY &&
554           ret != VK_SUBOPTIMAL_KHR;
555 }
556 
557 bool
zink_kopper_acquire(struct zink_context * ctx,struct zink_resource * res,uint64_t timeout)558 zink_kopper_acquire(struct zink_context *ctx, struct zink_resource *res, uint64_t timeout)
559 {
560    assert(zink_is_swapchain(res));
561    struct kopper_displaytarget *cdt = res->obj->dt;
562    if (!cdt)
563       /* dead swapchain */
564       return false;
565    if (cdt->is_kill) {
566       kill_swapchain(ctx, res);
567       return false;
568    }
569    const struct kopper_swapchain *cswap = cdt->swapchain;
570    res->obj->new_dt |= res->base.b.width0 != cswap->scci.imageExtent.width ||
571                        res->base.b.height0 != cswap->scci.imageExtent.height;
572    VkResult ret = kopper_acquire(zink_screen(ctx->base.screen), res, timeout);
573    if (ret == VK_SUCCESS || ret == VK_SUBOPTIMAL_KHR) {
574       if (cswap != cdt->swapchain) {
575          ctx->swapchain_size = cdt->swapchain->scci.imageExtent;
576          res->base.b.width0 = ctx->swapchain_size.width;
577          res->base.b.height0 = ctx->swapchain_size.height;
578       }
579    } else if (is_swapchain_kill(ret)) {
580       kill_swapchain(ctx, res);
581    }
582    return !is_swapchain_kill(ret);
583 }
584 
585 VkSemaphore
zink_kopper_acquire_submit(struct zink_screen * screen,struct zink_resource * res)586 zink_kopper_acquire_submit(struct zink_screen *screen, struct zink_resource *res)
587 {
588    assert(res->obj->dt);
589    struct kopper_displaytarget *cdt = res->obj->dt;
590    assert(res->obj->dt_idx != UINT32_MAX);
591    if (cdt->swapchain->images[res->obj->dt_idx].dt_has_data)
592       return VK_NULL_HANDLE;
593    assert(res->obj->dt_idx != UINT32_MAX);
594    if (cdt->swapchain->images[res->obj->dt_idx].acquired) {
595       assert(!cdt->swapchain->images[res->obj->dt_idx].acquire);
596       return VK_NULL_HANDLE;
597    }
598    assert(cdt->swapchain->images[res->obj->dt_idx].acquire);
599    cdt->swapchain->images[res->obj->dt_idx].acquired = true;
600    /* this is now owned by the batch */
601    VkSemaphore acquire = cdt->swapchain->images[res->obj->dt_idx].acquire;
602    cdt->swapchain->images[res->obj->dt_idx].acquire = VK_NULL_HANDLE;
603    cdt->swapchain->images[res->obj->dt_idx].dt_has_data = true;
604    return acquire;
605 }
606 
607 VkSemaphore
zink_kopper_present(struct zink_screen * screen,struct zink_resource * res)608 zink_kopper_present(struct zink_screen *screen, struct zink_resource *res)
609 {
610    assert(res->obj->dt);
611    assert(!res->obj->present);
612    VkSemaphoreCreateInfo sci = {
613       VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
614       NULL,
615       0
616    };
617    assert(zink_kopper_acquired(res->obj->dt, res->obj->dt_idx));
618    VkResult ret = VKSCR(CreateSemaphore)(screen->dev, &sci, NULL, &res->obj->present);
619    return zink_screen_handle_vkresult(screen, ret) ? res->obj->present : VK_NULL_HANDLE;
620 }
621 
622 struct kopper_present_info {
623    VkPresentInfoKHR info;
624    uint32_t image;
625    struct kopper_swapchain *swapchain;
626    struct zink_resource *res;
627    VkSemaphore sem;
628    bool indefinite_acquire;
629 };
630 
631 static void
kopper_present(void * data,void * gdata,int thread_idx)632 kopper_present(void *data, void *gdata, int thread_idx)
633 {
634    struct kopper_present_info *cpi = data;
635    struct kopper_displaytarget *cdt = cpi->res->obj->dt;
636    struct kopper_swapchain *swapchain = cpi->swapchain;
637    struct zink_screen *screen = gdata;
638    VkResult error = VK_SUCCESS;
639    cpi->info.pResults = &error;
640 
641    simple_mtx_lock(&screen->queue_lock);
642    if (screen->driver_workarounds.implicit_sync && cdt->type != KOPPER_WIN32) {
643       if (!screen->fence) {
644          VkFenceCreateInfo fci = {0};
645          fci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
646          VKSCR(CreateFence)(screen->dev, &fci, NULL, &screen->fence);
647       }
648       VKSCR(ResetFences)(screen->dev, 1, &screen->fence);
649       VkSubmitInfo si = {0};
650       si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
651       si.waitSemaphoreCount = 1;
652       si.pWaitSemaphores = cpi->info.pWaitSemaphores;
653       VkPipelineStageFlags stages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
654       si.pWaitDstStageMask = &stages;
655 
656       error = VKSCR(QueueSubmit)(screen->queue, 1, &si, screen->fence);
657       if (!zink_screen_handle_vkresult(screen, error)) {
658          simple_mtx_unlock(&screen->queue_lock);
659          VKSCR(DestroySemaphore)(screen->dev, cpi->sem, NULL);
660          goto out;
661       }
662       error = VKSCR(WaitForFences)(screen->dev, 1, &screen->fence, VK_TRUE, UINT64_MAX);
663       if (!zink_screen_handle_vkresult(screen, error)) {
664          simple_mtx_unlock(&screen->queue_lock);
665          VKSCR(DestroySemaphore)(screen->dev, cpi->sem, NULL);
666          goto out;
667       }
668       cpi->info.pWaitSemaphores = NULL;
669       cpi->info.waitSemaphoreCount = 0;
670    }
671    VkResult error2 = VKSCR(QueuePresentKHR)(screen->queue, &cpi->info);
672    simple_mtx_unlock(&screen->queue_lock);
673    swapchain->last_present = cpi->image;
674    if (cpi->indefinite_acquire)
675       p_atomic_dec(&swapchain->num_acquires);
676    if (error2 == VK_SUBOPTIMAL_KHR && cdt->swapchain == swapchain)
677       cpi->res->obj->new_dt = true;
678 
679    /* it's illegal to destroy semaphores if they're in use by a cmdbuf.
680     * but what does "in use" actually mean?
681     * in truth, when using timelines, nobody knows. especially not VVL.
682     *
683     * thus, to avoid infinite error spam and thread-related races,
684     * present semaphores need their own free queue based on the
685     * last-known completed timeline id so that the semaphore persists through
686     * normal cmdbuf submit/signal and then also exists here when it's needed for the present operation
687     */
688    struct util_dynarray *arr;
689    for (; screen->last_finished && swapchain->last_present_prune != screen->last_finished; swapchain->last_present_prune++) {
690       struct hash_entry *he = _mesa_hash_table_search(swapchain->presents,
691                                                       (void*)(uintptr_t)swapchain->last_present_prune);
692       if (he) {
693          arr = he->data;
694          while (util_dynarray_contains(arr, VkSemaphore))
695             VKSCR(DestroySemaphore)(screen->dev, util_dynarray_pop(arr, VkSemaphore), NULL);
696          util_dynarray_fini(arr);
697          free(arr);
698          _mesa_hash_table_remove(swapchain->presents, he);
699       }
700    }
701    /* queue this wait semaphore for deletion on completion of the next batch */
702    assert(screen->curr_batch > 0);
703    uint32_t next = (uint32_t)screen->curr_batch + 1;
704    /* handle overflow */
705    next = MAX2(next + 1, 1);
706    struct hash_entry *he = _mesa_hash_table_search(swapchain->presents, (void*)(uintptr_t)next);
707    if (he)
708       arr = he->data;
709    else {
710       arr = malloc(sizeof(struct util_dynarray));
711       util_dynarray_init(arr, NULL);
712       _mesa_hash_table_insert(swapchain->presents, (void*)(uintptr_t)next, arr);
713    }
714    util_dynarray_append(arr, VkSemaphore, cpi->sem);
715 out:
716    if (thread_idx != -1)
717       p_atomic_dec(&swapchain->async_presents);
718    free(cpi);
719 }
720 
721 void
zink_kopper_present_queue(struct zink_screen * screen,struct zink_resource * res)722 zink_kopper_present_queue(struct zink_screen *screen, struct zink_resource *res)
723 {
724    assert(res->obj->dt);
725    struct kopper_displaytarget *cdt = res->obj->dt;
726    assert(zink_kopper_acquired(res->obj->dt, res->obj->dt_idx));
727    assert(res->obj->present);
728    struct kopper_present_info *cpi = malloc(sizeof(struct kopper_present_info));
729    cpi->sem = res->obj->present;
730    cpi->res = res;
731    cpi->swapchain = cdt->swapchain;
732    cpi->indefinite_acquire = res->obj->indefinite_acquire;
733    res->obj->last_dt_idx = cpi->image = res->obj->dt_idx;
734    cpi->info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
735    cpi->info.pNext = NULL;
736    cpi->info.waitSemaphoreCount = 1;
737    cpi->info.pWaitSemaphores = &cpi->sem;
738    cpi->info.swapchainCount = 1;
739    cpi->info.pSwapchains = &cdt->swapchain->swapchain;
740    cpi->info.pImageIndices = &cpi->image;
741    cpi->info.pResults = NULL;
742    res->obj->present = VK_NULL_HANDLE;
743    /* Ex GLX_EXT_buffer_age:
744     *
745     *  Buffers' ages are initialized to 0 at buffer creation time.
746     *  When a frame boundary is reached, the following occurs before
747     *  any exchanging or copying of color buffers:
748     *
749     *  * The current back buffer's age is set to 1.
750     *  * Any other color buffers' ages are incremented by 1 if
751     *    their age was previously greater than 0.
752     */
753    for (int i = 0; i < cdt->swapchain->num_images; i++) {
754        if (i == res->obj->dt_idx)
755            cdt->swapchain->images[i].age = 1;
756        else if (cdt->swapchain->images[i].age > 0)
757            cdt->swapchain->images[i].age += 1;
758    }
759    if (util_queue_is_initialized(&screen->flush_queue)) {
760       p_atomic_inc(&cpi->swapchain->async_presents);
761       util_queue_add_job(&screen->flush_queue, cpi, &cdt->present_fence,
762                          kopper_present, NULL, 0);
763    } else {
764       kopper_present(cpi, screen, -1);
765    }
766    res->obj->indefinite_acquire = false;
767    cdt->swapchain->images[res->obj->dt_idx].acquired = false;
768    res->obj->dt_idx = UINT32_MAX;
769 }
770 
771 bool
zink_kopper_acquire_readback(struct zink_context * ctx,struct zink_resource * res)772 zink_kopper_acquire_readback(struct zink_context *ctx, struct zink_resource *res)
773 {
774    struct zink_screen *screen = zink_screen(ctx->base.screen);
775    assert(res->obj->dt);
776    struct kopper_displaytarget *cdt = res->obj->dt;
777    const struct kopper_swapchain *cswap = cdt->swapchain;
778    uint32_t last_dt_idx = res->obj->last_dt_idx;
779    VkResult ret = VK_SUCCESS;
780    /* if this hasn't been presented or if it has data, use this as the readback target */
781    if (res->obj->last_dt_idx == UINT32_MAX ||
782        (zink_kopper_acquired(cdt, res->obj->dt_idx) && cdt->swapchain->images[res->obj->dt_idx].dt_has_data))
783       return false;
784    while (res->obj->dt_idx != last_dt_idx) {
785       if (res->obj->dt_idx != UINT32_MAX && !zink_kopper_present_readback(ctx, res))
786          break;
787       do {
788          ret = kopper_acquire(screen, res, 0);
789       } while (!is_swapchain_kill(ret) && (ret == VK_NOT_READY || ret == VK_TIMEOUT));
790       if (is_swapchain_kill(ret)) {
791          kill_swapchain(ctx, res);
792          return false;
793       }
794    }
795    if (cswap != cdt->swapchain) {
796       ctx->swapchain_size = cdt->swapchain->scci.imageExtent;
797       res->base.b.width0 = ctx->swapchain_size.width;
798       res->base.b.height0 = ctx->swapchain_size.height;
799    }
800    return true;
801 }
802 
803 bool
zink_kopper_present_readback(struct zink_context * ctx,struct zink_resource * res)804 zink_kopper_present_readback(struct zink_context *ctx, struct zink_resource *res)
805 {
806    struct zink_screen *screen = zink_screen(ctx->base.screen);
807    VkSubmitInfo si = {0};
808    if (res->obj->last_dt_idx == UINT32_MAX)
809       return true;
810    if (res->layout != VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) {
811       zink_resource_image_barrier(ctx, res, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, 0, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
812       ctx->base.flush(&ctx->base, NULL, 0);
813    }
814    si.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
815    si.signalSemaphoreCount = 1;
816    VkPipelineStageFlags mask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
817    si.pWaitDstStageMask = &mask;
818    VkSemaphore acquire = zink_kopper_acquire_submit(screen, res);
819    VkSemaphore present = res->obj->present ? res->obj->present : zink_kopper_present(screen, res);
820    if (screen->threaded)
821       util_queue_finish(&screen->flush_queue);
822    si.waitSemaphoreCount = !!acquire;
823    si.pWaitSemaphores = &acquire;
824    si.pSignalSemaphores = &present;
825    VkResult error = VKSCR(QueueSubmit)(screen->queue, 1, &si, VK_NULL_HANDLE);
826    if (!zink_screen_handle_vkresult(screen, error))
827       return false;
828 
829    zink_kopper_present_queue(screen, res);
830    error = VKSCR(QueueWaitIdle)(screen->queue);
831    return zink_screen_handle_vkresult(screen, error);
832 }
833 
834 bool
zink_kopper_update(struct pipe_screen * pscreen,struct pipe_resource * pres,int * w,int * h)835 zink_kopper_update(struct pipe_screen *pscreen, struct pipe_resource *pres, int *w, int *h)
836 {
837    struct zink_resource *res = zink_resource(pres);
838    struct zink_screen *screen = zink_screen(pscreen);
839    assert(pres->bind & PIPE_BIND_DISPLAY_TARGET);
840    if (!res->obj->dt)
841       return false;
842    struct kopper_displaytarget *cdt = res->obj->dt;
843    if (cdt->type != KOPPER_X11) {
844       *w = res->base.b.width0;
845       *h = res->base.b.height0;
846       return true;
847    }
848    VkResult ret = update_caps(screen, cdt);
849    if (ret != VK_SUCCESS) {
850       mesa_loge("zink: failed to update swapchain capabilities: %s", vk_Result_to_str(ret));
851       cdt->is_kill = true;
852       return false;
853    }
854    *w = cdt->caps.currentExtent.width;
855    *h = cdt->caps.currentExtent.height;
856    return true;
857 }
858 
859 bool
zink_kopper_is_cpu(const struct pipe_screen * pscreen)860 zink_kopper_is_cpu(const struct pipe_screen *pscreen)
861 {
862    const struct zink_screen *screen = (const struct zink_screen*)pscreen;
863    return screen->is_cpu;
864 }
865 
866 void
zink_kopper_fixup_depth_buffer(struct zink_context * ctx)867 zink_kopper_fixup_depth_buffer(struct zink_context *ctx)
868 {
869    struct zink_screen *screen = zink_screen(ctx->base.screen);
870    if (!ctx->fb_state.zsbuf)
871       return;
872 
873    assert(ctx->fb_state.zsbuf->texture->bind & PIPE_BIND_DISPLAY_TARGET);
874 
875    struct zink_resource *res = zink_resource(ctx->fb_state.zsbuf->texture);
876    struct zink_surface *surf = zink_csurface(ctx->fb_state.zsbuf);
877    struct zink_ctx_surface *csurf = (struct zink_ctx_surface*)ctx->fb_state.zsbuf;
878    if (surf->info.width == ctx->fb_state.width &&
879        surf->info.height == ctx->fb_state.height)
880       return;
881 
882    struct pipe_resource templ = *ctx->fb_state.zsbuf->texture;
883    templ.width0 = ctx->fb_state.width;
884    templ.height0 = ctx->fb_state.height;
885    struct pipe_resource *pz = screen->base.resource_create(&screen->base, &templ);
886    struct zink_resource *z = zink_resource(pz);
887    zink_resource_object_reference(screen, &res->obj, z->obj);
888    res->base.b.width0 = ctx->fb_state.width;
889    res->base.b.height0 = ctx->fb_state.height;
890    pipe_resource_reference(&pz, NULL);
891 
892    ctx->fb_state.zsbuf->width = ctx->fb_state.width;
893    ctx->fb_state.zsbuf->height = ctx->fb_state.height;
894    struct pipe_surface *psurf = ctx->base.create_surface(&ctx->base, &res->base.b, ctx->fb_state.zsbuf);
895    struct zink_ctx_surface *cz = (struct zink_ctx_surface*)psurf;
896 
897    /* oh god why */
898    zink_surface_reference(screen, &csurf->surf, cz->surf);
899    pipe_surface_release(&ctx->base, &psurf);
900 }
901 
902 bool
zink_kopper_check(struct pipe_resource * pres)903 zink_kopper_check(struct pipe_resource *pres)
904 {
905    struct zink_resource *res = zink_resource(pres);
906    assert(pres->bind & PIPE_BIND_DISPLAY_TARGET);
907    if (!res->obj->dt)
908       return false;
909    struct kopper_displaytarget *cdt = res->obj->dt;
910    return !cdt->is_kill;
911 }
912 
913 void
zink_kopper_set_swap_interval(struct pipe_screen * pscreen,struct pipe_resource * pres,int interval)914 zink_kopper_set_swap_interval(struct pipe_screen *pscreen, struct pipe_resource *pres, int interval)
915 {
916    struct zink_resource *res = zink_resource(pres);
917    struct zink_screen *screen = zink_screen(pscreen);
918    assert(res->obj->dt);
919    struct kopper_displaytarget *cdt = res->obj->dt;
920    VkPresentModeKHR old_present_mode = cdt->present_mode;
921 
922    zink_kopper_set_present_mode_for_interval(cdt, interval);
923 
924    if (old_present_mode != cdt->present_mode)
925       update_swapchain(screen, cdt, cdt->caps.currentExtent.width, cdt->caps.currentExtent.height);
926 }
927 
928 int
zink_kopper_query_buffer_age(struct pipe_context * pctx,struct pipe_resource * pres)929 zink_kopper_query_buffer_age(struct pipe_context *pctx, struct pipe_resource *pres)
930 {
931    struct zink_context *ctx = zink_context(pctx);
932    struct zink_resource *res = zink_resource(pres);
933    assert(res->obj->dt);
934    struct kopper_displaytarget *cdt = res->obj->dt;
935 
936    ctx = zink_tc_context_unwrap(pctx);
937 
938    /* Returning 0 here isn't ideal (yes, the buffer is undefined, because you
939     * lost it) but threading the error up is more hassle than it's worth.
940     */
941    if (!zink_kopper_acquired(res->obj->dt, res->obj->dt_idx))
942       if (!zink_kopper_acquire(ctx, res, UINT64_MAX))
943          return 0;
944 
945    return cdt->swapchain->images[res->obj->dt_idx].age;
946 }
947