i965/gs: Implement support for geometry shader samplers.
[mesa.git] / src / mesa / drivers / dri / i965 / brw_wm_sampler_state.c
index 30672b4251beecb4aea7edd73a22cb3562d40b19..828820d533ef4fda7a0d036a286cfc0db4c1ac6c 100644 (file)
 #include "brw_context.h"
 #include "brw_state.h"
 #include "brw_defines.h"
+#include "intel_mipmap_tree.h"
 
 #include "main/macros.h"
-
+#include "main/samplerobj.h"
 
 
 /* Samplers aren't strictly wm state from the hardware's perspective,
 
 
 
-/* The brw (and related graphics cores) do not support GL_CLAMP.  The
- * Intel drivers for "other operating systems" implement GL_CLAMP as
- * GL_CLAMP_TO_EDGE, so the same is done here.
- */
-static GLuint translate_wrap_mode( GLenum wrap )
+uint32_t
+translate_wrap_mode(GLenum wrap, bool using_nearest)
 {
    switch( wrap ) {
    case GL_REPEAT: 
       return BRW_TEXCOORDMODE_WRAP;
-   case GL_CLAMP:  
-      return BRW_TEXCOORDMODE_CLAMP;
+   case GL_CLAMP:
+      /* GL_CLAMP is the weird mode where coordinates are clamped to
+       * [0.0, 1.0], so linear filtering of coordinates outside of
+       * [0.0, 1.0] give you half edge texel value and half border
+       * color.  The fragment shader will clamp the coordinates, and
+       * we set clamp_border here, which gets the result desired.  We
+       * just use clamp(_to_edge) for nearest, because for nearest
+       * clamping to 1.0 gives border color instead of the desired
+       * edge texels.
+       */
+      if (using_nearest)
+        return BRW_TEXCOORDMODE_CLAMP;
+      else
+        return BRW_TEXCOORDMODE_CLAMP_BORDER;
    case GL_CLAMP_TO_EDGE: 
-      return BRW_TEXCOORDMODE_CLAMP; /* conform likes it this way */
+      return BRW_TEXCOORDMODE_CLAMP;
    case GL_CLAMP_TO_BORDER: 
       return BRW_TEXCOORDMODE_CLAMP_BORDER;
    case GL_MIRRORED_REPEAT: 
@@ -66,84 +76,146 @@ static GLuint translate_wrap_mode( GLenum wrap )
    }
 }
 
