diff --git a/include/QF/GLSL/qf_textures.h b/include/QF/GLSL/qf_textures.h index 0a6e3ba6d..2298c3e16 100644 --- a/include/QF/GLSL/qf_textures.h +++ b/include/QF/GLSL/qf_textures.h @@ -31,14 +31,6 @@ #include "QF/qtypes.h" typedef struct scrap_s scrap_t; -typedef struct subpic_s { - const struct subpic_s * const next; - const scrap_t * const scrap; - const struct vrect_s * const rect; - const int width; ///< requested width - const int height; ///< requested height - const float size; ///< size factor for tex coords (mult) -} subpic_t; int GLSL_LoadQuakeTexture (const char *identifier, int width, int height, byte *data); @@ -55,9 +47,11 @@ scrap_t *GLSL_CreateScrap (int size, int format, int linear); void GLSL_DestroyScrap (scrap_t *scrap); void GLSL_ScrapClear (scrap_t *scrap); int GLSL_ScrapTexture (scrap_t *scrap) __attribute__((pure)); -subpic_t *GLSL_ScrapSubpic (scrap_t *scrap, int width, int height); //XXX slow! -void GLSL_SubpicDelete (subpic_t *subpic); //XXX slow! -void GLSL_SubpicUpdate (subpic_t *subpic, byte *data, int batch); +//XXX slow! +struct subpic_s *GLSL_ScrapSubpic (scrap_t *scrap, int width, int height); +//XXX slow! +void GLSL_SubpicDelete (struct subpic_s *subpic); +void GLSL_SubpicUpdate (struct subpic_s *subpic, byte *data, int batch); void GLSL_ScrapFlush (scrap_t *scrap); #endif//__QF_GLSL_textures_h diff --git a/include/QF/render.h b/include/QF/render.h index 7f02672e2..607b8b800 100644 --- a/include/QF/render.h +++ b/include/QF/render.h @@ -55,6 +55,15 @@ typedef enum { extern struct vid_render_funcs_s *r_funcs; extern struct vid_render_data_s *r_data; +typedef struct subpic_s { + const struct subpic_s *const next; + const struct scrap_s *const scrap; ///< renderer specific type + const struct vrect_s *const rect; + const int width; ///< requested width + const int height; ///< requested height + const float size; ///< size factor for tex coords (mult) +} subpic_t; + // dynamic lights =========================================================== typedef struct dlight_s diff --git a/include/r_scrap.h b/include/r_scrap.h new file mode 100644 index 000000000..5539d7480 --- /dev/null +++ b/include/r_scrap.h @@ -0,0 +1,49 @@ +/* + r_scrap.h + + Renderer agnostic scrap management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/12 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ + +#ifndef __r_scrap_h +#define __r_scrap_h + +typedef struct rscrap_s { + struct vrect_s *free_rects; ///< linked list of free areas + struct vrect_s *rects; ///< linked list of allocated rects + int width; ///< overall width of scrap + int height; ///< overall height of scrap +} rscrap_t; + +void R_ScrapInit (rscrap_t *scrap, int width, int height); +void R_ScrapDelete (rscrap_t *scrap); +struct vrect_s *R_ScrapAlloc (rscrap_t *scrap, int width, int height); +void R_ScrapFree (rscrap_t *scrap, struct vrect_s *rect); +void R_ScrapClear (rscrap_t *scrap); +size_t R_ScrapArea (rscrap_t *scrap) __attribute__((pure)); +void R_ScrapDump (rscrap_t *scrap); + +#endif//__r_scrap_h diff --git a/libs/video/renderer/Makemodule.am b/libs/video/renderer/Makemodule.am index 3e5dd4f70..05740af60 100644 --- a/libs/video/renderer/Makemodule.am +++ b/libs/video/renderer/Makemodule.am @@ -37,6 +37,7 @@ libs_video_renderer_libQFrenderer_la_DEPENDENCIES= $(renderer_libs) libs_video_renderer_libQFrenderer_la_SOURCES=\ libs/video/renderer/r_cvar.c \ libs/video/renderer/r_init.c \ + libs/video/renderer/r_scrap.c \ libs/video/renderer/r_screen.c \ libs/video/renderer/r_progs.c diff --git a/libs/video/renderer/glsl/glsl_textures.c b/libs/video/renderer/glsl/glsl_textures.c index 8130dfdf5..1edd77b71 100644 --- a/libs/video/renderer/glsl/glsl_textures.c +++ b/libs/video/renderer/glsl/glsl_textures.c @@ -44,6 +44,7 @@ #include "QF/cmd.h" #include "QF/mathlib.h" #include "QF/model.h" +#include "QF/render.h" #include "QF/sys.h" #include "QF/vrect.h" @@ -51,15 +52,15 @@ #include "QF/GLSL/funcs.h" #include "QF/GLSL/qf_textures.h" +#include "r_scrap.h" + struct scrap_s { + rscrap_t rscrap; GLuint tnum; - int size; // in pixels, for now, always square, power of 2 int format; int bpp; byte *data; // local copy of the texture so updates can be batched vrect_t *batch; - vrect_t *free_rects; - vrect_t *rects; subpic_t *subpics; struct scrap_s *next; }; @@ -248,23 +249,22 @@ static void glsl_scraps_f (void) { scrap_t *scrap; - vrect_t *rect; int area; + int size; if (!scrap_list) { Sys_Printf ("No scraps\n"); return; } for (scrap = scrap_list; scrap; scrap = scrap->next) { - for (rect = scrap->free_rects, area = 0; rect; rect = rect->next) - area += rect->width * rect->height; + area = R_ScrapArea (&scrap->rscrap); + // always square + size = scrap->rscrap.width; Sys_Printf ("tnum=%u size=%d format=%04x bpp=%d free=%d%%\n", - scrap->tnum, scrap->size, scrap->format, scrap->bpp, - area * 100 / (scrap->size * scrap->size)); + scrap->tnum, size, scrap->format, scrap->bpp, + area * 100 / (size * size)); if (Cmd_Argc () > 1) { - for (rect = scrap->rects, area = 0; rect; rect = rect->next) - Sys_Printf ("%d %d %d %d\n", rect->x, rect->y, - rect->width, rect->height); + R_ScrapDump (&scrap->rscrap); } } } @@ -309,11 +309,9 @@ GLSL_CreateScrap (int size, int format, int linear) } scrap = malloc (sizeof (scrap_t)); qfeglGenTextures (1, &scrap->tnum); - scrap->size = size; + R_ScrapInit (&scrap->rscrap, size, size); scrap->format = format; scrap->bpp = bpp; - scrap->free_rects = VRect_New (0, 0, size, size); - scrap->rects = 0; scrap->subpics = 0; scrap->next = scrap_list; scrap_list = scrap; @@ -341,26 +339,13 @@ GLSL_CreateScrap (int size, int format, int linear) void GLSL_ScrapClear (scrap_t *scrap) { - vrect_t *t; subpic_t *sp; - - while (scrap->free_rects) { - t = scrap->free_rects; - scrap->free_rects = t->next; - VRect_Delete (t); - } - while (scrap->rects) { - t = scrap->rects; - scrap->rects = t->next; - VRect_Delete (t); - } while (scrap->subpics) { sp = scrap->subpics; scrap->subpics = (subpic_t *) sp->next; free (sp); } - - scrap->free_rects = VRect_New (0, 0, scrap->size, scrap->size); + R_ScrapClear (&scrap->rscrap); } void @@ -375,7 +360,7 @@ GLSL_DestroyScrap (scrap_t *scrap) } } GLSL_ScrapClear (scrap); - VRect_Delete (scrap->free_rects); + R_ScrapDelete (&scrap->rscrap); GLSL_ReleaseTexture (scrap->tnum); free (scrap->data); free (scrap); @@ -390,47 +375,13 @@ GLSL_ScrapTexture (scrap_t *scrap) subpic_t * GLSL_ScrapSubpic (scrap_t *scrap, int width, int height) { - int i, w, h; - vrect_t **t, **best; - vrect_t *old, *frags, *rect; + vrect_t *rect; subpic_t *subpic; - for (i = 0; i < 16; i++) - if (width <= (1 << i)) - break; - w = 1 << i; - for (i = 0; i < 16; i++) - if (height <= (1 << i)) - break; - h = 1 << i; - - best = 0; - for (t = &scrap->free_rects; *t; t = &(*t)->next) { - if ((*t)->width < w || (*t)->height < h) - continue; // won't fit - if (!best) { - best = t; - continue; - } - if ((*t)->width <= (*best)->width || (*t)->height <= (*best)->height) - best = t; + rect = R_ScrapAlloc (&scrap->rscrap, width, height); + if (!rect) { + return 0; } - if (!best) - return 0; // couldn't find a spot - old = *best; - *best = old->next; - rect = VRect_New (old->x, old->y, w, h); - frags = VRect_Difference (old, rect); - VRect_Delete (old); - if (frags) { - // old was bigger than the requested size - for (old = frags; old->next; old = old->next) - ; - old->next = scrap->free_rects; - scrap->free_rects = frags; - } - rect->next = scrap->rects; - scrap->rects = rect; subpic = malloc (sizeof (subpic_t)); *((subpic_t **) &subpic->next) = scrap->subpics; @@ -439,7 +390,7 @@ GLSL_ScrapSubpic (scrap_t *scrap, int width, int height) *((vrect_t **) &subpic->rect) = rect; *((int *) &subpic->width) = width; *((int *) &subpic->height) = height; - *((float *) &subpic->size) = 1.0 / scrap->size; + *((float *) &subpic->size) = 1.0 / scrap->rscrap.width; return subpic; } @@ -448,8 +399,6 @@ GLSL_SubpicDelete (subpic_t *subpic) { scrap_t *scrap = (scrap_t *) subpic->scrap; vrect_t *rect = (vrect_t *) subpic->rect; - vrect_t *old, *merge; - vrect_t **t; subpic_t **sp; for (sp = &scrap->subpics; *sp; sp = (subpic_t **) &(*sp)->next) @@ -459,29 +408,7 @@ GLSL_SubpicDelete (subpic_t *subpic) Sys_Error ("GLSL_ScrapDelSubpic: broken subpic"); *sp = (subpic_t *) subpic->next; free (subpic); - for (t = &scrap->rects; *t; t = &(*t)->next) - if (*t == rect) - break; - if (*t != rect) - Sys_Error ("GLSL_ScrapDelSubpic: broken subpic"); - *t = rect->next; - - do { - merge = 0; - for (t = &scrap->free_rects; *t; t = &(*t)->next) { - merge = VRect_Merge (*t, rect); - if (merge) { - old = *t; - *t = (*t)->next; - VRect_Delete (old); - VRect_Delete (rect); - rect = merge; - break; - } - } - } while (merge); - rect->next = scrap->free_rects; - scrap->free_rects = rect; + R_ScrapFree (&scrap->rscrap, rect); } void @@ -502,7 +429,7 @@ GLSL_SubpicUpdate (subpic_t *subpic, byte *data, int batch) scrap->batch = VRect_New (rect->x, rect->y, rect->width, rect->height); } - step = scrap->size * scrap->bpp; + step = scrap->rscrap.width * scrap->bpp; sbytes = subpic->width * scrap->bpp; dest = scrap->data + rect->y * step + rect->x * scrap->bpp; for (i = 0; i < subpic->height; i++, dest += step, data += sbytes) @@ -519,6 +446,7 @@ void GLSL_ScrapFlush (scrap_t *scrap) { vrect_t *rect = scrap->batch; + int size = scrap->rscrap.width; if (!rect) return; @@ -526,9 +454,9 @@ GLSL_ScrapFlush (scrap_t *scrap) //should update to not update the entire horizontal block qfeglBindTexture (GL_TEXTURE_2D, scrap->tnum); qfeglTexSubImage2D (GL_TEXTURE_2D, 0, 0, rect->y, - scrap->size, rect->height, scrap->format, + size, rect->height, scrap->format, GL_UNSIGNED_BYTE, - scrap->data + rect->y * scrap->size * scrap->bpp); + scrap->data + rect->y * size * scrap->bpp); VRect_Delete (rect); scrap->batch = 0; } diff --git a/libs/video/renderer/r_init.c b/libs/video/renderer/r_init.c index f298538ba..733004a21 100644 --- a/libs/video/renderer/r_init.c +++ b/libs/video/renderer/r_init.c @@ -46,6 +46,7 @@ #include "QF/plugin/general.h" #include "r_internal.h" +#include "r_scrap.h" #include "vid_internal.h" cvar_t *vidrend_plugin; @@ -61,6 +62,7 @@ vid_render_funcs_t *r_funcs; #define U __attribute__ ((used)) static U void (*const r_progs_init)(struct progs_s *) = R_Progs_Init; +static U void (*const r_scrapdelete)(rscrap_t *) = R_ScrapDelete; #undef U static void diff --git a/libs/video/renderer/r_scrap.c b/libs/video/renderer/r_scrap.c new file mode 100644 index 000000000..86a4ca0a6 --- /dev/null +++ b/libs/video/renderer/r_scrap.c @@ -0,0 +1,183 @@ +/* + r_scrap.c + + Renderer agnostic scrap management + + Copyright (C) 2021 Bill Currie + + Author: Bill Currie + Date: 2021/1/12 + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA + +*/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "QF/sys.h" +#include "QF/vrect.h" + +#include "compat.h" +#include "r_scrap.h" + +static unsigned +pow2rup (unsigned x) +{ + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x++; + return x; +} + +VISIBLE void +R_ScrapInit (rscrap_t *scrap, int width, int height) +{ + width = pow2rup (width); + height = pow2rup (height); + scrap->width = width; + scrap->height = height; + scrap->rects = 0; + scrap->free_rects = VRect_New (0, 0, width, height); +} + +VISIBLE void +R_ScrapDelete (rscrap_t *scrap) +{ + R_ScrapClear (scrap); + VRect_Delete (scrap->free_rects); +} + +VISIBLE vrect_t * +R_ScrapAlloc (rscrap_t *scrap, int width, int height) +{ + int w, h; + vrect_t **t, **best; + vrect_t *old, *frags, *rect; + + w = pow2rup (width); + h = pow2rup (height); + + best = 0; + for (t = &scrap->free_rects; *t; t = &(*t)->next) { + if ((*t)->width < w || (*t)->height < h) + continue; // won't fit + if (!best) { + best = t; + continue; + } + if ((*t)->width <= (*best)->width || (*t)->height <= (*best)->height) + best = t; + } + if (!best) + return 0; // couldn't find a spot + old = *best; + *best = old->next; + rect = VRect_New (old->x, old->y, w, h); + frags = VRect_Difference (old, rect); + VRect_Delete (old); + if (frags) { + // old was bigger than the requested size + for (old = frags; old->next; old = old->next) + ; + old->next = scrap->free_rects; + scrap->free_rects = frags; + } + rect->next = scrap->rects; + scrap->rects = rect; + + return rect; +} + +VISIBLE void +R_ScrapFree (rscrap_t *scrap, vrect_t *rect) +{ + vrect_t *old, *merge; + vrect_t **t; + + for (t = &scrap->rects; *t; t = &(*t)->next) + if (*t == rect) + break; + if (*t != rect) + Sys_Error ("R_ScrapFree: broken rect"); + *t = rect->next; + + do { + merge = 0; + for (t = &scrap->free_rects; *t; t = &(*t)->next) { + merge = VRect_Merge (*t, rect); + if (merge) { + old = *t; + *t = (*t)->next; + VRect_Delete (old); + VRect_Delete (rect); + rect = merge; + break; + } + } + } while (merge); + rect->next = scrap->free_rects; + scrap->free_rects = rect; +} + +VISIBLE void +R_ScrapClear (rscrap_t *scrap) +{ + vrect_t *t; + + while (scrap->free_rects) { + t = scrap->free_rects; + scrap->free_rects = t->next; + VRect_Delete (t); + } + while (scrap->rects) { + t = scrap->rects; + scrap->rects = t->next; + VRect_Delete (t); + } + + scrap->free_rects = VRect_New (0, 0, scrap->width, scrap->height); +} + +VISIBLE size_t +R_ScrapArea (rscrap_t *scrap) +{ + vrect_t *rect; + size_t area; + + for (rect = scrap->free_rects, area = 0; rect; rect = rect->next) { + area += rect->width * rect->height; + } + return area; +} + +VISIBLE void +R_ScrapDump (rscrap_t *scrap) +{ + vrect_t *rect; + + for (rect = scrap->rects; rect; rect = rect->next) { + Sys_Printf ("%d %d %d %d\n", rect->x, rect->y, + rect->width, rect->height); + } +}