mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-20 13:50:11 +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);
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
mtx_destroy(&fence->mutex);
|
if (fence->type == LP_FENCE_TYPE_SW) {
|
||||||
cnd_destroy(&fence->signalled);
|
mtx_destroy(&fence->mutex);
|
||||||
|
cnd_destroy(&fence->signalled);
|
||||||
|
}
|
||||||
|
#ifdef HAVE_LIBDRM
|
||||||
|
else {
|
||||||
|
close(fence->sync_fd);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
FREE(fence);
|
FREE(fence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,27 +150,42 @@ 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);
|
||||||
|
|
||||||
mtx_lock(&fence->mutex);
|
if (fence->type == LP_FENCE_TYPE_SW) {
|
||||||
|
mtx_lock(&fence->mutex);
|
||||||
|
|
||||||
fence->count++;
|
fence->count++;
|
||||||
assert(fence->count <= fence->rank);
|
assert(fence->count <= fence->rank);
|
||||||
|
|
||||||
if (LP_DEBUG & DEBUG_FENCE)
|
if (LP_DEBUG & DEBUG_FENCE)
|
||||||
debug_printf("%s count=%u rank=%u\n", __func__,
|
debug_printf("%s count=%u rank=%u\n", __func__,
|
||||||
fence->count, fence->rank);
|
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
|
bool
|
||||||
lp_fence_signalled(struct lp_fence *f)
|
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)
|
if (LP_DEBUG & DEBUG_FENCE)
|
||||||
debug_printf("%s %d\n", __func__, f->id);
|
debug_printf("%s %d\n", __func__, f->id);
|
||||||
|
|
||||||
mtx_lock(&f->mutex);
|
if (f->type == LP_FENCE_TYPE_SW) {
|
||||||
assert(f->issued);
|
mtx_lock(&f->mutex);
|
||||||
while (f->count < f->rank) {
|
assert(f->issued);
|
||||||
cnd_wait(&f->signalled, &f->mutex);
|
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)
|
if (LP_DEBUG & DEBUG_FENCE)
|
||||||
debug_printf("%s %d\n", __func__, f->id);
|
debug_printf("%s %d\n", __func__, f->id);
|
||||||
|
|
||||||
mtx_lock(&f->mutex);
|
if (f->type == LP_FENCE_TYPE_SW) {
|
||||||
assert(f->issued);
|
mtx_lock(&f->mutex);
|
||||||
while (f->count < f->rank) {
|
assert(f->issued);
|
||||||
int ret;
|
while (f->count < f->rank) {
|
||||||
if (ts_overflow)
|
int ret;
|
||||||
ret = cnd_wait(&f->signalled, &f->mutex);
|
if (ts_overflow)
|
||||||
else
|
ret = cnd_wait(&f->signalled, &f->mutex);
|
||||||
ret = cnd_timedwait(&f->signalled, &f->mutex, &abs_ts);
|
else
|
||||||
if (ret != thrd_success)
|
ret = cnd_timedwait(&f->signalled, &f->mutex, &abs_ts);
|
||||||
break;
|
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);
|
if (lp_fence && lp_fence->sync_fd != -1) {
|
||||||
mtx_unlock(&f->mutex);
|
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
|
||||||
|
|
|
||||||
|
|
@ -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 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue