[Mesa-dev] [PATCH] mesa: avoid pthread_mutexes when adjusting object refcounts

Jan Ziak 0xe2.0x9a.0x9b at gmail.com
Wed Oct 19 19:06:00 UTC 2016


This patch removes locks around reference counts in favor of atomic inc/dec
operations on the refcounts. This makes the refcount adjustments much faster.

The set of GL objects benefiting from this patch corresponds to the names of
the patched files.

The new code benefits applications in which the ratio of the number of GL calls
to the datasize of the calls is relatively large (that is: bytes per call is
relatively low):

- A custom micro-benchmark was measured to run about 8% faster
- Shadow of Mordor, Mesh Quality set to "Ultra": +0.8 fps (+2%)
- Tomb Raider 2013, High quality: +1.2 fps (+1%)

Signed-off-by: Jan Ziak (http://atom-symbol.net) <0xe2.0x9a.0x9b at gmail.com>
---
 src/mesa/main/arrayobj.c     | 26 +++++++++++---------------
 src/mesa/main/bufferobj.c    | 23 ++++++++++-------------
 src/mesa/main/framebuffer.c  | 23 ++++++++++-------------
 src/mesa/main/pipelineobj.c  | 24 ++++++++++--------------
 src/mesa/main/renderbuffer.c | 21 +++++++++------------
 src/mesa/main/samplerobj.c   | 24 ++++++++++--------------
 src/mesa/main/shaderobj.c    | 37 +++++++++++++++++--------------------
 src/mesa/main/texobj.c       | 24 +++++++++---------------
 src/mesa/program/program.c   | 31 +++++++++++++------------------
 9 files changed, 99 insertions(+), 134 deletions(-)

diff --git a/src/mesa/main/arrayobj.c b/src/mesa/main/arrayobj.c
index 11e3482..261ed87 100644
--- a/src/mesa/main/arrayobj.c
+++ b/src/mesa/main/arrayobj.c
@@ -53,6 +53,7 @@
 #include "varray.h"
 #include "main/dispatch.h"
 #include "util/bitscan.h"
+#include "util/u_atomic.h"
 
 
 /**
@@ -185,21 +186,18 @@ _mesa_reference_vao_(struct gl_context *ctx,
                      struct gl_vertex_array_object **ptr,
                      struct gl_vertex_array_object *vao)
 {
-   assert(*ptr != vao);
+   struct gl_vertex_array_object *const oldVao = *ptr;
 
-   if (*ptr) {
+   assert(oldVao != vao);
+   if (oldVao) {
       /* Unreference the old array object */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_vertex_array_object *oldObj = *ptr;
+      GLint newRefCount;
 
-      mtx_lock(&oldObj->Mutex);
-      assert(oldObj->RefCount > 0);
-      oldObj->RefCount--;
-      deleteFlag = (oldObj->RefCount == 0);
-      mtx_unlock(&oldObj->Mutex);
+      newRefCount = p_atomic_dec_return(&oldVao->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag)
-         _mesa_delete_vao(ctx, oldObj);
+      if (newRefCount == 0)
+         _mesa_delete_vao(ctx, oldVao);
 
       *ptr = NULL;
    }
