static void do_readpixels(struct vrend_resource *res,
                          int idx, uint32_t level, uint32_t layer,
                          GLint x, GLint y,
                          GLsizei width, GLsizei height,
                          GLenum format, GLenum type,
                          GLsizei bufSize, void *data)
{
   GLuint fb_id;

   glGenFramebuffers(1, &fb_id);
   glBindFramebuffer(GL_FRAMEBUFFER, fb_id);

   vrend_fb_bind_texture(res, idx, level, layer);

   /* Warn if the driver doesn't agree about the read format and type.
      On desktop GL we can use basically any format and type to glReadPixels,
      so we picked the format and type that matches the native format.

      But on GLES we are limited to a very few set, luckily most GLES
      implementations should return type and format that match the native
      formats, and can be used for glReadPixels acording to the GLES spec.

      But we have found that at least Mesa returned the wrong formats, again
      luckily we are able to change Mesa. But just in case there are more bad
      drivers out there, or we mess up the format somewhere, we warn here. */
   if (vrend_state.use_gles) {
      GLint imp;
      if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_INT &&
          type != GL_INT && type != GL_FLOAT) {
         glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &imp);
         if (imp != (GLint)type) {
            vrend_printf( "GL_IMPLEMENTATION_COLOR_READ_TYPE is not expected native type 0x%x != imp 0x%x\n", type, imp);
         }
      }
      if (format != GL_RGBA && format != GL_RGBA_INTEGER) {
         glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &imp);
         if (imp != (GLint)format) {
            vrend_printf( "GL_IMPLEMENTATION_COLOR_READ_FORMAT is not expected native format 0x%x != imp 0x%x\n", format, imp);
         }
      }
   }

   /* read-color clamping is handled in the mesa frontend */
   if (!vrend_state.use_gles) {
       glClampColor(GL_CLAMP_READ_COLOR_ARB, GL_FALSE);
   }

   if (has_feature(feat_arb_robustness))
      glReadnPixelsARB(x, y, width, height, format, type, bufSize, data);
   else if (has_feature(feat_gles_khr_robustness))
      glReadnPixelsKHR(x, y, width, height, format, type, bufSize, data);
   else
      glReadPixels(x, y, width, height, format, type, data);

   glDeleteFramebuffers(1, &fb_id);
}
static void do_readpixels(struct vrend_resource *res,
                          int idx, uint32_t level, uint32_t layer,
                          GLint x, GLint y,
                          GLsizei width, GLsizei height,
                          GLenum format, GLenum type,
                          GLsizei bufSize, void *data)
{
   GLuint fb_id;

   glGenFramebuffers(1, &fb_id);
   glBindFramebuffer(GL_FRAMEBUFFER, fb_id);

   vrend_fb_bind_texture(res, idx, level, layer);

    
   if (vrend_state.use_gles) {
      GLint imp;
      if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_INT &&
          type != GL_INT && type != GL_FLOAT) {
         glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &imp);
         if (imp != (GLint)type) {
            vrend_printf( "GL_IMPLEMENTATION_COLOR_READ_TYPE is not expected native type 0x%x != imp 0x%x\n", type, imp);
         }
      }
      if (format != GL_RGBA && format != GL_RGBA_INTEGER) {
         glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &imp);
         if (imp != (GLint)format) {
            vrend_printf( "GL_IMPLEMENTATION_COLOR_READ_FORMAT is not expected native format 0x%x != imp 0x%x\n", format, imp);
         }
      }
   }

    
   if (!vrend_state.use_gles) {
       glClampColor(GL_CLAMP_READ_COLOR_ARB, GL_FALSE);
   }

   if (has_feature(feat_arb_robustness))
      glReadnPixelsARB(x, y, width, height, format, type, bufSize, data);
   else if (has_feature(feat_gles_khr_robustness))
      glReadnPixelsKHR(x, y, width, height, format, type, bufSize, data);
   else
      glReadPixels(x, y, width, height, format, type, data);

   glDeleteFramebuffers(1, &fb_id);
}