[Mesa-dev] [PATCH] st/mesa: implement query pause/resume for meta operations

Brian Paul brianp at vmware.com
Mon Aug 3 15:53:01 PDT 2015


glClear, blitting, mipmap generation, etc. implemented with quad drawing
should not effect queries like occlusion count.  To accomplish that, this
patch implements a simple pause/resume mechanism for active queries.

We now keep a list of active queries.  When we "pause" queries we actually
end them.  When we resume we get the query result and save it, then re-begin
the query.  When the user requests the query result we add the pre-paused
value.

As mentioned in the code, this is not a perfect/ideal solution but it fixes
Piglit's occlusion_query_meta_no_fragments and occlusion_query_meta_save
tests.  In practice, it's pretty unusual to do meta operations between
glBeginQuery/EndQuery() pairs so these are corner cases.
---
 src/mesa/state_tracker/st_cb_blit.c     |   7 ++
 src/mesa/state_tracker/st_cb_clear.c    |   5 +
 src/mesa/state_tracker/st_cb_queryobj.c | 199 ++++++++++++++++++++++++++++++++
 src/mesa/state_tracker/st_cb_queryobj.h |  10 ++
 src/mesa/state_tracker/st_context.c     |   3 +
 src/mesa/state_tracker/st_context.h     |  11 +-
 src/mesa/state_tracker/st_gen_mipmap.c  |   5 +
 7 files changed, 239 insertions(+), 1 deletion(-)

diff --git a/src/mesa/state_tracker/st_cb_blit.c b/src/mesa/state_tracker/st_cb_blit.c
index 1396906..77fd908 100644
--- a/src/mesa/state_tracker/st_cb_blit.c
+++ b/src/mesa/state_tracker/st_cb_blit.c
@@ -39,6 +39,7 @@
 #include "st_cb_bitmap.h"
 #include "st_cb_blit.h"
 #include "st_cb_fbo.h"
+#include "st_cb_queryobj.h"
 #include "st_manager.h"
 
 #include "util/u_format.h"
@@ -193,6 +194,8 @@ st_BlitFramebuffer(struct gl_context *ctx,
    blit.filter = pFilter;
    blit.render_condition_enable = TRUE;
 
+   st_pause_queries(st);
+
    if (mask & GL_COLOR_BUFFER_BIT) {
       struct gl_renderbuffer_attachment *srcAtt =
          &readFB->Attachment[readFB->_ColorReadBufferIndex];
@@ -204,6 +207,7 @@ st_BlitFramebuffer(struct gl_context *ctx,
          GLuint i;
 
          if (!srcObj || !srcObj->pt) {
+            st_resume_queries(st);
             return;
          }
 
@@ -239,6 +243,7 @@ st_BlitFramebuffer(struct gl_context *ctx,
          GLuint i;
 
          if (!srcRb || !srcRb->surface) {
+            st_resume_queries(st);
             return;
          }
 
@@ -345,6 +350,8 @@ st_BlitFramebuffer(struct gl_context *ctx,
          }
       }
    }
+
+   st_resume_queries(st);
 }
 
 
diff --git a/src/mesa/state_tracker/st_cb_clear.c b/src/mesa/state_tracker/st_cb_clear.c
index 137fac8..fae28ee 100644
--- a/src/mesa/state_tracker/st_cb_clear.c
+++ b/src/mesa/state_tracker/st_cb_clear.c
@@ -43,6 +43,7 @@
 #include "st_atom.h"
 #include "st_cb_clear.h"
 #include "st_cb_fbo.h"
+#include "st_cb_queryobj.h"
 #include "st_format.h"
 #include "st_program.h"
 
@@ -255,6 +256,8 @@ clear_with_quad(struct gl_context *ctx, unsigned clear_buffers)
 	  x1, y1);
    */
 
+   st_pause_queries(st);
+
    cso_save_blend(st->cso_context);
    cso_save_stencil_ref(st->cso_context);
    cso_save_depth_stencil_alpha(st->cso_context);
@@ -381,6 +384,8 @@ clear_with_quad(struct gl_context *ctx, unsigned clear_buffers)
    cso_restore_vertex_elements(st->cso_context);
    cso_restore_aux_vertex_buffer_slot(st->cso_context);
    cso_restore_stream_outputs(st->cso_context);
