static void vrend_apply_sampler_state(struct vrend_sub_context *sub_ctx,
                                      struct vrend_resource *res,
                                      uint32_t shader_type,
                                      int id,
                                      int sampler_id,
                                      struct vrend_sampler_view *tview)
{
   struct vrend_texture *tex = (struct vrend_texture *)res;
   struct vrend_sampler_state *vstate = sub_ctx->sampler_state[shader_type][id];
   struct pipe_sampler_state *state = &vstate->base;
   bool set_all = false;
   GLenum target = tex->base.target;

   assert(offsetof(struct vrend_sampler_state, base) == 0);
   if (!state)
      return;

   if (res->base.nr_samples > 0) {
      tex->state = *state;
      return;
   }

   if (has_bit(tex->base.storage_bits, VREND_STORAGE_GL_BUFFER)) {
      tex->state = *state;
      return;
   }

   /*
    * If we emulate alpha format with red, we need to tell
    * the sampler to use the red channel and not the alpha one
    * by swizzling the GL_TEXTURE_BORDER_COLOR parameter.
    */
   bool is_emulated_alpha = vrend_format_is_emulated_alpha(tview->format);
   if (has_feature(feat_samplers)) {
      int sampler = vstate->ids[tview->srgb_decode == GL_SKIP_DECODE_EXT ? 0 : 1];
      if (is_emulated_alpha) {
         union pipe_color_union border_color;
         border_color = state->border_color;
         border_color.ui[0] = border_color.ui[3];
         border_color.ui[3] = 0;
         apply_sampler_border_color(sampler, border_color.ui);
      }

      glBindSampler(sampler_id, sampler);
      return;
   }

   if (tex->state.max_lod == -1)
      set_all = true;

   if (tex->state.wrap_s != state->wrap_s || set_all)
      glTexParameteri(target, GL_TEXTURE_WRAP_S, convert_wrap(state->wrap_s));
   if (tex->state.wrap_t != state->wrap_t || set_all)
      glTexParameteri(target, GL_TEXTURE_WRAP_T, convert_wrap(state->wrap_t));
   if (tex->state.wrap_r != state->wrap_r || set_all)
      glTexParameteri(target, GL_TEXTURE_WRAP_R, convert_wrap(state->wrap_r));
   if (tex->state.min_img_filter != state->min_img_filter ||
       tex->state.min_mip_filter != state->min_mip_filter || set_all)
      glTexParameterf(target, GL_TEXTURE_MIN_FILTER, convert_min_filter(state->min_img_filter, state->min_mip_filter));
   if (tex->state.mag_img_filter != state->mag_img_filter || set_all)
      glTexParameterf(target, GL_TEXTURE_MAG_FILTER, convert_mag_filter(state->mag_img_filter));
   if (res->target != GL_TEXTURE_RECTANGLE) {
      if (tex->state.min_lod != state->min_lod || set_all)
         glTexParameterf(target, GL_TEXTURE_MIN_LOD, state->min_lod);
      if (tex->state.max_lod != state->max_lod || set_all)
         glTexParameterf(target, GL_TEXTURE_MAX_LOD, state->max_lod);
      if (tex->state.lod_bias != state->lod_bias || set_all) {
         if (vrend_state.use_gles) {
            if (state->lod_bias)
               report_gles_warn(sub_ctx->parent, GLES_WARN_LOD_BIAS);
         } else {
            glTexParameterf(target, GL_TEXTURE_LOD_BIAS, state->lod_bias);
         }
      }
   }

   if (tex->state.compare_mode != state->compare_mode || set_all)
      glTexParameteri(target, GL_TEXTURE_COMPARE_MODE, state->compare_mode ? GL_COMPARE_R_TO_TEXTURE : GL_NONE);
   if (tex->state.compare_func != state->compare_func || set_all)
      glTexParameteri(target, GL_TEXTURE_COMPARE_FUNC, GL_NEVER + state->compare_func);
   if (has_feature(feat_anisotropic_filter) && (tex->state.max_anisotropy != state->max_anisotropy || set_all))
      glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY, state->max_anisotropy);

   /*
    * Oh this is a fun one. On GLES 2.0 all cubemap MUST NOT be seamless.
    * But on GLES 3.0 all cubemaps MUST be seamless. Either way there is no
    * way to toggle between the behaviour when running on GLES. And adding
    * warnings will spew the logs quite bad. Ignore and hope for the best.
    */
   if (!vrend_state.use_gles) {
      if (state->seamless_cube_map) {
         glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
      } else {
         glDisable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
      }
   }

   if (memcmp(&tex->state.border_color, &state->border_color, 16) || set_all ||
       is_emulated_alpha) {
      if (is_emulated_alpha) {
         union pipe_color_union border_color;
         border_color = state->border_color;
         border_color.ui[0] = border_color.ui[3];
         border_color.ui[3] = 0;
         glTexParameterIuiv(target, GL_TEXTURE_BORDER_COLOR, border_color.ui);
      } else {
         glTexParameterIuiv(target, GL_TEXTURE_BORDER_COLOR, state->border_color.ui);
      }

   }
   tex->state = *state;
}