[Mesa-dev] [PATCH 2/6] st/mesa: guard sampler views changes with a mutex

Nicolai Hähnle nhaehnle at gmail.com
Fri Oct 6 20:38:12 UTC 2017


From: Nicolai Hähnle <nicolai.haehnle at 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



More information about the mesa-dev mailing list