[Mesa-dev] [PATCH v4 8/8] nine: Implement threadpool

Axel Davy axel.davy at ens.fr
Mon Nov 17 07:58:29 PST 2014


DRI_PRIME setups have different issues due the lack of dma-buf fences
support in the drivers. For DRI3 DRI_PRIME, a race can appear, making
tearings visible, or worse showing older content than expected. Until
dma-buf fences are well supported (and by all drivers), an alternative
is to send the buffers to the server only when rendering has finished.
Since waiting the rendering has finished in the main thread has a
performance impact, this patch uses an additional thread to offload the
wait and the sending of the buffers to the server.

Reviewed-by: David Heidelberg <david at ixit.cz>
Signed-off-by: Axel Davy <axel.davy at ens.fr>
---
 src/gallium/state_trackers/nine/Makefile.sources |   2 +
 src/gallium/state_trackers/nine/adapter9.h       |   1 +
 src/gallium/state_trackers/nine/swapchain9.c     |  86 +++++++++--
 src/gallium/state_trackers/nine/swapchain9.h     |   7 +
 src/gallium/state_trackers/nine/threadpool.c     | 183 +++++++++++++++++++++++
 src/gallium/state_trackers/nine/threadpool.h     |  55 +++++++
 src/gallium/targets/d3dadapter9/drm.c            |  16 +-
 src/mesa/drivers/dri/common/xmlpool/t_options.h  |   5 +
 8 files changed, 345 insertions(+), 10 deletions(-)
 create mode 100644 src/gallium/state_trackers/nine/threadpool.c
 create mode 100644 src/gallium/state_trackers/nine/threadpool.h

diff --git a/src/gallium/state_trackers/nine/Makefile.sources b/src/gallium/state_trackers/nine/Makefile.sources
index b821961..99b623a 100644
--- a/src/gallium/state_trackers/nine/Makefile.sources
+++ b/src/gallium/state_trackers/nine/Makefile.sources
@@ -59,6 +59,8 @@ C_SOURCES := \
 	swapchain9.h \
 	texture9.c \
 	texture9.h \
+	threadpool.c \
+	threadpool.h \
 	vertexbuffer9.c \
 	vertexbuffer9.h \
 	vertexdeclaration9.c \
diff --git a/src/gallium/state_trackers/nine/adapter9.h b/src/gallium/state_trackers/nine/adapter9.h
index 4db4f3c..df85b2d 100644
--- a/src/gallium/state_trackers/nine/adapter9.h
+++ b/src/gallium/state_trackers/nine/adapter9.h
@@ -38,6 +38,7 @@ struct d3dadapter9_context
     BOOL throttling;
     int throttling_value;
     int vblank_mode;
+    BOOL thread_submit;
 
     void (*destroy)( struct d3dadapter9_context *ctx );
 };
diff --git a/src/gallium/state_trackers/nine/swapchain9.c b/src/gallium/state_trackers/nine/swapchain9.c
index ce92a3a8..b6f5ef6 100644
--- a/src/gallium/state_trackers/nine/swapchain9.c
+++ b/src/gallium/state_trackers/nine/swapchain9.c
@@ -33,6 +33,8 @@
 #include "hud/hud_context.h"
 #include "state_tracker/drm_driver.h"
 
+#include "threadpool.h"
+
 #define DBG_CHANNEL DBG_SWAPCHAIN
 
 #define UNTESTED(n) DBG("UNTESTED point %d. Please tell if it worked\n", n)
@@ -70,6 +72,7 @@ NineSwapChain9_ctor( struct NineSwapChain9 *This,
         pPresentationParameters->hDeviceWindow = hFocusWindow;
 
     This->rendering_done = FALSE;
+    This->pool = NULL;
     return NineSwapChain9_Resize(This, pPresentationParameters, mode);
 }
 
