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