mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-08-03 10:47:39 -04:00
Fix loading default texture pack twice if it isn't default.zip
This commit is contained in:
parent
18bd436b39
commit
ad229da788
@ -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();
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user