Skip to content
Draft
23 changes: 22 additions & 1 deletion games/NXDoom/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#

config GAMES_NXDOOM
bool "NXDoom"
tristate "NXDoom"
default n
depends on ALLOW_GPL_COMPONENTS
depends on VIDEO_FB
Expand Down Expand Up @@ -178,6 +178,27 @@ 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---
The visplanes/openings/drawsegs/vissprites renderer scratch buffers
(sized by the options above) are static arrays by default, matching
vanilla DOOM. On a target where their combined size threatens the
internal DRAM budget once linked into a full application image,
enable this to allocate them from the heap instead (this target's
heap may be backed by external RAM/PSRAM). Static allocation is
preferred where DRAM budget is not a concern.

config GAMES_NXDOOM_STATDUMP_MAX_CAPTURES
int "Maximum statdump capture buffer entries"
default 32
range 1 1024
---help---
Number of playtime-statistics capture slots statdump.c reserves.
This is diagnostic/debug capture storage, not required for normal
gameplay - reduce it on a DRAM-constrained target.

config GAMES_NXDOOM_RANGECHECK
bool "Perform range checks"
default y
Expand Down
10 changes: 10 additions & 0 deletions games/NXDoom/src/d_iwad.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,16 @@ static void buld_iwad_dir_list(void)

add_iwad_dir(m_dir_name(myargv[0]));

/* Add the board's configured DOOM data directory. Kconfig documents
* CONFIG_GAMES_NXDOOM_PREFDIR as "Directory where DOOM WAD files are
* stored", but until now it was only used for the config/save file
* location -- nothing actually searched it for IWADs, forcing every
* launch to rely on the current directory or DOOMWADDIR/DOOMWADPATH
* being set by hand first.
*/

add_iwad_dir(CONFIG_GAMES_NXDOOM_PREFDIR);

/* Add DOOMWADDIR if it is in the environment */

env = getenv("DOOMWADDIR");
Expand Down
6 changes: 6 additions & 0 deletions games/NXDoom/src/doom/d_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,12 @@ void d_doomloop(void)

while (1)
{
/* Safe point (outside any framebuffer/heap access) for nxstore's
* SIGTERM-driven close request to actually take effect - see
* i_install_quit_signal() in i_system.h.
*/

i_poll_quit_signal();
d_run_frame();
}
}
Expand Down
4 changes: 4 additions & 0 deletions games/NXDoom/src/doom/r_bsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ line_t *linedef;
sector_t *frontsector;
sector_t *backsector;

#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
drawseg_t *drawsegs;
Comment thread
aviralgarg05 marked this conversation as resolved.
#else
drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS];
#endif
drawseg_t *ds_p;

/* newend is one past the last valid seg */
Expand Down
4 changes: 4 additions & 0 deletions games/NXDoom/src/doom/r_bsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ extern boolean markceiling;

extern boolean skymap;

#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
extern drawseg_t *drawsegs;
Comment thread
aviralgarg05 marked this conversation as resolved.
#else
extern drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS];
#endif
extern drawseg_t *ds_p;

extern lighttable_t **hscalelight;
Expand Down
4 changes: 2 additions & 2 deletions games/NXDoom/src/doom/r_draw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
18 changes: 18 additions & 0 deletions games/NXDoom/src/doom/r_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,24 @@ fixed_t r_scale_from_global_angle(angle_t visangle)

void r_set_view_size(int blocks, int detail)
{
/* screenblocks is only ever meant to hold 3..11 (set that way by the
* options menu and by the config default of 9). The renderer's view
* geometry math divides by values derived from it - notably
* pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth in
* r_execute_set_view_size() - so a 0 or otherwise out-of-range value
* turns into a divide-by-zero hardware exception (EXCCAUSE=6), which on
* this flat-memory build takes the whole board down rather than just
* this task. Clamp defensively so a bad/missing config value degrades
* to the default screen size instead of a system crash.
*/

if (blocks < 3 || blocks > 11)
{
printf("r_set_view_size: screenblocks=%d out of range, using 10\n",
blocks);
blocks = 10;
}
Comment thread
aviralgarg05 marked this conversation as resolved.

setsizeneeded = true;
setblocks = blocks;
setdetail = detail;
Expand Down
165 changes: 152 additions & 13 deletions games/NXDoom/src/doom/r_plane.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,17 @@ planefunction_t ceilingfunc;

/* Here comes the obnoxious "visplane". */

#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
visplane_t *visplanes;
Comment thread
aviralgarg05 marked this conversation as resolved.
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
Expand Down Expand Up @@ -114,12 +119,46 @@ 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)
/* y indexes cachedheight[]/cacheddistance[]/cachedxstep[]/cachedystep[]
* below, all sized SCREENHEIGHT - a y outside that range (observed on
* this port: y=255 against a 200-entry array, well past even
* viewheight) is an out-of-bounds array write, not just a "debug
* assertion". This used to be gated behind CONFIG_GAMES_NXDOOM_
* RANGECHECK and fatal (i_error(), which tears down the whole process
* on what vanilla Doom would just render as one glitched span) - both
* wrong: the memory-safety check must not be optional, and killing the
* entire game over one bad plane span is worse than just not drawing
* it. Clamp y into range instead of touching memory outside the
* buffers' real bounds - this still renders the span (as one glitched
* row, the same "wrong but visible" failure mode vanilla DOOM has) so
* a bad plane doesn't leave a blank gap on screen either.
*
* The clamp bound must be viewheight, not SCREENHEIGHT: this y is
* stored into ds_y and later used by r_draw_span() to index
* ylookup[] (r_draw.c), which r_init_buffer() only populates for
* [0, viewheight) - viewheight can be smaller than SCREENHEIGHT (a
* sub-window within the physical screen), so entries from viewheight
* up to SCREENHEIGHT are zero-initialized (NULL) pointers. Clamping
* to SCREENHEIGHT - 1 instead of viewheight - 1 traded the original
* out-of-bounds write for a NULL-pointer-plus-offset framebuffer
* write - confirmed on real hardware as a load/store exception at a
* small virtual address. viewheight is always <= SCREENHEIGHT, so
* this bound is safe for cachedheight[]/etc. too.
*/

