LCOV - code coverage report
Current view: top level - src/vulkan - swapchain.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 263 409 64.3 %
Date: 2025-03-29 09:04:10 Functions: 13 16 81.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 118 246 48.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * This file is part of libplacebo.
       3                 :            :  *
       4                 :            :  * libplacebo is free software; you can redistribute it and/or
       5                 :            :  * modify it under the terms of the GNU Lesser General Public
       6                 :            :  * License as published by the Free Software Foundation; either
       7                 :            :  * version 2.1 of the License, or (at your option) any later version.
       8                 :            :  *
       9                 :            :  * libplacebo is distributed in the hope that it will be useful,
      10                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12                 :            :  * GNU Lesser General Public License for more details.
      13                 :            :  *
      14                 :            :  * You should have received a copy of the GNU Lesser General Public
      15                 :            :  * License along with libplacebo.  If not, see <http://www.gnu.org/licenses/>.
      16                 :            :  */
      17                 :            : 
      18                 :            : #include "common.h"
      19                 :            : #include "command.h"
      20                 :            : #include "formats.h"
      21                 :            : #include "utils.h"
      22                 :            : #include "gpu.h"
      23                 :            : #include "swapchain.h"
      24                 :            : #include "pl_thread.h"
      25                 :            : 
      26                 :            : struct sem_pair {
      27                 :            :     VkSemaphore in;
      28                 :            :     VkSemaphore out;
      29                 :            : };
      30                 :            : 
      31                 :            : struct priv {
      32                 :            :     struct pl_sw_fns impl;
      33                 :            : 
      34                 :            :     pl_mutex lock;
      35                 :            :     struct vk_ctx *vk;
      36                 :            :     VkSurfaceKHR surf;
      37                 :            :     PL_ARRAY(VkSurfaceFormatKHR) formats;
      38                 :            : 
      39                 :            :     // current swapchain and metadata:
      40                 :            :     struct pl_vulkan_swapchain_params params;
      41                 :            :     VkSwapchainCreateInfoKHR protoInfo; // partially filled-in prototype
      42                 :            :     VkSwapchainKHR swapchain;
      43                 :            :     int cur_width, cur_height;
      44                 :            :     int swapchain_depth;
      45                 :            :     pl_rc_t frames_in_flight;       // number of frames currently queued
      46                 :            :     bool suboptimal;                // true once VK_SUBOPTIMAL_KHR is returned
      47                 :            :     bool needs_recreate;            // swapchain needs to be recreated
      48                 :            :     struct pl_color_repr color_repr;
      49                 :            :     struct pl_color_space color_space;
      50                 :            :     struct pl_hdr_metadata hdr_metadata;
      51                 :            : 
      52                 :            :     // state of the images:
      53                 :            :     PL_ARRAY(pl_tex) images;        // pl_tex wrappers for the VkImages
      54                 :            :     PL_ARRAY(struct sem_pair) sems; // pool of semaphores used to synchronize images
      55                 :            :     int idx_sems;                   // index of next free semaphore pair
      56                 :            :     int last_imgidx;                // the image index last acquired (for submit)
      57                 :            : };
      58                 :            : 
      59                 :            : static const struct pl_sw_fns vulkan_swapchain;
      60                 :            : 
      61                 :          4 : static bool map_color_space(VkColorSpaceKHR space, struct pl_color_space *out)
      62                 :            : {
      63   [ +  -  -  -  :          4 :     switch (space) {
          -  -  -  -  -  
             -  -  -  - ]
      64                 :            :     // Note: This is technically against the spec, but more often than not
      65                 :            :     // it's the correct result since `SRGB_NONLINEAR` is just a catch-all
      66                 :            :     // for any sort of typical SDR curve, which is better approximated by
      67                 :            :     // `pl_color_space_monitor`.
      68                 :          4 :     case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
      69                 :          4 :         *out = pl_color_space_monitor;
      70                 :          4 :         return true;
      71                 :            : 
      72                 :          0 :     case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
      73                 :          0 :         *out = pl_color_space_monitor;
      74                 :          0 :         return true;
      75                 :          0 :     case VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT:
      76                 :          0 :         *out = (struct pl_color_space) {
      77                 :            :             .primaries = PL_COLOR_PRIM_DISPLAY_P3,
      78                 :            :             .transfer  = PL_COLOR_TRC_BT_1886,
      79                 :            :         };
      80                 :          0 :         return true;
      81                 :          0 :     case VK_COLOR_SPACE_DCI_P3_LINEAR_EXT:
      82                 :          0 :         *out = (struct pl_color_space) {
      83                 :            :             .primaries = PL_COLOR_PRIM_DCI_P3,
      84                 :            :             .transfer  = PL_COLOR_TRC_LINEAR,
      85                 :            :         };
      86                 :          0 :         return true;
      87                 :          0 :     case VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT:
      88                 :          0 :         *out = (struct pl_color_space) {
      89                 :            :             .primaries = PL_COLOR_PRIM_DCI_P3,
      90                 :            :             .transfer  = PL_COLOR_TRC_BT_1886,
      91                 :            :         };
      92                 :          0 :         return true;
      93                 :            :     case VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT:
      94                 :            :     case VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT:
      95                 :            :         // TODO
      96                 :            :         return false;
      97                 :          0 :     case VK_COLOR_SPACE_BT709_LINEAR_EXT:
      98                 :          0 :         *out = (struct pl_color_space) {
      99                 :            :             .primaries = PL_COLOR_PRIM_BT_709,
     100                 :            :             .transfer  = PL_COLOR_TRC_LINEAR,
     101                 :            :         };
     102                 :          0 :         return true;
     103                 :          0 :     case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
     104                 :          0 :         *out = (struct pl_color_space) {
     105                 :            :             .primaries = PL_COLOR_PRIM_BT_2020,
     106                 :            :             .transfer  = PL_COLOR_TRC_LINEAR,
     107                 :            :         };
     108                 :          0 :         return true;
     109                 :          0 :     case VK_COLOR_SPACE_HDR10_ST2084_EXT:
     110                 :          0 :         *out = (struct pl_color_space) {
     111                 :            :             .primaries = PL_COLOR_PRIM_BT_2020,
     112                 :            :             .transfer  = PL_COLOR_TRC_PQ,
     113                 :            :         };
     114                 :          0 :         return true;
     115                 :            :     case VK_COLOR_SPACE_DOLBYVISION_EXT:
     116                 :            :         // Unlikely to ever be implemented
     117                 :            :         return false;
     118                 :          0 :     case VK_COLOR_SPACE_HDR10_HLG_EXT:
     119                 :          0 :         *out = (struct pl_color_space) {
     120                 :            :             .primaries = PL_COLOR_PRIM_BT_2020,
     121                 :            :             .transfer  = PL_COLOR_TRC_HLG,
     122                 :            :         };
     123                 :          0 :         return true;
     124                 :          0 :     case VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT:
     125                 :          0 :         *out = (struct pl_color_space) {
     126                 :            :             .primaries = PL_COLOR_PRIM_ADOBE,
     127                 :            :             .transfer  = PL_COLOR_TRC_LINEAR,
     128                 :            :         };
     129                 :          0 :         return true;
     130                 :          0 :     case VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT:
     131                 :          0 :         *out = (struct pl_color_space) {
     132                 :            :             .primaries = PL_COLOR_PRIM_ADOBE,
     133                 :            :             .transfer  = PL_COLOR_TRC_GAMMA22,
     134                 :            :         };
     135                 :          0 :         return true;
     136                 :          0 :     case VK_COLOR_SPACE_PASS_THROUGH_EXT:
     137                 :          0 :         *out = pl_color_space_unknown;
     138                 :          0 :         return true;
     139                 :            : 
     140                 :            : #ifdef VK_AMD_display_native_hdr
     141                 :            :     case VK_COLOR_SPACE_DISPLAY_NATIVE_AMD:
     142                 :            :         // TODO
     143                 :            :         return false;
     144                 :            : #endif
     145                 :            : 
     146                 :            :     default: return false;
     147                 :            :     }
     148                 :            : }
     149                 :            : 
     150                 :          1 : static bool pick_surf_format(pl_swapchain sw, const struct pl_color_space *hint)
     151                 :            : {
     152                 :          1 :     struct priv *p = PL_PRIV(sw);
     153                 :          1 :     struct vk_ctx *vk = p->vk;
     154                 :          1 :     pl_gpu gpu = sw->gpu;
     155                 :            : 
     156                 :            :     int best_score = 0, best_id;
     157                 :          1 :     bool wide_gamut = pl_color_primaries_is_wide_gamut(hint->primaries);
     158                 :          1 :     bool prefer_hdr = pl_color_transfer_is_hdr(hint->transfer);
     159                 :            : 
     160         [ +  + ]:          3 :     for (int i = 0; i < p->formats.num; i++) {
     161                 :            :         // Color space / format whitelist
     162                 :            :         struct pl_color_space space;
     163         [ -  + ]:          2 :         if (!map_color_space(p->formats.elem[i].colorSpace, &space))
     164                 :          0 :             continue;
     165                 :            : 
     166         [ +  - ]:          2 :         bool disable10 = !pl_color_transfer_is_hdr(space.transfer) &&
     167         [ +  - ]:          2 :                          p->params.disable_10bit_sdr;
     168                 :            : 
     169   [ -  +  -  -  :          2 :         switch (p->formats.elem[i].format) {
                      - ]
     170                 :            :         // Only accept floating point formats for linear curves
     171                 :          0 :         case VK_FORMAT_R16G16B16_SFLOAT:
     172                 :            :         case VK_FORMAT_R16G16B16A16_SFLOAT:
     173                 :            :         case VK_FORMAT_R32G32B32_SFLOAT:
     174                 :            :         case VK_FORMAT_R32G32B32A32_SFLOAT:
     175                 :            :         case VK_FORMAT_R64G64B64_SFLOAT:
     176                 :            :         case VK_FORMAT_R64G64B64A64_SFLOAT:
     177         [ #  # ]:          0 :             if (space.transfer == PL_COLOR_TRC_LINEAR)
     178                 :            :                 break; // accept
     179                 :          0 :             continue;
     180                 :            : 
     181                 :            :         // Only accept 8 bit for non-HDR curves
     182                 :          2 :         case VK_FORMAT_R8G8B8_UNORM:
     183                 :            :         case VK_FORMAT_B8G8R8_UNORM:
     184                 :            :         case VK_FORMAT_R8G8B8A8_UNORM:
     185                 :            :         case VK_FORMAT_B8G8R8A8_UNORM:
     186                 :            :         case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
     187         [ +  - ]:          2 :             if (!pl_color_transfer_is_hdr(space.transfer))
     188                 :            :                 break; // accept
     189                 :          0 :             continue;
     190                 :            : 
     191                 :            :         // Only accept 10 bit formats for non-linear curves
     192                 :          0 :         case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
     193                 :            :         case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
     194   [ #  #  #  # ]:          0 :             if (space.transfer != PL_COLOR_TRC_LINEAR && !disable10)
     195                 :            :                 break; // accept
     196                 :          0 :             continue;
     197                 :            : 
     198                 :            :         // Accept 16-bit formats for everything
     199                 :          0 :         case VK_FORMAT_R16G16B16_UNORM:
     200                 :            :         case VK_FORMAT_R16G16B16A16_UNORM:
     201         [ #  # ]:          0 :             if (!disable10)
     202                 :            :                 break; // accept
     203                 :          0 :             continue;
     204                 :            : 
     205                 :          0 :         default: continue;
     206                 :            :         }
     207                 :            : 
     208                 :            :         // Make sure we can wrap this format to a meaningful, valid pl_fmt
     209         [ +  + ]:        108 :         for (int n = 0; n < gpu->num_formats; n++) {
     210                 :        107 :             pl_fmt plfmt = gpu->formats[n];
     211                 :        107 :             const struct vk_format **pvkfmt = PL_PRIV(plfmt);
     212         [ +  + ]:        107 :             if ((*pvkfmt)->tfmt != p->formats.elem[i].format)
     213                 :        105 :                 continue;
     214                 :            : 
     215                 :            :             enum pl_fmt_caps render_caps = 0;
     216                 :            :             render_caps |= PL_FMT_CAP_RENDERABLE;
     217                 :            :             render_caps |= PL_FMT_CAP_BLITTABLE;
     218         [ -  + ]:          2 :             if ((plfmt->caps & render_caps) != render_caps)
     219                 :          0 :                 continue;
     220                 :            : 
     221                 :            :             // format valid, use it if it has a higher score
     222                 :            :             int score = 0;
     223         [ +  + ]:          8 :             for (int c = 0; c < 3; c++)
     224                 :          6 :                 score += plfmt->component_depth[c];
     225         [ +  - ]:          2 :             if (pl_color_primaries_is_wide_gamut(space.primaries) == wide_gamut)
     226                 :          2 :                 score += 1000;
     227         [ -  + ]:          2 :             if (space.primaries == hint->primaries)
     228                 :          0 :                 score += 2000;
     229         [ +  - ]:          2 :             if (pl_color_transfer_is_hdr(space.transfer) == prefer_hdr)
     230                 :          2 :                 score += 10000;
     231         [ +  - ]:          2 :             if (space.transfer == hint->transfer)
     232                 :          2 :                 score += 20000;
     233                 :            : 
     234   [ +  -  -  -  :          2 :             switch (plfmt->type) {
                      - ]
     235                 :            :             case PL_FMT_UNKNOWN: break;
     236                 :            :             case PL_FMT_UINT: break;
     237                 :            :             case PL_FMT_SINT: break;
     238                 :          2 :             case PL_FMT_UNORM: score += 500; break;
     239                 :          0 :             case PL_FMT_SNORM: score += 400; break;
     240                 :          0 :             case PL_FMT_FLOAT: score += 300; break;
     241                 :          0 :             case PL_FMT_TYPE_COUNT: pl_unreachable();
     242                 :            :             };
     243                 :            : 
     244         [ +  + ]:          2 :             if (score > best_score) {
     245                 :            :                 best_score = score;
     246                 :            :                 best_id = i;
     247                 :            :                 break;
     248                 :            :             }
     249                 :            :         }
     250                 :            :     }
     251                 :            : 
     252         [ -  + ]:          1 :     if (!best_score) {
     253                 :          0 :         PL_ERR(vk, "Failed picking any valid, renderable surface format!");
     254                 :          0 :         return false;
     255                 :            :     }
     256                 :            : 
     257                 :          1 :     VkSurfaceFormatKHR new_sfmt = p->formats.elem[best_id];
     258         [ -  + ]:          1 :     if (p->protoInfo.imageFormat != new_sfmt.format ||
     259         [ #  # ]:          0 :         p->protoInfo.imageColorSpace != new_sfmt.colorSpace)
     260                 :            :     {
     261                 :          1 :         PL_INFO(vk, "Picked surface configuration %d: %s + %s", best_id,
     262                 :            :                 vk_fmt_name(new_sfmt.format),
     263                 :            :                 vk_csp_name(new_sfmt.colorSpace));
     264                 :            : 
     265                 :          1 :         p->protoInfo.imageFormat = new_sfmt.format;
     266                 :          1 :         p->protoInfo.imageColorSpace = new_sfmt.colorSpace;
     267                 :          1 :         p->needs_recreate = true;
     268                 :            :     }
     269                 :            : 
     270                 :            :     return true;
     271                 :            : }
     272                 :            : 
     273                 :          2 : static void set_hdr_metadata(struct priv *p, const struct pl_hdr_metadata *metadata)
     274                 :            : {
     275                 :          2 :     struct vk_ctx *vk = p->vk;
     276         [ -  + ]:          2 :     if (!vk->SetHdrMetadataEXT)
     277                 :          2 :         return;
     278                 :            : 
     279                 :            :     // Whitelist only values that we support signalling metadata for
     280                 :          0 :     struct pl_hdr_metadata fix = {
     281                 :            :         .prim     = metadata->prim,
     282                 :          0 :         .min_luma = metadata->min_luma,
     283                 :          0 :         .max_luma = metadata->max_luma,
     284                 :          0 :         .max_cll  = metadata->max_cll,
     285                 :          0 :         .max_fall = metadata->max_fall,
     286                 :            :     };
     287                 :            : 
     288                 :            :     // Ignore no-op changes
     289         [ #  # ]:          0 :     if (pl_hdr_metadata_equal(&fix, &p->hdr_metadata))
     290                 :            :         return;
     291                 :            : 
     292                 :            :     // Remember the metadata so we can re-apply it after swapchain recreation
     293                 :          0 :     p->hdr_metadata = fix;
     294                 :            : 
     295                 :            :     // Ignore HDR metadata requests for SDR swapchains
     296         [ #  # ]:          0 :     if (!pl_color_transfer_is_hdr(p->color_space.transfer))
     297                 :            :         return;
     298                 :            : 
     299         [ #  # ]:          0 :     if (!p->swapchain)
     300                 :            :         return;
     301                 :            : 
     302                 :          0 :     vk->SetHdrMetadataEXT(vk->dev, 1, &p->swapchain, &(VkHdrMetadataEXT) {
     303                 :            :         .sType = VK_STRUCTURE_TYPE_HDR_METADATA_EXT,
     304                 :          0 :         .displayPrimaryRed   = { fix.prim.red.x,   fix.prim.red.y },
     305                 :          0 :         .displayPrimaryGreen = { fix.prim.green.x, fix.prim.green.y },
     306                 :          0 :         .displayPrimaryBlue  = { fix.prim.blue.x,  fix.prim.blue.y },
     307                 :          0 :         .whitePoint = { fix.prim.white.x, fix.prim.white.y },
     308                 :          0 :         .maxLuminance = fix.max_luma,
     309                 :          0 :         .minLuminance = fix.min_luma,
     310                 :          0 :         .maxContentLightLevel = fix.max_cll,
     311                 :          0 :         .maxFrameAverageLightLevel = fix.max_fall,
     312                 :            :     });
     313                 :            : 
     314                 :            :     // Keep track of applied HDR colorimetry metadata
     315                 :          0 :     p->color_space.hdr = p->hdr_metadata;
     316                 :            : }
     317                 :            : 
     318                 :          1 : pl_swapchain pl_vulkan_create_swapchain(pl_vulkan plvk,
     319                 :            :                               const struct pl_vulkan_swapchain_params *params)
     320                 :            : {
     321                 :          1 :     struct vk_ctx *vk = PL_PRIV(plvk);
     322                 :          1 :     pl_gpu gpu = plvk->gpu;
     323                 :            : 
     324         [ -  + ]:          1 :     if (!vk->CreateSwapchainKHR) {
     325                 :          0 :         PL_ERR(gpu, VK_KHR_SWAPCHAIN_EXTENSION_NAME " not enabled!");
     326                 :          0 :         return NULL;
     327                 :            :     }
     328                 :            : 
     329                 :          1 :     struct pl_swapchain_t *sw = pl_zalloc_obj(NULL, sw, struct priv);
     330                 :          1 :     sw->log = vk->log;
     331                 :          1 :     sw->gpu = gpu;
     332                 :            : 
     333                 :          1 :     struct priv *p = PL_PRIV(sw);
     334         [ -  + ]:          1 :     pl_mutex_init(&p->lock);
     335                 :          1 :     p->impl = vulkan_swapchain;
     336                 :          1 :     p->params = *params;
     337                 :          1 :     p->vk = vk;
     338                 :          1 :     p->surf = params->surface;
     339         [ -  + ]:          1 :     p->swapchain_depth = PL_DEF(params->swapchain_depth, 3);
     340         [ -  + ]:          1 :     pl_assert(p->swapchain_depth > 0);
     341                 :          1 :     atomic_init(&p->frames_in_flight, 0);
     342                 :          1 :     p->last_imgidx = -1;
     343                 :          1 :     p->protoInfo = (VkSwapchainCreateInfoKHR) {
     344                 :            :         .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
     345                 :          1 :         .surface = p->surf,
     346                 :            :         .imageArrayLayers = 1, // non-stereoscopic
     347                 :            :         .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
     348                 :          1 :         .minImageCount = p->swapchain_depth + 1, // +1 for the FB
     349                 :          1 :         .presentMode = params->present_mode,
     350                 :            :         .clipped = true,
     351                 :            :     };
     352                 :            : 
     353                 :            :     // These fields will be updated by `vk_sw_recreate`
     354                 :          1 :     p->color_space = pl_color_space_unknown;
     355                 :          1 :     p->color_repr = (struct pl_color_repr) {
     356                 :            :         .sys    = PL_COLOR_SYSTEM_RGB,
     357                 :            :         .levels = PL_COLOR_LEVELS_FULL,
     358                 :            :         .alpha  = PL_ALPHA_UNKNOWN,
     359                 :            :     };
     360                 :            : 
     361                 :            :     // Make sure the swapchain present mode is supported
     362                 :            :     VkPresentModeKHR *modes = NULL;
     363                 :          1 :     uint32_t num_modes = 0;
     364         [ -  + ]:          1 :     VK(vk->GetPhysicalDeviceSurfacePresentModesKHR(vk->physd, p->surf, &num_modes, NULL));
     365                 :          1 :     modes = pl_calloc_ptr(NULL, num_modes, modes);
     366         [ -  + ]:          1 :     VK(vk->GetPhysicalDeviceSurfacePresentModesKHR(vk->physd, p->surf, &num_modes, modes));
     367                 :            : 
     368                 :            :     bool supported = false;
     369         [ +  + ]:          3 :     for (int i = 0; i < num_modes; i++)
     370                 :          2 :         supported |= (modes[i] == p->protoInfo.presentMode);
     371                 :          1 :     pl_free_ptr(&modes);
     372                 :            : 
     373         [ +  - ]:          1 :     if (!supported) {
     374                 :          1 :         PL_WARN(vk, "Requested swap mode unsupported by this device, falling "
     375                 :            :                 "back to VK_PRESENT_MODE_FIFO_KHR");
     376                 :          1 :         p->protoInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
     377                 :            :     }
     378                 :            : 
     379                 :            :     // Enumerate the supported surface color spaces
     380                 :          1 :     uint32_t num_formats = 0;
     381         [ -  + ]:          1 :     VK(vk->GetPhysicalDeviceSurfaceFormatsKHR(vk->physd, p->surf, &num_formats, NULL));
     382         [ +  - ]:          1 :     PL_ARRAY_RESIZE(sw, p->formats, num_formats);
     383         [ -  + ]:          1 :     VK(vk->GetPhysicalDeviceSurfaceFormatsKHR(vk->physd, p->surf, &num_formats, p->formats.elem));
     384                 :          1 :     p->formats.num = num_formats;
     385                 :            : 
     386                 :          1 :     PL_INFO(gpu, "Available surface configurations:");
     387         [ +  + ]:          3 :     for (int i = 0; i < p->formats.num; i++) {
     388                 :          2 :         PL_INFO(gpu, "    %d: %-40s %s", i,
     389                 :            :                 vk_fmt_name(p->formats.elem[i].format),
     390                 :            :                 vk_csp_name(p->formats.elem[i].colorSpace));
     391                 :            :     }
     392                 :            : 
     393                 :            :     // Ensure there exists at least some valid renderable surface format
     394                 :          1 :     struct pl_color_space hint = {0};
     395         [ -  + ]:          1 :     if (!pick_surf_format(sw, &hint))
     396                 :          0 :         goto error;
     397                 :            : 
     398                 :            :     return sw;
     399                 :            : 
     400                 :          0 : error:
     401                 :          0 :     pl_free(modes);
     402                 :          0 :     pl_free(sw);
     403                 :          0 :     return NULL;
     404                 :            : }
     405                 :            : 
     406                 :          1 : static void vk_sw_destroy(pl_swapchain sw)
     407                 :            : {
     408                 :          1 :     pl_gpu gpu = sw->gpu;
     409                 :          1 :     struct priv *p = PL_PRIV(sw);
     410                 :          1 :     struct vk_ctx *vk = p->vk;
     411                 :            : 
     412                 :          1 :     pl_gpu_flush(gpu);
     413                 :          1 :     vk_wait_idle(vk);
     414                 :            : 
     415                 :            :     // Vulkan offers no way to know when a queue presentation command is done,
     416                 :            :     // leading to spec-mandated undefined behavior when destroying resources
     417                 :            :     // tied to the swapchain. Use an extra `vkQueueWaitIdle` on all of the
     418                 :            :     // queues we may have oustanding presentation calls on, to hopefully inform
     419                 :            :     // the driver that we want to wait until the device is truly idle.
     420         [ +  + ]:          2 :     for (int i = 0; i < vk->pool_graphics->num_queues; i++)
     421                 :          1 :         vk->QueueWaitIdle(vk->pool_graphics->queues[i]);
     422                 :            : 
     423         [ +  + ]:          5 :     for (int i = 0; i < p->images.num; i++)
     424                 :          4 :         pl_tex_destroy(gpu, &p->images.elem[i]);
     425         [ +  + ]:          5 :     for (int i = 0; i < p->sems.num; i++) {
     426                 :          4 :         vk->DestroySemaphore(vk->dev, p->sems.elem[i].in, PL_VK_ALLOC);
     427                 :          4 :         vk->DestroySemaphore(vk->dev, p->sems.elem[i].out, PL_VK_ALLOC);
     428                 :            :     }
     429                 :            : 
     430                 :          1 :     vk->DestroySwapchainKHR(vk->dev, p->swapchain, PL_VK_ALLOC);
     431                 :          1 :     pl_mutex_destroy(&p->lock);
     432                 :          1 :     pl_free((void *) sw);
     433                 :          1 : }
     434                 :            : 
     435                 :          0 : static int vk_sw_latency(pl_swapchain sw)
     436                 :            : {
     437                 :          0 :     struct priv *p = PL_PRIV(sw);
     438                 :          0 :     return p->swapchain_depth;
     439                 :            : }
     440                 :            : 
     441                 :          2 : static bool update_swapchain_info(struct priv *p, VkSwapchainCreateInfoKHR *info,
     442                 :            :                                   int w, int h)
     443                 :            : {
     444                 :          2 :     struct vk_ctx *vk = p->vk;
     445                 :            : 
     446                 :            :     // Query the supported capabilities and update this struct as needed
     447                 :          2 :     VkSurfaceCapabilitiesKHR caps = {0};
     448         [ -  + ]:          2 :     VK(vk->GetPhysicalDeviceSurfaceCapabilitiesKHR(vk->physd, p->surf, &caps));
     449                 :            : 
     450                 :            :     // Check for hidden/invisible window
     451   [ +  -  -  + ]:          2 :     if (!caps.currentExtent.width || !caps.currentExtent.height) {
     452                 :          0 :         PL_DEBUG(vk, "maxImageExtent reported as 0x0, hidden window? skipping");
     453                 :          0 :         return false;
     454                 :            :     }
     455                 :            : 
     456                 :            :     // Sorted by preference
     457                 :            :     static const struct { VkCompositeAlphaFlagsKHR vk_mode;
     458                 :            :                           enum pl_alpha_mode pl_mode;
     459                 :            :                         } alphaModes[] = {
     460                 :            :         {VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, PL_ALPHA_INDEPENDENT},
     461                 :            :         {VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,  PL_ALPHA_PREMULTIPLIED},
     462                 :            :         {VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,          PL_ALPHA_UNKNOWN},
     463                 :            :         {VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,         PL_ALPHA_UNKNOWN},
     464                 :            :     };
     465                 :            : 
     466         [ +  - ]:          4 :     for (int i = 0; i < PL_ARRAY_SIZE(alphaModes); i++) {
     467         [ +  + ]:          4 :         if (caps.supportedCompositeAlpha & alphaModes[i].vk_mode) {
     468                 :          2 :             info->compositeAlpha = alphaModes[i].vk_mode;
     469                 :          2 :             p->color_repr.alpha = alphaModes[i].pl_mode;
     470                 :          2 :             PL_DEBUG(vk, "Requested alpha compositing mode: %s",
     471                 :            :                      vk_alpha_mode(info->compositeAlpha));
     472                 :          2 :             break;
     473                 :            :         }
     474                 :            :     }
     475                 :            : 
     476         [ -  + ]:          2 :     if (!info->compositeAlpha) {
     477                 :          0 :         PL_ERR(vk, "Failed picking alpha compositing mode (caps: 0x%x)",
     478                 :            :                caps.supportedCompositeAlpha);
     479                 :          0 :         goto error;
     480                 :            :     }
     481                 :            : 
     482                 :            :     // Note: We could probably also allow picking a surface transform that
     483                 :            :     // flips the framebuffer and set `pl_swapchain_frame.flipped`, but this
     484                 :            :     // doesn't appear to be necessary for any vulkan implementations.
     485                 :            :     static const VkSurfaceTransformFlagsKHR rotModes[] = {
     486                 :            :         VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
     487                 :            :         VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR,
     488                 :            :     };
     489                 :            : 
     490         [ +  - ]:          2 :     for (int i = 0; i < PL_ARRAY_SIZE(rotModes); i++) {
     491         [ +  - ]:          2 :         if (caps.supportedTransforms & rotModes[i]) {
     492                 :          2 :             info->preTransform = rotModes[i];
     493                 :          2 :             PL_DEBUG(vk, "Requested surface transform: %s",
     494                 :            :                      vk_surface_transform(info->preTransform));
     495                 :          2 :             break;
     496                 :            :         }
     497                 :            :     }
     498                 :            : 
     499         [ -  + ]:          2 :     if (!info->preTransform) {
     500                 :          0 :         PL_ERR(vk, "Failed picking surface transform mode (caps: 0x%x)",
     501                 :            :                caps.supportedTransforms);
     502                 :          0 :         goto error;
     503                 :            :     }
     504                 :            : 
     505                 :            :     // Image count as required
     506                 :          2 :     PL_DEBUG(vk, "Requested image count: %d (min %d max %d)",
     507                 :            :              (int) info->minImageCount, (int) caps.minImageCount,
     508                 :            :              (int) caps.maxImageCount);
     509                 :            : 
     510                 :          2 :     info->minImageCount = PL_MAX(info->minImageCount, caps.minImageCount);
     511         [ -  + ]:          2 :     if (caps.maxImageCount)
     512                 :          0 :         info->minImageCount = PL_MIN(info->minImageCount, caps.maxImageCount);
     513                 :            : 
     514                 :          2 :     PL_DEBUG(vk, "Requested image size: %dx%d (min %dx%d < cur %dx%d < max %dx%d)",
     515                 :            :              w, h, caps.minImageExtent.width, caps.minImageExtent.height,
     516                 :            :              caps.currentExtent.width, caps.currentExtent.height,
     517                 :            :              caps.maxImageExtent.width, caps.maxImageExtent.height);
     518                 :            : 
     519                 :            :     // Default the requested size based on the reported extent
     520         [ -  + ]:          2 :     if (caps.currentExtent.width != 0xFFFFFFFF)
     521         [ #  # ]:          0 :         w = PL_DEF(w, caps.currentExtent.width);
     522         [ -  + ]:          2 :     if (caps.currentExtent.height != 0xFFFFFFFF)
     523         [ #  # ]:          0 :         h = PL_DEF(h, caps.currentExtent.height);
     524                 :            : 
     525                 :            :     // Otherwise, re-use the existing size if available
     526         [ -  + ]:          2 :     w = PL_DEF(w, info->imageExtent.width);
     527         [ -  + ]:          2 :     h = PL_DEF(h, info->imageExtent.height);
     528                 :            : 
     529         [ -  + ]:          2 :     if (!w || !h) {
     530                 :          0 :         PL_ERR(vk, "Failed resizing swapchain: unknown size?");
     531                 :          0 :         goto error;
     532                 :            :     }
     533                 :            : 
     534                 :            :     // Clamp the extent based on the supported limits
     535         [ -  + ]:          2 :     w = PL_CLAMP(w, caps.minImageExtent.width,  caps.maxImageExtent.width);
     536         [ -  + ]:          2 :     h = PL_CLAMP(h, caps.minImageExtent.height, caps.maxImageExtent.height);
     537                 :          2 :     info->imageExtent = (VkExtent2D) { w, h };
     538                 :            : 
     539                 :            :     // We just request whatever makes sense, and let the pl_vk decide what
     540                 :            :     // pl_tex_params that translates to. That said, we still need to intersect
     541                 :            :     // the swapchain usage flags with the format usage flags
     542                 :            :     VkImageUsageFlags req_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
     543                 :            :                                   VK_IMAGE_USAGE_TRANSFER_DST_BIT;
     544                 :            :     VkImageUsageFlags opt_flags = VK_IMAGE_USAGE_STORAGE_BIT;
     545                 :            : 
     546                 :          2 :     info->imageUsage = caps.supportedUsageFlags & (req_flags | opt_flags);
     547                 :          2 :     VkFormatProperties fmtprop = {0};
     548                 :          2 :     vk->GetPhysicalDeviceFormatProperties(vk->physd, info->imageFormat, &fmtprop);
     549                 :            : 
     550                 :            : #define CHECK(usage, feature) \
     551                 :            :     if (!((fmtprop.optimalTilingFeatures & VK_FORMAT_FEATURE_##feature##_BIT))) \
     552                 :            :         info->imageUsage &= ~VK_IMAGE_USAGE_##usage##_BIT
     553                 :            : 
     554         [ -  + ]:          2 :     CHECK(COLOR_ATTACHMENT, COLOR_ATTACHMENT);
     555         [ -  + ]:          2 :     CHECK(TRANSFER_DST, TRANSFER_DST);
     556         [ -  + ]:          2 :     CHECK(STORAGE, STORAGE_IMAGE);
     557                 :            : 
     558         [ -  + ]:          2 :     if ((info->imageUsage & req_flags) != req_flags) {
     559                 :          0 :         PL_ERR(vk, "The swapchain doesn't support rendering and blitting!");
     560                 :          0 :         goto error;
     561                 :            :     }
     562                 :            : 
     563                 :            :     return true;
     564                 :            : 
     565                 :            : error:
     566                 :            :     return false;
     567                 :            : }
     568                 :            : 
     569                 :            : static void destroy_swapchain(struct vk_ctx *vk, void *swapchain)
     570                 :            : {
     571                 :          2 :     vk->DestroySwapchainKHR(vk->dev, vk_unwrap_handle(swapchain), PL_VK_ALLOC);
     572                 :            : }
     573                 :            : 
     574                 :          2 : VK_CB_FUNC_DEF(destroy_swapchain);
     575                 :            : 
     576                 :          2 : static bool vk_sw_recreate(pl_swapchain sw, int w, int h)
     577                 :            : {
     578                 :          2 :     pl_gpu gpu = sw->gpu;
     579                 :          2 :     struct priv *p = PL_PRIV(sw);
     580                 :          2 :     struct vk_ctx *vk = p->vk;
     581                 :            : 
     582                 :            :     VkImage *vkimages = NULL;
     583                 :          2 :     uint32_t num_images = 0;
     584                 :            : 
     585         [ -  + ]:          2 :     if (!update_swapchain_info(p, &p->protoInfo, w, h))
     586                 :            :         return false;
     587                 :            : 
     588                 :          2 :     VkSwapchainCreateInfoKHR sinfo = p->protoInfo;
     589                 :            : #ifdef VK_EXT_full_screen_exclusive
     590                 :            :     // Explicitly disallow full screen exclusive mode if possible
     591                 :            :     static const VkSurfaceFullScreenExclusiveInfoEXT fsinfo = {
     592                 :            :         .sType = VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT,
     593                 :            :         .fullScreenExclusive = VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT,
     594                 :            :     };
     595                 :            :     if (vk->AcquireFullScreenExclusiveModeEXT)
     596                 :            :         vk_link_struct(&sinfo, &fsinfo);
     597                 :            : #endif
     598                 :            : 
     599                 :          2 :     p->suboptimal = false;
     600                 :          2 :     p->needs_recreate = false;
     601                 :          2 :     p->cur_width = sinfo.imageExtent.width;
     602                 :          2 :     p->cur_height = sinfo.imageExtent.height;
     603                 :            : 
     604                 :          2 :     PL_DEBUG(sw, "(Re)creating swapchain of size %dx%d",
     605                 :            :              sinfo.imageExtent.width,
     606                 :            :              sinfo.imageExtent.height);
     607                 :            : 
     608                 :            : #ifdef PL_HAVE_UNIX
     609         [ -  + ]:          2 :     if (vk->props.vendorID == VK_VENDOR_ID_NVIDIA) {
     610                 :          0 :         vk->DeviceWaitIdle(vk->dev);
     611                 :          0 :         vk_wait_idle(vk);
     612                 :            :     }
     613                 :            : #endif
     614                 :            : 
     615                 :            :     // Calling `vkCreateSwapchainKHR` puts sinfo.oldSwapchain into a retired
     616                 :            :     // state whether the call succeeds or not, so we always need to garbage
     617                 :            :     // collect it afterwards - asynchronously as it may still be in use
     618                 :          2 :     sinfo.oldSwapchain = p->swapchain;
     619                 :          2 :     p->swapchain = VK_NULL_HANDLE;
     620                 :          2 :     VkResult res = vk->CreateSwapchainKHR(vk->dev, &sinfo, PL_VK_ALLOC, &p->swapchain);
     621                 :          2 :     vk_dev_callback(vk, VK_CB_FUNC(destroy_swapchain), vk, vk_wrap_handle(sinfo.oldSwapchain));
     622         [ -  + ]:          2 :     PL_VK_ASSERT(res, "vk->CreateSwapchainKHR(...)");
     623                 :            : 
     624                 :            :     // Get the new swapchain images
     625         [ -  + ]:          2 :     VK(vk->GetSwapchainImagesKHR(vk->dev, p->swapchain, &num_images, NULL));
     626                 :          2 :     vkimages = pl_calloc_ptr(NULL, num_images, vkimages);
     627         [ -  + ]:          2 :     VK(vk->GetSwapchainImagesKHR(vk->dev, p->swapchain, &num_images, vkimages));
     628                 :            : 
     629         [ +  + ]:         10 :     for (int i = 0; i < num_images; i++)
     630         [ +  - ]:          8 :         PL_VK_NAME(IMAGE, vkimages[i], "swapchain");
     631                 :            : 
     632                 :            :     // If needed, allocate some more semaphores
     633         [ +  + ]:          6 :     while (num_images > p->sems.num) {
     634                 :          4 :         VkSemaphore sem_in = VK_NULL_HANDLE, sem_out = VK_NULL_HANDLE;
     635                 :            :         static const VkSemaphoreCreateInfo seminfo = {
     636                 :            :             .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
     637                 :            :         };
     638         [ -  + ]:          4 :         VK(vk->CreateSemaphore(vk->dev, &seminfo, PL_VK_ALLOC, &sem_in));
     639         [ -  + ]:          4 :         VK(vk->CreateSemaphore(vk->dev, &seminfo, PL_VK_ALLOC, &sem_out));
     640         [ +  - ]:          4 :         PL_VK_NAME(SEMAPHORE, sem_in, "swapchain in");
     641         [ +  - ]:          4 :         PL_VK_NAME(SEMAPHORE, sem_out, "swapchain out");
     642                 :            : 
     643   [ +  +  -  +  :          4 :         PL_ARRAY_APPEND(sw, p->sems, (struct sem_pair) {
                   -  + ]
     644                 :            :             .in = sem_in,
     645                 :            :             .out = sem_out,
     646                 :            :         });
     647                 :            :     }
     648                 :            : 
     649                 :            :     // Recreate the pl_tex wrappers
     650         [ +  + ]:          6 :     for (int i = 0; i < p->images.num; i++)
     651                 :          4 :         pl_tex_destroy(gpu, &p->images.elem[i]);
     652                 :          2 :     p->images.num = 0;
     653                 :            : 
     654         [ +  + ]:         10 :     for (int i = 0; i < num_images; i++) {
     655                 :            :         const VkExtent2D *ext = &sinfo.imageExtent;
     656                 :          8 :         pl_tex tex = pl_vulkan_wrap(gpu, pl_vulkan_wrap_params(
     657                 :            :             .image = vkimages[i],
     658                 :            :             .width = ext->width,
     659                 :            :             .height = ext->height,
     660                 :            :             .format = sinfo.imageFormat,
     661                 :            :             .usage = sinfo.imageUsage,
     662                 :            :         ));
     663         [ -  + ]:          8 :         if (!tex)
     664                 :          0 :             goto error;
     665   [ +  +  -  +  :          8 :         PL_ARRAY_APPEND(sw, p->images, tex);
                   -  + ]
     666                 :            :     }
     667                 :            : 
     668         [ -  + ]:          2 :     pl_assert(num_images > 0);
     669                 :            :     int bits = 0;
     670                 :            : 
     671                 :            :     // The channel with the most bits is probably the most authoritative about
     672                 :            :     // the actual color information (consider e.g. a2bgr10). Slight downside
     673                 :            :     // in that it results in rounding r/b for e.g. rgb565, but we don't pick
     674                 :            :     // surfaces with fewer than 8 bits anyway, so let's not care for now.
     675                 :          2 :     pl_fmt fmt = p->images.elem[0]->params.format;
     676         [ +  + ]:         10 :     for (int i = 0; i < fmt->num_components; i++)
     677                 :          8 :         bits = PL_MAX(bits, fmt->component_depth[i]);
     678                 :            : 
     679                 :          2 :     p->color_repr.bits.sample_depth = bits;
     680                 :          2 :     p->color_repr.bits.color_depth = bits;
     681                 :            : 
     682                 :            :     // Note: `p->color_space.hdr` is (re-)applied by `set_hdr_metadata`
     683                 :          2 :     map_color_space(sinfo.imageColorSpace, &p->color_space);
     684                 :            : 
     685                 :            :     // Forcibly re-apply HDR metadata, bypassing the no-op check
     686                 :          2 :     struct pl_hdr_metadata metadata = p->hdr_metadata;
     687                 :          2 :     p->hdr_metadata = pl_hdr_metadata_empty;
     688                 :          2 :     set_hdr_metadata(p, &metadata);
     689                 :            : 
     690                 :          2 :     pl_free(vkimages);
     691                 :          2 :     return true;
     692                 :            : 
     693                 :          0 : error:
     694                 :          0 :     PL_ERR(vk, "Failed (re)creating swapchain!");
     695                 :          0 :     pl_free(vkimages);
     696                 :          0 :     vk->DestroySwapchainKHR(vk->dev, p->swapchain, PL_VK_ALLOC);
     697                 :          0 :     p->swapchain = VK_NULL_HANDLE;
     698                 :          0 :     p->cur_width = p->cur_height = 0;
     699                 :          0 :     return false;
     700                 :            : }
     701                 :            : 
     702                 :         10 : static bool vk_sw_start_frame(pl_swapchain sw,
     703                 :            :                               struct pl_swapchain_frame *out_frame)
     704                 :            : {
     705                 :         10 :     struct priv *p = PL_PRIV(sw);
     706                 :         10 :     struct vk_ctx *vk = p->vk;
     707                 :         10 :     pl_mutex_lock(&p->lock);
     708                 :            : 
     709   [ +  -  -  + ]:         10 :     bool recreate = !p->swapchain || p->needs_recreate;
     710   [ -  +  -  - ]:         10 :     if (p->suboptimal && !p->params.allow_suboptimal)
     711                 :            :         recreate = true;
     712                 :            : 
     713   [ -  +  -  - ]:         10 :     if (recreate && !vk_sw_recreate(sw, 0, 0)) {
     714                 :          0 :         pl_mutex_unlock(&p->lock);
     715                 :          0 :         return false;
     716                 :            :     }
     717                 :            : 
     718                 :         10 :     VkSemaphore sem_in = p->sems.elem[p->idx_sems].in;
     719                 :         10 :     PL_TRACE(vk, "vkAcquireNextImageKHR signals 0x%"PRIx64, (uint64_t) sem_in);
     720                 :            : 
     721         [ +  - ]:         10 :     for (int attempts = 0; attempts < 2; attempts++) {
     722                 :         10 :         uint32_t imgidx = 0;
     723                 :         10 :         VkResult res = vk->AcquireNextImageKHR(vk->dev, p->swapchain, UINT64_MAX,
     724                 :            :                                                sem_in, VK_NULL_HANDLE, &imgidx);
     725                 :            : 
     726   [ -  +  -  - ]:         10 :         switch (res) {
     727                 :          0 :         case VK_SUBOPTIMAL_KHR:
     728                 :          0 :             p->suboptimal = true;
     729                 :            :             // fall through
     730                 :         10 :         case VK_SUCCESS:
     731                 :         10 :             p->last_imgidx = imgidx;
     732                 :         10 :             pl_vulkan_release_ex(sw->gpu, pl_vulkan_release_params(
     733                 :            :                 .tex        = p->images.elem[imgidx],
     734                 :            :                 .layout     = VK_IMAGE_LAYOUT_UNDEFINED,
     735                 :            :                 .qf         = VK_QUEUE_FAMILY_IGNORED,
     736                 :            :                 .semaphore  = { sem_in },
     737                 :            :             ));
     738                 :         10 :             *out_frame = (struct pl_swapchain_frame) {
     739                 :         10 :                 .fbo = p->images.elem[imgidx],
     740                 :            :                 .flipped = false,
     741                 :            :                 .color_repr = p->color_repr,
     742                 :            :                 .color_space = p->color_space,
     743                 :            :             };
     744                 :            :             // keep lock held
     745                 :         10 :             return true;
     746                 :            : 
     747                 :          0 :         case VK_ERROR_OUT_OF_DATE_KHR: {
     748                 :            :             // In these cases try recreating the swapchain
     749         [ #  # ]:          0 :             if (!vk_sw_recreate(sw, 0, 0)) {
     750                 :          0 :                 pl_mutex_unlock(&p->lock);
     751                 :          0 :                 return false;
     752                 :            :             }
     753                 :          0 :             continue;
     754                 :            :         }
     755                 :            : 
     756                 :          0 :         default:
     757                 :          0 :             PL_ERR(vk, "Failed acquiring swapchain image: %s", vk_res_str(res));
     758                 :          0 :             pl_mutex_unlock(&p->lock);
     759                 :          0 :             return false;
     760                 :            :         }
     761                 :            :     }
     762                 :            : 
     763                 :            :     // If we've exhausted the number of attempts to recreate the swapchain,
     764                 :            :     // just give up silently and let the user retry some time later.
     765                 :          0 :     pl_mutex_unlock(&p->lock);
     766                 :          0 :     return false;
     767                 :            : }
     768                 :            : 
     769                 :            : static void present_cb(struct priv *p, void *arg)
     770                 :            : {
     771                 :         10 :     (void) pl_rc_deref(&p->frames_in_flight);
     772                 :            : }
     773                 :            : 
     774                 :         10 : VK_CB_FUNC_DEF(present_cb);
     775                 :            : 
     776                 :         10 : static bool vk_sw_submit_frame(pl_swapchain sw)
     777                 :            : {
     778                 :         10 :     pl_gpu gpu = sw->gpu;
     779                 :         10 :     struct priv *p = PL_PRIV(sw);
     780                 :         10 :     struct vk_ctx *vk = p->vk;
     781         [ -  + ]:         10 :     pl_assert(p->last_imgidx >= 0);
     782         [ -  + ]:         10 :     pl_assert(p->swapchain);
     783                 :         10 :     uint32_t idx = p->last_imgidx;
     784                 :         10 :     VkSemaphore sem_out = p->sems.elem[p->idx_sems++].out;
     785                 :         10 :     p->idx_sems %= p->sems.num;
     786                 :         10 :     p->last_imgidx = -1;
     787                 :            : 
     788                 :         10 :     bool held = pl_vulkan_hold_ex(gpu, pl_vulkan_hold_params(
     789                 :            :         .tex        = p->images.elem[idx],
     790                 :            :         .layout     = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
     791                 :            :         .qf         = VK_QUEUE_FAMILY_IGNORED,
     792                 :            :         .semaphore  = { sem_out },
     793                 :            :     ));
     794                 :            : 
     795         [ -  + ]:         10 :     if (!held) {
     796                 :          0 :         PL_ERR(gpu, "Failed holding swapchain image for presentation");
     797                 :          0 :         pl_mutex_unlock(&p->lock);
     798                 :          0 :         return false;
     799                 :            :     }
     800                 :            : 
     801                 :         10 :     struct vk_cmd *cmd = pl_vk_steal_cmd(gpu);
     802         [ -  + ]:         10 :     if (!cmd) {
     803                 :          0 :         pl_mutex_unlock(&p->lock);
     804                 :          0 :         return false;
     805                 :            :     }
     806                 :            : 
     807                 :         10 :     pl_rc_ref(&p->frames_in_flight);
     808                 :         10 :     vk_cmd_callback(cmd, VK_CB_FUNC(present_cb), p, NULL);
     809         [ -  + ]:         10 :     if (!vk_cmd_submit(&cmd)) {
     810                 :          0 :         pl_mutex_unlock(&p->lock);
     811                 :          0 :         return false;
     812                 :            :     }
     813                 :            : 
     814                 :         10 :     struct vk_cmdpool *pool = vk->pool_graphics;
     815                 :         10 :     int qidx = pool->idx_queues;
     816                 :         10 :     VkQueue queue = pool->queues[qidx];
     817                 :            : 
     818                 :         10 :     vk_rotate_queues(p->vk);
     819                 :         10 :     vk_malloc_garbage_collect(vk->ma);
     820                 :            : 
     821                 :         10 :     VkPresentInfoKHR pinfo = {
     822                 :            :         .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
     823                 :            :         .waitSemaphoreCount = 1,
     824                 :            :         .pWaitSemaphores = &sem_out,
     825                 :            :         .swapchainCount = 1,
     826                 :         10 :         .pSwapchains = &p->swapchain,
     827                 :            :         .pImageIndices = &idx,
     828                 :            :     };
     829                 :            : 
     830                 :         10 :     PL_TRACE(vk, "vkQueuePresentKHR waits on 0x%"PRIx64, (uint64_t) sem_out);
     831                 :         10 :     vk->lock_queue(vk->queue_ctx, pool->qf, qidx);
     832                 :         10 :     VkResult res = vk->QueuePresentKHR(queue, &pinfo);
     833                 :         10 :     vk->unlock_queue(vk->queue_ctx, pool->qf, qidx);
     834                 :         10 :     pl_mutex_unlock(&p->lock);
     835                 :            : 
     836      [ -  -  + ]:         10 :     switch (res) {
     837                 :          0 :     case VK_SUBOPTIMAL_KHR:
     838                 :          0 :         p->suboptimal = true;
     839                 :            :         // fall through
     840                 :            :     case VK_SUCCESS:
     841                 :            :         return true;
     842                 :            : 
     843                 :            :     case VK_ERROR_OUT_OF_DATE_KHR:
     844                 :            :         // We can silently ignore this error, since the next start_frame will
     845                 :            :         // recreate the swapchain automatically.
     846                 :            :         return true;
     847                 :            : 
     848                 :          0 :     default:
     849                 :          0 :         PL_ERR(vk, "Failed presenting to queue %p: %s", (void *) queue,
     850                 :            :                vk_res_str(res));
     851                 :          0 :         return false;
     852                 :            :     }
     853                 :            : }
     854                 :            : 
     855                 :         10 : static void vk_sw_swap_buffers(pl_swapchain sw)
     856                 :            : {
     857                 :         10 :     struct priv *p = PL_PRIV(sw);
     858                 :            : 
     859                 :         10 :     pl_mutex_lock(&p->lock);
     860         [ -  + ]:         10 :     while (pl_rc_count(&p->frames_in_flight) >= p->swapchain_depth) {
     861                 :          0 :         pl_mutex_unlock(&p->lock); // don't hold mutex while blocking
     862                 :          0 :         vk_poll_commands(p->vk, UINT64_MAX);
     863                 :          0 :         pl_mutex_lock(&p->lock);
     864                 :            :     }
     865                 :         10 :     pl_mutex_unlock(&p->lock);
     866                 :         10 : }
     867                 :            : 
     868                 :          2 : static bool vk_sw_resize(pl_swapchain sw, int *width, int *height)
     869                 :            : {
     870                 :          2 :     struct priv *p = PL_PRIV(sw);
     871                 :            :     bool ok = true;
     872                 :            : 
     873                 :          2 :     pl_mutex_lock(&p->lock);
     874                 :            : 
     875   [ +  -  -  + ]:          2 :     bool width_changed = *width && *width != p->cur_width,
     876   [ +  -  -  + ]:          2 :          height_changed = *height && *height != p->cur_height;
     877                 :            : 
     878   [ +  +  +  - ]:          2 :     if (p->suboptimal || p->needs_recreate || width_changed || height_changed)
     879                 :          2 :         ok = vk_sw_recreate(sw, *width, *height);
     880                 :            : 
     881                 :          2 :     *width = p->cur_width;
     882                 :          2 :     *height = p->cur_height;
     883                 :            : 
     884                 :          2 :     pl_mutex_unlock(&p->lock);
     885                 :          2 :     return ok;
     886                 :            : }
     887                 :            : 
     888                 :          0 : static void vk_sw_colorspace_hint(pl_swapchain sw, const struct pl_color_space *csp)
     889                 :            : {
     890                 :          0 :     struct priv *p = PL_PRIV(sw);
     891                 :          0 :     pl_mutex_lock(&p->lock);
     892                 :            : 
     893                 :            :     // This should never fail if the swapchain already exists
     894                 :          0 :     bool ok = pick_surf_format(sw, csp);
     895                 :          0 :     set_hdr_metadata(p, &csp->hdr);
     896         [ #  # ]:          0 :     pl_assert(ok);
     897                 :            : 
     898                 :          0 :     pl_mutex_unlock(&p->lock);
     899                 :          0 : }
     900                 :            : 
     901                 :          0 : bool pl_vulkan_swapchain_suboptimal(pl_swapchain sw)
     902                 :            : {
     903                 :          0 :     struct priv *p = PL_PRIV(sw);
     904                 :          0 :     return p->suboptimal;
     905                 :            : }
     906                 :            : 
     907                 :            : static const struct pl_sw_fns vulkan_swapchain = {
     908                 :            :     .destroy            = vk_sw_destroy,
     909                 :            :     .latency            = vk_sw_latency,
     910                 :            :     .resize             = vk_sw_resize,
     911                 :            :     .colorspace_hint    = vk_sw_colorspace_hint,
     912                 :            :     .start_frame        = vk_sw_start_frame,
     913                 :            :     .submit_frame       = vk_sw_submit_frame,
     914                 :            :     .swap_buffers       = vk_sw_swap_buffers,
     915                 :            : };

Generated by: LCOV version 1.16