Merge branch 'mesa_7_6_branch'
[mesa.git] / src / gallium / drivers / cell / ppu / cell_texture.c
1 /**************************************************************************
2 *
3 * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27 /*
28 * Authors:
29 * Keith Whitwell <keith@tungstengraphics.com>
30 * Michel Dänzer <michel@tungstengraphics.com>
31 * Brian Paul
32 */
33
34 #include "pipe/p_context.h"
35 #include "pipe/p_defines.h"
36 #include "pipe/p_inlines.h"
37 #include "pipe/internal/p_winsys_screen.h"
38 #include "util/u_math.h"
39 #include "util/u_memory.h"
40
41 #include "cell_context.h"
42 #include "cell_state.h"
43 #include "cell_texture.h"
44
45
46
47 static void
48 cell_texture_layout(struct cell_texture *ct)
49 {
50 struct pipe_texture *pt = &ct->base;
51 unsigned level;
52 unsigned width = pt->width[0];
53 unsigned height = pt->height[0];
54 unsigned depth = pt->depth[0];
55
56 ct->buffer_size = 0;
57
58 for (level = 0; level <= pt->last_level; level++) {
59 unsigned size;
60 unsigned w_tile, h_tile;
61
62 assert(level < CELL_MAX_TEXTURE_LEVELS);
63
64 /* width, height, rounded up to tile size */
65 w_tile = align(width, TILE_SIZE);
66 h_tile = align(height, TILE_SIZE);
67
68 pt->width[level] = width;
69 pt->height[level] = height;
70 pt->depth[level] = depth;
71 pt->nblocksx[level] = pf_get_nblocksx(&pt->block, w_tile);
72 pt->nblocksy[level] = pf_get_nblocksy(&pt->block, h_tile);
73
74 ct->stride[level] = pt->nblocksx[level] * pt->block.size;
75
76 ct->level_offset[level] = ct->buffer_size;
77
78 size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size;
79 if (pt->target == PIPE_TEXTURE_CUBE)
80 size *= 6;
81 else
82 size *= depth;
83
84 ct->buffer_size += size;
85
86 width = minify(width);
87 height = minify(height);
88 depth = minify(depth);
89 }
90 }
91
92
93 static struct pipe_texture *
94 cell_texture_create(struct pipe_screen *screen,
95 const struct pipe_texture *templat)
96 {
97 struct cell_texture *ct = CALLOC_STRUCT(cell_texture);
98 if (!ct)
99 return NULL;
100
101 ct->base = *templat;
102 pipe_reference_init(&ct->base.reference, 1);
103 ct->base.screen = screen;
104
105 cell_texture_layout(ct);
106
107 ct->buffer = screen->buffer_create(screen, 32, PIPE_BUFFER_USAGE_PIXEL,
108 ct->buffer_size);
109
110 if (!ct->buffer) {
111 FREE(ct);
112 return NULL;
113 }
114
115 return &ct->base;
116 }
117
118
119 static void
120 cell_texture_destroy(struct pipe_texture *pt)
121 {
122 struct cell_texture *ct = cell_texture(pt);
123
124 if (ct->mapped) {
125 pipe_buffer_unmap(ct->buffer->screen, ct->buffer);
126 ct->mapped = NULL;
127 }
128
129 pipe_buffer_reference(&ct->buffer, NULL);
130
131 FREE(ct);
132 }
133
134
135
136 /**
137 * Convert image from linear layout to tiled layout. 4-byte pixels.
138 */
139 static void
140 twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
141 uint src_stride, const uint *src)
142 {
143 const uint tile_size2 = tile_size * tile_size;
144 const uint h_t = (h + tile_size - 1) / tile_size;
145 const uint w_t = (w + tile_size - 1) / tile_size;
146
147 uint it, jt; /* tile counters */
148 uint i, j; /* intra-tile counters */
149
150 src_stride /= 4; /* convert from bytes to pixels */
151
152 /* loop over dest tiles */
153 for (it = 0; it < h_t; it++) {
154 for (jt = 0; jt < w_t; jt++) {
155 /* start of dest tile: */
156 uint *tdst = dst + (it * w_t + jt) * tile_size2;
157
158 /* compute size of this tile (may be smaller than tile_size) */
159 /* XXX note: a compiler bug was found here. That's why the code
160 * looks as it does.
161 */
162 uint tile_width = w - jt * tile_size;
163 tile_width = MIN2(tile_width, tile_size);
164 uint tile_height = h - it * tile_size;
165 tile_height = MIN2(tile_height, tile_size);
166
167 /* loop over texels in the tile */
168 for (i = 0; i < tile_height; i++) {
169 for (j = 0; j < tile_width; j++) {
170 const uint srci = it * tile_size + i;
171 const uint srcj = jt * tile_size + j;
172 ASSERT(srci < h);
173 ASSERT(srcj < w);
174 tdst[i * tile_size + j] = src[srci * src_stride + srcj];
175 }
176 }
177 }
178 }
179 }
180
181
182 /**
183 * For Cell. Basically, rearrange the pixels/quads from this layout:
184 * +--+--+--+--+
185 * |p0|p1|p2|p3|....
186 * +--+--+--+--+
187 *
188 * to this layout:
189 * +--+--+
190 * |p0|p1|....
191 * +--+--+
192 * |p2|p3|
193 * +--+--+
194 */
195 static void
196 twiddle_tile(const uint *tileIn, uint *tileOut)
197 {
198 int y, x;
199
200 for (y = 0; y < TILE_SIZE; y+=2) {
201 for (x = 0; x < TILE_SIZE; x+=2) {
202 int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
203 tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
204 tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
205 tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
206 tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
207 }
208 }
209 }
210
211
212 /**
213 * Convert image from tiled layout to linear layout. 4-byte pixels.
214 */
215 static void
216 untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
217 uint dst_stride, const uint *src)
218 {
219 const uint tile_size2 = tile_size * tile_size;
220 const uint h_t = (h + tile_size - 1) / tile_size;
221 const uint w_t = (w + tile_size - 1) / tile_size;
222 uint *tile_buf;
223 uint it, jt; /* tile counters */
224 uint i, j; /* intra-tile counters */
225
226 dst_stride /= 4; /* convert from bytes to pixels */
227
228 tile_buf = align_malloc(tile_size * tile_size * 4, 16);
229
230 /* loop over src tiles */
231 for (it = 0; it < h_t; it++) {
232 for (jt = 0; jt < w_t; jt++) {
233 /* start of src tile: */
234 const uint *tsrc = src + (it * w_t + jt) * tile_size2;
235
236 twiddle_tile(tsrc, tile_buf);
237 tsrc = tile_buf;
238
239 /* compute size of this tile (may be smaller than tile_size) */
240 /* XXX note: a compiler bug was found here. That's why the code
241 * looks as it does.
242 */
243 uint tile_width = w - jt * tile_size;
244 tile_width = MIN2(tile_width, tile_size);
245 uint tile_height = h - it * tile_size;
246 tile_height = MIN2(tile_height, tile_size);
247
248 /* loop over texels in the tile */
249 for (i = 0; i < tile_height; i++) {
250 for (j = 0; j < tile_width; j++) {
251 uint dsti = it * tile_size + i;
252 uint dstj = jt * tile_size + j;
253 ASSERT(dsti < h);
254 ASSERT(dstj < w);
255 dst[dsti * dst_stride + dstj] = tsrc[i * tile_size + j];
256 }
257 }
258 }
259 }
260
261 align_free(tile_buf);
262 }
263
264
265 static struct pipe_surface *
266 cell_get_tex_surface(struct pipe_screen *screen,
267 struct pipe_texture *pt,
268 unsigned face, unsigned level, unsigned zslice,
269 unsigned usage)
270 {
271 struct cell_texture *ct = cell_texture(pt);
272 struct pipe_surface *ps;
273
274 ps = CALLOC_STRUCT(pipe_surface);
275 if (ps) {
276 pipe_reference_init(&ps->reference, 1);
277 pipe_texture_reference(&ps->texture, pt);
278 ps->format = pt->format;
279 ps->width = pt->width[level];
280 ps->height = pt->height[level];
281 ps->offset = ct->level_offset[level];
282 /* XXX may need to override usage flags (see sp_texture.c) */
283 ps->usage = usage;
284 ps->face = face;
285 ps->level = level;
286 ps->zslice = zslice;
287
288 if (pt->target == PIPE_TEXTURE_CUBE) {
289 ps->offset += face * pt->nblocksy[level] * ct->stride[level];
290 }
291 else if (pt->target == PIPE_TEXTURE_3D) {
292 ps->offset += zslice * pt->nblocksy[level] * ct->stride[level];
293 }
294 else {
295 assert(face == 0);
296 assert(zslice == 0);
297 }
298 }
299 return ps;
300 }
301
302
303 static void
304 cell_tex_surface_destroy(struct pipe_surface *surf)
305 {
306 pipe_texture_reference(&surf->texture, NULL);
307 FREE(surf);
308 }
309
310
311 /**
312 * Create new pipe_transfer object.
313 * This is used by the user to put tex data into a texture (and get it
314 * back out for glGetTexImage).
315 */
316 static struct pipe_transfer *
317 cell_get_tex_transfer(struct pipe_screen *screen,
318 struct pipe_texture *texture,
319 unsigned face, unsigned level, unsigned zslice,
320 enum pipe_transfer_usage usage,
321 unsigned x, unsigned y, unsigned w, unsigned h)
322 {
323 struct cell_texture *ct = cell_texture(texture);
324 struct cell_transfer *ctrans;
325
326 assert(texture);
327 assert(level <= texture->last_level);
328
329 ctrans = CALLOC_STRUCT(cell_transfer);
330 if (ctrans) {
331 struct pipe_transfer *pt = &ctrans->base;
332 pipe_texture_reference(&pt->texture, texture);
333 pt->format = texture->format;
334 pt->block = texture->block;
335 pt->x = x;
336 pt->y = y;
337 pt->width = w;
338 pt->height = h;
339 pt->nblocksx = texture->nblocksx[level];
340 pt->nblocksy = texture->nblocksy[level];
341 pt->stride = ct->stride[level];
342 pt->usage = usage;
343 pt->face = face;
344 pt->level = level;
345 pt->zslice = zslice;
346
347 ctrans->offset = ct->level_offset[level];
348
349 if (texture->target == PIPE_TEXTURE_CUBE) {
350 ctrans->offset += face * pt->nblocksy * pt->stride;
351 }
352 else if (texture->target == PIPE_TEXTURE_3D) {
353 ctrans->offset += zslice * pt->nblocksy * pt->stride;
354 }
355 else {
356 assert(face == 0);
357 assert(zslice == 0);
358 }
359 return pt;
360 }
361 return NULL;
362 }
363
364
365 static void
366 cell_tex_transfer_destroy(struct pipe_transfer *t)
367 {
368 struct cell_transfer *transfer = cell_transfer(t);
369 /* Effectively do the texture_update work here - if texture images
370 * needed post-processing to put them into hardware layout, this is
371 * where it would happen. For cell, nothing to do.
372 */
373 assert (transfer->base.texture);
374 pipe_texture_reference(&transfer->base.texture, NULL);
375 FREE(transfer);
376 }
377
378
379 /**
380 * Return pointer to texture image data in linear layout.
381 */
382 static void *
383 cell_transfer_map(struct pipe_screen *screen, struct pipe_transfer *transfer)
384 {
385 struct cell_transfer *ctrans = cell_transfer(transfer);
386 struct pipe_texture *pt = transfer->texture;
387 struct cell_texture *ct = cell_texture(pt);
388 const uint level = ctrans->base.level;
389 const uint texWidth = pt->width[level];
390 const uint texHeight = pt->height[level];
391 const uint stride = ct->stride[level];
392 unsigned size;
393
394 assert(transfer->texture);
395
396 if (!ct->mapped) {
397 /* map now */
398 ct->mapped = pipe_buffer_map(screen, ct->buffer,
399 pipe_transfer_buffer_flags(transfer));
400 }
401
402 /*
403 * Create a buffer of ordinary memory for the linear texture.
404 * This is the memory that the user will read/write.
405 */
406 size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size;
407
408 ctrans->map = align_malloc(size, 16);
409 if (!ctrans->map)
410 return NULL; /* out of memory */
411
412 if (transfer->usage & PIPE_TRANSFER_READ) {
413 /* need to untwiddle the texture to make a linear version */
414 const uint bpp = pf_get_size(ct->base.format);
415 if (bpp == 4) {
416 const uint *src = (uint *) (ct->mapped + ctrans->offset);
417 uint *dst = ctrans->map;
418 untwiddle_image_uint(texWidth, texHeight, TILE_SIZE,
419 dst, stride, src);
420 }
421 else {
422 // xxx fix
423 }
424 }
425
426 return ctrans->map;
427 }
428
429
430 /**
431 * Called when user is done reading/writing texture data.
432 * If new data was written, this is where we convert the linear data
433 * to tiled data.
434 */
435 static void
436 cell_transfer_unmap(struct pipe_screen *screen,
437 struct pipe_transfer *transfer)
438 {
439 struct cell_transfer *ctrans = cell_transfer(transfer);
440 struct pipe_texture *pt = transfer->texture;
441 struct cell_texture *ct = cell_texture(pt);
442 const uint level = ctrans->base.level;
443 const uint texWidth = pt->width[level];
444 const uint texHeight = pt->height[level];
445 const uint stride = ct->stride[level];
446
447 if (!ct->mapped) {
448 /* map now */
449 ct->mapped = pipe_buffer_map(screen, ct->buffer,
450 PIPE_BUFFER_USAGE_CPU_READ);
451 }
452
453 if (transfer->usage & PIPE_TRANSFER_WRITE) {
454 /* The user wrote new texture data into the mapped buffer.
455 * We need to convert the new linear data into the twiddled/tiled format.
456 */
457 const uint bpp = pf_get_size(ct->base.format);
458 if (bpp == 4) {
459 const uint *src = ctrans->map;
460 uint *dst = (uint *) (ct->mapped + ctrans->offset);
461 twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src);
462 }
463 else {
464 // xxx fix
465 }
466 }
467
468 align_free(ctrans->map);
469 ctrans->map = NULL;
470 }
471
472
473 void
474 cell_init_screen_texture_funcs(struct pipe_screen *screen)
475 {
476 screen->texture_create = cell_texture_create;
477 screen->texture_destroy = cell_texture_destroy;
478
479 screen->get_tex_surface = cell_get_tex_surface;
480 screen->tex_surface_destroy = cell_tex_surface_destroy;
481
482 screen->get_tex_transfer = cell_get_tex_transfer;
483 screen->tex_transfer_destroy = cell_tex_transfer_destroy;
484
485 screen->transfer_map = cell_transfer_map;
486 screen->transfer_unmap = cell_transfer_unmap;
487 }