summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@gmail.com>2016-01-18 14:08:56 +1000
committerDave Airlie <airlied@redhat.com>2016-01-18 14:16:20 +1000
commit45ed0af7ed5b52aa44c88b354cb4bfdcf4f78c17 (patch)
treec19b60d29069a96b1dac0582bcd77dd82115974a
parentc7b577e14b667750af9a73d63d4da615f1031508 (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.ac8
-rw-r--r--src/virglrenderer.c11
-rw-r--r--src/virglrenderer.h7
-rw-r--r--src/vrend_renderer.c259
-rw-r--r--src/vrend_renderer.h6
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