int vrend_create_sampler_state(struct vrend_context *ctx,
                               uint32_t handle,
                               struct pipe_sampler_state *templ)
{
   struct vrend_sampler_state *state = CALLOC_STRUCT(vrend_sampler_state);
   int ret_handle;

   if (!state)
      return ENOMEM;

   state->base = *templ;

   if (has_feature(feat_samplers)) {
      glGenSamplers(2, state->ids);

      for (int i = 0; i < 2; ++i) {
         glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_S, convert_wrap(templ->wrap_s));
         glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_T, convert_wrap(templ->wrap_t));
         glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_R, convert_wrap(templ->wrap_r));
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MIN_FILTER, convert_min_filter(templ->min_img_filter, templ->min_mip_filter));
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MAG_FILTER, convert_mag_filter(templ->mag_img_filter));
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MIN_LOD, templ->min_lod);
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MAX_LOD, templ->max_lod);
         glSamplerParameteri(state->ids[i], GL_TEXTURE_COMPARE_MODE, templ->compare_mode ? GL_COMPARE_R_TO_TEXTURE : GL_NONE);
         glSamplerParameteri(state->ids[i], GL_TEXTURE_COMPARE_FUNC, GL_NEVER + templ->compare_func);
         if (vrend_state.use_gles) {
            if (templ->lod_bias)
               report_gles_warn(ctx, GLES_WARN_LOD_BIAS);
         } else
            glSamplerParameterf(state->ids[i], GL_TEXTURE_LOD_BIAS, templ->lod_bias);

         if (vrend_state.use_gles) {
            if (templ->seamless_cube_map != 0) {
               report_gles_warn(ctx, GLES_WARN_SEAMLESS_CUBE_MAP);
            }
         } else {
            glSamplerParameteri(state->ids[i], GL_TEXTURE_CUBE_MAP_SEAMLESS, templ->seamless_cube_map);

         }

         apply_sampler_border_color(state->ids[i], templ->border_color.ui);
         glSamplerParameteri(state->ids[i], GL_TEXTURE_SRGB_DECODE_EXT, i == 0 ? GL_SKIP_DECODE_EXT : GL_DECODE_EXT);
      }
   }
   ret_handle = vrend_renderer_object_insert(ctx, state, handle,
                                             VIRGL_OBJECT_SAMPLER_STATE);
   if (!ret_handle) {
      if (has_feature(feat_samplers))
         glDeleteSamplers(2, state->ids);
      FREE(state);
      return ENOMEM;
   }
   return 0;
}
int vrend_create_sampler_state(struct vrend_context *ctx,
                               uint32_t handle,
                               struct pipe_sampler_state *templ)
{
   struct vrend_sampler_state *state = CALLOC_STRUCT(vrend_sampler_state);
   int ret_handle;

   if (!state)
      return ENOMEM;

   state->base = *templ;

   if (has_feature(feat_samplers)) {
      glGenSamplers(2, state->ids);

      for (int i = 0; i < 2; ++i) {
         glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_S, convert_wrap(templ->wrap_s));
         glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_T, convert_wrap(templ->wrap_t));
         glSamplerParameteri(state->ids[i], GL_TEXTURE_WRAP_R, convert_wrap(templ->wrap_r));
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MIN_FILTER, convert_min_filter(templ->min_img_filter, templ->min_mip_filter));
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MAG_FILTER, convert_mag_filter(templ->mag_img_filter));
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MIN_LOD, templ->min_lod);
         glSamplerParameterf(state->ids[i], GL_TEXTURE_MAX_LOD, templ->max_lod);
         glSamplerParameteri(state->ids[i], GL_TEXTURE_COMPARE_MODE, templ->compare_mode ? GL_COMPARE_R_TO_TEXTURE : GL_NONE);
         glSamplerParameteri(state->ids[i], GL_TEXTURE_COMPARE_FUNC, GL_NEVER + templ->compare_func);
         if (vrend_state.use_gles) {
            if (templ->lod_bias)
               report_gles_warn(ctx, GLES_WARN_LOD_BIAS);
         } else
            glSamplerParameterf(state->ids[i], GL_TEXTURE_LOD_BIAS, templ->lod_bias);

         if (vrend_state.use_gles) {
            if (templ->seamless_cube_map != 0) {
               report_gles_warn(ctx, GLES_WARN_SEAMLESS_CUBE_MAP);
            }
         } else {
            glSamplerParameteri(state->ids[i], GL_TEXTURE_CUBE_MAP_SEAMLESS, templ->seamless_cube_map);

         }

         apply_sampler_border_color(state->ids[i], templ->border_color.ui);
         glSamplerParameteri(state->ids[i], GL_TEXTURE_SRGB_DECODE_EXT, i == 0 ? GL_SKIP_DECODE_EXT : GL_DECODE_EXT);
      }
   }
   ret_handle = vrend_renderer_object_insert(ctx, state, handle,
                                             VIRGL_OBJECT_SAMPLER_STATE);
   if (!ret_handle) {
      if (has_feature(feat_samplers))
         glDeleteSamplers(2, state->ids);
      FREE(state);
      return ENOMEM;
   }
   return 0;
}