+
+   st_resume_queries(st);
 }
 
 
diff --git a/src/mesa/state_tracker/st_cb_queryobj.c b/src/mesa/state_tracker/st_cb_queryobj.c
index 71222e8..080e18d 100644
--- a/src/mesa/state_tracker/st_cb_queryobj.c
+++ b/src/mesa/state_tracker/st_cb_queryobj.c
@@ -33,6 +33,8 @@
  */
 
 
+#include "util/u_debug.h"
+
 #include "main/imports.h"
 #include "main/context.h"
 
@@ -80,6 +82,51 @@ st_DeleteQuery(struct gl_context *ctx, struct gl_query_object *q)
 }
 
 
+/**
+ * Add a query to the active list.
+ */
+static void
+save_active_query(struct st_context *st, struct st_query_object *stq)
+{
+   /* We don't care about pausing/resuming time queries */
+   if (stq->base.Target != GL_TIME_ELAPSED &&
+       stq->base.Target != GL_TIMESTAMP) {
+      struct st_query_list_node *node = CALLOC_STRUCT(st_query_list_node);
+
+      if (node) {
+         node->query = stq;
+         insert_at_tail(&st->active_queries.list, &node->list);
+      }
+   }
+}
+
+
+/**
+ * Remove a query from the active list.
+ */
+static void
+remove_active_query(struct st_context *st, struct st_query_object *stq)
+{
+   /* We don't care about pausing/resuming time queries */
+   if (stq->base.Target != GL_TIME_ELAPSED &&
+       stq->base.Target != GL_TIMESTAMP) {
+      struct simple_node *n;
+
+      foreach (n, &st->active_queries.list) {
+         struct st_query_list_node *node = (struct st_query_list_node *) n;
+
+         if (node->query == stq) {
+            /* found it, remove it */
+            remove_from_list(&node->list);
+            free(node);
+            return;
+         }
+      }
+      assert(!"query not found in remove_active_query()");
+   }
+}
+
+
 static void
 st_BeginQuery(struct gl_context *ctx, struct gl_query_object *q)
 {
@@ -163,6 +210,8 @@ st_BeginQuery(struct gl_context *ctx, struct gl_query_object *q)
       }
    }
    assert(stq->type == type);
+
+   save_active_query(st_context(ctx), stq);
 }
 
 
@@ -183,6 +232,68 @@ st_EndQuery(struct gl_context *ctx, struct gl_query_object *q)
 
    if (stq->pq)
       pipe->end_query(pipe, stq->pq);
+
+   remove_active_query(st_context(ctx), stq);
+}
+
+
+/**
+ * Basically compute orig += new for query results.
+ */
+static void
+combine_query_results(union pipe_query_result *orig,
+                      const union pipe_query_result *new,
+                      unsigned query_target)
+{
+   switch (query_target) {
+   case PIPE_QUERY_OCCLUSION_PREDICATE:
+   case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
+   case PIPE_QUERY_GPU_FINISHED:
+      orig->b |= new->b;
+      break;
+   case PIPE_QUERY_OCCLUSION_COUNTER:
+   case PIPE_QUERY_TIMESTAMP:
+   case PIPE_QUERY_TIME_ELAPSED:
+   case PIPE_QUERY_PRIMITIVES_GENERATED:
+   case PIPE_QUERY_PRIMITIVES_EMITTED:
+      orig->u64 += new->u64;
+      break;
+   case PIPE_QUERY_SO_STATISTICS:
+      orig->so_statistics.num_primitives_written
+         += new->so_statistics.num_primitives_written;
+      orig->so_statistics.primitives_storage_needed
+         += new->so_statistics.primitives_storage_needed;
+      break;
+   case PIPE_QUERY_TIMESTAMP_DISJOINT:
+      /* XXX is this right? */
+      orig->timestamp_disjoint.frequency = new->timestamp_disjoint.frequency;
+      orig->timestamp_disjoint.disjoint = TRUE;
+      break;
+   case PIPE_QUERY_PIPELINE_STATISTICS:
+      orig->pipeline_statistics.ia_vertices
+         += new->pipeline_statistics.ia_vertices;
+      orig->pipeline_statistics.vs_invocations
+         += new->pipeline_statistics.vs_invocations;
+      orig->pipeline_statistics.hs_invocations
+         += new->pipeline_statistics.hs_invocations;
+      orig->pipeline_statistics.ds_invocations
+         += new->pipeline_statistics.ds_invocations;
+      orig->pipeline_statistics.gs_invocations
+         += new->pipeline_statistics.gs_invocations;
+      orig->pipeline_statistics.gs_primitives
+         += new->pipeline_statistics.gs_primitives;
+      orig->pipeline_statistics.ps_invocations
+         += new->pipeline_statistics.ps_invocations;
+      orig->pipeline_statistics.cs_invocations
+         += new->pipeline_statistics.cs_invocations;
+      orig->pipeline_statistics.c_invocations
+         += new->pipeline_statistics.c_invocations;
+      orig->pipeline_statistics.c_primitives
+         += new->pipeline_statistics.c_primitives;
+      break;
+   default:
+      assert(!"Unexpected query type in combine_query_results()");
+   }
 }
 
 
