-
Notifications
You must be signed in to change notification settings - Fork 754
games/NXDoom: RGB565 support, loadable-module conversion, and crash fixes #3644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,7 +52,11 @@ extern boolean markceiling; | |
|
|
||
| extern boolean skymap; | ||
|
|
||
| #ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS | ||
| extern drawseg_t *drawsegs; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
| #else | ||
| extern drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS]; | ||
| #endif | ||
| extern drawseg_t *ds_p; | ||
|
|
||
| extern lighttable_t **hscalelight; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 on lines
+698
to
+704
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you actually run into an error that caused this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, a truncated config file left screenblocks at 0, and the view-size math divides by a value derived from it, so it hit a divide-by-zero hardware exception and took the whole board down. |
||
|
|
||
| setsizeneeded = true; | ||
| setblocks = blocks; | ||
| setdetail = detail; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,12 +57,17 @@ planefunction_t ceilingfunc; | |
|
|
||
| /* Here comes the obnoxious "visplane". */ | ||
|
|
||
| #ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS | ||
| visplane_t *visplanes; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will put it behind the same Kconfig switch. |
||
| 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,34 @@ 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. | ||
| */ | ||
|
|
||
| if (x2 < x1 || x1 < 0 || x2 >= viewwidth) | ||
| { | ||
| i_error("R_MapPlane: %i, %i at %i", x1, x2, y); | ||
| return; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why remove the error report?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The bounds check itself was already there, just gated behind a debug-only config and fatal when tripped,
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe rendering the glitch is better if there are no other side effects? More like vanilla DOOM. I don't know which looks worse from a rendering standpoint, skipping doesn't seem like a huge deal.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there's a middle ground: instead of returning early (which leaves a blank gap where that scanline should be), I can clamp
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In that case I would render the glitch instead. |
||
| } | ||
|
|
||
| if (y < 0) | ||
| { | ||
| y = 0; | ||
| } | ||
| else if (y >= SCREENHEIGHT) | ||
| { | ||
| y = SCREENHEIGHT - 1; | ||
| } | ||
| #endif | ||
|
|
||
| if (planeheight != cachedheight[y]) | ||
| { | ||
|
|
@@ -195,7 +222,55 @@ 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("r_init_planes: failed to allocate renderer buffers"); | ||
| } | ||
|
Comment on lines
+233
to
+243
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not think we should be allocating these, at least not without a Kconfig switch. I think the static allocation is better.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same as above |
||
|
|
||
| /* 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,17 +57,28 @@ void d_doom_main(void); | |
|
|
||
| int main(int argc, char **argv) | ||
| { | ||
| /* save arguments */ | ||
| /* save arguments | ||
| * | ||
| * +1 and an explicit NULL terminator: argv is conventionally | ||
| * NULL-terminated at argv[argc] (this is what the OS/exec path | ||
| * guarantees for the `argv` parameter above), and some of this | ||
| * codebase's own argument handling was written assuming that holds | ||
| * for myargv too - an under-sized allocation here leaves myargv[argc] | ||
| * pointing at whatever the allocator happens to return next, which | ||
| * only reads as "probably zero" by chance depending on heap layout. | ||
| */ | ||
|
|
||
| myargc = argc; | ||
| myargv = malloc(argc * sizeof(char *)); | ||
| myargv = malloc((argc + 1) * sizeof(char *)); | ||
| assert(myargv != NULL); | ||
|
|
||
| for (int i = 0; i < argc; i++) | ||
|
Comment on lines
+62
to
75
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you actually run into an error with this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, a bad-pointer-dereference crash (LoadProhibitedCause) on real hardware, root-caused through the crash dump and symbol resolution against the built ELF. myargv was allocated for exactly argc pointers but code elsewhere assumed it was NULL-terminated at argv[argc], so that slot pointed at whatever the allocator returned next (usually zero by luck)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you include the repro in this PR or with some before/after logs?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly, a clean repro recipe is tricky here since it's heap-layout dependent — that's exactly what made it read as "NULL by luck" instead of a guaranteed crash. It wasn't triggered by anything unusual on my end though, just an ordinary |
||
| { | ||
| myargv[i] = m_string_duplicate(argv[i]); | ||
| } | ||
|
|
||
| myargv[argc] = NULL; | ||
|
|
||
| /* Print the program version and exit. */ | ||
|
|
||
| if (m_parm_exists("-version") || m_parm_exists("--version")) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will put it behind the same Kconfig switch.