<div dir="ltr">On 28 August 2013 21:00, Kenneth Graunke <span dir="ltr"><<a href="mailto:kenneth@whitecape.org" target="_blank">kenneth@whitecape.org</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div class=""><div class="h5">On 08/26/2013 03:12 PM, Paul Berry wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Previously, we gave all of the URB space (other than the small amount<br>
that is used for push constants) to the vertex shader.  However, when<br>
a geometry shader is active, we need to divide it up between the<br>
vertex and geometry shaders.<br>
<br>
The size of the URB entries for the vertex and geometry shaders can<br>
vary dramatically from one shader to the next.  So it doesn't make<br>
sense to simply split the available space in two.  In particular:<br>
<br>
- On Ivy Bridge GT1, this would not leave enough space for the worst<br>
   case geometry shader, which requires 64k of URB space.<br>
<br>
- Due to hardware-imposed limits on the maximum number of URB entries,<br>
   sometimes a given shader stage will only be capable of using a small<br>
   amount of URB space.  When this happens, it may make sense to<br>
   allocate substantially less than half of the available space to that<br>
   stage.<br>
<br>
Our algorithm for dividing space between the two stages is to first<br>
compute (a) the minimum amount of URB space that each stage needs in<br>
order to function properly, and (b) the amount of additional URB space<br>
that each stage "wants" (i.e. that it would be capable of making use<br>
of).  If the total amount of space available is not enough to satisfy<br>
needs + wants, then each stage's "wants" amount is scaled back by the<br>
same factor in order to fit.<br>
<br>
When only a vertex shader is active, this algorithm produces<br>
equivalent results to the old algorithm (if the vertex shader stage<br>
can make use of all the available URB space, we assign all the space<br>
to it; if it can't, we let it use as much as it can).<br>
<br>
In the future, when we need to support tessellation control and<br>
tessellation evaluation pipeline stages, it should be straightforward<br>
to expand this algorithm to cover them.<br>
<br>
v2: Use "unsigned" rather than "GLuint".<br>
---<br>
  src/mesa/drivers/dri/i965/brw_<u></u>context.h  |   6 +-<br>
  src/mesa/drivers/dri/i965/<u></u>gen7_blorp.cpp |  16 ++--<br>
  src/mesa/drivers/dri/i965/<u></u>gen7_urb.c     | 155 +++++++++++++++++++++++++-----<u></u>-<br>
  3 files changed, 142 insertions(+), 35 deletions(-)<br>
<br>
diff --git a/src/mesa/drivers/dri/i965/<u></u>brw_context.h b/src/mesa/drivers/dri/i965/<u></u>brw_context.h<br>
index be5175f..77f2a6b 100644<br>
--- a/src/mesa/drivers/dri/i965/<u></u>brw_context.h<br>
+++ b/src/mesa/drivers/dri/i965/<u></u>brw_context.h<br>
@@ -1511,8 +1511,10 @@ void<br>
  gen7_allocate_push_constants(<u></u>struct brw_context *brw);<br>
<br>
  void<br>
-gen7_emit_urb_state(struct brw_context *brw, GLuint nr_vs_entries,<br>
-                    GLuint vs_size, GLuint vs_start);<br>
+gen7_emit_urb_state(struct brw_context *brw,<br>
+                    unsigned nr_vs_entries, unsigned vs_size,<br>
+                    unsigned vs_start, unsigned nr_gs_entries,<br>
+                    unsigned gs_size, unsigned gs_start);<br>
<br>
<br>
<br>
diff --git a/src/mesa/drivers/dri/i965/<u></u>gen7_blorp.cpp b/src/mesa/drivers/dri/i965/<u></u>gen7_blorp.cpp<br>
index a387836..6c798b1 100644<br>
--- a/src/mesa/drivers/dri/i965/<u></u>gen7_blorp.cpp<br>
+++ b/src/mesa/drivers/dri/i965/<u></u>gen7_blorp.cpp<br>
@@ -51,14 +51,16 @@ static void<br>
  gen7_blorp_emit_urb_config(<u></u>struct brw_context *brw,<br>
                             const brw_blorp_params *params)<br>
  {<br>
-   /* The minimum valid value is 32. See 3DSTATE_URB_VS,<br>
-    * Dword 1.15:0 "VS Number of URB Entries".<br>
+   /* The minimum valid number of VS entries is 32. See 3DSTATE_URB_VS, Dword<br>
+    * 1.15:0 "VS Number of URB Entries".<br>
      */<br>
-   int num_vs_entries = 32;<br>
-   int vs_size = 2;<br>
-   int vs_start = 2; /* skip over push constants */<br>
-<br>
-   gen7_emit_urb_state(brw, num_vs_entries, vs_size, vs_start);<br>
+   gen7_emit_urb_state(brw,<br>
+                       32 /* num_vs_entries */,<br>
+                       2 /* vs_size */,<br>
+                       2 /* vs_start */,<br>
+                       0 /* num_gs_entries */,<br>
+                       1 /* gs_size */,<br>
+                       2 /* gs_start */);<br>
  }<br>
<br>
<br>
diff --git a/src/mesa/drivers/dri/i965/<u></u>gen7_urb.c b/src/mesa/drivers/dri/i965/<u></u>gen7_urb.c<br>
index 927af37..2d10cc12 100644<br>
--- a/src/mesa/drivers/dri/i965/<u></u>gen7_urb.c<br>
+++ b/src/mesa/drivers/dri/i965/<u></u>gen7_urb.c<br>
@@ -74,34 +74,136 @@ gen7_upload_urb(struct brw_context *brw)<br>
  {<br>
     const int push_size_kB = brw->is_haswell && brw->gt == 3 ? 32 : 16;<br>
<br>
-   /* Total space for entries is URB size - 16kB for push constants */<br>
-   int handle_region_size = (brw->urb.size - push_size_kB) * 1024; /* bytes */<br>
-<br>
     /* CACHE_NEW_VS_PROG */<br>
     unsigned vs_size = MAX2(brw->vs.prog_data->base.<u></u>urb_entry_size, 1);<br>
-<br>
-   int nr_vs_entries = handle_region_size / (vs_size * 64);<br>
-   if (nr_vs_entries > brw->urb.max_vs_entries)<br>
-      nr_vs_entries = brw->urb.max_vs_entries;<br>
-<br>
-   /* According to volume 2a, nr_vs_entries must be a multiple of 8. */<br>
-   brw->urb.nr_vs_entries = ROUND_DOWN_TO(nr_vs_entries, 8);<br>
-<br>
-   /* URB Starting Addresses are specified in multiples of 8kB. */<br>
-   brw->urb.vs_start = push_size_kB / 8; /* skip over push constants */<br>
-<br>
-   assert(brw->urb.nr_vs_entries % 8 == 0);<br>
-   assert(brw->urb.nr_gs_entries % 8 == 0);<br>
-   /* GS requirement */<br>
-   assert(!brw->ff_gs.prog_<u></u>active);<br>
+   unsigned vs_entry_size_bytes = vs_size * 64;<br>
+   /* BRW_NEW_GEOMETRY_PROGRAM, CACHE_NEW_GS_PROG */<br>
+   bool gs_present = brw->geometry_program;<br>
+   unsigned gs_size = gs_present ? brw->gs.prog_data->base.urb_<u></u>entry_size : 1;<br>
+   unsigned gs_entry_size_bytes = gs_size * 64;<br>
+<br>
+   /* From p35 of the Ivy Bridge PRM (section 1.7.1: 3DSTATE_URB_GS):<br>
+    *<br>
+    *     VS Number of URB Entries must be divisible by 8 if the VS URB Entry<br>
+    *     Allocation Size is less than 9 512-bit URB entries.<br>
+    *<br>
+    * Similar text exists for GS.<br>
+    */<br>
+   unsigned vs_granularity = (vs_size < 9) ? 8 : 1;<br>
+   unsigned gs_granularity = (gs_size < 9) ? 8 : 1;<br>
+<br>
+   /* URB allocations must be done in 8k chunks. */<br>
+   unsigned chunk_size_bytes = 8192;<br>
+<br>
+   /* Determine the size of the URB in chunks.<br>
+    */<br>
+   unsigned urb_chunks = brw->urb.size * 1024 / chunk_size_bytes;<br>
+<br>
+   /* Reserve space for push constants */<br>
+   unsigned push_constant_bytes = 1024 * push_size_kB;<br>
+   unsigned push_constant_chunks =<br>
+      push_constant_bytes / chunk_size_bytes;<br>
+<br>
+   /* Initially, assign each stage the minimum amount of URB space it needs,<br>
+    * and make a note of how much additional space it "wants" (the amount of<br>
+    * additional space it could actually make use of).<br>
+    */<br>
+<br>
+   /* VS always requires at least 32 URB entries */<br>
+   unsigned vs_chunks =<br>
+      ALIGN(32 * vs_entry_size_bytes, chunk_size_bytes) / chunk_size_bytes;<br>
+   unsigned vs_wants =<br>
+      ALIGN(brw->urb.max_vs_entries * vs_entry_size_bytes,<br>
+            chunk_size_bytes) / chunk_size_bytes - vs_chunks;<br>
+<br>
+   unsigned gs_chunks = 0;<br>
+   unsigned gs_wants = 0;<br>
+   if (gs_present) {<br>
+      /* There are two constraints on the minimum amount of URB space we can<br>
+       * allocate:<br>
+       *<br>
+       * (1) We need room for at least 2 URB entries, since we always operate<br>
+       * the GS in DUAL_OBJECT mode.<br>
+       *<br>
+       * (2) We can't allocate less than nr_gs_entries_granularity.<br>
+       */<br>
+      gs_chunks = ALIGN(MAX2(gs_granularity, 2) * gs_entry_size_bytes,<br>
+                        chunk_size_bytes) / chunk_size_bytes;<br>
+      gs_wants =<br>
+         ALIGN(brw->urb.max_gs_entries * gs_entry_size_bytes,<br>
+               chunk_size_bytes) / chunk_size_bytes - gs_chunks;<br>
+   }<br>
+<br>
+   /* There should always be enough URB space to satisfy the minimum<br>
+    * requirements of each stage.<br>
+    */<br>
+   unsigned total_needs = push_constant_chunks + vs_chunks + gs_chunks;<br>
+   assert(total_needs <= urb_chunks);<br>
+<br>
+   /* Mete out remaining space (if any) in proportion to "wants". */<br>
+   unsigned total_wants = vs_wants + gs_wants;<br>
+   unsigned remaining_space = urb_chunks - total_needs;<br>
+   if (remaining_space > total_wants)<br>
+      remaining_space = total_wants;<br>
+   if (remaining_space > 0) {<br>
+      unsigned vs_additional = (unsigned)<br>
+         round(vs_wants * (((double) remaining_space) / total_wants));<br>
+      vs_chunks += vs_additional;<br>
+      remaining_space -= vs_additional;<br>
+      gs_chunks += remaining_space;<br>
+   }<br>
+<br>
+   /* Sanity check that we haven't over-allocated. */<br>
+   assert(push_constant_chunks + vs_chunks + gs_chunks <= urb_chunks);<br>
+<br>
+   /* Finally, compute the number of entries that can fit in the space<br>
+    * allocated to each stage.<br>
+    */<br>
+   unsigned nr_vs_entries = vs_chunks * chunk_size_bytes / vs_entry_size_bytes;<br>
+   unsigned nr_gs_entries = gs_chunks * chunk_size_bytes / gs_entry_size_bytes;<br>
+<br>
+   /* Since we rounded up when computing *_wants, this may be slightly more<br>
+    * than the maximum allowed amount, so correct for that.<br>
+    */<br>
+   nr_vs_entries = MIN2(nr_vs_entries, brw->urb.max_vs_entries);<br>
+   nr_gs_entries = MIN2(nr_gs_entries, brw->urb.max_gs_entries);<br>
+<br>
+   /* Ensure that we program a multiple of the granularity. */<br>
+   nr_vs_entries = ROUND_DOWN_TO(nr_vs_entries, vs_granularity);<br>
+   nr_gs_entries = ROUND_DOWN_TO(nr_gs_entries, gs_granularity);<br>
+<br>
+   /* Finally, sanity check to make sure we have at least the minimum number<br>
+    * of entries needed for each stage.<br>
+    */<br>
+   assert(nr_vs_entries >= 32);<br>
+   if (gs_present)<br>
+      assert(nr_gs_entries >= 2);<br>
+<br>
+   /* And store the values we computed in brw so that they can be used by<br>
+    * other state atoms.<br>
+    */<br>
</blockquote>
<br></div></div>
Actually, no other atoms ever use these.  The only reason these fields exist is for Gen4, where the VS_STATE, CLIP_STATE, etc. packets actually specified the URB configuration for that stage.<br>
<br>
I just set them because they were there, and putting data in them seemed better than leaving them uninitialized.<br></blockquote><div><br></div><div>Ok, I've changed the comment to say:<br><br>   /* Gen7 doesn't actually use brw->urb.nr_{vs,gs}_entries, but it seems<br>
    * better to put reasonable data in there rather than leave them<br>    * uninitialized.<br>    */<br><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

<br>
However, I can't see any reason any other atom would /ever/ care, so I'm somewhat tempted to keep these as locals.  Would be a tiny bit more efficient.<br></blockquote><div><br></div><div>I could go either way on this.  We can always do it in a follow-up patch if this state atom winds up looming large in profiling runs.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
This patch seems pretty reasonable.  It's rather complicated, but...I don't honestly know that I could come up with anything better.<div class=""><div class="h5"><br>
<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+   brw->urb.nr_vs_entries = nr_vs_entries;<br>
+   brw->urb.nr_gs_entries = nr_gs_entries;<br>
+<br>
+   /* Lay out the URB in the following order:<br>
+    * - push constants<br>
+    * - VS<br>
+    * - GS<br>
+    */<br>
+   brw->urb.vs_start = push_constant_chunks;<br>
+   brw->urb.gs_start = push_constant_chunks + vs_chunks;<br>
<br>
     gen7_emit_vs_workaround_flush(<u></u>brw);<br>
-   gen7_emit_urb_state(brw, brw->urb.nr_vs_entries, vs_size, brw->urb.vs_start);<br>
+   gen7_emit_urb_state(brw,<br>
+                       brw->urb.nr_vs_entries, vs_size, brw->urb.vs_start,<br>
+                       brw->urb.nr_gs_entries, gs_size, brw->urb.gs_start);<br>
  }<br>
<br>
  void<br>
-gen7_emit_urb_state(struct brw_context *brw, GLuint nr_vs_entries,<br>
-                    GLuint vs_size, GLuint vs_start)<br>
+gen7_emit_urb_state(struct brw_context *brw,<br>
+                    unsigned nr_vs_entries, unsigned vs_size,<br>
+                    unsigned vs_start, unsigned nr_gs_entries,<br>
+                    unsigned gs_size, unsigned gs_start)<br>
  {<br>
     BEGIN_BATCH(8);<br>
     OUT_BATCH(_3DSTATE_URB_VS << 16 | (2 - 2));<br>
@@ -109,11 +211,12 @@ gen7_emit_urb_state(struct brw_context *brw, GLuint nr_vs_entries,<br>
               ((vs_size - 1) << GEN7_URB_ENTRY_SIZE_SHIFT) |<br>
               (vs_start << GEN7_URB_STARTING_ADDRESS_<u></u>SHIFT));<br>
<br>
-   /* Allocate the GS, HS, and DS zero space - we don't use them. */<br>
     OUT_BATCH(_3DSTATE_URB_GS << 16 | (2 - 2));<br>
-   OUT_BATCH((0 << GEN7_URB_ENTRY_SIZE_SHIFT) |<br>
-             (vs_start << GEN7_URB_STARTING_ADDRESS_<u></u>SHIFT));<br>
+   OUT_BATCH(nr_gs_entries |<br>
+             ((gs_size - 1) << GEN7_URB_ENTRY_SIZE_SHIFT) |<br>
+             (gs_start << GEN7_URB_STARTING_ADDRESS_<u></u>SHIFT));<br>
<br>
+   /* Allocate the HS and DS zero space - we don't use them. */<br>
     OUT_BATCH(_3DSTATE_URB_HS << 16 | (2 - 2));<br>
     OUT_BATCH((0 << GEN7_URB_ENTRY_SIZE_SHIFT) |<br>
               (vs_start << GEN7_URB_STARTING_ADDRESS_<u></u>SHIFT));<br>
@@ -127,8 +230,8 @@ gen7_emit_urb_state(struct brw_context *brw, GLuint nr_vs_entries,<br>
  const struct brw_tracked_state gen7_urb = {<br>
     .dirty = {<br>
        .mesa = 0,<br>
-      .brw = BRW_NEW_CONTEXT,<br>
-      .cache = (CACHE_NEW_VS_PROG | CACHE_NEW_FF_GS_PROG),<br>
+      .brw = BRW_NEW_CONTEXT | BRW_NEW_GEOMETRY_PROGRAM,<br>
+      .cache = (CACHE_NEW_VS_PROG | CACHE_NEW_GS_PROG),<br>
     },<br>
     .emit = gen7_upload_urb,<br>
  };<br>
<br>
</blockquote>
<br>
</div></div></blockquote></div><br></div></div>