swapchain.c 22.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * This file is part of libplacebo.
 *
 * libplacebo is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * libplacebo is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "common.h"
#include "command.h"
#include "formats.h"
#include "utils.h"
22
#include "gpu.h"
23 24 25 26 27 28 29 30 31 32
#include "swapchain.h"

struct priv {
    struct vk_ctx *vk;
    VkSurfaceKHR surf;

    // current swapchain and metadata:
    VkSwapchainCreateInfoKHR protoInfo; // partially filled-in prototype
    VkSwapchainKHR swapchain;
    VkSwapchainKHR old_swapchain;
33
    int cur_width, cur_height;
34 35 36 37 38 39
    int swapchain_depth;
    int frames_in_flight;   // number of frames currently queued
    struct pl_color_repr color_repr;
    struct pl_color_space color_space;

    // state of the images:
40
    const struct pl_tex **images; // pl_tex wrappers for the VkImages
41 42 43 44 45 46 47 48
    int num_images;         // size of `images`
    VkSemaphore *sems_in;   // pool of semaphores used to synchronize images
    VkSemaphore *sems_out;  // outgoing semaphores (rendering complete)
    int num_sems;           // size of `sems_in` / `sems_out`
    int idx_sems;           // index of next free semaphore pair
    int last_imgidx;        // the image index last acquired (for submit)
};

49
static struct pl_sw_fns vulkan_swapchain;
50

51 52 53 54 55 56 57 58
static bool vk_map_color_space(VkColorSpaceKHR space, struct pl_color_space *out)
{
    switch (space) {
    // Note: This is technically against the spec, but more often than not
    // it's the correct result since `SRGB_NONLINEAR` is just a catch-all
    // for any sort of typical SDR curve, which is better approximated by
    // `pl_color_space_monitor`.
    case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
59 60 61 62
        *out = pl_color_space_monitor;
        return true;

#ifdef VK_EXT_swapchain_colorspace
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
        *out = pl_color_space_monitor;
        return true;
    case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_DISPLAY_P3,
            .transfer  = PL_COLOR_TRC_BT_1886,
        };
        return true;
    case VK_COLOR_SPACE_DCI_P3_LINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_DCI_P3,
            .transfer  = PL_COLOR_TRC_LINEAR,
        };
        return true;
    case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_DCI_P3,
            .transfer  = PL_COLOR_TRC_BT_1886,
        };
        return true;
    case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
    case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
        // TODO
        return false;
    case VK_COLOR_SPACE_BT709_LINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_DCI_P3,
            .transfer  = PL_COLOR_TRC_LINEAR,
        };
        return true;
    case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_BT_2020,
            .transfer  = PL_COLOR_TRC_LINEAR,
        };
        return true;
    case VK_COLOR_SPACE_HDR10_ST2084_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_BT_2020,
            .transfer  = PL_COLOR_TRC_PQ,
        };
        return true;
    case VK_COLOR_SPACE_DOLBYVISION_EXT:
        // Unlikely to ever be implemented
        return false;
    case VK_COLOR_SPACE_HDR10_HLG_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_BT_2020,
            .transfer  = PL_COLOR_TRC_HLG,
        };
        return true;
    case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_ADOBE,
            .transfer  = PL_COLOR_TRC_LINEAR,
        };
        return true;
    case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
        *out = (struct pl_color_space) {
            .primaries = PL_COLOR_PRIM_ADOBE,
            .transfer  = PL_COLOR_TRC_GAMMA22,
        };
        return true;
    case VK_COLOR_SPACE_PASS_THROUGH_EXT:
        *out = pl_color_space_unknown;
        return true;
130
#endif
131 132 133 134 135 136 137 138 139 140

    // Included to satisfy the switch coverage check
    case VK_COLOR_SPACE_RANGE_SIZE_KHR:
    case VK_COLOR_SPACE_MAX_ENUM_KHR:
        break;
    }

    return false;
}

141
static bool pick_surf_format(const struct pl_gpu *gpu, const struct vk_ctx *vk,
142 143
                             VkSurfaceKHR surf, VkSurfaceFormatKHR *out_format,
                             struct pl_color_space *space)
