diff --git a/src/gallium/drivers/llvmpipe/lp_context.c b/src/gallium/drivers/llvmpipe/lp_context.c index bdf956d45a9..36f1f378c74 100644 --- a/src/gallium/drivers/llvmpipe/lp_context.c +++ b/src/gallium/drivers/llvmpipe/lp_context.c @@ -253,6 +253,10 @@ llvmpipe_create_context(struct pipe_screen *screen, void *priv, llvmpipe_init_sampler_matrix(llvmpipe); +#ifdef HAVE_LIBDRM + llvmpipe_init_fence_funcs(&llvmpipe->pipe); +#endif + #ifdef USE_GLOBAL_LLVM_CONTEXT llvmpipe->context.ref = LLVMGetGlobalContext(); llvmpipe->context.owned = false; diff --git a/src/gallium/drivers/llvmpipe/lp_fence.c b/src/gallium/drivers/llvmpipe/lp_fence.c index 75207f357f2..3bb0e58ca1b 100644 --- a/src/gallium/drivers/llvmpipe/lp_fence.c +++ b/src/gallium/drivers/llvmpipe/lp_fence.c @@ -28,12 +28,60 @@ #include "pipe/p_screen.h" #include "util/u_memory.h" +#include "util/os_file.h" #include "lp_debug.h" #include "lp_fence.h" +#include "lp_screen.h" +#include "lp_texture.h" +#include "lp_flush.h" +#include "lp_context.h" #include "util/timespec.h" +#ifdef HAVE_LIBDRM +#include +#include +#include +#include "util/libsync.h" +#include "util/list.h" +#endif + +static unsigned fence_id = 0; + +#ifdef HAVE_LIBDRM +static int sync_fd_wait(int fd, uint64_t timeout) +{ + struct pollfd fds = {0}; + int ret; + struct timespec poll_start, poll_end, timeout_ts, diff; + timespec_from_nsec(&timeout_ts, timeout); + + fds.fd = fd; + fds.events = POLLIN; + + do { + clock_gettime(CLOCK_MONOTONIC, &poll_start); + ret = ppoll(&fds, 1, &timeout_ts, NULL); + clock_gettime(CLOCK_MONOTONIC, &poll_end); + if (ret > 0) { + if (fds.revents & (POLLERR | POLLNVAL)) { + errno = EINVAL; + return -1; + } + return 0; + } else if (ret == 0) { + errno = ETIME; + return -1; + } + + timespec_sub(&diff, &poll_end, &poll_start); + timespec_sub_saturate(&timeout_ts, &timeout_ts, &diff); + } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); + + return ret; +} +#endif /** * Create a new fence object. @@ -47,13 +95,13 @@ struct lp_fence * lp_fence_create(unsigned rank) { - static unsigned fence_id = 0; struct lp_fence *fence = CALLOC_STRUCT(lp_fence); if (!fence) return NULL; pipe_reference_init(&fence->reference, 1); + fence->type = LP_FENCE_TYPE_SW; (void) mtx_init(&fence->mutex, mtx_plain); cnd_init(&fence->signalled); @@ -61,13 +109,16 @@ lp_fence_create(unsigned rank) fence->id = p_atomic_inc_return(&fence_id) - 1; fence->rank = rank; +#ifdef HAVE_LIBDRM + fence->sync_fd = -1; +#endif + if (LP_DEBUG & DEBUG_FENCE) debug_printf("%s %d\n", __func__, fence->id); return fence; } - /** Destroy a fence. Called when refcount hits zero. */ void lp_fence_destroy(struct lp_fence *fence) @@ -75,8 +126,16 @@ lp_fence_destroy(struct lp_fence *fence) if (LP_DEBUG & DEBUG_FENCE) debug_printf("%s %d\n", __func__, fence->id); - mtx_destroy(&fence->mutex); - cnd_destroy(&fence->signalled); + if (fence->type == LP_FENCE_TYPE_SW) { + mtx_destroy(&fence->mutex); + cnd_destroy(&fence->signalled); + } +#ifdef HAVE_LIBDRM + else { + close(fence->sync_fd); + } +#endif + FREE(fence); } @@ -91,27 +150,42 @@ lp_fence_signal(struct lp_fence *fence) if (LP_DEBUG & DEBUG_FENCE) debug_printf("%s %d\n", __func__, fence->id); - mtx_lock(&fence->mutex); + if (fence->type == LP_FENCE_TYPE_SW) { + mtx_lock(&fence->mutex); - fence->count++; - assert(fence->count <= fence->rank); + fence->count++; + assert(fence->count <= fence->rank); - if (LP_DEBUG & DEBUG_FENCE) - debug_printf("%s count=%u rank=%u\n", __func__, - fence->count, fence->rank); + if (LP_DEBUG & DEBUG_FENCE) + debug_printf("%s count=%u rank=%u\n", __func__, + fence->count, fence->rank); - /* Wakeup all threads waiting on the mutex: + /* Wakeup all threads waiting on the mutex: + */ + cnd_broadcast(&fence->signalled); + + mtx_unlock(&fence->mutex); + } + + /* sync fd fence we create ourselves are always signalled so + * we don't need an else clause */ - cnd_broadcast(&fence->signalled); - - mtx_unlock(&fence->mutex); } bool lp_fence_signalled(struct lp_fence *f) { - return f->count == f->rank; + if (f->type == LP_FENCE_TYPE_SW) + return f->count == f->rank; +#ifdef HAVE_LIBDRM + else { + return sync_wait(f->sync_fd, 0) == 0; + } +#endif + + unreachable("Fence is an unknown type"); + return false; } @@ -121,12 +195,20 @@ lp_fence_wait(struct lp_fence *f) if (LP_DEBUG & DEBUG_FENCE) debug_printf("%s %d\n", __func__, f->id); - mtx_lock(&f->mutex); - assert(f->issued); - while (f->count < f->rank) { - cnd_wait(&f->signalled, &f->mutex); + if (f->type == LP_FENCE_TYPE_SW) { + mtx_lock(&f->mutex); + assert(f->issued); + while (f->count < f->rank) { + cnd_wait(&f->signalled, &f->mutex); + } + mtx_unlock(&f->mutex); } - mtx_unlock(&f->mutex); +#ifdef HAVE_LIBDRM + else { + assert(f->sync_fd != -1); + sync_wait(f->sync_fd, -1); + } +#endif } @@ -142,20 +224,133 @@ lp_fence_timedwait(struct lp_fence *f, uint64_t timeout) if (LP_DEBUG & DEBUG_FENCE) debug_printf("%s %d\n", __func__, f->id); - mtx_lock(&f->mutex); - assert(f->issued); - while (f->count < f->rank) { - int ret; - if (ts_overflow) - ret = cnd_wait(&f->signalled, &f->mutex); - else - ret = cnd_timedwait(&f->signalled, &f->mutex, &abs_ts); - if (ret != thrd_success) - break; + if (f->type == LP_FENCE_TYPE_SW) { + mtx_lock(&f->mutex); + assert(f->issued); + while (f->count < f->rank) { + int ret; + if (ts_overflow) + ret = cnd_wait(&f->signalled, &f->mutex); + else + ret = cnd_timedwait(&f->signalled, &f->mutex, &abs_ts); + if (ret != thrd_success) + break; + } + + const bool result = (f->count >= f->rank); + mtx_unlock(&f->mutex); + return result; + } +#ifdef HAVE_LIBDRM + else { + assert(f->sync_fd != -1); + return sync_fd_wait(f->sync_fd, timeout) == 0; + } +#endif + + unreachable("Fence is an unknown type"); + return false; +} + +#ifdef HAVE_LIBDRM +static int +lp_fence_get_fd(struct pipe_screen *pscreen, + struct pipe_fence_handle *fence) +{ + struct llvmpipe_screen *screen = llvmpipe_screen(pscreen); + struct lp_fence *lp_fence = (struct lp_fence *)fence; + + /* It's not ideal, but since we cannot properly support sync files + * from userspace, what we will do instead is wait for llvmpipe to + * finish rendering, and then export the sync file. If its not a + * sync file we imported we can just export a dummy one that is always + * signalled since llvmpipe should have now finished all its work. + */ + list_for_each_entry(struct llvmpipe_context, ctx, &screen->ctx_list, list) { + llvmpipe_finish((struct pipe_context *)ctx, __func__); } - const bool result = (f->count >= f->rank); - mtx_unlock(&f->mutex); + if (lp_fence && lp_fence->sync_fd != -1) { + return os_dupfd_cloexec(lp_fence->sync_fd); + } else if (screen->dummy_sync_fd != -1) { + return os_dupfd_cloexec(screen->dummy_sync_fd); + } - return result; + return -1; } + +static void +lp_create_fence_fd(struct pipe_context *pipe, + struct pipe_fence_handle **fence, + int fd, + enum pipe_fd_type type) +{ + /* Only sync fd are supported */ + if (type != PIPE_FD_TYPE_NATIVE_SYNC) + goto fail; + + struct lp_fence *f = CALLOC_STRUCT(lp_fence); + + if (!fence) + goto fail; + + pipe_reference_init(&f->reference, 1); + f->type = LP_FENCE_TYPE_SYNC_FD; + f->id = p_atomic_inc_return(&fence_id) - 1; + f->sync_fd = os_dupfd_cloexec(fd); + f->issued = true; + + *fence = (struct pipe_fence_handle*)f; + return; +fail: + *fence = NULL; + return; +} + +void +llvmpipe_init_screen_fence_funcs(struct pipe_screen *pscreen) +{ + struct llvmpipe_screen *screen = llvmpipe_screen(pscreen); + screen->dummy_sync_fd = -1; + + /* Try to create dummy dmabuf, and only set functions if we were able to */ + int fd; + screen->dummy_dmabuf = + (struct llvmpipe_memory_allocation*)pscreen->allocate_memory_fd( + pscreen, 1, &fd, true); + + /* We don't need this fd handle and API always creates it */ + if (fd != -1) + close(fd); + + if (screen->dummy_dmabuf) { + struct dma_buf_export_sync_file export = { + .flags = DMA_BUF_SYNC_RW, + .fd = -1, + }; + + if (drmIoctl(screen->dummy_dmabuf->dmabuf_fd, + DMA_BUF_IOCTL_EXPORT_SYNC_FILE, + &export)) + goto fail; + + screen->dummy_sync_fd = export.fd; + } + + pscreen->fence_get_fd = lp_fence_get_fd; + return; +fail: + if (screen->dummy_dmabuf) { + pscreen->free_memory_fd( + pscreen, (struct pipe_memory_allocation*)screen->dummy_dmabuf); + screen->dummy_dmabuf = NULL; + } + return; +} + +void +llvmpipe_init_fence_funcs(struct pipe_context *pipe) +{ + pipe->create_fence_fd = lp_create_fence_fd; +} +#endif diff --git a/src/gallium/drivers/llvmpipe/lp_fence.h b/src/gallium/drivers/llvmpipe/lp_fence.h index eaa7c0a4a58..aab797dca49 100644 --- a/src/gallium/drivers/llvmpipe/lp_fence.h +++ b/src/gallium/drivers/llvmpipe/lp_fence.h @@ -37,10 +37,17 @@ struct pipe_screen; +enum lp_fence_type +{ + LP_FENCE_TYPE_SW, + LP_FENCE_TYPE_SYNC_FD, +}; + struct lp_fence { struct pipe_reference reference; + enum lp_fence_type type; unsigned id; mtx_t mutex; @@ -49,6 +56,8 @@ struct lp_fence bool issued; unsigned rank; unsigned count; + + int sync_fd; }; @@ -94,5 +103,12 @@ lp_fence_issued(const struct lp_fence *fence) return fence->issued; } +#ifdef HAVE_LIBDRM +void +llvmpipe_init_screen_fence_funcs(struct pipe_screen *pscreen); + +void +llvmpipe_init_fence_funcs(struct pipe_context *pipe); +#endif #endif /* LP_FENCE_H */ diff --git a/src/gallium/drivers/llvmpipe/lp_screen.c b/src/gallium/drivers/llvmpipe/lp_screen.c index 7daa5319a12..24629878060 100644 --- a/src/gallium/drivers/llvmpipe/lp_screen.c +++ b/src/gallium/drivers/llvmpipe/lp_screen.c @@ -134,6 +134,8 @@ llvmpipe_get_param(struct pipe_screen *screen, enum pipe_cap param) return DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT; else return DRM_PRIME_CAP_IMPORT; + case PIPE_CAP_NATIVE_FENCE_FD: + return lscreen->dummy_sync_fd != -1; #endif case PIPE_CAP_NPOT_TEXTURES: case PIPE_CAP_MIXED_FRAMEBUFFER_SIZES: @@ -1175,6 +1177,7 @@ llvmpipe_create_screen(struct sw_winsys *winsys) #ifdef HAVE_LINUX_UDMABUF_H screen->udmabuf_fd = open("/dev/udmabuf", O_RDWR); + llvmpipe_init_screen_fence_funcs(&screen->base); #endif uint64_t alignment; diff --git a/src/gallium/drivers/llvmpipe/lp_screen.h b/src/gallium/drivers/llvmpipe/lp_screen.h index 2e18da15687..f36628340b3 100644 --- a/src/gallium/drivers/llvmpipe/lp_screen.h +++ b/src/gallium/drivers/llvmpipe/lp_screen.h @@ -85,6 +85,9 @@ struct llvmpipe_screen uint64_t mem_file_size; struct util_vma_heap mem_heap; #endif + + struct llvmpipe_memory_allocation *dummy_dmabuf; + int dummy_sync_fd; }; diff --git a/src/gallium/drivers/llvmpipe/lp_texture.h b/src/gallium/drivers/llvmpipe/lp_texture.h index b539b0d3160..594e4c410b1 100644 --- a/src/gallium/drivers/llvmpipe/lp_texture.h +++ b/src/gallium/drivers/llvmpipe/lp_texture.h @@ -51,7 +51,6 @@ enum llvmpipe_memory_fd_type LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF, }; - struct pipe_context; struct pipe_screen; struct llvmpipe_context;