DEADSOFTWARE

opengl: do not even try to allocate texture with size more than supported by texture...
[flatwaifu.git] / src / gl / render.c
index e74d6bb5229989165983fcc619a715a25cfdd41c..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;
@@ -82,6 +83,7 @@ static int fullscreen;
 static SDL_Surface *surf;
 static rgb playpal[256];
 static byte bright[256];
+static GLuint lastTexture;
 static cache *root;
 
 /* Game */
@@ -180,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,
@@ -193,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,
@@ -210,79 +216,150 @@ static node *R_node_alloc (node *p, int w, int h) {
   }
 }
 
-static cache *R_cache_new (void) {
-  GLuint id = 0;
+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;
-  cache *c = NULL;
   glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
-  size /= 2; // TODO remove hack or detect ibook bug
-  if (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);
+  }
+}
+
+static cache *R_cache_new (void) {
+  int w, h;
+  GLuint id;
+  cache *c = NULL;
+  R_cache_get_max_texture_size(&w, &h);
+  if (w && h) {
     glGenTextures(1, &id);
     if (id) {
-      glBindTexture(GL_TEXTURE_2D, 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);
-      int ok = glGetError() == GL_NO_ERROR;
-      glBindTexture(GL_TEXTURE_2D, 0);
-      if (ok) {
-        c = malloc(sizeof(cache));
-        if (c != NULL) {
-          *c = (cache) {
-            .id = id,
-            .root.r = size - 1,
-            .root.b = size - 1
-          };
-        }
-      }
-      if (c == 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 = w - 1,
+          .root.b = h - 1
+        };
+      } else {
         glDeleteTextures(1, &id);
       }
     }
   }
-  logo("new cache %p\n", c);
+  //logo("new cache %p\n", c);
   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);
-  glBindTexture(GL_TEXTURE_2D, n->base->id);
-  glTexSubImage2D(GL_TEXTURE_2D, 0, n->l, n->t, nw, nh, GL_RGBA, GL_UNSIGNED_BYTE, data);
-  assert(glGetError() == GL_NO_ERROR);
-  glBindTexture(GL_TEXTURE_2D, 0);
+  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 + 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,
@@ -425,7 +502,7 @@ static image R_gl_create_image (const rgba *buf, int w, int h) {
 }
 
 static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
-  image img = (image) { .res = -1 };
+  image img;
   vgaimg *v = R_getvga(id);
   if (v != NULL) {
     rgba *buf = (*fn)(v);
@@ -435,6 +512,10 @@ static image R_gl_get_special_image (int id, rgba *(*fn)(vgaimg*)) {
     img.res = id;
     M_unlock(v);
     free(buf);
+  } else {
+    img = (image) {
+      .res = id
+    };
   }
   return img;
 }
@@ -453,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) {
@@ -475,7 +557,7 @@ static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip
     GLfloat bx = (flip ? img->n->r + 1 : img->n->l) / nh;
     GLfloat ay = (img->n->t) / nw;
     GLfloat by = (img->n->b + 1) / nh;
-    glBindTexture(GL_TEXTURE_2D, img->n->base->id);
+    R_gl_bind_texture(img->n->base->id);
     glEnable(GL_TEXTURE_2D);
     glBegin(GL_QUADS);
     glTexCoord2f(ax, ay); glVertex2i(x + w, y);
@@ -483,12 +565,11 @@ static void R_gl_draw_textured (image *img, int x, int y, int w, int h, int flip
     glTexCoord2f(bx, by); glVertex2i(x,     y + h);
     glTexCoord2f(ax, by); glVertex2i(x + w, y + h);
     glEnd();
-    glDisable(GL_TEXTURE_2D);
-    glBindTexture(GL_TEXTURE_2D, 0);
   } else {
     glColor3ub(255, 0, 0);
     glDisable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glDisable(GL_TEXTURE_2D);
     R_gl_draw_quad(x, y, w, h);
   }
 }