-static drm_intel_bo *upload_default_color( struct brw_context *brw,
-                                    const GLfloat *color )
+/**
+ * Upload SAMPLER_BORDER_COLOR_STATE.
+ */
+void
+upload_default_color(struct brw_context *brw,
+                     struct gl_sampler_object *sampler,
+                     int unit,
+                     uint32_t *sdc_offset)
 {
-   struct intel_context *intel = &brw->intel;
-
-   if (intel->gen >= 5) {
-      struct gen5_sampler_default_color sdc;
-
-      memset(&sdc, 0, sizeof(sdc));
-
-      UNCLAMPED_FLOAT_TO_UBYTE(sdc.ub[0], color[0]);
-      UNCLAMPED_FLOAT_TO_UBYTE(sdc.ub[1], color[1]);
-      UNCLAMPED_FLOAT_TO_UBYTE(sdc.ub[2], color[2]);
-      UNCLAMPED_FLOAT_TO_UBYTE(sdc.ub[3], color[3]);
-
-      UNCLAMPED_FLOAT_TO_USHORT(sdc.us[0], color[0]);
-      UNCLAMPED_FLOAT_TO_USHORT(sdc.us[1], color[1]);
-      UNCLAMPED_FLOAT_TO_USHORT(sdc.us[2], color[2]);
-      UNCLAMPED_FLOAT_TO_USHORT(sdc.us[3], color[3]);
-
-      UNCLAMPED_FLOAT_TO_SHORT(sdc.s[0], color[0]);
-      UNCLAMPED_FLOAT_TO_SHORT(sdc.s[1], color[1]);
-      UNCLAMPED_FLOAT_TO_SHORT(sdc.s[2], color[2]);
-      UNCLAMPED_FLOAT_TO_SHORT(sdc.s[3], color[3]);
-
-      /* XXX: Fill in half floats */
-      /* XXX: Fill in signed bytes */
-
-      COPY_4V(sdc.f, color);
+   struct gl_context *ctx = &brw->ctx;
+   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
+   struct gl_texture_object *texObj = texUnit->_Current;
+   struct gl_texture_image *firstImage = texObj->Image[0][texObj->BaseLevel];
+   float color[4];
+
+   switch (firstImage->_BaseFormat) {
+   case GL_DEPTH_COMPONENT:
+      /* GL specs that border color for depth textures is taken from the
+       * R channel, while the hardware uses A.  Spam R into all the
+       * channels for safety.
+       */
+      color[0] = sampler->BorderColor.f[0];
+      color[1] = sampler->BorderColor.f[0];
+      color[2] = sampler->BorderColor.f[0];
+      color[3] = sampler->BorderColor.f[0];
+      break;
+   case GL_ALPHA:
+      color[0] = 0.0;
+      color[1] = 0.0;
+      color[2] = 0.0;
+      color[3] = sampler->BorderColor.f[3];
+      break;
+   case GL_INTENSITY:
+      color[0] = sampler->BorderColor.f[0];
+      color[1] = sampler->BorderColor.f[0];
+      color[2] = sampler->BorderColor.f[0];
+      color[3] = sampler->BorderColor.f[0];
+      break;
+   case GL_LUMINANCE:
+      color[0] = sampler->BorderColor.f[0];
+      color[1] = sampler->BorderColor.f[0];
+      color[2] = sampler->BorderColor.f[0];
+      color[3] = 1.0;
+      break;
+   case GL_LUMINANCE_ALPHA:
+      color[0] = sampler->BorderColor.f[0];
+      color[1] = sampler->BorderColor.f[0];
+      color[2] = sampler->BorderColor.f[0];
+      color[3] = sampler->BorderColor.f[3];
+      break;
+   default:
+      color[0] = sampler->BorderColor.f[0];
+      color[1] = sampler->BorderColor.f[1];
+      color[2] = sampler->BorderColor.f[2];
+      color[3] = sampler->BorderColor.f[3];
+      break;
+   }
 
-      return brw_cache_data(&brw->cache, BRW_SAMPLER_DEFAULT_COLOR,
-                           &sdc, sizeof(sdc));
+   /* In some cases we use an RGBA surface format for GL RGB textures,
+    * where we've initialized the A channel to 1.0.  We also have to set
+    * the border color alpha to 1.0 in that case.
+    */
+   if (firstImage->_BaseFormat == GL_RGB)
+      color[3] = 1.0;
+
+   if (brw->gen == 5 || brw->gen == 6) {
+      struct gen5_sampler_default_color *sdc;
+
+      sdc = brw_state_batch(brw, AUB_TRACE_SAMPLER_DEFAULT_COLOR,
+                           sizeof(*sdc), 32, sdc_offset);
+
+      memset(sdc, 0, sizeof(*sdc));
+
+      UNCLAMPED_FLOAT_TO_UBYTE(sdc->ub[0], color[0]);
+      UNCLAMPED_FLOAT_TO_UBYTE(sdc->ub[1], color[1]);
+      UNCLAMPED_FLOAT_TO_UBYTE(sdc->ub[2], color[2]);
+      UNCLAMPED_FLOAT_TO_UBYTE(sdc->ub[3], color[3]);
+
+      UNCLAMPED_FLOAT_TO_USHORT(sdc->us[0], color[0]);
+      UNCLAMPED_FLOAT_TO_USHORT(sdc->us[1], color[1]);
+      UNCLAMPED_FLOAT_TO_USHORT(sdc->us[2], color[2]);
+      UNCLAMPED_FLOAT_TO_USHORT(sdc->us[3], color[3]);
+
+      UNCLAMPED_FLOAT_TO_SHORT(sdc->s[0], color[0]);
+      UNCLAMPED_FLOAT_TO_SHORT(sdc->s[1], color[1]);
+      UNCLAMPED_FLOAT_TO_SHORT(sdc->s[2], color[2]);
+      UNCLAMPED_FLOAT_TO_SHORT(sdc->s[3], color[3]);
+
+      sdc->hf[0] = _mesa_float_to_half(color[0]);
+      sdc->hf[1] = _mesa_float_to_half(color[1]);
+      sdc->hf[2] = _mesa_float_to_half(color[2]);
+      sdc->hf[3] = _mesa_float_to_half(color[3]);
+
+      sdc->b[0] = sdc->s[0] >> 8;
+      sdc->b[1] = sdc->s[1] >> 8;
+      sdc->b[2] = sdc->s[2] >> 8;
+      sdc->b[3] = sdc->s[3] >> 8;
+
+      sdc->f[0] = color[0];
+      sdc->f[1] = color[1];
+      sdc->f[2] = color[2];
+      sdc->f[3] = color[3];
    } else {
-      struct brw_sampler_default_color sdc;
+      struct brw_sampler_default_color *sdc;
 
-      COPY_4V(sdc.color, color);
+      sdc = brw_state_batch(brw, AUB_TRACE_SAMPLER_DEFAULT_COLOR,
+                           sizeof(*sdc), 32, sdc_offset);
 
-      return brw_cache_data(&brw->cache, BRW_SAMPLER_DEFAULT_COLOR,
-                           &sdc, sizeof(sdc));
+      COPY_4V(sdc->color, color);
    }
 }
 
-
-struct wm_sampler_key {
-   int sampler_count;
-
-   struct wm_sampler_entry {
-      GLenum tex_target;
-      GLenum wrap_r, wrap_s, wrap_t;
-      float maxlod, minlod;
-      float lod_bias;
-      float max_aniso;
-      GLenum minfilter, magfilter;
-      GLenum comparemode, comparefunc;
-
-      /** If target is cubemap, take context setting.
-       */
-      GLboolean seamless_cube_map;
-   } sampler[BRW_MAX_TEX_UNIT];
-};
-
 /**
  * Sets the sampler state for a single unit based off of the sampler key
  * entry.
  */
 static void brw_update_sampler_state(struct brw_context *brw,
-                                    struct wm_sampler_entry *key,
-                                    drm_intel_bo *sdc_bo,
-                                    struct brw_sampler_state *sampler)
+                                    int unit,
+                                     int ss_index,
+                                     struct brw_sampler_state *sampler,
+                                     uint32_t sampler_state_table_offset,
+                                     uint32_t *sdc_offset)
 {
-   struct intel_context *intel = &brw->intel;
-
-   memset(sampler, 0, sizeof(*sampler));
+   struct gl_context *ctx = &brw->ctx;
+   struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
+   struct gl_texture_object *texObj = texUnit->_Current;
+   struct intel_texture_image *intel_image =
+      intel_texture_image(texObj->Image[0][texObj->BaseLevel]);
+   struct gl_sampler_object *gl_sampler = _mesa_get_samplerobj(ctx, unit);
+   bool using_nearest = false;
+
+   /* These don't use samplers at all. */
+   if (texObj->Target == GL_TEXTURE_BUFFER)
+      return;
 
-   switch (key->minfilter) {
+   switch (gl_sampler->MinFilter) {
    case GL_NEAREST:
       sampler->ss0.min_filter = BRW_MAPFILTER_NEAREST;
       sampler->ss0.mip_filter = BRW_MIPFILTER_NONE;
+      using_nearest = true;
       break;
    case GL_LINEAR:
       sampler->ss0.min_filter = BRW_MAPFILTER_LINEAR;
@@ -171,19 +243,20 @@ static void brw_update_sampler_state(struct brw_context *brw,
 
    /* Set Anisotropy: 
     */
-   if (key->max_aniso > 1.0) {
+   if (gl_sampler->MaxAnisotropy > 1.0) {
       sampler->ss0.min_filter = BRW_MAPFILTER_ANISOTROPIC; 
       sampler->ss0.mag_filter = BRW_MAPFILTER_ANISOTROPIC;
 
-      if (key->max_aniso > 2.0) {
-        sampler->ss3.max_aniso = MIN2((key->max_aniso - 2) / 2,
+      if (gl_sampler->MaxAnisotropy > 2.0) {
+        sampler->ss3.max_aniso = MIN2((gl_sampler->MaxAnisotropy - 2) / 2,
                                       BRW_ANISORATIO_16);
       }
    }
    else {
-      switch (key->magfilter) {
+      switch (gl_sampler->MagFilter) {
       case GL_NEAREST:
         sampler->ss0.mag_filter = BRW_MAPFILTER_NEAREST;
+        using_nearest = true;
         break;
       case GL_LINEAR:
         sampler->ss0.mag_filter = BRW_MAPFILTER_LINEAR;
@@ -193,20 +266,25 @@ static void brw_update_sampler_state(struct brw_context *brw,
       }  
    }
 
-   sampler->ss1.r_wrap_mode = translate_wrap_mode(key->wrap_r);
-   sampler->ss1.s_wrap_mode = translate_wrap_mode(key->wrap_s);
-   sampler->ss1.t_wrap_mode = translate_wrap_mode(key->wrap_t);
+   sampler->ss1.r_wrap_mode = translate_wrap_mode(gl_sampler->WrapR,
+                                                 using_nearest);
+   sampler->ss1.s_wrap_mode = translate_wrap_mode(gl_sampler->WrapS,
+                                                 using_nearest);
+   sampler->ss1.t_wrap_mode = translate_wrap_mode(gl_sampler->WrapT,
+                                                 using_nearest);
 
-   if (intel->gen >= 6 &&
+   if (brw->gen >= 6 &&
        sampler->ss0.min_filter != sampler->ss0.mag_filter)
        sampler->ss0.min_mag_neq = 1;
 
    /* Cube-maps on 965 and later must use the same wrap mode for all 3
     * coordinate dimensions.  Futher, only CUBE and CLAMP are valid.
     */
-   if (key->tex_target == GL_TEXTURE_CUBE_MAP) {
-      if (key->seamless_cube_map &&
-         (key->minfilter != GL_NEAREST || key->magfilter != GL_NEAREST)) {
+   if (texObj->Target == GL_TEXTURE_CUBE_MAP ||
+       texObj->Target == GL_TEXTURE_CUBE_MAP_ARRAY) {
+      if (ctx->Texture.CubeMapSeamless &&
+         (gl_sampler->MinFilter != GL_NEAREST ||
+          gl_sampler->MagFilter != GL_NEAREST)) {
         sampler->ss1.r_wrap_mode = BRW_TEXCOORDMODE_CUBE;
         sampler->ss1.s_wrap_mode = BRW_TEXCOORDMODE_CUBE;
         sampler->ss1.t_wrap_mode = BRW_TEXCOORDMODE_CUBE;
@@ -215,7 +293,7 @@ static void brw_update_sampler_state(struct brw_context *brw,
         sampler->ss1.s_wrap_mode = BRW_TEXCOORDMODE_CLAMP;
         sampler->ss1.t_wrap_mode = BRW_TEXCOORDMODE_CLAMP;
       }
-   } else if (key->tex_target == GL_TEXTURE_1D) {
+   } else if (texObj->Target == GL_TEXTURE_1D) {
       /* There's a bug in 1D texture sampling - it actually pays
        * attention to the wrap_t value, though it should not.
        * Override the wrap_t value here to GL_REPEAT to keep
@@ -227,175 +305,174 @@ static void brw_update_sampler_state(struct brw_context *brw,
 
    /* Set shadow function: 
     */
-   if (key->comparemode == GL_COMPARE_R_TO_TEXTURE_ARB) {
+   if (gl_sampler->CompareMode == GL_COMPARE_R_TO_TEXTURE_ARB) {
       /* Shadowing is "enabled" by emitting a particular sampler
        * message (sample_c).  So need to recompile WM program when
        * shadow comparison is enabled on each/any texture unit.
        */
       sampler->ss0.shadow_function =
-        intel_translate_shadow_compare_func(key->comparefunc);
+        intel_translate_shadow_compare_func(gl_sampler->CompareFunc);
    }
 
    /* Set LOD bias: 
     */
-   sampler->ss0.lod_bias = S_FIXED(CLAMP(key->lod_bias, -16, 15), 6);
+   sampler->ss0.lod_bias = S_FIXED(CLAMP(texUnit->LodBias +
+                                        gl_sampler->LodBias, -16, 15), 6);
 
    sampler->ss0.lod_preclamp = 1; /* OpenGL mode */
    sampler->ss0.default_color_mode = 0; /* OpenGL/DX10 mode */
 
-   /* Set BaseMipLevel, MaxLOD, MinLOD: 
-    *
-    * XXX: I don't think that using firstLevel, lastLevel works,
-    * because we always setup the surface state as if firstLevel ==
-    * level zero.  Probably have to subtract firstLevel from each of
-    * these:
+   int baselevel = texObj->BaseLevel - intel_image->mt->first_level;
+   sampler->ss0.base_level = U_FIXED(baselevel, 1);
+
+   sampler->ss1.max_lod = U_FIXED(CLAMP(baselevel +
+                                        gl_sampler->MaxLod, 0, 13), 6);
+   sampler->ss1.min_lod = U_FIXED(CLAMP(baselevel +
+                                        gl_sampler->MinLod, 0, 13), 6);
+
+   /* On Gen6+, the sampler can handle non-normalized texture
+    * rectangle coordinates natively
     */
-   sampler->ss0.base_level = U_FIXED(0, 1);
+   if (brw->gen >= 6 && texObj->Target == GL_TEXTURE_RECTANGLE) {
+      sampler->ss3.non_normalized_coord = 1;
+   }
+
+   upload_default_color(brw, gl_sampler, unit, sdc_offset);
 
-   sampler->ss1.max_lod = U_FIXED(CLAMP(key->maxlod, 0, 13), 6);
-   sampler->ss1.min_lod = U_FIXED(CLAMP(key->minlod, 0, 13), 6);
-   
-   sampler->ss2.default_color_pointer = sdc_bo->offset >> 5; /* reloc */
+   if (brw->gen >= 6) {
+      sampler->ss2.default_color_pointer = *sdc_offset >> 5;
+   } else {
+      /* reloc */
+      sampler->ss2.default_color_pointer = (brw->batch.bo->offset +
+                                           *sdc_offset) >> 5;
+
+      drm_intel_bo_emit_reloc(brw->batch.bo,
+                             sampler_state_table_offset +
+                             ss_index * sizeof(struct brw_sampler_state) +
+                             offsetof(struct brw_sampler_state, ss2),
+                             brw->batch.bo, *sdc_offset,
+                             I915_GEM_DOMAIN_SAMPLER, 0);
+   }
+
+   if (sampler->ss0.min_filter != BRW_MAPFILTER_NEAREST)
+      sampler->ss3.address_round |= BRW_ADDRESS_ROUNDING_ENABLE_U_MIN |
+                                    BRW_ADDRESS_ROUNDING_ENABLE_V_MIN |
+                                    BRW_ADDRESS_ROUNDING_ENABLE_R_MIN;
+   if (sampler->ss0.mag_filter != BRW_MAPFILTER_NEAREST)
+      sampler->ss3.address_round |= BRW_ADDRESS_ROUNDING_ENABLE_U_MAG |
+                                    BRW_ADDRESS_ROUNDING_ENABLE_V_MAG |
+                                    BRW_ADDRESS_ROUNDING_ENABLE_R_MAG;
 }
 
 
-/** Sets up the cache key for sampler state for all texture units */
 static void
-brw_wm_sampler_populate_key(struct brw_context *brw,
-                           struct wm_sampler_key *key)
+brw_upload_sampler_state_table(struct brw_context *brw,
+                               struct gl_program *prog,
+                               uint32_t sampler_count,
+                               uint32_t *sst_offset,
+                               uint32_t *sdc_offset)
 {
-   struct gl_context *ctx = &brw->intel.ctx;
-   int unit;
-   char *last_entry_end = ((char*)&key->sampler_count) + 
-      sizeof(key->sampler_count);
-
-   key->sampler_count = 0;
-
-   for (unit = 0; unit < BRW_MAX_TEX_UNIT; unit++) {
-      if (ctx->Texture.Unit[unit]._ReallyEnabled) {
-        struct wm_sampler_entry *entry = &key->sampler[unit];
-        struct gl_texture_unit *texUnit = &ctx->Texture.Unit[unit];
-        struct gl_texture_object *texObj = texUnit->_Current;
-        struct gl_texture_image *firstImage =
-           texObj->Image[0][texObj->BaseLevel];
-
-        memset(last_entry_end, 0, 
-               (char*)entry - last_entry_end + sizeof(*entry));
-        last_entry_end = ((char*)entry) + sizeof(*entry);
-
-         entry->tex_target = texObj->Target;
-
-        entry->seamless_cube_map = (texObj->Target == GL_TEXTURE_CUBE_MAP)
-           ? ctx->Texture.CubeMapSeamless : GL_FALSE;
-
-        entry->wrap_r = texObj->WrapR;
-        entry->wrap_s = texObj->WrapS;
-        entry->wrap_t = texObj->WrapT;
-
-        entry->maxlod = texObj->MaxLod;
-        entry->minlod = texObj->MinLod;
-        entry->lod_bias = texUnit->LodBias + texObj->LodBias;
-        entry->max_aniso = texObj->MaxAnisotropy;
-        entry->minfilter = texObj->MinFilter;
-        entry->magfilter = texObj->MagFilter;
-        entry->comparemode = texObj->CompareMode;
-         entry->comparefunc = texObj->CompareFunc;
-
-        drm_intel_bo_unreference(brw->wm.sdc_bo[unit]);
-        if (firstImage->_BaseFormat == GL_DEPTH_COMPONENT) {
-           float bordercolor[4] = {
-              texObj->BorderColor.f[0],
-              texObj->BorderColor.f[0],
-              texObj->BorderColor.f[0],
-              texObj->BorderColor.f[0]
-           };
-           /* GL specs that border color for depth textures is taken from the
-            * R channel, while the hardware uses A.  Spam R into all the
-            * channels for safety.
-            */
-           brw->wm.sdc_bo[unit] = upload_default_color(brw, bordercolor);
-        } else {
-           brw->wm.sdc_bo[unit] = upload_default_color(brw,
-                                                       texObj->BorderColor.f);
-        }
-        key->sampler_count = unit + 1;
+   struct gl_context *ctx = &brw->ctx;
+   struct brw_sampler_state *samplers;
+
+   GLbitfield SamplersUsed = prog->SamplersUsed;
+
+   if (sampler_count == 0)
+      return;
+
+   samplers = brw_state_batch(brw, AUB_TRACE_SAMPLER_STATE,
+                             sampler_count * sizeof(*samplers),
+                             32, sst_offset);
+   memset(samplers, 0, sampler_count * sizeof(*samplers));
+
+   for (unsigned s = 0; s < sampler_count; s++) {
+      if (SamplersUsed & (1 << s)) {
+         const unsigned unit = prog->SamplerUnits[s];
+         if (ctx->Texture.Unit[unit]._ReallyEnabled)
+            brw_update_sampler_state(brw, unit, s, &samplers[s],
+                                     *sst_offset, &sdc_offset[s]);
       }
    }
-   struct wm_sampler_entry *entry = &key->sampler[key->sampler_count];
-   memset(last_entry_end, 0, (char*)entry - last_entry_end);
+
+   brw->state.dirty.cache |= CACHE_NEW_SAMPLER;
 }
 
-/* All samplers must be uploaded in a single contiguous array, which
- * complicates various things.  However, this is still too confusing -
- * FIXME: simplify all the different new texture state flags.
- */
-static void upload_wm_samplers( struct brw_context *brw )
+static void
+brw_upload_fs_samplers(struct brw_context *brw)
 {
-   struct gl_context *ctx = &brw->intel.ctx;
-   struct wm_sampler_key key;
-   int i, sampler_key_size;
+   /* BRW_NEW_FRAGMENT_PROGRAM */
+   struct gl_program *fs = (struct gl_program *) brw->fragment_program;
+   brw->vtbl.upload_sampler_state_table(brw, fs,
+                                        brw->wm.sampler_count,
+                                        &brw->wm.sampler_offset,
+                                        brw->wm.sdc_offset);
+}
 
-   brw_wm_sampler_populate_key(brw, &key);
+const struct brw_tracked_state brw_fs_samplers = {
+   .dirty = {
+      .mesa = _NEW_TEXTURE,
+      .brw = BRW_NEW_BATCH |
+             BRW_NEW_FRAGMENT_PROGRAM,
+      .cache = 0
+   },
+   .emit = brw_upload_fs_samplers,
+};
 
-   if (brw->wm.sampler_count != key.sampler_count) {
-      brw->wm.sampler_count = key.sampler_count;
-      brw->state.dirty.cache |= CACHE_NEW_SAMPLER;
-   }
+static void
+brw_upload_vs_samplers(struct brw_context *brw)
+{
+   struct brw_stage_state *stage_state = &brw->vs.base;
+
+   /* BRW_NEW_VERTEX_PROGRAM */
+   struct gl_program *vs = (struct gl_program *) brw->vertex_program;
+   brw->vtbl.upload_sampler_state_table(brw, vs,
+                                        stage_state->sampler_count,
+                                        &stage_state->sampler_offset,
+                                        stage_state->sdc_offset);
+}
 
-   drm_intel_bo_unreference(brw->wm.sampler_bo);
-   brw->wm.sampler_bo = NULL;
-   if (brw->wm.sampler_count == 0)
-      return;
 
-   /* Only include the populated portion of the key in the search. */
-   sampler_key_size = offsetof(struct wm_sampler_key,
-                              sampler[key.sampler_count]);
-   brw->wm.sampler_bo = brw_search_cache(&brw->cache, BRW_SAMPLER,
-                                        &key, sampler_key_size,
-                                        brw->wm.sdc_bo, key.sampler_count,
-                                        NULL);
+const struct brw_tracked_state brw_vs_samplers = {
+   .dirty = {
+      .mesa = _NEW_TEXTURE,
+      .brw = BRW_NEW_BATCH |
+             BRW_NEW_VERTEX_PROGRAM,
+      .cache = 0
+   },
+   .emit = brw_upload_vs_samplers,
+};
 
-   /* If we didnt find it in the cache, compute the state and put it in the
-    * cache.
-    */
-   if (brw->wm.sampler_bo == NULL) {
-      struct brw_sampler_state sampler[BRW_MAX_TEX_UNIT];
 
-      memset(sampler, 0, sizeof(sampler));
-      for (i = 0; i < key.sampler_count; i++) {
-        if (brw->wm.sdc_bo[i] == NULL)
-           continue;
+static void
+brw_upload_gs_samplers(struct brw_context *brw)
+{
+   struct brw_stage_state *stage_state = &brw->gs.base;
 
-        brw_update_sampler_state(brw, &key.sampler[i], brw->wm.sdc_bo[i],
-                                 &sampler[i]);
-      }
+   /* BRW_NEW_GEOMETRY_PROGRAM */
+   struct gl_program *gs = (struct gl_program *) brw->geometry_program;
+   if (!gs)
+      return;
 
-      brw->wm.sampler_bo = brw_upload_cache(&brw->cache, BRW_SAMPLER,
-                                           &key, sampler_key_size,
-                                           brw->wm.sdc_bo, key.sampler_count,
-                                           &sampler, sizeof(sampler));
-
-      /* Emit SDC relocations */
-      for (i = 0; i < BRW_MAX_TEX_UNIT; i++) {
-        if (!ctx->Texture.Unit[i]._ReallyEnabled)
-           continue;
-
-        drm_intel_bo_emit_reloc(brw->wm.sampler_bo,
-                                i * sizeof(struct brw_sampler_state) +
-                                offsetof(struct brw_sampler_state, ss2),
-                                brw->wm.sdc_bo[i], 0,
-                                I915_GEM_DOMAIN_SAMPLER, 0);
-      }
-   }
+   brw->vtbl.upload_sampler_state_table(brw, gs,
+                                        stage_state->sampler_count,
+                                        &stage_state->sampler_offset,
+                                        stage_state->sdc_offset);
 }
 
-const struct brw_tracked_state brw_wm_samplers = {
+
+const struct brw_tracked_state brw_gs_samplers = {
    .dirty = {
       .mesa = _NEW_TEXTURE,
-      .brw = 0,
+      .brw = BRW_NEW_BATCH |
+             BRW_NEW_GEOMETRY_PROGRAM,
       .cache = 0
    },
-   .prepare = upload_wm_samplers,
+   .emit = brw_upload_gs_samplers,
 };
 
 
+void
+gen4_init_vtable_sampler_functions(struct brw_context *brw)
+{
+   brw->vtbl.upload_sampler_state_table = brw_upload_sampler_state_table;
+}