Commit message: vrend: check that the transfer iov holds enough data for the data upload

Closes #140

Signed-off-by: Gert Wollny <gert.wollny@collabora.com>
Reviewed-by: Emil Velikov <emil.velikov@collabora.com>
CVE: CVE-2019-18391
CWE: CWE-787
Vulnerability description: A heap-based buffer overflow in the vrend_renderer_transfer_write_iov function in vrend_renderer.c in virglrenderer through 0.8.0 allows guest OS users to cause a denial of service via VIRGL_CCMD_RESOURCE_INLINE_WRITE commands.

Project: virglrenderer
File: vrend_renderer.c

void vrend_renderer_blit(struct vrend_context *ctx,
                         uint32_t dst_handle, uint32_t src_handle,
                         const struct pipe_blit_info *info)
{
   struct vrend_resource *src_res, *dst_res;
   src_res = vrend_renderer_ctx_res_lookup(ctx, src_handle);
   dst_res = vrend_renderer_ctx_res_lookup(ctx, dst_handle);

   if (!src_res) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, src_handle);
      return;
   }
   if (!dst_res) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, dst_handle);
      return;
   }

   if (ctx->in_error)
      return;

   if (!info->src.format || (enum virgl_formats)info->src.format >= VIRGL_FORMAT_MAX) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_FORMAT, info->src.format);
      return;
   }

   if (!info->dst.format || (enum virgl_formats)info->dst.format >= VIRGL_FORMAT_MAX) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_FORMAT, info->dst.format);
      return;
   }

   if (info->render_condition_enable == false)
      vrend_pause_render_condition(ctx, true);

   VREND_DEBUG(dbg_blit, ctx, "BLIT: rc:%d scissor:%d filter:%d alpha:%d mask:0x%x\n"
                                   "  From %s(%s) ms:%d [%d, %d, %d]+[%d, %d, %d] lvl:%d\n"
                                   "  To   %s(%s) ms:%d [%d, %d, %d]+[%d, %d, %d] lvl:%d\n",
                                   info->render_condition_enable, info->scissor_enable,
                                   info->filter, info->alpha_blend, info->mask,
                                   util_format_name(src_res->base.format),
                                   util_format_name(info->src.format),
                                   src_res->base.nr_samples,
                                   info->src.box.x, info->src.box.y, info->src.box.z,
                                   info->src.box.width, info->src.box.height, info->src.box.depth,
                                   info->src.level,
                                   util_format_name(dst_res->base.format),
                                   util_format_name(info->dst.format),
                                   dst_res->base.nr_samples,
                                   info->dst.box.x, info->dst.box.y, info->dst.box.z,
                                   info->dst.box.width, info->dst.box.height, info->dst.box.depth,
                                   info->dst.level);

   /* The Gallium blit function can be called for a general blit that may
    * scale, convert the data, and apply some rander states, or it is called via
    * glCopyImageSubData. If the src or the dst image are equal, or the two
    * images formats are the same, then Galliums such calles are redirected
    * to resource_copy_region, in this case and if no render states etx need
    * to be applied, forward the call to glCopyImageSubData, otherwise do a
    * normal blit. */
   if (has_feature(feat_copy_image) &&
       (!info->render_condition_enable || !ctx->sub->cond_render_gl_mode) &&
       format_is_copy_compatible(info->src.format,info->dst.format, false) &&
       !info->scissor_enable && (info->filter == PIPE_TEX_FILTER_NEAREST) &&
       !info->alpha_blend && (info->mask == PIPE_MASK_RGBA) &&
       src_res->base.nr_samples == dst_res->base.nr_samples &&
       info->src.box.width == info->dst.box.width &&
       info->src.box.height == info->dst.box.height &&
       info->src.box.depth == info->dst.box.depth) {
      VREND_DEBUG(dbg_blit, ctx,  "  Use glCopyImageSubData\n");
      vrend_copy_sub_image(src_res, dst_res, info->src.level, &info->src.box,
                           info->dst.level, info->dst.box.x, info->dst.box.y,
                           info->dst.box.z);
   } else {
      VREND_DEBUG(dbg_blit, ctx, "  Use blit_int\n");
      vrend_renderer_blit_int(ctx, src_res, dst_res, info);
   }

   if (info->render_condition_enable == false)
      vrend_pause_render_condition(ctx, false);
}

Project: virglrenderer
File: vrend_renderer.c

