ilo: move transfer-related functions to a new file
authorChia-I Wu <olvaffe@gmail.com>
Mon, 13 May 2013 06:29:09 +0000 (14:29 +0800)
committerChia-I Wu <olvaffe@gmail.com>
Tue, 14 May 2013 08:01:20 +0000 (16:01 +0800)
Resource mapping is distinct from resource allocation, and is going to get
more and more complex.  Move the related functions to a new file to make the
separation clear.

src/gallium/drivers/ilo/Makefile.sources
src/gallium/drivers/ilo/ilo_context.c
src/gallium/drivers/ilo/ilo_resource.c
src/gallium/drivers/ilo/ilo_resource.h
src/gallium/drivers/ilo/ilo_transfer.c [new file with mode: 0644]
src/gallium/drivers/ilo/ilo_transfer.h [new file with mode: 0644]

index 661f3f12f3e1b726fa3947022e225b873ad3ed32..0a1cc85c58e44cb7fdd2d4d7aaddfa091de662f7 100644 (file)
@@ -16,6 +16,7 @@ C_SOURCES := \
        ilo_screen.c \
        ilo_shader.c \
        ilo_state.c \
+       ilo_transfer.c \
        ilo_video.c \
        shader/ilo_shader_cs.c \
        shader/ilo_shader_fs.c \
index 3a101b47e6f3c5c1f1c2bcefa72b551d9ada9b00..bd4197bd84bfeb91fdb6c695c721328ab8dccc0b 100644 (file)
@@ -37,6 +37,7 @@
 #include "ilo_screen.h"
 #include "ilo_shader.h"
 #include "ilo_state.h"
+#include "ilo_transfer.h"
 #include "ilo_video.h"
 #include "ilo_context.h"
 
index 41e4ffb1a9bccbcac08e7a1c102d27ad0cd77a43..96f9d0037937b03e6dc681afc8a678d645cd5efe 100644 (file)
  *    Chia-I Wu <olv@lunarg.com>
  */
 
-#include "util/u_surface.h"
-#include "util/u_transfer.h"
-#include "util/u_format_etc.h"
-
-#include "ilo_cp.h"
-#include "ilo_context.h"
 #include "ilo_screen.h"
 #include "ilo_resource.h"
 
 /* use PIPE_BIND_CUSTOM to indicate MCS */
 #define ILO_BIND_MCS PIPE_BIND_CUSTOM
 
-enum ilo_transfer_map_method {
-   ILO_TRANSFER_MAP_DIRECT,
-   ILO_TRANSFER_MAP_STAGING_SYS,
-};
-
-struct ilo_transfer {
-   struct pipe_transfer base;
-
-   enum ilo_transfer_map_method method;
-   void *ptr;
-
-   void *staging_sys;
-};
-
-static inline struct ilo_transfer *
-ilo_transfer(struct pipe_transfer *transfer)
-{
-   return (struct ilo_transfer *) transfer;
-}
-
 static struct intel_bo *
 alloc_buf_bo(const struct ilo_resource *res)
 {
@@ -162,8 +136,8 @@ alloc_tex_bo(const struct ilo_resource *res)
    return bo;
 }
 
-static bool
-realloc_bo(struct ilo_resource *res)
+bool
+ilo_resource_alloc_bo(struct ilo_resource *res)
 {
    struct intel_bo *old_bo = res->bo;
 
@@ -191,412 +165,6 @@ realloc_bo(struct ilo_resource *res)
    return true;
 }
 
