ulibrary/source/texVramManager.c
2024-08-06 01:10:18 +01:00

449 lines
13 KiB
C

#include "ulib.h"
// TODO: Calculer la taille totale de la VRAM textures en fonction de l'allocation des banques
// On commence au début de la VRAM mappée en LCD
void *ul_texVramBase = (void*)0x06800000;
// 128 ko par défaut
int ul_texVramSize = 128 << 10;
// Banque A seulement par défaut
UL_BANKS ul_texVramBanks = UL_BANK_A;
u8 ul_optimizeTextureSize = 1;
// By default, initialize every texture to zero
UL_TEXINITZERO_TYPE ul_initTexturesToZero = UL_TEXINITZERO_ALL;
#define DEFAULT_TABLE_SIZE 1024
int ulTexRamBlocksMax, ulTexRamBlocksNb;
typedef struct
{
u16 offset, size;
} UL_TEXRAMBLOCK;
#define isBlockFree(i) (ulTexRamBlocks[i].size & 0x8000)
#define getBlockSize(i) (ulTexRamBlocks[i].size & 0x7fff)
#define getBlockOffset(i) (ulTexRamBlocks[i].offset)
#define setBlockFree(i, free) \
(ulTexRamBlocks[i].size = (ulTexRamBlocks[i].size & ((free)? 0xffff : 0x7fff)) | ((free)? 0x8000 : 0))
#define setBlockSize(i, nsize) \
(ulTexRamBlocks[i].size = (ulTexRamBlocks[i].size & ~0x7fff) | (nsize))
#define setBlockOffset(i, noffset) \
(ulTexRamBlocks[i].offset = noffset)
// >> 3 dans textureparams et << 4 (mult. 16 ici)
#define getTextureOffset(name) ((ulTextureParams[name] & 0xFFFF) >> 1)
UL_TEXRAMBLOCK *ulTexRamBlocks;
void ulTexVramInit()
{
ulTexRamBlocksMax = DEFAULT_TABLE_SIZE;
ulTexRamBlocksNb = 1;
ulTexRamBlocks = (UL_TEXRAMBLOCK*)calloc(1, ulTexRamBlocksMax * sizeof(UL_TEXRAMBLOCK));
// Premier bloc: libre, taille totale de la VRAM, adresse 0
setBlockOffset(0, 0);
// La taille en blocs doit être divisée par 16 puisqu'on n'utilise pas des
// octets sinon il serait impossible de coder toute la VRAM sur 16 bits
setBlockSize(0, ul_texVramSize >> 4);
setBlockFree(0, 1);
}
int ulTexVramAllocBlock(int blockSize)
{
int i;
// Le bloc ne peut pas être de taille nulle ou négative
if (blockSize <= 0)
return -1;
// La taille est toujours multiple de 16 - arrondir au bloc supérieur
if (blockSize & 15)
blockSize += 16;
blockSize >>= 4;
for (i = 0; i < ulTexRamBlocksNb; i++)
{
// Ce bloc est-il suffisant?
if (isBlockFree(i) && getBlockSize(i) >= blockSize)
break;
}
// Aucun bloc libre
if (i >= ulTexRamBlocksNb)
return -1;
// Pile la mémoire qu'il faut? - pas géré, il faut toujours que le dernier
// bloc soit marqué comme libre (même s'il reste 0 octet) pour
// ulSetTexVramParameters()
if (getBlockSize(i) == blockSize && i != ulTexRamBlocksNb - 1)
{
// Il n'est plus libre
setBlockFree(i, 0);
}
else
{
// On va ajouter un nouveau bloc
ulTexRamBlocksNb++;
// Plus de mémoire pour le tableau? On l'aggrandit
if (ulTexRamBlocksNb >= ulTexRamBlocksMax)
{
UL_TEXRAMBLOCK *oldBlock = ulTexRamBlocks;
ulTexRamBlocksMax += DEFAULT_TABLE_SIZE;
ulTexRamBlocks = (UL_TEXRAMBLOCK*)realloc(ulTexRamBlocks, ulTexRamBlocksMax);
// Vérification que la mémoire a bien pu être allouée
if (!ulTexRamBlocks)
{
ulTexRamBlocks = oldBlock;
ulTexRamBlocksMax -= DEFAULT_TABLE_SIZE;
// Pas assez de mémoire
return -1;
}
}
// Décalage pour insérer notre nouvel élément
memmove(ulTexRamBlocks + i + 1, ulTexRamBlocks + i,
sizeof(UL_TEXRAMBLOCK) * (ulTexRamBlocksNb - i - 1));
// Remplissons notre nouveau bloc
setBlockSize(i, blockSize);
// Il a l'adresse du bloc qui était là avant
setBlockOffset(i, getBlockOffset(i + 1));
// Il n'est pas libre
setBlockFree(i, 0);
// Pour le prochain, sa taille diminue
setBlockSize(i + 1, getBlockSize(i + 1) - blockSize);
// ATTENTION: calcul d'offset
setBlockOffset(i + 1, getBlockOffset(i + 1) + blockSize);
}
// Note: il faut traduire l'offset en vraie adresse
return getBlockOffset(i);
}
// Note: il faut traduire une vraie adresse en offset
int ulTexVramFreeBlock(int blockOffset)
{
int i, j, updateNeeded;
for (i = 0; i < ulTexRamBlocksNb; i++)
{
if (getBlockOffset(i) == blockOffset)
break;
}
// Impossible de trouver le bloc
if (i >= ulTexRamBlocksNb)
return 0;
// Le bloc est maintenant libre ^^
setBlockFree(i, 1);
// Bon maintenant reste à "assembler" les blocs libres adjacents
do
{
updateNeeded = 0;
for (j = 0; j < ulTexRamBlocksNb - 1; j++)
{
// Cherchons deux blocs adjacents
if ((isBlockFree(j) && isBlockFree(j + 1)) ||
(isBlockFree(j) && getBlockSize(j) == 0))
{
// Assemblons ces blocs maintenant
int newSize = getBlockSize(j) + getBlockSize(j + 1);
int newAdd = getBlockOffset(j);
memmove(ulTexRamBlocks + j, ulTexRamBlocks + j + 1,
sizeof(UL_TEXRAMBLOCK) * (ulTexRamBlocksNb - j - 1));
setBlockOffset(j, newAdd);
setBlockSize(j, newSize);
// Le bloc entre deux est supprimé
ulTexRamBlocksNb--;
// ATT: On devra refaire un tour pour vérifier si de nouveaux
// blocs n'ont pas été créés
updateNeeded = 1;
}
}
} while (updateNeeded);
return 1;
}
// Inspirée de la fonction de libnds
//
// Attention: sizeX et sizeY sont les VRAIES tailles (en pixel)! Elles seront
// alignées aux prochaines puissances de deux!
int ulTexImage2D(int target, int empty1, UL_IMAGE_FORMATS type,
int sizeX, int sizeY, int empty2, int param, uint8* texture)
{
uint32 size = 0;
int32 texId;
uint32* addr;
// Unoptimized sizeX
int uoSizeX = sizeX;
//uint32 vramTemp;
sizeX = 1 << ulGetPowerOf2Count(sizeX);
// L'optimisation pour les tailles de palettes permet de réduire l'espace
// vertical alloué (pas d'alignement à une puissance de deux)
if (!ul_optimizeTextureSize)
sizeY = 1 << ulGetPowerOf2Count(sizeY);
size = (sizeX * sizeY * ul_pixelSizes[type]) >> 3;
texId = ulTexVramAllocBlock(size);
if (texId < 0)
return 0;
addr = ulTexVramOffsetToAddress(texId);
swiWaitForVBlank();
// unlock texture memory
ulChangeVramAllocation(ul_texVramBanks, UL_BANK_TYPE_LCD);
if (type == UL_PF_5550)
{
// We do UL_PF_5550 as UL_PF_5551, but we set each alpha bit to 1 during the copy
u16 * src = (u16*)texture;
u16 * dest = (u16*)addr;
// Valeur de la texture: utilisé pour GFX_TEX_FORMAT
ulTexParameter(ulGetPowerOf2Count(sizeX) - 3, ulGetPowerOf2Count(sizeY) - 3,
addr, UL_PF_5551, param);
if (texture)
{
int number = 0;
while (size--)
{
// Do not make opaque pixels that are outside of the screen
if (number < uoSizeX)
*dest++ = *src | (1 << 15);
else
*dest++ = *src & 0x7fff;
src++;
number++;
if (number >= sizeX)
number -= sizeX;
}
}
}
else
{
// For everything else, we do a straight copy
ulTexParameter(ulGetPowerOf2Count(sizeX) - 3, ulGetPowerOf2Count(sizeY) - 3,
addr, type, param);
if (texture)
swiCopy((uint32*)texture, addr , (size >> 2) | COPY_MODE_WORD);
}
// No texture passed - eventually fill the texture with zero
if (!texture)
{
if (ul_initTexturesToZero & UL_TEXINITZERO_VRAM)
memset(addr, 0, size);
}
ulChangeVramAllocation(ul_texVramBanks, UL_BANK_TYPE_TEXTURE);
return 1;
}
#define UL_MAX_TEXTURES 128
#define TEXTURE_FREE (~0)
int *ulTextureParams;
int ulTextureNb;
int ulTextureMax;
int ulTextureActive;
void ulInitTextures()
{
ulTextureActive = 0;
ulTextureMax = UL_MAX_TEXTURES;
ulTextureParams = (int *)malloc(sizeof(ulTextureParams[0]) * ulTextureMax);
memset(ulTextureParams, 0xff, sizeof(ulTextureParams[0]) * ulTextureMax);
ulTextureNb = 0;
}
int ulGetNextAvailableTexture()
{
int iterate = 0;
while (iterate < 3)
{
// Trouve la prochaine texture libre
while (ulTextureNb < ulTextureMax && ulTextureParams[ulTextureNb] != TEXTURE_FREE)
{
ulTextureNb++;
}
// Pas de texture disponible, on recommence à zéro
if (ulTextureNb >= ulTextureMax)
{
iterate++;
// On a déjà fait une fois un tour complet + un tour partiel mais on
// n'a rien trouvé => plus de mémoire
if (iterate == 2)
{
int *oldPtr = ulTextureParams;
// On rajoute un peu de mémoire
ulTextureParams =
(int *)realloc(ulTextureParams,
sizeof(ulTextureParams[0]) * (ulTextureMax + UL_MAX_TEXTURES));
// L'allocation a foiré => Plus de mémoire
if (!ulTextureParams)
{
ulTextureParams = oldPtr;
break;
}
// Remplit les prochaines éléments du tableau pour les marquer
// comme "libres"
memset(ulTextureParams + ulTextureMax, 0xff,
sizeof(ulTextureParams[0]) * UL_MAX_TEXTURES);
ulTextureMax += UL_MAX_TEXTURES;
}
ulTextureNb = 0;
}
else
{
// On a trouvé notre texture =)
break;
}
}
if (iterate >= 3)
{
// Rien à faire: (vraiment) plus de mémoire
return -1;
}
else
{
// La prochaine fois, on ne prendra plus la même (post incrémentation)
return ulTextureNb++;
}
}
int ulGenTextures(int n, int *names)
{
for (int index = 0; index < n; index++)
{
int newTexture = ulGetNextAvailableTexture();
if (newTexture >= 0)
{
names[index] = newTexture;
// Pour éviter de le considérer comme pris
ulTextureParams[newTexture] = 0;
}
else
{
return 0;
}
}
return 1;
}
void ulFreeTextures(int n, int *names)
{
for (int index = 0; index < n; index++)
{
// Pas libre => à libérer
if (ulTextureParams[names[index]] != TEXTURE_FREE)
ulTexVramFreeBlock(getTextureOffset(names[index]));
ulTextureParams[names[index]] = TEXTURE_FREE;
}
}
// Paramétrage d'une texture
void ulTexParameter(uint8 sizeX, uint8 sizeY, uint32* addr, UL_IMAGE_FORMATS mode, uint32 param)
{
ulTextureParams[ulTextureActive] = param | (sizeX << 20) | (sizeY << 23) |
(((uint32)addr >> 3) & 0xFFFF) | (mode << 26);
}
void ulBindTexture(int target, int name)
{
//if (name == 0)
// GFX_TEX_FORMAT = 0;
//else
GFX_TEX_FORMAT = ulTextureParams[name];
ulTextureActive = name;
}
#if 0
// Permet d'utiliser une texture de UL avec la librairie VideoGL intégrée à libnds
void ulBindTextureToGl(int target, int name)
{
glGlob->textures[glGlob->activeTexture] = ulTextureParams[name];
glBindTexture(target, name);
}
#endif
// 1 on success, 0 on failure
int ulSetTexVramParameters(int activeBanks, void *baseAddr, int totalSize)
{
int curVramSize = ul_texVramSize >> 4;
int blockNum = ulTexRamBlocksNb - 1;
int sizeDiff;
// La taille est toujours multiple de 16 - arrondir au bloc supérieur
if (totalSize & 15)
totalSize += 16;
totalSize >>= 4;
// Différence de taille (négatif pour réduction, positif pour aggrandissement)
sizeDiff = totalSize - curVramSize;
// Le dernier bloc est TOUJOURS libre, même s'il reste 0 octet. Cf la
// bidouille dans ulTexVramAlloc
if (isBlockFree(blockNum) && getBlockSize(blockNum) + sizeDiff >= 0)
{
setBlockSize(blockNum, getBlockSize(blockNum) + sizeDiff);
ul_texVramBase = baseAddr;
ul_texVramSize = totalSize;
ul_texVramBanks = activeBanks;
ulChangeVramAllocation(ul_texVramBanks, UL_BANK_TYPE_TEXTURE);
}
else
{
return 0;
}
return 1;
}
int ulGetTexVramAvailMemory()
{
return getBlockSize(ulTexRamBlocksNb - 1) << 4;
}
int ulGetTexVramTotalMemory()
{
return ul_texVramSize;
}
int ulGetTexVramUsedMemory()
{
return ul_texVramSize - ulGetTexVramAvailMemory();
}