int vrend_create_sampler_view(struct vrend_context *ctx,
                              uint32_t handle,
                              uint32_t res_handle, uint32_t format,
                              uint32_t val0, uint32_t val1, uint32_t swizzle_packed)
{
   struct vrend_sampler_view *view;
   struct vrend_resource *res;
   int ret_handle;
   uint8_t swizzle[4];

   res = vrend_renderer_ctx_res_lookup(ctx, res_handle);
   if (!res) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, res_handle);
      return EINVAL;
   }

   view = CALLOC_STRUCT(vrend_sampler_view);
   if (!view)
      return ENOMEM;

   pipe_reference_init(&view->reference, 1);
   view->format = format & 0xffffff;

   if (!view->format || (enum virgl_formats)view->format >= VIRGL_FORMAT_MAX) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_FORMAT, view->format);
      FREE(view);
      return EINVAL;
   }

   uint32_t pipe_target = (format >> 24) & 0xff;
   if (pipe_target >= PIPE_MAX_TEXTURE_TYPES) {
      report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_SAMPLER_VIEW_TARGET,
                           view->format);
      FREE(view);
      return EINVAL;
   }

   view->target = tgsitargettogltarget(pipe_target, res->base.nr_samples);

   /* Work around TEXTURE_RECTANGLE and TEXTURE_1D missing on GLES */
   if (vrend_state.use_gles) {
      if (view->target == GL_TEXTURE_RECTANGLE_NV ||
          view->target == GL_TEXTURE_1D)
         view->target = GL_TEXTURE_2D;
      else if (view->target == GL_TEXTURE_1D_ARRAY)
         view->target = GL_TEXTURE_2D_ARRAY;
   }

   view->val0 = val0;
   view->val1 = val1;

   swizzle[0] = swizzle_packed & 0x7;
   swizzle[1] = (swizzle_packed >> 3) & 0x7;
   swizzle[2] = (swizzle_packed >> 6) & 0x7;
   swizzle[3] = (swizzle_packed >> 9) & 0x7;

   vrend_resource_reference(&view->texture, res);

   view->id = view->texture->id;
   if (view->target == PIPE_BUFFER)
      view->target = view->texture->target;

   view->srgb_decode = GL_DECODE_EXT;
   if (view->format != view->texture->base.format) {
      if (util_format_is_srgb(view->texture->base.format) &&
          !util_format_is_srgb(view->format))
         view->srgb_decode = GL_SKIP_DECODE_EXT;
   }

   if (!(util_format_has_alpha(view->format) || util_format_is_depth_or_stencil(view->format))) {
      if (swizzle[0] == PIPE_SWIZZLE_ALPHA)
          swizzle[0] = PIPE_SWIZZLE_ONE;
      if (swizzle[1] == PIPE_SWIZZLE_ALPHA)
          swizzle[1] = PIPE_SWIZZLE_ONE;
      if (swizzle[2] == PIPE_SWIZZLE_ALPHA)
          swizzle[2] = PIPE_SWIZZLE_ONE;
      if (swizzle[3] == PIPE_SWIZZLE_ALPHA)
          swizzle[3] = PIPE_SWIZZLE_ONE;
   }

   if (tex_conv_table[view->format].flags & VIRGL_TEXTURE_NEED_SWIZZLE) {
      if (swizzle[0] <= PIPE_SWIZZLE_ALPHA)
         swizzle[0] = tex_conv_table[view->format].swizzle[swizzle[0]];
      if (swizzle[1] <= PIPE_SWIZZLE_ALPHA)
         swizzle[1] = tex_conv_table[view->format].swizzle[swizzle[1]];
      if (swizzle[2] <= PIPE_SWIZZLE_ALPHA)
         swizzle[2] = tex_conv_table[view->format].swizzle[swizzle[2]];
      if (swizzle[3] <= PIPE_SWIZZLE_ALPHA)
         swizzle[3] = tex_conv_table[view->format].swizzle[swizzle[3]];
   }

   view->gl_swizzle_r = to_gl_swizzle(swizzle[0]);
   view->gl_swizzle_g = to_gl_swizzle(swizzle[1]);
   view->gl_swizzle_b = to_gl_swizzle(swizzle[2]);
   view->gl_swizzle_a = to_gl_swizzle(swizzle[3]);

   if (has_feature(feat_texture_view) &&
       view->texture->storage != VREND_RESOURCE_STORAGE_BUFFER) {
      enum pipe_format format;
      bool needs_view = false;

      /*
       * Need to use a texture view if the gallium
       * view target is different than the underlying
       * texture target.
       */
      if (view->target != view->texture->target)
         needs_view = true;

      /*
       * If the formats are different and this isn't
       * a DS texture a view is required.
       * DS are special as they use different gallium
       * formats for DS views into a combined resource.
       * GL texture views can't be use for this, stencil
       * texturing is used instead. For DS formats
       * aways program the underlying DS format as a
       * view could be required for layers.
       */
      format = view->format;
      if (util_format_is_depth_or_stencil(view->texture->base.format))
         format = view->texture->base.format;
      else if (view->format != view->texture->base.format)
         needs_view = true;
      if (needs_view && (tex_conv_table[view->texture->base.format].flags & VIRGL_TEXTURE_CAN_TEXTURE_STORAGE)) {
        glGenTextures(1, &view->id);
        GLenum internalformat = tex_conv_table[format].internalformat;
        unsigned base_layer = view->val0 & 0xffff;
        unsigned max_layer = (view->val0 >> 16) & 0xffff;
        int base_level = view->val1 & 0xff;
        int max_level = (view->val1 >> 8) & 0xff;

        glTextureView(view->id, view->target, view->texture->id, internalformat,
                      base_level, (max_level - base_level) + 1,
                      base_layer, max_layer - base_layer + 1);

        glBindTexture(view->target, view->id);

        if (util_format_is_depth_or_stencil(view->format)) {
           if (vrend_state.use_core_profile == false) {
              /* setting depth texture mode is deprecated in core profile */
              if (view->depth_texture_mode != GL_RED) {
                 glTexParameteri(view->target, GL_DEPTH_TEXTURE_MODE, GL_RED);
                 view->depth_texture_mode = GL_RED;
              }
           }
           if (has_feature(feat_stencil_texturing)) {
              const struct util_format_description *desc = util_format_description(view->format);
              if (!util_format_has_depth(desc)) {
                 glTexParameteri(view->target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
              } else {
                 glTexParameteri(view->target, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
              }
           }
        }

        glTexParameteri(view->target, GL_TEXTURE_BASE_LEVEL, base_level);
        glTexParameteri(view->target, GL_TEXTURE_MAX_LEVEL, max_level);
        glTexParameteri(view->target, GL_TEXTURE_SWIZZLE_R, view->gl_swizzle_r);
        glTexParameteri(view->target, GL_TEXTURE_SWIZZLE_G, view->gl_swizzle_g);
        glTexParameteri(view->target, GL_TEXTURE_SWIZZLE_B, view->gl_swizzle_b);
        glTexParameteri(view->target, GL_TEXTURE_SWIZZLE_A, view->gl_swizzle_a);
        if (util_format_is_srgb(view->format) &&
            has_feature(feat_texture_srgb_decode)) {
           glTexParameteri(view->target, GL_TEXTURE_SRGB_DECODE_EXT,
                            view->srgb_decode);
        }
        glBindTexture(view->target, 0);
     }
   }

   ret_handle = vrend_renderer_object_insert(ctx, view, sizeof(*view), handle, VIRGL_OBJECT_SAMPLER_VIEW);
   if (ret_handle == 0) {
      FREE(view);
      return ENOMEM;
   }
   return 0;
}

Project: virglrenderer
File: vrend_renderer.c

int vrend_renderer_resource_create(struct vrend_renderer_resource_create_args *args,
                                   struct iovec *iov, uint32_t num_iovs, void *image_oes)
{
   struct vrend_resource *gr;
   int ret;
   char error_string[256];

   ret = check_resource_valid(args, error_string);
   if (ret) {
      vrend_printf("%s, Illegal resource parameters, error: %s\n", __func__, error_string);
      return EINVAL;
   }

   gr = (struct vrend_resource *)CALLOC_STRUCT(vrend_texture);
   if (!gr)
      return ENOMEM;

   vrend_renderer_resource_copy_args(args, gr);
   gr->iov = iov;
   gr->num_iovs = num_iovs;

   if (args->flags & VIRGL_RESOURCE_Y_0_TOP)
      gr->y_0_top = true;

   pipe_reference_init(&gr->base.reference, 1);

   if (args->target == PIPE_BUFFER) {
      if (args->bind == VIRGL_BIND_CUSTOM) {
         /* use iovec directly when attached */
         gr->storage = VREND_RESOURCE_STORAGE_GUEST_ELSE_SYSTEM;
         gr->ptr = malloc(args->width);
         if (!gr->ptr) {
            FREE(gr);
            return ENOMEM;
         }
      } else if (args->bind == VIRGL_BIND_STAGING) {
         /* Staging buffers use only guest memory. */
         gr->storage = VREND_RESOURCE_STORAGE_GUEST;
      } else if (args->bind == VIRGL_BIND_INDEX_BUFFER) {
         gr->target = GL_ELEMENT_ARRAY_BUFFER_ARB;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind == VIRGL_BIND_STREAM_OUTPUT) {
         gr->target = GL_TRANSFORM_FEEDBACK_BUFFER;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind == VIRGL_BIND_VERTEX_BUFFER) {
         gr->target = GL_ARRAY_BUFFER_ARB;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind == VIRGL_BIND_CONSTANT_BUFFER) {
         gr->target = GL_UNIFORM_BUFFER;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind == VIRGL_BIND_QUERY_BUFFER) {
         gr->target = GL_QUERY_BUFFER;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind == VIRGL_BIND_COMMAND_ARGS) {
         gr->target = GL_DRAW_INDIRECT_BUFFER;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind == 0 || args->bind == VIRGL_BIND_SHADER_BUFFER) {
         gr->target = GL_ARRAY_BUFFER_ARB;
         vrend_create_buffer(gr, args->width);
      } else if (args->bind & VIRGL_BIND_SAMPLER_VIEW) {
         /*
       * On Desktop we use GL_ARB_texture_buffer_object on GLES we use
       * GL_EXT_texture_buffer (it is in the ANDRIOD extension pack).
       */
#if GL_TEXTURE_BUFFER != GL_TEXTURE_BUFFER_EXT
#error "GL_TEXTURE_BUFFER enums differ, they shouldn't."
#endif

      /* need to check GL version here */
         if (has_feature(feat_arb_or_gles_ext_texture_buffer)) {
            gr->target = GL_TEXTURE_BUFFER;
         } else {
            gr->target = GL_PIXEL_PACK_BUFFER_ARB;
         }
         vrend_create_buffer(gr, args->width);
      } else {
         vrend_printf("%s: Illegal buffer binding flags 0x%x\n", __func__, args->bind);
         FREE(gr);
         return EINVAL;
      }
   } else {
      int r = vrend_renderer_resource_allocate_texture(gr, image_oes);
      if (r) {
         FREE(gr);
         return r;
      }
   }

   ret = vrend_resource_insert(gr, args->handle);
   if (ret == 0) {
      vrend_renderer_resource_destroy(gr);
      return ENOMEM;
   }
   return 0;
}

Project: virglrenderer
File: vrend_renderer.c

static void vrend_renderer_blit_int(struct vrend_context *ctx,
                                    struct vrend_resource *src_res,
                                    struct vrend_resource *dst_res,
                                    const struct pipe_blit_info *info)
{
   GLbitfield glmask = 0;
   int src_y1, src_y2, dst_y1, dst_y2;
   GLenum filter;
   int n_layers = 1, i;
   bool use_gl = false;
   bool make_intermediate_copy = false;
   bool skip_dest_swizzle = false;
   GLuint intermediate_fbo = 0;
   struct vrend_resource *intermediate_copy = 0;

   GLuint blitter_views[2] = {src_res->id, dst_res->id};

   filter = convert_mag_filter(info->filter);

   /* if we can't make FBO's use the fallback path */
   if (!vrend_format_can_render(src_res->base.format) &&
       !vrend_format_is_ds(src_res->base.format))
      use_gl = true;
   if (!vrend_format_can_render(dst_res->base.format) &&
       !vrend_format_is_ds(dst_res->base.format))
      use_gl = true;

   /* different depth formats */
   if (vrend_format_is_ds(src_res->base.format) &&
       vrend_format_is_ds(dst_res->base.format)) {
      if (src_res->base.format != dst_res->base.format) {
         if (!(src_res->base.format == PIPE_FORMAT_S8_UINT_Z24_UNORM &&
               (dst_res->base.format == PIPE_FORMAT_Z24X8_UNORM))) {
            use_gl = true;
         }
      }
   }
   /* glBlitFramebuffer - can support depth stencil with NEAREST
      which we use for mipmaps */
   if ((info->mask & (PIPE_MASK_Z | PIPE_MASK_S)) && info->filter == PIPE_TEX_FILTER_LINEAR)
      use_gl = true;

   /* for scaled MS blits we either need extensions or hand roll */
   if (info->mask & PIPE_MASK_RGBA &&
       src_res->base.nr_samples > 0 &&
       src_res->base.nr_samples != dst_res->base.nr_samples &&
       (info->src.box.width != info->dst.box.width ||
        info->src.box.height != info->dst.box.height)) {
      if (has_feature(feat_ms_scaled_blit))
         filter = GL_SCALED_RESOLVE_NICEST_EXT;
      else
         use_gl = true;
   }

   if (!dst_res->y_0_top) {
      dst_y1 = info->dst.box.y + info->dst.box.height;
      dst_y2 = info->dst.box.y;
   } else {
      dst_y1 = dst_res->base.height0 - info->dst.box.y - info->dst.box.height;
      dst_y2 = dst_res->base.height0 - info->dst.box.y;
   }

   if (!src_res->y_0_top) {
      src_y1 = info->src.box.y + info->src.box.height;
      src_y2 = info->src.box.y;
   } else {
      src_y1 = src_res->base.height0 - info->src.box.y - info->src.box.height;
      src_y2 = src_res->base.height0 - info->src.box.y;
   }

   /* GLES generally doesn't support blitting to a multi-sample FB, and also not
    * from a multi-sample FB where the regions are not exatly the same or the
    * source and target format are different. For
    * downsampling DS blits to zero samples we solve this by doing two blits */
   if (vrend_state.use_gles &&
       ((dst_res->base.nr_samples > 0) ||
        ((info->mask & PIPE_MASK_RGBA) &&
         (src_res->base.nr_samples > 0) &&
         (info->src.box.x != info->dst.box.x ||
          info->src.box.width != info->dst.box.width ||
          dst_y1 != src_y1 || dst_y2 != src_y2 ||
          info->src.format != info->dst.format))
        )
       ) {
      VREND_DEBUG(dbg_blit, ctx, "Use GL fallback because dst:ms:%d src:ms:%d (%d %d %d %d) -> (%d %d %d %d)\n",
                  dst_res->base.nr_samples, src_res->base.nr_samples, info->src.box.x, info->src.box.x + info->src.box.width,
                  src_y1, src_y2, info->dst.box.x, info->dst.box.x + info->dst.box.width, dst_y1, dst_y2);
      use_gl = true;
   }

   /* for 3D mipmapped blits - hand roll time */
   if (info->src.box.depth != info->dst.box.depth)
      use_gl = true;

   if (vrend_blit_needs_swizzle(vrend_format_replace_emulated(dst_res->base.bind, info->dst.format),
                                vrend_format_replace_emulated(src_res->base.bind, info->src.format))) {
      use_gl = true;

      if (vrend_state.use_gles &&
          (dst_res->base.bind & VIRGL_BIND_PREFER_EMULATED_BGRA) &&
          !vrend_get_tweak_is_active(&ctx->sub->tweaks, virgl_tweak_gles_brga_apply_dest_swizzle)) {
         skip_dest_swizzle = true;
      }
   }

   if (has_feature(feat_texture_view))
      blitter_views[0] = vrend_make_view(src_res, info->src.format);

   if ((dst_res->base.format != info->dst.format) && has_feature(feat_texture_view))
      blitter_views[1] = vrend_make_view(dst_res, info->dst.format);


   if (use_gl) {
      VREND_DEBUG(dbg_blit, ctx, "BLIT_INT: use GL fallback\n");
      vrend_renderer_blit_gl(ctx, src_res, dst_res, blitter_views, info,
                             has_feature(feat_texture_srgb_decode),
                             has_feature(feat_srgb_write_control),
                             skip_dest_swizzle);
      vrend_clicbs->make_current(ctx->sub->gl_context);
      goto cleanup;
   }

   if (info->mask & PIPE_MASK_Z)
      glmask |= GL_DEPTH_BUFFER_BIT;
   if (info->mask & PIPE_MASK_S)
      glmask |= GL_STENCIL_BUFFER_BIT;
   if (info->mask & PIPE_MASK_RGBA)
      glmask |= GL_COLOR_BUFFER_BIT;


   if (info->scissor_enable) {
      glScissor(info->scissor.minx, info->scissor.miny, info->scissor.maxx - info->scissor.minx, info->scissor.maxy - info->scissor.miny);
      ctx->sub->scissor_state_dirty = (1 << 0);
      glEnable(GL_SCISSOR_TEST);
   } else
      glDisable(GL_SCISSOR_TEST);

   /* An GLES GL_INVALID_OPERATION is generated if one wants to blit from a
    * multi-sample fbo to a non multi-sample fbo and the source and destination
    * rectangles are not defined with the same (X0, Y0) and (X1, Y1) bounds.
    *
    * Since stencil data can only be written in a fragment shader when
    * ARB_shader_stencil_export is available, the workaround using GL as given
    * above is usually not available. Instead, to work around the blit
    * limitations on GLES first copy the full frame to a non-multisample
    * surface and then copy the according area to the final target surface.
    */
   if (vrend_state.use_gles &&
       (info->mask & PIPE_MASK_ZS) &&
       ((src_res->base.nr_samples > 0) &&
        (src_res->base.nr_samples != dst_res->base.nr_samples)) &&
        ((info->src.box.x != info->dst.box.x) ||
         (src_y1 != dst_y1) ||
         (info->src.box.width != info->dst.box.width) ||
         (src_y2 != dst_y2))) {

      make_intermediate_copy = true;

      /* Create a texture that is the same like the src_res texture, but
       * without multi-sample */
      struct vrend_renderer_resource_create_args args;
      memset(&args, 0, sizeof(struct vrend_renderer_resource_create_args));
      args.width = src_res->base.width0;
      args.height = src_res->base.height0;
      args.depth = src_res->base.depth0;
      args.format = info->src.format;
      args.target = src_res->base.target;
      args.last_level = src_res->base.last_level;
      args.array_size = src_res->base.array_size;
      intermediate_copy = (struct vrend_resource *)CALLOC_STRUCT(vrend_texture);
      vrend_renderer_resource_copy_args(&args, intermediate_copy);
      MAYBE_UNUSED int r = vrend_renderer_resource_allocate_texture(intermediate_copy, NULL);
      assert(!r);

      glGenFramebuffers(1, &intermediate_fbo);
   } else {
      /* If no intermediate copy is needed make the variables point to the
       * original source to simplify the code below.
       */
      intermediate_fbo = ctx->sub->blit_fb_ids[0];
      intermediate_copy = src_res;
   }

   glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
   if (info->mask & PIPE_MASK_RGBA)
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                             GL_TEXTURE_2D, 0, 0);
   else
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                             GL_TEXTURE_2D, 0, 0);
   glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);
   if (info->mask & PIPE_MASK_RGBA)
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                             GL_TEXTURE_2D, 0, 0);
   else if (info->mask & (PIPE_MASK_Z | PIPE_MASK_S))
      glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                             GL_TEXTURE_2D, 0, 0);
   if (info->src.box.depth == info->dst.box.depth)
      n_layers = info->dst.box.depth;
   for (i = 0; i < n_layers; i++) {
      glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
      vrend_fb_bind_texture_id(src_res, blitter_views[0], 0, info->src.level, info->src.box.z + i);

      if (make_intermediate_copy) {
         int level_width = u_minify(src_res->base.width0, info->src.level);
         int level_height = u_minify(src_res->base.width0, info->src.level);
         glBindFramebuffer(GL_FRAMEBUFFER, intermediate_fbo);
         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                                GL_TEXTURE_2D, 0, 0);
         vrend_fb_bind_texture(intermediate_copy, 0, info->src.level, info->src.box.z + i);

         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediate_fbo);
         glBindFramebuffer(GL_READ_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
         glBlitFramebuffer(0, 0, level_width, level_height,
                           0, 0, level_width, level_height,
                           glmask, filter);
      }

      glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);
      vrend_fb_bind_texture_id(dst_res, blitter_views[1], 0, info->dst.level, info->dst.box.z + i);
      glBindFramebuffer(GL_DRAW_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);

      if (has_feature(feat_srgb_write_control)) {
         if (util_format_is_srgb(info->dst.format) ||
             util_format_is_srgb(info->src.format))
            glEnable(GL_FRAMEBUFFER_SRGB);
         else
            glDisable(GL_FRAMEBUFFER_SRGB);
      }

      glBindFramebuffer(GL_READ_FRAMEBUFFER, intermediate_fbo);

      glBlitFramebuffer(info->src.box.x,
                        src_y1,
                        info->src.box.x + info->src.box.width,
                        src_y2,
                        info->dst.box.x,
                        dst_y1,
                        info->dst.box.x + info->dst.box.width,
                        dst_y2,
                        glmask, filter);
   }

   glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[1]);
   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                          GL_TEXTURE_2D, 0, 0);
   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                          GL_TEXTURE_2D, 0, 0);

   glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->blit_fb_ids[0]);
   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                          GL_TEXTURE_2D, 0, 0);
   glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
                          GL_TEXTURE_2D, 0, 0);

   glBindFramebuffer(GL_FRAMEBUFFER, ctx->sub->fb_id);

   if (has_feature(feat_srgb_write_control)) {
      if (ctx->sub->framebuffer_srgb_enabled)
         glEnable(GL_FRAMEBUFFER_SRGB);
      else
         glDisable(GL_FRAMEBUFFER_SRGB);
   }

   if (make_intermediate_copy) {
      vrend_renderer_resource_destroy(intermediate_copy);
      glDeleteFramebuffers(1, &intermediate_fbo);
   }

   if (ctx->sub->rs_state.scissor)
      glEnable(GL_SCISSOR_TEST);
   else
      glDisable(GL_SCISSOR_TEST);

