Merge commit 'origin/gallium-0.1'
[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 unsigned
48 minify(unsigned d)
49 {
50 return MAX2(1, d>>1);
51 }
52
53
54 static void
55 cell_texture_layout(struct cell_texture *ct)
56 {
57 struct pipe_texture *pt = &ct->base;
58 unsigned level;
59 unsigned width = pt->width[0];
60 unsigned height = pt->height[0];
61 unsigned depth = pt->depth[0];
62
63 ct->buffer_size = 0;
64
65 for (level = 0; level <= pt->last_level; level++) {
66 unsigned size;
67 unsigned w_tile, h_tile;
68
69 assert(level < CELL_MAX_TEXTURE_LEVELS);
70
71 /* width, height, rounded up to tile size */
72 w_tile = align(width, TILE_SIZE);
73 h_tile = align(height, TILE_SIZE);
74
75 pt->width[level] = width;
76 pt->height[level] = height;
77 pt->depth[level] = depth;
78 pt->nblocksx[level] = pf_get_nblocksx(&pt->block, w_tile);
79 pt->nblocksy[level] = pf_get_nblocksy(&pt->block, h_tile);
80
81 ct->stride[level] = pt->nblocksx[level] * pt->block.size;
82
83 ct->level_offset[level] = ct->buffer_size;
84
85 size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size;
86 if (pt->target == PIPE_TEXTURE_CUBE)
87 size *= 6;
88 else
89 size *= depth;
90
91 ct->buffer_size += size;
92
93 width = minify(width);
94 height = minify(height);
95 depth = minify(depth);
96 }
97 }
98
99
100 static struct pipe_texture *
101 cell_texture_create(struct pipe_screen *screen,
102 const struct pipe_texture *templat)
103 {
104 struct pipe_winsys *ws = screen->winsys;
105 struct cell_texture *ct = CALLOC_STRUCT(cell_texture);
106 if (!ct)
107 return NULL;
108
109 ct->base = *templat;
110 ct->base.refcount = 1;
111 ct->base.screen = screen;
112
113 cell_texture_layout(ct);
114
115 ct->buffer = ws->buffer_create(ws, 32, PIPE_BUFFER_USAGE_PIXEL,
116 ct->buffer_size);
117
118 if (!ct->buffer) {
119 FREE(ct);
120 return NULL;
121 }
122
123 return &ct->base;
124 }
125
126
127 static void
128 cell_texture_release(struct pipe_screen *screen,
129 struct pipe_texture **pt)
130 {
131 if (!*pt)
132 return;
133
134 if (--(*pt)->refcount <= 0) {
135 /* Delete this texture now.
136 * But note that the underlying pipe_buffer may linger...
137 */
138 struct cell_texture *ct = cell_texture(*pt);
139
140 if (ct->mapped) {
141 pipe_buffer_unmap(screen, ct->buffer);
142 ct->mapped = NULL;
143 }
144
145 pipe_buffer_reference(screen, &ct->buffer, NULL);
146
147 FREE(ct);
148 }
149 *pt = NULL;
150 }
151
152
153
154 /**
155 * Convert image from linear layout to tiled layout. 4-byte pixels.
156 */
157 static void
158 twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
159 uint src_stride, const uint *src)
160 {
161 const uint tile_size2 = tile_size * tile_size;
162 const uint h_t = (h + tile_size - 1) / tile_size;
163 const uint w_t = (w + tile_size - 1) / tile_size;
164
165 uint it, jt; /* tile counters */
166 uint i, j; /* intra-tile counters */
167
168 src_stride /= 4; /* convert from bytes to pixels */
169
170 /* loop over dest tiles */
171 for (it = 0; it < h_t; it++) {
172 for (jt = 0; jt < w_t; jt++) {
173 /* start of dest tile: */
174 uint *tdst = dst + (it * w_t + jt) * tile_size2;
175
176 /* compute size of this tile (may be smaller than tile_size) */
177 /* XXX note: a compiler bug was found here. That's why the code
178 * looks as it does.
179 */
180 uint tile_width = w - jt * tile_size;
181 tile_width = MIN2(tile_width, tile_size);
182 uint tile_height = h - it * tile_size;
183 tile_height = MIN2(tile_height, tile_size);
184
185 /* loop over texels in the tile */
186 for (i = 0; i < tile_height; i++) {
187 for (j = 0; j < tile_width; j++) {
188 const uint srci = it * tile_size + i;
189 const uint srcj = jt * tile_size + j;
190 ASSERT(srci < h);
191 ASSERT(srcj < w);
192 tdst[i * tile_size + j] = src[srci * src_stride + srcj];
193 }
194 }
195 }
196 }
197 }
198
199
200 /**
201 * For Cell. Basically, rearrange the pixels/quads from this layout:
202 * +--+--+--+--+
203 * |p0|p1|p2|p3|....
204 * +--+--+--+--+
205 *
206 * to this layout:
207 * +--+--+
208 * |p0|p1|....
209 * +--+--+
210 * |p2|p3|
211 * +--+--+
212 */
213 static void
214 twiddle_tile(const uint *tileIn, uint *tileOut)
215 {
216 int y, x;
217
218 for (y = 0; y < TILE_SIZE; y+=2) {
219 for (x = 0; x < TILE_SIZE; x+=2) {
220 int k = 4 * (y/2 * TILE_SIZE/2 + x/2);
221 tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k];
222 tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1];
223 tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2];
224 tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3];
225 }
226 }
227 }
228
229
230 /**
231 * Convert image from tiled layout to linear layout. 4-byte pixels.
232 */
233 static void
234 untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
235 uint dst_stride, const uint *src)
236 {
237 const uint tile_size2 = tile_size * tile_size;
238 const uint h_t = (h + tile_size - 1) / tile_size;
239 const uint w_t = (w + tile_size - 1) / tile_size;
240 uint *tile_buf;
241 uint it, jt; /* tile counters */
242 uint i, j; /* intra-tile counters */
243
244 dst_stride /= 4; /* convert from bytes to pixels */
245
246 tile_buf = align_malloc(tile_size * tile_size * 4, 16);
247
248 /* loop over src tiles */
249 for (it = 0; it < h_t; it++) {
250 for (jt = 0; jt < w_t; jt++) {
251 /* start of src tile: */
252 const uint *tsrc = src + (it * w_t + jt) * tile_size2;
253
254 twiddle_tile(tsrc, tile_buf);
255 tsrc = tile_buf;
256
257 /* compute size of this tile (may be smaller than tile_size) */
258 /* XXX note: a compiler bug was found here. That's why the code
259 * looks as it does.
260 */
261 uint tile_width = w - jt * tile_size;
262 tile_width = MIN2(tile_width, tile_size);
263 uint tile_height = h - it * tile_size;
264 tile_height = MIN2(tile_height, tile_size);
265
266 /* loop over texels in the tile */
267 for (i = 0; i < tile_height; i++) {
268 for (j = 0; j < tile_width; j++) {
269 uint dsti = it * tile_size + i;
270 uint dstj = jt * tile_size + j;
271 ASSERT(dsti < h);
272 ASSERT(dstj < w);
273 dst[dsti * dst_stride + dstj] = tsrc[i * tile_size + j];
274 }
275 }
276 }
277 }
278
279 align_free(tile_buf);
280 }
281
282
283 static struct pipe_surface *
284 cell_get_tex_surface(struct pipe_screen *screen,
285 struct pipe_texture *pt,
286 unsigned face, unsigned level, unsigned zslice,
287 unsigned usage)
288 {
289 struct cell_texture *ct = cell_texture(pt);
290 struct pipe_surface *ps;
291
292 ps = CALLOC_STRUCT(pipe_surface);
293 if (ps) {
294 ps->refcount = 1;
295 pipe_texture_reference(&ps->texture, pt);
296 ps->format = pt->format;
297 ps->width = pt->width[level];
298 ps->height = pt->height[level];
299 ps->offset = ct->level_offset[level];
300 /* XXX may need to override usage flags (see sp_texture.c) */
301 ps->usage = usage;
302 ps->face = face;
303 ps->level = level;
304 ps->zslice = zslice;
305
306 if (pt->target == PIPE_TEXTURE_CUBE) {
307 ps->offset += face * pt->nblocksy[level] * ct->stride[level];
308 }
309 else if (pt->target == PIPE_TEXTURE_3D) {
310 ps->offset += zslice * pt->nblocksy[level] * ct->stride[level];
311 }
312 else {
313 assert(face == 0);
314 assert(zslice == 0);
315 }
316 }
317 return ps;
318 }
319
320
321 static void
322 cell_tex_surface_release(struct pipe_screen *screen,
323 struct pipe_surface **s)
324 {
325 struct pipe_surface *surf = *s;
326
327 if (--surf->refcount == 0) {
328 pipe_texture_reference(&surf->texture, NULL);
329 FREE(surf);
330 }
331 *s = NULL;
332 }
333
334
335 /**
336 * Create new pipe_transfer object.
337 * This is used by the user to put tex data into a texture (and get it
338 * back out for glGetTexImage).
339 */
340 static struct pipe_transfer *
341 cell_get_tex_transfer(struct pipe_screen *screen,
342 struct pipe_texture *texture,
343 unsigned face, unsigned level, unsigned zslice,
344 enum pipe_transfer_usage usage,
345 unsigned x, unsigned y, unsigned w, unsigned h)
346 {
347 struct cell_texture *ct = cell_texture(texture);
348 struct cell_transfer *ctrans;
349
350 assert(texture);
351 assert(level <= texture->last_level);
352
353 ctrans = CALLOC_STRUCT(cell_transfer);
354 if (ctrans) {
355 struct pipe_transfer *pt = &ctrans->base;
356 pt->refcount = 1;
357 pipe_texture_reference(&pt->texture, texture);
358 pt->format = texture->format;
359 pt->block = texture->block;
360 pt->x = x;
361 pt->y = y;
362 pt->width = w;
363 pt->height = h;
364 pt->nblocksx = texture->nblocksx[level];
365 pt->nblocksy = texture->nblocksy[level];
366 pt->stride = ct->stride[level];
367 pt->usage = usage;
368 pt->face = face;
369 pt->level = level;
370 pt->zslice = zslice;
371
372 ctrans->offset = ct->level_offset[level];
373
374 if (texture->target == PIPE_TEXTURE_CUBE) {
375 ctrans->offset += face * pt->nblocksy * pt->stride;
376 }
377 else if (texture->target == PIPE_TEXTURE_3D) {
378 ctrans->offset += zslice * pt->nblocksy * pt->stride;
379 }
380 else {
381 assert(face == 0);
382 assert(zslice == 0);
383 }
384 return pt;
385 }
386 return NULL;
387 }
388
389
390 static void
391 cell_tex_transfer_release(struct pipe_screen *screen,
392 struct pipe_transfer **t)
393 {
394 struct cell_transfer *transfer = cell_transfer(*t);
395 /* Effectively do the texture_update work here - if texture images
396 * needed post-processing to put them into hardware layout, this is
397 * where it would happen. For cell, nothing to do.
398 */
399 assert (transfer->base.texture);
400 if (--transfer->base.refcount == 0) {
401 pipe_texture_reference(&transfer->base.texture, NULL);
402 FREE(transfer);
403 }
404 *t = NULL;
405 }
406
407
408 /**
409 * Return pointer to texture image data in linear layout.
410 */
411 static void *
412 cell_transfer_map(struct pipe_screen *screen, struct pipe_transfer *transfer)
413 {
414 struct cell_transfer *ctrans = cell_transfer(transfer);
415 struct pipe_texture *pt = transfer->texture;
416 struct cell_texture *ct = cell_texture(pt);
417 const uint level = ctrans->base.level;
418 const uint texWidth = pt->width[level];
419 const uint texHeight = pt->height[level];
420 const uint stride = ct->stride[level];
421 unsigned flags = 0x0;
422 unsigned size;
423
424 assert(transfer->texture);
425
426 if (transfer->usage != PIPE_TRANSFER_READ) {
427 flags |= PIPE_BUFFER_USAGE_CPU_WRITE;
428 }
429
430 if (transfer->usage != PIPE_TRANSFER_WRITE) {
431 flags |= PIPE_BUFFER_USAGE_CPU_READ;
432 }
433
434 if (!ct->mapped) {
435 /* map now */
436 ct->mapped = pipe_buffer_map(screen, ct->buffer, flags);
437 }
438
439 /*
440 * Create a buffer of ordinary memory for the linear texture.
441 * This is the memory that the user will read/write.
442 */
443 size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size;
444
445 ctrans->map = align_malloc(size, 16);
446 if (!ctrans->map)
447 return NULL; /* out of memory */
448
449 if (transfer->usage & PIPE_TRANSFER_READ) {
450 /* need to untwiddle the texture to make a linear version */
451 const uint bpp = pf_get_size(ct->base.format);
452 if (bpp == 4) {
453 const uint *src = (uint *) (ct->mapped + ctrans->offset);
454 uint *dst = ctrans->map;
455 untwiddle_image_uint(texWidth, texHeight, TILE_SIZE,
456 dst, stride, src);
457 }
458 else {
459 // xxx fix
460 }
461 }
462
463 return ctrans->map;
464 }
465
466
467 /**
468 * Called when user is done reading/writing texture data.
469 * If new data was written, this is where we convert the linear data
470 * to tiled data.
471 */
472 static void
473 cell_transfer_unmap(struct pipe_screen *screen,
474 struct pipe_transfer *transfer)
475 {
476 struct cell_transfer *ctrans = cell_transfer(transfer);
477 struct pipe_texture *pt = transfer->texture;
478 struct cell_texture *ct = cell_texture(pt);
479 const uint level = ctrans->base.level;
480 const uint texWidth = pt->width[level];
481 const uint texHeight = pt->height[level];
482 const uint stride = ct->stride[level];
483
484 if (!ct->mapped) {
485 /* map now */
486 ct->mapped = pipe_buffer_map(screen, ct->buffer,
487 PIPE_BUFFER_USAGE_CPU_READ);
488 }
489
490 if (transfer->usage & PIPE_TRANSFER_WRITE) {
491 /* The user wrote new texture data into the mapped buffer.
492 * We need to convert the new linear data into the twiddled/tiled format.
493 */
494 const uint bpp = pf_get_size(ct->base.format);
495 if (bpp == 4) {
496 const uint *src = ctrans->map;
497 uint *dst = (uint *) (ct->mapped + ctrans->offset);
498 twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src);
499 }
500 else {
501 // xxx fix
502 }
503 }
504
505 align_free(ctrans->map);
506 ctrans->map = NULL;
507 }
508
509
510 void
511 cell_init_screen_texture_funcs(struct pipe_screen *screen)
512 {
513 screen->texture_create = cell_texture_create;
514 screen->texture_release = cell_texture_release;
515
516 screen->get_tex_surface = cell_get_tex_surface;
517 screen->tex_surface_release = cell_tex_surface_release;
518
519 screen->get_tex_transfer = cell_get_tex_transfer;
520 screen->tex_transfer_release = cell_tex_transfer_release;
521
522 screen->transfer_map = cell_transfer_map;
523 screen->transfer_unmap = cell_transfer_unmap;
524 }