On 24 August 2012 03:06, Kenneth Graunke <span dir="ltr"><<a href="mailto:kenneth@whitecape.org" target="_blank">kenneth@whitecape.org</a>></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">
When assigning uniform locations, the linker assigns each sampler<br>
uniform a sequential numerical ID.  gl_shader_program::SamplerUnits maps<br>
these sampler variable IDs to the actual texture units they reference<br>
(specified via glUniform1i).<br>
<br>
Previously, we encoded this mapping in the SEND instruction encoding:<br>
the "sampler" was the texture unit number, and the binding table index<br>
was SURF_INDEX_TEXTURE(the texture unit number).  This unfortunately<br>
meant that whenever the application changed the value of a sampler<br>
uniform, we had to recompile the shader to change the SEND instructions.<br>
<br>
This was horrible for the game Cogs, which repeatedly switches between<br>
using texture unit 0 and 1.  It also made fragment shader precompiles<br>
useless: we'd do the precompile at glLinkShader() time, before the<br>
application called glUniform1i to set the sampler values.  As soon as<br>
it did that, we'd have to recompile, wasting time and space in the<br>
program cache.<br>
<br>
This patch encodes the SamplerUnits indirection in the binding table,<br>
sampler state, and sampler default color tables.  Instead of baking the<br>
texture unit number into the shader, we bake in the sampler variable ID<br>
assigned by the linker.  Since those never change, we don't need to<br>
recompile programs on uniform changes.<br>
<br>
This does mean that the tables now depend on the linked shader program<br>
being used for rendering, rather than simply representing all available<br>
texture units.  This could cause an increase in state emission.<br>
<br>
Another plus is that the sampler state and sampler default color tables<br>
are now compact: we only emit as many entries as there are sampler<br>
uniforms, with no holes in the table since the new sampler IDs are<br>
sequential.  Previously we had to emit a full 16 entries every time,<br>
since the tables tracked the state of all active texture units.<br>
<br>
Signed-off-by: Kenneth Graunke <<a href="mailto:kenneth@whitecape.org">kenneth@whitecape.org</a>><br>
---<br>
 src/mesa/drivers/dri/i965/brw_fs_visitor.cpp     |  2 +-<br>
 src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp   |  2 +-<br>
 src/mesa/drivers/dri/i965/brw_wm_sampler_state.c | 27 ++++++++-----<br>
 src/mesa/drivers/dri/i965/brw_wm_surface_state.c | 51 ++++++++++++++++++------<br>
 src/mesa/drivers/dri/i965/gen7_sampler_state.c   | 27 ++++++++-----<br>
 5 files changed, 74 insertions(+), 35 deletions(-)<br>
<br>
The big patch that actually changes how we do things.<br>
<br>
diff --git a/src/mesa/drivers/dri/i965/brw_fs_visitor.cpp b/src/mesa/drivers/dri/i965/brw_fs_visitor.cpp<br>
index 601f83c..e3d3a98 100644<br>
--- a/src/mesa/drivers/dri/i965/brw_fs_visitor.cpp<br>
+++ b/src/mesa/drivers/dri/i965/brw_fs_visitor.cpp<br>
@@ -1345,7 +1345,7 @@ fs_visitor::visit(ir_texture *ir)<br>
    if (ir->offset != NULL && ir->op != ir_txf)<br>
       inst->texture_offset = brw_texture_offset(ir->offset->as_constant());<br>
<br>
-   inst->sampler = texunit;<br>
+   inst->sampler = sampler;<br>
<br>
    if (ir->shadow_comparitor)<br>
       inst->shadow_compare = true;<br>
diff --git a/src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp b/src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp<br>
index c22ba60..629ecb0 100644<br>
--- a/src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp<br>
+++ b/src/mesa/drivers/dri/i965/brw_vec4_visitor.cpp<br>
@@ -1897,7 +1897,7 @@ vec4_visitor::visit(ir_texture *ir)<br>
    inst->header_present = ir->offset || intel->gen < 5;<br>
    inst->base_mrf = 2;<br>
    inst->mlen = inst->header_present + 1; /* always at least one */<br>
