X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;ds=sidebyside;f=src%2Fvulkan%2Fwsi%2Fwsi_common_x11.c;h=2be3072587a8abfd7be6cb26ded871ea436707c2;hb=d0bc1ad3776bce7d4b356e2a3d8bdbb9ae64e588;hp=75aa83bb7b4415c6a9a948a17ef993fbcfd8ccf8;hpb=7629c00557c135aae4f0cdfbfa31002b43d8b31e;p=mesa.git diff --git a/src/vulkan/wsi/wsi_common_x11.c b/src/vulkan/wsi/wsi_common_x11.c index 75aa83bb7b4..2be3072587a 100644 --- a/src/vulkan/wsi/wsi_common_x11.c +++ b/src/vulkan/wsi/wsi_common_x11.c @@ -36,10 +36,12 @@ #include #include #include -#include +#include "drm-uapi/drm_fourcc.h" #include "util/hash_table.h" +#include "util/xmlconfig.h" #include "vk_util.h" +#include "vk_enum_to_str.h" #include "wsi_common_private.h" #include "wsi_common_x11.h" #include "wsi_common_queue.h" @@ -99,29 +101,22 @@ wsi_dri3_open(xcb_connection_t *conn, } static bool -wsi_x11_check_dri3_compatible(xcb_connection_t *conn, int local_fd) +wsi_x11_check_dri3_compatible(const struct wsi_device *wsi_dev, + xcb_connection_t *conn) { xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); xcb_screen_t *screen = screen_iter.data; int dri3_fd = wsi_dri3_open(conn, screen->root, None); - if (dri3_fd != -1) { - char *local_dev = drmGetRenderDeviceNameFromFd(local_fd); - char *dri3_dev = drmGetRenderDeviceNameFromFd(dri3_fd); - int ret; - - close(dri3_fd); + if (dri3_fd == -1) + return true; - ret = strcmp(local_dev, dri3_dev); + bool match = wsi_device_matches_drm_fd(wsi_dev, dri3_fd); - free(local_dev); - free(dri3_dev); + close(dri3_fd); - if (ret != 0) - return false; - } - return true; + return match; } static struct wsi_x11_connection * @@ -382,8 +377,6 @@ visual_has_alpha(xcb_visualtype_t *visual, unsigned depth) VkBool32 wsi_get_physical_device_xcb_presentation_support( struct wsi_device *wsi_device, uint32_t queueFamilyIndex, - int fd, - bool can_handle_different_gpu, xcb_connection_t* connection, xcb_visualid_t visual_id) { @@ -396,10 +389,6 @@ VkBool32 wsi_get_physical_device_xcb_presentation_support( if (!wsi_x11_check_for_dri3(wsi_conn)) return false; - if (!can_handle_different_gpu) - if (!wsi_x11_check_dri3_compatible(connection, fd)) - return false; - unsigned visual_depth; if (!connection_get_visualtype(connection, visual_id, &visual_depth)) return false; @@ -432,7 +421,6 @@ static VkResult x11_surface_get_support(VkIcdSurfaceBase *icd_surface, struct wsi_device *wsi_device, uint32_t queueFamilyIndex, - int local_fd, VkBool32* pSupported) { xcb_connection_t *conn = x11_surface_get_connection(icd_surface); @@ -463,8 +451,36 @@ x11_surface_get_support(VkIcdSurfaceBase *icd_surface, return VK_SUCCESS; } +static uint32_t +x11_get_min_image_count(struct wsi_device *wsi_device) +{ + if (wsi_device->x11.override_minImageCount) + return wsi_device->x11.override_minImageCount; + + /* For IMMEDIATE and FIFO, most games work in a pipelined manner where the + * can produce frames at a rate of 1/MAX(CPU duration, GPU duration), but + * the render latency is CPU duration + GPU duration. + * + * This means that with scanout from pageflipping we need 3 frames to run + * full speed: + * 1) CPU rendering work + * 2) GPU rendering work + * 3) scanout + * + * Once we have a nonblocking acquire that returns a semaphore we can merge + * 1 and 3. Hence the ideal implementation needs only 2 images, but games + * cannot tellwe currently do not have an ideal implementation and that + * hence they need to allocate 3 images. So let us do it for them. + * + * This is a tradeoff as it uses more memory than needed for non-fullscreen + * and non-performance intensive applications. + */ + return 3; +} + static VkResult x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface, + struct wsi_device *wsi_device, VkSurfaceCapabilitiesKHR *caps) { xcb_connection_t *conn = x11_surface_get_connection(icd_surface); @@ -496,10 +512,12 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface, * to come back from the compositor. In that case, we don't know the * size of the window so we just return valid "I don't know" stuff. */ - caps->currentExtent = (VkExtent2D) { -1, -1 }; + caps->currentExtent = (VkExtent2D) { UINT32_MAX, UINT32_MAX }; caps->minImageExtent = (VkExtent2D) { 1, 1 }; - /* This is the maximum supported size on Intel */ - caps->maxImageExtent = (VkExtent2D) { 1 << 14, 1 << 14 }; + caps->maxImageExtent = (VkExtent2D) { + wsi_device->maxImageDimension2D, + wsi_device->maxImageDimension2D, + }; } free(err); free(geom); @@ -512,13 +530,7 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface, VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; } - /* For true mailbox mode, we need at least 4 images: - * 1) One to scan out from - * 2) One to have queued for scan-out - * 3) One to be currently held by the X server - * 4) One to render to - */ - caps->minImageCount = 2; + caps->minImageCount = x11_get_min_image_count(wsi_device); /* There is no real maximum */ caps->maxImageCount = 0; @@ -529,6 +541,7 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface, VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | + VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; return VK_SUCCESS; @@ -536,12 +549,47 @@ x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface, static VkResult x11_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface, + struct wsi_device *wsi_device, const void *info_next, VkSurfaceCapabilities2KHR *caps) { assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR); - return x11_surface_get_capabilities(icd_surface, &caps->surfaceCapabilities); + VkResult result = + x11_surface_get_capabilities(icd_surface, wsi_device, + &caps->surfaceCapabilities); + + vk_foreach_struct(ext, caps->pNext) { + switch (ext->sType) { + case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: { + VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext; + protected->supportsProtected = VK_FALSE; + break; + } + + default: + /* Ignored */ + break; + } + } + + return result; +} + +static void +get_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats) +{ + memcpy(sorted_formats, formats, sizeof(formats)); + + if (wsi_device->force_bgra8_unorm_first) { + for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) { + if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) { + sorted_formats[i] = sorted_formats[0]; + sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM; + break; + } + } + } } static VkResult @@ -552,9 +600,12 @@ x11_surface_get_formats(VkIcdSurfaceBase *surface, { VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount); - for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) { + VkFormat sorted_formats[ARRAY_SIZE(formats)]; + get_sorted_vk_formats(wsi_device, sorted_formats); + + for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) { vk_outarray_append(&out, f) { - f->format = formats[i]; + f->format = sorted_formats[i]; f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; } } @@ -571,10 +622,13 @@ x11_surface_get_formats2(VkIcdSurfaceBase *surface, { VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount); - for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) { + VkFormat sorted_formats[ARRAY_SIZE(formats)]; + get_sorted_vk_formats(wsi_device, sorted_formats); + + for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) { vk_outarray_append(&out, f) { assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR); - f->surfaceFormat.format = formats[i]; + f->surfaceFormat.format = sorted_formats[i]; f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; } } @@ -599,6 +653,43 @@ x11_surface_get_present_modes(VkIcdSurfaceBase *surface, VK_INCOMPLETE : VK_SUCCESS; } +static VkResult +x11_surface_get_present_rectangles(VkIcdSurfaceBase *icd_surface, + struct wsi_device *wsi_device, + uint32_t* pRectCount, + VkRect2D* pRects) +{ + xcb_connection_t *conn = x11_surface_get_connection(icd_surface); + xcb_window_t window = x11_surface_get_window(icd_surface); + VK_OUTARRAY_MAKE(out, pRects, pRectCount); + + vk_outarray_append(&out, rect) { + xcb_generic_error_t *err = NULL; + xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(conn, window); + xcb_get_geometry_reply_t *geom = + xcb_get_geometry_reply(conn, geom_cookie, &err); + free(err); + if (geom) { + *rect = (VkRect2D) { + .offset = { 0, 0 }, + .extent = { geom->width, geom->height }, + }; + } else { + /* This can happen if the client didn't wait for the configure event + * to come back from the compositor. In that case, we don't know the + * size of the window so we just return valid "I don't know" stuff. + */ + *rect = (VkRect2D) { + .offset = { 0, 0 }, + .extent = { UINT32_MAX, UINT32_MAX }, + }; + } + free(geom); + } + + return vk_outarray_status(&out); +} + VkResult wsi_create_xcb_surface(const VkAllocationCallbacks *pAllocator, const VkXcbSurfaceCreateInfoKHR *pCreateInfo, VkSurfaceKHR *pSurface) @@ -661,8 +752,10 @@ struct x11_swapchain { uint64_t send_sbc; uint64_t last_present_msc; uint32_t stamp; + int sent_image_count; - bool threaded; + bool has_present_queue; + bool has_acquire_queue; VkResult status; xcb_present_complete_mode_t last_present_mode; struct wsi_queue present_queue; @@ -671,6 +764,8 @@ struct x11_swapchain { struct x11_image images[0]; }; +VK_DEFINE_NONDISP_HANDLE_CASTS(x11_swapchain, base.base, VkSwapchainKHR, + VK_OBJECT_TYPE_SWAPCHAIN_KHR) /** * Update the swapchain status with the result of an operation, and return @@ -682,7 +777,8 @@ struct x11_swapchain { * this has not been seen, success will be returned. */ static VkResult -x11_swapchain_result(struct x11_swapchain *chain, VkResult result) +_x11_swapchain_result(struct x11_swapchain *chain, VkResult result, + const char *file, int line) { /* Prioritise returning existing errors for consistency. */ if (chain->status < 0) @@ -690,6 +786,10 @@ x11_swapchain_result(struct x11_swapchain *chain, VkResult result) /* If we have a new error, mark it as permanent on the chain and return. */ if (result < 0) { +#ifndef NDEBUG + fprintf(stderr, "%s:%d: Swapchain status changed to %s\n", + file, line, vk_Result_to_str(result)); +#endif chain->status = result; return result; } @@ -702,6 +802,12 @@ x11_swapchain_result(struct x11_swapchain *chain, VkResult result) * and is always returned rather than success. */ if (result == VK_SUBOPTIMAL_KHR) { +#ifndef NDEBUG + if (chain->status != VK_SUBOPTIMAL_KHR) { + fprintf(stderr, "%s:%d: Swapchain status changed to %s\n", + file, line, vk_Result_to_str(result)); + } +#endif chain->status = result; return result; } @@ -709,6 +815,8 @@ x11_swapchain_result(struct x11_swapchain *chain, VkResult result) /* No changes, so return the last status. */ return chain->status; } +#define x11_swapchain_result(chain, result) \ + _x11_swapchain_result(chain, result, __FILE__, __LINE__) static struct wsi_image * x11_get_wsi_image(struct wsi_swapchain *wsi_chain, uint32_t image_index) @@ -741,7 +849,9 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain, for (unsigned i = 0; i < chain->base.image_count; i++) { if (chain->images[i].pixmap == idle->pixmap) { chain->images[i].busy = false; - if (chain->threaded) + chain->sent_image_count--; + assert(chain->sent_image_count >= 0); + if (chain->has_acquire_queue) wsi_queue_push(&chain->acquire_queue, i); break; } @@ -786,19 +896,9 @@ x11_handle_dri3_present_event(struct x11_swapchain *chain, } -static uint64_t wsi_get_current_time(void) -{ - uint64_t current_time; - struct timespec tv; - - clock_gettime(CLOCK_MONOTONIC, &tv); - current_time = tv.tv_nsec + tv.tv_sec*1000000000ull; - return current_time; -} - static uint64_t wsi_get_absolute_timeout(uint64_t timeout) { - uint64_t current_time = wsi_get_current_time(); + uint64_t current_time = wsi_common_get_current_time(); timeout = MIN2(UINT64_MAX - current_time, timeout); @@ -849,7 +949,7 @@ x11_acquire_next_image_poll_x11(struct x11_swapchain *chain, /* If a non-special event happens, the fd will still * poll. So recalculate the timeout now just in case. */ - uint64_t current_time = wsi_get_current_time(); + uint64_t current_time = wsi_common_get_current_time(); if (atimeout > current_time) timeout = atimeout - current_time; else @@ -872,7 +972,7 @@ static VkResult x11_acquire_next_image_from_queue(struct x11_swapchain *chain, uint32_t *image_index_out, uint64_t timeout) { - assert(chain->threaded); + assert(chain->has_acquire_queue); uint32_t image_index; VkResult result = wsi_queue_pull(&chain->acquire_queue, @@ -896,8 +996,8 @@ x11_acquire_next_image_from_queue(struct x11_swapchain *chain, } static VkResult -x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, - uint32_t target_msc) +x11_present_to_x11_dri3(struct x11_swapchain *chain, uint32_t image_index, + uint32_t target_msc) { struct x11_image *image = &chain->images[image_index]; @@ -916,9 +1016,26 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, options |= XCB_PRESENT_OPTION_SUBOPTIMAL; #endif + /* Poll for any available event and update the swapchain status. This could + * update the status of the swapchain to SUBOPTIMAL or OUT_OF_DATE if the + * associated X11 surface has been resized. + */ + xcb_generic_event_t *event; + while ((event = xcb_poll_for_special_event(chain->conn, chain->special_event))) { + VkResult result = x11_handle_dri3_present_event(chain, (void *)event); + free(event); + if (result < 0) + return x11_swapchain_result(chain, result); + x11_swapchain_result(chain, result); + } + xshmfence_reset(image->shm_fence); + ++chain->sent_image_count; + assert(chain->sent_image_count <= chain->base.image_count); + ++chain->send_sbc; + xcb_void_cookie_t cookie = xcb_present_pixmap(chain->conn, chain->window, @@ -936,13 +1053,47 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, divisor, remainder, 0, NULL); xcb_discard_reply(chain->conn, cookie.sequence); - image->busy = true; xcb_flush(chain->conn); return x11_swapchain_result(chain, VK_SUCCESS); } +static VkResult +x11_present_to_x11_sw(struct x11_swapchain *chain, uint32_t image_index, + uint32_t target_msc) +{ + struct x11_image *image = &chain->images[image_index]; + + xcb_void_cookie_t cookie; + void *myptr; + chain->base.wsi->MapMemory(chain->base.device, + image->base.memory, + 0, 0, 0, &myptr); + + cookie = xcb_put_image(chain->conn, XCB_IMAGE_FORMAT_Z_PIXMAP, + chain->window, + chain->gc, + image->base.row_pitches[0] / 4, + chain->extent.height, + 0,0,0,24, + image->base.row_pitches[0] * chain->extent.height, + myptr); + + chain->base.wsi->UnmapMemory(chain->base.device, image->base.memory); + xcb_discard_reply(chain->conn, cookie.sequence); + xcb_flush(chain->conn); + return x11_swapchain_result(chain, VK_SUCCESS); +} +static VkResult +x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index, + uint32_t target_msc) +{ + if (chain->base.wsi->sw) + return x11_present_to_x11_sw(chain, image_index, target_msc); + return x11_present_to_x11_dri3(chain, image_index, target_msc); +} + static VkResult x11_acquire_next_image(struct wsi_swapchain *anv_chain, const VkAcquireNextImageInfoKHR *info, @@ -951,7 +1102,15 @@ x11_acquire_next_image(struct wsi_swapchain *anv_chain, struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain; uint64_t timeout = info->timeout; - if (chain->threaded) { + /* If the swapchain is in an error state, don't go any further. */ + if (chain->status < 0) + return chain->status; + + if (chain->base.wsi->sw) { + *image_index = 0; + return VK_SUCCESS; + } + if (chain->has_acquire_queue) { return x11_acquire_next_image_from_queue(chain, image_index, timeout); } else { return x11_acquire_next_image_poll_x11(chain, image_index, timeout); @@ -965,7 +1124,12 @@ x11_queue_present(struct wsi_swapchain *anv_chain, { struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain; - if (chain->threaded) { + /* If the swapchain is in an error state, don't go any further. */ + if (chain->status < 0) + return chain->status; + + chain->images[image_index].busy = true; + if (chain->has_present_queue) { wsi_queue_push(&chain->present_queue, image_index); return chain->status; } else { @@ -979,8 +1143,7 @@ x11_manage_fifo_queues(void *state) struct x11_swapchain *chain = state; VkResult result = VK_SUCCESS; - assert(chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR); - + assert(chain->has_present_queue); while (chain->status >= 0) { /* It should be safe to unconditionally block here. Later in the loop * we blocks until the previous present has landed on-screen. At that @@ -1000,29 +1163,45 @@ x11_manage_fifo_queues(void *state) return NULL; } - uint64_t target_msc = chain->last_present_msc + 1; + if (chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR) { + result = chain->base.wsi->WaitForFences(chain->base.device, 1, + &chain->base.fences[image_index], + true, UINT64_MAX); + if (result != VK_SUCCESS) { + result = VK_ERROR_OUT_OF_DATE_KHR; + goto fail; + } + } + + uint64_t target_msc = 0; + if (chain->has_acquire_queue) + target_msc = chain->last_present_msc + 1; + result = x11_present_to_x11(chain, image_index, target_msc); if (result < 0) goto fail; - while (chain->last_present_msc < target_msc) { - xcb_generic_event_t *event = - xcb_wait_for_special_event(chain->conn, chain->special_event); - if (!event) { - result = VK_ERROR_OUT_OF_DATE_KHR; - goto fail; + if (chain->has_acquire_queue) { + while (chain->last_present_msc < target_msc) { + xcb_generic_event_t *event = + xcb_wait_for_special_event(chain->conn, chain->special_event); + if (!event) { + result = VK_ERROR_OUT_OF_DATE_KHR; + goto fail; + } + + result = x11_handle_dri3_present_event(chain, (void *)event); + free(event); + if (result < 0) + goto fail; } - - result = x11_handle_dri3_present_event(chain, (void *)event); - free(event); - if (result < 0) - goto fail; } } fail: x11_swapchain_result(chain, result); - wsi_queue_push(&chain->acquire_queue, UINT32_MAX); + if (chain->has_acquire_queue) + wsi_queue_push(&chain->acquire_queue, UINT32_MAX); return NULL; } @@ -1050,6 +1229,10 @@ x11_image_init(VkDevice device_h, struct x11_swapchain *chain, if (result < 0) return result; + if (chain->base.wsi->sw) { + image->busy = false; + return VK_SUCCESS; + } image->pixmap = xcb_generate_id(chain->conn); #ifdef HAVE_DRI3_MODIFIERS @@ -1138,12 +1321,14 @@ x11_image_finish(struct x11_swapchain *chain, { xcb_void_cookie_t cookie; - cookie = xcb_sync_destroy_fence(chain->conn, image->sync_fence); - xcb_discard_reply(chain->conn, cookie.sequence); - xshmfence_unmap_shm(image->shm_fence); + if (!chain->base.wsi->sw) { + cookie = xcb_sync_destroy_fence(chain->conn, image->sync_fence); + xcb_discard_reply(chain->conn, cookie.sequence); + xshmfence_unmap_shm(image->shm_fence); - cookie = xcb_free_pixmap(chain->conn, image->pixmap); - xcb_discard_reply(chain->conn, cookie.sequence); + cookie = xcb_free_pixmap(chain->conn, image->pixmap); + xcb_discard_reply(chain->conn, cookie.sequence); + } wsi_destroy_image(&chain->base, &image->base); } @@ -1232,12 +1417,14 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain, struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain; xcb_void_cookie_t cookie; - if (chain->threaded) { + if (chain->has_present_queue) { chain->status = VK_ERROR_OUT_OF_DATE_KHR; /* Push a UINT32_MAX to wake up the manager */ wsi_queue_push(&chain->present_queue, UINT32_MAX); pthread_join(chain->queue_manager, NULL); - wsi_queue_destroy(&chain->acquire_queue); + + if (chain->has_acquire_queue) + wsi_queue_destroy(&chain->acquire_queue); wsi_queue_destroy(&chain->present_queue); } @@ -1257,11 +1444,37 @@ x11_swapchain_destroy(struct wsi_swapchain *anv_chain, return VK_SUCCESS; } +static void +wsi_x11_set_adaptive_sync_property(xcb_connection_t *conn, + xcb_drawable_t drawable, + uint32_t state) +{ + static char const name[] = "_VARIABLE_REFRESH"; + xcb_intern_atom_cookie_t cookie; + xcb_intern_atom_reply_t* reply; + xcb_void_cookie_t check; + + cookie = xcb_intern_atom(conn, 0, strlen(name), name); + reply = xcb_intern_atom_reply(conn, cookie, NULL); + if (reply == NULL) + return; + + if (state) + check = xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE, + drawable, reply->atom, + XCB_ATOM_CARDINAL, 32, 1, &state); + else + check = xcb_delete_property_checked(conn, drawable, reply->atom); + + xcb_discard_reply(conn, check.sequence); + free(reply); +} + + static VkResult x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, VkDevice device, struct wsi_device *wsi_device, - int local_fd, const VkSwapchainCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks* pAllocator, struct wsi_swapchain **swapchain_out) @@ -1269,10 +1482,17 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, struct x11_swapchain *chain; xcb_void_cookie_t cookie; VkResult result; + VkPresentModeKHR present_mode = wsi_swapchain_get_present_mode(wsi_device, pCreateInfo); assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); - const unsigned num_images = pCreateInfo->minImageCount; + unsigned num_images = pCreateInfo->minImageCount; + if (wsi_device->x11.strict_imageCount) + num_images = pCreateInfo->minImageCount; + else if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + num_images = MAX2(num_images, 5); + else if (wsi_device->x11.ensure_minImageCount) + num_images = MAX2(num_images, x11_get_min_image_count(wsi_device)); xcb_connection_t *conn = x11_surface_get_connection(icd_surface); struct wsi_x11_connection *wsi_conn = @@ -1304,15 +1524,17 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, chain->base.get_wsi_image = x11_get_wsi_image; chain->base.acquire_next_image = x11_acquire_next_image; chain->base.queue_present = x11_queue_present; - chain->base.present_mode = pCreateInfo->presentMode; + chain->base.present_mode = present_mode; chain->base.image_count = num_images; chain->conn = conn; chain->window = window; chain->depth = bit_depth; chain->extent = pCreateInfo->imageExtent; chain->send_sbc = 0; + chain->sent_image_count = 0; chain->last_present_msc = 0; - chain->threaded = false; + chain->has_acquire_queue = false; + chain->has_present_queue = false; chain->status = VK_SUCCESS; chain->has_dri3_modifiers = wsi_conn->has_dri3_modifiers; @@ -1322,14 +1544,15 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, * mode which provokes reallocation when anything changes, to make * sure we have the most optimal allocation. */ - struct x11_swapchain *old_chain = (void *)(intptr_t) pCreateInfo->oldSwapchain; + VK_FROM_HANDLE(x11_swapchain, old_chain, pCreateInfo->oldSwapchain); if (old_chain) chain->last_present_mode = old_chain->last_present_mode; else chain->last_present_mode = XCB_PRESENT_COMPLETE_MODE_COPY; - if (!wsi_x11_check_dri3_compatible(conn, local_fd)) - chain->base.use_prime_blit = true; + if (!wsi_device->sw) + if (!wsi_x11_check_dri3_compatible(wsi_device, conn)) + chain->base.use_prime_blit = true; chain->event_id = xcb_generate_id(chain->conn); xcb_present_select_input(chain->conn, chain->event_id, chain->window, @@ -1377,39 +1600,55 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface, goto fail_init_images; } - if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) { - chain->threaded = true; + if ((chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR || + chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR) && !chain->base.wsi->sw) { + chain->has_present_queue = true; /* Initialize our queues. We make them base.image_count + 1 because we will * occasionally use UINT32_MAX to signal the other thread that an error * has occurred and we don't want an overflow. */ int ret; - ret = wsi_queue_init(&chain->acquire_queue, chain->base.image_count + 1); - if (ret) { - goto fail_init_images; - } - ret = wsi_queue_init(&chain->present_queue, chain->base.image_count + 1); if (ret) { - wsi_queue_destroy(&chain->acquire_queue); goto fail_init_images; } - for (unsigned i = 0; i < chain->base.image_count; i++) - wsi_queue_push(&chain->acquire_queue, i); + if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR) { + chain->has_acquire_queue = true; + + ret = wsi_queue_init(&chain->acquire_queue, chain->base.image_count + 1); + if (ret) { + wsi_queue_destroy(&chain->present_queue); + goto fail_init_images; + } + + for (unsigned i = 0; i < chain->base.image_count; i++) + wsi_queue_push(&chain->acquire_queue, i); + } ret = pthread_create(&chain->queue_manager, NULL, x11_manage_fifo_queues, chain); if (ret) { wsi_queue_destroy(&chain->present_queue); - wsi_queue_destroy(&chain->acquire_queue); + if (chain->has_acquire_queue) + wsi_queue_destroy(&chain->acquire_queue); + goto fail_init_images; } } + assert(chain->has_present_queue || !chain->has_acquire_queue); + for (int i = 0; i < ARRAY_SIZE(modifiers); i++) vk_free(pAllocator, modifiers[i]); + + /* It is safe to set it here as only one swapchain can be associated with + * the window, and swapchain creation does the association. At this point + * we know the creation is going to succeed. */ + wsi_x11_set_adaptive_sync_property(conn, window, + wsi_device->enable_adaptive_sync); + *swapchain_out = &chain->base; return VK_SUCCESS; @@ -1434,7 +1673,8 @@ fail_alloc: VkResult wsi_x11_init_wsi(struct wsi_device *wsi_device, - const VkAllocationCallbacks *alloc) + const VkAllocationCallbacks *alloc, + const struct driOptionCache *dri_options) { struct wsi_x11 *wsi; VkResult result; @@ -1465,11 +1705,28 @@ wsi_x11_init_wsi(struct wsi_device *wsi_device, goto fail_mutex; } + if (dri_options) { + if (driCheckOption(dri_options, "vk_x11_override_min_image_count", DRI_INT)) { + wsi_device->x11.override_minImageCount = + driQueryOptioni(dri_options, "vk_x11_override_min_image_count"); + } + if (driCheckOption(dri_options, "vk_x11_strict_image_count", DRI_BOOL)) { + wsi_device->x11.strict_imageCount = + driQueryOptionb(dri_options, "vk_x11_strict_image_count"); + } + if (driCheckOption(dri_options, "vk_x11_ensure_min_image_count", DRI_BOOL)) { + wsi_device->x11.ensure_minImageCount = + driQueryOptionb(dri_options, "vk_x11_ensure_min_image_count"); + } + + } + wsi->base.get_support = x11_surface_get_support; wsi->base.get_capabilities2 = x11_surface_get_capabilities2; wsi->base.get_formats = x11_surface_get_formats; wsi->base.get_formats2 = x11_surface_get_formats2; wsi->base.get_present_modes = x11_surface_get_present_modes; + wsi->base.get_present_rectangles = x11_surface_get_present_rectangles; wsi->base.create_swapchain = x11_surface_create_swapchain; wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = &wsi->base; @@ -1496,7 +1753,6 @@ wsi_x11_finish_wsi(struct wsi_device *wsi_device, (struct wsi_x11 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB]; if (wsi) { - struct hash_entry *entry; hash_table_foreach(wsi->connections, entry) wsi_x11_connection_destroy(wsi_device, entry->data);