Fix loading default texture pack twice if it isn't default.zip

This commit is contained in:
UnknownShadow200 2025-07-13 16:45:15 +10:00
parent 18bd436b39
commit ad229da788
6 changed files with 506 additions and 544 deletions

View File

@ -480,6 +480,7 @@ static cc_result ExtractTexturePack(const cc_string* path) {
void Launcher_TryLoadTexturePack(void) {
cc_string path; char pathBuffer[FILENAME_SIZE];
const char* default_path;
cc_string texPack;
/* TODO: Not duplicate TexturePack functionality */
@ -490,8 +491,9 @@ void Launcher_TryLoadTexturePack(void) {
}
/* user selected texture pack is missing some required .png files */
if (!hasBitmappedFont || dirtBmp.scan0 == NULL)
TexturePack_ExtractDefault(ExtractTexturePack);
if (!hasBitmappedFont || dirtBmp.scan0 == NULL) {
TexturePack_ExtractDefault(ExtractTexturePack, &default_path);
}
LBackend_UpdateTitleFont();
}

View File

@ -490,7 +490,7 @@ void TexturePack_SetDefault(const cc_string* texPack) {
Options_Set(OPT_DEFAULT_TEX_PACK, texPack);
}
cc_result TexturePack_ExtractDefault(DefaultZipCallback callback) {
cc_result TexturePack_ExtractDefault(DefaultZipCallback callback, const char** default_path) {
cc_result res = ReturnCode_FileNotFound;
const char* defaults[3];
cc_string path;
@ -504,8 +504,13 @@ cc_result TexturePack_ExtractDefault(DefaultZipCallback callback) {
{
path = String_FromReadonly(defaults[i]);
res = callback(&path);
if (!res) return 0;
if (res) continue;
*default_path = defaults[i];
return 0;
}
*default_path = NULL;
return res;
}
@ -597,16 +602,17 @@ static cc_result ExtractFromFile(const cc_string* path) {
#endif
static cc_result ExtractUserTextures(void) {
const char* default_path;
cc_string path;
cc_result res;
/* TODO: Log error for multiple default texture pack extract failure */
res = TexturePack_ExtractDefault(ExtractFromFile);
res = TexturePack_ExtractDefault(ExtractFromFile, &default_path);
/* Game shows a warning dialog if default textures are missing */
TexturePack_DefaultMissing = res == ReturnCode_FileNotFound;
path = TexturePack_Path;
if (String_CaselessEqualsConst(&path, "texpacks/default.zip")) path.length = 0;
if (default_path && String_CaselessEqualsConst(&path, default_path)) return res;
if (Game_ClassicMode || path.length == 0) return res;
/* override default textures with user's selected texture pack */

View File

@ -108,7 +108,7 @@ void TexturePack_CheckPending(void);
CC_API void TexturePack_Extract(const cc_string* url);
typedef cc_result (*DefaultZipCallback)(const cc_string* path);
cc_result TexturePack_ExtractDefault(DefaultZipCallback callback);
cc_result TexturePack_ExtractDefault(DefaultZipCallback callback, const char** default_path);
struct TextureEntry;
struct TextureEntry {

View File

@ -4,19 +4,25 @@
#define SIZE_TO_BLOCKS(size, blockSize) (((size) + ((blockSize) - 1)) / (blockSize))
#define BA_BLOCK_USED(table, block) (table[BA_PAGE(block)] & BA_BIT(block))
#define BA_BLOCK_MARK_FREE(table, block) table[BA_PAGE(block)] &= ~BA_BIT(block);
#define BA_BLOCK_MARK_USED(table, block) table[BA_PAGE(block)] |= BA_BIT(block);
static CC_INLINE int blockalloc_can_alloc(cc_uint8* table, int beg, int blocks) {
for (int i = beg; i < beg + blocks; i++)
int i;
for (i = beg; i < beg + blocks; i++)
{
cc_uint8 page = table[BA_PAGE(i)];
if (page & BA_BIT(i)) return false;
if (BA_BLOCK_USED(table, i)) return false;
}
return true;
}
static int blockalloc_alloc(cc_uint8* table, int maxBlocks, int blocks) {
int i, j;
if (blocks > maxBlocks) return -1;
for (int i = 0; i <= maxBlocks - blocks;)
for (i = 0; i <= maxBlocks - blocks;)
{
cc_uint8 page = table[BA_PAGE(i)];
@ -29,35 +35,49 @@ static int blockalloc_alloc(cc_uint8* table, int maxBlocks, int blocks) {
// If can't be allocated starting at block, try next
if (!blockalloc_can_alloc(table, i, blocks)) { i++; continue; }
for (int j = i; j < i + blocks; j++)
for (j = i; j < i + blocks; j++)
{
table[BA_PAGE(j)] |= BA_BIT(j);
BA_BLOCK_MARK_USED(table, j);
}
return i;
}
return -1;
}
static void blockalloc_dealloc(cc_uint8* table, int origin, int blocks) {
// Mark the used blocks as free again
for (int i = origin; i < origin + blocks; i++)
static void blockalloc_dealloc(cc_uint8* table, int base, int blocks) {
int i;
for (i = base; i < base + blocks; i++)
{
table[BA_PAGE(i)] &= ~BA_BIT(i);
BA_BLOCK_MARK_FREE(table, i);
}
}
static CC_INLINE int blockalloc_total_free(cc_uint8* table, int maxBlocks) {
int free_blocks = 0, i, used;
cc_uint8 page;
// could be optimised to look at entire page in 0x00 and 0xFF case
// but this method is called so infrequently, not really worth it
int i, total_free = 0;
// Could be optimised to look at entire page in 0x00 and 0xFF case
// But this method is called so infrequently, so not really worth it
for (i = 0; i < maxBlocks; i++)
{
page = table[BA_PAGE(i)];
used = page & BA_BIT(i);
if (!used) free_blocks++;
if (!BA_BLOCK_USED(table, i)) total_free++;
}
return free_blocks;
return total_free;
}
static CC_INLINE int blockalloc_shift(cc_uint8* table, int base, int blocks) {
int moved = 0;
// Try to shift downwards towards prior allocated entry
base--;
while (base >= 0 && !BA_BLOCK_USED(table, base))
{
// Mark previously last allocated block as now unused
BA_BLOCK_MARK_FREE(table, base + blocks);
moved++;
// Mark previously last empty block as now used
BA_BLOCK_MARK_USED(table, base);
base--;
}
return moved;
}

View File

@ -16,17 +16,7 @@ static cc_bool stateDirty;
#define VERTEX_BUFFER_SIZE 32 * 50000
#define PT_ALPHA_REF 0x011c
typedef struct {
uint32_t format;
int32_t base;
uint32_t log2_w: 4;
uint32_t log2_h: 4;
uint32_t size: 24;
} GPUTexture;
#define MAX_TEXTURE_COUNT 768
static GPUTexture tex_list[MAX_TEXTURE_COUNT];
struct GPUTexture;
/*########################################################################################################################*
*------------------------------------------------------Commands list------------------------------------------------------*
@ -74,126 +64,6 @@ static void CommandsList_Append(struct CommandsList* list, const void* cmd) {
list->length++;
}
/*########################################################################################################################*
*-----------------------------------------------------Texture memory------------------------------------------------------*
*#########################################################################################################################*/
// For PVR2 GPU, highly recommended that multiple textures don't cross the same 2048 byte page alignment
// So to avoid this, ensure that each texture is allocated at the start of a 2048 byte page
#define TEXMEM_PAGE_SIZE 2048
#define TEXMEM_PAGE_MASK (TEXMEM_PAGE_SIZE - 1)
// Round up to nearest page
#define TEXMEM_PAGE_ROUNDUP(size) (((size) + TEXMEM_PAGE_MASK) & ~TEXMEM_PAGE_MASK)
// Leave a little bit of memory available by KOS PVR code
#define TEXMEM_RESERVED (48 * 1024)
#define PAGE_TO_TEXMEM(page) (cc_uint16*)(texmem_base + (page) * TEXMEM_PAGE_SIZE)
// Base address in VRAM for textures
static cc_uint8* texmem_base;
// Total number of pages in VRAM
static cc_uint32 texmem_pages;
// Stores which pages in VRAM are currently used
static cc_uint8* texmem_used;
void texmem_init(void) {
size_t vram_free = pvr_mem_available();
size_t tmem_size = vram_free - TEXMEM_RESERVED;
void* base = pvr_mem_malloc(tmem_size);
texmem_base = (void*)TEXMEM_PAGE_ROUNDUP((cc_uintptr)base);
texmem_pages = tmem_size / TEXMEM_PAGE_SIZE;
texmem_used = Mem_AllocCleared(1, texmem_pages, "Page state");
}
static int texmem_move(cc_uint32 page, cc_uint32 size) {
cc_uint32 pages = TEXMEM_PAGE_ROUNDUP(size) / TEXMEM_PAGE_SIZE;
int moved = 0;
// Try to shift downwards towards prior allocated texture
while (page > 0 && texmem_used[page - 1] == 0) {
page--; moved++;
texmem_used[page + pages] = 0;
}
// Mark previously empty pages as now used
for (int i = 0; i < moved; i++)
{
texmem_used[page + i] = 1;
}
return moved;
}
static int texmem_defragment(void) {
int moved_any = false;
for (int i = 0; i < MAX_TEXTURE_COUNT; i++)
{
GPUTexture* tex = &tex_list[i];
if (!tex->size) continue;
int moved = texmem_move(tex->base, tex->size);
if (!moved) continue;
moved_any = true;
cc_uint16* dst = PAGE_TO_TEXMEM(tex->base - moved);
cc_uint16* src = PAGE_TO_TEXMEM(tex->base);
memmove(dst, src, tex->size);
tex->base -= moved;
}
return moved_any;
}
static CC_INLINE int texmem_can_alloc(cc_uint32 beg, cc_uint32 pages) {
if (texmem_used[beg]) return false;
for (cc_uint32 page = beg; page < beg + pages; page++)
{
if (texmem_used[page]) return false;
}
return true;
}
static int texmem_alloc_pages(cc_uint32 size) {
cc_uint32 pages = TEXMEM_PAGE_ROUNDUP(size) / TEXMEM_PAGE_SIZE;
if (pages > texmem_pages) return -1;
for (cc_uint32 page = 0; page < texmem_pages - pages; page++)
{
if (!texmem_can_alloc(page, pages)) continue;
for (cc_uint32 i = 0; i < pages; i++)
texmem_used[page + i] = 1;
return page;
}
return -1;
}
static int texmem_alloc(cc_uint32 size) {
int base = texmem_alloc_pages(size);
if (base >= 0) return base;
Platform_LogConst("Out of VRAM! Defragmenting..");
while (texmem_defragment()) { }
return texmem_alloc_pages(size);
}
static void texmem_free(cc_uint32 page, cc_uint32 size) {
cc_uint32 pages = TEXMEM_PAGE_ROUNDUP(size) / TEXMEM_PAGE_SIZE;
for (cc_uint32 i = 0; i < pages; i++)
texmem_used[page + i] = 0;
}
static int texmem_total_free(void) {
int free = 0;
for (cc_uint32 page = 0; page < texmem_pages; page++)
{
if (!texmem_used[page]) free++;
}
return free;
}
/*########################################################################################################################*
*---------------------------------------------------------General---------------------------------------------------------*
@ -263,9 +133,10 @@ static void InitGLState(void) {
CommandsList_Reserve(&listTR, 1024 * 3);
}
static void InitTexMemory(void);
void Gfx_Create(void) {
if (!Gfx.Created) InitGPU();
if (!Gfx.Created) texmem_init();
if (!Gfx.Created) InitTexMemory();
InitGLState();
@ -289,14 +160,393 @@ void Gfx_Free(void) {
}
/*########################################################################################################################*
*-----------------------------------------------------Texture memory------------------------------------------------------*
*#########################################################################################################################*/
struct GPUTexture {
uint32_t format; // PVR texture format
uint16_t base; // VRAM block number
uint16_t blocks; // VRAM blocks used
uint32_t log2_w: 4; // log2(width)
uint32_t log2_h: 4; // log2(height)
uint32_t size: 24; // Size in bytes
};
#define MAX_TEXTURE_COUNT 768
static struct GPUTexture tex_list[MAX_TEXTURE_COUNT];
static struct GPUTexture* tex_active;
// For PVR2 GPU, highly recommended that multiple textures don't cross the same 2048 byte VRAM page alignment
// So to avoid this, ensure that each texture is allocated at the start of a 2048 byte VRAM page
#define TEXMEM_BLOCK_SIZE 2048
#define TEXMEM_BLOCK_MASK (TEXMEM_BLOCK_SIZE - 1)
// Round up to nearest block
#define TEXMEM_BLOCK_ROUNDUP(size) (((size) + TEXMEM_BLOCK_MASK) & ~TEXMEM_BLOCK_MASK)
// Leave a little bit of memory available by KOS PVR code
#define TEXMEM_RESERVED (48 * 1024)
#define BLOCK_TO_TEXMEM(block) (cc_uint16*)(texmem_base + (block) * TEXMEM_BLOCK_SIZE)
#define TEXMEM_MAX_BLOCKS (PVR_RAM_SIZE / TEXMEM_BLOCK_SIZE)
// Base address in VRAM for textures
static cc_uint8* texmem_base;
// Total number of blocks available for textures in VRAM
static cc_uint32 texmem_blocks;
// Stores which blocks in VRAM are currently used
static cc_uint8 tex_table[TEXMEM_MAX_BLOCKS / BLOCKS_PER_PAGE];
static void InitTexMemory(void) {
size_t vram_free = pvr_mem_available();
size_t tmem_avail = vram_free - TEXMEM_RESERVED;
void* base = pvr_mem_malloc(tmem_avail);
texmem_base = (void*)TEXMEM_BLOCK_ROUNDUP((cc_uintptr)base);
texmem_blocks = tmem_avail / TEXMEM_BLOCK_SIZE;
}
static int DefragTexMemory(void) {
int moved_any = false;
for (int i = 0; i < MAX_TEXTURE_COUNT; i++)
{
struct GPUTexture* tex = &tex_list[i];
if (!tex->size) continue;
int moved = blockalloc_shift(tex_table, tex->base, tex->blocks);
if (!moved) continue;
moved_any = true;
cc_uint16* dst = BLOCK_TO_TEXMEM(tex->base - moved);
cc_uint16* src = BLOCK_TO_TEXMEM(tex->base);
memmove(dst, src, tex->size);
tex->base -= moved;
}
return moved_any;
}
static int AllocTexMemory(int blocks) {
int base = blockalloc_alloc(tex_table, texmem_blocks, blocks);
if (base >= 0) return base;
Platform_LogConst("Out of VRAM! Defragmenting..");
while (DefragTexMemory()) { }
return blockalloc_alloc(tex_table, texmem_blocks, blocks);
}
/*########################################################################################################################*
*---------------------------------------------------------Palettes--------------------------------------------------------*
*#########################################################################################################################*/
// PVR hardware only allows addressing the 1024 palette entries in either 16 entry or 256 entry groups
// Only 16 entry groups are supported (64 groups, relatively quick texture loading)
#define PAL_TOTAL_ENTRIES 1024
#define MAX_PAL_4BPP_ENTRIES 16
#define PAL_TOTAL_BLOCKS (PAL_TOTAL_ENTRIES / MAX_PAL_4BPP_ENTRIES)
static cc_uint8 pal_table[PAL_TOTAL_BLOCKS / BLOCKS_PER_PAGE];
static CC_INLINE int FindInPalette(BitmapCol* palette, int pal_count, BitmapCol color) {
for (int i = 0; i < pal_count; i++)
{
if (palette[i] == color) return i;
}
return -1;
}
static int CalcPalette(BitmapCol* palette, struct Bitmap* bmp, int rowWidth) {
int width = bmp->width, height = bmp->height;
BitmapCol* row = bmp->scan0;
int pal_count = 0;
for (int y = 0; y < height; y++, row += rowWidth)
{
for (int x = 0; x < width; x++)
{
BitmapCol color = row[x];
int idx = FindInPalette(palette, pal_count, color);
if (idx >= 0) continue;
// Too many distinct colours
if (pal_count >= MAX_PAL_4BPP_ENTRIES) return 0;
palette[pal_count] = color;
pal_count++;
}
}
return pal_count;
}
static void ApplyPalette(BitmapCol* palette, int pal_count, int pal_index) {
for (int i = 0; i < pal_count; i++)
{
int R = BitmapCol_R(palette[i]) >> 4;
int G = BitmapCol_G(palette[i]) >> 4;
int B = BitmapCol_B(palette[i]) >> 4;
int A = BitmapCol_A(palette[i]) >> 4;
cc_uint32 entry = (A << 12) | (R << 8) | (G << 4) | B;
pvr_set_pal_entry(pal_index * 16 + i, entry);
}
}
/*########################################################################################################################*
*---------------------------------------------------------Textures--------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_EnableMipmaps(void) { }
void Gfx_DisableMipmaps(void) { }
// Twiddled index looks like this (highest numbered bits are leftmost):
// - w = h: xyxy xyxy
// - w > h: xxxx xyxy
// - h > w: yyyy xyxy
// And can therefore be broken down into two components:
// 1) X and Y interleaved lower bits
// 2) X or Y linear higher bits
// For example, in the case of W=4 and H=8
// the bit pattern is Y_yx_yx_yx
// - lower 3 Y bits are interleaved
// - upper 1 Y bit is linear
// By calculating appropriate values, can increment X/Y
// in "interleaved one" and then a "linear one" at the end
// For example, consider XX XY XY
// - oneX = 00 01 10 maskX = 11 10 10
// - oneY = 00 10 11 maskY = 00 01 01
// Working through:
// X = 00 00 00 (x=0)
// X + 00 01 10 > 00 01 10
// & 11 10 10 > 00 00 10
// X = 00 00 10 (x=1)
// X + 00 01 10 > 00 10 00
// & 11 10 10 > 00 10 00
// X = 00 10 00 (x=2)
// X + 00 01 10 > 00 11 10
// & 11 10 10 > 00 10 10
// X = 00 10 10 (x=3)
// X + 00 01 10 > 01 00 00
// & 11 10 10 > 01 00 00
// X = 01 00 00 (x=4)
// X + 00 01 10 > 01 01 10
// & 11 10 10 > 01 00 10
// X = 01 00 10 (x=5)
// X + 00 01 10 > 01 10 00
// & 11 10 10 > 01 10 00
// X = 01 10 00 (x=6)
// X + 00 01 10 > 01 11 10
// & 11 10 10 > 01 10 10
// X = 01 10 10 (x=7)
// X + 00 01 10 > 10 00 00
// & 11 10 10 > 10 00 00
//
// As a further optimisation, note the bit patterns
// oneX = 00 01 10 oneY = -- 10 11
// maskX = 11 10 10 maskY = -- 01 01
// oneX = ~maskX + 1 oneY = ~maskY + 1
// And then using the following bitwise equivalence:
// x - y = x + (~y + 1)
// idx - mask = idx + (~mask + 1)
// idx - mask = idx + one
static CC_INLINE void TwiddleCalcFactors(unsigned w, unsigned h,
unsigned* maskX, unsigned* maskY) {
w = Math_NextPowOf2(w);
h = Math_NextPowOf2(h);
*maskX = 0;
*maskY = 0;
int shift = 0;
for (; w > 1 || h > 1; w >>= 1, h >>= 1)
{
if (w > 1 && h > 1) {
// Add interleaved X and Y bits
*maskX += 0x02 << shift;
*maskY += 0x01 << shift;
shift += 2;
} else if (w > 1) {
// Add a linear X bit
*maskX += 0x01 << shift;
shift += 1;
} else if (h > 1) {
// Add a linear Y bit
*maskY += 0x01 << shift;
shift += 1;
}
}
}
// B8 G8 R8 A8 > B4 G4 R4 A4
#define BGRA8_to_BGRA4(src) \
((src[0] & 0xF0) >> 4) | (src[1] & 0xF0) | ((src[2] & 0xF0) << 4) | ((src[3] & 0xF0) << 8);
static CC_INLINE void ConvertTexture_4444(cc_uint16* dst, struct Bitmap* bmp, int rowWidth) {
int width = bmp->width, height = bmp->height;
unsigned maskX, maskY;
unsigned X = 0, Y = 0;
TwiddleCalcFactors(width, height, &maskX, &maskY);
for (int y = 0; y < height; y++)
{
cc_uint8* src = (cc_uint8*)(bmp->scan0 + y * rowWidth);
X = 0;
for (int x = 0; x < width; x++, src += 4)
{
dst[X | Y] = BGRA8_to_BGRA4(src);
X = (X - maskX) & maskX;
}
Y = (Y - maskY) & maskY;
}
}
static CC_INLINE void ConvertTexture_Palette(cc_uint16* dst, struct Bitmap* bmp, int rowWidth, BitmapCol* palette, int pal_count) {
int width = bmp->width >> 1, height = bmp->height >> 1;
unsigned maskX, maskY;
unsigned X = 0, Y = 0;
TwiddleCalcFactors(width, height, &maskX, &maskY);
for (int y = 0; y < height; y++)
{
BitmapCol* src = bmp->scan0 + (y * 2) * rowWidth;
BitmapCol* next = src + rowWidth;
X = 0;
for (int x = 0; x < width; x++, src += 2, next += 2)
{
int pal_00 = FindInPalette(palette, pal_count, src[0]);
int pal_10 = FindInPalette(palette, pal_count, src[1]);
int pal_01 = FindInPalette(palette, pal_count, next[0]);
int pal_11 = FindInPalette(palette, pal_count, next[1]);
dst[X | Y] = pal_00 | (pal_01 << 4) | (pal_10 << 8) | (pal_11 << 12);
X = (X - maskX) & maskX;
}
Y = (Y - maskY) & maskY;
}
}
static struct GPUTexture* FindFreeTexture(void) {
for (int i = 0; i < MAX_TEXTURE_COUNT; i++)
{
struct GPUTexture* tex = &tex_list[i];
if (!tex->size) return tex;
}
return NULL;
}
GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) {
struct GPUTexture* tex = FindFreeTexture();
if (!tex) return NULL;
BitmapCol palette[MAX_PAL_4BPP_ENTRIES];
int pal_count = 0;
int pal_index = -1;
if (!(flags & TEXTURE_FLAG_DYNAMIC)) {
pal_count = CalcPalette(palette, bmp, rowWidth);
if (pal_count > 0) {
pal_index = blockalloc_alloc(pal_table, PAL_TOTAL_BLOCKS, 1);
}
if (pal_index >= 0) {
ApplyPalette(palette, pal_count, pal_index);
}
}
int dst_w = Math_NextPowOf2(bmp->width);
int dst_h = Math_NextPowOf2(bmp->height);
tex->log2_w = Math_ilog2(dst_w);
tex->log2_h = Math_ilog2(dst_h);
if (pal_index >= 0) {
tex->format = PVR_TXRFMT_PAL4BPP | PVR_TXRFMT_4BPP_PAL(pal_index);
// 4bpp = 2 pixels in 1 byte
tex->size = dst_w * dst_h / 2;
} else {
tex->format = PVR_TXRFMT_ARGB4444;
// 16 bpp = 1 pixel in 2 bytes
tex->size = dst_w * dst_h * 2;
}
int blocks = SIZE_TO_BLOCKS(tex->size, TEXMEM_BLOCK_SIZE);
int base = AllocTexMemory(blocks);
if (base < 0) { Platform_LogConst("Out of PVR VRAM!"); return NULL; }
tex->base = base;
tex->blocks = blocks;
cc_uint16* dst = BLOCK_TO_TEXMEM(base);
if (pal_index >= 0) {
ConvertTexture_Palette(dst, bmp, rowWidth, palette, pal_count);
} else {
ConvertTexture_4444(dst, bmp, rowWidth);
}
return tex;
}
void Gfx_UpdateTexture(GfxResourceID texId, int originX, int originY, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
struct GPUTexture* tex = (struct GPUTexture*)texId;
int width = part->width, height = part->height;
unsigned maskX, maskY;
unsigned X = 0, Y = 0;
TwiddleCalcFactors(1 << tex->log2_w, 1 << tex->log2_h, &maskX, &maskY);
// Calculate start twiddled X and Y values
for (int x = 0; x < originX; x++) { X = (X - maskX) & maskX; }
for (int y = 0; y < originY; y++) { Y = (Y - maskY) & maskY; }
unsigned startX = X;
cc_uint16* dst = BLOCK_TO_TEXMEM(tex->base);
for (int y = 0; y < height; y++)
{
cc_uint8* src = (cc_uint8*)(part->scan0 + rowWidth * y);
X = startX;
for (int x = 0; x < width; x++, src += 4)
{
dst[X | Y] = BGRA8_to_BGRA4(src);
X = (X - maskX) & maskX;
}
Y = (Y - maskY) & maskY;
}
// TODO: Do we need to flush VRAM?
}
void Gfx_BindTexture(GfxResourceID texId) {
tex_active = (struct GPUTexture*)texId;
stateDirty = true;
}
void Gfx_DeleteTexture(GfxResourceID* texId) {
struct GPUTexture* tex = (struct GPUTexture*)(*texId);
if (!tex) return;
blockalloc_dealloc(tex_table, tex->base, tex->blocks);
if (tex->format != PVR_TXRFMT_ARGB4444) {
int index = (tex->format & PVR_TXRFMT_4BPP_PAL(63)) >> 21;
blockalloc_dealloc(pal_table, index, 1);
}
tex->size = 0;
*texId = 0;
}
/*########################################################################################################################*
*------------------------------------------------------Polygon state------------------------------------------------------*
*#########################################################################################################################*/
static GPUTexture* tex_active;
static uint32_t SHADE_MODEL = PVR_SHADE_GOURAUD;
static CC_NOINLINE void BuildPolyContext(pvr_poly_hdr_t* dst, int list_type) {
GPUTexture* tex = tex_active;
struct GPUTexture* tex = tex_active;
int gen_culling = gfx_culling ? PVR_CULLING_CW : PVR_CULLING_SMALL;
int depth_comp = gfx_depthTest ? PVR_DEPTHCMP_GEQUAL : PVR_DEPTHCMP_ALWAYS;
@ -354,7 +604,7 @@ static CC_NOINLINE void BuildPolyContext(pvr_poly_hdr_t* dst, int list_type) {
return;
}
int tex_alpha = use_alpha ? PVR_TXRALPHA_ENABLE : PVR_TXRALPHA_DISABLE;
cc_uint16* tex_ptr = PAGE_TO_TEXMEM(tex->base);
cc_uint16* tex_ptr = BLOCK_TO_TEXMEM(tex->base);
dst->mode2 |= (tex_alpha << PVR_TA_PM2_TXRALPHA_SHIFT) & PVR_TA_PM2_TXRALPHA_MASK;
dst->mode2 |= (PVR_FILTER_NEAREST << PVR_TA_PM2_FILTER_SHIFT) & PVR_TA_PM2_FILTER_MASK;
@ -519,309 +769,6 @@ void Gfx_UnlockDynamicVb(GfxResourceID vb) {
void Gfx_DeleteDynamicVb(GfxResourceID* vb) { Gfx_DeleteVb(vb); }
/*########################################################################################################################*
*---------------------------------------------------------Palettees--------------------------------------------------------*
*#########################################################################################################################*/
// PVR hardware only allows addressing the 1024 palette entries in either 16 entry or 256 entry groups
// Only 16 entry groups are supported (64 groups, relatively quick texture loading)
#define MAX_PAL_ENTRIES 16
#define PAL_TOTAL_BLOCKS (1024 / MAX_PAL_ENTRIES)
static cc_uint8 pal_table[PAL_TOTAL_BLOCKS / BLOCKS_PER_PAGE];
static CC_INLINE int FindInPalette(BitmapCol* palette, int pal_count, BitmapCol color) {
for (int i = 0; i < pal_count; i++)
{
if (palette[i] == color) return i;
}
return -1;
}
static int CalcPalette(BitmapCol* palette, struct Bitmap* bmp, int rowWidth) {
int width = bmp->width, height = bmp->height;
BitmapCol* row = bmp->scan0;
int pal_count = 0;
for (int y = 0; y < height; y++, row += rowWidth)
{
for (int x = 0; x < width; x++)
{
BitmapCol color = row[x];
int idx = FindInPalette(palette, pal_count, color);
if (idx >= 0) continue;
// Too many distinct colours
if (pal_count >= MAX_PAL_ENTRIES) return 0;
palette[pal_count] = color;
pal_count++;
}
}
return pal_count;
}
static void ApplyPalette(BitmapCol* palette, int pal_count, int pal_index) {
for (int i = 0; i < pal_count; i++)
{
int R = BitmapCol_R(palette[i]) >> 4;
int G = BitmapCol_G(palette[i]) >> 4;
int B = BitmapCol_B(palette[i]) >> 4;
int A = BitmapCol_A(palette[i]) >> 4;
cc_uint32 entry = (A << 12) | (R << 8) | (G << 4) | B;
pvr_set_pal_entry(pal_index * 16 + i, entry);
}
}
/*########################################################################################################################*
*---------------------------------------------------------Textures--------------------------------------------------------*
*#########################################################################################################################*/
void Gfx_EnableMipmaps(void) { }
void Gfx_DisableMipmaps(void) { }
// Twiddled index looks like this (highest numbered bits are leftmost):
// - w = h: xyxy xyxy
// - w > h: xxxx xyxy
// - h > w: yyyy xyxy
// And can therefore be broken down into two components:
// 1) X and Y interleaved lower bits
// 2) X or Y linear higher bits
// For example, in the case of W=4 and H=8
// the bit pattern is Y_yx_yx_yx
// - lower 3 Y bits are interleaved
// - upper 1 Y bit is linear
// By calculating appropriate values, can increment X/Y
// in "interleaved one" and then a "linear one" at the end
// For example, consider XX XY XY
// - oneX = 00 01 10 maskX = 11 10 10
// - oneY = 00 10 11 maskY = 00 01 01
// Working through:
// X = 00 00 00 (x=0)
// X + 00 01 10 > 00 01 10
// & 11 10 10 > 00 00 10
// X = 00 00 10 (x=1)
// X + 00 01 10 > 00 10 00
// & 11 10 10 > 00 10 00
// X = 00 10 00 (x=2)
// X + 00 01 10 > 00 11 10
// & 11 10 10 > 00 10 10
// X = 00 10 10 (x=3)
// X + 00 01 10 > 01 00 00
// & 11 10 10 > 01 00 00
// X = 01 00 00 (x=4)
// X + 00 01 10 > 01 01 10
// & 11 10 10 > 01 00 10
// X = 01 00 10 (x=5)
// X + 00 01 10 > 01 10 00
// & 11 10 10 > 01 10 00
// X = 01 10 00 (x=6)
// X + 00 01 10 > 01 11 10
// & 11 10 10 > 01 10 10
// X = 01 10 10 (x=7)
// X + 00 01 10 > 10 00 00
// & 11 10 10 > 10 00 00
//
// As a further optimisation, note the bit patterns
// oneX = 00 01 10 oneY = -- 10 11
// maskX = 11 10 10 maskY = -- 01 01
// oneX = ~maskX + 1 oneY = ~maskY + 1
// And then using the following bitwise equivalence:
// x - y = x + (~y + 1)
// idx - mask = idx + (~mask + 1)
// idx - mask = idx + one
static CC_INLINE void TwiddleCalcFactors(unsigned w, unsigned h,
unsigned* maskX, unsigned* maskY) {
w = Math_NextPowOf2(w);
h = Math_NextPowOf2(h);
*maskX = 0;
*maskY = 0;
int shift = 0;
for (; w > 1 || h > 1; w >>= 1, h >>= 1)
{
if (w > 1 && h > 1) {
// Add interleaved X and Y bits
*maskX += 0x02 << shift;
*maskY += 0x01 << shift;
shift += 2;
} else if (w > 1) {
// Add a linear X bit
*maskX += 0x01 << shift;
shift += 1;
} else if (h > 1) {
// Add a linear Y bit
*maskY += 0x01 << shift;
shift += 1;
}
}
}
// B8 G8 R8 A8 > B4 G4 R4 A4
#define BGRA8_to_BGRA4(src) \
((src[0] & 0xF0) >> 4) | (src[1] & 0xF0) | ((src[2] & 0xF0) << 4) | ((src[3] & 0xF0) << 8);
static CC_INLINE void ConvertTexture_4444(cc_uint16* dst, struct Bitmap* bmp, int rowWidth) {
int width = bmp->width, height = bmp->height;
unsigned maskX, maskY;
unsigned X = 0, Y = 0;
TwiddleCalcFactors(width, height, &maskX, &maskY);
for (int y = 0; y < height; y++)
{
cc_uint8* src = (cc_uint8*)(bmp->scan0 + y * rowWidth);
X = 0;
for (int x = 0; x < width; x++, src += 4)
{
dst[X | Y] = BGRA8_to_BGRA4(src);
X = (X - maskX) & maskX;
}
Y = (Y - maskY) & maskY;
}
}
static CC_INLINE void ConvertTexture_Palette(cc_uint16* dst, struct Bitmap* bmp, int rowWidth, BitmapCol* palette, int pal_count) {
int width = bmp->width >> 1, height = bmp->height >> 1;
unsigned maskX, maskY;
unsigned X = 0, Y = 0;
TwiddleCalcFactors(width, height, &maskX, &maskY);
for (int y = 0; y < height; y++)
{
BitmapCol* src = bmp->scan0 + (y * 2) * rowWidth;
BitmapCol* next = src + rowWidth;
X = 0;
for (int x = 0; x < width; x++, src += 2, next += 2)
{
int pal_00 = FindInPalette(palette, pal_count, src[0]);
int pal_10 = FindInPalette(palette, pal_count, src[1]);
int pal_01 = FindInPalette(palette, pal_count, next[0]);
int pal_11 = FindInPalette(palette, pal_count, next[1]);
dst[X | Y] = pal_00 | (pal_01 << 4) | (pal_10 << 8) | (pal_11 << 12);
X = (X - maskX) & maskX;
}
Y = (Y - maskY) & maskY;
}
}
static GPUTexture* FindFreeTexture(void) {
for (int i = 0; i < MAX_TEXTURE_COUNT; i++)
{
GPUTexture* tex = &tex_list[i];
if (!tex->size) return tex;
}
return NULL;
}
GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) {
GPUTexture* tex = FindFreeTexture();
if (!tex) return NULL;
BitmapCol palette[MAX_PAL_ENTRIES];
int pal_count = 0;
int pal_index = -1;
if (!(flags & TEXTURE_FLAG_DYNAMIC)) {
pal_count = CalcPalette(palette, bmp, rowWidth);
if (pal_count > 0) {
pal_index = blockalloc_alloc(pal_table, PAL_TOTAL_BLOCKS, 1);
}
if (pal_index >= 0) {
ApplyPalette(palette, pal_count, pal_index);
}
}
int dst_w = Math_NextPowOf2(bmp->width);
int dst_h = Math_NextPowOf2(bmp->height);
tex->log2_w = Math_ilog2(dst_w);
tex->log2_h = Math_ilog2(dst_h);
if (pal_index >= 0) {
tex->format = PVR_TXRFMT_PAL4BPP | PVR_TXRFMT_4BPP_PAL(pal_index);
// 4bpp = 2 pixels in 1 byte
tex->size = dst_w * dst_h / 2;
} else {
tex->format = PVR_TXRFMT_ARGB4444;
// 16 bpp = 1 pixel in 2 bytes
tex->size = dst_w * dst_h * 2;
}
int base = texmem_alloc(tex->size);
if (base < 0) { Platform_LogConst("Out of PVR VRAM!"); return NULL; }
tex->base = base;
cc_uint16* dst = PAGE_TO_TEXMEM(base);
if (pal_index >= 0) {
ConvertTexture_Palette(dst, bmp, rowWidth, palette, pal_count);
} else {
ConvertTexture_4444(dst, bmp, rowWidth);
}
return tex;
}
void Gfx_UpdateTexture(GfxResourceID texId, int originX, int originY, struct Bitmap* part, int rowWidth, cc_bool mipmaps) {
GPUTexture* tex = (GPUTexture*)texId;
int width = part->width, height = part->height;
unsigned maskX, maskY;
unsigned X = 0, Y = 0;
TwiddleCalcFactors(1 << tex->log2_w, 1 << tex->log2_h, &maskX, &maskY);
// Calculate start twiddled X and Y values
for (int x = 0; x < originX; x++) { X = (X - maskX) & maskX; }
for (int y = 0; y < originY; y++) { Y = (Y - maskY) & maskY; }
unsigned startX = X;
cc_uint16* dst = PAGE_TO_TEXMEM(tex->base);
for (int y = 0; y < height; y++)
{
cc_uint8* src = (cc_uint8*)(part->scan0 + rowWidth * y);
X = startX;
for (int x = 0; x < width; x++, src += 4)
{
dst[X | Y] = BGRA8_to_BGRA4(src);
X = (X - maskX) & maskX;
}
Y = (Y - maskY) & maskY;
}
// TODO: Do we need to flush VRAM?
}
void Gfx_BindTexture(GfxResourceID texId) {
tex_active = (GPUTexture*)texId;
stateDirty = true;
}
void Gfx_DeleteTexture(GfxResourceID* texId) {
GPUTexture* tex = (GPUTexture*)(*texId);
if (!tex) return;
texmem_free(tex->base, tex->size);
if (tex->format != PVR_TXRFMT_ARGB4444) {
int index = (tex->format & PVR_TXRFMT_4BPP_PAL(63)) >> 21;
blockalloc_dealloc(pal_table, index, 1);
}
tex->size = 0;
*texId = 0;
}
/*########################################################################################################################*
*-----------------------------------------------------State management----------------------------------------------------*
*#########################################################################################################################*/
@ -1042,11 +989,11 @@ cc_result Gfx_TakeScreenshot(struct Stream* output) {
}
void Gfx_GetApiInfo(cc_string* info) {
int freeMem = texmem_total_free();
int usedMem = texmem_pages - freeMem;
int freeMem = blockalloc_total_free(tex_table, texmem_blocks);
int usedMem = texmem_blocks - freeMem;
freeMem *= TEXMEM_PAGE_SIZE;
usedMem *= TEXMEM_PAGE_SIZE;
freeMem *= TEXMEM_BLOCK_SIZE;
usedMem *= TEXMEM_BLOCK_SIZE;
float freeMemMB = freeMem / (1024.0f * 1024.0f);
float usedMemMB = usedMem / (1024.0f * 1024.0f);

View File

@ -1,6 +1,7 @@
#include "../_GraphicsBase.h"
#include "../Errors.h"
#include "../Window.h"
#include "../_BlockAlloc.h"
#include <packet.h>
#include <dma_tags.h>
@ -14,8 +15,6 @@
#include <draw3d.h>
#include <malloc.h>
#define MAX_PALETTE_ENTRIES 2048
#define QWORD_ALIGNED __attribute__((aligned(16)))
typedef struct Matrix VU0_matrix QWORD_ALIGNED;
@ -131,7 +130,8 @@ static void InitDrawingEnv(void) {
Q = dma_beg + 1;
}
static unsigned clut_offset, tex_offset;
static void InitPalette(void);
static unsigned tex_offset;
void Gfx_Create(void) {
primitive_type = 0; // PRIM_POINT, which isn't used here
@ -143,7 +143,7 @@ void Gfx_Create(void) {
formatDirty = true;
InitDrawingEnv();
clut_offset = graph_vram_allocate(MAX_PALETTE_ENTRIES, 1, GS_PSM_32, GRAPH_ALIGN_BLOCK);
InitPalette();
tex_offset = graph_vram_allocate(256, 256, GS_PSM_32, GRAPH_ALIGN_BLOCK);
// TODO maybe Min not actually needed?
@ -169,29 +169,29 @@ static CC_INLINE void DMAFlushBuffer(void) {
dma_channel_send_chain(DMA_CHANNEL_GIF, dma_beg, Q - dma_beg, 0, 0);
}
static int CalcTransferWords(int width, int height, int psm) {
static int CalcTransferBytes(int width, int height, int psm) {
switch (psm)
{
case GS_PSM_32:
case GS_PSM_24:
case GS_PSMZ_32:
case GS_PSMZ_24:
return (width * height);
return (width * height) << 2;
case GS_PSM_16:
case GS_PSM_16S:
case GS_PSMZ_16:
case GS_PSMZ_16S:
return (width * height) >> 1;
return (width * height) << 1;
case GS_PSM_8:
case GS_PSM_8H:
return (width * height) >> 2;
return (width * height);
case GS_PSM_4:
case GS_PSM_4HL:
case GS_PSM_4HH:
return (width * height) >> 3;
return (width * height) >> 1;
}
return 0;
}
@ -199,52 +199,38 @@ static int CalcTransferWords(int width, int height, int psm) {
static qword_t* BuildTransfer(qword_t* q, u8* src, int width, int height, int psm,
int dst_base, int dst_width)
{
int words = CalcTransferWords(width, height, psm); // 4 words = 1 qword
int qwords = (words + 3) / 4; // ceiling division by 4
int bytes = CalcTransferBytes(width, height, psm);
int qwords = (bytes + 15) / 16; // ceiling division by 16
// Parameters for RAM -> GS transfer
DMATAG_CNT(q,5,0,0,0);
DMATAG_CNT(q, 5, 0,0,0); q++;
{
q++;
PACK_GIFTAG(q, GIF_SET_TAG(4,0,0,0,GIF_FLG_PACKED,1), GIF_REG_AD);
q++;
PACK_GIFTAG(q, GS_SET_BITBLTBUF(0,0,0, dst_base >> 6, dst_width >> 6, psm), GS_REG_BITBLTBUF);
q++;
PACK_GIFTAG(q, GS_SET_TRXPOS(0,0,0,0,0), GS_REG_TRXPOS);
q++;
PACK_GIFTAG(q, GS_SET_TRXREG(width, height), GS_REG_TRXREG);
q++;
PACK_GIFTAG(q, GS_SET_TRXDIR(0), GS_REG_TRXDIR);
q++;
PACK_GIFTAG(q, GIF_SET_TAG(4,0,0,0,GIF_FLG_PACKED,1), GIF_REG_AD); q++;
PACK_GIFTAG(q, GS_SET_BITBLTBUF(0,0,0, dst_base >> 6, dst_width >> 6, psm), GS_REG_BITBLTBUF); q++;
PACK_GIFTAG(q, GS_SET_TRXPOS(0,0,0,0,0), GS_REG_TRXPOS); q++;
PACK_GIFTAG(q, GS_SET_TRXREG(width, height), GS_REG_TRXREG); q++;
PACK_GIFTAG(q, GS_SET_TRXDIR(0), GS_REG_TRXDIR); q++;
}
while (qwords)
{
int num_qwords = min(qwords, GIF_BLOCK_SIZE);
DMATAG_CNT(q,1,0,0,0);
DMATAG_CNT(q, 1, 0,0,0); q++;
{
q++;
PACK_GIFTAG(q, GIF_SET_TAG(num_qwords,0,0,0,GIF_FLG_IMAGE,0), 0);
q++;
PACK_GIFTAG(q, GIF_SET_TAG(num_qwords,0,0,0,GIF_FLG_IMAGE,0), 0); q++;
}
DMATAG_REF(q, num_qwords, (unsigned int)src, 0,0,0);
{
q++;
}
DMATAG_REF(q, num_qwords, (unsigned int)src, 0,0,0); q++;
src += num_qwords * 16;
qwords -= num_qwords;
}
DMATAG_END(q,2,0,0,0);
DMATAG_END(q, 2, 0,0,0); q++;
{
q++;
PACK_GIFTAG(q,GIF_SET_TAG(1,1,0,0,GIF_FLG_PACKED,1),GIF_REG_AD);
q++;
PACK_GIFTAG(q,1,GS_REG_TEXFLUSH);
q++;
PACK_GIFTAG(q, GIF_SET_TAG(1,1,0,0,GIF_FLG_PACKED,1), GIF_REG_AD); q++;
PACK_GIFTAG(q, 1, GS_REG_TEXFLUSH); q++;
}
return q;
@ -264,12 +250,20 @@ void Gfx_TransferPixels(void* src, int width, int height,
/*########################################################################################################################*
*---------------------------------------------------------Palettees--------------------------------------------------------*
*---------------------------------------------------------Palettes--------------------------------------------------------*
*#########################################################################################################################*/
#define MAX_PALETTES (MAX_PALETTE_ENTRIES / 64)
#define MAX_PAL_ENTRIES 8 // Could be 16, but 8 for balance between texture load speed and VRAM usage
#define PAL_TOTAL_ENTRIES 2048
#define MAX_PAL_4BPP_ENTRIES 16
static cc_uint8 palettes_used[MAX_PALETTES];
#define PAL_TOTAL_BLOCKS (PAL_TOTAL_ENTRIES / MAX_PAL_4BPP_ENTRIES)
static cc_uint8 pal_table[PAL_TOTAL_BLOCKS / BLOCKS_PER_PAGE];
static unsigned clut_offset;
#define PaletteAddr(index) (clut_offset + (index) * 64)
static void InitPalette(void) {
clut_offset = graph_vram_allocate(PAL_TOTAL_ENTRIES, 1, GS_PSM_32, GRAPH_ALIGN_BLOCK);
}
static CC_INLINE int FindInPalette(BitmapCol* palette, int pal_count, BitmapCol color) {
for (int i = 0; i < pal_count; i++)
@ -294,7 +288,7 @@ static int CalcPalette(BitmapCol* palette, struct Bitmap* bmp, int rowWidth) {
if (idx >= 0) continue;
// Too many distinct colours
if (pal_count >= MAX_PAL_ENTRIES) return 0;
if (pal_count >= MAX_PAL_4BPP_ENTRIES) return 0;
palette[pal_count] = color;
pal_count++;
@ -303,34 +297,22 @@ static int CalcPalette(BitmapCol* palette, struct Bitmap* bmp, int rowWidth) {
return pal_count;
}
#define PaletteAddr(index) (clut_offset + (index) * 64)
static void ApplyPalette(BitmapCol* palette, int pal_count, int pal_index) {
palettes_used[pal_index] = true;
static void ApplyPalette(BitmapCol* palette, int pal_index) {
dma_wait_fast();
// psm8, w=16 h=16
// psm8, w=16 h=16
// psm4, w=8 h=2
Gfx_TransferPixels(palette, 8, 2, GS_PSM_32, PaletteAddr(pal_index), 64);
}
static int FindFreePalette(cc_uint8 flags) {
if (flags & TEXTURE_FLAG_DYNAMIC) return -1;
for (int i = 0; i < MAX_PALETTES; i++)
{
if (!palettes_used[i]) return i;
}
return -1;
}
/*########################################################################################################################*
*---------------------------------------------------------Textures--------------------------------------------------------*
*#########################################################################################################################*/
typedef struct CCTexture_ {
cc_uint32 width, height; // 8 bytes
cc_uint16 log2_width, log2_height; // 4 bytes
cc_uint16 format, pal_index; // 4 bytes
cc_uint16 width, height; // 4 bytes
cc_uint32 padding; // 4 bytes
cc_uint16 log2_w, log2_h; // 4 bytes
cc_uint16 format, pal_index; // 4 bytes
BitmapCol pixels[]; // must be aligned to 16 bytes
} CCTexture;
@ -353,25 +335,30 @@ static void ConvertTexture_Palette(cc_uint8* dst, struct Bitmap* bmp, int rowWid
GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, int rowWidth, cc_uint8 flags, cc_bool mipmaps) {
int size = bmp->width * bmp->height * 4;
CCTexture* tex = (CCTexture*)memalign(16, 64 + size);
CCTexture* tex = (CCTexture*)memalign(16, 32 + size);
tex->width = bmp->width;
tex->height = bmp->height;
tex->log2_width = draw_log2(bmp->width);
tex->log2_height = draw_log2(bmp->height);
tex->pal_index = 0;
tex->width = bmp->width;
tex->height = bmp->height;
tex->log2_w = draw_log2(bmp->width);
tex->log2_h = draw_log2(bmp->height);
BitmapCol palette[MAX_PAL_ENTRIES] QWORD_ALIGNED;
int pal_count = 0;
int pal_index = FindFreePalette(flags);
BitmapCol palette[MAX_PAL_4BPP_ENTRIES] QWORD_ALIGNED;
int pal_count = 0;
int pal_index = -1;
if (pal_index >= 0) {
if (!(flags & TEXTURE_FLAG_DYNAMIC)) {
pal_count = CalcPalette(palette, bmp, rowWidth);
if (pal_count > 0) ApplyPalette(palette, pal_count, pal_index);
if (pal_count > 0) {
pal_index = blockalloc_alloc(pal_table, PAL_TOTAL_BLOCKS, 1);
}
if (pal_index >= 0) {
ApplyPalette(palette, pal_index);
}
}
//Platform_Log2("%i, %i", &pal_index, &pal_count);
//Platform_Log4("%i, %i (%i x %i)", &pal_index, &pal_count, &bmp->width, &bmp->height);
if (pal_count > 0) {
if (pal_index >= 0) {
tex->format = GS_PSM_4;
tex->pal_index = pal_index;
ConvertTexture_Palette((cc_uint8*)tex->pixels, bmp, rowWidth, palette, pal_count);
@ -395,7 +382,7 @@ static void UpdateTextureBuffer(int context, CCTexture* tex, unsigned buf_addr,
unsigned clut_mode = tex->format == GS_PSM_32 ? CLUT_NO_LOAD : CLUT_LOAD;
PACK_GIFTAG(Q, GS_SET_TEX0(buf_addr >> 6, buf_stride >> 6, tex->format,
tex->log2_width, tex->log2_height, TEXTURE_COMPONENTS_RGBA, TEXTURE_FUNCTION_MODULATE,
tex->log2_w, tex->log2_h, TEXTURE_COMPONENTS_RGBA, TEXTURE_FUNCTION_MODULATE,
clut_addr, GS_PSM_32, CLUT_STORAGE_MODE1, clut_entry, clut_mode), GS_REG_TEX0 + context);
Q++;
}
@ -426,7 +413,7 @@ void Gfx_DeleteTexture(GfxResourceID* texId) {
if (!tex) return;
if (tex->format != GS_PSM_32) {
palettes_used[tex->pal_index] = false;
blockalloc_dealloc(pal_table, tex->pal_index, 1);
}
Mem_Free(tex);