From ac6f730bed5dde5f779bc3454ee0b8574e082c9d Mon Sep 17 00:00:00 2001 From: Lephenixnoir Date: Fri, 17 Sep 2021 17:33:26 +0200 Subject: [PATCH] Draw full-screen backgrounds without loading them --- .gitignore | 1 + cgdoom/cgdoom-frag.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ cgdoom/cgdoom-frag.h | 44 +++++++++++++++++++++++++++++ cgdoom/d_main.c | 3 +- cgdoom/f_finale.c | 12 +++++--- cgdoom/m_menu.c | 12 +++++--- cgdoom/v_video.c | 37 ++++++++++++++++++++++++- cgdoom/v_video.h | 2 ++ cgdoom/wi_stuff.c | 7 +++-- 9 files changed, 171 insertions(+), 13 deletions(-) create mode 100644 cgdoom/cgdoom-frag.c create mode 100644 cgdoom/cgdoom-frag.h diff --git a/.gitignore b/.gitignore index 7cd1b80..8ac81af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build-cg/ build-sdl2/ wad/ +wad-edited/ diff --git a/cgdoom/cgdoom-frag.c b/cgdoom/cgdoom-frag.c new file mode 100644 index 0000000..c882b95 --- /dev/null +++ b/cgdoom/cgdoom-frag.c @@ -0,0 +1,66 @@ +#include "cgdoom-frag.h" +#include "os.h" +#include "w_wad.h" + +int CGD_Frag_Map(const char *lumpname, CGD_Frag *frag) +{ + int lump = W_CheckNumForName(lumpname); + if(lump < 0) + return 1; + + lumpinfo_t *l = &lumpinfo[lump]; + int size = l->size; + int position = l->position; + int f = 0; + + while(f < CGD_FRAG_ESTIMATE && size > 0) { + int found = FindInFlash(&frag->data[f], size, position); + frag->size[f] = found; + + size -= found; + position += found; + f++; + } + + if(size) { + I_Error("CGD_Frag_Map: failed on '%s' (size=%d, offset=%d), could only" + " obtain %d", lumpname, l->size, l->position, l->size - size); + return 1; + } + + frag->size[f] = 0; + return 0; +} + +byte CGD_Frag_u8(CGD_Frag const *frag, int offset) +{ + int f = 0; + while(frag->size[f] != 0 && offset >= frag->size[f]) { + offset -= frag->size[f]; + f++; + } + + if(frag->size[f] == 0) + return 0; /* read out-of-bounds */ + + const byte *data = frag->data[f]; + return data[offset]; +} + +short CGD_Frag_i16LE(CGD_Frag const *frag, int offset) +{ + byte b1 = CGD_Frag_u8(frag, offset); + byte b2 = CGD_Frag_u8(frag, offset + 1); + + return (b2 << 8) | b1; +} + +int CGD_Frag_i32LE(CGD_Frag const *frag, int offset) +{ + byte b1 = CGD_Frag_u8(frag, offset); + byte b2 = CGD_Frag_u8(frag, offset + 1); + byte b3 = CGD_Frag_u8(frag, offset + 2); + byte b4 = CGD_Frag_u8(frag, offset + 3); + + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; +} diff --git a/cgdoom/cgdoom-frag.h b/cgdoom/cgdoom-frag.h new file mode 100644 index 0000000..68ae6f4 --- /dev/null +++ b/cgdoom/cgdoom-frag.h @@ -0,0 +1,44 @@ +#ifndef CGDOOM_FRAG_H +#define CGDOOM_FRAG_H + +#include "doomtype.h" + +/* CGDoom's abstracted access to fragmented lumps + + In CGDoom, the main factor limiting compatibility is the amount of available + heap. Therefore, it is beneficial to reduce the pressure on the heap by + using lumps directly from ROM. For non-fragmented lumps it's mostly + straightforward and only involves alignment independence, but for fragmented + lumps it's quite more involved. + + This header offers an interface that can be used to access fragmented lumps + in ROM by deferring every access to an intermediate function that looks up + the proper lump fragment. + + This is /very/ slow, without a doubt, but speed is not always a limiting + factor. One of the main uses for this is displaying full-screen patches + without loading them, which occurs in the main menu and in the intermission + screens. The latter is the main focus, since the intermission picture needs + to be loaded on top of level data, which may leave the heap both full and + fragmented. + + This interface exists just in case it is be useful for other lumps. */ + +/* Maximum number of fragments that we expect in a lump (+1 for terminator) */ +#define CGD_FRAG_ESTIMATE 33 + +/* Fragmented lump lookup table */ +typedef struct { + const void *data[CGD_FRAG_ESTIMATE]; + int size[CGD_FRAG_ESTIMATE+1]; +} CGD_Frag; + +/* Compute the lookup table for a lump; if successful, returns 0 */ +int CGD_Frag_Map(const char *lumpname, CGD_Frag *frag); + +/* Read different types within the lump */ +byte CGD_Frag_u8(CGD_Frag const *frag, int offset); +short CGD_Frag_i16LE(CGD_Frag const *frag, int offset); +int CGD_Frag_i32LE(CGD_Frag const *frag, int offset); + +#endif /* CGDOOM_FRAG_H */ diff --git a/cgdoom/d_main.c b/cgdoom/d_main.c index dd94616..6093a27 100644 --- a/cgdoom/d_main.c +++ b/cgdoom/d_main.c @@ -314,7 +314,8 @@ void D_PageTicker (void) // void D_PageDrawer (void) { - V_DrawPatch (0,0, 0, (const patch_t *)W_CacheLumpNameConst(pagename, PU_CACHE)); +// V_DrawPatch (0,0, 0, (const patch_t *)W_CacheLumpNameConst(pagename, PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, pagename); } diff --git a/cgdoom/f_finale.c b/cgdoom/f_finale.c index cfb4fef..a37413f 100644 --- a/cgdoom/f_finale.c +++ b/cgdoom/f_finale.c @@ -654,18 +654,22 @@ void F_Drawer (void) { case 1: if ( gamemode == retail ) - V_DrawPatch (0,0,0,(const patch_t*)W_CacheLumpNameConst("CREDIT",PU_CACHE)); + // V_DrawPatch (0,0,0,(const patch_t*)W_CacheLumpNameConst("CREDIT",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "CREDIT"); else - V_DrawPatch (0,0,0,(const patch_t*)W_CacheLumpNameConst("HELP2",PU_CACHE)); + // V_DrawPatch (0,0,0,(const patch_t*)W_CacheLumpNameConst("HELP2",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "HELP2"); break; case 2: - V_DrawPatch(0,0,0,(const patch_t*)W_CacheLumpNameConst("VICTORY2",PU_CACHE)); + // V_DrawPatch(0,0,0,(const patch_t*)W_CacheLumpNameConst("VICTORY2",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "VICTORY2"); break; case 3: F_BunnyScroll (); break; case 4: - V_DrawPatch (0,0,0,(const patch_t*)W_CacheLumpNameConst("ENDPIC",PU_CACHE)); + // V_DrawPatch (0,0,0,(const patch_t*)W_CacheLumpNameConst("ENDPIC",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "ENDPIC"); break; } } diff --git a/cgdoom/m_menu.c b/cgdoom/m_menu.c index 7833e04..27664ab 100644 --- a/cgdoom/m_menu.c +++ b/cgdoom/m_menu.c @@ -548,12 +548,14 @@ void M_DrawReadThis1(void) switch ( gamemode ) { case commercial: - V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("HELP",PU_CACHE)); + // V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("HELP",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "HELP"); break; case shareware: case registered: case retail: - V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("HELP1",PU_CACHE)); + // V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("HELP1",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "HELP1"); break; default: break; @@ -575,11 +577,13 @@ void M_DrawReadThis2(void) case retail: case commercial: // This hack keeps us from having to change menus. - V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("CREDIT",PU_CACHE)); + // V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("CREDIT",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "CREDIT"); break; case shareware: case registered: - V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("HELP2",PU_CACHE)); + // V_DrawPatchDirect (0,0,0,W_CacheLumpNamePatch("HELP2",PU_CACHE)); + V_DrawFullscreenPatchFragmented(0, "HELP2"); break; default: break; diff --git a/cgdoom/v_video.c b/cgdoom/v_video.c index ceeb914..ed51a42 100644 --- a/cgdoom/v_video.c +++ b/cgdoom/v_video.c @@ -26,7 +26,7 @@ //static const char - +#include "cgdoom-frag.h" #include "i_system.h" #include "r_local.h" @@ -39,6 +39,9 @@ #include "v_video.h" +#include "z_zone.h" +#include "w_wad.h" + // Each screen is [SCREENWIDTH*SCREENHEIGHT]; @@ -239,6 +242,38 @@ void V_DrawPatch(int x,int y,int scrn,const patch_t* patch) } } +// +// V_DrawFullscreenPatchFragmented +// Draws a full-screen opaque patch without loading it to RAM, even if it's +// fragmented. This function is useful in avoiding loading ~66 kB as a single +// lump when full-screen images are used, which occasionally fails due to +// fragmentation. +// +void V_DrawFullscreenPatchFragmented(int scrn, const char *lumpname) +{ + CGD_Frag patch; + if (CGD_Frag_Map(lumpname, &patch)) { + V_DrawPatch(0, 0, scrn, W_CacheLumpNamePatch(lumpname, PU_CACHE)); + return; + } + + byte *dest = screens[scrn]; + short w = CGD_Frag_i16LE(&patch, offsetof(patch_t, widthLE)); + short h = CGD_Frag_i16LE(&patch, offsetof(patch_t, heightLE)); + + if (!scrn) + V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); + + for (int x = 0; x < w; x++) { + int offset = CGD_Frag_i32LE(&patch, offsetof(patch_t, columnofsLE) + 4*x); + int h0 = CGD_Frag_u8(&patch, offset+1); + + for (int y = 0; y < h; y++) { + dest[SCREENWIDTH * y + x] = CGD_Frag_u8(&patch, offset + y+3+3*(y>=h0)); + } + } +} + // // V_DrawPatchFlipped // Masks a column based masked pic to the screen. diff --git a/cgdoom/v_video.h b/cgdoom/v_video.h index 3d43c09..3473b95 100644 --- a/cgdoom/v_video.h +++ b/cgdoom/v_video.h @@ -74,6 +74,8 @@ V_CopyRect void V_DrawPatch(int x,int y, int scrn,const patch_t* patch); #define V_DrawPatchDirect(x,y,scrn,patch ) V_DrawPatch(x,y,scrn,patch) +/* CGDoom: render opaque full-screen path without loading it */ +void V_DrawFullscreenPatchFragmented(int scrn, const char *lumpname); // Draw a linear block of pixels into the view buffer. void V_DrawBlock( int x, int y,int scrn,int width,int height,const byte* src ); diff --git a/cgdoom/wi_stuff.c b/cgdoom/wi_stuff.c index a7514bd..ec3cf2e 100644 --- a/cgdoom/wi_stuff.c +++ b/cgdoom/wi_stuff.c @@ -338,7 +338,7 @@ static int NUMCMAPS; // // background (map of levels). -static const patch_t* bg; +// static const patch_t* bg; // You Are Here graphic static const patch_t* yah[2]; @@ -986,8 +986,9 @@ static void WI_loadData(void) } // background - bg = W_CacheLumpNamePatch(name, PU_CACHE); - V_DrawPatch(0, 0, 1, bg); +// bg = W_CacheLumpNamePatch(name, PU_CACHE); +// V_DrawPatch(0, 0, 1, bg); + V_DrawFullscreenPatchFragmented(1, name); // UNUSED unsigned char *pic = screens[1]; // if (gamemode == commercial)