1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/pvr2/texcache.c Mon Mar 13 12:39:07 2006 +0000
1.5 + * $Id: texcache.c,v 1.1 2006-03-13 12:39:07 nkeynes Exp $
1.7 + * Texture cache. Responsible for maintaining a working set of OpenGL
1.11 + * Copyright (c) 2005 Nathan Keynes.
1.13 + * This program is free software; you can redistribute it and/or modify
1.14 + * it under the terms of the GNU General Public License as published by
1.15 + * the Free Software Foundation; either version 2 of the License, or
1.16 + * (at your option) any later version.
1.18 + * This program is distributed in the hope that it will be useful,
1.19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.21 + * GNU General Public License for more details.
1.24 +#include <assert.h>
1.25 +#include "pvr2/pvr2.h"
1.27 +/** Specifies the maximum number of OpenGL
1.28 + * textures we're willing to have open at a time. If more are
1.29 + * needed, textures will be evicted in LRU order.
1.31 +#define MAX_TEXTURES 64
1.36 + * Main operations:
1.37 + * find entry by texture_addr
1.39 + * move entry to tail of lru list
1.43 +typedef signed short texcache_entry_index;
1.44 +#define EMPTY_ENTRY -1
1.46 +static texcache_entry_index texcache_free_ptr;
1.47 +static GLuint texcache_free_list[MAX_TEXTURES];
1.49 +typedef struct texcache_entry {
1.50 + uint32_t texture_addr;
1.51 + int width, height, mode;
1.52 + GLuint texture_id;
1.53 + texcache_entry_index next;
1.54 + uint32_t lru_count;
1.55 +} *texcache_entry_t;
1.57 +static uint8_t texcache_page_lookup[PVR2_RAM_PAGES];
1.58 +static uint32_t texcache_active_ptr;
1.59 +static uint32_t texcache_ref_counter;
1.60 +static struct texcache_entry texcache_active_list[MAX_TEXTURES];
1.63 + * Initialize the texture cache. Note that the GL context must have been
1.64 + * initialized before calling this function.
1.66 +void texcache_init( )
1.69 + GLuint texids[MAX_TEXTURES];
1.70 + glGenTextures( MAX_TEXTURES, texids );
1.71 + for( i=0; i<PVR2_RAM_PAGES; i++ ) {
1.72 + texcache_page_lookup[i] = EMPTY_ENTRY;
1.74 + for( i=0; i<MAX_TEXTURES; i++ ) {
1.75 + texcache_active_list[i].texture_id = texids[i];
1.76 + texcache_free_list[i] = i;
1.78 + texcache_free_ptr = 0;
1.79 + texcache_ref_counter = 0;
1.83 + * Flush all textures from the cache, returning them to the free list.
1.85 +void texcache_flush( )
1.88 + /* clear structures */
1.89 + for( i=0; i<PVR2_RAM_PAGES; i++ ) {
1.90 + texcache_page_lookup[i] = EMPTY_ENTRY;
1.92 + for( i=0; i<MAX_TEXTURES; i++ ) {
1.93 + texcache_free_list[i] = i;
1.95 + texcache_free_ptr = 0;
1.96 + texcache_ref_counter = 0;
1.100 + * Flush all textures and delete. The cache will be non-functional until
1.101 + * the next call to texcache_init(). This would typically be done if
1.102 + * switching GL targets.
1.104 +void texcache_shutdown( )
1.106 + GLuint texids[MAX_TEXTURES];
1.108 + texcache_flush();
1.110 + for( i=0; i<MAX_TEXTURES; i++ ) {
1.111 + texids[i] = texcache_active_list[i].texture_id;
1.113 + glDeleteTextures( MAX_TEXTURES, texids );
1.117 + * Evict all textures contained in the page identified by a texture address.
1.119 +void texcache_invalidate_page( uint32_t texture_addr ) {
1.120 + uint32_t texture_page = texture_addr >> 12;
1.121 + texcache_entry_index idx = texcache_page_lookup[texture_page];
1.122 + if( idx == EMPTY_ENTRY )
1.124 + assert( texcache_free_ptr > 0 );
1.126 + texcache_entry_t entry = &texcache_active_list[idx];
1.127 + /* release entry */
1.128 + texcache_free_ptr--;
1.129 + texcache_free_list[texcache_free_ptr] = idx;
1.130 + idx = entry->next;
1.131 + entry->next = EMPTY_ENTRY;
1.132 + } while( idx != EMPTY_ENTRY );
1.133 + texcache_page_lookup[texture_page] = EMPTY_ENTRY;
1.137 + * Evict a single texture from the cache.
1.138 + * @return the slot of the evicted texture.
1.140 +static texcache_entry_index texcache_evict( void )
1.142 + /* Full table scan - take over the entry with the lowest lru value */
1.143 + texcache_entry_index slot = 0;
1.144 + int lru_value = texcache_active_list[0].lru_count;
1.146 + for( i=1; i<MAX_TEXTURES; i++ ) {
1.147 + /* FIXME: account for rollover */
1.148 + if( texcache_active_list[i].lru_count < lru_value ) {
1.150 + lru_value = texcache_active_list[i].lru_count;
1.154 + /* Remove the selected slot from the lookup table */
1.155 + uint32_t evict_page = texcache_active_list[slot].texture_addr;
1.156 + texcache_entry_index replace_next = texcache_active_list[slot].next;
1.157 + texcache_active_list[slot].next = EMPTY_ENTRY; /* Just for safety */
1.158 + if( texcache_page_lookup[evict_page] == slot ) {
1.159 + texcache_page_lookup[evict_page] = replace_next;
1.161 + texcache_entry_index idx = texcache_page_lookup[evict_page];
1.162 + texcache_entry_index next;
1.164 + next = texcache_active_list[idx].next;
1.165 + if( next == slot ) {
1.166 + texcache_active_list[idx].next = replace_next;
1.170 + } while( next != EMPTY_ENTRY );
1.176 + * Load texture data from the given address and parameters into the currently
1.177 + * bound OpenGL texture.
1.179 +static texcache_load_texture( uint32_t texture_addr, int width, int height,
1.181 + uint32_t bytes = width * height;
1.183 + GLint intFormat, format, type;
1.184 + switch( mode & PVR2_TEX_FORMAT_MASK ) {
1.185 + case PVR2_TEX_FORMAT_ARGB1555:
1.187 + intFormat = GL_RGB5_A1;
1.188 + format = GL_RGBA;
1.189 + type = GL_UNSIGNED_SHORT_5_5_5_1;
1.191 + case PVR2_TEX_FORMAT_RGB565:
1.193 + intFormat = GL_RGBA;
1.194 + format = GL_RGBA;
1.195 + type = GL_UNSIGNED_SHORT_5_6_5;
1.197 + case PVR2_TEX_FORMAT_ARGB4444:
1.199 + intFormat = GL_RGBA4;
1.200 + format = GL_RGBA;
1.201 + type = GL_UNSIGNED_SHORT_4_4_4_4;
1.203 + case PVR2_TEX_FORMAT_YUV422:
1.204 + ERROR( "YUV textures not supported" );
1.206 + case PVR2_TEX_FORMAT_BUMPMAP:
1.207 + ERROR( "Bumpmap not supported" );
1.209 + case PVR2_TEX_FORMAT_IDX4:
1.212 + intFormat = GL_INTENSITY4;
1.213 + format = GL_COLOR_INDEX;
1.214 + type = GL_UNSIGNED_BYTE;
1.217 + case PVR2_TEX_FORMAT_IDX8:
1.218 + intFormat = GL_INTENSITY8;
1.219 + format = GL_COLOR_INDEX;
1.220 + type = GL_UNSIGNED_BYTE;
1.225 + unsigned char data[bytes];
1.226 + /* load data from image, detwiddling/uncompressing as required */
1.227 + if( PVR2_TEX_IS_COMPRESSED(mode) ) {
1.228 + ERROR( "VQ Compression not supported" );
1.230 + pvr2_vram64_read( &data, texture_addr, bytes );
1.231 + if( PVR2_TEX_IS_TWIDDLED(mode) ) {
1.236 + glTexImage2D( GL_TEXTURE_2D, 0, intFormat, width, height, 0, format, type,
1.241 + * Return a texture ID for the texture specified at the supplied address
1.242 + * and given parameters (the same sequence of bytes could in theory have
1.243 + * multiple interpretations). We use the texture address as the primary
1.244 + * index, but allow for multiple instances at each address. The texture
1.245 + * will be bound to the GL_TEXTURE_2D target before being returned.
1.247 + * If the texture has already been bound, return the ID to which it was
1.248 + * bound. Otherwise obtain an unused texture ID and set it up appropriately.
1.250 +GLuint texcache_get_texture( uint32_t texture_addr, int width, int height,
1.253 + uint32_t texture_page = texture_addr >> 12;
1.254 + texcache_entry_index idx = texcache_page_lookup[texture_page];
1.255 + while( idx != EMPTY_ENTRY ) {
1.256 + texcache_entry_t entry = &texcache_active_list[idx];
1.257 + if( entry->texture_addr == texture_addr &&
1.258 + entry->mode == mode &&
1.259 + entry->width == width &&
1.260 + entry->height == height ) {
1.261 + entry->lru_count = texcache_ref_counter++;
1.262 + glBindTexture( GL_TEXTURE_2D, entry->texture_id );
1.263 + return entry->texture_id;
1.265 + idx = entry->next;
1.268 + /* Not found - check the free list */
1.271 + if( texcache_free_ptr < MAX_TEXTURES ) {
1.272 + slot = texcache_free_list[texcache_free_ptr++];
1.274 + slot = texcache_evict();
1.277 + /* Construct new entry */
1.278 + texcache_active_list[slot].texture_addr = texture_addr;
1.279 + texcache_active_list[slot].width = width;
1.280 + texcache_active_list[slot].height = height;
1.281 + texcache_active_list[slot].mode = mode;
1.282 + texcache_active_list[slot].lru_count = texcache_ref_counter++;
1.284 + /* Add entry to the lookup table */
1.285 + texcache_active_list[slot].next = texcache_page_lookup[texture_page];
1.286 + texcache_page_lookup[texture_page] = slot;
1.288 + /* Construct the GL texture */
1.289 + GLuint texid = texcache_free_list[texcache_free_ptr++];
1.290 + glBindTexture( GL_TEXTURE_2D, texid );
1.291 + texcache_load_texture( texture_addr, width, height, mode );
1.293 + return texcache_active_list[slot].texture_id;