anv: move common wsi code to x11/wayland common files.

Next task is to rename all the anv_ out of this,
and move to a common location

Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
This commit is contained in:
Dave Airlie 2016-10-14 05:42:29 +01:00
parent e0d15fbe1d
commit 971523410f
10 changed files with 1851 additions and 1656 deletions

View file

@ -42,15 +42,19 @@ VULKAN_FILES := \
anv_query.c \
anv_util.c \
anv_wsi.c \
anv_wsi.h \
wsi_common.h \
genX_pipeline_util.h \
vk_format_info.h
VULKAN_WSI_WAYLAND_FILES := \
anv_wsi_wayland.c
anv_wsi_wayland.c \
wsi_common_wayland.c \
wsi_common_wayland.h
VULKAN_WSI_X11_FILES := \
anv_wsi_x11.c
anv_wsi_x11.c \
wsi_common_x11.c \
wsi_common_x11.h
VULKAN_GEM_FILES := \
anv_gem.c

View file

@ -66,6 +66,8 @@ struct gen_l3_config;
#include "brw_context.h"
#include "isl/isl.h"
#include "wsi_common.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -455,10 +457,6 @@ extern struct anv_dispatch_table dtable;
#define VK_ICD_WSI_PLATFORM_MAX 5
struct anv_wsi_device {
struct anv_wsi_interface * wsi[VK_ICD_WSI_PLATFORM_MAX];
};
struct anv_physical_device {
VK_LOADER_DATA _loader_data;

View file

@ -21,7 +21,8 @@
* IN THE SOFTWARE.
*/
#include "anv_wsi.h"
#include "anv_private.h"
#include "wsi_common.h"
#include "vk_format_info.h"
static const struct anv_wsi_callbacks anv_wsi_cbs = {

View file

@ -24,307 +24,11 @@
#include <wayland-client.h>
#include <wayland-drm-client-protocol.h>
#include "anv_wsi.h"
#include "vk_format_info.h"
#include <util/hash_table.h>
#define MIN_NUM_IMAGES 2
struct wsi_wayland;
struct wsi_wl_display {
struct wl_display * display;
struct wl_drm * drm;
struct wsi_wayland *wsi_wl;
/* Vector of VkFormats supported */
struct u_vector formats;
uint32_t capabilities;
};
struct wsi_wayland {
struct anv_wsi_interface base;
const VkAllocationCallbacks *alloc;
VkPhysicalDevice physical_device;
pthread_mutex_t mutex;
/* Hash table of wl_display -> wsi_wl_display mappings */
struct hash_table * displays;
const struct anv_wsi_callbacks *cbs;
};
static void
wsi_wl_display_add_vk_format(struct wsi_wl_display *display, VkFormat format)
{
/* Don't add a format that's already in the list */
VkFormat *f;
u_vector_foreach(f, &display->formats)
if (*f == format)
return;
/* Don't add formats that aren't renderable. */
VkFormatProperties props;
display->wsi_wl->cbs->get_phys_device_format_properties(display->wsi_wl->physical_device,
format, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
return;
f = u_vector_add(&display->formats);
if (f)
*f = format;
}
static void
drm_handle_device(void *data, struct wl_drm *drm, const char *name)
{
fprintf(stderr, "wl_drm.device(%s)\n", name);
}
static uint32_t
wl_drm_format_for_vk_format(VkFormat vk_format, bool alpha)
{
switch (vk_format) {
/* TODO: Figure out what all the formats mean and make this table
* correct.
*/
#if 0
case VK_FORMAT_R4G4B4A4_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR4444 : WL_DRM_FORMAT_XBGR4444;
case VK_FORMAT_R5G6B5_UNORM:
return WL_DRM_FORMAT_BGR565;
case VK_FORMAT_R5G5B5A1_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR1555 : WL_DRM_FORMAT_XBGR1555;
case VK_FORMAT_R8G8B8_UNORM:
return WL_DRM_FORMAT_XBGR8888;
case VK_FORMAT_R8G8B8A8_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR8888 : WL_DRM_FORMAT_XBGR8888;
case VK_FORMAT_R10G10B10A2_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR2101010 : WL_DRM_FORMAT_XBGR2101010;
case VK_FORMAT_B4G4R4A4_UNORM:
return alpha ? WL_DRM_FORMAT_ARGB4444 : WL_DRM_FORMAT_XRGB4444;
case VK_FORMAT_B5G6R5_UNORM:
return WL_DRM_FORMAT_RGB565;
case VK_FORMAT_B5G5R5A1_UNORM:
return alpha ? WL_DRM_FORMAT_XRGB1555 : WL_DRM_FORMAT_XRGB1555;
#endif
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SRGB:
return WL_DRM_FORMAT_BGRX8888;
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SRGB:
return alpha ? WL_DRM_FORMAT_ARGB8888 : WL_DRM_FORMAT_XRGB8888;
#if 0
case VK_FORMAT_B10G10R10A2_UNORM:
return alpha ? WL_DRM_FORMAT_ARGB2101010 : WL_DRM_FORMAT_XRGB2101010;
#endif
default:
assert(!"Unsupported Vulkan format");
return 0;
}
}
static void
drm_handle_format(void *data, struct wl_drm *drm, uint32_t wl_format)
{
struct wsi_wl_display *display = data;
switch (wl_format) {
#if 0
case WL_DRM_FORMAT_ABGR4444:
case WL_DRM_FORMAT_XBGR4444:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R4G4B4A4_UNORM);
break;
case WL_DRM_FORMAT_BGR565:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R5G6B5_UNORM);
break;
case WL_DRM_FORMAT_ABGR1555:
case WL_DRM_FORMAT_XBGR1555:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R5G5B5A1_UNORM);
break;
case WL_DRM_FORMAT_XBGR8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R8G8B8_UNORM);
/* fallthrough */
case WL_DRM_FORMAT_ABGR8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R8G8B8A8_UNORM);
break;
case WL_DRM_FORMAT_ABGR2101010:
case WL_DRM_FORMAT_XBGR2101010:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R10G10B10A2_UNORM);
break;
case WL_DRM_FORMAT_ARGB4444:
case WL_DRM_FORMAT_XRGB4444:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B4G4R4A4_UNORM);
break;
case WL_DRM_FORMAT_RGB565:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B5G6R5_UNORM);
break;
case WL_DRM_FORMAT_ARGB1555:
case WL_DRM_FORMAT_XRGB1555:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B5G5R5A1_UNORM);
break;
#endif
case WL_DRM_FORMAT_XRGB8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8_SRGB);
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8_UNORM);
/* fallthrough */
case WL_DRM_FORMAT_ARGB8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8A8_SRGB);
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8A8_UNORM);
break;
#if 0
case WL_DRM_FORMAT_ARGB2101010:
case WL_DRM_FORMAT_XRGB2101010:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B10G10R10A2_UNORM);
break;
#endif
}
}
static void
drm_handle_authenticated(void *data, struct wl_drm *drm)
{
}
static void
drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t capabilities)
{
struct wsi_wl_display *display = data;
display->capabilities = capabilities;
}
static const struct wl_drm_listener drm_listener = {
drm_handle_device,
drm_handle_format,
drm_handle_authenticated,
drm_handle_capabilities,
};
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct wsi_wl_display *display = data;
if (strcmp(interface, "wl_drm") == 0) {
assert(display->drm == NULL);
assert(version >= 2);
display->drm = wl_registry_bind(registry, name, &wl_drm_interface, 2);
if (display->drm)
wl_drm_add_listener(display->drm, &drm_listener, display);
}
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{ /* No-op */ }
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
static void
wsi_wl_display_destroy(struct wsi_wayland *wsi, struct wsi_wl_display *display)
{
u_vector_finish(&display->formats);
if (display->drm)
wl_drm_destroy(display->drm);
vk_free(wsi->alloc, display);
}
static struct wsi_wl_display *
wsi_wl_display_create(struct wsi_wayland *wsi, struct wl_display *wl_display)
{
struct wsi_wl_display *display =
vk_alloc(wsi->alloc, sizeof(*display), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!display)
return NULL;
memset(display, 0, sizeof(*display));
display->display = wl_display;
display->wsi_wl = wsi;
if (!u_vector_init(&display->formats, sizeof(VkFormat), 8))
goto fail;
struct wl_registry *registry = wl_display_get_registry(wl_display);
if (!registry)
return NULL;
wl_registry_add_listener(registry, &registry_listener, display);
/* Round-rip to get the wl_drm global */
wl_display_roundtrip(wl_display);
if (!display->drm)
goto fail;
/* Round-rip to get wl_drm formats and capabilities */
wl_display_roundtrip(wl_display);
/* We need prime support */
if (!(display->capabilities & WL_DRM_CAPABILITY_PRIME))
goto fail;
/* We don't need this anymore */
wl_registry_destroy(registry);
return display;
fail:
if (registry)
wl_registry_destroy(registry);
wsi_wl_display_destroy(wsi, display);
return NULL;
}
static struct wsi_wl_display *
wsi_wl_get_display(struct anv_wsi_device *wsi_device,
struct wl_display *wl_display)
{
struct wsi_wayland *wsi =
(struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND];
pthread_mutex_lock(&wsi->mutex);
struct hash_entry *entry = _mesa_hash_table_search(wsi->displays,
wl_display);
if (!entry) {
/* We're about to make a bunch of blocking calls. Let's drop the
* mutex for now so we don't block up too badly.
*/
pthread_mutex_unlock(&wsi->mutex);
struct wsi_wl_display *display = wsi_wl_display_create(wsi, wl_display);
pthread_mutex_lock(&wsi->mutex);
entry = _mesa_hash_table_search(wsi->displays, wl_display);
if (entry) {
/* Oops, someone raced us to it */
wsi_wl_display_destroy(wsi, display);
} else {
entry = _mesa_hash_table_insert(wsi->displays, wl_display, display);
}
}
pthread_mutex_unlock(&wsi->mutex);
return entry->data;
}
#include "anv_private.h"
#include "wsi_common_wayland.h"
VkBool32 anv_GetPhysicalDeviceWaylandPresentationSupportKHR(
VkPhysicalDevice physicalDevice,
@ -333,119 +37,7 @@ VkBool32 anv_GetPhysicalDeviceWaylandPresentationSupportKHR(
{
ANV_FROM_HANDLE(anv_physical_device, physical_device, physicalDevice);
return wsi_wl_get_display(&physical_device->wsi_device, display) != NULL;
}
static VkResult
wsi_wl_surface_get_support(VkIcdSurfaceBase *surface,
struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
VkBool32* pSupported)
{
*pSupported = true;
return VK_SUCCESS;
}
static const VkPresentModeKHR present_modes[] = {
VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_FIFO_KHR,
};
static VkResult
wsi_wl_surface_get_capabilities(VkIcdSurfaceBase *surface,
VkSurfaceCapabilitiesKHR* caps)
{
caps->minImageCount = MIN_NUM_IMAGES;
caps->maxImageCount = 4;
caps->currentExtent = (VkExtent2D) { -1, -1 };
caps->minImageExtent = (VkExtent2D) { 1, 1 };
caps->maxImageExtent = (VkExtent2D) { INT16_MAX, INT16_MAX };
caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->maxImageArrayLayers = 1;
caps->supportedCompositeAlpha =
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
caps->supportedUsageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return VK_SUCCESS;
}
static VkResult
wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface,
struct anv_wsi_device *wsi_device,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormatKHR* pSurfaceFormats)
{
VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface;
struct wsi_wl_display *display =
wsi_wl_get_display(wsi_device, surface->display);
uint32_t count = u_vector_length(&display->formats);
if (pSurfaceFormats == NULL) {
*pSurfaceFormatCount = count;
return VK_SUCCESS;
}
assert(*pSurfaceFormatCount >= count);
*pSurfaceFormatCount = count;
VkFormat *f;
u_vector_foreach(f, &display->formats) {
*(pSurfaceFormats++) = (VkSurfaceFormatKHR) {
.format = *f,
/* TODO: We should get this from the compositor somehow */
.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
};
}
return VK_SUCCESS;
}
static VkResult
wsi_wl_surface_get_present_modes(VkIcdSurfaceBase *surface,
uint32_t* pPresentModeCount,
VkPresentModeKHR* pPresentModes)
{
if (pPresentModes == NULL) {
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
assert(*pPresentModeCount >= ARRAY_SIZE(present_modes));
typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
static VkResult anv_create_wl_surface(const VkAllocationCallbacks *pAllocator,
const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface)
{
VkIcdSurfaceWayland *surface;
surface = vk_alloc(pAllocator, sizeof *surface, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
surface->base.platform = VK_ICD_WSI_PLATFORM_WAYLAND;
surface->display = pCreateInfo->display;
surface->surface = pCreateInfo->surface;
*pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
return VK_SUCCESS;
return wsi_wl_get_presentation_support(&physical_device->wsi_device, display);
}
VkResult anv_CreateWaylandSurfaceKHR(
@ -465,373 +57,3 @@ VkResult anv_CreateWaylandSurfaceKHR(
return anv_create_wl_surface(alloc, pCreateInfo, pSurface);
}
struct wsi_wl_image {
VkImage image;
VkDeviceMemory memory;
struct wl_buffer * buffer;
bool busy;
};
struct wsi_wl_swapchain {
struct anv_swapchain base;
struct wsi_wl_display * display;
struct wl_event_queue * queue;
struct wl_surface * surface;
VkExtent2D extent;
VkFormat vk_format;
uint32_t drm_format;
VkPresentModeKHR present_mode;
bool fifo_ready;
uint32_t image_count;
struct wsi_wl_image images[0];
};
static VkResult
wsi_wl_swapchain_get_images(struct anv_swapchain *anv_chain,
uint32_t *pCount, VkImage *pSwapchainImages)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
if (pSwapchainImages == NULL) {
*pCount = chain->image_count;
return VK_SUCCESS;
}
assert(chain->image_count <= *pCount);
for (uint32_t i = 0; i < chain->image_count; i++)
pSwapchainImages[i] = chain->images[i].image;
*pCount = chain->image_count;
return VK_SUCCESS;
}
static VkResult
wsi_wl_swapchain_acquire_next_image(struct anv_swapchain *anv_chain,
uint64_t timeout,
VkSemaphore semaphore,
uint32_t *image_index)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
int ret = wl_display_dispatch_queue_pending(chain->display->display,
chain->queue);
/* XXX: I'm not sure if out-of-date is the right error here. If
* wl_display_dispatch_queue_pending fails it most likely means we got
* kicked by the server so this seems more-or-less correct.
*/
if (ret < 0)
return vk_error(VK_ERROR_OUT_OF_DATE_KHR);
while (1) {
for (uint32_t i = 0; i < chain->image_count; i++) {
if (!chain->images[i].busy) {
/* We found a non-busy image */
*image_index = i;
chain->images[i].busy = true;
return VK_SUCCESS;
}
}
/* This time we do a blocking dispatch because we can't go
* anywhere until we get an event.
*/
int ret = wl_display_roundtrip_queue(chain->display->display,
chain->queue);
if (ret < 0)
return vk_error(VK_ERROR_OUT_OF_DATE_KHR);
}
}
static void
frame_handle_done(void *data, struct wl_callback *callback, uint32_t serial)
{
struct wsi_wl_swapchain *chain = data;
chain->fifo_ready = true;
wl_callback_destroy(callback);
}
static const struct wl_callback_listener frame_listener = {
frame_handle_done,
};
static VkResult
wsi_wl_swapchain_queue_present(struct anv_swapchain *anv_chain,
uint32_t image_index)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
while (!chain->fifo_ready) {
int ret = wl_display_dispatch_queue(chain->display->display,
chain->queue);
if (ret < 0)
return vk_error(VK_ERROR_OUT_OF_DATE_KHR);
}
}
assert(image_index < chain->image_count);
wl_surface_attach(chain->surface, chain->images[image_index].buffer, 0, 0);
wl_surface_damage(chain->surface, 0, 0, INT32_MAX, INT32_MAX);
if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
struct wl_callback *frame = wl_surface_frame(chain->surface);
wl_proxy_set_queue((struct wl_proxy *)frame, chain->queue);
wl_callback_add_listener(frame, &frame_listener, chain);
chain->fifo_ready = false;
}
chain->images[image_index].busy = true;
wl_surface_commit(chain->surface);
wl_display_flush(chain->display->display);
return VK_SUCCESS;
}
static void
buffer_handle_release(void *data, struct wl_buffer *buffer)
{
struct wsi_wl_image *image = data;
assert(image->buffer == buffer);
image->busy = false;
}
static const struct wl_buffer_listener buffer_listener = {
buffer_handle_release,
};
static VkResult
wsi_wl_image_init(struct wsi_wl_swapchain *chain,
struct wsi_wl_image *image,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks* pAllocator)
{
VkDevice vk_device = chain->base.device;
VkResult result;
int fd;
uint32_t size;
uint32_t row_pitch;
uint32_t offset;
result = chain->base.image_fns->create_wsi_image(vk_device,
pCreateInfo,
pAllocator,
&image->image,
&image->memory,
&size,
&offset,
&row_pitch,
&fd);
if (result != VK_SUCCESS)
return result;
image->buffer = wl_drm_create_prime_buffer(chain->display->drm,
fd, /* name */
chain->extent.width,
chain->extent.height,
chain->drm_format,
offset,
row_pitch,
0, 0, 0, 0 /* unused */);
wl_display_roundtrip(chain->display->display);
close(fd);
wl_proxy_set_queue((struct wl_proxy *)image->buffer, chain->queue);
wl_buffer_add_listener(image->buffer, &buffer_listener, image);
return VK_SUCCESS;
chain->base.image_fns->free_wsi_image(vk_device, pAllocator,
image->image, image->memory);
return result;
}
static VkResult
wsi_wl_swapchain_destroy(struct anv_swapchain *anv_chain,
const VkAllocationCallbacks *pAllocator)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
for (uint32_t i = 0; i < chain->image_count; i++) {
if (chain->images[i].buffer)
chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
chain->images[i].image,
chain->images[i].memory);
}
vk_free(pAllocator, chain);
return VK_SUCCESS;
}
static VkResult
wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
VkDevice device,
struct anv_wsi_device *wsi_device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
const struct anv_wsi_image_fns *image_fns,
struct anv_swapchain **swapchain_out)
{
VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface;
struct wsi_wl_swapchain *chain;
VkResult result;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
int num_images = pCreateInfo->minImageCount;
assert(num_images >= MIN_NUM_IMAGES);
/* 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 Wayland compositor
* 4) One to render to
*/
if (pCreateInfo->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
num_images = MAX2(num_images, 4);
size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
chain = vk_alloc(pAllocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (chain == NULL)
return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
chain->base.device = device;
chain->base.destroy = wsi_wl_swapchain_destroy;
chain->base.get_images = wsi_wl_swapchain_get_images;
chain->base.acquire_next_image = wsi_wl_swapchain_acquire_next_image;
chain->base.queue_present = wsi_wl_swapchain_queue_present;
chain->base.image_fns = image_fns;
chain->surface = surface->surface;
chain->extent = pCreateInfo->imageExtent;
chain->vk_format = pCreateInfo->imageFormat;
chain->drm_format = wl_drm_format_for_vk_format(chain->vk_format, false);
chain->present_mode = pCreateInfo->presentMode;
chain->fifo_ready = true;
chain->image_count = num_images;
/* Mark a bunch of stuff as NULL. This way we can just call
* destroy_swapchain for cleanup.
*/
for (uint32_t i = 0; i < chain->image_count; i++)
chain->images[i].buffer = NULL;
chain->queue = NULL;
chain->display = wsi_wl_get_display(wsi_device,
surface->display);
if (!chain->display) {
result = vk_error(VK_ERROR_INITIALIZATION_FAILED);
goto fail;
}
chain->queue = wl_display_create_queue(chain->display->display);
if (!chain->queue) {
result = vk_error(VK_ERROR_INITIALIZATION_FAILED);
goto fail;
}
for (uint32_t i = 0; i < chain->image_count; i++) {
result = wsi_wl_image_init(chain, &chain->images[i],
pCreateInfo, pAllocator);
if (result != VK_SUCCESS)
goto fail;
chain->images[i].busy = false;
}
*swapchain_out = &chain->base;
return VK_SUCCESS;
fail:
wsi_wl_swapchain_destroy(&chain->base, pAllocator);
return result;
}
VkResult
anv_wl_init_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device,
const struct anv_wsi_callbacks *cbs)
{
struct wsi_wayland *wsi;
VkResult result;
wsi = vk_alloc(alloc, sizeof(*wsi), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi) {
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail;
}
wsi->physical_device = physical_device;
wsi->alloc = alloc;
wsi->cbs = cbs;
int ret = pthread_mutex_init(&wsi->mutex, NULL);
if (ret != 0) {
if (ret == ENOMEM) {
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
} else {
/* FINISHME: Choose a better error. */
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
}
goto fail_alloc;
}
wsi->displays = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
_mesa_key_pointer_equal);
if (!wsi->displays) {
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail_mutex;
}
wsi->base.get_support = wsi_wl_surface_get_support;
wsi->base.get_capabilities = wsi_wl_surface_get_capabilities;
wsi->base.get_formats = wsi_wl_surface_get_formats;
wsi->base.get_present_modes = wsi_wl_surface_get_present_modes;
wsi->base.create_swapchain = wsi_wl_surface_create_swapchain;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND] = &wsi->base;
return VK_SUCCESS;
fail_mutex:
pthread_mutex_destroy(&wsi->mutex);
fail_alloc:
vk_free(alloc, wsi);
fail:
wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND] = NULL;
return result;
}
void
anv_wl_finish_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_wayland *wsi =
(struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND];
if (wsi) {
_mesa_hash_table_destroy(wsi->displays, NULL);
pthread_mutex_destroy(&wsi->mutex);
vk_free(alloc, wsi);
}
}

