r300_transfer.c revision ae3063066cb209efd07413f125bb86108906b033
1/*
2 * Copyright 2008 Corbin Simpson <MostAwesomeDude@gmail.com>
3 * Copyright 2010 Marek Olšák <maraeo@gmail.com>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * on the rights to use, copy, modify, merge, publish, distribute, sub
9 * license, and/or sell copies of the Software, and to permit persons to whom
10 * the Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
23
24#include "r300_transfer.h"
25#include "r300_texture.h"
26#include "r300_screen_buffer.h"
27
28#include "util/u_memory.h"
29#include "util/u_format.h"
30
31struct r300_transfer {
32    /* Parent class */
33    struct pipe_transfer transfer;
34
35    /* Offset from start of buffer. */
36    unsigned offset;
37
38    /* Detiled texture. */
39    struct r300_texture *detiled_texture;
40};
41
42/* Convenience cast wrapper. */
43static INLINE struct r300_transfer*
44r300_transfer(struct pipe_transfer* transfer)
45{
46    return (struct r300_transfer*)transfer;
47}
48
49/* Copy from a tiled texture to a detiled one. */
50static void r300_copy_from_tiled_texture(struct pipe_context *ctx,
51                                         struct r300_transfer *r300transfer)
52{
53    struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
54    struct pipe_resource *tex = transfer->resource;
55    struct pipe_subresource subdst;
56
57    subdst.face = 0;
58    subdst.level = 0;
59
60    /* XXX if we don't flush before copying the texture and mapping it,
61     * we get wrong pixels, i.e. it's like latest draw calls didn't happen,
62     * including this blit. Tests: e.g. piglit/provoking-vertex
63     *
64     * Since the flush immediately before mapping is implicit (the buffer is
65     * always referenced in resource_copy_region), every read transfer costs
66     * 2 flushes. That sucks. */
67    ctx->flush(ctx, 0, NULL);
68
69    ctx->resource_copy_region(ctx, &r300transfer->detiled_texture->b.b, subdst,
70			      0, 0, 0,
71			      tex, transfer->sr,
72			      transfer->box.x, transfer->box.y, transfer->box.z,
73			      transfer->box.width, transfer->box.height);
74
75    /* Flushing after the copy is implicit, issued by winsys. */
76}
77
78/* Copy a detiled texture to a tiled one. */
79static void r300_copy_into_tiled_texture(struct pipe_context *ctx,
80                                         struct r300_transfer *r300transfer)
81{
82    struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
83    struct pipe_resource *tex = transfer->resource;
84    struct pipe_subresource subsrc;
85
86    subsrc.face = 0;
87    subsrc.level = 0;
88
89    ctx->resource_copy_region(ctx, tex, transfer->sr,
90			      transfer->box.x, transfer->box.y, transfer->box.z,
91			      &r300transfer->detiled_texture->b.b, subsrc,
92			      0, 0, 0,
93			      transfer->box.width, transfer->box.height);
94
95    /* XXX this flush fixes a few piglit tests (e.g. glean/pixelFormats). */
96    ctx->flush(ctx, 0, NULL);
97}
98
99struct pipe_transfer*
100r300_texture_get_transfer(struct pipe_context *ctx,
101			  struct pipe_resource *texture,
102			  struct pipe_subresource sr,
103			  unsigned usage,
104			  const struct pipe_box *box)
105{
106    struct r300_texture *tex = r300_texture(texture);
107    struct r300_screen *r300screen = r300_screen(ctx->screen);
108    struct r300_transfer *trans;
109    struct pipe_resource base;
110    boolean referenced_cs, referenced_hw;
111
112    referenced_cs = r300screen->rws->is_buffer_referenced(
113                                r300screen->rws, tex->buffer, R300_REF_CS);
114    if (referenced_cs) {
115        referenced_hw = TRUE;
116    } else {
117        referenced_hw = r300screen->rws->is_buffer_referenced(
118                                r300screen->rws, tex->buffer, R300_REF_HW);
119    }
120
121    trans = CALLOC_STRUCT(r300_transfer);
122    if (trans) {
123        /* Initialize the transfer object. */
124        pipe_resource_reference(&trans->transfer.resource, texture);
125        trans->transfer.sr = sr;
126        trans->transfer.usage = usage;
127        trans->transfer.box = *box;
128
129        /* If the texture is tiled, we must create a temporary detiled texture
130         * for this transfer.
131         * Also make write transfers pipelined. */
132        if (tex->microtile || tex->macrotile ||
133            (referenced_hw & !(usage & PIPE_TRANSFER_READ))) {
134            base.target = PIPE_TEXTURE_2D;
135            base.format = texture->format;
136            base.width0 = box->width;
137            base.height0 = box->height;
138            base.depth0 = 0;
139            base.last_level = 0;
140            base.nr_samples = 0;
141            base.usage = PIPE_USAGE_DYNAMIC;
142            base.bind = 0;
143	    base.flags = R300_RESOURCE_FLAG_TRANSFER;
144
145            /* For texture reading, the temporary (detiled) texture is used as
146             * a render target when blitting from a tiled texture. */
147            if (usage & PIPE_TRANSFER_READ) {
148                base.bind |= PIPE_BIND_RENDER_TARGET;
149            }
150            /* For texture writing, the temporary texture is used as a sampler
151             * when blitting into a tiled texture. */
152            if (usage & PIPE_TRANSFER_WRITE) {
153                base.bind |= PIPE_BIND_SAMPLER_VIEW;
154            }
155
156            /* Create the temporary texture. */
157            trans->detiled_texture = r300_texture(
158               ctx->screen->resource_create(ctx->screen,
159                                            &base));
160
161            assert(!trans->detiled_texture->microtile &&
162                   !trans->detiled_texture->macrotile);
163
164            /* Set the stride.
165	     *
166	     * Even though we are using an internal texture for this,
167	     * the transfer sr, box and usage parameters still reflect
168	     * the arguments received to get_transfer.  We just do the
169	     * right thing internally.
170	     */
171            trans->transfer.stride =
172                r300_texture_get_stride(r300screen, trans->detiled_texture, 0);
173
174            if (usage & PIPE_TRANSFER_READ) {
175                /* We cannot map a tiled texture directly because the data is
176                 * in a different order, therefore we do detiling using a blit. */
177                r300_copy_from_tiled_texture(ctx, trans);
178
179                /* Always referenced in the blit. */
180                ctx->flush(ctx, 0, NULL);
181            }
182        } else {
183            trans->transfer.stride =
184                r300_texture_get_stride(r300screen, tex, sr.level);
185            trans->offset = r300_texture_get_offset(tex, sr.level, box->z, sr.face);
186
187            if (referenced_cs && (usage & PIPE_TRANSFER_READ))
188                ctx->flush(ctx, PIPE_FLUSH_RENDER_CACHE, NULL);
189        }
190    }
191    return &trans->transfer;
192}
193
194void r300_texture_transfer_destroy(struct pipe_context *ctx,
195				   struct pipe_transfer *trans)
196{
197    struct r300_transfer *r300transfer = r300_transfer(trans);
198
199    if (r300transfer->detiled_texture) {
200        if (trans->usage & PIPE_TRANSFER_WRITE) {
201            r300_copy_into_tiled_texture(ctx, r300transfer);
202        }
203
204        pipe_resource_reference(
205            (struct pipe_resource**)&r300transfer->detiled_texture, NULL);
206    }
207    pipe_resource_reference(&trans->resource, NULL);
208    FREE(trans);
209}
210
211void* r300_texture_transfer_map(struct pipe_context *ctx,
212				struct pipe_transfer *transfer)
213{
214    struct r300_winsys_screen *rws = (struct r300_winsys_screen *)ctx->winsys;
215    struct r300_transfer *r300transfer = r300_transfer(transfer);
216    struct r300_texture *tex = r300_texture(transfer->resource);
217    char *map;
218    enum pipe_format format = tex->b.b.format;
219
220    if (r300transfer->detiled_texture) {
221        /* The detiled texture is of the same size as the region being mapped
222         * (no offset needed). */
223        return rws->buffer_map(rws,
224                               r300transfer->detiled_texture->buffer,
225                               transfer->usage);
226    } else {
227        /* Tiling is disabled. */
228        map = rws->buffer_map(rws, tex->buffer,
229                              transfer->usage);
230
231        if (!map) {
232            return NULL;
233        }
234
235        return map + r300_transfer(transfer)->offset +
236            transfer->box.y / util_format_get_blockheight(format) * transfer->stride +
237            transfer->box.x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
238    }
239}
240
241void r300_texture_transfer_unmap(struct pipe_context *ctx,
242				 struct pipe_transfer *transfer)
243{
244    struct r300_winsys_screen *rws = (struct r300_winsys_screen *)ctx->winsys;
245    struct r300_transfer *r300transfer = r300_transfer(transfer);
246    struct r300_texture *tex = r300_texture(transfer->resource);
247
248    if (r300transfer->detiled_texture) {
249	rws->buffer_unmap(rws, r300transfer->detiled_texture->buffer);
250    } else {
251        rws->buffer_unmap(rws, tex->buffer);
252    }
253}
254