Skip to content

Commit ca9e8c6

Browse files
committed
Draw pics caching rework
- Unified cache for menu and CSQC pics - Dynamically allocated entries - Fast pic name to pic lookup using hash map - Protect scrap / pic cache update from CSQC with the same mutex as SCR_DrawGUI
1 parent 2eb8106 commit ca9e8c6

8 files changed

Lines changed: 195 additions & 126 deletions

File tree

Quake/draw.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
3030

3131
extern qpic_t *draw_disc; // also used on sbar
3232

33+
// clang-format off
34+
typedef enum
35+
{
36+
PICFLAG_AUTO = 0, // value used when no flags known
37+
PICFLAG_WAD = (1u << 0), // name matches that of a wad lump
38+
PICFLAG_WRAP = (1u << 2), // make sure npot stuff doesn't break wrapping.
39+
PICFLAG_MIPMAP = (1u << 3), // disable use of scrap...
40+
PICFLAG_NOLOAD = (1u << 31) // To request the cached status only (internal use only, cannot be marshalled from a QuakeC float anyway)
41+
} picflags_t;
42+
// clang-format on
43+
3344
// Use float coords and sizes in Draw_XXX to allow sub-pixel precision
3445
void Draw_Init (void);
3546
void Draw_Character (cb_context_t *cbx, float x, float y, int num);
@@ -42,10 +53,11 @@ void Draw_Fill (cb_context_t *cbx, float x, float y, float w, float h, int c, fl
4253
void Draw_FadeScreen (cb_context_t *cbx);
4354
void Draw_String (cb_context_t *cbx, float x, float y, const char *str);
4455
void Draw_String_3D (cb_context_t *cbx, vec3_t coords, float size, const char *str);
45-
qpic_t *Draw_PicFromWad2 (const char *name, unsigned int texflags);
56+
qpic_t *Draw_PicFromWad2 (const char *name, unsigned int texflags, int picflags);
4657
qpic_t *Draw_PicFromWad (const char *name);
4758
qpic_t *Draw_CachePic (const char *path);
48-
qpic_t *Draw_TryCachePic (const char *path, unsigned int texflags);
59+
qpic_t *Draw_GetCachedPic (const char *path);
60+
qpic_t *Draw_TryCachePic (const char *path, unsigned int texflags, int picflags);
4961
void Draw_NewGame (void);
5062

5163
void GL_Viewport (cb_context_t *cbx, float x, float y, float width, float height, float min_depth, float max_depth);

Quake/gl_draw.c

Lines changed: 125 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -87,16 +87,20 @@ typedef struct
8787

8888
typedef struct cachepic_s
8989
{
90-
char name[MAX_QPATH];
91-
qpic_t pic;
92-
byte padding[32]; // for appended glpic
90+
// dynamically-allocated chained cachepic_t
91+
struct cachepic_s *next;
92+
char name[MAX_QPATH];
93+
qpic_t pic;
94+
int picflags;
95+
byte padding[32]; // for appended glpic
9396
} cachepic_t;
9497

95-
// TODO : vso : should be dynamically allocated instead ?
96-
// On the other hand it takes so little memory that we can push MAX_CACHED_PICS very very high and never touch it again.
97-
#define MAX_CACHED_PICS 8192 // vso : from Spike = 512 - increased to avoid csqc issues.
98-
static cachepic_t menu_cachepics[MAX_CACHED_PICS];
99-
static int menu_numcachepics;
98+
// draw_qcvm_mutex also protects q_cachepics / scrap updates
99+
extern SDL_Mutex *draw_qcvm_mutex;
100+
101+
static cachepic_t *q_cachepics = NULL;
102+
// Fast lookup pic name => cachepic_t* from q_cachepics.
103+
hash_map_t *q_cachepics_map = NULL;
100104

101105
// scrap allocation
102106
// Allocate all the little status bar obejcts into a single texture
@@ -118,7 +122,7 @@ Scrap_AllocBlock
118122
returns an index into scrap_texnums[] and the position inside it
119123
================
120124
*/
121-
int Scrap_AllocBlock (int w, int h, int *x, int *y)
125+
static int Scrap_AllocBlock (int w, int h, int *x, int *y)
122126
{
123127
int i, j;
124128
int best, best2;
@@ -164,7 +168,7 @@ int Scrap_AllocBlock (int w, int h, int *x, int *y)
164168
Scrap_Upload -- johnfitz -- now uses TexMgr
165169
================
166170
*/
167-
void Scrap_Upload (void)
171+
static void Scrap_Upload (void)
168172
{
169173
char name[8];
170174
int i;
@@ -185,25 +189,24 @@ void Scrap_Upload (void)
185189
Draw_PicFromWad
186190
================
187191
*/
188-
qpic_t *Draw_PicFromWad2 (const char *name, unsigned int texflags)
192+
qpic_t *Draw_PicFromWad2 (const char *name, unsigned int texflags, int picflags)
189193
{
190194
int i;
191-
cachepic_t *pic;
192195
qpic_t *p;
196+
cachepic_t *pic;
193197
glpic_t gl;
194198
src_offset_t offset; // johnfitz
195199
lumpinfo_t *info;
196200

197-
// Spike -- added cachepic stuff here, to avoid glitches if the function is called multiple times with the same image.
198-
for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++)
199-
{
200-
if (!strncmp (name, pic->name, countof (pic->name)))
201-
return &pic->pic;
202-
}
203-
if (menu_numcachepics == MAX_CACHED_PICS)
204-
Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
201+
// Fast lookup:
202+
p = Draw_GetCachedPic (name);
203+
204+
if (p)
205+
return p;
205206

207+
// not cached, searched for it:
206208
p = (qpic_t *)W_GetLumpName (name, &info);
209+
207210
if (!p)
208211
{
209212
Con_Warning ("W_GetLumpName: %s not found\n", name);
@@ -266,17 +269,44 @@ qpic_t *Draw_PicFromWad2 (const char *name, unsigned int texflags)
266269
gl.th = 1;
267270
}
268271

269-
menu_numcachepics++;
272+
// Create a new pic:
273+
pic = Mem_Alloc (sizeof (*pic));
274+
pic->picflags = picflags;
275+
270276
q_strlcpy (pic->name, name, countof (pic->name));
271277
pic->pic = *p;
278+
272279
memcpy ((void *)&(pic->pic.data), &gl, sizeof (glpic_t));
273280

281+
// Add to cache:
282+
if (!q_cachepics)
283+
{
284+
q_cachepics = pic;
285+
}
286+
else
287+
{
288+
cachepic_t *current_pic = q_cachepics;
289+
cachepic_t *previous_pic = q_cachepics;
290+
291+
while (current_pic)
292+
{
293+
previous_pic = current_pic;
294+
current_pic = current_pic->next;
295+
}
296+
297+
previous_pic->next = pic;
298+
}
299+
300+
// we must downgrade pic->name as a pointer because the hashmap expects a key 8 bytes long (const char*)
301+
const char *pic_name_as_pointer = &pic->name[0];
302+
HashMap_Insert (q_cachepics_map, &pic_name_as_pointer, &pic);
303+
274304
return &pic->pic;
275305
}
276306

277307
qpic_t *Draw_PicFromWad (const char *name)
278308
{
279-
return Draw_PicFromWad2 (name, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP);
309+
return Draw_PicFromWad2 (name, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP, PICFLAG_AUTO);
280310
}
281311
#if 0 // vso - unused
282312
static qpic_t *Draw_GetCachedPic (const char *path)
@@ -292,24 +322,41 @@ static qpic_t *Draw_GetCachedPic (const char *path)
292322
return NULL;
293323
}
294324
#endif
325+
/*
326+
================
327+
Draw_GetCachedPic : get a pic from cache if already present, or return NULL if not.
328+
================
329+
*/
330+
qpic_t *Draw_GetCachedPic (const char *path)
331+
{
332+
// Fast lookup:
333+
cachepic_t **pic_ptr = HashMap_Lookup (cachepic_t *, q_cachepics_map, &path);
334+
335+
// found
336+
if (pic_ptr)
337+
{
338+
return &((*pic_ptr)->pic);
339+
}
340+
341+
return NULL;
342+
}
343+
295344
/*
296345
================
297346
Draw_CachePic
298347
================
299348
*/
300-
qpic_t *Draw_TryCachePic (const char *path, unsigned int texflags)
349+
qpic_t *Draw_TryCachePic (const char *path, unsigned int texflags, int picflags)
301350
{
351+
qpic_t *p;
302352
cachepic_t *pic;
303-
int i;
304353
glpic_t gl;
305354

306-
for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++)
307-
{
308-
if (!strncmp (path, pic->name, countof (pic->name)))
309-
return &pic->pic;
310-
}
311-
if (menu_numcachepics == MAX_CACHED_PICS)
312-
Sys_Error ("menu_numcachepics == MAX_CACHED_PICS");
355+
// Fast lookup:
356+
p = Draw_GetCachedPic (path);
357+
358+
if (p)
359+
return p;
313360

314361
//
315362
// load the pic from disk
@@ -327,9 +374,14 @@ qpic_t *Draw_TryCachePic (const char *path, unsigned int texflags)
327374
pic_data = Image_LoadImage (npath, (int *)&pic_width, (int *)&pic_height, &pic_fmt, 0);
328375

329376
if (!pic_data)
377+
{
330378
return NULL;
379+
}
380+
381+
// Create a new pic:
382+
pic = Mem_Alloc (sizeof (*pic));
383+
pic->picflags = picflags;
331384

332-
menu_numcachepics++;
333385
q_strlcpy (pic->name, path, countof (pic->name));
334386

335387
pic->pic.width = pic_width;
@@ -347,14 +399,36 @@ qpic_t *Draw_TryCachePic (const char *path, unsigned int texflags)
347399

348400
memcpy ((void *)&(pic->pic.data), &gl, sizeof (glpic_t));
349401

402+
// Add to cache:
403+
if (!q_cachepics)
404+
{
405+
q_cachepics = pic;
406+
}
407+
else
408+
{
409+
cachepic_t *current_pic = q_cachepics;
410+
cachepic_t *previous_pic = q_cachepics;
411+
412+
while (current_pic)
413+
{
414+
previous_pic = current_pic;
415+
current_pic = current_pic->next;
416+
}
417+
418+
previous_pic->next = pic;
419+
}
420+
// we must downgrade pic->name as a pointer because the hashmap expects a key 8 bytes long (const char*)
421+
const char *pic_name_as_pointer = &pic->name[0];
422+
HashMap_Insert (q_cachepics_map, &pic_name_as_pointer, &pic);
423+
350424
Mem_Free (pic_data);
351425

352426
return &pic->pic;
353427
}
354428

