1 /************************************************************************** 2 * 3 * Copyright 2009 Younes Manton. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR 22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 **************************************************************************/ 27 28 /* directly referenced from target Makefile, because of X dependencies */ 29 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <fcntl.h> 33 34 #include <X11/Xlib-xcb.h> 35 #include <xcb/dri2.h> 36 #include <xf86drm.h> 37 38 #include "pipe/p_screen.h" 39 #include "pipe/p_context.h" 40 #include "pipe/p_state.h" 41 #include "state_tracker/drm_driver.h" 42 43 #include "util/u_memory.h" 44 #include "util/u_hash.h" 45 #include "util/u_hash_table.h" 46 #include "util/u_inlines.h" 47 48 #include "vl/vl_compositor.h" 49 #include "vl/vl_winsys.h" 50 51 struct vl_dri_screen 52 { 53 struct vl_screen base; 54 xcb_connection_t *conn; 55 xcb_drawable_t drawable; 56 57 unsigned width, height; 58 59 bool current_buffer; 60 uint32_t buffer_names[2]; 61 struct u_rect dirty_areas[2]; 62 63 bool flushed; 64 xcb_dri2_swap_buffers_cookie_t swap_cookie; 65 xcb_dri2_wait_sbc_cookie_t wait_cookie; 66 xcb_dri2_get_buffers_cookie_t buffers_cookie; 67 68 int64_t last_ust, ns_frame, last_msc, next_msc, skew_msc; 69 }; 70 71 static const unsigned int attachments[1] = { XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT }; 72 73 static void 74 vl_dri2_handle_stamps(struct vl_dri_screen* scrn, 75 uint32_t ust_hi, uint32_t ust_lo, 76 uint32_t msc_hi, uint32_t msc_lo) 77 { 78 int64_t ust = ((((uint64_t)ust_hi) << 32) | ust_lo) * 1000; 79 int64_t msc = (((uint64_t)msc_hi) << 32) | msc_lo; 80 81 if (scrn->last_ust && scrn->last_msc && (ust > scrn->last_ust) && (msc > scrn->last_msc)) 82 scrn->ns_frame = (ust - scrn->last_ust) / (msc - scrn->last_msc); 83 84 if (scrn->next_msc && (scrn->next_msc < msc)) 85 scrn->skew_msc++; 86 87 scrn->last_ust = ust; 88 scrn->last_msc = msc; 89 } 90 91 static xcb_dri2_get_buffers_reply_t* 92 vl_dri2_get_flush_reply(struct vl_dri_screen *scrn) 93 { 94 xcb_dri2_wait_sbc_reply_t *wait_sbc_reply; 95 96 assert(scrn); 97 98 if (!scrn->flushed) 99 return NULL; 100 101 scrn->flushed = false; 102 103 free(xcb_dri2_swap_buffers_reply(scrn->conn, scrn->swap_cookie, NULL)); 104 105 wait_sbc_reply = xcb_dri2_wait_sbc_reply(scrn->conn, scrn->wait_cookie, NULL); 106 if (!wait_sbc_reply) 107 return NULL; 108 vl_dri2_handle_stamps(scrn, wait_sbc_reply->ust_hi, wait_sbc_reply->ust_lo, 109 wait_sbc_reply->msc_hi, wait_sbc_reply->msc_lo); 110 free(wait_sbc_reply); 111 112 return xcb_dri2_get_buffers_reply(scrn->conn, scrn->buffers_cookie, NULL); 113 } 114 115 static void 116 vl_dri2_flush_frontbuffer(struct pipe_screen *screen, 117 struct pipe_resource *resource, 118 unsigned level, unsigned layer, 119 void *context_private) 120 { 121 struct vl_dri_screen *scrn = (struct vl_dri_screen*)context_private; 122 uint32_t msc_hi, msc_lo; 123 124 assert(screen); 125 assert(resource); 126 assert(context_private); 127 128 free(vl_dri2_get_flush_reply(scrn)); 129 130 msc_hi = scrn->next_msc >> 32; 131 msc_lo = scrn->next_msc & 0xFFFFFFFF; 132 133 scrn->swap_cookie = xcb_dri2_swap_buffers_unchecked(scrn->conn, scrn->drawable, msc_hi, msc_lo, 0, 0, 0, 0); 134 scrn->wait_cookie = xcb_dri2_wait_sbc_unchecked(scrn->conn, scrn->drawable, 0, 0); 135 scrn->buffers_cookie = xcb_dri2_get_buffers_unchecked(scrn->conn, scrn->drawable, 1, 1, attachments); 136 137 scrn->flushed = true; 138 scrn->current_buffer = !scrn->current_buffer; 139 } 140 141 static void 142 vl_dri2_destroy_drawable(struct vl_dri_screen *scrn) 143 { 144 xcb_void_cookie_t destroy_cookie; 145 if (scrn->drawable) { 146 free(vl_dri2_get_flush_reply(scrn)); 147 destroy_cookie = xcb_dri2_destroy_drawable_checked(scrn->conn, scrn->drawable); 148 /* ignore any error here, since the drawable can be destroyed long ago */ 149 free(xcb_request_check(scrn->conn, destroy_cookie)); 150 } 151 } 152 153 static void 154 vl_dri2_set_drawable(struct vl_dri_screen *scrn, Drawable drawable) 155 { 156 assert(scrn); 157 assert(drawable); 158 159 if (scrn->drawable == drawable) 160 return; 161 162 vl_dri2_destroy_drawable(scrn); 163 164 xcb_dri2_create_drawable(scrn->conn, drawable); 165 scrn->current_buffer = false; 166 vl_compositor_reset_dirty_area(&scrn->dirty_areas[0]); 167 vl_compositor_reset_dirty_area(&scrn->dirty_areas[1]); 168 scrn->drawable = drawable; 169 } 170 171 struct pipe_resource* 172 vl_screen_texture_from_drawable(struct vl_screen *vscreen, Drawable drawable) 173 { 174 struct vl_dri_screen *scrn = (struct vl_dri_screen*)vscreen; 175 176 struct winsys_handle dri2_handle; 177 struct pipe_resource template, *tex; 178 179 xcb_dri2_get_buffers_reply_t *reply; 180 xcb_dri2_dri2_buffer_t *buffers, *back_left; 181 182 unsigned i; 183 184 assert(scrn); 185 186 vl_dri2_set_drawable(scrn, drawable); 187 reply = vl_dri2_get_flush_reply(scrn); 188 if (!reply) { 189 xcb_dri2_get_buffers_cookie_t cookie; 190 cookie = xcb_dri2_get_buffers_unchecked(scrn->conn, drawable, 1, 1, attachments); 191 reply = xcb_dri2_get_buffers_reply(scrn->conn, cookie, NULL); 192 } 193 if (!reply) 194 return NULL; 195 196 buffers = xcb_dri2_get_buffers_buffers(reply); 197 if (!buffers) { 198 free(reply); 199 return NULL; 200 } 201 202 for (i = 0; i < reply->count; ++i) { 203 if (buffers[i].attachment == XCB_DRI2_ATTACHMENT_BUFFER_BACK_LEFT) { 204 back_left = &buffers[i]; 205 break; 206 } 207 } 208 209 if (i == reply->count) { 210 free(reply); 211 return NULL; 212 } 213 214 if (reply->width != scrn->width || reply->height != scrn->height) { 215 vl_compositor_reset_dirty_area(&scrn->dirty_areas[0]); 216 vl_compositor_reset_dirty_area(&scrn->dirty_areas[1]); 217 scrn->width = reply->width; 218 scrn->height = reply->height; 219 220 } else if (back_left->name != scrn->buffer_names[scrn->current_buffer]) { 221 vl_compositor_reset_dirty_area(&scrn->dirty_areas[scrn->current_buffer]); 222 scrn->buffer_names[scrn->current_buffer] = back_left->name; 223 } 224 225 memset(&dri2_handle, 0, sizeof(dri2_handle)); 226 dri2_handle.type = DRM_API_HANDLE_TYPE_SHARED; 227 dri2_handle.handle = back_left->name; 228 dri2_handle.stride = back_left->pitch; 229 230 memset(&template, 0, sizeof(template)); 231 template.target = PIPE_TEXTURE_2D; 232 template.format = PIPE_FORMAT_B8G8R8X8_UNORM; 233 template.last_level = 0; 234 template.width0 = reply->width; 235 template.height0 = reply->height; 236 template.depth0 = 1; 237 template.array_size = 1; 238 template.usage = PIPE_USAGE_STATIC; 239 template.bind = PIPE_BIND_RENDER_TARGET; 240 template.flags = 0; 241 242 tex = scrn->base.pscreen->resource_from_handle(scrn->base.pscreen, &template, &dri2_handle); 243 free(reply); 244 245 return tex; 246 } 247 248 struct u_rect * 249 vl_screen_get_dirty_area(struct vl_screen *vscreen) 250 { 251 struct vl_dri_screen *scrn = (struct vl_dri_screen*)vscreen; 252 assert(scrn); 253 return &scrn->dirty_areas[scrn->current_buffer]; 254 } 255 256 uint64_t 257 vl_screen_get_timestamp(struct vl_screen *vscreen, Drawable drawable) 258 { 259 struct vl_dri_screen *scrn = (struct vl_dri_screen*)vscreen; 260 xcb_dri2_get_msc_cookie_t cookie; 261 xcb_dri2_get_msc_reply_t *reply; 262 263 assert(scrn); 264 265 vl_dri2_set_drawable(scrn, drawable); 266 if (!scrn->last_ust) { 267 cookie = xcb_dri2_get_msc_unchecked(scrn->conn, drawable); 268 reply = xcb_dri2_get_msc_reply(scrn->conn, cookie, NULL); 269 270 if (reply) { 271 vl_dri2_handle_stamps(scrn, reply->ust_hi, reply->ust_lo, 272 reply->msc_hi, reply->msc_lo); 273 free(reply); 274 } 275 } 276 return scrn->last_ust; 277 } 278 279 void 280 vl_screen_set_next_timestamp(struct vl_screen *vscreen, uint64_t stamp) 281 { 282 struct vl_dri_screen *scrn = (struct vl_dri_screen*)vscreen; 283 assert(scrn); 284 if (stamp && scrn->last_ust && scrn->ns_frame && scrn->last_msc) 285 scrn->next_msc = ((int64_t)stamp - scrn->last_ust) / scrn->ns_frame + scrn->last_msc + scrn->skew_msc; 286 else 287 scrn->next_msc = 0; 288 } 289 290 void* 291 vl_screen_get_private(struct vl_screen *vscreen) 292 { 293 return vscreen; 294 } 295 296 struct vl_screen* 297 vl_screen_create(Display *display, int screen) 298 { 299 struct vl_dri_screen *scrn; 300 const xcb_query_extension_reply_t *extension; 301 xcb_dri2_query_version_cookie_t dri2_query_cookie; 302 xcb_dri2_query_version_reply_t *dri2_query = NULL; 303 xcb_dri2_connect_cookie_t connect_cookie; 304 xcb_dri2_connect_reply_t *connect = NULL; 305 xcb_dri2_authenticate_cookie_t authenticate_cookie; 306 xcb_dri2_authenticate_reply_t *authenticate = NULL; 307 xcb_screen_iterator_t s; 308 xcb_generic_error_t *error = NULL; 309 char *device_name; 310 int fd, device_name_length; 311 312 drm_magic_t magic; 313 314 assert(display); 315 316 scrn = CALLOC_STRUCT(vl_dri_screen); 317 if (!scrn) 318 return NULL; 319 320 scrn->conn = XGetXCBConnection(display); 321 if (!scrn->conn) 322 goto free_screen; 323 324 xcb_prefetch_extension_data(scrn->conn, &xcb_dri2_id); 325 326 extension = xcb_get_extension_data(scrn->conn, &xcb_dri2_id); 327 if (!(extension && extension->present)) 328 goto free_screen; 329 330 dri2_query_cookie = xcb_dri2_query_version (scrn->conn, XCB_DRI2_MAJOR_VERSION, XCB_DRI2_MINOR_VERSION); 331 dri2_query = xcb_dri2_query_version_reply (scrn->conn, dri2_query_cookie, &error); 332 if (dri2_query == NULL || error != NULL || dri2_query->minor_version < 2) 333 goto free_screen; 334 335 s = xcb_setup_roots_iterator(xcb_get_setup(scrn->conn)); 336 connect_cookie = xcb_dri2_connect_unchecked(scrn->conn, s.data->root, XCB_DRI2_DRIVER_TYPE_DRI); 337 connect = xcb_dri2_connect_reply(scrn->conn, connect_cookie, NULL); 338 if (connect == NULL || connect->driver_name_length + connect->device_name_length == 0) 339 goto free_screen; 340 341 device_name_length = xcb_dri2_connect_device_name_length(connect); 342 device_name = CALLOC(1, device_name_length); 343 memcpy(device_name, xcb_dri2_connect_device_name(connect), device_name_length); 344 device_name[device_name_length] = 0; 345 fd = open(device_name, O_RDWR); 346 free(device_name); 347 348 if (fd < 0) 349 goto free_screen; 350 351 if (drmGetMagic(fd, &magic)) 352 goto free_screen; 353 354 authenticate_cookie = xcb_dri2_authenticate_unchecked(scrn->conn, s.data->root, magic); 355 authenticate = xcb_dri2_authenticate_reply(scrn->conn, authenticate_cookie, NULL); 356 357 if (authenticate == NULL || !authenticate->authenticated) 358 goto free_screen; 359 360 scrn->base.pscreen = driver_descriptor.create_screen(fd); 361 if (!scrn->base.pscreen) 362 goto free_screen; 363 364 scrn->base.pscreen->flush_frontbuffer = vl_dri2_flush_frontbuffer; 365 vl_compositor_reset_dirty_area(&scrn->dirty_areas[0]); 366 vl_compositor_reset_dirty_area(&scrn->dirty_areas[1]); 367 368 free(dri2_query); 369 free(connect); 370 free(authenticate); 371 372 return &scrn->base; 373 374 free_screen: 375 FREE(scrn); 376 377 free(dri2_query); 378 free(connect); 379 free(authenticate); 380 free(error); 381 382 return NULL; 383 } 384 385 void vl_screen_destroy(struct vl_screen *vscreen) 386 { 387 struct vl_dri_screen *scrn = (struct vl_dri_screen*)vscreen; 388 389 assert(vscreen); 390 391 if (scrn->flushed) { 392 free(xcb_dri2_swap_buffers_reply(scrn->conn, scrn->swap_cookie, NULL)); 393 free(xcb_dri2_wait_sbc_reply(scrn->conn, scrn->wait_cookie, NULL)); 394 free(xcb_dri2_get_buffers_reply(scrn->conn, scrn->buffers_cookie, NULL)); 395 } 396 397 vl_dri2_destroy_drawable(scrn); 398 scrn->base.pscreen->destroy(scrn->base.pscreen); 399 FREE(scrn); 400 } 401