vulkan/wsi/x11: document implementation

To extend the shared understanding of our code base and ease contributing
document purpose and flow for many of our internal functions.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6536>
This commit is contained in:
Roman Gilg 2020-08-31 02:35:57 +02:00 committed by Marge Bot
parent 978ea32acf
commit 6018d5c44a
1 changed files with 148 additions and 13 deletions

View File

@ -75,9 +75,8 @@ struct wsi_x11 {
};
/** wsi_dri3_open
*
* Wrapper around xcb_dri3_open
/**
* Wrapper around xcb_dri3_open. Returns the opened fd or -1 on error.
*/
static int
wsi_dri3_open(xcb_connection_t *conn,
@ -96,6 +95,7 @@ wsi_dri3_open(xcb_connection_t *conn,
if (!reply)
return -1;
/* According to DRI3 extension nfd must equal one. */
if (reply->nfd != 1) {
free(reply);
return -1;
@ -108,6 +108,13 @@ wsi_dri3_open(xcb_connection_t *conn,
return fd;
}
/**
* Checks compatibility of the device wsi_dev with the device the X server
* provides via DRI3.
*
* This returns true when no device could be retrieved from the X server or when
* the information for the X server device indicate that it is the same device.
*/
static bool
wsi_x11_check_dri3_compatible(const struct wsi_device *wsi_dev,
xcb_connection_t *conn)
@ -116,6 +123,9 @@ wsi_x11_check_dri3_compatible(const struct wsi_device *wsi_dev,
xcb_setup_roots_iterator(xcb_get_setup(conn));
xcb_screen_t *screen = screen_iter.data;
/* Open the DRI3 device from the X server. If we do not retrieve one we
* assume our local device is compatible.
*/
int dri3_fd = wsi_dri3_open(conn, screen->root, None);
if (dri3_fd == -1)
return true;
@ -322,6 +332,14 @@ wsi_x11_check_for_dri3(struct wsi_x11_connection *wsi_conn)
return false;
}
/**
* Get internal struct representing an xcb_connection_t.
*
* This can allocate the struct but the caller does not own the struct. It is
* deleted on wsi_x11_finish_wsi by the hash table it is inserted.
*
* If the allocation fails NULL is returned.
*/
static struct wsi_x11_connection *
wsi_x11_get_connection(struct wsi_device *wsi_dev,
xcb_connection_t *conn)
@ -1059,6 +1077,10 @@ static uint64_t wsi_get_absolute_timeout(uint64_t timeout)
return current_time + timeout;
}
/**
* Acquire a ready-to-use image directly from our swapchain. If all images are
* busy wait until one is not anymore or till timeout.
*/
static VkResult
x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
uint32_t *image_index, uint64_t timeout)
@ -1124,6 +1146,10 @@ x11_acquire_next_image_poll_x11(struct x11_swapchain *chain,
}
}
/**
* Acquire a ready-to-use image from the acquire-queue. Only relevant in fifo
* presentation mode.
*/
static VkResult
x11_acquire_next_image_from_queue(struct x11_swapchain *chain,
uint32_t *image_index_out, uint64_t timeout)
@ -1151,6 +1177,9 @@ x11_acquire_next_image_from_queue(struct x11_swapchain *chain,
return chain->status;
}
/**
* Send image to X server via Present extension.
*/
static VkResult
x11_present_to_x11_dri3(struct x11_swapchain *chain, uint32_t image_index,
uint64_t target_msc)
@ -1226,6 +1255,9 @@ x11_present_to_x11_dri3(struct x11_swapchain *chain, uint32_t image_index,
return x11_swapchain_result(chain, VK_SUCCESS);
}
/**
* Send image to X server unaccelerated (software drivers).
*/
static VkResult
x11_present_to_x11_sw(struct x11_swapchain *chain, uint32_t image_index,
uint64_t target_msc)
@ -1252,6 +1284,10 @@ x11_present_to_x11_sw(struct x11_swapchain *chain, uint32_t image_index,
xcb_flush(chain->conn);
return x11_swapchain_result(chain, VK_SUCCESS);
}
/**
* Send image to the X server for presentation at target_msc.
*/
static VkResult
x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
uint64_t target_msc)
@ -1261,6 +1297,12 @@ x11_present_to_x11(struct x11_swapchain *chain, uint32_t image_index,
return x11_present_to_x11_dri3(chain, image_index, target_msc);
}
/**
* Acquire a ready-to-use image from the swapchain.
*
* This means usually that the image is not waiting on presentation and that the
* image has been released by the X server to be used again by the consumer.
*/
static VkResult
x11_acquire_next_image(struct wsi_swapchain *anv_chain,
const VkAcquireNextImageInfoKHR *info,
@ -1273,10 +1315,12 @@ x11_acquire_next_image(struct wsi_swapchain *anv_chain,
if (chain->status < 0)
return chain->status;
/* For software drivers and without shared memory we only render to a single image. */
if (chain->base.wsi->sw && !chain->has_mit_shm) {
*image_index = 0;
return VK_SUCCESS;
}
if (chain->has_acquire_queue) {
return x11_acquire_next_image_from_queue(chain, image_index, timeout);
} else {
@ -1284,6 +1328,13 @@ x11_acquire_next_image(struct wsi_swapchain *anv_chain,
}
}
/**
* Queue a new presentation of an image that was previously acquired by the
* consumer.
*
* Note that in immediate presentation mode this does not really queue the
* presentation but directly asks the X server to show it.
*/
static VkResult
x11_queue_present(struct wsi_swapchain *anv_chain,
uint32_t image_index,
@ -1300,10 +1351,22 @@ x11_queue_present(struct wsi_swapchain *anv_chain,
wsi_queue_push(&chain->present_queue, image_index);
return chain->status;
} else {
/* No present queue means immedate mode, so we present immediately. */
return x11_present_to_x11(chain, image_index, 0);
}
}
/**
* Decides if an early wait on buffer fences before buffer submission is required. That is for:
* - Mailbox mode, as otherwise the latest image in the queue might not be fully rendered at
* present time, what could lead to missing a frame.
* - Immediate mode under Xwayland, as it works practically the same as mailbox mode using the
* mailbox mechanism of Wayland. Sending a buffer with fences not yet signalled can make the
* compositor miss a frame when compositing the final image with this buffer.
*
* Note though that early waits can be disabled in general on Xwayland by setting the
* 'vk_xwayland_wait_ready' DRIConf option to false.
*/
static bool
x11_needs_wait_for_fences(const struct wsi_device *wsi_device,
struct wsi_x11_connection *wsi_conn,
@ -1323,6 +1386,21 @@ x11_needs_wait_for_fences(const struct wsi_device *wsi_device,
}
}
/**
* Our queue manager. Albeit called x11_manage_fifo_queues only directly
* manages the present-queue and does this in general in fifo and mailbox presentation
* modes (there is no present-queue in immediate mode with the exception of Xwayland).
*
* Runs in a separate thread, blocks and reacts to queued images on the
* present-queue
*
* In mailbox mode the queue management is simplified since we only need to
* pull new images from the present queue and can directly present them.
*
* In fifo mode images can only be presented one after the other. For that after
* sending the image to the X server we wait until the image either has been
* presented or released and only then pull a new image from the present-queue.
*/
static void *
x11_manage_fifo_queues(void *state)
{
@ -1343,6 +1421,7 @@ x11_manage_fifo_queues(void *state)
uint32_t image_index = 0;
result = wsi_queue_pull(&chain->present_queue, &image_index, INT64_MAX);
assert(result != VK_TIMEOUT);
if (result < 0) {
goto fail;
} else if (chain->status < 0) {
@ -1352,6 +1431,9 @@ x11_manage_fifo_queues(void *state)
return NULL;
}
/* Waiting for the GPU work to finish at this point in time is required in certain usage
* scenarios. Otherwise we wait as usual in wsi_common_queue_present.
*/
if (x11_needs_wait_for_fences(chain->base.wsi, wsi_conn,
chain->base.present_mode)) {
result = chain->base.wsi->WaitForFences(chain->base.device, 1,
@ -1716,7 +1798,12 @@ wsi_x11_set_adaptive_sync_property(xcb_connection_t *conn,
free(reply);
}
/**
* Create the swapchain.
*
* Supports immediate, fifo and mailbox presentation mode.
*
*/
static VkResult
x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
VkDevice device,
@ -1732,12 +1819,20 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
/* Get xcb connection from the icd_surface and from that our internal struct
* representing it.
*/
xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
struct wsi_x11_connection *wsi_conn =
wsi_x11_get_connection(wsi_device, conn);
if (!wsi_conn)
return VK_ERROR_OUT_OF_HOST_MEMORY;
/* Get number of images in our swapchain. This count depends on:
* - requested minimal image count
* - device characteristics
* - presentation mode.
*/
unsigned num_images = pCreateInfo->minImageCount;
if (wsi_device->x11.strict_imageCount)
num_images = pCreateInfo->minImageCount;
@ -1746,8 +1841,12 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
else if (wsi_device->x11.ensure_minImageCount)
num_images = MAX2(num_images, x11_get_min_image_count(wsi_device));
/* Check for whether or not we have a window up-front */
/* Check that we have a window up-front. It is an error to not have one. */
xcb_window_t window = x11_surface_get_window(icd_surface);
/* Get the geometry of that window. The bit depth of the swapchain will be fitted and the
* chain's images extents should fit it for performance-optimizing flips.
*/
xcb_get_geometry_reply_t *geometry =
xcb_get_geometry_reply(conn, xcb_get_geometry(conn, window), NULL);
if (geometry == NULL)
@ -1757,12 +1856,16 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
const uint16_t cur_height = geometry->height;
free(geometry);
/* Allocate the actual swapchain. The size depends on image count. */
size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
chain = vk_zalloc(pAllocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (chain == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
/* When our local device is not compatible with the DRI3 device provided by
* the X server we assume this is a PRIME system.
*/
bool use_prime_blit = false;
if (!wsi_device->sw)
if (!wsi_x11_check_dri3_compatible(wsi_device, conn))
@ -1792,19 +1895,39 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
chain->has_dri3_modifiers = wsi_conn->has_dri3_modifiers;
chain->has_mit_shm = wsi_conn->has_mit_shm;
/* When images in the swapchain don't fit the window, X can still present them, but it won't
* happen by flip, only by copy. So this is a suboptimal copy, because if the client would change
* the chain extents X may be able to flip
*/
if (chain->extent.width != cur_width || chain->extent.height != cur_height)
chain->status = VK_SUBOPTIMAL_KHR;
/* We used to inherit copy_is_suboptimal from pCreateInfo->oldSwapchain.
* When it was true, and when the next present was completed with copying,
* we would return VK_SUBOPTIMAL_KHR and hint the app to reallocate again
* for no good reason. If all following presents on the surface were
* completed with copying because of some surface state change, we would
* always return VK_SUBOPTIMAL_KHR no matter how many times the app had
* reallocated.
/* On a new swapchain this helper variable is set to false. Once we present it will have an
* impact once we ever do at least one flip and go back to copying afterwards. It is presumed
* that in this case here is a high likelihood X could do flips again if the client reallocates a
* new swapchain.
*
* Note that we used to inheritted this property from 'pCreateInfo->oldSwapchain'. But when it
* was true, and when the next present was completed with copying, we would return
* VK_SUBOPTIMAL_KHR and hint the app to reallocate again for no good reason. If all following
* presents on the surface were completed with copying because of some surface state change, we
* would always return VK_SUBOPTIMAL_KHR no matter how many times the app had reallocated.
*
* Note also that is is questionable in general if that mechanism is really useful. It ist not
* clear why on a change from flipping to copying we can assume a reallocation has a high chance
* of making flips work again per se. In other words it is not clear why there is need for
* another way to inform clients about suboptimal copies besides forwarding the
* 'PresentOptionSuboptimal' complete mode.
*/
chain->copy_is_suboptimal = false;
/* For our swapchain we need to listen to following Present extension events:
* - Configure: Window dimensions changed. Images in the swapchain might need
* to be reallocated.
* - Complete: An image from our swapchain was presented on the output.
* - Idle: An image from our swapchain is not anymore accessed by the X
* server and can be reused.
*/
chain->event_id = xcb_generate_id(chain->conn);
xcb_present_select_input(chain->conn, chain->event_id, chain->window,
XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY |
@ -1818,6 +1941,7 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
xcb_register_for_special_xge(chain->conn, &xcb_present_id,
chain->event_id, NULL);
/* Create the graphics context. */
chain->gc = xcb_generate_id(chain->conn);
if (!chain->gc) {
/* FINISHME: Choose a better error. */
@ -1851,6 +1975,17 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
goto fail_init_images;
}
/* Initialize queues for images in our swapchain. Possible queues are:
* - Present queue: for images sent to the X server but not yet presented.
* - Acquire queue: for images already presented but not yet released by the
* X server.
*
* In general queues are not used on software drivers, otherwise which queues
* are used depends on our presentation mode:
* - Fifo: present and acquire
* - Mailbox: present only
* - Immediate: present when we wait on fences before buffer submission (Xwayland)
*/
if ((chain->base.present_mode == VK_PRESENT_MODE_FIFO_KHR ||
chain->base.present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR ||
x11_needs_wait_for_fences(wsi_device, wsi_conn,
@ -1858,7 +1993,7 @@ x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
!chain->base.wsi->sw) {
chain->has_present_queue = true;
/* Initialize our queues. We make them base.image_count + 1 because we will
/* The queues have a length of 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.
*/