diff options
author | Axel Davy <[email protected]> | 2014-11-17 16:58:29 +0100 |
---|---|---|
committer | Emil Velikov <[email protected]> | 2014-11-18 02:02:54 +0000 |
commit | 7f565845a1f3ec871cccee88e2acf6be2e429797 (patch) | |
tree | e6aa710a658af3c40561d3a7bd495ef45a3f3b81 | |
parent | 948e6c522827b64cfd9b02eb3ab34e7d1f145017 (diff) |
nine: Implement threadpool
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.
Acked-by: Jose Fonseca <[email protected]>
Reviewed-by: David Heidelberg <[email protected]>
Signed-off-by: Axel Davy <[email protected]>
-rw-r--r-- | src/gallium/state_trackers/nine/Makefile.sources | 2 | ||||
-rw-r--r-- | src/gallium/state_trackers/nine/adapter9.h | 1 | ||||
-rw-r--r-- | src/gallium/state_trackers/nine/swapchain9.c | 86 | ||||
-rw-r--r-- | src/gallium/state_trackers/nine/swapchain9.h | 7 | ||||
-rw-r--r-- | src/gallium/state_trackers/nine/threadpool.c | 183 | ||||
-rw-r--r-- | src/gallium/state_trackers/nine/threadpool.h | 55 | ||||
-rw-r--r-- | src/gallium/targets/d3dadapter9/drm.c | 16 | ||||
-rw-r--r-- | src/mesa/drivers/dri/common/xmlpool/t_options.h | 5 |
8 files changed, 345 insertions, 10 deletions
diff --git a/src/gallium/state_trackers/nine/Makefile.sources b/src/gallium/state_trackers/nine/Makefile.sources index b8219613f6c..99b623a5b59 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 4db4f3cfd53..df85b2dcc28 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 ce92a3a8966..b6f5ef65782 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 566f78ab0ed..2afd6ab2954 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 00000000000..2a965374305 --- /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 00000000000..00ad25e1b13 --- /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 a58e16715c0..dd916f1ea73 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 e4f6937d455..4e5a7217ee2 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 |