6ad08c6740e20392dd9cf3af5e0217dc6cf9fa71
[mesa.git] / src / gallium / drivers / r300 / r300_transfer.c
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_desc.h"
26 #include "r300_screen_buffer.h"
27
28 #include "util/u_memory.h"
29 #include "util/u_format.h"
30 #include "util/u_box.h"
31
32 struct r300_transfer {
33 /* Parent class */
34 struct pipe_transfer transfer;
35
36 /* Offset from start of buffer. */
37 unsigned offset;
38
39 /* Linear texture. */
40 struct r300_resource *linear_texture;
41 };
42
43 /* Convenience cast wrapper. */
44 static INLINE struct r300_transfer*
45 r300_transfer(struct pipe_transfer* transfer)
46 {
47 return (struct r300_transfer*)transfer;
48 }
49
50 /* Copy from a tiled texture to a detiled one. */
51 static void r300_copy_from_tiled_texture(struct pipe_context *ctx,
52 struct r300_transfer *r300transfer)
53 {
54 struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
55 struct pipe_resource *tex = transfer->resource;
56
57 ctx->resource_copy_region(ctx, &r300transfer->linear_texture->b.b, 0,
58 0, 0, 0,
59 tex, transfer->level, &transfer->box);
60 }
61
62 /* Copy a detiled texture to a tiled one. */
63 static void r300_copy_into_tiled_texture(struct pipe_context *ctx,
64 struct r300_transfer *r300transfer)
65 {
66 struct pipe_transfer *transfer = (struct pipe_transfer*)r300transfer;
67 struct pipe_resource *tex = transfer->resource;
68 struct pipe_box src_box;
69 u_box_origin_2d(transfer->box.width, transfer->box.height, &src_box);
70
71 ctx->resource_copy_region(ctx, tex, transfer->level,
72 transfer->box.x, transfer->box.y, transfer->box.z,
73 &r300transfer->linear_texture->b.b, 0, &src_box);
74
75 /* XXX remove this. */
76 r300_flush(ctx, 0, NULL);
77 }
78
79 void *
80 r300_texture_transfer_map(struct pipe_context *ctx,
81 struct pipe_resource *texture,
82 unsigned level,
83 unsigned usage,
84 const struct pipe_box *box,
85 struct pipe_transfer **transfer)
86 {
87 struct r300_context *r300 = r300_context(ctx);
88 struct r300_resource *tex = r300_resource(texture);
89 struct r300_transfer *trans;
90 struct pipe_resource base;
91 boolean referenced_cs, referenced_hw;
92 enum pipe_format format = tex->b.b.format;
93 char *map;
94
95 referenced_cs =
96 r300->rws->cs_is_buffer_referenced(r300->cs, tex->cs_buf, RADEON_USAGE_READWRITE);
97 if (referenced_cs) {
98 referenced_hw = TRUE;
99 } else {
100 referenced_hw =
101 r300->rws->buffer_is_busy(tex->buf, RADEON_USAGE_READWRITE);
102 }
103
104 trans = CALLOC_STRUCT(r300_transfer);
105 if (trans) {
106 /* Initialize the transfer object. */
107 trans->transfer.resource = texture;
108 trans->transfer.level = level;
109 trans->transfer.usage = usage;
110 trans->transfer.box = *box;
111
112 /* If the texture is tiled, we must create a temporary detiled texture
113 * for this transfer.
114 * Also make write transfers pipelined. */
115 if (tex->tex.microtile || tex->tex.macrotile[level] ||
116 (referenced_hw && !(usage & PIPE_TRANSFER_READ) &&
117 r300_is_blit_supported(texture->format))) {
118 if (r300->blitter->running) {
119 fprintf(stderr, "r300: ERROR: Blitter recursion in texture_get_transfer.\n");
120 os_break();
121 }
122
123 memset(&base, 0, sizeof(base));
124 base.target = PIPE_TEXTURE_2D;
125 base.format = texture->format;
126 base.width0 = box->width;
127 base.height0 = box->height;
128 base.depth0 = 1;
129 base.array_size = 1;
130 base.usage = PIPE_USAGE_STAGING;
131 base.flags = R300_RESOURCE_FLAG_TRANSFER;
132
133 /* Create the temporary texture. */
134 trans->linear_texture = r300_resource(
135 ctx->screen->resource_create(ctx->screen,
136 &base));
137
138 if (!trans->linear_texture) {
139 /* Oh crap, the thing can't create the texture.
140 * Let's flush and try again. */
141 r300_flush(ctx, 0, NULL);
142
143 trans->linear_texture = r300_resource(
144 ctx->screen->resource_create(ctx->screen,
145 &base));
146
147 if (!trans->linear_texture) {
148 fprintf(stderr,
149 "r300: Failed to create a transfer object.\n");
150 FREE(trans);
151 return NULL;
152 }
153 }
154
155 assert(!trans->linear_texture->tex.microtile &&
156 !trans->linear_texture->tex.macrotile[0]);
157
158 /* Set the stride. */
159 trans->transfer.stride =
160 trans->linear_texture->tex.stride_in_bytes[0];
161
162 if (usage & PIPE_TRANSFER_READ) {
163 /* We cannot map a tiled texture directly because the data is
164 * in a different order, therefore we do detiling using a blit. */
165 r300_copy_from_tiled_texture(ctx, trans);
166
167 /* Always referenced in the blit. */
168 r300_flush(ctx, 0, NULL);
169 }
170 } else {
171 /* Unpipelined transfer. */
172 trans->transfer.stride = tex->tex.stride_in_bytes[level];
173 trans->offset = r300_texture_get_offset(tex, level, box->z);
174
175 if (referenced_cs &&
176 !(usage & PIPE_TRANSFER_UNSYNCHRONIZED)) {
177 r300_flush(ctx, 0, NULL);
178 }
179 }
180 }
181
182 if (trans->linear_texture) {
183 /* The detiled texture is of the same size as the region being mapped
184 * (no offset needed). */
185 map = r300->rws->buffer_map(trans->linear_texture->cs_buf,
186 r300->cs, usage);
187 if (!map) {
188 pipe_resource_reference(
189 (struct pipe_resource**)&trans->linear_texture, NULL);
190 FREE(trans);
191 return NULL;
192 }
193 *transfer = &trans->transfer;
194 return map;
195 } else {
196 /* Tiling is disabled. */
197 map = r300->rws->buffer_map(tex->cs_buf, r300->cs, usage);
198 if (!map) {
199 FREE(trans);
200 return NULL;
201 }
202
203 *transfer = &trans->transfer;
204 return map + trans->offset +
205 box->y / util_format_get_blockheight(format) * trans->transfer.stride +
206 box->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
207 }
208 }
209
210 void r300_texture_transfer_unmap(struct pipe_context *ctx,
211 struct pipe_transfer *transfer)
212 {
213 struct radeon_winsys *rws = r300_context(ctx)->rws;
214 struct r300_transfer *trans = r300_transfer(transfer);
215 struct r300_resource *tex = r300_resource(transfer->resource);
216
217 if (trans->linear_texture) {
218 rws->buffer_unmap(trans->linear_texture->cs_buf);
219
220 if (transfer->usage & PIPE_TRANSFER_WRITE) {
221 r300_copy_into_tiled_texture(ctx, trans);
222 }
223
224 pipe_resource_reference(
225 (struct pipe_resource**)&trans->linear_texture, NULL);
226 } else {
227 rws->buffer_unmap(tex->cs_buf);
228 }
229 FREE(transfer);
230 }