-static void
-ilo_transfer_inline_write(struct pipe_context *pipe,
-                          struct pipe_resource *r,
-                          unsigned level,
-                          unsigned usage,
-                          const struct pipe_box *box,
-                          const void *data,
-                          unsigned stride,
-                          unsigned layer_stride)
-{
-   struct ilo_context *ilo = ilo_context(pipe);
-   struct ilo_resource *res = ilo_resource(r);
-   int offset, size;
-   bool will_be_busy;
-
-   /*
-    * Fall back to map(), memcpy(), and unmap().  We use this path for
-    * unsynchronized write, as the buffer is likely to be busy and pwrite()
-    * will stall.
-    */
-   if (unlikely(res->base.target != PIPE_BUFFER) ||
-       (usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
-      u_default_transfer_inline_write(pipe, r,
-            level, usage, box, data, stride, layer_stride);
-
-      return;
-   }
-
-   /*
-    * XXX With hardware context support, the bo may be needed by GPU without
-    * being referenced by ilo->cp->bo.  We have to flush unconditionally, and
-    * that is bad.
-    */
-   if (ilo->cp->hw_ctx)
-      ilo_cp_flush(ilo->cp);
-
-   will_be_busy = ilo->cp->bo->references(ilo->cp->bo, res->bo);
-
-   /* see if we can avoid stalling */
-   if (will_be_busy || intel_bo_is_busy(res->bo)) {
-      bool will_stall = true;
-
-      if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
-         /* old data not needed so discard the old bo to avoid stalling */
-         if (realloc_bo(res))
-            will_stall = false;
-      }
-      else {
-         /*
-          * We could allocate a temporary bo to hold the data and emit
-          * pipelined copy blit to move them to res->bo.  But for now, do
-          * nothing.
-          */
-      }
-
-      /* flush to make bo busy (so that pwrite() stalls as it should be) */
-      if (will_stall && will_be_busy)
-         ilo_cp_flush(ilo->cp);
-   }
-
-   /* for PIPE_BUFFERs, conversion should not be needed */
-   assert(res->bo_format == res->base.format);
-
-   /* they should specify just an offset and a size */
-   assert(level == 0);
-   assert(box->y == 0);
-   assert(box->z == 0);
-   assert(box->height == 1);
-   assert(box->depth == 1);
-   offset = box->x;
-   size = box->width;
-
-   res->bo->pwrite(res->bo, offset, size, data);
-}
-
-static void
-transfer_unmap_sys_convert(enum pipe_format dst_fmt,
-                           const struct pipe_transfer *dst_xfer,
-                           void *dst,
-                           enum pipe_format src_fmt,
-                           const struct pipe_transfer *src_xfer,
-                           const void *src)
-{
-   int i;
-
-   switch (src_fmt) {
-   case PIPE_FORMAT_ETC1_RGB8:
-      assert(dst_fmt == PIPE_FORMAT_R8G8B8X8_UNORM);
-
-      for (i = 0; i < dst_xfer->box.depth; i++) {
-         util_format_etc1_rgb8_unpack_rgba_8unorm(dst,
-               dst_xfer->stride, src, src_xfer->stride,
-               dst_xfer->box.width, dst_xfer->box.height);
-
-         dst += dst_xfer->layer_stride;
-         src += src_xfer->layer_stride;
-      }
-      break;
-   default:
-      assert(!"unable to convert the staging data");
-      break;
-   }
-}
-
-static void
-transfer_unmap_sys(struct ilo_context *ilo,
-                   struct ilo_resource *res,
-                   struct ilo_transfer *xfer)
-{
-   const void *src = xfer->ptr;
-   struct pipe_transfer *dst_xfer;
-   void *dst;
-
-   dst = ilo->base.transfer_map(&ilo->base,
-         xfer->base.resource, xfer->base.level,
-         PIPE_TRANSFER_WRITE |
-         PIPE_TRANSFER_MAP_DIRECTLY |
-         PIPE_TRANSFER_DISCARD_RANGE,
-         &xfer->base.box, &dst_xfer);
-   if (!dst_xfer) {
-      ilo_err("failed to map resource for moving staging data\n");
-      FREE(xfer->staging_sys);
-      return;
-   }
-
-   if (likely(res->bo_format != res->base.format)) {
-      transfer_unmap_sys_convert(res->bo_format, dst_xfer, dst,
-            res->base.format, &xfer->base, src);
-   }
-   else {
-      util_copy_box(dst, res->bo_format,
-            dst_xfer->stride, dst_xfer->layer_stride, 0, 0, 0,
-            dst_xfer->box.width, dst_xfer->box.height, dst_xfer->box.depth,
-            src, xfer->base.stride, xfer->base.layer_stride, 0, 0, 0);
-   }
-
-   ilo->base.transfer_unmap(&ilo->base, dst_xfer);
-   FREE(xfer->staging_sys);
-}
-
-static bool
-transfer_map_sys(struct ilo_context *ilo,
-                 struct ilo_resource *res,
-                 struct ilo_transfer *xfer)
-{
-   const struct pipe_box *box = &xfer->base.box;
-   const size_t stride = util_format_get_stride(res->base.format, box->width);
-   const size_t size =
-      util_format_get_2d_size(res->base.format, stride, box->height);
-   bool read_back = false;
-
-   if (xfer->base.usage & PIPE_TRANSFER_READ) {
-      read_back = true;
-   }
-   else if (xfer->base.usage & PIPE_TRANSFER_WRITE) {
-      const unsigned discard_flags =
-         (PIPE_TRANSFER_DISCARD_RANGE | PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE);
-
-      if (!(xfer->base.usage & discard_flags))
-         read_back = true;
-   }
-
-   /* TODO */
-   if (read_back)
-      return false;
-
-   xfer->staging_sys = MALLOC(size * box->depth);
-   if (!xfer->staging_sys)
-      return false;
-
-   xfer->base.stride = stride;
-   xfer->base.layer_stride = size;
-   xfer->ptr = xfer->staging_sys;
-
-   return true;
-}
-
-static void
-transfer_unmap_direct(struct ilo_context *ilo,
-                      struct ilo_resource *res,
-                      struct ilo_transfer *xfer)
-{
-   res->bo->unmap(res->bo);
-}
-
-static bool
-transfer_map_direct(struct ilo_context *ilo,
-                    struct ilo_resource *res,
-                    struct ilo_transfer *xfer)
-{
-   int x, y, err;
-
-   if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
-      err = res->bo->map_unsynchronized(res->bo);
-   /* prefer map() when there is the last-level cache */
-   else if (res->tiling == INTEL_TILING_NONE &&
-            (ilo->dev->has_llc || (xfer->base.usage & PIPE_TRANSFER_READ)))
-      err = res->bo->map(res->bo, (xfer->base.usage & PIPE_TRANSFER_WRITE));
-   else
-      err = res->bo->map_gtt(res->bo);
-
-   if (err)
-      return false;
-
-   /* note that stride is for a block row, not a texel row */
-   xfer->base.stride = res->bo_stride;
-
-   /*
-    * we can walk through layers when the resource is a texture array or
-    * when this is the first level of a 3D texture being mapped
-    */
-   if (res->base.array_size > 1 ||
-       (res->base.target == PIPE_TEXTURE_3D && xfer->base.level == 0)) {
-      const unsigned qpitch = res->slice_offsets[xfer->base.level][1].y -
-         res->slice_offsets[xfer->base.level][0].y;
-
-      assert(qpitch % res->block_height == 0);
-      xfer->base.layer_stride = (qpitch / res->block_height) * xfer->base.stride;
-   }
-   else {
-      xfer->base.layer_stride = 0;
-   }
-
-   x = res->slice_offsets[xfer->base.level][xfer->base.box.z].x;
-   y = res->slice_offsets[xfer->base.level][xfer->base.box.z].y;
-
-   x += xfer->base.box.x;
-   y += xfer->base.box.y;
-
-   /* in blocks */
-   assert(x % res->block_width == 0 && y % res->block_height == 0);
-   x /= res->block_width;
-   y /= res->block_height;
-
-   xfer->ptr = res->bo->get_virtual(res->bo);
-   xfer->ptr += y * res->bo_stride + x * res->bo_cpp;
-
-   return true;
-}
-
-/**
- * Choose the best mapping method, depending on the transfer usage and whether
- * the bo is busy.
- */
-static bool
-transfer_map_choose_method(struct ilo_context *ilo,
-                           struct ilo_resource *res,
-                           struct ilo_transfer *xfer)
-{
-   bool will_be_busy, will_stall;
-
-   /* need to convert on-the-fly */
-   if (res->bo_format != res->base.format &&
-       !(xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)) {
-      xfer->method = ILO_TRANSFER_MAP_STAGING_SYS;
-
-      return true;
-   }
-
-   xfer->method = ILO_TRANSFER_MAP_DIRECT;
-
-   /* unsynchronized map does not stall */
-   if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
-      return true;
-
-   will_be_busy = ilo->cp->bo->references(ilo->cp->bo, res->bo);
-   if (!will_be_busy) {
-      /*
-       * XXX With hardware context support, the bo may be needed by GPU
-       * without being referenced by ilo->cp->bo.  We have to flush
-       * unconditionally, and that is bad.
-       */
-      if (ilo->cp->hw_ctx)
-         ilo_cp_flush(ilo->cp);
-
-      if (!intel_bo_is_busy(res->bo))
-         return true;
-   }
-
-   /* bo is busy and mapping it will stall */
-   will_stall = true;
-
-   if (xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY) {
-      /* nothing we can do */
-   }
-   else if (xfer->base.usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
-      /* discard old bo and allocate a new one for mapping */
-      if (realloc_bo(res))
-         will_stall = false;
-   }
-   else if (xfer->base.usage & PIPE_TRANSFER_FLUSH_EXPLICIT) {
-      /*
-       * We could allocate and return a system buffer here.  When a region of
-       * the buffer is explicitly flushed, we pwrite() the region to a
-       * temporary bo and emit pipelined copy blit.
-       *
-       * For now, do nothing.
-       */
-   }
-   else if (xfer->base.usage & PIPE_TRANSFER_DISCARD_RANGE) {
-      /*
-       * We could allocate a temporary bo for mapping, and emit pipelined copy
-       * blit upon unmapping.
-       *
-       * For now, do nothing.
-       */
-   }
-
-   if (will_stall) {
-      if (xfer->base.usage & PIPE_TRANSFER_DONTBLOCK)
-         return false;
-
-      /* flush to make bo busy (so that map() stalls as it should be) */
-      if (will_be_busy)
-         ilo_cp_flush(ilo->cp);
-   }
-
-   return true;
-}
-
-static void
-ilo_transfer_flush_region(struct pipe_context *pipe,
-                          struct pipe_transfer *transfer,
-                          const struct pipe_box *box)
-{
-}
-
-static void
-ilo_transfer_unmap(struct pipe_context *pipe,
-                   struct pipe_transfer *transfer)
-{
-   struct ilo_context *ilo = ilo_context(pipe);
-   struct ilo_resource *res = ilo_resource(transfer->resource);
-   struct ilo_transfer *xfer = ilo_transfer(transfer);
-
-   switch (xfer->method) {
-   case ILO_TRANSFER_MAP_DIRECT:
-      transfer_unmap_direct(ilo, res, xfer);
-      break;
-   case ILO_TRANSFER_MAP_STAGING_SYS:
-      transfer_unmap_sys(ilo, res, xfer);
-      break;
-   default:
-      assert(!"unknown mapping method");
-      break;
-   }
-
-   pipe_resource_reference(&xfer->base.resource, NULL);
-   FREE(xfer);
-}
-
-static void *
-ilo_transfer_map(struct pipe_context *pipe,
-                 struct pipe_resource *r,
-                 unsigned level,
-                 unsigned usage,
-                 const struct pipe_box *box,
-                 struct pipe_transfer **transfer)
-{
-   struct ilo_context *ilo = ilo_context(pipe);
-   struct ilo_resource *res = ilo_resource(r);
-   struct ilo_transfer *xfer;
-   int ok;
-
-   xfer = MALLOC_STRUCT(ilo_transfer);
-   if (!xfer) {
-      *transfer = NULL;
-      return NULL;
-   }
-
-   xfer->base.resource = NULL;
-   pipe_resource_reference(&xfer->base.resource, &res->base);
-   xfer->base.level = level;
-   xfer->base.usage = usage;
-   xfer->base.box = *box;
-
-   ok = transfer_map_choose_method(ilo, res, xfer);
-   if (ok) {
-      switch (xfer->method) {
-      case ILO_TRANSFER_MAP_DIRECT:
-         ok = transfer_map_direct(ilo, res, xfer);
-         break;
-      case ILO_TRANSFER_MAP_STAGING_SYS:
-         ok = transfer_map_sys(ilo, res, xfer);
-         break;
-      default:
-         assert(!"unknown mapping method");
-         ok = false;
-         break;
-      }
-   }
-
-   if (!ok) {
-      pipe_resource_reference(&xfer->base.resource, NULL);
-      FREE(xfer);
-
-      *transfer = NULL;
-
-      return NULL;
-   }
-
-   *transfer = &xfer->base;
-
-   return xfer->ptr;
-}
-
 static bool
 alloc_slice_offsets(struct ilo_resource *res)
 {
@@ -1373,7 +941,7 @@ create_resource(struct pipe_screen *screen,
    else
       init_texture(res);
 
-   if (!realloc_bo(res)) {
+   if (!ilo_resource_alloc_bo(res)) {
       free_slice_offsets(res);
       FREE(res);
       return NULL;
@@ -1448,18 +1016,6 @@ ilo_init_resource_functions(struct ilo_screen *is)
    is->base.resource_destroy = ilo_resource_destroy;
 }
 
-/**
- * Initialize transfer-related functions.
- */
-void
-ilo_init_transfer_functions(struct ilo_context *ilo)
-{
-   ilo->base.transfer_map = ilo_transfer_map;
-   ilo->base.transfer_flush_region = ilo_transfer_flush_region;
-   ilo->base.transfer_unmap = ilo_transfer_unmap;
-   ilo->base.transfer_inline_write = ilo_transfer_inline_write;
-}
-
 /**
  * Return the offset (in bytes) to a slice within the bo.
  *
index 03d31d4bcb9578f626901918947a82254df71521..87f80d5c5757702e34046b6b6ada93f92c4c1eb8 100644 (file)
@@ -33,7 +33,6 @@
 #include "ilo_common.h"
 
 struct ilo_screen;
-struct ilo_context;
 struct winsys_handle;
 
 /*
@@ -89,8 +88,8 @@ ilo_resource(struct pipe_resource *res)
 void
 ilo_init_resource_functions(struct ilo_screen *is);
 
-void
-ilo_init_transfer_functions(struct ilo_context *ilo);
+bool
+ilo_resource_alloc_bo(struct ilo_resource *res);
 
 unsigned
 ilo_resource_get_slice_offset(const struct ilo_resource *res,
diff --git a/src/gallium/drivers/ilo/ilo_transfer.c b/src/gallium/drivers/ilo/ilo_transfer.c
new file mode 100644 (file)
index 0000000..3b9c4d6
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2012-2013 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Chia-I Wu <olv@lunarg.com>
+ */
+
+#include "util/u_surface.h"
+#include "util/u_transfer.h"
+#include "util/u_format_etc.h"
+
+#include "ilo_cp.h"
+#include "ilo_context.h"
+#include "ilo_resource.h"
+#include "ilo_transfer.h"
+
+enum ilo_transfer_map_method {
+   ILO_TRANSFER_MAP_DIRECT,
+   ILO_TRANSFER_MAP_STAGING_SYS,
+};
+
+struct ilo_transfer {
+   struct pipe_transfer base;
+
+   enum ilo_transfer_map_method method;
+   void *ptr;
+
+   void *staging_sys;
+};
+
+static inline struct ilo_transfer *
+ilo_transfer(struct pipe_transfer *transfer)
+{
+   return (struct ilo_transfer *) transfer;
+}
+
+static void
+ilo_transfer_inline_write(struct pipe_context *pipe,
+                          struct pipe_resource *r,
+                          unsigned level,
+                          unsigned usage,
+                          const struct pipe_box *box,
+                          const void *data,
+                          unsigned stride,
+                          unsigned layer_stride)
+{
+   struct ilo_context *ilo = ilo_context(pipe);
+   struct ilo_resource *res = ilo_resource(r);
+   int offset, size;
+   bool will_be_busy;
+
+   /*
+    * Fall back to map(), memcpy(), and unmap().  We use this path for
+    * unsynchronized write, as the buffer is likely to be busy and pwrite()
+    * will stall.
+    */
+   if (unlikely(res->base.target != PIPE_BUFFER) ||
+       (usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
+      u_default_transfer_inline_write(pipe, r,
+            level, usage, box, data, stride, layer_stride);
+
+      return;
+   }
+
+   /*
+    * XXX With hardware context support, the bo may be needed by GPU without
+    * being referenced by ilo->cp->bo.  We have to flush unconditionally, and
+    * that is bad.
+    */
+   if (ilo->cp->hw_ctx)
+      ilo_cp_flush(ilo->cp);
+
+   will_be_busy = ilo->cp->bo->references(ilo->cp->bo, res->bo);
+
+   /* see if we can avoid stalling */
+   if (will_be_busy || intel_bo_is_busy(res->bo)) {
+      bool will_stall = true;
+
+      if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
+         /* old data not needed so discard the old bo to avoid stalling */
+         if (ilo_resource_alloc_bo(res))
+            will_stall = false;
+      }
+      else {
+         /*
+          * We could allocate a temporary bo to hold the data and emit
+          * pipelined copy blit to move them to res->bo.  But for now, do
+          * nothing.
+          */
+      }
+
+      /* flush to make bo busy (so that pwrite() stalls as it should be) */
+      if (will_stall && will_be_busy)
+         ilo_cp_flush(ilo->cp);
+   }
+
+   /* for PIPE_BUFFERs, conversion should not be needed */
+   assert(res->bo_format == res->base.format);
+
+   /* they should specify just an offset and a size */
+   assert(level == 0);
+   assert(box->y == 0);
+   assert(box->z == 0);
+   assert(box->height == 1);
+   assert(box->depth == 1);
+   offset = box->x;
+   size = box->width;
+
+   res->bo->pwrite(res->bo, offset, size, data);
+}
+
+static void
+transfer_unmap_sys_convert(enum pipe_format dst_fmt,
+                           const struct pipe_transfer *dst_xfer,
+                           void *dst,
+                           enum pipe_format src_fmt,
+                           const struct pipe_transfer *src_xfer,
+                           const void *src)
+{
+   int i;
+
+   switch (src_fmt) {
+   case PIPE_FORMAT_ETC1_RGB8:
+      assert(dst_fmt == PIPE_FORMAT_R8G8B8X8_UNORM);
+
+      for (i = 0; i < dst_xfer->box.depth; i++) {
+         util_format_etc1_rgb8_unpack_rgba_8unorm(dst,
+               dst_xfer->stride, src, src_xfer->stride,
+               dst_xfer->box.width, dst_xfer->box.height);
+
+         dst += dst_xfer->layer_stride;
+         src += src_xfer->layer_stride;
+      }
+      break;
+   default:
+      assert(!"unable to convert the staging data");
+      break;
+   }
+}
+
+static void
+transfer_unmap_sys(struct ilo_context *ilo,
+                   struct ilo_resource *res,
+                   struct ilo_transfer *xfer)
+{
+   const void *src = xfer->ptr;
+   struct pipe_transfer *dst_xfer;
+   void *dst;
+
+   dst = ilo->base.transfer_map(&ilo->base,
+         xfer->base.resource, xfer->base.level,
+         PIPE_TRANSFER_WRITE |
+         PIPE_TRANSFER_MAP_DIRECTLY |
+         PIPE_TRANSFER_DISCARD_RANGE,
+         &xfer->base.box, &dst_xfer);
+   if (!dst_xfer) {
+      ilo_err("failed to map resource for moving staging data\n");
+      FREE(xfer->staging_sys);
+      return;
+   }
+
+   if (likely(res->bo_format != res->base.format)) {
+      transfer_unmap_sys_convert(res->bo_format, dst_xfer, dst,
+            res->base.format, &xfer->base, src);
+   }
+   else {
+      util_copy_box(dst, res->bo_format,
+            dst_xfer->stride, dst_xfer->layer_stride, 0, 0, 0,
+            dst_xfer->box.width, dst_xfer->box.height, dst_xfer->box.depth,
+            src, xfer->base.stride, xfer->base.layer_stride, 0, 0, 0);
+   }
+
+   ilo->base.transfer_unmap(&ilo->base, dst_xfer);
+   FREE(xfer->staging_sys);
+}
+
+static bool
+transfer_map_sys(struct ilo_context *ilo,
+                 struct ilo_resource *res,
+                 struct ilo_transfer *xfer)
+{
+   const struct pipe_box *box = &xfer->base.box;
+   const size_t stride = util_format_get_stride(res->base.format, box->width);
+   const size_t size =
+      util_format_get_2d_size(res->base.format, stride, box->height);
+   bool read_back = false;
+
+   if (xfer->base.usage & PIPE_TRANSFER_READ) {
+      read_back = true;
+   }
+   else if (xfer->base.usage & PIPE_TRANSFER_WRITE) {
+      const unsigned discard_flags =
+         (PIPE_TRANSFER_DISCARD_RANGE | PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE);
+
+      if (!(xfer->base.usage & discard_flags))
+         read_back = true;
+   }
+
+   /* TODO */
+   if (read_back)
+      return false;
+
+   xfer->staging_sys = MALLOC(size * box->depth);
+   if (!xfer->staging_sys)
+      return false;
+
+   xfer->base.stride = stride;
+   xfer->base.layer_stride = size;
+   xfer->ptr = xfer->staging_sys;
+
+   return true;
+}
+
+static void
+transfer_unmap_direct(struct ilo_context *ilo,
+                      struct ilo_resource *res,
+                      struct ilo_transfer *xfer)
+{
+   res->bo->unmap(res->bo);
+}
+
+static bool
+transfer_map_direct(struct ilo_context *ilo,
+                    struct ilo_resource *res,
+                    struct ilo_transfer *xfer)
+{
+   int x, y, err;
+
+   if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
+      err = res->bo->map_unsynchronized(res->bo);
+   /* prefer map() when there is the last-level cache */
+   else if (res->tiling == INTEL_TILING_NONE &&
+            (ilo->dev->has_llc || (xfer->base.usage & PIPE_TRANSFER_READ)))
+      err = res->bo->map(res->bo, (xfer->base.usage & PIPE_TRANSFER_WRITE));
+   else
+      err = res->bo->map_gtt(res->bo);
+
+   if (err)
+      return false;
+
+   /* note that stride is for a block row, not a texel row */
+   xfer->base.stride = res->bo_stride;
+
+   /*
+    * we can walk through layers when the resource is a texture array or
+    * when this is the first level of a 3D texture being mapped
+    */
+   if (res->base.array_size > 1 ||
+       (res->base.target == PIPE_TEXTURE_3D && xfer->base.level == 0)) {
+      const unsigned qpitch = res->slice_offsets[xfer->base.level][1].y -
+         res->slice_offsets[xfer->base.level][0].y;
+
+      assert(qpitch % res->block_height == 0);
+      xfer->base.layer_stride = (qpitch / res->block_height) * xfer->base.stride;
+   }
+   else {
+      xfer->base.layer_stride = 0;
+   }
+
+   x = res->slice_offsets[xfer->base.level][xfer->base.box.z].x;
+   y = res->slice_offsets[xfer->base.level][xfer->base.box.z].y;
+
+   x += xfer->base.box.x;
+   y += xfer->base.box.y;
+
+   /* in blocks */
+   assert(x % res->block_width == 0 && y % res->block_height == 0);
+   x /= res->block_width;
+   y /= res->block_height;
+
+   xfer->ptr = res->bo->get_virtual(res->bo);
+   xfer->ptr += y * res->bo_stride + x * res->bo_cpp;
+
+   return true;
+}
+
+/**
+ * Choose the best mapping method, depending on the transfer usage and whether
+ * the bo is busy.
+ */
+static bool
+transfer_map_choose_method(struct ilo_context *ilo,
+                           struct ilo_resource *res,
+                           struct ilo_transfer *xfer)
+{
+   bool will_be_busy, will_stall;
+
+   /* need to convert on-the-fly */
+   if (res->bo_format != res->base.format &&
+       !(xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)) {
+      xfer->method = ILO_TRANSFER_MAP_STAGING_SYS;
+
+      return true;
+   }
+
+   xfer->method = ILO_TRANSFER_MAP_DIRECT;
+
+   /* unsynchronized map does not stall */
+   if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
+      return true;
+
+   will_be_busy = ilo->cp->bo->references(ilo->cp->bo, res->bo);
+   if (!will_be_busy) {
+      /*
+       * XXX With hardware context support, the bo may be needed by GPU
+       * without being referenced by ilo->cp->bo.  We have to flush
+       * unconditionally, and that is bad.
+       */
+      if (ilo->cp->hw_ctx)
+         ilo_cp_flush(ilo->cp);
+
+      if (!intel_bo_is_busy(res->bo))
+         return true;
+   }
+
+   /* bo is busy and mapping it will stall */
+   will_stall = true;
+
+   if (xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY) {
+      /* nothing we can do */
+   }
+   else if (xfer->base.usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
+      /* discard old bo and allocate a new one for mapping */
+      if (ilo_resource_alloc_bo(res))
+         will_stall = false;
+   }
+   else if (xfer->base.usage & PIPE_TRANSFER_FLUSH_EXPLICIT) {
+      /*
+       * We could allocate and return a system buffer here.  When a region of
+       * the buffer is explicitly flushed, we pwrite() the region to a
+       * temporary bo and emit pipelined copy blit.
+       *
+       * For now, do nothing.
+       */
+   }
+   else if (xfer->base.usage & PIPE_TRANSFER_DISCARD_RANGE) {
+      /*
+       * We could allocate a temporary bo for mapping, and emit pipelined copy
+       * blit upon unmapping.
+       *
+       * For now, do nothing.
+       */
+   }
+
+   if (will_stall) {
+      if (xfer->base.usage & PIPE_TRANSFER_DONTBLOCK)
+         return false;
+
+      /* flush to make bo busy (so that map() stalls as it should be) */
+      if (will_be_busy)
+         ilo_cp_flush(ilo->cp);
+   }
+
+   return true;
+}
+
+static void
+ilo_transfer_flush_region(struct pipe_context *pipe,
+                          struct pipe_transfer *transfer,
+                          const struct pipe_box *box)
+{
+}
+
+static void
+ilo_transfer_unmap(struct pipe_context *pipe,
+                   struct pipe_transfer *transfer)
+{
+   struct ilo_context *ilo = ilo_context(pipe);
+   struct ilo_resource *res = ilo_resource(transfer->resource);
+   struct ilo_transfer *xfer = ilo_transfer(transfer);
+
+   switch (xfer->method) {
+   case ILO_TRANSFER_MAP_DIRECT:
+      transfer_unmap_direct(ilo, res, xfer);
+      break;
+   case ILO_TRANSFER_MAP_STAGING_SYS:
+      transfer_unmap_sys(ilo, res, xfer);
+      break;
+   default:
+      assert(!"unknown mapping method");
+      break;
+   }
+
+   pipe_resource_reference(&xfer->base.resource, NULL);
+   FREE(xfer);
+}
+
+static void *
+ilo_transfer_map(struct pipe_context *pipe,
+                 struct pipe_resource *r,
+                 unsigned level,
+                 unsigned usage,
+                 const struct pipe_box *box,
+                 struct pipe_transfer **transfer)
+{
+   struct ilo_context *ilo = ilo_context(pipe);
+   struct ilo_resource *res = ilo_resource(r);
+   struct ilo_transfer *xfer;
+   int ok;
+
+   xfer = MALLOC_STRUCT(ilo_transfer);
+   if (!xfer) {
+      *transfer = NULL;
+      return NULL;
+   }
+
+   xfer->base.resource = NULL;
+   pipe_resource_reference(&xfer->base.resource, &res->base);
+   xfer->base.level = level;
+   xfer->base.usage = usage;
+   xfer->base.box = *box;
+
+   ok = transfer_map_choose_method(ilo, res, xfer);
+   if (ok) {
+      switch (xfer->method) {
+      case ILO_TRANSFER_MAP_DIRECT:
+         ok = transfer_map_direct(ilo, res, xfer);
+         break;
+      case ILO_TRANSFER_MAP_STAGING_SYS:
+         ok = transfer_map_sys(ilo, res, xfer);
+         break;
+      default:
+         assert(!"unknown mapping method");
+         ok = false;
+         break;
+      }
+   }
+
+   if (!ok) {
+      pipe_resource_reference(&xfer->base.resource, NULL);
+      FREE(xfer);
+
+      *transfer = NULL;
+
+      return NULL;
+   }
+
+   *transfer = &xfer->base;
+
+   return xfer->ptr;
+}
+
+/**
+ * Initialize transfer-related functions.
+ */
+void
+ilo_init_transfer_functions(struct ilo_context *ilo)
+{
+   ilo->base.transfer_map = ilo_transfer_map;
+   ilo->base.transfer_flush_region = ilo_transfer_flush_region;
+   ilo->base.transfer_unmap = ilo_transfer_unmap;
+   ilo->base.transfer_inline_write = ilo_transfer_inline_write;
+}
diff --git a/src/gallium/drivers/ilo/ilo_transfer.h b/src/gallium/drivers/ilo/ilo_transfer.h
new file mode 100644 (file)
index 0000000..25bcc44
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Mesa 3-D graphics library
+ *
+ * Copyright (C) 2012-2013 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Chia-I Wu <olv@lunarg.com>
+ */
+
+#ifndef ILO_TRANSFER_H
+#define ILO_TRANSFER_H
+
+#include "ilo_common.h"
+
+struct ilo_context;
+
+void
+ilo_init_transfer_functions(struct ilo_context *ilo);
+
+#endif /* ILO_TRANSFER_H */