[Mesa-dev] [PATCH v2 11/26] gallium/u_threaded: avoid syncs for get_query_result

Marek Olšák maraeo at gmail.com
Fri Nov 10 00:51:38 UTC 2017


This commit makes most query piglit tests crash. I've not investigated further.

Marek

On Mon, Nov 6, 2017 at 11:23 AM, Nicolai Hähnle <nhaehnle at gmail.com> wrote:
> From: Nicolai Hähnle <nicolai.haehnle at amd.com>
>
> Queries should still get marked as flushed when flushes are executed
> asynchronously in the driver thread.
>
> To this end, the management of the unflushed_queries list is moved into
> the driver thread.
>
> Reviewed-by: Marek Olšák <marek.olsak at amd.com>
> ---
>  src/gallium/auxiliary/util/u_threaded_context.c | 65 ++++++++++++++++++-------
>  1 file changed, 48 insertions(+), 17 deletions(-)
>
> diff --git a/src/gallium/auxiliary/util/u_threaded_context.c b/src/gallium/auxiliary/util/u_threaded_context.c
> index 0bb645e8522..4908ea8a7ba 100644
> --- a/src/gallium/auxiliary/util/u_threaded_context.c
> +++ b/src/gallium/auxiliary/util/u_threaded_context.c
> @@ -321,91 +321,107 @@ tc_create_batch_query(struct pipe_context *_pipe, unsigned num_queries,
>  {
>     struct threaded_context *tc = threaded_context(_pipe);
>     struct pipe_context *pipe = tc->pipe;
>
>     return pipe->create_batch_query(pipe, num_queries, query_types);
>  }
>
>  static void
>  tc_call_destroy_query(struct pipe_context *pipe, union tc_payload *payload)
>  {
> +   struct threaded_query *tq = threaded_query(payload->query);
> +
> +   if (tq->head_unflushed.next)
> +      LIST_DEL(&tq->head_unflushed);
> +
>     pipe->destroy_query(pipe, payload->query);
>  }
>
>  static void
>  tc_destroy_query(struct pipe_context *_pipe, struct pipe_query *query)
>  {
>     struct threaded_context *tc = threaded_context(_pipe);
> -   struct threaded_query *tq = threaded_query(query);
> -
> -   if (tq->head_unflushed.next)
> -      LIST_DEL(&tq->head_unflushed);
>
>     tc_add_small_call(tc, TC_CALL_destroy_query)->query = query;
>  }
>
>  static void
>  tc_call_begin_query(struct pipe_context *pipe, union tc_payload *payload)
>  {
>     pipe->begin_query(pipe, payload->query);
>  }
>
>  static boolean
>  tc_begin_query(struct pipe_context *_pipe, struct pipe_query *query)
>  {
>     struct threaded_context *tc = threaded_context(_pipe);
>     union tc_payload *payload = tc_add_small_call(tc, TC_CALL_begin_query);
>
>     payload->query = query;
>     return true; /* we don't care about the return value for this call */
>  }
>
> +struct tc_end_query_payload {
> +   struct threaded_context *tc;
> +   struct pipe_query *query;
> +};
> +
>  static void
>  tc_call_end_query(struct pipe_context *pipe, union tc_payload *payload)
>  {
> -   pipe->end_query(pipe, payload->query);
> +   struct tc_end_query_payload *p = (struct tc_end_query_payload *)payload;
> +   struct threaded_query *tq = threaded_query(p->query);
> +
> +   if (!tq->head_unflushed.next)
> +      LIST_ADD(&tq->head_unflushed, &p->tc->unflushed_queries);
> +
> +   pipe->end_query(pipe, p->query);
>  }
>
>  static bool
>  tc_end_query(struct pipe_context *_pipe, struct pipe_query *query)
>  {
>     struct threaded_context *tc = threaded_context(_pipe);
>     struct threaded_query *tq = threaded_query(query);
> -   union tc_payload *payload = tc_add_small_call(tc, TC_CALL_end_query);
> +   struct tc_end_query_payload *payload =
> +      tc_add_struct_typed_call(tc, TC_CALL_end_query, tc_end_query_payload);
> +
> +   tc_add_small_call(tc, TC_CALL_end_query);
>
> +   payload->tc = tc;
>     payload->query = query;
>
>     tq->flushed = false;
> -   if (!tq->head_unflushed.next)
> -      LIST_ADD(&tq->head_unflushed, &tc->unflushed_queries);
>
>     return true; /* we don't care about the return value for this call */
>  }
>
>  static boolean
>  tc_get_query_result(struct pipe_context *_pipe,
>                      struct pipe_query *query, boolean wait,
>                      union pipe_query_result *result)
>  {
>     struct threaded_context *tc = threaded_context(_pipe);
>     struct threaded_query *tq = threaded_query(query);
>     struct pipe_context *pipe = tc->pipe;
>
>     if (!tq->flushed)
>        tc_sync_msg(tc, wait ? "wait" : "nowait");
>
>     bool success = pipe->get_query_result(pipe, query, wait, result);
>
>     if (success) {
>        tq->flushed = true;
> -      if (tq->head_unflushed.next)
> +      if (tq->head_unflushed.next) {
> +         /* This is safe because it can only happen after we sync'd. */
>           LIST_DEL(&tq->head_unflushed);
> +      }
>     }
>     return success;
>  }
>
>  struct tc_query_result_resource {
>     struct pipe_query *query;
>     boolean wait;
>     enum pipe_query_value_type result_type;
>     int index;
>     struct pipe_resource *resource;
> @@ -1806,42 +1822,60 @@ tc_create_video_buffer(struct pipe_context *_pipe,
>     unreachable("Threaded context should not be enabled for video APIs");
>     return NULL;
>  }
>
>
>  /********************************************************************
>   * draw, launch, clear, blit, copy, flush
>   */
>
>  struct tc_flush_payload {
> +   struct threaded_context *tc;
>     struct pipe_fence_handle *fence;
>     unsigned flags;
>  };
>
>  static void
> +tc_flush_queries(struct threaded_context *tc)
> +{
> +   struct threaded_query *tq, *tmp;
> +   LIST_FOR_EACH_ENTRY_SAFE(tq, tmp, &tc->unflushed_queries, head_unflushed) {
> +      LIST_DEL(&tq->head_unflushed);
> +
> +      /* Memory release semantics: due to a possible race with
> +       * tc_get_query_result, we must ensure that the linked list changes
> +       * are visible before setting tq->flushed.
> +       */
> +      p_atomic_set(&tq->flushed, true);
> +   }
> +}
> +
> +static void
>  tc_call_flush(struct pipe_context *pipe, union tc_payload *payload)
>  {
>     struct tc_flush_payload *p = (struct tc_flush_payload *)payload;
>     struct pipe_screen *screen = pipe->screen;
>
>     pipe->flush(pipe, p->fence ? &p->fence : NULL, p->flags);
>     screen->fence_reference(screen, &p->fence, NULL);
> +
> +   if (!(p->flags & PIPE_FLUSH_DEFERRED))
> +      tc_flush_queries(p->tc);
>  }
>
>  static void
>  tc_flush(struct pipe_context *_pipe, struct pipe_fence_handle **fence,
>           unsigned flags)
>  {
>     struct threaded_context *tc = threaded_context(_pipe);
>     struct pipe_context *pipe = tc->pipe;
>     struct pipe_screen *screen = pipe->screen;
> -   struct threaded_query *tq, *tmp;
>     bool async = flags & PIPE_FLUSH_DEFERRED;
>
>     if (flags & PIPE_FLUSH_ASYNC) {
>        struct tc_batch *last = &tc->batch_slots[tc->last];
>
>        /* Prefer to do the flush in the driver thread, but avoid the inter-thread
>         * communication overhead if the driver thread is currently idle and the
>         * caller is going to wait for the fence immediately anyway.
>         */
>        if (!(util_queue_fence_is_signalled(&last->fence) &&
> @@ -1863,38 +1897,35 @@ tc_flush(struct pipe_context *_pipe, struct pipe_fence_handle **fence,
>              next->token->tc = tc;
>           }
>
>           screen->fence_reference(screen, fence, tc->create_fence(pipe, token));
>           if (!*fence)
>              goto out_of_memory;
>        }
>
>        struct tc_flush_payload *p =
>           tc_add_struct_typed_call(tc, TC_CALL_flush, tc_flush_payload);
> +      p->tc = tc;
>        p->fence = fence ? *fence : NULL;
>        p->flags = flags | TC_FLUSH_ASYNC;
>
>        if (!(flags & PIPE_FLUSH_DEFERRED))
>           tc_batch_flush(tc);
>        return;
>     }
>
>  out_of_memory:
> -   if (!(flags & PIPE_FLUSH_DEFERRED)) {
> -      LIST_FOR_EACH_ENTRY_SAFE(tq, tmp, &tc->unflushed_queries, head_unflushed) {
> -         tq->flushed = true;
> -         LIST_DEL(&tq->head_unflushed);
> -      }
> -   }
> -
>     tc_sync_msg(tc, flags & PIPE_FLUSH_END_OF_FRAME ? "end of frame" :
>                     flags & PIPE_FLUSH_DEFERRED ? "deferred fence" : "normal");
> +
> +   if (!(flags & PIPE_FLUSH_DEFERRED))
> +      tc_flush_queries(tc);
>     pipe->flush(pipe, fence, flags);
>  }
>
>  /* This is actually variable-sized, because indirect isn't allocated if it's
>   * not needed. */
>  struct tc_full_draw_info {
>     struct pipe_draw_info draw;
>     struct pipe_draw_indirect_info indirect;
>  };
>
> --
> 2.11.0
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/mesa-dev


More information about the mesa-dev mailing list