@@ -507,16 +588,12 @@ static void R_gl_draw_image_color (image *img, int x, int y, int flip) {
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
-  glDisable(GL_BLEND);
 }
 
 /* draw sprite with offset */
 static void R_gl_draw_image (image *img, int x, int y, int flip) {
-  int xx = flip ? x - img->w + img->x : x - img->x;
-  glEnable(GL_BLEND);
   glColor3ub(255, 255, 255);
-  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-  R_gl_draw_textured(img, xx, y - img->y, img->w, img->h, flip);
+  R_gl_draw_image_color(img, x, y, flip);
 }
 
 static void R_gl_set_color (byte c) {
@@ -769,6 +846,7 @@ static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int f
             }
             glEnable(GL_BLEND);
             glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+            glDisable(GL_TEXTURE_2D);
             R_gl_draw_quad(i * CELW, j * CELW, CELW, CELH);
           }
         } else {
@@ -781,6 +859,9 @@ static void R_draw_fld (byte *fld, int minx, int miny, int maxx, int maxy, int f
 
 static void R_draw_dots (void) {
   int i;
+  glDisable(GL_BLEND);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+  glDisable(GL_TEXTURE_2D);
   glBegin(GL_POINTS);
   for (i = 0; i < MAXDOT; i++) {
     if (dot[i].t != 0) {
@@ -1086,6 +1167,9 @@ static void R_draw_effects (void) {
         R_gl_draw_image(&fx_spr[s], fx[i].x, fx[i].y, fx_sprd[s]);
         break;
       case BUBL:
+        glDisable(GL_BLEND);
+        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+        glDisable(GL_TEXTURE_2D);
         glBegin(GL_POINTS);
         R_gl_set_color(0xC0 + fx[i].s);
         glVertex2i(fx[i].x >> 8, (fx[i].y >> 8) + 1);
@@ -1121,6 +1205,8 @@ static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
     }
   } else {
     glDisable(GL_BLEND);
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    glDisable(GL_TEXTURE_2D);
     R_gl_set_color(DEFAULT_SKY_COLOR);
     R_gl_draw_quad(0, 0, w, h);
   }
@@ -1146,6 +1232,7 @@ static void R_draw_view (int x, int y, int w, int h, int camx, int camy) {
     glColor4ub(255, 255, 255, 255);
     glEnable(GL_BLEND);
     glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+    glDisable(GL_TEXTURE_2D);
     R_gl_draw_quad(0, 0, w, h);
   }
   glPopMatrix();
@@ -1165,22 +1252,25 @@ static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
   if (p->invl) {
     if (get_pu_st(p->invl)) {
       glEnable(GL_BLEND);
-      glColor4ub(191, 191, 191, 255);
       glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO);
+      glDisable(GL_TEXTURE_2D);
+      glColor4ub(191, 191, 191, 255);
       R_gl_draw_quad(0, 0, cw, h);
     }
   } else {
     if (p->suit && get_pu_st(p->suit)) {
       glEnable(GL_BLEND);
-      glColor4ub(0, 255, 0, 192);
       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glDisable(GL_TEXTURE_2D);
+      glColor4ub(0, 255, 0, 192);
       R_gl_draw_quad(0, 0, cw, h);
     }
     int f = min(max(p->pain * 3, 0), 255);
     if (f > 0) {
       glEnable(GL_BLEND);
-      glColor4ub(255, 0, 0, f);
       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glDisable(GL_TEXTURE_2D);
+      glColor4ub(255, 0, 0, f);
       R_gl_draw_quad(0, 0, cw, h);
     }
   }
@@ -1196,6 +1286,8 @@ static void R_draw_player_view (player_t *p, int x, int y, int w, int h) {
     if (p->air < PL_AIR) {
       int a = min(max(p->air, 0), MAXAIR) * 100 / MAXAIR;
       glDisable(GL_BLEND);
+      glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+      glDisable(GL_TEXTURE_2D);
       R_gl_set_color(0xC8);
       R_gl_draw_quad(10, 49, a, 2);
     }
@@ -1316,7 +1408,7 @@ static void W_act (void) {
 void R_draw (void) {
   W_act();
   glClearColor(0, 0, 0, 1);
-  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+  glClear(GL_COLOR_BUFFER_BIT);
   glEnable(GL_SCISSOR_TEST);
   R_gl_setmatrix();
   switch (g_st) {
@@ -1598,7 +1690,7 @@ void R_init (void) {
 }
 
 void R_done (void) {
-  // do nothing
+  R_cache_free(root, 1);
 }
 
 void R_setgamma (int g) {
@@ -1722,7 +1814,6 @@ void R_end_load (void) {
 
 void R_loadsky (int sky) {
   char s[6];
-  logo("R_loadsky(%i)\n", sky);
   strcpy(s, "RSKYx");
   s[4] = '0' + sky;
   R_gl_free_image(&horiz);