static int virgl_drm_get_caps(struct virgl_winsys *vws, struct virgl_drm_caps *caps) { struct virgl_drm_winsys *vdws = virgl_drm_winsys(vws); struct drm_virtgpu_get_caps args; int ret; virgl_ws_fill_new_caps_defaults(caps); memset(&args, 0, sizeof(args)); if (vdws->has_capset_query_fix) { /* if we have the query fix - try and get cap set id 2 first */ args.cap_set_id = 2; args.size = sizeof(union virgl_caps); } else { args.cap_set_id = 1; args.size = sizeof(struct virgl_caps_v1); } args.addr = (unsigned long)&caps->caps; ret = drmIoctl(vdws->fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &args); if (ret == -1 && errno == EINVAL) { /* Fallback to v1 */ args.cap_set_id = 1; args.size = sizeof(struct virgl_caps_v1); ret = drmIoctl(vdws->fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &args); if (ret == -1) return ret; } return ret; }
static int virgl_drm_winsys_submit_cmd(struct virgl_winsys *qws, struct virgl_cmd_buf *_cbuf) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct virgl_drm_cmd_buf *cbuf = virgl_drm_cmd_buf(_cbuf); struct drm_virtgpu_execbuffer eb; int ret; if (cbuf->base.cdw == 0) return 0; memset(&eb, 0, sizeof(struct drm_virtgpu_execbuffer)); eb.command = (unsigned long)(void*)cbuf->buf; eb.size = cbuf->base.cdw * 4; eb.num_bo_handles = cbuf->cres; eb.bo_handles = (unsigned long)(void *)cbuf->res_hlist; ret = drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &eb); if (ret == -1) fprintf(stderr,"got error from kernel - expect bad rendering %d\n", errno); cbuf->base.cdw = 0; virgl_drm_release_all_res(qdws, cbuf); memset(cbuf->is_handle_added, 0, sizeof(cbuf->is_handle_added)); return ret; }
static void virgl_drm_winsys_resource_unref(struct virgl_winsys *qws, struct virgl_hw_res *hres) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); virgl_drm_resource_reference(qdws, &hres, NULL); }
static void virgl_fence_reference(struct virgl_winsys *vws, struct pipe_fence_handle **dst, struct pipe_fence_handle *src) { struct virgl_drm_winsys *vdws = virgl_drm_winsys(vws); virgl_drm_resource_reference(vdws, (struct virgl_hw_res **)dst, virgl_hw_res(src)); }
static struct virgl_hw_res * virgl_drm_winsys_resource_create(struct virgl_winsys *qws, enum pipe_texture_target target, uint32_t format, uint32_t bind, uint32_t width, uint32_t height, uint32_t depth, uint32_t array_size, uint32_t last_level, uint32_t nr_samples, uint32_t size) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct drm_virtgpu_resource_create createcmd; int ret; struct virgl_hw_res *res; uint32_t stride = width * util_format_get_blocksize(format); res = CALLOC_STRUCT(virgl_hw_res); if (!res) return NULL; memset(&createcmd, 0, sizeof(createcmd)); createcmd.target = target; createcmd.format = format; createcmd.bind = bind; createcmd.width = width; createcmd.height = height; createcmd.depth = depth; createcmd.array_size = array_size; createcmd.last_level = last_level; createcmd.nr_samples = nr_samples; createcmd.stride = stride; createcmd.size = size; ret = drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE, &createcmd); if (ret != 0) { FREE(res); return NULL; } res->bind = bind; res->format = format; res->res_handle = createcmd.res_handle; res->bo_handle = createcmd.bo_handle; res->size = size; res->stride = stride; pipe_reference_init(&res->reference, 1); res->num_cs_references = 0; return res; }
static int virgl_drm_get_caps(struct virgl_winsys *vws, struct virgl_drm_caps *caps) { struct virgl_drm_winsys *vdws = virgl_drm_winsys(vws); struct drm_virtgpu_get_caps args; memset(&args, 0, sizeof(args)); args.cap_set_id = 1; args.addr = (unsigned long)&caps->caps; args.size = sizeof(union virgl_caps); return drmIoctl(vdws->fd, DRM_IOCTL_VIRTGPU_GET_CAPS, &args); }
static void virgl_drm_emit_res(struct virgl_winsys *qws, struct virgl_cmd_buf *_cbuf, struct virgl_hw_res *res, boolean write_buf) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct virgl_drm_cmd_buf *cbuf = virgl_drm_cmd_buf(_cbuf); boolean already_in_list = virgl_drm_lookup_res(cbuf, res); if (write_buf) cbuf->base.buf[cbuf->base.cdw++] = res->res_handle; if (!already_in_list) virgl_drm_add_res(qdws, cbuf, res); }
static void virgl_drm_winsys_destroy(struct virgl_winsys *qws) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); virgl_cache_flush(qdws); util_hash_table_destroy(qdws->bo_handles); util_hash_table_destroy(qdws->bo_names); mtx_destroy(&qdws->bo_handles_mutex); mtx_destroy(&qdws->mutex); FREE(qdws); }
static void virgl_drm_resource_wait(struct virgl_winsys *qws, struct virgl_hw_res *res) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct drm_virtgpu_3d_wait waitcmd; int ret; memset(&waitcmd, 0, sizeof(waitcmd)); waitcmd.handle = res->bo_handle; again: ret = drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_WAIT, &waitcmd); if (ret == -EAGAIN) goto again; }
static void virgl_drm_screen_destroy(struct pipe_screen *pscreen) { struct virgl_screen *screen = virgl_screen(pscreen); boolean destroy; mtx_lock(&virgl_screen_mutex); destroy = --screen->refcnt == 0; if (destroy) { int fd = virgl_drm_winsys(screen->vws)->fd; util_hash_table_remove(fd_tab, intptr_to_pointer(fd)); } mtx_unlock(&virgl_screen_mutex); if (destroy) { pscreen->destroy = screen->winsys_priv; pscreen->destroy(pscreen); } }
static int virgl_bo_transfer_get(struct virgl_winsys *vws, struct virgl_hw_res *res, const struct pipe_box *box, uint32_t stride, uint32_t layer_stride, uint32_t buf_offset, uint32_t level) { struct virgl_drm_winsys *vdws = virgl_drm_winsys(vws); struct drm_virtgpu_3d_transfer_from_host fromhostcmd; memset(&fromhostcmd, 0, sizeof(fromhostcmd)); fromhostcmd.bo_handle = res->bo_handle; fromhostcmd.level = level; fromhostcmd.offset = buf_offset; // fromhostcmd.stride = stride; // fromhostcmd.layer_stride = layer_stride; fromhostcmd.box = *(struct drm_virtgpu_3d_box *)box; return drmIoctl(vdws->fd, DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST, &fromhostcmd); }
static boolean virgl_drm_winsys_resource_get_handle(struct virgl_winsys *qws, struct virgl_hw_res *res, uint32_t stride, struct winsys_handle *whandle) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct drm_gem_flink flink; if (!res) return FALSE; if (whandle->type == DRM_API_HANDLE_TYPE_SHARED) { if (!res->flinked) { memset(&flink, 0, sizeof(flink)); flink.handle = res->bo_handle; if (drmIoctl(qdws->fd, DRM_IOCTL_GEM_FLINK, &flink)) { return FALSE; } res->flinked = TRUE; res->flink = flink.name; mtx_lock(&qdws->bo_handles_mutex); util_hash_table_set(qdws->bo_names, (void *)(uintptr_t)res->flink, res); mtx_unlock(&qdws->bo_handles_mutex); } whandle->handle = res->flink; } else if (whandle->type == DRM_API_HANDLE_TYPE_KMS) { whandle->handle = res->bo_handle; } else if (whandle->type == DRM_API_HANDLE_TYPE_FD) { if (drmPrimeHandleToFD(qdws->fd, res->bo_handle, DRM_CLOEXEC, (int*)&whandle->handle)) return FALSE; mtx_lock(&qdws->bo_handles_mutex); util_hash_table_set(qdws->bo_handles, (void *)(uintptr_t)res->bo_handle, res); mtx_unlock(&qdws->bo_handles_mutex); } whandle->stride = stride; return TRUE; }
static bool virgl_fence_wait(struct virgl_winsys *vws, struct pipe_fence_handle *fence, uint64_t timeout) { struct virgl_drm_winsys *vdws = virgl_drm_winsys(vws); struct virgl_hw_res *res = virgl_hw_res(fence); if (timeout == 0) return !virgl_drm_resource_is_busy(vdws, res); if (timeout != PIPE_TIMEOUT_INFINITE) { int64_t start_time = os_time_get(); timeout /= 1000; while (virgl_drm_resource_is_busy(vdws, res)) { if (os_time_get() - start_time >= timeout) return FALSE; os_time_sleep(10); } return TRUE; } virgl_drm_resource_wait(vws, res); return TRUE; }
static int virgl_bo_transfer_put(struct virgl_winsys *vws, struct virgl_hw_res *res, const struct pipe_box *box, uint32_t stride, uint32_t layer_stride, uint32_t buf_offset, uint32_t level) { struct virgl_drm_winsys *vdws = virgl_drm_winsys(vws); struct drm_virtgpu_3d_transfer_to_host tohostcmd; memset(&tohostcmd, 0, sizeof(tohostcmd)); tohostcmd.bo_handle = res->bo_handle; tohostcmd.box.x = box->x; tohostcmd.box.y = box->y; tohostcmd.box.z = box->z; tohostcmd.box.w = box->width; tohostcmd.box.h = box->height; tohostcmd.box.d = box->depth; tohostcmd.offset = buf_offset; tohostcmd.level = level; // tohostcmd.stride = stride; // tohostcmd.layer_stride = stride; return drmIoctl(vdws->fd, DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST, &tohostcmd); }
static void *virgl_drm_resource_map(struct virgl_winsys *qws, struct virgl_hw_res *res) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct drm_virtgpu_map mmap_arg; void *ptr; if (res->ptr) return res->ptr; memset(&mmap_arg, 0, sizeof(mmap_arg)); mmap_arg.handle = res->bo_handle; if (drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_MAP, &mmap_arg)) return NULL; ptr = os_mmap(0, res->size, PROT_READ|PROT_WRITE, MAP_SHARED, qdws->fd, mmap_arg.offset); if (ptr == MAP_FAILED) return NULL; res->ptr = ptr; return ptr; }
static struct virgl_hw_res * virgl_drm_winsys_resource_cache_create(struct virgl_winsys *qws, enum pipe_texture_target target, uint32_t format, uint32_t bind, uint32_t width, uint32_t height, uint32_t depth, uint32_t array_size, uint32_t last_level, uint32_t nr_samples, uint32_t size) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct virgl_hw_res *res, *curr_res; struct list_head *curr, *next; int64_t now; int ret; /* only store binds for vertex/index/const buffers */ if (bind != VIRGL_BIND_CONSTANT_BUFFER && bind != VIRGL_BIND_INDEX_BUFFER && bind != VIRGL_BIND_VERTEX_BUFFER && bind != VIRGL_BIND_CUSTOM) goto alloc; mtx_lock(&qdws->mutex); res = NULL; curr = qdws->delayed.next; next = curr->next; now = os_time_get(); while (curr != &qdws->delayed) { curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head); if (!res && ((ret = virgl_is_res_compat(qdws, curr_res, size, bind, format)) > 0)) res = curr_res; else if (os_time_timeout(curr_res->start, curr_res->end, now)) { LIST_DEL(&curr_res->head); virgl_hw_res_destroy(qdws, curr_res); } else break; if (ret == -1) break; curr = next; next = curr->next; } if (!res && ret != -1) { while (curr != &qdws->delayed) { curr_res = LIST_ENTRY(struct virgl_hw_res, curr, head); ret = virgl_is_res_compat(qdws, curr_res, size, bind, format); if (ret > 0) { res = curr_res; break; } if (ret == -1) break; curr = next; next = curr->next; } } if (res) { LIST_DEL(&res->head); --qdws->num_delayed; mtx_unlock(&qdws->mutex); pipe_reference_init(&res->reference, 1); return res; } mtx_unlock(&qdws->mutex); alloc: res = virgl_drm_winsys_resource_create(qws, target, format, bind, width, height, depth, array_size, last_level, nr_samples, size); if (bind == VIRGL_BIND_CONSTANT_BUFFER || bind == VIRGL_BIND_INDEX_BUFFER || bind == VIRGL_BIND_VERTEX_BUFFER) res->cacheable = TRUE; return res; }
static struct virgl_hw_res * virgl_drm_winsys_resource_create_handle(struct virgl_winsys *qws, struct winsys_handle *whandle) { struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws); struct drm_gem_open open_arg = {}; struct drm_virtgpu_resource_info info_arg = {}; struct virgl_hw_res *res; uint32_t handle = whandle->handle; if (whandle->offset != 0) { fprintf(stderr, "attempt to import unsupported winsys offset %u\n", whandle->offset); return NULL; } mtx_lock(&qdws->bo_handles_mutex); if (whandle->type == DRM_API_HANDLE_TYPE_SHARED) { res = util_hash_table_get(qdws->bo_names, (void*)(uintptr_t)handle); if (res) { struct virgl_hw_res *r = NULL; virgl_drm_resource_reference(qdws, &r, res); goto done; } } if (whandle->type == DRM_API_HANDLE_TYPE_FD) { int r; r = drmPrimeFDToHandle(qdws->fd, whandle->handle, &handle); if (r) { res = NULL; goto done; } } res = util_hash_table_get(qdws->bo_handles, (void*)(uintptr_t)handle); fprintf(stderr, "resource %p for handle %d, pfd=%d\n", res, handle, whandle->handle); if (res) { struct virgl_hw_res *r = NULL; virgl_drm_resource_reference(qdws, &r, res); goto done; } res = CALLOC_STRUCT(virgl_hw_res); if (!res) goto done; if (whandle->type == DRM_API_HANDLE_TYPE_FD) { res->bo_handle = handle; } else { fprintf(stderr, "gem open handle %d\n", handle); memset(&open_arg, 0, sizeof(open_arg)); open_arg.name = whandle->handle; if (drmIoctl(qdws->fd, DRM_IOCTL_GEM_OPEN, &open_arg)) { FREE(res); res = NULL; goto done; } res->bo_handle = open_arg.handle; } res->name = handle; memset(&info_arg, 0, sizeof(info_arg)); info_arg.bo_handle = res->bo_handle; if (drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, &info_arg)) { /* close */ FREE(res); res = NULL; goto done; } res->res_handle = info_arg.res_handle; res->size = info_arg.size; res->stride = info_arg.stride; pipe_reference_init(&res->reference, 1); res->num_cs_references = 0; util_hash_table_set(qdws->bo_handles, (void *)(uintptr_t)handle, res); done: mtx_unlock(&qdws->bo_handles_mutex); return res; }