13464ebd5Sriastradh/*
23464ebd5Sriastradh * Copyright 2008 Corbin Simpson <MostAwesomeDude@gmail.com>
33464ebd5Sriastradh * Copyright 2010 Marek Olšák <maraeo@gmail.com>
43464ebd5Sriastradh *
53464ebd5Sriastradh * Permission is hereby granted, free of charge, to any person obtaining a
63464ebd5Sriastradh * copy of this software and associated documentation files (the "Software"),
73464ebd5Sriastradh * to deal in the Software without restriction, including without limitation
83464ebd5Sriastradh * on the rights to use, copy, modify, merge, publish, distribute, sub
93464ebd5Sriastradh * license, and/or sell copies of the Software, and to permit persons to whom
103464ebd5Sriastradh * the Software is furnished to do so, subject to the following conditions:
113464ebd5Sriastradh *
123464ebd5Sriastradh * The above copyright notice and this permission notice (including the next
133464ebd5Sriastradh * paragraph) shall be included in all copies or substantial portions of the
143464ebd5Sriastradh * Software.
153464ebd5Sriastradh *
163464ebd5Sriastradh * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
173464ebd5Sriastradh * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
183464ebd5Sriastradh * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
193464ebd5Sriastradh * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
203464ebd5Sriastradh * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
213464ebd5Sriastradh * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
223464ebd5Sriastradh * USE OR OTHER DEALINGS IN THE SOFTWARE. */
233464ebd5Sriastradh
243464ebd5Sriastradh#include "r300_transfer.h"
253464ebd5Sriastradh#include "r300_texture_desc.h"
263464ebd5Sriastradh#include "r300_screen_buffer.h"
273464ebd5Sriastradh
283464ebd5Sriastradh#include "util/u_memory.h"
297ec681f3Smrg#include "util/format/u_format.h"
303464ebd5Sriastradh#include "util/u_box.h"
313464ebd5Sriastradh
323464ebd5Sriastradhstruct r300_transfer {
333464ebd5Sriastradh    /* Parent class */
343464ebd5Sriastradh    struct pipe_transfer transfer;
353464ebd5Sriastradh
363464ebd5Sriastradh    /* Linear texture. */
373464ebd5Sriastradh    struct r300_resource *linear_texture;
383464ebd5Sriastradh};
393464ebd5Sriastradh
403464ebd5Sriastradh/* Convenience cast wrapper. */
4101e04c3fSmrgstatic inline struct r300_transfer*
423464ebd5Sriastradhr300_transfer(struct pipe_transfer* transfer)
433464ebd5Sriastradh{
443464ebd5Sriastradh    return (struct r300_transfer*)transfer;
453464ebd5Sriastradh}
463464ebd5Sriastradh
473464ebd5Sriastradh/* Copy from a tiled texture to a detiled one. */
483464ebd5Sriastradhstatic void r300_copy_from_tiled_texture(struct pipe_context *ctx,
493464ebd5Sriastradh                                         struct r300_transfer *r300transfer)
503464ebd5Sriastradh{
513464ebd5Sriastradh    struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
52af69d88dSmrg    struct pipe_resource *src = transfer->resource;
537ec681f3Smrg    struct pipe_resource *dst = &r300transfer->linear_texture->b;
543464ebd5Sriastradh
55af69d88dSmrg    if (src->nr_samples <= 1) {
56af69d88dSmrg        ctx->resource_copy_region(ctx, dst, 0, 0, 0, 0,
57af69d88dSmrg                                  src, transfer->level, &transfer->box);
58af69d88dSmrg    } else {
59af69d88dSmrg        /* Resolve the resource. */
60af69d88dSmrg        struct pipe_blit_info blit;
61af69d88dSmrg
62af69d88dSmrg        memset(&blit, 0, sizeof(blit));
63af69d88dSmrg        blit.src.resource = src;
64af69d88dSmrg        blit.src.format = src->format;
65af69d88dSmrg        blit.src.level = transfer->level;
66af69d88dSmrg        blit.src.box = transfer->box;
67af69d88dSmrg        blit.dst.resource = dst;
68af69d88dSmrg        blit.dst.format = dst->format;
69af69d88dSmrg        blit.dst.box.width = transfer->box.width;
70af69d88dSmrg        blit.dst.box.height = transfer->box.height;
71af69d88dSmrg        blit.dst.box.depth = transfer->box.depth;
72af69d88dSmrg        blit.mask = PIPE_MASK_RGBA;
73af69d88dSmrg        blit.filter = PIPE_TEX_FILTER_NEAREST;
74af69d88dSmrg
75af69d88dSmrg        ctx->blit(ctx, &blit);
76af69d88dSmrg    }
773464ebd5Sriastradh}
783464ebd5Sriastradh
793464ebd5Sriastradh/* Copy a detiled texture to a tiled one. */
803464ebd5Sriastradhstatic void r300_copy_into_tiled_texture(struct pipe_context *ctx,
813464ebd5Sriastradh                                         struct r300_transfer *r300transfer)
823464ebd5Sriastradh{
833464ebd5Sriastradh    struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
843464ebd5Sriastradh    struct pipe_resource *tex = transfer->resource;
853464ebd5Sriastradh    struct pipe_box src_box;
86af69d88dSmrg
87af69d88dSmrg    u_box_3d(0, 0, 0,
88af69d88dSmrg             transfer->box.width, transfer->box.height, transfer->box.depth,
89af69d88dSmrg             &src_box);
903464ebd5Sriastradh
913464ebd5Sriastradh    ctx->resource_copy_region(ctx, tex, transfer->level,
923464ebd5Sriastradh                              transfer->box.x, transfer->box.y, transfer->box.z,
937ec681f3Smrg                              &r300transfer->linear_texture->b, 0, &src_box);
943464ebd5Sriastradh
953464ebd5Sriastradh    /* XXX remove this. */
963464ebd5Sriastradh    r300_flush(ctx, 0, NULL);
973464ebd5Sriastradh}
983464ebd5Sriastradh
99af69d88dSmrgvoid *
100af69d88dSmrgr300_texture_transfer_map(struct pipe_context *ctx,
1013464ebd5Sriastradh                          struct pipe_resource *texture,
1023464ebd5Sriastradh                          unsigned level,
1033464ebd5Sriastradh                          unsigned usage,
104af69d88dSmrg                          const struct pipe_box *box,
105af69d88dSmrg                          struct pipe_transfer **transfer)
1063464ebd5Sriastradh{
1073464ebd5Sriastradh    struct r300_context *r300 = r300_context(ctx);
1083464ebd5Sriastradh    struct r300_resource *tex = r300_resource(texture);
1093464ebd5Sriastradh    struct r300_transfer *trans;
110af69d88dSmrg    boolean referenced_cs, referenced_hw;
1117ec681f3Smrg    enum pipe_format format = tex->b.format;
112af69d88dSmrg    char *map;
1133464ebd5Sriastradh
1143464ebd5Sriastradh    referenced_cs =
1157ec681f3Smrg        r300->rws->cs_is_buffer_referenced(&r300->cs, tex->buf, RADEON_USAGE_READWRITE);
1163464ebd5Sriastradh    if (referenced_cs) {
1173464ebd5Sriastradh        referenced_hw = TRUE;
1183464ebd5Sriastradh    } else {
1193464ebd5Sriastradh        referenced_hw =
1207ec681f3Smrg            !r300->rws->buffer_wait(r300->rws, tex->buf, 0, RADEON_USAGE_READWRITE);
1213464ebd5Sriastradh    }
1223464ebd5Sriastradh
1233464ebd5Sriastradh    trans = CALLOC_STRUCT(r300_transfer);
1243464ebd5Sriastradh    if (trans) {
1253464ebd5Sriastradh        /* Initialize the transfer object. */
126af69d88dSmrg        trans->transfer.resource = texture;
1273464ebd5Sriastradh        trans->transfer.level = level;
1283464ebd5Sriastradh        trans->transfer.usage = usage;
1293464ebd5Sriastradh        trans->transfer.box = *box;
1303464ebd5Sriastradh
1313464ebd5Sriastradh        /* If the texture is tiled, we must create a temporary detiled texture
1323464ebd5Sriastradh         * for this transfer.
1333464ebd5Sriastradh         * Also make write transfers pipelined. */
1343464ebd5Sriastradh        if (tex->tex.microtile || tex->tex.macrotile[level] ||
1357ec681f3Smrg            (referenced_hw && !(usage & PIPE_MAP_READ) &&
136af69d88dSmrg             r300_is_blit_supported(texture->format))) {
137af69d88dSmrg            struct pipe_resource base;
138af69d88dSmrg
1393464ebd5Sriastradh            if (r300->blitter->running) {
1403464ebd5Sriastradh                fprintf(stderr, "r300: ERROR: Blitter recursion in texture_get_transfer.\n");
1413464ebd5Sriastradh                os_break();
1423464ebd5Sriastradh            }
1433464ebd5Sriastradh
144af69d88dSmrg            memset(&base, 0, sizeof(base));
1453464ebd5Sriastradh            base.target = PIPE_TEXTURE_2D;
1463464ebd5Sriastradh            base.format = texture->format;
1473464ebd5Sriastradh            base.width0 = box->width;
1483464ebd5Sriastradh            base.height0 = box->height;
1493464ebd5Sriastradh            base.depth0 = 1;
1503464ebd5Sriastradh            base.array_size = 1;
151af69d88dSmrg            base.usage = PIPE_USAGE_STAGING;
1523464ebd5Sriastradh            base.flags = R300_RESOURCE_FLAG_TRANSFER;
1533464ebd5Sriastradh
154af69d88dSmrg            /* We must set the correct texture target and dimensions if needed for a 3D transfer. */
155af69d88dSmrg            if (box->depth > 1 && util_max_layer(texture, level) > 0) {
156af69d88dSmrg                base.target = texture->target;
157af69d88dSmrg
158af69d88dSmrg                if (base.target == PIPE_TEXTURE_3D) {
159af69d88dSmrg                    base.depth0 = util_next_power_of_two(box->depth);
160af69d88dSmrg                }
1613464ebd5Sriastradh            }
1623464ebd5Sriastradh
1633464ebd5Sriastradh            /* Create the temporary texture. */
1643464ebd5Sriastradh            trans->linear_texture = r300_resource(
1653464ebd5Sriastradh               ctx->screen->resource_create(ctx->screen,
1663464ebd5Sriastradh                                            &base));
1673464ebd5Sriastradh
1683464ebd5Sriastradh            if (!trans->linear_texture) {
1693464ebd5Sriastradh                /* Oh crap, the thing can't create the texture.
1703464ebd5Sriastradh                 * Let's flush and try again. */
1713464ebd5Sriastradh                r300_flush(ctx, 0, NULL);
1723464ebd5Sriastradh
1733464ebd5Sriastradh                trans->linear_texture = r300_resource(
1743464ebd5Sriastradh                   ctx->screen->resource_create(ctx->screen,
1753464ebd5Sriastradh                                                &base));
1763464ebd5Sriastradh
1773464ebd5Sriastradh                if (!trans->linear_texture) {
1783464ebd5Sriastradh                    fprintf(stderr,
179af69d88dSmrg                            "r300: Failed to create a transfer object.\n");
1803464ebd5Sriastradh                    FREE(trans);
1813464ebd5Sriastradh                    return NULL;
1823464ebd5Sriastradh                }
1833464ebd5Sriastradh            }
1843464ebd5Sriastradh
1853464ebd5Sriastradh            assert(!trans->linear_texture->tex.microtile &&
1863464ebd5Sriastradh                   !trans->linear_texture->tex.macrotile[0]);
1873464ebd5Sriastradh
1883464ebd5Sriastradh            /* Set the stride. */
1893464ebd5Sriastradh            trans->transfer.stride =
1903464ebd5Sriastradh                    trans->linear_texture->tex.stride_in_bytes[0];
191af69d88dSmrg            trans->transfer.layer_stride =
192af69d88dSmrg                    trans->linear_texture->tex.layer_size_in_bytes[0];
1933464ebd5Sriastradh
1947ec681f3Smrg            if (usage & PIPE_MAP_READ) {
1953464ebd5Sriastradh                /* We cannot map a tiled texture directly because the data is
1963464ebd5Sriastradh                 * in a different order, therefore we do detiling using a blit. */
1973464ebd5Sriastradh                r300_copy_from_tiled_texture(ctx, trans);
1983464ebd5Sriastradh
1993464ebd5Sriastradh                /* Always referenced in the blit. */
2003464ebd5Sriastradh                r300_flush(ctx, 0, NULL);
2013464ebd5Sriastradh            }
202af69d88dSmrg        } else {
203af69d88dSmrg            /* Unpipelined transfer. */
204af69d88dSmrg            trans->transfer.stride = tex->tex.stride_in_bytes[level];
205af69d88dSmrg            trans->transfer.layer_stride = tex->tex.layer_size_in_bytes[level];
2067ec681f3Smrg            trans->transfer.offset = r300_texture_get_offset(tex, level, box->z);
207af69d88dSmrg
208af69d88dSmrg            if (referenced_cs &&
2097ec681f3Smrg                !(usage & PIPE_MAP_UNSYNCHRONIZED)) {
210af69d88dSmrg                r300_flush(ctx, 0, NULL);
211af69d88dSmrg            }
2123464ebd5Sriastradh        }
2133464ebd5Sriastradh    }
2143464ebd5Sriastradh
215af69d88dSmrg    if (trans->linear_texture) {
2163464ebd5Sriastradh        /* The detiled texture is of the same size as the region being mapped
2173464ebd5Sriastradh         * (no offset needed). */
2187ec681f3Smrg        map = r300->rws->buffer_map(r300->rws, trans->linear_texture->buf,
2197ec681f3Smrg                                    &r300->cs, usage);
220af69d88dSmrg        if (!map) {
221af69d88dSmrg            pipe_resource_reference(
222af69d88dSmrg                (struct pipe_resource**)&trans->linear_texture, NULL);
223af69d88dSmrg            FREE(trans);
224af69d88dSmrg            return NULL;
225af69d88dSmrg        }
226af69d88dSmrg	*transfer = &trans->transfer;
227af69d88dSmrg        return map;
2283464ebd5Sriastradh    } else {
2293464ebd5Sriastradh        /* Tiling is disabled. */
2307ec681f3Smrg        map = r300->rws->buffer_map(r300->rws, tex->buf, &r300->cs, usage);
2313464ebd5Sriastradh        if (!map) {
232af69d88dSmrg            FREE(trans);
2333464ebd5Sriastradh            return NULL;
2343464ebd5Sriastradh        }
2353464ebd5Sriastradh
236af69d88dSmrg	*transfer = &trans->transfer;
2377ec681f3Smrg        return map + trans->transfer.offset +
238af69d88dSmrg            box->y / util_format_get_blockheight(format) * trans->transfer.stride +
239af69d88dSmrg            box->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
2403464ebd5Sriastradh    }
2413464ebd5Sriastradh}
2423464ebd5Sriastradh
2433464ebd5Sriastradhvoid r300_texture_transfer_unmap(struct pipe_context *ctx,
2443464ebd5Sriastradh				 struct pipe_transfer *transfer)
2453464ebd5Sriastradh{
246af69d88dSmrg    struct r300_transfer *trans = r300_transfer(transfer);
2473464ebd5Sriastradh
248af69d88dSmrg    if (trans->linear_texture) {
2497ec681f3Smrg        if (transfer->usage & PIPE_MAP_WRITE) {
250af69d88dSmrg            r300_copy_into_tiled_texture(ctx, trans);
251af69d88dSmrg        }
252af69d88dSmrg
253af69d88dSmrg        pipe_resource_reference(
254af69d88dSmrg            (struct pipe_resource**)&trans->linear_texture, NULL);
2553464ebd5Sriastradh    }
256af69d88dSmrg    FREE(transfer);
2573464ebd5Sriastradh}
258