cleanup:
   if (blitter_views[0] != src_res->id)
      glDeleteTextures(1, &blitter_views[0]);

   if (blitter_views[1] != dst_res->id)
      glDeleteTextures(1, &blitter_views[1]);
}

Project: virglrenderer
File: vrend_renderer.c

static int check_resource_valid(struct vrend_renderer_resource_create_args *args,
                                char errmsg[256])
{
   /* do not accept handle 0 */
   if (args->handle == 0) {
      snprintf(errmsg, 256, "Invalid handle");
      return -1;
   }

   /* limit the target */
   if (args->target >= PIPE_MAX_TEXTURE_TYPES) {
      snprintf(errmsg, 256, "Invalid texture target %d (>= %d)",
               args->target, PIPE_MAX_TEXTURE_TYPES);
      return -1;
   }

   if (args->format >= VIRGL_FORMAT_MAX) {
      snprintf(errmsg, 256, "Invalid texture format %d (>=%d)",
               args->format, VIRGL_FORMAT_MAX);
      return -1;
   }

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

   /* only texture 2d and 2d array can have multiple samples */
   if (args->nr_samples > 0) {
      if (!has_feature(feat_texture_multisample)) {
         snprintf(errmsg, 256, "Multisample textures not supported");
         return -1;
      }

      if (args->target != PIPE_TEXTURE_2D && args->target != PIPE_TEXTURE_2D_ARRAY) {
         snprintf(errmsg, 256, "Multisample textures not 2D (target:%d)", args->target);
         return -1;
      }
      /* multisample can't have miplevels */
      if (args->last_level > 0) {
         snprintf(errmsg, 256, "Multisample textures don't support mipmaps");
         return -1;
      }
   }

   if (args->last_level > 0) {
      /* buffer and rect textures can't have mipmaps */
      if (args->target == PIPE_BUFFER) {
         snprintf(errmsg, 256, "Buffers don't support mipmaps");
         return -1;
      }

      if (args->target == PIPE_TEXTURE_RECT) {
         snprintf(errmsg, 256, "RECT textures don't support mipmaps");
         return -1;
      }

      if (args->last_level > (floor(log2(MAX2(args->width, args->height))) + 1)) {
         snprintf(errmsg, 256, "Mipmap levels %d too large for texture size (%d, %d)",
                  args->last_level, args->width, args->height);
         return -1;
      }
   }

   if (args->flags != 0 && args->flags != VIRGL_RESOURCE_Y_0_TOP) {
      snprintf(errmsg, 256, "Resource flags 0x%x not supported", args->flags);
      return -1;
   }

   if (args->flags & VIRGL_RESOURCE_Y_0_TOP) {
      if (args->target != PIPE_TEXTURE_2D && args->target != PIPE_TEXTURE_RECT) {
         snprintf(errmsg, 256, "VIRGL_RESOURCE_Y_0_TOP only supported for 2D or RECT textures");
         return -1;
      }
   }

   /* array size for array textures only */
   if (args->target == PIPE_TEXTURE_CUBE) {
      if (args->array_size != 6) {
         snprintf(errmsg, 256, "Cube map: unexpected array size %d", args->array_size);
         return -1;
      }
   } else if (args->target == PIPE_TEXTURE_CUBE_ARRAY) {
      if (!has_feature(feat_cube_map_array)) {
         snprintf(errmsg, 256, "Cube map arrays not supported");
         return -1;
      }
      if (args->array_size % 6) {
         snprintf(errmsg, 256, "Cube map array: unexpected array size %d", args->array_size);
         return -1;
      }
   } else if (args->array_size > 1) {
      if (args->target != PIPE_TEXTURE_2D_ARRAY &&
          args->target != PIPE_TEXTURE_1D_ARRAY) {
         snprintf(errmsg, 256, "Texture target %d can't be an array ", args->target);
         return -1;
      }

      if (!has_feature(feat_texture_array)) {
         snprintf(errmsg, 256, "Texture arrays are not supported");
         return -1;
      }
   }

   if (format_can_texture_storage && !args->width) {
      snprintf(errmsg, 256, "Texture storage texture width must be >0");
      return -1;
   }

   if (args->bind == 0 ||
       args->bind == VIRGL_BIND_CUSTOM ||
       args->bind == VIRGL_BIND_STAGING ||
       args->bind == VIRGL_BIND_INDEX_BUFFER ||
       args->bind == VIRGL_BIND_STREAM_OUTPUT ||
       args->bind == VIRGL_BIND_VERTEX_BUFFER ||
       args->bind == VIRGL_BIND_CONSTANT_BUFFER ||
       args->bind == VIRGL_BIND_QUERY_BUFFER ||
       args->bind == VIRGL_BIND_COMMAND_ARGS ||
       args->bind == VIRGL_BIND_SHADER_BUFFER) {
      if (args->target != PIPE_BUFFER) {
         snprintf(errmsg, 256, "Buffer bind flags requre the buffer target but this is target %d", args->target);
         return -1;
      }
      if (args->height != 1 || args->depth != 1) {
         snprintf(errmsg, 256, "Buffer target: Got height=%u, depth=%u, expect (1,1)", args->height, args->depth);
         return -1;
      }
      if (args->bind == VIRGL_BIND_QUERY_BUFFER && !has_feature(feat_qbo)) {
         snprintf(errmsg, 256, "Query buffers are not supported");
         return -1;
      }
      if (args->bind == VIRGL_BIND_COMMAND_ARGS && !has_feature(feat_indirect_draw)) {
         snprintf(errmsg, 256, "Command args buffer requested but indirect draw is not supported");
         return -1;
      }
   } else {
      if (!((args->bind & VIRGL_BIND_SAMPLER_VIEW) ||
            (args->bind & VIRGL_BIND_DEPTH_STENCIL) ||
            (args->bind & VIRGL_BIND_RENDER_TARGET) ||
            (args->bind & VIRGL_BIND_CURSOR) ||
            (args->bind & VIRGL_BIND_SHARED) ||
            (args->bind & VIRGL_BIND_LINEAR))) {
         snprintf(errmsg, 256, "Invalid texture bind flags 0x%x", args->bind);
         return -1;
      }

      if (args->target == PIPE_TEXTURE_2D ||
          args->target == PIPE_TEXTURE_RECT ||
          args->target == PIPE_TEXTURE_CUBE ||
          args->target == PIPE_TEXTURE_2D_ARRAY ||
          args->target == PIPE_TEXTURE_CUBE_ARRAY) {
         if (args->depth != 1) {
            snprintf(errmsg, 256, "2D texture target with depth=%u != 1", args->depth);
            return -1;
         }
         if (format_can_texture_storage && !args->height) {
            snprintf(errmsg, 256, "2D Texture storage requires non-zero height");
            return -1;
         }
      }
      if (args->target == PIPE_TEXTURE_1D ||
          args->target == PIPE_TEXTURE_1D_ARRAY) {
         if (args->height != 1 || args->depth != 1) {
            snprintf(errmsg, 256, "Got height=%u, depth=%u, expect (1,1)",
                     args->height, args->depth);
            return -1;
         }
         if (args->width > vrend_state.max_texture_2d_size) {
            snprintf(errmsg, 256, "1D Texture width (%u) exceeds supported value (%u)",
                     args->width, vrend_state.max_texture_2d_size);
            return -1;
         }
      }

      if (args->target == PIPE_TEXTURE_2D ||
          args->target == PIPE_TEXTURE_RECT ||
          args->target == PIPE_TEXTURE_2D_ARRAY) {
         if (args->width > vrend_state.max_texture_2d_size ||
             args->height > vrend_state.max_texture_2d_size) {
            snprintf(errmsg, 256, "2D Texture size components (%u, %u) exceeds supported value (%u)",
                     args->width, args->height, vrend_state.max_texture_2d_size);
            return -1;
         }
      }

      if (args->target == PIPE_TEXTURE_3D) {
         if (format_can_texture_storage &&
             (!args->height || !args->depth)) {
            snprintf(errmsg, 256, "Texture storage expects non-zero height (%u) and depth (%u)",
                     args->height, args->depth);
            return -1;
         }
         if (args->width > vrend_state.max_texture_3d_size ||
             args->height > vrend_state.max_texture_3d_size ||
             args->depth > vrend_state.max_texture_3d_size) {
            snprintf(errmsg, 256, "3D Texture sizes (%u, %u, %u) exceeds supported value (%u)",
                     args->width, args->height, args->depth,
                     vrend_state.max_texture_3d_size);
            return -1;
         }
      }
      if (args->target == PIPE_TEXTURE_2D_ARRAY ||
          args->target == PIPE_TEXTURE_CUBE_ARRAY ||
          args->target == PIPE_TEXTURE_1D_ARRAY) {
         if (format_can_texture_storage &&
             !args->array_size) {
            snprintf(errmsg, 256, "Texture arrays require a non-zero arrays size "
                                  "when allocated with glTexStorage");
            return -1;
         }
      }
      if (args->target == PIPE_TEXTURE_CUBE ||
          args->target == PIPE_TEXTURE_CUBE_ARRAY) {
         if (args->width != args->height) {
            snprintf(errmsg, 256, "Cube maps require width (%u) == height (%u)",
                     args->width, args->height);
            return -1;
         }
         if (args->width > vrend_state.max_texture_cube_size) {
            snprintf(errmsg, 256, "Cube maps size (%u) exceeds supported value (%u)",
                     args->width, vrend_state.max_texture_cube_size);
            return -1;
         }
      }
   }
   return 0;
}

