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, ¶ms);
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, ¶ms);
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 : : }
|