working on implementing device memory
[kazan.git] / src / vulkan_icd / x11_wsi.cpp
1 /*
2 * Copyright 2017 Jacob Lifshay
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 */
23 #include "wsi.h"
24
25 #ifdef VK_USE_PLATFORM_XCB_KHR
26 #include <xcb/xcb.h>
27 #include <sys/types.h>
28 #include <sys/ipc.h>
29 #include <sys/shm.h>
30 #include <xcb/shm.h>
31 #include <xcb/present.h>
32 #include <cassert>
33 #include <cstdlib>
34 #include <memory>
35 #include <cstring>
36 #include <iostream>
37 #include <list>
38 #include <utility>
39 #include <algorithm>
40 #include <cstdlib>
41 #include <atomic>
42 #include "util/optional.h"
43 #include "util/circular_queue.h"
44
45 namespace kazan
46 {
47 namespace vulkan_icd
48 {
49 struct Xcb_wsi::Implementation
50 {
51 static constexpr std::size_t max_swapchain_image_count = 16;
52 static std::uint32_t u32_from_bytes(std::uint8_t b0,
53 std::uint8_t b1,
54 std::uint8_t b2,
55 std::uint8_t b3) noexcept
56 {
57 static_assert(sizeof(std::uint8_t) == 1 && sizeof(std::uint32_t) == 4, "");
58 union
59 {
60 std::uint8_t bytes[4];
61 std::uint32_t u32;
62 };
63 bytes[0] = b0;
64 bytes[1] = b1;
65 bytes[2] = b2;
66 bytes[3] = b3;
67 return u32;
68 }
69 template <typename T = void>
70 struct Free_functor
71 {
72 void operator()(T *p) noexcept
73 {
74 std::free(p);
75 }
76 };
77 typedef std::unique_ptr<xcb_query_extension_reply_t, Free_functor<xcb_query_extension_reply_t>>
78 Query_extension_reply;
79 typedef std::unique_ptr<xcb_get_geometry_reply_t, Free_functor<xcb_get_geometry_reply_t>>
80 Get_geometry_reply;
81 typedef std::unique_ptr<xcb_get_window_attributes_reply_t,
82 Free_functor<xcb_get_window_attributes_reply_t>>
83 Get_window_attributes_reply;
84 typedef std::unique_ptr<xcb_query_tree_reply_t, Free_functor<xcb_query_tree_reply_t>>
85 Query_tree_reply;
86 typedef std::unique_ptr<xcb_shm_query_version_reply_t,
87 Free_functor<xcb_shm_query_version_reply_t>> Shm_query_version_reply;
88 typedef std::unique_ptr<xcb_generic_error_t, Free_functor<xcb_generic_error_t>> Generic_error;
89 template <typename Id_type,
90 xcb_void_cookie_t (*free_function)(xcb_connection_t *connection, Id_type id)>
91 class Server_object
92 {
93 private:
94 Id_type value;
95 xcb_connection_t *connection;
96
97 public:
98 constexpr Server_object() noexcept : value(), connection()
99 {
100 }
101 constexpr Server_object(std::nullptr_t) noexcept : value(), connection()
102 {
103 }
104 constexpr Server_object(Id_type value, xcb_connection_t *connection) noexcept
105 : value(value),
106 connection(connection)
107 {
108 assert(connection);
109 }
110 void swap(Server_object &other) noexcept
111 {
112 using std::swap;
113 swap(value, other.value);
114 swap(connection, other.connection);
115 }
116 Server_object(Server_object &&rt) noexcept : value(), connection()
117 {
118 swap(rt);
119 }
120 Server_object &operator=(Server_object rt) noexcept
121 {
122 swap(rt);
123 return *this;
124 }
125 ~Server_object() noexcept
126 {
127 if(connection)
128 free_function(connection, value);
129 }
130 Id_type get() const noexcept
131 {
132 return value;
133 }
134 };
135 typedef Server_object<xcb_gcontext_t, &xcb_free_gc> Gc;
136 typedef Server_object<xcb_pixmap_t, &xcb_free_pixmap> Pixmap;
137 typedef Server_object<xcb_shm_seg_t, &xcb_shm_detach> Server_shm_seg;
138 class Shared_memory_segment
139 {
140 private:
141 int value;
142
143 public:
144 constexpr Shared_memory_segment() noexcept : value(-1)
145 {
146 }
147 constexpr Shared_memory_segment(std::nullptr_t) noexcept : Shared_memory_segment()
148 {
149 }
150 explicit Shared_memory_segment(int value) noexcept : value(value)
151 {
152 }
153 static Shared_memory_segment create(std::size_t size, int flags = IPC_CREAT | 0777)
154 {
155 Shared_memory_segment retval(shmget(IPC_PRIVATE, size, flags));
156 if(!retval)
157 throw std::runtime_error("shmget failed");
158 return retval;
159 }
160 void swap(Shared_memory_segment &other) noexcept
161 {
162 using std::swap;
163 swap(value, other.value);
164 }
165 Shared_memory_segment(Shared_memory_segment &&rt) noexcept : Shared_memory_segment()
166 {
167 swap(rt);
168 }
169 Shared_memory_segment &operator=(Shared_memory_segment rt) noexcept
170 {
171 swap(rt);
172 return *this;
173 }
174 ~Shared_memory_segment() noexcept
175 {
176 if(*this)
177 shmctl(value, IPC_RMID, nullptr);
178 }
179 explicit operator bool() const noexcept
180 {
181 return value != -1;
182 }
183 std::shared_ptr<void> map()
184 {
185 assert(*this);
186 void *memory = shmat(value, nullptr, 0);
187 if(memory == reinterpret_cast<void *>(-1))
188 throw std::runtime_error("shmat failed");
189 return std::shared_ptr<void>(memory,
190 [](void *memory) noexcept
191 {
192 shmdt(memory);
193 });
194 }
195 int get() const noexcept
196 {
197 return value;
198 }
199 };
200 static xcb_query_extension_cookie_t query_extension(xcb_connection_t *connection,
201 const char *extension_name) noexcept
202 {
203 return xcb_query_extension(connection, std::strlen(extension_name), extension_name);
204 }
205 enum class Surface_format_group
206 {
207 B8G8R8A8,
208 };
209 struct Start_setup_results
210 {
211 enum class Status
212 {
213 Bad_surface,
214 No_support,
215 Success,
216 };
217 Status status;
218 Gc gc;
219 bool shm_is_supported;
220 unsigned window_depth;
221 std::uint32_t image_width;
222 std::uint32_t image_height;
223 Surface_format_group surface_format_group;
224 util::optional<std::vector<VkPresentModeKHR>> present_modes;
225 VkSurfaceCapabilitiesKHR capabilities;
226 std::size_t image_pixel_size;
227 std::size_t scanline_alignment;
228 xcb_shm_query_version_cookie_t shm_query_version_cookie;
229 vulkan::Vulkan_image_descriptor image_descriptor;
230 Start_setup_results(Gc gc,
231 bool shm_is_supported,
232 unsigned window_depth,
233 std::uint32_t image_width,
234 std::uint32_t image_height,
235 Surface_format_group surface_format_group,
236 std::vector<VkPresentModeKHR> present_modes,
237 const VkSurfaceCapabilitiesKHR &capabilities,
238 std::size_t image_pixel_size,
239 std::size_t scanline_alignment,
240 xcb_shm_query_version_cookie_t shm_query_version_cookie,
241 const vulkan::Vulkan_image_descriptor &image_descriptor) noexcept
242 : status(Status::Success),
243 gc(std::move(gc)),
244 shm_is_supported(shm_is_supported),
245 window_depth(window_depth),
246 image_width(image_width),
247 image_height(image_height),
248 surface_format_group(surface_format_group),
249 present_modes(std::move(present_modes)),
250 capabilities(capabilities),
251 image_pixel_size(image_pixel_size),
252 scanline_alignment(scanline_alignment),
253 shm_query_version_cookie(shm_query_version_cookie),
254 image_descriptor(image_descriptor)
255 {
256 }
257 constexpr Start_setup_results(Status status) noexcept : status(status),
258 gc(),
259 shm_is_supported(),
260 window_depth(),
261 image_width(),
262 image_height(),
263 surface_format_group(),
264 present_modes(),
265 capabilities{},
266 image_pixel_size(),
267 scanline_alignment(),
268 shm_query_version_cookie(),
269 image_descriptor()
270 {
271 assert(status != Status::Success);
272 }
273 };
274 static Start_setup_results start_setup(xcb_connection_t *connection,
275 xcb_window_t window,
276 bool is_full_setup)
277 {
278 auto mit_shm_cookie = query_extension(connection, "MIT-SHM");
279 auto get_geometry_cookie = xcb_get_geometry(connection, window);
280 auto get_window_attributes_cookie = xcb_get_window_attributes(connection, window);
281 auto query_tree_cookie = xcb_query_tree(connection, window);
282 auto gc_id = xcb_generate_id(connection);
283 const std::uint32_t gc_params[1] = {
284 0, // value for XCB_GC_GRAPHICS_EXPOSURES
285 };
286 xcb_create_gc(connection, gc_id, window, XCB_GC_GRAPHICS_EXPOSURES, gc_params);
287 auto gc = Gc(gc_id, connection);
288 auto mit_shm_reply =
289 Query_extension_reply(xcb_query_extension_reply(connection, mit_shm_cookie, nullptr));
290 bool shm_is_supported = mit_shm_reply && mit_shm_reply->present;
291 xcb_shm_query_version_cookie_t shm_query_version_cookie{};
292 if(shm_is_supported && is_full_setup)
293 shm_query_version_cookie = xcb_shm_query_version(connection);
294 auto get_geometry_reply =
295 Get_geometry_reply(xcb_get_geometry_reply(connection, get_geometry_cookie, nullptr));
296 if(!get_geometry_reply)
297 return Start_setup_results::Status::Bad_surface;
298 std::uint32_t image_width = get_geometry_reply->width;
299 std::uint32_t image_height = get_geometry_reply->height;
300 auto get_window_attributes_reply = Get_window_attributes_reply(
301 xcb_get_window_attributes_reply(connection, get_window_attributes_cookie, nullptr));
302 if(!get_window_attributes_reply)
303 return Start_setup_results::Status::Bad_surface;
304 auto window_visual_id = get_window_attributes_reply->visual;
305 auto query_tree_reply =
306 Query_tree_reply(xcb_query_tree_reply(connection, query_tree_cookie, nullptr));
307 if(!query_tree_reply)
308 return Start_setup_results::Status::Bad_surface;
309 auto root_window = query_tree_reply->root;
310 xcb_screen_t *screen = nullptr;
311 for(auto iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); iter.rem;
312 xcb_screen_next(&iter))
313 {
314 if(iter.data->root == root_window)
315 {
316 screen = iter.data;
317 break;
318 }
319 }
320 if(!screen)
321 return Start_setup_results::Status::Bad_surface;
322 xcb_visualtype_t *window_visual_type = nullptr;
323 unsigned window_depth = 0;
324 for(auto depth_iter = xcb_screen_allowed_depths_iterator(screen); depth_iter.rem;
325 xcb_depth_next(&depth_iter))
326 {
327 for(auto visual_iter = xcb_depth_visuals_iterator(depth_iter.data); visual_iter.rem;
328 xcb_visualtype_next(&visual_iter))
329 {
330 if(visual_iter.data->visual_id == window_visual_id)
331 {
332 window_visual_type = visual_iter.data;
333 window_depth = depth_iter.data->depth;
334 break;
335 }
336 }
337 if(window_visual_type)
338 break;
339 }
340 if(!window_visual_type)
341 return Start_setup_results::Status::Bad_surface;
342 std::uint32_t red_mask = window_visual_type->red_mask;
343 std::uint32_t green_mask = window_visual_type->green_mask;
344 std::uint32_t blue_mask = window_visual_type->blue_mask;
345 std::uint32_t alpha_mask;
346 switch(window_depth)
347 {
348 case 24:
349 alpha_mask = 0;
350 break;
351 case 32:
352 alpha_mask = ~(red_mask | green_mask | blue_mask);
353 break;
354 default:
355 return Start_setup_results::Status::No_support;
356 }
357 xcb_format_t *window_pixmap_format = nullptr;
358 for(auto iter = xcb_setup_pixmap_formats_iterator(xcb_get_setup(connection)); iter.rem;
359 xcb_format_next(&iter))
360 {
361 if(iter.data->depth == window_depth)
362 {
363 window_pixmap_format = iter.data;
364 break;
365 }
366 }
367 if(!window_pixmap_format)
368 return Start_setup_results::Status::Bad_surface;
369 std::size_t image_pixel_size;
370 switch(window_pixmap_format->bits_per_pixel)
371 {
372 case 24:
373 image_pixel_size = 3;
374 break;
375 case 32:
376 image_pixel_size = 4;
377 break;
378 default:
379 return Start_setup_results::Status::No_support;
380 }
381 Surface_format_group surface_format_group;
382 if(red_mask == u32_from_bytes(0, 0, 0xFF, 0) && green_mask == u32_from_bytes(0, 0xFF, 0, 0)
383 && blue_mask == u32_from_bytes(0xFF, 0, 0, 0)
384 && (alpha_mask == 0 || alpha_mask == u32_from_bytes(0, 0, 0, 0xFF))
385 && image_pixel_size == 4)
386 surface_format_group = Surface_format_group::B8G8R8A8;
387 else
388 return Start_setup_results::Status::No_support;
389 std::size_t scanline_alignment = 1;
390 switch(window_pixmap_format->scanline_pad)
391 {
392 case 8:
393 scanline_alignment = 1;
394 break;
395 case 16:
396 scanline_alignment = 2;
397 break;
398 case 32:
399 scanline_alignment = 4;
400 break;
401 default:
402 assert(!"invalid pixmap format scanline-pad");
403 }
404 std::vector<VkPresentModeKHR> present_modes = {
405 #warning properly implement fifo present mode using X11 Present extension
406 VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR,
407 };
408 VkSurfaceCapabilitiesKHR capabilities = {
409 .minImageCount = 2,
410 .maxImageCount = max_swapchain_image_count,
411 .currentExtent =
412 {
413 .width = image_width, .height = image_height,
414 },
415 .minImageExtent =
416 {
417 .width = image_width, .height = image_height,
418 },
419 .maxImageExtent =
420 {
421 .width = image_width, .height = image_height,
422 },
423 .maxImageArrayLayers = 1,
424 .supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
425 .currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
426 .supportedCompositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
427 .supportedUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
428 | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT
429 | VK_IMAGE_USAGE_SAMPLED_BIT
430 | VK_IMAGE_USAGE_STORAGE_BIT
431 | VK_IMAGE_USAGE_TRANSFER_DST_BIT
432 | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
433 };
434 return Start_setup_results(std::move(gc),
435 shm_is_supported,
436 window_depth,
437 image_width,
438 image_height,
439 surface_format_group,
440 present_modes,
441 capabilities,
442 image_pixel_size,
443 scanline_alignment,
444 shm_query_version_cookie,
445 vulkan::Vulkan_image_descriptor(
446 0,
447 VK_IMAGE_TYPE_2D,
448 VK_FORMAT_UNDEFINED,
449 VkExtent3D{
450 .width = image_width, .height = image_height, .depth = 1,
451 },
452 1,
453 1,
454 VK_SAMPLE_COUNT_1_BIT,
455 VK_IMAGE_TILING_OPTIMAL));
456 }
457 struct Swapchain final : public Vulkan_swapchain
458 {
459 enum class Image_owner
460 {
461 Swapchain,
462 Application,
463 Presentation_engine,
464 };
465 enum class Status
466 {
467 Setup_failed,
468 No_surface,
469 Out_of_date,
470 Good,
471 };
472 struct Swapchain_image final : public vulkan::Vulkan_image
473 {
474 Shared_memory_segment shared_memory_segment;
475 Server_shm_seg server_shm_seg;
476 Pixmap pixmap;
477 Image_owner owner;
478 xcb_get_geometry_cookie_t get_geometry_cookie{};
479 Swapchain_image(const vulkan::Vulkan_image_descriptor &descriptor,
480 std::shared_ptr<void> pixels,
481 Shared_memory_segment shared_memory_segment,
482 Server_shm_seg server_shm_seg,
483 Pixmap pixmap) noexcept
484 : Vulkan_image(descriptor, std::move(pixels)),
485 shared_memory_segment(std::move(shared_memory_segment)),
486 server_shm_seg(std::move(server_shm_seg)),
487 pixmap(std::move(pixmap)),
488 owner(Image_owner::Swapchain)
489 {
490 }
491 };
492 Swapchain_image &get_image(std::size_t index) noexcept
493 {
494 assert(index < images.size());
495 assert(dynamic_cast<Swapchain_image *>(images[index].get()));
496 return *static_cast<Swapchain_image *>(images[index].get());
497 }
498 xcb_connection_t *connection;
499 xcb_window_t window;
500 bool shm_is_supported;
501 Status status;
502 util::Static_circular_deque<std::size_t, max_swapchain_image_count> presenting_image_queue;
503 std::uint32_t swapchain_width;
504 std::uint32_t swapchain_height;
505 Gc gc;
506 unsigned window_depth;
507 explicit Swapchain(Start_setup_results start_setup_results,
508 xcb_connection_t *connection,
509 xcb_window_t window,
510 const VkSwapchainCreateInfoKHR &create_info)
511 : Vulkan_swapchain({}),
512 connection(connection),
513 window(window),
514 shm_is_supported(start_setup_results.shm_is_supported),
515 status(Status::Good),
516 presenting_image_queue(),
517 gc(std::move(start_setup_results.gc)),
518 window_depth(start_setup_results.window_depth)
519 {
520 assert(create_info.sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
521 #warning formats other than VK_FORMAT_B8G8R8A8_UNORM are unimplemented
522 assert(create_info.imageFormat == VK_FORMAT_B8G8R8A8_UNORM);
523 assert(create_info.imageColorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR);
524 assert(create_info.imageArrayLayers
525 <= start_setup_results.capabilities.maxImageArrayLayers);
526 assert(create_info.imageArrayLayers != 0);
527 assert((create_info.imageUsage & ~start_setup_results.capabilities.supportedUsageFlags)
528 == 0);
529 assert(create_info.preTransform == start_setup_results.capabilities.currentTransform);
530 assert((create_info.compositeAlpha
531 & ~start_setup_results.capabilities.supportedCompositeAlpha)
532 == 0);
533 switch(start_setup_results.status)
534 {
535 case Start_setup_results::Status::Bad_surface:
536 case Start_setup_results::Status::No_support:
537 status = Status::Setup_failed;
538 return;
539 case Start_setup_results::Status::Success:
540 break;
541 }
542 if(start_setup_results.image_width != create_info.imageExtent.width
543 || start_setup_results.image_height != create_info.imageExtent.height)
544 {
545 status = Status::Out_of_date;
546 }
547 start_setup_results.image_descriptor.format = create_info.imageFormat;
548 swapchain_width = start_setup_results.image_width;
549 swapchain_height = start_setup_results.image_height;
550 const char *warning_message_present_mode_name = nullptr;
551 switch(create_info.presentMode)
552 {
553 case VK_PRESENT_MODE_IMMEDIATE_KHR:
554 break;
555 case VK_PRESENT_MODE_FIFO_KHR:
556 {
557 static std::atomic_bool wrote_warning_message(false);
558 if(!wrote_warning_message.exchange(true, std::memory_order_relaxed))
559 warning_message_present_mode_name = "FIFO";
560 break;
561 }
562 case VK_PRESENT_MODE_MAILBOX_KHR:
563 {
564 static std::atomic_bool wrote_warning_message(false);
565 if(!wrote_warning_message.exchange(true, std::memory_order_relaxed))
566 warning_message_present_mode_name = "MAILBOX";
567 break;
568 }
569 case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
570 {
571 static std::atomic_bool wrote_warning_message(false);
572 if(!wrote_warning_message.exchange(true, std::memory_order_relaxed))
573 warning_message_present_mode_name = "FIFO_RELAXED";
574 break;
575 }
576 case VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR:
577 case VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR:
578 case VK_PRESENT_MODE_RANGE_SIZE_KHR:
579 case VK_PRESENT_MODE_MAX_ENUM_KHR:
580 assert(!"bad present mode");
581 break;
582 }
583 if(warning_message_present_mode_name)
584 std::cerr << warning_message_present_mode_name
585 << " present mode is not implemented; falling back to IMMEDIATE"
586 << std::endl;
587 std::size_t unpadded_scanline_size =
588 start_setup_results.image_pixel_size * start_setup_results.image_width;
589 std::size_t padded_scanline_size =
590 (unpadded_scanline_size + start_setup_results.scanline_alignment - 1U)
591 & ~(start_setup_results.scanline_alignment - 1U);
592 std::size_t image_size = padded_scanline_size * start_setup_results.image_height;
593 if(shm_is_supported)
594 {
595 auto shm_query_version_reply = Shm_query_version_reply(xcb_shm_query_version_reply(
596 connection, start_setup_results.shm_query_version_cookie, nullptr));
597 if(!shm_query_version_reply || !shm_query_version_reply->shared_pixmaps
598 || shm_query_version_reply->pixmap_format != XCB_IMAGE_FORMAT_Z_PIXMAP)
599 {
600 std::cerr
601 << "shared memory pixmaps are not supported, falling back to using core "
602 "X protocol"
603 << std::endl;
604 shm_is_supported = false;
605 }
606 }
607 auto image_count = std::max<std::uint32_t>(create_info.minImageCount, 2);
608 while(true)
609 {
610 bool shm_failed = false;
611 for(std::uint32_t i = 0; i < image_count; i++)
612 {
613 Shared_memory_segment shared_memory_segment;
614 std::shared_ptr<void> pixels;
615 Server_shm_seg server_shm_seg;
616 Pixmap pixmap;
617 if(shm_is_supported)
618 {
619 shared_memory_segment = Shared_memory_segment::create(image_size);
620 pixels = shared_memory_segment.map();
621 auto seg_id = xcb_generate_id(connection);
622 auto shm_attach_cookie = xcb_shm_attach_checked(
623 connection, seg_id, shared_memory_segment.get(), false);
624 auto error =
625 Generic_error(xcb_request_check(connection, shm_attach_cookie));
626 if(error)
627 {
628 shm_failed = true;
629 break;
630 }
631 server_shm_seg = Server_shm_seg(seg_id, connection);
632 auto pixmap_id = xcb_generate_id(connection);
633 error = Generic_error(xcb_request_check(
634 connection,
635 xcb_shm_create_pixmap_checked(connection,
636 pixmap_id,
637 window,
638 start_setup_results.image_width,
639 start_setup_results.image_height,
640 start_setup_results.window_depth,
641 server_shm_seg.get(),
642 0)));
643 if(error)
644 {
645 shm_failed = true;
646 break;
647 }
648 pixmap = Pixmap(pixmap_id, connection);
649 }
650 else
651 {
652 pixels = std::shared_ptr<unsigned char>(new unsigned char[image_size],
653 [](unsigned char *p) noexcept
654 {
655 delete[] p;
656 });
657 }
658 images.push_back(
659 std::make_unique<Swapchain_image>(start_setup_results.image_descriptor,
660 std::move(pixels),
661 std::move(shared_memory_segment),
662 std::move(server_shm_seg),
663 std::move(pixmap)));
664 }
665 if(shm_failed)
666 {
667 std::cerr << "using shared memory failed, falling back to using core X protocol"
668 << std::endl;
669 shm_is_supported = false;
670 images.clear();
671 continue;
672 }
673 break;
674 }
675 }
676 virtual VkResult acquire_next_image(std::uint64_t timeout,
677 vulkan::Vulkan_semaphore *semaphore,
678 vulkan::Vulkan_fence *fence,
679 std::uint32_t &returned_image_index) override
680 {
681 #warning figure out how to use timeouts with xcb blocking for X server responses
682 switch(status)
683 {
684 case Status::No_surface:
685 case Status::Setup_failed:
686 return VK_ERROR_SURFACE_LOST_KHR;
687 case Status::Out_of_date:
688 return VK_ERROR_OUT_OF_DATE_KHR;
689 case Status::Good:
690 break;
691 }
692 while(true)
693 {
694 for(std::size_t i = 0; i < images.size(); i++)
695 {
696 auto &image = get_image(i);
697 if(image.owner == Image_owner::Swapchain)
698 {
699 image.owner = Image_owner::Application;
700 returned_image_index = i;
701 if(semaphore)
702 semaphore->signal();
703 if(fence)
704 fence->signal();
705 return VK_SUCCESS;
706 }
707 }
708 if(presenting_image_queue.empty())
709 {
710 std::cerr << "vkAcquireNextImageKHR called when application has already "
711 "acquired all swapchain images; aborting"
712 << std::endl;
713 std::abort();
714 }
715 assert(shm_is_supported);
716 std::size_t image_index = presenting_image_queue.front();
717 presenting_image_queue.pop_front();
718 auto &image = get_image(image_index);
719 // wait for the presentation request to finish
720 // we use a xcb_get_geometry command after the xcb_copy_area command, so we can wait
721 // on the xcb_get_geometry command since the X server processes commands in order
722 auto get_geometry_reply = Get_geometry_reply(
723 xcb_get_geometry_reply(connection, image.get_geometry_cookie, nullptr));
724 image.owner = Image_owner::Swapchain;
725 if(!get_geometry_reply)
726 {
727 status = Status::No_surface;
728 return VK_ERROR_SURFACE_LOST_KHR;
729 }
730 if(get_geometry_reply->width != swapchain_width
731 || get_geometry_reply->height != swapchain_height)
732 {
733 status = Status::Out_of_date;
734 return VK_ERROR_OUT_OF_DATE_KHR;
735 }
736 image.owner = Image_owner::Application;
737 returned_image_index = image_index;
738 if(semaphore)
739 semaphore->signal();
740 if(fence)
741 fence->signal();
742 return VK_SUCCESS;
743 }
744 }
745 virtual VkResult queue_present(std::uint32_t image_index,
746 vulkan::Vulkan_device::Queue &queue) override
747 {
748 assert(image_index < images.size());
749 switch(status)
750 {
751 case Status::No_surface:
752 case Status::Setup_failed:
753 return VK_ERROR_SURFACE_LOST_KHR;
754 case Status::Out_of_date:
755 return VK_ERROR_OUT_OF_DATE_KHR;
756 case Status::Good:
757 break;
758 }
759 auto &image = get_image(image_index);
760 assert(image.owner == Image_owner::Application);
761 // wait for rendering to catch up
762 {
763 vulkan::Vulkan_fence fence(0);
764 queue.queue_fence_signal(fence);
765 fence.wait(-1);
766 }
767
768 if(shm_is_supported)
769 {
770 xcb_copy_area(connection,
771 image.pixmap.get(),
772 window,
773 gc.get(),
774 0,
775 0,
776 0,
777 0,
778 swapchain_width,
779 swapchain_height);
780 }
781 else
782 {
783 std::size_t image_size = image.descriptor.get_memory_properties().size;
784 assert(static_cast<std::uint32_t>(image_size) == image_size);
785 xcb_put_image(connection,
786 XCB_IMAGE_FORMAT_Z_PIXMAP,
787 window,
788 gc.get(),
789 swapchain_width,
790 swapchain_height,
791 0,
792 0,
793 0,
794 window_depth,
795 image_size,
796 static_cast<const std::uint8_t *>(image.memory.get()));
797 }
798 image.get_geometry_cookie = xcb_get_geometry(connection, window);
799 image.owner = Image_owner::Presentation_engine;
800 presenting_image_queue.push_back(image_index);
801 xcb_flush(connection);
802 return VK_SUCCESS;
803 }
804 };
805 };
806
807 VkIcdSurfaceBase *Xcb_wsi::create_surface(const VkXcbSurfaceCreateInfoKHR &create_info) const
808 {
809 assert(create_info.sType == VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR);
810 assert(create_info.flags == 0);
811 return reinterpret_cast<VkIcdSurfaceBase *>(new Surface_type{
812 .base =
813 {
814 .platform = VK_ICD_WSI_PLATFORM_XCB,
815 },
816 .connection = create_info.connection,
817 .window = create_info.window,
818 });
819 }
820
821 void Xcb_wsi::destroy_surface(VkIcdSurfaceBase *surface) const noexcept
822 {
823 delete reinterpret_cast<Surface_type *>(surface);
824 }
825
826 VkResult Xcb_wsi::get_surface_support(VkIcdSurfaceBase *surface_, bool &supported) const
827 {
828 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
829 switch(Implementation::start_setup(surface.connection, surface.window, false).status)
830 {
831 case Implementation::Start_setup_results::Status::Bad_surface:
832 return VK_ERROR_SURFACE_LOST_KHR;
833 case Implementation::Start_setup_results::Status::No_support:
834 supported = false;
835 return VK_SUCCESS;
836 case Implementation::Start_setup_results::Status::Success:
837 supported = true;
838 return VK_SUCCESS;
839 }
840 assert(!"unreachable");
841 return {};
842 }
843
844 VkResult Xcb_wsi::get_surface_formats(VkIcdSurfaceBase *surface_,
845 std::vector<VkSurfaceFormatKHR> &surface_formats) const
846 {
847 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
848 auto start_setup_result =
849 Implementation::start_setup(surface.connection, surface.window, false);
850 switch(start_setup_result.status)
851 {
852 case Implementation::Start_setup_results::Status::Bad_surface:
853 case Implementation::Start_setup_results::Status::No_support:
854 return VK_ERROR_SURFACE_LOST_KHR;
855 case Implementation::Start_setup_results::Status::Success:
856 {
857 surface_formats.clear();
858 switch(start_setup_result.surface_format_group)
859 {
860 case Implementation::Surface_format_group::B8G8R8A8:
861 surface_formats =
862 {
863 #if 1
864 #warning implement VK_FORMAT_B8G8R8A8_SRGB
865 #else
866 {
867 .format = VK_FORMAT_B8G8R8A8_SRGB,
868 .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
869 },
870 #endif
871 {
872 .format = VK_FORMAT_B8G8R8A8_UNORM,
873 .colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR,
874 },
875 };
876 break;
877 }
878 return VK_SUCCESS;
879 }
880 }
881 assert(!"unreachable");
882 return {};
883 }
884
885 VkResult Xcb_wsi::get_present_modes(VkIcdSurfaceBase *surface_,
886 std::vector<VkPresentModeKHR> &present_modes) const
887 {
888 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
889 auto start_setup_result =
890 Implementation::start_setup(surface.connection, surface.window, false);
891 switch(start_setup_result.status)
892 {
893 case Implementation::Start_setup_results::Status::Bad_surface:
894 case Implementation::Start_setup_results::Status::No_support:
895 return VK_ERROR_SURFACE_LOST_KHR;
896 case Implementation::Start_setup_results::Status::Success:
897 present_modes = std::move(start_setup_result.present_modes.value());
898 return VK_SUCCESS;
899 }
900 assert(!"unreachable");
901 return {};
902 }
903
904 VkResult Xcb_wsi::get_surface_capabilities(VkIcdSurfaceBase *surface_,
905 VkSurfaceCapabilitiesKHR &capabilities) const
906 {
907 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
908 auto start_setup_result =
909 Implementation::start_setup(surface.connection, surface.window, false);
910 switch(start_setup_result.status)
911 {
912 case Implementation::Start_setup_results::Status::Bad_surface:
913 case Implementation::Start_setup_results::Status::No_support:
914 return VK_ERROR_SURFACE_LOST_KHR;
915 case Implementation::Start_setup_results::Status::Success:
916 capabilities = start_setup_result.capabilities;
917 return VK_SUCCESS;
918 }
919 assert(!"unreachable");
920 return {};
921 }
922
923 util::variant<VkResult, std::unique_ptr<Vulkan_swapchain>> Xcb_wsi::create_swapchain(
924 vulkan::Vulkan_device &device, const VkSwapchainCreateInfoKHR &create_info) const
925 {
926 auto &surface = *reinterpret_cast<Surface_type *>(create_info.surface);
927 auto swapchain = std::make_unique<Implementation::Swapchain>(
928 Implementation::start_setup(surface.connection, surface.window, true),
929 surface.connection,
930 surface.window,
931 create_info);
932 switch(swapchain->status)
933 {
934 case Implementation::Swapchain::Status::Setup_failed:
935 case Implementation::Swapchain::Status::No_surface:
936 return VK_ERROR_SURFACE_LOST_KHR;
937 case Implementation::Swapchain::Status::Good:
938 case Implementation::Swapchain::Status::Out_of_date: // we'll return out of date later
939 return std::move(swapchain);
940 }
941 assert(!"unreachable");
942 return {};
943 }
944
945 const Xcb_wsi &Xcb_wsi::get() noexcept
946 {
947 static const Xcb_wsi retval{};
948 return retval;
949 }
950 }
951 }
952 #endif
953
954 #ifdef VK_USE_PLATFORM_XLIB_KHR
955 #ifndef VK_USE_PLATFORM_XCB_KHR
956 #error can't Xlib WSI interface depends on XCB WSI interface for the implementation
957 #endif
958 #include <X11/Xlib-xcb.h>
959
960 namespace kazan
961 {
962 namespace vulkan_icd
963 {
964 struct Xlib_wsi::Implementation : public Xcb_wsi::Implementation
965 {
966 static VkIcdSurfaceXcb get_xcb_surface(const VkIcdSurfaceXlib &surface) noexcept
967 {
968 return VkIcdSurfaceXcb{
969 .base = {.platform = VK_ICD_WSI_PLATFORM_XCB},
970 .connection = XGetXCBConnection(surface.dpy),
971 .window = static_cast<xcb_window_t>(surface.window),
972 };
973 }
974 };
975
976 VkIcdSurfaceBase *Xlib_wsi::create_surface(const VkXlibSurfaceCreateInfoKHR &create_info) const
977 {
978 assert(create_info.sType == VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR);
979 assert(create_info.flags == 0);
980 return reinterpret_cast<VkIcdSurfaceBase *>(new Surface_type{
981 .base =
982 {
983 .platform = VK_ICD_WSI_PLATFORM_XLIB,
984 },
985 .dpy = create_info.dpy,
986 .window = create_info.window,
987 });
988 }
989
990 void Xlib_wsi::destroy_surface(VkIcdSurfaceBase *surface) const noexcept
991 {
992 delete reinterpret_cast<Surface_type *>(surface);
993 }
994
995 VkResult Xlib_wsi::get_surface_support(VkIcdSurfaceBase *surface_, bool &supported) const
996 {
997 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
998 auto xcb_surface = Implementation::get_xcb_surface(surface);
999 return Xcb_wsi::get().get_surface_support(reinterpret_cast<VkIcdSurfaceBase *>(&xcb_surface),
1000 supported);
1001 }
1002
1003 VkResult Xlib_wsi::get_surface_formats(VkIcdSurfaceBase *surface_,
1004 std::vector<VkSurfaceFormatKHR> &surface_formats) const
1005 {
1006 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
1007 auto xcb_surface = Implementation::get_xcb_surface(surface);
1008 return Xcb_wsi::get().get_surface_formats(reinterpret_cast<VkIcdSurfaceBase *>(&xcb_surface),
1009 surface_formats);
1010 }
1011
1012 VkResult Xlib_wsi::get_present_modes(VkIcdSurfaceBase *surface_,
1013 std::vector<VkPresentModeKHR> &present_modes) const
1014 {
1015 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
1016 auto xcb_surface = Implementation::get_xcb_surface(surface);
1017 return Xcb_wsi::get().get_present_modes(reinterpret_cast<VkIcdSurfaceBase *>(&xcb_surface),
1018 present_modes);
1019 }
1020
1021 VkResult Xlib_wsi::get_surface_capabilities(VkIcdSurfaceBase *surface_,
1022 VkSurfaceCapabilitiesKHR &capabilities) const
1023 {
1024 auto &surface = *reinterpret_cast<Surface_type *>(surface_);
1025 auto xcb_surface = Implementation::get_xcb_surface(surface);
1026 return Xcb_wsi::get().get_surface_capabilities(
1027 reinterpret_cast<VkIcdSurfaceBase *>(&xcb_surface), capabilities);
1028 }
1029
1030 util::variant<VkResult, std::unique_ptr<Vulkan_swapchain>> Xlib_wsi::create_swapchain(
1031 vulkan::Vulkan_device &device, const VkSwapchainCreateInfoKHR &create_info) const
1032 {
1033 assert(create_info.surface);
1034 auto &surface = *reinterpret_cast<Surface_type *>(create_info.surface);
1035 auto xcb_surface = Implementation::get_xcb_surface(surface);
1036 VkSwapchainCreateInfoKHR xcb_create_info = create_info;
1037 xcb_create_info.surface = reinterpret_cast<VkSurfaceKHR>(&xcb_surface);
1038 return Xcb_wsi::get().create_swapchain(device, xcb_create_info);
1039 }
1040
1041 const Xlib_wsi &Xlib_wsi::get() noexcept
1042 {
1043 static const Xlib_wsi retval{};
1044 return retval;
1045 }
1046 }
1047 }
1048 #endif