st/mesa: implement "zombie" shaders list
authorBrian Paul <brianp@vmware.com>
Tue, 12 Mar 2019 22:01:56 +0000 (16:01 -0600)
committerBrian Paul <brianp@vmware.com>
Mon, 18 Mar 2019 02:07:22 +0000 (20:07 -0600)
As with the preceding patch for sampler views, this patch does
basically the same thing but for shaders.  However, reference counting
isn't needed here (instead of calling cso_delete_XXX_shader() we call
st_save_zombie_shader().

The Redway3D Watch is one app/demo that needs this change.  Otherwise,
the vmwgfx driver generates an error about trying to destroy a shader
ID that doesn't exist in the context.

Note that if PIPE_CAP_SHAREABLE_SHADERS = TRUE, then we can use/delete
any shader with any context and this mechanism is not used.

Tested with: google-chrome, google earth, Redway3D Watch/Turbine demos
and a few Linux games.

Reviewed-by: Roland Scheidegger <sroland@vmware.com>
Reviewed-by: Neha Bhende <bhenden@vmware.com>
Reviewed-by: Mathias Fröhlich <Mathias.Froehlich@web.de>
Reviewed-By: Jose Fonseca <jfonseca@vmware.com>
src/mesa/state_tracker/st_context.c
src/mesa/state_tracker/st_context.h
src/mesa/state_tracker/st_program.c

index c38f8e501879fe1bb7fa56a3057d35695a9a760a..de2264da5898afd5ca44666f4a2731d7d1b5022e 100644 (file)
@@ -293,6 +293,39 @@ st_save_zombie_sampler_view(struct st_context *st,
 }
 
 
+/*
+ * Since OpenGL shaders may be shared among contexts, we can wind up
+ * with variants of a shader created with different contexts.
+ * When we go to destroy a gallium shader, we want to free it with the
+ * same context that it was created with, unless the driver reports
+ * PIPE_CAP_SHAREABLE_SHADERS = TRUE.
+ */
+void
+st_save_zombie_shader(struct st_context *st,
+                      enum pipe_shader_type type,
+                      struct pipe_shader_state *shader)
+{
+   struct st_zombie_shader_node *entry;
+
+   /* we shouldn't be here if the driver supports shareable shaders */
+   assert(!st->has_shareable_shaders);
+
+   entry = MALLOC_STRUCT(st_zombie_shader_node);
+   if (!entry)
+      return;
+
+   entry->shader = shader;
+   entry->type = type;
+
+   /* We need a mutex since this function may be called from one thread
+    * while free_zombie_shaders() is called from another.
+    */
+   mtx_lock(&st->zombie_shaders.mutex);
+   LIST_ADDTAIL(&entry->node, &st->zombie_shaders.list.node);
+   mtx_unlock(&st->zombie_shaders.mutex);
+}
+
+
 /*
  * Free any zombie sampler views that may be attached to this context.
  */
@@ -323,6 +356,55 @@ free_zombie_sampler_views(struct st_context *st)
 }
 
 
+/*
+ * Free any zombie shaders that may be attached to this context.
+ */
+static void
+free_zombie_shaders(struct st_context *st)
+{
+   struct st_zombie_shader_node *entry, *next;
+
+   if (LIST_IS_EMPTY(&st->zombie_shaders.list.node)) {
+      return;
+   }
+
+   mtx_lock(&st->zombie_shaders.mutex);
+
+   LIST_FOR_EACH_ENTRY_SAFE(entry, next,
+                            &st->zombie_shaders.list.node, node) {
+      LIST_DEL(&entry->node);  // remove this entry from the list
+
+      switch (entry->type) {
+      case PIPE_SHADER_VERTEX:
+         cso_delete_vertex_shader(st->cso_context, entry->shader);
+         break;
+      case PIPE_SHADER_FRAGMENT:
+         cso_delete_fragment_shader(st->cso_context, entry->shader);
+         break;
+      case PIPE_SHADER_GEOMETRY:
+         cso_delete_geometry_shader(st->cso_context, entry->shader);
+         break;
+      case PIPE_SHADER_TESS_CTRL:
+         cso_delete_tessctrl_shader(st->cso_context, entry->shader);
+         break;
+      case PIPE_SHADER_TESS_EVAL:
+         cso_delete_tesseval_shader(st->cso_context, entry->shader);
+         break;
+      case PIPE_SHADER_COMPUTE:
+         cso_delete_compute_shader(st->cso_context, entry->shader);
+         break;
+      default:
+         unreachable("invalid shader type in free_zombie_shaders()");
+      }
+      free(entry);
+   }
+
+   assert(LIST_IS_EMPTY(&st->zombie_shaders.list.node));
+
+   mtx_unlock(&st->zombie_shaders.mutex);
+}
+
+
 /*
  * This function is called periodically to free any zombie objects
  * which are attached to this context.
@@ -331,6 +413,7 @@ void
 st_context_free_zombie_objects(struct st_context *st)
 {
    free_zombie_sampler_views(st);
+   free_zombie_shaders(st);
 }
 
 
@@ -643,6 +726,8 @@ st_create_context_priv(struct gl_context *ctx, struct pipe_context *pipe,
 
    LIST_INITHEAD(&st->zombie_sampler_views.list.node);
    mtx_init(&st->zombie_sampler_views.mutex, mtx_plain);
+   LIST_INITHEAD(&st->zombie_shaders.list.node);
+   mtx_init(&st->zombie_shaders.mutex, mtx_plain);
 
    return st;
 }
@@ -847,6 +932,7 @@ st_destroy_context(struct st_context *st)
    st_context_free_zombie_objects(st);
 
    mtx_destroy(&st->zombie_sampler_views.mutex);
+   mtx_destroy(&st->zombie_shaders.mutex);
 
    /* This must be called first so that glthread has a chance to finish */
    _mesa_glthread_destroy(ctx);
