Events can be added to an OpenCL command queue concurrently from multiple threads, but pipe_context and pipe_screen objects are not threadsafe. The threadsafe wrappers protect all pipe_screen and pipe_context function calls with a mutex, so we can safely use them with multiple threads. --- src/gallium/state_trackers/clover/Makefile.am | 6 +- src/gallium/state_trackers/clover/Makefile.sources | 4 + src/gallium/state_trackers/clover/core/device.cpp | 2 + .../clover/core/pipe_threadsafe_context.c | 272 +++++++++++++++++++++ .../clover/core/pipe_threadsafe_screen.c | 184 ++++++++++++++ .../state_trackers/clover/core/threadsafe.h | 39 +++ src/gallium/targets/opencl/Makefile.am | 3 +- 7 files changed, 508 insertions(+), 2 deletions(-) create mode 100644 src/gallium/state_trackers/clover/core/pipe_threadsafe_context.c create mode 100644 src/gallium/state_trackers/clover/core/pipe_threadsafe_screen.c create mode 100644 src/gallium/state_trackers/clover/core/threadsafe.h
diff --git a/src/gallium/state_trackers/clover/Makefile.am b/src/gallium/state_trackers/clover/Makefile.am index f46d9ef..8b615ae 100644 --- a/src/gallium/state_trackers/clover/Makefile.am +++ b/src/gallium/state_trackers/clover/Makefile.am @@ -1,5 +1,6 @@ AUTOMAKE_OPTIONS = subdir-objects +include $(top_srcdir)/src/gallium/Automake.inc include Makefile.sources AM_CPPFLAGS = \ @@ -32,6 +33,9 @@ cl_HEADERS = \ $(top_srcdir)/include/CL/opencl.h endif +AM_CFLAGS = \ + $(GALLIUM_CFLAGS) + noinst_LTLIBRARIES = libclover.la libcltgsi.la libclllvm.la libcltgsi_la_CXXFLAGS = \ @@ -58,6 +62,6 @@ libclover_la_CXXFLAGS = \ libclover_la_LIBADD = \ libcltgsi.la libclllvm.la -libclover_la_SOURCES = $(CPP_SOURCES) +libclover_la_SOURCES = $(CPP_SOURCES) $(C_SOURCES) EXTRA_DIST = Doxyfile diff --git a/src/gallium/state_trackers/clover/Makefile.sources b/src/gallium/state_trackers/clover/Makefile.sources index 10bbda0..90e6b7e 100644 --- a/src/gallium/state_trackers/clover/Makefile.sources +++ b/src/gallium/state_trackers/clover/Makefile.sources @@ -53,6 +53,10 @@ CPP_SOURCES := \ util/range.hpp \ util/tuple.hpp +C_SOURCES := \ + core/pipe_threadsafe_context.c \ + core/pipe_threadsafe_screen.c + LLVM_SOURCES := \ llvm/invocation.cpp diff --git a/src/gallium/state_trackers/clover/core/device.cpp b/src/gallium/state_trackers/clover/core/device.cpp index 42b45b7..b145027 100644 --- a/src/gallium/state_trackers/clover/core/device.cpp +++ b/src/gallium/state_trackers/clover/core/device.cpp @@ -22,6 +22,7 @@ #include "core/device.hpp" #include "core/platform.hpp" +#include "core/threadsafe.h" #include "pipe/p_screen.h" #include "pipe/p_state.h" @@ -47,6 +48,7 @@ device::device(clover::platform &platform, pipe_loader_device *ldev) : pipe->destroy(pipe); throw error(CL_INVALID_DEVICE); } + pipe = pipe_threadsafe_screen(pipe); } device::~device() { diff --git a/src/gallium/state_trackers/clover/core/pipe_threadsafe_context.c b/src/gallium/state_trackers/clover/core/pipe_threadsafe_context.c new file mode 100644 index 0000000..f08f56c --- /dev/null +++ b/src/gallium/state_trackers/clover/core/pipe_threadsafe_context.c @@ -0,0 +1,272 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * 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. + * + * Authors: Tom Stellard <thomas.stell...@amd.com> + * + */ + +#include <stdio.h> + +/** + * \file + * + * threadsafe_context is a wrapper around a pipe_context to make it thread + * safe. + */ + +#include "os/os_thread.h" +#include "pipe/p_context.h" +#include "util/u_memory.h" + +#include "threadsafe.h" + + + +struct threadsafe_context { + struct pipe_context base; + struct pipe_context *ctx; + pipe_mutex mutex; +}; + +static struct pipe_context *unwrap(struct pipe_context *ctx) { + if (!ctx) + return NULL; + struct threadsafe_context *ts_ctx = (struct threadsafe_context*)ctx; + return ts_ctx->ctx; +} + +#define THREADSAFE_CALL_VOID(ctx, function) \ + struct threadsafe_context *ts_ctx = (struct threadsafe_context*)(ctx); \ + pipe_mutex_lock(ts_ctx->mutex); \ + (unwrap(ctx)->function); \ + pipe_mutex_unlock(ts_ctx->mutex); + +#define THREADSAFE_CALL_RET(ctx, return_type, function) \ + struct threadsafe_context *ts_ctx = (struct threadsafe_context*)(ctx); \ + pipe_mutex_lock(ts_ctx->mutex); \ + return_type ret = (unwrap(ctx)->function); \ + pipe_mutex_unlock(ts_ctx->mutex); \ + return ret; + + +static void threadsafe_destroy(struct pipe_context *ctx) { + THREADSAFE_CALL_VOID(ctx, destroy(unwrap(ctx))); + FREE(ctx); +} + +static void *threadsafe_create_sampler_state(struct pipe_context *ctx, + const struct pipe_sampler_state *state) { + THREADSAFE_CALL_RET(ctx, void*, create_sampler_state(unwrap(ctx), state)); +} + +static struct pipe_query *threadsafe_create_query(struct pipe_context *ctx, + unsigned query_type, + unsigned index) { + THREADSAFE_CALL_RET(ctx, struct pipe_query*, + create_query(unwrap(ctx), query_type, index)); +} + +static void threadsafe_bind_compute_state(struct pipe_context *ctx, + void * state) { + THREADSAFE_CALL_VOID(ctx, bind_compute_state(unwrap(ctx), state)); +} + +static void threadsafe_bind_sampler_states(struct pipe_context *ctx, + unsigned shader, unsigned start_slot, + unsigned num_samplers, + void **samplers) { + THREADSAFE_CALL_VOID(ctx, + bind_sampler_states(unwrap(ctx), shader, start_slot, num_samplers, + samplers)); +} + +static void threadsafe_set_sampler_views(struct pipe_context *ctx, + unsigned shader, unsigned start_slot, + unsigned num_views, + struct pipe_sampler_view ** views) { + THREADSAFE_CALL_VOID(ctx, + set_sampler_views(unwrap(ctx), shader, start_slot, num_views, views)); +} + + +static void threadsafe_set_compute_resources(struct pipe_context *ctx, + unsigned start, unsigned count, + struct pipe_surface **resources) { + THREADSAFE_CALL_VOID(ctx, + set_compute_resources(unwrap(ctx), start, count, resources)); +} + +static void threadsafe_set_global_binding(struct pipe_context *ctx, + unsigned first, unsigned count, + struct pipe_resource **resources, + uint32_t **handles) { + THREADSAFE_CALL_VOID(ctx, + set_global_binding(unwrap(ctx), first, count, resources, handles)); +} + +static void threadsafe_launch_grid(struct pipe_context *ctx, + const uint *block_layout, + const uint *grid_layout, + uint32_t pc, const void *input) { + THREADSAFE_CALL_VOID(ctx, + launch_grid(unwrap(ctx), block_layout, grid_layout, pc, input)); +} + +static void threadsafe_delete_compute_state(struct pipe_context *ctx, + void *state) { + THREADSAFE_CALL_VOID(ctx, delete_compute_state(unwrap(ctx), state)); +} + +static void *threadsafe_create_compute_state(struct pipe_context *ctx, + const struct pipe_compute_state *state) { + THREADSAFE_CALL_RET(ctx, void*, create_compute_state(unwrap(ctx), state)); +} + +static void threadsafe_flush(struct pipe_context *ctx, + struct pipe_fence_handle **fence, + unsigned flags) { + THREADSAFE_CALL_VOID(ctx, flush(unwrap(ctx), fence, flags)); +} + +static struct pipe_sampler_view *threadsafe_create_sampler_view( + struct pipe_context *ctx, + struct pipe_resource *texture, + const struct pipe_sampler_view *templat) { + THREADSAFE_CALL_RET(ctx, struct pipe_sampler_view*, + create_sampler_view(unwrap(ctx), texture, templat)); +} + +static void threadsafe_resource_copy_region(struct pipe_context *ctx, + struct pipe_resource *dst, + unsigned dst_level, + unsigned dstx, unsigned dsty, unsigned dstz, + struct pipe_resource *src, + unsigned src_level, + const struct pipe_box *src_box) { + THREADSAFE_CALL_VOID(ctx, + resource_copy_region(unwrap(ctx), dst, dst_level, dstx, dsty, dstz, src, + src_level, src_box)); +} + +static void threadsafe_sampler_view_destroy(struct pipe_context *ctx, + struct pipe_sampler_view *view) { + THREADSAFE_CALL_VOID(ctx, sampler_view_destroy(unwrap(ctx), view)); +} + +static struct pipe_surface *threadsafe_create_surface(struct pipe_context *ctx, + struct pipe_resource *resource, + const struct pipe_surface *templat) { + THREADSAFE_CALL_RET(ctx, struct pipe_surface*, + create_surface(unwrap(ctx), resource, templat)); +} + + +static void threadsafe_surface_destroy(struct pipe_context *ctx, + struct pipe_surface *surface) { + THREADSAFE_CALL_VOID(ctx, surface_destroy(unwrap(ctx), surface)); +} + +static void threadsafe_transfer_inline_write(struct pipe_context *ctx, + struct pipe_resource *resource, + unsigned level, unsigned usage, + const struct pipe_box *pipe_box, + const void *data, + unsigned stride, + unsigned layer_stride) { + THREADSAFE_CALL_VOID(ctx, + transfer_inline_write(unwrap(ctx), resource, level, usage, pipe_box, data, + stride, layer_stride)); +} + +static void *threadsafe_transfer_map(struct pipe_context *ctx, + struct pipe_resource *resource, + unsigned level, + unsigned usage, + const struct pipe_box *box, + struct pipe_transfer **out_transfer) { + THREADSAFE_CALL_RET(ctx, void*, + transfer_map(unwrap(ctx), resource, level, usage, box, out_transfer)); +} + +static void threadsafe_transfer_unmap(struct pipe_context *ctx, + struct pipe_transfer *transfer) { + THREADSAFE_CALL_VOID(ctx, transfer_unmap(unwrap(ctx), transfer)); +} + +static void threadsafe_delete_sampler_state(struct pipe_context *ctx, + void *state) { + THREADSAFE_CALL_VOID(ctx, delete_sampler_state(unwrap(ctx), state)); +} + +static void threadsafe_end_query(struct pipe_context *ctx, + struct pipe_query *q) { + THREADSAFE_CALL_VOID(ctx, end_query(unwrap(ctx), q)); +} + +static void threadsafe_destroy_query(struct pipe_context *ctx, + struct pipe_query *q) { + THREADSAFE_CALL_VOID(ctx, destroy_query(unwrap(ctx), q)); +} + +static boolean threadsafe_get_query_result(struct pipe_context *ctx, + struct pipe_query *q, + boolean wait, + union pipe_query_result *result) { + THREADSAFE_CALL_RET(ctx, boolean, + get_query_result(unwrap(ctx), q, wait, result)); +} + +#define INIT_FUNCTION(name) \ + ts_ctx->base.name = threadsafe_ ## name; + +struct pipe_context *pipe_threadsafe_context(struct pipe_context *ctx) { + struct threadsafe_context *ts_ctx = CALLOC_STRUCT(threadsafe_context); + + ts_ctx->ctx = ctx; + pipe_mutex_init(ts_ctx->mutex); + + INIT_FUNCTION(destroy); + INIT_FUNCTION(create_query); + INIT_FUNCTION(bind_compute_state); + INIT_FUNCTION(bind_sampler_states); + INIT_FUNCTION(set_sampler_views); + INIT_FUNCTION(set_compute_resources); + INIT_FUNCTION(set_global_binding); + INIT_FUNCTION(launch_grid); + INIT_FUNCTION(delete_compute_state); + INIT_FUNCTION(create_compute_state); + INIT_FUNCTION(flush); + INIT_FUNCTION(create_sampler_view); + INIT_FUNCTION(resource_copy_region); + INIT_FUNCTION(sampler_view_destroy); + INIT_FUNCTION(create_surface); + INIT_FUNCTION(surface_destroy); + INIT_FUNCTION(transfer_inline_write); + INIT_FUNCTION(transfer_map); + INIT_FUNCTION(transfer_unmap); + INIT_FUNCTION(create_sampler_state); + INIT_FUNCTION(delete_sampler_state); + INIT_FUNCTION(end_query); + INIT_FUNCTION(destroy_query); + INIT_FUNCTION(get_query_result); + return &ts_ctx->base; +} diff --git a/src/gallium/state_trackers/clover/core/pipe_threadsafe_screen.c b/src/gallium/state_trackers/clover/core/pipe_threadsafe_screen.c new file mode 100644 index 0000000..cc2f842 --- /dev/null +++ b/src/gallium/state_trackers/clover/core/pipe_threadsafe_screen.c @@ -0,0 +1,184 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * 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. + * + * Authors: Tom Stellard <thomas.stell...@amd.com> + * + */ + +#include <stdio.h> + + +/** + * \file + * + * threadsafe_screen is a wrapper around a pipe_screen to make it thread + * safe. Some functions are already theadsafe and don't require the mutex + * guard. + */ + +#include "os/os_thread.h" +#include "pipe/p_context.h" +#include "pipe/p_screen.h" +#include "util/u_memory.h" + +#include "threadsafe.h" + +struct threadsafe_screen { + struct pipe_screen base; + struct pipe_screen *screen; + pipe_mutex mutex; +}; + +static struct pipe_screen *unwrap(struct pipe_screen *screen) { + if (!screen) + return NULL; + struct threadsafe_screen *ts_screen = (struct threadsafe_screen*)screen; + return ts_screen->screen; +} + +#define THREADSAFE_CALL_VOID(screen, function) \ + struct threadsafe_screen *ts_screen = (struct threadsafe_screen*)(screen); \ + pipe_mutex_lock(ts_screen->mutex); \ + (unwrap(screen)->function); \ + pipe_mutex_unlock(ts_screen->mutex); + +#define THREADSAFE_CALL_RET(screen, return_type, function) \ + struct threadsafe_screen *ts_screen = (struct threadsafe_screen*)(screen); \ + pipe_mutex_lock(ts_screen->mutex); \ + return_type ret = (unwrap(screen)->function); \ + pipe_mutex_unlock(ts_screen->mutex); \ + return ret; + +static struct pipe_context *threadsafe_context_create( + struct pipe_screen *screen, + void *priv) { + struct threadsafe_screen *ts_screen = (struct threadsafe_screen*)(screen); + struct pipe_context *ctx; + + pipe_mutex_lock(ts_screen->mutex); + ctx = unwrap(screen)->context_create(unwrap(screen), priv); + ctx = pipe_threadsafe_context(ctx); + ctx->screen = screen; + pipe_mutex_unlock(ts_screen->mutex); + return ctx; +} + +static void threadsafe_destroy(struct pipe_screen *screen) { + THREADSAFE_CALL_VOID(screen, destroy(unwrap(screen))); + FREE(screen); +} + +static boolean threadsafe_is_format_supported(struct pipe_screen *screen, + enum pipe_format format, + enum pipe_texture_target target, + unsigned sample_count, + unsigned bindings) { + return unwrap(screen)->is_format_supported(unwrap(screen), format, target, + sample_count, bindings); +} + + +static struct pipe_resource *threadsafe_resource_create( + struct pipe_screen *screen, + const struct pipe_resource *templat) { + THREADSAFE_CALL_RET(screen, struct pipe_resource*, + resource_create(unwrap(screen), templat)); +} + +static void threadsafe_resource_destroy(struct pipe_screen *screen, + struct pipe_resource *pt) { + THREADSAFE_CALL_VOID(screen, resource_destroy(unwrap(screen), pt)); +} + +static int threadsafe_get_compute_param(struct pipe_screen *screen, + enum pipe_compute_cap param, + void *ret) { + return unwrap(screen)->get_compute_param(unwrap(screen), param, ret); +} + +static int threadsafe_get_param(struct pipe_screen *screen, + enum pipe_cap param) { + return unwrap(screen)->get_param(unwrap(screen), param); +} + +static const char* threadsafe_get_name(struct pipe_screen *screen) { + return unwrap(screen)->get_name(unwrap(screen)); +} + +static const char* threadsafe_get_device_vendor(struct pipe_screen *screen) { + return unwrap(screen)->get_device_vendor(unwrap(screen)); +} + +static int threadsafe_get_shader_param(struct pipe_screen *screen, + unsigned shader, + enum pipe_shader_cap param) { + return unwrap(screen)->get_shader_param(unwrap(screen), shader, param); +} + +static void threadsafe_fence_reference(struct pipe_screen *screen, + struct pipe_fence_handle **ptr, + struct pipe_fence_handle *fence) { + THREADSAFE_CALL_VOID(screen, fence_reference(unwrap(screen), ptr, fence)); +} + +static uint64_t threadsafe_get_timestamp(struct pipe_screen *screen) { + THREADSAFE_CALL_RET(screen, uint64_t, get_timestamp(unwrap(screen))); +} + +static boolean threadsafe_fence_signalled(struct pipe_screen *screen, + struct pipe_fence_handle *fence) { + THREADSAFE_CALL_RET(screen, boolean, fence_signalled(unwrap(screen), fence)); +} + +static boolean threadsafe_fence_finish(struct pipe_screen *screen, + struct pipe_fence_handle *fence, + uint64_t timeout) { + THREADSAFE_CALL_RET(screen, boolean, + fence_finish(unwrap(screen), fence, timeout)); +} + +#define INIT_FUNCTION(name) \ + ts_screen->base.name = threadsafe_ ## name; + +struct pipe_screen *pipe_threadsafe_screen(struct pipe_screen *screen) { + struct threadsafe_screen *ts_screen = CALLOC_STRUCT(threadsafe_screen); + + ts_screen->screen = screen; + pipe_mutex_init(ts_screen->mutex); + + INIT_FUNCTION(context_create); + INIT_FUNCTION(destroy); + INIT_FUNCTION(is_format_supported); + INIT_FUNCTION(resource_create); + INIT_FUNCTION(resource_destroy); + INIT_FUNCTION(get_compute_param); + INIT_FUNCTION(get_param); + INIT_FUNCTION(get_name); + INIT_FUNCTION(get_device_vendor); + INIT_FUNCTION(get_shader_param); + INIT_FUNCTION(fence_reference); + INIT_FUNCTION(get_timestamp); + INIT_FUNCTION(fence_signalled); + INIT_FUNCTION(fence_finish); + + return &ts_screen->base; +} diff --git a/src/gallium/state_trackers/clover/core/threadsafe.h b/src/gallium/state_trackers/clover/core/threadsafe.h new file mode 100644 index 0000000..f49ff8c --- /dev/null +++ b/src/gallium/state_trackers/clover/core/threadsafe.h @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Advanced Micro Devices, Inc. + * + * 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. + * + * Authors: Tom Stellard <thomas.stell...@amd.com> + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct pipe_context; +struct pipe_screen; + +struct pipe_context *pipe_threadsafe_context(struct pipe_context *ctx); +struct pipe_screen *pipe_threadsafe_screen(struct pipe_screen *screen); + +#ifdef __cplusplus +} +#endif diff --git a/src/gallium/targets/opencl/Makefile.am b/src/gallium/targets/opencl/Makefile.am index 5daf327..268370f 100644 --- a/src/gallium/targets/opencl/Makefile.am +++ b/src/gallium/targets/opencl/Makefile.am @@ -38,7 +38,8 @@ lib@OPENCL_LIBNAME@_la_LIBADD = \ -lclangEdit \ -lclangLex \ -lclangBasic \ - $(LLVM_LIBS) + $(LLVM_LIBS) \ + $(PTHREAD_LIBS) nodist_EXTRA_lib@OPENCL_LIBNAME@_la_SOURCES = dummy.cpp lib@OPENCL_LIBNAME@_la_SOURCES = -- 2.0.4 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev