X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fstate_tracker%2Fst_sampler_view.c;h=a31e573c01a3dec0371ddcbbf94d306c3ccbc6b5;hb=7f0b6a5df8e360e52a97f59948dda927fe9df15e;hp=892725671d2a6dee54546d67e8fa851150c87e11;hpb=0dcf30e55091a0c78fc84d98f5ea36286f3f1961;p=mesa.git diff --git a/src/mesa/state_tracker/st_sampler_view.c b/src/mesa/state_tracker/st_sampler_view.c index 892725671d2..a31e573c01a 100644 --- a/src/mesa/state_tracker/st_sampler_view.c +++ b/src/mesa/state_tracker/st_sampler_view.c @@ -24,7 +24,7 @@ */ #include "pipe/p_context.h" -#include "util/u_format.h" +#include "util/format/u_format.h" #include "util/u_inlines.h" #include "main/context.h" @@ -43,24 +43,39 @@ /** - * 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. + * Set the given view as the current context's view for the texture. + * + * Overwrites any pre-existing view of the context. + * + * Takes ownership of the view (i.e., stores the view without incrementing the + * reference count). + * + * \return the view, or NULL on error. In case of error, the reference to the + * view is released. */ -static struct st_sampler_view * -st_texture_get_sampler_view(struct st_context *st, - struct st_texture_object *stObj) +static struct pipe_sampler_view * +st_texture_set_sampler_view(struct st_context *st, + struct st_texture_object *stObj, + struct pipe_sampler_view *view, + bool glsl130_or_later, bool srgb_skip_decode) { + struct st_sampler_views *views; struct st_sampler_view *free = NULL; + struct st_sampler_view *sv; GLuint i; - for (i = 0; i < stObj->num_sampler_views; ++i) { - struct st_sampler_view *sv = &stObj->sampler_views[i]; + simple_mtx_lock(&stObj->validate_mutex); + views = stObj->sampler_views; + + for (i = 0; i < views->count; ++i) { + sv = &views->views[i]; + /* Is the array entry used ? */ if (sv->view) { /* check if the context matches */ if (sv->view->context == st->pipe) { - return sv; + pipe_sampler_view_reference(&sv->view, NULL); + goto found; } } else { /* Found a free slot, remember that */ @@ -69,64 +84,185 @@ st_texture_get_sampler_view(struct st_context *st, } /* Couldn't find a slot for our context, create a new one */ + if (free) { + sv = free; + } else { + if (views->count >= views->max) { + /* Allocate a larger container. */ + unsigned new_max = 2 * views->max; + unsigned new_size = sizeof(*views) + new_max * sizeof(views->views[0]); + + if (new_max < views->max || + new_max > (UINT_MAX - sizeof(*views)) / sizeof(views->views[0])) { + pipe_sampler_view_reference(&view, NULL); + goto out; + } + + struct st_sampler_views *new_views = malloc(new_size); + if (!new_views) { + pipe_sampler_view_reference(&view, NULL); + goto out; + } + + new_views->count = views->count; + new_views->max = new_max; + memcpy(&new_views->views[0], &views->views[0], + views->count * sizeof(views->views[0])); + + /* Initialize the pipe_sampler_view pointers to zero so that we don't + * have to worry about racing against readers when incrementing + * views->count. + */ + memset(&new_views->views[views->count], 0, + (new_max - views->count) * sizeof(views->views[0])); + + /* Use memory release semantics to ensure that concurrent readers will + * get the correct contents of the new container. + * + * Also, the write should be atomic, but that's guaranteed anyway on + * all supported platforms. + */ + p_atomic_set(&stObj->sampler_views, new_views); - if (!free) { - /* Haven't even found a free one, resize the array */ - unsigned new_size = (stObj->num_sampler_views + 1) * - sizeof(struct st_sampler_view); - stObj->sampler_views = realloc(stObj->sampler_views, new_size); - free = &stObj->sampler_views[stObj->num_sampler_views++]; - free->view = NULL; + /* We keep the old container around until the texture object is + * deleted, because another thread may still be reading from it. We + * double the size of the container each time, so we end up with + * at most twice the total memory allocation. + */ + views->next = stObj->sampler_views_old; + stObj->sampler_views_old = views; + + views = new_views; + } + + sv = &views->views[views->count]; + + /* Since modification is guarded by the lock, only the write part of the + * increment has to be atomic, and that's already guaranteed on all + * supported platforms without using an atomic intrinsic. + */ + views->count++; } - assert(free->view == NULL); +found: + assert(sv->view == NULL); - return free; + sv->glsl130_or_later = glsl130_or_later; + sv->srgb_skip_decode = srgb_skip_decode; + sv->view = view; + sv->st = st; + +out: + simple_mtx_unlock(&stObj->validate_mutex); + return view; +} + + +/** + * Return the most-recently validated sampler view for the texture \p stObj + * in the given context, if any. + * + * Performs no additional validation. + */ +const struct st_sampler_view * +st_texture_get_current_sampler_view(const struct st_context *st, + const struct st_texture_object *stObj) +{ + const struct st_sampler_views *views = p_atomic_read(&stObj->sampler_views); + + for (unsigned i = 0; i < views->count; ++i) { + const struct st_sampler_view *sv = &views->views[i]; + if (sv->view && sv->view->context == st->pipe) + return sv; + } + + return NULL; } /** * For the given texture object, release any sampler views which belong - * to the calling context. + * to the calling context. This is used to free any sampler views + * which belong to the context before the context is destroyed. */ void -st_texture_release_sampler_view(struct st_context *st, - struct st_texture_object *stObj) +st_texture_release_context_sampler_view(struct st_context *st, + struct st_texture_object *stObj) { GLuint i; - for (i = 0; i < stObj->num_sampler_views; ++i) { - struct pipe_sampler_view **sv = &stObj->sampler_views[i].view; + simple_mtx_lock(&stObj->validate_mutex); + struct st_sampler_views *views = stObj->sampler_views; + for (i = 0; i < views->count; ++i) { + struct pipe_sampler_view **sv = &views->views[i].view; if (*sv && (*sv)->context == st->pipe) { pipe_sampler_view_reference(sv, NULL); break; } } + simple_mtx_unlock(&stObj->validate_mutex); } /** * Release all sampler views attached to the given texture object, regardless - * of the context. + * of the context. This is called fairly frequently. For example, whenever + * the texture's base level, max level or swizzle change. */ void st_texture_release_all_sampler_views(struct st_context *st, struct st_texture_object *stObj) { - GLuint i; - - for (i = 0; i < stObj->num_sampler_views; ++i) - pipe_sampler_view_release(st->pipe, &stObj->sampler_views[i].view); + /* TODO: This happens while a texture is deleted, because the Driver API + * is asymmetric: the driver allocates the texture object memory, but + * mesa/main frees it. + */ + if (!stObj->sampler_views) + return; + + simple_mtx_lock(&stObj->validate_mutex); + struct st_sampler_views *views = stObj->sampler_views; + for (unsigned i = 0; i < views->count; ++i) { + struct st_sampler_view *stsv = &views->views[i]; + if (stsv->view) { + if (stsv->st && stsv->st != st) { + /* Transfer this reference to the zombie list. It will + * likely be freed when the zombie list is freed. + */ + st_save_zombie_sampler_view(stsv->st, stsv->view); + stsv->view = NULL; + } else { + pipe_sampler_view_reference(&stsv->view, NULL); + } + } + } + views->count = 0; + simple_mtx_unlock(&stObj->validate_mutex); } +/* + * Delete the texture's sampler views and st_sampler_views containers. + * This is to be called just before a texture is deleted. + */ void -st_texture_free_sampler_views(struct st_texture_object *stObj) +st_delete_texture_sampler_views(struct st_context *st, + struct st_texture_object *stObj) { + st_texture_release_all_sampler_views(st, stObj); + + /* Free the container of the current per-context sampler views */ + assert(stObj->sampler_views->count == 0); free(stObj->sampler_views); stObj->sampler_views = NULL; - stObj->num_sampler_views = 0; + + /* Free old sampler view containers */ + while (stObj->sampler_views_old) { + struct st_sampler_views *views = stObj->sampler_views_old; + stObj->sampler_views_old = views->next; + free(views); + } } @@ -291,7 +427,7 @@ get_texture_format_swizzle(const struct st_context *st, * * \param stObj the st texture object, */ -MAYBE_UNUSED static boolean +ASSERTED static boolean check_sampler_swizzle(const struct st_context *st, const struct st_texture_object *stObj, const struct pipe_sampler_view *sv, @@ -353,12 +489,30 @@ get_sampler_view_format(struct st_context *st, if (srgb_skip_decode) format = util_format_linear(format); + /* if resource format matches then YUV wasn't lowered */ + if (format == stObj->pt->format) + return format; + /* Use R8_UNORM for video formats */ switch (format) { case PIPE_FORMAT_NV12: case PIPE_FORMAT_IYUV: format = PIPE_FORMAT_R8_UNORM; break; + case PIPE_FORMAT_P010: + case PIPE_FORMAT_P016: + format = PIPE_FORMAT_R16_UNORM; + break; + case PIPE_FORMAT_YUYV: + case PIPE_FORMAT_UYVY: + format = PIPE_FORMAT_R8G8_UNORM; + break; + case PIPE_FORMAT_AYUV: + format = PIPE_FORMAT_RGBA8888_UNORM; + break; + case PIPE_FORMAT_XYUV: + format = PIPE_FORMAT_RGBX8888_UNORM; + break; default: break; } @@ -368,8 +522,8 @@ get_sampler_view_format(struct st_context *st, static struct pipe_sampler_view * st_create_texture_sampler_view_from_stobj(struct st_context *st, - struct st_texture_object *stObj, - enum pipe_format format, + struct st_texture_object *stObj, + enum pipe_format format, bool glsl130_or_later) { /* There is no need to clear this structure (consider CPU overhead). */ @@ -378,13 +532,13 @@ st_create_texture_sampler_view_from_stobj(struct st_context *st, templ.format = format; - if (stObj->level_override) { + if (stObj->level_override >= 0) { templ.u.tex.first_level = templ.u.tex.last_level = stObj->level_override; } else { templ.u.tex.first_level = stObj->base.MinLevel + stObj->base.BaseLevel; templ.u.tex.last_level = last_level(stObj); } - if (stObj->layer_override) { + if (stObj->layer_override >= 0) { templ.u.tex.first_layer = templ.u.tex.last_layer = stObj->layer_override; } else { templ.u.tex.first_layer = stObj->base.MinLayer; @@ -410,47 +564,45 @@ st_get_texture_sampler_view_from_stobj(struct st_context *st, bool glsl130_or_later, bool ignore_srgb_decode) { - struct st_sampler_view *sv; - struct pipe_sampler_view *view; + const struct st_sampler_view *sv; bool srgb_skip_decode = false; - sv = st_texture_get_sampler_view(st, stObj); - view = sv->view; - if (!ignore_srgb_decode && samp->sRGBDecode == GL_SKIP_DECODE_EXT) srgb_skip_decode = true; - if (view && + sv = st_texture_get_current_sampler_view(st, stObj); + + if (sv && sv->glsl130_or_later == glsl130_or_later && sv->srgb_skip_decode == srgb_skip_decode) { /* 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->view; + struct pipe_sampler_view *view = sv->view; assert(stObj->pt == view->texture); assert(!check_sampler_swizzle(st, stObj, view, glsl130_or_later)); assert(get_sampler_view_format(st, stObj, srgb_skip_decode) == view->format); assert(gl_target_to_pipe(stObj->base.Target) == view->target); - assert(stObj->level_override || + assert(stObj->level_override >= 0 || stObj->base.MinLevel + stObj->base.BaseLevel == view->u.tex.first_level); - assert(stObj->level_override || last_level(stObj) == view->u.tex.last_level); - assert(stObj->layer_override || stObj->base.MinLayer == view->u.tex.first_layer); - assert(stObj->layer_override || last_layer(stObj) == view->u.tex.last_layer); - assert(!stObj->layer_override || + assert(stObj->level_override >= 0 || last_level(stObj) == view->u.tex.last_level); + assert(stObj->layer_override >= 0 || stObj->base.MinLayer == view->u.tex.first_layer); + assert(stObj->layer_override >= 0 || last_layer(stObj) == view->u.tex.last_layer); + assert(stObj->layer_override < 0 || (stObj->layer_override == view->u.tex.first_layer && stObj->layer_override == view->u.tex.last_layer)); + return view; } - else { - /* create new sampler view */ - enum pipe_format format = get_sampler_view_format(st, stObj, srgb_skip_decode); - sv->glsl130_or_later = glsl130_or_later; - sv->srgb_skip_decode = srgb_skip_decode; + /* create new sampler view */ + enum pipe_format format = get_sampler_view_format(st, stObj, + srgb_skip_decode); + struct pipe_sampler_view *view = + st_create_texture_sampler_view_from_stobj(st, stObj, format, + glsl130_or_later); - pipe_sampler_view_release(st->pipe, &sv->view); - view = sv->view = - st_create_texture_sampler_view_from_stobj(st, stObj, format, glsl130_or_later); - } + view = st_texture_set_sampler_view(st, stObj, view, + glsl130_or_later, srgb_skip_decode); return view; } @@ -460,58 +612,66 @@ struct pipe_sampler_view * st_get_buffer_sampler_view_from_stobj(struct st_context *st, struct st_texture_object *stObj) { - struct st_sampler_view *sv; + const struct st_sampler_view *sv; struct st_buffer_object *stBuf = st_buffer_object(stObj->base.BufferObject); if (!stBuf || !stBuf->buffer) return NULL; - sv = st_texture_get_sampler_view(st, stObj); + sv = st_texture_get_current_sampler_view(st, stObj); struct pipe_resource *buf = stBuf->buffer; - struct pipe_sampler_view *view = sv->view; - 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) + if (sv) { + struct pipe_sampler_view *view = sv->view; + + if (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, + assert(view->target == PIPE_BUFFER); + ASSERTED unsigned base = stObj->base.BufferOffset; + ASSERTED 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; + assert(view->u.buf.offset == base); + assert(view->u.buf.size == size); + return view; + } + } - if (base >= buf->width0) - return NULL; + unsigned base = stObj->base.BufferOffset; - unsigned size = buf->width0 - base; - size = MIN2(size, (unsigned)stObj->base.BufferSize); - if (!size) - return NULL; + if (base >= buf->width0) + return NULL; + + unsigned size = buf->width0 - base; + size = MIN2(size, (unsigned)stObj->base.BufferSize); + if (!size) + return NULL; + + /* 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; + + struct pipe_sampler_view *view = + st->pipe->create_sampler_view(st->pipe, buf, &templ); + + view = st_texture_set_sampler_view(st, stObj, view, false, false); - /* 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_release(st->pipe, &sv->view); - view = sv->view = st->pipe->create_sampler_view(st->pipe, buf, &templ); - } return view; }