Mesa (master): st/mesa: eliminate all atomic ops when setting vertex buffers
GitLab Mirror
gitlab-mirror at kemper.freedesktop.org
Thu Jan 28 00:13:26 UTC 2021
Module: Mesa
Branch: master
Commit: 7688b8ae980223f094be9c70fe695e2122caf3e3
URL: http://cgit.freedesktop.org/mesa/mesa/commit/?id=7688b8ae980223f094be9c70fe695e2122caf3e3
Author: Marek Olšák <marek.olsak at amd.com>
Date: Sat Jan 2 16:40:11 2021 -0500
st/mesa: eliminate all atomic ops when setting vertex buffers
This implements the same optimization as u_upload_mgr.
Reviewed-by: Pierre-Eric Pelloux-Prayer <pierre-eric.pelloux-prayer at amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/8298>
---
src/mesa/state_tracker/st_atom_array.c | 23 ++++---------
src/mesa/state_tracker/st_cb_bufferobjects.c | 33 ++++++++++++++----
src/mesa/state_tracker/st_cb_bufferobjects.h | 51 ++++++++++++++++++++++++++++
3 files changed, 84 insertions(+), 23 deletions(-)
diff --git a/src/mesa/state_tracker/st_atom_array.c b/src/mesa/state_tracker/st_atom_array.c
index 97f93627550..8adc899d818 100644
--- a/src/mesa/state_tracker/st_atom_array.c
+++ b/src/mesa/state_tracker/st_atom_array.c
@@ -160,9 +160,8 @@ st_setup_arrays(struct st_context *st,
/* Set the vertex buffer. */
if (binding->BufferObj) {
- struct st_buffer_object *stobj = st_buffer_object(binding->BufferObj);
-
- vbuffer[bufidx].buffer.resource = stobj ? stobj->buffer : NULL;
+ vbuffer[bufidx].buffer.resource =
+ st_get_buffer_reference(ctx, binding->BufferObj);
vbuffer[bufidx].is_user_buffer = false;
vbuffer[bufidx].buffer_offset = binding->Offset +
attrib->RelativeOffset;
@@ -190,9 +189,8 @@ st_setup_arrays(struct st_context *st,
if (binding->BufferObj) {
/* Set the binding */
- struct st_buffer_object *stobj = st_buffer_object(binding->BufferObj);
-
- vbuffer[bufidx].buffer.resource = stobj ? stobj->buffer : NULL;
+ vbuffer[bufidx].buffer.resource =
+ st_get_buffer_reference(ctx, binding->BufferObj);
vbuffer[bufidx].is_user_buffer = false;
vbuffer[bufidx].buffer_offset = _mesa_draw_binding_offset(binding);
} else {
@@ -229,7 +227,7 @@ st_setup_arrays(struct st_context *st,
* Return the index of the vertex buffer where current attribs have been
* uploaded.
*/
-static int ALWAYS_INLINE
+static void ALWAYS_INLINE
st_setup_current(struct st_context *st,
const struct st_vertex_program *vp,
const struct st_common_variant *vp_variant,
@@ -286,9 +284,7 @@ st_setup_current(struct st_context *st,
&vbuffer[bufidx].buffer.resource);
/* Always unmap. The uploader might use explicit flushes. */
u_upload_unmap(uploader);
- return bufidx;
}
- return -1;
}
void
@@ -341,8 +337,7 @@ st_update_array(struct st_context *st)
/* _NEW_CURRENT_ATTRIB */
/* Setup zero-stride attribs. */
- int current_attrib_buffer =
- st_setup_current(st, vp, vp_variant, &velements, vbuffer, &num_vbuffers);
+ st_setup_current(st, vp, vp_variant, &velements, vbuffer, &num_vbuffers);
velements.count = vp->num_inputs + vp_variant->key.passthrough_edgeflags;
@@ -354,12 +349,8 @@ st_update_array(struct st_context *st)
cso_set_vertex_buffers_and_elements(cso, &velements,
num_vbuffers,
unbind_trailing_vbuffers,
- false,
+ true,
uses_user_vertex_buffers,
vbuffer);
st->last_num_vbuffers = num_vbuffers;
-
- /* Unreference uploaded current attrib buffer. */
- if (current_attrib_buffer >= 0)
- pipe_resource_reference(&vbuffer[current_attrib_buffer].buffer.resource, NULL);
}
diff --git a/src/mesa/state_tracker/st_cb_bufferobjects.c b/src/mesa/state_tracker/st_cb_bufferobjects.c
index b6d42a2e62f..7756a5cfa82 100644
--- a/src/mesa/state_tracker/st_cb_bufferobjects.c
+++ b/src/mesa/state_tracker/st_cb_bufferobjects.c
@@ -70,6 +70,28 @@ st_bufferobj_alloc(struct gl_context *ctx, GLuint name)
}
+static void
+release_buffer(struct gl_buffer_object *obj)
+{
+ struct st_buffer_object *st_obj = st_buffer_object(obj);
+
+ if (!st_obj->buffer)
+ return;
+
+ /* Subtract the remaining private references before unreferencing
+ * the buffer. See the header file for explanation.
+ */
+ if (st_obj->private_refcount) {
+ assert(st_obj->private_refcount > 0);
+ p_atomic_add(&st_obj->buffer->reference.count,
+ -st_obj->private_refcount);
+ st_obj->private_refcount = 0;
+ }
+ st_obj->ctx = NULL;
+
+ pipe_resource_reference(&st_obj->buffer, NULL);
+}
+
/**
* Deallocate/free a vertex/pixel buffer object.
@@ -78,14 +100,9 @@ st_bufferobj_alloc(struct gl_context *ctx, GLuint name)
static void
st_bufferobj_free(struct gl_context *ctx, struct gl_buffer_object *obj)
{
- struct st_buffer_object *st_obj = st_buffer_object(obj);
-
assert(obj->RefCount == 0);
_mesa_buffer_unmap_all_mappings(ctx, obj);
-
- if (st_obj->buffer)
- pipe_resource_reference(&st_obj->buffer, NULL);
-
+ release_buffer(obj);
_mesa_delete_buffer_object(ctx, obj);
}
@@ -330,7 +347,7 @@ bufferobj_data(struct gl_context *ctx,
st_obj->Base.Usage = usage;
st_obj->Base.StorageFlags = storageFlags;
- pipe_resource_reference( &st_obj->buffer, NULL );
+ release_buffer(obj);
const unsigned bindings = buffer_target_to_bind_flags(target);
@@ -375,6 +392,8 @@ bufferobj_data(struct gl_context *ctx,
st_obj->Base.Size = 0;
return GL_FALSE;
}
+
+ st_obj->ctx = ctx;
}
/* The current buffer may be bound, so we have to revalidate all atoms that
diff --git a/src/mesa/state_tracker/st_cb_bufferobjects.h b/src/mesa/state_tracker/st_cb_bufferobjects.h
index 8776839aa1d..2a1874f6ae2 100644
--- a/src/mesa/state_tracker/st_cb_bufferobjects.h
+++ b/src/mesa/state_tracker/st_cb_bufferobjects.h
@@ -43,6 +43,25 @@ struct st_buffer_object
{
struct gl_buffer_object Base;
struct pipe_resource *buffer; /* GPU storage */
+
+ struct gl_context *ctx; /* the context that owns private_refcount */
+
+ /* This mechanism allows passing buffer references to the driver without
+ * using atomics to increase the reference count.
+ *
+ * This private refcount can be decremented without atomics but only one
+ * context (ctx above) can use this counter to be thread-safe.
+ *
+ * This number is atomically added to buffer->reference.count at
+ * initialization. If it's never used, the same number is atomically
+ * subtracted from buffer->reference.count before destruction. If this
+ * number is decremented, we can pass that reference to the driver without
+ * touching reference.count. At buffer destruction we only subtract
+ * the number of references we did not return. This can possibly turn
+ * a million atomic increments into 1 add and 1 subtract atomic op.
+ */
+ int private_refcount;
+
struct pipe_transfer *transfer[MAP_COUNT];
};
@@ -63,5 +82,37 @@ extern void
st_init_bufferobject_functions(struct pipe_screen *screen,
struct dd_function_table *functions);
+static inline struct pipe_resource *
+st_get_buffer_reference(struct gl_context *ctx, struct gl_buffer_object *obj)
+{
+ if (unlikely(!obj))
+ return NULL;
+
+ struct st_buffer_object *stobj = st_buffer_object(obj);
+ struct pipe_resource *buffer = stobj->buffer;
+
+ if (unlikely(!buffer))
+ return NULL;
+
+ /* Only one context is using the fast path. All other contexts must use
+ * the slow path.
+ */
+ if (unlikely(stobj->ctx != ctx)) {
+ p_atomic_inc(&buffer->reference.count);
+ return buffer;
+ }
+
+ if (unlikely(stobj->private_refcount <= 0)) {
+ assert(stobj->private_refcount == 0);
+
+ /* This is the number of atomic increments we will skip. */
+ stobj->private_refcount = 100000000;
+ p_atomic_add(&buffer->reference.count, stobj->private_refcount);
+ }
+
+ /* Return a buffer reference while decrementing the private refcount. */
+ stobj->private_refcount--;
+ return buffer;
+}
#endif
More information about the mesa-commit
mailing list