ilo: rework ilo_texture
authorChia-I Wu <olvaffe@gmail.com>
Mon, 13 May 2013 07:19:55 +0000 (15:19 +0800)
committerChia-I Wu <olvaffe@gmail.com>
Tue, 14 May 2013 08:07:22 +0000 (16:07 +0800)
Use ilo_buffer for buffer resources and ilo_texture for texture resources.  A
major cleanup is necessitated by the separation.

src/gallium/drivers/ilo/ilo_gpe_gen6.c
src/gallium/drivers/ilo/ilo_gpe_gen7.c
src/gallium/drivers/ilo/ilo_resource.c
src/gallium/drivers/ilo/ilo_resource.h
src/gallium/drivers/ilo/ilo_transfer.c

index e2a5dbe344200176e221b30d6878302aed46ce15..42a89f569097e7bfd56e32e9081ae0d7f3015553 100644 (file)
@@ -757,15 +757,15 @@ gen6_emit_3DSTATE_VERTEX_BUFFERS(const struct ilo_dev_info *dev,
 
       /* use null vb if there is no buffer or the stride is out of range */
       if (vb->buffer && vb->stride <= 2048) {
-         const struct ilo_texture *tex = ilo_texture(vb->buffer);
+         const struct ilo_buffer *buf = ilo_buffer(vb->buffer);
          const uint32_t start_offset = vb->buffer_offset;
-         const uint32_t end_offset = tex->bo->get_size(tex->bo) - 1;
+         const uint32_t end_offset = buf->bo->get_size(buf->bo) - 1;
 
          dw |= vb->stride << BRW_VB0_PITCH_SHIFT;
 
          ilo_cp_write(cp, dw);
-         ilo_cp_write_bo(cp, start_offset, tex->bo, INTEL_DOMAIN_VERTEX, 0);
-         ilo_cp_write_bo(cp, end_offset, tex->bo, INTEL_DOMAIN_VERTEX, 0);
+         ilo_cp_write_bo(cp, start_offset, buf->bo, INTEL_DOMAIN_VERTEX, 0);
+         ilo_cp_write_bo(cp, end_offset, buf->bo, INTEL_DOMAIN_VERTEX, 0);
          ilo_cp_write(cp, instance_divisor);
       }
       else {
@@ -925,13 +925,13 @@ gen6_emit_3DSTATE_INDEX_BUFFER(const struct ilo_dev_info *dev,
 {
    const uint32_t cmd = ILO_GPE_CMD(0x3, 0x0, 0x0a);
    const uint8_t cmd_len = 3;
-   const struct ilo_texture *tex = ilo_texture(ib->buffer);
+   const struct ilo_buffer *buf = ilo_buffer(ib->buffer);
    uint32_t start_offset, end_offset;
    int format;
 
    ILO_GPE_VALID_GEN(dev, 6, 7);
 
-   if (!tex)
+   if (!buf)
       return;
 
    format = gen6_translate_index_size(ib->index_size);
@@ -945,7 +945,7 @@ gen6_emit_3DSTATE_INDEX_BUFFER(const struct ilo_dev_info *dev,
    }
 
    /* end_offset must also be aligned */
-   end_offset = tex->bo->get_size(tex->bo);
+   end_offset = buf->bo->get_size(buf->bo);
    end_offset -= (end_offset % ib->index_size);
    /* it is inclusive */
    end_offset -= 1;
@@ -954,8 +954,8 @@ gen6_emit_3DSTATE_INDEX_BUFFER(const struct ilo_dev_info *dev,
    ilo_cp_write(cp, cmd | (cmd_len - 2) |
                     ((enable_cut_index) ? BRW_CUT_INDEX_ENABLE : 0) |
                     format << 8);
-   ilo_cp_write_bo(cp, start_offset, tex->bo, INTEL_DOMAIN_VERTEX, 0);
-   ilo_cp_write_bo(cp, end_offset, tex->bo, INTEL_DOMAIN_VERTEX, 0);
+   ilo_cp_write_bo(cp, start_offset, buf->bo, INTEL_DOMAIN_VERTEX, 0);
+   ilo_cp_write_bo(cp, end_offset, buf->bo, INTEL_DOMAIN_VERTEX, 0);
    ilo_cp_end(cp);
 }
 
@@ -3569,7 +3569,7 @@ gen6_fill_null_SURFACE_STATE(const struct ilo_dev_info *dev,
 
 static void
 gen6_fill_buffer_SURFACE_STATE(const struct ilo_dev_info *dev,
-                               const struct ilo_texture *tex,
+                               const struct ilo_buffer *buf,
                                unsigned offset, unsigned size,
                                unsigned struct_size,
                                enum pipe_format elem_format,
@@ -3629,14 +3629,6 @@ gen6_fill_buffer_SURFACE_STATE(const struct ilo_dev_info *dev,
     */
    pitch = struct_size;
 
-   /*
-    * From the Sandy Bridge PRM, volume 4 part 1, page 82:
-    *
-    *     "If Surface Type is SURFTYPE_BUFFER, this field (Tiled Surface) must
-    *      be false (buffers are supported only in linear memory)"
-    */
-   assert(tex->tiling == INTEL_TILING_NONE);
-
    pitch--;
    num_entries--;
    /* bits [6:0] */
@@ -3939,17 +3931,17 @@ gen6_emit_cbuf_SURFACE_STATE(const struct ilo_dev_info *dev,
                              struct ilo_cp *cp)
 {
    const enum pipe_format elem_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
-   struct ilo_texture *tex = ilo_texture(cbuf->buffer);
+   struct ilo_buffer *buf = ilo_buffer(cbuf->buffer);
    uint32_t dw[6];
 
    ILO_GPE_VALID_GEN(dev, 6, 6);
 
-   gen6_fill_buffer_SURFACE_STATE(dev, tex,
+   gen6_fill_buffer_SURFACE_STATE(dev, buf,
          cbuf->buffer_offset, cbuf->buffer_size,
          util_format_get_blocksize(elem_format), elem_format,
          false, false, dw, Elements(dw));
 
-   return gen6_emit_SURFACE_STATE(dev, tex->bo, false, dw, Elements(dw), cp);
+   return gen6_emit_SURFACE_STATE(dev, buf->bo, false, dw, Elements(dw), cp);
 }
 
 static uint32_t
@@ -3959,7 +3951,7 @@ gen6_emit_so_SURFACE_STATE(const struct ilo_dev_info *dev,
                            int so_index,
                            struct ilo_cp *cp)
 {
-   struct ilo_texture *tex = ilo_texture(so->buffer);
+   struct ilo_buffer *buf = ilo_buffer(so->buffer);
    unsigned bo_offset, struct_size;
    enum pipe_format elem_format;
    uint32_t dw[6];
@@ -3988,10 +3980,10 @@ gen6_emit_so_SURFACE_STATE(const struct ilo_dev_info *dev,
       break;
    }
 
-   gen6_fill_buffer_SURFACE_STATE(dev, tex, bo_offset, so->buffer_size,
+   gen6_fill_buffer_SURFACE_STATE(dev, buf, bo_offset, so->buffer_size,
          struct_size, elem_format, false, true, dw, Elements(dw));
 
-   return gen6_emit_SURFACE_STATE(dev, tex->bo, false, dw, Elements(dw), cp);
+   return gen6_emit_SURFACE_STATE(dev, buf->bo, false, dw, Elements(dw), cp);
 }
 
 static uint32_t
index 27fa12438036a7e65204ecdac0da31e4357d3476..ba69906271ba86fd1993bc04ce060e0fb37ca1af 100644 (file)
@@ -1130,7 +1130,7 @@ gen7_emit_3DSTATE_SO_BUFFER(const struct ilo_dev_info *dev,
 {
    const uint32_t cmd = ILO_GPE_CMD(0x3, 0x1, 0x18);
    const uint8_t cmd_len = 4;
-   struct ilo_texture *tex;
+   struct ilo_buffer *buf;
    int end;
 
    ILO_GPE_VALID_GEN(dev, 7, 7);
@@ -1145,7 +1145,7 @@ gen7_emit_3DSTATE_SO_BUFFER(const struct ilo_dev_info *dev,
       return;
    }
 
-   tex = ilo_texture(so_target->buffer);
+   buf = ilo_buffer(so_target->buffer);
 
    /* DWord-aligned */
    assert(stride % 4 == 0 && base % 4 == 0);
@@ -1159,8 +1159,8 @@ gen7_emit_3DSTATE_SO_BUFFER(const struct ilo_dev_info *dev,
    ilo_cp_write(cp, cmd | (cmd_len - 2));
    ilo_cp_write(cp, index << SO_BUFFER_INDEX_SHIFT |
                     stride);
-   ilo_cp_write_bo(cp, base, tex->bo, INTEL_DOMAIN_RENDER, INTEL_DOMAIN_RENDER);
-   ilo_cp_write_bo(cp, end, tex->bo, INTEL_DOMAIN_RENDER, INTEL_DOMAIN_RENDER);
+   ilo_cp_write_bo(cp, base, buf->bo, INTEL_DOMAIN_RENDER, INTEL_DOMAIN_RENDER);
+   ilo_cp_write_bo(cp, end, buf->bo, INTEL_DOMAIN_RENDER, INTEL_DOMAIN_RENDER);
    ilo_cp_end(cp);
 }
 
@@ -1296,7 +1296,7 @@ gen7_fill_null_SURFACE_STATE(const struct ilo_dev_info *dev,
 
 static void
 gen7_fill_buffer_SURFACE_STATE(const struct ilo_dev_info *dev,
-                               const struct ilo_texture *tex,
+                               const struct ilo_buffer *buf,
                                unsigned offset, unsigned size,
                                unsigned struct_size,
                                enum pipe_format elem_format,
@@ -1374,14 +1374,6 @@ gen7_fill_buffer_SURFACE_STATE(const struct ilo_dev_info *dev,
 
    pitch = struct_size;
 
-   /*
-    * From the Ivy Bridge PRM, volume 4 part 1, page 65:
-    *
-    *     "If Surface Type is SURFTYPE_BUFFER, this field (Tiled Surface) must
-    *      be false (because buffers are supported only in linear memory)."
-    */
-   assert(tex->tiling == INTEL_TILING_NONE);
-
    pitch--;
    num_entries--;
    /* bits [6:0] */
@@ -1722,17 +1714,17 @@ gen7_emit_cbuf_SURFACE_STATE(const struct ilo_dev_info *dev,
                              struct ilo_cp *cp)
 {
    const enum pipe_format elem_format = PIPE_FORMAT_R32G32B32A32_FLOAT;
-   struct ilo_texture *tex = ilo_texture(cbuf->buffer);
+   struct ilo_buffer *buf = ilo_buffer(cbuf->buffer);
    uint32_t dw[8];
 
    ILO_GPE_VALID_GEN(dev, 7, 7);
 
-   gen7_fill_buffer_SURFACE_STATE(dev, tex,
+   gen7_fill_buffer_SURFACE_STATE(dev, buf,
          cbuf->buffer_offset, cbuf->buffer_size,
          util_format_get_blocksize(elem_format), elem_format,
          false, false, dw, Elements(dw));
 
-   return gen7_emit_SURFACE_STATE(dev, tex->bo, false, dw, Elements(dw), cp);
+   return gen7_emit_SURFACE_STATE(dev, buf->bo, false, dw, Elements(dw), cp);
 }
 
 static uint32_t
index e4233e20018f6843c0a333d5a142f2873395d28b..b41ff5b621576424bbe39a3912dafcd30e6bb19f 100644 (file)
 /* use PIPE_BIND_CUSTOM to indicate MCS */
 #define ILO_BIND_MCS PIPE_BIND_CUSTOM
 
-static struct intel_bo *
-alloc_buf_bo(const struct ilo_texture *tex)
-{
-   struct ilo_screen *is = ilo_screen(tex->base.screen);
-   struct intel_bo *bo;
-   const char *name;
-   const unsigned size = tex->bo_width;
-
-   switch (tex->base.bind) {
-   case PIPE_BIND_VERTEX_BUFFER:
-      name = "vertex buffer";
-      break;
-   case PIPE_BIND_INDEX_BUFFER:
-      name = "index buffer";
-      break;
-   case PIPE_BIND_CONSTANT_BUFFER:
-      name = "constant buffer";
-      break;
-   case PIPE_BIND_STREAM_OUTPUT:
-      name = "stream output";
-      break;
-   default:
-      name = "unknown buffer";
-      break;
-   }
-
-   /* this is what a buffer supposed to be like */
-   assert(tex->bo_width * tex->bo_height * tex->bo_cpp == size);
-   assert(tex->tiling == INTEL_TILING_NONE);
-   assert(tex->bo_stride == 0);
-
-   if (tex->handle) {
-      bo = is->winsys->import_handle(is->winsys, name,
-            tex->bo_width, tex->bo_height, tex->bo_cpp, tex->handle);
-
-      /* since the bo is shared to us, make sure it meets the expectations */
-      if (bo) {
-         assert(bo->get_size(tex->bo) == size);
-         assert(bo->get_tiling(tex->bo) == tex->tiling);
-         assert(bo->get_pitch(tex->bo) == tex->bo_stride);
-      }
-   }
-   else {
-      bo = is->winsys->alloc_buffer(is->winsys, name, size, 0);
-   }
+struct tex_layout {
+   const struct ilo_dev_info *dev;
+   const struct pipe_resource *templ;
 
-   return bo;
-}
+   enum pipe_format format;
+   unsigned block_width, block_height, block_size;
+   bool compressed;
+   bool has_depth, has_stencil;
 
-static struct intel_bo *
-alloc_tex_bo(const struct ilo_texture *tex)
-{
-   struct ilo_screen *is = ilo_screen(tex->base.screen);
-   struct intel_bo *bo;
-   const char *name;
+   enum intel_tiling_mode tiling;
+   bool can_be_linear;
 
-   switch (tex->base.target) {
-   case PIPE_TEXTURE_1D:
-      name = "1D texture";
-      break;
-   case PIPE_TEXTURE_2D:
-      name = "2D texture";
-      break;
-   case PIPE_TEXTURE_3D:
-      name = "3D texture";
-      break;
-   case PIPE_TEXTURE_CUBE:
-      name = "cube texture";
-      break;
-   case PIPE_TEXTURE_RECT:
-      name = "rectangle texture";
-      break;
-   case PIPE_TEXTURE_1D_ARRAY:
-      name = "1D array texture";
-      break;
-   case PIPE_TEXTURE_2D_ARRAY:
-      name = "2D array texture";
-      break;
-   case PIPE_TEXTURE_CUBE_ARRAY:
-      name = "cube array texture";
-      break;
-   default:
-      name ="unknown texture";
-      break;
-   }
+   bool array_spacing_full;
+   bool interleaved;
 
-   if (tex->handle) {
-      bo = is->winsys->import_handle(is->winsys, name,
-            tex->bo_width, tex->bo_height, tex->bo_cpp, tex->handle);
-   }
-   else {
-      const bool for_render =
-         (tex->base.bind & (PIPE_BIND_DEPTH_STENCIL |
-                            PIPE_BIND_RENDER_TARGET));
-      const unsigned long flags =
-         (for_render) ? INTEL_ALLOC_FOR_RENDER : 0;
+   struct {
+      int w, h, d;
+      struct ilo_texture_slice *slices;
+   } levels[PIPE_MAX_TEXTURE_LEVELS];
 
-      bo = is->winsys->alloc(is->winsys, name,
-            tex->bo_width, tex->bo_height, tex->bo_cpp,
-            tex->tiling, flags);
-   }
+   int align_i, align_j;
+   int qpitch;
 
-   return bo;
-}
+   int width, height;
+};
 
-bool
-ilo_texture_alloc_bo(struct ilo_texture *tex)
+static void
+tex_layout_init_qpitch(struct tex_layout *layout)
 {
-   struct intel_bo *old_bo = tex->bo;
+   const struct pipe_resource *templ = layout->templ;
+   int h0, h1;
 
-   /* a shared bo cannot be reallocated */
-   if (old_bo && tex->handle)
-      return false;
+   if (templ->array_size <= 1)
+      return;
 
-   if (tex->base.target == PIPE_BUFFER)
-      tex->bo = alloc_buf_bo(tex);
-   else
-      tex->bo = alloc_tex_bo(tex);
+   h0 = align(layout->levels[0].h, layout->align_j);
 
-   if (!tex->bo) {
-      tex->bo = old_bo;
-      return false;
+   if (!layout->array_spacing_full) {
+      layout->qpitch = h0;
+      return;
    }
 
-   /* winsys may decide to use a different tiling */
-   tex->tiling = tex->bo->get_tiling(tex->bo);
-   tex->bo_stride = tex->bo->get_pitch(tex->bo);
-
-   if (old_bo)
-      old_bo->unreference(old_bo);
-
-   return true;
-}
-
-static bool
-alloc_slice_offsets(struct ilo_texture *tex)
-{
-   int depth, lv;
-
-   /* sum the depths of all levels */
-   depth = 0;
-   for (lv = 0; lv <= tex->base.last_level; lv++)
-      depth += u_minify(tex->base.depth0, lv);
+   h1 = align(layout->levels[1].h, layout->align_j);
 
    /*
-    * There are (depth * tex->base.array_size) slices.  Either depth is one
-    * (non-3D) or tex->base.array_size is one (non-array), but it does not
-    * matter.
+    * From the Sandy Bridge PRM, volume 1 part 1, page 115:
+    *
+    *     "The following equation is used for surface formats other than
+    *      compressed textures:
+    *
+    *        QPitch = (h0 + h1 + 11j)"
+    *
+    *     "The equation for compressed textures (BC* and FXT1 surface formats)
+    *      follows:
+    *
+    *        QPitch = (h0 + h1 + 11j) / 4"
+    *
+    *     "[DevSNB] Errata: Sampler MSAA Qpitch will be 4 greater than the
+    *      value calculated in the equation above, for every other odd Surface
+    *      Height starting from 1 i.e. 1,5,9,13"
+    *
+    * From the Ivy Bridge PRM, volume 1 part 1, page 111-112:
+    *
+    *     "If Surface Array Spacing is set to ARYSPC_FULL (note that the depth
+    *      buffer and stencil buffer have an implied value of ARYSPC_FULL):
+    *
+    *        QPitch = (h0 + h1 + 12j)
+    *        QPitch = (h0 + h1 + 12j) / 4 (compressed)
+    *
+    *      (There are many typos or missing words here...)"
+    *
+    * To access the N-th slice, an offset of (Stride * QPitch * N) is added to
+    * the base address.  The PRM divides QPitch by 4 for compressed formats
+    * because the block height for those formats are 4, and it wants QPitch to
+    * mean the number of memory rows, as opposed to texel rows, between
+    * slices.  Since we use texel rows in tex->slice_offsets, we do not need
+    * to divide QPitch by 4.
     */
-   tex->slice_offsets[0] =
-      CALLOC(depth * tex->base.array_size, sizeof(tex->slice_offsets[0][0]));
-   if (!tex->slice_offsets[0])
-      return false;
-
-   /* point to the respective positions in the buffer */
-   for (lv = 1; lv <= tex->base.last_level; lv++) {
-      tex->slice_offsets[lv] = tex->slice_offsets[lv - 1] +
-         u_minify(tex->base.depth0, lv - 1) * tex->base.array_size;
-   }
-
-   return true;
-}
-
-static void
-free_slice_offsets(struct ilo_texture *tex)
-{
-   int lv;
+   layout->qpitch = h0 + h1 +
+      ((layout->dev->gen >= ILO_GEN(7)) ? 12 : 11) * layout->align_j;
 
-   FREE(tex->slice_offsets[0]);
-   for (lv = 0; lv <= tex->base.last_level; lv++)
-      tex->slice_offsets[lv] = NULL;
+   if (layout->dev->gen == ILO_GEN(6) && templ->nr_samples > 1 &&
+       templ->height0 % 4 == 1)
+      layout->qpitch += 4;
 }
 
-struct layout_tex_info {
-   bool compressed;
-   int block_width, block_height;
-   int align_i, align_j;
-   bool array_spacing_full;
-   bool interleaved;
-   int qpitch;
-
-   struct {
-      int w, h, d;
-   } sizes[PIPE_MAX_TEXTURE_LEVELS];
-};
-
-/**
- * Prepare for texture layout.
- */
 static void
-layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
+tex_layout_init_alignments(struct tex_layout *layout)
 {
-   struct ilo_screen *is = ilo_screen(tex->base.screen);
-   const enum pipe_format bo_format = tex->bo_format;
-   const enum intel_tiling_mode tiling = tex->tiling;
-   const struct pipe_resource *templ = &tex->base;
-   int last_level, lv;
-
-   memset(info, 0, sizeof(*info));
-
-   info->compressed = util_format_is_compressed(bo_format);
-   info->block_width = util_format_get_blockwidth(bo_format);
-   info->block_height = util_format_get_blockheight(bo_format);
+   const struct pipe_resource *templ = layout->templ;
 
    /*
     * From the Sandy Bridge PRM, volume 1 part 1, page 113:
@@ -323,21 +210,21 @@ layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
     *  others                          4 or 8         2 or 4
     */
 
-   if (info->compressed) {
+   if (layout->compressed) {
       /* this happens to be the case */
-      info->align_i = info->block_width;
-      info->align_j = info->block_height;
+      layout->align_i = layout->block_width;
+      layout->align_j = layout->block_height;
    }
-   else if (util_format_is_depth_or_stencil(bo_format)) {
-      if (is->dev.gen >= ILO_GEN(7)) {
-         switch (bo_format) {
+   else if (layout->has_depth || layout->has_stencil) {
+      if (layout->dev->gen >= ILO_GEN(7)) {
+         switch (layout->format) {
          case PIPE_FORMAT_Z16_UNORM:
-            info->align_i = 8;
-            info->align_j = 4;
+            layout->align_i = 8;
+            layout->align_j = 4;
             break;
          case PIPE_FORMAT_S8_UINT:
-            info->align_i = 8;
-            info->align_j = 8;
+            layout->align_i = 8;
+            layout->align_j = 8;
             break;
          default:
             /*
@@ -350,35 +237,35 @@ layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
              * We will make use of them and setting align_i to 8 help us meet
              * the requirement.
              */
-            info->align_i = (templ->last_level > 0) ? 8 : 4;
-            info->align_j = 4;
+            layout->align_i = (templ->last_level > 0) ? 8 : 4;
+            layout->align_j = 4;
             break;
          }
       }
       else {
-         switch (bo_format) {
+         switch (layout->format) {
          case PIPE_FORMAT_S8_UINT:
-            info->align_i = 4;
-            info->align_j = 2;
+            layout->align_i = 4;
+            layout->align_j = 2;
             break;
          default:
-            info->align_i = 4;
-            info->align_j = 4;
+            layout->align_i = 4;
+            layout->align_j = 4;
             break;
          }
       }
    }
    else {
       const bool valign_4 = (templ->nr_samples > 1) ||
-         (is->dev.gen >= ILO_GEN(7) &&
-          (templ->bind & PIPE_BIND_RENDER_TARGET) &&
-          tiling == INTEL_TILING_Y);
+         (layout->dev->gen >= ILO_GEN(7) &&
+          layout->tiling == INTEL_TILING_Y &&
+          (templ->bind & PIPE_BIND_RENDER_TARGET));
 
       if (valign_4)
-         assert(util_format_get_blocksizebits(bo_format) != 96);
+         assert(layout->block_size != 12);
 
-      info->align_i = 4;
-      info->align_j = (valign_4) ? 4 : 2;
+      layout->align_i = 4;
+      layout->align_j = (valign_4) ? 4 : 2;
    }
 
    /*
@@ -387,75 +274,26 @@ layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
     * size, slices start at block boundaries, and many of the computations
     * work.
     */
-   assert(info->align_i % info->block_width == 0);
-   assert(info->align_j % info->block_height == 0);
+   assert(layout->align_i % layout->block_width == 0);
+   assert(layout->align_j % layout->block_height == 0);
 
    /* make sure align() works */
-   assert(util_is_power_of_two(info->align_i) &&
-          util_is_power_of_two(info->align_j));
-   assert(util_is_power_of_two(info->block_width) &&
-          util_is_power_of_two(info->block_height));
-
-   if (is->dev.gen >= ILO_GEN(7)) {
-      /*
-       * It is not explicitly states, but render targets are expected to be
-       * UMS/CMS (samples non-interleaved) and depth/stencil buffers are
-       * expected to be IMS (samples interleaved).
-       *
-       * See "Multisampled Surface Storage Format" field of SURFACE_STATE.
-       */
-      if (util_format_is_depth_or_stencil(bo_format)) {
-         info->interleaved = true;
-
-         /*
-          * From the Ivy Bridge PRM, volume 1 part 1, page 111:
-          *
-          *     "note that the depth buffer and stencil buffer have an implied
-          *      value of ARYSPC_FULL"
-          */
-         info->array_spacing_full = true;
-      }
-      else {
-         info->interleaved = false;
-
-         /*
-          * From the Ivy Bridge PRM, volume 4 part 1, page 66:
-          *
-          *     "If Multisampled Surface Storage Format is MSFMT_MSS and
-          *      Number of Multisamples is not MULTISAMPLECOUNT_1, this field
-          *      (Surface Array Spacing) must be set to ARYSPC_LOD0."
-          *
-          * As multisampled resources are not mipmapped, we never use
-          * ARYSPC_FULL for them.
-          */
-         if (templ->nr_samples > 1)
-            assert(templ->last_level == 0);
-         info->array_spacing_full = (templ->last_level > 0);
-      }
-   }
-   else {
-      /* GEN6 supports only interleaved samples */
-      info->interleaved = true;
+   assert(util_is_power_of_two(layout->align_i) &&
+          util_is_power_of_two(layout->align_j));
+   assert(util_is_power_of_two(layout->block_width) &&
+          util_is_power_of_two(layout->block_height));
+}
 
-      /*
-       * From the Sandy Bridge PRM, volume 1 part 1, page 115:
-       *
-       *     "The separate stencil buffer does not support mip mapping, thus
-       *      the storage for LODs other than LOD 0 is not needed. The
-       *      following QPitch equation applies only to the separate stencil
-       *      buffer:
-       *
-       *        QPitch = h_0"
-       *
-       * GEN6 does not support compact spacing otherwise.
-       */
-      info->array_spacing_full = (bo_format != PIPE_FORMAT_S8_UINT);
-   }
+static void
+tex_layout_init_levels(struct tex_layout *layout)
+{
+   const struct pipe_resource *templ = layout->templ;
+   int last_level, lv;
 
    last_level = templ->last_level;
 
    /* need at least 2 levels to compute full qpitch */
-   if (last_level == 0 && templ->array_size > 1 && info->array_spacing_full)
+   if (last_level == 0 && templ->array_size > 1 && layout->array_spacing_full)
       last_level++;
 
    /* compute mip level sizes */
@@ -474,8 +312,8 @@ layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
        *      above. Then, if necessary, they are padded out to compression
        *      block boundaries."
        */
-      w = align(w, info->block_width);
-      h = align(h, info->block_height);
+      w = align(w, layout->block_width);
+      h = align(h, layout->block_height);
 
       /*
        * From the Sandy Bridge PRM, volume 1 part 1, page 111:
@@ -516,7 +354,7 @@ layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
        *   w = align(w, 2) * 2;
        *   y = align(y, 2) * 2;
        */
-      if (info->interleaved) {
+      if (layout->interleaved) {
          switch (templ->nr_samples) {
          case 0:
          case 1:
@@ -542,142 +380,341 @@ layout_tex_init(const struct ilo_texture *tex, struct layout_tex_info *info)
          }
       }
 
-      info->sizes[lv].w = w;
-      info->sizes[lv].h = h;
-      info->sizes[lv].d = d;
+      layout->levels[lv].w = w;
+      layout->levels[lv].h = h;
+      layout->levels[lv].d = d;
    }
+}
 
-   if (templ->array_size > 1) {
-      const int h0 = align(info->sizes[0].h, info->align_j);
+static void
+tex_layout_init_spacing(struct tex_layout *layout)
+{
+   const struct pipe_resource *templ = layout->templ;
 
-      if (info->array_spacing_full) {
-         const int h1 = align(info->sizes[1].h, info->align_j);
+   if (layout->dev->gen >= ILO_GEN(7)) {
+      /*
+       * It is not explicitly states, but render targets are expected to be
+       * UMS/CMS (samples non-interleaved) and depth/stencil buffers are
+       * expected to be IMS (samples interleaved).
+       *
+       * See "Multisampled Surface Storage Format" field of SURFACE_STATE.
+       */
+      if (layout->has_depth || layout->has_stencil) {
+         layout->interleaved = true;
 
          /*
-          * From the Sandy Bridge PRM, volume 1 part 1, page 115:
-          *
-          *     "The following equation is used for surface formats other than
-          *      compressed textures:
-          *
-          *        QPitch = (h0 + h1 + 11j)"
-          *
-          *     "The equation for compressed textures (BC* and FXT1 surface
-          *      formats) follows:
-          *
-          *        QPitch = (h0 + h1 + 11j) / 4"
-          *
-          *     "[DevSNB] Errata: Sampler MSAA Qpitch will be 4 greater than
-          *      the value calculated in the equation above, for every other
-          *      odd Surface Height starting from 1 i.e. 1,5,9,13"
-          *
-          * From the Ivy Bridge PRM, volume 1 part 1, page 111-112:
-          *
-          *     "If Surface Array Spacing is set to ARYSPC_FULL (note that the
-          *      depth buffer and stencil buffer have an implied value of
-          *      ARYSPC_FULL):
-          *
-          *        QPitch = (h0 + h1 + 12j)
-          *        QPitch = (h0 + h1 + 12j) / 4 (compressed)
-          *
-          *      (There are many typos or missing words here...)"
+          * From the Ivy Bridge PRM, volume 1 part 1, page 111:
           *
-          * To access the N-th slice, an offset of (Stride * QPitch * N) is
-          * added to the base address.  The PRM divides QPitch by 4 for
-          * compressed formats because the block height for those formats are
-          * 4, and it wants QPitch to mean the number of memory rows, as
-          * opposed to texel rows, between slices.  Since we use texel rows in
-          * tex->slice_offsets, we do not need to divide QPitch by 4.
+          *     "note that the depth buffer and stencil buffer have an implied
+          *      value of ARYSPC_FULL"
           */
-         info->qpitch = h0 + h1 +
-            ((is->dev.gen >= ILO_GEN(7)) ? 12 : 11) * info->align_j;
-
-         if (is->dev.gen == ILO_GEN(6) && templ->nr_samples > 1 &&
-               templ->height0 % 4 == 1)
-            info->qpitch += 4;
+         layout->array_spacing_full = true;
       }
       else {
-         info->qpitch = h0;
+         layout->interleaved = false;
+
+         /*
+          * From the Ivy Bridge PRM, volume 4 part 1, page 66:
+          *
+          *     "If Multisampled Surface Storage Format is MSFMT_MSS and
+          *      Number of Multisamples is not MULTISAMPLECOUNT_1, this field
+          *      (Surface Array Spacing) must be set to ARYSPC_LOD0."
+          *
+          * As multisampled resources are not mipmapped, we never use
+          * ARYSPC_FULL for them.
+          */
+         if (templ->nr_samples > 1)
+            assert(templ->last_level == 0);
+         layout->array_spacing_full = (templ->last_level > 0);
       }
    }
+   else {
+      /* GEN6 supports only interleaved samples */
+      layout->interleaved = true;
+
+      /*
+       * From the Sandy Bridge PRM, volume 1 part 1, page 115:
+       *
+       *     "The separate stencil buffer does not support mip mapping, thus
+       *      the storage for LODs other than LOD 0 is not needed. The
+       *      following QPitch equation applies only to the separate stencil
+       *      buffer:
+       *
+       *        QPitch = h_0"
+       *
+       * GEN6 does not support compact spacing otherwise.
+       */
+      layout->array_spacing_full = (layout->format != PIPE_FORMAT_S8_UINT);
+   }
 }
 
-/**
- * Layout a 2D texture.
- */
 static void
-layout_tex_2d(struct ilo_texture *tex, const struct layout_tex_info *info)
+tex_layout_init_tiling(struct tex_layout *layout)
 {
-   const struct pipe_resource *templ = &tex->base;
-   unsigned int level_x, level_y, num_slices;
-   int lv;
+   const struct pipe_resource *templ = layout->templ;
+   const enum pipe_format format = layout->format;
+   const unsigned tile_none = 1 << INTEL_TILING_NONE;
+   const unsigned tile_x = 1 << INTEL_TILING_X;
+   const unsigned tile_y = 1 << INTEL_TILING_Y;
+   unsigned valid_tilings = tile_none | tile_x | tile_y;
 
-   tex->bo_width = 0;
-   tex->bo_height = 0;
-
-   level_x = 0;
-   level_y = 0;
-   for (lv = 0; lv <= templ->last_level; lv++) {
-      const unsigned int level_w = info->sizes[lv].w;
-      const unsigned int level_h = info->sizes[lv].h;
+   /*
+    * From the Sandy Bridge PRM, volume 1 part 2, page 32:
+    *
+    *     "Display/Overlay   Y-Major not supported.
+    *                        X-Major required for Async Flips"
+    */
+   if (unlikely(templ->bind & PIPE_BIND_SCANOUT))
+      valid_tilings &= tile_x;
+
+   /*
+    * From the Sandy Bridge PRM, volume 3 part 2, page 158:
+    *
+    *     "The cursor surface address must be 4K byte aligned. The cursor must
+    *      be in linear memory, it cannot be tiled."
+    */
+   if (unlikely(templ->bind & PIPE_BIND_CURSOR))
+      valid_tilings &= tile_none;
+
+   /*
+    * From the Ivy Bridge PRM, volume 4 part 1, page 76:
+    *
+    *     "The MCS surface must be stored as Tile Y."
+    */
+   if (templ->bind & ILO_BIND_MCS)
+      valid_tilings &= tile_y;
+
+   /*
+    * From the Sandy Bridge PRM, volume 2 part 1, page 318:
+    *
+    *     "[DevSNB+]: This field (Tiled Surface) must be set to TRUE. Linear
+    *      Depth Buffer is not supported."
+    *
+    *     "The Depth Buffer, if tiled, must use Y-Major tiling."
+    *
+    * From the Sandy Bridge PRM, volume 1 part 2, page 22:
+    *
+    *     "W-Major Tile Format is used for separate stencil."
+    *
+    * Since the HW does not support W-tiled fencing, we have to do it in the
+    * driver.
+    */
+   if (templ->bind & PIPE_BIND_DEPTH_STENCIL) {
+      switch (format) {
+      case PIPE_FORMAT_S8_UINT:
+         valid_tilings &= tile_none;
+         break;
+      default:
+         valid_tilings &= tile_y;
+         break;
+      }
+   }
+
+   if (templ->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW)) {
+      if (templ->bind & PIPE_BIND_RENDER_TARGET) {
+         /*
+          * From the Sandy Bridge PRM, volume 1 part 2, page 32:
+          *
+          *     "NOTE: 128BPE Format Color buffer ( render target ) MUST be
+          *      either TileX or Linear."
+          */
+         if (layout->block_size == 16)
+            valid_tilings &= ~tile_y;
+
+         /*
+          * From the Ivy Bridge PRM, volume 4 part 1, page 63:
+          *
+          *     "This field (Surface Vertical Aligment) must be set to
+          *      VALIGN_4 for all tiled Y Render Target surfaces."
+          *
+          *     "VALIGN_4 is not supported for surface format
+          *      R32G32B32_FLOAT."
+          */
+         if (layout->dev->gen >= ILO_GEN(7) && layout->block_size == 12)
+            valid_tilings &= ~tile_y;
+      }
+
+      /*
+       * Also, heuristically set a minimum width/height for enabling tiling.
+       */
+      if (templ->width0 < 64 && (valid_tilings & ~tile_x))
+         valid_tilings &= ~tile_x;
+
+      if ((templ->width0 < 32 || templ->height0 < 16) &&
+          (templ->width0 < 16 || templ->height0 < 32) &&
+          (valid_tilings & ~tile_y))
+         valid_tilings &= ~tile_y;
+   }
+   else {
+      /* force linear if we are not sure where the texture is bound to */
+      if (valid_tilings & tile_none)
+         valid_tilings &= tile_none;
+   }
+
+   /* no conflicting binding flags */
+   assert(valid_tilings);
+
+   /* prefer tiled than linear */
+   if (valid_tilings & tile_y)
+      layout->tiling = INTEL_TILING_Y;
+   else if (valid_tilings & tile_x)
+      layout->tiling = INTEL_TILING_X;
+   else
+      layout->tiling = INTEL_TILING_NONE;
+
+   layout->can_be_linear = valid_tilings & tile_none;
+}
+
+static void
+tex_layout_init_format(struct tex_layout *layout)
+{
+   const struct pipe_resource *templ = layout->templ;
+   enum pipe_format format;
+   const struct util_format_description *desc;
+
+   switch (templ->format) {
+   case PIPE_FORMAT_ETC1_RGB8:
+      format = PIPE_FORMAT_R8G8B8X8_UNORM;
+      break;
+   default:
+      format = templ->format;
+      break;
+   }
+
+   layout->format = format;
+
+   layout->block_width = util_format_get_blockwidth(format);
+   layout->block_height = util_format_get_blockheight(format);
+   layout->block_size = util_format_get_blocksize(format);
+   layout->compressed = util_format_is_compressed(format);
+
+   desc = util_format_description(format);
+   layout->has_depth = util_format_has_depth(desc);
+   layout->has_stencil = util_format_has_stencil(desc);
+}
+
+static void
+tex_layout_init(struct tex_layout *layout,
+                struct pipe_screen *screen,
+                const struct pipe_resource *templ,
+                struct ilo_texture_slice **slices)
+{
+   struct ilo_screen *is = ilo_screen(screen);
+
+   memset(layout, 0, sizeof(*layout));
+
+   layout->dev = &is->dev;
+   layout->templ = templ;
+
+   /* note that there are dependencies between these functions */
+   tex_layout_init_format(layout);
+   tex_layout_init_tiling(layout);
+   tex_layout_init_spacing(layout);
+   tex_layout_init_levels(layout);
+   tex_layout_init_alignments(layout);
+   tex_layout_init_qpitch(layout);
+
+   if (slices) {
+      int lv;
+
+      for (lv = 0; lv <= templ->last_level; lv++)
+         layout->levels[lv].slices = slices[lv];
+   }
+}
+
+static bool
+tex_layout_force_linear(struct tex_layout *layout)
+{
+   if (!layout->can_be_linear)
+      return false;
+
+   /*
+    * we may be able to switch from VALIGN_4 to VALIGN_2 when the layout was
+    * Y-tiled, but let's keep it simple
+    */
+   layout->tiling = INTEL_TILING_NONE;
+
+   return true;
+}
+
+/**
+ * Layout a 2D texture.
+ */
+static void
+tex_layout_2d(struct tex_layout *layout)
+{
+   const struct pipe_resource *templ = layout->templ;
+   unsigned int level_x, level_y, num_slices;
+   int lv;
+
+   level_x = 0;
+   level_y = 0;
+   for (lv = 0; lv <= templ->last_level; lv++) {
+      const unsigned int level_w = layout->levels[lv].w;
+      const unsigned int level_h = layout->levels[lv].h;
       int slice;
 
-      for (slice = 0; slice < templ->array_size; slice++) {
-         tex->slice_offsets[lv][slice].x = level_x;
-         /* slices are qpitch apart in Y-direction */
-         tex->slice_offsets[lv][slice].y = level_y + info->qpitch * slice;
+      /* set slice offsets */
+      if (layout->levels[lv].slices) {
+         for (slice = 0; slice < templ->array_size; slice++) {
+            layout->levels[lv].slices[slice].x = level_x;
+            /* slices are qpitch apart in Y-direction */
+            layout->levels[lv].slices[slice].y =
+               level_y + layout->qpitch * slice;
+         }
       }
 
       /* extend the size of the monolithic bo to cover this mip level */
-      if (tex->bo_width < level_x + level_w)
-         tex->bo_width = level_x + level_w;
-      if (tex->bo_height < level_y + level_h)
-         tex->bo_height = level_y + level_h;
+      if (layout->width < level_x + level_w)
+         layout->width = level_x + level_w;
+      if (layout->height < level_y + level_h)
+         layout->height = level_y + level_h;
 
       /* MIPLAYOUT_BELOW */
       if (lv == 1)
-         level_x += align(level_w, info->align_i);
+         level_x += align(level_w, layout->align_i);
       else
-         level_y += align(level_h, info->align_j);
+         level_y += align(level_h, layout->align_j);
    }
 
    num_slices = templ->array_size;
    /* samples of the same index are stored in a slice */
-   if (templ->nr_samples > 1 && !info->interleaved)
+   if (templ->nr_samples > 1 && !layout->interleaved)
       num_slices *= templ->nr_samples;
 
    /* we did not take slices into consideration in the computation above */
-   tex->bo_height += info->qpitch * (num_slices - 1);
+   layout->height += layout->qpitch * (num_slices - 1);
 }
 
 /**
  * Layout a 3D texture.
  */
 static void
-layout_tex_3d(struct ilo_texture *tex, const struct layout_tex_info *info)
+tex_layout_3d(struct tex_layout *layout)
 {
-   const struct pipe_resource *templ = &tex->base;
+   const struct pipe_resource *templ = layout->templ;
    unsigned int level_y;
    int lv;
 
-   tex->bo_width = 0;
-   tex->bo_height = 0;
-
    level_y = 0;
    for (lv = 0; lv <= templ->last_level; lv++) {
-      const unsigned int level_w = info->sizes[lv].w;
-      const unsigned int level_h = info->sizes[lv].h;
-      const unsigned int level_d = info->sizes[lv].d;
-      const unsigned int slice_pitch = align(level_w, info->align_i);
-      const unsigned int slice_qpitch = align(level_h, info->align_j);
+      const unsigned int level_w = layout->levels[lv].w;
+      const unsigned int level_h = layout->levels[lv].h;
+      const unsigned int level_d = layout->levels[lv].d;
+      const unsigned int slice_pitch = align(level_w, layout->align_i);
+      const unsigned int slice_qpitch = align(level_h, layout->align_j);
       const unsigned int num_slices_per_row = 1 << lv;
       int slice;
 
       for (slice = 0; slice < level_d; slice += num_slices_per_row) {
          int i;
 
-         for (i = 0; i < num_slices_per_row && slice + i < level_d; i++) {
-            tex->slice_offsets[lv][slice + i].x = slice_pitch * i;
-            tex->slice_offsets[lv][slice + i].y = level_y;
+         /* set slice offsets */
+         if (layout->levels[lv].slices) {
+            for (i = 0; i < num_slices_per_row && slice + i < level_d; i++) {
+               layout->levels[lv].slices[slice + i].x = slice_pitch * i;
+               layout->levels[lv].slices[slice + i].y = level_y;
+            }
          }
 
          /* move on to the next slice row */
@@ -688,239 +725,200 @@ layout_tex_3d(struct ilo_texture *tex, const struct layout_tex_info *info)
       slice = MIN2(num_slices_per_row, level_d) - 1;
 
       /* extend the size of the monolithic bo to cover this slice */
-      if (tex->bo_width < slice_pitch * slice + level_w)
-         tex->bo_width = slice_pitch * slice + level_w;
+      if (layout->width < slice_pitch * slice + level_w)
+         layout->width = slice_pitch * slice + level_w;
       if (lv == templ->last_level)
-         tex->bo_height = (level_y - slice_qpitch) + level_h;
+         layout->height = (level_y - slice_qpitch) + level_h;
    }
 }
 
-/**
- * Guess the texture size.  For large textures, the errors are relative small.
- */
-static size_t
-guess_tex_size(const struct pipe_resource *templ,
-               enum intel_tiling_mode tiling)
+static void
+tex_layout_validate(struct tex_layout *layout)
 {
-   int bo_width, bo_height, bo_stride;
-
-   /* HALIGN_8 and VALIGN_4 */
-   bo_width = align(templ->width0, 8);
-   bo_height = align(templ->height0, 4);
-
-   if (templ->target == PIPE_TEXTURE_3D) {
-      const int num_rows = util_next_power_of_two(templ->depth0);
-      int lv, sum;
+   /*
+    * From the Sandy Bridge PRM, volume 1 part 2, page 22:
+    *
+    *     "A 4KB tile is subdivided into 8-high by 8-wide array of Blocks for
+    *      W-Major Tiles (W Tiles). Each Block is 8 rows by 8 bytes."
+    *
+    * Since we ask for INTEL_TILING_NONE instead of the non-existent
+    * INTEL_TILING_W, we need to manually align the width and height to the
+    * tile boundaries.
+    */
+   if (layout->templ->format == PIPE_FORMAT_S8_UINT) {
+      layout->width = align(layout->width, 64);
+      layout->height = align(layout->height, 64);
+   }
 
-      sum = bo_height * templ->depth0;
-      for (lv = 1; lv <= templ->last_level; lv++)
-         sum += u_minify(bo_height, lv) * u_minify(num_rows, lv);
+   assert(layout->width % layout->block_width == 0);
+   assert(layout->height % layout->block_height == 0);
+   assert(layout->qpitch % layout->block_height == 0);
+}
 
-      bo_height = sum;
-   }
-   else if (templ->last_level > 0) {
-      /* MIPLAYOUT_BELOW, ignore qpich */
-      bo_height = (bo_height + u_minify(bo_height, 1)) * templ->array_size;
-   }
+static size_t
+tex_layout_estimate_size(const struct tex_layout *layout)
+{
+   unsigned stride, height;
 
-   bo_stride = util_format_get_stride(templ->format, bo_width);
+   stride = (layout->width / layout->block_width) * layout->block_size;
+   height = layout->height / layout->block_height;
 
-   switch (tiling) {
+   switch (layout->tiling) {
    case INTEL_TILING_X:
-      bo_stride = align(bo_stride, 512);
-      bo_height = align(bo_height, 8);
+      stride = align(stride, 512);
+      height = align(height, 8);
       break;
    case INTEL_TILING_Y:
-      bo_stride = align(bo_stride, 128);
-      bo_height = align(bo_height, 32);
+      stride = align(stride, 128);
+      height = align(height, 32);
       break;
    default:
-      bo_height = align(bo_height, 2);
+      height = align(height, 2);
       break;
    }
 
-   return util_format_get_2d_size(templ->format, bo_stride, bo_height);
+   return stride * height;
 }
 
-static enum intel_tiling_mode
-get_tex_tiling(const struct ilo_texture *tex)
+static void
+tex_layout_apply(const struct tex_layout *layout, struct ilo_texture *tex)
 {
-   const struct pipe_resource *templ = &tex->base;
-   const enum pipe_format bo_format = tex->bo_format;
+   tex->bo_format = layout->format;
 
-   /*
-    * From the Sandy Bridge PRM, volume 1 part 2, page 32:
-    *
-    *     "Display/Overlay   Y-Major not supported.
-    *                        X-Major required for Async Flips"
-    */
-   if (unlikely(templ->bind & PIPE_BIND_SCANOUT))
-      return INTEL_TILING_X;
+   /* in blocks */
+   tex->bo_width = layout->width / layout->block_width;
+   tex->bo_height = layout->height / layout->block_height;
+   tex->bo_cpp = layout->block_size;
+   tex->tiling = layout->tiling;
+
+   tex->compressed = layout->compressed;
+   tex->block_width = layout->block_width;
+   tex->block_height = layout->block_height;
+
+   tex->halign_8 = (layout->align_i == 8);
+   tex->valign_4 = (layout->align_j == 4);
+   tex->array_spacing_full = layout->array_spacing_full;
+   tex->interleaved = layout->interleaved;
+}
 
-   /*
-    * From the Sandy Bridge PRM, volume 3 part 2, page 158:
-    *
-    *     "The cursor surface address must be 4K byte aligned. The cursor must
-    *      be in linear memory, it cannot be tiled."
-    */
-   if (unlikely(templ->bind & PIPE_BIND_CURSOR))
-      return INTEL_TILING_NONE;
+static void
+tex_free_slices(struct ilo_texture *tex)
+{
+   FREE(tex->slice_offsets[0]);
+}
 
-   /*
-    * From the Ivy Bridge PRM, volume 4 part 1, page 76:
-    *
-    *     "The MCS surface must be stored as Tile Y."
-    */
-   if (templ->bind & ILO_BIND_MCS)
-      return INTEL_TILING_Y;
+static bool
+tex_alloc_slices(struct ilo_texture *tex)
+{
+   const struct pipe_resource *templ = &tex->base;
+   struct ilo_texture_slice *slices;
+   int depth, lv;
+
+   /* sum the depths of all levels */
+   depth = 0;
+   for (lv = 0; lv <= templ->last_level; lv++)
+      depth += u_minify(templ->depth0, lv);
 
    /*
-    * From the Sandy Bridge PRM, volume 2 part 1, page 318:
-    *
-    *     "[DevSNB+]: This field (Tiled Surface) must be set to TRUE. Linear
-    *      Depth Buffer is not supported."
-    *
-    *     "The Depth Buffer, if tiled, must use Y-Major tiling."
+    * There are (depth * tex->base.array_size) slices in total.  Either depth
+    * is one (non-3D) or templ->array_size is one (non-array), but it does
+    * not matter.
     */
-   if (templ->bind & PIPE_BIND_DEPTH_STENCIL) {
-      /* separate stencil uses W-tiling but we do not know how to specify that */
-      return (bo_format == PIPE_FORMAT_S8_UINT) ?
-         INTEL_TILING_NONE : INTEL_TILING_Y;
-   }
-
-   if (templ->bind & (PIPE_BIND_RENDER_TARGET | PIPE_BIND_SAMPLER_VIEW)) {
-      enum intel_tiling_mode tiling = INTEL_TILING_NONE;
-
-      /*
-       * From the Sandy Bridge PRM, volume 1 part 2, page 32:
-       *
-       *     "NOTE: 128BPE Format Color buffer ( render target ) MUST be
-       *      either TileX or Linear."
-       *
-       * Also, heuristically set a minimum width/height for enabling tiling.
-       */
-      if (util_format_get_blocksizebits(bo_format) == 128 &&
-          (templ->bind & PIPE_BIND_RENDER_TARGET) && templ->width0 >= 64)
-         tiling = INTEL_TILING_X;
-      else if ((templ->width0 >= 32 && templ->height0 >= 16) ||
-               (templ->width0 >= 16 && templ->height0 >= 32))
-         tiling = INTEL_TILING_Y;
-
-      /* make sure the bo can be mapped through GTT if tiled */
-      if (tiling != INTEL_TILING_NONE) {
-         /*
-          * Usually only the first 256MB of the GTT is mappable.
-          *
-          * See also how intel_context::max_gtt_map_object_size is calculated.
-          */
-         const size_t mappable_gtt_size = 256 * 1024 * 1024;
-         const size_t size = guess_tex_size(templ, tiling);
+   slices = CALLOC(depth * templ->array_size, sizeof(*slices));
+   if (!slices)
+      return false;
 
-         /* be conservative */
-         if (size > mappable_gtt_size / 4)
-            tiling = INTEL_TILING_NONE;
-      }
+   tex->slice_offsets[0] = slices;
 
-      return tiling;
+   /* point to the respective positions in the buffer */
+   for (lv = 1; lv <= templ->last_level; lv++) {
+      tex->slice_offsets[lv] = tex->slice_offsets[lv - 1] +
+         u_minify(templ->depth0, lv - 1) * templ->array_size;
    }
 
-   return INTEL_TILING_NONE;
+   return true;
 }
 
-static void
-init_texture(struct ilo_texture *tex)
+static struct intel_bo *
+tex_create_bo(const struct ilo_texture *tex,
+              const struct winsys_handle *handle)
 {
-   struct layout_tex_info info;
-
-   switch (tex->base.format) {
-   case PIPE_FORMAT_ETC1_RGB8:
-      tex->bo_format = PIPE_FORMAT_R8G8B8X8_UNORM;
-      break;
-   default:
-      tex->bo_format = tex->base.format;
-      break;
-   }
-
-   /* determine tiling first as it may affect the layout */
-   tex->tiling = get_tex_tiling(tex);
-
-   layout_tex_init(tex, &info);
-
-   tex->compressed = info.compressed;
-   tex->block_width = info.block_width;
-   tex->block_height = info.block_height;
-
-   tex->halign_8 = (info.align_i == 8);
-   tex->valign_4 = (info.align_j == 4);
-   tex->array_spacing_full = info.array_spacing_full;
-   tex->interleaved = info.interleaved;
+   struct ilo_screen *is = ilo_screen(tex->base.screen);
+   const char *name;
+   struct intel_bo *bo;
 
    switch (tex->base.target) {
    case PIPE_TEXTURE_1D:
+      name = "1D texture";
+      break;
    case PIPE_TEXTURE_2D:
+      name = "2D texture";
+      break;
+   case PIPE_TEXTURE_3D:
+      name = "3D texture";
+      break;
    case PIPE_TEXTURE_CUBE:
+      name = "cube texture";
+      break;
    case PIPE_TEXTURE_RECT:
+      name = "rectangle texture";
+      break;
    case PIPE_TEXTURE_1D_ARRAY:
+      name = "1D array texture";
+      break;
    case PIPE_TEXTURE_2D_ARRAY:
-   case PIPE_TEXTURE_CUBE_ARRAY:
-      layout_tex_2d(tex, &info);
+      name = "2D array texture";
       break;
-   case PIPE_TEXTURE_3D:
-      layout_tex_3d(tex, &info);
+   case PIPE_TEXTURE_CUBE_ARRAY:
+      name = "cube array texture";
       break;
    default:
-      assert(!"unknown resource target");
+      name ="unknown texture";
       break;
    }
 
-   /*
-    * From the Sandy Bridge PRM, volume 1 part 2, page 22:
-    *
-    *     "A 4KB tile is subdivided into 8-high by 8-wide array of Blocks for
-    *      W-Major Tiles (W Tiles). Each Block is 8 rows by 8 bytes."
-    *
-    * Since we ask for INTEL_TILING_NONE instead lf INTEL_TILING_W, we need to
-    * manually align the bo width and height to the tile boundaries.
-    */
-   if (tex->bo_format == PIPE_FORMAT_S8_UINT) {
-      tex->bo_width = align(tex->bo_width, 64);
-      tex->bo_height = align(tex->bo_height, 64);
+   if (handle) {
+      bo = is->winsys->import_handle(is->winsys, name,
+            tex->bo_width, tex->bo_height, tex->bo_cpp, handle);
+   }
+   else {
+      bo = is->winsys->alloc(is->winsys, name,
+            tex->bo_width, tex->bo_height, tex->bo_cpp,
+            tex->tiling, tex->bo_flags);
    }
 
-   /* in blocks */
-   assert(tex->bo_width % info.block_width == 0);
-   assert(tex->bo_height % info.block_height == 0);
-   tex->bo_width /= info.block_width;
-   tex->bo_height /= info.block_height;
-   tex->bo_cpp = util_format_get_blocksize(tex->bo_format);
+   return bo;
+}
+
+static void
+tex_set_bo(struct ilo_texture *tex, struct intel_bo *bo)
+{
+   if (tex->bo)
+      tex->bo->unreference(tex->bo);
+
+   tex->bo = bo;
+
+   /* winsys may decide to use a different tiling */
+   tex->tiling = tex->bo->get_tiling(tex->bo);
+   tex->bo_stride = tex->bo->get_pitch(tex->bo);
 }
 
 static void
-init_buffer(struct ilo_texture *tex)
+tex_destroy(struct ilo_texture *tex)
 {
-   tex->bo_format = tex->base.format;
-   tex->bo_width = tex->base.width0;
-   tex->bo_height = 1;
-   tex->bo_cpp = 1;
-   tex->bo_stride = 0;
-   tex->tiling = INTEL_TILING_NONE;
-
-   tex->compressed = false;
-   tex->block_width = 1;
-   tex->block_height = 1;
-
-   tex->halign_8 = false;
-   tex->valign_4 = false;
-   tex->array_spacing_full = false;
-   tex->interleaved = false;
+   tex->bo->unreference(tex->bo);
+   tex_free_slices(tex);
+   FREE(tex);
 }
 
 static struct pipe_resource *
-create_resource(struct pipe_screen *screen,
-                const struct pipe_resource *templ,
-                struct winsys_handle *handle)
+tex_create(struct pipe_screen *screen,
+           const struct pipe_resource *templ,
+           const struct winsys_handle *handle)
 {
+   struct tex_layout layout;
    struct ilo_texture *tex;
+   struct intel_bo *bo;
 
    tex = CALLOC_STRUCT(ilo_texture);
    if (!tex)
@@ -929,27 +927,177 @@ create_resource(struct pipe_screen *screen,
    tex->base = *templ;
    tex->base.screen = screen;
    pipe_reference_init(&tex->base.reference, 1);
-   tex->handle = handle;
 
-   if (!alloc_slice_offsets(tex)) {
+   if (!tex_alloc_slices(tex)) {
       FREE(tex);
       return NULL;
    }
 
-   if (templ->target == PIPE_BUFFER)
-      init_buffer(tex);
-   else
-      init_texture(tex);
+   tex->imported = (handle != NULL);
+
+   if (tex->base.bind & (PIPE_BIND_DEPTH_STENCIL |
+                         PIPE_BIND_RENDER_TARGET))
+      tex->bo_flags |= INTEL_ALLOC_FOR_RENDER;
 
-   if (!ilo_texture_alloc_bo(tex)) {
-      free_slice_offsets(tex);
+   tex_layout_init(&layout, screen, templ, tex->slice_offsets);
+
+   switch (templ->target) {
+   case PIPE_TEXTURE_1D:
+   case PIPE_TEXTURE_2D:
+   case PIPE_TEXTURE_CUBE:
+   case PIPE_TEXTURE_RECT:
+   case PIPE_TEXTURE_1D_ARRAY:
+   case PIPE_TEXTURE_2D_ARRAY:
+   case PIPE_TEXTURE_CUBE_ARRAY:
+      tex_layout_2d(&layout);
+      break;
+   case PIPE_TEXTURE_3D:
+      tex_layout_3d(&layout);
+      break;
+   default:
+      assert(!"unknown resource target");
+      break;
+   }
+
+   tex_layout_validate(&layout);
+
+   /* make sure the bo can be mapped through GTT if tiled */
+   if (layout.tiling != INTEL_TILING_NONE) {
+      /*
+       * Usually only the first 256MB of the GTT is mappable.
+       *
+       * See also how intel_context::max_gtt_map_object_size is calculated.
+       */
+      const size_t mappable_gtt_size = 256 * 1024 * 1024;
+      const size_t size = tex_layout_estimate_size(&layout);
+
+      /* be conservative */
+      if (size > mappable_gtt_size / 4)
+         tex_layout_force_linear(&layout);
+   }
+
+   tex_layout_apply(&layout, tex);
+
+   bo = tex_create_bo(tex, handle);
+   if (!bo) {
+      tex_free_slices(tex);
       FREE(tex);
       return NULL;
    }
 
+   tex_set_bo(tex, bo);
+
    return &tex->base;
 }
 
+static bool
+tex_get_handle(struct ilo_texture *tex, struct winsys_handle *handle)
+{
+   int err;
+
+   err = tex->bo->export_handle(tex->bo, handle);
+
+   return !err;
+}
+
+/**
+ * Estimate the texture size.  For large textures, the errors should be pretty
+ * small.
+ */
+static size_t
+tex_estimate_size(struct pipe_screen *screen,
+                  const struct pipe_resource *templ)
+{
+   struct tex_layout layout;
+
+   tex_layout_init(&layout, screen, templ, NULL);
+
+   switch (templ->target) {
+   case PIPE_TEXTURE_3D:
+      tex_layout_3d(&layout);
+      break;
+   default:
+      tex_layout_2d(&layout);
+      break;
+   }
+
+   tex_layout_validate(&layout);
+
+   return tex_layout_estimate_size(&layout);
+}
+
+static struct intel_bo *
+buf_create_bo(const struct ilo_buffer *buf)
+{
+   struct ilo_screen *is = ilo_screen(buf->base.screen);
+   const char *name;
+
+   switch (buf->base.bind) {
+   case PIPE_BIND_VERTEX_BUFFER:
+      name = "vertex buffer";
+      break;
+   case PIPE_BIND_INDEX_BUFFER:
+      name = "index buffer";
+      break;
+   case PIPE_BIND_CONSTANT_BUFFER:
+      name = "constant buffer";
+      break;
+   case PIPE_BIND_STREAM_OUTPUT:
+      name = "stream output";
+      break;
+   default:
+      name = "unknown buffer";
+      break;
+   }
+
+   return is->winsys->alloc_buffer(is->winsys,
+         name, buf->bo_size, buf->bo_flags);
+}
+
+static void
+buf_set_bo(struct ilo_buffer *buf, struct intel_bo *bo)
+{
+   if (buf->bo)
+      buf->bo->unreference(buf->bo);
+
+   buf->bo = bo;
+}
+
+static void
+buf_destroy(struct ilo_buffer *buf)
+{
+   buf->bo->unreference(buf->bo);
+   FREE(buf);
+}
+
+static struct pipe_resource *
+buf_create(struct pipe_screen *screen, const struct pipe_resource *templ)
+{
+   struct ilo_buffer *buf;
+   struct intel_bo *bo;
+
+   buf = CALLOC_STRUCT(ilo_buffer);
+   if (!buf)
+      return NULL;
+
+   buf->base = *templ;
+   buf->base.screen = screen;
+   pipe_reference_init(&buf->base.reference, 1);
+
+   buf->bo_size = templ->width0;
+   buf->bo_flags = 0;
+
+   bo = buf_create_bo(buf);
+   if (!bo) {
+      FREE(buf);
+      return NULL;
+   }
+
+   buf_set_bo(buf, bo);
+
+   return &buf->base;
+}
+
 static boolean
 ilo_can_create_resource(struct pipe_screen *screen,
                         const struct pipe_resource *templ)
@@ -959,7 +1107,12 @@ ilo_can_create_resource(struct pipe_screen *screen,
     * So just set a limit on the texture size.
     */
    const size_t max_size = 1 * 1024 * 1024 * 1024;
-   const size_t size = guess_tex_size(templ, INTEL_TILING_Y);
+   size_t size;
+
+   if (templ->target == PIPE_BUFFER)
+      size = templ->width0;
+   else
+      size = tex_estimate_size(screen, templ);
 
    return (size <= max_size);
 }
@@ -968,7 +1121,10 @@ static struct pipe_resource *
 ilo_resource_create(struct pipe_screen *screen,
                     const struct pipe_resource *templ)
 {
-   return create_resource(screen, templ, NULL);
+   if (templ->target == PIPE_BUFFER)
+      return buf_create(screen, templ);
+   else
+      return tex_create(screen, templ, NULL);
 }
 
 static struct pipe_resource *
@@ -976,7 +1132,10 @@ ilo_resource_from_handle(struct pipe_screen *screen,
                          const struct pipe_resource *templ,
                          struct winsys_handle *handle)
 {
-   return create_resource(screen, templ, handle);
+   if (templ->target == PIPE_BUFFER)
+      return NULL;
+   else
+      return tex_create(screen, templ, handle);
 }
 
 static boolean
@@ -984,23 +1143,21 @@ ilo_resource_get_handle(struct pipe_screen *screen,
                         struct pipe_resource *res,
                         struct winsys_handle *handle)
 {
-   struct ilo_texture *tex = ilo_texture(res);
-   int err;
-
-   err = tex->bo->export_handle(tex->bo, handle);
+   if (res->target == PIPE_BUFFER)
+      return false;
+   else
+      return tex_get_handle(ilo_texture(res), handle);
 
-   return !err;
 }
 
 static void
 ilo_resource_destroy(struct pipe_screen *screen,
                      struct pipe_resource *res)
 {
-   struct ilo_texture *tex = ilo_texture(res);
-
-   free_slice_offsets(tex);
-   tex->bo->unreference(tex->bo);
-   FREE(tex);
+   if (res->target == PIPE_BUFFER)
+      buf_destroy(ilo_buffer(res));
+   else
+      tex_destroy(ilo_texture(res));
 }
 
 /**
@@ -1016,6 +1173,38 @@ ilo_init_resource_functions(struct ilo_screen *is)
    is->base.resource_destroy = ilo_resource_destroy;
 }
 
+bool
+ilo_buffer_alloc_bo(struct ilo_buffer *buf)
+{
+   struct intel_bo *bo;
+
+   bo = buf_create_bo(buf);
+   if (!bo)
+      return false;
+
+   buf_set_bo(buf, bo);
+
+   return true;
+}
+
+bool
+ilo_texture_alloc_bo(struct ilo_texture *tex)
+{
+   struct intel_bo *bo;
+
+   /* a shared bo cannot be reallocated */
+   if (tex->imported)
+      return false;
+
+   bo = tex_create_bo(tex, NULL);
+   if (!bo)
+      return false;
+
+   tex_set_bo(tex, bo);
+
+   return true;
+}
+
 /**
  * Return the offset (in bytes) to a slice within the bo.
  *
index cf54dc83bcd86d7c09c3c5ffe54f352825968c06..124603c352356a63b71ab96a1c00f9bfcd5d7770 100644 (file)
 #include "ilo_common.h"
 
 struct ilo_screen;
-struct winsys_handle;
 
-/*
- * TODO we should have
- *
- *   ilo_resource, inherited by
- *    - ilo_buffer
- *    - ilo_texture
- *    - ilo_global_binding
- */
+struct ilo_buffer {
+   struct pipe_resource base;
+
+   struct intel_bo *bo;
+   unsigned bo_size;
+   unsigned bo_flags;
+};
+
 struct ilo_texture {
    struct pipe_resource base;
-   struct winsys_handle *handle;
+
+   bool imported;
+   unsigned bo_flags;
 
    enum pipe_format bo_format;
    struct intel_bo *bo;
@@ -73,21 +74,32 @@ struct ilo_texture {
    bool interleaved;
 
    /* 2D offsets into a layer/slice/face */
-   struct {
+   struct ilo_texture_slice {
       unsigned x;
       unsigned y;
    } *slice_offsets[PIPE_MAX_TEXTURE_LEVELS];
 };
 
+static inline struct ilo_buffer *
+ilo_buffer(struct pipe_resource *res)
+{
+   return (struct ilo_buffer *)
+      ((res && res->target == PIPE_BUFFER) ? res : NULL);
+}
+
 static inline struct ilo_texture *
 ilo_texture(struct pipe_resource *res)
 {
-   return (struct ilo_texture *) res;
+   return (struct ilo_texture *)
+      ((res && res->target != PIPE_BUFFER) ? res : NULL);
 }
 
 void
 ilo_init_resource_functions(struct ilo_screen *is);
 
+bool
+ilo_buffer_alloc_bo(struct ilo_buffer *buf);
+
 bool
 ilo_texture_alloc_bo(struct ilo_texture *tex);
 
index 30751d32b0fdb373da052074ac8f45f0dcbe09f3..a378d03eee634579b8c1cb2e3b936e1d75d2e5db 100644 (file)
@@ -54,88 +54,109 @@ ilo_transfer(struct pipe_transfer *transfer)
    return (struct ilo_transfer *) transfer;
 }
 
-static void
-ilo_transfer_inline_write(struct pipe_context *pipe,
-                          struct pipe_resource *res,
-                          unsigned level,
-                          unsigned usage,
-                          const struct pipe_box *box,
-                          const void *data,
-                          unsigned stride,
-                          unsigned layer_stride)
+/**
+ * Choose the best mapping method, depending on the transfer usage and whether
+ * the bo is busy.
+ */
+static bool
+transfer_choose_method(struct ilo_context *ilo, struct ilo_transfer *xfer)
 {
-   struct ilo_context *ilo = ilo_context(pipe);
-   struct ilo_texture *tex = ilo_texture(res);
-   int offset, size;
-   bool will_be_busy;
+   struct pipe_resource *res = xfer->base.resource;
+   struct ilo_texture *tex;
+   struct ilo_buffer *buf;
+   struct intel_bo *bo;
+   bool will_be_busy, will_stall;
 
-   /*
-    * 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(tex->base.target != PIPE_BUFFER) ||
-       (usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
-      u_default_transfer_inline_write(pipe, res,
-            level, usage, box, data, stride, layer_stride);
+   if (res->target == PIPE_BUFFER) {
+      tex = NULL;
 
-      return;
+      buf = ilo_buffer(res);
+      bo = buf->bo;
    }
+   else {
+      buf = NULL;
 
-   /*
-    * 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, tex->bo);
+      tex = ilo_texture(res);
+      bo = tex->bo;
 
-   /* see if we can avoid stalling */
-   if (will_be_busy || intel_bo_is_busy(tex->bo)) {
-      bool will_stall = true;
+      /* need to convert on-the-fly */
+      if (tex->bo_format != tex->base.format &&
+          !(xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)) {
+         xfer->method = ILO_TRANSFER_MAP_STAGING_SYS;
 
-      if (usage & PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) {
-         /* old data not needed so discard the old bo to avoid stalling */
-         if (ilo_texture_alloc_bo(tex))
-            will_stall = false;
-      }
-      else {
-         /*
-          * We could allocate a temporary bo to hold the data and emit
-          * pipelined copy blit to move them to tex->bo.  But for now, do
-          * nothing.
-          */
+         return true;
       }
+   }
 
-      /* flush to make bo busy (so that pwrite() stalls as it should be) */
-      if (will_stall && will_be_busy)
+   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, 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(bo))
+         return true;
    }
 
-   /* for PIPE_BUFFERs, conversion should not be needed */
-   assert(tex->bo_format == tex->base.format);
+   /* bo is busy and mapping it will stall */
+   will_stall = true;
 
-   /* 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;
+   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 ((tex && ilo_texture_alloc_bo(tex)) ||
+          (buf && ilo_buffer_alloc_bo(buf)))
+         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);
+   }
 
-   tex->bo->pwrite(tex->bo, offset, size, data);
+   return true;
 }
 
 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)
+tex_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;
 
@@ -159,9 +180,9 @@ transfer_unmap_sys_convert(enum pipe_format dst_fmt,
 }
 
 static void
-transfer_unmap_sys(struct ilo_context *ilo,
-                   struct ilo_texture *tex,
-                   struct ilo_transfer *xfer)
+tex_unmap_sys(struct ilo_context *ilo,
+              struct ilo_texture *tex,
+              struct ilo_transfer *xfer)
 {
    const void *src = xfer->ptr;
    struct pipe_transfer *dst_xfer;
@@ -180,7 +201,7 @@ transfer_unmap_sys(struct ilo_context *ilo,
    }
 
    if (likely(tex->bo_format != tex->base.format)) {
-      transfer_unmap_sys_convert(tex->bo_format, dst_xfer, dst,
+      tex_unmap_sys_convert(tex->bo_format, dst_xfer, dst,
             tex->base.format, &xfer->base, src);
    }
    else {
@@ -195,9 +216,9 @@ transfer_unmap_sys(struct ilo_context *ilo,
 }
 
 static bool
-transfer_map_sys(struct ilo_context *ilo,
-                 struct ilo_texture *tex,
-                 struct ilo_transfer *xfer)
+tex_map_sys(struct ilo_context *ilo,
+            struct ilo_texture *tex,
+            struct ilo_transfer *xfer)
 {
    const struct pipe_box *box = &xfer->base.box;
    const size_t stride = util_format_get_stride(tex->base.format, box->width);
@@ -232,19 +253,19 @@ transfer_map_sys(struct ilo_context *ilo,
 }
 
 static void
-transfer_unmap_direct(struct ilo_context *ilo,
-                      struct ilo_texture *tex,
-                      struct ilo_transfer *xfer)
+tex_unmap_direct(struct ilo_context *ilo,
+                 struct ilo_texture *tex,
+                 struct ilo_transfer *xfer)
 {
    tex->bo->unmap(tex->bo);
 }
 
 static bool
-transfer_map_direct(struct ilo_context *ilo,
-                    struct ilo_texture *tex,
-                    struct ilo_transfer *xfer)
+tex_map_direct(struct ilo_context *ilo,
+               struct ilo_texture *tex,
+               struct ilo_transfer *xfer)
 {
-   int x, y, err;
+   int err, x, y;
 
    if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
       err = tex->bo->map_unsynchronized(tex->bo);
@@ -294,84 +315,133 @@ transfer_map_direct(struct ilo_context *ilo,
    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_texture *tex,
-                           struct ilo_transfer *xfer)
+tex_map(struct ilo_context *ilo, struct ilo_transfer *xfer)
 {
-   bool will_be_busy, will_stall;
+   struct ilo_texture *tex = ilo_texture(xfer->base.resource);
+   bool success;
 
-   /* need to convert on-the-fly */
-   if (tex->bo_format != tex->base.format &&
-       !(xfer->base.usage & PIPE_TRANSFER_MAP_DIRECTLY)) {
-      xfer->method = ILO_TRANSFER_MAP_STAGING_SYS;
+   success = transfer_choose_method(ilo, xfer);
+   if (!success)
+      return false;
 
-      return true;
+   switch (xfer->method) {
+   case ILO_TRANSFER_MAP_DIRECT:
+      success = tex_map_direct(ilo, tex, xfer);
+      break;
+   case ILO_TRANSFER_MAP_STAGING_SYS:
+      success = tex_map_sys(ilo, tex, xfer);
+      break;
+   default:
+      assert(!"unknown mapping method");
+      success = false;
+      break;
    }
 
-   xfer->method = ILO_TRANSFER_MAP_DIRECT;
+   return success;
+}
+
+static void
+tex_unmap(struct ilo_context *ilo, struct ilo_transfer *xfer)
+{
+   struct ilo_texture *tex = ilo_texture(xfer->base.resource);
+
+   switch (xfer->method) {
+   case ILO_TRANSFER_MAP_DIRECT:
+      tex_unmap_direct(ilo, tex, xfer);
+      break;
+   case ILO_TRANSFER_MAP_STAGING_SYS:
+      tex_unmap_sys(ilo, tex, xfer);
+      break;
+   default:
+      assert(!"unknown mapping method");
+      break;
+   }
+}
+
+static bool
+buf_map(struct ilo_context *ilo, struct ilo_transfer *xfer)
+{
+   struct ilo_buffer *buf = ilo_buffer(xfer->base.resource);
+   int err;
+
+   if (!transfer_choose_method(ilo, xfer))
+      return false;
+
+   assert(xfer->method == ILO_TRANSFER_MAP_DIRECT);
 
-   /* unsynchronized map does not stall */
    if (xfer->base.usage & PIPE_TRANSFER_UNSYNCHRONIZED)
-      return true;
+      err = buf->bo->map_unsynchronized(buf->bo);
+   else if (ilo->dev->has_llc || (xfer->base.usage & PIPE_TRANSFER_READ))
+      err = buf->bo->map(buf->bo, (xfer->base.usage & PIPE_TRANSFER_WRITE));
+   else
+      err = buf->bo->map_gtt(buf->bo);
 
-   will_be_busy = ilo->cp->bo->references(ilo->cp->bo, tex->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 (err)
+      return false;
 
-      if (!intel_bo_is_busy(tex->bo))
-         return true;
-   }
+   assert(xfer->base.level == 0);
+   assert(xfer->base.box.y == 0);
+   assert(xfer->base.box.z == 0);
+   assert(xfer->base.box.height == 1);
+   assert(xfer->base.box.depth == 1);
 
-   /* bo is busy and mapping it will stall */
-   will_stall = true;
+   xfer->base.stride = 0;
+   xfer->base.layer_stride = 0;
 
-   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_texture_alloc_bo(tex))
-         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.
-       */
-   }
+   xfer->ptr = buf->bo->get_virtual(buf->bo);
+   xfer->ptr += xfer->base.box.x;
 
-   if (will_stall) {
-      if (xfer->base.usage & PIPE_TRANSFER_DONTBLOCK)
-         return false;
+   return true;
+}
 
-      /* flush to make bo busy (so that map() stalls as it should be) */
-      if (will_be_busy)
+static void
+buf_unmap(struct ilo_context *ilo, struct ilo_transfer *xfer)
+{
+   struct ilo_buffer *buf = ilo_buffer(xfer->base.resource);
+
+   buf->bo->unmap(buf->bo);
+}
+
+static void
+buf_pwrite(struct ilo_context *ilo, struct ilo_buffer *buf,
+           unsigned usage, int offset, int size, const void *data)
+{
+   bool 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);
+
+   will_be_busy = ilo->cp->bo->references(ilo->cp->bo, buf->bo);
+
+   /* see if we can avoid stalling */
+   if (will_be_busy || intel_bo_is_busy(buf->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_buffer_alloc_bo(buf))
+            will_stall = false;
+      }
+      else {
+         /*
+          * We could allocate a temporary bo to hold the data and emit
+          * pipelined copy blit to move them to buf->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);
    }
 
-   return true;
+   buf->bo->pwrite(buf->bo, offset, size, data);
 }
 
 static void
@@ -386,20 +456,12 @@ ilo_transfer_unmap(struct pipe_context *pipe,
                    struct pipe_transfer *transfer)
 {
    struct ilo_context *ilo = ilo_context(pipe);
-   struct ilo_texture *tex = ilo_texture(transfer->resource);
    struct ilo_transfer *xfer = ilo_transfer(transfer);
 
-   switch (xfer->method) {
-   case ILO_TRANSFER_MAP_DIRECT:
-      transfer_unmap_direct(ilo, tex, xfer);
-      break;
-   case ILO_TRANSFER_MAP_STAGING_SYS:
-      transfer_unmap_sys(ilo, tex, xfer);
-      break;
-   default:
-      assert(!"unknown mapping method");
-      break;
-   }
+   if (xfer->base.resource->target == PIPE_BUFFER)
+      buf_unmap(ilo, xfer);
+   else
+      tex_unmap(ilo, xfer);
 
    pipe_resource_reference(&xfer->base.resource, NULL);
    FREE(xfer);
@@ -414,9 +476,8 @@ ilo_transfer_map(struct pipe_context *pipe,
                  struct pipe_transfer **transfer)
 {
    struct ilo_context *ilo = ilo_context(pipe);
-   struct ilo_texture *tex = ilo_texture(res);
    struct ilo_transfer *xfer;
-   int ok;
+   bool success;
 
    xfer = MALLOC_STRUCT(ilo_transfer);
    if (!xfer) {
@@ -425,33 +486,20 @@ ilo_transfer_map(struct pipe_context *pipe,
    }
 
    xfer->base.resource = NULL;
-   pipe_resource_reference(&xfer->base.resource, &tex->base);
+   pipe_resource_reference(&xfer->base.resource, res);
    xfer->base.level = level;
    xfer->base.usage = usage;
    xfer->base.box = *box;
 
-   ok = transfer_map_choose_method(ilo, tex, xfer);
-   if (ok) {
-      switch (xfer->method) {
-      case ILO_TRANSFER_MAP_DIRECT:
-         ok = transfer_map_direct(ilo, tex, xfer);
-         break;
-      case ILO_TRANSFER_MAP_STAGING_SYS:
-         ok = transfer_map_sys(ilo, tex, xfer);
-         break;
-      default:
-         assert(!"unknown mapping method");
-         ok = false;
-         break;
-      }
-   }
+   if (res->target == PIPE_BUFFER)
+      success = buf_map(ilo, xfer);
+   else
+      success = tex_map(ilo, xfer);
 
-   if (!ok) {
+   if (!success) {
       pipe_resource_reference(&xfer->base.resource, NULL);
       FREE(xfer);
-
       *transfer = NULL;
-
       return NULL;
    }
 
@@ -460,6 +508,34 @@ ilo_transfer_map(struct pipe_context *pipe,
    return xfer->ptr;
 }
 
+static void
+ilo_transfer_inline_write(struct pipe_context *pipe,
+                          struct pipe_resource *res,
+                          unsigned level,
+                          unsigned usage,
+                          const struct pipe_box *box,
+                          const void *data,
+                          unsigned stride,
+                          unsigned layer_stride)
+{
+   if (likely(res->target == PIPE_BUFFER) &&
+       !(usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
+      /* 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);
+
+      buf_pwrite(ilo_context(pipe), ilo_buffer(res),
+            usage, box->x, box->width, data);
+   }
+   else {
+      u_default_transfer_inline_write(pipe, res,
+            level, usage, box, data, stride, layer_stride);
+   }
+}
+
 /**
  * Initialize transfer-related functions.
  */