144 145 146 147
{
    VkSurfaceFormatKHR *formats = NULL;
    int num = 0;

148 149
    // Specific format requested by user
    if (out_format->format) {
150
        if (vk_map_color_space(out_format->colorSpace, space)) {
151 152 153 154 155 156 157
            return true;
        } else {
            PL_ERR(gpu, "User-supplied surface format unsupported: 0x%x",
                   (unsigned int) out_format->format);
        }
    }

158 159 160 161 162 163 164 165 166
    VK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk->physd, surf, &num, NULL));
    formats = talloc_array(NULL, VkSurfaceFormatKHR, num);
    VK(vkGetPhysicalDeviceSurfaceFormatsKHR(vk->physd, surf, &num, formats));

    for (int i = 0; i < num; i++) {
        // A value of VK_FORMAT_UNDEFINED means we can pick anything we want
        if (formats[i].format == VK_FORMAT_UNDEFINED) {
            *out_format = (VkSurfaceFormatKHR) {
                .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
167
                .format = VK_FORMAT_R8G8B8A8_UNORM,
168 169 170 171 172
            };
            talloc_free(formats);
            return true;
        }

173 174
        // Color space / format whitelist
        if (!vk_map_color_space(formats[i].colorSpace, space))
175 176 177
            continue;

        switch (formats[i].format) {
178 179 180 181 182 183 184 185 186 187 188 189
        // Only accept floating point formats for linear curves
        case VK_FORMAT_R16G16B16_SFLOAT:
        case VK_FORMAT_R16G16B16A16_SFLOAT:
        case VK_FORMAT_R32G32B32_SFLOAT:
        case VK_FORMAT_R32G32B32A32_SFLOAT:
        case VK_FORMAT_R64G64B64_SFLOAT:
        case VK_FORMAT_R64G64B64A64_SFLOAT:
            if (space->transfer == PL_COLOR_TRC_LINEAR)
                break; // accept
            continue;

        // Only accept 8 bit for non-HDR curves
190 191 192 193 194
        case VK_FORMAT_R8G8B8_UNORM:
        case VK_FORMAT_B8G8R8_UNORM:
        case VK_FORMAT_R8G8B8A8_UNORM:
        case VK_FORMAT_B8G8R8A8_UNORM:
        case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
195 196 197 198 199
            if (!pl_color_transfer_is_hdr(space->transfer))
                break; // accept
            continue;

        // Accept 10/16 bit formats universally
200 201 202 203 204
        case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
        case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
        case VK_FORMAT_R16G16B16_UNORM:
        case VK_FORMAT_R16G16B16A16_UNORM:
             break; // accept
205

206 207 208
        default: continue;
        }

209
        // Make sure we can wrap this format to a meaningful, valid pl_format
210 211
        for (int n = 0; n < gpu->num_formats; n++) {
            const struct pl_fmt *rafmt = gpu->formats[n];
212 213 214
            const struct vk_format *vkfmt = rafmt->priv;
            if (vkfmt->ifmt != formats[i].format)
                continue;
215 216 217 218 219

            enum pl_fmt_caps render_caps = 0;
            render_caps |= PL_FMT_CAP_RENDERABLE;
            render_caps |= PL_FMT_CAP_BLITTABLE;
            if ((rafmt->caps & render_caps) != render_caps)
220
                continue;
221

222 223 224 225 226 227 228 229 230 231 232 233 234 235
            // format valid, use it
            *out_format = formats[i];
            talloc_free(formats);
            return true;
        }
    }

    // fall through
error:
    PL_FATAL(vk, "Failed picking any valid, renderable surface format!");
    talloc_free(formats);
    return false;
}