Project: virglrenderer
File: vrend_renderer.c

int vrend_renderer_transfer_iov(const struct vrend_transfer_info *info,
                                int transfer_mode)
{
   struct vrend_resource *res;
   struct vrend_context *ctx;
   struct iovec *iov;
   int num_iovs;

   if (!info->box)
      return EINVAL;

   ctx = vrend_lookup_renderer_ctx(info->ctx_id);
   if (!ctx)
      return EINVAL;

   if (info->ctx_id == 0)
      res = vrend_resource_lookup(info->handle, 0);
   else
      res = vrend_renderer_ctx_res_lookup(ctx, info->handle);

   if (!res) {
      if (info->ctx_id)
         report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, info->handle);
      return EINVAL;
   }

   iov = info->iovec;
   num_iovs = info->iovec_cnt;

   if (res->iov && (!iov || num_iovs == 0)) {
      iov = res->iov;
      num_iovs = res->num_iovs;
   }

   if (!iov) {
      if (info->ctx_id)
         report_context_error(ctx, VIRGL_ERROR_CTX_ILLEGAL_RESOURCE, info->handle);
      return EINVAL;
   }

#ifdef ENABLE_GBM_ALLOCATION
   if (res->gbm_bo)
      return virgl_gbm_transfer(res->gbm_bo, transfer_mode, iov, num_iovs, info);