@@ -207,18 +205,16 @@ _mesa_reference_vao_(struct gl_context *ctx,
 
    if (vao) {
       /* reference new array object */
-      mtx_lock(&vao->Mutex);
-      if (vao->RefCount == 0) {
+      if (unlikely(p_atomic_inc_return(&vao->RefCount) == 1)) {
          /* this array's being deleted (look just above) */
          /* Not sure this can every really happen.  Warn if it does. */
          _mesa_problem(NULL, "referencing deleted array object");
+         p_atomic_dec(&vao->RefCount); // revert the refcount to previous value
          *ptr = NULL;
       }
       else {
-         vao->RefCount++;
          *ptr = vao;
       }
-      mtx_unlock(&vao->Mutex);
    }
 }
 
diff --git a/src/mesa/main/bufferobj.c b/src/mesa/main/bufferobj.c
index 885a99d..0821904 100644
--- a/src/mesa/main/bufferobj.c
+++ b/src/mesa/main/bufferobj.c
@@ -45,6 +45,7 @@
 #include "texstore.h"
 #include "transformfeedback.h"
 #include "varray.h"
+#include "util/u_atomic.h"
 
 
 /* Debug flags */
@@ -488,18 +489,16 @@ _mesa_reference_buffer_object_(struct gl_context *ctx,
                                struct gl_buffer_object **ptr,
                                struct gl_buffer_object *bufObj)
 {
-   if (*ptr) {
+   struct gl_buffer_object *const oldObj = *ptr;
+
+   if (oldObj) {
       /* Unreference the old buffer */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_buffer_object *oldObj = *ptr;
+      GLint newRefCount;
 
-      mtx_lock(&oldObj->Mutex);
-      assert(oldObj->RefCount > 0);
-      oldObj->RefCount--;
-      deleteFlag = (oldObj->RefCount == 0);
-      mtx_unlock(&oldObj->Mutex);
+      newRefCount = p_atomic_dec_return(&oldObj->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag) {
+      if (newRefCount == 0) {
 	 assert(ctx->Driver.DeleteBuffer);
          ctx->Driver.DeleteBuffer(ctx, oldObj);
       }
@@ -510,18 +509,16 @@ _mesa_reference_buffer_object_(struct gl_context *ctx,
 
    if (bufObj) {
       /* reference new buffer */
-      mtx_lock(&bufObj->Mutex);
-      if (bufObj->RefCount == 0) {
+      if (unlikely(p_atomic_inc_return(&bufObj->RefCount) == 1)) {
          /* this buffer's being deleted (look just above) */
          /* Not sure this can every really happen.  Warn if it does. */
          _mesa_problem(NULL, "referencing deleted buffer object");
+         p_atomic_dec(&bufObj->RefCount); // revert the refcount to previous value
          *ptr = NULL;
       }
       else {
-         bufObj->RefCount++;
          *ptr = bufObj;
       }
-      mtx_unlock(&bufObj->Mutex);
    }
 }
 
diff --git a/src/mesa/main/framebuffer.c b/src/mesa/main/framebuffer.c
index 46a6f64..92dcb90 100644
--- a/src/mesa/main/framebuffer.c
+++ b/src/mesa/main/framebuffer.c
@@ -44,6 +44,7 @@
 #include "renderbuffer.h"
 #include "texobj.h"
 #include "glformats.h"
+#include "util/u_atomic.h"
 
 
 
@@ -240,18 +241,16 @@ void
 _mesa_reference_framebuffer_(struct gl_framebuffer **ptr,
                              struct gl_framebuffer *fb)
 {
-   if (*ptr) {
+   struct gl_framebuffer *const oldFb = *ptr;
+
+   if (oldFb) {
       /* unreference old renderbuffer */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_framebuffer *oldFb = *ptr;
-
-      mtx_lock(&oldFb->Mutex);
-      assert(oldFb->RefCount > 0);
-      oldFb->RefCount--;
-      deleteFlag = (oldFb->RefCount == 0);
-      mtx_unlock(&oldFb->Mutex);
+      GLint newRefCount;
+
+      newRefCount = p_atomic_dec_return(&oldFb->RefCount);
+      assert(newRefCount >= 0);
       
-      if (deleteFlag)
+      if (newRefCount == 0)
          oldFb->Delete(oldFb);
 
       *ptr = NULL;
@@ -259,9 +258,7 @@ _mesa_reference_framebuffer_(struct gl_framebuffer **ptr,
    assert(!*ptr);
 
    if (fb) {
-      mtx_lock(&fb->Mutex);
-      fb->RefCount++;
-      mtx_unlock(&fb->Mutex);
+      p_atomic_inc(&fb->RefCount);
       *ptr = fb;
    }
 }
diff --git a/src/mesa/main/pipelineobj.c b/src/mesa/main/pipelineobj.c
index 8229840..d68f0a4 100644
--- a/src/mesa/main/pipelineobj.c
+++ b/src/mesa/main/pipelineobj.c
@@ -48,6 +48,7 @@
 #include "program/program.h"
 #include "program/prog_parameter.h"
 #include "util/ralloc.h"
+#include "util/u_atomic.h"
 
 /**
  * Delete a pipeline object.
@@ -180,20 +181,17 @@ _mesa_reference_pipeline_object_(struct gl_context *ctx,
                                  struct gl_pipeline_object **ptr,
                                  struct gl_pipeline_object *obj)
 {
-   assert(*ptr != obj);
+   struct gl_pipeline_object *const oldObj = *ptr;
 
-   if (*ptr) {
+   assert(oldObj != obj);
+   if (oldObj) {
       /* Unreference the old pipeline object */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_pipeline_object *oldObj = *ptr;
+      GLint newRefCount;
 
-      mtx_lock(&oldObj->Mutex);
-      assert(oldObj->RefCount > 0);
-      oldObj->RefCount--;
-      deleteFlag = (oldObj->RefCount == 0);
-      mtx_unlock(&oldObj->Mutex);
+      newRefCount = p_atomic_dec_return(&oldObj->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag) {
+      if (newRefCount == 0) {
          _mesa_delete_pipeline_object(ctx, oldObj);
       }
 
@@ -203,18 +201,16 @@ _mesa_reference_pipeline_object_(struct gl_context *ctx,
 
    if (obj) {
       /* reference new pipeline object */
-      mtx_lock(&obj->Mutex);
-      if (obj->RefCount == 0) {
+      if (unlikely(p_atomic_inc_return(&obj->RefCount) == 1)) {
          /* this pipeline's being deleted (look just above) */
          /* Not sure this can ever really happen.  Warn if it does. */
          _mesa_problem(NULL, "referencing deleted pipeline object");
+         p_atomic_dec(&obj->RefCount); // revert the refcount to previous value
          *ptr = NULL;
       }
       else {
-         obj->RefCount++;
          *ptr = obj;
       }
-      mtx_unlock(&obj->Mutex);
    }
 }
 
diff --git a/src/mesa/main/renderbuffer.c b/src/mesa/main/renderbuffer.c
index b0d4c8c..37acea0 100644
--- a/src/mesa/main/renderbuffer.c
+++ b/src/mesa/main/renderbuffer.c
@@ -30,6 +30,7 @@
 #include "formats.h"
 #include "mtypes.h"
 #include "renderbuffer.h"
+#include "util/u_atomic.h"
 
 
 /**
@@ -166,18 +167,16 @@ void
 _mesa_reference_renderbuffer_(struct gl_renderbuffer **ptr,
                               struct gl_renderbuffer *rb)
 {
-   if (*ptr) {
+   struct gl_renderbuffer *const oldRb = *ptr;
+
+   if (oldRb) {
       /* Unreference the old renderbuffer */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_renderbuffer *oldRb = *ptr;
+      GLint newRefCount;
 
-      mtx_lock(&oldRb->Mutex);
-      assert(oldRb->RefCount > 0);
-      oldRb->RefCount--;
-      deleteFlag = (oldRb->RefCount == 0);
-      mtx_unlock(&oldRb->Mutex);
+      newRefCount = p_atomic_dec_return(&oldRb->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag) {
+      if (newRefCount == 0) {
          GET_CURRENT_CONTEXT(ctx);
          oldRb->Delete(ctx, oldRb);
       }
@@ -188,9 +187,7 @@ _mesa_reference_renderbuffer_(struct gl_renderbuffer **ptr,
 
    if (rb) {
       /* reference new renderbuffer */
-      mtx_lock(&rb->Mutex);
-      rb->RefCount++;
-      mtx_unlock(&rb->Mutex);
+      p_atomic_inc(&rb->RefCount);
       *ptr = rb;
    }
 }
diff --git a/src/mesa/main/samplerobj.c b/src/mesa/main/samplerobj.c
index 2118f0e..e27ffeb 100644
--- a/src/mesa/main/samplerobj.c
+++ b/src/mesa/main/samplerobj.c
@@ -38,6 +38,7 @@
 #include "main/macros.h"
 #include "main/mtypes.h"
 #include "main/samplerobj.h"
+#include "util/u_atomic.h"
 
 
 struct gl_sampler_object *
@@ -98,20 +99,17 @@ _mesa_reference_sampler_object_(struct gl_context *ctx,
                                 struct gl_sampler_object **ptr,
                                 struct gl_sampler_object *samp)
 {
-   assert(*ptr != samp); /* The inline wrapper should prevent no-op calls */
+   struct gl_sampler_object *const oldSamp = *ptr;
 
-   if (*ptr) {
+   assert(oldSamp != samp); /* The inline wrapper should prevent no-op calls */
+   if (oldSamp) {
       /* Unreference the old sampler */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_sampler_object *oldSamp = *ptr;
+      GLint newRefCount;
 
-      mtx_lock(&oldSamp->Mutex);
-      assert(oldSamp->RefCount > 0);
-      oldSamp->RefCount--;
-      deleteFlag = (oldSamp->RefCount == 0);
-      mtx_unlock(&oldSamp->Mutex);
+      newRefCount = p_atomic_dec_return(&oldSamp->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag)
+      if (newRefCount == 0)
          delete_sampler_object(ctx, oldSamp);
 
       *ptr = NULL;
@@ -120,18 +118,16 @@ _mesa_reference_sampler_object_(struct gl_context *ctx,
 
    if (samp) {
       /* reference new sampler */
-      mtx_lock(&samp->Mutex);
-      if (samp->RefCount == 0) {
+      if (unlikely(p_atomic_inc_return(&samp->RefCount) == 1)) {
          /* this sampler's being deleted (look just above) */
          /* Not sure this can every really happen.  Warn if it does. */
          _mesa_problem(NULL, "referencing deleted sampler object");
+         p_atomic_dec(&samp->RefCount); // revert the refcount to previous value
          *ptr = NULL;
       }
       else {
-         samp->RefCount++;
          *ptr = samp;
       }
-      mtx_unlock(&samp->Mutex);
    }
 }
 
diff --git a/src/mesa/main/shaderobj.c b/src/mesa/main/shaderobj.c
index 136ac7b..5ef020d 100644
--- a/src/mesa/main/shaderobj.c
+++ b/src/mesa/main/shaderobj.c
@@ -41,6 +41,7 @@
 #include "program/prog_parameter.h"
 #include "util/ralloc.h"
 #include "util/string_to_uint_map.h"
+#include "util/u_atomic.h"
 
 /**********************************************************************/
 /*** Shader object functions                                        ***/
@@ -57,21 +58,19 @@ void
 _mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
                        struct gl_shader *sh)
 {
-   assert(ptr);
-   if (*ptr == sh) {
+   struct gl_shader *const old = *ptr;
+   if (old == sh) {
       /* no-op */
       return;
    }
-   if (*ptr) {
+   if (old) {
       /* Unreference the old shader */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_shader *old = *ptr;
+      GLint newRefCount;
 
-      assert(old->RefCount > 0);
-      old->RefCount--;
-      deleteFlag = (old->RefCount == 0);
+      newRefCount = p_atomic_dec_return(&old->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag) {
+      if (newRefCount == 0) {
 	 if (old->Name != 0)
 	    _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
          _mesa_delete_shader(ctx, old);
@@ -83,7 +82,7 @@ _mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
 
    if (sh) {
       /* reference new */
-      sh->RefCount++;
+      p_atomic_inc(&sh->RefCount);
       *ptr = sh;
    }
 }
@@ -219,21 +218,19 @@ _mesa_reference_shader_program_(struct gl_context *ctx,
                                 struct gl_shader_program **ptr,
                                 struct gl_shader_program *shProg)
 {
-   assert(ptr);
-   if (*ptr == shProg) {
+   struct gl_shader_program *const old = *ptr;
+   if (old == shProg) {
       /* no-op */
       return;
    }
-   if (*ptr) {
+   if (old) {
       /* Unreference the old shader program */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_shader_program *old = *ptr;
+      GLint newRefCount;
 
-      assert(old->RefCount > 0);
-      old->RefCount--;
-      deleteFlag = (old->RefCount == 0);
+      newRefCount = p_atomic_dec_return(&old->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag) {
+      if (newRefCount == 0) {
 	 if (old->Name != 0)
 	    _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
          _mesa_delete_shader_program(ctx, old);
@@ -244,7 +241,7 @@ _mesa_reference_shader_program_(struct gl_context *ctx,
    assert(!*ptr);
 
    if (shProg) {
-      shProg->RefCount++;
+      p_atomic_inc(&shProg->RefCount);
       *ptr = shProg;
    }
 }
diff --git a/src/mesa/main/texobj.c b/src/mesa/main/texobj.c
index fbd498d..4601883 100644
--- a/src/mesa/main/texobj.c
+++ b/src/mesa/main/texobj.c
@@ -43,6 +43,7 @@
 #include "texstate.h"
 #include "mtypes.h"
 #include "program/prog_instruction.h"
+#include "util/u_atomic.h"
 
 
 
@@ -542,24 +543,19 @@ void
 _mesa_reference_texobj_(struct gl_texture_object **ptr,
                         struct gl_texture_object *tex)
 {
-   assert(ptr);
+   struct gl_texture_object *const oldTex = *ptr;
 
-   if (*ptr) {
+   if (oldTex) {
       /* Unreference the old texture */
-      GLboolean deleteFlag = GL_FALSE;
-      struct gl_texture_object *oldTex = *ptr;
+      GLint newRefCount;
 
       assert(valid_texture_object(oldTex));
       (void) valid_texture_object; /* silence warning in release builds */
 
-      mtx_lock(&oldTex->Mutex);
-      assert(oldTex->RefCount > 0);
-      oldTex->RefCount--;
+      newRefCount = p_atomic_dec_return(&oldTex->RefCount);
+      assert(newRefCount >= 0);
 
-      deleteFlag = (oldTex->RefCount == 0);
-      mtx_unlock(&oldTex->Mutex);
-
-      if (deleteFlag) {
+      if (newRefCount == 0) {
          /* Passing in the context drastically changes the driver code for
           * framebuffer deletion.
           */
@@ -577,18 +573,16 @@ _mesa_reference_texobj_(struct gl_texture_object **ptr,
    if (tex) {
       /* reference new texture */
       assert(valid_texture_object(tex));
-      mtx_lock(&tex->Mutex);
-      if (tex->RefCount == 0) {
+      if (unlikely(p_atomic_inc_return(&tex->RefCount) == 1)) {
          /* this texture's being deleted (look just above) */
          /* Not sure this can every really happen.  Warn if it does. */
          _mesa_problem(NULL, "referencing deleted texture object");
+         p_atomic_dec(&tex->RefCount); // revert the refcount to previous value
          *ptr = NULL;
       }
       else {
-         tex->RefCount++;
          *ptr = tex;
       }
-      mtx_unlock(&tex->Mutex);
    }
 }
 
diff --git a/src/mesa/program/program.c b/src/mesa/program/program.c
index 6767216..a93778d 100644
--- a/src/mesa/program/program.c
+++ b/src/mesa/program/program.c
@@ -39,6 +39,7 @@
 #include "prog_parameter.h"
 #include "prog_instruction.h"
 #include "util/ralloc.h"
+#include "util/u_atomic.h"
 
 
 /**
@@ -306,32 +307,28 @@ _mesa_reference_program_(struct gl_context *ctx,
                          struct gl_program **ptr,
                          struct gl_program *prog)
 {
+   struct gl_program *const oldProg = *ptr;
+
 #ifndef NDEBUG
-   assert(ptr);
-   if (*ptr && prog) {
+   if (oldProg && prog) {
       /* sanity check */
-      if ((*ptr)->Target == GL_VERTEX_PROGRAM_ARB)
+      if (oldProg->Target == GL_VERTEX_PROGRAM_ARB)
          assert(prog->Target == GL_VERTEX_PROGRAM_ARB);
-      else if ((*ptr)->Target == GL_FRAGMENT_PROGRAM_ARB)
+      else if (oldProg->Target == GL_FRAGMENT_PROGRAM_ARB)
          assert(prog->Target == GL_FRAGMENT_PROGRAM_ARB ||
                 prog->Target == GL_FRAGMENT_PROGRAM_NV);
-      else if ((*ptr)->Target == GL_GEOMETRY_PROGRAM_NV)
+      else if (oldProg->Target == GL_GEOMETRY_PROGRAM_NV)
          assert(prog->Target == GL_GEOMETRY_PROGRAM_NV);
    }
 #endif
 
-   if (*ptr) {
-      GLboolean deleteFlag;
-      struct gl_program *oldProg = *ptr;
-
-      mtx_lock(&oldProg->Mutex);
-      assert(oldProg->RefCount > 0);
-      oldProg->RefCount--;
+   if (oldProg) {
+      GLint newRefCount;
 
-      deleteFlag = (oldProg->RefCount == 0);
-      mtx_unlock(&oldProg->Mutex);
+      newRefCount = p_atomic_dec_return(&oldProg->RefCount);
+      assert(newRefCount >= 0);
 
-      if (deleteFlag) {
+      if (newRefCount == 0) {
          assert(ctx);
          ctx->Driver.DeleteProgram(ctx, oldProg);
       }
@@ -341,9 +338,7 @@ _mesa_reference_program_(struct gl_context *ctx,
 
    assert(!*ptr);
    if (prog) {
-      mtx_lock(&prog->Mutex);
-      prog->RefCount++;
-      mtx_unlock(&prog->Mutex);
+      p_atomic_inc(&prog->RefCount);
    }
 
    *ptr = prog;


More information about the mesa-dev mailing list