示例#1
0
/**
 * Copy an image between textures with the vgpu10 CopyRegion command.
 */
static void
copy_region_vgpu10(struct svga_context *svga, struct pipe_resource *src_tex,
                    unsigned src_x, unsigned src_y, unsigned src_z,
                    unsigned src_level, unsigned src_layer_face,
                    struct pipe_resource *dst_tex,
                    unsigned dst_x, unsigned dst_y, unsigned dst_z,
                    unsigned dst_level, unsigned dst_layer_face,
                    unsigned width, unsigned height, unsigned depth)
{
   uint32 srcSubResource, dstSubResource;
   struct svga_texture *dtex, *stex;

   stex = svga_texture(src_tex);
   dtex = svga_texture(dst_tex);

   svga_surfaces_flush(svga);

   srcSubResource = src_layer_face * (src_tex->last_level + 1) + src_level;
   dstSubResource = dst_layer_face * (dst_tex->last_level + 1) + dst_level;

   svga_texture_copy_region(svga, stex->handle, srcSubResource,
                            src_x, src_y, src_z,
                            dtex->handle, dstSubResource,
                            dst_x, dst_y, dst_z,
                            width, height, depth);

   /* Mark the texture subresource as defined. */
   svga_define_texture_level(dtex, dst_layer_face, dst_level);

   /* Mark the texture subresource as rendered-to. */
   svga_set_texture_rendered_to(dtex, dst_layer_face, dst_level);
}
示例#2
0
/**
 * Copy when src texture and dst texture are same with IntraSurfaceCopy
 * command.
 */
static void
intra_surface_copy(struct svga_context *svga, struct pipe_resource *tex,
                    unsigned src_x, unsigned src_y, unsigned src_z,
                    unsigned level, unsigned layer_face,
                    unsigned dst_x, unsigned dst_y, unsigned dst_z,
                    unsigned width, unsigned height, unsigned depth)
{
   enum pipe_error ret;
   SVGA3dCopyBox box;
   struct svga_texture *stex;

   /*
    * Makes sure we have flushed all buffered draw operations and also
    * synchronizes all surfaces with any emulated surface views.
    */
   svga_surfaces_flush(svga);

   stex = svga_texture(tex);

   box.x = dst_x;
   box.y = dst_y;
   box.z = dst_z;
   box.w = width;
   box.h = height;
   box.d = depth;
   box.srcx = src_x;
   box.srcy = src_y;
   box.srcz = src_z;

   ret = SVGA3D_vgpu10_IntraSurfaceCopy(svga->swc,
                                 stex->handle, level, layer_face,  &box);
   if (ret != PIPE_OK) {
      svga_context_flush(svga, NULL);
   ret = SVGA3D_vgpu10_IntraSurfaceCopy(svga->swc,
                                 stex->handle, level, layer_face, &box);
      assert(ret == PIPE_OK);
   }

   /* Mark the texture subresource as rendered-to. */
   svga_set_texture_rendered_to(stex, layer_face, level);
}
示例#3
0
static void svga_flush( struct pipe_context *pipe,
                        struct pipe_fence_handle **fence,
                        unsigned flags)
{
   struct svga_context *svga = svga_context(pipe);

   /* Emit buffered drawing commands, and any back copies.
    */
   svga_surfaces_flush( svga );

   if (flags & PIPE_FLUSH_FENCE_FD)
      svga->swc->hints |= SVGA_HINT_FLAG_EXPORT_FENCE_FD;

   /* Flush command queue.
    */
   svga_context_flush(svga, fence);

   SVGA_DBG(DEBUG_DMA|DEBUG_PERF, "%s fence_ptr %p\n",
            __FUNCTION__, fence ? *fence : 0x0);

   /* Enable to dump BMPs of the color/depth buffers each frame */
   if (0) {
      struct pipe_framebuffer_state *fb = &svga->curr.framebuffer;
      static unsigned frame_no = 1;
      char filename[256];
      unsigned i;

      for (i = 0; i < fb->nr_cbufs; i++) {
         util_snprintf(filename, sizeof(filename), "cbuf%u_%04u.bmp", i, frame_no);
         debug_dump_surface_bmp(&svga->pipe, filename, fb->cbufs[i]);
      }

      if (0 && fb->zsbuf) {
         util_snprintf(filename, sizeof(filename), "zsbuf_%04u.bmp", frame_no);
         debug_dump_surface_bmp(&svga->pipe, filename, fb->zsbuf);
      }

      ++frame_no;
   }
}
示例#4
0
static void
svga_transfer_dma(struct svga_context *svga,
                  struct svga_transfer *st,
                  SVGA3dTransferType transfer,
                  SVGA3dSurfaceDMAFlags flags)
{
   struct svga_texture *texture = svga_texture(st->base.resource);
   struct svga_screen *screen = svga_screen(texture->b.b.screen);
   struct svga_winsys_screen *sws = screen->sws;
   struct pipe_fence_handle *fence = NULL;

   assert(!st->use_direct_map);

   if (transfer == SVGA3D_READ_HOST_VRAM) {
      SVGA_DBG(DEBUG_PERF, "%s: readback transfer\n", __FUNCTION__);
   }

   /* Ensure any pending operations on host surfaces are queued on the command
    * buffer first.
    */
   svga_surfaces_flush( svga );

   if (!st->swbuf) {
      /* Do the DMA transfer in a single go */
      svga_transfer_dma_band(svga, st, transfer,
                             st->base.box.y, st->base.box.height, 0,
                             flags);

      if (transfer == SVGA3D_READ_HOST_VRAM) {
         svga_context_flush(svga, &fence);
         sws->fence_finish(sws, fence, 0);
         sws->fence_reference(sws, &fence, NULL);
      }
   }
   else {
      int y, h, srcy;
      unsigned blockheight =
         util_format_get_blockheight(st->base.resource->format);

      h = st->hw_nblocksy * blockheight;
      srcy = 0;

      for (y = 0; y < st->base.box.height; y += h) {
         unsigned offset, length;
         void *hw, *sw;

         if (y + h > st->base.box.height)
            h = st->base.box.height - y;

         /* Transfer band must be aligned to pixel block boundaries */
         assert(y % blockheight == 0);
         assert(h % blockheight == 0);

         offset = y * st->base.stride / blockheight;
         length = h * st->base.stride / blockheight;

         sw = (uint8_t *) st->swbuf + offset;

         if (transfer == SVGA3D_WRITE_HOST_VRAM) {
            unsigned usage = PIPE_TRANSFER_WRITE;

            /* Wait for the previous DMAs to complete */
            /* TODO: keep one DMA (at half the size) in the background */
            if (y) {
               svga_context_flush(svga, NULL);
               usage |= PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE;
            }

            hw = sws->buffer_map(sws, st->hwbuf, usage);
            assert(hw);
            if (hw) {
               memcpy(hw, sw, length);
               sws->buffer_unmap(sws, st->hwbuf);
            }
         }

         svga_transfer_dma_band(svga, st, transfer, y, h, srcy, flags);

         /*
          * Prevent the texture contents to be discarded on the next band
          * upload.
          */
         flags.discard = FALSE;

         if (transfer == SVGA3D_READ_HOST_VRAM) {
            svga_context_flush(svga, &fence);
            sws->fence_finish(sws, fence, 0);

            hw = sws->buffer_map(sws, st->hwbuf, PIPE_TRANSFER_READ);
            assert(hw);
            if (hw) {
               memcpy(sw, hw, length);
               sws->buffer_unmap(sws, st->hwbuf);
            }
         }
      }
   }
}
示例#5
0
static void *
svga_texture_transfer_map(struct pipe_context *pipe,
                          struct pipe_resource *texture,
                          unsigned level,
                          unsigned usage,
                          const struct pipe_box *box,
                          struct pipe_transfer **ptransfer)
{
   struct svga_context *svga = svga_context(pipe);
   struct svga_screen *ss = svga_screen(pipe->screen);
   struct svga_winsys_screen *sws = ss->sws;
   struct svga_texture *tex = svga_texture(texture);
   struct svga_transfer *st;
   unsigned nblocksx, nblocksy;
   boolean use_direct_map = svga_have_gb_objects(svga) &&
      !svga_have_gb_dma(svga);
   unsigned d;
   void *returnVal;
   int64_t begin = os_time_get();

   /* We can't map texture storage directly unless we have GB objects */
   if (usage & PIPE_TRANSFER_MAP_DIRECTLY) {
      if (svga_have_gb_objects(svga))
         use_direct_map = TRUE;
      else
         return NULL;
   }

