blob: bf80e18233f2aac8eeb1b3d4c849401be7a5148a [file] [log] [blame]
/**************************************************************************
*
* 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>
*/
#include "pipe/p_state.h"
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_inlines.h"
#include "pipe/p_util.h"
#include "pipe/p_winsys.h"
#include "i915_context.h"
#include "i915_texture.h"
#include "i915_debug.h"
static unsigned minify( unsigned d )
{
return MAX2(1, d>>1);
}
static void
i915_miptree_set_level_info(struct i915_texture *tex,
unsigned level,
unsigned nr_images,
unsigned x, unsigned y, 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;
tex->level_offset[level] = (x + y * tex->pitch) * pt->cpp;
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] = (x + y * tex->pitch);
/*
DBG("%s level %d img %d pos %d,%d image_offset %x\n",
__FUNCTION__, level, img, x, y, tex->image_offset[level][img]);
*/
}
static void
i945_miptree_layout_2d( struct i915_texture *tex )
{
struct pipe_texture *pt = &tex->base;
int align_h = 2, align_w = 4;
unsigned level;
unsigned x = 0;
unsigned y = 0;
unsigned width = pt->width[0];
unsigned height = pt->height[0];
tex->pitch = pt->width[0];
/* May need to adjust pitch to accomodate the placement of
* the 2nd mipmap. This occurs when the alignment
* constraints of mipmap placement push the right edge of the
* 2nd mipmap out past the width of its parent.
*/
if (pt->first_level != pt->last_level) {
unsigned mip1_width = align_int(minify(pt->width[0]), align_w)
+ minify(minify(pt->width[0]));
if (mip1_width > pt->width[0])
tex->pitch = mip1_width;
}
/* Pitch must be a whole number of dwords, even though we
* express it in texels.
*/
tex->pitch = align_int(tex->pitch * pt->cpp, 4) / pt->cpp;
tex->total_height = 0;
for ( level = pt->first_level ; level <= pt->last_level ; level++ ) {
unsigned img_height;
i915_miptree_set_level_info(tex, level, 1, x, y, width, height, 1);
if (pt->compressed)
img_height = MAX2(1, height/4);
else
img_height = align_int(height, align_h);
/* Because the images are packed better, the final offset
* might not be the maximal one:
*/
tex->total_height = MAX2(tex->total_height, y + img_height);
/* Layout_below: step right after second mipmap.
*/
if (level == pt->first_level + 1) {
x += align_int(width, align_w);
}
else {
y += img_height;
}
width = minify(width);
height = minify(height);
}
}
static const int initial_offsets[6][2] = {
{0, 0},
{0, 2},
{1, 0},
{1, 2},
{1, 1},
{1, 3}
};
static const int step_offsets[6][2] = {
{0, 2},
{0, 2},
{-1, 2},
{-1, 2},
{-1, 1},
{-1, 1}
};
static boolean
i915_miptree_layout(struct pipe_context *pipe, struct i915_texture * tex)
{
struct pipe_texture *pt = &tex->base;
unsigned level;
switch (pt->target) {
case PIPE_TEXTURE_CUBE: {
const unsigned dim = pt->width[0];
unsigned face;
unsigned lvlWidth = pt->width[0], lvlHeight = pt->height[0];
assert(lvlWidth == lvlHeight); /* cubemap images are square */
/* double pitch for cube layouts */
tex->pitch = ((dim * pt->cpp * 2 + 3) & ~3) / pt->cpp;
tex->total_height = dim * 4;
for (level = pt->first_level; level <= pt->last_level; level++) {
i915_miptree_set_level_info(tex, level, 6,
0, 0,
/*OLD: tex->pitch, tex->total_height,*/
lvlWidth, lvlHeight,
1);
lvlWidth /= 2;
lvlHeight /= 2;
}
for (face = 0; face < 6; face++) {
unsigned x = initial_offsets[face][0] * dim;
unsigned y = initial_offsets[face][1] * dim;
unsigned d = dim;
for (level = pt->first_level; 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;
}
}
break;
}
case PIPE_TEXTURE_3D:{
unsigned width = pt->width[0];
unsigned height = pt->height[0];
unsigned depth = pt->depth[0];
unsigned stack_height = 0;
/* Calculate the size of a single slice.
*/
tex->pitch = ((pt->width[0] * pt->cpp + 3) & ~3) / pt->cpp;
/* XXX: hardware expects/requires 9 levels at minimum.
*/
for (level = pt->first_level; level <= MAX2(8, pt->last_level);
level++) {
i915_miptree_set_level_info(tex, level, depth, 0, tex->total_height,
width, height, depth);
stack_height += MAX2(2, height);
width = minify(width);
height = minify(height);
depth = minify(depth);
}
/* Fixup depth image_offsets:
*/
depth = pt->depth[0];
for (level = pt->first_level; level <= pt->last_level; level++) {
unsigned i;
for (i = 0; i < depth; i++)
i915_miptree_set_image_offset(tex, level, i,
0, i * stack_height);
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_height = stack_height * pt->depth[0];
break;
}
default:{
unsigned width = pt->width[0];
unsigned height = pt->height[0];
unsigned img_height;
tex->pitch = ((pt->width[0] * pt->cpp + 3) & ~3) / pt->cpp;
tex->total_height = 0;
for (level = pt->first_level; level <= pt->last_level; level++) {
i915_miptree_set_level_info(tex, level, 1,
0, tex->total_height,
width, height, 1);
if (pt->compressed)
img_height = MAX2(1, height / 4);
else
img_height = (MAX2(2, height) + 1) & ~1;
tex->total_height += img_height;
width = minify(width);
height = minify(height);
}
break;
}
}
/*
DBG("%s: %dx%dx%d - sz 0x%x\n", __FUNCTION__,
tex->pitch,
tex->total_height, pt->cpp, tex->pitch * tex->total_height * pt->cpp);
*/
return TRUE;
}
static boolean
i945_miptree_layout(struct pipe_context *pipe, struct i915_texture * tex)
{
struct pipe_texture *pt = &tex->base;
unsigned level;
switch (pt->target) {
case PIPE_TEXTURE_CUBE:{
const unsigned dim = pt->width[0];
unsigned face;
unsigned lvlWidth = pt->width[0], lvlHeight = pt->height[0];
assert(lvlWidth == lvlHeight); /* cubemap images are square */
/* 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 (dim > 32)
tex->pitch = ((dim * pt->cpp * 2 + 3) & ~3) / pt->cpp;
else
tex->pitch = 14 * 8;
tex->total_height = dim * 4 + 4;
/* Set all the levels to effectively occupy the whole rectangular region.
*/
for (level = pt->first_level; level <= pt->last_level; level++) {
i915_miptree_set_level_info(tex, level, 6,
0, 0,
lvlWidth, lvlHeight, 1);
lvlWidth /= 2;
lvlHeight /= 2;
}
for (face = 0; face < 6; face++) {
unsigned x = initial_offsets[face][0] * dim;
unsigned y = initial_offsets[face][1] * dim;
unsigned d = dim;
if (dim == 4 && face >= 4) {
y = tex->total_height - 4;
x = (face - 4) * 8;
}
else if (dim < 4 && (face > 0 || pt->first_level > 0)) {
y = tex->total_height - 4;
x = face * 8;
}
for (level = pt->first_level; level <= pt->last_level; level++) {
i915_miptree_set_image_offset(tex, level, face, x, y);
d >>= 1;
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:
x += step_offsets[face][0] * d;
y += step_offsets[face][1] * d;
break;
}
}
}
break;
}
case PIPE_TEXTURE_3D:{
unsigned width = pt->width[0];
unsigned height = pt->height[0];
unsigned depth = pt->depth[0];
unsigned pack_x_pitch, pack_x_nr;
unsigned pack_y_pitch;
unsigned level;
tex->pitch = ((pt->width[0] * pt->cpp + 3) & ~3) / pt->cpp;
tex->total_height = 0;
pack_y_pitch = MAX2(pt->height[0], 2);
pack_x_pitch = tex->pitch;
pack_x_nr = 1;
for (level = pt->first_level; level <= pt->last_level; level++) {
unsigned nr_images = pt->target == PIPE_TEXTURE_3D ? depth : 6;
int x = 0;
int y = 0;
unsigned q, j;
i915_miptree_set_level_info(tex, level, nr_images,
0, tex->total_height,
width, height, depth);
for (q = 0; q < nr_images;) {
for (j = 0; j < pack_x_nr && q < nr_images; j++, q++) {
i915_miptree_set_image_offset(tex, level, q, x, y);
x += pack_x_pitch;
}
x = 0;
y += pack_y_pitch;
}
tex->total_height += y;
if (pack_x_pitch > 4) {
pack_x_pitch >>= 1;
pack_x_nr <<= 1;
assert(pack_x_pitch * pack_x_nr <= tex->pitch);
}
if (pack_y_pitch > 2) {
pack_y_pitch >>= 1;
}
width = minify(width);
height = minify(height);
depth = minify(depth);
}
break;
}
case PIPE_TEXTURE_1D:
case PIPE_TEXTURE_2D:
// case PIPE_TEXTURE_RECTANGLE:
i945_miptree_layout_2d(tex);
break;
default:
assert(0);
return FALSE;
}
/*
DBG("%s: %dx%dx%d - sz 0x%x\n", __FUNCTION__,
tex->pitch,
tex->total_height, pt->cpp, tex->pitch * tex->total_height * pt->cpp);
*/
return TRUE;
}
void
i915_texture_create(struct pipe_context *pipe, struct pipe_texture **pt)
{
struct i915_texture *tex = REALLOC(*pt, sizeof(struct pipe_texture),
sizeof(struct i915_texture));
if (tex) {
struct i915_context *i915 = i915_context(pipe);
memset(&tex->base + 1, 0,
sizeof(struct i915_texture) - sizeof(struct pipe_texture));
if (i915->flags.is_i945 ? i945_miptree_layout(pipe, tex) :
i915_miptree_layout(pipe, tex))
tex->buffer = pipe->winsys->buffer_create(pipe->winsys, 64,
PIPE_BUFFER_USAGE_PIXEL,
tex->pitch * tex->base.cpp *
tex->total_height);
if (!tex->buffer) {
FREE(tex);
tex = NULL;
}
}
*pt = &tex->base;
}
void
i915_texture_release(struct pipe_context *pipe, struct pipe_texture **pt)
{
if (!*pt)
return;
/*
DBG("%s %p refcount will be %d\n",
__FUNCTION__, (void *) *pt, (*pt)->refcount - 1);
*/
if (--(*pt)->refcount <= 0) {
struct i915_texture *tex = (struct i915_texture *)*pt;
uint i;
/*
DBG("%s deleting %p\n", __FUNCTION__, (void *) tex);
*/
pipe->winsys->buffer_reference(pipe->winsys, &tex->buffer, NULL);
for (i = 0; i < PIPE_MAX_TEXTURE_LEVELS; i++)
if (tex->image_offset[i])
FREE(tex->image_offset[i]);
FREE(tex);
}
*pt = NULL;
}