vrend_renderer_pipe_resource_set_type(struct vrend_context *ctx,
                                      uint32_t res_id,
                                      const struct vrend_renderer_resource_set_type_args *args)
{
   struct virgl_resource *res = NULL;

   /* look up the untyped resource */
   if (ctx->untyped_resource_cache &&
       ctx->untyped_resource_cache->res_id == res_id) {
      res = ctx->untyped_resource_cache;
      ctx->untyped_resource_cache = NULL;
   } else {
      /* cache miss */
      struct vrend_untyped_resource *iter;
      LIST_FOR_EACH_ENTRY(iter, &ctx->untyped_resources, head) {
         if (iter->resource->res_id == res_id) {
            res = iter->resource;
            list_del(&iter->head);
            free(iter);
            break;
         }
      }
   }

   /* either a bad res_id or the resource is already typed */
   if (!res) {
      if (vrend_renderer_ctx_res_lookup(ctx, res_id))
         return 0;

      vrend_report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, res_id);
      return EINVAL;
   }

   /* resource is still untyped */
   if (!res->pipe_resource) {
#ifdef HAVE_EPOXY_EGL_H
      const struct vrend_renderer_resource_create_args create_args = {
         .target = PIPE_TEXTURE_2D,
         .format = args->format,
         .bind = args->bind,
         .width = args->width,
         .height = args->height,
         .depth = 1,
         .array_size = 1,
         .last_level = 0,
         .nr_samples = 0,
         .flags = 0,
      };
      int plane_fds[VIRGL_GBM_MAX_PLANES];
      struct vrend_resource *gr;
      uint32_t virgl_format;
      uint32_t drm_format;
      int ret;

      if (res->fd_type != VIRGL_RESOURCE_FD_DMABUF)
         return EINVAL;

      for (uint32_t i = 0; i < args->plane_count; i++)
         plane_fds[i] = res->fd;

      gr = vrend_resource_create(&create_args);
      if (!gr)
         return ENOMEM;

      virgl_format = gr->base.format;
      drm_format = 0;
      if (virgl_gbm_convert_format(&virgl_format, &drm_format)) {
         vrend_printf("%s: unsupported format %d\n", __func__, virgl_format);
         FREE(gr);
         return EINVAL;
      }

      gr->egl_image = virgl_egl_image_from_dmabuf(egl,
                                                  args->width,
                                                  args->height,
                                                  drm_format,
                                                  args->modifier,
                                                  args->plane_count,
                                                  plane_fds,
                                                  args->plane_strides,
                                                  args->plane_offsets);
      if (!gr->egl_image) {
         vrend_printf("%s: failed to create egl image\n", __func__);
         FREE(gr);
         return EINVAL;
      }

      gr->storage_bits |= VREND_STORAGE_EGL_IMAGE;

      ret = vrend_resource_alloc_texture(gr, virgl_format, gr->egl_image);
      if (ret) {
         virgl_egl_image_destroy(egl, gr->egl_image);
         FREE(gr);
         return ret;
      }

      /* "promote" the fd to pipe_resource */
      close(res->fd);
      res->fd = -1;
      res->fd_type = VIRGL_RESOURCE_FD_INVALID;
      res->pipe_resource = &gr->base;
#else /* HAVE_EPOXY_EGL_H */
      (void)args;
      vrend_printf("%s: no EGL support \n", __func__);
      return EINVAL;
#endif /* HAVE_EPOXY_EGL_H */
   }

   vrend_ctx_resource_insert(ctx->res_hash,
                             res->res_id,
                             (struct vrend_resource *)res->pipe_resource);

   return 0;
}