static int vrend_transfer_send_getteximage(struct vrend_resource *res,
                                           const struct iovec *iov, int num_iovs,
                                           const struct vrend_transfer_info *info)
{
   GLenum format, type;
   uint32_t tex_size;
   char *data;
   int elsize = util_format_get_blocksize(res->base.format);
   int compressed = util_format_is_compressed(res->base.format);
   GLenum target;
   uint32_t send_offset = 0;
   format = tex_conv_table[res->base.format].glformat;
   type = tex_conv_table[res->base.format].gltype;

   if (compressed)
      format = tex_conv_table[res->base.format].internalformat;

   tex_size = util_format_get_nblocks(res->base.format, u_minify(res->base.width0, info->level), u_minify(res->base.height0, info->level)) *
              util_format_get_blocksize(res->base.format) * vrend_get_texture_depth(res, info->level);

   if (info->box->z && res->target != GL_TEXTURE_CUBE_MAP) {
      send_offset = util_format_get_nblocks(res->base.format, u_minify(res->base.width0, info->level), u_minify(res->base.height0, info->level)) * util_format_get_blocksize(res->base.format) * info->box->z;
   }

   data = malloc(tex_size);
   if (!data)
      return ENOMEM;

   switch (elsize) {
   case 1:
      glPixelStorei(GL_PACK_ALIGNMENT, 1);
      break;
   case 2:
      glPixelStorei(GL_PACK_ALIGNMENT, 2);
      break;
   case 4:
   default:
      glPixelStorei(GL_PACK_ALIGNMENT, 4);
      break;
   case 8:
      glPixelStorei(GL_PACK_ALIGNMENT, 8);
      break;
   }

   GLint old_tex = 0;
   get_current_texture(res->target, &old_tex);
   glBindTexture(res->target, res->id);
   if (res->target == GL_TEXTURE_CUBE_MAP) {
      target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + info->box->z;
   } else
      target = res->target;

   if (compressed) {
      if (has_feature(feat_arb_robustness)) {
         glGetnCompressedTexImageARB(target, info->level, tex_size, data);
      } else if (vrend_state.use_gles) {
         report_gles_missing_func(NULL, "glGetCompressedTexImage");
      } else {
         glGetCompressedTexImage(target, info->level, data);
      }
   } else {
      if (has_feature(feat_arb_robustness)) {
         glGetnTexImageARB(target, info->level, format, type, tex_size, data);
      } else if (vrend_state.use_gles) {
         report_gles_missing_func(NULL, "glGetTexImage");
      } else {
         glGetTexImage(target, info->level, format, type, data);
      }
   }

   glPixelStorei(GL_PACK_ALIGNMENT, 4);

   write_transfer_data(&res->base, iov, num_iovs, data + send_offset,
                       info->stride, info->box, info->level, info->offset,
                       false);
   free(data);
   glBindTexture(res->target, old_tex);
   return 0;
}
static int vrend_transfer_send_getteximage(struct vrend_resource *res,
                                           const struct iovec *iov, int num_iovs,
                                           const struct vrend_transfer_info *info)
{
   GLenum format, type;
   uint32_t tex_size;
   char *data;
   int elsize = util_format_get_blocksize(res->base.format);
   int compressed = util_format_is_compressed(res->base.format);
   GLenum target;
   uint32_t send_offset = 0;
   format = tex_conv_table[res->base.format].glformat;
   type = tex_conv_table[res->base.format].gltype;

   if (compressed)
      format = tex_conv_table[res->base.format].internalformat;

   tex_size = util_format_get_nblocks(res->base.format, u_minify(res->base.width0, info->level), u_minify(res->base.height0, info->level)) *
              util_format_get_blocksize(res->base.format) * vrend_get_texture_depth(res, info->level);

   if (info->box->z && res->target != GL_TEXTURE_CUBE_MAP) {
      send_offset = util_format_get_nblocks(res->base.format, u_minify(res->base.width0, info->level), u_minify(res->base.height0, info->level)) * util_format_get_blocksize(res->base.format) * info->box->z;
   }

   data = malloc(tex_size);
   if (!data)
      return ENOMEM;

   switch (elsize) {
   case 1:
      glPixelStorei(GL_PACK_ALIGNMENT, 1);
      break;
   case 2:
      glPixelStorei(GL_PACK_ALIGNMENT, 2);
      break;
   case 4:
   default:
      glPixelStorei(GL_PACK_ALIGNMENT, 4);
      break;
   case 8:
      glPixelStorei(GL_PACK_ALIGNMENT, 8);
      break;
   }

   GLint old_tex = 0;
   get_current_texture(res->target, &old_tex);
   glBindTexture(res->target, res->id);
   if (res->target == GL_TEXTURE_CUBE_MAP) {
      target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + info->box->z;
   } else
      target = res->target;

   if (compressed) {
      if (has_feature(feat_arb_robustness)) {
         glGetnCompressedTexImageARB(target, info->level, tex_size, data);
      } else if (vrend_state.use_gles) {
         report_gles_missing_func(NULL, "glGetCompressedTexImage");
      } else {
         glGetCompressedTexImage(target, info->level, data);
      }
   } else {
      if (has_feature(feat_arb_robustness)) {
         glGetnTexImageARB(target, info->level, format, type, tex_size, data);
      } else if (vrend_state.use_gles) {
         report_gles_missing_func(NULL, "glGetTexImage");
      } else {
         glGetTexImage(target, info->level, format, type, data);
      }
   }

   glPixelStorei(GL_PACK_ALIGNMENT, 4);

   write_transfer_data(&res->base, iov, num_iovs, data + send_offset,
                       info->stride, info->box, info->level, info->offset,
                       false);
   free(data);
   glBindTexture(res->target, old_tex);
   return 0;
}