From: Nicolai Hähnle <nicolai.haeh...@amd.com> Locking is unfortunately required, because well-formed GL programs can have multiple threads racing to access the same texture, e.g.: two threads/contexts rendering from the same texture, or one thread destroying a context while the other is rendering from or modifying a texture. --- src/mesa/state_tracker/st_atom_sampler.c | 12 ++----- src/mesa/state_tracker/st_cb_texture.c | 2 ++ src/mesa/state_tracker/st_sampler_view.c | 58 +++++++++++++++++++++++++++++--- src/mesa/state_tracker/st_sampler_view.h | 3 ++ src/mesa/state_tracker/st_texture.h | 3 ++ 5 files changed, 64 insertions(+), 14 deletions(-)
diff --git a/src/mesa/state_tracker/st_atom_sampler.c b/src/mesa/state_tracker/st_atom_sampler.c index d9e8de3c9e0..34a0c18b0a5 100644 --- a/src/mesa/state_tracker/st_atom_sampler.c +++ b/src/mesa/state_tracker/st_atom_sampler.c @@ -36,20 +36,21 @@ #include "main/mtypes.h" #include "main/glformats.h" #include "main/samplerobj.h" #include "main/teximage.h" #include "main/texobj.h" #include "st_context.h" #include "st_cb_texture.h" #include "st_format.h" #include "st_atom.h" +#include "st_sampler_view.h" #include "st_texture.h" #include "pipe/p_context.h" #include "pipe/p_defines.h" #include "cso_cache/cso_context.h" #include "util/u_format.h" /** @@ -157,31 +158,22 @@ st_convert_sampler(const struct st_context *st, (sampler->wrap_s | sampler->wrap_t | sampler->wrap_r) & 0x1 && (msamp->BorderColor.ui[0] || msamp->BorderColor.ui[1] || msamp->BorderColor.ui[2] || msamp->BorderColor.ui[3])) { const GLboolean is_integer = texobj->_IsIntegerFormat; GLenum texBaseFormat = _mesa_base_tex_image(texobj)->_BaseFormat; if (st->apply_texture_swizzle_to_border_color) { const struct st_texture_object *stobj = st_texture_object_const(texobj); - const struct pipe_sampler_view *sv = NULL; - - /* Just search for the first used view. We can do this because the - swizzle is per-texture, not per context. */ /* XXX: clean that up to not use the sampler view at all */ - for (unsigned i = 0; i < stobj->num_sampler_views; ++i) { - if (stobj->sampler_views[i]) { - sv = stobj->sampler_views[i]; - break; - } - } + const struct pipe_sampler_view *sv = st_texture_get_current_sampler_view(st, stobj); if (sv) { union pipe_color_union tmp; const unsigned char swz[4] = { sv->swizzle_r, sv->swizzle_g, sv->swizzle_b, sv->swizzle_a, }; diff --git a/src/mesa/state_tracker/st_cb_texture.c b/src/mesa/state_tracker/st_cb_texture.c index b5006b05a7b..bac2a0289f5 100644 --- a/src/mesa/state_tracker/st_cb_texture.c +++ b/src/mesa/state_tracker/st_cb_texture.c @@ -148,36 +148,38 @@ st_DeleteTextureImage(struct gl_context * ctx, struct gl_texture_image *img) /** called via ctx->Driver.NewTextureObject() */ static struct gl_texture_object * st_NewTextureObject(struct gl_context * ctx, GLuint name, GLenum target) { struct st_texture_object *obj = ST_CALLOC_STRUCT(st_texture_object); DBG("%s\n", __func__); _mesa_initialize_texture_object(ctx, &obj->base, name, target); + mtx_init(&obj->validate_mutex, mtx_plain); obj->needs_validation = true; return &obj->base; } /** called via ctx->Driver.DeleteTextureObject() */ static void st_DeleteTextureObject(struct gl_context *ctx, struct gl_texture_object *texObj) { struct st_context *st = st_context(ctx); struct st_texture_object *stObj = st_texture_object(texObj); pipe_resource_reference(&stObj->pt, NULL); st_texture_release_all_sampler_views(st, stObj); st_texture_free_sampler_views(stObj); + mtx_destroy(&stObj->validate_mutex); _mesa_delete_texture_object(ctx, texObj); } /** called via ctx->Driver.FreeTextureImageBuffer() */ static void st_FreeTextureImageBuffer(struct gl_context *ctx, struct gl_texture_image *texImage) { struct st_context *st = st_context(ctx); diff --git a/src/mesa/state_tracker/st_sampler_view.c b/src/mesa/state_tracker/st_sampler_view.c index d1715a888d9..f46e759dc24 100644 --- a/src/mesa/state_tracker/st_sampler_view.c +++ b/src/mesa/state_tracker/st_sampler_view.c @@ -39,20 +39,22 @@ #include "st_texture.h" #include "st_format.h" #include "st_cb_bufferobjects.h" #include "st_cb_texture.h" /** * Try to find a matching sampler view for the given context. * If none is found an empty slot is initialized with a * template and returned instead. + * + * Must be called with the validate_mutex locked. */ static struct pipe_sampler_view ** st_texture_get_sampler_view(struct st_context *st, struct st_texture_object *stObj) { struct pipe_sampler_view **free = NULL; GLuint i; for (i = 0; i < stObj->num_sampler_views; ++i) { struct pipe_sampler_view **sv = &stObj->sampler_views[i]; @@ -79,52 +81,90 @@ st_texture_get_sampler_view(struct st_context *st, *free = NULL; } assert(*free == NULL); return free; } /** + * Return the most-recently validated sampler view for the texture \p stObj + * in the given context, if any. + * + * Performs no additional validation. + */ +struct pipe_sampler_view * +st_texture_get_current_sampler_view(const struct st_context *st, + const struct st_texture_object *stObj) +{ + struct pipe_sampler_view *view = NULL; + + /* const-cast since C does not have the mutable keyword */ + mtx_lock((mtx_t*)&stObj->validate_mutex); + for (unsigned i = 0; i < stObj->num_sampler_views; ++i) { + const struct st_sampler_view *sv = &stObj->sampler_views[i]; + if (sv->view && sv->view->context == st->pipe) { + view = sv->view; + break; + } + } + mtx_unlock((mtx_t*)&stObj->validate_mutex); + + return view; +} + + +/** * For the given texture object, release any sampler views which belong * to the calling context. */ void st_texture_release_sampler_view(struct st_context *st, struct st_texture_object *stObj) { GLuint i; + /* This function is called during context destruction, which can race with + * operations on the texture in another context in a well-formed GL program. + */ + mtx_lock(&stObj->validate_mutex); for (i = 0; i < stObj->num_sampler_views; ++i) { struct pipe_sampler_view **sv = &stObj->sampler_views[i]; if (*sv && (*sv)->context == st->pipe) { pipe_sampler_view_reference(sv, NULL); break; } } + mtx_unlock(&stObj->validate_mutex); } /** * Release all sampler views attached to the given texture object, regardless * of the context. */ void st_texture_release_all_sampler_views(struct st_context *st, struct st_texture_object *stObj) { GLuint i; + /* This function is called from code paths that do not correspond to a + * modification of the texture, so it may race with other accesses of the + * sampler views array in a well-formed GL program. + */ + mtx_lock(&stObj->validate_mutex); for (i = 0; i < stObj->num_sampler_views; ++i) pipe_sampler_view_release(st->pipe, &stObj->sampler_views[i]); + mtx_unlock(&stObj->validate_mutex); } void st_texture_free_sampler_views(struct st_texture_object *stObj) { free(stObj->sampler_views); stObj->sampler_views = NULL; stObj->num_sampler_views = 0; } @@ -400,20 +440,21 @@ st_create_texture_sampler_view_from_stobj(struct st_context *st, struct pipe_sampler_view * st_get_texture_sampler_view_from_stobj(struct st_context *st, struct st_texture_object *stObj, const struct gl_sampler_object *samp, bool glsl130_or_later) { struct pipe_sampler_view **sv; + mtx_lock(&stObj->validate_mutex); sv = st_texture_get_sampler_view(st, stObj); if (*sv) { /* Debug check: make sure that the sampler view's parameters are * what they're supposed to be. */ MAYBE_UNUSED struct pipe_sampler_view *view = *sv; assert(stObj->pt == view->texture); assert(!check_sampler_swizzle(st, stObj, view, glsl130_or_later)); assert(get_sampler_view_format(st, stObj, samp) == view->format); @@ -429,73 +470,82 @@ st_get_texture_sampler_view_from_stobj(struct st_context *st, } else { /* create new sampler view */ enum pipe_format format = get_sampler_view_format(st, stObj, samp); *sv = st_create_texture_sampler_view_from_stobj(st, stObj, format, glsl130_or_later); } - return *sv; + struct pipe_sampler_view *view = *sv; + mtx_unlock(&stObj->validate_mutex); + + return view; } struct pipe_sampler_view * st_get_buffer_sampler_view_from_stobj(struct st_context *st, struct st_texture_object *stObj) { struct pipe_sampler_view **sv; struct st_buffer_object *stBuf = st_buffer_object(stObj->base.BufferObject); if (!stBuf || !stBuf->buffer) return NULL; + mtx_lock(&stObj->validate_mutex); sv = st_texture_get_sampler_view(st, stObj); struct pipe_resource *buf = stBuf->buffer; struct pipe_sampler_view *view = *sv; if (view && view->texture == buf) { /* Debug check: make sure that the sampler view's parameters are * what they're supposed to be. */ assert(st_mesa_format_to_pipe_format(st, stObj->base._BufferObjectFormat) == view->format); assert(view->target == PIPE_BUFFER); unsigned base = stObj->base.BufferOffset; MAYBE_UNUSED unsigned size = MIN2(buf->width0 - base, (unsigned) stObj->base.BufferSize); assert(view->u.buf.offset == base); assert(view->u.buf.size == size); } else { unsigned base = stObj->base.BufferOffset; + view = NULL; + if (base >= buf->width0) - return NULL; + goto out; unsigned size = buf->width0 - base; size = MIN2(size, (unsigned)stObj->base.BufferSize); if (!size) - return NULL; + goto out; /* Create a new sampler view. There is no need to clear the entire * structure (consider CPU overhead). */ struct pipe_sampler_view templ; templ.format = st_mesa_format_to_pipe_format(st, stObj->base._BufferObjectFormat); templ.target = PIPE_BUFFER; templ.swizzle_r = PIPE_SWIZZLE_X; templ.swizzle_g = PIPE_SWIZZLE_Y; templ.swizzle_b = PIPE_SWIZZLE_Z; templ.swizzle_a = PIPE_SWIZZLE_W; templ.u.buf.offset = base; templ.u.buf.size = size; pipe_sampler_view_reference(sv, NULL); *sv = st->pipe->create_sampler_view(st->pipe, buf, &templ); + view = *sv; } - return *sv; +out: + mtx_unlock(&stObj->validate_mutex); + return view; } diff --git a/src/mesa/state_tracker/st_sampler_view.h b/src/mesa/state_tracker/st_sampler_view.h index 392206be4f7..9e5b964976e 100644 --- a/src/mesa/state_tracker/st_sampler_view.h +++ b/src/mesa/state_tracker/st_sampler_view.h @@ -61,20 +61,23 @@ extern void st_texture_release_sampler_view(struct st_context *st, struct st_texture_object *stObj); extern void st_texture_release_all_sampler_views(struct st_context *st, struct st_texture_object *stObj); void st_texture_free_sampler_views(struct st_texture_object *stObj); +struct pipe_sampler_view * +st_texture_get_current_sampler_view(const struct st_context *st, + const struct st_texture_object *stObj); struct pipe_sampler_view * st_get_texture_sampler_view_from_stobj(struct st_context *st, struct st_texture_object *stObj, const struct gl_sampler_object *samp, bool glsl130_or_later); struct pipe_sampler_view * st_get_buffer_sampler_view_from_stobj(struct st_context *st, struct st_texture_object *stObj); diff --git a/src/mesa/state_tracker/st_texture.h b/src/mesa/state_tracker/st_texture.h index 8448f4c6f02..5aed8960541 100644 --- a/src/mesa/state_tracker/st_texture.h +++ b/src/mesa/state_tracker/st_texture.h @@ -85,20 +85,23 @@ struct st_texture_object GLuint lastLevel; unsigned int validated_first_level; unsigned int validated_last_level; /* On validation any active images held in main memory or in other * textures will be copied to this texture and the old storage freed. */ struct pipe_resource *pt; + /* Protect iteration over the sampler_views array */ + mtx_t validate_mutex; + /* Number of views in sampler_views array */ GLuint num_sampler_views; /* Array of sampler views (one per context) attached to this texture * object. Created lazily on first binding in context. */ struct pipe_sampler_view **sampler_views; /* True if this texture comes from the window system. Such a texture * cannot be reallocated and the format can only be changed with a sampler -- 2.11.0 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev