svga: single sample surface can be created as non-multisamples surface
[mesa.git] / src / gallium / drivers / svga / svga_resource_texture.c
index c975a192e497e220365f8f9214d23be878548ac0..484a24049d9d2702b82bf350742e5605ffab1532 100644 (file)
 #include "svga_debug.h"
 
 
-/* XXX: This isn't a real hardware flag, but just a hack for kernel to
- * know about primary surfaces. Find a better way to accomplish this.
- */
-#define SVGA3D_SURFACE_HINT_SCANOUT (1 << 9)
-
-
 static void
 svga_transfer_dma_band(struct svga_context *svga,
                        struct svga_transfer *st,
                        SVGA3dTransferType transfer,
-                       unsigned y, unsigned h, unsigned srcy,
+                       unsigned x, unsigned y, unsigned z,
+                       unsigned w, unsigned h, unsigned d,
+                       unsigned srcx, unsigned srcy, unsigned srcz,
                        SVGA3dSurfaceDMAFlags flags)
 {
-   struct svga_texture *texture = svga_texture(st->base.resource); 
+   struct svga_texture *texture = svga_texture(st->base.resource);
    SVGA3dCopyBox box;
    enum pipe_error ret;
+
    assert(!st->use_direct_map);
 
-   box.x = st->base.box.x;
+   box.x = x;
    box.y = y;
-   box.z = st->base.box.z;
-   box.w = st->base.box.width;
+   box.z = z;
+   box.w = w;
    box.h = h;
-   box.d = 1;
-   box.srcx = 0;
+   box.d = d;
+   box.srcx = srcx;
    box.srcy = srcy;
-   box.srcz = 0;
-
-   if (st->base.resource->target == PIPE_TEXTURE_CUBE) {
-      st->face = st->base.box.z;
-      box.z = 0;
-   }
-   else
-      st->face = 0;
-
-   SVGA_DBG(DEBUG_DMA, "dma %s sid %p, face %u, (%u, %u, %u) - (%u, %u, %u), %ubpp\n",
-                transfer == SVGA3D_WRITE_HOST_VRAM ? "to" : "from", 
-                texture->handle,
-                st->face,
-                st->base.box.x,
-                y,
-                box.z,
-                st->base.box.x + st->base.box.width,
-                y + h,
-                box.z + 1,
-                util_format_get_blocksize(texture->b.b.format) * 8 /
-                (util_format_get_blockwidth(texture->b.b.format)*util_format_get_blockheight(texture->b.b.format)));
+   box.srcz = srcz;
+
+   SVGA_DBG(DEBUG_DMA, "dma %s sid %p, face %u, (%u, %u, %u) - "
+            "(%u, %u, %u), %ubpp\n",
+            transfer == SVGA3D_WRITE_HOST_VRAM ? "to" : "from",
+            texture->handle,
+            st->slice,
+            x,
+            y,
+            z,
+            x + w,
+            y + h,
+            z + 1,
+            util_format_get_blocksize(texture->b.b.format) * 8 /
+            (util_format_get_blockwidth(texture->b.b.format)
+             * util_format_get_blockheight(texture->b.b.format)));
 
    ret = SVGA3D_SurfaceDMA(svga->swc, st, transfer, &box, 1, flags);
-   if(ret != PIPE_OK) {
+   if (ret != PIPE_OK) {
       svga_context_flush(svga, NULL);
       ret = SVGA3D_SurfaceDMA(svga->swc, st, transfer, &box, 1, flags);
       assert(ret == PIPE_OK);
@@ -110,7 +101,7 @@ svga_transfer_dma(struct svga_context *svga,
                   SVGA3dTransferType transfer,
                   SVGA3dSurfaceDMAFlags flags)
 {
-   struct svga_texture *texture = svga_texture(st->base.resource); 
+   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;
@@ -126,14 +117,15 @@ svga_transfer_dma(struct svga_context *svga,
     */
    svga_surfaces_flush( svga );
 
-   if(!st->swbuf) {
+   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,
+                             st->base.box.x, st->base.box.y, st->base.box.z,
+                             st->base.box.width, st->base.box.height, st->base.box.depth,
+                             0, 0, 0,
                              flags);
 
-      if(transfer == SVGA3D_READ_HOST_VRAM) {
+      if (transfer == SVGA3D_READ_HOST_VRAM) {
          svga_context_flush(svga, &fence);
          sws->fence_finish(sws, fence, 0);
          sws->fence_reference(sws, &fence, NULL);
@@ -141,10 +133,13 @@ svga_transfer_dma(struct svga_context *svga,
    }
    else {
       int y, h, srcy;
-      unsigned blockheight = util_format_get_blockheight(st->base.resource->format);
+      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) {
+
+      for (y = 0; y < st->base.box.height; y += h) {
          unsigned offset, length;
          void *hw, *sw;
 
@@ -158,7 +153,7 @@ svga_transfer_dma(struct svga_context *svga,
          offset = y * st->base.stride / blockheight;
          length = h * st->base.stride / blockheight;
 
-         sw = (uint8_t *)st->swbuf + offset;
+         sw = (uint8_t *) st->swbuf + offset;
 
          if (transfer == SVGA3D_WRITE_HOST_VRAM) {
             unsigned usage = PIPE_TRANSFER_WRITE;
@@ -178,22 +173,24 @@ svga_transfer_dma(struct svga_context *svga,
             }
          }
 
-         svga_transfer_dma_band(svga, st, transfer, y, h, srcy, flags);
+         svga_transfer_dma_band(svga, st, transfer,
+                                st->base.box.x, y, st->base.box.z,
+                                st->base.box.width, h, st->base.box.depth,
+                                0, srcy, 0, flags);
 
          /*
           * Prevent the texture contents to be discarded on the next band
           * upload.
           */
-
          flags.discard = FALSE;
 
-         if(transfer == SVGA3D_READ_HOST_VRAM) {
+         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) {
+            if (hw) {
                memcpy(sw, hw, length);
                sws->buffer_unmap(sws, st->hwbuf);
             }
@@ -203,19 +200,22 @@ svga_transfer_dma(struct svga_context *svga,
 }
 
 
-static boolean 
+static boolean
 svga_texture_get_handle(struct pipe_screen *screen,
-                               struct pipe_resource *texture,
-                               struct winsys_handle *whandle)
+                        struct pipe_resource *texture,
+                        struct winsys_handle *whandle)
 {
    struct svga_winsys_screen *sws = svga_winsys_screen(texture->screen);
    unsigned stride;
 
    assert(svga_texture(texture)->key.cachable == 0);
    svga_texture(texture)->key.cachable = 0;
+
    stride = util_format_get_nblocksx(texture->format, texture->width0) *
             util_format_get_blocksize(texture->format);
-   return sws->surface_get_handle(sws, svga_texture(texture)->handle, stride, whandle);
+
+   return sws->surface_get_handle(sws, svga_texture(texture)->handle,
+                                  stride, whandle);
 }
 
 
@@ -236,10 +236,16 @@ svga_texture_destroy(struct pipe_screen *screen,
    SVGA_DBG(DEBUG_DMA, "unref sid %p (texture)\n", tex->handle);
    svga_screen_surface_destroy(ss, &tex->key, &tex->handle);
 
-   ss->total_resource_bytes -= tex->size;
+   ss->hud.total_resource_bytes -= tex->size;
 
+   FREE(tex->defined);
    FREE(tex->rendered_to);
+   FREE(tex->dirty);
    FREE(tex);
+
+   assert(ss->hud.num_resources > 0);
+   if (ss->hud.num_resources > 0)
+      ss->hud.num_resources--;
 }
 
 
@@ -274,10 +280,43 @@ need_tex_readback(struct pipe_transfer *transfer)
 }
 
 
+static enum pipe_error
+readback_image_vgpu9(struct svga_context *svga,
+                   struct svga_winsys_surface *surf,
+                   unsigned slice,
+                   unsigned level)
+{
+   enum pipe_error ret;
+
+   ret = SVGA3D_ReadbackGBImage(svga->swc, surf, slice, level);
+   if (ret != PIPE_OK) {
+      svga_context_flush(svga, NULL);
+      ret = SVGA3D_ReadbackGBImage(svga->swc, surf, slice, level);
+   }
+   return ret;
+}
+
+
+static enum pipe_error
+readback_image_vgpu10(struct svga_context *svga,
+                    struct svga_winsys_surface *surf,
+                    unsigned slice,
+                    unsigned level,
+                    unsigned numMipLevels)
+{
+   enum pipe_error ret;
+   unsigned subResource;
+
+   subResource = slice * numMipLevels + level;
+   ret = SVGA3D_vgpu10_ReadbackSubResource(svga->swc, surf, subResource);
+   if (ret != PIPE_OK) {
+      svga_context_flush(svga, NULL);
+      ret = SVGA3D_vgpu10_ReadbackSubResource(svga->swc, surf, subResource);
+   }
+   return ret;
+}
+
 
-/* XXX: Still implementing this as if it was a screen function, but
- * can now modify it to queue transfers on the context.
- */
 static void *
 svga_texture_transfer_map(struct pipe_context *pipe,
                           struct pipe_resource *texture,
@@ -289,23 +328,52 @@ svga_texture_transfer_map(struct pipe_context *pipe,
    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 = NULL;
+   int64_t begin = svga_get_time(svga);
+
+   SVGA_STATS_TIME_PUSH(sws, SVGA_STATS_TIME_TEXTRANSFERMAP);
 
    /* 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;
+         goto done;
    }
 
    st = CALLOC_STRUCT(svga_transfer);
    if (!st)
-      return NULL;
+      goto done;
+
+   st->base.level = level;
+   st->base.usage = usage;
+   st->base.box = *box;
+
+   switch (tex->b.b.target) {
+   case PIPE_TEXTURE_CUBE:
+      st->slice = st->base.box.z;
+      st->base.box.z = 0;   /* so we don't apply double offsets below */
+      break;
+   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 */
+
+      /* Force direct map for transfering multiple slices */
+      if (st->base.box.depth > 1)
+         use_direct_map = svga_have_gb_objects(svga);
+
+      break;
+   default:
+      st->slice = 0;
+      break;
+   }
 
    {
       unsigned w, h;
@@ -326,49 +394,53 @@ svga_texture_transfer_map(struct pipe_context *pipe,
    }
 
    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;
+   st->use_direct_map = use_direct_map;
+
+   *ptransfer = &st->base;
+
+
+   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);
+      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);
+         st->hwbuf = svga_winsys_buffer_create(svga, 1, 0,
+                                   st->hw_nblocksy * st->base.stride * d);
       }
 
       if (!st->hwbuf) {
          FREE(st);
-         return NULL;
+         goto done;
       }
 
-      if(st->hw_nblocksy < nblocksy) {
-         /* We couldn't allocate a hardware buffer big enough for the transfer, 
+      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);
+                         (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;
+            goto done;
          }
       }
 
@@ -379,45 +451,37 @@ svga_texture_transfer_map(struct pipe_context *pipe,
       }
    } else {
       struct pipe_transfer *transfer = &st->base;
-      struct svga_texture *tex = svga_texture(transfer->resource);
       struct svga_winsys_surface *surf = tex->handle;
-      unsigned face;
 
-      assert(surf);
+      if (!surf) {
+         FREE(st);
+         goto done;
+      }
 
-      if (tex->b.b.target == PIPE_TEXTURE_CUBE) {
-        face = transfer->box.z;
-      } else {
-        face = 0;
+      /* If this is the first time mapping to the surface in this
+       * command buffer, clear the dirty masks of this surface.
+       */
+      if (sws->surface_is_flushed(sws, surf)) {
+         svga_clear_texture_dirty(tex);
       }
 
       if (need_tex_readback(transfer)) {
-        SVGA3dBox box;
         enum pipe_error ret;
 
-        box.x = transfer->box.x;
-        box.y = transfer->box.y;
-        box.w = transfer->box.width;
-        box.h = transfer->box.height;
-        box.d = transfer->box.depth;
-        if (tex->b.b.target == PIPE_TEXTURE_CUBE) {
-           box.z = 0;
-        }
-        else {
-           box.z = transfer->box.z;
-        }
-
-         (void) box;  /* not used at this time */
-
          svga_surfaces_flush(svga);
 
-        ret = SVGA3D_ReadbackGBImage(svga->swc, surf, face, transfer->level);
+         if (svga_have_vgpu10(svga)) {
+            ret = readback_image_vgpu10(svga, surf, st->slice, level,
+                                        tex->b.b.last_level + 1);
+         } else {
+            ret = readback_image_vgpu9(svga, surf, st->slice, level);
+         }
+
+         svga->hud.num_readbacks++;
+         SVGA_STATS_COUNT_INC(sws, SVGA_STATS_COUNT_TEXREADBACK);
 
-        if (ret != PIPE_OK) {
-           svga_context_flush(svga, NULL);
-           ret = SVGA3D_ReadbackGBImage(svga->swc, surf, face, transfer->level);
-           assert(ret == PIPE_OK);
-        }
+         assert(ret == PIPE_OK);
+         (void) ret;
 
         svga_context_flush(svga, NULL);
 
@@ -425,43 +489,49 @@ svga_texture_transfer_map(struct pipe_context *pipe,
           * 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, face, transfer->level);
+         svga_clear_texture_rendered_to(tex, st->slice, 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);
+        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);
+               }
+            }
         }
       }
+      if (usage & PIPE_TRANSFER_WRITE) {
+         /* mark this texture level as dirty */
+         svga_set_texture_dirty(tex, st->slice, level);
+      }
    }
 
-   st->use_direct_map = use_direct_map;
-
-   *ptransfer = &st->base;
-
    /*
     * Begin mapping code
     */
    if (st->swbuf) {
-      return st->swbuf;
+      returnVal = st->swbuf;
    }
-   else if (!st->use_direct_map) {
-      return sws->buffer_map(sws, st->hwbuf, usage);
+   else if (!use_direct_map) {
+      returnVal = sws->buffer_map(sws, st->hwbuf, usage);
    }
    else {
-      struct svga_screen *screen = svga_screen(svga->pipe.screen);
-      SVGA3dSurfaceFormat format;
       SVGA3dSize baseLevelSize;
-      struct svga_texture *tex = svga_texture(texture);
       struct svga_winsys_surface *surf = tex->handle;
       uint8_t *map;
       boolean retry;
-      unsigned face, offset, mip_width, mip_height;
-      unsigned xoffset = box->x;
-      unsigned yoffset = box->y;
-      unsigned zoffset = box->z;
+      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) {
@@ -469,36 +539,37 @@ svga_texture_transfer_map(struct pipe_context *pipe,
           * 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 whe return NULL if the map fails
+       * Make sure we return NULL if the map fails
        */
-      if (map == NULL) {
+      if (!map) {
          FREE(st);
-         return map;
+         returnVal = map;
+         goto done;
       }
 
       /**
        * Compute the offset to the specific texture slice in the buffer.
        */
-      if (tex->b.b.target == PIPE_TEXTURE_CUBE) {
-         face = zoffset;
-         zoffset = 0;
-      } else {
-         face = 0;
-      }
-
-      format = svga_translate_format(screen, tex->b.b.format, 0);
       baseLevelSize.width = tex->b.b.width0;
       baseLevelSize.height = tex->b.b.height0;
       baseLevelSize.depth = tex->b.b.depth0;
 
-      offset = svga3dsurface_get_image_offset(format, baseLevelSize,
+      if ((tex->b.b.target == PIPE_TEXTURE_1D_ARRAY) ||
+          (tex->b.b.target == PIPE_TEXTURE_2D_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 */
-                                              face, level);
+                                              st->slice, level);
       if (level > 0) {
          assert(offset > 0);
       }
@@ -506,11 +577,18 @@ svga_texture_transfer_map(struct pipe_context *pipe,
       mip_width = u_minify(tex->b.b.width0, level);
       mip_height = u_minify(tex->b.b.height0, level);
 
-      offset += svga3dsurface_get_pixel_offset(format, mip_width, mip_height,
+      offset += svga3dsurface_get_pixel_offset(tex->key.format,
+                                               mip_width, mip_height,
                                                xoffset, yoffset, zoffset);
-
-      return (void *) (map + offset);
+      returnVal = (void *) (map + offset);
    }
+
+   svga->hud.map_buffer_time += (svga_get_time(svga) - begin);
+   svga->hud.num_textures_mapped++;
+
+done:
+   SVGA_STATS_TIME_POP(sws);
+   return returnVal;
 }
 
 
@@ -541,9 +619,45 @@ svga_texture_surface_unmap(struct svga_context *svga,
 }
 
 
-/* XXX: Still implementing this as if it was a screen function, but
- * can now modify it to queue transfers on the context.
- */
+static enum pipe_error
+update_image_vgpu9(struct svga_context *svga,
+                   struct svga_winsys_surface *surf,
+                   const SVGA3dBox *box,
+                   unsigned slice,
+                   unsigned level)
+{
+   enum pipe_error ret;
+
+   ret = SVGA3D_UpdateGBImage(svga->swc, surf, box, slice, level);
+   if (ret != PIPE_OK) {
+      svga_context_flush(svga, NULL);
+      ret = SVGA3D_UpdateGBImage(svga->swc, surf, box, slice, level);
+   }
+   return ret;
+}
+
+
+static enum pipe_error
+update_image_vgpu10(struct svga_context *svga,
+                    struct svga_winsys_surface *surf,
+                    const SVGA3dBox *box,
+                    unsigned slice,
+                    unsigned level,
+                    unsigned numMipLevels)
+{
+   enum pipe_error ret;
+   unsigned subResource;
+
+   subResource = slice * numMipLevels + level;
+   ret = SVGA3D_vgpu10_UpdateSubResource(svga->swc, surf, box, subResource);
+   if (ret != PIPE_OK) {
+      svga_context_flush(svga, NULL);
+      ret = SVGA3D_vgpu10_UpdateSubResource(svga->swc, surf, box, subResource);
+   }
+   return ret;
+}
+
+
 static void
 svga_texture_transfer_unmap(struct pipe_context *pipe,
                            struct pipe_transfer *transfer)
@@ -554,6 +668,8 @@ svga_texture_transfer_unmap(struct pipe_context *pipe,
    struct svga_transfer *st = svga_transfer(transfer);
    struct svga_texture *tex = svga_texture(transfer->resource);
 
+   SVGA_STATS_TIME_PUSH(sws, SVGA_STATS_TIME_TEXTRANSFERUNMAP);
+
    if (!st->swbuf) {
       if (st->use_direct_map) {
          svga_texture_surface_unmap(svga, transfer);
@@ -577,51 +693,68 @@ svga_texture_transfer_unmap(struct pipe_context *pipe,
 
       svga_transfer_dma(svga, st, SVGA3D_WRITE_HOST_VRAM, flags);
    } else if (transfer->usage & PIPE_TRANSFER_WRITE) {
-      struct svga_winsys_surface *surf =
-        svga_texture(transfer->resource)->handle;
-      unsigned face;
+      struct svga_winsys_surface *surf = tex->handle;
       SVGA3dBox box;
       enum pipe_error ret;
+      unsigned nlayers = 1;
 
       assert(svga_have_gb_objects(svga));
 
       /* update the effected region */
-      if (tex->b.b.target == PIPE_TEXTURE_CUBE) {
-        face = transfer->box.z;
-      } else {
-        face = 0;
-      }
-
       box.x = transfer->box.x;
       box.y = transfer->box.y;
-      if (tex->b.b.target == PIPE_TEXTURE_CUBE) {
-         box.z = 0;
-      }
-      else {
-         box.z = transfer->box.z;
-      }
       box.w = transfer->box.width;
       box.h = transfer->box.height;
       box.d = transfer->box.depth;
 
+      switch (tex->b.b.target) {
+      case PIPE_TEXTURE_CUBE:
+         box.z = 0;
+         break;
+      case PIPE_TEXTURE_2D_ARRAY:
+         nlayers = box.d;
+         box.z = 0;
+         box.d = 1;
+         break;
+      case PIPE_TEXTURE_1D_ARRAY:
+         nlayers = box.d;
+         box.y = box.z = 0;
+         box.d = 1;
+         break;
+      default:
+         box.z = transfer->box.z;
+         break;
+      }
+
       if (0)
          debug_printf("%s %d, %d, %d  %d x %d x %d\n",
                       __FUNCTION__,
                       box.x, box.y, box.z,
                       box.w, box.h, box.d);
 
-      ret = SVGA3D_UpdateGBImage(svga->swc, surf, &box, face, transfer->level);
-      if (ret != PIPE_OK) {
-         svga_context_flush(svga, NULL);
-         ret = SVGA3D_UpdateGBImage(svga->swc, surf, &box, face, transfer->level);
+      if (svga_have_vgpu10(svga)) {
+         unsigned i;
+         for (i = 0; i < nlayers; i++) {
+            ret = update_image_vgpu10(svga, surf, &box,
+                                      st->slice + i, transfer->level,
+                                      tex->b.b.last_level + 1);
+            assert(ret == PIPE_OK);
+         }
+      } else {
+         assert(nlayers == 1);
+         ret = update_image_vgpu9(svga, surf, &box, st->slice, transfer->level);
          assert(ret == PIPE_OK);
       }
+
+      svga->hud.num_resource_updates++;
+
+      (void) ret;
    }
 
    ss->texture_timestamp++;
    svga_age_texture_view(tex, transfer->level);
    if (transfer->resource->target == PIPE_TEXTURE_CUBE)
-      svga_define_texture_level(tex, transfer->box.z, transfer->level);
+      svga_define_texture_level(tex, st->slice, transfer->level);
    else
       svga_define_texture_level(tex, 0, transfer->level);
 
@@ -632,17 +765,28 @@ svga_texture_transfer_unmap(struct pipe_context *pipe,
       sws->buffer_destroy(sws, st->hwbuf);
    }
    FREE(st);
+   SVGA_STATS_TIME_POP(sws);
 }
 
 
-struct u_resource_vtbl svga_texture_vtbl = 
+/**
+ * Does format store depth values?
+ */
+static inline boolean
+format_has_depth(enum pipe_format format)
+{
+   const struct util_format_description *desc = util_format_description(format);
+   return util_format_has_depth(desc);
+}
+
+
+struct u_resource_vtbl svga_texture_vtbl =
 {
    svga_texture_get_handle,          /* get_handle */
    svga_texture_destroy,             /* resource_destroy */
    svga_texture_transfer_map,        /* transfer_map */
    u_default_transfer_flush_region,   /* transfer_flush_region */
    svga_texture_transfer_unmap,              /* transfer_unmap */
-   u_default_transfer_inline_write    /* transfer_inline_write */
 };
 
 
@@ -651,57 +795,139 @@ svga_texture_create(struct pipe_screen *screen,
                     const struct pipe_resource *template)
 {
    struct svga_screen *svgascreen = svga_screen(screen);
-   struct svga_texture *tex = CALLOC_STRUCT(svga_texture);
+   struct svga_texture *tex;
+   unsigned bindings = template->bind;
 
-   if (!tex)
-      goto error1;
+   SVGA_STATS_TIME_PUSH(svgascreen->sws,
+                        SVGA_STATS_TIME_CREATETEXTURE);
+
+   assert(template->last_level < SVGA_MAX_TEXTURE_LEVELS);
+   if (template->last_level >= SVGA_MAX_TEXTURE_LEVELS) {
+      goto fail_notex;
+   }
+
+   tex = CALLOC_STRUCT(svga_texture);
+   if (!tex) {
+      goto fail_notex;
+   }
+
+   tex->defined = CALLOC(template->depth0 * template->array_size,
+                         sizeof(tex->defined[0]));
+   if (!tex->defined) {
+      FREE(tex);
+      goto fail_notex;
+   }
+
+   tex->rendered_to = CALLOC(template->depth0 * template->array_size,
+                             sizeof(tex->rendered_to[0]));
+   if (!tex->rendered_to) {
+      goto fail;
+   }
+
+   tex->dirty = CALLOC(template->depth0 * template->array_size,
+                             sizeof(tex->dirty[0]));
+   if (!tex->dirty) {
+      goto fail;
+   }
 
    tex->b.b = *template;
    tex->b.vtbl = &svga_texture_vtbl;
    pipe_reference_init(&tex->b.b.reference, 1);
    tex->b.b.screen = screen;
 
-   assert(template->last_level < SVGA_MAX_TEXTURE_LEVELS);
-   if(template->last_level >= SVGA_MAX_TEXTURE_LEVELS)
-      goto error2;
-   
    tex->key.flags = 0;
    tex->key.size.width = template->width0;
    tex->key.size.height = template->height0;
    tex->key.size.depth = template->depth0;
+   tex->key.arraySize = 1;
+   tex->key.numFaces = 1;
 
-   if(template->target == PIPE_TEXTURE_CUBE) {
-      tex->key.flags |= SVGA3D_SURFACE_CUBEMAP;
-      tex->key.numFaces = 6;
-   }
-   else {
-      tex->key.numFaces = 1;
+   /* single sample texture can be treated as non-multisamples texture */
+   tex->key.sampleCount = template->nr_samples > 1 ? template->nr_samples : 0;
+
+   if (template->nr_samples > 1) {
+      tex->key.flags |= SVGA3D_SURFACE_MASKABLE_ANTIALIAS;
    }
 
-   if (template->target == PIPE_TEXTURE_3D) {
-      tex->key.flags |= SVGA3D_SURFACE_VOLUME;
+   if (svgascreen->sws->have_vgpu10) {
+      switch (template->target) {
+      case PIPE_TEXTURE_1D:
+         tex->key.flags |= SVGA3D_SURFACE_1D;
+         break;
+      case PIPE_TEXTURE_1D_ARRAY:
+         tex->key.flags |= SVGA3D_SURFACE_1D;
+         /* fall-through */
+      case PIPE_TEXTURE_2D_ARRAY:
+         tex->key.flags |= SVGA3D_SURFACE_ARRAY;
+         tex->key.arraySize = template->array_size;
+         break;
+      case PIPE_TEXTURE_3D:
+         tex->key.flags |= SVGA3D_SURFACE_VOLUME;
+         break;
+      case PIPE_TEXTURE_CUBE:
+         tex->key.flags |= (SVGA3D_SURFACE_CUBEMAP | SVGA3D_SURFACE_ARRAY);
+         tex->key.numFaces = 6;
+         break;
+      default:
+         break;
+      }
+   }
+   else {
+      switch (template->target) {
+      case PIPE_TEXTURE_3D:
+         tex->key.flags |= SVGA3D_SURFACE_VOLUME;
+         break;
+      case PIPE_TEXTURE_CUBE:
+         tex->key.flags |= SVGA3D_SURFACE_CUBEMAP;
+         tex->key.numFaces = 6;
+         break;
+      default:
+         break;
+      }
    }
 
    tex->key.cachable = 1;
 
-   if (template->bind & PIPE_BIND_SAMPLER_VIEW)
+   if ((bindings & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_DEPTH_STENCIL)) &&
+       !(bindings & PIPE_BIND_SAMPLER_VIEW)) {
+      /* Also check if the format can be sampled from */
+      if (screen->is_format_supported(screen, template->format,
+                                      template->target,
+                                      template->nr_samples,
+                                      PIPE_BIND_SAMPLER_VIEW)) {
+         bindings |= PIPE_BIND_SAMPLER_VIEW;
+      }
+   }
+
+   if (bindings & PIPE_BIND_SAMPLER_VIEW) {
       tex->key.flags |= SVGA3D_SURFACE_HINT_TEXTURE;
+      tex->key.flags |= SVGA3D_SURFACE_BIND_SHADER_RESOURCE;
+
+      if (!(bindings & PIPE_BIND_RENDER_TARGET)) {
+         /* Also check if the format is renderable */
+         if (screen->is_format_supported(screen, template->format,
+                                         template->target,
+                                         template->nr_samples,
+                                         PIPE_BIND_RENDER_TARGET)) {
+            bindings |= PIPE_BIND_RENDER_TARGET;
+         }
+      }
+   }
 
-   if (template->bind & PIPE_BIND_DISPLAY_TARGET) {
+   if (bindings & PIPE_BIND_DISPLAY_TARGET) {
       tex->key.cachable = 0;
    }
 
-   if (template->bind & PIPE_BIND_SHARED) {
+   if (bindings & PIPE_BIND_SHARED) {
       tex->key.cachable = 0;
    }
 
-   if (template->bind & (PIPE_BIND_SCANOUT |
-                         PIPE_BIND_CURSOR)) {
-      tex->key.flags |= SVGA3D_SURFACE_HINT_SCANOUT;
+   if (bindings & (PIPE_BIND_SCANOUT | PIPE_BIND_CURSOR)) {
+      tex->key.scanout = 1;
       tex->key.cachable = 0;
    }
 
-   /* 
+   /*
     * Note: Previously we never passed the
     * SVGA3D_SURFACE_HINT_RENDERTARGET hint. Mesa cannot
     * know beforehand whether a texture will be used as a rendertarget or not
@@ -712,23 +938,62 @@ svga_texture_create(struct pipe_screen *screen,
     * (XA for example) uses it accurately and certain device versions
     * relies on it in certain situations to render correctly.
     */
-   if((template->bind & PIPE_BIND_RENDER_TARGET) &&
-      !util_format_is_s3tc(template->format))
+   if ((bindings & PIPE_BIND_RENDER_TARGET) &&
+       !util_format_is_s3tc(template->format)) {
       tex->key.flags |= SVGA3D_SURFACE_HINT_RENDERTARGET;
-   
-   if(template->bind & PIPE_BIND_DEPTH_STENCIL)
+      tex->key.flags |= SVGA3D_SURFACE_BIND_RENDER_TARGET;
+   }
+
+   if (bindings & PIPE_BIND_DEPTH_STENCIL) {
       tex->key.flags |= SVGA3D_SURFACE_HINT_DEPTHSTENCIL;
-   
+      tex->key.flags |= SVGA3D_SURFACE_BIND_DEPTH_STENCIL;
+   }
+
    tex->key.numMipLevels = template->last_level + 1;
-   
-   tex->key.format = svga_translate_format(svgascreen, template->format, template->bind);
-   if(tex->key.format == SVGA3D_FORMAT_INVALID)
-      goto error2;
+
+   tex->key.format = svga_translate_format(svgascreen, template->format,
+                                           bindings);
+   if (tex->key.format == SVGA3D_FORMAT_INVALID) {
+      goto fail;
+   }
+
+   /* The actual allocation is done with a typeless format.  Typeless
+    * formats can be reinterpreted as other formats.  For example,
+    * SVGA3D_R8G8B8A8_UNORM_TYPELESS can be interpreted as
+    * SVGA3D_R8G8B8A8_UNORM_SRGB or SVGA3D_R8G8B8A8_UNORM.
+    * Do not use typeless formats for SHARED, DISPLAY_TARGET or SCANOUT
+    * buffers.
+    */
+   if (svgascreen->sws->have_vgpu10
+       && ((bindings & (PIPE_BIND_SHARED |
+                        PIPE_BIND_DISPLAY_TARGET |
+                        PIPE_BIND_SCANOUT)) == 0)) {
+      SVGA3dSurfaceFormat typeless = svga_typeless_format(tex->key.format);
+      if (0) {
+         debug_printf("Convert resource type %s -> %s (bind 0x%x)\n",
+                      svga_format_name(tex->key.format),
+                      svga_format_name(typeless),
+                      bindings);
+      }
+
+      if (svga_format_is_uncompressed_snorm(tex->key.format)) {
+         /* We can't normally render to snorm surfaces, but once we
+          * substitute a typeless format, we can if the rendertarget view
+          * is unorm.  This can happen with GL_ARB_copy_image.
+          */
+         tex->key.flags |= SVGA3D_SURFACE_HINT_RENDERTARGET;
+         tex->key.flags |= SVGA3D_SURFACE_BIND_RENDER_TARGET;
+      }
+
+      tex->key.format = typeless;
+   }
 
    SVGA_DBG(DEBUG_DMA, "surface_create for texture\n", tex->handle);
-   tex->handle = svga_screen_surface_create(svgascreen, &tex->key);
-   if (!tex->handle)
-       goto error2;
+   tex->handle = svga_screen_surface_create(svgascreen, bindings,
+                                            tex->b.b.usage, &tex->key);
+   if (!tex->handle) {
+      goto fail;
+   }
 
    SVGA_DBG(DEBUG_DMA, "  --> got sid %p (texture)\n", tex->handle);
 
@@ -736,19 +1001,23 @@ svga_texture_create(struct pipe_screen *screen,
                    (debug_reference_descriptor)debug_describe_resource, 0);
 
    tex->size = util_resource_size(template);
-   svgascreen->total_resource_bytes += tex->size;
+   svgascreen->hud.total_resource_bytes += tex->size;
+   svgascreen->hud.num_resources++;
 
-   tex->rendered_to = CALLOC(template->depth0 * template->array_size,
-                             sizeof(tex->rendered_to[0]));
-   if (!tex->rendered_to)
-      goto error2;
+   SVGA_STATS_TIME_POP(svgascreen->sws);
 
    return &tex->b.b;
 
-error2:
-   FREE(tex->rendered_to);
+fail:
+   if (tex->dirty)
+      FREE(tex->dirty);
+   if (tex->rendered_to)
+      FREE(tex->rendered_to);
+   if (tex->defined)
+      FREE(tex->defined);
    FREE(tex);
-error1:
+fail_notex:
+   SVGA_STATS_TIME_POP(svgascreen->sws);
    return NULL;
 }
 
@@ -759,6 +1028,7 @@ svga_texture_from_handle(struct pipe_screen *screen,
                         struct winsys_handle *whandle)
 {
    struct svga_winsys_screen *sws = svga_winsys_screen(screen);
+   struct svga_screen *ss = svga_screen(screen);
    struct svga_winsys_surface *srf;
    struct svga_texture *tex;
    enum SVGA3dSurfaceFormat format = 0;
@@ -777,16 +1047,28 @@ svga_texture_from_handle(struct pipe_screen *screen,
    if (!srf)
       return NULL;
 
-   if (svga_translate_format(svga_screen(screen), template->format, template->bind) != format) {
-      unsigned f1 = svga_translate_format(svga_screen(screen), template->format, template->bind);
+   if (svga_translate_format(svga_screen(screen), template->format,
+                             template->bind) != format) {
+      unsigned f1 = svga_translate_format(svga_screen(screen),
+                                          template->format, template->bind);
       unsigned f2 = format;
 
-      /* It's okay for XRGB and ARGB or depth with/out stencil to get mixed up */
-      if ( !( (f1 == SVGA3D_X8R8G8B8 && f2 == SVGA3D_A8R8G8B8) ||
+      /* It's okay for XRGB and ARGB or depth with/out stencil to get mixed up.
+       */
+      if (f1 == SVGA3D_B8G8R8A8_UNORM)
+         f1 = SVGA3D_A8R8G8B8;
+      if (f1 == SVGA3D_B8G8R8X8_UNORM)
+         f1 = SVGA3D_X8R8G8B8;
+
+      if ( !( (f1 == f2) ||
+              (f1 == SVGA3D_X8R8G8B8 && f2 == SVGA3D_A8R8G8B8) ||
+              (f1 == SVGA3D_X8R8G8B8 && f2 == SVGA3D_B8G8R8X8_UNORM) ||
               (f1 == SVGA3D_A8R8G8B8 && f2 == SVGA3D_X8R8G8B8) ||
+              (f1 == SVGA3D_A8R8G8B8 && f2 == SVGA3D_B8G8R8A8_UNORM) ||
               (f1 == SVGA3D_Z_D24X8 && f2 == SVGA3D_Z_D24S8) ||
               (f1 == SVGA3D_Z_DF24 && f2 == SVGA3D_Z_D24S8_INT) ) ) {
-         debug_printf("%s wrong format %u != %u\n", __FUNCTION__, f1, f2);
+         debug_printf("%s wrong format %s != %s\n", __FUNCTION__,
+                      svga_format_name(f1), svga_format_name(f2));
          return NULL;
       }
    }
@@ -795,6 +1077,13 @@ svga_texture_from_handle(struct pipe_screen *screen,
    if (!tex)
       return NULL;
 
+   tex->defined = CALLOC(template->depth0 * template->array_size,
+                         sizeof(tex->defined[0]));
+   if (!tex->defined) {
+      FREE(tex);
+      return NULL;
+   }
+
    tex->b.b = *template;
    tex->b.vtbl = &svga_texture_vtbl;
    pipe_reference_init(&tex->b.b.reference, 1);
@@ -803,9 +1092,93 @@ svga_texture_from_handle(struct pipe_screen *screen,
    SVGA_DBG(DEBUG_DMA, "wrap surface sid %p\n", srf);
 
    tex->key.cachable = 0;
+   tex->key.format = format;
    tex->handle = srf;
 
    tex->rendered_to = CALLOC(1, sizeof(tex->rendered_to[0]));
+   if (!tex->rendered_to)
+      goto fail;
+
+   tex->dirty = CALLOC(1, sizeof(tex->dirty[0]));
+   if (!tex->dirty)
+      goto fail;
+
+   tex->imported = TRUE;
+
+   ss->hud.num_resources++;
 
    return &tex->b.b;
+
+fail:
+   if (tex->defined)
+      FREE(tex->defined);
+   if (tex->rendered_to)
+      FREE(tex->rendered_to);
+   if (tex->dirty)
+      FREE(tex->dirty);
+   FREE(tex);
+   return NULL;
+}
+
+boolean
+svga_texture_generate_mipmap(struct pipe_context *pipe,
+                             struct pipe_resource *pt,
+                             enum pipe_format format,
+                             unsigned base_level,
+                             unsigned last_level,
+                             unsigned first_layer,
+                             unsigned last_layer)
+{
+   struct pipe_sampler_view templ, *psv;
+   struct svga_pipe_sampler_view *sv;
+   struct svga_context *svga = svga_context(pipe);
+   struct svga_texture *tex = svga_texture(pt);
+   enum pipe_error ret;
+
+   assert(svga_have_vgpu10(svga));
+
+   /* Only support 2D texture for now */
+   if (pt->target != PIPE_TEXTURE_2D)
+      return FALSE;
+
+   /* Fallback to the mipmap generation utility for those formats that
+    * do not support hw generate mipmap
+    */
+   if (!svga_format_support_gen_mips(format))
+      return FALSE;
+
+   /* Make sure the texture surface was created with
+    * SVGA3D_SURFACE_BIND_RENDER_TARGET
+    */
+   if (!tex->handle || !(tex->key.flags & SVGA3D_SURFACE_BIND_RENDER_TARGET))
+      return FALSE;
+
+   templ.format = format;
+   templ.u.tex.first_layer = first_layer;
+   templ.u.tex.last_layer = last_layer;
+   templ.u.tex.first_level = base_level;
+   templ.u.tex.last_level = last_level;
+
+   psv = pipe->create_sampler_view(pipe, pt, &templ);
+   if (psv == NULL)
+      return FALSE;
+
+   sv = svga_pipe_sampler_view(psv);
+   ret = svga_validate_pipe_sampler_view(svga, sv);
+   if (ret != PIPE_OK) {
+      svga_context_flush(svga, NULL);
+      ret = svga_validate_pipe_sampler_view(svga, sv);
+      assert(ret == PIPE_OK);
+   }
+
+   ret = SVGA3D_vgpu10_GenMips(svga->swc, sv->id, tex->handle);
+   if (ret != PIPE_OK) {
+      svga_context_flush(svga, NULL);
+      ret = SVGA3D_vgpu10_GenMips(svga->swc, sv->id, tex->handle);
+   }
+   pipe_sampler_view_reference(&psv, NULL);
+
+   svga->hud.num_generate_mipmap++;
+
+   return TRUE;
 }