Arachnoid GLESv1.1 plugin. Compile and run (a bit glitchy and no frameskip) on the...
[mupen64plus-pandora.git] / source / mupen64plus-video-arachnoid / src / texture / TextureCache.cpp
CommitLineData
22726e4d 1/******************************************************************************
2 * Arachnoid Graphics Plugin for Mupen64Plus
3 * http://bitbucket.org/wahrhaft/mupen64plus-video-arachnoid/
4 *
5 * Copyright (C) 2007 Kristofer Karlsson, Rickard Niklasson
6 *
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.
11 *
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.
16 *
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 *****************************************************************************/
21
22#include "TextureCache.h"
23
24#include "RDP.h"
25#include "RSP.h"
26#include "CachedTexture.h"
27#include "MathLib.h"
28#include <algorithm>
29 using std::min;
30#include "m64p.h"
31#include "OpenGL.h"
32#include "Memory.h"
33#include "OpenGLRenderer.h"
34#include "MultiTexturingExt.h"
35 //gSPBgRect1Cyc
36//gSPBgRectCopy
37#define GL_CLAMP_TO_EDGE 0x812F
38#define GL_GENERATE_MIPMAP 0x8191
39
40#include "Logger.h"
41#include <iostream>
42
43//-----------------------------------------------------------------------------
44//! Constructor
45//-----------------------------------------------------------------------------
46TextureCache::TextureCache()
47{
48 m_currentTextures[0] = 0;
49 m_currentTextures[1] = 0;
50}
51
52//-----------------------------------------------------------------------------
53//! Destructor
54//-----------------------------------------------------------------------------
55TextureCache::~TextureCache()
56{
57 dispose();
58}
59
60//-----------------------------------------------------------------------------
61//* Initialize
62//-----------------------------------------------------------------------------
63bool TextureCache::initialize(RSP* rsp, RDP* rdp, Memory* memory, unsigned int textureBitDepth, unsigned int cacheSize)
64{
65 m_rsp = rsp;
66 m_rdp = rdp;
67 m_memory = memory;
68 m_bitDepth = textureBitDepth;
69 m_maxBytes = cacheSize;
70
71 return true;
72}
73
74//-----------------------------------------------------------------------------
75//* Update
76//-----------------------------------------------------------------------------
77void TextureCache::update(unsigned int tile)
78{
79 //if (cache.bitDepth != OGL.textureBitDepth)
80 //{
81 // TextureCache_Destroy();
82 // TextureCache_Init();
83 //}
84
85 //Special textures?
86 if ( m_rdp->getTextureMode() == TM_BGIMAGE )
87 {
88 return;
89 }
90 else if ( m_rdp->getTextureMode() == TM_FRAMEBUFFER )
91 {
92 return;
93 }
94
95
96 CachedTexture temp;
97 unsigned int maskWidth = 0, maskHeight = 0;
98 _calculateTextureSize(tile, &temp, maskWidth, maskHeight);
99
100 static int hits = 0;
101 static int misses = 0;
102
103 //For each texture in texture cache
104 for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it)
105 {
106 CachedTexture* temp2 = (*it);
107
108 if ( *temp2 == temp )
109 {
110 _activateTexture( tile, (*it) );
111 hits++;
112 return;
113 }
114 }
115 misses++;
116
117 // If multitexturing, set the appropriate texture
118 //if (OGL.ARB_multitexture)
119 glActiveTextureARB( GL_TEXTURE0_ARB + tile );
120
121 //Add new texture to cache
122 m_currentTextures[tile] = addTop();
123 m_currentTextures[tile]->activate();
124
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;
131
132 m_currentTextures[tile]->format = m_rsp->getTile(tile)->format;
133 m_currentTextures[tile]->size = m_rsp->getTile(tile)->size;
134
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;
150
151
152
153// cache.current[tile]->lastDList = RSP.DList;
154// cache.current[tile]->frameBufferTexture = FALSE;
155
156 //Calculate Real Width
157 if (m_currentTextures[tile]->clampS)
158 {
159 m_currentTextures[tile]->realWidth = pow2( temp.clampWidth );
160 }
161 else if (m_currentTextures[tile]->mirrorS)
162 {
163 m_currentTextures[tile]->realWidth = maskWidth << 1;
164 }
165 else
166 {
167 m_currentTextures[tile]->realWidth = pow2( temp.width );
168 }
169
170 //Calculate Real Height
171 if (m_currentTextures[tile]->clampT)
172 {
173 m_currentTextures[tile]->realHeight = pow2( temp.clampHeight );
174 }
175 else if (m_currentTextures[tile]->mirrorT)
176 {
177 m_currentTextures[tile]->realHeight = maskHeight << 1;
178 }
179 else
180 {
181 m_currentTextures[tile]->realHeight = pow2( temp.height );
182 }
183
184 //Calculate Scale
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;
189 #if 0
190 //m_currentTextures[tile]->offsetS = OGL.enable2xSaI ? 0.25f : 0.5f;
191 //m_currentTextures[tile]->offsetT = OGL.enable2xSaI ? 0.25f : 0.5f;
192 #else
193 m_currentTextures[tile]->offsetS = 0.5f;
194 m_currentTextures[tile]->offsetT = 0.5f;
195 #endif
196
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);
201
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);
206
207
208
209
210
211 _loadTexture( m_currentTextures[tile] );
212 _activateTexture( tile, m_currentTextures[tile] );
213
214 m_cachedBytes += m_currentTextures[tile]->getTextureSize();
215
216}
217
218//-----------------------------------------------------------------------------
219//* Add Top
220//! Adds a texture to cache
221//-----------------------------------------------------------------------------
222CachedTexture* TextureCache::addTop()
223{
224 //If no memory left, remove old textures from cache
225 while ( m_cachedBytes > m_maxBytes )
226 {
227 this->removeBottom();
228 }
229
230 //Allocate memory
231 CachedTexture* newTexture = new CachedTexture();
232
233 //Generate a texture
234 glGenTextures(1, &newTexture->m_id);
235
236 //Add Texture to cache
237 m_cachedTextures.push_front(newTexture);
238
239 return newTexture;
240}
241
242//-----------------------------------------------------------------------------
243// Remove Bottom
244//-----------------------------------------------------------------------------
245void TextureCache::removeBottom()
246{
247 //Get Last Texture in list
248 CachedTexture* lastTexture = *(--m_cachedTextures.end());
249
250 //Remove Texture
251 m_cachedTextures.pop_back();
252 m_cachedBytes -= lastTexture->getTextureSize();
253
254 //if (cache.bottom->frameBufferTexture)
255 // FrameBuffer_RemoveBuffer( cache.bottom->address );
256
257 //Delete texture
258 glDeleteTextures(1, &lastTexture->m_id);
259
260 delete lastTexture;
261}
262
263//-----------------------------------------------------------------------------
264// Remove
265//-----------------------------------------------------------------------------
266void TextureCache::remove( CachedTexture *texture )
267{
268
269}
270
271//-----------------------------------------------------------------------------
272//Move Texture to top
273//-----------------------------------------------------------------------------
274void TextureCache::moveToTop( CachedTexture *newtop )
275{
276 //Get Texture
277 TextureList::iterator it = std::find( m_cachedTextures.begin(), m_cachedTextures.end(), newtop);
278
279 //Erase Texture
280 if ( it != m_cachedTextures.end() )
281 {
282 m_cachedTextures.erase(it);
283 }
284
285 //Add texture to the front of the list
286 m_cachedTextures.push_front(newtop);
287}
288
289//-----------------------------------------------------------------------------
290// Dispose
291//-----------------------------------------------------------------------------
292void TextureCache::dispose()
293{
294 //For each texture
295 for (TextureList::iterator it=m_cachedTextures.begin(); it!=m_cachedTextures.end(); ++it )
296 {
297 delete (*it);
298 }
299 m_cachedTextures.clear();
300}
301
302//-----------------------------------------------------------------------------
303// Load Texture
304//-----------------------------------------------------------------------------
305void TextureCache::_loadTexture(CachedTexture* texture)
306{
307 //Select Image Type
308 GetTexelFunc getTexelFunc;
309 unsigned int internalFormat;
310 int imageType;
311 m_formatSelector.detectImageFormat(texture, m_bitDepth, getTexelFunc, internalFormat, imageType, m_rdp->getTextureLUT());
312
313 //Allocate memory
314 unsigned int* dest = new unsigned int[ texture->getTextureSize() ];
315
316 //Get Line Size
317 unsigned short line = (unsigned short)texture->line;
318 if (texture->size == G_IM_SIZ_32b)
319 line <<= 1;
320
321 //
322 //Work Your magic
323 //
324
325 unsigned short mirrorSBit, maskSMask, clampSClamp;
326 unsigned short mirrorTBit, maskTMask, clampTClamp;
327
328 if (texture->maskS)
329 {
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;
333 }
334 else
335 {
336 clampSClamp = (unsigned short)min( texture->clampWidth, texture->width ) - 1;
337 maskSMask = 0xFFFF;
338 mirrorSBit = 0x0000;
339 }
340
341 if (texture->maskT)
342 {
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;
346 }
347 else
348 {
349 clampTClamp = (unsigned short)min( texture->clampHeight, texture->height ) - 1;
350 maskTMask = 0xFFFF;
351 mirrorTBit = 0x0000;
352 }
353
354 // Hack for Zelda warp texture
355 if (((texture->tMem << 3) + (texture->width * texture->height << texture->size >> 1)) > 4096)
356 texture->tMem = 0;
357
358 // limit clamp values to min-0 (Perfect Dark has height=0 textures, making negative clamps)
359 if (clampTClamp & 0x8000)
360 clampTClamp = 0;
361 if (clampSClamp & 0x8000)
362 clampSClamp = 0;
363
364 //
365 //Retrive texture from source (TMEM) and copy it to dest
366 //
367
368 unsigned short x, y, i, j, tx, ty;
369
370 unsigned long long* src;
371
372 j = 0;
373 for (y = 0; y < texture->realHeight; y++)
374 {
375 ty = min(y, clampTClamp) & maskTMask;
376
377 if (y & mirrorTBit) {
378 ty ^= maskTMask;
379 }
380
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);
384
385
386 i = (ty & 1) << 1;
387 for (x = 0; x < texture->realWidth; x++)
388 {
389 tx = min(x, clampSClamp) & maskSMask;
390
391 if (x & mirrorSBit)
392 tx ^= maskSMask;
393
394 if (internalFormat == GL_RGBA8)
395 ((unsigned int*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette );
396 else
397 ((unsigned short*)dest)[j++] = getTexelFunc( src, tx, i, texture->palette );
398 }
399 }
400
401 //Send Texture to OpenGL
402#ifdef HAVE_GLES
403 glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest );
404#else
405 glTexImage2D( GL_TEXTURE_2D, 0, internalFormat, texture->realWidth, texture->realHeight, 0, GL_RGBA, imageType, dest );
406#endif
407 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
408 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
409 delete[] dest;
410}
411
412
413void TextureCache::_calculateTextureSize(unsigned int tile, CachedTexture* out, unsigned int& maskWidth, unsigned int& maskHeight )
414{
415 RDPTile* rspTile = m_rsp->getTile(tile);
416
417 //Calculate Tile Size
418 unsigned int tileWidth = rspTile->getWidth();
419 unsigned int tileHeight = rspTile->getHeight();
420
421 //Get Mask Size
422 maskWidth = 1 << rspTile->masks;
423 maskHeight = 1 << rspTile->maskt;
424
425 //Get Current Tile Size
426 unsigned int loadWidth = m_rdp->getCurrentTile()->getWidth();
427 unsigned int loadHeight = m_rdp->getCurrentTile()->getHeight();
428
429 unsigned int maxTexels = ImageFormatSelector::imageFormats[rspTile->size][rspTile->format].maxTexels;
430
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 );
436 else
437 lineHeight = 0;
438
439 unsigned int width;
440 unsigned int height;
441
442 if ( m_rdp->getTextureMode() == TM_TEXRECT )
443 {
444 unsigned short texRectWidth = (unsigned short)(m_rdp->getTexRectWidth() - rspTile->uls);
445 unsigned short texRectHeight = (unsigned short)(m_rdp->getTexRectHeight() - rspTile->ult);
446
447 if (rspTile->masks && ((maskWidth * maskHeight) <= maxTexels))
448 width = maskWidth;
449 else if ((tileWidth * tileHeight) <= maxTexels)
450 width = tileWidth;
451 else if ((tileWidth * texRectHeight) <= maxTexels)
452 width = tileWidth;
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)
458 width = loadWidth;
459 else
460 width = lineWidth;
461
462 if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels))
463 height = maskHeight;
464 else if ((tileWidth * tileHeight) <= maxTexels)
465 height = tileHeight;
466 else if ((tileWidth * texRectHeight) <= maxTexels)
467 height = m_rdp->getTexRectHeight();
468 else if ((texRectWidth * tileHeight) <= maxTexels)
469 height = tileHeight;
470 else if ((texRectWidth * texRectHeight) <= maxTexels)
471 height = m_rdp->getTexRectHeight();
472 else if (m_rdp->getLoadType() == LOADTYPE_TILE)
473 height = loadHeight;
474 else
475 height = lineHeight;
476 }
477 else
478 {
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
485 else
486 width = lineWidth; // else use line-based width
487
488 if (rspTile->maskt && ((maskWidth * maskHeight) <= maxTexels))
489 height = maskHeight;
490 else if ((tileWidth * tileHeight) <= maxTexels)
491 height = tileHeight;
492 else if (m_rdp->getLoadType() == LOADTYPE_TILE)
493 height = loadHeight;
494 else
495 height = lineHeight;
496 }
497
498 unsigned int clampWidth = rspTile->clamps ? tileWidth : width;
499 unsigned int clampHeight = rspTile->clampt ? tileHeight : height;
500
501 if (clampWidth > 256)
502 rspTile->clamps = 0;
503 if (clampHeight > 256)
504 rspTile->clampt = 0;
505
506 // Make sure masking is valid
507 if (maskWidth > width)
508 {
509 rspTile->masks = powof( width );
510 maskWidth = 1 << rspTile->masks;
511 }
512
513 if (maskHeight > height)
514 {
515 rspTile->maskt = powof( height );
516 maskHeight = 1 << rspTile->maskt;
517 }
518
519 //Set output data
520 out->width = width;
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 );
533}
534
535unsigned int TextureCache::_calculateCRC(unsigned int t, unsigned int width, unsigned int height)
536{
537 RDPTile* tile = m_rsp->getTile(t);
538
539 unsigned int crc;
540 unsigned int y, bpl, line;
541 unsigned long long *src;
542
543 //TODO: remove if new works
544 //src = m_memory->getTextureMemory(tile->tmem);
545 bpl = width << tile->size >> 1;
546
547 line = tile->line;
548 if (tile->size == G_IM_SIZ_32b)
549 line <<= 1;
550
551 crc = 0xFFFFFFFF;
552 for (y=0; y<height; ++y)
553 {
554 src = m_memory->getTextureMemory((tile->tmem + (y * line)) & 511);
555 crc = m_crcCalculator.calcCRC( crc, src, bpl );
556 //TODO: remove if new works
557 //src += line;
558 }
559
560 if ( tile->format == G_IM_FMT_CI )
561 {
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 );
566 }
567 return crc;
568}
569
570void TextureCache::_activateTexture( unsigned int t, CachedTexture *texture )
571{
572 // If multitexturing, set the appropriate texture
573 //if (OGL.ARB_multitexture)
574 glActiveTextureARB( GL_TEXTURE0_ARB + t );
575
576 // Bind the cached texture
577 texture->activate();
578
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 )
582 {
583 if( m_mipmap > 0 )
584 {
585 // Set Mipmap
586 if(m_mipmap == 1) // nearest
587 {
588 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
589 }
590 else if(m_mipmap == 2) // bilinear
591 {
592 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
593 }
594 else if(m_mipmap == 3) // trilinear
595 {
596 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
597 }
598
599 // Tell to hardware to generate mipmap (himself) when glTexImage2D is called
600 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
601 }
602 else // no mipmapping
603 {
604 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
605 glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE );
606 }
607 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
608
609 }
610 else
611 {
612 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
613 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
614 }
615
616
617
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 );
621
622 //texture->lastDList = RSP.DList;
623
624 moveToTop( texture );
625
626 m_currentTextures[t] = texture;
627}