/************************************************************************** * * 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 * Michel Dänzer */ #include "pipe/p_state.h" #include "pipe/p_context.h" #include "pipe/p_defines.h" #include "pipe/p_inlines.h" #include "pipe/internal/p_winsys_screen.h" #include "util/u_math.h" #include "util/u_memory.h" #include "i915_context.h" #include "i915_texture.h" #include "i915_debug.h" #include "i915_screen.h" #include "intel_winsys.h" /* * Helper function and arrays */ /** * Initial offset for Cube map. */ static const int initial_offsets[6][2] = { {0, 0}, {0, 2}, {1, 0}, {1, 2}, {1, 1}, {1, 3} }; /** * Step offsets for Cube map. */ static const int step_offsets[6][2] = { {0, 2}, {0, 2}, {-1, 2}, {-1, 2}, {-1, 1}, {-1, 1} }; static unsigned power_of_two(unsigned x) { unsigned value = 1; while (value < x) value = value << 1; return value; } static unsigned round_up(unsigned n, unsigned multiple) { return (n + multiple - 1) & ~(multiple - 1); } /* * More advanced helper funcs */ static void i915_miptree_set_level_info(struct i915_texture *tex, unsigned level, unsigned nr_images, unsigned w, unsigned h, unsigned d) { struct pipe_texture *pt = &tex->base; assert(level < PIPE_MAX_TEXTURE_LEVELS); pt->width[level] = w; pt->height[level] = h; pt->depth[level] = d; pt->nblocksx[level] = pf_get_nblocksx(&pt->block, w); pt->nblocksy[level] = pf_get_nblocksy(&pt->block, h); tex->nr_images[level] = nr_images; /* DBG("%s level %d size: %d,%d,%d offset %d,%d (0x%x)\n", __FUNCTION__, level, w, h, d, x, y, tex->level_offset[level]); */ /* Not sure when this would happen, but anyway: */ if (tex->image_offset[level]) { FREE(tex->image_offset[level]); tex->image_offset[level] = NULL; } assert(nr_images); assert(!tex->image_offset[level]); tex->image_offset[level] = (unsigned *) MALLOC(nr_images * sizeof(unsigned)); tex->image_offset[level][0] = 0; } static void i915_miptree_set_image_offset(struct i915_texture *tex, unsigned level, unsigned img, unsigned x, unsigned y) { if (img == 0 && level == 0) assert(x == 0 && y == 0); assert(img < tex->nr_images[level]); tex->image_offset[level][img] = y * tex->stride + x * tex->base.block.size; /* printf("%s level %d img %d pos %d,%d image_offset %x\n", __FUNCTION__, level, img, x, y, tex->image_offset[level][img]); */ } /* * i915 layout functions, some used by i945 */ /** * Special case to deal with scanout textures. */ static boolean i915_scanout_layout(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; if (pt->last_level > 0 || pt->block.size != 4) return FALSE; i915_miptree_set_level_info(tex, 0, 1, tex->base.width[0], tex->base.height[0], 1); i915_miptree_set_image_offset(tex, 0, 0, 0, 0); if (tex->base.width[0] >= 240) { tex->stride = power_of_two(tex->base.nblocksx[0] * pt->block.size); tex->total_nblocksy = round_up(tex->base.nblocksy[0], 8); tex->hw_tiled = INTEL_TILE_X; } else if (tex->base.width[0] == 64 && tex->base.height[0] == 64) { tex->stride = power_of_two(tex->base.nblocksx[0] * pt->block.size); tex->total_nblocksy = round_up(tex->base.nblocksy[0], 8); } else { return FALSE; } debug_printf("%s size: %d,%d,%d offset %d,%d (0x%x)\n", __FUNCTION__, tex->base.width[0], tex->base.height[0], pt->block.size, tex->stride, tex->total_nblocksy, tex->stride * tex->total_nblocksy); return TRUE; } static void i915_miptree_layout_2d(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; unsigned level; unsigned width = pt->width[0]; unsigned height = pt->height[0]; unsigned nblocksx = pt->nblocksx[0]; unsigned nblocksy = pt->nblocksy[0]; tex->stride = round_up(pt->nblocksx[0] * pt->block.size, 4); tex->total_nblocksy = 0; for (level = 0; level <= pt->last_level; level++) { i915_miptree_set_level_info(tex, level, 1, width, height, 1); i915_miptree_set_image_offset(tex, level, 0, 0, tex->total_nblocksy); nblocksy = round_up(MAX2(2, nblocksy), 2); tex->total_nblocksy += nblocksy; width = minify(width); height = minify(height); nblocksx = pf_get_nblocksx(&pt->block, width); nblocksy = pf_get_nblocksy(&pt->block, height); } } static void i915_miptree_layout_3d(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; unsigned level; unsigned width = pt->width[0]; unsigned height = pt->height[0]; unsigned depth = pt->depth[0]; unsigned nblocksx = pt->nblocksx[0]; unsigned nblocksy = pt->nblocksy[0]; unsigned stack_nblocksy = 0; /* Calculate the size of a single slice. */ tex->stride = round_up(pt->nblocksx[0] * pt->block.size, 4); /* XXX: hardware expects/requires 9 levels at minimum. */ for (level = 0; level <= MAX2(8, pt->last_level); level++) { i915_miptree_set_level_info(tex, level, depth, width, height, depth); stack_nblocksy += MAX2(2, nblocksy); width = minify(width); height = minify(height); depth = minify(depth); nblocksx = pf_get_nblocksx(&pt->block, width); nblocksy = pf_get_nblocksy(&pt->block, height); } /* Fixup depth image_offsets: */ depth = pt->depth[0]; for (level = 0; level <= pt->last_level; level++) { unsigned i; for (i = 0; i < depth; i++) i915_miptree_set_image_offset(tex, level, i, 0, i * stack_nblocksy); depth = minify(depth); } /* Multiply slice size by texture depth for total size. It's * remarkable how wasteful of memory the i915 texture layouts * are. They are largely fixed in the i945. */ tex->total_nblocksy = stack_nblocksy * pt->depth[0]; } static void i915_miptree_layout_cube(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; unsigned width = pt->width[0], height = pt->height[0]; const unsigned nblocks = pt->nblocksx[0]; unsigned level; unsigned face; assert(width == height); /* cubemap images are square */ /* double pitch for cube layouts */ tex->stride = round_up(nblocks * pt->block.size * 2, 4); tex->total_nblocksy = nblocks * 4; for (level = 0; level <= pt->last_level; level++) { i915_miptree_set_level_info(tex, level, 6, width, height, 1); width /= 2; height /= 2; } for (face = 0; face < 6; face++) { unsigned x = initial_offsets[face][0] * nblocks; unsigned y = initial_offsets[face][1] * nblocks; unsigned d = nblocks; for (level = 0; level <= pt->last_level; level++) { i915_miptree_set_image_offset(tex, level, face, x, y); d >>= 1; x += step_offsets[face][0] * d; y += step_offsets[face][1] * d; } } } static boolean i915_miptree_layout(struct i915_texture * tex) { struct pipe_texture *pt = &tex->base; switch (pt->target) { case PIPE_TEXTURE_1D: case PIPE_TEXTURE_2D: i915_miptree_layout_2d(tex); break; case PIPE_TEXTURE_3D: i915_miptree_layout_3d(tex); break; case PIPE_TEXTURE_CUBE: i915_miptree_layout_cube(tex); break; default: assert(0); return FALSE; } return TRUE; } /* * i945 layout functions */ static void i945_miptree_layout_2d(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; const int align_x = 2, align_y = 4; unsigned level; unsigned x = 0; unsigned y = 0; unsigned width = pt->width[0]; unsigned height = pt->height[0]; unsigned nblocksx = pt->nblocksx[0]; unsigned nblocksy = pt->nblocksy[0]; /* used for scanouts that need special layouts */ if (tex->base.tex_usage & PIPE_TEXTURE_USAGE_PRIMARY) if (i915_scanout_layout(tex)) return; tex->stride = round_up(pt->nblocksx[0] * pt->block.size, 4); /* May need to adjust pitch to accomodate the placement of * the 2nd mipmap level. This occurs when the alignment * constraints of mipmap placement push the right edge of the * 2nd mipmap level out past the width of its parent. */ if (pt->last_level > 0) { unsigned mip1_nblocksx = align(pf_get_nblocksx(&pt->block, minify(width)), align_x) + pf_get_nblocksx(&pt->block, minify(minify(width))); if (mip1_nblocksx > nblocksx) tex->stride = mip1_nblocksx * pt->block.size; } /* Pitch must be a whole number of dwords */ tex->stride = align(tex->stride, 64); tex->total_nblocksy = 0; for (level = 0; level <= pt->last_level; level++) { i915_miptree_set_level_info(tex, level, 1, width, height, 1); i915_miptree_set_image_offset(tex, level, 0, x, y); nblocksy = align(nblocksy, align_y); /* Because the images are packed better, the final offset * might not be the maximal one: */ tex->total_nblocksy = MAX2(tex->total_nblocksy, y + nblocksy); /* Layout_below: step right after second mipmap level. */ if (level == 1) { x += align(nblocksx, align_x); } else { y += nblocksy; } width = minify(width); height = minify(height); nblocksx = pf_get_nblocksx(&pt->block, width); nblocksy = pf_get_nblocksy(&pt->block, height); } } static void i945_miptree_layout_3d(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; unsigned width = pt->width[0]; unsigned height = pt->height[0]; unsigned depth = pt->depth[0]; unsigned nblocksx = pt->nblocksx[0]; unsigned nblocksy = pt->nblocksy[0]; unsigned pack_x_pitch, pack_x_nr; unsigned pack_y_pitch; unsigned level; tex->stride = round_up(pt->nblocksx[0] * pt->block.size, 4); tex->total_nblocksy = 0; pack_y_pitch = MAX2(pt->nblocksy[0], 2); pack_x_pitch = tex->stride / pt->block.size; pack_x_nr = 1; for (level = 0; level <= pt->last_level; level++) { int x = 0; int y = 0; unsigned q, j; i915_miptree_set_level_info(tex, level, depth, width, height, depth); for (q = 0; q < depth;) { for (j = 0; j < pack_x_nr && q < depth; j++, q++) { i915_miptree_set_image_offset(tex, level, q, x, y + tex->total_nblocksy); x += pack_x_pitch; } x = 0; y += pack_y_pitch; } tex->total_nblocksy += y; if (pack_x_pitch > 4) { pack_x_pitch >>= 1; pack_x_nr <<= 1; assert(pack_x_pitch * pack_x_nr * pt->block.size <= tex->stride); } if (pack_y_pitch > 2) { pack_y_pitch >>= 1; } width = minify(width); height = minify(height); depth = minify(depth); nblocksx = pf_get_nblocksx(&pt->block, width); nblocksy = pf_get_nblocksy(&pt->block, height); } } static void i945_miptree_layout_cube(struct i915_texture *tex) { struct pipe_texture *pt = &tex->base; unsigned level; const unsigned nblocks = pt->nblocksx[0]; unsigned face; unsigned width = pt->width[0]; unsigned height = pt->height[0]; /* printf("%s %i, %i\n", __FUNCTION__, pt->width[0], pt->height[0]); */ assert(width == height); /* cubemap images are square */ /* * XXX Should only be used for compressed formats. But lets * keep this code active just in case. * * Depending on the size of the largest images, pitch can be * determined either by the old-style packing of cubemap faces, * or the final row of 4x4, 2x2 and 1x1 faces below this. */ if (nblocks > 32) tex->stride = round_up(nblocks * pt->block.size * 2, 4); else tex->stride = 14 * 8 * pt->block.size; tex->total_nblocksy = nblocks * 4; /* Set all the levels to effectively occupy the whole rectangular region. */ for (level = 0; level <= pt->last_level; level++) { i915_miptree_set_level_info(tex, level, 6, width, height, 1); width /= 2; height /= 2; } for (face = 0; face < 6; face++) { unsigned x = initial_offsets[face][0] * nblocks; unsigned y = initial_offsets[face][1] * nblocks; unsigned d = nblocks; #if 0 /* Fix and enable this code for compressed formats */ if (nblocks == 4 && face >= 4) { y = tex->total_height - 4; x = (face - 4) * 8; } else if (nblocks < 4 && (face > 0)) { y = tex->total_height - 4; x = face * 8; } #endif for (level = 0; level <= pt->last_level; level++) { i915_miptree_set_image_offset(tex, level, face, x, y); d >>= 1; #if 0 /* Fix and enable this code for compressed formats */ switch (d) { case 4: switch (face) { case PIPE_TEX_FACE_POS_X: case PIPE_TEX_FACE_NEG_X: x += step_offsets[face][0] * d; y += step_offsets[face][1] * d; break; case PIPE_TEX_FACE_POS_Y: case PIPE_TEX_FACE_NEG_Y: y += 12; x -= 8; break; case PIPE_TEX_FACE_POS_Z: case PIPE_TEX_FACE_NEG_Z: y = tex->total_height - 4; x = (face - 4) * 8; break; } case 2: y = tex->total_height - 4; x = 16 + face * 8; break; case 1: x += 48; break; default: #endif x += step_offsets[face][0] * d; y += step_offsets[face][1] * d; #if 0 break; } #endif } } } static boolean i945_miptree_layout(struct i915_texture * tex) { struct pipe_texture *pt = &tex->base; switch (pt->target) { case PIPE_TEXTURE_1D: case PIPE_TEXTURE_2D: i945_miptree_layout_2d(tex); break; case PIPE_TEXTURE_3D: i945_miptree_layout_3d(tex); break; case PIPE_TEXTURE_CUBE: i945_miptree_layout_cube(tex); break; default: assert(0); return FALSE; } return TRUE; } /* * Screen texture functions */ static struct pipe_texture * i915_texture_create(struct pipe_screen *screen, const struct pipe_texture *templat) { struct i915_screen *is = i915_screen(screen); struct intel_winsys *iws = is->iws; struct i915_texture *tex = CALLOC_STRUCT(i915_texture); size_t tex_size; unsigned buf_usage = 0; if (!tex) return NULL; tex->base = *templat; pipe_reference_init(&tex->base.reference, 1); tex->base.screen = screen; tex->base.nblocksx[0] = pf_get_nblocksx(&tex->base.block, tex->base.width[0]); tex->base.nblocksy[0] = pf_get_nblocksy(&tex->base.block, tex->base.height[0]); if (is->is_i945) { if (!i945_miptree_layout(tex)) goto fail; } else { if (!i915_miptree_layout(tex)) goto fail; } tex_size = tex->stride * tex->total_nblocksy; /* for scanouts and cursors, cursors arn't scanouts */ if (templat->tex_usage & PIPE_TEXTURE_USAGE_PRIMARY && templat->width[0] != 64) buf_usage = INTEL_NEW_SCANOUT; else buf_usage = INTEL_NEW_TEXTURE; tex->buffer = iws->buffer_create(iws, tex_size, 64, buf_usage); if (!tex->buffer) goto fail; /* setup any hw fences */ if (tex->hw_tiled) { assert(tex->sw_tiled == INTEL_TILE_NONE); iws->buffer_set_fence_reg(iws, tex->buffer, tex->stride, tex->hw_tiled); } #if 0 void *ptr = ws->buffer_map(ws, tex->buffer, PIPE_BUFFER_USAGE_CPU_WRITE); memset(ptr, 0x80, tex_size); ws->buffer_unmap(ws, tex->buffer); #endif return &tex->base; fail: FREE(tex); return NULL; } static struct pipe_texture * i915_texture_blanket(struct pipe_screen * screen, const struct pipe_texture *base, const unsigned *stride, struct pipe_buffer *buffer) { #if 0 struct i915_texture *tex; assert(screen); /* Only supports one type */ if (base->target != PIPE_TEXTURE_2D || base->last_level != 0 || base->depth[0] != 1) { return NULL; } tex = CALLOC_STRUCT(i915_texture); if (!tex) return NULL; tex->base = *base; pipe_reference_init(&tex->base.reference, 1); tex->base.screen = screen; tex->stride = stride[0]; i915_miptree_set_level_info(tex, 0, 1, base->width[0], base->height[0], 1); i915_miptree_set_image_offset(tex, 0, 0, 0, 0); pipe_buffer_reference(&tex->buffer, buffer); return &tex->base; #else return NULL; #endif } static void i915_texture_destroy(struct pipe_texture *pt) { struct i915_texture *tex = (struct i915_texture *)pt; struct intel_winsys *iws = i915_screen(pt->screen)->iws; uint i; /* DBG("%s deleting %p\n", __FUNCTION__, (void *) tex); */ iws->buffer_destroy(iws, tex->buffer); for (i = 0; i < PIPE_MAX_TEXTURE_LEVELS; i++) if (tex->image_offset[i]) FREE(tex->image_offset[i]); FREE(tex); } /* * Screen surface functions */ static struct pipe_surface * i915_get_tex_surface(struct pipe_screen *screen, struct pipe_texture *pt, unsigned face, unsigned level, unsigned zslice, unsigned flags) { struct i915_texture *tex = (struct i915_texture *)pt; struct pipe_surface *ps; unsigned offset; /* in bytes */ if (pt->target == PIPE_TEXTURE_CUBE) { offset = tex->image_offset[level][face]; } else if (pt->target == PIPE_TEXTURE_3D) { offset = tex->image_offset[level][zslice]; } else { offset = tex->image_offset[level][0]; assert(face == 0); assert(zslice == 0); } ps = CALLOC_STRUCT(pipe_surface); if (ps) { pipe_reference_init(&ps->reference, 1); pipe_texture_reference(&ps->texture, pt); ps->format = pt->format; ps->width = pt->width[level]; ps->height = pt->height[level]; ps->offset = offset; ps->usage = flags; } return ps; } static void i915_tex_surface_destroy(struct pipe_surface *surf) { pipe_texture_reference(&surf->texture, NULL); FREE(surf); } /* * Screen transfer functions */ static struct pipe_transfer* i915_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 i915_texture *tex = (struct i915_texture *)texture; struct i915_transfer *trans; unsigned offset; /* in bytes */ if (texture->target == PIPE_TEXTURE_CUBE) { offset = tex->image_offset[level][face]; } else if (texture->target == PIPE_TEXTURE_3D) { offset = tex->image_offset[level][zslice]; } else { offset = tex->image_offset[level][0]; assert(face == 0); assert(zslice == 0); } trans = CALLOC_STRUCT(i915_transfer); if (trans) { pipe_texture_reference(&trans->base.texture, texture); trans->base.format = trans->base.format; trans->base.x = x; trans->base.y = y; trans->base.width = w; trans->base.height = h; trans->base.block = texture->block; trans->base.nblocksx = texture->nblocksx[level]; trans->base.nblocksy = texture->nblocksy[level]; trans->base.stride = tex->stride; trans->offset = offset; trans->base.usage = usage; } return &trans->base; } static void * i915_transfer_map(struct pipe_screen *screen, struct pipe_transfer *transfer) { struct i915_texture *tex = (struct i915_texture *)transfer->texture; struct intel_winsys *iws = i915_screen(tex->base.screen)->iws; char *map; boolean write = FALSE; if (transfer->usage != PIPE_TRANSFER_READ) write = TRUE; map = iws->buffer_map(iws, tex->buffer, write); if (map == NULL) return NULL; return map + i915_transfer(transfer)->offset + transfer->y / transfer->block.height * transfer->stride + transfer->x / transfer->block.width * transfer->block.size; } static void i915_transfer_unmap(struct pipe_screen *screen, struct pipe_transfer *transfer) { struct i915_texture *tex = (struct i915_texture *)transfer->texture; struct intel_winsys *iws = i915_screen(tex->base.screen)->iws; iws->buffer_unmap(iws, tex->buffer); } static void i915_tex_transfer_destroy(struct pipe_transfer *trans) { pipe_texture_reference(&trans->texture, NULL); FREE(trans); } /* * Other texture functions */ void i915_init_screen_texture_functions(struct i915_screen *is) { is->base.texture_create = i915_texture_create; is->base.texture_blanket = i915_texture_blanket; is->base.texture_destroy = i915_texture_destroy; is->base.get_tex_surface = i915_get_tex_surface; is->base.tex_surface_destroy = i915_tex_surface_destroy; is->base.get_tex_transfer = i915_get_tex_transfer; is->base.transfer_map = i915_transfer_map; is->base.transfer_unmap = i915_transfer_unmap; is->base.tex_transfer_destroy = i915_tex_transfer_destroy; } struct pipe_texture * i915_texture_blanket_intel(struct pipe_screen *screen, struct pipe_texture *base, unsigned stride, struct intel_buffer *buffer) { struct i915_texture *tex; assert(screen); /* Only supports one type */ if (base->target != PIPE_TEXTURE_2D || base->last_level != 0 || base->depth[0] != 1) { return NULL; } tex = CALLOC_STRUCT(i915_texture); if (!tex) return NULL; tex->base = *base; pipe_reference_init(&tex->base.reference, 1); tex->base.screen = screen; tex->stride = stride; i915_miptree_set_level_info(tex, 0, 1, base->width[0], base->height[0], 1); i915_miptree_set_image_offset(tex, 0, 0, 0, 0); tex->buffer = buffer; return &tex->base; } boolean i915_get_texture_buffer_intel(struct pipe_texture *texture, struct intel_buffer **buffer, unsigned *stride) { struct i915_texture *tex = (struct i915_texture *)texture; if (!texture) return FALSE; *stride = tex->stride; *buffer = tex->buffer; return TRUE; }