static int vrend_resource_alloc_texture(struct vrend_resource *gr,
                                        enum virgl_formats format,
                                        void *image_oes)
{
   uint level;
   GLenum internalformat, glformat, gltype;
   struct vrend_texture *gt = (struct vrend_texture *)gr;
   struct pipe_resource *pr = &gr->base;

   const bool format_can_texture_storage = has_feature(feat_texture_storage) &&
        (tex_conv_table[format].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE);

   if (format_can_texture_storage)
      gr->storage_bits |= VREND_STORAGE_GL_IMMUTABLE;

   if (!image_oes) {
      vrend_resource_gbm_init(gr, format);
      if (gr->gbm_bo && !has_bit(gr->storage_bits, VREND_STORAGE_EGL_IMAGE))
         return 0;

      image_oes = gr->egl_image;
   }

   gr->target = tgsitargettogltarget(pr->target, pr->nr_samples);
   gr->storage_bits |= VREND_STORAGE_GL_TEXTURE;

   /* ugly workaround for texture rectangle missing on GLES */
   if (vrend_state.use_gles && gr->target == GL_TEXTURE_RECTANGLE_NV) {
      /* for some guests this is the only usage of rect */
      if (pr->width0 != 1 || pr->height0 != 1) {
         report_gles_warn(NULL, GLES_WARN_TEXTURE_RECT);
      }
      gr->target = GL_TEXTURE_2D;
   }

   /* fallback for 1D textures */
   if (vrend_state.use_gles && gr->target == GL_TEXTURE_1D) {
      gr->target = GL_TEXTURE_2D;
   }

   /* fallback for 1D array textures */
   if (vrend_state.use_gles && gr->target == GL_TEXTURE_1D_ARRAY) {
      gr->target = GL_TEXTURE_2D_ARRAY;
   }

   glGenTextures(1, &gr->id);
   glBindTexture(gr->target, gr->id);

   debug_texture(__func__, gr);

   if (image_oes) {
      if (has_bit(gr->storage_bits, VREND_STORAGE_GL_IMMUTABLE) &&
          has_feature(feat_egl_image_storage)) {
         glEGLImageTargetTexStorageEXT(gr->target, (GLeglImageOES) image_oes, NULL);
      } else if (has_feature(feat_egl_image_external)) {
         gr->storage_bits &= ~VREND_STORAGE_GL_IMMUTABLE;
         glEGLImageTargetTexture2DOES(gr->target, (GLeglImageOES) image_oes);
      } else {
         vrend_printf( "missing GL_OES_EGL_image_external extensions\n");
         glBindTexture(gr->target, 0);
         return EINVAL;
      }
      gr->storage_bits |= VREND_STORAGE_EGL_IMAGE;
   } else {
      internalformat = tex_conv_table[format].internalformat;
      glformat = tex_conv_table[format].glformat;
      gltype = tex_conv_table[format].gltype;

      if (internalformat == 0) {
         vrend_printf("unknown format is %d\n", pr->format);
         glBindTexture(gr->target, 0);
         return EINVAL;
      }

      if (pr->nr_samples > 0) {
         if (format_can_texture_storage) {
            if (gr->target == GL_TEXTURE_2D_MULTISAMPLE) {
               glTexStorage2DMultisample(gr->target, pr->nr_samples,
                                         internalformat, pr->width0, pr->height0,
                                         GL_TRUE);
            } else {
               glTexStorage3DMultisample(gr->target, pr->nr_samples,
                                         internalformat, pr->width0, pr->height0, pr->array_size,
                                         GL_TRUE);
            }
         } else {
            if (gr->target == GL_TEXTURE_2D_MULTISAMPLE) {
               glTexImage2DMultisample(gr->target, pr->nr_samples,
                                       internalformat, pr->width0, pr->height0,
                                       GL_TRUE);
            } else {
               glTexImage3DMultisample(gr->target, pr->nr_samples,
                                       internalformat, pr->width0, pr->height0, pr->array_size,
                                       GL_TRUE);
            }
         }
      } else if (gr->target == GL_TEXTURE_CUBE_MAP) {
            int i;
            if (format_can_texture_storage)
               glTexStorage2D(GL_TEXTURE_CUBE_MAP, pr->last_level + 1, internalformat, pr->width0, pr->height0);
            else {
               for (i = 0; i < 6; i++) {
                  GLenum ctarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
                  for (level = 0; level <= pr->last_level; level++) {
                     unsigned mwidth = u_minify(pr->width0, level);
                     unsigned mheight = u_minify(pr->height0, level);

                     glTexImage2D(ctarget, level, internalformat, mwidth, mheight, 0, glformat,
                                  gltype, NULL);
                  }
               }
            }
      } else if (gr->target == GL_TEXTURE_3D ||
                 gr->target == GL_TEXTURE_2D_ARRAY ||
                 gr->target == GL_TEXTURE_CUBE_MAP_ARRAY) {
         if (format_can_texture_storage) {
            unsigned depth_param = (gr->target == GL_TEXTURE_2D_ARRAY || gr->target == GL_TEXTURE_CUBE_MAP_ARRAY) ?
                                      pr->array_size : pr->depth0;
            glTexStorage3D(gr->target, pr->last_level + 1, internalformat, pr->width0, pr->height0, depth_param);
         } else {
            for (level = 0; level <= pr->last_level; level++) {
               unsigned depth_param = (gr->target == GL_TEXTURE_2D_ARRAY || gr->target == GL_TEXTURE_CUBE_MAP_ARRAY) ?
                                         pr->array_size : u_minify(pr->depth0, level);
               unsigned mwidth = u_minify(pr->width0, level);
               unsigned mheight = u_minify(pr->height0, level);
               glTexImage3D(gr->target, level, internalformat, mwidth, mheight,
                            depth_param, 0, glformat, gltype, NULL);
            }
         }
      } else if (gr->target == GL_TEXTURE_1D && vrend_state.use_gles) {
         report_gles_missing_func(NULL, "glTexImage1D");
      } else if (gr->target == GL_TEXTURE_1D) {
         if (format_can_texture_storage) {
            glTexStorage1D(gr->target, pr->last_level + 1, internalformat, pr->width0);
         } else {
            for (level = 0; level <= pr->last_level; level++) {
               unsigned mwidth = u_minify(pr->width0, level);
               glTexImage1D(gr->target, level, internalformat, mwidth, 0,
                            glformat, gltype, NULL);
            }
         }
      } else {
         if (format_can_texture_storage)
            glTexStorage2D(gr->target, pr->last_level + 1, internalformat, pr->width0,
                           gr->target == GL_TEXTURE_1D_ARRAY ? pr->array_size : pr->height0);
         else {
            for (level = 0; level <= pr->last_level; level++) {
               unsigned mwidth = u_minify(pr->width0, level);
               unsigned mheight = u_minify(pr->height0, level);
               glTexImage2D(gr->target, level, internalformat, mwidth,
                            gr->target == GL_TEXTURE_1D_ARRAY ? pr->array_size : mheight,
                            0, glformat, gltype, NULL);
            }
         }
      }
   }

   if (!format_can_texture_storage) {
      glTexParameteri(gr->target, GL_TEXTURE_BASE_LEVEL, 0);
      glTexParameteri(gr->target, GL_TEXTURE_MAX_LEVEL, pr->last_level);
   }

   glBindTexture(gr->target, 0);

   if (image_oes && gr->gbm_bo) {
#ifdef ENABLE_MINIGBM_ALLOCATION
      if (!has_bit(gr->storage_bits, VREND_STORAGE_GL_BUFFER) &&
            !vrend_format_can_texture_view(gr->base.format)) {
         for (int i = 0; i < gbm_bo_get_plane_count(gr->gbm_bo); i++) {
            gr->aux_plane_egl_image[i] =
                  virgl_egl_aux_plane_image_from_gbm_bo(egl, gr->gbm_bo, i);
         }
      }
#endif
   }

   gt->state.max_lod = -1;
   gt->cur_swizzle[0] = gt->cur_swizzle[1] = gt->cur_swizzle[2] = gt->cur_swizzle[3] = -1;
   gt->cur_base = -1;
   gt->cur_max = 10000;
   return 0;
}