[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