View file

@ -27,237 +27,12 @@
#include <xcb/dri3.h>
#include <xcb/present.h>
#include "anv_wsi.h"
#include "anv_private.h"
#include "wsi_common_x11.h"
#include "vk_format_info.h"
#include "util/hash_table.h"
struct wsi_x11_connection {
bool has_dri3;
bool has_present;
};
struct wsi_x11 {
struct anv_wsi_interface base;
pthread_mutex_t mutex;
/* Hash table of xcb_connection -> wsi_x11_connection mappings */
struct hash_table *connections;
};
static struct wsi_x11_connection *
wsi_x11_connection_create(const VkAllocationCallbacks *alloc,
xcb_connection_t *conn)
{
xcb_query_extension_cookie_t dri3_cookie, pres_cookie;
xcb_query_extension_reply_t *dri3_reply, *pres_reply;
struct wsi_x11_connection *wsi_conn =
vk_alloc(alloc, sizeof(*wsi_conn), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi_conn)
return NULL;
dri3_cookie = xcb_query_extension(conn, 4, "DRI3");
pres_cookie = xcb_query_extension(conn, 7, "PRESENT");
dri3_reply = xcb_query_extension_reply(conn, dri3_cookie, NULL);
pres_reply = xcb_query_extension_reply(conn, pres_cookie, NULL);
if (dri3_reply == NULL || pres_reply == NULL) {
free(dri3_reply);
free(pres_reply);
vk_free(alloc, wsi_conn);
return NULL;
}
wsi_conn->has_dri3 = dri3_reply->present != 0;
wsi_conn->has_present = pres_reply->present != 0;
free(dri3_reply);
free(pres_reply);
return wsi_conn;
}
static void
wsi_x11_connection_destroy(const VkAllocationCallbacks *alloc,
struct wsi_x11_connection *conn)
{
vk_free(alloc, conn);
}
static struct wsi_x11_connection *
wsi_x11_get_connection(struct anv_wsi_device *wsi_dev,
const VkAllocationCallbacks *alloc,
xcb_connection_t *conn)
{
struct wsi_x11 *wsi =
(struct wsi_x11 *)wsi_dev->wsi[VK_ICD_WSI_PLATFORM_XCB];
pthread_mutex_lock(&wsi->mutex);
struct hash_entry *entry = _mesa_hash_table_search(wsi->connections, conn);
if (!entry) {
/* We're about to make a bunch of blocking calls. Let's drop the
* mutex for now so we don't block up too badly.
*/
pthread_mutex_unlock(&wsi->mutex);
struct wsi_x11_connection *wsi_conn =
wsi_x11_connection_create(alloc, conn);
pthread_mutex_lock(&wsi->mutex);
entry = _mesa_hash_table_search(wsi->connections, conn);
if (entry) {
/* Oops, someone raced us to it */
wsi_x11_connection_destroy(alloc, wsi_conn);
} else {
entry = _mesa_hash_table_insert(wsi->connections, conn, wsi_conn);
}
}
pthread_mutex_unlock(&wsi->mutex);
return entry->data;
}
static const VkSurfaceFormatKHR formats[] = {
{ .format = VK_FORMAT_B8G8R8A8_SRGB, },
{ .format = VK_FORMAT_B8G8R8A8_UNORM, },
};
static const VkPresentModeKHR present_modes[] = {
VK_PRESENT_MODE_MAILBOX_KHR,
};
static xcb_screen_t *
get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
{
xcb_screen_iterator_t screen_iter =
xcb_setup_roots_iterator(xcb_get_setup(conn));
for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
if (screen_iter.data->root == root)
return screen_iter.data;
}
return NULL;
}
static xcb_visualtype_t *
screen_get_visualtype(xcb_screen_t *screen, xcb_visualid_t visual_id,
unsigned *depth)
{
xcb_depth_iterator_t depth_iter =
xcb_screen_allowed_depths_iterator(screen);
for (; depth_iter.rem; xcb_depth_next (&depth_iter)) {
xcb_visualtype_iterator_t visual_iter =
xcb_depth_visuals_iterator (depth_iter.data);
for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) {
if (visual_iter.data->visual_id == visual_id) {
if (depth)
*depth = depth_iter.data->depth;
return visual_iter.data;
}
}
}
return NULL;
}
static xcb_visualtype_t *
connection_get_visualtype(xcb_connection_t *conn, xcb_visualid_t visual_id,
unsigned *depth)
{
xcb_screen_iterator_t screen_iter =
xcb_setup_roots_iterator(xcb_get_setup(conn));
/* For this we have to iterate over all of the screens which is rather
* annoying. Fortunately, there is probably only 1.
*/
for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
xcb_visualtype_t *visual = screen_get_visualtype(screen_iter.data,
visual_id, depth);
if (visual)
return visual;
}
return NULL;
}
static xcb_visualtype_t *
get_visualtype_for_window(xcb_connection_t *conn, xcb_window_t window,
unsigned *depth)
{
xcb_query_tree_cookie_t tree_cookie;
xcb_get_window_attributes_cookie_t attrib_cookie;
xcb_query_tree_reply_t *tree;
xcb_get_window_attributes_reply_t *attrib;
tree_cookie = xcb_query_tree(conn, window);
attrib_cookie = xcb_get_window_attributes(conn, window);
tree = xcb_query_tree_reply(conn, tree_cookie, NULL);
attrib = xcb_get_window_attributes_reply(conn, attrib_cookie, NULL);
if (attrib == NULL || tree == NULL) {
free(attrib);
free(tree);
return NULL;
}
xcb_window_t root = tree->root;
xcb_visualid_t visual_id = attrib->visual;
free(attrib);
free(tree);
xcb_screen_t *screen = get_screen_for_root(conn, root);
if (screen == NULL)
return NULL;
return screen_get_visualtype(screen, visual_id, depth);
}
static bool
visual_has_alpha(xcb_visualtype_t *visual, unsigned depth)
{
uint32_t rgb_mask = visual->red_mask |
visual->green_mask |
visual->blue_mask;
uint32_t all_mask = 0xffffffff >> (32 - depth);
/* Do we have bits left over after RGB? */
return (all_mask & ~rgb_mask) != 0;
}
static VkBool32 anv_get_physical_device_xcb_presentation_support(
struct anv_wsi_device *wsi_device,
VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
xcb_connection_t* connection,
xcb_visualid_t visual_id)
{
struct wsi_x11_connection *wsi_conn =
wsi_x11_get_connection(wsi_device, alloc, connection);
if (!wsi_conn->has_dri3) {
fprintf(stderr, "vulkan: No DRI3 support\n");
return false;
}
unsigned visual_depth;
if (!connection_get_visualtype(connection, visual_id, &visual_depth))
return false;
if (visual_depth != 24 && visual_depth != 32)
return false;
return true;
}
VkBool32 anv_GetPhysicalDeviceXcbPresentationSupportKHR(
VkPhysicalDevice physicalDevice,
uint32_t queueFamilyIndex,
@ -286,173 +61,6 @@ VkBool32 anv_GetPhysicalDeviceXlibPresentationSupportKHR(
queueFamilyIndex, XGetXCBConnection(dpy), visualID);
}
static xcb_connection_t*
x11_surface_get_connection(VkIcdSurfaceBase *icd_surface)
{
if (icd_surface->platform == VK_ICD_WSI_PLATFORM_XLIB)
return XGetXCBConnection(((VkIcdSurfaceXlib *)icd_surface)->dpy);
else
return ((VkIcdSurfaceXcb *)icd_surface)->connection;
}
static xcb_window_t
x11_surface_get_window(VkIcdSurfaceBase *icd_surface)
{
if (icd_surface->platform == VK_ICD_WSI_PLATFORM_XLIB)
return ((VkIcdSurfaceXlib *)icd_surface)->window;
else
return ((VkIcdSurfaceXcb *)icd_surface)->window;
}
static VkResult
x11_surface_get_support(VkIcdSurfaceBase *icd_surface,
struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
VkBool32* pSupported)
{
xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
xcb_window_t window = x11_surface_get_window(icd_surface);
struct wsi_x11_connection *wsi_conn =
wsi_x11_get_connection(wsi_device, alloc, conn);
if (!wsi_conn)
return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
if (!wsi_conn->has_dri3) {
fprintf(stderr, "vulkan: No DRI3 support\n");
*pSupported = false;
return VK_SUCCESS;
}
unsigned visual_depth;
if (!get_visualtype_for_window(conn, window, &visual_depth)) {
*pSupported = false;
return VK_SUCCESS;
}
if (visual_depth != 24 && visual_depth != 32) {
*pSupported = false;
return VK_SUCCESS;
}
*pSupported = true;
return VK_SUCCESS;
}
static VkResult
x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
VkSurfaceCapabilitiesKHR *caps)
{
xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
xcb_window_t window = x11_surface_get_window(icd_surface);
xcb_get_geometry_cookie_t geom_cookie;
xcb_generic_error_t *err;
xcb_get_geometry_reply_t *geom;
unsigned visual_depth;
geom_cookie = xcb_get_geometry(conn, window);
/* This does a round-trip. This is why we do get_geometry first and
* wait to read the reply until after we have a visual.
*/
xcb_visualtype_t *visual =
get_visualtype_for_window(conn, window, &visual_depth);
geom = xcb_get_geometry_reply(conn, geom_cookie, &err);
if (geom) {
VkExtent2D extent = { geom->width, geom->height };
caps->currentExtent = extent;
caps->minImageExtent = extent;
caps->maxImageExtent = extent;
} 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.
*/
caps->currentExtent = (VkExtent2D) { -1, -1 };
caps->minImageExtent = (VkExtent2D) { 1, 1 };
caps->maxImageExtent = (VkExtent2D) { INT16_MAX, INT16_MAX };
}
free(err);
free(geom);
if (visual_has_alpha(visual, visual_depth)) {
caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
} else {
caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
}
caps->minImageCount = 2;
caps->maxImageCount = 4;
caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->maxImageArrayLayers = 1;
caps->supportedUsageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return VK_SUCCESS;
}
static VkResult
x11_surface_get_formats(VkIcdSurfaceBase *surface,
struct anv_wsi_device *wsi_device,
uint32_t *pSurfaceFormatCount,
VkSurfaceFormatKHR *pSurfaceFormats)
{
if (pSurfaceFormats == NULL) {
*pSurfaceFormatCount = ARRAY_SIZE(formats);
return VK_SUCCESS;
}
assert(*pSurfaceFormatCount >= ARRAY_SIZE(formats));
typed_memcpy(pSurfaceFormats, formats, *pSurfaceFormatCount);
*pSurfaceFormatCount = ARRAY_SIZE(formats);
return VK_SUCCESS;
}
static VkResult
x11_surface_get_present_modes(VkIcdSurfaceBase *surface,
uint32_t *pPresentModeCount,
VkPresentModeKHR *pPresentModes)
{
if (pPresentModes == NULL) {
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
assert(*pPresentModeCount >= ARRAY_SIZE(present_modes));
typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
static VkResult anv_create_xcb_surface(const VkAllocationCallbacks *pAllocator,
const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface)
{
VkIcdSurfaceXcb *surface;
surface = vk_alloc(pAllocator, sizeof *surface, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
surface->base.platform = VK_ICD_WSI_PLATFORM_XCB;
surface->connection = pCreateInfo->connection;
surface->window = pCreateInfo->window;
*pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
return VK_SUCCESS;
}
VkResult anv_CreateXcbSurfaceKHR(
VkInstance _instance,
const VkXcbSurfaceCreateInfoKHR* pCreateInfo,
@ -471,25 +79,6 @@ VkResult anv_CreateXcbSurfaceKHR(
return anv_create_xcb_surface(alloc, pCreateInfo, pSurface);
}
static VkResult anv_create_xlib_surface(const VkAllocationCallbacks *pAllocator,
const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface)
{
VkIcdSurfaceXlib *surface;
surface = vk_alloc(pAllocator, sizeof *surface, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
surface->base.platform = VK_ICD_WSI_PLATFORM_XLIB;
surface->dpy = pCreateInfo->dpy;
surface->window = pCreateInfo->window;
*pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
return VK_SUCCESS;
}
VkResult anv_CreateXlibSurfaceKHR(
VkInstance _instance,
const VkXlibSurfaceCreateInfoKHR* pCreateInfo,
@ -508,432 +97,3 @@ VkResult anv_CreateXlibSurfaceKHR(
return anv_create_xlib_surface(alloc, pCreateInfo, pSurface);
}
struct x11_image {
VkImage image;
VkDeviceMemory memory;
xcb_pixmap_t pixmap;
bool busy;
struct xshmfence * shm_fence;
uint32_t sync_fence;
};
struct x11_swapchain {
struct anv_swapchain base;
xcb_connection_t * conn;
xcb_window_t window;
xcb_gc_t gc;
VkExtent2D extent;
uint32_t image_count;
xcb_present_event_t event_id;
xcb_special_event_t * special_event;
uint64_t send_sbc;
uint32_t stamp;
struct x11_image images[0];
};
static VkResult
x11_get_images(struct anv_swapchain *anv_chain,
uint32_t* pCount, VkImage *pSwapchainImages)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
if (pSwapchainImages == NULL) {
*pCount = chain->image_count;
return VK_SUCCESS;
}
assert(chain->image_count <= *pCount);
for (uint32_t i = 0; i < chain->image_count; i++)
pSwapchainImages[i] = chain->images[i].image;
*pCount = chain->image_count;
return VK_SUCCESS;
}
static VkResult
x11_handle_dri3_present_event(struct x11_swapchain *chain,
xcb_present_generic_event_t *event)
{
switch (event->evtype) {
case XCB_PRESENT_CONFIGURE_NOTIFY: {
xcb_present_configure_notify_event_t *config = (void *) event;
if (config->width != chain->extent.width ||
config->height != chain->extent.height)
return vk_error(VK_ERROR_OUT_OF_DATE_KHR);
break;
}
case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
xcb_present_idle_notify_event_t *idle = (void *) event;
for (unsigned i = 0; i < chain->image_count; i++) {
if (chain->images[i].pixmap == idle->pixmap) {
chain->images[i].busy = false;
break;
}
}
break;
}
case XCB_PRESENT_COMPLETE_NOTIFY:
default:
break;
}
return VK_SUCCESS;
}
static VkResult
x11_acquire_next_image(struct anv_swapchain *anv_chain,
uint64_t timeout,
VkSemaphore semaphore,
uint32_t *image_index)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
while (1) {
for (uint32_t i = 0; i < chain->image_count; i++) {
if (!chain->images[i].busy) {
/* We found a non-busy image */
xshmfence_await(chain->images[i].shm_fence);
*image_index = i;
chain->images[i].busy = true;
return VK_SUCCESS;
}
}
xcb_flush(chain->conn);
xcb_generic_event_t *event =
xcb_wait_for_special_event(chain->conn, chain->special_event);
if (!event)
return vk_error(VK_ERROR_OUT_OF_DATE_KHR);
VkResult result = x11_handle_dri3_present_event(chain, (void *)event);
free(event);
if (result != VK_SUCCESS)
return result;
}
}
static VkResult
x11_queue_present(struct anv_swapchain *anv_chain,
uint32_t image_index)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
struct x11_image *image = &chain->images[image_index];
assert(image_index < chain->image_count);
uint32_t options = XCB_PRESENT_OPTION_NONE;
int64_t target_msc = 0;
int64_t divisor = 0;
int64_t remainder = 0;
options |= XCB_PRESENT_OPTION_ASYNC;
xshmfence_reset(image->shm_fence);
++chain->send_sbc;
xcb_void_cookie_t cookie =
xcb_present_pixmap(chain->conn,
chain->window,
image->pixmap,
(uint32_t) chain->send_sbc,
0, /* valid */
0, /* update */
0, /* x_off */
0, /* y_off */
XCB_NONE, /* target_crtc */
XCB_NONE,
image->sync_fence,
options,
target_msc,
divisor,
remainder, 0, NULL);
xcb_discard_reply(chain->conn, cookie.sequence);
image->busy = true;
xcb_flush(chain->conn);
return VK_SUCCESS;
}
static VkResult
x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks* pAllocator,
struct x11_image *image)
{
xcb_void_cookie_t cookie;
VkResult result;
uint32_t row_pitch;
uint32_t offset;
uint32_t bpp = 32;
uint32_t depth = 24;
int fd;
uint32_t size;
result = chain->base.image_fns->create_wsi_image(device_h,
pCreateInfo,
pAllocator,
&image->image,
&image->memory,
&size,
&offset,
&row_pitch,
&fd);
if (result != VK_SUCCESS)
return result;
image->pixmap = xcb_generate_id(chain->conn);
cookie =
xcb_dri3_pixmap_from_buffer_checked(chain->conn,
image->pixmap,
chain->window,
size,
pCreateInfo->imageExtent.width,
pCreateInfo->imageExtent.height,
row_pitch,
depth, bpp, fd);
xcb_discard_reply(chain->conn, cookie.sequence);
int fence_fd = xshmfence_alloc_shm();
if (fence_fd < 0)
goto fail_pixmap;
image->shm_fence = xshmfence_map_shm(fence_fd);
if (image->shm_fence == NULL)
goto fail_shmfence_alloc;
image->sync_fence = xcb_generate_id(chain->conn);
xcb_dri3_fence_from_fd(chain->conn,
image->pixmap,
image->sync_fence,
false,
fence_fd);
image->busy = false;
xshmfence_trigger(image->shm_fence);
return VK_SUCCESS;
fail_shmfence_alloc:
close(fence_fd);
fail_pixmap:
cookie = xcb_free_pixmap(chain->conn, image->pixmap);
xcb_discard_reply(chain->conn, cookie.sequence);
chain->base.image_fns->free_wsi_image(device_h, pAllocator,
image->image, image->memory);
return result;
}
static void
x11_image_finish(struct x11_swapchain *chain,
const VkAllocationCallbacks* pAllocator,
struct x11_image *image)
{
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);
cookie = xcb_free_pixmap(chain->conn, image->pixmap);
xcb_discard_reply(chain->conn, cookie.sequence);
chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
image->image, image->memory);
}
static VkResult
x11_swapchain_destroy(struct anv_swapchain *anv_chain,
const VkAllocationCallbacks *pAllocator)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
for (uint32_t i = 0; i < chain->image_count; i++)
x11_image_finish(chain, pAllocator, &chain->images[i]);
xcb_unregister_for_special_event(chain->conn, chain->special_event);
vk_free(pAllocator, chain);
return VK_SUCCESS;
}
static VkResult
x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
VkDevice device,
struct anv_wsi_device *wsi_device,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks* pAllocator,
const struct anv_wsi_image_fns *image_fns,
struct anv_swapchain **swapchain_out)
{
struct x11_swapchain *chain;
xcb_void_cookie_t cookie;
VkResult result;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
int num_images = pCreateInfo->minImageCount;
/* 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 Wayland compositor
* 4) One to render to
*/
if (pCreateInfo->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
num_images = MAX2(num_images, 4);
size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
chain = vk_alloc(pAllocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (chain == NULL)
return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
chain->base.device = device;
chain->base.destroy = x11_swapchain_destroy;
chain->base.get_images = x11_get_images;
chain->base.acquire_next_image = x11_acquire_next_image;
chain->base.queue_present = x11_queue_present;
chain->base.image_fns = image_fns;
chain->conn = x11_surface_get_connection(icd_surface);
chain->window = x11_surface_get_window(icd_surface);
chain->extent = pCreateInfo->imageExtent;
chain->image_count = num_images;
chain->send_sbc = 0;
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 |
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
/* Create an XCB event queue to hold present events outside of the usual
* application event queue
*/
chain->special_event =
xcb_register_for_special_xge(chain->conn, &xcb_present_id,
chain->event_id, NULL);
chain->gc = xcb_generate_id(chain->conn);
if (!chain->gc) {
/* FINISHME: Choose a better error. */
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail_register;
}
cookie = xcb_create_gc(chain->conn,
chain->gc,
chain->window,
XCB_GC_GRAPHICS_EXPOSURES,
(uint32_t []) { 0 });
xcb_discard_reply(chain->conn, cookie.sequence);
uint32_t image = 0;
for (; image < chain->image_count; image++) {
result = x11_image_init(device, chain, pCreateInfo, pAllocator,
&chain->images[image]);
if (result != VK_SUCCESS)
goto fail_init_images;
}
*swapchain_out = &chain->base;
return VK_SUCCESS;
fail_init_images:
for (uint32_t j = 0; j < image; j++)
x11_image_finish(chain, pAllocator, &chain->images[j]);
fail_register:
xcb_unregister_for_special_event(chain->conn, chain->special_event);
vk_free(pAllocator, chain);
return result;
}
VkResult
anv_x11_init_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_x11 *wsi;
VkResult result;
wsi = vk_alloc(alloc, sizeof(*wsi), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi) {
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail;
}
int ret = pthread_mutex_init(&wsi->mutex, NULL);
if (ret != 0) {
if (ret == ENOMEM) {
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
} else {
/* FINISHME: Choose a better error. */
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
}
goto fail_alloc;
}
wsi->connections = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
_mesa_key_pointer_equal);
if (!wsi->connections) {
result = vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
goto fail_mutex;
}
wsi->base.get_support = x11_surface_get_support;
wsi->base.get_capabilities = x11_surface_get_capabilities;
wsi->base.get_formats = x11_surface_get_formats;
wsi->base.get_present_modes = x11_surface_get_present_modes;
wsi->base.create_swapchain = x11_surface_create_swapchain;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = &wsi->base;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XLIB] = &wsi->base;
return VK_SUCCESS;
fail_mutex:
pthread_mutex_destroy(&wsi->mutex);
fail_alloc:
vk_free(alloc, wsi);
fail:
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = NULL;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XLIB] = NULL;
return result;
}
void
anv_x11_finish_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_x11 *wsi =
(struct wsi_x11 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB];
if (wsi) {
_mesa_hash_table_destroy(wsi->connections, NULL);
pthread_mutex_destroy(&wsi->mutex);
vk_free(alloc, wsi);
}
}