236
const struct pl_swapchain *pl_vulkan_create_swapchain(const struct pl_vulkan *plvk,
237 238 239
                              const struct pl_vulkan_swapchain_params *params)
{
    struct vk_ctx *vk = plvk->priv;
240
    const struct pl_gpu *gpu = plvk->gpu;
241 242

    VkSurfaceFormatKHR sfmt = params->surface_format;
243 244
    struct pl_color_space csp;
    if (!pick_surf_format(gpu, vk, params->surface, &sfmt, &csp))
245 246
        return NULL;

247
    struct pl_swapchain *sw = talloc_zero(NULL, struct pl_swapchain);
248 249
    sw->impl = &vulkan_swapchain;
    sw->ctx = vk->ctx;
250
    sw->gpu = gpu;
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268

    struct priv *p = sw->priv = talloc_zero(sw, struct priv);
    p->vk = vk;
    p->surf = params->surface;
    p->swapchain_depth = PL_DEF(params->swapchain_depth, 3);
    pl_assert(p->swapchain_depth > 0);
    p->protoInfo = (VkSwapchainCreateInfoKHR) {
        .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
        .surface = p->surf,
        .imageFormat = sfmt.format,
        .imageColorSpace = sfmt.colorSpace,
        .imageArrayLayers = 1, // non-stereoscopic
        .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
        .minImageCount = p->swapchain_depth + 1, // +1 for the FB
        .presentMode = params->present_mode,
        .clipped = true,
    };

269
    p->color_space = csp;
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
    p->color_repr = (struct pl_color_repr) {
        .sys    = PL_COLOR_SYSTEM_RGB,
        .levels = PL_COLOR_LEVELS_PC,
        .alpha  = PL_ALPHA_UNKNOWN, // will be updated by vk_sw_recreate
    };

    // Make sure the swapchain present mode is supported
    VkPresentModeKHR *modes = NULL;
    int num_modes;
    VK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk->physd, p->surf,
                                                 &num_modes, NULL));
    modes = talloc_array(NULL, VkPresentModeKHR, num_modes);
    VK(vkGetPhysicalDeviceSurfacePresentModesKHR(vk->physd, p->surf,
                                                 &num_modes, modes));

    bool supported = false;
    for (int i = 0; i < num_modes; i++)
        supported |= (modes[i] == p->protoInfo.presentMode);
288
    TA_FREEP(&modes);
289 290 291 292 293 294 295 296 297 298 299

    if (!supported) {
        PL_WARN(vk, "Requested swap mode unsupported by this device, falling "
                "back to VK_PRESENT_MODE_FIFO_KHR");
        p->protoInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
    }

    return sw;

error:
    talloc_free(modes);
300
    talloc_free(sw);
301 302 303
    return NULL;
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
static void vk_sw_destroy(const struct pl_swapchain *sw)
{
    const struct pl_gpu *gpu = sw->gpu;
    struct priv *p = sw->priv;
    struct vk_ctx *vk = p->vk;

    pl_gpu_flush(gpu);
    vk_wait_idle(vk);
    for (int i = 0; i < p->num_images; i++)
        pl_tex_destroy(gpu, &p->images[i]);
    for (int i = 0; i < p->num_sems; i++) {
        vkDestroySemaphore(vk->dev, p->sems_in[i], VK_ALLOC);
        vkDestroySemaphore(vk->dev, p->sems_out[i], VK_ALLOC);
    }

    vkDestroySwapchainKHR(vk->dev, p->swapchain, VK_ALLOC);
    talloc_free((void *) sw);
}

323
static int vk_sw_latency(const struct pl_swapchain *sw)
324 325 326 327 328
{
    struct priv *p = sw->priv;
    return p->swapchain_depth;
}

329 330
static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info,
                                  int w, int h)
331 332 333 334 335 336 337 338 339 340 341 342 343
{
    struct vk_ctx *vk = p->vk;

    // Query the supported capabilities and update this struct as needed
    VkSurfaceCapabilitiesKHR caps;
    VK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk->physd, p->surf, &caps));

    // Sorted by preference
    static const struct { VkCompositeAlphaFlagsKHR vk_mode;
                          enum pl_alpha_mode pl_mode;
                        } alphaModes[] = {
        {VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,  PL_ALPHA_PREMULTIPLIED},
        {VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, PL_ALPHA_INDEPENDENT},
344
        {VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,         PL_ALPHA_UNKNOWN},
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
        {VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,          PL_ALPHA_UNKNOWN},
    };

    for (int i = 0; i < PL_ARRAY_SIZE(alphaModes); i++) {
        if (caps.supportedCompositeAlpha & alphaModes[i].vk_mode) {
            info->compositeAlpha = alphaModes[i].vk_mode;
            p->color_repr.alpha = alphaModes[i].pl_mode;
            break;
        }
    }

    if (!info->compositeAlpha) {
        PL_ERR(vk, "Failed picking alpha compositing mode (caps: 0x%x)",
               caps.supportedCompositeAlpha);
        goto error;
    }

    // Note: We could probably also allow picking a surface transform that
