summaryrefslogtreecommitdiff
path: root/src/gallium/drivers/cell/ppu/cell_texture.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers/cell/ppu/cell_texture.c')
-rw-r--r--src/gallium/drivers/cell/ppu/cell_texture.c660
1 files changed, 660 insertions, 0 deletions
diff --git a/src/gallium/drivers/cell/ppu/cell_texture.c b/src/gallium/drivers/cell/ppu/cell_texture.c
new file mode 100644
index 0000000000..b3042df779
--- /dev/null
+++ b/src/gallium/drivers/cell/ppu/cell_texture.c
@@ -0,0 +1,660 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ **************************************************************************/
+ /*
+ * 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 "util/u_inlines.h"
+#include "util/u_transfer.h"
+#include "util/u_format.h"
+#include "util/u_math.h"
+#include "util/u_memory.h"
+
+#include "cell_context.h"
+#include "cell_screen.h"
+#include "cell_state.h"
+#include "cell_texture.h"
+
+#include "state_tracker/sw_winsys.h"
+
+
+
+static boolean
+cell_resource_layout(struct pipe_screen *screen,
+ struct cell_resource *ct)
+{
+ struct pipe_resource *pt = &ct->base;
+ unsigned level;
+ unsigned width = pt->width0;
+ unsigned height = pt->height0;
+ unsigned depth = pt->depth0;
+
+ ct->buffer_size = 0;
+
+ 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);
+
+ ct->stride[level] = util_format_get_stride(pt->format, w_tile);
+
+ ct->level_offset[level] = ct->buffer_size;
+
+ size = ct->stride[level] * util_format_get_nblocksy(pt->format, h_tile);
+ if (pt->target == PIPE_TEXTURE_CUBE)
+ size *= 6;
+ else
+ size *= depth;
+
+ ct->buffer_size += size;
+
+ width = u_minify(width, 1);
+ height = u_minify(height, 1);
+ depth = u_minify(depth, 1);
+ }
+
+ ct->data = align_malloc(ct->buffer_size, 16);
+
+ return ct->data != NULL;
+}
+
+
+/**
+ * Texture layout for simple color buffers.
+ */
+static boolean
+cell_displaytarget_layout(struct pipe_screen *screen,
+ struct cell_resource * ct)
+{
+ struct sw_winsys *winsys = cell_screen(screen)->winsys;
+
+ /* Round up the surface size to a multiple of the tile size?
+ */
+ ct->dt = winsys->displaytarget_create(winsys,
+ ct->base.bind,
+ ct->base.format,
+ ct->base.width0,
+ ct->base.height0,
+ 16,
+ &ct->dt_stride );
+
+ return ct->dt != NULL;
+}
+
+static struct pipe_resource *
+cell_resource_create(struct pipe_screen *screen,
+ const struct pipe_resource *templat)
+{
+ struct cell_resource *ct = CALLOC_STRUCT(cell_resource);
+ if (!ct)
+ return NULL;
+
+ ct->base = *templat;
+ pipe_reference_init(&ct->base.reference, 1);
+ ct->base.screen = screen;
+
+ /* Create both a displaytarget (linear) and regular texture
+ * (twiddled). Convert twiddled->linear at flush_frontbuffer time.
+ */
+ if (ct->base.bind & (PIPE_BIND_DISPLAY_TARGET |
+ PIPE_BIND_SCANOUT |
+ PIPE_BIND_SHARED)) {
+ if (!cell_displaytarget_layout(screen, ct))
+ goto fail;
+ }
+
+ if (!cell_resource_layout(screen, ct))
+ goto fail;
+
+ return &ct->base;
+
+fail:
+ if (ct->dt) {
+ struct sw_winsys *winsys = cell_screen(screen)->winsys;
+ winsys->displaytarget_destroy(winsys, ct->dt);
+ }
+
+ FREE(ct);
+
+ return NULL;
+}
+
+
+static void
+cell_resource_destroy(struct pipe_screen *scrn, struct pipe_resource *pt)
+{
+ struct cell_screen *screen = cell_screen(scrn);
+ struct sw_winsys *winsys = screen->winsys;
+ struct cell_resource *ct = cell_resource(pt);
+
+ if (ct->dt) {
+ /* display target */
+ winsys->displaytarget_destroy(winsys, ct->dt);
+ }
+ else if (!ct->userBuffer) {
+ align_free(ct->data);
+ }
+
+ FREE(ct);
+}
+
+
+
+/**
+ * Convert image from linear layout to tiled layout. 4-byte pixels.
+ */
+static void
+twiddle_image_uint(uint w, uint h, uint tile_size, uint *dst,
+ uint src_stride, const uint *src)
+{
+ 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;
+
+ uint it, jt; /* tile counters */
+ uint i, j; /* intra-tile counters */
+
+ src_stride /= 4; /* convert from bytes to pixels */
+
+ /* 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;
+
+ /* 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_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];
+ }
+ }
+ }
+ }
+}
+
+
+/**
+ * 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];
+ }
+ }
+}
+
+
+/**
+ * Convert image from tiled layout to linear layout. 4-byte pixels.
+ */
+static void
+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 - 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 */
+
+ 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 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_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);
+}
+
+
+static struct pipe_surface *
+cell_get_tex_surface(struct pipe_screen *screen,
+ struct pipe_resource *pt,
+ unsigned face, unsigned level, unsigned zslice,
+ unsigned usage)
+{
+ struct cell_resource *ct = cell_resource(pt);
+ struct pipe_surface *ps;
+
+ ps = CALLOC_STRUCT(pipe_surface);
+ if (ps) {
+ pipe_reference_init(&ps->reference, 1);
+ pipe_resource_reference(&ps->texture, pt);
+ ps->format = pt->format;
+ ps->width = u_minify(pt->width0, level);
+ ps->height = u_minify(pt->height0, 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;
+
+ if (pt->target == PIPE_TEXTURE_CUBE) {
+ unsigned h_tile = align(ps->height, TILE_SIZE);
+ ps->offset += face * util_format_get_nblocksy(ps->format, h_tile) * ct->stride[level];
+ }
+ else if (pt->target == PIPE_TEXTURE_3D) {
+ unsigned h_tile = align(ps->height, TILE_SIZE);
+ ps->offset += zslice * util_format_get_nblocksy(ps->format, h_tile) * ct->stride[level];
+ }
+ else {
+ assert(face == 0);
+ assert(zslice == 0);
+ }
+ }
+ return ps;
+}
+
+
+static void
+cell_tex_surface_destroy(struct pipe_surface *surf)
+{
+ pipe_resource_reference(&surf->texture, NULL);
+ FREE(surf);
+}
+
+
+/**
+ * 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_transfer(struct pipe_context *ctx,
+ struct pipe_resource *resource,
+ struct pipe_subresource sr,
+ unsigned usage,
+ const struct pipe_box *box)
+{
+ struct cell_resource *ct = cell_resource(resource);
+ struct cell_transfer *ctrans;
+ enum pipe_format format = resource->format;
+
+ assert(resource);
+ assert(sr.level <= resource->last_level);
+
+ /* make sure the requested region is in the image bounds */
+ assert(box->x + box->width <= u_minify(resource->width0, sr.level));
+ assert(box->y + box->height <= u_minify(resource->height0, sr.level));
+ assert(box->z + box->depth <= u_minify(resource->depth0, sr.level));
+
+ ctrans = CALLOC_STRUCT(cell_transfer);
+ if (ctrans) {
+ struct pipe_transfer *pt = &ctrans->base;
+ pipe_resource_reference(&pt->resource, resource);
+ pt->sr = sr;
+ pt->usage = usage;
+ pt->box = *box;
+ pt->stride = ct->stride[sr.level];
+
+ ctrans->offset = ct->level_offset[sr.level];
+
+ if (resource->target == PIPE_TEXTURE_CUBE) {
+ unsigned h_tile = align(u_minify(resource->height0, sr.level), TILE_SIZE);
+ ctrans->offset += sr.face * util_format_get_nblocksy(format, h_tile) * pt->stride;
+ }
+ else if (resource->target == PIPE_TEXTURE_3D) {
+ unsigned h_tile = align(u_minify(resource->height0, sr.level), TILE_SIZE);
+ ctrans->offset += box->z * util_format_get_nblocksy(format, h_tile) * pt->stride;
+ }
+ else {
+ assert(sr.face == 0);
+ assert(box->z == 0);
+ }
+
+ return pt;
+ }
+ return NULL;
+}
+
+
+static void
+cell_transfer_destroy(struct pipe_context *ctx, 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 cell, nothing to do.
+ */
+ assert (transfer->base.resource);
+ pipe_resource_reference(&transfer->base.resource, NULL);
+ FREE(transfer);
+}
+
+
+/**
+ * Return pointer to texture image data in linear layout.
+ */
+static void *
+cell_transfer_map(struct pipe_context *ctx, struct pipe_transfer *transfer)
+{
+ struct cell_transfer *ctrans = cell_transfer(transfer);
+ struct pipe_resource *pt = transfer->resource;
+ struct cell_resource *ct = cell_resource(pt);
+
+ assert(transfer->resource);
+
+ if (ct->mapped == NULL) {
+ ct->mapped = ct->data;
+ }
+
+
+ /* Better test would be resource->is_linear
+ */
+ if (transfer->resource->target != PIPE_BUFFER) {
+ const uint level = ctrans->base.sr.level;
+ const uint texWidth = u_minify(pt->width0, level);
+ const uint texHeight = u_minify(pt->height0, level);
+ unsigned size;
+
+
+ /*
+ * Create a buffer of ordinary memory for the linear texture.
+ * This is the memory that the user will read/write.
+ */
+ size = (util_format_get_stride(pt->format, align(texWidth, TILE_SIZE)) *
+ util_format_get_nblocksy(pt->format, align(texHeight, TILE_SIZE)));
+
+ ctrans->map = align_malloc(size, 16);
+ if (!ctrans->map)
+ return NULL; /* out of memory */
+
+ if (transfer->usage & PIPE_TRANSFER_READ) {
+ /* Textures always stored twiddled, need to untwiddle the
+ * texture to make a linear version.
+ */
+ const uint bpp = util_format_get_blocksize(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, transfer->stride, src);
+ }
+ else {
+ // xxx fix
+ }
+ }
+ }
+ else {
+ unsigned stride = transfer->stride;
+ enum pipe_format format = pt->format;
+ unsigned blocksize = util_format_get_blocksize(format);
+
+ ctrans->map = (ct->mapped +
+ ctrans->offset +
+ ctrans->base.box.y / util_format_get_blockheight(format) * stride +
+ ctrans->base.box.x / util_format_get_blockwidth(format) * blocksize);
+ }
+
+
+ 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_transfer_unmap(struct pipe_context *ctx,
+ struct pipe_transfer *transfer)
+{
+ struct cell_transfer *ctrans = cell_transfer(transfer);
+ struct pipe_resource *pt = transfer->resource;
+ struct cell_resource *ct = cell_resource(pt);
+ const uint level = ctrans->base.sr.level;
+ const uint texWidth = u_minify(pt->width0, level);
+ const uint texHeight = u_minify(pt->height0, level);
+ const uint stride = ct->stride[level];
+
+ if (!ct->mapped) {
+ assert(0);
+ return;
+ }
+
+ if (pt->target != PIPE_BUFFER) {
+ 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 = util_format_get_blocksize(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
+ }
+ }
+
+ align_free(ctrans->map);
+ }
+ else {
+ /* nothing to do */
+ }
+
+ ctrans->map = NULL;
+}
+
+
+
+/* This used to be overriden by the co-state tracker, but really needs
+ * to be active with sw_winsys.
+ *
+ * Contrasting with llvmpipe and softpipe, this is the only place
+ * where we use the ct->dt display target in any real sense.
+ *
+ * Basically just untwiddle our local data into the linear
+ * displaytarget.
+ */
+static void
+cell_flush_frontbuffer(struct pipe_screen *_screen,
+ struct pipe_surface *surface,
+ void *context_private)
+{
+ struct cell_screen *screen = cell_screen(_screen);
+ struct sw_winsys *winsys = screen->winsys;
+ struct cell_resource *ct = cell_resource(surface->texture);
+
+ if (!ct->dt)
+ return;
+
+ /* Need to untwiddle from our internal representation here:
+ */
+ {
+ unsigned *map = winsys->displaytarget_map(winsys, ct->dt,
+ (PIPE_TRANSFER_READ |
+ PIPE_TRANSFER_WRITE));
+ unsigned *src = (unsigned *)(ct->data + ct->level_offset[surface->level]);
+
+ untwiddle_image_uint(surface->width,
+ surface->height,
+ TILE_SIZE,
+ map,
+ ct->dt_stride,
+ src);
+
+ winsys->displaytarget_unmap(winsys, ct->dt);
+ }
+
+ winsys->displaytarget_display(winsys, ct->dt, context_private);
+}
+
+
+
+/**
+ * Create buffer which wraps user-space data.
+ */
+static struct pipe_resource *
+cell_user_buffer_create(struct pipe_screen *screen,
+ void *ptr,
+ unsigned bytes,
+ unsigned bind_flags)
+{
+ struct cell_resource *buffer;
+
+ buffer = CALLOC_STRUCT(cell_resource);
+ if(!buffer)
+ return NULL;
+
+ pipe_reference_init(&buffer->base.reference, 1);
+ buffer->base.screen = screen;
+ buffer->base.format = PIPE_FORMAT_R8_UNORM; /* ?? */
+ buffer->base.bind = PIPE_BIND_TRANSFER_READ | bind_flags;
+ buffer->base.usage = PIPE_USAGE_IMMUTABLE;
+ buffer->base.flags = 0;
+ buffer->base.width0 = bytes;
+ buffer->base.height0 = 1;
+ buffer->base.depth0 = 1;
+ buffer->userBuffer = TRUE;
+ buffer->data = ptr;
+
+ return &buffer->base;
+}
+
+
+static struct pipe_resource *
+cell_resource_from_handle(struct pipe_screen *screen,
+ const struct pipe_resource *templat,
+ struct winsys_handle *handle)
+{
+ /* XXX todo */
+ return NULL;
+}
+
+
+static boolean
+cell_resource_get_handle(struct pipe_screen *scree,
+ struct pipe_resource *tex,
+ struct winsys_handle *handle)
+{
+ /* XXX todo */
+ return FALSE;
+}
+
+
+void
+cell_init_screen_texture_funcs(struct pipe_screen *screen)
+{
+ screen->resource_create = cell_resource_create;
+ screen->resource_destroy = cell_resource_destroy;
+ screen->resource_from_handle = cell_resource_from_handle;
+ screen->resource_get_handle = cell_resource_get_handle;
+ screen->user_buffer_create = cell_user_buffer_create;
+
+ screen->get_tex_surface = cell_get_tex_surface;
+ screen->tex_surface_destroy = cell_tex_surface_destroy;
+
+ screen->flush_frontbuffer = cell_flush_frontbuffer;
+}
+
+void
+cell_init_texture_transfer_funcs(struct cell_context *cell)
+{
+ cell->pipe.get_transfer = cell_get_transfer;
+ cell->pipe.transfer_destroy = cell_transfer_destroy;
+ cell->pipe.transfer_map = cell_transfer_map;
+ cell->pipe.transfer_unmap = cell_transfer_unmap;
+
+ cell->pipe.transfer_flush_region = u_default_transfer_flush_region;
+ cell->pipe.transfer_inline_write = u_default_transfer_inline_write;
+}