index 1106bb628a38c5f901d019e8bb70bb66b0311f2e..c87ff81f97344d833c423f855b1aa53db249b7fe 100644 (file)
@@ -106,6 +106,17 @@ struct st_zombie_sampler_view_node
 };
 
 
+/*
+ * Node for a linked list of dead shaders.
+ */
+struct st_zombie_shader_node
+{
+   void *shader;
+   enum pipe_shader_type type;
+   struct list_head node;
+};
+
+
 struct st_context
 {
    struct st_context_iface iface;
@@ -322,6 +333,12 @@ struct st_context
       struct st_zombie_sampler_view_node list;
       mtx_t mutex;
    } zombie_sampler_views;
+
+   struct {
+      struct st_zombie_shader_node list;
+      mtx_t mutex;
+   } zombie_shaders;
+
 };
 
 
@@ -354,6 +371,12 @@ extern void
 st_save_zombie_sampler_view(struct st_context *st,
                             struct pipe_sampler_view *view);
 
+extern void
+st_save_zombie_shader(struct st_context *st,
+                      enum pipe_shader_type type,
+                      struct pipe_shader_state *shader);
+
+
 void
 st_context_free_zombie_objects(struct st_context *st);
 
index 0df800a60e0841976aa3dab34a3f5248bba14cb2..9f6e492d6fb64498a6814b5ea84dc50c17c3121e 100644 (file)
@@ -229,9 +229,15 @@ delete_ir(struct pipe_shader_state *ir)
 static void
 delete_vp_variant(struct st_context *st, struct st_vp_variant *vpv)
 {
-   if (vpv->driver_shader) 
-      cso_delete_vertex_shader(st->cso_context, vpv->driver_shader);
-      
+   if (vpv->driver_shader) {
+      if (st->has_shareable_shaders || vpv->key.st == st) {
+         cso_delete_vertex_shader(st->cso_context, vpv->driver_shader);
+      } else {
+         st_save_zombie_shader(vpv->key.st, PIPE_SHADER_VERTEX,
+                               vpv->driver_shader);
+      }
+   }
+
    if (vpv->draw_shader)
       draw_delete_vertex_shader( st->draw, vpv->draw_shader );
 
@@ -271,8 +277,15 @@ st_release_vp_variants( struct st_context *st,
 static void
 delete_fp_variant(struct st_context *st, struct st_fp_variant *fpv)
 {
-   if (fpv->driver_shader) 
-      cso_delete_fragment_shader(st->cso_context, fpv->driver_shader);
+   if (fpv->driver_shader) {
+      if (st->has_shareable_shaders || fpv->key.st == st) {
+         cso_delete_fragment_shader(st->cso_context, fpv->driver_shader);
+      } else {
+         st_save_zombie_shader(fpv->key.st, PIPE_SHADER_FRAGMENT,
+                               fpv->driver_shader);
+      }
+   }
+
    free(fpv);
 }
 
@@ -306,21 +319,45 @@ delete_basic_variant(struct st_context *st, struct st_basic_variant *v,
                      GLenum target)
 {
    if (v->driver_shader) {
-      switch (target) {
-      case GL_TESS_CONTROL_PROGRAM_NV:
-         cso_delete_tessctrl_shader(st->cso_context, v->driver_shader);
-         break;
-      case GL_TESS_EVALUATION_PROGRAM_NV:
-         cso_delete_tesseval_shader(st->cso_context, v->driver_shader);
-         break;
-      case GL_GEOMETRY_PROGRAM_NV:
-         cso_delete_geometry_shader(st->cso_context, v->driver_shader);
-         break;
-      case GL_COMPUTE_PROGRAM_NV:
-         cso_delete_compute_shader(st->cso_context, v->driver_shader);
-         break;
-      default:
-         assert(!"this shouldn't occur");
+      if (st->has_shareable_shaders || v->key.st == st) {
+         /* The shader's context matches the calling context, or we
+          * don't care.
+          */
+         switch (target) {
+         case GL_TESS_CONTROL_PROGRAM_NV:
+            cso_delete_tessctrl_shader(st->cso_context, v->driver_shader);
+            break;
+         case GL_TESS_EVALUATION_PROGRAM_NV:
+            cso_delete_tesseval_shader(st->cso_context, v->driver_shader);
+            break;
+         case GL_GEOMETRY_PROGRAM_NV:
+            cso_delete_geometry_shader(st->cso_context, v->driver_shader);
+            break;
+         case GL_COMPUTE_PROGRAM_NV:
+            cso_delete_compute_shader(st->cso_context, v->driver_shader);
+            break;
+         default:
+            unreachable("bad shader type in delete_basic_variant");
+         }
+      } else {
+         /* We can't delete a shader with a context different from the one
+          * that created it.  Add it to the creating context's zombie list.
+          */
+         enum pipe_shader_type type;
+         switch (target) {
+         case GL_TESS_CONTROL_PROGRAM_NV:
+            type = PIPE_SHADER_TESS_CTRL;
+            break;
+         case GL_TESS_EVALUATION_PROGRAM_NV:
+            type = PIPE_SHADER_TESS_EVAL;
+            break;
+         case GL_GEOMETRY_PROGRAM_NV:
+            type = PIPE_SHADER_GEOMETRY;
+            break;
+         default:
+            unreachable("");
+         }
+         st_save_zombie_shader(v->key.st, type, v->driver_shader);
       }
    }