   st = CALLOC_STRUCT(svga_transfer);
   if (!st)
      return NULL;

   {
      unsigned w, h;
      if (use_direct_map) {
         /* we'll directly access the guest-backed surface */
         w = u_minify(texture->width0, level);
         h = u_minify(texture->height0, level);
         d = u_minify(texture->depth0, level);
      }
      else {
         /* we'll put the data into a tightly packed buffer */
         w = box->width;
         h = box->height;
         d = box->depth;
      }
      nblocksx = util_format_get_nblocksx(texture->format, w);
      nblocksy = util_format_get_nblocksy(texture->format, h);
   }

   pipe_resource_reference(&st->base.resource, texture);

   st->base.level = level;
   st->base.usage = usage;
   st->base.box = *box;
   st->base.stride = nblocksx*util_format_get_blocksize(texture->format);
   st->base.layer_stride = st->base.stride * nblocksy;

   switch (tex->b.b.target) {
   case PIPE_TEXTURE_CUBE:
   case PIPE_TEXTURE_2D_ARRAY:
   case PIPE_TEXTURE_1D_ARRAY:
      st->slice = st->base.box.z;
      st->base.box.z = 0;   /* so we don't apply double offsets below */
      break;
   default:
      st->slice = 0;
      break;
   }

   if (usage & PIPE_TRANSFER_WRITE) {
      /* record texture upload for HUD */
      svga->hud.num_bytes_uploaded +=
         nblocksx * nblocksy * d * util_format_get_blocksize(texture->format);
   }

   if (!use_direct_map) {
      /* Use a DMA buffer */
      st->hw_nblocksy = nblocksy;

      st->hwbuf = svga_winsys_buffer_create(svga, 1, 0,
                                   st->hw_nblocksy * st->base.stride * d);
      while(!st->hwbuf && (st->hw_nblocksy /= 2)) {
         st->hwbuf = svga_winsys_buffer_create(svga, 1, 0,
                                   st->hw_nblocksy * st->base.stride * d);
      }

      if (!st->hwbuf) {
         FREE(st);
         return NULL;
      }

      if (st->hw_nblocksy < nblocksy) {
         /* We couldn't allocate a hardware buffer big enough for the transfer,
          * so allocate regular malloc memory instead */
         if (0) {
            debug_printf("%s: failed to allocate %u KB of DMA, "
                         "splitting into %u x %u KB DMA transfers\n",
                         __FUNCTION__,
                         (nblocksy*st->base.stride + 1023)/1024,
                         (nblocksy + st->hw_nblocksy - 1)/st->hw_nblocksy,
                         (st->hw_nblocksy*st->base.stride + 1023)/1024);
         }

         st->swbuf = MALLOC(nblocksy * st->base.stride * d);
         if (!st->swbuf) {
            sws->buffer_destroy(sws, st->hwbuf);
            FREE(st);
            return NULL;
         }
      }

      if (usage & PIPE_TRANSFER_READ) {
         SVGA3dSurfaceDMAFlags flags;
         memset(&flags, 0, sizeof flags);
         svga_transfer_dma(svga, st, SVGA3D_READ_HOST_VRAM, flags);
      }
   } else {
      struct pipe_transfer *transfer = &st->base;
      struct svga_winsys_surface *surf = tex->handle;

      if (!surf) {
         FREE(st);
         return NULL;
      }

      if (need_tex_readback(transfer)) {
	 enum pipe_error ret;

         svga_surfaces_flush(svga);

         if (svga_have_vgpu10(svga)) {
            ret = readback_image_vgpu10(svga, surf, st->slice, transfer->level,
                                        tex->b.b.last_level + 1);
         } else {
            ret = readback_image_vgpu9(svga, surf, st->slice, transfer->level);
         }

         assert(ret == PIPE_OK);
         (void) ret;

	 svga_context_flush(svga, NULL);

         /*
          * Note: if PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE were specified
          * we could potentially clear the flag for all faces/layers/mips.
          */
         svga_clear_texture_rendered_to(tex, st->slice, transfer->level);
      }
      else {
	 assert(transfer->usage & PIPE_TRANSFER_WRITE);
	 if ((transfer->usage & PIPE_TRANSFER_UNSYNCHRONIZED) == 0) {
            svga_surfaces_flush(svga);
            if (!sws->surface_is_flushed(sws, surf))
               svga_context_flush(svga, NULL);
	 }
      }
   }

