draw: Only run prepare when state, prim and opt changes
authorJakob Bornecrantz <wallbraker@gmail.com>
Mon, 24 Jan 2011 01:11:59 +0000 (02:11 +0100)
committerStéphane Marchesin <marcheu@chromium.org>
Thu, 26 Jan 2012 09:38:46 +0000 (01:38 -0800)
In bad applications like ipers which does a lot of draw calls with
no state changes this helps to greatly reduce time spent in prepare.
In ipers around 7% of CPU was spent in various prepare functions,
after this commit no prepare function show on the profile.

This commit also has the added benefit of now grouping all pipelined
drawing into a single draw call if the driver uses vbuf_render.

Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Tested-by: Stéphane Marchesin <marcheu@chromium.org>
src/gallium/auxiliary/draw/draw_context.c
src/gallium/auxiliary/draw/draw_private.h
src/gallium/auxiliary/draw/draw_pt.c
src/gallium/auxiliary/draw/draw_pt.h
src/gallium/auxiliary/draw/draw_pt_vsplit.c

index 4ce444550849a4bd3b99a5e9e446eafda4f13726..3c0b1aa39c9efde4abd7e8cfbb29d0524c211605 100644 (file)
@@ -355,6 +355,10 @@ draw_set_vertex_elements(struct draw_context *draw,
 {
    assert(count <= PIPE_MAX_ATTRIBS);
 
+   /* We could improve this by only flushing the frontend and the fetch part
+    * of the middle. This would avoid recalculating the emit keys.*/
+   draw_do_flush( draw, DRAW_FLUSH_STATE_CHANGE );
+
    memcpy(draw->pt.vertex_element, elements, count * sizeof(elements[0]));
    draw->pt.nr_vertex_elements = count;
 }
@@ -654,6 +658,8 @@ void draw_do_flush( struct draw_context *draw, unsigned flags )
 
       draw_pipeline_flush( draw, flags );
 
+      draw_pt_flush( draw, flags );
+
       draw->flushing = FALSE;
    }
 }
index 1a0286de891015e66d1d5afcd18db58400a2740a..c3eca973e92fcfd6b6735c9b2dcc1a8922f81ace 100644 (file)
@@ -63,6 +63,7 @@ struct draw_stage;
 struct vbuf_render;
 struct tgsi_exec_machine;
 struct tgsi_sampler;
+struct draw_pt_front_end;
 
 
 /**
@@ -137,6 +138,12 @@ struct draw_context
    /* Support prototype passthrough path:
     */
    struct {
+      /* Current active frontend */
+      struct draw_pt_front_end *frontend;
+      unsigned prim;
+      unsigned opt;
+      unsigned eltSize; /* saved eltSize for flushing */
+
       struct {
          struct draw_pt_middle_end *fetch_emit;
          struct draw_pt_middle_end *fetch_shade_emit;
@@ -391,6 +398,7 @@ void draw_remove_extra_vertex_attribs(struct draw_context *draw);
 boolean draw_pt_init( struct draw_context *draw );
 void draw_pt_destroy( struct draw_context *draw );
 void draw_pt_reset_vertex_ids( struct draw_context *draw );
+void draw_pt_flush( struct draw_context *draw, unsigned flags );
 
 
 /*******************************************************************************
index 9a017fd0567954fb6992c36f0a19a3712e591330..025d53989e48c2520a92fdf0ba3385b75d4bf860 100644 (file)
@@ -52,7 +52,7 @@ DEBUG_GET_ONCE_BOOL_OPTION(draw_no_fse, "DRAW_NO_FSE", FALSE)
  *     - backend  -- the vbuf_render provided by the driver.
  */
 static boolean
-draw_pt_arrays(struct draw_context *draw, 
+draw_pt_arrays(struct draw_context *draw,
                unsigned prim,
                unsigned start, 
                unsigned count)
@@ -106,17 +106,56 @@ draw_pt_arrays(struct draw_context *draw,
          middle = draw->pt.middle.general;
    }
 
-   frontend = draw->pt.front.vsplit;
+   frontend = draw->pt.frontend;
+
+   if (frontend ) {
+      if (draw->pt.prim != prim || draw->pt.opt != opt) {
+         /* In certain conditions switching primitives requires us to flush
+          * and validate the different stages. One example is when smooth
+          * lines are active but first drawn with triangles and then with
+          * lines.
+          */
+         draw_do_flush( draw, DRAW_FLUSH_STATE_CHANGE );
+         frontend = NULL;
+      } else if (draw->pt.eltSize != draw->pt.user.eltSize) {
+         /* Flush draw state if eltSize changed.
+          * This could be improved so only the frontend is flushed since it
+          * converts all indices to ushorts and the fetch part of the middle
+          * always perpares both linear and indexed.
+          */
+         frontend->flush( frontend, DRAW_FLUSH_STATE_CHANGE );
+         frontend = NULL;
+      }
+   }
 
-   frontend->prepare( frontend, prim, middle, opt );
+   if (!frontend) {
+      frontend = draw->pt.front.vsplit;
 
-   frontend->run(frontend, start, count);
+      frontend->prepare( frontend, prim, middle, opt );
 
-   frontend->finish( frontend );
+      draw->pt.frontend = frontend;
+      draw->pt.eltSize = draw->pt.user.eltSize;
+      draw->pt.prim = prim;
+      draw->pt.opt = opt;
+   }
+
+   frontend->run( frontend, start, count );
 
    return TRUE;
 }
 
+void draw_pt_flush( struct draw_context *draw, unsigned flags )
+{
+   if (draw->pt.frontend) {
+      draw->pt.frontend->flush( draw->pt.frontend, flags );
+
+      /* don't prepare if we only are flushing the backend */
+      if (!(flags & DRAW_FLUSH_BACKEND))
+         draw->pt.frontend = NULL;
+   }
+}
+
+
 
 boolean draw_pt_init( struct draw_context *draw )
 {
index 9a45845521af62e6ff4aee129466bbfbb3319249..2c2efdc1c599f30a2e56e35d484684e290afca6a 100644 (file)
@@ -73,7 +73,7 @@ struct draw_pt_front_end {
                 unsigned start,
                 unsigned count );
 
-   void (*finish)( struct draw_pt_front_end * );
+   void (*flush)( struct draw_pt_front_end *, unsigned flags );
    void (*destroy)( struct draw_pt_front_end * );
 };
 
index c19dcd9e1f7fe44d54e1f0de8942b60b9603c4e3..0fed057efbe7fa8ee1b86ae074443a09e1dba1af 100644 (file)
@@ -178,11 +178,14 @@ static void vsplit_prepare(struct draw_pt_front_end *frontend,
 }
 
 
-static void vsplit_finish(struct draw_pt_front_end *frontend)
+static void vsplit_flush(struct draw_pt_front_end *frontend, unsigned flags)
 {
    struct vsplit_frontend *vsplit = (struct vsplit_frontend *) frontend;
-   vsplit->middle->finish(vsplit->middle);
-   vsplit->middle = NULL;
+
+   if (!(flags & DRAW_FLUSH_BACKEND)) {
+      vsplit->middle->finish(vsplit->middle);
+      vsplit->middle = NULL;
+   }
 }
 
 
@@ -202,7 +205,7 @@ struct draw_pt_front_end *draw_pt_vsplit(struct draw_context *draw)
 
    vsplit->base.prepare = vsplit_prepare;
    vsplit->base.run     = NULL;
-   vsplit->base.finish  = vsplit_finish;
+   vsplit->base.flush   = vsplit_flush;
    vsplit->base.destroy = vsplit_destroy;
    vsplit->draw = draw;