nkeynes@103: /** nkeynes@108: * $Id: texcache.c,v 1.3 2006-03-15 13:16:50 nkeynes Exp $ nkeynes@103: * nkeynes@103: * Texture cache. Responsible for maintaining a working set of OpenGL nkeynes@103: * textures. nkeynes@103: * nkeynes@103: * nkeynes@103: * Copyright (c) 2005 Nathan Keynes. nkeynes@103: * nkeynes@103: * This program is free software; you can redistribute it and/or modify nkeynes@103: * it under the terms of the GNU General Public License as published by nkeynes@103: * the Free Software Foundation; either version 2 of the License, or nkeynes@103: * (at your option) any later version. nkeynes@103: * nkeynes@103: * This program is distributed in the hope that it will be useful, nkeynes@103: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@103: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@103: * GNU General Public License for more details. nkeynes@103: */ nkeynes@103: nkeynes@103: #include nkeynes@103: #include "pvr2/pvr2.h" nkeynes@103: nkeynes@103: /** Specifies the maximum number of OpenGL nkeynes@103: * textures we're willing to have open at a time. If more are nkeynes@103: * needed, textures will be evicted in LRU order. nkeynes@103: */ nkeynes@103: #define MAX_TEXTURES 64 nkeynes@103: nkeynes@103: /** nkeynes@103: * Data structure: nkeynes@103: * nkeynes@103: * Main operations: nkeynes@103: * find entry by texture_addr nkeynes@103: * add new entry nkeynes@103: * move entry to tail of lru list nkeynes@103: * remove entry nkeynes@103: */ nkeynes@103: nkeynes@103: typedef signed short texcache_entry_index; nkeynes@107: #define EMPTY_ENTRY 0xFF nkeynes@103: nkeynes@107: static texcache_entry_index texcache_free_ptr = 0; nkeynes@103: static GLuint texcache_free_list[MAX_TEXTURES]; nkeynes@103: nkeynes@103: typedef struct texcache_entry { nkeynes@103: uint32_t texture_addr; nkeynes@103: int width, height, mode; nkeynes@103: GLuint texture_id; nkeynes@103: texcache_entry_index next; nkeynes@103: uint32_t lru_count; nkeynes@103: } *texcache_entry_t; nkeynes@103: nkeynes@103: static uint8_t texcache_page_lookup[PVR2_RAM_PAGES]; nkeynes@103: static uint32_t texcache_ref_counter; nkeynes@103: static struct texcache_entry texcache_active_list[MAX_TEXTURES]; nkeynes@103: nkeynes@103: /** nkeynes@108: * Initialize the texture cache. nkeynes@103: */ nkeynes@103: void texcache_init( ) nkeynes@103: { nkeynes@103: int i; nkeynes@103: for( i=0; i> 12; nkeynes@103: texcache_entry_index idx = texcache_page_lookup[texture_page]; nkeynes@103: if( idx == EMPTY_ENTRY ) nkeynes@103: return; nkeynes@107: assert( texcache_free_ptr >= 0 ); nkeynes@103: do { nkeynes@103: texcache_entry_t entry = &texcache_active_list[idx]; nkeynes@103: /* release entry */ nkeynes@103: texcache_free_ptr--; nkeynes@103: texcache_free_list[texcache_free_ptr] = idx; nkeynes@103: idx = entry->next; nkeynes@103: entry->next = EMPTY_ENTRY; nkeynes@103: } while( idx != EMPTY_ENTRY ); nkeynes@103: texcache_page_lookup[texture_page] = EMPTY_ENTRY; nkeynes@103: } nkeynes@103: nkeynes@103: /** nkeynes@103: * Evict a single texture from the cache. nkeynes@103: * @return the slot of the evicted texture. nkeynes@103: */ nkeynes@103: static texcache_entry_index texcache_evict( void ) nkeynes@103: { nkeynes@103: /* Full table scan - take over the entry with the lowest lru value */ nkeynes@103: texcache_entry_index slot = 0; nkeynes@103: int lru_value = texcache_active_list[0].lru_count; nkeynes@103: int i; nkeynes@103: for( i=1; i> 25) &0x03; nkeynes@108: unsigned char data[bytes<=0; i-- ) { nkeynes@108: char ch = data[i]; nkeynes@108: if( shift == 2 ) nkeynes@108: ((uint32_t *)data)[i] = ((uint32_t *)palette)[ch]; nkeynes@108: else nkeynes@108: ((uint16_t *)data)[i] = ((uint16_t *)palette)[ch]; nkeynes@108: } nkeynes@108: /* TODO: Detwiddle */ nkeynes@108: glTexImage2D( GL_TEXTURE_2D, 0, intFormat, width, height, 0, format, type, nkeynes@108: data ); nkeynes@108: nkeynes@108: } nkeynes@108: } else { nkeynes@108: switch( tex_format ) { nkeynes@108: case PVR2_TEX_FORMAT_ARGB1555: nkeynes@108: bytes <<= 1; nkeynes@108: intFormat = GL_RGB5_A1; nkeynes@108: format = GL_RGBA; nkeynes@108: type = GL_UNSIGNED_SHORT_5_5_5_1; nkeynes@108: break; nkeynes@108: case PVR2_TEX_FORMAT_RGB565: nkeynes@108: bytes <<= 1; nkeynes@108: intFormat = GL_RGBA; nkeynes@108: format = GL_RGBA; nkeynes@108: type = GL_UNSIGNED_SHORT_5_6_5; nkeynes@108: break; nkeynes@108: case PVR2_TEX_FORMAT_ARGB4444: nkeynes@108: bytes <<= 1; nkeynes@108: intFormat = GL_RGBA4; nkeynes@108: format = GL_RGBA; nkeynes@108: type = GL_UNSIGNED_SHORT_4_4_4_4; nkeynes@108: break; nkeynes@108: case PVR2_TEX_FORMAT_YUV422: nkeynes@108: ERROR( "YUV textures not supported" ); nkeynes@108: break; nkeynes@108: case PVR2_TEX_FORMAT_BUMPMAP: nkeynes@108: ERROR( "Bumpmap not supported" ); nkeynes@108: break; nkeynes@108: case PVR2_TEX_FORMAT_IDX4: nkeynes@108: /* Supported? */ nkeynes@108: bytes >>= 1; nkeynes@108: intFormat = GL_INTENSITY4; nkeynes@108: format = GL_COLOR_INDEX; nkeynes@108: type = GL_UNSIGNED_BYTE; nkeynes@108: shift = 0; nkeynes@108: break; nkeynes@108: case PVR2_TEX_FORMAT_IDX8: nkeynes@108: intFormat = GL_INTENSITY8; nkeynes@108: format = GL_COLOR_INDEX; nkeynes@108: type = GL_UNSIGNED_BYTE; nkeynes@108: shift = 0; nkeynes@108: break; nkeynes@108: } nkeynes@108: nkeynes@108: unsigned char data[bytes]; nkeynes@108: /* load data from image, detwiddling/uncompressing as required */ nkeynes@108: if( PVR2_TEX_IS_COMPRESSED(mode) ) { nkeynes@108: ERROR( "VQ Compression not supported" ); nkeynes@108: } else { nkeynes@108: pvr2_vram64_read( &data, texture_addr, bytes ); nkeynes@108: if( PVR2_TEX_IS_TWIDDLED(mode) ) { nkeynes@108: /* Untwiddle */ nkeynes@108: } nkeynes@108: } nkeynes@108: nkeynes@108: /* Pass to GL */ nkeynes@108: glTexImage2D( GL_TEXTURE_2D, 0, intFormat, width, height, 0, format, type, nkeynes@108: data ); nkeynes@103: } nkeynes@108: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nkeynes@108: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nkeynes@103: } nkeynes@103: nkeynes@103: /** nkeynes@103: * Return a texture ID for the texture specified at the supplied address nkeynes@103: * and given parameters (the same sequence of bytes could in theory have nkeynes@103: * multiple interpretations). We use the texture address as the primary nkeynes@103: * index, but allow for multiple instances at each address. The texture nkeynes@103: * will be bound to the GL_TEXTURE_2D target before being returned. nkeynes@103: * nkeynes@103: * If the texture has already been bound, return the ID to which it was nkeynes@103: * bound. Otherwise obtain an unused texture ID and set it up appropriately. nkeynes@103: */ nkeynes@103: GLuint texcache_get_texture( uint32_t texture_addr, int width, int height, nkeynes@103: int mode ) nkeynes@103: { nkeynes@103: uint32_t texture_page = texture_addr >> 12; nkeynes@103: texcache_entry_index idx = texcache_page_lookup[texture_page]; nkeynes@103: while( idx != EMPTY_ENTRY ) { nkeynes@103: texcache_entry_t entry = &texcache_active_list[idx]; nkeynes@103: if( entry->texture_addr == texture_addr && nkeynes@103: entry->mode == mode && nkeynes@103: entry->width == width && nkeynes@103: entry->height == height ) { nkeynes@103: entry->lru_count = texcache_ref_counter++; nkeynes@103: glBindTexture( GL_TEXTURE_2D, entry->texture_id ); nkeynes@103: return entry->texture_id; nkeynes@103: } nkeynes@103: idx = entry->next; nkeynes@103: } nkeynes@103: nkeynes@103: /* Not found - check the free list */ nkeynes@103: int slot = 0; nkeynes@103: nkeynes@103: if( texcache_free_ptr < MAX_TEXTURES ) { nkeynes@103: slot = texcache_free_list[texcache_free_ptr++]; nkeynes@103: } else { nkeynes@103: slot = texcache_evict(); nkeynes@103: } nkeynes@103: nkeynes@103: /* Construct new entry */ nkeynes@103: texcache_active_list[slot].texture_addr = texture_addr; nkeynes@103: texcache_active_list[slot].width = width; nkeynes@103: texcache_active_list[slot].height = height; nkeynes@103: texcache_active_list[slot].mode = mode; nkeynes@103: texcache_active_list[slot].lru_count = texcache_ref_counter++; nkeynes@103: nkeynes@103: /* Add entry to the lookup table */ nkeynes@103: texcache_active_list[slot].next = texcache_page_lookup[texture_page]; nkeynes@103: texcache_page_lookup[texture_page] = slot; nkeynes@103: nkeynes@103: /* Construct the GL texture */ nkeynes@108: glBindTexture( GL_TEXTURE_2D, texcache_active_list[slot].texture_id ); nkeynes@103: texcache_load_texture( texture_addr, width, height, mode ); nkeynes@103: nkeynes@103: return texcache_active_list[slot].texture_id; nkeynes@103: }