   st->use_direct_map = use_direct_map;

   *ptransfer = &st->base;

   /*
    * Begin mapping code
    */
   if (st->swbuf) {
      returnVal = st->swbuf;
   }
   else if (!st->use_direct_map) {
      returnVal = sws->buffer_map(sws, st->hwbuf, usage);
   }
   else {
      SVGA3dSize baseLevelSize;
      struct svga_texture *tex = svga_texture(texture);
      struct svga_winsys_surface *surf = tex->handle;
      uint8_t *map;
      boolean retry;
      unsigned offset, mip_width, mip_height;
      unsigned xoffset = st->base.box.x;
      unsigned yoffset = st->base.box.y;
      unsigned zoffset = st->base.box.z;

      map = svga->swc->surface_map(svga->swc, surf, usage, &retry);
      if (map == NULL && retry) {
         /*
          * At this point, the svga_surfaces_flush() should already have
          * called in svga_texture_get_transfer().
          */
         svga_context_flush(svga, NULL);
         map = svga->swc->surface_map(svga->swc, surf, usage, &retry);
      }

      /*
       * Make sure we return NULL if the map fails
       */
      if (!map) {
         FREE(st);
         return map;
      }

      /**
       * Compute the offset to the specific texture slice in the buffer.
       */
      baseLevelSize.width = tex->b.b.width0;
      baseLevelSize.height = tex->b.b.height0;
      baseLevelSize.depth = tex->b.b.depth0;

      offset = svga3dsurface_get_image_offset(tex->key.format, baseLevelSize,
                                              tex->b.b.last_level + 1, /* numMips */
                                              st->slice, level);
      if (level > 0) {
         assert(offset > 0);
      }

      mip_width = u_minify(tex->b.b.width0, level);
      mip_height = u_minify(tex->b.b.height0, level);

      offset += svga3dsurface_get_pixel_offset(tex->key.format,
                                               mip_width, mip_height,
                                               xoffset, yoffset, zoffset);
      returnVal = (void *) (map + offset);
   }

   svga->hud.map_buffer_time += (os_time_get() - begin);
   svga->hud.num_resources_mapped++;

   return returnVal;
}
示例#6
0
/* XXX still have doubts about this... */
static void svga_surface_copy(struct pipe_context *pipe,
                              struct pipe_resource* dst_tex,
                              unsigned dst_level,
                              unsigned dstx, unsigned dsty, unsigned dstz,
                              struct pipe_resource* src_tex,
                              unsigned src_level,
                              const struct pipe_box *src_box)
 {
   struct svga_context *svga = svga_context(pipe);
   struct svga_texture *stex, *dtex;
/*   struct pipe_screen *screen = pipe->screen;
   SVGA3dCopyBox *box;
   enum pipe_error ret;
   struct pipe_surface *srcsurf, *dstsurf;*/
   unsigned dst_face, dst_z, src_face, src_z;

   /* Emit buffered drawing commands, and any back copies.
    */
   svga_surfaces_flush( svga );

   /* Fallback for buffers. */
   if (dst_tex->target == PIPE_BUFFER && src_tex->target == PIPE_BUFFER) {
      util_resource_copy_region(pipe, dst_tex, dst_level, dstx, dsty, dstz,
                                src_tex, src_level, src_box);
      return;
   }

   stex = svga_texture(src_tex);
   dtex = svga_texture(dst_tex);

#if 0
   srcsurf = screen->get_tex_surface(screen, src_tex,
                                     src_level, src_box->z, src_box->z,
                                     PIPE_BIND_SAMPLER_VIEW);

   dstsurf = screen->get_tex_surface(screen, dst_tex,
                                     dst_level, dst_box->z, dst_box->z,
                                     PIPE_BIND_RENDER_TARGET);

   SVGA_DBG(DEBUG_DMA, "blit to sid %p (%d,%d), from sid %p (%d,%d) sz %dx%d\n",
            svga_surface(dstsurf)->handle,
            dstx, dsty,
            svga_surface(srcsurf)->handle,
            src_box->x, src_box->y,
            width, height);

   ret = SVGA3D_BeginSurfaceCopy(svga->swc,
                                 srcsurf,
                                 dstsurf,
                                 &box,
                                 1);
   if(ret != PIPE_OK) {

      svga_context_flush(svga, NULL);

      ret = SVGA3D_BeginSurfaceCopy(svga->swc,
                                    srcsurf,
                                    dstsurf,
                                    &box,
                                    1);
      assert(ret == PIPE_OK);
   }

   box->x = dstx;
   box->y = dsty;
   box->z = 0;
   box->w = width;
   box->h = height;
   box->d = 1;
   box->srcx = src_box->x;
   box->srcy = src_box->y;
   box->srcz = 0;

   SVGA_FIFOCommitAll(svga->swc);

   svga_surface(dstsurf)->dirty = TRUE;
   svga_propagate_surface(pipe, dstsurf);

   pipe_surface_reference(&srcsurf, NULL);
   pipe_surface_reference(&dstsurf, NULL);

#else
   if (src_tex->target == PIPE_TEXTURE_CUBE) {
      src_face = src_box->z;
      src_z = 0;
      assert(src_box->depth == 1);
   }
   else {
      src_face = 0;
      src_z = src_box->z;
   }
   /* different src/dst type???*/
   if (dst_tex->target == PIPE_TEXTURE_CUBE) {
      dst_face = dstz;
      dst_z = 0;
      assert(src_box->depth == 1);
   }
   else {
      dst_face = 0;
      dst_z = dstz;
   }
   svga_texture_copy_handle(svga,
                            stex->handle,
                            src_box->x, src_box->y, src_z,
                            src_level, src_face,
                            dtex->handle,
                            dstx, dsty, dst_z,
                            dst_level, dst_face,
                            src_box->width, src_box->height, src_box->depth);

#endif

}
/**
 * Use direct map for the transfer request
 */
