Path: blob/21.2-virgl/src/vulkan/wsi/wsi_common_x11.c
7417 views
/*1* Copyright © 2015 Intel Corporation2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223#include <X11/Xlib-xcb.h>24#include <X11/xshmfence.h>25#include <xcb/xcb.h>26#include <xcb/dri3.h>27#include <xcb/present.h>2829#include "util/macros.h"30#include <stdlib.h>31#include <stdio.h>32#include <unistd.h>33#include <errno.h>34#include <string.h>35#include <fcntl.h>36#include <poll.h>37#include <xf86drm.h>38#include "drm-uapi/drm_fourcc.h"39#include "util/hash_table.h"40#include "util/u_thread.h"41#include "util/xmlconfig.h"4243#include "vk_util.h"44#include "vk_enum_to_str.h"45#include "wsi_common_private.h"46#include "wsi_common_x11.h"47#include "wsi_common_queue.h"4849struct wsi_x11_connection {50bool has_dri3;51bool has_dri3_modifiers;52bool has_present;53bool is_proprietary_x11;54bool is_xwayland;55};5657struct wsi_x11 {58struct wsi_interface base;5960pthread_mutex_t mutex;61/* Hash table of xcb_connection -> wsi_x11_connection mappings */62struct hash_table *connections;63};646566/** wsi_dri3_open67*68* Wrapper around xcb_dri3_open69*/70static int71wsi_dri3_open(xcb_connection_t *conn,72xcb_window_t root,73uint32_t provider)74{75xcb_dri3_open_cookie_t cookie;76xcb_dri3_open_reply_t *reply;77int fd;7879cookie = xcb_dri3_open(conn,80root,81provider);8283reply = xcb_dri3_open_reply(conn, cookie, NULL);84if (!reply)85return -1;8687if (reply->nfd != 1) {88free(reply);89return -1;90}9192fd = xcb_dri3_open_reply_fds(conn, reply)[0];93free(reply);94fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);9596return fd;97}9899static bool100wsi_x11_check_dri3_compatible(const struct wsi_device *wsi_dev,101xcb_connection_t *conn)102{103xcb_screen_iterator_t screen_iter =104xcb_setup_roots_iterator(xcb_get_setup(conn));105xcb_screen_t *screen = screen_iter.data;106107int dri3_fd = wsi_dri3_open(conn, screen->root, None);108if (dri3_fd == -1)109return true;110111bool match = wsi_device_matches_drm_fd(wsi_dev, dri3_fd);112113close(dri3_fd);114115return match;116}117118static bool119wsi_x11_detect_xwayland(xcb_connection_t *conn)120{121xcb_randr_query_version_cookie_t ver_cookie =122xcb_randr_query_version_unchecked(conn, 1, 3);123xcb_randr_query_version_reply_t *ver_reply =124xcb_randr_query_version_reply(conn, ver_cookie, NULL);125bool has_randr_v1_3 = ver_reply && (ver_reply->major_version > 1 ||126ver_reply->minor_version >= 3);127free(ver_reply);128129if (!has_randr_v1_3)130return false;131132const xcb_setup_t *setup = xcb_get_setup(conn);133xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);134135xcb_randr_get_screen_resources_current_cookie_t gsr_cookie =136xcb_randr_get_screen_resources_current_unchecked(conn, iter.data->root);137xcb_randr_get_screen_resources_current_reply_t *gsr_reply =138xcb_randr_get_screen_resources_current_reply(conn, gsr_cookie, NULL);139140if (!gsr_reply || gsr_reply->num_outputs == 0) {141free(gsr_reply);142return false;143}144145xcb_randr_output_t *randr_outputs =146xcb_randr_get_screen_resources_current_outputs(gsr_reply);147xcb_randr_get_output_info_cookie_t goi_cookie =148xcb_randr_get_output_info(conn, randr_outputs[0], gsr_reply->config_timestamp);149free(gsr_reply);150151xcb_randr_get_output_info_reply_t *goi_reply =152xcb_randr_get_output_info_reply(conn, goi_cookie, NULL);153if (!goi_reply) {154return false;155}156157char *output_name = (char*)xcb_randr_get_output_info_name(goi_reply);158bool is_xwayland = output_name && strncmp(output_name, "XWAYLAND", 8) == 0;159free(goi_reply);160161return is_xwayland;162}163164static struct wsi_x11_connection *165wsi_x11_connection_create(struct wsi_device *wsi_dev,166xcb_connection_t *conn)167{168xcb_query_extension_cookie_t dri3_cookie, pres_cookie, randr_cookie, amd_cookie, nv_cookie;169xcb_query_extension_reply_t *dri3_reply, *pres_reply, *randr_reply, *amd_reply, *nv_reply;170bool has_dri3_v1_2 = false;171bool has_present_v1_2 = false;172173struct wsi_x11_connection *wsi_conn =174vk_alloc(&wsi_dev->instance_alloc, sizeof(*wsi_conn), 8,175VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);176if (!wsi_conn)177return NULL;178179dri3_cookie = xcb_query_extension(conn, 4, "DRI3");180pres_cookie = xcb_query_extension(conn, 7, "Present");181randr_cookie = xcb_query_extension(conn, 5, "RANDR");182183/* We try to be nice to users and emit a warning if they try to use a184* Vulkan application on a system without DRI3 enabled. However, this ends185* up spewing the warning when a user has, for example, both Intel186* integrated graphics and a discrete card with proprietary drivers and are187* running on the discrete card with the proprietary DDX. In this case, we188* really don't want to print the warning because it just confuses users.189* As a heuristic to detect this case, we check for a couple of proprietary190* X11 extensions.191*/192amd_cookie = xcb_query_extension(conn, 11, "ATIFGLRXDRI");193nv_cookie = xcb_query_extension(conn, 10, "NV-CONTROL");194195dri3_reply = xcb_query_extension_reply(conn, dri3_cookie, NULL);196pres_reply = xcb_query_extension_reply(conn, pres_cookie, NULL);197randr_reply = xcb_query_extension_reply(conn, randr_cookie, NULL);198amd_reply = xcb_query_extension_reply(conn, amd_cookie, NULL);199nv_reply = xcb_query_extension_reply(conn, nv_cookie, NULL);200if (!dri3_reply || !pres_reply) {201free(dri3_reply);202free(pres_reply);203free(randr_reply);204free(amd_reply);205free(nv_reply);206vk_free(&wsi_dev->instance_alloc, wsi_conn);207return NULL;208}209210wsi_conn->has_dri3 = dri3_reply->present != 0;211#ifdef HAVE_DRI3_MODIFIERS212if (wsi_conn->has_dri3) {213xcb_dri3_query_version_cookie_t ver_cookie;214xcb_dri3_query_version_reply_t *ver_reply;215216ver_cookie = xcb_dri3_query_version(conn, 1, 2);217ver_reply = xcb_dri3_query_version_reply(conn, ver_cookie, NULL);218has_dri3_v1_2 =219(ver_reply->major_version > 1 || ver_reply->minor_version >= 2);220free(ver_reply);221}222#endif223224wsi_conn->has_present = pres_reply->present != 0;225#ifdef HAVE_DRI3_MODIFIERS226if (wsi_conn->has_present) {227xcb_present_query_version_cookie_t ver_cookie;228xcb_present_query_version_reply_t *ver_reply;229230ver_cookie = xcb_present_query_version(conn, 1, 2);231ver_reply = xcb_present_query_version_reply(conn, ver_cookie, NULL);232has_present_v1_2 =233(ver_reply->major_version > 1 || ver_reply->minor_version >= 2);234free(ver_reply);235}236#endif237238if (randr_reply && randr_reply->present != 0)239wsi_conn->is_xwayland = wsi_x11_detect_xwayland(conn);240else241wsi_conn->is_xwayland = false;242243wsi_conn->has_dri3_modifiers = has_dri3_v1_2 && has_present_v1_2;244wsi_conn->is_proprietary_x11 = false;245if (amd_reply && amd_reply->present)246wsi_conn->is_proprietary_x11 = true;247if (nv_reply && nv_reply->present)248wsi_conn->is_proprietary_x11 = true;249250free(dri3_reply);251free(pres_reply);252free(randr_reply);253free(amd_reply);254free(nv_reply);255256return wsi_conn;257}258259static void260wsi_x11_connection_destroy(struct wsi_device *wsi_dev,261struct wsi_x11_connection *conn)262{263vk_free(&wsi_dev->instance_alloc, conn);264}265266static bool267wsi_x11_check_for_dri3(struct wsi_x11_connection *wsi_conn)268{269if (wsi_conn->has_dri3)270return true;271if (!wsi_conn->is_proprietary_x11) {272fprintf(stderr, "vulkan: No DRI3 support detected - required for presentation\n"273"Note: you can probably enable DRI3 in your Xorg config\n");274}275return false;276}277278static struct wsi_x11_connection *279wsi_x11_get_connection(struct wsi_device *wsi_dev,280xcb_connection_t *conn)281{282struct wsi_x11 *wsi =283(struct wsi_x11 *)wsi_dev->wsi[VK_ICD_WSI_PLATFORM_XCB];284285pthread_mutex_lock(&wsi->mutex);286287struct hash_entry *entry = _mesa_hash_table_search(wsi->connections, conn);288if (!entry) {289/* We're about to make a bunch of blocking calls. Let's drop the290* mutex for now so we don't block up too badly.291*/292pthread_mutex_unlock(&wsi->mutex);293294struct wsi_x11_connection *wsi_conn =295wsi_x11_connection_create(wsi_dev, conn);296if (!wsi_conn)297return NULL;298299pthread_mutex_lock(&wsi->mutex);300301entry = _mesa_hash_table_search(wsi->connections, conn);302if (entry) {303/* Oops, someone raced us to it */304wsi_x11_connection_destroy(wsi_dev, wsi_conn);305} else {306entry = _mesa_hash_table_insert(wsi->connections, conn, wsi_conn);307}308}309310pthread_mutex_unlock(&wsi->mutex);311312return entry->data;313}314315static const VkFormat formats[] = {316VK_FORMAT_B8G8R8A8_SRGB,317VK_FORMAT_B8G8R8A8_UNORM,318};319320static const VkPresentModeKHR present_modes[] = {321VK_PRESENT_MODE_IMMEDIATE_KHR,322VK_PRESENT_MODE_MAILBOX_KHR,323VK_PRESENT_MODE_FIFO_KHR,324VK_PRESENT_MODE_FIFO_RELAXED_KHR,325};326327static xcb_screen_t *328get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)329{330xcb_screen_iterator_t screen_iter =331xcb_setup_roots_iterator(xcb_get_setup(conn));332333for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {334if (screen_iter.data->root == root)335return screen_iter.data;336}337338return NULL;339}340341static xcb_visualtype_t *342screen_get_visualtype(xcb_screen_t *screen, xcb_visualid_t visual_id,343unsigned *depth)344{345xcb_depth_iterator_t depth_iter =346xcb_screen_allowed_depths_iterator(screen);347348for (; depth_iter.rem; xcb_depth_next (&depth_iter)) {349xcb_visualtype_iterator_t visual_iter =350xcb_depth_visuals_iterator (depth_iter.data);351352for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) {353if (visual_iter.data->visual_id == visual_id) {354if (depth)355*depth = depth_iter.data->depth;356return visual_iter.data;357}358}359}360361return NULL;362}363364static xcb_visualtype_t *365connection_get_visualtype(xcb_connection_t *conn, xcb_visualid_t visual_id,366unsigned *depth)367{368xcb_screen_iterator_t screen_iter =369xcb_setup_roots_iterator(xcb_get_setup(conn));370371/* For this we have to iterate over all of the screens which is rather372* annoying. Fortunately, there is probably only 1.373*/374for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {375xcb_visualtype_t *visual = screen_get_visualtype(screen_iter.data,376visual_id, depth);377if (visual)378return visual;379}380381return NULL;382}383384static xcb_visualtype_t *385get_visualtype_for_window(xcb_connection_t *conn, xcb_window_t window,386unsigned *depth)387{388xcb_query_tree_cookie_t tree_cookie;389xcb_get_window_attributes_cookie_t attrib_cookie;390xcb_query_tree_reply_t *tree;391xcb_get_window_attributes_reply_t *attrib;392393tree_cookie = xcb_query_tree(conn, window);394attrib_cookie = xcb_get_window_attributes(conn, window);395396tree = xcb_query_tree_reply(conn, tree_cookie, NULL);397attrib = xcb_get_window_attributes_reply(conn, attrib_cookie, NULL);398if (attrib == NULL || tree == NULL) {399free(attrib);400free(tree);401return NULL;402}403404xcb_window_t root = tree->root;405xcb_visualid_t visual_id = attrib->visual;406free(attrib);407free(tree);408409xcb_screen_t *screen = get_screen_for_root(conn, root);410if (screen == NULL)411return NULL;412413return screen_get_visualtype(screen, visual_id, depth);414}415416static bool417visual_has_alpha(xcb_visualtype_t *visual, unsigned depth)418{419uint32_t rgb_mask = visual->red_mask |420visual->green_mask |421visual->blue_mask;422423uint32_t all_mask = 0xffffffff >> (32 - depth);424425/* Do we have bits left over after RGB? */426return (all_mask & ~rgb_mask) != 0;427}428429VkBool32 wsi_get_physical_device_xcb_presentation_support(430struct wsi_device *wsi_device,431uint32_t queueFamilyIndex,432xcb_connection_t* connection,433xcb_visualid_t visual_id)434{435struct wsi_x11_connection *wsi_conn =436wsi_x11_get_connection(wsi_device, connection);437438if (!wsi_conn)439return false;440441if (!wsi_device->sw) {442if (!wsi_x11_check_for_dri3(wsi_conn))443return false;444}445446unsigned visual_depth;447if (!connection_get_visualtype(connection, visual_id, &visual_depth))448return false;449450if (visual_depth != 24 && visual_depth != 32)451return false;452453return true;454}455456static xcb_connection_t*457x11_surface_get_connection(VkIcdSurfaceBase *icd_surface)458{459if (icd_surface->platform == VK_ICD_WSI_PLATFORM_XLIB)460return XGetXCBConnection(((VkIcdSurfaceXlib *)icd_surface)->dpy);461else462return ((VkIcdSurfaceXcb *)icd_surface)->connection;463}464465static xcb_window_t466x11_surface_get_window(VkIcdSurfaceBase *icd_surface)467{468if (icd_surface->platform == VK_ICD_WSI_PLATFORM_XLIB)469return ((VkIcdSurfaceXlib *)icd_surface)->window;470else471return ((VkIcdSurfaceXcb *)icd_surface)->window;472}473474static VkResult475x11_surface_get_support(VkIcdSurfaceBase *icd_surface,476struct wsi_device *wsi_device,477uint32_t queueFamilyIndex,478VkBool32* pSupported)479{480xcb_connection_t *conn = x11_surface_get_connection(icd_surface);481xcb_window_t window = x11_surface_get_window(icd_surface);482483struct wsi_x11_connection *wsi_conn =484wsi_x11_get_connection(wsi_device, conn);485if (!wsi_conn)486return VK_ERROR_OUT_OF_HOST_MEMORY;487488if (!wsi_device->sw) {489if (!wsi_x11_check_for_dri3(wsi_conn)) {490*pSupported = false;491return VK_SUCCESS;492}493}494495unsigned visual_depth;496if (!get_visualtype_for_window(conn, window, &visual_depth)) {497*pSupported = false;498return VK_SUCCESS;499}500501if (visual_depth != 24 && visual_depth != 32) {502*pSupported = false;503return VK_SUCCESS;504}505506*pSupported = true;507return VK_SUCCESS;508}509510static uint32_t511x11_get_min_image_count(struct wsi_device *wsi_device)512{513if (wsi_device->x11.override_minImageCount)514return wsi_device->x11.override_minImageCount;515516/* For IMMEDIATE and FIFO, most games work in a pipelined manner where the517* can produce frames at a rate of 1/MAX(CPU duration, GPU duration), but518* the render latency is CPU duration + GPU duration.519*520* This means that with scanout from pageflipping we need 3 frames to run521* full speed:522* 1) CPU rendering work523* 2) GPU rendering work524* 3) scanout525*526* Once we have a nonblocking acquire that returns a semaphore we can merge527* 1 and 3. Hence the ideal implementation needs only 2 images, but games528* cannot tellwe currently do not have an ideal implementation and that529* hence they need to allocate 3 images. So let us do it for them.530*531* This is a tradeoff as it uses more memory than needed for non-fullscreen532* and non-performance intensive applications.533*/534return 3;535}536537static VkResult538x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,539struct wsi_device *wsi_device,540VkSurfaceCapabilitiesKHR *caps)541{542xcb_connection_t *conn = x11_surface_get_connection(icd_surface);543xcb_window_t window = x11_surface_get_window(icd_surface);544xcb_get_geometry_cookie_t geom_cookie;545xcb_generic_error_t *err;546xcb_get_geometry_reply_t *geom;547unsigned visual_depth;548549geom_cookie = xcb_get_geometry(conn, window);550551/* This does a round-trip. This is why we do get_geometry first and552* wait to read the reply until after we have a visual.553*/554xcb_visualtype_t *visual =555get_visualtype_for_window(conn, window, &visual_depth);556557if (!visual)558return VK_ERROR_SURFACE_LOST_KHR;559560geom = xcb_get_geometry_reply(conn, geom_cookie, &err);561if (geom) {562VkExtent2D extent = { geom->width, geom->height };563caps->currentExtent = extent;564caps->minImageExtent = extent;565caps->maxImageExtent = extent;566} else {567/* This can happen if the client didn't wait for the configure event568* to come back from the compositor. In that case, we don't know the569* size of the window so we just return valid "I don't know" stuff.570*/571caps->currentExtent = (VkExtent2D) { UINT32_MAX, UINT32_MAX };572caps->minImageExtent = (VkExtent2D) { 1, 1 };573caps->maxImageExtent = (VkExtent2D) {574wsi_device->maxImageDimension2D,575wsi_device->maxImageDimension2D,576};577}578free(err);579free(geom);580581if (visual_has_alpha(visual, visual_depth)) {582caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |583VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;584} else {585caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |586VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;587}588589caps->minImageCount = x11_get_min_image_count(wsi_device);590/* There is no real maximum */591caps->maxImageCount = 0;592593caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;594caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;595caps->maxImageArrayLayers = 1;596caps->supportedUsageFlags =597VK_IMAGE_USAGE_TRANSFER_SRC_BIT |598VK_IMAGE_USAGE_SAMPLED_BIT |599VK_IMAGE_USAGE_TRANSFER_DST_BIT |600VK_IMAGE_USAGE_STORAGE_BIT |601VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;602603return VK_SUCCESS;604}605606static VkResult607x11_surface_get_capabilities2(VkIcdSurfaceBase *icd_surface,608struct wsi_device *wsi_device,609const void *info_next,610VkSurfaceCapabilities2KHR *caps)611{612assert(caps->sType == VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR);613614VkResult result =615x11_surface_get_capabilities(icd_surface, wsi_device,616&caps->surfaceCapabilities);617618vk_foreach_struct(ext, caps->pNext) {619switch (ext->sType) {620case VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR: {621VkSurfaceProtectedCapabilitiesKHR *protected = (void *)ext;622protected->supportsProtected = VK_FALSE;623break;624}625626default:627/* Ignored */628break;629}630}631632return result;633}634635static void636get_sorted_vk_formats(struct wsi_device *wsi_device, VkFormat *sorted_formats)637{638memcpy(sorted_formats, formats, sizeof(formats));639640if (wsi_device->force_bgra8_unorm_first) {641for (unsigned i = 0; i < ARRAY_SIZE(formats); i++) {642if (sorted_formats[i] == VK_FORMAT_B8G8R8A8_UNORM) {643sorted_formats[i] = sorted_formats[0];644sorted_formats[0] = VK_FORMAT_B8G8R8A8_UNORM;645break;646}647}648}649}650651static VkResult652x11_surface_get_formats(VkIcdSurfaceBase *surface,653struct wsi_device *wsi_device,654uint32_t *pSurfaceFormatCount,655VkSurfaceFormatKHR *pSurfaceFormats)656{657VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount);658659VkFormat sorted_formats[ARRAY_SIZE(formats)];660get_sorted_vk_formats(wsi_device, sorted_formats);661662for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {663vk_outarray_append(&out, f) {664f->format = sorted_formats[i];665f->colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;666}667}668669return vk_outarray_status(&out);670}671672static VkResult673x11_surface_get_formats2(VkIcdSurfaceBase *surface,674struct wsi_device *wsi_device,675const void *info_next,676uint32_t *pSurfaceFormatCount,677VkSurfaceFormat2KHR *pSurfaceFormats)678{679VK_OUTARRAY_MAKE(out, pSurfaceFormats, pSurfaceFormatCount);680681VkFormat sorted_formats[ARRAY_SIZE(formats)];682get_sorted_vk_formats(wsi_device, sorted_formats);683684for (unsigned i = 0; i < ARRAY_SIZE(sorted_formats); i++) {685vk_outarray_append(&out, f) {686assert(f->sType == VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR);687f->surfaceFormat.format = sorted_formats[i];688f->surfaceFormat.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;689}690}691692return vk_outarray_status(&out);693}694695static VkResult696x11_surface_get_present_modes(VkIcdSurfaceBase *surface,697uint32_t *pPresentModeCount,698VkPresentModeKHR *pPresentModes)699{700if (pPresentModes == NULL) {701*pPresentModeCount = ARRAY_SIZE(present_modes);702return VK_SUCCESS;703}704705*pPresentModeCount = MIN2(*pPresentModeCount, ARRAY_SIZE(present_modes));706typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);707708return *pPresentModeCount < ARRAY_SIZE(present_modes) ?709VK_INCOMPLETE : VK_SUCCESS;710}711712static VkResult713x11_surface_get_present_rectangles(VkIcdSurfaceBase *icd_surface,714struct wsi_device *wsi_device,715uint32_t* pRectCount,716VkRect2D* pRects)717{718xcb_connection_t *conn = x11_surface_get_connection(icd_surface);719xcb_window_t window = x11_surface_get_window(icd_surface);720VK_OUTARRAY_MAKE(out, pRects, pRectCount);721722vk_outarray_append(&out, rect) {723xcb_generic_error_t *err = NULL;724xcb_get_geometry_cookie_t geom_cookie = xcb_get_geometry(conn, window);725xcb_get_geometry_reply_t *geom =726xcb_get_geometry_reply(conn, geom_cookie, &err);727free(err);728if (geom) {729*rect = (VkRect2D) {730.offset = { 0, 0 },731.extent = { geom->width, geom->height },732};733} else {734/* This can happen if the client didn't wait for the configure event735* to come back from the compositor. In that case, we don't know the736* size of the window so we just return valid "I don't know" stuff.737*/738*rect = (VkRect2D) {739.offset = { 0, 0 },740.extent = { UINT32_MAX, UINT32_MAX },741};742}743free(geom);744}745746return vk_outarray_status(&out);747}748749VkResult wsi_create_xcb_surface(const VkAllocationCallbacks *pAllocator,750const VkXcbSurfaceCreateInfoKHR *pCreateInfo,751VkSurfaceKHR *pSurface)752{753VkIcdSurfaceXcb *surface;754755surface = vk_alloc(pAllocator, sizeof *surface, 8,756VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);757if (surface == NULL)758return VK_ERROR_OUT_OF_HOST_MEMORY;759760surface->base.platform = VK_ICD_WSI_PLATFORM_XCB;761surface->connection = pCreateInfo->connection;762surface->window = pCreateInfo->window;763764*pSurface = VkIcdSurfaceBase_to_handle(&surface->base);765return VK_SUCCESS;766}767768VkResult wsi_create_xlib_surface(const VkAllocationCallbacks *pAllocator,769const VkXlibSurfaceCreateInfoKHR *pCreateInfo,770VkSurfaceKHR *pSurface)771{772VkIcdSurfaceXlib *surface;773774surface = vk_alloc(pAllocator, sizeof *surface, 8,775VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);776if (surface == NULL)777return VK_ERROR_OUT_OF_HOST_MEMORY;778779surface->base.platform = VK_ICD_WSI_PLATFORM_XLIB;780surface->dpy = pCreateInfo->dpy;781surface->window = pCreateInfo->window;782783*pSurface = VkIcdSurfaceBase_to_handle(&surface->base);784return VK_SUCCESS;785}786787struct x11_image {788struct wsi_image base;789xcb_pixmap_t pixmap;790bool busy;791bool present_queued;792struct xshmfence * shm_fence;793uint32_t sync_fence;794uint32_t serial;795};796797struct x11_swapchain {798struct wsi_swapchain base;799800bool has_dri3_modifiers;801802xcb_connection_t * conn;803xcb_window_t window;804xcb_gc_t gc;805uint32_t depth;806VkExtent2D extent;807808xcb_present_event_t event_id;809xcb_special_event_t * special_event;810uint64_t send_sbc;811uint64_t last_present_msc;812uint32_t stamp;813int sent_image_count;814815bool has_present_queue;816bool has_acquire_queue;817VkResult status;818bool copy_is_suboptimal;819struct wsi_queue present_queue;820struct wsi_queue acquire_queue;821pthread_t queue_manager;822823struct x11_image images[0];824};825VK_DEFINE_NONDISP_HANDLE_CASTS(x11_swapchain, base.base, VkSwapchainKHR,826VK_OBJECT_TYPE_SWAPCHAIN_KHR)827828/**829* Update the swapchain status with the result of an operation, and return830* the combined status. The chain status will eventually be returned from831* AcquireNextImage and QueuePresent.832*833* We make sure to 'stick' more pessimistic statuses: an out-of-date error834* is permanent once seen, and every subsequent call will return this. If835* this has not been seen, success will be returned.836*/837static VkResult838_x11_swapchain_result(struct x11_swapchain *chain, VkResult result,839const char *file, int line)840{841/* Prioritise returning existing errors for consistency. */842if (chain->status < 0)843return chain->status;844845/* If we have a new error, mark it as permanent on the chain and return. */846if (result < 0) {847#ifndef NDEBUG848fprintf(stderr, "%s:%d: Swapchain status changed to %s\n",849file, line, vk_Result_to_str(result));850#endif851chain->status = result;852return result;853}854855/* Return temporary errors, but don't persist them. */856if (result == VK_TIMEOUT || result == VK_NOT_READY)857return result;858859/* Suboptimal isn't an error, but is a status which sticks to the swapchain860* and is always returned rather than success.861*/862if (result == VK_SUBOPTIMAL_KHR) {863#ifndef NDEBUG864if (chain->status != VK_SUBOPTIMAL_KHR) {865fprintf(stderr, "%s:%d: Swapchain status changed to %s\n",866file, line, vk_Result_to_str(result));867}868#endif869chain->status = result;870return result;871}872873/* No changes, so return the last status. */874return chain->status;875}876#define x11_swapchain_result(chain, result) \877_x11_swapchain_result(chain, result, __FILE__, __LINE__)878879static struct wsi_image *880x11_get_wsi_image(struct wsi_swapchain *wsi_chain, uint32_t image_index)881{882struct x11_swapchain *chain = (struct x11_swapchain *)wsi_chain;883return &chain->images[image_index].base;884}885886/**887* Process an X11 Present event. Does not update chain->status.888*/889static VkResult890x11_handle_dri3_present_event(struct x11_swapchain *chain,891xcb_present_generic_event_t *event)892{893switch (event->evtype) {894case XCB_PRESENT_CONFIGURE_NOTIFY: {895xcb_present_configure_notify_event_t *config = (void *) event;896897if (config->width != chain->extent.width ||898config->height != chain->extent.height)899return VK_SUBOPTIMAL_KHR;900901break;902}903904case XCB_PRESENT_EVENT_IDLE_NOTIFY: {905xcb_present_idle_notify_event_t *idle = (void *) event;906907for (unsigned i = 0; i < chain->base.image_count; i++) {908if (chain->images[i].pixmap == idle->pixmap) {909chain->images[i].busy = false;910chain->sent_image_count--;911assert(chain->sent_image_count >= 0);912if (chain->has_acquire_queue)913wsi_queue_push(&chain->acquire_queue, i);914break;915}916}917918break;919}920921case XCB_PRESENT_EVENT_COMPLETE_NOTIFY: {922xcb_present_complete_notify_event_t *complete = (void *) event;923if (complete->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) {924unsigned i;925for (i = 0; i < chain->base.image_count; i++) {926struct x11_image *image = &chain->images[i];927if (image->present_queued && image->serial == complete->serial)928image->present_queued = false;929}930chain->last_present_msc = complete->msc;931}932933VkResult result = VK_SUCCESS;934switch (complete->mode) {935case XCB_PRESENT_COMPLETE_MODE_COPY:936if (chain->copy_is_suboptimal)937result = VK_SUBOPTIMAL_KHR;938break;939case XCB_PRESENT_COMPLETE_MODE_FLIP:940/* If we ever go from flipping to copying, the odds are very likely941* that we could reallocate in a more optimal way if we didn't have942* to care about scanout, so we always do this.943*/944chain->copy_is_suboptimal = true;945break;946#ifdef HAVE_DRI3_MODIFIERS947case XCB_PRESENT_COMPLETE_MODE_SUBOPTIMAL_COPY:948/* The winsys is now trying to flip directly and cannot due to our949* configuration. Request the user reallocate.950*/951result = VK_SUBOPTIMAL_KHR;952break;953#endif954default:955break;956}957958return result;959}960961default:962break;963}964965return VK_SUCCESS;966}967968969static uint64_t wsi_get_absolute_timeout(uint64_t timeout)970{971uint64_t current_time = wsi_common_get_current_time();972973timeout = MIN2(UINT64_MAX - current_time, timeout);974975return current_time + timeout;976}977978static VkResult979x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,980uint32_t *image_index, uint64_t timeout)981{982xcb_generic_event_t *event;983struct pollfd pfds;984uint64_t atimeout;985while (1) {986for (uint32_t i = 0; i < chain->base.image_count; i++) {987if (!chain->images[i].busy) {988/* We found a non-busy image */989xshmfence_await(chain->images[i].shm_fence);990*image_index = i;991chain->images[i].busy = true;992return x11_swapchain_result(chain, VK_SUCCESS);993}994}995996xcb_flush(chain->conn);997998if (timeout == UINT64_MAX) {999event = xcb_wait_for_special_event(chain->conn, chain->special_event);1000if (!event)1001return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);1002} else {1003event = xcb_poll_for_special_event(chain->conn, chain->special_event);1004if (!event) {1005int ret;1006if (timeout == 0)1007return x11_swapchain_result(chain, VK_NOT_READY);10081009atimeout = wsi_get_absolute_timeout(timeout);10101011pfds.fd = xcb_get_file_descriptor(chain->conn);1012pfds.events = POLLIN;1013ret = poll(&pfds, 1, timeout / 1000 / 1000);1014if (ret == 0)1015return x11_swapchain_result(chain, VK_TIMEOUT);1016if (ret == -1)1017return x11_swapchain_result(chain, VK_ERROR_OUT_OF_DATE_KHR);10181019/* If a non-special event happens, the fd will still1020* poll. So recalculate the timeout now just in case.1021*/1022uint64_t current_time = wsi_common_get_current_time();1023if (atimeout > current_time)1024timeout = atimeout - current_time;1025else1026timeout = 0;1027continue;1028}1029}10301031/* Update the swapchain status here. We may catch non-fatal errors here,1032* in which case we need to update the status and continue.1033*/1034VkResult result = x11_handle_dri3_present_event(chain, (void *)event);1035free(event);1036if (result < 0)1037return x11_swapchain_result(chain, result);1038}1039}10401041static VkResult1042x11_acquire_next_image_from_queue(struct x11_swapchain *chain,1043uint32_t *image_index_out, uint64_t timeout)1044{1045assert(chain->has_acquire_queue);10461047uint32_t image_index;1048VkResult result = wsi_queue_pull(&chain->acquire_queue,1049&image_index, timeout);1050if (result < 0 || result == VK_TIMEOUT) {1051/* On error, the thread has shut down, so safe to update chain->status.1052* Calling x11_swapchain_result with VK_TIMEOUT won't modify1053* chain->status so that is also safe.1054*/1055return x11_swapchain_result(chain, result);1056} else if (chain->status < 0) {1057return chain->status;1058}10591060assert(image_index < chain->base.image_count);1061xshmfence_await(chain->images[image_index].shm_fence);10621063*image_index_out = image_index;10641065return chain->status;1066}10671068static VkResult1069x11_present_to_x11_dri3(struct x11_swapchain *chain, uint32_t image_index,1070uint64_t target_msc)1071{1072struct x11_image *image = &chain->images[image_index];10731074assert(image_index < chain->base.image_count);10751076uint32_t options = XCB_PRESENT_OPTION_NONE;10771078int64_t divisor = 0;1079int64_t remainder = 0;10801081struct wsi_x11_connection *wsi_conn =1082wsi_x11_get_connection((struct wsi_device*)chain->base.wsi, chain->conn);1083if (!wsi_conn)1084return VK_ERROR_OUT_OF_HOST_MEMORY;10851086if (chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR ||1087(chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR &&1088wsi_conn->is_xwayland) ||1089chain->base.present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR)1090options |= XCB_PRESENT_OPTION_ASYNC;10911092#ifdef HAVE_DRI3_MODIFIERS1093if (chain->has_dri3_modifiers)1094options |= XCB_PRESENT_OPTION_SUBOPTIMAL;1095#endif10961097/* Poll for any available event and update the swapchain status. This could1098* update the status of the swapchain to SUBOPTIMAL or OUT_OF_DATE if the1099* associated X11 surface has been resized.1100*/1101xcb_generic_event_t *event;1102while ((event = xcb_poll_for_special_event(chain->conn, chain->special_event))) {1103VkResult result = x11_handle_dri3_present_event(chain, (void *)event);1104free(event);1105if (result < 0)1106return x11_swapchain_result(chain, result);1107x11_swapchain_result(chain, result);1108}11091110xshmfence_reset(image->shm_fence);11111112++chain->sent_image_count;1113assert(chain->sent_image_count <= chain->base.image_count);11141115++chain->send_sbc;1116image->present_queued = true;1117image->serial = (uint32_t) chain->send_sbc;11181119xcb_void_cookie_t cookie =1120xcb_present_pixmap(chain->conn,1121chain->window,1122image->pixmap,1123image->serial,11240, /* valid */11250, /* update */11260, /* x_off */11270, /* y_off */1128XCB_NONE, /* target_crtc */1129XCB_NONE,1130image->sync_fence,1131options,1132target_msc,1133divisor,1134remainder, 0, NULL);1135xcb_discard_reply(chain->conn, cookie.sequence);11361137xcb_flush(chain->conn);11381139return x11_swapchain_result(chain, VK_SUCCESS);1140}11411142static VkResult1143x11_present_to_x11_sw(struct x11_swapchain *chain, uint32_t image_index,1144uint64_t target_msc)1145{1146struct x11_image *image = &chain->images[image_index];11471148xcb_void_cookie_t cookie;1149void *myptr;1150chain->base.wsi->MapMemory(chain->base.device,1151image->base.memory,11520, 0, 0, &myptr);11531154cookie = xcb_put_image(chain->conn, XCB_IMAGE_FORMAT_Z_PIXMAP,1155chain->window,1156chain->gc,1157image->base.row_pitches[0] / 4,1158chain->extent.height,11590,0,0,24,1160image->base.row_pitches[0] * chain->extent.height,1161myptr);11621163chain->base.wsi->UnmapMemory(chain->base.device, image->base.memory);1164xcb_discard_reply(chain->conn, cookie.sequence);1165xcb_flush(chain->conn);1166return x11_swapchain_result(chain, VK_SUCCESS);1167}1168static VkResult1169x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,1170uint64_t target_msc)1171{1172if (chain->base.wsi->sw)1173return x11_present_to_x11_sw(chain, image_index, target_msc);1174return x11_present_to_x11_dri3(chain, image_index, target_msc);1175}11761177static VkResult1178x11_acquire_next_image(struct wsi_swapchain *anv_chain,1179const VkAcquireNextImageInfoKHR *info,1180uint32_t *image_index)1181{1182struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;1183uint64_t timeout = info->timeout;11841185/* If the swapchain is in an error state, don't go any further. */1186if (chain->status < 0)1187return chain->status;11881189if (chain->base.wsi->sw) {1190*image_index = 0;1191return VK_SUCCESS;1192}1193if (chain->has_acquire_queue) {1194return x11_acquire_next_image_from_queue(chain, image_index, timeout);1195} else {1196return x11_acquire_next_image_poll_x11(chain, image_index, timeout);1197}1198}11991200static VkResult1201x11_queue_present(struct wsi_swapchain *anv_chain,1202uint32_t image_index,1203const VkPresentRegionKHR *damage)1204{1205struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;12061207/* If the swapchain is in an error state, don't go any further. */1208if (chain->status < 0)1209return chain->status;12101211chain->images[image_index].busy = true;1212if (chain->has_present_queue) {1213wsi_queue_push(&chain->present_queue, image_index);1214return chain->status;1215} else {1216return x11_present_to_x11(chain, image_index, 0);1217}1218}12191220static void *1221x11_manage_fifo_queues(void *state)1222{1223struct x11_swapchain *chain = state;1224struct wsi_x11_connection *wsi_conn =1225wsi_x11_get_connection((struct wsi_device*)chain->base.wsi, chain->conn);1226VkResult result = VK_SUCCESS;12271228assert(chain->has_present_queue);12291230u_thread_setname("WSI swapchain queue");12311232while (chain->status >= 0) {1233/* We can block here unconditionally because after an image was sent to1234* the server (later on in this loop) we ensure at least one image is1235* acquirable by the consumer or wait there on such an event.1236*/1237uint32_t image_index = 0;1238result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX);1239assert(result != VK_TIMEOUT);1240if (result < 0) {1241goto fail;1242} else if (chain->status < 0) {1243/* The status can change underneath us if the swapchain is destroyed1244* from another thread.1245*/1246return NULL;1247}12481249if (chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR ||1250(chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR &&1251wsi_conn->is_xwayland)) {1252result = chain->base.wsi->WaitForFences(chain->base.device, 1,1253&chain->base.fences[image_index],1254true, UINT64_MAX);1255if (result != VK_SUCCESS) {1256result = VK_ERROR_OUT_OF_DATE_KHR;1257goto fail;1258}1259}12601261uint64_t target_msc = 0;1262if (chain->has_acquire_queue)1263target_msc = chain->last_present_msc + 1;12641265result = x11_present_to_x11(chain, image_index, target_msc);1266if (result < 0)1267goto fail;12681269if (chain->has_acquire_queue) {1270/* Wait for our presentation to occur and ensure we have at least one1271* image that can be acquired by the client afterwards. This ensures we1272* can pull on the present-queue on the next loop.1273*/1274while (chain->images[image_index].present_queued ||1275chain->sent_image_count == chain->base.image_count) {1276xcb_generic_event_t *event =1277xcb_wait_for_special_event(chain->conn, chain->special_event);1278if (!event) {1279result = VK_ERROR_OUT_OF_DATE_KHR;1280goto fail;1281}12821283result = x11_handle_dri3_present_event(chain, (void *)event);1284free(event);1285if (result < 0)1286goto fail;1287}1288}1289}12901291fail:1292x11_swapchain_result(chain, result);1293if (chain->has_acquire_queue)1294wsi_queue_push(&chain->acquire_queue, UINT32_MAX);12951296return NULL;1297}12981299static VkResult1300x11_image_init(VkDevice device_h, struct x11_swapchain *chain,1301const VkSwapchainCreateInfoKHR *pCreateInfo,1302const VkAllocationCallbacks* pAllocator,1303const uint64_t *const *modifiers,1304const uint32_t *num_modifiers,1305int num_tranches, struct x11_image *image)1306{1307xcb_void_cookie_t cookie;1308VkResult result;1309uint32_t bpp = 32;13101311if (chain->base.use_prime_blit) {1312bool use_modifier = num_tranches > 0;1313result = wsi_create_prime_image(&chain->base, pCreateInfo, use_modifier, &image->base);1314} else {1315result = wsi_create_native_image(&chain->base, pCreateInfo,1316num_tranches, num_modifiers, modifiers,1317&image->base);1318}1319if (result < 0)1320return result;13211322if (chain->base.wsi->sw) {1323image->busy = false;1324return VK_SUCCESS;1325}1326image->pixmap = xcb_generate_id(chain->conn);13271328#ifdef HAVE_DRI3_MODIFIERS1329if (image->base.drm_modifier != DRM_FORMAT_MOD_INVALID) {1330/* If the image has a modifier, we must have DRI3 v1.2. */1331assert(chain->has_dri3_modifiers);13321333cookie =1334xcb_dri3_pixmap_from_buffers_checked(chain->conn,1335image->pixmap,1336chain->window,1337image->base.num_planes,1338pCreateInfo->imageExtent.width,1339pCreateInfo->imageExtent.height,1340image->base.row_pitches[0],1341image->base.offsets[0],1342image->base.row_pitches[1],1343image->base.offsets[1],1344image->base.row_pitches[2],1345image->base.offsets[2],1346image->base.row_pitches[3],1347image->base.offsets[3],1348chain->depth, bpp,1349image->base.drm_modifier,1350image->base.fds);1351} else1352#endif1353{1354/* Without passing modifiers, we can't have multi-plane RGB images. */1355assert(image->base.num_planes == 1);13561357cookie =1358xcb_dri3_pixmap_from_buffer_checked(chain->conn,1359image->pixmap,1360chain->window,1361image->base.sizes[0],1362pCreateInfo->imageExtent.width,1363pCreateInfo->imageExtent.height,1364image->base.row_pitches[0],1365chain->depth, bpp,1366image->base.fds[0]);1367}13681369xcb_discard_reply(chain->conn, cookie.sequence);13701371/* XCB has now taken ownership of the FDs. */1372for (int i = 0; i < image->base.num_planes; i++)1373image->base.fds[i] = -1;13741375int fence_fd = xshmfence_alloc_shm();1376if (fence_fd < 0)1377goto fail_pixmap;13781379image->shm_fence = xshmfence_map_shm(fence_fd);1380if (image->shm_fence == NULL)1381goto fail_shmfence_alloc;13821383image->sync_fence = xcb_generate_id(chain->conn);1384xcb_dri3_fence_from_fd(chain->conn,1385image->pixmap,1386image->sync_fence,1387false,1388fence_fd);13891390image->busy = false;1391xshmfence_trigger(image->shm_fence);13921393return VK_SUCCESS;13941395fail_shmfence_alloc:1396close(fence_fd);13971398fail_pixmap:1399cookie = xcb_free_pixmap(chain->conn, image->pixmap);1400xcb_discard_reply(chain->conn, cookie.sequence);14011402wsi_destroy_image(&chain->base, &image->base);14031404return result;1405}14061407static void1408x11_image_finish(struct x11_swapchain *chain,1409const VkAllocationCallbacks* pAllocator,1410struct x11_image *image)1411{1412xcb_void_cookie_t cookie;14131414if (!chain->base.wsi->sw) {1415cookie = xcb_sync_destroy_fence(chain->conn, image->sync_fence);1416xcb_discard_reply(chain->conn, cookie.sequence);1417xshmfence_unmap_shm(image->shm_fence);14181419cookie = xcb_free_pixmap(chain->conn, image->pixmap);1420xcb_discard_reply(chain->conn, cookie.sequence);1421}14221423wsi_destroy_image(&chain->base, &image->base);1424}14251426static void1427wsi_x11_get_dri3_modifiers(struct wsi_x11_connection *wsi_conn,1428xcb_connection_t *conn, xcb_window_t window,1429uint8_t depth, uint8_t bpp,1430VkCompositeAlphaFlagsKHR vk_alpha,1431uint64_t **modifiers_in, uint32_t *num_modifiers_in,1432uint32_t *num_tranches_in,1433const VkAllocationCallbacks *pAllocator)1434{1435if (!wsi_conn->has_dri3_modifiers)1436goto out;14371438#ifdef HAVE_DRI3_MODIFIERS1439xcb_generic_error_t *error = NULL;1440xcb_dri3_get_supported_modifiers_cookie_t mod_cookie =1441xcb_dri3_get_supported_modifiers(conn, window, depth, bpp);1442xcb_dri3_get_supported_modifiers_reply_t *mod_reply =1443xcb_dri3_get_supported_modifiers_reply(conn, mod_cookie, &error);1444free(error);14451446if (!mod_reply || (mod_reply->num_window_modifiers == 0 &&1447mod_reply->num_screen_modifiers == 0)) {1448free(mod_reply);1449goto out;1450}14511452uint32_t n = 0;1453uint32_t counts[2];1454uint64_t *modifiers[2];14551456if (mod_reply->num_window_modifiers) {1457counts[n] = mod_reply->num_window_modifiers;1458modifiers[n] = vk_alloc(pAllocator,1459counts[n] * sizeof(uint64_t),14608, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);1461if (!modifiers[n]) {1462free(mod_reply);1463goto out;1464}14651466memcpy(modifiers[n],1467xcb_dri3_get_supported_modifiers_window_modifiers(mod_reply),1468counts[n] * sizeof(uint64_t));1469n++;1470}14711472if (mod_reply->num_screen_modifiers) {1473counts[n] = mod_reply->num_screen_modifiers;1474modifiers[n] = vk_alloc(pAllocator,1475counts[n] * sizeof(uint64_t),14768, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);1477if (!modifiers[n]) {1478if (n > 0)1479vk_free(pAllocator, modifiers[0]);1480free(mod_reply);1481goto out;1482}14831484memcpy(modifiers[n],1485xcb_dri3_get_supported_modifiers_screen_modifiers(mod_reply),1486counts[n] * sizeof(uint64_t));1487n++;1488}14891490for (int i = 0; i < n; i++) {1491modifiers_in[i] = modifiers[i];1492num_modifiers_in[i] = counts[i];1493}1494*num_tranches_in = n;14951496free(mod_reply);1497return;1498#endif1499out:1500*num_tranches_in = 0;1501}15021503static VkResult1504x11_swapchain_destroy(struct wsi_swapchain *anv_chain,1505const VkAllocationCallbacks *pAllocator)1506{1507struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;1508xcb_void_cookie_t cookie;15091510if (chain->has_present_queue) {1511chain->status = VK_ERROR_OUT_OF_DATE_KHR;1512/* Push a UINT32_MAX to wake up the manager */1513wsi_queue_push(&chain->present_queue, UINT32_MAX);1514pthread_join(chain->queue_manager, NULL);15151516if (chain->has_acquire_queue)1517wsi_queue_destroy(&chain->acquire_queue);1518wsi_queue_destroy(&chain->present_queue);1519}15201521for (uint32_t i = 0; i < chain->base.image_count; i++)1522x11_image_finish(chain, pAllocator, &chain->images[i]);15231524xcb_unregister_for_special_event(chain->conn, chain->special_event);1525cookie = xcb_present_select_input_checked(chain->conn, chain->event_id,1526chain->window,1527XCB_PRESENT_EVENT_MASK_NO_EVENT);1528xcb_discard_reply(chain->conn, cookie.sequence);15291530wsi_swapchain_finish(&chain->base);15311532vk_free(pAllocator, chain);15331534return VK_SUCCESS;1535}15361537static void1538wsi_x11_set_adaptive_sync_property(xcb_connection_t *conn,1539xcb_drawable_t drawable,1540uint32_t state)1541{1542static char const name[] = "_VARIABLE_REFRESH";1543xcb_intern_atom_cookie_t cookie;1544xcb_intern_atom_reply_t* reply;1545xcb_void_cookie_t check;15461547cookie = xcb_intern_atom(conn, 0, strlen(name), name);1548reply = xcb_intern_atom_reply(conn, cookie, NULL);1549if (reply == NULL)1550return;15511552if (state)1553check = xcb_change_property_checked(conn, XCB_PROP_MODE_REPLACE,1554drawable, reply->atom,1555XCB_ATOM_CARDINAL, 32, 1, &state);1556else1557check = xcb_delete_property_checked(conn, drawable, reply->atom);15581559xcb_discard_reply(conn, check.sequence);1560free(reply);1561}156215631564static VkResult1565x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,1566VkDevice device,1567struct wsi_device *wsi_device,1568const VkSwapchainCreateInfoKHR *pCreateInfo,1569const VkAllocationCallbacks* pAllocator,1570struct wsi_swapchain **swapchain_out)1571{1572struct x11_swapchain *chain;1573xcb_void_cookie_t cookie;1574VkResult result;1575VkPresentModeKHR present_mode = wsi_swapchain_get_present_mode(wsi_device, pCreateInfo);15761577assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);15781579xcb_connection_t *conn = x11_surface_get_connection(icd_surface);1580struct wsi_x11_connection *wsi_conn =1581wsi_x11_get_connection(wsi_device, conn);1582if (!wsi_conn)1583return VK_ERROR_OUT_OF_HOST_MEMORY;15841585unsigned num_images = pCreateInfo->minImageCount;1586if (wsi_device->x11.strict_imageCount)1587num_images = pCreateInfo->minImageCount;1588else if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR ||1589(present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR &&1590wsi_conn->is_xwayland))1591num_images = MAX2(num_images, 5);1592else if (wsi_device->x11.ensure_minImageCount)1593num_images = MAX2(num_images, x11_get_min_image_count(wsi_device));15941595/* Check for whether or not we have a window up-front */1596xcb_window_t window = x11_surface_get_window(icd_surface);1597xcb_get_geometry_reply_t *geometry =1598xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), NULL);1599if (geometry == NULL)1600return VK_ERROR_SURFACE_LOST_KHR;1601const uint32_t bit_depth = geometry->depth;1602const uint16_t cur_width = geometry->width;1603const uint16_t cur_height = geometry->height;1604free(geometry);16051606size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);1607chain = vk_alloc(pAllocator, size, 8,1608VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);1609if (chain == NULL)1610return VK_ERROR_OUT_OF_HOST_MEMORY;16111612result = wsi_swapchain_init(wsi_device, &chain->base, device,1613pCreateInfo, pAllocator);1614if (result != VK_SUCCESS)1615goto fail_alloc;16161617chain->base.destroy = x11_swapchain_destroy;1618chain->base.get_wsi_image = x11_get_wsi_image;1619chain->base.acquire_next_image = x11_acquire_next_image;1620chain->base.queue_present = x11_queue_present;1621chain->base.present_mode = present_mode;1622chain->base.image_count = num_images;1623chain->conn = conn;1624chain->window = window;1625chain->depth = bit_depth;1626chain->extent = pCreateInfo->imageExtent;1627chain->send_sbc = 0;1628chain->sent_image_count = 0;1629chain->last_present_msc = 0;1630chain->has_acquire_queue = false;1631chain->has_present_queue = false;1632chain->status = VK_SUCCESS;1633chain->has_dri3_modifiers = wsi_conn->has_dri3_modifiers;16341635if (chain->extent.width != cur_width || chain->extent.height != cur_height)1636chain->status = VK_SUBOPTIMAL_KHR;16371638/* We used to inherit copy_is_suboptimal from pCreateInfo->oldSwapchain.1639* When it was true, and when the next present was completed with copying,1640* we would return VK_SUBOPTIMAL_KHR and hint the app to reallocate again1641* for no good reason. If all following presents on the surface were1642* completed with copying because of some surface state change, we would1643* always return VK_SUBOPTIMAL_KHR no matter how many times the app had1644* reallocated.1645*/1646chain->copy_is_suboptimal = false;16471648if (!wsi_device->sw)1649if (!wsi_x11_check_dri3_compatible(wsi_device, conn))1650chain->base.use_prime_blit = true;16511652chain->event_id = xcb_generate_id(chain->conn);1653xcb_present_select_input(chain->conn, chain->event_id, chain->window,1654XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |1655XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |1656XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);16571658/* Create an XCB event queue to hold present events outside of the usual1659* application event queue1660*/1661chain->special_event =1662xcb_register_for_special_xge(chain->conn, &xcb_present_id,1663chain->event_id, NULL);16641665chain->gc = xcb_generate_id(chain->conn);1666if (!chain->gc) {1667/* FINISHME: Choose a better error. */1668result = VK_ERROR_OUT_OF_HOST_MEMORY;1669goto fail_register;1670}16711672cookie = xcb_create_gc(chain->conn,1673chain->gc,1674chain->window,1675XCB_GC_GRAPHICS_EXPOSURES,1676(uint32_t []) { 0 });1677xcb_discard_reply(chain->conn, cookie.sequence);16781679uint64_t *modifiers[2] = {NULL, NULL};1680uint32_t num_modifiers[2] = {0, 0};1681uint32_t num_tranches = 0;1682if (wsi_device->supports_modifiers)1683wsi_x11_get_dri3_modifiers(wsi_conn, conn, window, chain->depth, 32,1684pCreateInfo->compositeAlpha,1685modifiers, num_modifiers, &num_tranches,1686pAllocator);16871688uint32_t image = 0;1689for (; image < chain->base.image_count; image++) {1690result = x11_image_init(device, chain, pCreateInfo, pAllocator,1691(const uint64_t *const *)modifiers,1692num_modifiers, num_tranches,1693&chain->images[image]);1694if (result != VK_SUCCESS)1695goto fail_init_images;1696}16971698if ((chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ||1699chain->base.present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR ||1700chain->base.present_mode == VK_PRESENT_MODE_MAILBOX_KHR ||1701(chain->base.present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR &&1702wsi_conn->is_xwayland)) && !chain->base.wsi->sw) {1703chain->has_present_queue = true;17041705/* Initialize our queues. We make them base.image_count + 1 because we will1706* occasionally use UINT32_MAX to signal the other thread that an error1707* has occurred and we don't want an overflow.1708*/1709int ret;1710ret = wsi_queue_init(&chain->present_queue, chain->base.image_count + 1);1711if (ret) {1712goto fail_init_images;1713}17141715if (chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ||1716chain->base.present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) {1717chain->has_acquire_queue = true;17181719ret = wsi_queue_init(&chain->acquire_queue, chain->base.image_count + 1);1720if (ret) {1721wsi_queue_destroy(&chain->present_queue);1722goto fail_init_images;1723}17241725for (unsigned i = 0; i < chain->base.image_count; i++)1726wsi_queue_push(&chain->acquire_queue, i);1727}17281729ret = pthread_create(&chain->queue_manager, NULL,1730x11_manage_fifo_queues, chain);1731if (ret) {1732wsi_queue_destroy(&chain->present_queue);1733if (chain->has_acquire_queue)1734wsi_queue_destroy(&chain->acquire_queue);17351736goto fail_init_images;1737}1738}17391740assert(chain->has_present_queue || !chain->has_acquire_queue);17411742for (int i = 0; i < ARRAY_SIZE(modifiers); i++)1743vk_free(pAllocator, modifiers[i]);17441745/* It is safe to set it here as only one swapchain can be associated with1746* the window, and swapchain creation does the association. At this point1747* we know the creation is going to succeed. */1748wsi_x11_set_adaptive_sync_property(conn, window,1749wsi_device->enable_adaptive_sync);17501751*swapchain_out = &chain->base;17521753return VK_SUCCESS;17541755fail_init_images:1756for (uint32_t j = 0; j < image; j++)1757x11_image_finish(chain, pAllocator, &chain->images[j]);17581759for (int i = 0; i < ARRAY_SIZE(modifiers); i++)1760vk_free(pAllocator, modifiers[i]);17611762fail_register:1763xcb_unregister_for_special_event(chain->conn, chain->special_event);17641765wsi_swapchain_finish(&chain->base);17661767fail_alloc:1768vk_free(pAllocator, chain);17691770return result;1771}17721773VkResult1774wsi_x11_init_wsi(struct wsi_device *wsi_device,1775const VkAllocationCallbacks *alloc,1776const struct driOptionCache *dri_options)1777{1778struct wsi_x11 *wsi;1779VkResult result;17801781wsi = vk_alloc(alloc, sizeof(*wsi), 8,1782VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);1783if (!wsi) {1784result = VK_ERROR_OUT_OF_HOST_MEMORY;1785goto fail;1786}17871788int ret = pthread_mutex_init(&wsi->mutex, NULL);1789if (ret != 0) {1790if (ret == ENOMEM) {1791result = VK_ERROR_OUT_OF_HOST_MEMORY;1792} else {1793/* FINISHME: Choose a better error. */1794result = VK_ERROR_OUT_OF_HOST_MEMORY;1795}17961797goto fail_alloc;1798}17991800wsi->connections = _mesa_hash_table_create(NULL, _mesa_hash_pointer,1801_mesa_key_pointer_equal);1802if (!wsi->connections) {1803result = VK_ERROR_OUT_OF_HOST_MEMORY;1804goto fail_mutex;1805}18061807if (dri_options) {1808if (driCheckOption(dri_options, "vk_x11_override_min_image_count", DRI_INT)) {1809wsi_device->x11.override_minImageCount =1810driQueryOptioni(dri_options, "vk_x11_override_min_image_count");1811}1812if (driCheckOption(dri_options, "vk_x11_strict_image_count", DRI_BOOL)) {1813wsi_device->x11.strict_imageCount =1814driQueryOptionb(dri_options, "vk_x11_strict_image_count");1815}1816if (driCheckOption(dri_options, "vk_x11_ensure_min_image_count", DRI_BOOL)) {1817wsi_device->x11.ensure_minImageCount =1818driQueryOptionb(dri_options, "vk_x11_ensure_min_image_count");1819}18201821}18221823wsi->base.get_support = x11_surface_get_support;1824wsi->base.get_capabilities2 = x11_surface_get_capabilities2;1825wsi->base.get_formats = x11_surface_get_formats;1826wsi->base.get_formats2 = x11_surface_get_formats2;1827wsi->base.get_present_modes = x11_surface_get_present_modes;1828wsi->base.get_present_rectangles = x11_surface_get_present_rectangles;1829wsi->base.create_swapchain = x11_surface_create_swapchain;18301831wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = &wsi->base;1832wsi_device->wsi[VK_ICD_WSI_PLATFORM_XLIB] = &wsi->base;18331834return VK_SUCCESS;18351836fail_mutex:1837pthread_mutex_destroy(&wsi->mutex);1838fail_alloc:1839vk_free(alloc, wsi);1840fail:1841wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = NULL;1842wsi_device->wsi[VK_ICD_WSI_PLATFORM_XLIB] = NULL;18431844return result;1845}18461847void1848wsi_x11_finish_wsi(struct wsi_device *wsi_device,1849const VkAllocationCallbacks *alloc)1850{1851struct wsi_x11 *wsi =1852(struct wsi_x11 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB];18531854if (wsi) {1855hash_table_foreach(wsi->connections, entry)1856wsi_x11_connection_destroy(wsi_device, entry->data);18571858_mesa_hash_table_destroy(wsi->connections, NULL);18591860pthread_mutex_destroy(&wsi->mutex);18611862vk_free(alloc, wsi);1863}1864}186518661867