nkeynes@103: /** nkeynes@561: * $Id$ 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@647: #include nkeynes@103: #include "pvr2/pvr2.h" nkeynes@677: #include "pvr2/pvr2mmio.h" nkeynes@1140: #include "pvr2/glutil.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@349: #define MAX_TEXTURES 256 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@462: #define EMPTY_ENTRY -1 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@1135: uint32_t poly2_mode, tex_mode; nkeynes@103: GLuint texture_id; nkeynes@856: render_buffer_t buffer; nkeynes@103: texcache_entry_index next; nkeynes@103: uint32_t lru_count; nkeynes@103: } *texcache_entry_t; nkeynes@103: nkeynes@462: static texcache_entry_index 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@886: static uint32_t texcache_palette_mode; nkeynes@886: static uint32_t texcache_stride_width; nkeynes@1140: static gboolean texcache_have_palette_shader; nkeynes@1140: static gboolean texcache_palette_valid; nkeynes@1140: static GLuint texcache_palette_texid; 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; iflushed ) nkeynes@856: pvr2_render_buffer_copy_to_sh4(buffer); nkeynes@856: pvr2_destroy_render_buffer(buffer); nkeynes@856: } nkeynes@856: nkeynes@108: /** nkeynes@103: * Flush all textures from the cache, returning them to the free list. nkeynes@103: */ nkeynes@103: void texcache_flush( ) nkeynes@103: { nkeynes@103: int i; nkeynes@103: /* clear structures */ nkeynes@103: for( i=0; i> 12; nkeynes@103: texcache_entry_index replace_next = texcache_active_list[slot].next; nkeynes@337: texcache_active_list[slot].texture_addr = -1; nkeynes@103: texcache_active_list[slot].next = EMPTY_ENTRY; /* Just for safety */ nkeynes@856: if( texcache_active_list[slot].buffer != NULL ) { nkeynes@856: texcache_release_render_buffer(texcache_active_list[slot].buffer); nkeynes@856: texcache_active_list[slot].buffer = NULL; nkeynes@856: } nkeynes@103: if( texcache_page_lookup[evict_page] == slot ) { nkeynes@736: texcache_page_lookup[evict_page] = replace_next; nkeynes@103: } else { nkeynes@736: texcache_entry_index idx = texcache_page_lookup[evict_page]; nkeynes@736: texcache_entry_index next; nkeynes@736: do { nkeynes@736: next = texcache_active_list[idx].next; nkeynes@736: if( next == slot ) { nkeynes@736: assert( idx != replace_next ); nkeynes@736: texcache_active_list[idx].next = replace_next; nkeynes@736: break; nkeynes@736: } nkeynes@736: idx = next; nkeynes@736: } while( next != EMPTY_ENTRY ); nkeynes@103: } nkeynes@337: } nkeynes@337: nkeynes@337: /** nkeynes@337: * Evict a single texture from the cache. nkeynes@337: * @return the slot of the evicted texture. nkeynes@337: */ nkeynes@337: static texcache_entry_index texcache_evict_lru( void ) nkeynes@337: { nkeynes@337: /* Full table scan - take over the entry with the lowest lru value */ nkeynes@337: texcache_entry_index slot = 0; nkeynes@337: int lru_value = texcache_active_list[0].lru_count; nkeynes@337: int i; nkeynes@337: for( i=1; i> 12; nkeynes@337: texcache_entry_index idx = texcache_page_lookup[texture_page]; nkeynes@337: if( idx == EMPTY_ENTRY ) nkeynes@736: return; nkeynes@337: assert( texcache_free_ptr >= 0 ); nkeynes@337: do { nkeynes@736: texcache_entry_t entry = &texcache_active_list[idx]; nkeynes@736: entry->texture_addr = -1; nkeynes@856: if( entry->buffer != NULL ) { nkeynes@856: texcache_release_render_buffer(entry->buffer); nkeynes@856: entry->buffer = NULL; nkeynes@856: } nkeynes@736: /* release entry */ nkeynes@736: texcache_free_ptr--; nkeynes@736: texcache_free_list[texcache_free_ptr] = idx; nkeynes@736: idx = entry->next; nkeynes@736: entry->next = EMPTY_ENTRY; nkeynes@337: } while( idx != EMPTY_ENTRY ); nkeynes@337: texcache_page_lookup[texture_page] = EMPTY_ENTRY; nkeynes@337: } nkeynes@337: nkeynes@337: /** nkeynes@1140: * Load the palette into 4 textures of 256 entries each. This mirrors the nkeynes@1140: * banking done by the PVR2 for 8-bit textures, and also ensures that we nkeynes@1140: * can use 8-bit paletted textures ourselves. nkeynes@1140: */ nkeynes@1140: static void texcache_load_palette_texture( gboolean format_changed ) nkeynes@1140: { nkeynes@1140: GLint format, type, intFormat = GL_RGBA; nkeynes@1140: unsigned i; nkeynes@1140: int bpp = 2; nkeynes@1140: uint32_t *palette = (uint32_t *)mmio_region_PVR2PAL.mem; nkeynes@1140: uint16_t packed_palette[1024]; nkeynes@1140: char *data = (char *)palette; nkeynes@1140: nkeynes@1140: switch( texcache_palette_mode ) { nkeynes@1140: case 0: /* ARGB1555 */ nkeynes@1140: format = GL_BGRA; nkeynes@1140: type = GL_UNSIGNED_SHORT_1_5_5_5_REV; nkeynes@1140: break; nkeynes@1140: case 1: /* RGB565 */ nkeynes@1140: intFormat = GL_RGB; nkeynes@1140: format = GL_RGB; nkeynes@1140: type = GL_UNSIGNED_SHORT_5_6_5; nkeynes@1140: break; nkeynes@1140: case 2: /* ARGB4444 */ nkeynes@1140: format = GL_BGRA; nkeynes@1140: type = GL_UNSIGNED_SHORT_4_4_4_4_REV; nkeynes@1140: break; nkeynes@1140: case 3: /* ARGB8888 */ nkeynes@1140: format = GL_BGRA; nkeynes@1140: type = GL_UNSIGNED_BYTE; nkeynes@1140: bpp = 4; nkeynes@1140: break; nkeynes@1140: default: nkeynes@1140: break; /* Can't happen */ nkeynes@1140: } nkeynes@1140: nkeynes@1140: nkeynes@1140: if( bpp == 2 ) { nkeynes@1140: for( i=0; i<1024; i++ ) { nkeynes@1140: packed_palette[i] = (uint16_t)palette[i]; nkeynes@1140: } nkeynes@1140: data = (char *)packed_palette; nkeynes@1140: nkeynes@1140: } nkeynes@1140: nkeynes@1140: glActiveTexture(GL_TEXTURE1); nkeynes@1140: if( format_changed ) nkeynes@1221: glTexImage2D(GL_TEXTURE_2D, 0, intFormat, 1024, 1, 0, format, type, data ); nkeynes@1140: else nkeynes@1221: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1024, 1, format, type, data); nkeynes@1140: glActiveTexture(GL_TEXTURE0); nkeynes@1140: texcache_palette_valid = TRUE; nkeynes@1140: } nkeynes@1140: nkeynes@1140: nkeynes@1140: /** nkeynes@1140: * Mark the palette as having changed. If we have palette support (via shaders) nkeynes@1140: * we just flag the palette, otherwise we have to invalidate all palette nkeynes@1140: * textures. nkeynes@337: */ nkeynes@337: void texcache_invalidate_palette( ) nkeynes@337: { nkeynes@1140: if( texcache_have_palette_shader ) { nkeynes@1140: texcache_palette_valid = FALSE; nkeynes@1140: } else { nkeynes@1140: int i; nkeynes@1140: for( i=0; i> 4)]; nkeynes@736: in++; nkeynes@315: } nkeynes@315: } nkeynes@315: nkeynes@1140: static void decode_pal4_to_pal8( uint8_t *out, uint8_t *in, int inbytes ) nkeynes@1140: { nkeynes@1140: int i; nkeynes@1140: for( i=0; i> 4); nkeynes@1140: in++; nkeynes@1140: } nkeynes@1140: } nkeynes@1140: nkeynes@1140: nkeynes@315: nkeynes@321: static void decode_pal4_to_16( uint16_t *out, uint8_t *in, int inbytes, uint32_t *pal ) nkeynes@315: { nkeynes@315: int i; nkeynes@315: for( i=0; i> 4)]; nkeynes@736: in++; nkeynes@315: } nkeynes@315: } nkeynes@315: nkeynes@224: #define VQ_CODEBOOK_SIZE 2048 /* 256 entries * 4 pixels per quad * 2 byte pixels */ nkeynes@224: nkeynes@224: struct vq_codebook { nkeynes@224: uint16_t quad[256][4]; nkeynes@224: }; nkeynes@224: nkeynes@270: static void vq_get_codebook( struct vq_codebook *codebook, nkeynes@736: uint16_t *input ) nkeynes@270: { nkeynes@270: /* Detwiddle the codebook, for the sake of my own sanity if nothing else */ nkeynes@270: uint16_t *p = (uint16_t *)input; nkeynes@270: int i; nkeynes@270: for( i=0; i<256; i++ ) { nkeynes@736: codebook->quad[i][0] = *p++; nkeynes@736: codebook->quad[i][2] = *p++; nkeynes@736: codebook->quad[i][1] = *p++; nkeynes@736: codebook->quad[i][3] = *p++; nkeynes@270: } nkeynes@270: } nkeynes@270: nkeynes@429: static void vq_decode( uint16_t *output, unsigned char *input, int width, int height, nkeynes@736: struct vq_codebook *codebook ) { nkeynes@224: int i,j; nkeynes@736: nkeynes@270: uint8_t *c = (uint8_t *)input; nkeynes@311: for( j=0; jquad[code][0]; nkeynes@736: output[i + 1 + j*width] = codebook->quad[code][1]; nkeynes@736: output[i + (j+1)*width] = codebook->quad[code][2]; nkeynes@736: output[i + 1 + (j+1)*width] = codebook->quad[code][3]; nkeynes@736: } nkeynes@224: } nkeynes@224: } nkeynes@113: nkeynes@282: static inline uint32_t yuv_to_rgb32( float y, float u, float v ) nkeynes@282: { nkeynes@282: u -= 128; nkeynes@282: v -= 128; nkeynes@282: int r = (int)(y + v*1.375); nkeynes@282: int g = (int)(y - u*0.34375 - v*0.6875); nkeynes@282: int b = (int)(y + u*1.71875); nkeynes@282: if( r > 255 ) { r = 255; } else if( r < 0 ) { r = 0; } nkeynes@282: if( g > 255 ) { g = 255; } else if( g < 0 ) { g = 0; } nkeynes@282: if( b > 255 ) { b = 255; } else if( b < 0 ) { b = 0; } nkeynes@289: return 0xFF000000 | (r<<16) | (g<<8) | (b); nkeynes@282: } nkeynes@282: nkeynes@282: nkeynes@282: /** nkeynes@311: * Convert raster YUV texture data into RGB32 data - most GL implementations don't nkeynes@282: * directly support this format unfortunately. The input data is formatted as nkeynes@282: * 32 bits = 2 horizontal pixels, UYVY. This is currently done rather inefficiently nkeynes@282: * in floating point. nkeynes@282: */ nkeynes@311: static void yuv_decode( uint32_t *output, uint32_t *input, int width, int height ) nkeynes@282: { nkeynes@282: int x, y; nkeynes@282: uint32_t *p = input; nkeynes@282: for( y=0; y>8)&0xFF ); nkeynes@736: float v = (float)( (*p>>16)&0xFF ); nkeynes@736: float y1 = (float)( (*p>>24)&0xFF ); nkeynes@736: *output++ = yuv_to_rgb32( y0, u, v ); nkeynes@736: *output++ = yuv_to_rgb32( y1, u, v ); nkeynes@736: p++; nkeynes@736: } nkeynes@282: } nkeynes@282: } nkeynes@282: nkeynes@856: static gboolean is_npot_texture( int width ) nkeynes@856: { nkeynes@856: while( width != 0 ) { nkeynes@856: if( width & 1 ) nkeynes@856: return width != 1; nkeynes@856: width >>= 1; nkeynes@856: } nkeynes@856: return TRUE; nkeynes@856: } nkeynes@856: nkeynes@103: /** nkeynes@103: * Load texture data from the given address and parameters into the currently nkeynes@103: * bound OpenGL texture. nkeynes@103: */ nkeynes@429: static void texcache_load_texture( uint32_t texture_addr, int width, int height, nkeynes@736: int mode ) { nkeynes@284: int bpp_shift = 1; /* bytes per (output) pixel as a power of 2 */ nkeynes@349: GLint intFormat = GL_RGBA, format, type; nkeynes@108: int tex_format = mode & PVR2_TEX_FORMAT_MASK; nkeynes@270: struct vq_codebook codebook; nkeynes@1140: GLint min_filter = GL_LINEAR; nkeynes@1140: GLint max_filter = GL_LINEAR; nkeynes@1140: GLint mipmapfilter = GL_LINEAR_MIPMAP_LINEAR; nkeynes@108: nkeynes@270: /* Decode the format parameters */ nkeynes@270: switch( tex_format ) { nkeynes@270: case PVR2_TEX_FORMAT_IDX4: nkeynes@270: case PVR2_TEX_FORMAT_IDX8: nkeynes@1140: if( texcache_have_palette_shader ) { nkeynes@1221: intFormat = GL_ALPHA; nkeynes@1140: format = GL_ALPHA; nkeynes@736: type = GL_UNSIGNED_BYTE; nkeynes@1140: bpp_shift = 0; nkeynes@1140: min_filter = max_filter = GL_NEAREST; nkeynes@1140: mipmapfilter = GL_NEAREST_MIPMAP_NEAREST; nkeynes@1140: } else { nkeynes@1140: /* For indexed-colour modes, we need to lookup the palette control nkeynes@1140: * word to determine the de-indexed texture format. nkeynes@1140: */ nkeynes@1140: switch( texcache_palette_mode ) { nkeynes@1140: case 0: /* ARGB1555 */ nkeynes@1140: format = GL_BGRA; nkeynes@1140: type = GL_UNSIGNED_SHORT_1_5_5_5_REV; nkeynes@1140: break; nkeynes@1140: case 1: /* RGB565 */ nkeynes@1140: intFormat = GL_RGB; nkeynes@1140: format = GL_RGB; nkeynes@1140: type = GL_UNSIGNED_SHORT_5_6_5; nkeynes@1140: break; nkeynes@1140: case 2: /* ARGB4444 */ nkeynes@1140: format = GL_BGRA; nkeynes@1140: type = GL_UNSIGNED_SHORT_4_4_4_4_REV; nkeynes@1140: break; nkeynes@1140: case 3: /* ARGB8888 */ nkeynes@1140: format = GL_BGRA; nkeynes@1140: type = GL_UNSIGNED_BYTE; nkeynes@1140: bpp_shift = 2; nkeynes@1140: break; nkeynes@1140: default: nkeynes@1140: return; /* Can't happen, but it makes gcc stop complaining */ nkeynes@1140: } nkeynes@736: } nkeynes@736: break; nkeynes@736: nkeynes@860: default: nkeynes@736: case PVR2_TEX_FORMAT_ARGB1555: nkeynes@736: format = GL_BGRA; nkeynes@736: type = GL_UNSIGNED_SHORT_1_5_5_5_REV; nkeynes@736: break; nkeynes@736: case PVR2_TEX_FORMAT_RGB565: nkeynes@736: intFormat = GL_RGB; nkeynes@736: format = GL_RGB; nkeynes@736: type = GL_UNSIGNED_SHORT_5_6_5; nkeynes@736: break; nkeynes@736: case PVR2_TEX_FORMAT_ARGB4444: nkeynes@736: format = GL_BGRA; nkeynes@736: type = GL_UNSIGNED_SHORT_4_4_4_4_REV; nkeynes@736: break; nkeynes@736: case PVR2_TEX_FORMAT_YUV422: nkeynes@736: /* YUV422 isn't directly supported by most implementations, so decode nkeynes@736: * it to a (reasonably) standard ARGB32. nkeynes@736: */ nkeynes@736: bpp_shift = 2; nkeynes@736: format = GL_BGRA; nkeynes@736: type = GL_UNSIGNED_BYTE; nkeynes@736: break; nkeynes@736: case PVR2_TEX_FORMAT_BUMPMAP: nkeynes@860: WARN( "Bumpmap not supported" ); nkeynes@736: return; nkeynes@270: } nkeynes@736: nkeynes@321: if( PVR2_TEX_IS_STRIDE(mode) && tex_format != PVR2_TEX_FORMAT_IDX4 && nkeynes@736: tex_format != PVR2_TEX_FORMAT_IDX8 ) { nkeynes@736: /* Stride textures cannot be mip-mapped, compressed, indexed or twiddled */ nkeynes@736: unsigned char data[(width*height) << bpp_shift]; nkeynes@736: if( tex_format == PVR2_TEX_FORMAT_YUV422 ) { nkeynes@736: unsigned char tmp[(width*height)<<1]; nkeynes@886: pvr2_vram64_read_stride( tmp, width<<1, texture_addr, texcache_stride_width<<1, height ); nkeynes@736: yuv_decode( (uint32_t *)data, (uint32_t *)tmp, width, height ); nkeynes@736: } else { nkeynes@886: pvr2_vram64_read_stride( data, width<>last_level)*(width>>last_level)); nkeynes@736: } nkeynes@736: if( width != 1 ) { nkeynes@736: src_offset += 3; nkeynes@736: } nkeynes@736: if( PVR2_TEX_IS_COMPRESSED(mode) ) { nkeynes@736: src_offset >>= 2; nkeynes@736: } else if( tex_format == PVR2_TEX_FORMAT_IDX4 ) { nkeynes@736: src_offset >>= 1; nkeynes@736: } else if( tex_format == PVR2_TEX_FORMAT_YUV422 ) { nkeynes@736: src_offset <<= 1; nkeynes@736: } else if( tex_format != PVR2_TEX_FORMAT_IDX8 ) { nkeynes@736: src_offset <<= bpp_shift; nkeynes@736: } nkeynes@736: texture_addr += src_offset; nkeynes@270: } nkeynes@736: nkeynes@654: nkeynes@349: dest_bytes = (mip_width * mip_height) << bpp_shift; nkeynes@349: src_bytes = dest_bytes; // Modes will change this (below) nkeynes@108: nkeynes@654: for( level=0; level<= last_level; level++ ) { nkeynes@736: unsigned char data[dest_bytes]; nkeynes@736: /* load data from image, detwiddling/uncompressing as required */ nkeynes@736: if( tex_format == PVR2_TEX_FORMAT_IDX8 ) { nkeynes@1140: if( texcache_have_palette_shader ) { nkeynes@1140: pvr2_vram64_read_twiddled_8( data, texture_addr, mip_width, mip_height ); nkeynes@736: } else { nkeynes@1140: src_bytes = (mip_width * mip_height); nkeynes@1140: int bank = (mode >> 25) &0x03; nkeynes@1140: uint32_t *palette = ((uint32_t *)mmio_region_PVR2PAL.mem) + (bank<<8); nkeynes@1140: unsigned char tmp[src_bytes]; nkeynes@1140: pvr2_vram64_read_twiddled_8( tmp, texture_addr, mip_width, mip_height ); nkeynes@1140: if( bpp_shift == 2 ) { nkeynes@1140: decode_pal8_to_32( (uint32_t *)data, tmp, src_bytes, palette ); nkeynes@1140: } else { nkeynes@1140: decode_pal8_to_16( (uint16_t *)data, tmp, src_bytes, palette ); nkeynes@1140: } nkeynes@736: } nkeynes@736: } else if( tex_format == PVR2_TEX_FORMAT_IDX4 ) { nkeynes@736: src_bytes = (mip_width * mip_height) >> 1; nkeynes@736: unsigned char tmp[src_bytes]; nkeynes@1140: if( texcache_have_palette_shader ) { nkeynes@1140: pvr2_vram64_read_twiddled_4( tmp, texture_addr, mip_width, mip_height ); nkeynes@1140: decode_pal4_to_pal8( data, tmp, src_bytes ); nkeynes@736: } else { nkeynes@1140: int bank = (mode >>21 ) & 0x3F; nkeynes@1140: uint32_t *palette = ((uint32_t *)mmio_region_PVR2PAL.mem) + (bank<<4); nkeynes@1140: pvr2_vram64_read_twiddled_4( tmp, texture_addr, mip_width, mip_height ); nkeynes@1140: if( bpp_shift == 2 ) { nkeynes@1140: decode_pal4_to_32( (uint32_t *)data, tmp, src_bytes, palette ); nkeynes@1140: } else { nkeynes@1140: decode_pal4_to_16( (uint16_t *)data, tmp, src_bytes, palette ); nkeynes@1140: } nkeynes@736: } nkeynes@736: } else if( tex_format == PVR2_TEX_FORMAT_YUV422 ) { nkeynes@736: src_bytes = ((mip_width*mip_height)<<1); nkeynes@736: unsigned char tmp[src_bytes]; nkeynes@736: if( PVR2_TEX_IS_TWIDDLED(mode) ) { nkeynes@736: pvr2_vram64_read_twiddled_16( tmp, texture_addr, mip_width, mip_height ); nkeynes@736: } else { nkeynes@736: pvr2_vram64_read( tmp, texture_addr, src_bytes ); nkeynes@736: } nkeynes@736: yuv_decode( (uint32_t *)data, (uint32_t *)tmp, mip_width, mip_height ); nkeynes@736: } else if( PVR2_TEX_IS_COMPRESSED(mode) ) { nkeynes@736: src_bytes = ((mip_width*mip_height) >> 2); nkeynes@736: unsigned char tmp[src_bytes]; nkeynes@736: if( PVR2_TEX_IS_TWIDDLED(mode) ) { nkeynes@736: pvr2_vram64_read_twiddled_8( tmp, texture_addr, mip_width>>1, mip_height>>1 ); nkeynes@736: } else { nkeynes@736: pvr2_vram64_read( tmp, texture_addr, src_bytes ); nkeynes@736: } nkeynes@736: vq_decode( (uint16_t *)data, tmp, mip_width, mip_height, &codebook ); nkeynes@736: } else if( PVR2_TEX_IS_TWIDDLED(mode) ) { nkeynes@736: pvr2_vram64_read_twiddled_16( data, texture_addr, mip_width, mip_height ); nkeynes@736: } else { nkeynes@736: pvr2_vram64_read( data, texture_addr, src_bytes ); nkeynes@736: } nkeynes@270: nkeynes@736: /* Pass to GL */ nkeynes@736: if( level == last_level && level != 0 ) { /* 1x1 stored within a 2x2 */ nkeynes@736: glTexImage2D( GL_TEXTURE_2D, level, intFormat, 1, 1, 0, format, type, nkeynes@736: data + (3 << bpp_shift) ); nkeynes@736: } else { nkeynes@736: glTexImage2D( GL_TEXTURE_2D, level, intFormat, mip_width, mip_height, 0, format, type, nkeynes@736: data ); nkeynes@736: if( mip_width > 2 ) { nkeynes@736: mip_width >>= 1; nkeynes@736: mip_height >>= 1; nkeynes@736: dest_bytes >>= 2; nkeynes@736: src_bytes >>= 2; nkeynes@736: } nkeynes@736: texture_addr -= src_bytes; nkeynes@736: } nkeynes@103: } nkeynes@270: nkeynes@1140: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); nkeynes@1140: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, max_filter); nkeynes@103: } nkeynes@103: nkeynes@1135: static int texcache_find_texture_slot( uint32_t poly2_masked_word, uint32_t texture_word ) nkeynes@103: { nkeynes@653: uint32_t texture_addr = (texture_word & 0x000FFFFF)<<3; nkeynes@103: uint32_t texture_page = texture_addr >> 12; nkeynes@462: texcache_entry_index next; nkeynes@103: texcache_entry_index idx = texcache_page_lookup[texture_page]; nkeynes@103: while( idx != EMPTY_ENTRY ) { nkeynes@736: texcache_entry_t entry = &texcache_active_list[idx]; nkeynes@1135: if( entry->tex_mode == texture_word && nkeynes@1135: entry->poly2_mode == poly2_masked_word ) { nkeynes@736: entry->lru_count = texcache_ref_counter++; nkeynes@856: return idx; nkeynes@736: } nkeynes@103: idx = entry->next; nkeynes@103: } nkeynes@856: return -1; nkeynes@856: } nkeynes@103: nkeynes@1135: static int texcache_alloc_texture_slot( uint32_t poly2_word, uint32_t texture_word ) nkeynes@856: { nkeynes@856: uint32_t texture_addr = (texture_word & 0x000FFFFF)<<3; nkeynes@856: uint32_t texture_page = texture_addr >> 12; nkeynes@462: texcache_entry_index slot = 0; nkeynes@103: nkeynes@103: if( texcache_free_ptr < MAX_TEXTURES ) { nkeynes@736: slot = texcache_free_list[texcache_free_ptr++]; nkeynes@103: } else { nkeynes@736: slot = texcache_evict_lru(); nkeynes@103: } nkeynes@103: nkeynes@103: /* Construct new entry */ nkeynes@886: assert( texcache_active_list[slot].texture_addr == -1 ); nkeynes@103: texcache_active_list[slot].texture_addr = texture_addr; nkeynes@1135: texcache_active_list[slot].tex_mode = texture_word; nkeynes@1135: texcache_active_list[slot].poly2_mode = poly2_word; nkeynes@103: texcache_active_list[slot].lru_count = texcache_ref_counter++; nkeynes@103: nkeynes@103: /* Add entry to the lookup table */ nkeynes@856: int next = texcache_page_lookup[texture_page]; nkeynes@462: if( next == slot ) { nkeynes@736: int i; nkeynes@736: fprintf( stderr, "Active list: " ); nkeynes@736: for( i=0; i> 3) & 0x000FFFFF) | PVR2_TEX_UNTWIDDLED; nkeynes@856: switch( mode ) { nkeynes@856: case COLFMT_BGRA1555: texture_word |= PVR2_TEX_FORMAT_ARGB1555; break; nkeynes@856: case COLFMT_RGB565: texture_word |= PVR2_TEX_FORMAT_RGB565; break; nkeynes@856: case COLFMT_BGRA4444: texture_word |= PVR2_TEX_FORMAT_ARGB4444; break; nkeynes@856: default: nkeynes@856: WARN( "Rendering to non-texture colour format" ); nkeynes@856: } nkeynes@856: if( is_npot_texture(width) ) nkeynes@856: texture_word |= PVR2_TEX_STRIDE; nkeynes@635: nkeynes@856: nkeynes@856: int slot = texcache_find_texture_slot( texture_word, width, height ); nkeynes@856: if( slot == -1 ) { nkeynes@856: slot = texcache_alloc_texture_slot( texture_word, width, height ); nkeynes@856: } nkeynes@856: nkeynes@856: texcache_entry_t entry = &texcache_active_list[slot]; nkeynes@103: nkeynes@870: if( entry->buffer == NULL ) { nkeynes@856: entry->buffer = pvr2_create_render_buffer( texture_addr, width, height, entry->texture_id ); nkeynes@870: } else if( entry->buffer->width != width || entry->buffer->height != height ) { nkeynes@870: texcache_release_render_buffer(entry->buffer); nkeynes@870: entry->buffer = pvr2_create_render_buffer( texture_addr, width, height, entry->texture_id ); nkeynes@103: } nkeynes@103: nkeynes@856: return entry->buffer; nkeynes@103: } nkeynes@1135: #endif nkeynes@635: nkeynes@635: /** nkeynes@635: * Check the integrity of the texcache. Verifies that every cache slot nkeynes@635: * appears exactly once on either the free list or one page list. For nkeynes@635: * active slots, the texture address must also match the page it appears on. nkeynes@635: * nkeynes@635: */ nkeynes@635: void texcache_integrity_check() nkeynes@635: { nkeynes@635: int i; nkeynes@635: int slot_found[MAX_TEXTURES]; nkeynes@736: nkeynes@635: memset( slot_found, 0, sizeof(slot_found) ); nkeynes@635: nkeynes@635: /* Check entries on the free list */ nkeynes@635: for( i= texcache_free_ptr; i< MAX_TEXTURES; i++ ) { nkeynes@736: int slot = texcache_free_list[i]; nkeynes@736: assert( slot_found[slot] == 0 ); nkeynes@736: assert( texcache_active_list[slot].next == EMPTY_ENTRY ); nkeynes@736: slot_found[slot] = 1; nkeynes@635: } nkeynes@635: nkeynes@635: /* Check entries on the active lists */ nkeynes@635: for( i=0; i< PVR2_RAM_PAGES; i++ ) { nkeynes@736: int slot = texcache_page_lookup[i]; nkeynes@736: while( slot != EMPTY_ENTRY ) { nkeynes@736: assert( slot_found[slot] == 0 ); nkeynes@736: assert( (texcache_active_list[slot].texture_addr >> 12) == i ); nkeynes@736: slot_found[slot] = 2; nkeynes@736: slot = texcache_active_list[slot].next; nkeynes@736: } nkeynes@635: } nkeynes@635: nkeynes@635: /* Make sure we didn't miss any entries */ nkeynes@635: for( i=0; i>1); nkeynes@1142: char tmp[src_bytes]; nkeynes@1142: char data[width*width]; nkeynes@1142: pvr2_vram64_read_twiddled_4( tmp, texture_addr, width, width ); nkeynes@1142: decode_pal4_to_pal8( data, tmp, src_bytes ); nkeynes@1142: for( y=0; y