View file

@ -20,14 +20,17 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef WSI_COMMON_H
#define WSI_COMMON_H
#ifndef ANV_WSI_H
#define ANV_WSI_H
#include <stdint.h>
#include <stdbool.h>
#include "anv_private.h"
struct anv_swapchain;
#include "util/vk_alloc.h"
#include <vulkan/vulkan.h>
#include <vulkan/vk_icd.h>
struct anv_wsi_device;
struct anv_wsi_image_fns {
VkResult (*create_wsi_image)(VkDevice device_h,
const VkSwapchainCreateInfoKHR *pCreateInfo,
@ -44,6 +47,24 @@ struct anv_wsi_image_fns {
VkDeviceMemory memory_h);
};
struct anv_swapchain {
VkDevice device;
VkAllocationCallbacks alloc;
const struct anv_wsi_image_fns *image_fns;
VkFence fences[3];
VkResult (*destroy)(struct anv_swapchain *swapchain,
const VkAllocationCallbacks *pAllocator);
VkResult (*get_images)(struct anv_swapchain *swapchain,
uint32_t *pCount, VkImage *pSwapchainImages);
VkResult (*acquire_next_image)(struct anv_swapchain *swap_chain,
uint64_t timeout, VkSemaphore semaphore,
uint32_t *image_index);
VkResult (*queue_present)(struct anv_swapchain *swap_chain,
uint32_t image_index);
};
struct anv_wsi_interface {
VkResult (*get_support)(VkIcdSurfaceBase *surface,
struct anv_wsi_device *wsi_device,
@ -68,42 +89,43 @@ struct anv_wsi_interface {
struct anv_swapchain **swapchain);
};
struct anv_swapchain {
#define VK_ICD_WSI_PLATFORM_MAX 5
VkDevice device;
VkAllocationCallbacks alloc;
const struct anv_wsi_image_fns *image_fns;
VkFence fences[3];
VkResult (*destroy)(struct anv_swapchain *swapchain,
const VkAllocationCallbacks *pAllocator);
VkResult (*get_images)(struct anv_swapchain *swapchain,
uint32_t *pCount, VkImage *pSwapchainImages);
VkResult (*acquire_next_image)(struct anv_swapchain *swap_chain,
uint64_t timeout, VkSemaphore semaphore,
uint32_t *image_index);
VkResult (*queue_present)(struct anv_swapchain *swap_chain,
uint32_t image_index);
struct anv_wsi_device {
struct anv_wsi_interface * wsi[VK_ICD_WSI_PLATFORM_MAX];
};
ANV_DEFINE_NONDISP_HANDLE_CASTS(_VkIcdSurfaceBase, VkSurfaceKHR)
ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_swapchain, VkSwapchainKHR)
struct anv_wsi_callbacks {
void (*get_phys_device_format_properties)(VkPhysicalDevice physicalDevice,
VkFormat format,
VkFormatProperties *pFormatProperties);
};
#define WSI_DEFINE_NONDISP_HANDLE_CASTS(__wsi_type, __VkType) \
\
static inline struct __wsi_type * \
__wsi_type ## _from_handle(__VkType _handle) \
{ \
return (struct __wsi_type *)(uintptr_t) _handle; \
} \
\
static inline __VkType \
__wsi_type ## _to_handle(struct __wsi_type *_obj) \
{ \
return (__VkType)(uintptr_t) _obj; \
}
WSI_DEFINE_NONDISP_HANDLE_CASTS(_VkIcdSurfaceBase, VkSurfaceKHR)
WSI_DEFINE_NONDISP_HANDLE_CASTS(anv_swapchain, VkSwapchainKHR)
VkResult anv_x11_init_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc);
void anv_x11_finish_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc);
VkResult anv_wl_init_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device,
const struct anv_wsi_callbacks *cbs);
void anv_wl_finish_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc);
#endif /* ANV_WSI_H */
#endif