static void *
svga_texture_transfer_map_direct(struct svga_context *svga,
                                 struct svga_transfer *st)
{
   struct svga_winsys_screen *sws = svga_screen(svga->pipe.screen)->sws;
   struct pipe_transfer *transfer = &st->base;
   struct pipe_resource *texture = transfer->resource;
   struct svga_texture *tex = svga_texture(texture);
   struct svga_winsys_surface *surf = tex->handle;
   unsigned level = st->base.level;
   unsigned w, h, nblocksx, nblocksy, i;
   unsigned usage = st->base.usage;

   if (need_tex_readback(st)) {
      enum pipe_error ret;

      svga_surfaces_flush(svga);

      for (i = 0; i < st->box.d; i++) {
         if (svga_have_vgpu10(svga)) {
            ret = readback_image_vgpu10(svga, surf, st->slice + i, level,
                                        tex->b.b.last_level + 1);
         } else {
            ret = readback_image_vgpu9(svga, surf, st->slice + i, level);
         }
      }
      svga->hud.num_readbacks++;
      SVGA_STATS_COUNT_INC(sws, SVGA_STATS_COUNT_TEXREADBACK);

      assert(ret == PIPE_OK);
      (void) ret;

      svga_context_flush(svga, NULL);
      /*
       * Note: if PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE were specified
       * we could potentially clear the flag for all faces/layers/mips.
       */
      svga_clear_texture_rendered_to(tex, st->slice, level);
   }
   else {
      assert(usage & PIPE_TRANSFER_WRITE);
      if ((usage & PIPE_TRANSFER_UNSYNCHRONIZED) == 0) {
         if (svga_is_texture_dirty(tex, st->slice, level)) {
            /*
             * do a surface flush if the subresource has been modified
             * in this command buffer.
             */
            svga_surfaces_flush(svga);
            if (!sws->surface_is_flushed(sws, surf)) {
               svga->hud.surface_write_flushes++;
               SVGA_STATS_COUNT_INC(sws, SVGA_STATS_COUNT_SURFACEWRITEFLUSH);
               svga_context_flush(svga, NULL);
            }
         }
      }
   }

   /* we'll directly access the guest-backed surface */
   w = u_minify(texture->width0, level);
   h = u_minify(texture->height0, level);
   nblocksx = util_format_get_nblocksx(texture->format, w);
   nblocksy = util_format_get_nblocksy(texture->format, h);
   st->hw_nblocksy = nblocksy;
   st->base.stride = nblocksx*util_format_get_blocksize(texture->format);
   st->base.layer_stride = st->base.stride * nblocksy;

