Emulate Heap Fragmentation - #3863
Conversation
| @@ -0,0 +1,675 @@ | |||
| #include "stairs.h" | |||
There was a problem hiding this comment.
Given that this file is basically simulating the entire heap, which could be used for more than just this graveyard use case, I would recommend renaming this file and all associated heap functions that aren't specific to just this use case to something more general, like heap_sim or simulated_heap.
| #undef DEFINE_ACTOR | ||
|
|
||
| // Original size subtracted by various constant allocations which are always made on init, so those allocations don't need to be made there | ||
| size_t stairsAllocSize = 0x1D4790 - 0x26960 - 0x2200 - (0x55 * 0x60) - (3 * (48 * 16) / 2) - (4 * (32 * 32 * 4)) - 0x1000 - (20 * sizeof(MtxF)); |
There was a problem hiding this comment.
It would be nice to mention what all these magic numbers correspond to so that future uses will know if and how they need to do something similar.
There was a problem hiding this comment.
Alright, this section is already something which isnt the most accurate so may replace it with a more correct system anyway
| actorCtx->absoluteSpace = NULL; | ||
| } | ||
|
|
||
| if (CVarGetInteger("gStairs", 0)) { |
There was a problem hiding this comment.
It would make more sense to me to have all these simulated heap functions controlled by the gSimulateHeap cvar, and then use your gStairs one ONLY for the graveyard use case.
There was a problem hiding this comment.
so the "gStairs" cvar is just a silent variable (i.e. the user has no control over it) and then gSimulateHeap cvar will set this on/off at the start of play init. this is so that the heap is not allocated memory when it shouldnt, and that the memory always gets freed properly at the end of the play loop
There was a problem hiding this comment.
I understand the reasoning here, however, if this is going to be useful for emulating more than the Royal Tomb Grave unloading glitch, this variable should have a better name.
|
|
||
| // #Region [SoH] Stairs | ||
| if (CVarGetInteger("gSimulateHeap", 0)) { | ||
| CVarSetInteger("gStairs", 1); |
There was a problem hiding this comment.
By placing this in Play_Init, changes to the checkbox in the UI won't apply until the next time you change areas, since that's when Play_Init is run, right? It might be nice to mention that in the tooltip that that checking or unchecking the box won't apply until the next area load.
There was a problem hiding this comment.
Yes will add that to the tooltip!
|
Would this cause any issues with how we've been handling (i.e. removing) Object Dependency? I'll need to deep dive before I can properly review this because I don't fully understand it yet, but that question just popped into my head upon reading your description. I'm not sure if it's really connected in anyway at all so if you're not sure the answer is probably no. I'll do a full review at some point soon. |
|
Nah fortunately this is unrelated to object dependency, this is in regards to the heap rather than the object list that has been loaded. The actors don't actually use this memory anyway, it's all done so that a condition which checks 'do i have space to load this actor' returns as false |
|
Also thank you @jbodner09 ! I will try and find some time this week to address your review comments |
OK cool, and this is behind a toggle right? It looks like it is just making sure. Otherwise I would worry about this potentially limiting modding potential. |
|
Yeah it has a kind of locking system, so you can toggle it, but the toggle only takes effect when play init happens (i.e. new scene load). otherwise you would need to worry about freeing the heap or issues allocating to a heap with no initialised size |
OK, as long as that's mentioned somewhere I think that's OK. It wouldn't be the first enhancement that has issues toggling without a scene reload. I'm about to start an actual review but it might take a bit for me to parse it all. |
leggettc18
left a comment
There was a problem hiding this comment.
I think this looks reasonable to the best of my understanding, although I have asked for some clarification on naming, a few commented out chunks that need either an explanation for why they are commented or to just be removed, and some magic numbers that I would like some explanation for.
As a followup, not necessarily needed for this PR, would it be possible to enable the heap emulation and visualization, without it actually affecting gameplay? I could see that being useful for research purposes (although I suppose it could still be useful for research without decoupling the visualization from the gameplay). That may not be reasonable or all that useful, just a thought that popped into my head.
| Arena sStairsArena; | ||
|
|
||
| // using sizes from the debug rom | ||
| std::unordered_map<u16, size_t> actorOverlaySizes = { |
There was a problem hiding this comment.
To the best of my understanding, this should probably be static const. So static const std::unordered_map.... Assuming you aren't changing this at runtime any. Shouldn't impact functionality, more of a correctness factor for storage/type specification.
| return true; | ||
| } | ||
|
|
||
| void* StairsArena_Malloc(size_t size) { |
There was a problem hiding this comment.
This might just be lack of knowledge on my part, but I don't really understand what the difference is between Malloc, MallocR, MallocGeneral, MallocRGeneral, etc. are. If that's the kind of thing that can be briefly explained in a comment somewhere in here I think that would be good, if not I'll ask you at some point later because it's probably just some deep memory management knowledge that I'm missing.
| UIWidgets::PaddedEnhancementCheckbox("Simulate N64 Heap", "gSimulateHeap", true, false); | ||
| UIWidgets::Tooltip("This restores heap fragmentation glitches, such as causing the Royal Tomb to not load when reloading its graveyard room."); |
There was a problem hiding this comment.
I would add a note about requiring a scene reload for this to take effect.
| UIWidgets::EnhancementCheckbox("Stairs heap display", "gStairsDisplay"); | ||
| UIWidgets::Tooltip("Visualises the stair heap"); |
There was a problem hiding this comment.
IIRC there was some talk about this being used for more than just the stairs outside shadow temple, so genericizing this language would be good (or do the other places where this is relevant require a separate visualizer?)
| actorCtx->absoluteSpace = NULL; | ||
| } | ||
|
|
||
| if (CVarGetInteger("gStairs", 0)) { |
There was a problem hiding this comment.
I understand the reasoning here, however, if this is going to be useful for emulating more than the Royal Tomb Grave unloading glitch, this variable should have a better name.
|
|
||
| #include <time.h> | ||
| #include <assert.h> | ||
| // #include <stdlib.h> |
There was a problem hiding this comment.
If this is truly not needed, should probably delete it instead of commenting it out.
|
|
||
| // todo: figure out correct 'size' rather than 0x10000 (seems quite large?) | ||
| // if (CVarGetInteger("gStairs", 0)) { | ||
| // if (skyboxId == SKYBOX_NORMAL_SKY || skyboxId == SKYBOX_OVERCAST_SUNSET || skyboxId == SKYBOX_CUTSCENE_MAP) { | ||
| // Stairs_DecreaseSize(0x10000 * 4); | ||
| // } else { | ||
| // Stairs_DecreaseSize(0x10000 * 2); | ||
| // } | ||
| // } |
There was a problem hiding this comment.
Delete or explain why this is commented out instead of being deleted
| if (CVarGetInteger("gStairs", 0)) { | ||
| Stairs_DecreaseSize(8 * 150 * sizeof(Gfx)); | ||
| } | ||
|
|
There was a problem hiding this comment.
Would appreciate some explanation of these magic numbers.
|
i'm not fully following why since i can't remember where off the top of my head but i'm pretty sure we have some ship specific global vars somewhere, it probably makes sense to follow that pattern for |
|
Am i right in remembering that this is being put on hold to implement it in a better way? If so it should be closed or marked do not merge. |
|
Going to close this for now. This generally feels like a LOT just to accomodate one or 2 glitches, but if there's no other way, let's revisit this some other point when it's updated again. |
????????????????????????????
This is a system for simulating how the heap would be allocated on original hardware. Actors are then checked against this heap to see if it would be possible to load the actor on hardware, before it gets loaded. Allocated memory to this heap is all zeroed and not used for any actual data used by the game.
The most common use case for this is the 'stairs' glitch where the Royal Tomb is not loaded after switching between the two 'rooms' on the stairs in the graveyard scene, allowing you to enter the Tomb without Zelda's Lullaby.
Would love any feedback on the system and how I can improve the stairs.cpp in particular (or any better renames).
There are a few things which can be done differently to get a better alignment to console, including a better method for calculating the original heap size to start with. I'm not quite sure how much the skybox, for example, should be allocating. The other shortcoming is that actor struct sizes may differ in SoH than on console, which should only lead to minor differences itself.
I've also added some watches in the Value Viewer since it seemed appropriate and helped me debug a lot.
Build Artifacts