/************************************************************************** * * Copyright 2007 VMware, Inc. * 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 VMWARE 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. * **************************************************************************/ /** * Texture tile caching. * * Author: * Brian Paul */ #include "util/u_inlines.h" #include "util/u_memory.h" #include "util/u_tile.h" #include "util/u_format.h" #include "util/u_math.h" #include "sp_context.h" #include "sp_texture.h" #include "sp_tex_tile_cache.h" struct softpipe_tex_tile_cache * sp_create_tex_tile_cache( struct pipe_context *pipe ) { struct softpipe_tex_tile_cache *tc; uint pos; /* make sure max texture size works */ assert((TEX_TILE_SIZE << TEX_ADDR_BITS) >= (1 << (SP_MAX_TEXTURE_2D_LEVELS-1))); tc = CALLOC_STRUCT( softpipe_tex_tile_cache ); if (tc) { tc->pipe = pipe; for (pos = 0; pos < ARRAY_SIZE(tc->entries); pos++) { tc->entries[pos].addr.bits.invalid = 1; } tc->last_tile = &tc->entries[0]; /* any tile */ } return tc; } void sp_destroy_tex_tile_cache(struct softpipe_tex_tile_cache *tc) { if (tc) { uint pos; for (pos = 0; pos < ARRAY_SIZE(tc->entries); pos++) { /*assert(tc->entries[pos].x < 0);*/ } if (tc->transfer) { tc->pipe->transfer_unmap(tc->pipe, tc->transfer); } if (tc->tex_trans) { tc->pipe->transfer_unmap(tc->pipe, tc->tex_trans); } FREE( tc ); } } /** * Invalidate all cached tiles for the cached texture. * Should be called when the texture is modified. */ void sp_tex_tile_cache_validate_texture(struct softpipe_tex_tile_cache *tc) { unsigned i; assert(tc); assert(tc->texture); for (i = 0; i < ARRAY_SIZE(tc->entries); i++) { tc->entries[i].addr.bits.invalid = 1; } } static boolean sp_tex_tile_is_compat_view(struct softpipe_tex_tile_cache *tc, struct pipe_sampler_view *view) { if (!view) return FALSE; return (tc->texture == view->texture && tc->format == view->format && tc->swizzle_r == view->swizzle_r && tc->swizzle_g == view->swizzle_g && tc->swizzle_b == view->swizzle_b && tc->swizzle_a == view->swizzle_a); } /** * Specify the sampler view to cache. */ void sp_tex_tile_cache_set_sampler_view(struct softpipe_tex_tile_cache *tc, struct pipe_sampler_view *view) { struct pipe_resource *texture = view ? view->texture : NULL; uint i; assert(!tc->transfer); if (!sp_tex_tile_is_compat_view(tc, view)) { pipe_resource_reference(&tc->texture, texture); if (tc->tex_trans_map) { tc->pipe->transfer_unmap(tc->pipe, tc->tex_trans); tc->tex_trans = NULL; tc->tex_trans_map = NULL; } if (view) { tc->swizzle_r = view->swizzle_r; tc->swizzle_g = view->swizzle_g; tc->swizzle_b = view->swizzle_b; tc->swizzle_a = view->swizzle_a; tc->format = view->format; } /* mark as entries as invalid/empty */ /* XXX we should try to avoid this when the teximage hasn't changed */ for (i = 0; i < ARRAY_SIZE(tc->entries); i++) { tc->entries[i].addr.bits.invalid = 1; } tc->tex_z = -1; /* any invalid value here */ } } /** * Flush the tile cache: write all dirty tiles back to the transfer. * any tiles "flagged" as cleared will be "really" cleared. */ void sp_flush_tex_tile_cache(struct softpipe_tex_tile_cache *tc) { int pos; if (tc->texture) { /* caching a texture, mark all entries as empty */ for (pos = 0; pos < ARRAY_SIZE(tc->entries); pos++) { tc->entries[pos].addr.bits.invalid = 1; } tc->tex_z = -1; } } /** * Given the texture face, level, zslice, x and y values, compute * the cache entry position/index where we'd hope to find the * cached texture tile. * This is basically a direct-map cache. * XXX There's probably lots of ways in which we can improve this. */ static inline uint tex_cache_pos( union tex_tile_address addr ) { uint entry = (addr.bits.x + addr.bits.y * 9 + addr.bits.z + addr.bits.level * 7); return entry % NUM_TEX_TILE_ENTRIES; } /** * Similar to sp_get_cached_tile() but for textures. * Tiles are read-only and indexed with more params. */ const struct softpipe_tex_cached_tile * sp_find_cached_tile_tex(struct softpipe_tex_tile_cache *tc, union tex_tile_address addr ) { struct softpipe_tex_cached_tile *tile; boolean zs = util_format_is_depth_or_stencil(tc->format); tile = tc->entries + tex_cache_pos( addr ); if (addr.value != tile->addr.value) { /* cache miss. Most misses are because we've invalidated the * texture cache previously -- most commonly on binding a new * texture. Currently we effectively flush the cache on texture * bind. */ #if 0 _debug_printf("miss at %u: x=%d y=%d z=%d face=%d level=%d\n" " tile %u: x=%d y=%d z=%d face=%d level=%d\n", pos, x/TILE_SIZE, y/TILE_SIZE, z, face, level, pos, tile->addr.bits.x, tile->addr.bits.y, tile->z, tile->face, tile->level); #endif /* check if we need to get a new transfer */ if (!tc->tex_trans || tc->tex_level != addr.bits.level || tc->tex_z != addr.bits.z) { /* get new transfer (view into texture) */ unsigned width, height, layer; if (tc->tex_trans_map) { tc->pipe->transfer_unmap(tc->pipe, tc->tex_trans); tc->tex_trans = NULL; tc->tex_trans_map = NULL; } width = u_minify(tc->texture->width0, addr.bits.level); if (tc->texture->target == PIPE_TEXTURE_1D_ARRAY) { height = tc->texture->array_size; layer = 0; } else { height = u_minify(tc->texture->height0, addr.bits.level); layer = addr.bits.z; } tc->tex_trans_map = pipe_transfer_map(tc->pipe, tc->texture, addr.bits.level, layer, PIPE_TRANSFER_READ | PIPE_TRANSFER_UNSYNCHRONIZED, 0, 0, width, height, &tc->tex_trans); tc->tex_level = addr.bits.level; tc->tex_z = addr.bits.z; } /* Get tile from the transfer (view into texture), explicitly passing * the image format. */ if (!zs && util_format_is_pure_uint(tc->format)) { pipe_get_tile_ui_format(tc->tex_trans, tc->tex_trans_map, addr.bits.x * TEX_TILE_SIZE, addr.bits.y * TEX_TILE_SIZE, TEX_TILE_SIZE, TEX_TILE_SIZE, tc->format, (unsigned *) tile->data.colorui); } else if (!zs && util_format_is_pure_sint(tc->format)) { pipe_get_tile_i_format(tc->tex_trans, tc->tex_trans_map, addr.bits.x * TEX_TILE_SIZE, addr.bits.y * TEX_TILE_SIZE, TEX_TILE_SIZE, TEX_TILE_SIZE, tc->format, (int *) tile->data.colori); } else { pipe_get_tile_rgba_format(tc->tex_trans, tc->tex_trans_map, addr.bits.x * TEX_TILE_SIZE, addr.bits.y * TEX_TILE_SIZE, TEX_TILE_SIZE, TEX_TILE_SIZE, tc->format, (float *) tile->data.color); } tile->addr = addr; } tc->last_tile = tile; return tile; }