X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=source%2Fmupen64plus-video-arachnoid%2Fsrc%2Ftexture%2FTextureCache.cpp;fp=source%2Fmupen64plus-video-arachnoid%2Fsrc%2Ftexture%2FTextureCache.cpp;h=c30e7f10fd80dd12a5fca03856472b583359ba9c;hb=22726e4d55be26faa48b57b22689cbedde27ae44;hp=0000000000000000000000000000000000000000;hpb=fc5d46b49a19d41f9f2da5a9336daec452900475;p=mupen64plus-pandora.git diff --git a/source/mupen64plus-video-arachnoid/src/texture/TextureCache.cpp b/source/mupen64plus-video-arachnoid/src/texture/TextureCache.cpp new file mode 100755 index 0000000..c30e7f1 --- /dev/null +++ b/source/mupen64plus-video-arachnoid/src/texture/TextureCache.cpp @@ -0,0 +1,627 @@ +/****************************************************************************** + * Arachnoid Graphics Plugin for Mupen64Plus + * http://bitbucket.org/wahrhaft/mupen64plus-video-arachnoid/ + * + * Copyright (C) 2007 Kristofer Karlsson, Rickard Niklasson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + *****************************************************************************/ + +#include "TextureCache.h" + +#include "RDP.h" +#include "RSP.h" +#include "CachedTexture.h" +#include "MathLib.h" +#include + using std::min; +#include "m64p.h" +#include "OpenGL.h" +#include "Memory.h" +#include "OpenGLRenderer.h" +#include "MultiTexturingExt.h" + //gSPBgRect1Cyc +//gSPBgRectCopy +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_GENERATE_MIPMAP 0x8191 + +#include "Logger.h" +#include + +//----------------------------------------------------------------------------- +//! Constructor +//----------------------------------------------------------------------------- +TextureCache::TextureCache() +{ + m_currentTextures[0] = 0; + m_currentTextures[1] = 0; +} + +//----------------------------------------------------------------------------- +//! Destructor +//----------------------------------------------------------------------------- +TextureCache::~TextureCache() +{ + dispose(); +} + +//----------------------------------------------------------------------------- +//* Initialize +//----------------------------------------------------------------------------- +bool TextureCache::initialize(RSP* rsp, RDP* rdp, Memory* memory, unsigned int textureBitDepth, unsigned int cacheSize) +{ + m_rsp = rsp; + m_rdp = rdp; + m_memory = memory; + m_bitDepth = textureBitDepth; + m_maxBytes = cacheSize; + + return true; +} + +//----------------------------------------------------------------------------- +//* Update +//----------------------------------------------------------------------------- +void TextureCache::update(unsigned int tile) +{ + //if (cache.bitDepth != OGL.textureBitDepth) + //{ + // TextureCache_Destroy(); + // TextureCache_Init(); + //} + + //Special textures? + if ( m_rdp->getTextureMode() == TM_BGIMAGE ) + { + return; + } + else if ( m_rdp->getTextureMode() == TM_FRAMEBUFFER ) + { + return; + } + + + CachedTexture temp; + unsigned int maskWidth = 0, maskHeight = 0; + _calculateTextureSize(tile, &temp, maskWidth, maskHeight); + + static int hits = 0; + static int misses = 0; + + //For each texture in texture cache + for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it) + { + CachedTexture* temp2 = (*it); + + if ( *temp2 == temp ) + { + _activateTexture( tile, (*it) ); + hits++; + return; + } + } + misses++; + + // If multitexturing, set the appropriate texture + //if (OGL.ARB_multitexture) + glActiveTextureARB( GL_TEXTURE0_ARB + tile ); + + //Add new texture to cache + m_currentTextures[tile] = addTop(); + m_currentTextures[tile]->activate(); + + m_currentTextures[tile]->address = m_rdp->getTextureImage()->address; + m_currentTextures[tile]->crc = temp.crc; + m_currentTextures[tile]->width = temp.width; + m_currentTextures[tile]->height = temp.height; + m_currentTextures[tile]->clampWidth = temp.clampWidth; + m_currentTextures[tile]->clampHeight = temp.clampHeight; + + m_currentTextures[tile]->format = m_rsp->getTile(tile)->format; + m_currentTextures[tile]->size = m_rsp->getTile(tile)->size; + + m_currentTextures[tile]->palette = m_rsp->getTile(tile)->palette; +/* m_currentTextures[tile]->fulS = rsp.getTile(tile)->fulS; + m_currentTextures[tile]->fulT = rsp.getTile(tile)->fulT; + m_currentTextures[tile]->ulS = rsp.getTile(tile)->ulS; + m_currentTextures[tile]->ulT = rsp.getTile(tile)->ulT; + m_currentTextures[tile]->lrS = rsp.getTile(tile)->lrS; + m_currentTextures[tile]->lrT = rsp.getTile(tile)->lrT;*/ + m_currentTextures[tile]->maskS = m_rsp->getTile(tile)->masks; + m_currentTextures[tile]->maskT = m_rsp->getTile(tile)->maskt; + m_currentTextures[tile]->mirrorS = m_rsp->getTile(tile)->mirrors; + m_currentTextures[tile]->mirrorT = m_rsp->getTile(tile)->mirrort; + m_currentTextures[tile]->clampS = m_rsp->getTile(tile)->clamps; + m_currentTextures[tile]->clampT = m_rsp->getTile(tile)->clampt; + m_currentTextures[tile]->line = m_rsp->getTile(tile)->line; + m_currentTextures[tile]->tMem = m_rsp->getTile(tile)->tmem; + + + +// cache.current[tile]->lastDList = RSP.DList; +// cache.current[tile]->frameBufferTexture = FALSE; + + //Calculate Real Width + if (m_currentTextures[tile]->clampS) + { + m_currentTextures[tile]->realWidth = pow2( temp.clampWidth ); + } + else if (m_currentTextures[tile]->mirrorS) + { + m_currentTextures[tile]->realWidth = maskWidth << 1; + } + else + { + m_currentTextures[tile]->realWidth = pow2( temp.width ); + } + + //Calculate Real Height + if (m_currentTextures[tile]->clampT) + { + m_currentTextures[tile]->realHeight = pow2( temp.clampHeight ); + } + else if (m_currentTextures[tile]->mirrorT) + { + m_currentTextures[tile]->realHeight = maskHeight << 1; + } + else + { + m_currentTextures[tile]->realHeight = pow2( temp.height ); + } + + //Calculate Scale + m_currentTextures[tile]->scaleS = 1.0f / (float)(m_currentTextures[tile]->realWidth); + m_currentTextures[tile]->scaleT = 1.0f / (float)(m_currentTextures[tile]->realHeight); + m_currentTextures[tile]->shiftScaleS = 1.0f; + m_currentTextures[tile]->shiftScaleT= 1.0f; + #if 0 + //m_currentTextures[tile]->offsetS = OGL.enable2xSaI ? 0.25f : 0.5f; + //m_currentTextures[tile]->offsetT = OGL.enable2xSaI ? 0.25f : 0.5f; + #else + m_currentTextures[tile]->offsetS = 0.5f; + m_currentTextures[tile]->offsetT = 0.5f; + #endif + + if (m_rsp->getTile(tile)->shifts > 10) + m_currentTextures[tile]->shiftScaleS = (float)(1 << (16 - m_rsp->getTile(tile)->shifts)); + else if (m_rsp->getTile(tile)->shifts > 0) + m_currentTextures[tile]->shiftScaleS /= (float)(1 << m_rsp->getTile(tile)->shifts); + + if (m_rsp->getTile(tile)->shiftt > 10) + m_currentTextures[tile]->shiftScaleT = (float)(1 << (16 - m_rsp->getTile(tile)->shiftt)); + else if (m_rsp->getTile(tile)->shiftt > 0) + m_currentTextures[tile]->shiftScaleT /= (float)(1 << m_rsp->getTile(tile)->shiftt); + + + + + + _loadTexture( m_currentTextures[tile] ); + _activateTexture( tile, m_currentTextures[tile] ); + + m_cachedBytes += m_currentTextures[tile]->getTextureSize(); + +} + +//----------------------------------------------------------------------------- +//* Add Top +//! Adds a texture to cache +//----------------------------------------------------------------------------- +CachedTexture* TextureCache::addTop() +{ + //If no memory left, remove old textures from cache + while ( m_cachedBytes > m_maxBytes ) + { + this->removeBottom(); + } + + //Allocate memory + CachedTexture* newTexture = new CachedTexture(); + + //Generate a texture + glGenTextures(1, &newTexture->m_id); + + //Add Texture to cache + m_cachedTextures.push_front(newTexture); + + return newTexture; +} + +//----------------------------------------------------------------------------- +// Remove Bottom +//----------------------------------------------------------------------------- +void TextureCache::removeBottom() +{ + //Get Last Texture in list + CachedTexture* lastTexture = *(--m_cachedTextures.end()); + + //Remove Texture + m_cachedTextures.pop_back(); + m_cachedBytes -= lastTexture->getTextureSize(); + + //if (cache.bottom->frameBufferTexture) + // FrameBuffer_RemoveBuffer( cache.bottom->address ); + + //Delete texture + glDeleteTextures(1, &lastTexture->m_id); + + delete lastTexture; +} + +//----------------------------------------------------------------------------- +// Remove +//----------------------------------------------------------------------------- +void TextureCache::remove( CachedTexture *texture ) +{ + +} + +//----------------------------------------------------------------------------- +//Move Texture to top +//----------------------------------------------------------------------------- +void TextureCache::moveToTop( CachedTexture *newtop ) +{ + //Get Texture + TextureList::iterator it = std::find( m_cachedTextures.begin(), m_cachedTextures.end(), newtop); + + //Erase Texture + if ( it != m_cachedTextures.end() ) + { + m_cachedTextures.erase(it); + } + + //Add texture to the front of the list + m_cachedTextures.push_front(newtop); +} + +//----------------------------------------------------------------------------- +// Dispose +//----------------------------------------------------------------------------- +void TextureCache::dispose() +{ + //For each texture + for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it ) + { + delete (*it); + } + m_cachedTextures.clear(); +} + +//----------------------------------------------------------------------------- +// Load Texture +//----------------------------------------------------------------------------- +void TextureCache::_loadTexture(CachedTexture* texture) +{ + //Select Image Type + GetTexelFunc getTexelFunc; + unsigned int internalFormat; + int imageType; + m_formatSelector.detectImageFormat(texture, m_bitDepth, getTexelFunc, internalFormat, imageType, m_rdp->getTextureLUT()); + + //Allocate memory + unsigned int* dest = new unsigned int[ texture->getTextureSize() ]; + + //Get Line Size + unsigned short line = (unsigned short)texture->line; + if (texture->size == G_IM_SIZ_32b) + line <<= 1; + + // + //Work Your magic + // + + unsigned short mirrorSBit, maskSMask, clampSClamp; + unsigned short mirrorTBit, maskTMask, clampTClamp; + + if (texture->maskS) + { + clampSClamp = (unsigned short)(texture->clampS ? texture->clampWidth - 1 : (texture->mirrorS ? (texture->width << 1) - 1 : texture->width - 1)); + maskSMask = (1 << texture->maskS) - 1; + mirrorSBit = texture->mirrorS ? 1 << texture->maskS : 0; + } + else + { + clampSClamp = (unsigned short)min( texture->clampWidth, texture->width ) - 1; + maskSMask = 0xFFFF; + mirrorSBit = 0x0000; + } + + if (texture->maskT) + { + clampTClamp = (unsigned short)(texture->clampT ? texture->clampHeight - 1 : (texture->mirrorT ? (texture->height << 1) - 1: texture->height - 1)); + maskTMask = (1 << texture->maskT) - 1; + mirrorTBit = texture->mirrorT ? 1 << texture->maskT : 0; + } + else + { + clampTClamp = (unsigned short)min( texture->clampHeight, texture->height ) - 1; + maskTMask = 0xFFFF; + mirrorTBit = 0x0000; + } + + // Hack for Zelda warp texture + if (((texture->tMem << 3) + (texture->width * texture->height << texture->size >> 1)) > 4096) + texture->tMem = 0; + + // limit clamp values to min-0 (Perfect Dark has height=0 textures, making negative clamps) + if (clampTClamp & 0x8000) + clampTClamp = 0; + if (clampSClamp & 0x8000) + clampSClamp = 0; + + // + //Retrive texture from source (TMEM) and copy it to dest + // + + unsigned short x, y, i, j, tx, ty; + + unsigned long long* src; + + j = 0; + for (y = 0; y < texture->realHeight; y++) + { + ty = min(y, clampTClamp) & maskTMask; + + if (y & mirrorTBit) { + ty ^= maskTMask; + } + + //TODO: remove old if new works + //src = m_memory->getTextureMemory(texture->tMem) + line * ty; + src = m_memory->getTextureMemory((texture->tMem + line * ty) & 511); + + + i = (ty & 1) << 1; + for (x = 0; x < texture->realWidth; x++) + { + tx = min(x, clampSClamp) & maskSMask; + + if (x & mirrorSBit) + tx ^= maskSMask; + + if (internalFormat == GL_RGBA8) + ((unsigned int*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette ); + else + ((unsigned short*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette ); + } + } + + //Send Texture to OpenGL +#ifdef HAVE_GLES + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest ); +#else + glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest ); +#endif + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + delete[] dest; +} + + +void TextureCache::_calculateTextureSize(unsigned int tile, CachedTexture* out, unsigned int& maskWidth, unsigned int& maskHeight ) +{ + RDPTile* rspTile = m_rsp->getTile(tile); + + //Calculate Tile Size + unsigned int tileWidth = rspTile->getWidth(); + unsigned int tileHeight = rspTile->getHeight(); + + //Get Mask Size + maskWidth = 1 << rspTile->masks; + maskHeight = 1 << rspTile->maskt; + + //Get Current Tile Size + unsigned int loadWidth = m_rdp->getCurrentTile()->getWidth(); + unsigned int loadHeight = m_rdp->getCurrentTile()->getHeight(); + + unsigned int maxTexels = ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].maxTexels; + + //Get Line Width (depending on imageformat) + unsigned int lineWidth = rspTile->line << ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].lineShift; + unsigned int lineHeight; + if ( lineWidth ) // Don't allow division by zero + lineHeight = min( maxTexels / lineWidth, tileHeight ); + else + lineHeight = 0; + + unsigned int width; + unsigned int height; + + if ( m_rdp->getTextureMode() == TM_TEXRECT ) + { + unsigned short texRectWidth = (unsigned short)(m_rdp->getTexRectWidth() - rspTile->uls); + unsigned short texRectHeight = (unsigned short)(m_rdp->getTexRectHeight() - rspTile->ult); + + if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels)) + width = maskWidth; + else if ((tileWidth * tileHeight) <= maxTexels) + width = tileWidth; + else if ((tileWidth * texRectHeight) <= maxTexels) + width = tileWidth; + else if ((texRectWidth * tileHeight) <= maxTexels) + width = m_rdp->getTexRectWidth(); + else if ((texRectWidth * texRectHeight) <= maxTexels) + width = m_rdp->getTexRectWidth(); + else if (m_rdp->getLoadType() == LOADTYPE_TILE) + width = loadWidth; + else + width = lineWidth; + + if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels)) + height = maskHeight; + else if ((tileWidth * tileHeight) <= maxTexels) + height = tileHeight; + else if ((tileWidth * texRectHeight) <= maxTexels) + height = m_rdp->getTexRectHeight(); + else if ((texRectWidth * tileHeight) <= maxTexels) + height = tileHeight; + else if ((texRectWidth * texRectHeight) <= maxTexels) + height = m_rdp->getTexRectHeight(); + else if (m_rdp->getLoadType() == LOADTYPE_TILE) + height = loadHeight; + else + height = lineHeight; + } + else + { + if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels)) + width = maskWidth; // Use mask width if set and valid + else if ((tileWidth * tileHeight) <= maxTexels) + width = tileWidth; // else use tile width if valid + else if (m_rdp->getLoadType() == LOADTYPE_TILE) + width = loadWidth; // else use load width if load done with LoadTile + else + width = lineWidth; // else use line-based width + + if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels)) + height = maskHeight; + else if ((tileWidth * tileHeight) <= maxTexels) + height = tileHeight; + else if (m_rdp->getLoadType() == LOADTYPE_TILE) + height = loadHeight; + else + height = lineHeight; + } + + unsigned int clampWidth = rspTile->clamps ? tileWidth : width; + unsigned int clampHeight = rspTile->clampt ? tileHeight : height; + + if (clampWidth > 256) + rspTile->clamps = 0; + if (clampHeight > 256) + rspTile->clampt = 0; + + // Make sure masking is valid + if (maskWidth > width) + { + rspTile->masks = powof( width ); + maskWidth = 1 << rspTile->masks; + } + + if (maskHeight > height) + { + rspTile->maskt = powof( height ); + maskHeight = 1 << rspTile->maskt; + } + + //Set output data + out->width = width; + out->height = height; + out->clampWidth = clampWidth; + out->clampHeight = clampHeight; + out->maskS = m_rsp->getTile(tile)->masks; + out->maskT = m_rsp->getTile(tile)->maskt; + out->mirrorS = m_rsp->getTile(tile)->mirrors; + out->mirrorT = m_rsp->getTile(tile)->mirrort; + out->clampS = m_rsp->getTile(tile)->clamps; + out->clampT = m_rsp->getTile(tile)->clampt; + out->format = m_rsp->getTile(tile)->format; + out->size = m_rsp->getTile(tile)->size; + out->crc = _calculateCRC(tile, width, height ); +} + +unsigned int TextureCache::_calculateCRC(unsigned int t, unsigned int width, unsigned int height) +{ + RDPTile* tile = m_rsp->getTile(t); + + unsigned int crc; + unsigned int y, bpl, line; + unsigned long long *src; + + //TODO: remove if new works + //src = m_memory->getTextureMemory(tile->tmem); + bpl = width << tile->size >> 1; + + line = tile->line; + if (tile->size == G_IM_SIZ_32b) + line <<= 1; + + crc = 0xFFFFFFFF; + for (y=0; ygetTextureMemory((tile->tmem + (y * line)) & 511); + crc = m_crcCalculator.calcCRC( crc, src, bpl ); + //TODO: remove if new works + //src += line; + } + + if ( tile->format == G_IM_FMT_CI ) + { + if ( tile->size == G_IM_SIZ_4b ) + crc = m_crcCalculator.calcCRC( crc, &m_rdp->m_paletteCRC16[tile->palette], 4 ); + else if (tile->size == G_IM_SIZ_8b) + crc = m_crcCalculator.calcCRC( crc, &m_rdp->m_paletteCRC256, 4 ); + } + return crc; +} + +void TextureCache::_activateTexture( unsigned int t, CachedTexture *texture ) +{ + // If multitexturing, set the appropriate texture + //if (OGL.ARB_multitexture) + glActiveTextureARB( GL_TEXTURE0_ARB + t ); + + // Bind the cached texture + texture->activate(); + + // Set filter mode. Almost always bilinear, but check anyways + unsigned int textureFiltering = m_rdp->getTextureFiltering(); + if ( textureFiltering == G_TF_BILERP || textureFiltering == G_TF_AVERAGE ) + { + if( m_mipmap > 0 ) + { + // Set Mipmap + if(m_mipmap == 1) // nearest + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + } + else if(m_mipmap == 2) // bilinear + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + } + else if(m_mipmap == 3) // trilinear + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + + // Tell to hardware to generate mipmap (himself) when glTexImage2D is called + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + } + else // no mipmapping + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE ); + } + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + } + + + + // Set clamping modes + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture->clampS ? GL_CLAMP_TO_EDGE : GL_REPEAT ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture->clampT ? GL_CLAMP_TO_EDGE : GL_REPEAT ); + + //texture->lastDList = RSP.DList; + + moveToTop( texture ); + + m_currentTextures[t] = texture; +}