@@ -227,6 +230,21 @@ NineSwapChain9_Resize( struct NineSwapChain9 *This,
     desc.Width = pParams->BackBufferWidth;
     desc.Height = pParams->BackBufferHeight;
 
+    if (This->pool) {
+        _mesa_threadpool_destroy(This->pool);
+        This->pool = NULL;
+    }
+    This->enable_threadpool = This->actx->thread_submit && (pParams->SwapEffect != D3DSWAPEFFECT_COPY);
+    if (This->enable_threadpool)
+        This->pool = _mesa_threadpool_create();
+    if (!This->pool)
+        This->enable_threadpool = FALSE;
+
+    This->tasks = REALLOC(This->tasks,
+                          oldBufferCount * sizeof(struct threadpool_task *),
+                          newBufferCount * sizeof(struct threadpool_task *));
+    memset(This->tasks, 0, newBufferCount * sizeof(struct threadpool_task *));
+
     for (i = 0; i < oldBufferCount; i++) {
         ID3DPresent_DestroyD3DWindowBuffer(This->present, This->present_handles[i]);
         This->present_handles[i] = NULL;
@@ -444,6 +462,9 @@ NineSwapChain9_dtor( struct NineSwapChain9 *This )
 
     DBG("This=%p\n", This);
 
+    if (This->pool)
+        _mesa_threadpool_destroy(This->pool);
+
     if (This->buffers) {
         for (i = 0; i < This->params.BackBufferCount; i++) {
             NineUnknown_Destroy(NineUnknown(This->buffers[i]));
@@ -541,6 +562,40 @@ handle_draw_cursor_and_hud( struct NineSwapChain9 *This, struct pipe_resource *r
     }
 }
 
+struct end_present_struct {
+    struct pipe_screen *screen;
+    struct pipe_fence_handle *fence_to_wait;
+    ID3DPresent *present;
+    D3DWindowBuffer *present_handle;
+    HWND hDestWindowOverride;
+};
+
+static void work_present(void *data)
+{
+    struct end_present_struct *work = data;
+    if (work->fence_to_wait) {
+        (void) work->screen->fence_finish(work->screen, work->fence_to_wait, PIPE_TIMEOUT_INFINITE);
+        work->screen->fence_reference(work->screen, &(work->fence_to_wait), NULL);
+    }
+    ID3DPresent_PresentBuffer(work->present, work->present_handle, work->hDestWindowOverride, NULL, NULL, NULL, 0);
+    free(work);
+}
+
+static void pend_present(struct NineSwapChain9 *This,
+                         HWND hDestWindowOverride)
+{
+    struct end_present_struct *work = calloc(1, sizeof(struct end_present_struct));
+
+    work->screen = This->screen;
+    work->fence_to_wait = swap_fences_pop_front(This);
+    work->present = This->present;
+    work->present_handle = This->present_handles[0];
+    work->hDestWindowOverride = hDestWindowOverride;
+    This->tasks[0] = _mesa_threadpool_queue_task(This->pool, work_present, work);
+
+    return;
+}
+
 static INLINE HRESULT
 present( struct NineSwapChain9 *This,
          const RECT *pSourceRect,
@@ -642,21 +697,26 @@ bypass_rendering:
             return D3DERR_WASSTILLDRAWING;
     }
 
-    fence = swap_fences_pop_front(This);
-    if (fence) {
-        (void) This->screen->fence_finish(This->screen, fence, PIPE_TIMEOUT_INFINITE);
-        This->screen->fence_reference(This->screen, &fence, NULL);
-    }
-
     if (This->present_buffers)
         resource = This->present_buffers[0];
     else
         resource = This->buffers[0]->base.resource;
     This->pipe->flush_resource(This->pipe, resource);
-    hr = ID3DPresent_PresentBuffer(This->present, This->present_handles[0], hDestWindowOverride, pSourceRect, pDestRect, pDirtyRegion, dwFlags);
 
-    if (FAILED(hr)) { UNTESTED(3);return hr; }
+    if (!This->enable_threadpool) {
+        This->tasks[0]=NULL;
+        fence = swap_fences_pop_front(This);
+        if (fence) {
+            (void) This->screen->fence_finish(This->screen, fence, PIPE_TIMEOUT_INFINITE);
+            This->screen->fence_reference(This->screen, &fence, NULL);
+        }
+
+        hr = ID3DPresent_PresentBuffer(This->present, This->present_handles[0], hDestWindowOverride, pSourceRect, pDestRect, pDirtyRegion, dwFlags);
 
+        if (FAILED(hr)) { UNTESTED(3);return hr; }
+    } else {
+        pend_present(This, hDestWindowOverride);
+    }
     This->rendering_done = FALSE;
 
     return D3D_OK;
@@ -672,8 +732,8 @@ NineSwapChain9_Present( struct NineSwapChain9 *This,
 {
     struct pipe_resource *res = NULL;
     D3DWindowBuffer *handle_temp;
+    struct threadpool_task *task_temp;
     int i;
-    BOOL released;
     HRESULT hr = present(This, pSourceRect, pDestRect,
                          hDestWindowOverride, pDirtyRegion, dwFlags);
 
@@ -707,6 +767,11 @@ NineSwapChain9_Present( struct NineSwapChain9 *This,
                 This->present_handles[i-1] = This->present_handles[i];
             }
             This->present_handles[This->params.BackBufferCount] = handle_temp;
+            task_temp = This->tasks[0];
+            for (i = 1; i <= This->params.BackBufferCount; i++) {
+                This->tasks[i-1] = This->tasks[i];
+            }
+            This->tasks[This->params.BackBufferCount] = task_temp;
             break;
 
         case D3DSWAPEFFECT_COPY:
@@ -723,6 +788,9 @@ NineSwapChain9_Present( struct NineSwapChain9 *This,
             break;
     }
 
+    if (This->tasks[0])
+        _mesa_threadpool_wait_for_task(This->pool, &(This->tasks[0]));
+
     ID3DPresent_WaitBufferReleased(This->present, This->present_handles[0]);
 
     This->base.device->state.changed.group |= NINE_STATE_FB;
diff --git a/src/gallium/state_trackers/nine/swapchain9.h b/src/gallium/state_trackers/nine/swapchain9.h
index 566f78a..2afd6ab 100644
--- a/src/gallium/state_trackers/nine/swapchain9.h
+++ b/src/gallium/state_trackers/nine/swapchain9.h
@@ -28,6 +28,8 @@
 
 #include "d3dadapter/d3dadapter9.h"
 
+#include "threadpool.h"
+
 struct NineDevice9;
 struct NineSurface9;
 struct nine_winsys_swapchain;
@@ -68,7 +70,12 @@ struct NineSwapChain9
     struct NineSurface9 *zsbuf;
 
     D3DGAMMARAMP gamma;
+
+    struct threadpool *pool;
+    struct threadpool_task **tasks;
+    BOOL enable_threadpool;
 };
+
 static INLINE struct NineSwapChain9 *
 NineSwapChain9( void *data )
 {
diff --git a/src/gallium/state_trackers/nine/threadpool.c b/src/gallium/state_trackers/nine/threadpool.c
new file mode 100644
index 0000000..2a96537
--- /dev/null
+++ b/src/gallium/state_trackers/nine/threadpool.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "swapchain9.h"
+#include "surface9.h"
+#include "device9.h"
+
+#include "nine_helpers.h"
+#include "nine_pipe.h"
+#include "nine_dump.h"
+
+#include "util/u_inlines.h"
+#include "util/u_surface.h"
+#include "hud/hud_context.h"
+#include "state_tracker/drm_driver.h"
+
+#include "os/os_thread.h"
+#include "threadpool.h"
+
+static void *
+threadpool_worker(void *data)
+{
+    struct threadpool *pool = data;
+
+    pthread_mutex_lock(&pool->m);
+
+    while (!pool->shutdown) {
+        struct threadpool_task *task;
+
+        /* Block (dropping the lock) until new work arrives for us. */
+        while (!pool->workqueue && !pool->shutdown)
+            pthread_cond_wait(&pool->new_work, &pool->m);
+
+        if (pool->shutdown) {
+            pthread_mutex_unlock(&pool->m);
+            return NULL;
+        }
+
+        /* Pull the first task from the list.  We don't free it -- it now lacks
+         * a reference other than the worker creator's, whose responsibility it
+         * is to call threadpool_wait_for_work() to free it.
+         */
+        task = pool->workqueue;
+        pool->workqueue = task->next;
+
+        /* Call the task's work func. */
+        pthread_mutex_unlock(&pool->m);
+        task->work(task->data);
+        pthread_mutex_lock(&pool->m);
+        task->finished = TRUE;
+        pthread_cond_broadcast(&task->finish);
+    }
+
+    pthread_mutex_unlock(&pool->m);
+
+    return NULL;
+}
+
+struct threadpool *
+_mesa_threadpool_create(void)
+{
+    struct threadpool *pool = calloc(1, sizeof(*pool));
+
+    if (!pool)
+        return NULL;
+
+    pthread_mutex_init(&pool->m, NULL);
+    pthread_cond_init(&pool->new_work, NULL);
+
+    pthread_create(&pool->thread, NULL, threadpool_worker, pool);
+
+    return pool;
+}
+
+void
+_mesa_threadpool_destroy(struct threadpool *pool)
+{
+    if (!pool)
+        return;
+
+    pthread_mutex_lock(&pool->m);
+    pool->shutdown = TRUE;
+    pthread_cond_broadcast(&pool->new_work);
+    pthread_mutex_unlock(&pool->m);
+
+    pthread_join(pool->thread, NULL);
+
+    pthread_cond_destroy(&pool->new_work);
+    pthread_mutex_destroy(&pool->m);
+    free(pool);
+}
+
+/**
+ * Queues a request for the work function to be asynchronously executed by the
+ * thread pool.
+ *
+ * The work func will get the "data" argument as its parameter -- any
+ * communication between the caller and the work function will occur through
+ * that.
+ *
+ * If there is an error, the work function is called immediately and NULL is
+ * returned.
+ */
+struct threadpool_task *
+_mesa_threadpool_queue_task(struct threadpool *pool,
+                            threadpool_task_func work, void *data)
+{
+    struct threadpool_task *task, *previous;
+
+    if (!pool) {
+        work(data);
+        return NULL;
+    }
+
+    task = calloc(1, sizeof(*task));
+    if (!task) {
+        work(data);
+        return NULL;
+    }
+
+    task->work = work;
+    task->data = data;
+    task->next = NULL;
+    pthread_cond_init(&task->finish, NULL);
+
+    pthread_mutex_lock(&pool->m);
+
+    if (!pool->workqueue) {
+        pool->workqueue = task;
+    } else {
+        previous = pool->workqueue;
+        while (previous && previous->next)
+            previous = previous->next;
+
+        previous->next = task;
+    }
+    pthread_cond_signal(&pool->new_work);
+    pthread_mutex_unlock(&pool->m);
+
+    return task;
+}
+
+/**
+ * Blocks on the completion of the given task and frees the task.
+ */
+void
+_mesa_threadpool_wait_for_task(struct threadpool *pool,
+                               struct threadpool_task **task_handle)
+{
+    struct threadpool_task *task = *task_handle;
+
+    if (!pool || !task)
+        return;
+
+    pthread_mutex_lock(&pool->m);
+    while (!task->finished)
+        pthread_cond_wait(&task->finish, &pool->m);
+    pthread_mutex_unlock(&pool->m);
+
+    pthread_cond_destroy(&task->finish);
+    free(task);
+    *task_handle = NULL;
+}
diff --git a/src/gallium/state_trackers/nine/threadpool.h b/src/gallium/state_trackers/nine/threadpool.h
new file mode 100644
index 0000000..00ad25e
--- /dev/null
+++ b/src/gallium/state_trackers/nine/threadpool.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2012 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _THREADPOOL_H_
+#define _THREADPOOL_H_
+
+#define MAXTHREADS 1
+
+struct threadpool {
+    pthread_mutex_t m;
+    pthread_cond_t new_work;
+
+    pthread_t thread;
+    struct threadpool_task *workqueue;
+    BOOL shutdown;
+};
+
+typedef void (*threadpool_task_func)(void *data);
+
+struct threadpool_task {
+    threadpool_task_func work;
+    void *data;
+    struct threadpool_task *next;
+    pthread_cond_t finish;
+    BOOL finished;
+};
+
+struct threadpool *_mesa_threadpool_create(void);
+void _mesa_threadpool_destroy(struct threadpool *pool);
+struct threadpool_task *_mesa_threadpool_queue_task(struct threadpool *pool,
+                                                    threadpool_task_func func,
+                                                    void *data);
+void _mesa_threadpool_wait_for_task(struct threadpool *pool,
+                                    struct threadpool_task **task);
+#endif
\ No newline at end of file
diff --git a/src/gallium/targets/d3dadapter9/drm.c b/src/gallium/targets/d3dadapter9/drm.c
index a58e167..dd916f1 100644
--- a/src/gallium/targets/d3dadapter9/drm.c
+++ b/src/gallium/targets/d3dadapter9/drm.c
@@ -59,6 +59,7 @@ DRI_CONF_BEGIN
     DRI_CONF_SECTION_END
     DRI_CONF_SECTION_NINE
         DRI_CONF_NINE_THROTTLE(-2)
+        DRI_CONF_NINE_THREADSUBMIT("false")
     DRI_CONF_SECTION_END
 DRI_CONF_END;
 
@@ -244,7 +245,7 @@ drm_create_adapter( int fd,
     const struct drm_conf_ret *dmabuf_ret = NULL;
     driOptionCache defaultInitOptions;
     driOptionCache userInitOptions;
-    int throttling_value_user;
+    int throttling_value_user = -2;
 
 #if !GALLIUM_STATIC_TARGETS
     const char *paths[] = {
@@ -322,6 +323,19 @@ drm_create_adapter( int fd,
     else
         ctx->base.vblank_mode = 1;
 
+    if (driCheckOption(&userInitOptions, "thread_submit", DRI_BOOL)) {
+        ctx->base.thread_submit = driQueryOptionb(&userInitOptions, "thread_submit");
+        if (ctx->base.thread_submit && (throttling_value_user == -2 || throttling_value_user == 0)) {
+            ctx->base.throttling_value = 0;
+        } else if (ctx->base.thread_submit) {
+            DBG("You have set a non standard throttling value in combination with thread_submit."
+                "We advise to use a throttling value of -2/0");
+        }
+        if (ctx->base.thread_submit && !different_device)
+            DBG("You have set thread_submit but do not use a different device than the server."
+                "You should not expect any benefit.");
+    }
+
     driDestroyOptionCache(&userInitOptions);
     driDestroyOptionInfo(&defaultInitOptions);
 
diff --git a/src/mesa/drivers/dri/common/xmlpool/t_options.h b/src/mesa/drivers/dri/common/xmlpool/t_options.h
index e4f6937..4e5a721 100644
--- a/src/mesa/drivers/dri/common/xmlpool/t_options.h
+++ b/src/mesa/drivers/dri/common/xmlpool/t_options.h
@@ -353,3 +353,8 @@ DRI_CONF_SECTION_BEGIN \
 DRI_CONF_OPT_BEGIN(throttle_value, int, def) \
         DRI_CONF_DESC(en,gettext("Define the throttling value. -1 for no throttling, -2 for default (usually 2), 0 for glfinish behaviour")) \
 DRI_CONF_OPT_END
+
+#define DRI_CONF_NINE_THREADSUBMIT(def) \
+DRI_CONF_OPT_BEGIN_B(thread_submit, def) \
+        DRI_CONF_DESC(en,gettext("Use an additional thread to submit buffers.")) \
+DRI_CONF_OPT_END
-- 
2.1.0



More information about the mesa-dev mailing list