mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-20 11:40:10 +01:00
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:
parent
57c81bab04
commit
d21aa86b54
6 changed files with 254 additions and 34 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 <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.
|
||||
|
|
@ -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);
|
||||
|
||||
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,6 +150,7 @@ lp_fence_signal(struct lp_fence *fence)
|
|||
if (LP_DEBUG & DEBUG_FENCE)
|
||||
debug_printf("%s %d\n", __func__, fence->id);
|
||||
|
||||
if (fence->type == LP_FENCE_TYPE_SW) {
|
||||
mtx_lock(&fence->mutex);
|
||||
|
||||
fence->count++;
|
||||
|
|
@ -105,13 +165,27 @@ lp_fence_signal(struct lp_fence *fence)
|
|||
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
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
lp_fence_signalled(struct lp_fence *f)
|
||||
{
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
#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)
|
||||
debug_printf("%s %d\n", __func__, f->id);
|
||||
|
||||
if (f->type == LP_FENCE_TYPE_SW) {
|
||||
mtx_lock(&f->mutex);
|
||||
assert(f->issued);
|
||||
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);
|
||||
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__);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ enum llvmpipe_memory_fd_type
|
|||
LLVMPIPE_MEMORY_FD_TYPE_DMA_BUF,
|
||||
};
|
||||
|
||||
|
||||
struct pipe_context;
|
||||
struct pipe_screen;
|
||||
struct llvmpipe_context;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue