diff --git a/games/NXDoom/Kconfig b/games/NXDoom/Kconfig index d2bd2080f21..064ae79a57d 100644 --- a/games/NXDoom/Kconfig +++ b/games/NXDoom/Kconfig @@ -4,7 +4,7 @@ # config GAMES_NXDOOM - bool "NXDoom" + tristate "NXDoom" default n depends on ALLOW_GPL_COMPONENTS depends on VIDEO_FB @@ -252,6 +252,20 @@ config GAMES_NXDOOM_MAXDRAWSEGS memory, so you may reduce the number. However, too few will cause rendering issues (overflow is checked to avoid crashes). +config GAMES_NXDOOM_HEAP_BUFFERS + bool "Allocate renderer scratch buffers on the heap" + default n + ---help--- + Use heap-backed renderer buffers instead of the default static arrays. + Enable this only when the target heap can hold them. + +config GAMES_NXDOOM_STATDUMP_MAX_CAPTURES + int "Maximum statdump capture buffer entries" + default 32 + range 1 1024 + ---help--- + Number of diagnostic playtime-statistics capture slots. + config GAMES_NXDOOM_RANGECHECK bool "Perform range checks" default y diff --git a/games/NXDoom/src/d_iwad.c b/games/NXDoom/src/d_iwad.c index a6f1cd9453d..fcbf901a97d 100644 --- a/games/NXDoom/src/d_iwad.c +++ b/games/NXDoom/src/d_iwad.c @@ -271,6 +271,10 @@ static void buld_iwad_dir_list(void) add_iwad_dir(m_dir_name(myargv[0])); + /* Add the configured DOOM data directory */ + + add_iwad_dir(CONFIG_GAMES_NXDOOM_PREFDIR); + /* Add DOOMWADDIR if it is in the environment */ env = getenv("DOOMWADDIR"); diff --git a/games/NXDoom/src/doom/d_main.c b/games/NXDoom/src/doom/d_main.c index 52df6eda110..d44ff7d2e38 100644 --- a/games/NXDoom/src/doom/d_main.c +++ b/games/NXDoom/src/doom/d_main.c @@ -1294,6 +1294,7 @@ void d_doomloop(void) while (1) { + i_poll_quit_signal(); d_run_frame(); } } diff --git a/games/NXDoom/src/doom/r_bsp.c b/games/NXDoom/src/doom/r_bsp.c index 605b21beaa8..7dbd64b708e 100644 --- a/games/NXDoom/src/doom/r_bsp.c +++ b/games/NXDoom/src/doom/r_bsp.c @@ -78,7 +78,11 @@ line_t *linedef; sector_t *frontsector; sector_t *backsector; +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS +drawseg_t *drawsegs; +#else drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]; +#endif drawseg_t *ds_p; /* newend is one past the last valid seg */ diff --git a/games/NXDoom/src/doom/r_bsp.h b/games/NXDoom/src/doom/r_bsp.h index 66c6d611fff..c964b234c4a 100644 --- a/games/NXDoom/src/doom/r_bsp.h +++ b/games/NXDoom/src/doom/r_bsp.h @@ -52,7 +52,11 @@ extern boolean markceiling; extern boolean skymap; +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS +extern drawseg_t *drawsegs; +#else extern drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]; +#endif extern drawseg_t *ds_p; extern lighttable_t **hscalelight; diff --git a/games/NXDoom/src/doom/r_draw.c b/games/NXDoom/src/doom/r_draw.c index ff243912aed..00feeb9f7f3 100644 --- a/games/NXDoom/src/doom/r_draw.c +++ b/games/NXDoom/src/doom/r_draw.c @@ -597,7 +597,7 @@ void r_draw_span(void) #ifdef CONFIG_GAMES_NXDOOM_RANGECHECK if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || - (unsigned)ds_y > SCREENHEIGHT) + ds_y < 0 || ds_y >= viewheight) { i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y); } @@ -724,7 +724,7 @@ void r_draw_span_low(void) #ifdef CONFIG_GAMES_NXDOOM_RANGECHECK if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH || - (unsigned)ds_y > SCREENHEIGHT) + ds_y < 0 || ds_y >= viewheight) { i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y); } diff --git a/games/NXDoom/src/doom/r_main.c b/games/NXDoom/src/doom/r_main.c index b50cdc33773..2cfe6cb730f 100644 --- a/games/NXDoom/src/doom/r_main.c +++ b/games/NXDoom/src/doom/r_main.c @@ -31,6 +31,7 @@ #include "d_loop.h" #include "doomdef.h" +#include "i_system.h" #include "m_bbox.h" #include "m_menu.h" @@ -685,6 +686,13 @@ fixed_t r_scale_from_global_angle(angle_t visangle) void r_set_view_size(int blocks, int detail) { + /* Reject invalid screen sizes before calculating view geometry. */ + + if (blocks < 3 || blocks > 11) + { + i_error("r_set_view_size: screenblocks=%d out of range", blocks); + } + setsizeneeded = true; setblocks = blocks; setdetail = detail; diff --git a/games/NXDoom/src/doom/r_plane.c b/games/NXDoom/src/doom/r_plane.c index 65a2c2fa78b..e849de7ce9d 100644 --- a/games/NXDoom/src/doom/r_plane.c +++ b/games/NXDoom/src/doom/r_plane.c @@ -57,12 +57,17 @@ planefunction_t ceilingfunc; /* Here comes the obnoxious "visplane". */ +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS +visplane_t *visplanes; +short *openings; +#else visplane_t visplanes[CONFIG_GAMES_NXDOOM_MAXVISPLANES]; +short openings[MAXOPENINGS]; +#endif visplane_t *lastvisplane; visplane_t *floorplane; visplane_t *ceilingplane; -short openings[MAXOPENINGS]; short *lastopening; /* Clip values are the solid pixel bounding the range. floorclip starts out @@ -114,12 +119,21 @@ static void r_map_plane(int y, int x1, int x2) fixed_t length; unsigned index; -#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK - if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) + /* Ensure array indices are in range before access. */ + + if (x2 < x1 || x1 < 0 || x2 >= viewwidth) { - i_error("R_MapPlane: %i, %i at %i", x1, x2, y); + return; + } + + if (y < 0) + { + y = 0; + } + else if (y >= viewheight) + { + y = viewheight - 1; } -#endif if (planeheight != cachedheight[y]) { @@ -160,27 +174,42 @@ static void r_map_plane(int y, int x1, int x2) spanfunc(); } +static inline boolean r_row_in_range(int row) +{ + return row >= 0 && row < SCREENHEIGHT; +} + static void r_make_spans(int x, int t1, int b1, int t2, int b2) { + /* Check that row is in range before indexing arrays. */ + while (t1 < t2 && t1 <= b1) { - r_map_plane(t1, spanstart[t1], x - 1); + r_map_plane(t1, r_row_in_range(t1) ? spanstart[t1] : 0, x - 1); t1++; } while (b1 > b2 && b1 >= t1) { - r_map_plane(b1, spanstart[b1], x - 1); + r_map_plane(b1, r_row_in_range(b1) ? spanstart[b1] : 0, x - 1); b1--; } while (t2 < t1 && t2 <= b2) { - spanstart[t2] = x; + if (r_row_in_range(t2)) + { + spanstart[t2] = x; + } + t2++; } while (b2 > b1 && b2 >= t2) { - spanstart[b2] = x; + if (r_row_in_range(b2)) + { + spanstart[b2] = x; + } + b2--; } } @@ -195,7 +224,58 @@ static void r_make_spans(int x, int t1, int b1, int t2, int b2) void r_init_planes(void) { - /* Doh! */ +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS + visplanes = malloc(sizeof(visplane_t) * CONFIG_GAMES_NXDOOM_MAXVISPLANES); + openings = malloc(sizeof(short) * MAXOPENINGS); + drawsegs = malloc(sizeof(drawseg_t) * CONFIG_GAMES_NXDOOM_MAXDRAWSEGS); + vissprites = malloc(sizeof(vissprite_t) * + CONFIG_GAMES_NXDOOM_MAXVISSPRITES); + + if (visplanes == NULL || openings == NULL || drawsegs == NULL || + vissprites == NULL) + { + r_shutdown_planes(); + + i_error("r_init_planes: failed to allocate renderer buffers"); + } + + /* Release heap buffers when the game exits. */ + + i_at_exit(r_shutdown_planes, true); +#endif +} + +/* r_shutdown_planes + * Free heap-backed renderer buffers. + */ + +void r_shutdown_planes(void) +{ +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS + if (visplanes != NULL) + { + free(visplanes); + visplanes = NULL; + } + + if (openings != NULL) + { + free(openings); + openings = NULL; + } + + if (drawsegs != NULL) + { + free(drawsegs); + drawsegs = NULL; + } + + if (vissprites != NULL) + { + free(vissprites); + vissprites = NULL; + } +#endif } /* r_clear_planes @@ -314,13 +394,15 @@ visplane_t *r_check_plane(visplane_t *pl, int start, int stop) /* make a new visplane */ + if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES) + { + i_error("r_check_plane: no more visplanes"); + } + lastvisplane->height = pl->height; lastvisplane->picnum = pl->picnum; lastvisplane->lightlevel = pl->lightlevel; - if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES) - i_error("r_check_plane: no more visplanes"); - pl = lastvisplane++; pl->minx = start; pl->maxx = stop; diff --git a/games/NXDoom/src/doom/r_plane.h b/games/NXDoom/src/doom/r_plane.h index 1ae3b79254b..f51ccfa37fa 100644 --- a/games/NXDoom/src/doom/r_plane.h +++ b/games/NXDoom/src/doom/r_plane.h @@ -58,6 +58,7 @@ extern fixed_t distscale[SCREENWIDTH]; ****************************************************************************/ void r_init_planes(void); +void r_shutdown_planes(void); void r_clear_planes(void); void r_draw_planes(void); diff --git a/games/NXDoom/src/doom/r_things.c b/games/NXDoom/src/doom/r_things.c index 019d83a7048..0084da909df 100644 --- a/games/NXDoom/src/doom/r_things.c +++ b/games/NXDoom/src/doom/r_things.c @@ -93,7 +93,11 @@ spriteframe_t sprtemp[29]; int maxframe; const char *spritename; +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS +vissprite_t *vissprites; +#else vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]; +#endif vissprite_t *vissprite_p; int newvissprite; diff --git a/games/NXDoom/src/doom/r_things.h b/games/NXDoom/src/doom/r_things.h index cdd29948368..eb730c28d7e 100644 --- a/games/NXDoom/src/doom/r_things.h +++ b/games/NXDoom/src/doom/r_things.h @@ -28,7 +28,11 @@ * Public Data ****************************************************************************/ +#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS +extern vissprite_t *vissprites; +#else extern vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES]; +#endif extern vissprite_t *vissprite_p; extern vissprite_t vsprsortedhead; diff --git a/games/NXDoom/src/doom/statdump.c b/games/NXDoom/src/doom/statdump.c index 1db5657ab6c..977a4b9b160 100644 --- a/games/NXDoom/src/doom/statdump.c +++ b/games/NXDoom/src/doom/statdump.c @@ -39,7 +39,7 @@ * Pre-processor Definitions ****************************************************************************/ -#define MAX_CAPTURES 32 +#define MAX_CAPTURES CONFIG_GAMES_NXDOOM_STATDUMP_MAX_CAPTURES /**************************************************************************** * Private Data diff --git a/games/NXDoom/src/i_main.c b/games/NXDoom/src/i_main.c index bd9dab60910..1961b873446 100644 --- a/games/NXDoom/src/i_main.c +++ b/games/NXDoom/src/i_main.c @@ -57,6 +57,8 @@ void d_doom_main(void); int main(int argc, char **argv) { + i_install_quit_signal(); + /* save arguments */ myargc = argc; diff --git a/games/NXDoom/src/i_system.c b/games/NXDoom/src/i_system.c index b3867e0add9..083118bda43 100644 --- a/games/NXDoom/src/i_system.c +++ b/games/NXDoom/src/i_system.c @@ -22,6 +22,8 @@ * Included Files ****************************************************************************/ +#include +#include #include #include #include @@ -75,6 +77,10 @@ static atexit_listentry_t *exit_funcs = NULL; static boolean already_quitting = false; +/* Set by the signal handler and polled from the main loop. */ + +static volatile sig_atomic_t quit_requested = 0; + /* Read Access Violation emulation. * * From PrBoom+, by entryway. @@ -320,6 +326,69 @@ void i_quit(void) exit(0); } +/**************************************************************************** + * Name: i_quit_signal_handler + * + * Description: + * Records that a quit was requested. The work is deferred to + * i_poll_quit_signal() so that no cleanup runs from signal context. + * + ****************************************************************************/ + +static void i_quit_signal_handler(int signo) +{ + (void)signo; + quit_requested = 1; +} + +/**************************************************************************** + * Name: i_install_quit_signal + * + * Description: + * Installs the SIGTERM handler used to request a clean exit. + * + ****************************************************************************/ + +void i_install_quit_signal(void) +{ + struct sigaction sa; + + /* Reset state left by an earlier built-in run. */ + + quit_requested = 0; + exit_funcs = NULL; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = i_quit_signal_handler; + sigemptyset(&sa.sa_mask); + + if (sigaction(SIGTERM, &sa, NULL) < 0) + { + /* Not fatal: the game runs, it just cannot be asked to exit. */ + + printf("nxdoom: failed to install SIGTERM handler: %d\n", + errno); + } +} + +/**************************************************************************** + * Name: i_poll_quit_signal + * + * Description: + * Exits if a quit was requested. Called from the main loop, where the + * cleanup i_quit() performs is safe to run. + * + ****************************************************************************/ + +void i_poll_quit_signal(void) +{ + if (quit_requested) + { + printf("nxdoom: quit signal seen, calling i_quit\n"); + i_quit(); + } +} + void i_error(const char *error, ...) { char msgbuf[512]; diff --git a/games/NXDoom/src/i_system.h b/games/NXDoom/src/i_system.h index ae1c568068b..82138da593e 100644 --- a/games/NXDoom/src/i_system.h +++ b/games/NXDoom/src/i_system.h @@ -73,6 +73,26 @@ ticcmd_t *i_base_ticcmd(void); void i_quit(void) NORETURN; +/**************************************************************************** + * Name: i_install_quit_signal + * + * Description: + * Install the SIGTERM handler used to request a clean exit. + * + ****************************************************************************/ + +void i_install_quit_signal(void); + +/**************************************************************************** + * Name: i_poll_quit_signal + * + * Description: + * Exit if SIGTERM requested a clean shutdown. + * + ****************************************************************************/ + +void i_poll_quit_signal(void); + void i_error(const char *error, ...) NORETURN PRINTF_ATTR(1, 2); void i_tactile(int on, int off, int total); diff --git a/games/NXDoom/src/i_video.c b/games/NXDoom/src/i_video.c index d9a76a65144..0b111b4d7ed 100644 --- a/games/NXDoom/src/i_video.c +++ b/games/NXDoom/src/i_video.c @@ -105,6 +105,11 @@ struct graphics_state_s unsigned outw; unsigned outh; + /* Scaled image origin in pixels; origin is the byte offset. */ + + unsigned outx; + unsigned outy; + /* Maps an output column onto the source column it is drawn from, so that * the inner loop needs neither a division nor a separate case for * fractional scaling. @@ -487,6 +492,25 @@ static void blit_screen(void) prevsy = sy; prevrow = out; } + +#ifdef CONFIG_FB_UPDATE + /* Notify cached or DMA-backed frame buffers after drawing. */ + + { + struct fb_area_s area; + + area.x = g_graphics_state.outx; + area.y = g_graphics_state.outy; + area.w = outw; + area.h = outh; + + if (ioctl(g_graphics_state.fd, FBIO_UPDATE, + (unsigned long)((uintptr_t)&area)) < 0) + { + i_error("ioctl(FBIO_UPDATE) failed: %d\n", errno); + } + } +#endif } static void update_grab(void) @@ -936,13 +960,12 @@ void i_init_graphics(void) g_graphics_state.outh = SCREENHEIGHT * g_graphics_state.scale; #endif - /* Centre the scaled image in the frame buffer */ + /* Center after the plane stride and pixel size are known. */ - g_graphics_state.origin = - (g_graphics_state.vinfo.yres - g_graphics_state.outh) / 2 * - g_graphics_state.pinfo.stride + - (g_graphics_state.vinfo.xres - g_graphics_state.outw) / 2 * - (g_graphics_state.pinfo.bpp >> 3); + g_graphics_state.outx = + (g_graphics_state.vinfo.xres - g_graphics_state.outw) / 2; + g_graphics_state.outy = + (g_graphics_state.vinfo.yres - g_graphics_state.outh) / 2; /* Build the output column to source column map once */ @@ -965,6 +988,10 @@ void i_init_graphics(void) i_error("ioctl(FBIOGET_PLANEINFO) failed: %d\n", errno); } + g_graphics_state.origin = + g_graphics_state.outy * g_graphics_state.pinfo.stride + + g_graphics_state.outx * (g_graphics_state.pinfo.bpp >> 3); + /* Initialize frame buffer memory for actual rendering */ g_graphics_state.fbmem =