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