   /*
    * Begin mapping code
    */
   {
      SVGA3dSize baseLevelSize;
      uint8_t *map;
      boolean retry;
      unsigned offset, mip_width, mip_height;

      map = svga->swc->surface_map(svga->swc, surf, usage, &retry);
      if (map == NULL && retry) {
         /*
          * At this point, the svga_surfaces_flush() should already have
          * called in svga_texture_get_transfer().
          */
         svga->hud.surface_write_flushes++;
         svga_context_flush(svga, NULL);
         map = svga->swc->surface_map(svga->swc, surf, usage, &retry);
      }

      /*
       * Make sure we return NULL if the map fails
       */
      if (!map) {
         return NULL;
      }

      /**
       * Compute the offset to the specific texture slice in the buffer.
       */
      baseLevelSize.width = tex->b.b.width0;
      baseLevelSize.height = tex->b.b.height0;
      baseLevelSize.depth = tex->b.b.depth0;

      if ((tex->b.b.target == PIPE_TEXTURE_1D_ARRAY) ||
          (tex->b.b.target == PIPE_TEXTURE_2D_ARRAY) ||
          (tex->b.b.target == PIPE_TEXTURE_CUBE_ARRAY)) {
         st->base.layer_stride =
            svga3dsurface_get_image_offset(tex->key.format, baseLevelSize,
                                           tex->b.b.last_level + 1, 1, 0);
      }

      offset = svga3dsurface_get_image_offset(tex->key.format, baseLevelSize,
                                              tex->b.b.last_level + 1, /* numMips */
                                              st->slice, level);
      if (level > 0) {
         assert(offset > 0);
      }

      mip_width = u_minify(tex->b.b.width0, level);
      mip_height = u_minify(tex->b.b.height0, level);

      offset += svga3dsurface_get_pixel_offset(tex->key.format,
                                               mip_width, mip_height,
                                               st->box.x,
                                               st->box.y,
                                               st->box.z);

      return (void *) (map + offset);
   }
}
示例#8
0
/**
 * Try region copy using one of the region copy commands
 */
static bool
try_copy_region(struct svga_context *svga,
                const struct pipe_blit_info *blit)
{
   unsigned src_layer_face, src_z, dst_layer_face, dst_z;

   if (!can_blit_via_svga_copy_region(svga, blit))
      return false;

   adjust_z_layer(blit->src.resource->target, blit->src.box.z,
                  &src_layer_face, &src_z);

   adjust_z_layer(blit->dst.resource->target, blit->dst.box.z,
                  &dst_layer_face, &dst_z);

   if (can_blit_via_copy_region_vgpu10(svga, blit)) {
      svga_toggle_render_condition(svga, blit->render_condition_enable, FALSE);

      copy_region_vgpu10(svga,
                         blit->src.resource,
                         blit->src.box.x, blit->src.box.y, src_z,
                         blit->src.level, src_layer_face,
                         blit->dst.resource,
                         blit->dst.box.x, blit->dst.box.y, dst_z,
                         blit->dst.level, dst_layer_face,
                         blit->src.box.width, blit->src.box.height,
                         blit->src.box.depth);

      svga_toggle_render_condition(svga, blit->render_condition_enable, TRUE);

      return true;
   }

   if (can_blit_via_surface_copy(svga, blit)) {
      struct svga_texture *stex = svga_texture(blit->src.resource);
      struct svga_texture *dtex = svga_texture(blit->dst.resource);

      svga_surfaces_flush(svga);

      svga_texture_copy_handle(svga,
                               stex->handle,
                               blit->src.box.x, blit->src.box.y, src_z,
                               blit->src.level, src_layer_face,
                               dtex->handle,
                               blit->dst.box.x, blit->dst.box.y, dst_z,
                               blit->dst.level, dst_layer_face,
                               blit->src.box.width, blit->src.box.height,
                               blit->src.box.depth);

      svga_define_texture_level(dtex, dst_layer_face, blit->dst.level);
      svga_set_texture_rendered_to(dtex, dst_layer_face, blit->dst.level);
      return true;
   }

   if (can_blit_via_intra_surface_copy(svga, blit)) {
      intra_surface_copy(svga,
                         blit->src.resource,
                         blit->src.box.x, blit->src.box.y, src_z,
                         blit->src.level, src_layer_face,
                         blit->dst.box.x, blit->dst.box.y, dst_z,
                         blit->src.box.width, blit->src.box.height,
                         blit->src.box.depth);
      return true;
   }

   return false;
}