/**************************************************************************
*
* 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 "util/u_inlines.h"
#include "util/u_format.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_rect.h"
#include "i915_context.h"
#include "i915_resource.h"
#include "i915_screen.h"
#include "i915_winsys.h"
#include "i915_debug.h"
#define DEBUG_TEXTURES 0
/*
* Helper function and arrays
*/
/**
* Initial offset for Cube map.
*/
static const int initial_offsets[6][2] = {
[PIPE_TEX_FACE_POS_X] = {0, 0},
[PIPE_TEX_FACE_POS_Y] = {1, 0},
[PIPE_TEX_FACE_POS_Z] = {1, 1},
[PIPE_TEX_FACE_NEG_X] = {0, 2},
[PIPE_TEX_FACE_NEG_Y] = {1, 2},
[PIPE_TEX_FACE_NEG_Z] = {1, 3},
};
/**
* Step offsets for Cube map.
*/
static const int step_offsets[6][2] = {
[PIPE_TEX_FACE_POS_X] = { 0, 2},
[PIPE_TEX_FACE_POS_Y] = {-1, 2},
[PIPE_TEX_FACE_POS_Z] = {-1, 1},
[PIPE_TEX_FACE_NEG_X] = { 0, 2},
[PIPE_TEX_FACE_NEG_Y] = {-1, 2},
[PIPE_TEX_FACE_NEG_Z] = {-1, 1},
};
/**
* For compressed level 2
*/
static const int bottom_offsets[6] = {
[PIPE_TEX_FACE_POS_X] = 16 + 0 * 8,
[PIPE_TEX_FACE_POS_Y] = 16 + 1 * 8,
[PIPE_TEX_FACE_POS_Z] = 16 + 2 * 8,
[PIPE_TEX_FACE_NEG_X] = 16 + 3 * 8,
[PIPE_TEX_FACE_NEG_Y] = 16 + 4 * 8,
[PIPE_TEX_FACE_NEG_Z] = 16 + 5 * 8,
};
static INLINE unsigned
align_nblocksx(enum pipe_format format, unsigned width, unsigned align_to)
{
return align(util_format_get_nblocksx(format, width), align_to);
}
static INLINE unsigned
align_nblocksy(enum pipe_format format, unsigned width, unsigned align_to)
{
return align(util_format_get_nblocksy(format, width), align_to);
}
static INLINE unsigned
get_pot_stride(enum pipe_format format, unsigned width)
{
return util_next_power_of_two(util_format_get_stride(format, width));
}
static INLINE const char*
get_tiling_string(enum i915_winsys_buffer_tile tile)
{
switch(tile) {
case I915_TILE_NONE:
return "none";
case I915_TILE_X:
return "x";
case I915_TILE_Y:
return "y";
default:
assert(FALSE);
return "?";
}
}
/*
* More advanced helper funcs
*/
static void
i915_texture_set_level_info(struct i915_texture *tex,
unsigned level, unsigned nr_images)
{
assert(level < Elements(tex->nr_images));
assert(nr_images);
assert(!tex->image_offset[level]);
tex->nr_images[level] = nr_images;
tex->image_offset[level] = MALLOC(nr_images * sizeof(struct offset_pair));
tex->image_offset[level][0].nblocksx = 0;
tex->image_offset[level][0].nblocksy = 0;
}
INLINE unsigned i915_texture_offset(struct i915_texture *tex,
unsigned level, unsigned layer)
{
unsigned x, y;
x = tex->image_offset[level][layer].nblocksx
* util_format_get_blocksize(tex->b.b.format);
y = tex->image_offset[level][layer].nblocksy;
return y * tex->stride + x;
}
static void
i915_texture_set_image_offset(struct i915_texture *tex,
unsigned level, unsigned img,
unsigned nblocksx, unsigned nblocksy)
{
/* for the first image and level make sure offset is zero */
assert(!(img == 0 && level == 0) || (nblocksx == 0 && nblocksy == 0));
assert(img < tex->nr_images[level]);
tex->image_offset[level][img].nblocksx = nblocksx;
tex->image_offset[level][img].nblocksy = nblocksy;
#if DEBUG_TEXTURES
debug_printf("%s: %p level %u, img %u (%u, %u)\n", __FUNCTION__,
tex, level, img, x, y);
#endif
}
static enum i915_winsys_buffer_tile
i915_texture_tiling(struct i915_screen *is, struct i915_texture *tex)
{
if (!is->debug.tiling)
return I915_TILE_NONE;
if (tex->b.b.target == PIPE_TEXTURE_1D)
return I915_TILE_NONE;
if (util_format_is_s3tc(tex->b.b.format))
return I915_TILE_X;
if (is->debug.use_blitter)
return I915_TILE_X;
else
return I915_TILE_Y;
}
/*
* Shared layout functions
*/
/**
* Special case to deal with scanout textures.
*/
static boolean
i9x5_scanout_layout(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
if (pt->last_level > 0 || util_format_get_blocksize(pt->format) != 4)
return FALSE;
i915_texture_set_level_info(tex, 0, 1);
i915_texture_set_image_offset(tex, 0, 0, 0, 0);
if (pt->width0 >= 240) {
tex->stride = align(util_format_get_stride(pt->format, pt->width0), 64);
tex->total_nblocksy = align_nblocksy(pt->format, pt->height0, 8);
tex->tiling = I915_TILE_X;
/* special case for cursors */
} else if (pt->width0 == 64 && pt->height0 == 64) {
tex->stride = get_pot_stride(pt->format, pt->width0);
tex->total_nblocksy = align_nblocksy(pt->format, pt->height0, 8);
} else {
return FALSE;
}
#if DEBUG_TEXTURE
debug_printf("%s size: %d,%d,%d offset %d,%d (0x%x)\n", __FUNCTION__,
pt->width0, pt->height0, util_format_get_blocksize(pt->format),
tex->stride, tex->total_nblocksy, tex->stride * tex->total_nblocksy);
#endif
return TRUE;
}
/**
* Special case to deal with shared textures.
*/
static boolean
i9x5_display_target_layout(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
if (pt->last_level > 0 || util_format_get_blocksize(pt->format) != 4)
return FALSE;
/* fallback to normal textures for small textures */
if (pt->width0 < 240)
return FALSE;
i915_texture_set_level_info(tex, 0, 1);
i915_texture_set_image_offset(tex, 0, 0, 0, 0);
tex->stride = align(util_format_get_stride(pt->format, pt->width0), 64);
tex->total_nblocksy = align_nblocksy(pt->format, pt->height0, 8);
tex->tiling = I915_TILE_X;
#if DEBUG_TEXTURE
debug_printf("%s size: %d,%d,%d offset %d,%d (0x%x)\n", __FUNCTION__,
pt->width0, pt->height0, util_format_get_blocksize(pt->format),
tex->stride, tex->total_nblocksy, tex->stride * tex->total_nblocksy);
#endif
return TRUE;
}
/**
* Helper function for special layouts
*/
static boolean
i9x5_special_layout(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
/* Scanouts needs special care */
if (pt->bind & PIPE_BIND_SCANOUT)
if (i9x5_scanout_layout(tex))
return TRUE;
/* Shared buffers needs to be compatible with X servers
*
* XXX: need a better name than shared for this if it is to be part
* of core gallium, and probably move the flag to resource.flags,
* rather than bindings.
*/
if (pt->bind & (PIPE_BIND_SHARED | PIPE_BIND_DISPLAY_TARGET))
if (i9x5_display_target_layout(tex))
return TRUE;
return FALSE;
}
/**
* Cube layout used on i915 and for non-compressed textures on i945.
*/
static void
i9x5_texture_layout_cube(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
const unsigned nblocks = util_format_get_nblocksx(pt->format, pt->width0);
unsigned level;
unsigned face;
assert(pt->width0 == pt->height0); /* cubemap images are square */
/* double pitch for cube layouts */
tex->stride = align(nblocks * util_format_get_blocksize(pt->format) * 2, 4);
tex->total_nblocksy = nblocks * 4;
for (level = 0; level <= pt->last_level; level++)
i915_texture_set_level_info(tex, level, 6);
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_texture_set_image_offset(tex, level, face, x, y);
d >>= 1;
x += step_offsets[face][0] * d;
y += step_offsets[face][1] * d;
}
}
}
/*
* i915 layout functions
*/
static void
i915_texture_layout_2d(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
unsigned level;
unsigned width = pt->width0;
unsigned height = pt->height0;
unsigned nblocksy = util_format_get_nblocksy(pt->format, pt->width0);
unsigned align_y = 2;
if (util_format_is_s3tc(pt->format))
align_y = 1;
tex->stride = align(util_format_get_stride(pt->format, pt->width0), 4);
tex->total_nblocksy = 0;
for (level = 0; level <= pt->last_level; level++) {
i915_texture_set_level_info(tex, level, 1);
i915_texture_set_image_offset(tex, level, 0, 0, tex->total_nblocksy);
tex->total_nblocksy += nblocksy;
width = u_minify(width, 1);
height = u_minify(height, 1);
nblocksy = align_nblocksy(pt->format, height, align_y);
}
}
static void
i915_texture_layout_3d(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
unsigned level;
unsigned width = pt->width0;
unsigned height = pt->height0;
unsigned depth = pt->depth0;
unsigned nblocksy = util_format_get_nblocksy(pt->format, pt->height0);
unsigned stack_nblocksy = 0;
/* Calculate the size of a single slice.
*/
tex->stride = align(util_format_get_stride(pt->format, pt->width0), 4);
/* XXX: hardware expects/requires 9 levels at minimum.
*/
for (level = 0; level <= MAX2(8, pt->last_level); level++) {
i915_texture_set_level_info(tex, level, depth);
stack_nblocksy += MAX2(2, nblocksy);
width = u_minify(width, 1);
height = u_minify(height, 1);
nblocksy = util_format_get_nblocksy(pt->format, height);
}
/* Fixup depth image_offsets:
*/
for (level = 0; level <= pt->last_level; level++) {
unsigned i;
for (i = 0; i < depth; i++)
i915_texture_set_image_offset(tex, level, i, 0, i * stack_nblocksy);
depth = u_minify(depth, 1);
}
/* 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->depth0;
}
static boolean
i915_texture_layout(struct i915_texture * tex)
{
switch (tex->b.b.target) {
case PIPE_TEXTURE_1D:
case PIPE_TEXTURE_2D:
case PIPE_TEXTURE_RECT:
if (!i9x5_special_layout(tex))
i915_texture_layout_2d(tex);
break;
case PIPE_TEXTURE_3D:
i915_texture_layout_3d(tex);
break;
case PIPE_TEXTURE_CUBE:
i9x5_texture_layout_cube(tex);
break;
default:
assert(0);
return FALSE;
}
return TRUE;
}
/*
* i945 layout functions
*/
static void
i945_texture_layout_2d(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
int align_x = 4, align_y = 2;
unsigned level;
unsigned x = 0;
unsigned y = 0;
unsigned width = pt->width0;
unsigned height = pt->height0;
unsigned nblocksx = util_format_get_nblocksx(pt->format, pt->width0);
unsigned nblocksy = util_format_get_nblocksy(pt->format, pt->height0);
if (util_format_is_s3tc(pt->format)) {
align_x = 1;
align_y = 1;
}
tex->stride = align(util_format_get_stride(pt->format, pt->width0), 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_nblocksx(pt->format, u_minify(pt->width0, 1), align_x) +
util_format_get_nblocksx(pt->format, u_minify(pt->width0, 2));
if (mip1_nblocksx > nblocksx)
tex->stride = mip1_nblocksx * util_format_get_blocksize(pt->format);
}
/* 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_texture_set_level_info(tex, level, 1);
i915_texture_set_image_offset(tex, level, 0, x, 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 += nblocksx;
} else {
y += nblocksy;
}
width = u_minify(width, 1);
height = u_minify(height, 1);
nblocksx = align_nblocksx(pt->format, width, align_x);
nblocksy = align_nblocksy(pt->format, height, align_y);
}
}
static void
i945_texture_layout_3d(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
unsigned width = pt->width0;
unsigned height = pt->height0;
unsigned depth = pt->depth0;
unsigned nblocksy = util_format_get_nblocksy(pt->format, pt->width0);
unsigned pack_x_pitch, pack_x_nr;
unsigned pack_y_pitch;
unsigned level;
tex->stride = align(util_format_get_stride(pt->format, pt->width0), 4);
tex->total_nblocksy = 0;
pack_y_pitch = MAX2(nblocksy, 2);
pack_x_pitch = tex->stride / util_format_get_blocksize(pt->format);
pack_x_nr = 1;
for (level = 0; level <= pt->last_level; level++) {
int x = 0;
int y = 0;
unsigned q, j;
i915_texture_set_level_info(tex, level, depth);
for (q = 0; q < depth;) {
for (j = 0; j < pack_x_nr && q < depth; j++, q++) {
i915_texture_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 * util_format_get_blocksize(pt->format) <= tex->stride);
}
if (pack_y_pitch > 2) {
pack_y_pitch >>= 1;
}
width = u_minify(width, 1);
height = u_minify(height, 1);
depth = u_minify(depth, 1);
nblocksy = util_format_get_nblocksy(pt->format, height);
}
}
static void
i945_texture_layout_cube(struct i915_texture *tex)
{
struct pipe_resource *pt = &tex->b.b;
const unsigned nblocks = util_format_get_nblocksx(pt->format, pt->width0);
const unsigned dim = pt->width0;
unsigned level;
unsigned face;
assert(pt->width0 == pt->height0); /* cubemap images are square */
assert(util_next_power_of_two(pt->width0) == pt->width0); /* npot only */
assert(util_format_is_s3tc(pt->format)); /* compressed only */
/*
* 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.
*
* 64 * 2 / 4 = 32
* 14 * 2 = 28
*/
if (pt->width0 >= 64)
tex->stride = nblocks * 2 * util_format_get_blocksize(pt->format);
else
tex->stride = 14 * 2 * util_format_get_blocksize(pt->format);
/*
* Something similary apply for height as well.
*/
if (pt->width0 >= 4)
tex->total_nblocksy = nblocks * 4 + 1;
else
tex->total_nblocksy = 1;
/* Set all the levels to effectively occupy the whole rectangular region */
for (level = 0; level <= pt->last_level; level++)
i915_texture_set_level_info(tex, level, 6);
for (face = 0; face < 6; face++) {
/* all calculations in pixels */
unsigned total_height = tex->total_nblocksy * 4;
unsigned x = initial_offsets[face][0] * dim;
unsigned y = initial_offsets[face][1] * dim;
unsigned d = dim;
if (dim == 4 && face >= 4) {
x = (face - 4) * 8;
y = tex->total_nblocksy * 4 - 4; /* 4 = 1 block */
} else if (dim < 4 && (face > 0)) {
x = face * 8;
y = total_height - 4;
}
for (level = 0; level <= pt->last_level; level++) {
i915_texture_set_image_offset(tex, level, face,
util_format_get_nblocksx(pt->format, x),
util_format_get_nblocksy(pt->format, 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 = total_height - 4;
x = (face - 4) * 8;
break;
}
break;
case 2:
y = total_height - 4;
x = bottom_offsets[face];
break;
case 1:
x += 48;
break;
default:
x += step_offsets[face][0] * d;
y += step_offsets[face][1] * d;
break;
}
}
}
}
static boolean
i945_texture_layout(struct i915_texture * tex)
{
switch (tex->b.b.target) {
case PIPE_TEXTURE_1D:
case PIPE_TEXTURE_2D:
case PIPE_TEXTURE_RECT:
if (!i9x5_special_layout(tex))
i945_texture_layout_2d(tex);
break;
case PIPE_TEXTURE_3D:
i945_texture_layout_3d(tex);
break;
case PIPE_TEXTURE_CUBE:
if (!util_format_is_s3tc(tex->b.b.format))
i9x5_texture_layout_cube(tex);
else
i945_texture_layout_cube(tex);
break;
default:
assert(0);
return FALSE;
}
return TRUE;
}
/*
* Screen texture functions
*/
static boolean
i915_texture_get_handle(struct pipe_screen * screen,
struct pipe_resource *texture,
struct winsys_handle *whandle)
{
struct i915_screen *is = i915_screen(screen);
struct i915_texture *tex = i915_texture(texture);
struct i915_winsys *iws = is->iws;
return iws->buffer_get_handle(iws, tex->buffer, whandle, tex->stride);
}
static void
i915_texture_destroy(struct pipe_screen *screen,
struct pipe_resource *pt)
{
struct i915_texture *tex = i915_texture(pt);
struct i915_winsys *iws = i915_screen(screen)->iws;
uint i;
if (tex->buffer)
iws->buffer_destroy(iws, tex->buffer);
for (i = 0; i < Elements(tex->image_offset); i++)
if (tex->image_offset[i])
FREE(tex->image_offset[i]);
FREE(tex);
}
static struct pipe_transfer *
i915_texture_get_transfer(struct pipe_context *pipe,
struct pipe_resource *resource,
unsigned level,
unsigned usage,
const struct pipe_box *box)
{
struct i915_context *i915 = i915_context(pipe);
struct i915_texture *tex = i915_texture(resource);
struct i915_transfer *transfer = util_slab_alloc(&i915->texture_transfer_pool);
boolean use_staging_texture = FALSE;
if (transfer == NULL)
return NULL;
transfer->b.resource = resource;
transfer->b.level = level;
transfer->b.usage = usage;
transfer->b.box = *box;
transfer->b.stride = tex->stride;
transfer->staging_texture = NULL;
/* XXX: handle depth textures everyhwere*/
transfer->b.layer_stride = 0;
transfer->b.data = NULL;
/* if we use staging transfers, only support textures we can render to,
* because we need that for u_blitter */
if (i915->blitter &&
util_blitter_is_copy_supported(i915->blitter, resource, resource,
PIPE_MASK_RGBAZS) &&
(usage & PIPE_TRANSFER_WRITE) &&
!(usage & (PIPE_TRANSFER_READ | PIPE_TRANSFER_DONTBLOCK | PIPE_TRANSFER_UNSYNCHRONIZED)))
use_staging_texture = TRUE;
use_staging_texture = FALSE;
if (use_staging_texture) {
/*
* Allocate the untiled staging texture.
* If the alloc fails, transfer->staging_texture is NULL and we fallback to a map()
*/
transfer->staging_texture = i915_texture_create(pipe->screen, resource, TRUE);
}
return (struct pipe_transfer*)transfer;
}
static void
i915_transfer_destroy(struct pipe_context *pipe,
struct pipe_transfer *transfer)
{
struct i915_context *i915 = i915_context(pipe);
struct i915_transfer *itransfer = (struct i915_transfer*)transfer;
if ((itransfer->staging_texture) &&
(transfer->usage & PIPE_TRANSFER_WRITE)) {
struct pipe_box sbox;
u_box_origin_2d(itransfer->b.box.width, itransfer->b.box.height, &sbox);
pipe->resource_copy_region(pipe, itransfer->b.resource, itransfer->b.level,
itransfer->b.box.x, itransfer->b.box.y, itransfer->b.box.z,
itransfer->staging_texture,
0, &sbox);
pipe->flush(pipe, NULL);
pipe_resource_reference(&itransfer->staging_texture, NULL);
}
util_slab_free(&i915->texture_transfer_pool, itransfer);
}
static void *
i915_texture_transfer_map(struct pipe_context *pipe,
struct pipe_transfer *transfer)
{
struct i915_transfer *itransfer = (struct i915_transfer*)transfer;
struct pipe_resource *resource = itransfer->b.resource;
struct i915_texture *tex = NULL;
struct i915_winsys *iws = i915_screen(pipe->screen)->iws;
struct pipe_box *box = &itransfer->b.box;
enum pipe_format format = resource->format;
unsigned offset;
char *map;
if (resource->target != PIPE_TEXTURE_3D &&
resource->target != PIPE_TEXTURE_CUBE)
assert(box->z == 0);
if (itransfer->staging_texture) {
tex = i915_texture(itransfer->staging_texture);
} else {
/* TODO this is a sledgehammer */
tex = i915_texture(resource);
pipe->flush(pipe, NULL);
}
offset = i915_texture_offset(tex, itransfer->b.level, box->z);
map = iws->buffer_map(iws, tex->buffer,
(itransfer->b.usage & PIPE_TRANSFER_WRITE) ? TRUE : FALSE);
if (map == NULL) {
return NULL;
}
return map + offset +
box->y / util_format_get_blockheight(format) * itransfer->b.stride +
box->x / util_format_get_blockwidth(format) * util_format_get_blocksize(format);
}
static void
i915_texture_transfer_unmap(struct pipe_context *pipe,
struct pipe_transfer *transfer)
{
struct i915_transfer *itransfer = (struct i915_transfer*)transfer;
struct i915_texture *tex = i915_texture(itransfer->b.resource);
struct i915_winsys *iws = i915_screen(tex->b.b.screen)->iws;
if (itransfer->staging_texture)
tex = i915_texture(itransfer->staging_texture);
iws->buffer_unmap(iws, tex->buffer);
}
static void i915_transfer_inline_write( struct pipe_context *pipe,
struct pipe_resource *resource,
unsigned level,
unsigned usage,
const struct pipe_box *box,
const void *data,
unsigned stride,
unsigned layer_stride)
{
struct pipe_transfer *transfer = NULL;
struct i915_transfer *itransfer = NULL;
const uint8_t *src_data = data;
unsigned i;
transfer = pipe->get_transfer(pipe,
resource,
level,
usage,
box );
if (transfer == NULL)
goto out;
itransfer = (struct i915_transfer*)transfer;
if (itransfer->staging_texture) {
struct i915_texture *tex = i915_texture(itransfer->staging_texture);
enum pipe_format format = tex->b.b.format;
struct i915_winsys *iws = i915_screen(tex->b.b.screen)->iws;
size_t offset;
size_t size;
offset = i915_texture_offset(tex, transfer->level, transfer->box.z);
for (i = 0; i < box->depth; i++) {
if (!tex->b.b.last_level &&
tex->b.b.width0 == transfer->box.width) {
unsigned nby = util_format_get_nblocksy(format, transfer->box.y);
assert(!offset);
assert(!transfer->box.x);
assert(tex->stride == transfer->stride);
offset += tex->stride * nby;
size = util_format_get_2d_size(format, transfer->stride,
transfer->box.height);
iws->buffer_write(iws, tex->buffer, offset, size, transfer->data);
} else {
unsigned nby = util_format_get_nblocksy(format, transfer->box.y);
int i;
offset += util_format_get_stride(format, transfer->box.x);
size = transfer->stride;
for (i = 0; i < nby; i++) {
iws->buffer_write(iws, tex->buffer, offset, size, transfer->data);
offset += tex->stride;
}
}
offset += layer_stride;
}
} else {
uint8_t *map = pipe_transfer_map(pipe, &itransfer->b);
if (map == NULL)
goto nomap;
for (i = 0; i < box->depth; i++) {
util_copy_rect(map,
resource->format,
itransfer->b.stride, /* bytes */
0, 0,
box->width,
box->height,
src_data,
stride, /* bytes */
0, 0);
map += itransfer->b.layer_stride;
src_data += layer_stride;
}
nomap:
if (map)
pipe_transfer_unmap(pipe, &itransfer->b);
}
out:
if (itransfer)
pipe_transfer_destroy(pipe, &itransfer->b);
}
struct u_resource_vtbl i915_texture_vtbl =
{
i915_texture_get_handle, /* get_handle */
i915_texture_destroy, /* resource_destroy */
i915_texture_get_transfer, /* get_transfer */
i915_transfer_destroy, /* transfer_destroy */
i915_texture_transfer_map, /* transfer_map */
u_default_transfer_flush_region, /* transfer_flush_region */
i915_texture_transfer_unmap, /* transfer_unmap */
i915_transfer_inline_write /* transfer_inline_write */
};
struct pipe_resource *
i915_texture_create(struct pipe_screen *screen,
const struct pipe_resource *template,
boolean force_untiled)
{
struct i915_screen *is = i915_screen(screen);
struct i915_winsys *iws = is->iws;
struct i915_texture *tex = CALLOC_STRUCT(i915_texture);
unsigned buf_usage = 0;
if (!tex)
return NULL;
tex->b.b = *template;
tex->b.vtbl = &i915_texture_vtbl;
pipe_reference_init(&tex->b.b.reference, 1);
tex->b.b.screen = screen;
if ( (force_untiled) || (template->usage == PIPE_USAGE_STREAM) )
tex->tiling = I915_TILE_NONE;
else
tex->tiling = i915_texture_tiling(is, tex);
if (is->is_i945) {
if (!i945_texture_layout(tex))
goto fail;
} else {
if (!i915_texture_layout(tex))
goto fail;
}
/* for scanouts and cursors, cursors arn't scanouts */
/* XXX: use a custom flag for cursors, don't rely on magically
* guessing that this is Xorg asking for a cursor
*/
if ((template->bind & PIPE_BIND_SCANOUT) && template->width0 != 64)
buf_usage = I915_NEW_SCANOUT;
else
buf_usage = I915_NEW_TEXTURE;
tex->buffer = iws->buffer_create_tiled(iws, &tex->stride, tex->total_nblocksy,
&tex->tiling, buf_usage);
if (!tex->buffer)
goto fail;
I915_DBG(DBG_TEXTURE, "%s: %p stride %u, blocks (%u, %u) tiling %s\n", __func__,
tex, tex->stride,
tex->stride / util_format_get_blocksize(tex->b.b.format),
tex->total_nblocksy, get_tiling_string(tex->tiling));
return &tex->b.b;
fail:
FREE(tex);
return NULL;
}
struct pipe_resource *
i915_texture_from_handle(struct pipe_screen * screen,
const struct pipe_resource *template,
struct winsys_handle *whandle)
{
struct i915_screen *is = i915_screen(screen);
struct i915_texture *tex;
struct i915_winsys *iws = is->iws;
struct i915_winsys_buffer *buffer;
unsigned stride;
enum i915_winsys_buffer_tile tiling;
assert(screen);
buffer = iws->buffer_from_handle(iws, whandle, &tiling, &stride);
/* Only supports one type */
if ((template->target != PIPE_TEXTURE_2D &&
template->target != PIPE_TEXTURE_RECT) ||
template->last_level != 0 ||
template->depth0 != 1) {
return NULL;
}
tex = CALLOC_STRUCT(i915_texture);
if (!tex)
return NULL;
tex->b.b = *template;
tex->b.vtbl = &i915_texture_vtbl;
pipe_reference_init(&tex->b.b.reference, 1);
tex->b.b.screen = screen;
tex->stride = stride;
tex->tiling = tiling;
tex->total_nblocksy = align_nblocksy(tex->b.b.format, tex->b.b.height0, 8);
i915_texture_set_level_info(tex, 0, 1);
i915_texture_set_image_offset(tex, 0, 0, 0, 0);
tex->buffer = buffer;
I915_DBG(DBG_TEXTURE, "%s: %p stride %u, blocks (%u, %u) tiling %s\n", __func__,
tex, tex->stride,
tex->stride / util_format_get_blocksize(tex->b.b.format),
tex->total_nblocksy, get_tiling_string(tex->tiling));
return &tex->b.b;
}