/* * Copyright 2012 Red Hat 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 * on the rights to use, copy, modify, merge, publish, distribute, sub * license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHOR(S) AND/OR THEIR SUPPLIERS 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: * Jerome Glisse */ #include #include #include #include #include #include #include #include #include #include "libdrm/radeon_drm.h" #include "replayx.h" #include "xf86drm.h" static int ctx_start_visual(struct ctx *ctx) { xcb_screen_iterator_t s; xcb_depth_iterator_t d; xcb_visualtype_t *visuals; unsigned i; s = xcb_setup_roots_iterator(xcb_get_setup(ctx->con)); d = xcb_screen_allowed_depths_iterator(s.data); ctx->root = s.data->root; while (d.rem > 0) { if (d.data->depth == ctx->depth) { visuals = xcb_depth_visuals(d.data); for (i = 0; i < xcb_depth_visuals_length(d.data); i++) { if (visuals[i].red_mask == 0xff0000 && visuals[i].green_mask == 0xff00 && visuals[i].blue_mask == 0xff) { ctx->visual_id = visuals[i].visual_id; return 0; } } } xcb_depth_next(&d); } return -EINVAL; } static int ctx_dri2_bo(struct ctx *ctx) { struct drm_gem_open open_arg; int r; memset(&open_arg, 0, sizeof(open_arg)); open_arg.name = ctx->front.name; r = drmIoctl(ctx->fd, DRM_IOCTL_GEM_OPEN, &open_arg); if (r) { fprintf(stderr, "%s failed to open front bo (%d)\n", __func__, r); return -EINVAL; } ctx->front.handle = open_arg.handle; if (!ctx_bo_map(ctx, &ctx->front)) { memset(ctx->front.data, 0, ctx->front.size); } return 0; } static int ctx_dri2_buffer(struct ctx *ctx) { xcb_dri2_get_buffers_reply_t *reply; xcb_dri2_get_buffers_cookie_t cookie; xcb_dri2_dri2_buffer_t *buffers; uint32_t attachments[2]; unsigned count = 2; attachments[0] = XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT; attachments[1] = XCB_DRI2_ATTACHMENT_BUFFER_FAKE_FRONT_LEFT; cookie = xcb_dri2_get_buffers_unchecked(ctx->con, ctx->window, count, count, attachments); reply = xcb_dri2_get_buffers_reply(ctx->con, cookie, NULL); if (reply == NULL) { fprintf(stderr, "%s failed to get dri2 buffer\n", __func__); return -EINVAL; } buffers = xcb_dri2_get_buffers_buffers(reply); if (buffers == NULL) { free(reply); return -EINVAL; } if (reply->width != ctx->win_w || reply->height != ctx->win_h) { fprintf(stderr, "%s window & dri2 drawable missmatch (%d x %d) vs (%d x %d)\n", __func__, ctx->win_w, ctx->win_h, reply->width, reply->height); free(reply); return -EINVAL; } if (reply->count != count) { fprintf(stderr, "%s expect %d dri2 drawable got %d\n", __func__, count, reply->count); free(reply); return -EINVAL; } if (buffers[0].attachment != XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT) { fprintf(stderr, "%s expect fake front left dri2 got %d\n", __func__, buffers[1].attachment); free(reply); return -EINVAL; } if (buffers[0].name == ctx->front.name) { /* still same bo */ return 0; } /* close previous bo */ ctx_bo_free(ctx, &ctx->front); ctx->front.w = reply->width; ctx->front.h = reply->height; ctx->front.pitch = buffers[0].pitch / 4; ctx->front.flags = buffers[0].flags; ctx->front.name = buffers[0].name; ctx->front.cpp = buffers[0].cpp; ctx->front.size = ctx->front.pitch * ctx->front.h * ctx->front.cpp; free(reply); return ctx_dri2_bo(ctx); } static int ctx_dri2_swap(struct ctx *ctx) { xcb_dri2_swap_buffers_cookie_t cookie; xcb_dri2_swap_buffers_reply_t *reply; cookie = xcb_dri2_swap_buffers(ctx->con, ctx->window, 0, 0, 0, 0, 0, 0); reply = xcb_dri2_swap_buffers_reply(ctx->con, cookie, NULL); if (reply == NULL) { fprintf(stderr, "%s failed to swap buffer\n", __func__); return -EINVAL; } free(reply); return ctx_dri2_buffer(ctx); } static int ctx_x11_authenticate(struct ctx *ctx, uint32_t id) { xcb_dri2_authenticate_reply_t *authenticate; xcb_dri2_authenticate_cookie_t authenticate_cookie; xcb_screen_iterator_t s; s = xcb_setup_roots_iterator(xcb_get_setup(ctx->con)); authenticate_cookie = xcb_dri2_authenticate_unchecked(ctx->con, s.data->root, id); authenticate = xcb_dri2_authenticate_reply(ctx->con, authenticate_cookie, NULL); if (authenticate == NULL || !authenticate->authenticated) { free(authenticate); return -EINVAL; } free(authenticate); return 0; } static int ctx_dri2_authenticate(struct ctx *ctx) { drm_magic_t magic; if (drmGetMagic(ctx->fd, &magic)) { return -EINVAL; } return ctx_x11_authenticate(ctx, magic); } static int ctx_start_dri2(struct ctx *ctx) { xcb_prefetch_extension_data(ctx->con, &xcb_xfixes_id); xcb_prefetch_extension_data(ctx->con, &xcb_dri2_id); const xcb_query_extension_reply_t *extension; xcb_dri2_connect_cookie_t connect_cookie; xcb_dri2_connect_reply_t *con; const char *device_name; int r; extension = xcb_get_extension_data(ctx->con, &xcb_xfixes_id); if (!(extension && extension->present)) { fprintf(stderr, "%s xfixes extension needed for dri2\n", __func__); return -EINVAL; } extension = xcb_get_extension_data(ctx->con, &xcb_dri2_id); if (!(extension && extension->present)) { fprintf(stderr, "%s dri2 extension missing\n", __func__); return -EINVAL; } connect_cookie = xcb_dri2_connect_unchecked(ctx->con, ctx->root, XCB_DRI2_DRIVER_TYPE_DRI); con = xcb_dri2_connect_reply(ctx->con, connect_cookie, NULL); if (con == NULL || con->driver_name_length + con->device_name_length == 0) { fprintf(stderr, "%s dri2 failed to authenticate\n", __func__); free(con); return -EINVAL; } device_name = xcb_dri2_connect_device_name(con); ctx->device_name = strdup(device_name); if (ctx->device_name == NULL) { free(con); return -ENOMEM; } free(con); ctx->fd = open(ctx->device_name, O_RDWR | O_CLOEXEC); if (ctx->fd == -1) { perror(NULL); fprintf(stderr, "%s dri2 could not open |%s| %d\n", __func__, ctx->device_name, errno); return -EINVAL; } r = ctx_dri2_authenticate(ctx); if (r) { fprintf(stderr, "%s dri2 authentification failed\n", __func__); return r; } xcb_dri2_create_drawable(ctx->con, ctx->window); r = ctx_dri2_buffer(ctx); if (r) { return r; } return ctx_dri2_swap(ctx); } static int ctx_start(struct ctx *ctx, unsigned win_w, unsigned win_h) { uint32_t value_mask, value_list[1]; int r; /* connect and get visual */ ctx->depth = 24; ctx->win_w = win_w; ctx->win_h = win_h; ctx->con = xcb_connect(0, &ctx->screen_num); r = ctx_start_visual(ctx); if (r) { return r; } /* create window */ value_mask = XCB_CW_EVENT_MASK; value_list[0] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_RESIZE_REDIRECT; ctx->window = xcb_generate_id(ctx->con); xcb_create_window(ctx->con, ctx->depth, ctx->window, ctx->root, 64, 64, ctx->win_w, ctx->win_h, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, ctx->visual_id, value_mask, value_list); xcb_map_window(ctx->con, ctx->window); xcb_flush(ctx->con); return ctx_start_dri2(ctx); } static void ctx_end(struct ctx *ctx) { xcb_disconnect(ctx->con); free(ctx->device_name); if (ctx->fd) { close(ctx->fd); } } static int ctx_do_blit(struct ctx *ctx) { int r; r = ctx->drv.blit(ctx, ctx->target[ctx->ctarget++]); if (r) { /* finish execution */ fprintf(stderr, "%s blit failed\n", __func__); return r; } if (ctx->ctarget >= ctx->ntarget) { ctx->ctarget = 0; } return r; } static void ctx_main(struct ctx *ctx) { xcb_generic_event_t *e; xcb_resize_request_event_t *er; int r; while ((e = xcb_wait_for_event (ctx->con))) { switch (e->response_type) { case XCB_RESIZE_REQUEST: er = (void*)e; if (ctx->win_w != er->width || ctx->win_h != er->height) { /* resize */ ctx->win_w = er->width; ctx->win_h = er->height; printf("resize %d %d\n", ctx->win_w, ctx->win_h); } r = ctx_dri2_buffer(ctx); if (r) { /* finish execution */ return; } break; case XCB_KEY_PRESS: r = ctx_do_blit(ctx); if (r) { return; } r = ctx_dri2_swap(ctx); if (r) { /* finish execution */ return; } break; default: break; } free(e); } } static void usage(const char *exename) { printf("%s \n", exename); exit(0); } int main(int argc, char *argv[]) { struct ctx ctx = {0}; if (argc != 2) { usage(argv[0]); } if (ctx_start(&ctx, 800, 600)) { return -1; } if (ctx_rati_load(&ctx, argv[1])) { return -1; } if (ctx_drv_init(&ctx)) { return -1; } if (ctx_cs_rati(&ctx)) { fprintf(stderr, "cs submission failed\n"); return -1; } if (ctx_do_blit(&ctx)) { return -1; } ctx_main(&ctx); ctx_drv_fini(&ctx); ctx_end(&ctx); return 0; }