/************************************************************************** * * Copyright 2016 Advanced Micro Devices, Inc. * All Rights Reserved. * * 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, 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 VMWARE AND/OR ITS 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. * **************************************************************************/ #include #include #include #include #include #include #include "loader.h" #include "pipe/p_screen.h" #include "pipe/p_state.h" #include "pipe-loader/pipe_loader.h" #include "util/u_memory.h" #include "util/u_inlines.h" #include "vl/vl_compositor.h" #include "vl/vl_winsys.h" #define BACK_BUFFER_NUM 3 struct vl_dri3_buffer { struct pipe_resource *texture; struct pipe_resource *linear_texture; uint32_t pixmap; uint32_t sync_fence; struct xshmfence *shm_fence; bool busy; uint32_t width, height, pitch; }; struct vl_dri3_screen { struct vl_screen base; xcb_connection_t *conn; xcb_drawable_t drawable; uint32_t width, height, depth; xcb_present_event_t eid; xcb_special_event_t *special_event; struct pipe_context *pipe; struct pipe_resource *output_texture; uint32_t clip_width, clip_height; struct vl_dri3_buffer *back_buffers[BACK_BUFFER_NUM]; int cur_back; int next_back; struct u_rect dirty_areas[BACK_BUFFER_NUM]; struct vl_dri3_buffer *front_buffer; bool is_pixmap; uint32_t send_msc_serial, recv_msc_serial; uint64_t send_sbc, recv_sbc; int64_t last_ust, ns_frame, last_msc, next_msc; bool flushed; int is_different_gpu; }; static void dri3_free_front_buffer(struct vl_dri3_screen *scrn, struct vl_dri3_buffer *buffer) { xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence); xshmfence_unmap_shm(buffer->shm_fence); pipe_resource_reference(&buffer->texture, NULL); FREE(buffer); } static void dri3_free_back_buffer(struct vl_dri3_screen *scrn, struct vl_dri3_buffer *buffer) { xcb_free_pixmap(scrn->conn, buffer->pixmap); xcb_sync_destroy_fence(scrn->conn, buffer->sync_fence); xshmfence_unmap_shm(buffer->shm_fence); if (!scrn->output_texture) pipe_resource_reference(&buffer->texture, NULL); if (buffer->linear_texture) pipe_resource_reference(&buffer->linear_texture, NULL); FREE(buffer); } static void dri3_handle_stamps(struct vl_dri3_screen *scrn, uint64_t ust, uint64_t msc) { int64_t ust_ns = ust * 1000; if (scrn->last_ust && (ust_ns > scrn->last_ust) && scrn->last_msc && (msc > scrn->last_msc)) scrn->ns_frame = (ust_ns - scrn->last_ust) / (msc - scrn->last_msc); scrn->last_ust = ust_ns; scrn->last_msc = msc; } static void dri3_handle_present_event(struct vl_dri3_screen *scrn, xcb_present_generic_event_t *ge) { switch (ge->evtype) { case XCB_PRESENT_CONFIGURE_NOTIFY: { xcb_present_configure_notify_event_t *ce = (void *) ge; scrn->width = ce->width; scrn->height = ce->height; break; } case XCB_PRESENT_COMPLETE_NOTIFY: { xcb_present_complete_notify_event_t *ce = (void *) ge; if (ce->kind == XCB_PRESENT_COMPLETE_KIND_PIXMAP) { scrn->recv_sbc = (scrn->send_sbc & 0xffffffff00000000LL) | ce->serial; if (scrn->recv_sbc > scrn->send_sbc) scrn->recv_sbc -= 0x100000000; dri3_handle_stamps(scrn, ce->ust, ce->msc); } else if (ce->kind == XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) { scrn->recv_msc_serial = ce->serial; dri3_handle_stamps(scrn, ce->ust, ce->msc); } break; } case XCB_PRESENT_EVENT_IDLE_NOTIFY: { xcb_present_idle_notify_event_t *ie = (void *) ge; int b; for (b = 0; b < BACK_BUFFER_NUM; b++) { struct vl_dri3_buffer *buf = scrn->back_buffers[b]; if (buf && buf->pixmap == ie->pixmap) { buf->busy = false; break; } } break; } } free(ge); } static void dri3_flush_present_events(struct vl_dri3_screen *scrn) { if (scrn->special_event) { xcb_generic_event_t *ev; while ((ev = xcb_poll_for_special_event( scrn->conn, scrn->special_event)) != NULL) dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev); } } static bool dri3_wait_present_events(struct vl_dri3_screen *scrn) { if (scrn->special_event) { xcb_generic_event_t *ev; ev = xcb_wait_for_special_event(scrn->conn, scrn->special_event); if (!ev) return false; dri3_handle_present_event(scrn, (xcb_present_generic_event_t *)ev); return true; } return false; } static int dri3_find_back(struct vl_dri3_screen *scrn) { int b; for (;;) { for (b = 0; b < BACK_BUFFER_NUM; b++) { int id = (b + scrn->cur_back) % BACK_BUFFER_NUM; struct vl_dri3_buffer *buffer = scrn->back_buffers[id]; if (!buffer || !buffer->busy) return id; } xcb_flush(scrn->conn); if (!dri3_wait_present_events(scrn)) return -1; } } static struct vl_dri3_buffer * dri3_alloc_back_buffer(struct vl_dri3_screen *scrn) { struct vl_dri3_buffer *buffer; xcb_pixmap_t pixmap; xcb_sync_fence_t sync_fence; struct xshmfence *shm_fence; int buffer_fd, fence_fd; struct pipe_resource templ, *pixmap_buffer_texture; struct winsys_handle whandle; unsigned usage; buffer = CALLOC_STRUCT(vl_dri3_buffer); if (!buffer) return NULL; fence_fd = xshmfence_alloc_shm(); if (fence_fd < 0) goto free_buffer; shm_fence = xshmfence_map_shm(fence_fd); if (!shm_fence) goto close_fd; memset(&templ, 0, sizeof(templ)); templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW; templ.format = PIPE_FORMAT_B8G8R8X8_UNORM; templ.target = PIPE_TEXTURE_2D; templ.last_level = 0; templ.width0 = (scrn->output_texture) ? scrn->output_texture->width0 : scrn->width; templ.height0 = (scrn->output_texture) ? scrn->output_texture->height0 : scrn->height; templ.depth0 = 1; templ.array_size = 1; if (scrn->is_different_gpu) { buffer->texture = (scrn->output_texture) ? scrn->output_texture : scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ); if (!buffer->texture) goto unmap_shm; templ.bind |= PIPE_BIND_SCANOUT | PIPE_BIND_SHARED | PIPE_BIND_LINEAR; buffer->linear_texture = scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ); pixmap_buffer_texture = buffer->linear_texture; if (!buffer->linear_texture) goto no_linear_texture; } else { templ.bind |= PIPE_BIND_SCANOUT | PIPE_BIND_SHARED; buffer->texture = (scrn->output_texture) ? scrn->output_texture : scrn->base.pscreen->resource_create(scrn->base.pscreen, &templ); if (!buffer->texture) goto unmap_shm; pixmap_buffer_texture = buffer->texture; } memset(&whandle, 0, sizeof(whandle)); whandle.type= DRM_API_HANDLE_TYPE_FD; usage = PIPE_HANDLE_USAGE_EXPLICIT_FLUSH | PIPE_HANDLE_USAGE_READ; scrn->base.pscreen->resource_get_handle(scrn->base.pscreen, NULL, pixmap_buffer_texture, &whandle, usage); buffer_fd = whandle.handle; buffer->pitch = whandle.stride; buffer->width = templ.width0; buffer->height = templ.height0; xcb_dri3_pixmap_from_buffer(scrn->conn, (pixmap = xcb_generate_id(scrn->conn)), scrn->drawable, 0, buffer->width, buffer->height, buffer->pitch, scrn->depth, 32, buffer_fd); xcb_dri3_fence_from_fd(scrn->conn, pixmap, (sync_fence = xcb_generate_id(scrn->conn)), false, fence_fd); buffer->pixmap = pixmap; buffer->sync_fence = sync_fence; buffer->shm_fence = shm_fence; xshmfence_trigger(buffer->shm_fence); return buffer; no_linear_texture: pipe_resource_reference(&buffer->texture, NULL); unmap_shm: xshmfence_unmap_shm(shm_fence); close_fd: close(fence_fd); free_buffer: FREE(buffer); return NULL; } static struct vl_dri3_buffer * dri3_get_back_buffer(struct vl_dri3_screen *scrn) { struct vl_dri3_buffer *buffer; struct pipe_resource *texture = NULL; bool allocate_new_buffer = false; int b, id; assert(scrn); scrn->cur_back = dri3_find_back(scrn); if (scrn->cur_back < 0) return NULL; buffer = scrn->back_buffers[scrn->cur_back]; if (scrn->output_texture) { if (!buffer || buffer->width < scrn->width || buffer->height < scrn->height) allocate_new_buffer = true; else if (scrn->is_different_gpu) /* In case of different gpu we can reuse the linear * texture so we only need to set the external * texture for copying */ buffer->texture = scrn->output_texture; else { /* In case of a single gpu we search if the texture is * already present as buffer if not we get the * handle and pixmap for the texture that is set */ for (b = 0; b < BACK_BUFFER_NUM; b++) { id = (b + scrn->cur_back) % BACK_BUFFER_NUM; buffer = scrn->back_buffers[id]; if (buffer && !buffer->busy && buffer->texture == scrn->output_texture) { scrn->cur_back = id; break; } } if (b == BACK_BUFFER_NUM) { allocate_new_buffer = true; scrn->cur_back = scrn->next_back; scrn->next_back = (scrn->next_back + 1) % BACK_BUFFER_NUM; buffer = scrn->back_buffers[scrn->cur_back]; } } } else { if (!buffer || buffer->width != scrn->width || buffer->height != scrn->height) allocate_new_buffer = true; } if (allocate_new_buffer) { struct vl_dri3_buffer *new_buffer; new_buffer = dri3_alloc_back_buffer(scrn); if (!new_buffer) return NULL; if (buffer) dri3_free_back_buffer(scrn, buffer); if (!scrn->output_texture) vl_compositor_reset_dirty_area(&scrn->dirty_areas[scrn->cur_back]); buffer = new_buffer; scrn->back_buffers[scrn->cur_back] = buffer; } pipe_resource_reference(&texture, buffer->texture); xcb_flush(scrn->conn); xshmfence_await(buffer->shm_fence); return buffer; } static bool dri3_set_drawable(struct vl_dri3_screen *scrn, Drawable drawable) { xcb_get_geometry_cookie_t geom_cookie; xcb_get_geometry_reply_t *geom_reply; xcb_void_cookie_t cookie; xcb_generic_error_t *error; bool ret = true; assert(drawable); if (scrn->drawable == drawable) return true; scrn->drawable = drawable; geom_cookie = xcb_get_geometry(scrn->conn, scrn->drawable); geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL); if (!geom_reply) return false; scrn->width = geom_reply->width; scrn->height = geom_reply->height; scrn->depth = geom_reply->depth; free(geom_reply); if (scrn->special_event) { xcb_unregister_for_special_event(scrn->conn, scrn->special_event); scrn->special_event = NULL; cookie = xcb_present_select_input_checked(scrn->conn, scrn->eid, scrn->drawable, XCB_PRESENT_EVENT_MASK_NO_EVENT); xcb_discard_reply(scrn->conn, cookie.sequence); } scrn->is_pixmap = false; scrn->eid = xcb_generate_id(scrn->conn); cookie = xcb_present_select_input_checked(scrn->conn, scrn->eid, scrn->drawable, XCB_PRESENT_EVENT_MASK_CONFIGURE_NOTIFY | XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY | XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY); error = xcb_request_check(scrn->conn, cookie); if (error) { if (error->error_code != BadWindow) ret = false; else { scrn->is_pixmap = true; if (scrn->front_buffer) { dri3_free_front_buffer(scrn, scrn->front_buffer); scrn->front_buffer = NULL; } } free(error); } else scrn->special_event = xcb_register_for_special_xge(scrn->conn, &xcb_present_id, scrn->eid, 0); dri3_flush_present_events(scrn); return ret; } static struct vl_dri3_buffer * dri3_get_front_buffer(struct vl_dri3_screen *scrn) { xcb_dri3_buffer_from_pixmap_cookie_t bp_cookie; xcb_dri3_buffer_from_pixmap_reply_t *bp_reply; xcb_sync_fence_t sync_fence; struct xshmfence *shm_fence; int fence_fd, *fds; struct winsys_handle whandle; struct pipe_resource templ, *texture = NULL; if (scrn->front_buffer) { pipe_resource_reference(&texture, scrn->front_buffer->texture); return scrn->front_buffer; } scrn->front_buffer = CALLOC_STRUCT(vl_dri3_buffer); if (!scrn->front_buffer) return NULL; fence_fd = xshmfence_alloc_shm(); if (fence_fd < 0) goto free_buffer; shm_fence = xshmfence_map_shm(fence_fd); if (!shm_fence) goto close_fd; bp_cookie = xcb_dri3_buffer_from_pixmap(scrn->conn, scrn->drawable); bp_reply = xcb_dri3_buffer_from_pixmap_reply(scrn->conn, bp_cookie, NULL); if (!bp_reply) goto unmap_shm; fds = xcb_dri3_buffer_from_pixmap_reply_fds(scrn->conn, bp_reply); if (fds[0] < 0) goto free_reply; memset(&whandle, 0, sizeof(whandle)); whandle.type = DRM_API_HANDLE_TYPE_FD; whandle.handle = (unsigned)fds[0]; whandle.stride = bp_reply->stride; memset(&templ, 0, sizeof(templ)); templ.bind = PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW; templ.format = PIPE_FORMAT_B8G8R8X8_UNORM; templ.target = PIPE_TEXTURE_2D; templ.last_level = 0; templ.width0 = bp_reply->width; templ.height0 = bp_reply->height; templ.depth0 = 1; templ.array_size = 1; scrn->front_buffer->texture = scrn->base.pscreen->resource_from_handle(scrn->base.pscreen, &templ, &whandle, PIPE_HANDLE_USAGE_READ_WRITE); close(fds[0]); if (!scrn->front_buffer->texture) goto free_reply; xcb_dri3_fence_from_fd(scrn->conn, scrn->drawable, (sync_fence = xcb_generate_id(scrn->conn)), false, fence_fd); pipe_resource_reference(&texture, scrn->front_buffer->texture); scrn->front_buffer->pixmap = scrn->drawable; scrn->front_buffer->width = bp_reply->width; scrn->front_buffer->height = bp_reply->height; scrn->front_buffer->shm_fence = shm_fence; scrn->front_buffer->sync_fence = sync_fence; free(bp_reply); return scrn->front_buffer; free_reply: free(bp_reply); unmap_shm: xshmfence_unmap_shm(shm_fence); close_fd: close(fence_fd); free_buffer: FREE(scrn->front_buffer); return NULL; } static void vl_dri3_flush_frontbuffer(struct pipe_screen *screen, struct pipe_resource *resource, unsigned level, unsigned layer, void *context_private, struct pipe_box *sub_box) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)context_private; uint32_t options = XCB_PRESENT_OPTION_NONE; struct vl_dri3_buffer *back; struct pipe_box src_box; xcb_xfixes_region_t region; xcb_rectangle_t rectangle; back = scrn->back_buffers[scrn->cur_back]; if (!back) return; if (scrn->flushed) { while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc) if (!dri3_wait_present_events(scrn)) return; } rectangle.x = 0; rectangle.y = 0; rectangle.width = (scrn->output_texture) ? scrn->clip_width : scrn->width; rectangle.height = (scrn->output_texture) ? scrn->clip_height : scrn->height; region = xcb_generate_id(scrn->conn); xcb_xfixes_create_region(scrn->conn, region, 2, &rectangle); if (scrn->is_different_gpu) { u_box_origin_2d(back->width, back->height, &src_box); scrn->pipe->resource_copy_region(scrn->pipe, back->linear_texture, 0, 0, 0, 0, back->texture, 0, &src_box); scrn->pipe->flush(scrn->pipe, NULL, 0); } xshmfence_reset(back->shm_fence); back->busy = true; xcb_present_pixmap(scrn->conn, scrn->drawable, back->pixmap, (uint32_t)(++scrn->send_sbc), 0, region, 0, 0, None, None, back->sync_fence, options, scrn->next_msc, 0, 0, 0, NULL); xcb_flush(scrn->conn); scrn->flushed = true; return; } static struct pipe_resource * vl_dri3_screen_texture_from_drawable(struct vl_screen *vscreen, void *drawable) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen; struct vl_dri3_buffer *buffer; assert(scrn); if (!dri3_set_drawable(scrn, (Drawable)drawable)) return NULL; if (scrn->flushed) { while (scrn->special_event && scrn->recv_sbc < scrn->send_sbc) if (!dri3_wait_present_events(scrn)) return NULL; } scrn->flushed = false; buffer = (scrn->is_pixmap) ? dri3_get_front_buffer(scrn) : dri3_get_back_buffer(scrn); if (!buffer) return NULL; return buffer->texture; } static struct u_rect * vl_dri3_screen_get_dirty_area(struct vl_screen *vscreen) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen; assert(scrn); return &scrn->dirty_areas[scrn->cur_back]; } static uint64_t vl_dri3_screen_get_timestamp(struct vl_screen *vscreen, void *drawable) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen; assert(scrn); if (!dri3_set_drawable(scrn, (Drawable)drawable)) return 0; if (!scrn->last_ust) { xcb_present_notify_msc(scrn->conn, scrn->drawable, ++scrn->send_msc_serial, 0, 0, 0); xcb_flush(scrn->conn); while (scrn->special_event && scrn->send_msc_serial > scrn->recv_msc_serial) { if (!dri3_wait_present_events(scrn)) return 0; } } return scrn->last_ust; } static void vl_dri3_screen_set_next_timestamp(struct vl_screen *vscreen, uint64_t stamp) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen; assert(scrn); if (stamp && scrn->last_ust && scrn->ns_frame && scrn->last_msc) scrn->next_msc = ((int64_t)stamp - scrn->last_ust + scrn->ns_frame/2) / scrn->ns_frame + scrn->last_msc; else scrn->next_msc = 0; } static void * vl_dri3_screen_get_private(struct vl_screen *vscreen) { return vscreen; } static void vl_dri3_screen_set_back_texture_from_output(struct vl_screen *vscreen, struct pipe_resource *buffer, uint32_t width, uint32_t height) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen; assert(scrn); scrn->output_texture = buffer; scrn->clip_width = (width) ? width : scrn->width; scrn->clip_height = (height) ? height : scrn->height; } static void vl_dri3_screen_destroy(struct vl_screen *vscreen) { struct vl_dri3_screen *scrn = (struct vl_dri3_screen *)vscreen; int i; assert(vscreen); dri3_flush_present_events(scrn); if (scrn->front_buffer) { dri3_free_front_buffer(scrn, scrn->front_buffer); scrn->front_buffer = NULL; return; } for (i = 0; i < BACK_BUFFER_NUM; ++i) { if (scrn->back_buffers[i]) { dri3_free_back_buffer(scrn, scrn->back_buffers[i]); scrn->back_buffers[i] = NULL; } } if (scrn->special_event) { xcb_void_cookie_t cookie = xcb_present_select_input_checked(scrn->conn, scrn->eid, scrn->drawable, XCB_PRESENT_EVENT_MASK_NO_EVENT); xcb_discard_reply(scrn->conn, cookie.sequence); xcb_unregister_for_special_event(scrn->conn, scrn->special_event); } scrn->pipe->destroy(scrn->pipe); scrn->base.pscreen->destroy(scrn->base.pscreen); pipe_loader_release(&scrn->base.dev, 1); FREE(scrn); return; } struct vl_screen * vl_dri3_screen_create(Display *display, int screen) { struct vl_dri3_screen *scrn; const xcb_query_extension_reply_t *extension; xcb_dri3_open_cookie_t open_cookie; xcb_dri3_open_reply_t *open_reply; xcb_get_geometry_cookie_t geom_cookie; xcb_get_geometry_reply_t *geom_reply; xcb_xfixes_query_version_cookie_t xfixes_cookie; xcb_xfixes_query_version_reply_t *xfixes_reply; xcb_generic_error_t *error; int fd; assert(display); scrn = CALLOC_STRUCT(vl_dri3_screen); if (!scrn) return NULL; scrn->conn = XGetXCBConnection(display); if (!scrn->conn) goto free_screen; xcb_prefetch_extension_data(scrn->conn , &xcb_dri3_id); xcb_prefetch_extension_data(scrn->conn, &xcb_present_id); xcb_prefetch_extension_data (scrn->conn, &xcb_xfixes_id); extension = xcb_get_extension_data(scrn->conn, &xcb_dri3_id); if (!(extension && extension->present)) goto free_screen; extension = xcb_get_extension_data(scrn->conn, &xcb_present_id); if (!(extension && extension->present)) goto free_screen; extension = xcb_get_extension_data(scrn->conn, &xcb_xfixes_id); if (!(extension && extension->present)) goto free_screen; xfixes_cookie = xcb_xfixes_query_version(scrn->conn, XCB_XFIXES_MAJOR_VERSION, XCB_XFIXES_MINOR_VERSION); xfixes_reply = xcb_xfixes_query_version_reply(scrn->conn, xfixes_cookie, &error); if (!xfixes_reply || error || xfixes_reply->major_version < 2) { free(error); free(xfixes_reply); goto free_screen; } free(xfixes_reply); open_cookie = xcb_dri3_open(scrn->conn, RootWindow(display, screen), None); open_reply = xcb_dri3_open_reply(scrn->conn, open_cookie, NULL); if (!open_reply) goto free_screen; if (open_reply->nfd != 1) { free(open_reply); goto free_screen; } fd = xcb_dri3_open_reply_fds(scrn->conn, open_reply)[0]; if (fd < 0) { free(open_reply); goto free_screen; } fcntl(fd, F_SETFD, FD_CLOEXEC); free(open_reply); fd = loader_get_user_preferred_fd(fd, &scrn->is_different_gpu); geom_cookie = xcb_get_geometry(scrn->conn, RootWindow(display, screen)); geom_reply = xcb_get_geometry_reply(scrn->conn, geom_cookie, NULL); if (!geom_reply) goto close_fd; /* TODO support depth other than 24 */ if (geom_reply->depth != 24) { free(geom_reply); goto close_fd; } free(geom_reply); if (pipe_loader_drm_probe_fd(&scrn->base.dev, fd)) scrn->base.pscreen = pipe_loader_create_screen(scrn->base.dev); if (!scrn->base.pscreen) goto release_pipe; scrn->pipe = scrn->base.pscreen->context_create(scrn->base.pscreen, &scrn->base, 0); if (!scrn->pipe) goto no_context; scrn->base.destroy = vl_dri3_screen_destroy; scrn->base.texture_from_drawable = vl_dri3_screen_texture_from_drawable; scrn->base.get_dirty_area = vl_dri3_screen_get_dirty_area; scrn->base.get_timestamp = vl_dri3_screen_get_timestamp; scrn->base.set_next_timestamp = vl_dri3_screen_set_next_timestamp; scrn->base.get_private = vl_dri3_screen_get_private; scrn->base.pscreen->flush_frontbuffer = vl_dri3_flush_frontbuffer; scrn->base.set_back_texture_from_output = vl_dri3_screen_set_back_texture_from_output; scrn->next_back = 1; return &scrn->base; no_context: scrn->base.pscreen->destroy(scrn->base.pscreen); release_pipe: if (scrn->base.dev) { pipe_loader_release(&scrn->base.dev, 1); fd = -1; } close_fd: if (fd != -1) close(fd); free_screen: FREE(scrn); return NULL; }