363
    // flips the framebuffer and set `pl_swapchain_frame.flipped`, but this
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
    // doesn't appear to be necessary for any vulkan implementations.
    static const VkSurfaceTransformFlagsKHR rotModes[] = {
        VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
        VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR,
    };

    for (int i = 0; i < PL_ARRAY_SIZE(rotModes); i++) {
        if (caps.supportedTransforms & rotModes[i]) {
            info->preTransform = rotModes[i];
            break;
        }
    }

    if (!info->preTransform) {
        PL_ERR(vk, "Failed picking surface transform mode (caps: 0x%x)",
               caps.supportedTransforms);
        goto error;
    }

    // Image count as required
384
    PL_DEBUG(vk, "Requested image count: %d (min %d max %d)",
385 386 387 388 389 390 391
             (int) info->minImageCount, (int) caps.minImageCount,
             (int) caps.maxImageCount);

    info->minImageCount = PL_MAX(info->minImageCount, caps.minImageCount);
    if (caps.maxImageCount)
        info->minImageCount = PL_MIN(info->minImageCount, caps.maxImageCount);

392 393 394 395 396 397 398 399 400
    // Default the requested size based on the reported extent
    if (caps.currentExtent.width != 0xFFFFFFFF)
        w = PL_DEF(w, caps.currentExtent.width);
    if (caps.currentExtent.height != 0xFFFFFFFF)
        h = PL_DEF(h, caps.currentExtent.height);

    // Otherwise, re-use the existing size if available
    w = PL_DEF(w, info->imageExtent.width);
    h = PL_DEF(h, info->imageExtent.height);
401

402 403
    if (!w || !h) {
        PL_ERR(vk, "Failed resizing swapchain: unknown size?");
404 405 406
        goto error;
    }

407 408 409 410
    // Clamp the extent based on the supported limits
    w = PL_MIN(PL_MAX(w, caps.minImageExtent.width), caps.maxImageExtent.width);
    h = PL_MIN(PL_MAX(h, caps.minImageExtent.height), caps.maxImageExtent.height);

411 412
    // We just request whatever usage we can, and let the pl_vk decide what
    // pl_tex_params that translates to. This makes the images as flexible
413 414 415 416 417 418 419 420 421
    // as possible. However, require at least blitting and rendering.
    VkImageUsageFlags required_flags = 0;
    required_flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
    required_flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    if ((caps.supportedUsageFlags & required_flags) != required_flags) {
        PL_ERR(vk, "The swapchain doesn't support rendering and blitting!");
        goto error;
    }

422
    info->imageUsage = caps.supportedUsageFlags;
423
    info->imageExtent = (VkExtent2D) { w, h };
424 425 426 427 428 429 430 431 432 433
    return true;

error:
    return false;
}

static void destroy_swapchain(struct vk_ctx *vk, struct priv *p)
{
    assert(p->old_swapchain);
    vkDestroySwapchainKHR(vk->dev, p->old_swapchain, VK_ALLOC);
434
    p->old_swapchain = VK_NULL_HANDLE;
435 436
}

