diff options
Diffstat (limited to 'src/vrend_renderer.c')
-rw-r--r-- | src/vrend_renderer.c | 259 |
1 files changed, 241 insertions, 18 deletions
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; +} |