1 /******************************************************************************
2 * Arachnoid Graphics Plugin for Mupen64Plus
3 * http://bitbucket.org/wahrhaft/mupen64plus-video-arachnoid/
5 * Copyright (C) 2007 Kristofer Karlsson, Rickard Niklasson
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *****************************************************************************/
22 #include "TextureCache.h"
26 #include "CachedTexture.h"
33 #include "OpenGLRenderer.h"
34 #include "MultiTexturingExt.h"
37 #define GL_CLAMP_TO_EDGE 0x812F
38 #define GL_GENERATE_MIPMAP 0x8191
43 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 TextureCache::TextureCache()
48 m_currentTextures[0] = 0;
49 m_currentTextures[1] = 0;
52 //-----------------------------------------------------------------------------
54 //-----------------------------------------------------------------------------
55 TextureCache::~TextureCache()
60 //-----------------------------------------------------------------------------
62 //-----------------------------------------------------------------------------
63 bool TextureCache::initialize(RSP* rsp, RDP* rdp, Memory* memory, unsigned int textureBitDepth, unsigned int cacheSize)
68 m_bitDepth = textureBitDepth;
69 m_maxBytes = cacheSize;
74 //-----------------------------------------------------------------------------
76 //-----------------------------------------------------------------------------
77 void TextureCache::update(unsigned int tile)
79 //if (cache.bitDepth != OGL.textureBitDepth)
81 // TextureCache_Destroy();
82 // TextureCache_Init();
86 if ( m_rdp->getTextureMode() == TM_BGIMAGE )
90 else if ( m_rdp->getTextureMode() == TM_FRAMEBUFFER )
97 unsigned int maskWidth = 0, maskHeight = 0;
98 _calculateTextureSize(tile, &temp, maskWidth, maskHeight);
101 static int misses = 0;
103 //For each texture in texture cache
104 for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it)
106 CachedTexture* temp2 = (*it);
108 if ( *temp2 == temp )
110 _activateTexture( tile, (*it) );
117 // If multitexturing, set the appropriate texture
118 //if (OGL.ARB_multitexture)
119 glActiveTextureARB( GL_TEXTURE0_ARB + tile );
121 //Add new texture to cache
122 m_currentTextures[tile] = addTop();
123 m_currentTextures[tile]->activate();
125 m_currentTextures[tile]->address = m_rdp->getTextureImage()->address;
126 m_currentTextures[tile]->crc = temp.crc;
127 m_currentTextures[tile]->width = temp.width;
128 m_currentTextures[tile]->height = temp.height;
129 m_currentTextures[tile]->clampWidth = temp.clampWidth;
130 m_currentTextures[tile]->clampHeight = temp.clampHeight;
132 m_currentTextures[tile]->format = m_rsp->getTile(tile)->format;
133 m_currentTextures[tile]->size = m_rsp->getTile(tile)->size;
135 m_currentTextures[tile]->palette = m_rsp->getTile(tile)->palette;
136 /* m_currentTextures[tile]->fulS = rsp.getTile(tile)->fulS;
137 m_currentTextures[tile]->fulT = rsp.getTile(tile)->fulT;
138 m_currentTextures[tile]->ulS = rsp.getTile(tile)->ulS;
139 m_currentTextures[tile]->ulT = rsp.getTile(tile)->ulT;
140 m_currentTextures[tile]->lrS = rsp.getTile(tile)->lrS;
141 m_currentTextures[tile]->lrT = rsp.getTile(tile)->lrT;*/
142 m_currentTextures[tile]->maskS = m_rsp->getTile(tile)->masks;
143 m_currentTextures[tile]->maskT = m_rsp->getTile(tile)->maskt;
144 m_currentTextures[tile]->mirrorS = m_rsp->getTile(tile)->mirrors;
145 m_currentTextures[tile]->mirrorT = m_rsp->getTile(tile)->mirrort;
146 m_currentTextures[tile]->clampS = m_rsp->getTile(tile)->clamps;
147 m_currentTextures[tile]->clampT = m_rsp->getTile(tile)->clampt;
148 m_currentTextures[tile]->line = m_rsp->getTile(tile)->line;
149 m_currentTextures[tile]->tMem = m_rsp->getTile(tile)->tmem;
153 // cache.current[tile]->lastDList = RSP.DList;
154 // cache.current[tile]->frameBufferTexture = FALSE;
156 //Calculate Real Width
157 if (m_currentTextures[tile]->clampS)
159 m_currentTextures[tile]->realWidth = pow2( temp.clampWidth );
161 else if (m_currentTextures[tile]->mirrorS)
163 m_currentTextures[tile]->realWidth = maskWidth << 1;
167 m_currentTextures[tile]->realWidth = pow2( temp.width );
170 //Calculate Real Height
171 if (m_currentTextures[tile]->clampT)
173 m_currentTextures[tile]->realHeight = pow2( temp.clampHeight );
175 else if (m_currentTextures[tile]->mirrorT)
177 m_currentTextures[tile]->realHeight = maskHeight << 1;
181 m_currentTextures[tile]->realHeight = pow2( temp.height );
185 m_currentTextures[tile]->scaleS = 1.0f / (float)(m_currentTextures[tile]->realWidth);
186 m_currentTextures[tile]->scaleT = 1.0f / (float)(m_currentTextures[tile]->realHeight);
187 m_currentTextures[tile]->shiftScaleS = 1.0f;
188 m_currentTextures[tile]->shiftScaleT= 1.0f;
190 //m_currentTextures[tile]->offsetS = OGL.enable2xSaI ? 0.25f : 0.5f;
191 //m_currentTextures[tile]->offsetT = OGL.enable2xSaI ? 0.25f : 0.5f;
193 m_currentTextures[tile]->offsetS = 0.5f;
194 m_currentTextures[tile]->offsetT = 0.5f;
197 if (m_rsp->getTile(tile)->shifts > 10)
198 m_currentTextures[tile]->shiftScaleS = (float)(1 << (16 - m_rsp->getTile(tile)->shifts));
199 else if (m_rsp->getTile(tile)->shifts > 0)
200 m_currentTextures[tile]->shiftScaleS /= (float)(1 << m_rsp->getTile(tile)->shifts);
202 if (m_rsp->getTile(tile)->shiftt > 10)
203 m_currentTextures[tile]->shiftScaleT = (float)(1 << (16 - m_rsp->getTile(tile)->shiftt));
204 else if (m_rsp->getTile(tile)->shiftt > 0)
205 m_currentTextures[tile]->shiftScaleT /= (float)(1 << m_rsp->getTile(tile)->shiftt);
211 _loadTexture( m_currentTextures[tile] );
212 _activateTexture( tile, m_currentTextures[tile] );
214 m_cachedBytes += m_currentTextures[tile]->getTextureSize();
218 //-----------------------------------------------------------------------------
220 //! Adds a texture to cache
221 //-----------------------------------------------------------------------------
222 CachedTexture* TextureCache::addTop()
224 //If no memory left, remove old textures from cache
225 while ( m_cachedBytes > m_maxBytes )
227 this->removeBottom();
231 CachedTexture* newTexture = new CachedTexture();
234 glGenTextures(1, &newTexture->m_id);
236 //Add Texture to cache
237 m_cachedTextures.push_front(newTexture);
242 //-----------------------------------------------------------------------------
244 //-----------------------------------------------------------------------------
245 void TextureCache::removeBottom()
247 //Get Last Texture in list
248 CachedTexture* lastTexture = *(--m_cachedTextures.end());
251 m_cachedTextures.pop_back();
252 m_cachedBytes -= lastTexture->getTextureSize();
254 //if (cache.bottom->frameBufferTexture)
255 // FrameBuffer_RemoveBuffer( cache.bottom->address );
258 glDeleteTextures(1, &lastTexture->m_id);
263 //-----------------------------------------------------------------------------
265 //-----------------------------------------------------------------------------
266 void TextureCache::remove( CachedTexture *texture )
271 //-----------------------------------------------------------------------------
272 //Move Texture to top
273 //-----------------------------------------------------------------------------
274 void TextureCache::moveToTop( CachedTexture *newtop )
277 TextureList::iterator it = std::find( m_cachedTextures.begin(), m_cachedTextures.end(), newtop);
280 if ( it != m_cachedTextures.end() )
282 m_cachedTextures.erase(it);
285 //Add texture to the front of the list
286 m_cachedTextures.push_front(newtop);
289 //-----------------------------------------------------------------------------
291 //-----------------------------------------------------------------------------
292 void TextureCache::dispose()
295 for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it )
299 m_cachedTextures.clear();
302 //-----------------------------------------------------------------------------
304 //-----------------------------------------------------------------------------
305 void TextureCache::_loadTexture(CachedTexture* texture)
308 GetTexelFunc getTexelFunc;
309 unsigned int internalFormat;
311 m_formatSelector.detectImageFormat(texture, m_bitDepth, getTexelFunc, internalFormat, imageType, m_rdp->getTextureLUT());
314 unsigned int* dest = new unsigned int[ texture->getTextureSize() ];
317 unsigned short line = (unsigned short)texture->line;
318 if (texture->size == G_IM_SIZ_32b)
325 unsigned short mirrorSBit, maskSMask, clampSClamp;
326 unsigned short mirrorTBit, maskTMask, clampTClamp;
330 clampSClamp = (unsigned short)(texture->clampS ? texture->clampWidth - 1 : (texture->mirrorS ? (texture->width << 1) - 1 : texture->width - 1));
331 maskSMask = (1 << texture->maskS) - 1;
332 mirrorSBit = texture->mirrorS ? 1 << texture->maskS : 0;
336 clampSClamp = (unsigned short)min( texture->clampWidth, texture->width ) - 1;
343 clampTClamp = (unsigned short)(texture->clampT ? texture->clampHeight - 1 : (texture->mirrorT ? (texture->height << 1) - 1: texture->height - 1));
344 maskTMask = (1 << texture->maskT) - 1;
345 mirrorTBit = texture->mirrorT ? 1 << texture->maskT : 0;
349 clampTClamp = (unsigned short)min( texture->clampHeight, texture->height ) - 1;
354 // Hack for Zelda warp texture
355 if (((texture->tMem << 3) + (texture->width * texture->height << texture->size >> 1)) > 4096)
358 // limit clamp values to min-0 (Perfect Dark has height=0 textures, making negative clamps)
359 if (clampTClamp & 0x8000)
361 if (clampSClamp & 0x8000)
365 //Retrive texture from source (TMEM) and copy it to dest
368 unsigned short x, y, i, j, tx, ty;
370 unsigned long long* src;
373 for (y = 0; y < texture->realHeight; y++)
375 ty = min(y, clampTClamp) & maskTMask;
377 if (y & mirrorTBit) {
381 //TODO: remove old if new works
382 //src = m_memory->getTextureMemory(texture->tMem) + line * ty;
383 src = m_memory->getTextureMemory((texture->tMem + line * ty) & 511);
387 for (x = 0; x < texture->realWidth; x++)
389 tx = min(x, clampSClamp) & maskSMask;
394 if (internalFormat == GL_RGBA8)
395 ((unsigned int*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette );
397 ((unsigned short*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette );
401 //Send Texture to OpenGL
403 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest );
405 glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest );
407 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
413 void TextureCache::_calculateTextureSize(unsigned int tile, CachedTexture* out, unsigned int& maskWidth, unsigned int& maskHeight )
415 RDPTile* rspTile = m_rsp->getTile(tile);
417 //Calculate Tile Size
418 unsigned int tileWidth = rspTile->getWidth();
419 unsigned int tileHeight = rspTile->getHeight();
422 maskWidth = 1 << rspTile->masks;
423 maskHeight = 1 << rspTile->maskt;
425 //Get Current Tile Size
426 unsigned int loadWidth = m_rdp->getCurrentTile()->getWidth();
427 unsigned int loadHeight = m_rdp->getCurrentTile()->getHeight();
429 unsigned int maxTexels = ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].maxTexels;
431 //Get Line Width (depending on imageformat)
432 unsigned int lineWidth = rspTile->line << ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].lineShift;
433 unsigned int lineHeight;
434 if ( lineWidth ) // Don't allow division by zero
435 lineHeight = min( maxTexels / lineWidth, tileHeight );
442 if ( m_rdp->getTextureMode() == TM_TEXRECT )
444 unsigned short texRectWidth = (unsigned short)(m_rdp->getTexRectWidth() - rspTile->uls);
445 unsigned short texRectHeight = (unsigned short)(m_rdp->getTexRectHeight() - rspTile->ult);
447 if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels))
449 else if ((tileWidth * tileHeight) <= maxTexels)
451 else if ((tileWidth * texRectHeight) <= maxTexels)
453 else if ((texRectWidth * tileHeight) <= maxTexels)
454 width = m_rdp->getTexRectWidth();
455 else if ((texRectWidth * texRectHeight) <= maxTexels)
456 width = m_rdp->getTexRectWidth();
457 else if (m_rdp->getLoadType() == LOADTYPE_TILE)
462 if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels))
464 else if ((tileWidth * tileHeight) <= maxTexels)
466 else if ((tileWidth * texRectHeight) <= maxTexels)
467 height = m_rdp->getTexRectHeight();
468 else if ((texRectWidth * tileHeight) <= maxTexels)
470 else if ((texRectWidth * texRectHeight) <= maxTexels)
471 height = m_rdp->getTexRectHeight();
472 else if (m_rdp->getLoadType() == LOADTYPE_TILE)
479 if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels))
480 width = maskWidth; // Use mask width if set and valid
481 else if ((tileWidth * tileHeight) <= maxTexels)
482 width = tileWidth; // else use tile width if valid
483 else if (m_rdp->getLoadType() == LOADTYPE_TILE)
484 width = loadWidth; // else use load width if load done with LoadTile
486 width = lineWidth; // else use line-based width
488 if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels))
490 else if ((tileWidth * tileHeight) <= maxTexels)
492 else if (m_rdp->getLoadType() == LOADTYPE_TILE)
498 unsigned int clampWidth = rspTile->clamps ? tileWidth : width;
499 unsigned int clampHeight = rspTile->clampt ? tileHeight : height;
501 if (clampWidth > 256)
503 if (clampHeight > 256)
506 // Make sure masking is valid
507 if (maskWidth > width)
509 rspTile->masks = powof( width );
510 maskWidth = 1 << rspTile->masks;
513 if (maskHeight > height)
515 rspTile->maskt = powof( height );
516 maskHeight = 1 << rspTile->maskt;
521 out->height = height;
522 out->clampWidth = clampWidth;
523 out->clampHeight = clampHeight;
524 out->maskS = m_rsp->getTile(tile)->masks;
525 out->maskT = m_rsp->getTile(tile)->maskt;
526 out->mirrorS = m_rsp->getTile(tile)->mirrors;
527 out->mirrorT = m_rsp->getTile(tile)->mirrort;
528 out->clampS = m_rsp->getTile(tile)->clamps;
529 out->clampT = m_rsp->getTile(tile)->clampt;
530 out->format = m_rsp->getTile(tile)->format;
531 out->size = m_rsp->getTile(tile)->size;
532 out->crc = _calculateCRC(tile, width, height );
535 unsigned int TextureCache::_calculateCRC(unsigned int t, unsigned int width, unsigned int height)
537 RDPTile* tile = m_rsp->getTile(t);
540 unsigned int y, bpl, line;
541 unsigned long long *src;
543 //TODO: remove if new works
544 //src = m_memory->getTextureMemory(tile->tmem);
545 bpl = width << tile->size >> 1;
548 if (tile->size == G_IM_SIZ_32b)
552 for (y=0; y<height; ++y)
554 src = m_memory->getTextureMemory((tile->tmem + (y * line)) & 511);
555 crc = m_crcCalculator.calcCRC( crc, src, bpl );
556 //TODO: remove if new works
560 if ( tile->format == G_IM_FMT_CI )
562 if ( tile->size == G_IM_SIZ_4b )
563 crc = m_crcCalculator.calcCRC( crc, &m_rdp->m_paletteCRC16[tile->palette], 4 );
564 else if (tile->size == G_IM_SIZ_8b)
565 crc = m_crcCalculator.calcCRC( crc, &m_rdp->m_paletteCRC256, 4 );
570 void TextureCache::_activateTexture( unsigned int t, CachedTexture *texture )
572 // If multitexturing, set the appropriate texture
573 //if (OGL.ARB_multitexture)
574 glActiveTextureARB( GL_TEXTURE0_ARB + t );
576 // Bind the cached texture
579 // Set filter mode. Almost always bilinear, but check anyways
580 unsigned int textureFiltering = m_rdp->getTextureFiltering();
581 if ( textureFiltering == G_TF_BILERP || textureFiltering == G_TF_AVERAGE )
586 if(m_mipmap == 1) // nearest
588 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
590 else if(m_mipmap == 2) // bilinear
592 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
594 else if(m_mipmap == 3) // trilinear
596 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
599 // Tell to hardware to generate mipmap (himself) when glTexImage2D is called
600 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
602 else // no mipmapping
604 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
605 glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE );
607 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
612 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
613 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
618 // Set clamping modes
619 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->clampS ? GL_CLAMP_TO_EDGE : GL_REPEAT );
620 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture->clampT ? GL_CLAMP_TO_EDGE : GL_REPEAT );
622 //texture->lastDList = RSP.DList;
624 moveToTop( texture );
626 m_currentTextures[t] = texture;