summary refs log tree commit diff
path: root/third_party/gldc/src/texture.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/gldc/src/texture.c')
-rw-r--r--third_party/gldc/src/texture.c235
1 files changed, 235 insertions, 0 deletions
diff --git a/third_party/gldc/src/texture.c b/third_party/gldc/src/texture.c
new file mode 100644
index 0000000..5fc2875
--- /dev/null
+++ b/third_party/gldc/src/texture.c
@@ -0,0 +1,235 @@
+#include "private.h"
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sh4.h"
+#include "yalloc/yalloc.h"
+
+#ifndef NDEBUG
+/* We're debugging, use normal assert */
+#include <assert.h>
+#define gl_assert assert
+#else
+/* Release mode, use our custom assert */
+#include <stdio.h>
+#include <stdlib.h>
+
+#define gl_assert(x) \
+    do {\
+        if(!(x)) {\
+            fprintf(stderr, "Assertion failed at %s:%d\n", __FILE__, __LINE__);\
+            exit(1);\
+        }\
+    } while(0); \
+
+#endif
+
+
+/* We always leave this amount of vram unallocated to prevent
+ * issues with the allocator */
+#define PVR_MEM_BUFFER_SIZE (64 * 1024)
+
+TextureObject* TEXTURE_ACTIVE = NULL;
+static TextureObject TEXTURE_LIST[MAX_TEXTURE_COUNT];
+static unsigned char TEXTURE_USED[MAX_TEXTURE_COUNT / 8];
+
+static int texture_id_map_used(unsigned int id) {
+    unsigned int i = id / 8;
+    unsigned int j = id % 8;
+
+    return TEXTURE_USED[i] & (unsigned char)(1 << j);
+}
+
+static void texture_id_map_reserve(unsigned int id) {
+    unsigned int i = id / 8;
+    unsigned int j = id % 8;
+    TEXTURE_USED[i] |= (unsigned char)(1 << j);
+}
+
+static void texture_id_map_release(unsigned int id) {
+    unsigned int i = id / 8;
+    unsigned int j = id % 8;
+    TEXTURE_USED[i] &= (unsigned char)~(1 << j);
+}
+
+unsigned int texture_id_map_alloc(void) {
+    unsigned int id;
+    
+    // ID 0 is reserved for default texture
+    for(id = 1; id < MAX_TEXTURE_COUNT; ++id) {
+        if(!texture_id_map_used(id)) {
+            texture_id_map_reserve(id);
+            return id;
+        }
+    }
+    return 0;
+}
+
+
+static void* YALLOC_BASE = NULL;
+static size_t YALLOC_SIZE = 0;
+
+static void* yalloc_alloc_and_defrag(size_t size) {
+    void* ret = yalloc_alloc(YALLOC_BASE, size);
+
+    if(!ret) {
+        /* Tried to allocate, but out of room, let's try defragging
+         * and repeating the alloc */
+        fprintf(stderr, "Ran out of memory, defragmenting\n");
+        glDefragmentTextureMemory_KOS();
+        ret = yalloc_alloc(YALLOC_BASE, size);
+    }
+
+    gl_assert(ret && "Out of PVR memory!");
+
+    return ret;
+}
+
+#define GL_KOS_INTERNAL_DEFAULT_MIPMAP_LOD_BIAS 4
+static void _glInitializeTextureObject(TextureObject* txr, unsigned int id) {
+    txr->index  = id;
+    txr->width  = txr->height = 0;
+    txr->mipmap = 0;
+    txr->data   = NULL;
+    txr->minFilter = GL_NEAREST;
+    txr->magFilter = GL_NEAREST;
+    txr->mipmap_bias = GL_KOS_INTERNAL_DEFAULT_MIPMAP_LOD_BIAS;
+}
+
+void _glInitTextures() {
+    memset(TEXTURE_USED, 0, sizeof(TEXTURE_USED));
+
+    // Initialize zero as an actual texture object though because apparently it is!
+    TextureObject* default_tex = &TEXTURE_LIST[0];
+    _glInitializeTextureObject(default_tex, 0);
+    texture_id_map_reserve(0);
+    TEXTURE_ACTIVE = default_tex;
+
+    size_t vram_free = pvr_mem_available();
+    YALLOC_SIZE = vram_free - PVR_MEM_BUFFER_SIZE; /* Take all but 64kb VRAM */
+    YALLOC_BASE = pvr_mem_malloc(YALLOC_SIZE);
+
+#ifdef __DREAMCAST__
+    /* Ensure memory is aligned */
+    gl_assert((uintptr_t) YALLOC_BASE % 32 == 0);
+#endif
+
+    yalloc_init(YALLOC_BASE, YALLOC_SIZE);
+}
+
+GLuint gldcGenTexture(void) {
+    GLuint id = texture_id_map_alloc();
+    gl_assert(id);  // Generated IDs must never be zero
+    
+    TextureObject* txr = &TEXTURE_LIST[id];
+    _glInitializeTextureObject(txr, id);
+
+    gl_assert(txr->index == id);
+    
+    return id;
+}
+
+void gldcDeleteTexture(GLuint id) {
+    if(id == 0) return;
+    /* Zero is the "default texture" and we never allow deletion of it */
+
+    if(texture_id_map_used(id)) {
+    	TextureObject* txr = &TEXTURE_LIST[id];
+        gl_assert(txr->index == id);
+
+        if(txr == TEXTURE_ACTIVE) {
+            // Reset to the default texture
+            TEXTURE_ACTIVE = &TEXTURE_LIST[0];
+        }
+
+        if(txr->data) {
+            yalloc_free(YALLOC_BASE, txr->data);
+            txr->data = NULL;
+        }
+
+        texture_id_map_release(id);
+    }
+}
+
+void gldcBindTexture(GLuint id) {
+    gl_assert(texture_id_map_used(id));
+    TextureObject* txr = &TEXTURE_LIST[id];
+
+    TEXTURE_ACTIVE = txr;
+    gl_assert(TEXTURE_ACTIVE->index == id);
+
+    STATE_DIRTY = GL_TRUE;
+}
+
+int gldcAllocTexture(int w, int h, int format) {
+    TextureObject* active = TEXTURE_ACTIVE;
+
+    if (active->data) {
+        /* pre-existing texture - check if changed */
+        if (active->width != w || active->height != h) {
+            /* changed - free old texture memory */
+            yalloc_free(YALLOC_BASE, active->data);
+            active->data = NULL;
+            active->mipmap = 0;
+        }
+    }
+
+    /* All colour formats are represented as shorts internally. */
+    GLuint bytes   = w * h * 2;
+    active->width  = w;
+    active->height = h;
+    active->color  = format;
+
+    if(!active->data) {
+        /* need texture memory */
+        active->data = yalloc_alloc_and_defrag(bytes);
+    }
+    if (!active->data) return GL_OUT_OF_MEMORY;
+
+    /* Mark level 0 as set in the mipmap bitmask */
+    active->mipmap |= (1 << 0);
+    return 0;
+}
+
+void gldcGetTexture(void** data, int* width, int* height) {
+    TextureObject* active = TEXTURE_ACTIVE;
+    *data   = active->data;
+    *width  = active->width;
+    *height = active->height;
+}
+
+GLuint _glMaxTextureMemory() {
+    return YALLOC_SIZE;
+}
+
+GLuint _glFreeTextureMemory() {
+    return yalloc_count_free(YALLOC_BASE);
+}
+
+GLuint _glUsedTextureMemory() {
+    return YALLOC_SIZE - _glFreeTextureMemory();
+}
+
+GLuint _glFreeContiguousTextureMemory() {
+    return yalloc_count_continuous(YALLOC_BASE);
+}
+
+void glDefragmentTextureMemory_KOS(void) {
+    yalloc_defrag_start(YALLOC_BASE);
+
+    GLuint id;
+
+    /* Replace all texture pointers */
+    for(id = 0; id < MAX_TEXTURE_COUNT; id++){
+        if(texture_id_map_used(id)){
+            TextureObject* txr = &TEXTURE_LIST[id];
+            gl_assert(txr->index == id);
+            txr->data = yalloc_defrag_address(YALLOC_BASE, txr->data);
+        }
+    }
+
+    yalloc_defrag_commit(YALLOC_BASE);
+}