if (x2 < x1 || x1 < 0 || x2 >= viewwidth)
{
i_error("R_MapPlane: %i, %i at %i", x1, x2, y);
return;
Comment thread
aviralgarg05 marked this conversation as resolved.
}

if (y < 0)
{
y = 0;
}
else if (y >= viewheight)
{
y = viewheight - 1;
}
#endif

if (planeheight != cachedheight[y])
{
Expand Down Expand Up @@ -160,27 +199,62 @@ static void r_map_plane(int y, int x1, int x2)
spanfunc();
}

/* Row indices into spanstart[] (sized SCREENHEIGHT) that are only ever
* safe to use as an array index within that range - t1/b1/t2/b2 in
* r_make_spans() below are also compared directly against each other to
* drive the span-tracking state machine (including vanilla DOOM's 0xff
* sentinel for "no span"/edge-of-plane), and that comparison logic must
* see the real, un-clamped values or the sentinel handling breaks. Only
* the array touches themselves need guarding.
*/

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)
{
/* t1/b1/t2/b2 come from a visplane's top[]/bottom[] arrays. In valid
* play these are either a real screen row or vanilla DOOM's 0xff
* (255) "no span here" sentinel; the loop conditions normally keep
* that sentinel away from spanstart[]. A malformed renderer state
* can violate that invariant, however: row 255 was observed reaching
* r_map_plane() on real hardware, after spanstart[t1]/[b1] had already
* been evaluated as the call argument. Guard every spanstart[] touch
* directly instead of altering t1/b1/t2/b2, so the state-machine
* comparisons and normal sentinel handling remain unchanged. An
* invalid closing row uses column zero as its bounded fallback; an
* invalid opening row is ignored.
*/

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--;
}
}
Expand All @@ -195,7 +269,70 @@ 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
/* These renderer scratch buffers are sized for a comfortable margin
* above vanilla DOOM's original limits and, on a DRAM-constrained
* target, blow the internal DRAM budget as static arrays - opt-in
* heap allocation instead (comes out of the PSRAM-backed user heap
* on this target) via 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)
{
/* i_error() doesn't necessarily terminate the whole board on this
* flat, single address-space build (see the comment below on
* relaunch) - free whatever partially succeeded so a failed
* allocation attempt doesn't leak across a subsequent relaunch.
*/

free(visplanes);
free(openings);
free(drawsegs);
free(vissprites);
visplanes = NULL;
openings = NULL;
drawsegs = NULL;
vissprites = NULL;

i_error("r_init_planes: failed to allocate renderer buffers");
}
Comment thread
aviralgarg05 marked this conversation as resolved.

/* i_quit() can be followed by another r_init_planes() call within the
* same boot (relaunching the game via nxpkg on this flat, single
* address-space build), so these heap buffers must be freed on exit
* or every relaunch leaks the previous allocation permanently.
*/

i_at_exit(r_shutdown_planes, true);
#endif
}

/* r_shutdown_planes
* Frees the renderer scratch buffers allocated by r_init_planes. Only
* registered as an exit handler when CONFIG_GAMES_NXDOOM_HEAP_BUFFERS is
* set - the static-array buffers have nothing to free.
*/

void r_shutdown_planes(void)
{
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
free(visplanes);
free(openings);
free(drawsegs);
free(vissprites);

visplanes = NULL;
openings = NULL;
drawsegs = NULL;
vissprites = NULL;
#endif
}

/* r_clear_planes
Expand Down Expand Up @@ -314,13 +451,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;
Expand Down
1 change: 1 addition & 0 deletions games/NXDoom/src/doom/r_plane.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions games/NXDoom/src/doom/r_things.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 4 additions & 0 deletions games/NXDoom/src/doom/r_things.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Loading
Loading