diff options
author | Marc-André Lureau <marcandre.lureau@gmail.com> | 2016-01-18 14:08:56 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-01-18 14:16:20 +1000 |
commit | 45ed0af7ed5b52aa44c88b354cb4bfdcf4f78c17 (patch) | |
tree | c19b60d29069a96b1dac0582bcd77dd82115974a | |
parent | c7b577e14b667750af9a73d63d4da615f1031508 (diff) |
renderer: use a thread to block for fences.thread-sync
Instead of polling the fences regularly, have a thread
that blocks for a single fence using a separate shared
context, then uses eventfd to wake up the main thread
when something happens.
[airlied: rewrote patch to use gallium thread API,
and rework some other bits]
-rw-r--r-- | configure.ac | 8 | ||||
-rw-r--r-- | src/virglrenderer.c | 11 | ||||
-rw-r--r-- | src/virglrenderer.h | 7 | ||||
-rw-r--r-- | src/vrend_renderer.c | 259 | ||||
-rw-r--r-- | src/vrend_renderer.h | 6 |
5 files changed, 271 insertions, 20 deletions
diff --git a/configure.ac b/configure.ac index eb9bd12..a362edc 100644 --- a/configure.ac +++ b/configure.ac @@ -73,6 +73,14 @@ if test "x$build_tests" = "xyes"; then AC_PATH_PROG(VALGRIND, [valgrind]) fi +AC_MSG_CHECKING([for eventfd]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include <sys/eventfd.h>]], + [[eventfd (0, 0);]])], + [AC_MSG_RESULT([yes]) + CFLAGS="$CFLAGS -DCONFIG_HAVE_EVENTFD"], + [AC_MSG_RESULT([no])]) + AM_CONDITIONAL(HAVE_VALGRIND, [test "x$VALGRIND" != "x"]) AM_CONDITIONAL(BUILD_TESTS, [test "x$build_tests" = "xyes"]) diff --git a/src/virglrenderer.c b/src/virglrenderer.c index d75fdc8..05a922b 100644 --- a/src/virglrenderer.c +++ b/src/virglrenderer.c @@ -249,6 +249,7 @@ void virgl_renderer_cleanup(void *cookie) int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks *cbs) { + uint32_t renderer_flags = 0; if (!cookie || !cbs) return -1; @@ -265,7 +266,10 @@ int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks use_egl_context = 1; } - return vrend_renderer_init(&virgl_cbs); + if (flags & VIRGL_RENDERER_THREAD_SYNC) + renderer_flags |= VREND_USE_THREAD_SYNC; + + return vrend_renderer_init(&virgl_cbs, renderer_flags); } int virgl_renderer_get_fd_for_texture(uint32_t tex_id, int *fd) @@ -277,3 +281,8 @@ void virgl_renderer_reset(void) { vrend_renderer_reset(); } + +int virgl_renderer_get_poll_fd(void) +{ + return vrend_renderer_get_poll_fd(); +} diff --git a/src/virglrenderer.h b/src/virglrenderer.h index 8794dd6..e9d3e83 100644 --- a/src/virglrenderer.h +++ b/src/virglrenderer.h @@ -56,6 +56,11 @@ struct virgl_renderer_callbacks { /* virtio-gpu compatible interface */ #define VIRGL_RENDERER_USE_EGL 1 +/* + * Wait for sync objects in thread rather than polling + * need to use virgl_renderer_get_poll_fd to know if this feature is in effect. + */ +#define VIRGL_RENDERER_THREAD_SYNC 2 VIRGL_EXPORT int virgl_renderer_init(void *cookie, int flags, struct virgl_renderer_callbacks *cb); VIRGL_EXPORT void virgl_renderer_poll(void); /* force fences */ @@ -163,4 +168,6 @@ VIRGL_EXPORT void virgl_renderer_cleanup(void *cookie); /* reset the rendererer - destroy all contexts and resource */ VIRGL_EXPORT void virgl_renderer_reset(void); + +VIRGL_EXPORT int virgl_renderer_get_poll_fd(void); #endif diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c index 549b379..3f854de 100644 --- a/src/vrend_renderer.c +++ b/src/vrend_renderer.c @@ -21,6 +21,9 @@ * OTHER DEALINGS IN THE SOFTWARE. * **************************************************************************/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include <epoxy/gl.h> @@ -36,6 +39,7 @@ #include "util/u_memory.h" #include "util/u_dual_blend.h" +#include "os/os_thread.h" #include "util/u_double_list.h" #include "util/u_format.h" #include "tgsi/tgsi_parse.h" @@ -48,6 +52,11 @@ #include "virgl_hw.h" #include "tgsi/tgsi_text.h" + +#ifdef CONFIG_HAVE_EVENTFD +#include <sys/eventfd.h> +#endif + /* debugging aid to dump shaders */ int vrend_dump_shaders; @@ -80,7 +89,6 @@ struct global_renderer_state { int gl_major_ver; int gl_minor_ver; - struct list_head fence_list; struct vrend_context *current_ctx; struct vrend_context *current_hw_ctx; struct list_head waiting_query_list; @@ -103,6 +111,18 @@ struct global_renderer_state { bool use_explicit_locations; uint32_t max_uniform_blocks; struct list_head active_ctx_list; + + /* threaded sync */ + bool stop_sync_thread; + int eventfd; + + pipe_mutex fence_mutex; + struct list_head fence_list; + struct list_head fence_wait_list; + pipe_condvar fence_cond; + + pipe_thread sync_thread; + virgl_gl_context sync_context; }; static struct global_renderer_state vrend_state; @@ -390,7 +410,6 @@ static void vrend_apply_sampler_state(struct vrend_context *ctx, struct vrend_resource *res, uint32_t shader_type, int id, int sampler_id, uint32_t srgb_decode); - void vrend_update_stencil_state(struct vrend_context *ctx); static struct vrend_format_table tex_conv_table[VIRGL_FORMAT_MAX]; @@ -3483,7 +3502,147 @@ static GLenum tgsitargettogltarget(const enum pipe_texture_target target, int nr return PIPE_BUFFER; } -int vrend_renderer_init(struct vrend_if_cbs *cbs) +static void vrend_free_sync_thread(void) +{ + if (!vrend_state.sync_thread) + return; + + pipe_mutex_lock(vrend_state.fence_mutex); + vrend_state.stop_sync_thread = true; + pipe_mutex_unlock(vrend_state.fence_mutex); + + pipe_condvar_signal(vrend_state.fence_cond); + pipe_thread_wait(vrend_state.sync_thread); + vrend_state.sync_thread = 0; +} + +static ssize_t +write_full(int fd, const void *buf, size_t count) +{ + ssize_t ret = 0; + ssize_t total = 0; + + while (count) { + ret = write(fd, buf, count); + if (ret < 0) { + if (errno == EINTR) + continue; + break; + } + count -= ret; + buf += ret; + total += ret; + } + return total; +} + +static void wait_sync(struct vrend_fence *fence) +{ + GLenum glret; + ssize_t n; + uint64_t value = 1; + + do { + glret = glClientWaitSync(fence->syncobj, 0, 1000000000); + + switch (glret) { + case GL_WAIT_FAILED: + fprintf(stderr, "wait sync failed: illegal fence object %p\n", fence->syncobj); + break; + case GL_ALREADY_SIGNALED: + case GL_CONDITION_SATISFIED: + break; + default: + break; + } + } while (glret == GL_TIMEOUT_EXPIRED); + + pipe_mutex_lock(vrend_state.fence_mutex); + list_addtail(&fence->fences, &vrend_state.fence_list); + pipe_mutex_unlock(vrend_state.fence_mutex); + + n = write_full(vrend_state.eventfd, &value, sizeof(value)); + if (n != sizeof(value)) { + perror("failed to write to eventfd\n"); + } +} + +static int thread_sync(void *arg) +{ + virgl_gl_context gl_context = vrend_state.sync_context; + struct vrend_fence *fence, *stor; + + pipe_mutex_lock(vrend_state.fence_mutex); + vrend_clicbs->make_current(0, gl_context); + + while (!vrend_state.stop_sync_thread) { + if (LIST_IS_EMPTY(&vrend_state.fence_wait_list) && + pipe_condvar_wait(vrend_state.fence_cond, vrend_state.fence_mutex) != 0) { + fprintf(stderr, "error while waiting on condition\n"); + break; + } + + LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &vrend_state.fence_wait_list, fences) { + if (vrend_state.stop_sync_thread) + break; + list_del(&fence->fences); + pipe_mutex_unlock(vrend_state.fence_mutex); + wait_sync(fence); + pipe_mutex_lock(vrend_state.fence_mutex); + } + } + + vrend_clicbs->make_current(0, 0); + vrend_clicbs->destroy_gl_context(vrend_state.sync_context); + pipe_mutex_unlock(vrend_state.fence_mutex); + return 0; +} + +#ifdef CONFIG_HAVE_EVENTFD +static void vrend_renderer_use_threaded_sync(void) +{ + struct virgl_gl_ctx_param ctx_params; + + if (getenv("VIRGL_DISABLE_MT")) + return; + + ctx_params.shared = true; + ctx_params.major_ver = vrend_state.gl_major_ver; + ctx_params.minor_ver = vrend_state.gl_minor_ver; + + vrend_state.stop_sync_thread = false; + vrend_state.eventfd = -1; + + vrend_state.sync_context = vrend_clicbs->create_gl_context(0, &ctx_params); + if (vrend_state.sync_context == NULL) { + fprintf(stderr, "failed to create sync opengl context\n"); + return; + } + + vrend_state.eventfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (vrend_state.eventfd == -1) { + fprintf(stderr, "Failed to create eventfd\n"); + vrend_clicbs->destroy_gl_context(vrend_state.sync_context); + return; + } + + pipe_condvar_init(vrend_state.fence_cond); + pipe_mutex_init(vrend_state.fence_mutex); + + vrend_state.sync_thread = pipe_thread_create(thread_sync, NULL); + if (!vrend_state.sync_thread) { + close(vrend_state.eventfd); + vrend_state.eventfd = -1; + vrend_clicbs->destroy_gl_context(vrend_state.sync_context); + } +} +#else +static void vrend_renderer_use_threaded_sync(void) +{ +} +#endif + +int vrend_renderer_init(struct vrend_if_cbs *cbs, uint32_t flags) { int gl_ver; virgl_gl_context gl_context; @@ -3550,8 +3709,13 @@ int vrend_renderer_init(struct vrend_if_cbs *cbs) vrend_build_format_list(); + if (flags & VREND_USE_THREAD_SYNC) { + vrend_renderer_use_threaded_sync(); + } + vrend_clicbs->destroy_gl_context(gl_context); list_inithead(&vrend_state.fence_list); + list_inithead(&vrend_state.fence_wait_list); list_inithead(&vrend_state.waiting_query_list); list_inithead(&vrend_state.active_ctx_list); /* create 0 context */ @@ -3566,6 +3730,7 @@ vrend_renderer_fini(void) if (!vrend_state.inited) return; + vrend_free_sync_thread(); vrend_decode_reset(false); vrend_object_fini_resource_table(); vrend_decode_reset(true); @@ -5258,8 +5423,39 @@ int vrend_renderer_create_fence(int client_fence_id, uint32_t ctx_id) fence->ctx_id = ctx_id; fence->fence_id = client_fence_id; fence->syncobj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - list_addtail(&fence->fences, &vrend_state.fence_list); + + if (fence->syncobj == NULL) + goto fail; + + if (vrend_state.sync_thread) { + pipe_mutex_lock(vrend_state.fence_mutex); + list_addtail(&fence->fences, &vrend_state.fence_wait_list); + pipe_mutex_unlock(vrend_state.fence_mutex); + pipe_condvar_signal(vrend_state.fence_cond); + } else + list_addtail(&fence->fences, &vrend_state.fence_list); return 0; + + fail: + fprintf(stderr, "failed to create fence sync object\n"); + free(fence); + return ENOMEM; +} + +static void free_fence_locked(struct vrend_fence *fence) +{ + list_del(&fence->fences); + glDeleteSync(fence->syncobj); + free(fence); +} + +static void flush_eventfd(int fd) +{ + ssize_t len; + uint64_t value; + do { + len = read(fd, &value, sizeof(value)); + } while ((len == -1 && errno == EINTR) || len == sizeof(value)); } void vrend_renderer_check_fences(void) @@ -5271,18 +5467,28 @@ void vrend_renderer_check_fences(void) if (!vrend_state.inited) return; - vrend_renderer_force_ctx_0(); - LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &vrend_state.fence_list, fences) { - glret = glClientWaitSync(fence->syncobj, 0, 0); - if (glret == GL_ALREADY_SIGNALED){ - latest_id = fence->fence_id; - list_del(&fence->fences); - glDeleteSync(fence->syncobj); - free(fence); + if (vrend_state.sync_thread) { + flush_eventfd(vrend_state.eventfd); + pipe_mutex_lock(vrend_state.fence_mutex); + LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &vrend_state.fence_list, fences) { + if (fence->fence_id > latest_id) + latest_id = fence->fence_id; + free_fence_locked(fence); } - /* don't bother checking any subsequent ones */ - else if (glret == GL_TIMEOUT_EXPIRED) { - break; + pipe_mutex_unlock(vrend_state.fence_mutex); + } else { + vrend_renderer_force_ctx_0(); + + LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &vrend_state.fence_list, fences) { + glret = glClientWaitSync(fence->syncobj, 0, 0); + if (glret == GL_ALREADY_SIGNALED){ + latest_id = fence->fence_id; + free_fence_locked(fence); + } + /* don't bother checking any subsequent ones */ + else if (glret == GL_TIMEOUT_EXPIRED) { + break; + } } } @@ -6030,15 +6236,24 @@ static void vrend_reset_fences(void) { struct vrend_fence *fence, *stor; + if (vrend_state.sync_thread) + pipe_mutex_lock(vrend_state.fence_mutex); + LIST_FOR_EACH_ENTRY_SAFE(fence, stor, &vrend_state.fence_list, fences) { - list_del(&fence->fences); - glDeleteSync(fence->syncobj); - free(fence); + free_fence_locked(fence); } + + if (vrend_state.sync_thread) + pipe_mutex_unlock(vrend_state.fence_mutex); } void vrend_renderer_reset(void) { + + if (vrend_state.sync_thread) { + vrend_free_sync_thread(); + vrend_state.stop_sync_thread = false; + } vrend_reset_fences(); vrend_decode_reset(false); vrend_object_fini_resource_table(); @@ -6046,3 +6261,11 @@ void vrend_renderer_reset(void) vrend_object_init_resource_table(); vrend_renderer_context_create_internal(0, 0, NULL); } + +int vrend_renderer_get_poll_fd(void) +{ + if (!vrend_state.inited) + return -1; + + return vrend_state.eventfd; +} diff --git a/src/vrend_renderer.h b/src/vrend_renderer.h index bc2e447..b78fba9 100644 --- a/src/vrend_renderer.h +++ b/src/vrend_renderer.h @@ -98,7 +98,10 @@ struct vrend_if_cbs { void (*destroy_gl_context)(virgl_gl_context ctx); int (*make_current)(int scanout, virgl_gl_context ctx); }; -int vrend_renderer_init(struct vrend_if_cbs *cbs); + +#define VREND_USE_THREAD_SYNC 1 + +int vrend_renderer_init(struct vrend_if_cbs *cbs, uint32_t flags); void vrend_insert_format(struct vrend_format_table *entry, uint32_t bindings); void vrend_insert_format_swizzle(int override_format, struct vrend_format_table *entry, uint32_t bindings, uint8_t swizzle[4]); @@ -370,6 +373,7 @@ void vrend_renderer_blit_gl(struct vrend_context *ctx, const struct pipe_blit_info *info); void vrend_renderer_reset(void); +int vrend_renderer_get_poll_fd(void); void vrend_decode_reset(bool ctx_0_only); #define VREND_GL_VER_MAJOR 3 #define VREND_GL_VER_MINOR 1 |