@@ -203,6 +314,9 @@ get_query_result(struct pipe_context *pipe,
    if (!pipe->get_query_result(pipe, stq->pq, wait, &data))
       return FALSE;
 
+   /* accumulate any results from being paused with this result */
+   combine_query_results(&data, &stq->pre_paused_result, stq->type);
+
    switch (stq->base.Target) {
    case GL_VERTICES_SUBMITTED_ARB:
       stq->base.Result = data.pipeline_statistics.ia_vertices;
@@ -305,3 +419,88 @@ void st_init_query_functions(struct dd_function_table *functions)
    functions->CheckQuery = st_CheckQuery;
    functions->GetTimestamp = st_GetTimestamp;
 }
+
+
+/**
+ * This is used to temporarily suspend any active queries (occlusion, xfb,
+ * etc) when we're about to do a "meta" operation, such as doing a scissored
+ * glClear with polygon drawing.
+ * We basically implement the pause/resume by stopping/re-beginning the query
+ * and saving the pre-pause query result.  The pre-pause result is added to
+ * the later query when its result is returned.
+ *
+ * Note: this scheme is not perfect:
+ * 1. It doesn't work correctly with predicated rendering.  But it would be
+ *    pretty unusual to do a glClear/Blit/GenerateMipmap between the
+ *    glBeginQuery/EndQuery() pair for the occlusion test phase.
+ * 2. If we implement GL_ARB_query_buffer_object, we'd have to stuff the
+ *    accumulated query result into the buffer, negating the performance
+ *    benefit of that feature.  Again, it's quite unusual to have meta
+ *    operations inside glBeginQuery/EndQuery().
+ */
+void
+st_pause_queries(struct st_context *st)
+{
+   struct simple_node *n;
+
+   assert(!st->queries_paused);
+   st->queries_paused = TRUE;
+
+   /* Loop over active queries */
+   foreach (n, &st->active_queries.list) {
+      struct st_query_list_node *qln = (struct st_query_list_node *) n;
+
+      assert(qln->query->pq);
+      if (!qln->query->pq)
+         continue;
+
+      /* end the query */
+      st->pipe->end_query(st->pipe, qln->query->pq);
+
+      /* Note: we don't get the query result here.  We postpone that until
+       * st_resume_queries() to give the graphics pipe a little more time
+       * to possibly finish the query to avoid blocking.
+       */
+   }
+}
+
+
+/**
+ * Counterpart to st_pause_queries(), called after finishing meta operations.
+ * We "resume" the active queries by beginning new queries.
+ */
+void
+st_resume_queries(struct st_context *st)
+{
+   struct simple_node *n;
+
+   assert(st->queries_paused);
+   st->queries_paused = FALSE;
+
+   /* Loop over active queries */
+   foreach (n, &st->active_queries.list) {
+      struct st_query_list_node *qln = (struct st_query_list_node *) n;
+      union pipe_query_result result;
+
+      assert(qln->query->pq);
+      if (!qln->query->pq)
+         continue;
+
+      /* get the result for the query which we ended in st_pause_queries() */
+      if (!st->pipe->get_query_result(st->pipe, qln->query->pq,
+                                      TRUE, &result)) {
+         debug_printf("get query result failed\n");
+         continue;
+      }
+
+      /* Accumulate this result with previously paused result.
+       * Note that we could pause/resume a query many times so we have to
+       * accumulate here.
+       */
+      combine_query_results(&qln->query->pre_paused_result, &result,
+                            qln->query->type);
+
+      /* re-begin the query */
+      st->pipe->begin_query(st->pipe, qln->query->pq);
+   }
+}
diff --git a/src/mesa/state_tracker/st_cb_queryobj.h b/src/mesa/state_tracker/st_cb_queryobj.h
index 2406321..6de3e9e 100644
--- a/src/mesa/state_tracker/st_cb_queryobj.h
+++ b/src/mesa/state_tracker/st_cb_queryobj.h
@@ -42,6 +42,11 @@ struct st_query_object
    /* Begin TIMESTAMP query for GL_TIME_ELAPSED_EXT queries */
    struct pipe_query *pq_begin;
 
+   /* Used for pausing/resuming queries.  When a query result is requested,
+    * we have to add this to the returned result.
+    */
+   union pipe_query_result pre_paused_result;
+
    unsigned type;  /**< PIPE_QUERY_x */
 };
 