437
static bool vk_sw_recreate(const struct pl_swapchain *sw, int w, int h)
438
{
439
    const struct pl_gpu *gpu = sw->gpu;
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
    struct priv *p = sw->priv;
    struct vk_ctx *vk = p->vk;

    VkImage *vkimages = NULL;
    int num_images = 0;

    // It's invalid to trigger another swapchain recreation while there's more
    // than one swapchain already active, so we need to flush any pending
    // asynchronous swapchain release operations that may be ongoing
    while (p->old_swapchain)
        vk_poll_commands(vk, 1000000); // 1 ms

    VkSwapchainCreateInfoKHR sinfo = p->protoInfo;
    sinfo.oldSwapchain = p->swapchain;

455
    if (!update_swapchain_info(p, &sinfo, w, h))
456 457
        goto error;

458 459 460 461
    PL_INFO(sw, "(Re)creating swapchain of size %dx%d",
            sinfo.imageExtent.width,
            sinfo.imageExtent.height);

462 463
    VK(vkCreateSwapchainKHR(vk->dev, &sinfo, VK_ALLOC, &p->swapchain));

464 465 466
    p->cur_width = sinfo.imageExtent.width;
    p->cur_height = sinfo.imageExtent.height;

467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
    // Freeing the old swapchain while it's still in use is an error, so do it
    // asynchronously once the device is idle
    if (sinfo.oldSwapchain) {
        p->old_swapchain = sinfo.oldSwapchain;
        vk_dev_callback(vk, (vk_cb) destroy_swapchain, vk, p);
    }

    // Get the new swapchain images
    VK(vkGetSwapchainImagesKHR(vk->dev, p->swapchain, &num_images, NULL));
    vkimages = talloc_array(NULL, VkImage, num_images);
    VK(vkGetSwapchainImagesKHR(vk->dev, p->swapchain, &num_images, vkimages));

    // If needed, allocate some more semaphores
    while (num_images > p->num_sems) {
        VkSemaphore sem_in, sem_out;
        static const VkSemaphoreCreateInfo seminfo = {
            .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
        };
        VK(vkCreateSemaphore(vk->dev, &seminfo, VK_ALLOC, &sem_in));
        VK(vkCreateSemaphore(vk->dev, &seminfo, VK_ALLOC, &sem_out));

        int idx = p->num_sems++;
        TARRAY_GROW(p, p->sems_in, idx);
        TARRAY_GROW(p, p->sems_out, idx);
        p->sems_in[idx] = sem_in;
        p->sems_out[idx] = sem_out;
    }

495
    // Recreate the pl_tex wrappers
496
    for (int i = 0; i < p->num_images; i++)
497
        pl_tex_destroy(gpu, &p->images[i]);
498 499 500 501

    p->num_images = num_images;
    TARRAY_GROW(p, p->images, num_images);
    for (int i = 0; i < num_images; i++) {
502 503 504
        const VkExtent2D *ext = &sinfo.imageExtent;
        p->images[i] = pl_vulkan_wrap(gpu, vkimages[i], ext->width, ext->height,
                                      0, sinfo.imageFormat, sinfo.imageUsage);
505 506 507 508 509 510 511 512 513 514 515
        if (!p->images[i])
            goto error;
    }

    pl_assert(num_images > 0);
    int bits = 0;

    // The channel with the most bits is probably the most authoritative about
    // the actual color information (consider e.g. a2bgr10). Slight downside
    // in that it results in rounding r/b for e.g. rgb565, but we don't pick
    // surfaces with fewer than 8 bits anyway, so let's not care for now.
516
    const struct pl_fmt *fmt = p->images[0]->params.format;
517 518 519 520 521 522 523 524 525 526 527 528 529
    for (int i = 0; i < fmt->num_components; i++)
        bits = PL_MAX(bits, fmt->component_depth[i]);

    p->color_repr.bits.sample_depth = bits;
    p->color_repr.bits.color_depth = bits;

    talloc_free(vkimages);
    return true;

error:
    PL_ERR(vk, "Failed (re)creating swapchain!");
    talloc_free(vkimages);
    vkDestroySwapchainKHR(vk->dev, p->swapchain, VK_ALLOC);
530
    p->swapchain = VK_NULL_HANDLE;
531
    p->cur_width = p->cur_height = 0;
532 533 534
    return false;
}

535 536
static bool vk_sw_start_frame(const struct pl_swapchain *sw,
                              struct pl_swapchain_frame *out_frame)
537 538 539
{
    struct priv *p = sw->priv;
    struct vk_ctx *vk = p->vk;
540
    if (!p->swapchain && !vk_sw_recreate(sw, 0, 0))
541 542 543 544 545 546 547 548
        return false;

    VkSemaphore sem_in = p->sems_in[p->idx_sems];
    PL_TRACE(vk, "vkAcquireNextImageKHR signals %p", (void *) sem_in);

    for (int attempts = 0; attempts < 2; attempts++) {
        uint32_t imgidx = 0;
        VkResult res = vkAcquireNextImageKHR(vk->dev, p->swapchain, UINT64_MAX,
549
                                             sem_in, VK_NULL_HANDLE, &imgidx);
550 551 552 553

        switch (res) {
        case VK_SUCCESS:
            p->last_imgidx = imgidx;
554 555
            pl_vulkan_release(sw->gpu, p->images[imgidx],
                              VK_IMAGE_LAYOUT_UNDEFINED, 0, sem_in);
556
            *out_frame = (struct pl_swapchain_frame) {
557 558 559 560 561 562 563
                .fbo = p->images[imgidx],
                .flipped = false,
                .color_repr = p->color_repr,
                .color_space = p->color_space,
            };
            return true;

564
        case VK_SUBOPTIMAL_KHR:
565 566
        case VK_ERROR_OUT_OF_DATE_KHR: {
            // In these cases try recreating the swapchain
567
            if (!vk_sw_recreate(sw, 0, 0))
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
                return false;
            continue;
        }

        default:
            PL_ERR(vk, "Failed acquiring swapchain image: %s", vk_res_str(res));
            return false;
        }
    }

    // If we've exhausted the number of attempts to recreate the swapchain,
    // just give up silently and let the user retry some time later.
    return false;
}

