DEADSOFTWARE

opengl: do not even try to allocate texture with size more than supported by texture...
[flatwaifu.git] / src / gl / render.c
index e93bb126dab9e86b5972bff420f18c1ed65a20fe..91707ef5d32d2e7af2209a0a61d8c34d44672bb9 100644 (file)
@@ -57,6 +57,7 @@ typedef struct rgba {
 typedef struct node {
   struct cache *base;
   struct node *left, *right;
+  struct node *up;
   int l, t, r, b;
   int leaf;
 } node;
@@ -181,12 +182,14 @@ static node *R_node_alloc (node *p, int w, int h) {
       p->right = malloc(sizeof(node));
       if (pw - w > ph - h) {
         *p->left = (node) {
+          .up = p,
           .l = p->l,
           .t = p->t,
           .r = p->l + w - 1,
           .b = p->b
         };
         *p->right = (node) {
+          .up = p,
           .l = p->l + w,
           .t = p->t,
           .r = p->r,
@@ -194,12 +197,14 @@ static node *R_node_alloc (node *p, int w, int h) {
         };
       } else {
         *p->left = (node) {
+          .up = p,
           .l = p->l,
           .t = p->t,
           .r = p->r,
           .b = p->t + h - 1
         };
         *p->right = (node) {
+          .up = p,
           .l = p->l,
           .t = p->t + h,
           .r = p->r,
@@ -211,6 +216,52 @@ static node *R_node_alloc (node *p, int w, int h) {
   }
 }
 
+static int R_node_have_leaf (node *n) {
+  return n && (n->leaf || R_node_have_leaf(n->left) || R_node_have_leaf(n->right));
+}
+
+static void R_node_free_recursive (node *n) {
+  if (n) {
+    R_node_free_recursive(n->left);
+    R_node_free_recursive(n->right);
+    free(n);
+  }
+}
+
+static void R_node_free (node *n) {
+  if (n) {
+    //logo("free node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
+    assert(n->leaf);
+    assert(n->left == NULL);
+    assert(n->right == NULL);
+    n->leaf = 0;
+    n->base = NULL;
+    node *p = n->up;
+    while (p != NULL) {
+      assert(p->leaf == 0);
+      assert(p->left);
+      assert(p->right);
+      if (R_node_have_leaf(p) == 0) {
+        R_node_free_recursive(p->left);
+        p->left = NULL;
+        R_node_free_recursive(p->right);
+        p->right = NULL;
+        p = p->up;
+      } else {
+        p = NULL;
+      }
+    }
+  }
+}
+
+static void R_cache_get_max_texture_size (int *w, int *h) {
+  GLint size = 0;
+  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
+  size = min(max(size, 0), 512); // more can be buggy on older hardware
+  *w = size;
+  *h = size;
+}
+
 static void R_gl_bind_texture (GLuint id) {
   if (id != lastTexture) {
     glBindTexture(GL_TEXTURE_2D, id);
@@ -218,24 +269,23 @@ static void R_gl_bind_texture (GLuint id) {
 }
 
 static cache *R_cache_new (void) {
-  GLuint id = 0;
-  GLint size = 0;
+  int w, h;
+  GLuint id;
   cache *c = NULL;
-  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
-  size = size < 512 ? size : 512; // more can be buggy on older hardware
-  if (size) {
+  R_cache_get_max_texture_size(&w, &h);
+  if (w && h) {
     glGenTextures(1, &id);
     if (id) {
       R_gl_bind_texture(id);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
       c = malloc(sizeof(cache));
       if (c != NULL) {
         *c = (cache) {
           .id = id,
-          .root.r = size - 1,
-          .root.b = size - 1
+          .root.r = w - 1,
+          .root.b = h - 1
         };
       } else {
         glDeleteTextures(1, &id);
@@ -246,43 +296,70 @@ static cache *R_cache_new (void) {
   return c;
 }
 
+static void R_cache_free (cache *root, int freetexture) {
+  cache *next;
+  cache *c = root;
+  while (c != NULL) {
+    next = c->next;
+    R_node_free_recursive(c->root.left);
+    R_node_free_recursive(c->root.right);
+    if (freetexture && c->id != 0) {
+      glDeleteTextures(1, &c->id);
+    }
+    free(c);
+    c = next;
+  }
+}
+
 static node *R_cache_alloc (cache *root, int w, int h) {
   assert(root);
   assert(w > 0 && h > 0);
   node *n = NULL;
   cache *p = NULL;
   cache *c = root;
-  while (c && !n) {
-    n = R_node_alloc(&c->root, w, h);
-    if (n) {
-      n->base = c;
-    }
-    p = c;
-    c = c->next;
-  }
-  if (!n) {
-    c = R_cache_new();
-    if (c) {
-      p->next = c;
+  int maxw, maxh;
+  R_cache_get_max_texture_size(&maxw, &maxh);
+  if (w <= maxw && h <= maxh) {
+    while (c && !n) {
       n = R_node_alloc(&c->root, w, h);
       if (n) {
+        assert(n->leaf);
         n->base = c;
       }
+      p = c;
+      c = c->next;
+    }
+    if (!n) {
+      c = R_cache_new();
+      if (c) {
+        p->next = c;
+        n = R_node_alloc(&c->root, w, h);
+        if (n) {
+          assert(n->leaf);
+          n->base = c;
+        }
+      }
     }
   }
+  if (n) {
+    //logo("new node %p {%i:%i:%i:%i}\n", n, n->l, n->t, n->r, n->b);
+  } else {
+    logo("new node failed\n");
+  }
   return n;
 }
 
-static void R_cache_update (node *n, const void *data, int w, int h) {
+static void R_cache_update (node *n, const void *data, int x, int y, int w, int h) {
   assert(n);
+  assert(n->leaf);
   assert(n->base);
   assert(data);
-  int nw = n->r - n->l + 1;
-  int nh = n->b - n->t + 1;
-  assert(w == nw);
-  assert(h == nh);
+  assert(x >= 0);
+  assert(y >= 0);
+  assert(n->l + x + w - 1 <= n->r);
+  assert(n->t + y + h - 1 <= n->b);
   R_gl_bind_texture(n->base->id);
-  glTexSubImage2D(GL_TEXTURE_2D, 0, n->l, n->t, nw, nh, GL_RGBA, GL_UNSIGNED_BYTE, data);
+  glTexSubImage2D(GL_TEXTURE_2D, 0, n->l + x, n->t + y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data);
 }
 
 /* Generic helpers */
@@ -414,7 +491,7 @@ static rgba *R_extract_rgba_spr (vgaimg *v) {
 static image R_gl_create_image (const rgba *buf, int w, int h) {
   node *n = R_cache_alloc(root, w, h);
   if (n) {
-    R_cache_update(n, buf, w, h);
+    R_cache_update(n, buf, 0, 0, w, h);
   }
   return (image) {
     .n = n,
@@ -457,9 +534,10 @@ static image R_gl_get_special_spr (const char n[4], int s, int d, rgba *(*fn)(vg
 
 static void R_gl_free_image (image *img) {
   if (img->n != NULL && img->res >= 0) {
-    // TODO delete node
+    R_node_free(img->n);
   }
   img->n = NULL;
+  img->res = -1;
 }
 
 static void R_gl_draw_quad (int x, int y, int w, int h) {
@@ -1612,7 +1690,7 @@ void R_init (void) {
 }
 
 void R_done (void) {
-  // do nothing
+  R_cache_free(root, 1);
 }
 
 void R_setgamma (int g) {