On 7 December 2011 00:41, Kenneth Graunke <span dir="ltr">&lt;<a href="mailto:kenneth@whitecape.org">kenneth@whitecape.org</a>&gt;</span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="HOEnZb"><div class="h5">On 12/05/2011 09:40 AM, Paul Berry wrote:<br>
&gt; Prior to this patch, in the Gen4 and Gen5 GS, we used GRF 0 (called<br>
&gt; &quot;R0&quot; in the code) as a staging area to prepare the message header for<br>
&gt; the FF_SYNC and URB_WRITE messages.  This cleverly avoided an<br>
&gt; unnecessary MOV operation (since the initial value of GRF 0 contains<br>
&gt; data that needs to be included in the message header), but it made the<br>
&gt; code confusing, since GRF 0 could no longer be relied upon to contain<br>
&gt; its initial value once the GS started preparing its first message.<br>
&gt; This patch avoids confusion by using a separate register (&quot;header&quot;) as<br>
&gt; the staging area, at the cost of one MOV instruction.<br>
&gt;<br>
&gt; Worse yet, prior to this patch, the GS would completely overwrite the<br>
&gt; contents of GRF 0 with the writeback data it received from a completed<br>
&gt; FF_SYNC or URB_WRITE message.  It did this because DWORD 0 of the<br>
&gt; writeback data contains the new URB handle, and that neds to be<br>
&gt; included in DWORD 0 of the next URB_WRITE message header.  However,<br>
&gt; that caused the rest of the message header to be corrupted either with<br>
&gt; undefined data or zeros.  Astonishingly, this did not produce any<br>
&gt; known failures (probably by dumb luck).  However, it seems really<br>
&gt; dodgy--corrupting FFTID in particular seems likely to cause GPU hangs.<br>
&gt; This patch avoids the corruption by storing the writeback data in a<br>
&gt; temporary register and then copying just DWORD 0 to the header for the<br>
&gt; next message.  This costs one extra MOV instruction per message sent,<br>
&gt; except for the final message.<br>
&gt;<br>
&gt; Also, this patch moves the logic for overriding DWORD 2 of the header<br>
&gt; (which contains PrimType, PrimStart, PrimEnd, and some other data that<br>
&gt; we don&#39;t care about yet).  This logic is now in the function<br>
&gt; brw_gs_overwrite_header_dw2() rather than in brw_gs_emit_vue().  This<br>
&gt; saves one MOV instruction in brw_gs_quads() and brw_gs_quad_strip(),<br>
&gt; and paves the way for the Gen6 GS, which will need more complex logic<br>
&gt; to override DWORD 2 of the header.<br>
&gt;<br>
&gt; Finally, the function brw_gs_alloc_regs() contained a benign bug: it<br>
&gt; neglected to increment the register counter when allocating space for<br>
&gt; the &quot;temp&quot; register.  This turned out not to have any effect because<br>
&gt; the temp register wasn&#39;t used on Gen4 and Gen5, the only hardware<br>
&gt; models (so far) to require a GS program.  Now, all the registers<br>
&gt; allocated by brw_gs_alloc_regs() are actually used, and properly<br>
&gt; accounted for.<br>
&gt; ---<br>
&gt;  src/mesa/drivers/dri/i965/brw_gs.h      |    1 +<br>
&gt;  src/mesa/drivers/dri/i965/brw_gs_emit.c |  175 +++++++++++++++++++------------<br>
&gt;  2 files changed, 111 insertions(+), 65 deletions(-)<br>
<br>
</div></div>I&#39;m really glad to see the overwriting of g0 die in a fire.  That was<br>
just maddening.  Also, your comments are fantastic.<br>
<div class="im"><br>
&gt; diff --git a/src/mesa/drivers/dri/i965/brw_gs.h b/src/mesa/drivers/dri/i965/brw_gs.h<br>
&gt; index 157afa3..d71609f 100644<br>
&gt; --- a/src/mesa/drivers/dri/i965/brw_gs.h<br>
&gt; +++ b/src/mesa/drivers/dri/i965/brw_gs.h<br>
&gt; @@ -58,6 +58,7 @@ struct brw_gs_compile {<br>
&gt;     struct {<br>
&gt;        struct brw_reg R0;<br>
&gt;        struct brw_reg vertex[MAX_GS_VERTS];<br>
&gt; +      struct brw_reg header;<br>
&gt;        struct brw_reg temp;<br>
&gt;     } reg;<br>
&gt;  };<br>
&gt; diff --git a/src/mesa/drivers/dri/i965/brw_gs_emit.c b/src/mesa/drivers/dri/i965/brw_gs_emit.c<br>
&gt; index 8097fad..935e663 100644<br>
&gt; --- a/src/mesa/drivers/dri/i965/brw_gs_emit.c<br>
&gt; +++ b/src/mesa/drivers/dri/i965/brw_gs_emit.c<br>
&gt; @@ -58,35 +58,66 @@ static void brw_gs_alloc_regs( struct brw_gs_compile *c,<br>
&gt;        i += c-&gt;key.nr_regs;<br>
&gt;     }<br>
&gt;<br>
&gt; -   c-&gt;reg.temp = brw_vec8_grf(i, 0);<br>
&gt; +   c-&gt;reg.header = retype(brw_vec8_grf(i++, 0), BRW_REGISTER_TYPE_UD);<br>
&gt; +   c-&gt;reg.temp = retype(brw_vec8_grf(i++, 0), BRW_REGISTER_TYPE_UD);<br>
<br>
</div>Hmm.  I&#39;d originally been thinking of an MRF for the header directly,<br>
but I suppose in many places the code it&#39;s useful to be able to read<br>
from it, and on Gen4-5 we always have the implied MOV to the leading MRF<br>
anyway.  So this is probably the right thing to do; I&#39;m fine with it.<br>
<div><div class="h5"><br>
&gt;     c-&gt;prog_data.urb_read_length = c-&gt;key.nr_regs;<br>
&gt;     c-&gt;prog_data.total_grf = i;<br>
&gt;  }<br>
&gt;<br>
&gt;<br>
&gt; +/**<br>
&gt; + * Set up the initial value of c-&gt;reg.header register based on c-&gt;reg.R0.<br>
&gt; + *<br>
&gt; + * The following information is passed to the GS thread in R0, and needs to be<br>
&gt; + * included in the first URB_WRITE or FF_SYNC message sent by the GS:<br>
&gt; + *<br>
&gt; + * - DWORD 0 [31:0] handle info (Gen4 only)<br>
&gt; + * - DWORD 5 [7:0] FFTID<br>
&gt; + * - DWORD 6 [31:0] Debug info<br>
&gt; + * - DWORD 7 [31:0] Debug info<br>
&gt; + *<br>
&gt; + * This function sets up the above data by copying by copying the contents of<br>
&gt; + * R0 to the header register.<br>
&gt; + */<br>
&gt; +static void brw_gs_initialize_header(struct brw_gs_compile *c)<br>
&gt; +{<br>
&gt; +   struct brw_compile *p = &amp;c-&gt;func;<br>
&gt; +   brw_MOV(p, c-&gt;reg.header, c-&gt;reg.R0);<br>
&gt; +}<br>
&gt; +<br>
&gt; +/**<br>
&gt; + * Overwrite DWORD 2 of c-&gt;reg.header with the given immediate unsigned value.<br>
&gt; + *<br>
&gt; + * In URB_WRITE messages, DWORD 2 contains the fields PrimType, PrimStart,<br>
&gt; + * PrimEnd, Increment CL_INVOCATIONS, and SONumPrimsWritten, many of which we<br>
&gt; + * need to be able to update on a per-vertex basis.<br>
&gt; + */<br>
&gt; +static void brw_gs_overwrite_header_dw2(struct brw_gs_compile *c,<br>
&gt; +                                        unsigned dw2)<br>
&gt; +{<br>
&gt; +   struct brw_compile *p = &amp;c-&gt;func;<br>
&gt; +   brw_MOV(p, get_element_ud(c-&gt;reg.header, 2), brw_imm_ud(dw2));<br>
&gt; +}<br>
<br>
</div></div>I must admit I was skeptical about these 1 line functions, though<br>
brw_gs_overwrite_header_dw2 does seem lot easier to use than a raw<br>
brw_MOV, and gets a lot of use.  The comments are a real plus.<br>
<div><div class="h5"><br>
&gt; +/**<br>
&gt; + * Emit a vertex using the URB_WRITE message.  Use the contents of<br>
&gt; + * c-&gt;reg.header for the message header, and the registers starting at \c vert<br>
&gt; + * for the vertex data.<br>
&gt; + *<br>
&gt; + * If \c last is true, then this is the last vertex, so no further URB space<br>
&gt; + * should be allocated, and this message should end the thread.<br>
&gt; + *<br>
&gt; + * If \c last is false, then a new URB entry will be allocated, and its handle<br>
&gt; + * will be stored in DWORD 0 of c-&gt;reg.header for use in the next URB_WRITE<br>
&gt; + * message.<br>
&gt; + */<br>
&gt;  static void brw_gs_emit_vue(struct brw_gs_compile *c,<br>
&gt;                           struct brw_reg vert,<br>
&gt; -                         bool last,<br>
&gt; -                         GLuint header)<br>
&gt; +                         bool last)<br>
&gt;  {<br>
&gt;     struct brw_compile *p = &amp;c-&gt;func;<br>
&gt; -   struct intel_context *intel = &amp;c-&gt;func.brw-&gt;intel;<br>
&gt;     bool allocate = !last;<br>
&gt; -   struct brw_reg temp;<br>
&gt; -<br>
&gt; -   if (intel-&gt;gen &lt; 6)<br>
&gt; -      temp = c-&gt;reg.R0;<br>
&gt; -   else {<br>
&gt; -      temp = c-&gt;reg.temp;<br>
&gt; -      brw_MOV(p, retype(temp, BRW_REGISTER_TYPE_UD),<br>
&gt; -           retype(c-&gt;reg.R0, BRW_REGISTER_TYPE_UD));<br>
&gt; -   }<br>
&gt; -<br>
&gt; -   /* Overwrite PrimType and PrimStart in the message header, for<br>
&gt; -    * each vertex in turn:<br>
&gt; -    */<br>
&gt; -   brw_MOV(p, get_element_ud(temp, 2), brw_imm_ud(header));<br>
&gt;<br>
&gt;     /* Copy the vertex from vertn into m1..mN+1:<br>
&gt;      */<br>
&gt; @@ -99,9 +130,10 @@ static void brw_gs_emit_vue(struct brw_gs_compile *c,<br>
&gt;      * allocated each time.<br>
&gt;      */<br>
&gt;     brw_urb_WRITE(p,<br>
&gt; -              allocate ? temp : retype(brw_null_reg(), BRW_REGISTER_TYPE_UD),<br>
&gt; +              allocate ? c-&gt;reg.temp<br>
&gt; +                          : retype(brw_null_reg(), BRW_REGISTER_TYPE_UD),<br>
&gt;                0,<br>
&gt; -              temp,<br>
&gt; +              c-&gt;reg.header,<br>
&gt;                allocate,<br>
&gt;                1,             /* used */<br>
&gt;                c-&gt;key.nr_regs + 1, /* msg length */<br>
&gt; @@ -111,38 +143,37 @@ static void brw_gs_emit_vue(struct brw_gs_compile *c,<br>
&gt;                0,             /* urb offset */<br>
&gt;                BRW_URB_SWIZZLE_NONE);<br>
&gt;<br>
&gt; -   if (intel-&gt;gen &gt;= 6 &amp;&amp; allocate)<br>
&gt; -       brw_MOV(p, get_element_ud(c-&gt;reg.R0, 0), get_element_ud(temp, 0));<br>
&gt; +   if (allocate) {<br>
&gt; +      brw_MOV(p, get_element_ud(c-&gt;reg.header, 0),<br>
&gt; +              get_element_ud(c-&gt;reg.temp, 0));<br>
&gt; +   }<br>
<br>
</div></div>Hmm.  I was wondering if you could just use c-&gt;reg.header as the SEND<br>
destination.  I suppose you can&#39;t since you want to preserve everything<br>
except DW0.  So nevermind.<br>
<div><div class="h5"><br>
&gt;  }<br>
&gt;<br>
&gt; +/**<br>
&gt; + * Send an FF_SYNC message to ensure that all previously spawned GS threads<br>
&gt; + * have finished sending primitives down the pipeline, and to allocate a URB<br>
&gt; + * entry for the first output vertex.  Only needed when intel-&gt;needs_ff_sync<br>
&gt; + * is true.<br>
&gt; + *<br>
&gt; + * This function modifies c-&gt;reg.header: in DWORD 1, it stores num_prim (which<br>
&gt; + * is needed by the FF_SYNC message), and in DWORD 0, it stores the handle to<br>
&gt; + * the allocated URB entry (which will be needed by the URB_WRITE meesage that<br>
&gt; + * follows).<br>
&gt; + */<br>
&gt;  static void brw_gs_ff_sync(struct brw_gs_compile *c, int num_prim)<br>
&gt;  {<br>
&gt;     struct brw_compile *p = &amp;c-&gt;func;<br>
&gt; -   struct intel_context *intel = &amp;c-&gt;func.brw-&gt;intel;<br>
&gt;<br>
&gt; -   if (intel-&gt;gen &lt; 6) {<br>
&gt; -      brw_MOV(p, get_element_ud(c-&gt;reg.R0, 1), brw_imm_ud(num_prim));<br>
&gt; -      brw_ff_sync(p,<br>
&gt; -               c-&gt;reg.R0,<br>
&gt; -               0,<br>
&gt; -               c-&gt;reg.R0,<br>
&gt; -               1, /* allocate */<br>
&gt; -               1, /* response length */<br>
&gt; -               0 /* eot */);<br>
&gt; -   } else {<br>
&gt; -      brw_MOV(p, retype(c-&gt;reg.temp, BRW_REGISTER_TYPE_UD),<br>
&gt; -           retype(c-&gt;reg.R0, BRW_REGISTER_TYPE_UD));<br>
&gt; -      brw_MOV(p, get_element_ud(c-&gt;reg.temp, 1), brw_imm_ud(num_prim));<br>
&gt; -      brw_ff_sync(p,<br>
&gt; -               c-&gt;reg.temp,<br>
&gt; -               0,<br>
&gt; -               c-&gt;reg.temp,<br>
&gt; -               1, /* allocate */<br>
&gt; -               1, /* response length */<br>
&gt; -               0 /* eot */);<br>
&gt; -      brw_MOV(p, get_element_ud(c-&gt;reg.R0, 0),<br>
&gt; -      get_element_ud(c-&gt;reg.temp, 0));<br>
&gt; -   }<br>
&gt; +   brw_MOV(p, get_element_ud(c-&gt;reg.header, 1), brw_imm_ud(num_prim));<br>
&gt; +   brw_ff_sync(p,<br>
&gt; +               c-&gt;reg.temp,<br>
&gt; +               0,<br>
&gt; +               c-&gt;reg.header,<br>
&gt; +               1, /* allocate */<br>
&gt; +               1, /* response length */<br>
&gt; +               0 /* eot */);<br>
&gt; +   brw_MOV(p, get_element_ud(c-&gt;reg.header, 0),<br>
&gt; +           get_element_ud(c-&gt;reg.temp, 0));<br>
&gt;  }<br>
&gt;<br>
&gt;<br>
&gt; @@ -151,23 +182,28 @@ void brw_gs_quads( struct brw_gs_compile *c, struct brw_gs_prog_key *key )<br>
&gt;     struct intel_context *intel = &amp;c-&gt;func.brw-&gt;intel;<br>
&gt;<br>
&gt;     brw_gs_alloc_regs(c, 4);<br>
&gt; -<br>
&gt; +   brw_gs_initialize_header(c);<br>
&gt;     /* Use polygons for correct edgeflag behaviour. Note that vertex 3<br>
&gt;      * is the PV for quads, but vertex 0 for polygons:<br>
&gt;      */<br>
&gt;     if (intel-&gt;needs_ff_sync)<br>
&gt;        brw_gs_ff_sync(c, 1);<br>
&gt; +   brw_gs_overwrite_header_dw2(c, (_3DPRIM_POLYGON &lt;&lt; 2) | R02_PRIM_START);<br>
&gt;     if (key-&gt;pv_first) {<br>
&gt; -      brw_gs_emit_vue(c, c-&gt;reg.vertex[0], 0, ((_3DPRIM_POLYGON &lt;&lt; 2) | R02_PRIM_START));<br>
&gt; -      brw_gs_emit_vue(c, c-&gt;reg.vertex[1], 0, (_3DPRIM_POLYGON &lt;&lt; 2));<br>
&gt; -      brw_gs_emit_vue(c, c-&gt;reg.vertex[2], 0, (_3DPRIM_POLYGON &lt;&lt; 2));<br>
&gt; -      brw_gs_emit_vue(c, c-&gt;reg.vertex[3], 1, ((_3DPRIM_POLYGON &lt;&lt; 2) | R02_PRIM_END));<br>
&gt; +      brw_gs_emit_vue(c, c-&gt;reg.vertex[0], 0);<br>
&gt; +      brw_gs_overwrite_header_dw2(c, _3DPRIM_POLYGON &lt;&lt; 2);<br>
<br>
</div></div>This is correct, but really puzzled me for a while.  In the old code,<br>
you had to specify the header on every brw_gs_emit_vue, since it<br>
overwrote it completely.  In your new code, you&#39;ve updated that function<br>
so it only overwrites DW0, so you can set some stuff in the header and<br>
it&#39;ll remain in place.<br>
<br>
So now, you have to overwrite DW2 of the header on the first, second,<br>
and last vertex:<br>
- First because you need to set PRIM_START<br>
- Second because you need to *unset* PRIM_START<br>
- Last because you need to set PRIM_END<br>
<br>
It took quite a bit of staring to understand why you needed it on the<br>
second vertex (after all, the 3DPRIM_POLYGON &lt;&lt; 2 is already there...)<br>
<br>
In the end, this API change lets you omit setting _3DPRIM_POLYGON on the<br>
third vertex.  That...doesn&#39;t really seem worth it.  I suppose it saves<br>
you a MOV.  Mostly I suppose it&#39;s useful for your later work, which is a<br>
much more compelling argument. :)<br>
<br>
I wonder if this complexity could be encapsulated in a function which<br>
emits a list of VUEs.  Something like:<br>
<br>
void<br>
brw_gs_emit_vertices(brw_gs_compile *c, int prim_type, int count,<br>
                     int *indices)<br>
{<br>
   /* Set primitive type and PRIM_START flag */<br>
   brw_gs_overwrite_header_dw2(c, (prim_type &lt;&lt; 2) | PRIM_START);<br>
   brw_gs_emit_vue(c, c-&gt;reg.vertex[indices[0]]);<br>
<br>
   /* Unset PRIM_START flag and emit middle vertices */<br>
   brw_gs_overwrite_header_dw2(c, (prim_type &lt;&lt; 2));<br>
   for (int i = 1; i &lt; count - 1; i++)<br>
      brw_gs_emit_vue(c, c-&gt;reg.vertex[indices[i]]);<br>
<br>
   /* Emit last vertex with PRIM_END */<br>
   brw_gs_overwrite_header_dw2(c, (prim_type &lt;&lt; 2) | PRIM_END);<br>
   brw_gs_emit_vue(c, c-&gt;reg.vertex[indices[count - 1]]);<br>
}<br>
<br>
Then you could just do:<br>
brw_gs_emit_vertices(c, _3DPRIM_POLYGON, 4, ((int[]){0,1,2,3});<br>
brw_gs_emit_vertices(c, _3DPRIM_POLYGON, 4, ((int[]){3,0,1,2});<br>
or whatever you need.<br>
( If you haven&#39;t seen this C99 syntax, check out this awesome article:<br>
  <a href="http://www.run.montefiore.ulg.ac.be/%7Emartin/resources/kung-f00.html" target="_blank">http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html</a> )<br>
<br>
But I suppose this discussion is rather moot, since you need to do more<br>
complex stuff later anyway (like checking edges and conditionally<br>
skipping vertices.)<br><br></blockquote><div> </div></div>Yeah, I pretty much agree with everything you&#39;ve said here.  The code is definitely not ideal, but in light of what&#39;s coming later in the series, it&#39;s hard to see how to improve it in a way that won&#39;t have to be dismantled two patches later.<br>
<br>The idea that I&#39;m most inclined to act on is the brw_gs_emit_vertices() idea, but that would only help gen5 code, and since my focus at this point is to implement transform feedback on gen6, I&#39;m trying to keep gen5 changes as minimal as possible to reduce the risk of regressions.<br>
<br>So I think I&#39;ll leave it as is for now.  If anyone comes up with a better idea, or really can&#39;t stand the what the gen5 code looks like after the dust settles, I&#39;m open to following up with some clean-up patches.<br>