View file

@ -0,0 +1,827 @@
/*
* Copyright © 2015 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <wayland-client.h>
#include <wayland-drm-client-protocol.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "wsi_common_wayland.h"
#include "vk_format_info.h"
#include <util/hash_table.h>
#include <util/u_vector.h>
#define typed_memcpy(dest, src, count) ({ \
static_assert(sizeof(*src) == sizeof(*dest), ""); \
memcpy((dest), (src), (count) * sizeof(*(src))); \
})
#define MIN_NUM_IMAGES 2
struct wsi_wayland;
struct wsi_wl_display {
struct wl_display * display;
struct wl_drm * drm;
struct wsi_wayland *wsi_wl;
/* Vector of VkFormats supported */
struct u_vector formats;
uint32_t capabilities;
};
struct wsi_wayland {
struct anv_wsi_interface base;
const VkAllocationCallbacks *alloc;
VkPhysicalDevice physical_device;
pthread_mutex_t mutex;
/* Hash table of wl_display -> wsi_wl_display mappings */
struct hash_table * displays;
const struct anv_wsi_callbacks *cbs;
};
static void
wsi_wl_display_add_vk_format(struct wsi_wl_display *display, VkFormat format)
{
/* Don't add a format that's already in the list */
VkFormat *f;
u_vector_foreach(f, &display->formats)
if (*f == format)
return;
/* Don't add formats that aren't renderable. */
VkFormatProperties props;
display->wsi_wl->cbs->get_phys_device_format_properties(display->wsi_wl->physical_device,
format, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
return;
f = u_vector_add(&display->formats);
if (f)
*f = format;
}
static void
drm_handle_device(void *data, struct wl_drm *drm, const char *name)
{
fprintf(stderr, "wl_drm.device(%s)\n", name);
}
static uint32_t
wl_drm_format_for_vk_format(VkFormat vk_format, bool alpha)
{
switch (vk_format) {
/* TODO: Figure out what all the formats mean and make this table
* correct.
*/
#if 0
case VK_FORMAT_R4G4B4A4_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR4444 : WL_DRM_FORMAT_XBGR4444;
case VK_FORMAT_R5G6B5_UNORM:
return WL_DRM_FORMAT_BGR565;
case VK_FORMAT_R5G5B5A1_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR1555 : WL_DRM_FORMAT_XBGR1555;
case VK_FORMAT_R8G8B8_UNORM:
return WL_DRM_FORMAT_XBGR8888;
case VK_FORMAT_R8G8B8A8_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR8888 : WL_DRM_FORMAT_XBGR8888;
case VK_FORMAT_R10G10B10A2_UNORM:
return alpha ? WL_DRM_FORMAT_ABGR2101010 : WL_DRM_FORMAT_XBGR2101010;
case VK_FORMAT_B4G4R4A4_UNORM:
return alpha ? WL_DRM_FORMAT_ARGB4444 : WL_DRM_FORMAT_XRGB4444;
case VK_FORMAT_B5G6R5_UNORM:
return WL_DRM_FORMAT_RGB565;
case VK_FORMAT_B5G5R5A1_UNORM:
return alpha ? WL_DRM_FORMAT_XRGB1555 : WL_DRM_FORMAT_XRGB1555;
#endif
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SRGB:
return WL_DRM_FORMAT_BGRX8888;
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SRGB:
return alpha ? WL_DRM_FORMAT_ARGB8888 : WL_DRM_FORMAT_XRGB8888;
#if 0
case VK_FORMAT_B10G10R10A2_UNORM:
return alpha ? WL_DRM_FORMAT_ARGB2101010 : WL_DRM_FORMAT_XRGB2101010;
#endif
default:
assert(!"Unsupported Vulkan format");
return 0;
}
}
static void
drm_handle_format(void *data, struct wl_drm *drm, uint32_t wl_format)
{
struct wsi_wl_display *display = data;
switch (wl_format) {
#if 0
case WL_DRM_FORMAT_ABGR4444:
case WL_DRM_FORMAT_XBGR4444:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R4G4B4A4_UNORM);
break;
case WL_DRM_FORMAT_BGR565:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R5G6B5_UNORM);
break;
case WL_DRM_FORMAT_ABGR1555:
case WL_DRM_FORMAT_XBGR1555:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R5G5B5A1_UNORM);
break;
case WL_DRM_FORMAT_XBGR8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R8G8B8_UNORM);
/* fallthrough */
case WL_DRM_FORMAT_ABGR8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R8G8B8A8_UNORM);
break;
case WL_DRM_FORMAT_ABGR2101010:
case WL_DRM_FORMAT_XBGR2101010:
wsi_wl_display_add_vk_format(display, VK_FORMAT_R10G10B10A2_UNORM);
break;
case WL_DRM_FORMAT_ARGB4444:
case WL_DRM_FORMAT_XRGB4444:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B4G4R4A4_UNORM);
break;
case WL_DRM_FORMAT_RGB565:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B5G6R5_UNORM);
break;
case WL_DRM_FORMAT_ARGB1555:
case WL_DRM_FORMAT_XRGB1555:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B5G5R5A1_UNORM);
break;
#endif
case WL_DRM_FORMAT_XRGB8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8_SRGB);
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8_UNORM);
/* fallthrough */
case WL_DRM_FORMAT_ARGB8888:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8A8_SRGB);
wsi_wl_display_add_vk_format(display, VK_FORMAT_B8G8R8A8_UNORM);
break;
#if 0
case WL_DRM_FORMAT_ARGB2101010:
case WL_DRM_FORMAT_XRGB2101010:
wsi_wl_display_add_vk_format(display, VK_FORMAT_B10G10R10A2_UNORM);
break;
#endif
}
}
static void
drm_handle_authenticated(void *data, struct wl_drm *drm)
{
}
static void
drm_handle_capabilities(void *data, struct wl_drm *drm, uint32_t capabilities)
{
struct wsi_wl_display *display = data;
display->capabilities = capabilities;
}
static const struct wl_drm_listener drm_listener = {
drm_handle_device,
drm_handle_format,
drm_handle_authenticated,
drm_handle_capabilities,
};
static void
registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version)
{
struct wsi_wl_display *display = data;
if (strcmp(interface, "wl_drm") == 0) {
assert(display->drm == NULL);
assert(version >= 2);
display->drm = wl_registry_bind(registry, name, &wl_drm_interface, 2);
if (display->drm)
wl_drm_add_listener(display->drm, &drm_listener, display);
}
}
static void
registry_handle_global_remove(void *data, struct wl_registry *registry,
uint32_t name)
{ /* No-op */ }
static const struct wl_registry_listener registry_listener = {
registry_handle_global,
registry_handle_global_remove
};
static void
wsi_wl_display_destroy(struct wsi_wayland *wsi, struct wsi_wl_display *display)
{
u_vector_finish(&display->formats);
if (display->drm)
wl_drm_destroy(display->drm);
vk_free(wsi->alloc, display);
}
static struct wsi_wl_display *
wsi_wl_display_create(struct wsi_wayland *wsi, struct wl_display *wl_display)
{
struct wsi_wl_display *display =
vk_alloc(wsi->alloc, sizeof(*display), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!display)
return NULL;
memset(display, 0, sizeof(*display));
display->display = wl_display;
display->wsi_wl = wsi;
if (!u_vector_init(&display->formats, sizeof(VkFormat), 8))
goto fail;
struct wl_registry *registry = wl_display_get_registry(wl_display);
if (!registry)
return NULL;
wl_registry_add_listener(registry, &registry_listener, display);
/* Round-rip to get the wl_drm global */
wl_display_roundtrip(wl_display);
if (!display->drm)
goto fail;
/* Round-rip to get wl_drm formats and capabilities */
wl_display_roundtrip(wl_display);
/* We need prime support */
if (!(display->capabilities & WL_DRM_CAPABILITY_PRIME))
goto fail;
/* We don't need this anymore */
wl_registry_destroy(registry);
return display;
fail:
if (registry)
wl_registry_destroy(registry);
wsi_wl_display_destroy(wsi, display);
return NULL;
}
static struct wsi_wl_display *
wsi_wl_get_display(struct anv_wsi_device *wsi_device,
struct wl_display *wl_display)
{
struct wsi_wayland *wsi =
(struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND];
pthread_mutex_lock(&wsi->mutex);
struct hash_entry *entry = _mesa_hash_table_search(wsi->displays,
wl_display);
if (!entry) {
/* We're about to make a bunch of blocking calls. Let's drop the
* mutex for now so we don't block up too badly.
*/
pthread_mutex_unlock(&wsi->mutex);
struct wsi_wl_display *display = wsi_wl_display_create(wsi, wl_display);
pthread_mutex_lock(&wsi->mutex);
entry = _mesa_hash_table_search(wsi->displays, wl_display);
if (entry) {
/* Oops, someone raced us to it */
wsi_wl_display_destroy(wsi, display);
} else {
entry = _mesa_hash_table_insert(wsi->displays, wl_display, display);
}
}
pthread_mutex_unlock(&wsi->mutex);
return entry->data;
}
VkBool32
wsi_wl_get_presentation_support(struct anv_wsi_device *wsi_device,
struct wl_display *wl_display)
{
return wsi_wl_get_display(wsi_device, wl_display) != NULL;
}
static VkResult
wsi_wl_surface_get_support(VkIcdSurfaceBase *surface,
struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
VkBool32* pSupported)
{
*pSupported = true;
return VK_SUCCESS;
}
static const VkPresentModeKHR present_modes[] = {
VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_FIFO_KHR,
};
static VkResult
wsi_wl_surface_get_capabilities(VkIcdSurfaceBase *surface,
VkSurfaceCapabilitiesKHR* caps)
{
caps->minImageCount = MIN_NUM_IMAGES;
caps->maxImageCount = 4;
caps->currentExtent = (VkExtent2D) { -1, -1 };
caps->minImageExtent = (VkExtent2D) { 1, 1 };
caps->maxImageExtent = (VkExtent2D) { INT16_MAX, INT16_MAX };
caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->maxImageArrayLayers = 1;
caps->supportedCompositeAlpha =
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
caps->supportedUsageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return VK_SUCCESS;
}
static VkResult
wsi_wl_surface_get_formats(VkIcdSurfaceBase *icd_surface,
struct anv_wsi_device *wsi_device,
uint32_t* pSurfaceFormatCount,
VkSurfaceFormatKHR* pSurfaceFormats)
{
VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface;
struct wsi_wl_display *display =
wsi_wl_get_display(wsi_device, surface->display);
uint32_t count = u_vector_length(&display->formats);
if (pSurfaceFormats == NULL) {
*pSurfaceFormatCount = count;
return VK_SUCCESS;
}
assert(*pSurfaceFormatCount >= count);
*pSurfaceFormatCount = count;
VkFormat *f;
u_vector_foreach(f, &display->formats) {
*(pSurfaceFormats++) = (VkSurfaceFormatKHR) {
.format = *f,
/* TODO: We should get this from the compositor somehow */
.colorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
};
}
return VK_SUCCESS;
}
static VkResult
wsi_wl_surface_get_present_modes(VkIcdSurfaceBase *surface,
uint32_t* pPresentModeCount,
VkPresentModeKHR* pPresentModes)
{
if (pPresentModes == NULL) {
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
assert(*pPresentModeCount >= ARRAY_SIZE(present_modes));
typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
VkResult anv_create_wl_surface(const VkAllocationCallbacks *pAllocator,
const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface)
{
VkIcdSurfaceWayland *surface;
surface = vk_alloc(pAllocator, sizeof *surface, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
surface->base.platform = VK_ICD_WSI_PLATFORM_WAYLAND;
surface->display = pCreateInfo->display;
surface->surface = pCreateInfo->surface;
*pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
return VK_SUCCESS;
}
struct wsi_wl_image {
VkImage image;
VkDeviceMemory memory;
struct wl_buffer * buffer;
bool busy;
};
struct wsi_wl_swapchain {
struct anv_swapchain base;
struct wsi_wl_display * display;
struct wl_event_queue * queue;
struct wl_surface * surface;
VkExtent2D extent;
VkFormat vk_format;
uint32_t drm_format;
VkPresentModeKHR present_mode;
bool fifo_ready;
uint32_t image_count;
struct wsi_wl_image images[0];
};
static VkResult
wsi_wl_swapchain_get_images(struct anv_swapchain *anv_chain,
uint32_t *pCount, VkImage *pSwapchainImages)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
if (pSwapchainImages == NULL) {
*pCount = chain->image_count;
return VK_SUCCESS;
}
assert(chain->image_count <= *pCount);
for (uint32_t i = 0; i < chain->image_count; i++)
pSwapchainImages[i] = chain->images[i].image;
*pCount = chain->image_count;
return VK_SUCCESS;
}
static VkResult
wsi_wl_swapchain_acquire_next_image(struct anv_swapchain *anv_chain,
uint64_t timeout,
VkSemaphore semaphore,
uint32_t *image_index)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
int ret = wl_display_dispatch_queue_pending(chain->display->display,
chain->queue);
/* XXX: I'm not sure if out-of-date is the right error here. If
* wl_display_dispatch_queue_pending fails it most likely means we got
* kicked by the server so this seems more-or-less correct.
*/
if (ret < 0)
return VK_ERROR_OUT_OF_DATE_KHR;
while (1) {
for (uint32_t i = 0; i < chain->image_count; i++) {
if (!chain->images[i].busy) {
/* We found a non-busy image */
*image_index = i;
chain->images[i].busy = true;
return VK_SUCCESS;
}
}
/* This time we do a blocking dispatch because we can't go
* anywhere until we get an event.
*/
int ret = wl_display_roundtrip_queue(chain->display->display,
chain->queue);
if (ret < 0)
return VK_ERROR_OUT_OF_DATE_KHR;
}
}
static void
frame_handle_done(void *data, struct wl_callback *callback, uint32_t serial)
{
struct wsi_wl_swapchain *chain = data;
chain->fifo_ready = true;
wl_callback_destroy(callback);
}
static const struct wl_callback_listener frame_listener = {
frame_handle_done,
};
static VkResult
wsi_wl_swapchain_queue_present(struct anv_swapchain *anv_chain,
uint32_t image_index)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
while (!chain->fifo_ready) {
int ret = wl_display_dispatch_queue(chain->display->display,
chain->queue);
if (ret < 0)
return VK_ERROR_OUT_OF_DATE_KHR;
}
}
assert(image_index < chain->image_count);
wl_surface_attach(chain->surface, chain->images[image_index].buffer, 0, 0);
wl_surface_damage(chain->surface, 0, 0, INT32_MAX, INT32_MAX);
if (chain->present_mode == VK_PRESENT_MODE_FIFO_KHR) {
struct wl_callback *frame = wl_surface_frame(chain->surface);
wl_proxy_set_queue((struct wl_proxy *)frame, chain->queue);
wl_callback_add_listener(frame, &frame_listener, chain);
chain->fifo_ready = false;
}
chain->images[image_index].busy = true;
wl_surface_commit(chain->surface);
wl_display_flush(chain->display->display);
return VK_SUCCESS;
}
static void
buffer_handle_release(void *data, struct wl_buffer *buffer)
{
struct wsi_wl_image *image = data;
assert(image->buffer == buffer);
image->busy = false;
}
static const struct wl_buffer_listener buffer_listener = {
buffer_handle_release,
};
static VkResult
wsi_wl_image_init(struct wsi_wl_swapchain *chain,
struct wsi_wl_image *image,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks* pAllocator)
{
VkDevice vk_device = chain->base.device;
VkResult result;
int fd;
uint32_t size;
uint32_t row_pitch;
uint32_t offset;
result = chain->base.image_fns->create_wsi_image(vk_device,
pCreateInfo,
pAllocator,
&image->image,
&image->memory,
&size,
&offset,
&row_pitch,
&fd);
if (result != VK_SUCCESS)
return result;
image->buffer = wl_drm_create_prime_buffer(chain->display->drm,
fd, /* name */
chain->extent.width,
chain->extent.height,
chain->drm_format,
offset,
row_pitch,
0, 0, 0, 0 /* unused */);
wl_display_roundtrip(chain->display->display);
close(fd);
wl_proxy_set_queue((struct wl_proxy *)image->buffer, chain->queue);
wl_buffer_add_listener(image->buffer, &buffer_listener, image);
return VK_SUCCESS;
chain->base.image_fns->free_wsi_image(vk_device, pAllocator,
image->image, image->memory);
return result;
}
static VkResult
wsi_wl_swapchain_destroy(struct anv_swapchain *anv_chain,
const VkAllocationCallbacks *pAllocator)
{
struct wsi_wl_swapchain *chain = (struct wsi_wl_swapchain *)anv_chain;
for (uint32_t i = 0; i < chain->image_count; i++) {
if (chain->images[i].buffer)
chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
chain->images[i].image,
chain->images[i].memory);
}
vk_free(pAllocator, chain);
return VK_SUCCESS;
}
static VkResult
wsi_wl_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
VkDevice device,
struct anv_wsi_device *wsi_device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
const struct anv_wsi_image_fns *image_fns,
struct anv_swapchain **swapchain_out)
{
VkIcdSurfaceWayland *surface = (VkIcdSurfaceWayland *)icd_surface;
struct wsi_wl_swapchain *chain;
VkResult result;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
int num_images = pCreateInfo->minImageCount;
assert(num_images >= MIN_NUM_IMAGES);
/* 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 Wayland compositor
* 4) One to render to
*/
if (pCreateInfo->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
num_images = MAX2(num_images, 4);
size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
chain = vk_alloc(pAllocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (chain == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
chain->base.device = device;
chain->base.destroy = wsi_wl_swapchain_destroy;
chain->base.get_images = wsi_wl_swapchain_get_images;
chain->base.acquire_next_image = wsi_wl_swapchain_acquire_next_image;
chain->base.queue_present = wsi_wl_swapchain_queue_present;
chain->base.image_fns = image_fns;
chain->surface = surface->surface;
chain->extent = pCreateInfo->imageExtent;
chain->vk_format = pCreateInfo->imageFormat;
chain->drm_format = wl_drm_format_for_vk_format(chain->vk_format, false);
chain->present_mode = pCreateInfo->presentMode;
chain->fifo_ready = true;
chain->image_count = num_images;
/* Mark a bunch of stuff as NULL. This way we can just call
* destroy_swapchain for cleanup.
*/
for (uint32_t i = 0; i < chain->image_count; i++)
chain->images[i].buffer = NULL;
chain->queue = NULL;
chain->display = wsi_wl_get_display(wsi_device,
surface->display);
if (!chain->display) {
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
chain->queue = wl_display_create_queue(chain->display->display);
if (!chain->queue) {
result = VK_ERROR_INITIALIZATION_FAILED;
goto fail;
}
for (uint32_t i = 0; i < chain->image_count; i++) {
result = wsi_wl_image_init(chain, &chain->images[i],
pCreateInfo, pAllocator);
if (result != VK_SUCCESS)
goto fail;
chain->images[i].busy = false;
}
*swapchain_out = &chain->base;
return VK_SUCCESS;
fail:
wsi_wl_swapchain_destroy(&chain->base, pAllocator);
return result;
}
VkResult
anv_wl_init_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
VkPhysicalDevice physical_device,
const struct anv_wsi_callbacks *cbs)
{
struct wsi_wayland *wsi;
VkResult result;
wsi = vk_alloc(alloc, sizeof(*wsi), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
wsi->physical_device = physical_device;
wsi->alloc = alloc;
wsi->cbs = cbs;
int ret = pthread_mutex_init(&wsi->mutex, NULL);
if (ret != 0) {
if (ret == ENOMEM) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
} else {
/* FINISHME: Choose a better error. */
result = VK_ERROR_OUT_OF_HOST_MEMORY;
}
goto fail_alloc;
}
wsi->displays = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
_mesa_key_pointer_equal);
if (!wsi->displays) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_mutex;
}
wsi->base.get_support = wsi_wl_surface_get_support;
wsi->base.get_capabilities = wsi_wl_surface_get_capabilities;
wsi->base.get_formats = wsi_wl_surface_get_formats;
wsi->base.get_present_modes = wsi_wl_surface_get_present_modes;
wsi->base.create_swapchain = wsi_wl_surface_create_swapchain;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND] = &wsi->base;
return VK_SUCCESS;
fail_mutex:
pthread_mutex_destroy(&wsi->mutex);
fail_alloc:
vk_free(alloc, wsi);
fail:
wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND] = NULL;
return result;
}
void
anv_wl_finish_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_wayland *wsi =
(struct wsi_wayland *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_WAYLAND];
if (wsi) {
_mesa_hash_table_destroy(wsi->displays, NULL);
pthread_mutex_destroy(&wsi->mutex);
vk_free(alloc, wsi);
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright © 2015 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef WSI_COMMON_WAYLAND_H
#define WSI_COMMON_WAYLAND_H
#include "wsi_common.h"
VkBool32
wsi_wl_get_presentation_support(struct anv_wsi_device *wsi_device,
struct wl_display *wl_display);
VkResult anv_create_wl_surface(const VkAllocationCallbacks *pAllocator,
const VkWaylandSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface);
#endif

View file

@ -0,0 +1,884 @@
/*
* Copyright © 2015 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <X11/Xlib-xcb.h>
#include <X11/xshmfence.h>
#include <xcb/xcb.h>
#include <xcb/dri3.h>
#include <xcb/present.h>
#include "util/hash_table.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "wsi_common.h"
#include "wsi_common_x11.h"
#define typed_memcpy(dest, src, count) ({ \
static_assert(sizeof(*src) == sizeof(*dest), ""); \
memcpy((dest), (src), (count) * sizeof(*(src))); \
})
struct wsi_x11_connection {
bool has_dri3;
bool has_present;
};
struct wsi_x11 {
struct anv_wsi_interface base;
pthread_mutex_t mutex;
/* Hash table of xcb_connection -> wsi_x11_connection mappings */
struct hash_table *connections;
};
static struct wsi_x11_connection *
wsi_x11_connection_create(const VkAllocationCallbacks *alloc,
xcb_connection_t *conn)
{
xcb_query_extension_cookie_t dri3_cookie, pres_cookie;
xcb_query_extension_reply_t *dri3_reply, *pres_reply;
struct wsi_x11_connection *wsi_conn =
vk_alloc(alloc, sizeof(*wsi_conn), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi_conn)
return NULL;
dri3_cookie = xcb_query_extension(conn, 4, "DRI3");
pres_cookie = xcb_query_extension(conn, 7, "PRESENT");
dri3_reply = xcb_query_extension_reply(conn, dri3_cookie, NULL);
pres_reply = xcb_query_extension_reply(conn, pres_cookie, NULL);
if (dri3_reply == NULL || pres_reply == NULL) {
free(dri3_reply);
free(pres_reply);
vk_free(alloc, wsi_conn);
return NULL;
}
wsi_conn->has_dri3 = dri3_reply->present != 0;
wsi_conn->has_present = pres_reply->present != 0;
free(dri3_reply);
free(pres_reply);
return wsi_conn;
}
static void
wsi_x11_connection_destroy(const VkAllocationCallbacks *alloc,
struct wsi_x11_connection *conn)
{
vk_free(alloc, conn);
}
static struct wsi_x11_connection *
wsi_x11_get_connection(struct anv_wsi_device *wsi_dev,
const VkAllocationCallbacks *alloc,
xcb_connection_t *conn)
{
struct wsi_x11 *wsi =
(struct wsi_x11 *)wsi_dev->wsi[VK_ICD_WSI_PLATFORM_XCB];
pthread_mutex_lock(&wsi->mutex);
struct hash_entry *entry = _mesa_hash_table_search(wsi->connections, conn);
if (!entry) {
/* We're about to make a bunch of blocking calls. Let's drop the
* mutex for now so we don't block up too badly.
*/
pthread_mutex_unlock(&wsi->mutex);
struct wsi_x11_connection *wsi_conn =
wsi_x11_connection_create(alloc, conn);
pthread_mutex_lock(&wsi->mutex);
entry = _mesa_hash_table_search(wsi->connections, conn);
if (entry) {
/* Oops, someone raced us to it */
wsi_x11_connection_destroy(alloc, wsi_conn);
} else {
entry = _mesa_hash_table_insert(wsi->connections, conn, wsi_conn);
}
}
pthread_mutex_unlock(&wsi->mutex);
return entry->data;
}
static const VkSurfaceFormatKHR formats[] = {
{ .format = VK_FORMAT_B8G8R8A8_SRGB, },
{ .format = VK_FORMAT_B8G8R8A8_UNORM, },
};
static const VkPresentModeKHR present_modes[] = {
VK_PRESENT_MODE_MAILBOX_KHR,
};
static xcb_screen_t *
get_screen_for_root(xcb_connection_t *conn, xcb_window_t root)
{
xcb_screen_iterator_t screen_iter =
xcb_setup_roots_iterator(xcb_get_setup(conn));
for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
if (screen_iter.data->root == root)
return screen_iter.data;
}
return NULL;
}
static xcb_visualtype_t *
screen_get_visualtype(xcb_screen_t *screen, xcb_visualid_t visual_id,
unsigned *depth)
{
xcb_depth_iterator_t depth_iter =
xcb_screen_allowed_depths_iterator(screen);
for (; depth_iter.rem; xcb_depth_next (&depth_iter)) {
xcb_visualtype_iterator_t visual_iter =
xcb_depth_visuals_iterator (depth_iter.data);
for (; visual_iter.rem; xcb_visualtype_next (&visual_iter)) {
if (visual_iter.data->visual_id == visual_id) {
if (depth)
*depth = depth_iter.data->depth;
return visual_iter.data;
}
}
}
return NULL;
}
static xcb_visualtype_t *
connection_get_visualtype(xcb_connection_t *conn, xcb_visualid_t visual_id,
unsigned *depth)
{
xcb_screen_iterator_t screen_iter =
xcb_setup_roots_iterator(xcb_get_setup(conn));
/* For this we have to iterate over all of the screens which is rather
* annoying. Fortunately, there is probably only 1.
*/
for (; screen_iter.rem; xcb_screen_next (&screen_iter)) {
xcb_visualtype_t *visual = screen_get_visualtype(screen_iter.data,
visual_id, depth);
if (visual)
return visual;
}
return NULL;
}
static xcb_visualtype_t *
get_visualtype_for_window(xcb_connection_t *conn, xcb_window_t window,
unsigned *depth)
{
xcb_query_tree_cookie_t tree_cookie;
xcb_get_window_attributes_cookie_t attrib_cookie;
xcb_query_tree_reply_t *tree;
xcb_get_window_attributes_reply_t *attrib;
tree_cookie = xcb_query_tree(conn, window);
attrib_cookie = xcb_get_window_attributes(conn, window);
tree = xcb_query_tree_reply(conn, tree_cookie, NULL);
attrib = xcb_get_window_attributes_reply(conn, attrib_cookie, NULL);
if (attrib == NULL || tree == NULL) {
free(attrib);
free(tree);
return NULL;
}
xcb_window_t root = tree->root;
xcb_visualid_t visual_id = attrib->visual;
free(attrib);
free(tree);
xcb_screen_t *screen = get_screen_for_root(conn, root);
if (screen == NULL)
return NULL;
return screen_get_visualtype(screen, visual_id, depth);
}
static bool
visual_has_alpha(xcb_visualtype_t *visual, unsigned depth)
{
uint32_t rgb_mask = visual->red_mask |
visual->green_mask |
visual->blue_mask;
uint32_t all_mask = 0xffffffff >> (32 - depth);
/* Do we have bits left over after RGB? */
return (all_mask & ~rgb_mask) != 0;
}
VkBool32 anv_get_physical_device_xcb_presentation_support(
struct anv_wsi_device *wsi_device,
VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
xcb_connection_t* connection,
xcb_visualid_t visual_id)
{
struct wsi_x11_connection *wsi_conn =
wsi_x11_get_connection(wsi_device, alloc, connection);
if (!wsi_conn->has_dri3) {
fprintf(stderr, "vulkan: No DRI3 support\n");
return false;
}
unsigned visual_depth;
if (!connection_get_visualtype(connection, visual_id, &visual_depth))
return false;
if (visual_depth != 24 && visual_depth != 32)
return false;
return true;
}
static xcb_connection_t*
x11_surface_get_connection(VkIcdSurfaceBase *icd_surface)
{
if (icd_surface->platform == VK_ICD_WSI_PLATFORM_XLIB)
return XGetXCBConnection(((VkIcdSurfaceXlib *)icd_surface)->dpy);
else
return ((VkIcdSurfaceXcb *)icd_surface)->connection;
}
static xcb_window_t
x11_surface_get_window(VkIcdSurfaceBase *icd_surface)
{
if (icd_surface->platform == VK_ICD_WSI_PLATFORM_XLIB)
return ((VkIcdSurfaceXlib *)icd_surface)->window;
else
return ((VkIcdSurfaceXcb *)icd_surface)->window;
}
static VkResult
x11_surface_get_support(VkIcdSurfaceBase *icd_surface,
struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
VkBool32* pSupported)
{
xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
xcb_window_t window = x11_surface_get_window(icd_surface);
struct wsi_x11_connection *wsi_conn =
wsi_x11_get_connection(wsi_device, alloc, conn);
if (!wsi_conn)
return VK_ERROR_OUT_OF_HOST_MEMORY;
if (!wsi_conn->has_dri3) {
fprintf(stderr, "vulkan: No DRI3 support\n");
*pSupported = false;
return VK_SUCCESS;
}
unsigned visual_depth;
if (!get_visualtype_for_window(conn, window, &visual_depth)) {
*pSupported = false;
return VK_SUCCESS;
}
if (visual_depth != 24 && visual_depth != 32) {
*pSupported = false;
return VK_SUCCESS;
}
*pSupported = true;
return VK_SUCCESS;
}
static VkResult
x11_surface_get_capabilities(VkIcdSurfaceBase *icd_surface,
VkSurfaceCapabilitiesKHR *caps)
{
xcb_connection_t *conn = x11_surface_get_connection(icd_surface);
xcb_window_t window = x11_surface_get_window(icd_surface);
xcb_get_geometry_cookie_t geom_cookie;
xcb_generic_error_t *err;
xcb_get_geometry_reply_t *geom;
unsigned visual_depth;
geom_cookie = xcb_get_geometry(conn, window);
/* This does a round-trip. This is why we do get_geometry first and
* wait to read the reply until after we have a visual.
*/
xcb_visualtype_t *visual =
get_visualtype_for_window(conn, window, &visual_depth);
geom = xcb_get_geometry_reply(conn, geom_cookie, &err);
if (geom) {
VkExtent2D extent = { geom->width, geom->height };
caps->currentExtent = extent;
caps->minImageExtent = extent;
caps->maxImageExtent = extent;
} 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.
*/
caps->currentExtent = (VkExtent2D) { -1, -1 };
caps->minImageExtent = (VkExtent2D) { 1, 1 };
caps->maxImageExtent = (VkExtent2D) { INT16_MAX, INT16_MAX };
}
free(err);
free(geom);
if (visual_has_alpha(visual, visual_depth)) {
caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
} else {
caps->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR |
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
}
caps->minImageCount = 2;
caps->maxImageCount = 4;
caps->supportedTransforms = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->currentTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
caps->maxImageArrayLayers = 1;
caps->supportedUsageFlags =
VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
return VK_SUCCESS;
}
static VkResult
x11_surface_get_formats(VkIcdSurfaceBase *surface,
struct anv_wsi_device *wsi_device,
uint32_t *pSurfaceFormatCount,
VkSurfaceFormatKHR *pSurfaceFormats)
{
if (pSurfaceFormats == NULL) {
*pSurfaceFormatCount = ARRAY_SIZE(formats);
return VK_SUCCESS;
}
assert(*pSurfaceFormatCount >= ARRAY_SIZE(formats));
typed_memcpy(pSurfaceFormats, formats, *pSurfaceFormatCount);
*pSurfaceFormatCount = ARRAY_SIZE(formats);
return VK_SUCCESS;
}
static VkResult
x11_surface_get_present_modes(VkIcdSurfaceBase *surface,
uint32_t *pPresentModeCount,
VkPresentModeKHR *pPresentModes)
{
if (pPresentModes == NULL) {
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
assert(*pPresentModeCount >= ARRAY_SIZE(present_modes));
typed_memcpy(pPresentModes, present_modes, *pPresentModeCount);
*pPresentModeCount = ARRAY_SIZE(present_modes);
return VK_SUCCESS;
}
VkResult anv_create_xcb_surface(const VkAllocationCallbacks *pAllocator,
const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface)
{
VkIcdSurfaceXcb *surface;
surface = vk_alloc(pAllocator, sizeof *surface, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
surface->base.platform = VK_ICD_WSI_PLATFORM_XCB;
surface->connection = pCreateInfo->connection;
surface->window = pCreateInfo->window;
*pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
return VK_SUCCESS;
}
VkResult anv_create_xlib_surface(const VkAllocationCallbacks *pAllocator,
const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface)
{
VkIcdSurfaceXlib *surface;
surface = vk_alloc(pAllocator, sizeof *surface, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (surface == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
surface->base.platform = VK_ICD_WSI_PLATFORM_XLIB;
surface->dpy = pCreateInfo->dpy;
surface->window = pCreateInfo->window;
*pSurface = _VkIcdSurfaceBase_to_handle(&surface->base);
return VK_SUCCESS;
}
struct x11_image {
VkImage image;
VkDeviceMemory memory;
xcb_pixmap_t pixmap;
bool busy;
struct xshmfence * shm_fence;
uint32_t sync_fence;
};
struct x11_swapchain {
struct anv_swapchain base;
xcb_connection_t * conn;
xcb_window_t window;
xcb_gc_t gc;
VkExtent2D extent;
uint32_t image_count;
xcb_present_event_t event_id;
xcb_special_event_t * special_event;
uint64_t send_sbc;
uint32_t stamp;
struct x11_image images[0];
};
static VkResult
x11_get_images(struct anv_swapchain *anv_chain,
uint32_t* pCount, VkImage *pSwapchainImages)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
if (pSwapchainImages == NULL) {
*pCount = chain->image_count;
return VK_SUCCESS;
}
assert(chain->image_count <= *pCount);
for (uint32_t i = 0; i < chain->image_count; i++)
pSwapchainImages[i] = chain->images[i].image;
*pCount = chain->image_count;
return VK_SUCCESS;
}
static VkResult
x11_handle_dri3_present_event(struct x11_swapchain *chain,
xcb_present_generic_event_t *event)
{
switch (event->evtype) {
case XCB_PRESENT_CONFIGURE_NOTIFY: {
xcb_present_configure_notify_event_t *config = (void *) event;
if (config->width != chain->extent.width ||
config->height != chain->extent.height)
return VK_ERROR_OUT_OF_DATE_KHR;
break;
}
case XCB_PRESENT_EVENT_IDLE_NOTIFY: {
xcb_present_idle_notify_event_t *idle = (void *) event;
for (unsigned i = 0; i < chain->image_count; i++) {
if (chain->images[i].pixmap == idle->pixmap) {
chain->images[i].busy = false;
break;
}
}
break;
}
case XCB_PRESENT_COMPLETE_NOTIFY:
default:
break;
}
return VK_SUCCESS;
}
static VkResult
x11_acquire_next_image(struct anv_swapchain *anv_chain,
uint64_t timeout,
VkSemaphore semaphore,
uint32_t *image_index)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
while (1) {
for (uint32_t i = 0; i < chain->image_count; i++) {
if (!chain->images[i].busy) {
/* We found a non-busy image */
xshmfence_await(chain->images[i].shm_fence);
*image_index = i;
chain->images[i].busy = true;
return VK_SUCCESS;
}
}
xcb_flush(chain->conn);
xcb_generic_event_t *event =
xcb_wait_for_special_event(chain->conn, chain->special_event);
if (!event)
return VK_ERROR_OUT_OF_DATE_KHR;
VkResult result = x11_handle_dri3_present_event(chain, (void *)event);
free(event);
if (result != VK_SUCCESS)
return result;
}
}
static VkResult
x11_queue_present(struct anv_swapchain *anv_chain,
uint32_t image_index)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
struct x11_image *image = &chain->images[image_index];
assert(image_index < chain->image_count);
uint32_t options = XCB_PRESENT_OPTION_NONE;
int64_t target_msc = 0;
int64_t divisor = 0;
int64_t remainder = 0;
options |= XCB_PRESENT_OPTION_ASYNC;
xshmfence_reset(image->shm_fence);
++chain->send_sbc;
xcb_void_cookie_t cookie =
xcb_present_pixmap(chain->conn,
chain->window,
image->pixmap,
(uint32_t) chain->send_sbc,
0, /* valid */
0, /* update */
0, /* x_off */
0, /* y_off */
XCB_NONE, /* target_crtc */
XCB_NONE,
image->sync_fence,
options,
target_msc,
divisor,
remainder, 0, NULL);
xcb_discard_reply(chain->conn, cookie.sequence);
image->busy = true;
xcb_flush(chain->conn);
return VK_SUCCESS;
}
static VkResult
x11_image_init(VkDevice device_h, struct x11_swapchain *chain,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks* pAllocator,
struct x11_image *image)
{
xcb_void_cookie_t cookie;
VkResult result;
uint32_t row_pitch;
uint32_t offset;
uint32_t bpp = 32;
uint32_t depth = 24;
int fd;
uint32_t size;
result = chain->base.image_fns->create_wsi_image(device_h,
pCreateInfo,
pAllocator,
&image->image,
&image->memory,
&size,
&offset,
&row_pitch,
&fd);
if (result != VK_SUCCESS)
return result;
image->pixmap = xcb_generate_id(chain->conn);
cookie =
xcb_dri3_pixmap_from_buffer_checked(chain->conn,
image->pixmap,
chain->window,
size,
pCreateInfo->imageExtent.width,
pCreateInfo->imageExtent.height,
row_pitch,
depth, bpp, fd);
xcb_discard_reply(chain->conn, cookie.sequence);
int fence_fd = xshmfence_alloc_shm();
if (fence_fd < 0)
goto fail_pixmap;
image->shm_fence = xshmfence_map_shm(fence_fd);
if (image->shm_fence == NULL)
goto fail_shmfence_alloc;
image->sync_fence = xcb_generate_id(chain->conn);
xcb_dri3_fence_from_fd(chain->conn,
image->pixmap,
image->sync_fence,
false,
fence_fd);
image->busy = false;
xshmfence_trigger(image->shm_fence);
return VK_SUCCESS;
fail_shmfence_alloc:
close(fence_fd);
fail_pixmap:
cookie = xcb_free_pixmap(chain->conn, image->pixmap);
xcb_discard_reply(chain->conn, cookie.sequence);
chain->base.image_fns->free_wsi_image(device_h, pAllocator,
image->image, image->memory);
return result;
}
static void
x11_image_finish(struct x11_swapchain *chain,
const VkAllocationCallbacks* pAllocator,
struct x11_image *image)
{
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);
cookie = xcb_free_pixmap(chain->conn, image->pixmap);
xcb_discard_reply(chain->conn, cookie.sequence);
chain->base.image_fns->free_wsi_image(chain->base.device, pAllocator,
image->image, image->memory);
}
static VkResult
x11_swapchain_destroy(struct anv_swapchain *anv_chain,
const VkAllocationCallbacks *pAllocator)
{
struct x11_swapchain *chain = (struct x11_swapchain *)anv_chain;
for (uint32_t i = 0; i < chain->image_count; i++)
x11_image_finish(chain, pAllocator, &chain->images[i]);
xcb_unregister_for_special_event(chain->conn, chain->special_event);
vk_free(pAllocator, chain);
return VK_SUCCESS;
}
static VkResult
x11_surface_create_swapchain(VkIcdSurfaceBase *icd_surface,
VkDevice device,
struct anv_wsi_device *wsi_device,
const VkSwapchainCreateInfoKHR *pCreateInfo,
const VkAllocationCallbacks* pAllocator,
const struct anv_wsi_image_fns *image_fns,
struct anv_swapchain **swapchain_out)
{
struct x11_swapchain *chain;
xcb_void_cookie_t cookie;
VkResult result;
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR);
int num_images = pCreateInfo->minImageCount;
/* 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 Wayland compositor
* 4) One to render to
*/
if (pCreateInfo->presentMode == VK_PRESENT_MODE_MAILBOX_KHR)
num_images = MAX2(num_images, 4);
size_t size = sizeof(*chain) + num_images * sizeof(chain->images[0]);
chain = vk_alloc(pAllocator, size, 8,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (chain == NULL)
return VK_ERROR_OUT_OF_HOST_MEMORY;
chain->base.device = device;
chain->base.destroy = x11_swapchain_destroy;
chain->base.get_images = x11_get_images;
chain->base.acquire_next_image = x11_acquire_next_image;
chain->base.queue_present = x11_queue_present;
chain->base.image_fns = image_fns;
chain->conn = x11_surface_get_connection(icd_surface);
chain->window = x11_surface_get_window(icd_surface);
chain->extent = pCreateInfo->imageExtent;
chain->image_count = num_images;
chain->send_sbc = 0;
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 |
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY |
XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY);
/* Create an XCB event queue to hold present events outside of the usual
* application event queue
*/
chain->special_event =
xcb_register_for_special_xge(chain->conn, &xcb_present_id,
chain->event_id, NULL);
chain->gc = xcb_generate_id(chain->conn);
if (!chain->gc) {
/* FINISHME: Choose a better error. */
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_register;
}
cookie = xcb_create_gc(chain->conn,
chain->gc,
chain->window,
XCB_GC_GRAPHICS_EXPOSURES,
(uint32_t []) { 0 });
xcb_discard_reply(chain->conn, cookie.sequence);
uint32_t image = 0;
for (; image < chain->image_count; image++) {
result = x11_image_init(device, chain, pCreateInfo, pAllocator,
&chain->images[image]);
if (result != VK_SUCCESS)
goto fail_init_images;
}
*swapchain_out = &chain->base;
return VK_SUCCESS;
fail_init_images:
for (uint32_t j = 0; j < image; j++)
x11_image_finish(chain, pAllocator, &chain->images[j]);
fail_register:
xcb_unregister_for_special_event(chain->conn, chain->special_event);
vk_free(pAllocator, chain);
return result;
}
VkResult
anv_x11_init_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_x11 *wsi;
VkResult result;
wsi = vk_alloc(alloc, sizeof(*wsi), 8,
VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
if (!wsi) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail;
}
int ret = pthread_mutex_init(&wsi->mutex, NULL);
if (ret != 0) {
if (ret == ENOMEM) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
} else {
/* FINISHME: Choose a better error. */
result = VK_ERROR_OUT_OF_HOST_MEMORY;
}
goto fail_alloc;
}
wsi->connections = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
_mesa_key_pointer_equal);
if (!wsi->connections) {
result = VK_ERROR_OUT_OF_HOST_MEMORY;
goto fail_mutex;
}
wsi->base.get_support = x11_surface_get_support;
wsi->base.get_capabilities = x11_surface_get_capabilities;
wsi->base.get_formats = x11_surface_get_formats;
wsi->base.get_present_modes = x11_surface_get_present_modes;
wsi->base.create_swapchain = x11_surface_create_swapchain;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = &wsi->base;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XLIB] = &wsi->base;
return VK_SUCCESS;
fail_mutex:
pthread_mutex_destroy(&wsi->mutex);
fail_alloc:
vk_free(alloc, wsi);
fail:
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB] = NULL;
wsi_device->wsi[VK_ICD_WSI_PLATFORM_XLIB] = NULL;
return result;
}
void
anv_x11_finish_wsi(struct anv_wsi_device *wsi_device,
const VkAllocationCallbacks *alloc)
{
struct wsi_x11 *wsi =
(struct wsi_x11 *)wsi_device->wsi[VK_ICD_WSI_PLATFORM_XCB];
if (wsi) {
_mesa_hash_table_destroy(wsi->connections, NULL);
pthread_mutex_destroy(&wsi->mutex);
vk_free(alloc, wsi);
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright © 2015 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef WSI_COMMON_X11_H
#define WSI_COMMON_X11_H
#include "wsi_common.h"
VkBool32 anv_get_physical_device_xcb_presentation_support(
struct anv_wsi_device *wsi_device,
VkAllocationCallbacks *alloc,
uint32_t queueFamilyIndex,
xcb_connection_t* connection,
xcb_visualid_t visual_id);
VkResult anv_create_xcb_surface(const VkAllocationCallbacks *pAllocator,
const VkXcbSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface);
VkResult anv_create_xlib_surface(const VkAllocationCallbacks *pAllocator,
const VkXlibSurfaceCreateInfoKHR *pCreateInfo,
VkSurfaceKHR *pSurface);
#endif