-   inst->sampler = texunit;<br>
+   inst->sampler = sampler;<br>
    inst->dst = dst_reg(this, ir->type);<br>
    inst->shadow_compare = ir->shadow_comparitor != NULL;<br>
<br>
diff --git a/src/mesa/drivers/dri/i965/brw_wm_sampler_state.c b/src/mesa/drivers/dri/i965/brw_wm_sampler_state.c<br>
index 10deb1d..610ef34 100644<br>
--- a/src/mesa/drivers/dri/i965/brw_wm_sampler_state.c<br>
+++ b/src/mesa/drivers/dri/i965/brw_wm_sampler_state.c<br>
@@ -334,13 +334,14 @@ brw_upload_samplers(struct brw_context *brw)<br>
 {<br>
    struct gl_context *ctx = &brw->intel.ctx;<br>
    struct brw_sampler_state *samplers;<br>
-   int i;<br>
<br>
-   brw->sampler.count = 0;<br>
-   for (i = 0; i < BRW_MAX_TEX_UNIT; i++) {<br>
-      if (ctx->Texture.Unit[i]._ReallyEnabled)<br>
-        brw->sampler.count = i + 1;<br>
-   }<br>
+   /* BRW_NEW_VERTEX_PROGRAM and BRW_NEW_FRAGMENT_PROGRAM */<br>
+   struct gl_program *vs = (struct gl_program *) brw->vertex_program;<br>
+   struct gl_program *fs = (struct gl_program *) brw->fragment_program;<br>
+<br>
+   GLbitfield SamplersUsed = vs->SamplersUsed | fs->SamplersUsed;<br>
+<br>
+   brw->sampler.count = _mesa_bitcount(SamplersUsed);<br>
<br>
    if (brw->sampler.count == 0)<br>
       return;<br>
@@ -350,9 +351,13 @@ brw_upload_samplers(struct brw_context *brw)<br>
                              32, &brw->sampler.offset);<br>
    memset(samplers, 0, brw->sampler.count * sizeof(*samplers));<br>
<br>
-   for (i = 0; i < brw->sampler.count; i++) {<br>
-      if (ctx->Texture.Unit[i]._ReallyEnabled)<br>
-        brw_update_sampler_state(brw, i, i, &samplers[i]);<br>
+   for (unsigned s = 0; s < brw->sampler.count; s++) {<br>
+      if (SamplersUsed & (1 << s)) {<br>
+         const unsigned unit = (fs->SamplersUsed & (1 << s)) ?<br>
+            fs->SamplerUnits[s] : vs->SamplerUnits[s];<br></blockquote><div><br>Presumably the correctness of this code relies on the assumption that if a given sampler is used by both the VS and the FS, then fs->SamplerUnits[s] == vs->SamplerUnits[s].  Would you mind adding a comment explaining why that is the case?  It's not obvious to me without digging around the linker code.  (An assertion to this effect might be a good idea too, since we're relying on the behaviour of code way off in glsl land).<br>
 </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+         if (ctx->Texture.Unit[unit]._ReallyEnabled)<br>
+            brw_update_sampler_state(brw, unit, s, &samplers[s]);<br>
+      }<br>
    }<br>
<br>
    brw->state.dirty.cache |= CACHE_NEW_SAMPLER;<br>
