summaryrefslogtreecommitdiffstats
path: root/src/gallium/drivers/llvmpipe/lp_texture.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gallium/drivers/llvmpipe/lp_texture.c')
-rw-r--r--src/gallium/drivers/llvmpipe/lp_texture.c648
1 files changed, 601 insertions, 47 deletions
diff --git a/src/gallium/drivers/llvmpipe/lp_texture.c b/src/gallium/drivers/llvmpipe/lp_texture.c
index 61210de8901..7e4e4d5f0be 100644
--- a/src/gallium/drivers/llvmpipe/lp_texture.c
+++ b/src/gallium/drivers/llvmpipe/lp_texture.c
@@ -30,64 +30,101 @@
* Michel Dänzer <[email protected]>
*/
+#include <stdio.h>
+
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
-#include "util/u_inlines.h"
+#include "util/u_inlines.h"
#include "util/u_format.h"
#include "util/u_math.h"
#include "util/u_memory.h"
#include "util/u_transfer.h"
#include "lp_context.h"
-#include "lp_screen.h"
#include "lp_flush.h"
+#include "lp_screen.h"
+#include "lp_tile_image.h"
#include "lp_texture.h"
#include "lp_setup.h"
#include "lp_tile_size.h"
+
#include "state_tracker/sw_winsys.h"
+static INLINE boolean
+resource_is_texture(const struct pipe_resource *resource)
+{
+ const unsigned tex_binds = (PIPE_BIND_DISPLAY_TARGET |
+ PIPE_BIND_SCANOUT |
+ PIPE_BIND_SHARED |
+ PIPE_BIND_DEPTH_STENCIL |
+ PIPE_BIND_SAMPLER_VIEW);
+ const struct llvmpipe_resource *lpt = llvmpipe_resource_const(resource);
+
+ return (lpt->base.bind & tex_binds) ? TRUE : FALSE;
+}
+
+
+
+/**
+ * Allocate storage for llvmpipe_texture::layout array.
+ * The number of elements is width_in_tiles * height_in_tiles.
+ */
+static enum lp_texture_layout *
+alloc_layout_array(unsigned width, unsigned height)
+{
+ const unsigned tx = align(width, TILE_SIZE) / TILE_SIZE;
+ const unsigned ty = align(height, TILE_SIZE) / TILE_SIZE;
+
+ assert(tx * ty > 0);
+ assert(LP_TEX_LAYOUT_NONE == 0); /* calloc'ing LP_TEX_LAYOUT_NONE here */
+
+ return (enum lp_texture_layout *)
+ calloc(tx * ty, sizeof(enum lp_texture_layout));
+}
+
+
+
/**
* Conventional allocation path for non-display textures:
- * Simple, maximally packed layout.
+ * Just compute row strides here. Storage is allocated on demand later.
*/
static boolean
-llvmpipe_resource_layout(struct llvmpipe_screen *screen,
+llvmpipe_texture_layout(struct llvmpipe_screen *screen,
struct llvmpipe_resource *lpt)
{
struct pipe_resource *pt = &lpt->base;
unsigned level;
unsigned width = pt->width0;
unsigned height = pt->height0;
- unsigned depth = pt->depth0;
- unsigned buffer_size = 0;
+
+ assert(LP_MAX_TEXTURE_2D_LEVELS <= LP_MAX_TEXTURE_LEVELS);
+ assert(LP_MAX_TEXTURE_3D_LEVELS <= LP_MAX_TEXTURE_LEVELS);
for (level = 0; level <= pt->last_level; level++) {
- unsigned nblocksx, nblocksy;
+ const unsigned num_faces = lpt->base.target == PIPE_TEXTURE_CUBE ? 6 : 1;
+ unsigned nblocksx, face;
/* Allocate storage for whole quads. This is particularly important
* for depth surfaces, which are currently stored in a swizzled format.
*/
nblocksx = util_format_get_nblocksx(pt->format, align(width, TILE_SIZE));
- nblocksy = util_format_get_nblocksy(pt->format, align(height, TILE_SIZE));
- lpt->stride[level] = align(nblocksx * util_format_get_blocksize(pt->format), 16);
+ lpt->stride[level] =
+ align(nblocksx * util_format_get_blocksize(pt->format), 16);
- lpt->level_offset[level] = buffer_size;
+ lpt->tiles_per_row[level] = align(width, TILE_SIZE) / TILE_SIZE;
- buffer_size += (nblocksy *
- ((pt->target == PIPE_TEXTURE_CUBE) ? 6 : depth) *
- lpt->stride[level]);
+ for (face = 0; face < num_faces; face++) {
+ lpt->layout[face][level] = alloc_layout_array(width, height);
+ }
width = u_minify(width, 1);
height = u_minify(height, 1);
- depth = u_minify(depth, 1);
}
- lpt->data = align_malloc(buffer_size, 16);
-
- return lpt->data != NULL;
+ return TRUE;
}
@@ -104,6 +141,10 @@ llvmpipe_displaytarget_layout(struct llvmpipe_screen *screen,
unsigned width = align(lpt->base.width0, TILE_SIZE);
unsigned height = align(lpt->base.height0, TILE_SIZE);
+ lpt->tiles_per_row[0] = align(width, TILE_SIZE) / TILE_SIZE;
+
+ lpt->layout[0][0] = alloc_layout_array(width, height);
+
lpt->dt = winsys->displaytarget_create(winsys,
lpt->base.bind,
lpt->base.format,
@@ -117,8 +158,9 @@ llvmpipe_displaytarget_layout(struct llvmpipe_screen *screen,
static struct pipe_resource *
llvmpipe_resource_create(struct pipe_screen *_screen,
- const struct pipe_resource *templat)
+ const struct pipe_resource *templat)
{
+ static unsigned id_counter = 0;
struct llvmpipe_screen *screen = llvmpipe_screen(_screen);
struct llvmpipe_resource *lpt = CALLOC_STRUCT(llvmpipe_resource);
if (!lpt)
@@ -128,17 +170,38 @@ llvmpipe_resource_create(struct pipe_screen *_screen,
pipe_reference_init(&lpt->base.reference, 1);
lpt->base.screen = &screen->base;
+ assert(lpt->base.bind);
+
if (lpt->base.bind & (PIPE_BIND_DISPLAY_TARGET |
PIPE_BIND_SCANOUT |
PIPE_BIND_SHARED)) {
+ /* displayable surface */
if (!llvmpipe_displaytarget_layout(screen, lpt))
goto fail;
+ assert(lpt->layout[0][0][0] == LP_TEX_LAYOUT_NONE);
+ }
+ else if (lpt->base.bind & (PIPE_BIND_SAMPLER_VIEW |
+ PIPE_BIND_DEPTH_STENCIL)) {
+ /* texture map */
+ if (!llvmpipe_texture_layout(screen, lpt))
+ goto fail;
+ assert(lpt->layout[0][0][0] == LP_TEX_LAYOUT_NONE);
}
else {
- if (!llvmpipe_resource_layout(screen, lpt))
+ /* other data (vertex buffer, const buffer, etc) */
+ const enum pipe_format format = templat->format;
+ const uint w = templat->width0 / util_format_get_blockheight(format);
+ const uint h = templat->height0 / util_format_get_blockwidth(format);
+ const uint d = templat->depth0;
+ const uint bpp = util_format_get_blocksize(format);
+ const uint bytes = w * h * d * bpp;
+ lpt->data = align_malloc(bytes, 16);
+ if (!lpt->data)
goto fail;
}
+ lpt->id = id_counter++;
+
return &lpt->base;
fail:
@@ -159,8 +222,37 @@ llvmpipe_resource_destroy(struct pipe_screen *pscreen,
struct sw_winsys *winsys = screen->winsys;
winsys->displaytarget_destroy(winsys, lpt->dt);
}
- else if (!lpt->userBuffer) {
+ else if (resource_is_texture(pt)) {
/* regular texture */
+ const uint num_faces = pt->target == PIPE_TEXTURE_CUBE ? 6 : 1;
+ uint level, face;
+
+ /* free linear image data */
+ for (level = 0; level < Elements(lpt->linear); level++) {
+ if (lpt->linear[level].data) {
+ align_free(lpt->linear[level].data);
+ lpt->linear[level].data = NULL;
+ }
+ }
+
+ /* free tiled image data */
+ for (level = 0; level < Elements(lpt->tiled); level++) {
+ if (lpt->tiled[level].data) {
+ align_free(lpt->tiled[level].data);
+ lpt->tiled[level].data = NULL;
+ }
+ }
+
+ /* free layout flag arrays */
+ for (level = 0; level < Elements(lpt->tiled); level++) {
+ for (face = 0; face < num_faces; face++) {
+ free(lpt->layout[face][level]);
+ lpt->layout[face][level] = NULL;
+ }
+ }
+ }
+ else if (!lpt->userBuffer) {
+ assert(lpt->data);
align_free(lpt->data);
}
@@ -169,63 +261,88 @@ llvmpipe_resource_destroy(struct pipe_screen *pscreen,
/**
- * Map a texture. Without any synchronization.
+ * Map a texture for read/write (rendering). Without any synchronization.
*/
void *
llvmpipe_resource_map(struct pipe_resource *texture,
- unsigned usage,
unsigned face,
unsigned level,
- unsigned zslice)
+ unsigned zslice,
+ enum lp_texture_usage tex_usage,
+ enum lp_texture_layout layout)
{
struct llvmpipe_resource *lpt = llvmpipe_resource(texture);
uint8_t *map;
+ assert(face < 6);
+ assert(level < LP_MAX_TEXTURE_LEVELS);
+
+ assert(tex_usage == LP_TEX_USAGE_READ ||
+ tex_usage == LP_TEX_USAGE_READ_WRITE ||
+ tex_usage == LP_TEX_USAGE_WRITE_ALL);
+
+ assert(layout == LP_TEX_LAYOUT_NONE ||
+ layout == LP_TEX_LAYOUT_TILED ||
+ layout == LP_TEX_LAYOUT_LINEAR);
+
if (lpt->dt) {
/* display target */
struct llvmpipe_screen *screen = llvmpipe_screen(texture->screen);
struct sw_winsys *winsys = screen->winsys;
+ unsigned dt_usage;
+
+ if (tex_usage == LP_TEX_USAGE_READ) {
+ dt_usage = PIPE_TRANSFER_READ;
+ }
+ else {
+ dt_usage = PIPE_TRANSFER_READ_WRITE;
+ }
assert(face == 0);
assert(level == 0);
assert(zslice == 0);
/* FIXME: keep map count? */
- map = winsys->displaytarget_map(winsys, lpt->dt, usage);
- }
- else {
- /* regular texture */
- unsigned offset;
- unsigned stride;
+ map = winsys->displaytarget_map(winsys, lpt->dt, dt_usage);
- map = lpt->data;
+ /* install this linear image in texture data structure */
+ lpt->linear[level].data = map;
- assert(level < LP_MAX_TEXTURE_2D_LEVELS);
+ map = llvmpipe_get_texture_image(lpt, face, level, tex_usage, layout);
+ assert(map);
- offset = lpt->level_offset[level];
- stride = lpt->stride[level];
+ return map;
+ }
+ else if (resource_is_texture(texture)) {
+ /* regular texture */
+ const unsigned tex_height = u_minify(texture->height0, level);
+ const unsigned nblocksy =
+ util_format_get_nblocksy(texture->format, tex_height);
+ const unsigned stride = lpt->stride[level];
+ unsigned offset = 0;
- /* XXX shouldn't that rather be
- tex_height = align(u_minify(texture->height0, level), 2)
- to account for alignment done in llvmpipe_resource_layout ?
- */
if (texture->target == PIPE_TEXTURE_CUBE) {
- unsigned tex_height = u_minify(texture->height0, level);
- offset += face * util_format_get_nblocksy(texture->format, tex_height) * stride;
+ /* XXX incorrect
+ offset = face * nblocksy * stride;
+ */
}
else if (texture->target == PIPE_TEXTURE_3D) {
- unsigned tex_height = u_minify(texture->height0, level);
- offset += zslice * util_format_get_nblocksy(texture->format, tex_height) * stride;
+ offset = zslice * nblocksy * stride;
}
else {
assert(face == 0);
assert(zslice == 0);
+ offset = 0;
}
+ map = llvmpipe_get_texture_image(lpt, face, level, tex_usage, layout);
+ assert(map);
map += offset;
+ return map;
+ }
+ else {
+ return lpt->data;
}
-
- return map;
}
@@ -249,11 +366,30 @@ llvmpipe_resource_unmap(struct pipe_resource *texture,
assert(level == 0);
assert(zslice == 0);
+ /* make sure linear image is up to date */
+ (void) llvmpipe_get_texture_image(lpt, 0, 0,
+ LP_TEX_USAGE_READ,
+ LP_TEX_LAYOUT_LINEAR);
+
winsys->displaytarget_unmap(winsys, lpt->dt);
}
}
+void *
+llvmpipe_resource_data(struct pipe_resource *resource)
+{
+ struct llvmpipe_resource *lpt = llvmpipe_resource(resource);
+
+ assert((lpt->base.bind & (PIPE_BIND_DISPLAY_TARGET |
+ PIPE_BIND_SCANOUT |
+ PIPE_BIND_SHARED |
+ PIPE_BIND_SAMPLER_VIEW)) == 0);
+
+ return lpt->data;
+}
+
+
static struct pipe_resource *
llvmpipe_resource_from_handle(struct pipe_screen *screen,
const struct pipe_resource *template,
@@ -303,7 +439,7 @@ static struct pipe_surface *
llvmpipe_get_tex_surface(struct pipe_screen *screen,
struct pipe_resource *pt,
unsigned face, unsigned level, unsigned zslice,
- unsigned usage)
+ enum lp_texture_usage usage)
{
struct pipe_surface *ps;
@@ -389,6 +525,34 @@ llvmpipe_transfer_map( struct pipe_context *pipe,
ubyte *map;
struct llvmpipe_resource *lpt;
enum pipe_format format;
+ enum lp_texture_usage tex_usage;
+ const char *mode;
+
+ assert(transfer->sr.face < 6);
+ assert(transfer->sr.level < LP_MAX_TEXTURE_LEVELS);
+
+ /*
+ printf("tex_transfer_map(%d, %d %d x %d of %d x %d, usage %d )\n",
+ transfer->x, transfer->y, transfer->width, transfer->height,
+ transfer->texture->width0,
+ transfer->texture->height0,
+ transfer->usage);
+ */
+
+ if (transfer->usage == PIPE_TRANSFER_READ) {
+ tex_usage = LP_TEX_USAGE_READ;
+ mode = "read";
+ }
+ else {
+ tex_usage = LP_TEX_USAGE_READ_WRITE;
+ mode = "read/write";
+ }
+
+ if (0) {
+ struct llvmpipe_resource *lpt = llvmpipe_resource(transfer->resource);
+ printf("transfer map tex %u mode %s\n", lpt->id, mode);
+ }
+
assert(transfer->resource);
lpt = llvmpipe_resource(transfer->resource);
@@ -408,12 +572,13 @@ llvmpipe_transfer_map( struct pipe_context *pipe,
FALSE); /* do_not_flush */
map = llvmpipe_resource_map(transfer->resource,
- transfer->usage,
transfer->sr.face,
transfer->sr.level,
- transfer->box.z);
+ transfer->box.z,
+ tex_usage, LP_TEX_LAYOUT_LINEAR);
+
- /* May want to different things here depending on read/write nature
+ /* May want to do different things here depending on read/write nature
* of the map:
*/
if (transfer->usage & PIPE_TRANSFER_WRITE) {
@@ -488,6 +653,395 @@ llvmpipe_user_buffer_create(struct pipe_screen *screen,
}
+/**
+ * Compute size (in bytes) need to store a texture image / mipmap level,
+ * for just one cube face.
+ */
+static unsigned
+tex_image_face_size(const struct llvmpipe_resource *lpt, unsigned level,
+ enum lp_texture_layout layout)
+{
+ /* for tiled layout, force a 32bpp format */
+ enum pipe_format format = layout == LP_TEX_LAYOUT_TILED
+ ? PIPE_FORMAT_B8G8R8A8_UNORM : lpt->base.format;
+ const unsigned height = u_minify(lpt->base.height0, level);
+ const unsigned depth = u_minify(lpt->base.depth0, level);
+ const unsigned nblocksy =
+ util_format_get_nblocksy(format, align(height, TILE_SIZE));
+ const unsigned buffer_size =
+ nblocksy * lpt->stride[level] *
+ (lpt->base.target == PIPE_TEXTURE_3D ? depth : 1);
+ return buffer_size;
+}
+
+
+/**
+ * Compute size (in bytes) need to store a texture image / mipmap level,
+ * including all cube faces.
+ */
+static unsigned
+tex_image_size(const struct llvmpipe_resource *lpt, unsigned level,
+ enum lp_texture_layout layout)
+{
+ const unsigned buf_size = tex_image_face_size(lpt, level, layout);
+ const unsigned num_faces = lpt->base.target == PIPE_TEXTURE_CUBE ? 6 : 1;
+ return buf_size * num_faces;
+}
+
+
+/**
+ * This function encapsulates some complicated logic for determining
+ * how to convert a tile of image data from linear layout to tiled
+ * layout, or vice versa.
+ * \param cur_layout the current tile layout
+ * \param target_layout the desired tile layout
+ * \param usage how the tile will be accessed (R/W vs. read-only, etc)
+ * \param new_layout_return returns the new layout mode
+ * \param convert_return returns TRUE if image conversion is needed
+ */
+static void
+layout_logic(enum lp_texture_layout cur_layout,
+ enum lp_texture_layout target_layout,
+ enum lp_texture_usage usage,
+ enum lp_texture_layout *new_layout_return,
+ boolean *convert)
+{
+ enum lp_texture_layout other_layout, new_layout;
+
+ *convert = FALSE;
+
+ new_layout = 99; /* debug check */
+
+ if (target_layout == LP_TEX_LAYOUT_LINEAR) {
+ other_layout = LP_TEX_LAYOUT_TILED;
+ }
+ else {
+ assert(target_layout == LP_TEX_LAYOUT_TILED);
+ other_layout = LP_TEX_LAYOUT_LINEAR;
+ }
+
+ new_layout = target_layout; /* may get changed below */
+
+ if (cur_layout == LP_TEX_LAYOUT_BOTH) {
+ if (usage == LP_TEX_USAGE_READ) {
+ new_layout = LP_TEX_LAYOUT_BOTH;
+ }
+ }
+ else if (cur_layout == other_layout) {
+ if (usage != LP_TEX_USAGE_WRITE_ALL) {
+ /* need to convert tiled data to linear or vice versa */
+ *convert = TRUE;
+
+ if (usage == LP_TEX_USAGE_READ)
+ new_layout = LP_TEX_LAYOUT_BOTH;
+ }
+ }
+ else {
+ assert(cur_layout == LP_TEX_LAYOUT_NONE ||
+ cur_layout == target_layout);
+ }
+
+ assert(new_layout == LP_TEX_LAYOUT_BOTH ||
+ new_layout == target_layout);
+
+ *new_layout_return = new_layout;
+}
+
+
+/**
+ * Return pointer to a texture image. No tiled/linear conversion is done.
+ */
+void *
+llvmpipe_get_texture_image_address(struct llvmpipe_resource *lpt,
+ unsigned face, unsigned level,
+ enum lp_texture_layout layout)
+{
+ struct llvmpipe_texture_image *img;
+ unsigned face_offset;
+
+ if (layout == LP_TEX_LAYOUT_LINEAR) {
+ img = &lpt->linear[level];
+ }
+ else {
+ assert (layout == LP_TEX_LAYOUT_TILED);
+ img = &lpt->tiled[level];
+ }
+
+ if (face > 0)
+ face_offset = face * tex_image_face_size(lpt, level, layout);
+ else
+ face_offset = 0;
+
+ return (ubyte *) img->data + face_offset;
+}
+
+
+
+/**
+ * Return pointer to texture image data (either linear or tiled layout).
+ * \param usage one of LP_TEX_USAGE_READ/WRITE_ALL/READ_WRITE
+ * \param layout either LP_TEX_LAYOUT_LINEAR or LP_TEX_LAYOUT_TILED
+ */
+void *
+llvmpipe_get_texture_image(struct llvmpipe_resource *lpt,
+ unsigned face, unsigned level,
+ enum lp_texture_usage usage,
+ enum lp_texture_layout layout)
+{
+ /*
+ * 'target' refers to the image which we're retrieving (either in
+ * tiled or linear layout).
+ * 'other' refers to the same image but in the other layout. (it may
+ * or may not exist.
+ */
+ struct llvmpipe_texture_image *target_img;
+ struct llvmpipe_texture_image *other_img;
+ void *target_data;
+ void *other_data;
+ const unsigned width = u_minify(lpt->base.width0, level);
+ const unsigned height = u_minify(lpt->base.height0, level);
+ const unsigned width_t = align(width, TILE_SIZE) / TILE_SIZE;
+ const unsigned height_t = align(height, TILE_SIZE) / TILE_SIZE;
+ enum lp_texture_layout other_layout;
+
+ assert(layout == LP_TEX_LAYOUT_NONE ||
+ layout == LP_TEX_LAYOUT_TILED ||
+ layout == LP_TEX_LAYOUT_LINEAR);
+
+ assert(usage == LP_TEX_USAGE_READ ||
+ usage == LP_TEX_USAGE_READ_WRITE ||
+ usage == LP_TEX_USAGE_WRITE_ALL);
+
+ if (lpt->dt) {
+ assert(lpt->linear[level].data);
+ }
+
+ /* which is target? which is other? */
+ if (layout == LP_TEX_LAYOUT_LINEAR) {
+ target_img = &lpt->linear[level];
+ other_img = &lpt->tiled[level];
+ other_layout = LP_TEX_LAYOUT_TILED;
+ }
+ else {
+ target_img = &lpt->tiled[level];
+ other_img = &lpt->linear[level];
+ other_layout = LP_TEX_LAYOUT_LINEAR;
+ }
+
+ target_data = target_img->data;
+ other_data = other_img->data;
+
+ if (!target_data) {
+ /* allocate memory for the target image now */
+ unsigned buffer_size = tex_image_size(lpt, level, layout);
+ target_img->data = align_malloc(buffer_size, 16);
+ target_data = target_img->data;
+ }
+
+ if (face > 0) {
+ unsigned offset = face * tex_image_face_size(lpt, level, layout);
+ if (target_data) {
+ target_data = (uint8_t *) target_data + offset;
+ }
+ if (other_data) {
+ other_data = (uint8_t *) other_data + offset;
+ }
+ }
+
+ if (layout == LP_TEX_LAYOUT_NONE) {
+ /* just allocating memory */
+ return target_data;
+ }
+
+ if (other_data) {
+ /* may need to convert other data to the requested layout */
+ enum lp_texture_layout new_layout;
+ unsigned x, y, i = 0;
+
+ /* loop over all image tiles, doing layout conversion where needed */
+ for (y = 0; y < height_t; y++) {
+ for (x = 0; x < width_t; x++) {
+ enum lp_texture_layout cur_layout = lpt->layout[face][level][i];
+ boolean convert;
+
+ layout_logic(cur_layout, layout, usage, &new_layout, &convert);
+
+ if (convert) {
+ if (layout == LP_TEX_LAYOUT_TILED) {
+ lp_linear_to_tiled(other_data, target_data,
+ x * TILE_SIZE, y * TILE_SIZE,
+ TILE_SIZE, TILE_SIZE,
+ lpt->base.format,
+ lpt->stride[level]);
+ }
+ else {
+ lp_tiled_to_linear(other_data, target_data,
+ x * TILE_SIZE, y * TILE_SIZE,
+ TILE_SIZE, TILE_SIZE,
+ lpt->base.format,
+ lpt->stride[level]);
+ }
+ }
+
+ lpt->layout[face][level][i] = new_layout;
+ i++;
+ }
+ }
+ }
+ else {
+ /* no other data */
+ unsigned i;
+ for (i = 0; i < width_t * height_t; i++) {
+ lpt->layout[face][level][i] = layout;
+ }
+ }
+
+ assert(target_data);
+
+ return target_data;
+}
+
+
+static INLINE enum lp_texture_layout
+llvmpipe_get_texture_tile_layout(const struct llvmpipe_resource *lpt,
+ unsigned face, unsigned level,
+ unsigned x, unsigned y)
+{
+ uint i;
+ assert(resource_is_texture(&lpt->base));
+ assert(x < lpt->tiles_per_row[level]);
+ i = y * lpt->tiles_per_row[level] + x;
+ return lpt->layout[face][level][i];
+}
+
+
+static INLINE void
+llvmpipe_set_texture_tile_layout(struct llvmpipe_resource *lpt,
+ unsigned face, unsigned level,
+ unsigned x, unsigned y,
+ enum lp_texture_layout layout)
+{
+ uint i;
+ assert(resource_is_texture(&lpt->base));
+ assert(x < lpt->tiles_per_row[level]);
+ i = y * lpt->tiles_per_row[level] + x;
+ lpt->layout[face][level][i] = layout;
+}
+
+
+/**
+ * Get pointer to a linear image where the tile at (x,y) is known to be
+ * in linear layout.
+ * Conversion from tiled to linear will be done if necessary.
+ * \return pointer to start of image/face (not the tile)
+ */
+ubyte *
+llvmpipe_get_texture_tile_linear(struct llvmpipe_resource *lpt,
+ unsigned face, unsigned level,
+ enum lp_texture_usage usage,
+ unsigned x, unsigned y)
+{
+ struct llvmpipe_texture_image *tiled_img = &lpt->tiled[level];
+ struct llvmpipe_texture_image *linear_img = &lpt->linear[level];
+ enum lp_texture_layout cur_layout, new_layout;
+ const unsigned tx = x / TILE_SIZE, ty = y / TILE_SIZE;
+ boolean convert;
+
+ assert(resource_is_texture(&lpt->base));
+ assert(x % TILE_SIZE == 0);
+ assert(y % TILE_SIZE == 0);
+
+ if (!linear_img->data) {
+ /* allocate memory for the tiled image now */
+ unsigned buffer_size = tex_image_size(lpt, level, LP_TEX_LAYOUT_LINEAR);
+ linear_img->data = align_malloc(buffer_size, 16);
+ }
+
+ cur_layout = llvmpipe_get_texture_tile_layout(lpt, face, level, tx, ty);
+
+ layout_logic(cur_layout, LP_TEX_LAYOUT_LINEAR, usage,
+ &new_layout, &convert);
+
+ if (convert) {
+ lp_tiled_to_linear(tiled_img->data, linear_img->data,
+ x, y, TILE_SIZE, TILE_SIZE, lpt->base.format,
+ lpt->stride[level]);
+ }
+
+ if (new_layout != cur_layout)
+ llvmpipe_set_texture_tile_layout(lpt, face, level, tx, ty, new_layout);
+
+ if (face > 0) {
+ unsigned offset
+ = face * tex_image_face_size(lpt, level, LP_TEX_LAYOUT_LINEAR);
+ return (ubyte *) linear_img->data + offset;
+ }
+ else {
+ return linear_img->data;
+ }
+}
+
+
+/**
+ * Get pointer to tiled data for rendering.
+ * \return pointer to the tiled data at the given tile position
+ */
+ubyte *
+llvmpipe_get_texture_tile(struct llvmpipe_resource *lpt,
+ unsigned face, unsigned level,
+ enum lp_texture_usage usage,
+ unsigned x, unsigned y)
+{
+ const unsigned width = u_minify(lpt->base.width0, level);
+ struct llvmpipe_texture_image *tiled_img = &lpt->tiled[level];
+ struct llvmpipe_texture_image *linear_img = &lpt->linear[level];
+ enum lp_texture_layout cur_layout, new_layout;
+ const unsigned tx = x / TILE_SIZE, ty = y / TILE_SIZE;
+ boolean convert;
+
+ assert(x % TILE_SIZE == 0);
+ assert(y % TILE_SIZE == 0);
+
+ if (!tiled_img->data) {
+ /* allocate memory for the tiled image now */
+ unsigned buffer_size = tex_image_size(lpt, level, LP_TEX_LAYOUT_TILED);
+ tiled_img->data = align_malloc(buffer_size, 16);
+ }
+
+ cur_layout = llvmpipe_get_texture_tile_layout(lpt, face, level, tx, ty);
+
+ layout_logic(cur_layout, LP_TEX_LAYOUT_TILED, usage, &new_layout, &convert);
+ if (convert) {
+ lp_linear_to_tiled(linear_img->data, tiled_img->data,
+ x, y, TILE_SIZE, TILE_SIZE, lpt->base.format,
+ lpt->stride[level]);
+ }
+
+ if (new_layout != cur_layout)
+ llvmpipe_set_texture_tile_layout(lpt, face, level, tx, ty, new_layout);
+
+ /* compute, return address of the 64x64 tile */
+ {
+ unsigned tiles_per_row, tile_offset, face_offset;
+
+ tiles_per_row = align(width, TILE_SIZE) / TILE_SIZE;
+
+ assert(tiles_per_row == lpt->tiles_per_row[level]);
+
+ tile_offset = ty * tiles_per_row + tx;
+ tile_offset *= TILE_SIZE * TILE_SIZE * 4;
+
+ assert(tiled_img->data);
+
+ face_offset = (face > 0)
+ ? (face * tex_image_face_size(lpt, level, LP_TEX_LAYOUT_TILED))
+ : 0;
+
+ return (ubyte *) tiled_img->data + face_offset + tile_offset;
+ }
+}
+
+
void
llvmpipe_init_screen_resource_funcs(struct pipe_screen *screen)
{