LCOV - code coverage report
Current view: top level - src/tests - vulkan.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 96 99 97.0 %
Date: 2025-03-29 09:04:10 Functions: 3 3 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 38 77 49.4 %

           Branch data     Line data    Source code
       1                 :            : #include <vulkan/vulkan.h>
       2                 :            : 
       3                 :            : #include "gpu_tests.h"
       4                 :            : #include "vulkan/command.h"
       5                 :            : #include "vulkan/gpu.h"
       6                 :            : 
       7                 :            : #include <libplacebo/renderer.h>
       8                 :            : #include <libplacebo/vulkan.h>
       9                 :            : 
      10                 :          4 : static void vulkan_interop_tests(pl_vulkan pl_vk,
      11                 :            :                                  enum pl_handle_type handle_type)
      12                 :            : {
      13                 :          4 :     pl_gpu gpu = pl_vk->gpu;
      14                 :            :     printf("testing vulkan interop for handle type 0x%x\n", handle_type);
      15                 :            : 
      16         [ +  - ]:          4 :     if (gpu->export_caps.buf & handle_type) {
      17                 :          4 :         pl_buf buf = pl_buf_create(gpu, pl_buf_params(
      18                 :            :             .size = 1024,
      19                 :            :             .export_handle = handle_type,
      20                 :            :         ));
      21                 :            : 
      22         [ -  + ]:          4 :         REQUIRE(buf);
      23   [ +  -  -  -  :          4 :         REQUIRE_HANDLE(buf->shared_mem, handle_type);
          -  -  +  -  -  
             -  -  -  - ]
      24         [ -  + ]:          4 :         REQUIRE_CMP(buf->shared_mem.size, >=, buf->params.size, "zu");
      25         [ -  + ]:          4 :         REQUIRE(pl_buf_export(gpu, buf));
      26                 :          4 :         pl_buf_destroy(gpu, &buf);
      27                 :            :     }
      28                 :            : 
      29                 :          4 :     pl_fmt fmt = pl_find_fmt(gpu, PL_FMT_UNORM, 1, 0, 0, PL_FMT_CAP_BLITTABLE);
      30         [ +  - ]:          4 :     if (!fmt)
      31                 :            :         return;
      32                 :            : 
      33                 :            :     // Test interop API
      34         [ +  - ]:          4 :     if (gpu->export_caps.tex & handle_type) {
      35                 :          4 :         VkSemaphore sem = pl_vulkan_sem_create(gpu, pl_vulkan_sem_params(
      36                 :            :             .type           = VK_SEMAPHORE_TYPE_TIMELINE,
      37                 :            :             .initial_value  = 0,
      38                 :            :         ));
      39                 :            : 
      40                 :          4 :         pl_tex tex = pl_tex_create(gpu, pl_tex_params(
      41                 :            :             .w              = 32,
      42                 :            :             .h              = 32,
      43                 :            :             .format         = fmt,
      44                 :            :             .blit_dst       = true,
      45                 :            :             .export_handle  = handle_type,
      46                 :            :         ));
      47                 :            : 
      48         [ -  + ]:          4 :         REQUIRE(sem);
      49         [ -  + ]:          4 :         REQUIRE(tex);
      50                 :            : 
      51         [ -  + ]:          4 :         REQUIRE(pl_vulkan_hold_ex(gpu, pl_vulkan_hold_params(
      52                 :            :             .tex            = tex,
      53                 :            :             .layout         = VK_IMAGE_LAYOUT_GENERAL,
      54                 :            :             .qf             = VK_QUEUE_FAMILY_EXTERNAL,
      55                 :            :             .semaphore      = { sem, 1 },
      56                 :            :         )));
      57                 :            : 
      58                 :          4 :         pl_vulkan_release_ex(gpu, pl_vulkan_release_params(
      59                 :            :             .tex            = tex,
      60                 :            :             .layout         = VK_IMAGE_LAYOUT_GENERAL,
      61                 :            :             .qf             = VK_QUEUE_FAMILY_EXTERNAL,
      62                 :            :             .semaphore      = { sem, 1 },
      63                 :            :         ));
      64                 :            : 
      65                 :          4 :         pl_tex_clear(gpu, tex, (float[4]){0});
      66                 :          4 :         pl_gpu_finish(gpu);
      67         [ -  + ]:          4 :         REQUIRE(!pl_tex_poll(gpu, tex, 0));
      68                 :            : 
      69                 :          4 :         pl_vulkan_sem_destroy(gpu, &sem);
      70                 :          4 :         pl_tex_destroy(gpu, &tex);
      71                 :            :     }
      72                 :            : }
      73                 :            : 
      74                 :          1 : static void vulkan_swapchain_tests(pl_vulkan vk, VkSurfaceKHR surf)
      75                 :            : {
      76         [ -  + ]:          1 :     if (!surf)
      77                 :          0 :         return;
      78                 :            : 
      79                 :            :     printf("testing vulkan swapchain\n");
      80                 :          1 :     pl_gpu gpu = vk->gpu;
      81                 :            :     pl_swapchain sw;
      82                 :          1 :     sw = pl_vulkan_create_swapchain(vk, pl_vulkan_swapchain_params(
      83                 :            :         .surface = surf,
      84                 :            :     ));
      85         [ -  + ]:          1 :     REQUIRE(sw);
      86                 :            : 
      87                 :            :     // Attempt actually initializing the swapchain
      88                 :          1 :     int w = 640, h = 480;
      89         [ -  + ]:          1 :     REQUIRE(pl_swapchain_resize(sw, &w, &h));
      90                 :            : 
      91         [ +  + ]:         11 :     for (int i = 0; i < 10; i++) {
      92                 :            :         struct pl_swapchain_frame frame;
      93         [ -  + ]:         10 :         REQUIRE(pl_swapchain_start_frame(sw, &frame));
      94         [ +  - ]:         10 :         if (frame.fbo->params.blit_dst)
      95                 :         10 :             pl_tex_clear(gpu, frame.fbo, (float[4]){0});
      96                 :            : 
      97                 :            :         // TODO: test this with an actual pl_renderer instance
      98                 :            :         struct pl_frame target;
      99                 :         10 :         pl_frame_from_swapchain(&target, &frame);
     100                 :            : 
     101         [ -  + ]:         10 :         REQUIRE(pl_swapchain_submit_frame(sw));
     102                 :         10 :         pl_swapchain_swap_buffers(sw);
     103                 :            : 
     104                 :            :         // Try resizing the swapchain in the middle of rendering
     105         [ +  + ]:         10 :         if (i == 5) {
     106                 :          1 :             w = 320;
     107                 :          1 :             h = 240;
     108         [ -  + ]:          1 :             REQUIRE(pl_swapchain_resize(sw, &w, &h));
     109                 :            :         }
     110                 :            :     }
     111                 :            : 
     112                 :          1 :     pl_swapchain_destroy(&sw);
     113                 :            : }
     114                 :            : 
     115                 :          1 : int main()
     116                 :            : {
     117                 :          1 :     pl_log log = pl_test_logger();
     118                 :          1 :     pl_vk_inst inst = pl_vk_inst_create(log, pl_vk_inst_params(
     119                 :            :         .debug = true,
     120                 :            :         .debug_extra = true,
     121                 :            :         .get_proc_addr = vkGetInstanceProcAddr,
     122                 :            :         .opt_extensions = (const char *[]){
     123                 :            :             VK_KHR_SURFACE_EXTENSION_NAME,
     124                 :            :             VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME,
     125                 :            :         },
     126                 :            :         .num_opt_extensions = 2,
     127                 :            :     ));
     128                 :            : 
     129         [ +  - ]:          1 :     if (!inst)
     130                 :          0 :         return SKIP;
     131                 :            : 
     132                 :          1 :     PL_VK_LOAD_FUN(inst->instance, EnumeratePhysicalDevices, inst->get_proc_addr);
     133                 :          1 :     PL_VK_LOAD_FUN(inst->instance, GetPhysicalDeviceProperties, inst->get_proc_addr);
     134                 :            : 
     135                 :          1 :     uint32_t num = 0;
     136                 :          1 :     EnumeratePhysicalDevices(inst->instance, &num, NULL);
     137         [ +  - ]:          1 :     if (!num)
     138                 :            :         return SKIP;
     139                 :            : 
     140                 :          1 :     VkPhysicalDevice *devices = calloc(num, sizeof(*devices));
     141         [ +  - ]:          1 :     if (!devices)
     142                 :            :         return 1;
     143                 :          1 :     EnumeratePhysicalDevices(inst->instance, &num, devices);
     144                 :            : 
     145                 :          1 :     VkSurfaceKHR surf = VK_NULL_HANDLE;
     146                 :            : 
     147                 :          1 :     PL_VK_LOAD_FUN(inst->instance, CreateHeadlessSurfaceEXT, inst->get_proc_addr);
     148         [ +  - ]:          1 :     if (CreateHeadlessSurfaceEXT) {
     149                 :          1 :         VkHeadlessSurfaceCreateInfoEXT info = {
     150                 :            :             .sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT,
     151                 :            :         };
     152                 :            : 
     153                 :          1 :         VkResult res = CreateHeadlessSurfaceEXT(inst->instance, &info, NULL, &surf);
     154         [ -  + ]:          1 :         REQUIRE_CMP(res, ==, VK_SUCCESS, "u");
     155                 :            :     }
     156                 :            : 
     157                 :            :     // Make sure choosing any device works
     158                 :            :     VkPhysicalDevice dev;
     159                 :          1 :     dev = pl_vulkan_choose_device(log, pl_vulkan_device_params(
     160                 :            :         .instance = inst->instance,
     161                 :            :         .get_proc_addr = inst->get_proc_addr,
     162                 :            :         .allow_software = true,
     163                 :            :         .surface = surf,
     164                 :            :     ));
     165         [ +  - ]:          1 :     if (!dev)
     166                 :            :         return SKIP;
     167                 :            : 
     168                 :            :     // Test all attached devices
     169         [ +  + ]:          3 :     for (int i = 0; i < num; i++) {
     170                 :          2 :         VkPhysicalDeviceProperties props = {0};
     171                 :          2 :         GetPhysicalDeviceProperties(devices[i], &props);
     172                 :            : #ifndef CI_ALLOW_SW
     173         [ +  + ]:          2 :         if (props.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
     174                 :            :             printf("Skipping device %d: %s\n", i, props.deviceName);
     175                 :          1 :             continue;
     176                 :            :         }
     177                 :            : #endif
     178                 :            :         printf("Testing device %d: %s\n", i, props.deviceName);
     179                 :            : 
     180                 :            :         // Make sure we can choose this device by name
     181                 :          1 :         dev = pl_vulkan_choose_device(log, pl_vulkan_device_params(
     182                 :            :             .instance = inst->instance,
     183                 :            :             .get_proc_addr = inst->get_proc_addr,
     184                 :            :             .device_name = props.deviceName,
     185                 :            :         ));
     186         [ -  + ]:          1 :         REQUIRE_CMP(dev, ==, devices[i], "p");
     187                 :            : 
     188                 :          1 :         struct pl_vulkan_params params = *pl_vulkan_params(
     189                 :            :             .instance = inst->instance,
     190                 :            :             .get_proc_addr = inst->get_proc_addr,
     191                 :            :             .device = devices[i],
     192                 :            :             .queue_count = 8, // test inter-queue stuff
     193                 :            :             .surface = surf,
     194                 :            :         );
     195                 :            : 
     196                 :          1 :         pl_vulkan vk = pl_vulkan_create(log, &params);
     197         [ -  + ]:          1 :         if (!vk)
     198                 :          0 :             continue;
     199                 :            : 
     200                 :          1 :         gpu_shader_tests(vk->gpu);
     201                 :          1 :         vulkan_swapchain_tests(vk, surf);
     202                 :            : 
     203                 :            :         // Print heap statistics
     204                 :          1 :         pl_vk_print_heap(vk->gpu, PL_LOG_DEBUG);
     205                 :            : 
     206                 :            :         // Test importing this context via the vulkan interop API
     207                 :          1 :         pl_vulkan vk2 = pl_vulkan_import(log, pl_vulkan_import_params(
     208                 :            :             .instance = vk->instance,
     209                 :            :             .get_proc_addr = inst->get_proc_addr,
     210                 :            :             .phys_device = vk->phys_device,
     211                 :            :             .device = vk->device,
     212                 :            : 
     213                 :            :             .extensions = vk->extensions,
     214                 :            :             .num_extensions = vk->num_extensions,
     215                 :            :             .features = vk->features,
     216                 :            :             .queue_graphics = vk->queue_graphics,
     217                 :            :             .queue_compute = vk->queue_compute,
     218                 :            :             .queue_transfer = vk->queue_transfer,
     219                 :            :         ));
     220         [ -  + ]:          1 :         REQUIRE(vk2);
     221                 :          1 :         pl_vulkan_destroy(&vk2);
     222                 :            : 
     223                 :            :         // Run these tests last because they disable some validation layers
     224                 :            : #ifdef PL_HAVE_UNIX
     225                 :          1 :         vulkan_interop_tests(vk, PL_HANDLE_FD);
     226                 :          1 :         vulkan_interop_tests(vk, PL_HANDLE_DMA_BUF);
     227                 :            : #endif
     228                 :            : #ifdef PL_HAVE_WIN32
     229                 :            :         vulkan_interop_tests(vk, PL_HANDLE_WIN32);
     230                 :            :         vulkan_interop_tests(vk, PL_HANDLE_WIN32_KMT);
     231                 :            : #endif
     232                 :          1 :         gpu_interop_tests(vk->gpu);
     233                 :          1 :         pl_vulkan_destroy(&vk);
     234                 :            : 
     235                 :            :         // Re-run the same export/import tests with async queues disabled
     236                 :          1 :         params.async_compute = false;
     237                 :          1 :         params.async_transfer = false;
     238                 :          1 :         vk = pl_vulkan_create(log, &params);
     239         [ -  + ]:          1 :         REQUIRE(vk); // it succeeded the first time
     240                 :            : 
     241                 :            : #ifdef PL_HAVE_UNIX
     242                 :          1 :         vulkan_interop_tests(vk, PL_HANDLE_FD);
     243                 :          1 :         vulkan_interop_tests(vk, PL_HANDLE_DMA_BUF);
     244                 :            : #endif
     245                 :            : #ifdef PL_HAVE_WIN32
     246                 :            :         vulkan_interop_tests(vk, PL_HANDLE_WIN32);
     247                 :            :         vulkan_interop_tests(vk, PL_HANDLE_WIN32_KMT);
     248                 :            : #endif
     249                 :          1 :         gpu_interop_tests(vk->gpu);
     250                 :          1 :         pl_vulkan_destroy(&vk);
     251                 :            : 
     252                 :            :         // Reduce log spam after first tested device
     253                 :          1 :         pl_log_level_update(log, PL_LOG_INFO);
     254                 :            :     }
     255                 :            : 
     256         [ +  - ]:          1 :     if (surf)
     257                 :          1 :         vkDestroySurfaceKHR(inst->instance, surf, NULL);
     258                 :          1 :     pl_vk_inst_destroy(&inst);
     259                 :          1 :     pl_log_destroy(&log);
     260                 :          1 :     free(devices);
     261                 :            : }

Generated by: LCOV version 1.16