diff options
Diffstat (limited to 'src/gallium/drivers/cell/ppu/cell_texture.c')
-rw-r--r-- | src/gallium/drivers/cell/ppu/cell_texture.c | 513 |
1 files changed, 325 insertions, 188 deletions
diff --git a/src/gallium/drivers/cell/ppu/cell_texture.c b/src/gallium/drivers/cell/ppu/cell_texture.c index b6590dfb86..74724393f7 100644 --- a/src/gallium/drivers/cell/ppu/cell_texture.c +++ b/src/gallium/drivers/cell/ppu/cell_texture.c @@ -28,12 +28,13 @@ * Authors: * Keith Whitwell <keith@tungstengraphics.com> * Michel Dänzer <michel@tungstengraphics.com> + * Brian Paul */ #include "pipe/p_context.h" #include "pipe/p_defines.h" #include "pipe/p_inlines.h" -#include "pipe/p_winsys.h" +#include "pipe/internal/p_winsys_screen.h" #include "util/u_math.h" #include "util/u_memory.h" @@ -42,30 +43,31 @@ #include "cell_texture.h" -/* Simple, maximally packed layout. - */ -static unsigned minify( unsigned d ) +static unsigned +minify(unsigned d) { return MAX2(1, d>>1); } static void -cell_texture_layout(struct cell_texture * spt) +cell_texture_layout(struct cell_texture *ct) { - struct pipe_texture *pt = &spt->base; + struct pipe_texture *pt = &ct->base; unsigned level; unsigned width = pt->width[0]; unsigned height = pt->height[0]; unsigned depth = pt->depth[0]; - spt->buffer_size = 0; + ct->buffer_size = 0; - for ( level = 0 ; level <= pt->last_level ; level++ ) { + for (level = 0; level <= pt->last_level; level++) { unsigned size; unsigned w_tile, h_tile; + assert(level < CELL_MAX_TEXTURE_LEVELS); + /* width, height, rounded up to tile size */ w_tile = align(width, TILE_SIZE); h_tile = align(height, TILE_SIZE); @@ -76,9 +78,9 @@ cell_texture_layout(struct cell_texture * spt) pt->nblocksx[level] = pf_get_nblocksx(&pt->block, w_tile); pt->nblocksy[level] = pf_get_nblocksy(&pt->block, h_tile); - spt->stride[level] = pt->nblocksx[level] * pt->block.size; + ct->stride[level] = pt->nblocksx[level] * pt->block.size; - spt->level_offset[level] = spt->buffer_size; + ct->level_offset[level] = ct->buffer_size; size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size; if (pt->target == PIPE_TEXTURE_CUBE) @@ -86,9 +88,9 @@ cell_texture_layout(struct cell_texture * spt) else size *= depth; - spt->buffer_size += size; + ct->buffer_size += size; - width = minify(width); + width = minify(width); height = minify(height); depth = minify(depth); } @@ -100,26 +102,25 @@ cell_texture_create(struct pipe_screen *screen, const struct pipe_texture *templat) { struct pipe_winsys *ws = screen->winsys; - struct cell_texture *spt = CALLOC_STRUCT(cell_texture); - if (!spt) + struct cell_texture *ct = CALLOC_STRUCT(cell_texture); + if (!ct) return NULL; - spt->base = *templat; - spt->base.refcount = 1; - spt->base.screen = screen; + ct->base = *templat; + ct->base.refcount = 1; + ct->base.screen = screen; - cell_texture_layout(spt); + cell_texture_layout(ct); - spt->buffer = ws->buffer_create(ws, 32, - PIPE_BUFFER_USAGE_PIXEL, - spt->buffer_size); + ct->buffer = ws->buffer_create(ws, 32, PIPE_BUFFER_USAGE_PIXEL, + ct->buffer_size); - if (!spt->buffer) { - FREE(spt); + if (!ct->buffer) { + FREE(ct); return NULL; } - return &spt->base; + return &ct->base; } @@ -130,246 +131,379 @@ cell_texture_release(struct pipe_screen *screen, if (!*pt) return; - /* - DBG("%s %p refcount will be %d\n", - __FUNCTION__, (void *) *pt, (*pt)->refcount - 1); - */ if (--(*pt)->refcount <= 0) { - struct cell_texture *spt = cell_texture(*pt); + /* Delete this texture now. + * But note that the underlying pipe_buffer may linger... + */ + struct cell_texture *ct = cell_texture(*pt); - /* - DBG("%s deleting %p\n", __FUNCTION__, (void *) spt); - */ + if (ct->mapped) { + pipe_buffer_unmap(screen, ct->buffer); + ct->mapped = NULL; + } - pipe_buffer_reference(screen, &spt->buffer, NULL); + pipe_buffer_reference(screen, &ct->buffer, NULL); - FREE(spt); + FREE(ct); } *pt = NULL; } -#if 0 + +/** + * Convert image from linear layout to tiled layout. 4-byte pixels. + */ static void -cell_texture_update(struct pipe_context *pipe, struct pipe_texture *texture, - uint face, uint levelsMask) +twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst, + uint src_stride, const uint *src) { - /* XXX TO DO: re-tile the texture data ... */ + const uint tile_size2 = tile_size * tile_size; + const uint h_t = (h + tile_size - 1) / tile_size; + const uint w_t = (w + tile_size - 1) / tile_size; -} -#endif + uint it, jt; /* tile counters */ + uint i, j; /* intra-tile counters */ + src_stride /= 4; /* convert from bytes to pixels */ -static struct pipe_surface * -cell_get_tex_surface(struct pipe_screen *screen, - struct pipe_texture *pt, - unsigned face, unsigned level, unsigned zslice, - unsigned usage) -{ - struct pipe_winsys *ws = screen->winsys; - struct cell_texture *spt = cell_texture(pt); - struct pipe_surface *ps; + /* loop over dest tiles */ + for (it = 0; it < h_t; it++) { + for (jt = 0; jt < w_t; jt++) { + /* start of dest tile: */ + uint *tdst = dst + (it * w_t + jt) * tile_size2; - ps = ws->surface_alloc(ws); - if (ps) { - assert(ps->refcount); - assert(ps->winsys); - winsys_buffer_reference(ws, &ps->buffer, spt->buffer); - ps->format = pt->format; - ps->block = pt->block; - ps->width = pt->width[level]; - ps->height = pt->height[level]; - ps->nblocksx = pt->nblocksx[level]; - ps->nblocksy = pt->nblocksy[level]; - ps->stride = spt->stride[level]; - ps->offset = spt->level_offset[level]; - ps->usage = usage; + /* compute size of this tile (may be smaller than tile_size) */ + /* XXX note: a compiler bug was found here. That's why the code + * looks as it does. + */ + uint tile_width = w - jt * tile_size; + tile_width = MIN2(tile_width, tile_size); + uint tile_height = h - it * tile_size; + tile_height = MIN2(tile_height, tile_size); - /* XXX may need to override usage flags (see sp_texture.c) */ + /* loop over texels in the tile */ + for (i = 0; i < tile_height; i++) { + for (j = 0; j < tile_width; j++) { + const uint srci = it * tile_size + i; + const uint srcj = jt * tile_size + j; + ASSERT(srci < h); + ASSERT(srcj < w); + tdst[i * tile_size + j] = src[srci * src_stride + srcj]; + } + } + } + } +} - pipe_texture_reference(&ps->texture, pt); - ps->face = face; - ps->level = level; - ps->zslice = zslice; - if (pt->target == PIPE_TEXTURE_CUBE || pt->target == PIPE_TEXTURE_3D) { - ps->offset += ((pt->target == PIPE_TEXTURE_CUBE) ? face : zslice) * - ps->nblocksy * - ps->stride; - } - else { - assert(face == 0); - assert(zslice == 0); +/** + * For Cell. Basically, rearrange the pixels/quads from this layout: + * +--+--+--+--+ + * |p0|p1|p2|p3|.... + * +--+--+--+--+ + * + * to this layout: + * +--+--+ + * |p0|p1|.... + * +--+--+ + * |p2|p3| + * +--+--+ + */ +static void +twiddle_tile(const uint *tileIn, uint *tileOut) +{ + int y, x; + + for (y = 0; y < TILE_SIZE; y+=2) { + for (x = 0; x < TILE_SIZE; x+=2) { + int k = 4 * (y/2 * TILE_SIZE/2 + x/2); + tileOut[y * TILE_SIZE + (x + 0)] = tileIn[k]; + tileOut[y * TILE_SIZE + (x + 1)] = tileIn[k+1]; + tileOut[(y + 1) * TILE_SIZE + (x + 0)] = tileIn[k+2]; + tileOut[(y + 1) * TILE_SIZE + (x + 1)] = tileIn[k+3]; } } - return ps; } - /** - * Copy tile data from linear layout to tiled layout. - * XXX this should be rolled into the future surface-creation code. - * XXX also need "untile" code... + * Convert image from tiled layout to linear layout. 4-byte pixels. */ static void -tile_copy_data(uint w, uint h, uint tile_size, uint *dst, const uint *src) +untwiddle_image_uint(uint w, uint h, uint tile_size, uint *dst, + uint dst_stride, const uint *src) { const uint tile_size2 = tile_size * tile_size; - const uint h_t = h / tile_size, w_t = w / tile_size; - + const uint h_t = (h + tile_size - 1) / tile_size; + const uint w_t = (w + tile_size - 1) / tile_size; + uint *tile_buf; uint it, jt; /* tile counters */ uint i, j; /* intra-tile counters */ - /* loop over dest tiles */ + dst_stride /= 4; /* convert from bytes to pixels */ + + tile_buf = align_malloc(tile_size * tile_size * 4, 16); + + /* loop over src tiles */ for (it = 0; it < h_t; it++) { for (jt = 0; jt < w_t; jt++) { - /* start of dest tile: */ - uint *tdst = dst + (it * w_t + jt) * tile_size2; + /* start of src tile: */ + const uint *tsrc = src + (it * w_t + jt) * tile_size2; + + twiddle_tile(tsrc, tile_buf); + tsrc = tile_buf; + + /* compute size of this tile (may be smaller than tile_size) */ + /* XXX note: a compiler bug was found here. That's why the code + * looks as it does. + */ + uint tile_width = w - jt * tile_size; + tile_width = MIN2(tile_width, tile_size); + uint tile_height = h - it * tile_size; + tile_height = MIN2(tile_height, tile_size); + /* loop over texels in the tile */ - for (i = 0; i < tile_size; i++) { - for (j = 0; j < tile_size; j++) { - const uint srci = it * tile_size + i; - const uint srcj = jt * tile_size + j; - *tdst++ = src[srci * w + srcj]; + for (i = 0; i < tile_height; i++) { + for (j = 0; j < tile_width; j++) { + uint dsti = it * tile_size + i; + uint dstj = jt * tile_size + j; + ASSERT(dsti < h); + ASSERT(dstj < w); + dst[dsti * dst_stride + dstj] = tsrc[i * tile_size + j]; } } } } -} + align_free(tile_buf); +} -/** - * Convert linear texture image data to tiled format for SPU usage. - * XXX recast this in terms of pipe_surfaces (aka texture views). - */ -static void -cell_tile_texture(struct cell_context *cell, - struct cell_texture *texture) +static struct pipe_surface * +cell_get_tex_surface(struct pipe_screen *screen, + struct pipe_texture *pt, + unsigned face, unsigned level, unsigned zslice, + unsigned usage) { - struct pipe_screen *screen = cell->pipe.screen; - uint face = 0, level = 0, zslice = 0; - struct pipe_surface *surf; - const uint w = texture->base.width[0], h = texture->base.height[0]; - const uint *src; - - /* temporary restrictions: */ - assert(w >= TILE_SIZE); - assert(h >= TILE_SIZE); - assert(w % TILE_SIZE == 0); - assert(h % TILE_SIZE == 0); - - surf = screen->get_tex_surface(screen, &texture->base, face, level, zslice, - PIPE_BUFFER_USAGE_CPU_WRITE); - ASSERT(surf); - - src = (const uint *) pipe_surface_map(surf, PIPE_BUFFER_USAGE_CPU_WRITE); - - if (texture->tiled_data) { - align_free(texture->tiled_data); - } - texture->tiled_data = align_malloc(w * h * 4, 16); - - tile_copy_data(w, h, TILE_SIZE, texture->tiled_data, src); + struct cell_texture *ct = cell_texture(pt); + struct pipe_surface *ps; - pipe_surface_unmap(surf); + ps = CALLOC_STRUCT(pipe_surface); + if (ps) { + ps->refcount = 1; + pipe_texture_reference(&ps->texture, pt); + ps->format = pt->format; + ps->width = pt->width[level]; + ps->height = pt->height[level]; + ps->offset = ct->level_offset[level]; + /* XXX may need to override usage flags (see sp_texture.c) */ + ps->usage = usage; + ps->face = face; + ps->level = level; + ps->zslice = zslice; - pipe_surface_reference(&surf, NULL); + if (pt->target == PIPE_TEXTURE_CUBE) { + ps->offset += face * pt->nblocksy[level] * ct->stride[level]; + } + else if (pt->target == PIPE_TEXTURE_3D) { + ps->offset += zslice * pt->nblocksy[level] * ct->stride[level]; + } + else { + assert(face == 0); + assert(zslice == 0); + } + } + return ps; } -void -cell_update_texture_mapping(struct cell_context *cell) +static void +cell_tex_surface_release(struct pipe_screen *screen, + struct pipe_surface **s) { -#if 0 - uint face = 0, level = 0, zslice = 0; -#endif - uint i; - - for (i = 0; i < CELL_MAX_SAMPLERS; i++) { - if (cell->texture[i]) - cell_tile_texture(cell, cell->texture[i]); - } + struct pipe_surface *surf = *s; -#if 0 - if (cell->tex_surf && cell->tex_map) { - pipe_surface_unmap(cell->tex_surf); - cell->tex_map = NULL; + if (--surf->refcount == 0) { + pipe_texture_reference(&surf->texture, NULL); + FREE(surf); } + *s = NULL; +} - /* XXX free old surface */ - - cell->tex_surf = cell_get_tex_surface(&cell->pipe, - &cell->texture[0]->base, - face, level, zslice); - cell->tex_map = pipe_surface_map(cell->tex_surf); -#endif +/** + * Create new pipe_transfer object. + * This is used by the user to put tex data into a texture (and get it + * back out for glGetTexImage). + */ +static struct pipe_transfer * +cell_get_tex_transfer(struct pipe_screen *screen, + struct pipe_texture *texture, + unsigned face, unsigned level, unsigned zslice, + enum pipe_transfer_usage usage, + unsigned x, unsigned y, unsigned w, unsigned h) +{ + struct cell_texture *ct = cell_texture(texture); + struct cell_transfer *ctrans; + + assert(texture); + assert(level <= texture->last_level); + + ctrans = CALLOC_STRUCT(cell_transfer); + if (ctrans) { + struct pipe_transfer *pt = &ctrans->base; + pt->refcount = 1; + pipe_texture_reference(&pt->texture, texture); + pt->format = texture->format; + pt->block = texture->block; + pt->x = x; + pt->y = y; + pt->width = w; + pt->height = h; + pt->nblocksx = texture->nblocksx[level]; + pt->nblocksy = texture->nblocksy[level]; + pt->stride = ct->stride[level]; + pt->usage = usage; + pt->face = face; + pt->level = level; + pt->zslice = zslice; + + ctrans->offset = ct->level_offset[level]; + + if (texture->target == PIPE_TEXTURE_CUBE) { + ctrans->offset += face * pt->nblocksy * pt->stride; + } + else if (texture->target == PIPE_TEXTURE_3D) { + ctrans->offset += zslice * pt->nblocksy * pt->stride; + } + else { + assert(face == 0); + assert(zslice == 0); + } + return pt; + } + return NULL; } static void -cell_tex_surface_release(struct pipe_screen *screen, - struct pipe_surface **s) +cell_tex_transfer_release(struct pipe_screen *screen, + struct pipe_transfer **t) { + struct cell_transfer *transfer = cell_transfer(*t); /* Effectively do the texture_update work here - if texture images * needed post-processing to put them into hardware layout, this is - * where it would happen. For softpipe, nothing to do. + * where it would happen. For cell, nothing to do. */ - assert ((*s)->texture); - pipe_texture_reference(&(*s)->texture, NULL); - - screen->winsys->surface_release(screen->winsys, s); + assert (transfer->base.texture); + if (--transfer->base.refcount == 0) { + pipe_texture_reference(&transfer->base.texture, NULL); + FREE(transfer); + } + *t = NULL; } +/** + * Return pointer to texture image data in linear layout. + */ static void * -cell_surface_map( struct pipe_screen *screen, - struct pipe_surface *surface, - unsigned flags ) +cell_transfer_map(struct pipe_screen *screen, struct pipe_transfer *transfer) { - ubyte *map; + struct cell_transfer *ctrans = cell_transfer(transfer); + struct pipe_texture *pt = transfer->texture; + struct cell_texture *ct = cell_texture(pt); + const uint level = ctrans->base.level; + const uint texWidth = pt->width[level]; + const uint texHeight = pt->height[level]; + const uint stride = ct->stride[level]; + unsigned flags = 0x0; + unsigned size; + + assert(transfer->texture); + + if (transfer->usage != PIPE_TRANSFER_READ) { + flags |= PIPE_BUFFER_USAGE_CPU_WRITE; + } - if (flags & ~surface->usage) { - assert(0); - return NULL; + if (transfer->usage != PIPE_TRANSFER_WRITE) { + flags |= PIPE_BUFFER_USAGE_CPU_READ; } - map = pipe_buffer_map( screen, surface->buffer, flags ); - if (map == NULL) - return NULL; + if (!ct->mapped) { + /* map now */ + ct->mapped = pipe_buffer_map(screen, ct->buffer, flags); + } - /* May want to different things here depending on read/write nature - * of the map: + /* + * Create a buffer of ordinary memory for the linear texture. + * This is the memory that the user will read/write. */ - if (surface->texture && - (flags & PIPE_BUFFER_USAGE_CPU_WRITE)) - { - /* Do something to notify sharing contexts of a texture change. - * In softpipe, that would mean flushing the texture cache. - */ -#if 0 - cell_screen(screen)->timestamp++; -#endif + size = pt->nblocksx[level] * pt->nblocksy[level] * pt->block.size; + + ctrans->map = align_malloc(size, 16); + if (!ctrans->map) + return NULL; /* out of memory */ + + if (transfer->usage & PIPE_TRANSFER_READ) { + /* need to untwiddle the texture to make a linear version */ + const uint bpp = pf_get_size(ct->base.format); + if (bpp == 4) { + const uint *src = (uint *) (ct->mapped + ctrans->offset); + uint *dst = ctrans->map; + untwiddle_image_uint(texWidth, texHeight, TILE_SIZE, + dst, stride, src); + } + else { + // xxx fix + } } - - return map + surface->offset; + + return ctrans->map; } +/** + * Called when user is done reading/writing texture data. + * If new data was written, this is where we convert the linear data + * to tiled data. + */ static void -cell_surface_unmap(struct pipe_screen *screen, - struct pipe_surface *surface) +cell_transfer_unmap(struct pipe_screen *screen, + struct pipe_transfer *transfer) { - pipe_buffer_unmap( screen, surface->buffer ); -} + struct cell_transfer *ctrans = cell_transfer(transfer); + struct pipe_texture *pt = transfer->texture; + struct cell_texture *ct = cell_texture(pt); + const uint level = ctrans->base.level; + const uint texWidth = pt->width[level]; + const uint texHeight = pt->height[level]; + const uint stride = ct->stride[level]; + + if (!ct->mapped) { + /* map now */ + ct->mapped = pipe_buffer_map(screen, ct->buffer, + PIPE_BUFFER_USAGE_CPU_READ); + } + if (transfer->usage & PIPE_TRANSFER_WRITE) { + /* The user wrote new texture data into the mapped buffer. + * We need to convert the new linear data into the twiddled/tiled format. + */ + const uint bpp = pf_get_size(ct->base.format); + if (bpp == 4) { + const uint *src = ctrans->map; + uint *dst = (uint *) (ct->mapped + ctrans->offset); + twiddle_image_uint(texWidth, texHeight, TILE_SIZE, dst, stride, src); + } + else { + // xxx fix + } + } -void -cell_init_texture_functions(struct cell_context *cell) -{ - /*cell->pipe.texture_update = cell_texture_update;*/ + align_free(ctrans->map); + ctrans->map = NULL; } @@ -382,6 +516,9 @@ cell_init_screen_texture_funcs(struct pipe_screen *screen) screen->get_tex_surface = cell_get_tex_surface; screen->tex_surface_release = cell_tex_surface_release; - screen->surface_map = cell_surface_map; - screen->surface_unmap = cell_surface_unmap; + screen->get_tex_transfer = cell_get_tex_transfer; + screen->tex_transfer_release = cell_tex_transfer_release; + + screen->transfer_map = cell_transfer_map; + screen->transfer_unmap = cell_transfer_unmap; } |