From: Axel Davy <axel.d...@ens.fr> Useful for PRIME setups.
Signed-off-by: Axel Davy <axel.d...@ens.fr> Signed-off-by: David Heidelberger <david.heidelber...@ixit.cz> --- include/d3dadapter/present.h | 7 +- src/gallium/state_trackers/nine/Makefile.am | 1 + src/gallium/state_trackers/nine/adapter9.h | 1 + src/gallium/state_trackers/nine/swapchain9.c | 111 +++++++++---- src/gallium/state_trackers/nine/swapchain9.h | 7 + src/gallium/state_trackers/nine/threadpool.c | 202 ++++++++++++++++++++++++ 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 + 9 files changed, 368 insertions(+), 37 deletions(-) create mode 100644 src/gallium/state_trackers/nine/threadpool.c create mode 100644 src/gallium/state_trackers/nine/threadpool.h diff --git a/include/d3dadapter/present.h b/include/d3dadapter/present.h index c302638..803513d 100644 --- a/include/d3dadapter/present.h +++ b/include/d3dadapter/present.h @@ -56,9 +56,7 @@ typedef struct ID3DPresentVtbl /* After presenting a buffer to the window system, the buffer * may be used as is (no copy of the content) by the window system. * You must not use a non-released buffer, else the user may see undefined content. */ - HRESULT (WINAPI *IsBufferReleased)(ID3DPresent *This, D3DWindowBuffer *buffer, BOOL *bReleased); - /* It is possible buffers are not released in order */ - HRESULT (WINAPI *WaitOneBufferReleased)(ID3DPresent *This); + HRESULT (WINAPI *WaitBufferReleased)(ID3DPresent *This, D3DWindowBuffer *buffer); HRESULT (WINAPI *FrontBufferCopy)(ID3DPresent *This, D3DWindowBuffer *buffer); /* It is possible to do partial copy, but impossible to do resizing, which must * be done by the client after checking the front buffer size */ @@ -88,8 +86,7 @@ struct ID3DPresent #define ID3DPresent_SetPresentParameters(p,a) (p)->lpVtbl->SetPresentParameters(p,a) #define ID3DPresent_NewD3DWindowBufferFromDmaBuf(p,a,b,c,d,e,f,g) (p)->lpVtbl->NewD3DWindowBufferFromDmaBuf(p,a,b,c,d,e,f,g) #define ID3DPresent_DestroyD3DWindowBuffer(p,a) (p)->lpVtbl->DestroyD3DWindowBuffer(p,a) -#define ID3DPresent_IsBufferReleased(p,a,b) (p)->lpVtbl->IsBufferReleased(p,a,b) -#define ID3DPresent_WaitOneBufferReleased(p) (p)->lpVtbl->WaitOneBufferReleased(p) +#define ID3DPresent_WaitBufferReleased(p,a) (p)->lpVtbl->WaitBufferReleased(p,a) #define ID3DPresent_FrontBufferCopy(p,a) (p)->lpVtbl->FrontBufferCopy(p,a) #define ID3DPresent_PresentBuffer(p,a,b,c,d,e,f) (p)->lpVtbl->PresentBuffer(p,a,b,c,d,e,f) #define ID3DPresent_GetRasterStatus(p,a) (p)->lpVtbl->GetRasterStatus(p,a) diff --git a/src/gallium/state_trackers/nine/Makefile.am b/src/gallium/state_trackers/nine/Makefile.am index c8d5bdb..6018ab6 100644 --- a/src/gallium/state_trackers/nine/Makefile.am +++ b/src/gallium/state_trackers/nine/Makefile.am @@ -37,6 +37,7 @@ libninetracker_la_SOURCES = \ swapchain9.c \ swapchain9ex.c \ texture9.c \ + threadpool.c \ vertexbuffer9.c \ vertexdeclaration9.c \ vertexshader9.c \ diff --git a/src/gallium/state_trackers/nine/adapter9.h b/src/gallium/state_trackers/nine/adapter9.h index c0fe14c..4522c99 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 vsync_force; + 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 3195cc6..ea95cef 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) @@ -69,6 +71,7 @@ NineSwapChain9_ctor( struct NineSwapChain9 *This, params.hDeviceWindow = hFocusWindow; This->rendering_done = FALSE; + This->pool = NULL; return NineSwapChain9_Resize(This, ¶ms); } @@ -217,6 +220,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; @@ -434,6 +452,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])); @@ -532,6 +553,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, @@ -634,21 +689,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; @@ -664,8 +724,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); @@ -699,6 +759,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: @@ -714,28 +779,12 @@ NineSwapChain9_Present( struct NineSwapChain9 *This, /* XXX not implemented */ break; } - /* Because d3d9 has a render ahead queue, - * we either copy right away the buffer on the screen, - * or we schedule flips with never more than 1 buffer - * per vblank. With standard X Present extension we should always - * have This->buffers[0] released here (it means the server has - * finished all work with it). However slightly different behaviour - * can occur in the future when Present will allow the compositor - * to manage what happens, or with XWayland. - * Then wait here This->buffers[0] is released. - * However in Copy mode we'll ignore if the buffer is released, - * since if the buffer is used for flip it might never be released. - * The Copy mode will have tearings, but that's expected - * Note: the Copy mode doesn't render to the buffer presented, - * it only copies to it. Thus we can't have glitches here. Only tearings.*/ - if (This->params.SwapEffect != D3DSWAPEFFECT_COPY) { - ID3DPresent_IsBufferReleased(This->present, This->present_handles[0], &released); - while (!released) { - UNTESTED(6); - ID3DPresent_WaitOneBufferReleased(This->present); - ID3DPresent_IsBufferReleased(This->present, This->present_handles[0], &released); - } - } + + 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; nine_update_state(This->base.device, NINE_STATE_FB); diff --git a/src/gallium/state_trackers/nine/swapchain9.h b/src/gallium/state_trackers/nine/swapchain9.h index 8d42462..b9fda62 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; @@ -67,7 +69,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..2979fc7 --- /dev/null +++ b/src/gallium/state_trackers/nine/threadpool.c @@ -0,0 +1,202 @@ +/* + * 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)); + struct sched_param sched_param; + pthread_attr_t attr; + int s; + + if (!pool) + return NULL; + + pthread_mutex_init(&pool->m, NULL); + pthread_cond_init(&pool->new_work, NULL); + + /* we use only one thread to allow the execution order predictable. + * We put maximum priority on it */ + s = pthread_attr_init(&attr); + if (!s) { + s |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + s |= pthread_attr_setschedpolicy(&attr, SCHED_RR); + sched_param.sched_priority = 99; + s |= pthread_attr_setschedparam(&attr, &sched_param); + if (s != 0) + pthread_attr_destroy(&attr); + } + if (s != 0) + pthread_create(&pool->thread, NULL, threadpool_worker, pool); + else { + pthread_create(&pool->thread, &attr, threadpool_worker, pool); + pthread_attr_destroy(&attr); + } + + 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 c1af702..fbd3b80 100644 --- a/src/gallium/targets/d3dadapter9/drm.c +++ b/src/gallium/targets/d3dadapter9/drm.c @@ -55,6 +55,7 @@ DRI_CONF_BEGIN DRI_CONF_SECTION_NINE DRI_CONF_NINE_VSYNC(0) DRI_CONF_NINE_THROTTLE(-2) + DRI_CONF_NINE_THREADSUBMIT("false") DRI_CONF_SECTION_END DRI_CONF_END; @@ -236,7 +237,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; const char *paths[] = { getenv("D3D9_DRIVERS_PATH"), @@ -304,6 +305,19 @@ drm_create_adapter( int fd, if (driCheckOption(&userInitOptions, "vsync_force", DRI_ENUM)) ctx->base.vsync_force = driQueryOptioni(&userInitOptions, "vsync_force"); + 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 e93341b..5bf7aab 100644 --- a/src/mesa/drivers/dri/common/xmlpool/t_options.h +++ b/src/mesa/drivers/dri/common/xmlpool/t_options.h @@ -362,3 +362,8 @@ DRI_CONF_OPT_END 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 additionnal thread to submit buffers.")) \ +DRI_CONF_OPT_END -- 2.1.2 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev