llvmpipe: Implement EGL_ANDROID_native_fence_sync

Add support for the native fence extension through the use
of udmabuf's dmabuffers. Thanks to the fact that llvmpipe
is synchronous we can export a sync file from a dmabuf that
is always signalled and use this to implement the extension.
For importing sync files we can simply poll the sync file
to wait until it is signalled.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28735>
This commit is contained in:
Lucas Fryzek 2024-03-12 13:03:11 -04:00 committed by Marge Bot
parent 57c81bab04
commit d21aa86b54
6 changed files with 254 additions and 34 deletions

View file

@ -253,6 +253,10 @@ llvmpipe_create_context(struct pipe_screen *screen, void *priv,
llvmpipe_init_sampler_matrix(llvmpipe); llvmpipe_init_sampler_matrix(llvmpipe);
#ifdef HAVE_LIBDRM
llvmpipe_init_fence_funcs(&llvmpipe->pipe);
#endif
#ifdef USE_GLOBAL_LLVM_CONTEXT #ifdef USE_GLOBAL_LLVM_CONTEXT
llvmpipe->context.ref = LLVMGetGlobalContext(); llvmpipe->context.ref = LLVMGetGlobalContext();
llvmpipe->context.owned = false; llvmpipe->context.owned = false;

View file

@ -28,12 +28,60 @@
#include "pipe/p_screen.h" #include "pipe/p_screen.h"
#include "util/u_memory.h" #include "util/u_memory.h"
#include "util/os_file.h"
#include "lp_debug.h" #include "lp_debug.h"
#include "lp_fence.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" #include "util/timespec.h"
#ifdef HAVE_LIBDRM
#include <xf86drm.h>
#include <drm-uapi/dma-buf.h>
#include <poll.h>
#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. * Create a new fence object.
@ -47,13 +95,13 @@
struct lp_fence * struct lp_fence *
lp_fence_create(unsigned rank) lp_fence_create(unsigned rank)
{ {
static unsigned fence_id = 0;
struct lp_fence *fence = CALLOC_STRUCT(lp_fence); struct lp_fence *fence = CALLOC_STRUCT(lp_fence);
if (!fence) if (!fence)
return NULL; return NULL;
pipe_reference_init(&fence->reference, 1); pipe_reference_init(&fence->reference, 1);
fence->type = LP_FENCE_TYPE_SW;
(void) mtx_init(&fence->mutex, mtx_plain); (void) mtx_init(&fence->mutex, mtx_plain);
cnd_init(&fence->signalled); cnd_init(&fence->signalled);
@ -61,13 +109,16 @@ lp_fence_create(unsigned rank)
fence->id = p_atomic_inc_return(&fence_id) - 1; fence->id = p_atomic_inc_return(&fence_id) - 1;
fence->rank = rank; fence->rank = rank;
#ifdef HAVE_LIBDRM
fence->sync_fd = -1;
#endif
if (LP_DEBUG & DEBUG_FENCE) if (LP_DEBUG & DEBUG_FENCE)
debug_printf("%s %d\n", __func__, fence->id); debug_printf("%s %d\n", __func__, fence->id);
return fence; return fence;
} }
/** Destroy a fence. Called when refcount hits zero. */ /** Destroy a fence. Called when refcount hits zero. */
void void
lp_fence_destroy(struct lp_fence *fence) lp_fence_destroy(struct lp_fence *fence)
@ -75,8 +126,16 @@ lp_fence_destroy(struct lp_fence *fence)
if (LP_DEBUG & DEBUG_FENCE) if (LP_DEBUG & DEBUG_FENCE)
debug_printf("%s %d\n", __func__, fence->id); debug_printf("%s %d\n", __func__, fence->id);
if (fence->type == LP_FENCE_TYPE_SW) {
mtx_destroy(&fence->mutex); mtx_destroy(&fence->mutex);
cnd_destroy(&fence->signalled); cnd_destroy(&fence->signalled);
}
#ifdef HAVE_LIBDRM
else {
close(fence->sync_fd);
}
#endif
FREE(fence); FREE(fence);
} }
@ -91,6 +150,7 @@ lp_fence_signal(struct lp_fence *fence)
if (LP_DEBUG & DEBUG_FENCE) if (LP_DEBUG & DEBUG_FENCE)
debug_printf("%s %d\n", __func__, fence->id); debug_printf("%s %d\n", __func__, fence->id);
if (fence->type == LP_FENCE_TYPE_SW) {
mtx_lock(&fence->mutex); mtx_lock(&fence->mutex);
fence->count++; fence->count++;
@ -105,13 +165,27 @@ lp_fence_signal(struct lp_fence *fence)
cnd_broadcast(&fence->signalled); cnd_broadcast(&fence->signalled);
mtx_unlock(&fence->mutex); mtx_unlock(&fence->mutex);
}
/* sync fd fence we create ourselves are always signalled so
* we don't need an else clause
*/
} }
bool bool
lp_fence_signalled(struct lp_fence *f) lp_fence_signalled(struct lp_fence *f)
{ {
if (f->type == LP_FENCE_TYPE_SW)
return f->count == f->rank; 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) if (LP_DEBUG & DEBUG_FENCE)
debug_printf("%s %d\n", __func__, f->id); debug_printf("%s %d\n", __func__, f->id);
if (f->type == LP_FENCE_TYPE_SW) {
mtx_lock(&f->mutex); mtx_lock(&f->mutex);
assert(f->issued); assert(f->issued);
while (f->count < f->rank) { while (f->count < f->rank) {
cnd_wait(&f->signalled, &f->mutex); 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,6 +224,7 @@ lp_fence_timedwait(struct lp_fence *f, uint64_t timeout)
if (LP_DEBUG & DEBUG_FENCE) if (LP_DEBUG & DEBUG_FENCE)
debug_printf("%s %d\n", __func__, f->id); debug_printf("%s %d\n", __func__, f->id);
if (f->type == LP_FENCE_TYPE_SW) {
mtx_lock(&f->mutex); mtx_lock(&f->mutex);
assert(f->issued); assert(f->issued);
while (f->count < f->rank) { while (f->count < f->rank) {
@ -156,6 +239,118 @@ lp_fence_timedwait(struct lp_fence *f, uint64_t timeout)
const bool result = (f->count >= f->rank); const bool result = (f->count >= f->rank);
mtx_unlock(&f->mutex); mtx_unlock(&f->mutex);
return result; 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__);
}
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 -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

View file

@ -37,10 +37,17 @@
struct pipe_screen; struct pipe_screen;
enum lp_fence_type
{
LP_FENCE_TYPE_SW,
LP_FENCE_TYPE_SYNC_FD,
};
struct lp_fence struct lp_fence
{ {
struct pipe_reference reference; struct pipe_reference reference;
enum lp_fence_type type;
unsigned id; unsigned id;
mtx_t mutex; mtx_t mutex;
@ -49,6 +56,8 @@ struct lp_fence
bool issued; bool issued;
unsigned rank; unsigned rank;
unsigned count; unsigned count;
int sync_fd;
}; };
@ -94,5 +103,12 @@ lp_fence_issued(const struct lp_fence *fence)
return fence->issued; 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 */ #endif /* LP_FENCE_H */

View file

@ -134,6 +134,8 @@ llvmpipe_get_param(struct pipe_screen *screen, enum pipe_cap param)
return DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT; return DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT;
else else
return DRM_PRIME_CAP_IMPORT; return DRM_PRIME_CAP_IMPORT;
case PIPE_CAP_NATIVE_FENCE_FD:
return lscreen->dummy_sync_fd != -1;
#endif #endif
case PIPE_CAP_NPOT_TEXTURES: case PIPE_CAP_NPOT_TEXTURES:
case PIPE_CAP_MIXED_FRAMEBUFFER_SIZES: case PIPE_CAP_MIXED_FRAMEBUFFER_SIZES:
@ -1175,6 +1177,7 @@ llvmpipe_create_screen(struct sw_winsys *winsys)
#ifdef HAVE_LINUX_UDMABUF_H #ifdef HAVE_LINUX_UDMABUF_H
screen->udmabuf_fd = open("/dev/udmabuf", O_RDWR); screen->udmabuf_fd = open("/dev/udmabuf", O_RDWR);
llvmpipe_init_screen_fence_funcs(&screen->base);
#endif #endif
uint64_t alignment; uint64_t alignment;

View file

@ -85,6 +85,9 @@ struct llvmpipe_screen
uint64_t mem_file_size; uint64_t mem_file_size;
struct util_vma_heap mem_heap; struct util_vma_heap mem_heap;
#endif #endif
struct llvmpipe_memory_allocation *dummy_dmabuf;
int dummy_sync_fd;
}; };

View file

@ -51,7 +51,6 @@ enum llvmpipe_memory_fd_type
LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF, LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF,
}; };
struct pipe_context; struct pipe_context;
struct pipe_screen; struct pipe_screen;
struct llvmpipe_context; struct llvmpipe_context;