@@ -361,7 +366,9 @@ brw_upload_samplers(struct brw_context *brw)<br>
 const struct brw_tracked_state brw_samplers = {<br>
    .dirty = {<br>
       .mesa = _NEW_TEXTURE,<br>
-      .brw = BRW_NEW_BATCH,<br>
+      .brw = BRW_NEW_BATCH |<br>
+             BRW_NEW_VERTEX_PROGRAM |<br>
+             BRW_NEW_FRAGMENT_PROGRAM,<br>
       .cache = 0<br>
    },<br>
    .emit = brw_upload_samplers,<br>
diff --git a/src/mesa/drivers/dri/i965/brw_wm_surface_state.c b/src/mesa/drivers/dri/i965/brw_wm_surface_state.c<br>
index 0c87b84..eefa427 100644<br>
--- a/src/mesa/drivers/dri/i965/brw_wm_surface_state.c<br>
+++ b/src/mesa/drivers/dri/i965/brw_wm_surface_state.c<br>
@@ -1238,22 +1238,45 @@ const struct brw_tracked_state gen6_renderbuffer_surfaces = {<br>
 static void<br>
 brw_update_texture_surfaces(struct brw_context *brw)<br>
 {<br>
-   struct gl_context *ctx = &brw->intel.ctx;<br>
+   struct intel_context *intel = &brw->intel;<br>
+   struct gl_context *ctx = &intel->ctx;<br>
+<br>
+   /* BRW_NEW_VERTEX_PROGRAM and BRW_NEW_FRAGMENT_PROGRAM:<br>
+    * Unfortunately, we're stuck using the gl_program structs until the<br>
+    * ARB_fragment_program front-end gets converted to GLSL IR.  These<br>
+    * have the downside that SamplerUnits is split and only contains the<br>
+    * mappings for samplers active in that stage.<br>
+    */<br></blockquote><div><br>A duplicate of this comment would be helpful in brw_upload_samplers() and gen7_upload_samplers() too.<br> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

+   struct gl_program *vs = (struct gl_program *) brw->vertex_program;<br>
+   struct gl_program *fs = (struct gl_program *) brw->fragment_program;<br>
<br>
-   for (unsigned i = 0; i < BRW_MAX_TEX_UNIT; i++) {<br>
-      const struct gl_texture_unit *texUnit = &ctx->Texture.Unit[i];<br>
-      const GLuint surf = SURF_INDEX_TEXTURE(i);<br>
+   unsigned num_samplers = _mesa_bitcount(vs->SamplersUsed | fs->SamplersUsed);<br>
<br>
-      /* _NEW_TEXTURE */<br>
-      if (texUnit->_ReallyEnabled) {<br>
-        brw->intel.vtbl.update_texture_surface(ctx, i, brw->wm.surf_offset, surf);<br>
-      } else {<br>
-         brw->wm.surf_offset[surf] = 0;<br>
+   for (unsigned s = 0; s < num_samplers; s++) {<br>
+      brw->vs.surf_offset[SURF_INDEX_VS_TEXTURE(s)] = 0;<br>
+      brw->wm.surf_offset[SURF_INDEX_TEXTURE(s)] = 0;<br>
+<br>
+      if (vs->SamplersUsed & (1 << s)) {<br>
+         const unsigned unit = vs->SamplerUnits[s];<br>
+<br>
+         /* _NEW_TEXTURE */<br>
+         if (ctx->Texture.Unit[unit]._ReallyEnabled) {<br>
+            intel->vtbl.update_texture_surface(ctx, unit,<br>
+                                               brw->vs.surf_offset,<br>
+                                               SURF_INDEX_VS_TEXTURE(s));<br>
+         }<br>
       }<br>
<br>
-      /* For now, just mirror the texture setup to the VS slots. */<br>
-      brw->vs.surf_offset[SURF_INDEX_VS_TEXTURE(i)] =<br>
-        brw->wm.surf_offset[surf];<br>
+      if (fs->SamplersUsed & (1 << s)) {<br>
+         const unsigned unit = fs->SamplerUnits[s];<br>
+<br>
+         /* _NEW_TEXTURE */<br>
+         if (ctx->Texture.Unit[unit]._ReallyEnabled) {<br>
+            intel->vtbl.update_texture_surface(ctx, unit,<br>
+                                               brw->wm.surf_offset,<br>
+                                               SURF_INDEX_TEXTURE(s));<br>
+         }<br>
+      }<br>
    }<br>
<br>
    brw->state.dirty.brw |= BRW_NEW_SURFACES;<br>
@@ -1262,7 +1285,9 @@ brw_update_texture_surfaces(struct brw_context *brw)<br>
 const struct brw_tracked_state brw_texture_surfaces = {<br>
    .dirty = {<br>
       .mesa = _NEW_TEXTURE,<br>
-      .brw = BRW_NEW_BATCH,<br>
+      .brw = BRW_NEW_BATCH |<br>
+             BRW_NEW_VERTEX_PROGRAM |<br>
+             BRW_NEW_FRAGMENT_PROGRAM,<br>
       .cache = 0<br>
    },<br>
    .emit = brw_update_texture_surfaces,<br>
diff --git a/src/mesa/drivers/dri/i965/gen7_sampler_state.c b/src/mesa/drivers/dri/i965/gen7_sampler_state.c<br>
index 8969119..379a9c5 100644<br>
--- a/src/mesa/drivers/dri/i965/gen7_sampler_state.c<br>
+++ b/src/mesa/drivers/dri/i965/gen7_sampler_state.c<br>
@@ -188,13 +188,14 @@ gen7_upload_samplers(struct brw_context *brw)<br>
 {<br>
    struct gl_context *ctx = &brw->intel.ctx;<br>
    struct gen7_sampler_state *samplers;<br>
-   int i;<br>
<br>
-   brw->sampler.count = 0;<br>
-   for (i = 0; i < BRW_MAX_TEX_UNIT; i++) {<br>
-      if (ctx->Texture.Unit[i]._ReallyEnabled)<br>
-        brw->sampler.count = i + 1;<br>
-   }<br>
+   /* BRW_NEW_VERTEX_PROGRAM and BRW_NEW_FRAGMENT_PROGRAM */<br>
+   struct gl_program *vs = (struct gl_program *) brw->vertex_program;<br>
+   struct gl_program *fs = (struct gl_program *) brw->fragment_program;<br>
+<br>
+   GLbitfield SamplersUsed = vs->SamplersUsed | fs->SamplersUsed;<br>
+<br>
+   brw->sampler.count = _mesa_bitcount(SamplersUsed);<br>
<br>
    if (brw->sampler.count == 0)<br>
       return;<br>
@@ -204,9 +205,13 @@ gen7_upload_samplers(struct brw_context *brw)<br>
                              32, &brw->sampler.offset);<br>
    memset(samplers, 0, brw->sampler.count * sizeof(*samplers));<br>
<br>
-   for (i = 0; i < brw->sampler.count; i++) {<br>
-      if (ctx->Texture.Unit[i]._ReallyEnabled)<br>
-        gen7_update_sampler_state(brw, i, i, &samplers[i]);<br>
+   for (unsigned s = 0; s < brw->sampler.count; s++) {<br>
+      if (SamplersUsed & (1 << s)) {<br>
+         const unsigned unit = (fs->SamplersUsed & (1 << s)) ?<br>
+            fs->SamplerUnits[s] : vs->SamplerUnits[s];<br>
+         if (ctx->Texture.Unit[unit]._ReallyEnabled)<br>
+            gen7_update_sampler_state(brw, unit, s, &samplers[s]);<br>
+      }<br>
    }<br>
<br>
    brw->state.dirty.cache |= CACHE_NEW_SAMPLER;<br>
@@ -215,7 +220,9 @@ gen7_upload_samplers(struct brw_context *brw)<br>
 const struct brw_tracked_state gen7_samplers = {<br>
    .dirty = {<br>
       .mesa = _NEW_TEXTURE,<br>
-      .brw = BRW_NEW_BATCH,<br>
+      .brw = BRW_NEW_BATCH |<br>
+             BRW_NEW_VERTEX_PROGRAM |<br>
+             BRW_NEW_FRAGMENT_PROGRAM,<br>
       .cache = 0<br>
    },<br>
    .emit = gen7_upload_samplers,<br>
<span class="HOEnZb"><font color="#888888">--<br>
1.7.11.4<br>
<br>
_______________________________________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/mesa-dev" target="_blank">http://lists.freedesktop.org/mailman/listinfo/mesa-dev</a><br>
</font></span></blockquote></div><br>I admit to being pretty ignorant about this corner of the code, so for this patch let's just say<br><br>Acked-by: Paul Berry <<a href="mailto:stereotype441@gmail.com">stereotype441@gmail.com</a>><br>