blob: 8c1afc82e3d443112bf6114dfef096fd31d5c132 [file] [log] [blame]
/*
* XGL
*
* Copyright (C) 2014 LunarG, Inc.
*
* 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 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.
*
* Authors:
* Chia-I Wu <olv@lunarg.com>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <xcb/xcb.h>
#include <xcb/dri3.h>
#include <xcb/present.h>
#include "kmd/winsys.h"
#include "dev.h"
#include "fence.h"
#include "gpu.h"
#include "img.h"
#include "mem.h"
#include "queue.h"
#include "wsi_x11.h"
struct intel_wsi_x11_window {
xcb_window_t window_id;
xcb_present_event_t present_special_event_id;
xcb_special_event_t *present_special_event;
struct {
uint32_t serial;
} local;
struct {
uint32_t serial;
XGL_UINT64 msc;
} remote;
struct intel_wsi_x11_window *next;
};
struct intel_wsi_x11 {
xcb_connection_t *c;
xcb_window_t root;
xcb_randr_provider_t provider;
int root_depth;
int dri3_major, dri3_minor;
int present_major, present_minor;
int fd;
struct intel_wsi_x11_window *windows;
};
/**
* Return true if DRI3 and Present are supported by the server.
*/
static bool wsi_x11_has_dri3_and_present(xcb_connection_t *c)
{
const xcb_query_extension_reply_t *ext;
xcb_prefetch_extension_data(c, &xcb_dri3_id);
xcb_prefetch_extension_data(c, &xcb_present_id);
ext = xcb_get_extension_data(c, &xcb_dri3_id);
if (!ext || !ext->present)
return false;
ext = xcb_get_extension_data(c, &xcb_present_id);
if (!ext || !ext->present)
return false;
return true;
}
/**
* Return the depth of the root window.
*/
static int wsi_x11_get_root_depth(struct intel_wsi_x11 *x11)
{
const xcb_setup_t *setup;
xcb_screen_iterator_t iter;
setup = xcb_get_setup(x11->c);
iter = xcb_setup_roots_iterator(setup);
for (; iter.rem; xcb_screen_next(&iter)) {
if (iter.data->root == x11->root)
return iter.data->root_depth;
}
return 0;
}
/**
* Query DRI3 and Present versions and return an intel_wsi_x11.
*/
static struct intel_wsi_x11 *wsi_x11_create(xcb_connection_t *c,
xcb_window_t root,
xcb_randr_provider_t provider)
{
xcb_dri3_query_version_cookie_t dri3_cookie;
xcb_dri3_query_version_reply_t *dri3_reply;
xcb_present_query_version_cookie_t present_cookie;
xcb_present_query_version_reply_t *present_reply;
struct intel_wsi_x11 *x11;
dri3_cookie = xcb_dri3_query_version(c,
XCB_DRI3_MAJOR_VERSION, XCB_DRI3_MINOR_VERSION);
present_cookie = xcb_present_query_version(c,
XCB_PRESENT_MAJOR_VERSION, XCB_PRESENT_MINOR_VERSION);
x11 = icd_alloc(sizeof(*x11), 0, XGL_SYSTEM_ALLOC_INTERNAL);
if (!x11)
return NULL;
memset(x11, 0, sizeof(*x11));
x11->c = c;
x11->root = root;
x11->provider = provider;
x11->root_depth = wsi_x11_get_root_depth(x11);
dri3_reply = xcb_dri3_query_version_reply(c, dri3_cookie, NULL);
if (!dri3_reply) {
icd_free(x11);
return NULL;
}
x11->dri3_major = dri3_reply->major_version;
x11->dri3_minor = dri3_reply->minor_version;
free(dri3_reply);
present_reply = xcb_present_query_version_reply(c, present_cookie, NULL);
if (!present_reply) {
icd_free(x11);
return NULL;
}
x11->present_major = present_reply->major_version;
x11->present_minor = present_reply->minor_version;
free(present_reply);
x11->fd = -1;
return x11;
}
/**
* Return true if x11->fd points to the primary or render node of the GPU.
*/
static bool wsi_x11_uses_gpu(const struct intel_wsi_x11 *x11,
const struct intel_gpu *gpu)
{
struct stat x11_stat, gpu_stat;
if (fstat(x11->fd, &x11_stat))
return false;
/* is it the primary node? */
if (!stat(gpu->primary_node, &gpu_stat) &&
!memcmp(&x11_stat, &gpu_stat, sizeof(x11_stat)))
return true;
/* is it the render node? */
if (gpu->render_node && !stat(gpu->render_node, &gpu_stat) &&
!memcmp(&x11_stat, &gpu_stat, sizeof(x11_stat)))
return true;
return false;
}
/**
* Send a DRI3Open to get the server GPU fd.
*/
static XGL_RESULT wsi_x11_dri3_open(struct intel_wsi_x11 *x11)
{
xcb_dri3_open_cookie_t cookie;
xcb_dri3_open_reply_t *reply;
int fd;
cookie = xcb_dri3_open(x11->c, x11->root, x11->provider);
reply = xcb_dri3_open_reply(x11->c, cookie, NULL);
if (!reply)
return XGL_ERROR_UNKNOWN;
fd = (reply->nfd == 1) ? xcb_dri3_open_reply_fds(x11->c, reply)[0] : -1;
free(reply);
if (fd < 0)
return XGL_ERROR_UNKNOWN;
fcntl(fd, F_SETFD, FD_CLOEXEC);
x11->fd = fd;
return XGL_SUCCESS;
}
/**
* Send a DRI3PixmapFromBuffer to create a Pixmap from \p mem for \p img.
*/
static XGL_RESULT wsi_x11_dri3_pixmap_from_buffer(struct intel_wsi_x11 *x11,
struct intel_dev *dev,
struct intel_img *img,
struct intel_mem *mem)
{
struct intel_winsys_handle export;
xcb_pixmap_t pixmap;
/* get prime fd of the bo first */
export.type = INTEL_WINSYS_HANDLE_FD;
if (intel_winsys_export_handle(dev->winsys, mem->bo, img->layout.tiling,
img->layout.bo_stride, img->layout.bo_height, &export))
return XGL_ERROR_UNKNOWN;
pixmap = xcb_generate_id(x11->c);
/* create a pixmap from the prime fd */
xcb_dri3_pixmap_from_buffer(x11->c, pixmap,
x11->root, img->total_size,
img->layout.width0, img->layout.height0,
img->layout.bo_stride, x11->root_depth,
img->layout.block_size * 8, export.handle);
img->x11_prime_fd = export.handle;
img->x11_pixmap = pixmap;
return XGL_SUCCESS;
}
/**
* Send a PresentSelectInput to select interested events.
*/
static XGL_RESULT wsi_x11_present_select_input(struct intel_wsi_x11 *x11,
struct intel_wsi_x11_window *win)
{
xcb_void_cookie_t cookie;
xcb_generic_error_t *error;
/* create the event queue */
win->present_special_event_id = xcb_generate_id(x11->c);
win->present_special_event = xcb_register_for_special_xge(x11->c,
&xcb_present_id, win->present_special_event_id, NULL);
cookie = xcb_present_select_input_checked(x11->c,
win->present_special_event_id, win->window_id,
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
error = xcb_request_check(x11->c, cookie);
if (error) {
free(error);
return XGL_ERROR_UNKNOWN;
}
return XGL_SUCCESS;
}
/**
* Send a PresentPixmap.
*/
static XGL_RESULT wsi_x11_present_pixmap(struct intel_wsi_x11 *x11,
struct intel_wsi_x11_window *win,
const XGL_WSI_X11_PRESENT_INFO *info)
{
struct intel_img *img = intel_img(info->srcImage);
uint32_t options = XCB_PRESENT_OPTION_NONE;
xcb_void_cookie_t cookie;
xcb_generic_error_t *err;
if (info->async)
options |= XCB_PRESENT_OPTION_ASYNC;
if (!info->flip)
options |= XCB_PRESENT_OPTION_COPY;
cookie = xcb_present_pixmap(x11->c,
win->window_id,
img->x11_pixmap,
++win->local.serial,
0, /* valid-area */
0, /* update-area */
0, /* x-off */
0, /* y-off */
info->crtc,
0, /* wait-fence */
0, /* idle-fence */
options,
info->target_msc,
info->divisor,
info->remainder,
0, NULL);
err = xcb_request_check(x11->c, cookie);
if (err) {
free(err);
return XGL_ERROR_UNKNOWN;
}
return XGL_SUCCESS;
}
/**
* Send a PresentNotifyMSC for the current MSC.
*/
static void wsi_x11_present_notify_msc(struct intel_wsi_x11 *x11,
struct intel_wsi_x11_window *win)
{
/* cannot specify CRTC? */
xcb_present_notify_msc(x11->c, win->window_id, ++win->local.serial,
0, 0, 0);
xcb_flush(x11->c);
}
/**
* Handle a Present event.
*/
static void wsi_x11_present_event(struct intel_wsi_x11 *x11,
struct intel_wsi_x11_window *win,
const xcb_present_generic_event_t *ev)
{
union {
const xcb_present_generic_event_t *ev;
const xcb_present_complete_notify_event_t *complete;
} u = { .ev = ev };
switch (u.ev->evtype) {
case XCB_PRESENT_COMPLETE_NOTIFY:
win->remote.serial = u.complete->serial;
win->remote.msc = u.complete->msc;
break;
default:
break;
}
}
static struct intel_wsi_x11_window *wsi_x11_create_window(struct intel_wsi_x11 *x11,
xcb_window_t win_id)
{
struct intel_wsi_x11_window *win;
win = icd_alloc(sizeof(*win), 0, XGL_SYSTEM_ALLOC_INTERNAL);
if (!win)
return NULL;
memset(win, 0, sizeof(*win));
win->window_id = win_id;
if (wsi_x11_present_select_input(x11, win) != XGL_SUCCESS) {
icd_free(win);
return NULL;
}
return win;
}
static void wsi_x11_destroy_window(struct intel_wsi_x11 *x11,
struct intel_wsi_x11_window *win)
{
if (win->present_special_event)
xcb_unregister_for_special_event(x11->c, win->present_special_event);
icd_free(win);
}
static struct intel_wsi_x11_window *wsi_x11_lookup_window(struct intel_wsi_x11 *x11,
xcb_window_t win_id)
{
struct intel_wsi_x11_window *win = x11->windows;
while (win) {
if (win->window_id == win_id)
break;
win = win->next;
}
/* lookup failed */
if (!win) {
win = wsi_x11_create_window(x11, win_id);
if (win) {
win->next = x11->windows;
x11->windows = win;
}
}
return win;
}
void intel_wsi_x11_destroy(struct intel_wsi_x11 *x11)
{
struct intel_wsi_x11_window *win = x11->windows;
while (win) {
struct intel_wsi_x11_window *next = win->next;
wsi_x11_destroy_window(x11, win);
win = next;
}
if (x11->fd >= 0)
close(x11->fd);
icd_free(x11);
}
XGL_RESULT intel_wsi_x11_wait(struct intel_wsi_x11 *x11,
struct intel_wsi_x11_window *win,
uint32_t serial, bool wait)
{
while (win->remote.serial < serial) {
xcb_present_generic_event_t *ev;
if (wait) {
ev = (xcb_present_generic_event_t *)
xcb_wait_for_special_event(x11->c,
win->present_special_event);
if (!ev)
return XGL_ERROR_UNKNOWN;
} else {
ev = (xcb_present_generic_event_t *)
xcb_poll_for_special_event(x11->c,
win->present_special_event);
if (!ev)
return XGL_NOT_READY;
}
wsi_x11_present_event(x11, win, ev);
free(ev);
}
return XGL_SUCCESS;
}
static bool wsi_x11_is_format_presentable(struct intel_wsi_x11 *x11,
struct intel_dev *dev,
XGL_FORMAT format)
{
/* this is what DDX expects */
switch (format.channelFormat) {
case XGL_CH_FMT_B5G6R5:
if (format.numericFormat == XGL_NUM_FMT_UNORM)
return true;
break;
case XGL_CH_FMT_B8G8R8A8:
if (format.numericFormat == XGL_NUM_FMT_UNORM ||
format.numericFormat == XGL_NUM_FMT_SRGB)
return true;
break;
default:
break;
}
return false;
}
/**
* Create a presentable image.
*/
static XGL_RESULT wsi_x11_img_create(struct intel_wsi_x11 *x11,
struct intel_dev *dev,
const XGL_WSI_X11_PRESENTABLE_IMAGE_CREATE_INFO *info,
struct intel_img **img_ret)
{
XGL_IMAGE_CREATE_INFO img_info;
XGL_MEMORY_ALLOC_INFO mem_info;
struct intel_img *img;
struct intel_mem *mem;
XGL_RESULT ret;
if (!wsi_x11_is_format_presentable(x11, dev, info->format)) {
intel_dev_log(dev, XGL_DBG_MSG_ERROR, XGL_VALIDATION_LEVEL_0,
XGL_NULL_HANDLE, 0, 0, "invalid presentable image format");
return XGL_ERROR_INVALID_VALUE;
}
/* create image */
memset(&img_info, 0, sizeof(img_info));
img_info.sType = XGL_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
img_info.imageType = XGL_IMAGE_2D;
img_info.format = info->format;
img_info.extent.width = info->extent.width;
img_info.extent.height = info->extent.height;
img_info.extent.depth = 1;
img_info.mipLevels = 1;
img_info.arraySize = 1;
img_info.samples = 1;
img_info.tiling = XGL_OPTIMAL_TILING;
img_info.usage = info->usage;
img_info.flags = 0;
ret = intel_img_create(dev, &img_info, true, &img);
if (ret != XGL_SUCCESS)
return ret;
/* allocate memory */
memset(&mem_info, 0, sizeof(mem_info));
mem_info.sType = XGL_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
mem_info.allocationSize = img->total_size;
mem_info.alignment = 4096;
mem_info.flags = 0;
mem_info.heapCount = 1;
mem_info.memPriority = XGL_MEMORY_PRIORITY_HIGH;
static XGL_UINT heapInfo[1];
mem_info.pHeaps = (const XGL_UINT *)&heapInfo;
ret = intel_mem_alloc(dev, &mem_info, &mem);
if (ret != XGL_SUCCESS) {
intel_img_destroy(img);
return ret;
}
ret = wsi_x11_dri3_pixmap_from_buffer(x11, dev, img, mem);
if (ret != XGL_SUCCESS) {
intel_mem_free(mem);
intel_img_destroy(img);
return ret;
}
intel_obj_bind_mem(&img->obj, mem, 0);
*img_ret = img;
return XGL_SUCCESS;
}
ICD_EXPORT XGL_RESULT XGLAPI xglWsiX11AssociateConnection(
XGL_PHYSICAL_GPU gpu_,
const XGL_WSI_X11_CONNECTION_INFO* pConnectionInfo)
{
struct intel_gpu *gpu = intel_gpu(gpu_);
struct intel_wsi_x11 *x11;
XGL_RESULT ret;
if (gpu->x11)
return XGL_SUCCESS;
if (gpu->winsys)
return XGL_ERROR_DEVICE_ALREADY_CREATED;
if (!wsi_x11_has_dri3_and_present(pConnectionInfo->pConnection))
return XGL_ERROR_UNKNOWN;
x11 = wsi_x11_create(pConnectionInfo->pConnection,
pConnectionInfo->root, pConnectionInfo->provider);
if (!x11)
return XGL_ERROR_UNKNOWN;
ret = wsi_x11_dri3_open(x11);
if (ret != XGL_SUCCESS) {
intel_wsi_x11_destroy(x11);
return ret;
}
if (!wsi_x11_uses_gpu(x11, gpu)) {
intel_wsi_x11_destroy(x11);
return XGL_ERROR_UNKNOWN;
}
intel_gpu_associate_x11(gpu, x11, x11->fd);
return XGL_SUCCESS;
}
ICD_EXPORT XGL_RESULT XGLAPI xglWsiX11GetMSC(
XGL_DEVICE device,
xcb_window_t window,
xcb_randr_crtc_t crtc,
XGL_UINT64* pMsc)
{
struct intel_dev *dev = intel_dev(device);
struct intel_wsi_x11 *x11 = dev->gpu->x11;
struct intel_wsi_x11_window *win;
XGL_RESULT ret;
win = wsi_x11_lookup_window(x11, window);
if (!win)
return XGL_ERROR_UNKNOWN;
wsi_x11_present_notify_msc(x11, win);
/* wait for the event */
ret = intel_wsi_x11_wait(x11, win, win->local.serial, -1);
if (ret != XGL_SUCCESS)
return ret;
*pMsc = win->remote.msc;
return XGL_SUCCESS;
}
ICD_EXPORT XGL_RESULT XGLAPI xglWsiX11CreatePresentableImage(
XGL_DEVICE device,
const XGL_WSI_X11_PRESENTABLE_IMAGE_CREATE_INFO* pCreateInfo,
XGL_IMAGE* pImage,
XGL_GPU_MEMORY* pMem)
{
struct intel_dev *dev = intel_dev(device);
struct intel_wsi_x11 *x11 = dev->gpu->x11;
struct intel_img *img;
XGL_RESULT ret;
ret = wsi_x11_img_create(x11, dev, pCreateInfo, &img);
if (ret == XGL_SUCCESS) {
*pImage = (XGL_IMAGE) img;
*pMem = (XGL_GPU_MEMORY) img->obj.mem;
}
return ret;
}
ICD_EXPORT XGL_RESULT XGLAPI xglWsiX11QueuePresent(
XGL_QUEUE queue_,
const XGL_WSI_X11_PRESENT_INFO* pPresentInfo,
XGL_FENCE fence_)
{
struct intel_queue *queue = intel_queue(queue_);
struct intel_fence *fence = intel_fence(fence_);
struct intel_wsi_x11 *x11 = queue->dev->gpu->x11;
struct intel_wsi_x11_window *win;
XGL_RESULT ret;
win = wsi_x11_lookup_window(x11, pPresentInfo->destWindow);
if (!win)
return XGL_ERROR_UNKNOWN;
ret = wsi_x11_present_pixmap(x11, win, pPresentInfo);
if (ret != XGL_SUCCESS)
return ret;
if (fence)
intel_fence_set_x11(fence, x11, win, win->local.serial);
return XGL_SUCCESS;
}