static void present_cb(struct priv *p, void *arg)
{
    p->frames_in_flight--;
}

588
static bool vk_sw_submit_frame(const struct pl_swapchain *sw)
589
{
590
    const struct pl_gpu *gpu = sw->gpu;
591 592 593 594 595 596 597
    struct priv *p = sw->priv;
    struct vk_ctx *vk = p->vk;
    if (!p->swapchain)
        return false;

    VkSemaphore sem_out = p->sems_out[p->idx_sems++];
    p->idx_sems %= p->num_sems;
598

599
    pl_vulkan_hold(gpu, p->images[p->last_imgidx],
600 601 602 603 604 605
                   VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                   VK_ACCESS_MEMORY_READ_BIT, sem_out);

    struct vk_cmd *cmd = pl_vk_steal_cmd(gpu);
    if (!cmd)
        return false;
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626

    p->frames_in_flight++;
    vk_cmd_callback(cmd, (vk_cb) present_cb, p, NULL);

    vk_cmd_queue(vk, cmd);
    if (!vk_flush_commands(vk))
        return false;

    // Older nvidia drivers can spontaneously combust when submitting to the
    // same queue as we're rendering from, in a multi-queue scenario. Safest
    // option is to flush the commands first and then submit to the next queue.
    // We can drop this hack in the future, I suppose.
    struct vk_cmdpool *pool = vk->pool_graphics;
    VkQueue queue = pool->queues[pool->idx_queues];

    VkPresentInfoKHR pinfo = {
        .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
        .waitSemaphoreCount = 1,
        .pWaitSemaphores = &sem_out,
        .swapchainCount = 1,
        .pSwapchains = &p->swapchain,
627
        .pImageIndices = &p->last_imgidx,
628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
    };

    PL_TRACE(vk, "vkQueuePresentKHR waits on %p", (void *) sem_out);
    VkResult res = vkQueuePresentKHR(queue, &pinfo);
    switch (res) {
    case VK_SUCCESS:
    case VK_SUBOPTIMAL_KHR:
        return true;

    case VK_ERROR_OUT_OF_DATE_KHR:
        // We can silently ignore this error, since the next start_frame will
        // recreate the swapchain automatically.
        return true;

    default:
        PL_ERR(vk, "Failed presenting to queue %p: %s", (void *) queue,
               vk_res_str(res));
        return false;
    }
}

649
static void vk_sw_swap_buffers(const struct pl_swapchain *sw)
650 651 652 653 654 655 656
{
    struct priv *p = sw->priv;

    while (p->frames_in_flight >= p->swapchain_depth)
        vk_poll_commands(p->vk, 1000000); // 1 ms
}

657 658 659 660 661 662 663 664 665 666 667 668 669
static bool vk_sw_resize(const struct pl_swapchain *sw, int *width, int *height)
{
    struct priv *p = sw->priv;
    bool ok = true;

    if ((*width && *width != p->cur_width) || (*height && *height != p->cur_height))
        ok = vk_sw_recreate(sw, *width, *height);

    *width = p->cur_width;
    *height = p->cur_height;
    return ok;
}

670
static struct pl_sw_fns vulkan_swapchain = {
671 672
    .destroy      = vk_sw_destroy,
    .latency      = vk_sw_latency,
673
    .resize       = vk_sw_resize,
674 675 676 677
    .start_frame  = vk_sw_start_frame,
    .submit_frame = vk_sw_submit_frame,
    .swap_buffers = vk_sw_swap_buffers,
};