diff --git a/src/Launcher.c b/src/Launcher.c index c96f3966a..246fbd979 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -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(); } diff --git a/src/TexturePack.c b/src/TexturePack.c index a3cfea56b..4e5c02e72 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -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 */ diff --git a/src/TexturePack.h b/src/TexturePack.h index c75751c49..d1b02da62 100644 --- a/src/TexturePack.h +++ b/src/TexturePack.h @@ -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 { diff --git a/src/_BlockAlloc.h b/src/_BlockAlloc.h index 487d11361..e9df1f902 100644 --- a/src/_BlockAlloc.h +++ b/src/_BlockAlloc.h @@ -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; } diff --git a/src/dreamcast/Graphics_Dreamcast.c b/src/dreamcast/Graphics_Dreamcast.c index e762ec52e..47111d604 100644 --- a/src/dreamcast/Graphics_Dreamcast.c +++ b/src/dreamcast/Graphics_Dreamcast.c @@ -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); diff --git a/src/ps2/Graphics_PS2.c b/src/ps2/Graphics_PS2.c index 73200cd7a..d7cbc988c 100644 --- a/src/ps2/Graphics_PS2.c +++ b/src/ps2/Graphics_PS2.c @@ -1,6 +1,7 @@ #include "../_GraphicsBase.h" #include "../Errors.h" #include "../Window.h" +#include "../_BlockAlloc.h" #include #include @@ -14,8 +15,6 @@ #include #include -#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);