r300g: add ability to tile/detile textures using blit during transfers
authorMarek Olšák <maraeo@gmail.com>
Sun, 14 Feb 2010 02:19:01 +0000 (03:19 +0100)
committerMarek Olšák <maraeo@gmail.com>
Sun, 7 Mar 2010 14:39:37 +0000 (15:39 +0100)
src/gallium/drivers/r300/r300_context.c
src/gallium/drivers/r300/r300_screen.h
src/gallium/drivers/r300/r300_texture.c
src/gallium/drivers/r300/r300_transfer.c
src/gallium/drivers/r300/r300_transfer.h

index ec1c5865220167451f4a28c6e90f76e3170f9a20..e43e088b3689851651a99ba55a3d213cfbd1f910 100644 (file)
@@ -159,6 +159,8 @@ struct pipe_context* r300_create_context(struct pipe_screen* screen,
     if (!r300)
         return NULL;
 
+    r300screen->ctx = (struct pipe_context*)r300;
+
     r300->winsys = radeon_winsys;
 
     r300->context.winsys = (struct pipe_winsys*)radeon_winsys;
index 6d72fec778811b4d62beabcfbb280f14b2943603..484bde6a6be89abbd8926dd150953c813c910a8b 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "r300_chipset.h"
 
+#define R300_TEXTURE_USAGE_TRANSFER PIPE_TEXTURE_USAGE_CUSTOM
+
 struct radeon_winsys;
 
 struct r300_screen {
@@ -36,6 +38,10 @@ struct r300_screen {
 
     struct radeon_winsys* radeon_winsys;
 
+    /* XXX This hack will be removed once texture transfers become part of
+     * pipe_context. */
+    struct pipe_context* ctx;
+
     /* Chipset capabilities */
     struct r300_capabilities* caps;
 
index c0144f64b4a36e096b8955ace145071a227907a9..04e326eb7871cd17a196b8cab3476a8581816a6d 100644 (file)
@@ -725,9 +725,8 @@ static void r300_setup_flags(struct r300_texture* tex)
 }
 
 /* Create a new texture. */
-static struct pipe_texture*
-    r300_texture_create(struct pipe_screen* screen,
-                        const struct pipe_texture* template)
+static struct pipe_texture* r300_texture_create(struct pipe_screen* screen,
+                                         const struct pipe_texture* template)
 {
     struct r300_texture* tex = CALLOC_STRUCT(r300_texture);
     struct r300_screen* rscreen = r300_screen(screen);
index fa48688db2858f31389cbeb40ca17a5df86e1d74..56a6976e8b61a8293a10522e6c5c3459742dd6a0 100644 (file)
 #include "util/u_memory.h"
 #include "util/u_format.h"
 
+struct r300_transfer {
+    /* Parent class */
+    struct pipe_transfer transfer;
+
+    /* Pipe context. */
+    struct pipe_context *ctx;
+
+    /* Parameters of get_tex_transfer. */
+    unsigned x, y, level, zslice, face;
+
+    /* Offset from start of buffer. */
+    unsigned offset;
+
+    /* Detiled texture. */
+    struct r300_texture *detiled_texture;
+
+    /* Transfer and format flags. */
+    unsigned buffer_usage, render_target_usage;
+};
+
+/* Convenience cast wrapper. */
+static INLINE struct r300_transfer*
+r300_transfer(struct pipe_transfer* transfer)
+{
+    return (struct r300_transfer*)transfer;
+}
+
+/* Copy from a tiled texture to a detiled one. */
+static void r300_copy_from_tiled_texture(struct pipe_context *ctx,
+                                         struct r300_transfer *r300transfer)
+{
+    struct pipe_screen *screen = ctx->screen;
+    struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
+    struct pipe_texture *tex = transfer->texture;
+    struct pipe_surface *src, *dst;
+
+    src = screen->get_tex_surface(screen, tex, r300transfer->face,
+                                  r300transfer->level, r300transfer->zslice,
+                                  PIPE_BUFFER_USAGE_GPU_READ |
+                                  PIPE_BUFFER_USAGE_PIXEL);
+
+    dst = screen->get_tex_surface(screen, &r300transfer->detiled_texture->tex,
+                                  0, 0, 0,
+                                  PIPE_BUFFER_USAGE_GPU_WRITE |
+                                  PIPE_BUFFER_USAGE_PIXEL |
+                                  r300transfer->buffer_usage);
+
+    ctx->surface_copy(ctx, dst, 0, 0, src, r300transfer->x, r300transfer->y,
+                      transfer->width, transfer->height);
+
+    pipe_surface_reference(&src, NULL);
+    pipe_surface_reference(&dst, NULL);
+}
+
+/* Copy a detiled texture to a tiled one. */
+static void r300_copy_into_tiled_texture(struct pipe_context *ctx,
+                                         struct r300_transfer *r300transfer)
+{
+    struct pipe_screen *screen = ctx->screen;
+    struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
+    struct pipe_texture *tex = transfer->texture;
+    struct pipe_surface *src, *dst;
+
+    src = screen->get_tex_surface(screen, &r300transfer->detiled_texture->tex,
+                                  0, 0, 0,
+                                  PIPE_BUFFER_USAGE_GPU_READ |
+                                  PIPE_BUFFER_USAGE_PIXEL);
+
+    dst = screen->get_tex_surface(screen, tex, r300transfer->face,
+                                  r300transfer->level, r300transfer->zslice,
+                                  PIPE_BUFFER_USAGE_GPU_WRITE |
+                                  PIPE_BUFFER_USAGE_PIXEL);
+
+    ctx->surface_copy(ctx, dst, r300transfer->x, r300transfer->y, src, 0, 0,
+                      transfer->width, transfer->height);
+
+    /* XXX this flush fixes lots of regressions, not sure why */
+    ctx->flush(ctx, 0, NULL);
+
+    pipe_surface_reference(&src, NULL);
+    pipe_surface_reference(&dst, NULL);
+}
+
 static struct pipe_transfer*
 r300_get_tex_transfer(struct pipe_screen *screen,
                       struct pipe_texture *texture,
@@ -38,58 +121,139 @@ r300_get_tex_transfer(struct pipe_screen *screen,
 {
     struct r300_texture *tex = (struct r300_texture *)texture;
     struct r300_transfer *trans;
-    struct r300_screen *rscreen = r300_screen(screen);
-    unsigned offset;
-
-    offset = r300_texture_get_offset(tex, level, zslice, face);  /* in bytes */
+    struct r300_screen *r300screen = r300_screen(screen);
+    struct pipe_texture template;
 
     trans = CALLOC_STRUCT(r300_transfer);
     if (trans) {
+        /* Initialize the transfer object. */
         pipe_texture_reference(&trans->transfer.texture, texture);
-        trans->transfer.x = x;
-        trans->transfer.y = y;
+        trans->transfer.usage = usage;
         trans->transfer.width = w;
         trans->transfer.height = h;
-        trans->transfer.stride = r300_texture_get_stride(rscreen, tex, level);
-        trans->transfer.usage = usage;
-        trans->transfer.zslice = zslice;
-        trans->transfer.face = face;
+        trans->ctx = r300screen->ctx;
+        trans->x = x;
+        trans->y = y;
+        trans->level = level;
+        trans->zslice = zslice;
+        trans->face = face;
+
+        /* If the texture is tiled, we must create a temporary detiled texture
+         * for this transfer. */
+        if (tex->microtile || tex->macrotile) {
+            trans->buffer_usage = pipe_transfer_buffer_flags(&trans->transfer);
+            trans->render_target_usage =
+                util_format_is_depth_or_stencil(texture->format) ?
+                PIPE_TEXTURE_USAGE_DEPTH_STENCIL :
+                PIPE_TEXTURE_USAGE_RENDER_TARGET;
+
+            template.target = PIPE_TEXTURE_2D;
+            template.format = texture->format;
+            template.width0 = w;
+            template.height0 = h;
+            template.depth0 = 0;
+            template.last_level = 0;
+            template.nr_samples = 0;
+            template.tex_usage = PIPE_TEXTURE_USAGE_DYNAMIC |
+                                 R300_TEXTURE_USAGE_TRANSFER;
 
-        trans->offset = offset;
+            /* For texture reading, the temporary (detiled) texture is used as
+             * a render target when blitting from a tiled texture. */
+            if (usage & PIPE_TRANSFER_READ) {
+                template.tex_usage |= trans->render_target_usage;
+            }
+            /* For texture writing, the temporary texture is used as a sampler
+             * when blitting into a tiled texture. */
+            if (usage & PIPE_TRANSFER_WRITE) {
+                template.tex_usage |= PIPE_TEXTURE_USAGE_SAMPLER;
+            }
+
+            /* Create the temporary texture. */
+            trans->detiled_texture =
+                (struct r300_texture*)screen->texture_create(screen, &template);
+            assert(!trans->detiled_texture->microtile &&
+                   !trans->detiled_texture->macrotile);
+
+            /* Set the stride.
+             * Parameters x, y, level, zslice, and face remain zero. */
+            trans->transfer.stride =
+                r300_texture_get_stride(r300screen, trans->detiled_texture, 0);
+
+            if (usage & PIPE_TRANSFER_READ) {
+                /* We cannot map a tiled texture directly because the data is
+                 * in a different order, therefore we do detiling using a blit. */
+                r300_copy_from_tiled_texture(r300screen->ctx, trans);
+            }
+        } else {
+            trans->transfer.x = x;
+            trans->transfer.y = y;
+            trans->transfer.stride =
+                r300_texture_get_stride(r300screen, tex, level);
+            trans->transfer.level = level;
+            trans->transfer.zslice = zslice;
+            trans->transfer.face = face;
+            trans->offset = r300_texture_get_offset(tex, level, zslice, face);
+        }
     }
     return &trans->transfer;
 }
 
 static void r300_tex_transfer_destroy(struct pipe_transfer *trans)
 {
-   pipe_texture_reference(&trans->texture, NULL);
-   FREE(trans);
+    struct r300_transfer *r300transfer = r300_transfer(trans);
+
+    if (r300transfer->detiled_texture) {
+        if (trans->usage & PIPE_TRANSFER_WRITE) {
+            r300_copy_into_tiled_texture(r300transfer->ctx, r300transfer);
+        }
+
+        pipe_texture_reference(
+            (struct pipe_texture**)&r300transfer->detiled_texture, NULL);
+    }
+    pipe_texture_reference(&trans->texture, NULL);
+    FREE(trans);
 }
 
 static void* r300_transfer_map(struct pipe_screen *screen,
-                              struct pipe_transfer *transfer)
+                               struct pipe_transfer *transfer)
 {
+    struct r300_transfer *r300transfer = r300_transfer(transfer);
     struct r300_texture *tex = (struct r300_texture*)transfer->texture;
     char *map;
     enum pipe_format format = tex->tex.format;
 
-    map = pipe_buffer_map(screen, tex->buffer,
-                          pipe_transfer_buffer_flags(transfer));
+    if (r300transfer->detiled_texture) {
+        /* The detiled texture is of the same size as the region being mapped
+         * (no offset needed). */
+        return pipe_buffer_map(screen,
+                               r300transfer->detiled_texture->buffer,
+                               pipe_transfer_buffer_flags(transfer));
+    } else {
+        /* Tiling is disabled. */
+        map = pipe_buffer_map(screen, tex->buffer,
+                              pipe_transfer_buffer_flags(transfer));
 
-    if (!map) {
-        return NULL;
-    }
+        if (!map) {
+            return NULL;
+        }
 
-    return map + r300_transfer(transfer)->offset +
-        transfer->y / util_format_get_blockheight(format) * transfer->stride +
-        transfer->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
+        return map + r300_transfer(transfer)->offset +
+            transfer->y / util_format_get_blockheight(format) * transfer->stride +
+            transfer->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
+    }
 }
 
 static void r300_transfer_unmap(struct pipe_screen *screen,
                                 struct pipe_transfer *transfer)
 {
+    struct r300_transfer *r300transfer = r300_transfer(transfer);
     struct r300_texture *tex = (struct r300_texture*)transfer->texture;
-    pipe_buffer_unmap(screen, tex->buffer);
+
+    if (r300transfer->detiled_texture) {
+        pipe_buffer_unmap(screen, r300transfer->detiled_texture->buffer);
+    } else {
+        pipe_buffer_unmap(screen, tex->buffer);
+    }
 }
 
 void r300_init_screen_transfer_functions(struct pipe_screen *screen)
index faf62338efbcb9ce871a4012e1754d20db3d7f63..60d1d3dc85c3d71f123a07b09272afbeb0561814 100644 (file)
 #define R300_TRANSFER
 
 #include "pipe/p_screen.h"
-#include "pipe/p_state.h"
-
-struct r300_texture;
-struct r300_screen;
-
-struct r300_transfer {
-    /* Parent class */
-    struct pipe_transfer transfer;
-
-    /* Parameters of get_tex_transfer. */
-    unsigned x, y, level, zslice, face;
-
-    /* Offset from start of buffer. */
-    unsigned offset;
-
-    /* Untiled texture. */
-    struct r300_texture *untiled_texture;
-
-    /* Transfer and format flags. */
-    unsigned buffer_usage, render_target_usage;
-};
-
-/* Convenience cast wrapper. */
-static INLINE struct r300_transfer*
-r300_transfer(struct pipe_transfer* transfer)
-{
-    return (struct r300_transfer*)transfer;
-}
 
 void r300_init_screen_transfer_functions(struct pipe_screen *screen);
 
 #endif
-