#endif

   if (!check_transfer_bounds(res, info)) {
      report_context_error(ctx, VIRGL_ERROR_CTX_TRANSFER_IOV_BOUNDS, res->id);
      return EINVAL;
   }

   if (!check_iov_bounds(res, info, iov, num_iovs)) {
      report_context_error(ctx, VIRGL_ERROR_CTX_TRANSFER_IOV_BOUNDS, res->id);
      return EINVAL;
   }

   if (info->context0) {
      vrend_renderer_force_ctx_0();
      ctx = NULL;
   }

   switch (transfer_mode) {
   case VIRGL_TRANSFER_TO_HOST:
      return vrend_renderer_transfer_write_iov(ctx, res, iov, num_iovs, info);
   case VIRGL_TRANSFER_FROM_HOST:
      return vrend_renderer_transfer_send_iov(res, iov, num_iovs, info);

   default:
      assert(0);
   }
   return 0;
}

Project: virglrenderer
File: vrend_renderer.c

static int vrend_renderer_transfer_write_iov(struct vrend_context *ctx,
                                             struct vrend_resource *res,
                                             struct iovec *iov, int num_iovs,
                                             const struct vrend_transfer_info *info)
{
   void *data;

   if (res->storage == VREND_RESOURCE_STORAGE_GUEST ||
       (res->storage == VREND_RESOURCE_STORAGE_GUEST_ELSE_SYSTEM && res->iov)) {
      return vrend_copy_iovec(iov, num_iovs, info->offset,
                              res->iov, res->num_iovs, info->box->x,
                              info->box->width, res->ptr);
   }

   if (res->storage == VREND_RESOURCE_STORAGE_GUEST_ELSE_SYSTEM) {
      assert(!res->iov);
      vrend_read_from_iovec(iov, num_iovs, info->offset,
                            res->ptr + info->box->x, info->box->width);
      return 0;
   }

   if (res->storage == VREND_RESOURCE_STORAGE_BUFFER) {
      GLuint map_flags = GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_WRITE_BIT;
      struct virgl_sub_upload_data d;
      d.box = info->box;
      d.target = res->target;

      if (!info->synchronized)
         map_flags |= GL_MAP_UNSYNCHRONIZED_BIT;

      glBindBufferARB(res->target, res->id);
      data = glMapBufferRange(res->target, info->box->x, info->box->width, map_flags);
      if (data == NULL) {
	 vrend_printf("map failed for element buffer\n");
	 vrend_read_from_iovec_cb(iov, num_iovs, info->offset, info->box->width, &iov_buffer_upload, &d);
      } else {
	 vrend_read_from_iovec(iov, num_iovs, info->offset, data, info->box->width);
	 glUnmapBuffer(res->target);
      }
      glBindBufferARB(res->target, 0);
   } else {
      GLenum glformat;
      GLenum gltype;
      int need_temp = 0;
      int elsize = util_format_get_blocksize(res->base.format);
      int x = 0, y = 0;
      bool compressed;
      bool invert = false;
      float depth_scale;
      GLuint send_size = 0;
      uint32_t stride = info->stride;
      uint32_t layer_stride = info->layer_stride;

      if (ctx)
         vrend_use_program(ctx, 0);
      else
         glUseProgram(0);

      if (!stride)
         stride = util_format_get_nblocksx(res->base.format, u_minify(res->base.width0, info->level)) * elsize;

      if (!layer_stride)
         layer_stride = util_format_get_2d_size(res->base.format, stride,
                                                u_minify(res->base.height0, info->level));

      compressed = util_format_is_compressed(res->base.format);
      if (num_iovs > 1 || compressed) {
         need_temp = true;
      }

      if (vrend_state.use_core_profile == true && (res->y_0_top || (res->base.format == (enum pipe_format)VIRGL_FORMAT_Z24X8_UNORM))) {
         need_temp = true;
         if (res->y_0_top)
            invert = true;
      }

      send_size = util_format_get_nblocks(res->base.format, info->box->width,
                                          info->box->height) * elsize;
      if (res->target == GL_TEXTURE_3D ||
          res->target == GL_TEXTURE_2D_ARRAY ||
          res->target == GL_TEXTURE_CUBE_MAP_ARRAY)
          send_size *= info->box->depth;

      if (need_temp) {
         data = malloc(send_size);
         if (!data)
            return ENOMEM;
         read_transfer_data(iov, num_iovs, data, res->base.format, info->offset,
                            stride, layer_stride, info->box, invert);
      } else {
         if (send_size > iov[0].iov_len - info->offset)
            return EINVAL;
         data = (char*)iov[0].iov_base + info->offset;
      }

      if (!need_temp) {
         assert(stride);
         glPixelStorei(GL_UNPACK_ROW_LENGTH, stride / elsize);
         glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, layer_stride / stride);
      } else
         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);

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

      glformat = tex_conv_table[res->base.format].glformat;
      gltype = tex_conv_table[res->base.format].gltype;

      if ((!vrend_state.use_core_profile) && (res->y_0_top)) {
         GLuint buffers;

         if (res->readback_fb_id == 0 || (int)res->readback_fb_level != info->level) {
            GLuint fb_id;
            if (res->readback_fb_id)
               glDeleteFramebuffers(1, &res->readback_fb_id);

            glGenFramebuffers(1, &fb_id);
            glBindFramebuffer(GL_FRAMEBUFFER, fb_id);
            vrend_fb_bind_texture(res, 0, info->level, 0);

            res->readback_fb_id = fb_id;
            res->readback_fb_level = info->level;
         } else {
            glBindFramebuffer(GL_FRAMEBUFFER, res->readback_fb_id);
         }

         buffers = GL_COLOR_ATTACHMENT0;
         glDrawBuffers(1, &buffers);
         glDisable(GL_BLEND);
         if (ctx) {
            vrend_depth_test_enable(ctx, false);
            vrend_alpha_test_enable(ctx, false);
            vrend_stencil_test_enable(ctx, false);
         } else {
            glDisable(GL_DEPTH_TEST);
            glDisable(GL_ALPHA_TEST);
            glDisable(GL_STENCIL_TEST);
         }
         glPixelZoom(1.0f, res->y_0_top ? -1.0f : 1.0f);
         glWindowPos2i(info->box->x, res->y_0_top ? (int)res->base.height0 - info->box->y : info->box->y);
         glDrawPixels(info->box->width, info->box->height, glformat, gltype,
                      data);
      } else {
         uint32_t comp_size;
         glBindTexture(res->target, res->id);

         if (compressed) {
            glformat = tex_conv_table[res->base.format].internalformat;
            comp_size = util_format_get_nblocks(res->base.format, info->box->width,
                                                info->box->height) * util_format_get_blocksize(res->base.format);
         }

         if (glformat == 0) {
            glformat = GL_BGRA;
            gltype = GL_UNSIGNED_BYTE;
         }

         x = info->box->x;
         y = invert ? (int)res->base.height0 - info->box->y - info->box->height : info->box->y;


         /* mipmaps are usually passed in one iov, and we need to keep the offset
          * into the data in case we want to read back the data of a surface
          * that can not be rendered. Since we can not assume that the whole texture
          * is filled, we evaluate the offset for origin (0,0,0). Since it is also
          * possible that a resource is reused and resized update the offset every time.
          */
         if (info->level < VR_MAX_TEXTURE_2D_LEVELS) {
            int64_t level_height = u_minify(res->base.height0, info->level);
            res->mipmap_offsets[info->level] = info->offset -
                                               ((info->box->z * level_height + y) * stride + x * elsize);
         }

         if (res->base.format == (enum pipe_format)VIRGL_FORMAT_Z24X8_UNORM) {
            /* we get values from the guest as 24-bit scaled integers
               but we give them to the host GL and it interprets them
               as 32-bit scaled integers, so we need to scale them here */
            depth_scale = 256.0;
            if (!vrend_state.use_core_profile)
               glPixelTransferf(GL_DEPTH_SCALE, depth_scale);
            else
               vrend_scale_depth(data, send_size, depth_scale);
         }
         if (res->target == GL_TEXTURE_CUBE_MAP) {
            GLenum ctarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + info->box->z;
            if (compressed) {
               glCompressedTexSubImage2D(ctarget, info->level, x, y,
                                         info->box->width, info->box->height,
                                         glformat, comp_size, data);
            } else {
               glTexSubImage2D(ctarget, info->level, x, y, info->box->width, info->box->height,
                               glformat, gltype, data);
            }
         } else if (res->target == GL_TEXTURE_3D || res->target == GL_TEXTURE_2D_ARRAY || res->target == GL_TEXTURE_CUBE_MAP_ARRAY) {
            if (compressed) {
               glCompressedTexSubImage3D(res->target, info->level, x, y, info->box->z,
                                         info->box->width, info->box->height, info->box->depth,
                                         glformat, comp_size, data);
            } else {
               glTexSubImage3D(res->target, info->level, x, y, info->box->z,
                               info->box->width, info->box->height, info->box->depth,
                               glformat, gltype, data);
            }
         } else if (res->target == GL_TEXTURE_1D) {
            if (vrend_state.use_gles) {
               /* Covers both compressed and none compressed. */
               report_gles_missing_func(ctx, "gl[Compressed]TexSubImage1D");
            } else if (compressed) {
               glCompressedTexSubImage1D(res->target, info->level, info->box->x,
                                         info->box->width,
                                         glformat, comp_size, data);
            } else {
               glTexSubImage1D(res->target, info->level, info->box->x, info->box->width,
                               glformat, gltype, data);
            }
         } else {
            if (compressed) {
               glCompressedTexSubImage2D(res->target, info->level, x, res->target == GL_TEXTURE_1D_ARRAY ? info->box->z : y,
                                         info->box->width, info->box->height,
                                         glformat, comp_size, data);
            } else {
               glTexSubImage2D(res->target, info->level, x, res->target == GL_TEXTURE_1D_ARRAY ? info->box->z : y,
                               info->box->width,
                               res->target == GL_TEXTURE_1D_ARRAY ? info->box->depth : info->box->height,
                               glformat, gltype, data);
            }
         }
         if (res->base.format == (enum pipe_format)VIRGL_FORMAT_Z24X8_UNORM) {
            if (!vrend_state.use_core_profile)
               glPixelTransferf(GL_DEPTH_SCALE, 1.0);
         }
         glBindTexture(res->target, 0);
      }

      if (stride && !need_temp) {
         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
         glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
      }

      glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

      if (need_temp)
         free(data);
   }
   return 0;
}