355429
qpic_t *Draw_CachePic (const char *path)
356430
{
357-
qpic_t *pic = Draw_TryCachePic (path, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP);
431+
qpic_t *pic = Draw_TryCachePic (path, TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP, PICFLAG_AUTO);
358432
if (!pic)
359433
Sys_Error ("Draw_CachePic: failed to load %s", path);
360434
return pic;
@@ -421,26 +495,35 @@ Draw_NewGame -- johnfitz
421495
*/
422496
void Draw_NewGame (void)
423497
{
424-
cachepic_t *pic;
425-
int i;
498+
SDL_LockMutex (draw_qcvm_mutex);
426499

427500
// empty scrap and reallocate gltextures
428501
memset (scrap_allocated, 0, sizeof (scrap_allocated));
429502
memset (scrap_texels, 255, sizeof (scrap_texels));
430503

431504
Scrap_Upload (); // creates 2 empty gltextures
432505

433-
// empty lmp cache
434-
for (pic = menu_cachepics, i = 0; i < menu_numcachepics; pic++, i++)
435-
pic->name[0] = 0;
436-
menu_numcachepics = 0;
506+
// empty pic cache :
507+
cachepic_t *cached_pic = q_cachepics;
508+
cachepic_t *next_cached_pic;
509+
510+
while (cached_pic)
511+
{
512+
next_cached_pic = cached_pic->next;
513+
Mem_Free (cached_pic);
514+
cached_pic = next_cached_pic;
515+
}
516+
q_cachepics = NULL;
517+
518+
HashMap_Clear (q_cachepics_map);
437519

438520
// reload wad pics
439521
W_LoadWadFile (); // johnfitz -- filename is now hard-coded for honesty
440522
Draw_LoadPics ();
441523
SCR_LoadPics ();
442524
Sbar_LoadPics ();
443-
PR_ReloadPics (false);
525+
526+
SDL_UnlockMutex (draw_qcvm_mutex);
444527
}
445528

446529
/*
@@ -450,6 +533,8 @@ Draw_Init -- johnfitz -- rewritten
450533
*/
451534
void Draw_Init (void)
452535
{
536+
q_cachepics_map = HashMap_Create (const char *, cachepic_t *, &HashStr, &HashStrCmp);
537+
453538
Cvar_RegisterVariable (&scr_conalpha);
454539

455540
// clear scrap and allocate gltextures

Quake/gl_rmisc.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ static SDL_Mutex *uniform_allocate_mutex;
9393
static SDL_Mutex *storage_allocate_mutex;
9494
static SDL_Mutex *garbage_mutex;
9595

96+
extern SDL_Mutex *draw_qcvm_mutex;
97+
9698
qboolean R_UseAlphaSort (void)
9799
{
98100
return r_alphasort.value && !R_UseOIT ();
@@ -263,8 +265,6 @@ static void R_ShowbboxesFilter_Completion_f (const char *partial)
263265
if (!sv.active)
264266
return;
265267

266-
extern SDL_Mutex *draw_qcvm_mutex;
267-
268268
SDL_LockMutex (draw_qcvm_mutex);
269269
PR_SwitchQCVM (&sv.qcvm);
270270

@@ -289,9 +289,8 @@ R_ShowbboxesFilterClear_f
289289
*/
290290
static void R_ShowbboxesFilterClear_f (void)
291291
{
292-
extern char *r_showbboxes_filter_strings;
293-
extern qboolean r_showbboxes_filter_byindex;
294-
extern SDL_Mutex *draw_qcvm_mutex;
292+
extern char *r_showbboxes_filter_strings;
293+
extern qboolean r_showbboxes_filter_byindex;
295294

296295
SDL_LockMutex (draw_qcvm_mutex);
297296

Quake/gl_screen.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,14 +1134,12 @@ static void SCR_DrawGUI (void *unused)
11341134
SCR_TileClear (cbx);
11351135

11361136
const qboolean cscqhud = (scr_style.value < 1.0f) && cl.qcvm.extfuncs.CSQC_DrawHud;
1137-
qboolean use_mutex = r_showbboxes.value && cscqhud;
1138-
1139-
if (use_mutex)
1140-
SDL_LockMutex (draw_qcvm_mutex);
11411137

11421138
if (cscqhud && setjmp (screen_error))
11431139
PR_ClearProgs (&cl.qcvm);
11441140

1141+
SDL_LockMutex (draw_qcvm_mutex);
1142+
11451143
if (scr_drawdialog) // new game confirm
11461144
{
11471145
if (con_forcedup)
@@ -1181,9 +1179,7 @@ static void SCR_DrawGUI (void *unused)
11811179
M_Draw (cbx);
11821180
}
11831181

1184-
if (use_mutex)
1185-
SDL_UnlockMutex (draw_qcvm_mutex);
1186-
1182+
SDL_UnlockMutex (draw_qcvm_mutex);
11871183
R_EndDebugUtilsLabel (cbx);
11881184
}
11891185

Quake/menu.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ M_DrawPic
278278
*/
279279
void M_DrawPic (cb_context_t *cbx, int x, int y, qpic_t *pic)
280280
{
281-
Draw_Pic (cbx, x, y, pic, 1.0f, false); // johnfitz -- simplified becuase centering is handled elsewhere
281+
Draw_Pic (cbx, x, y, pic, 1.0f, false); // johnfitz -- simplified because centering is handled elsewhere
282282
}
283283

284284
/*
@@ -632,7 +632,7 @@ static qpic_t *Get_Menu2 ()
632632
{
633633
qboolean base_game = COM_GetGameNames (false)[0] == 0;
634634
// Check if user has actually installed vkquake.pak, otherwise fall back to old menu
635-
return (base_game && registered.value) ? Draw_TryCachePic ("gfx/mainmenu2.lmp", TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP) : NULL;
635+
return (base_game && registered.value) ? Draw_TryCachePic ("gfx/mainmenu2.lmp", TEXPREF_ALPHA | TEXPREF_PAD | TEXPREF_NOPICMIP, PICFLAG_AUTO) : NULL;
636636
}
637637

638638
void M_Main_Draw (cb_context_t *cbx)

0 commit comments

Comments
 (0)