/********************************************************** * Copyright 2009 VMware, 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, 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. * **********************************************************/ #include "pipe/p_compiler.h" #include "pipe/p_inlines.h" #include "util/u_memory.h" #include "vmw_screen.h" #include "trace/tr_drm.h" #include "vmw_screen.h" #include "vmw_surface.h" #include "vmw_fence.h" #include "vmw_context.h" #include #include #include #include #include static struct dri1_api dri1_api_hooks; static struct dri1_api_version ddx_required = { 0, 1, 0 }; static struct dri1_api_version ddx_compat = { 0, 0, 0 }; static struct dri1_api_version dri_required = { 4, 0, 0 }; static struct dri1_api_version dri_compat = { 4, 0, 0 }; static struct dri1_api_version drm_required = { 0, 1, 0 }; static struct dri1_api_version drm_compat = { 0, 0, 0 }; static boolean vmw_dri1_check_version(const struct dri1_api_version *cur, const struct dri1_api_version *required, const struct dri1_api_version *compat, const char component[]) { if (cur->major > required->major && cur->major <= compat->major) return TRUE; if (cur->major == required->major && cur->minor >= required->minor) return TRUE; fprintf(stderr, "%s version failure.\n", component); fprintf(stderr, "%s version is %d.%d.%d and this driver can only work\n" "with versions %d.%d.x through %d.x.x.\n", component, cur->major, cur->minor, cur->patch_level, required->major, required->minor, compat->major); return FALSE; } /* This is actually the entrypoint to the entire driver, called by the * libGL (or EGL, or ...) code via the drm_api_hooks table at the * bottom of the file. */ static struct pipe_screen * vmw_drm_create_screen(struct drm_api *drm_api, int fd, struct drm_create_screen_arg *arg) { struct vmw_winsys_screen *vws; struct pipe_screen *screen; struct dri1_create_screen_arg *dri1; if (arg != NULL) { switch (arg->mode) { case DRM_CREATE_NORMAL: break; case DRM_CREATE_DRI1: dri1 = (struct dri1_create_screen_arg *)arg; if (!vmw_dri1_check_version(&dri1->ddx_version, &ddx_required, &ddx_compat, "ddx - driver api")) return NULL; if (!vmw_dri1_check_version(&dri1->dri_version, &dri_required, &dri_compat, "dri info")) return NULL; if (!vmw_dri1_check_version(&dri1->drm_version, &drm_required, &drm_compat, "vmwgfx drm driver")) return NULL; dri1->api = &dri1_api_hooks; break; default: return NULL; } } vws = vmw_winsys_create( fd ); if (!vws) goto out_no_vws; screen = svga_screen_create( &vws->base ); if (!screen) goto out_no_screen; return screen; /* Failure cases: */ out_no_screen: vmw_winsys_destroy( vws ); out_no_vws: return NULL; } static INLINE boolean vmw_dri1_intersect_src_bbox(struct drm_clip_rect *dst, int dst_x, int dst_y, const struct drm_clip_rect *src, const struct drm_clip_rect *bbox) { int xy1; int xy2; xy1 = ((int)src->x1 > (int)bbox->x1 + dst_x) ? src->x1 : (int)bbox->x1 + dst_x; xy2 = ((int)src->x2 < (int)bbox->x2 + dst_x) ? src->x2 : (int)bbox->x2 + dst_x; if (xy1 >= xy2 || xy1 < 0) return FALSE; dst->x1 = xy1; dst->x2 = xy2; xy1 = ((int)src->y1 > (int)bbox->y1 + dst_y) ? src->y1 : (int)bbox->y1 + dst_y; xy2 = ((int)src->y2 < (int)bbox->y2 + dst_y) ? src->y2 : (int)bbox->y2 + dst_y; if (xy1 >= xy2 || xy1 < 0) return FALSE; dst->y1 = xy1; dst->y2 = xy2; return TRUE; } /** * No fancy get-surface-from-sarea stuff here. * Just use the present blit. */ static void vmw_dri1_present_locked(struct pipe_context *locked_pipe, struct pipe_surface *surf, const struct drm_clip_rect *rect, unsigned int num_clip, int x_draw, int y_draw, const struct drm_clip_rect *bbox, struct pipe_fence_handle **p_fence) { struct svga_winsys_surface *srf = svga_screen_texture_get_winsys_surface(surf->texture); struct vmw_svga_winsys_surface *vsrf = vmw_svga_winsys_surface(srf); struct vmw_winsys_screen *vws = vmw_winsys_screen(svga_winsys_screen(locked_pipe->screen)); struct drm_clip_rect clip; int i; struct { SVGA3dCmdHeader header; SVGA3dCmdPresent body; SVGA3dCopyRect rect; } cmd; boolean visible = FALSE; uint32_t fence_seq = 0; VMW_FUNC; cmd.header.id = SVGA_3D_CMD_PRESENT; cmd.header.size = sizeof cmd.body + sizeof cmd.rect; cmd.body.sid = vsrf->sid; for (i = 0; i < num_clip; ++i) { if (!vmw_dri1_intersect_src_bbox(&clip, x_draw, y_draw, rect++, bbox)) continue; cmd.rect.x = clip.x1; cmd.rect.y = clip.y1; cmd.rect.w = clip.x2 - clip.x1; cmd.rect.h = clip.y2 - clip.y1; cmd.rect.srcx = (int)clip.x1 - x_draw; cmd.rect.srcy = (int)clip.y1 - y_draw; vmw_printf("%s: Clip %d x %d y %d w %d h %d srcx %d srcy %d\n", __FUNCTION__, i, cmd.rect.x, cmd.rect.y, cmd.rect.w, cmd.rect.h, cmd.rect.srcx, cmd.rect.srcy); vmw_ioctl_command(vws, &cmd, sizeof cmd.header + cmd.header.size, &fence_seq); visible = TRUE; } *p_fence = (visible) ? vmw_pipe_fence(fence_seq) : NULL; vmw_svga_winsys_surface_reference(&vsrf, NULL); } /** * FIXME: We'd probably want to cache these buffers in the * screen, based on handle. */ static struct pipe_buffer * vmw_drm_buffer_from_handle(struct drm_api *drm_api, struct pipe_screen *screen, const char *name, unsigned handle) { struct vmw_svga_winsys_surface *vsrf; struct svga_winsys_surface *ssrf; struct vmw_winsys_screen *vws = vmw_winsys_screen(svga_winsys_screen(screen)); struct pipe_buffer *buf; union drm_vmw_surface_reference_arg arg; struct drm_vmw_surface_arg *req = &arg.req; struct drm_vmw_surface_create_req *rep = &arg.rep; int ret; int i; /** * The vmware device specific handle is the hardware SID. * FIXME: We probably want to move this to the ioctl implementations. */ memset(&arg, 0, sizeof(arg)); req->sid = handle; ret = drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_REF_SURFACE, &arg, sizeof(arg)); if (ret) { fprintf(stderr, "Failed referencing shared surface. SID %d.\n" "Error %d (%s).\n", handle, ret, strerror(-ret)); return NULL; } if (rep->mip_levels[0] != 1) { fprintf(stderr, "Incorrect number of mipmap levels on shared surface." " SID %d, levels %d\n", handle, rep->mip_levels[0]); goto out_mip; } for (i=1; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { if (rep->mip_levels[i] != 0) { fprintf(stderr, "Incorrect number of faces levels on shared surface." " SID %d, face %d present.\n", handle, i); goto out_mip; } } vsrf = CALLOC_STRUCT(vmw_svga_winsys_surface); if (!vsrf) goto out_mip; pipe_reference_init(&vsrf->refcnt, 1); p_atomic_set(&vsrf->validated, 0); vsrf->sid = handle; ssrf = svga_winsys_surface(vsrf); buf = svga_screen_buffer_wrap_surface(screen, rep->format, ssrf); if (!buf) vmw_svga_winsys_surface_reference(&vsrf, NULL); return buf; out_mip: vmw_ioctl_surface_destroy(vws, handle); return NULL; } static struct pipe_texture * vmw_drm_texture_from_handle(struct drm_api *drm_api, struct pipe_screen *screen, struct pipe_texture *templat, const char *name, unsigned stride, unsigned handle) { struct pipe_buffer *buffer; buffer = vmw_drm_buffer_from_handle(drm_api, screen, name, handle); if (!buffer) return NULL; return screen->texture_blanket(screen, templat, &stride, buffer); } static boolean vmw_drm_handle_from_buffer(struct drm_api *drm_api, struct pipe_screen *screen, struct pipe_buffer *buffer, unsigned *handle) { struct svga_winsys_surface *surface = svga_screen_buffer_get_winsys_surface(buffer); struct vmw_svga_winsys_surface *vsrf; if (!surface) return FALSE; vsrf = vmw_svga_winsys_surface(surface); *handle = vsrf->sid; vmw_svga_winsys_surface_reference(&vsrf, NULL); return TRUE; } static boolean vmw_drm_handle_from_texture(struct drm_api *drm_api, struct pipe_screen *screen, struct pipe_texture *texture, unsigned *stride, unsigned *handle) { struct pipe_buffer *buffer; if (!svga_screen_buffer_from_texture(texture, &buffer, stride)) return FALSE; return vmw_drm_handle_from_buffer(drm_api, screen, buffer, handle); } static struct pipe_context* vmw_drm_create_context(struct drm_api *drm_api, struct pipe_screen *screen) { return vmw_svga_context_create(screen); } static struct dri1_api dri1_api_hooks = { .front_srf_locked = NULL, .present_locked = vmw_dri1_present_locked }; static struct drm_api vmw_drm_api_hooks = { .create_screen = vmw_drm_create_screen, .create_context = vmw_drm_create_context, .texture_from_shared_handle = vmw_drm_texture_from_handle, .shared_handle_from_texture = vmw_drm_handle_from_texture, .local_handle_from_texture = vmw_drm_handle_from_texture, }; struct drm_api* drm_api_create() { return trace_drm_create(&vmw_drm_api_hooks); }