@@ -59,5 +64,10 @@ st_query_object(struct gl_query_object *q)
 extern void
 st_init_query_functions(struct dd_function_table *functions);
 
+extern void
+st_pause_queries(struct st_context *st);
+
+extern void
+st_resume_queries(struct st_context *st);
 
 #endif
diff --git a/src/mesa/state_tracker/st_context.c b/src/mesa/state_tracker/st_context.c
index 72c23ca..224bd0d 100644
--- a/src/mesa/state_tracker/st_context.c
+++ b/src/mesa/state_tracker/st_context.c
@@ -305,6 +305,9 @@ st_create_context_priv( struct gl_context *ctx, struct pipe_context *pipe,
    _mesa_initialize_dispatch_tables(ctx);
    _mesa_initialize_vbo_vtxfmt(ctx);
 
+   /* Init query object list */
+   make_empty_list(&st->active_queries.list);
+
    return st;
 }
 
diff --git a/src/mesa/state_tracker/st_context.h b/src/mesa/state_tracker/st_context.h
index 81d5480..ce9815c 100644
--- a/src/mesa/state_tracker/st_context.h
+++ b/src/mesa/state_tracker/st_context.h
@@ -28,6 +28,7 @@
 #ifndef ST_CONTEXT_H
 #define ST_CONTEXT_H
 
+#include "util/simple_list.h"
 #include "main/mtypes.h"
 #include "pipe/p_state.h"
 #include "state_tracker/st_api.h"
@@ -74,7 +75,11 @@ struct st_tracked_state {
    void (*update)( struct st_context *st );
 };
 
-
+struct st_query_list_node
+{
+   struct simple_node list;
+   struct st_query_object *query;
+};
 
 struct st_context
 {
@@ -215,6 +220,10 @@ struct st_context
    int32_t read_stamp;
 
    struct st_config_options options;
+
+   /** List of active queries */
+   struct st_query_list_node active_queries;
+   boolean queries_paused;
 };
 
 
diff --git a/src/mesa/state_tracker/st_gen_mipmap.c b/src/mesa/state_tracker/st_gen_mipmap.c
index 26e1c21..956794d 100644
--- a/src/mesa/state_tracker/st_gen_mipmap.c
+++ b/src/mesa/state_tracker/st_gen_mipmap.c
@@ -40,6 +40,7 @@
 #include "st_context.h"
 #include "st_texture.h"
 #include "st_gen_mipmap.h"
+#include "st_cb_queryobj.h"
 #include "st_cb_texture.h"
 
 
@@ -145,6 +146,8 @@ st_generate_mipmap(struct gl_context *ctx, GLenum target,
       last_layer = util_max_layer(pt, baseLevel);
    }
 
+   st_pause_queries(st);
+
    /* Try to generate the mipmap by rendering/texturing.  If that fails,
     * use the software fallback.
     */
@@ -153,6 +156,8 @@ st_generate_mipmap(struct gl_context *ctx, GLenum target,
       _mesa_generate_mipmap(ctx, target, texObj);
    }
 
+   st_resume_queries(st);
+
    /* Fill in the Mesa gl_texture_image fields */
    for (dstLevel = baseLevel + 1; dstLevel <= lastLevel; dstLevel++) {
       const uint srcLevel = dstLevel - 1;
-- 
1.9.1



More information about the mesa-dev mailing list