[04/10] gallium: add user_stride parameter to pipe_context::transfer_map

Submitted by Marek Olšák on April 25, 2018, 9:16 p.m.

Details

Message ID 20180425211631.16860-5-maraeo@gmail.com
State New
Headers show
Series "DRI interface, gallium: User-specified transfer stride" ( rev: 2 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Marek Olšák April 25, 2018, 9:16 p.m.
From: Nicolai Hähnle <nicolai.haehnle@amd.com>

Allow callers to prescribe a desired stride for a transfer. Drivers
are free to ignore this new parameter.

There is no new capability because it's unclear how strict requirements
on this feature should be expressed.
---
 src/gallium/auxiliary/driver_ddebug/dd_draw.c    | 3 ++-
 src/gallium/auxiliary/driver_noop/noop_pipe.c    | 1 +
 src/gallium/auxiliary/driver_rbug/rbug_context.c | 5 ++++-
 src/gallium/auxiliary/driver_trace/tr_context.c  | 3 ++-
 src/gallium/auxiliary/util/u_inlines.h           | 8 ++++----
 src/gallium/auxiliary/util/u_threaded_context.c  | 5 +++--
 src/gallium/auxiliary/util/u_transfer.c          | 3 ++-
 src/gallium/auxiliary/util/u_transfer.h          | 2 ++
 src/gallium/auxiliary/util/u_transfer_helper.c   | 1 +
 src/gallium/auxiliary/util/u_transfer_helper.h   | 1 +
 src/gallium/drivers/etnaviv/etnaviv_transfer.c   | 1 +
 src/gallium/drivers/i915/i915_resource_buffer.c  | 1 +
 src/gallium/drivers/i915/i915_resource_texture.c | 1 +
 src/gallium/drivers/llvmpipe/lp_texture.c        | 1 +
 src/gallium/drivers/nouveau/nouveau_buffer.c     | 1 +
 src/gallium/drivers/nouveau/nv30/nv30_miptree.c  | 1 +
 src/gallium/drivers/nouveau/nv50/nv50_transfer.c | 1 +
 src/gallium/drivers/nouveau/nvc0/nvc0_transfer.c | 1 +
 src/gallium/drivers/r300/r300_screen_buffer.c    | 1 +
 src/gallium/drivers/r300/r300_transfer.c         | 1 +
 src/gallium/drivers/r300/r300_transfer.h         | 1 +
 src/gallium/drivers/r600/evergreen_compute.c     | 1 +
 src/gallium/drivers/r600/r600_buffer_common.c    | 3 ++-
 src/gallium/drivers/r600/r600_texture.c          | 1 +
 src/gallium/drivers/radeonsi/si_buffer.c         | 3 ++-
 src/gallium/drivers/radeonsi/si_texture.c        | 1 +
 src/gallium/drivers/softpipe/sp_texture.c        | 1 +
 src/gallium/drivers/svga/svga_resource_buffer.c  | 1 +
 src/gallium/drivers/svga/svga_resource_texture.c | 1 +
 src/gallium/drivers/vc4/vc4_resource.c           | 1 +
 src/gallium/drivers/virgl/virgl_buffer.c         | 1 +
 src/gallium/drivers/virgl/virgl_texture.c        | 1 +
 src/gallium/include/pipe/p_context.h             | 7 +++++++
 33 files changed, 53 insertions(+), 12 deletions(-)

Patch hide | download patch | download mbox

diff --git a/src/gallium/auxiliary/driver_ddebug/dd_draw.c b/src/gallium/auxiliary/driver_ddebug/dd_draw.c
index cb5db8ab83b..125f6041324 100644
--- a/src/gallium/auxiliary/driver_ddebug/dd_draw.c
+++ b/src/gallium/auxiliary/driver_ddebug/dd_draw.c
@@ -1478,33 +1478,34 @@  dd_context_clear_texture(struct pipe_context *_pipe,
 }
 
 /********************************************************************
  * transfer
  */
 
 static void *
 dd_context_transfer_map(struct pipe_context *_pipe,
                         struct pipe_resource *resource, unsigned level,
                         unsigned usage, const struct pipe_box *box,
+                        unsigned user_stride,
                         struct pipe_transfer **transfer)
 {
    struct dd_context *dctx = dd_context(_pipe);
    struct pipe_context *pipe = dctx->pipe;
    struct dd_draw_record *record =
       dd_screen(dctx->base.screen)->transfers ? dd_create_record(dctx) : NULL;
 
    if (record) {
       record->call.type = CALL_TRANSFER_MAP;
 
       dd_before_draw(dctx, record);
    }
-   void *ptr = pipe->transfer_map(pipe, resource, level, usage, box, transfer);
+   void *ptr = pipe->transfer_map(pipe, resource, level, usage, box, user_stride, transfer);
    if (record) {
       record->call.info.transfer_map.transfer_ptr = *transfer;
       record->call.info.transfer_map.ptr = ptr;
       if (*transfer) {
          record->call.info.transfer_map.transfer = **transfer;
          record->call.info.transfer_map.transfer.resource = NULL;
          pipe_resource_reference(&record->call.info.transfer_map.transfer.resource,
                                  (*transfer)->resource);
       } else {
          memset(&record->call.info.transfer_map.transfer, 0, sizeof(struct pipe_transfer));
diff --git a/src/gallium/auxiliary/driver_noop/noop_pipe.c b/src/gallium/auxiliary/driver_noop/noop_pipe.c
index d1e795dab16..cc74fcbd5df 100644
--- a/src/gallium/auxiliary/driver_noop/noop_pipe.c
+++ b/src/gallium/auxiliary/driver_noop/noop_pipe.c
@@ -167,20 +167,21 @@  static void noop_resource_destroy(struct pipe_screen *screen,
 
 
 /*
  * transfer
  */
 static void *noop_transfer_map(struct pipe_context *pipe,
                                struct pipe_resource *resource,
                                unsigned level,
                                enum pipe_transfer_usage usage,
                                const struct pipe_box *box,
+                               unsigned user_stride,
                                struct pipe_transfer **ptransfer)
 {
    struct pipe_transfer *transfer;
    struct noop_resource *nresource = (struct noop_resource *)resource;
 
    transfer = CALLOC_STRUCT(pipe_transfer);
    if (!transfer)
       return NULL;
    pipe_resource_reference(&transfer->resource, resource);
    transfer->level = level;
diff --git a/src/gallium/auxiliary/driver_rbug/rbug_context.c b/src/gallium/auxiliary/driver_rbug/rbug_context.c
index e1f3c4f2844..a03f4a79b46 100644
--- a/src/gallium/auxiliary/driver_rbug/rbug_context.c
+++ b/src/gallium/auxiliary/driver_rbug/rbug_context.c
@@ -1065,35 +1065,38 @@  rbug_context_surface_destroy(struct pipe_context *_pipe,
 }
 
 
 
 static void *
 rbug_context_transfer_map(struct pipe_context *_context,
                           struct pipe_resource *_resource,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **transfer)
 {
    struct rbug_context *rb_pipe = rbug_context(_context);
    struct rbug_resource *rb_resource = rbug_resource(_resource);
    struct pipe_context *context = rb_pipe->pipe;
    struct pipe_resource *resource = rb_resource->resource;
    struct pipe_transfer *result;
    void *map;
 
    mtx_lock(&rb_pipe->call_mutex);
    map = context->transfer_map(context,
                                resource,
                                level,
                                usage,
-                               box, &result);
+                               box,
+                               user_stride,
+                               &result);
    mtx_unlock(&rb_pipe->call_mutex);
 
    *transfer = rbug_transfer_create(rb_pipe, rb_resource, result);
    return *transfer ? map : NULL;
 }
 
 static void
 rbug_context_transfer_flush_region(struct pipe_context *_context,
                                    struct pipe_transfer *_transfer,
                                    const struct pipe_box *box)
diff --git a/src/gallium/auxiliary/driver_trace/tr_context.c b/src/gallium/auxiliary/driver_trace/tr_context.c
index 6d918d42a38..ff8b556b596 100644
--- a/src/gallium/auxiliary/driver_trace/tr_context.c
+++ b/src/gallium/auxiliary/driver_trace/tr_context.c
@@ -1368,33 +1368,34 @@  trace_context_destroy(struct pipe_context *_pipe)
  * transfer
  */
 
 
 static void *
 trace_context_transfer_map(struct pipe_context *_context,
                            struct pipe_resource *resource,
                            unsigned level,
                            unsigned usage,
                            const struct pipe_box *box,
+                           unsigned user_stride,
                            struct pipe_transfer **transfer)
 {
    struct trace_context *tr_context = trace_context(_context);
    struct pipe_context *context = tr_context->pipe;
    struct pipe_transfer *result = NULL;
    void *map;
 
    /*
     * Map and transfers can't be serialized so we convert all write transfers
     * to texture/buffer_subdata and ignore read transfers.
     */
 
-   map = context->transfer_map(context, resource, level, usage, box, &result);
+   map = context->transfer_map(context, resource, level, usage, box, user_stride, &result);
    if (!map)
       return NULL;
 
    *transfer = trace_transfer_create(tr_context, resource, result);
 
    if (map) {
       if (usage & PIPE_TRANSFER_WRITE) {
          trace_transfer(*transfer)->map = map;
       }
    }
diff --git a/src/gallium/auxiliary/util/u_inlines.h b/src/gallium/auxiliary/util/u_inlines.h
index b7a28568807..3baa00f518d 100644
--- a/src/gallium/auxiliary/util/u_inlines.h
+++ b/src/gallium/auxiliary/util/u_inlines.h
@@ -322,21 +322,21 @@  pipe_buffer_map_range(struct pipe_context *pipe,
 {
    struct pipe_box box;
    void *map;
 
    assert(offset < buffer->width0);
    assert(offset + length <= buffer->width0);
    assert(length);
 
    u_box_1d(offset, length, &box);
 
-   map = pipe->transfer_map(pipe, buffer, 0, access, &box, transfer);
+   map = pipe->transfer_map(pipe, buffer, 0, access, &box, 0, transfer);
    if (!map) {
       return NULL;
    }
 
    return map;
 }
 
 
 /**
  * Map whole resource.
@@ -459,21 +459,21 @@  pipe_buffer_read(struct pipe_context *pipe,
  * Map a resource for reading/writing.
  */
 static inline void *
 pipe_transfer_map_box(struct pipe_context *context,
                       struct pipe_resource *resource,
                       unsigned level,
                       enum pipe_transfer_usage usage,
                       const struct pipe_box *box,
                       struct pipe_transfer **out_transfer)
 {
-   return context->transfer_map(context, resource, level, usage, box,
+   return context->transfer_map(context, resource, level, usage, box, 0,
                                 out_transfer);
 }
 
 
 /**
  * Map a resource for reading/writing.
  * \param access  bitmask of PIPE_TRANSFER_x flags
  */
 static inline void *
 pipe_transfer_map(struct pipe_context *context,
@@ -483,21 +483,21 @@  pipe_transfer_map(struct pipe_context *context,
                   unsigned x, unsigned y,
                   unsigned w, unsigned h,
                   struct pipe_transfer **transfer)
 {
    struct pipe_box box;
    u_box_2d_zslice(x, y, layer, w, h, &box);
    return context->transfer_map(context,
                                 resource,
                                 level,
                                 access,
-                                &box, transfer);
+                                &box, 0, transfer);
 }
 
 
 /**
  * Map a 3D (texture) resource for reading/writing.
  * \param access  bitmask of PIPE_TRANSFER_x flags
  */
 static inline void *
 pipe_transfer_map_3d(struct pipe_context *context,
                      struct pipe_resource *resource,
@@ -506,21 +506,21 @@  pipe_transfer_map_3d(struct pipe_context *context,
                      unsigned x, unsigned y, unsigned z,
                      unsigned w, unsigned h, unsigned d,
                      struct pipe_transfer **transfer)
 {
    struct pipe_box box;
    u_box_3d(x, y, z, w, h, d, &box);
    return context->transfer_map(context,
                                 resource,
                                 level,
                                 access,
-                                &box, transfer);
+                                &box, 0, transfer);
 }
 
 static inline void
 pipe_transfer_unmap( struct pipe_context *context,
                      struct pipe_transfer *transfer )
 {
    context->transfer_unmap( context, transfer );
 }
 
 static inline void
diff --git a/src/gallium/auxiliary/util/u_threaded_context.c b/src/gallium/auxiliary/util/u_threaded_context.c
index 1c647a3efd0..0c41f5582a9 100644
--- a/src/gallium/auxiliary/util/u_threaded_context.c
+++ b/src/gallium/auxiliary/util/u_threaded_context.c
@@ -1429,20 +1429,21 @@  tc_improve_map_buffer_flags(struct threaded_context *tc,
       usage |= TC_TRANSFER_MAP_THREADED_UNSYNC; /* notify the driver */
    }
 
    return usage;
 }
 
 static void *
 tc_transfer_map(struct pipe_context *_pipe,
                 struct pipe_resource *resource, unsigned level,
                 unsigned usage, const struct pipe_box *box,
+                unsigned user_stride,
                 struct pipe_transfer **transfer)
 {
    struct threaded_context *tc = threaded_context(_pipe);
    struct threaded_resource *tres = threaded_resource(resource);
    struct pipe_context *pipe = tc->pipe;
 
    if (resource->target == PIPE_BUFFER) {
       usage = tc_improve_map_buffer_flags(tc, tres, usage, box->x, box->width);
 
       /* Do a staging transfer within the threaded context. The driver should
@@ -1473,21 +1474,21 @@  tc_transfer_map(struct pipe_context *_pipe,
       }
    }
 
    /* Unsychronized buffer mappings don't have to synchronize the thread. */
    if (!(usage & TC_TRANSFER_MAP_THREADED_UNSYNC))
       tc_sync_msg(tc, resource->target != PIPE_BUFFER ? "  texture" :
                       usage & PIPE_TRANSFER_DISCARD_RANGE ? "  discard_range" :
                       usage & PIPE_TRANSFER_READ ? "  read" : "  ??");
 
    return pipe->transfer_map(pipe, tres->latest ? tres->latest : resource,
-                             level, usage, box, transfer);
+                             level, usage, box, user_stride, transfer);
 }
 
 struct tc_transfer_flush_region {
    struct pipe_transfer *transfer;
    struct pipe_box box;
 };
 
 static void
 tc_call_transfer_flush_region(struct pipe_context *pipe,
                               union tc_payload *payload)
@@ -1634,21 +1635,21 @@  tc_buffer_subdata(struct pipe_context *_pipe,
     */
    if (usage & (PIPE_TRANSFER_UNSYNCHRONIZED |
                 PIPE_TRANSFER_DISCARD_WHOLE_RESOURCE) ||
        size > TC_MAX_SUBDATA_BYTES) {
       struct pipe_transfer *transfer;
       struct pipe_box box;
       uint8_t *map = NULL;
 
       u_box_1d(offset, size, &box);
 
-      map = tc_transfer_map(_pipe, resource, 0, usage, &box, &transfer);
+      map = tc_transfer_map(_pipe, resource, 0, usage, &box, 0, &transfer);
       if (map) {
          memcpy(map, data, size);
          tc_transfer_unmap(_pipe, transfer);
       }
       return;
    }
 
    util_range_add(&tres->valid_buffer_range, offset, offset + size);
 
    /* The upload is small. Enqueue it. */
diff --git a/src/gallium/auxiliary/util/u_transfer.c b/src/gallium/auxiliary/util/u_transfer.c
index 0e0c4cc91cd..5f07ea067fd 100644
--- a/src/gallium/auxiliary/util/u_transfer.c
+++ b/src/gallium/auxiliary/util/u_transfer.c
@@ -125,25 +125,26 @@  void u_resource_destroy_vtbl(struct pipe_screen *screen,
 {
    struct u_resource *ur = u_resource(resource);
    ur->vtbl->resource_destroy(screen, resource);
 }
 
 void *u_transfer_map_vtbl(struct pipe_context *context,
                           struct pipe_resource *resource,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **transfer)
 {
    struct u_resource *ur = u_resource(resource);
    return ur->vtbl->transfer_map(context, resource, level, usage, box,
-                                 transfer);
+                                 user_stride, transfer);
 }
 
 void u_transfer_flush_region_vtbl( struct pipe_context *pipe,
                                    struct pipe_transfer *transfer,
                                    const struct pipe_box *box)
 {
    struct u_resource *ur = u_resource(transfer->resource);
    ur->vtbl->transfer_flush_region(pipe, transfer, box);
 }
 
diff --git a/src/gallium/auxiliary/util/u_transfer.h b/src/gallium/auxiliary/util/u_transfer.h
index 14084983daf..9f1fcb626f3 100644
--- a/src/gallium/auxiliary/util/u_transfer.h
+++ b/src/gallium/auxiliary/util/u_transfer.h
@@ -51,20 +51,21 @@  struct u_resource_vtbl {
                                   struct winsys_handle *handle);
 
    void (*resource_destroy)(struct pipe_screen *,
                             struct pipe_resource *pt);
 
    void *(*transfer_map)(struct pipe_context *,
                          struct pipe_resource *resource,
                          unsigned level,
                          unsigned usage,
                          const struct pipe_box *,
+                         unsigned user_stride,
                          struct pipe_transfer **);
 
 
    void (*transfer_flush_region)( struct pipe_context *,
                                   struct pipe_transfer *transfer,
                                   const struct pipe_box *);
 
    void (*transfer_unmap)( struct pipe_context *,
                            struct pipe_transfer *transfer );
 };
@@ -83,20 +84,21 @@  boolean u_resource_get_handle_vtbl(struct pipe_screen *screen,
                                    unsigned usage);
 
 void u_resource_destroy_vtbl(struct pipe_screen *screen,
                              struct pipe_resource *resource);
 
 void *u_transfer_map_vtbl(struct pipe_context *context,
                           struct pipe_resource *resource,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **transfer);
 
 void u_transfer_flush_region_vtbl( struct pipe_context *pipe,
                                    struct pipe_transfer *transfer,
                                    const struct pipe_box *box);
 
 void u_transfer_unmap_vtbl( struct pipe_context *rm_ctx,
                             struct pipe_transfer *transfer );
 
 #ifdef __cplusplus
diff --git a/src/gallium/auxiliary/util/u_transfer_helper.c b/src/gallium/auxiliary/util/u_transfer_helper.c
index f69015af519..b5ad30c522d 100644
--- a/src/gallium/auxiliary/util/u_transfer_helper.c
+++ b/src/gallium/auxiliary/util/u_transfer_helper.c
@@ -217,20 +217,21 @@  transfer_map_msaa(struct pipe_context *pctx,
 
    *pptrans = ptrans;
    return ss_map;
 }
 
 void *
 u_transfer_helper_transfer_map(struct pipe_context *pctx,
                                struct pipe_resource *prsc,
                                unsigned level, unsigned usage,
                                const struct pipe_box *box,
+                               unsigned user_stride,
                                struct pipe_transfer **pptrans)
 {
    struct u_transfer_helper *helper = pctx->screen->transfer_helper;
    struct u_transfer *trans;
    struct pipe_transfer *ptrans;
    enum pipe_format format = prsc->format;
    unsigned width = box->width;
    unsigned height = box->height;
 
    if (!handle_transfer(prsc))
diff --git a/src/gallium/auxiliary/util/u_transfer_helper.h b/src/gallium/auxiliary/util/u_transfer_helper.h
index b13a1ec06c1..351efa894da 100644
--- a/src/gallium/auxiliary/util/u_transfer_helper.h
+++ b/src/gallium/auxiliary/util/u_transfer_helper.h
@@ -102,20 +102,21 @@  struct pipe_resource *u_transfer_helper_resource_create(
       struct pipe_screen *pscreen, const struct pipe_resource *templ);
 
 void u_transfer_helper_resource_destroy(struct pipe_screen *pscreen,
                                         struct pipe_resource *prsc);
 
 void *u_transfer_helper_transfer_map(struct pipe_context *pctx,
                                      struct pipe_resource *prsc,
                                      unsigned level,
                                      unsigned usage,
                                      const struct pipe_box *box,
+                                     unsigned user_stride,
                                      struct pipe_transfer **pptrans);
 
 
 void u_transfer_helper_transfer_flush_region(struct pipe_context *pctx,
                                              struct pipe_transfer *ptrans,
                                              const struct pipe_box *box);
 
 void u_transfer_helper_transfer_unmap(struct pipe_context *pctx,
                                       struct pipe_transfer *ptrans);
 
diff --git a/src/gallium/drivers/etnaviv/etnaviv_transfer.c b/src/gallium/drivers/etnaviv/etnaviv_transfer.c
index 30ae3bfc39d..8d079ad4e29 100644
--- a/src/gallium/drivers/etnaviv/etnaviv_transfer.c
+++ b/src/gallium/drivers/etnaviv/etnaviv_transfer.c
@@ -131,20 +131,21 @@  etna_transfer_unmap(struct pipe_context *pctx, struct pipe_transfer *ptrans)
    pipe_resource_reference(&trans->rsc, NULL);
    pipe_resource_reference(&ptrans->resource, NULL);
    slab_free(&ctx->transfer_pool, trans);
 }
 
 static void *
 etna_transfer_map(struct pipe_context *pctx, struct pipe_resource *prsc,
                   unsigned level,
                   unsigned usage,
                   const struct pipe_box *box,
+                  unsigned user_stride,
                   struct pipe_transfer **out_transfer)
 {
    struct etna_context *ctx = etna_context(pctx);
    struct etna_resource *rsc = etna_resource(prsc);
    struct etna_transfer *trans;
    struct pipe_transfer *ptrans;
    enum pipe_format format = prsc->format;
 
    trans = slab_alloc(&ctx->transfer_pool);
    if (!trans)
diff --git a/src/gallium/drivers/i915/i915_resource_buffer.c b/src/gallium/drivers/i915/i915_resource_buffer.c
index 2572fc40b2c..88a3181ee68 100644
--- a/src/gallium/drivers/i915/i915_resource_buffer.c
+++ b/src/gallium/drivers/i915/i915_resource_buffer.c
@@ -59,20 +59,21 @@  i915_buffer_destroy(struct pipe_screen *screen,
    FREE(buffer);
 }
 
 
 static void *
 i915_buffer_transfer_map(struct pipe_context *pipe,
                          struct pipe_resource *resource,
                          unsigned level,
                          unsigned usage,
                          const struct pipe_box *box,
+                         unsigned user_stride,
                          struct pipe_transfer **ptransfer)
 {
    struct i915_context *i915 = i915_context(pipe);
    struct i915_buffer *buffer = i915_buffer(resource);
    struct pipe_transfer *transfer = slab_alloc_st(&i915->transfer_pool);
 
    if (!transfer)
       return NULL;
 
    transfer->resource = resource;
diff --git a/src/gallium/drivers/i915/i915_resource_texture.c b/src/gallium/drivers/i915/i915_resource_texture.c
index 4ade04f223c..fbf83624275 100644
--- a/src/gallium/drivers/i915/i915_resource_texture.c
+++ b/src/gallium/drivers/i915/i915_resource_texture.c
@@ -710,20 +710,21 @@  i915_texture_destroy(struct pipe_screen *screen,
 
    FREE(tex);
 }
 
 static void *
 i915_texture_transfer_map(struct pipe_context *pipe,
                           struct pipe_resource *resource,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **ptransfer)
 {
    struct i915_context *i915 = i915_context(pipe);
    struct i915_texture *tex = i915_texture(resource);
    struct i915_transfer *transfer = slab_alloc_st(&i915->texture_transfer_pool);
    boolean use_staging_texture = FALSE;
    struct i915_winsys *iws = i915_screen(pipe->screen)->iws;
    enum pipe_format format = resource->format;
    unsigned offset;
    char *map;
diff --git a/src/gallium/drivers/llvmpipe/lp_texture.c b/src/gallium/drivers/llvmpipe/lp_texture.c
index 89852cc95c3..8d2ec6dc0e6 100644
--- a/src/gallium/drivers/llvmpipe/lp_texture.c
+++ b/src/gallium/drivers/llvmpipe/lp_texture.c
@@ -503,20 +503,21 @@  llvmpipe_resource_get_handle(struct pipe_screen *screen,
    return winsys->displaytarget_get_handle(winsys, lpr->dt, whandle);
 }
 
 
 static void *
 llvmpipe_transfer_map( struct pipe_context *pipe,
                        struct pipe_resource *resource,
                        unsigned level,
                        unsigned usage,
                        const struct pipe_box *box,
+                       unsigned user_stride,
                        struct pipe_transfer **transfer )
 {
    struct llvmpipe_context *llvmpipe = llvmpipe_context(pipe);
    struct llvmpipe_screen *screen = llvmpipe_screen(pipe->screen);
    struct llvmpipe_resource *lpr = llvmpipe_resource(resource);
    struct llvmpipe_transfer *lpt;
    struct pipe_transfer *pt;
    ubyte *map;
    enum pipe_format format;
    enum lp_texture_usage tex_usage;
diff --git a/src/gallium/drivers/nouveau/nouveau_buffer.c b/src/gallium/drivers/nouveau/nouveau_buffer.c
index 2c604419ce0..c5be45cadbd 100644
--- a/src/gallium/drivers/nouveau/nouveau_buffer.c
+++ b/src/gallium/drivers/nouveau/nouveau_buffer.c
@@ -370,20 +370,21 @@  nouveau_buffer_should_discard(struct nv04_resource *buf, unsigned usage)
  *   be overwritten.
  *
  * The strategy for determining what kind of memory area to return is complex,
  * see comments inside of the function.
  */
 static void *
 nouveau_buffer_transfer_map(struct pipe_context *pipe,
                             struct pipe_resource *resource,
                             unsigned level, unsigned usage,
                             const struct pipe_box *box,
+                            unsigned user_stride,
                             struct pipe_transfer **ptransfer)
 {
    struct nouveau_context *nv = nouveau_context(pipe);
    struct nv04_resource *buf = nv04_resource(resource);
    struct nouveau_transfer *tx = MALLOC_STRUCT(nouveau_transfer);
    uint8_t *map;
    int ret;
 
    if (!tx)
       return NULL;
diff --git a/src/gallium/drivers/nouveau/nv30/nv30_miptree.c b/src/gallium/drivers/nouveau/nv30/nv30_miptree.c
index 4f991776323..6832289e2cb 100644
--- a/src/gallium/drivers/nouveau/nv30/nv30_miptree.c
+++ b/src/gallium/drivers/nouveau/nv30/nv30_miptree.c
@@ -254,20 +254,21 @@  nv30_blit(struct pipe_context *pipe,
 void
 nv30_flush_resource(struct pipe_context *pipe,
                     struct pipe_resource *resource)
 {
 }
 
 static void *
 nv30_miptree_transfer_map(struct pipe_context *pipe, struct pipe_resource *pt,
                           unsigned level, unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **ptransfer)
 {
    struct nv30_context *nv30 = nv30_context(pipe);
    struct nouveau_device *dev = nv30->screen->base.device;
    struct nv30_transfer *tx;
    unsigned access = 0;
    int ret;
 
    tx = CALLOC_STRUCT(nv30_transfer);
    if (!tx)
diff --git a/src/gallium/drivers/nouveau/nv50/nv50_transfer.c b/src/gallium/drivers/nouveau/nv50/nv50_transfer.c
index 8209f1f1e82..debc01e8fba 100644
--- a/src/gallium/drivers/nouveau/nv50/nv50_transfer.c
+++ b/src/gallium/drivers/nouveau/nv50/nv50_transfer.c
@@ -240,20 +240,21 @@  nv50_m2mf_copy_linear(struct nouveau_context *nv,
 
    nouveau_bufctx_reset(bctx, 0);
 }
 
 void *
 nv50_miptree_transfer_map(struct pipe_context *pctx,
                           struct pipe_resource *res,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **ptransfer)
 {
    struct nv50_screen *screen = nv50_screen(pctx->screen);
    struct nv50_context *nv50 = nv50_context(pctx);
    struct nouveau_device *dev = nv50->screen->base.device;
    const struct nv50_miptree *mt = nv50_miptree(res);
    struct nv50_transfer *tx;
    uint32_t size;
    int ret;
    unsigned flags = 0;
diff --git a/src/gallium/drivers/nouveau/nvc0/nvc0_transfer.c b/src/gallium/drivers/nouveau/nvc0/nvc0_transfer.c
index 225b8947171..69a031a46f0 100644
--- a/src/gallium/drivers/nouveau/nvc0/nvc0_transfer.c
+++ b/src/gallium/drivers/nouveau/nvc0/nvc0_transfer.c
@@ -365,20 +365,21 @@  nvc0_mt_sync(struct nvc0_context *nvc0, struct nv50_miptree *mt, unsigned usage)
       return !mt->base.fence || nouveau_fence_wait(mt->base.fence, &nvc0->base.debug);
    return !mt->base.fence_wr || nouveau_fence_wait(mt->base.fence_wr, &nvc0->base.debug);
 }
 
 void *
 nvc0_miptree_transfer_map(struct pipe_context *pctx,
                           struct pipe_resource *res,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **ptransfer)
 {
    struct nvc0_context *nvc0 = nvc0_context(pctx);
    struct nouveau_device *dev = nvc0->screen->base.device;
    struct nv50_miptree *mt = nv50_miptree(res);
    struct nvc0_transfer *tx;
    uint32_t size;
    int ret;
    unsigned flags = 0;
 
diff --git a/src/gallium/drivers/r300/r300_screen_buffer.c b/src/gallium/drivers/r300/r300_screen_buffer.c
index 4af1c46856e..5b761fdd5b9 100644
--- a/src/gallium/drivers/r300/r300_screen_buffer.c
+++ b/src/gallium/drivers/r300/r300_screen_buffer.c
@@ -62,20 +62,21 @@  static void r300_buffer_destroy(struct pipe_screen *screen,
 
     FREE(rbuf);
 }
 
 static void *
 r300_buffer_transfer_map( struct pipe_context *context,
                           struct pipe_resource *resource,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **ptransfer )
 {
     struct r300_context *r300 = r300_context(context);
     struct radeon_winsys *rws = r300->screen->rws;
     struct r300_resource *rbuf = r300_resource(resource);
     struct pipe_transfer *transfer;
     uint8_t *map;
 
     transfer = slab_alloc(&r300->pool_transfers);
     transfer->resource = resource;
diff --git a/src/gallium/drivers/r300/r300_transfer.c b/src/gallium/drivers/r300/r300_transfer.c
index 9d00f4d9373..35361b5b7b4 100644
--- a/src/gallium/drivers/r300/r300_transfer.c
+++ b/src/gallium/drivers/r300/r300_transfer.c
@@ -98,20 +98,21 @@  static void r300_copy_into_tiled_texture(struct pipe_context *ctx,
     /* XXX remove this. */
     r300_flush(ctx, 0, NULL);
 }
 
 void *
 r300_texture_transfer_map(struct pipe_context *ctx,
                           struct pipe_resource *texture,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **transfer)
 {
     struct r300_context *r300 = r300_context(ctx);
     struct r300_resource *tex = r300_resource(texture);
     struct r300_transfer *trans;
     boolean referenced_cs, referenced_hw;
     enum pipe_format format = tex->b.b.format;
     char *map;
 
     referenced_cs =
diff --git a/src/gallium/drivers/r300/r300_transfer.h b/src/gallium/drivers/r300/r300_transfer.h
index 45477ae6d0d..54cc5aa7042 100644
--- a/src/gallium/drivers/r300/r300_transfer.h
+++ b/src/gallium/drivers/r300/r300_transfer.h
@@ -27,18 +27,19 @@ 
 #include "pipe/p_context.h"
 
 struct r300_context;
 
 void *
 r300_texture_transfer_map(struct pipe_context *ctx,
                           struct pipe_resource *texture,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **transfer);
 
 void
 r300_texture_transfer_unmap(struct pipe_context *ctx,
                             struct pipe_transfer *transfer);
 
 
 #endif
diff --git a/src/gallium/drivers/r600/evergreen_compute.c b/src/gallium/drivers/r600/evergreen_compute.c
index 866837dfa6e..0338f25b881 100644
--- a/src/gallium/drivers/r600/evergreen_compute.c
+++ b/src/gallium/drivers/r600/evergreen_compute.c
@@ -1189,20 +1189,21 @@  void evergreen_init_compute_state_functions(struct r600_context *rctx)
 	rctx->b.b.set_global_binding = evergreen_set_global_binding;
 	rctx->b.b.launch_grid = evergreen_launch_grid;
 
 }
 
 static void *r600_compute_global_transfer_map(struct pipe_context *ctx,
 					      struct pipe_resource *resource,
 					      unsigned level,
 					      unsigned usage,
 					      const struct pipe_box *box,
+                                              unsigned user_stride,
 					      struct pipe_transfer **ptransfer)
 {
 	struct r600_context *rctx = (struct r600_context*)ctx;
 	struct compute_memory_pool *pool = rctx->screen->global_pool;
 	struct r600_resource_global* buffer =
 		(struct r600_resource_global*)resource;
 
 	struct compute_memory_item *item = buffer->chunk;
 	struct pipe_resource *dst = NULL;
 	unsigned offset = box->x;
diff --git a/src/gallium/drivers/r600/r600_buffer_common.c b/src/gallium/drivers/r600/r600_buffer_common.c
index 17a8c3a596f..b460bdb05f1 100644
--- a/src/gallium/drivers/r600/r600_buffer_common.c
+++ b/src/gallium/drivers/r600/r600_buffer_common.c
@@ -339,20 +339,21 @@  static bool r600_can_dma_copy_buffer(struct r600_common_context *rctx,
 	       (dword_aligned && (rctx->dma.cs ||
 				  rctx->screen->has_streamout));
 
 }
 
 static void *r600_buffer_transfer_map(struct pipe_context *ctx,
                                       struct pipe_resource *resource,
                                       unsigned level,
                                       unsigned usage,
                                       const struct pipe_box *box,
+                                      unsigned user_stride,
                                       struct pipe_transfer **ptransfer)
 {
 	struct r600_common_context *rctx = (struct r600_common_context*)ctx;
 	struct r600_common_screen *rscreen = (struct r600_common_screen*)ctx->screen;
 	struct r600_resource *rbuffer = r600_resource(resource);
 	uint8_t *data;
 
 	assert(box->x + box->width <= resource->width0);
 
 	/* From GL_AMD_pinned_memory issues:
@@ -545,21 +546,21 @@  void r600_buffer_subdata(struct pipe_context *ctx,
 {
 	struct pipe_transfer *transfer = NULL;
 	struct pipe_box box;
 	uint8_t *map = NULL;
 
 	u_box_1d(offset, size, &box);
 	map = r600_buffer_transfer_map(ctx, buffer, 0,
 				       PIPE_TRANSFER_WRITE |
 				       PIPE_TRANSFER_DISCARD_RANGE |
 				       usage,
-				       &box, &transfer);
+				       &box, 0, &transfer);
 	if (!map)
 		return;
 
 	memcpy(map, data, size);
 	r600_buffer_transfer_unmap(ctx, transfer);
 }
 
 static const struct u_resource_vtbl r600_buffer_vtbl =
 {
 	NULL,				/* get_handle */
diff --git a/src/gallium/drivers/r600/r600_texture.c b/src/gallium/drivers/r600/r600_texture.c
index c39c00c2e3e..b889e27b5dd 100644
--- a/src/gallium/drivers/r600/r600_texture.c
+++ b/src/gallium/drivers/r600/r600_texture.c
@@ -1268,20 +1268,21 @@  static void r600_texture_invalidate_storage(struct r600_common_context *rctx,
 	p_atomic_inc(&rscreen->dirty_tex_counter);
 
 	rctx->num_alloc_tex_transfer_bytes += rtex->size;
 }
 
 static void *r600_texture_transfer_map(struct pipe_context *ctx,
 				       struct pipe_resource *texture,
 				       unsigned level,
 				       unsigned usage,
 				       const struct pipe_box *box,
+                                       unsigned user_stride,
 				       struct pipe_transfer **ptransfer)
 {
 	struct r600_common_context *rctx = (struct r600_common_context*)ctx;
 	struct r600_texture *rtex = (struct r600_texture*)texture;
 	struct r600_transfer *trans;
 	struct r600_resource *buf;
 	unsigned offset = 0;
 	char *map;
 	bool use_staging_texture = false;
 
diff --git a/src/gallium/drivers/radeonsi/si_buffer.c b/src/gallium/drivers/radeonsi/si_buffer.c
index d17b2c6a831..2cfc3c7ca4e 100644
--- a/src/gallium/drivers/radeonsi/si_buffer.c
+++ b/src/gallium/drivers/radeonsi/si_buffer.c
@@ -358,20 +358,21 @@  static void *si_buffer_get_transfer(struct pipe_context *ctx,
 	transfer->staging = staging;
 	*ptransfer = &transfer->b.b;
 	return data;
 }
 
 static void *si_buffer_transfer_map(struct pipe_context *ctx,
 				    struct pipe_resource *resource,
 				    unsigned level,
 				    unsigned usage,
 				    const struct pipe_box *box,
+				    unsigned user_stride,
 				    struct pipe_transfer **ptransfer)
 {
 	struct si_context *sctx = (struct si_context*)ctx;
 	struct r600_resource *rbuffer = r600_resource(resource);
 	uint8_t *data;
 
 	assert(box->x + box->width <= resource->width0);
 
 	/* From GL_AMD_pinned_memory issues:
 	 *
@@ -578,21 +579,21 @@  static void si_buffer_subdata(struct pipe_context *ctx,
 {
 	struct pipe_transfer *transfer = NULL;
 	struct pipe_box box;
 	uint8_t *map = NULL;
 
 	u_box_1d(offset, size, &box);
 	map = si_buffer_transfer_map(ctx, buffer, 0,
 				       PIPE_TRANSFER_WRITE |
 				       PIPE_TRANSFER_DISCARD_RANGE |
 				       usage,
-				       &box, &transfer);
+				       &box, 0, &transfer);
 	if (!map)
 		return;
 
 	memcpy(map, data, size);
 	si_buffer_transfer_unmap(ctx, transfer);
 }
 
 static const struct u_resource_vtbl si_buffer_vtbl =
 {
 	NULL,				/* get_handle */
diff --git a/src/gallium/drivers/radeonsi/si_texture.c b/src/gallium/drivers/radeonsi/si_texture.c
index 0a2939bdd16..43f1560ec3e 100644
--- a/src/gallium/drivers/radeonsi/si_texture.c
+++ b/src/gallium/drivers/radeonsi/si_texture.c
@@ -1644,20 +1644,21 @@  static void si_texture_invalidate_storage(struct si_context *sctx,
 	p_atomic_inc(&sscreen->dirty_tex_counter);
 
 	sctx->num_alloc_tex_transfer_bytes += rtex->size;
 }
 
 static void *si_texture_transfer_map(struct pipe_context *ctx,
 				     struct pipe_resource *texture,
 				     unsigned level,
 				     unsigned usage,
 				     const struct pipe_box *box,
+				     unsigned user_stride,
 				     struct pipe_transfer **ptransfer)
 {
 	struct si_context *sctx = (struct si_context*)ctx;
 	struct r600_texture *rtex = (struct r600_texture*)texture;
 	struct r600_transfer *trans;
 	struct r600_resource *buf;
 	unsigned offset = 0;
 	char *map;
 	bool use_staging_texture = false;
 
diff --git a/src/gallium/drivers/softpipe/sp_texture.c b/src/gallium/drivers/softpipe/sp_texture.c
index c49bfcaba55..8ab85dff8d8 100644
--- a/src/gallium/drivers/softpipe/sp_texture.c
+++ b/src/gallium/drivers/softpipe/sp_texture.c
@@ -350,20 +350,21 @@  softpipe_surface_destroy(struct pipe_context *pipe,
  * \param level  which mipmap level
  * \param usage  bitmask of PIPE_TRANSFER_x flags
  * \param box  the 1D/2D/3D region of interest
  */
 static void *
 softpipe_transfer_map(struct pipe_context *pipe,
                       struct pipe_resource *resource,
                       unsigned level,
                       unsigned usage,
                       const struct pipe_box *box,
+                      unsigned user_stride,
                       struct pipe_transfer **transfer)
 {
    struct sw_winsys *winsys = softpipe_screen(pipe->screen)->winsys;
    struct softpipe_resource *spr = softpipe_resource(resource);
    struct softpipe_transfer *spt;
    struct pipe_transfer *pt;
    enum pipe_format format = resource->format;
    uint8_t *map;
 
    assert(resource);
diff --git a/src/gallium/drivers/svga/svga_resource_buffer.c b/src/gallium/drivers/svga/svga_resource_buffer.c
index e9d31de6166..dc706cccced 100644
--- a/src/gallium/drivers/svga/svga_resource_buffer.c
+++ b/src/gallium/drivers/svga/svga_resource_buffer.c
@@ -63,20 +63,21 @@  svga_buffer_needs_hw_storage(unsigned usage)
  * processing.  This means we need to exercise extra care here to ensure that
  * the end result is exactly the same as if one DMA was used for every mapped
  * range.
  */
 static void *
 svga_buffer_transfer_map(struct pipe_context *pipe,
                          struct pipe_resource *resource,
                          unsigned level,
                          unsigned usage,
                          const struct pipe_box *box,
+                         unsigned user_stride,
                          struct pipe_transfer **ptransfer)
 {
    struct svga_context *svga = svga_context(pipe);
    struct svga_screen *ss = svga_screen(pipe->screen);
    struct svga_buffer *sbuf = svga_buffer(resource);
    struct pipe_transfer *transfer;
    uint8_t *map = NULL;
    int64_t begin = svga_get_time(svga);
 
    SVGA_STATS_TIME_PUSH(svga_sws(svga), SVGA_STATS_TIME_BUFFERTRANSFERMAP);
diff --git a/src/gallium/drivers/svga/svga_resource_texture.c b/src/gallium/drivers/svga/svga_resource_texture.c
index 71b8ebe7d42..5a0141316b1 100644
--- a/src/gallium/drivers/svga/svga_resource_texture.c
+++ b/src/gallium/drivers/svga/svga_resource_texture.c
@@ -533,20 +533,21 @@  svga_texture_transfer_map_direct(struct svga_context *svga,
 
 /**
  * Request a transfer map to the texture resource
  */
 static void *
 svga_texture_transfer_map(struct pipe_context *pipe,
                           struct pipe_resource *texture,
                           unsigned level,
                           unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **ptransfer)
 {
    struct svga_context *svga = svga_context(pipe);
    struct svga_winsys_screen *sws = svga_screen(pipe->screen)->sws;
    struct svga_texture *tex = svga_texture(texture);
    struct svga_transfer *st;
    struct svga_winsys_surface *surf = tex->handle;
    boolean use_direct_map = svga_have_gb_objects(svga) &&
                             !svga_have_gb_dma(svga);
    void *map = NULL;
diff --git a/src/gallium/drivers/vc4/vc4_resource.c b/src/gallium/drivers/vc4/vc4_resource.c
index a86bd814d20..12f0c433972 100644
--- a/src/gallium/drivers/vc4/vc4_resource.c
+++ b/src/gallium/drivers/vc4/vc4_resource.c
@@ -140,20 +140,21 @@  vc4_get_temp_resource(struct pipe_context *pctx,
         temp_setup.array_size = 1;
 
         return pctx->screen->resource_create(pctx->screen, &temp_setup);
 }
 
 static void *
 vc4_resource_transfer_map(struct pipe_context *pctx,
                           struct pipe_resource *prsc,
                           unsigned level, unsigned usage,
                           const struct pipe_box *box,
+                          unsigned user_stride,
                           struct pipe_transfer **pptrans)
 {
         struct vc4_context *vc4 = vc4_context(pctx);
         struct vc4_resource *rsc = vc4_resource(prsc);
         struct vc4_transfer *trans;
         struct pipe_transfer *ptrans;
         enum pipe_format format = prsc->format;
         char *buf;
 
         /* Upgrade DISCARD_RANGE to WHOLE_RESOURCE if the whole resource is
diff --git a/src/gallium/drivers/virgl/virgl_buffer.c b/src/gallium/drivers/virgl/virgl_buffer.c
index 2e63aebc72c..d6af398b101 100644
--- a/src/gallium/drivers/virgl/virgl_buffer.c
+++ b/src/gallium/drivers/virgl/virgl_buffer.c
@@ -36,20 +36,21 @@  static void virgl_buffer_destroy(struct pipe_screen *screen,
    util_range_destroy(&vbuf->valid_buffer_range);
    vs->vws->resource_unref(vs->vws, vbuf->base.hw_res);
    FREE(vbuf);
 }
 
 static void *virgl_buffer_transfer_map(struct pipe_context *ctx,
                                        struct pipe_resource *resource,
                                        unsigned level,
                                        unsigned usage,
                                        const struct pipe_box *box,
+                                       unsigned user_stride,
                                        struct pipe_transfer **transfer)
 {
    struct virgl_context *vctx = virgl_context(ctx);
    struct virgl_screen *vs = virgl_screen(ctx->screen);
    struct virgl_buffer *vbuf = virgl_buffer(resource);
    struct virgl_transfer *trans;
    void *ptr;
    bool readback;
    uint32_t offset;
    bool doflushwait = false;
diff --git a/src/gallium/drivers/virgl/virgl_texture.c b/src/gallium/drivers/virgl/virgl_texture.c
index 150a5ebd8c7..903418f97a6 100644
--- a/src/gallium/drivers/virgl/virgl_texture.c
+++ b/src/gallium/drivers/virgl/virgl_texture.c
@@ -117,20 +117,21 @@  vrend_get_tex_image_offset(const struct virgl_texture *res,
    }
 
    return offset;
 }
 
 static void *virgl_texture_transfer_map(struct pipe_context *ctx,
                                         struct pipe_resource *resource,
                                         unsigned level,
                                         unsigned usage,
                                         const struct pipe_box *box,
+                                        unsigned user_stride,
                                         struct pipe_transfer **transfer)
 {
    struct virgl_context *vctx = virgl_context(ctx);
    struct virgl_screen *vs = virgl_screen(ctx->screen);
    struct virgl_texture *vtex = virgl_texture(resource);
    enum pipe_format format = resource->format;
    struct virgl_transfer *trans;
    void *ptr;
    boolean readback = TRUE;
    uint32_t offset;
diff --git a/src/gallium/include/pipe/p_context.h b/src/gallium/include/pipe/p_context.h
index c3dc5edf57d..6d99479e4b7 100644
--- a/src/gallium/include/pipe/p_context.h
+++ b/src/gallium/include/pipe/p_context.h
@@ -566,26 +566,33 @@  struct pipe_context {
 
    /**
     * Map a resource.
     *
     * Transfers are (by default) context-private and allow uploads to be
     * interleaved with rendering.
     *
     * out_transfer will contain the transfer object that must be passed
     * to all the other transfer functions. It also contains useful
     * information (like texture strides).
+    *
+    * If \p user_stride is non-zero, the driver should attempt to provide
+    * a transfer with stride == user_stride. However, the resulting stride
+    * may be different, e.g. if user_stride is not sufficiently aligned.
+    * The map may also fail if user_stride is insufficiently aligned or too
+    * large, and the behavior is undefined if user_stride is too small.
     */
    void *(*transfer_map)(struct pipe_context *,
                          struct pipe_resource *resource,
                          unsigned level,
                          unsigned usage,  /* a combination of PIPE_TRANSFER_x */
                          const struct pipe_box *,
+                         unsigned user_stride,
                          struct pipe_transfer **out_transfer);
 
    /* If transfer was created with WRITE|FLUSH_EXPLICIT, only the
     * regions specified with this call are guaranteed to be written to
     * the resource.
     */
    void (*transfer_flush_region)( struct pipe_context *,
 				  struct pipe_transfer *transfer,
 				  const struct pipe_box *);