duplicate every instance of a display list (#720)

Co-authored-by: PeachyPeachSM64 <72323920+PeachyPeachSM64@users.noreply.github.com>
This commit is contained in:
Isaac0-dev 2025-03-28 09:17:53 +10:00 committed by GitHub
parent 14692e549b
commit d81512680b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 115 additions and 13 deletions

View file

@ -79,6 +79,7 @@ struct GraphNode* dynos_model_load_dl(u32* aId, enum ModelPool aModelPool, u8 aL
struct GraphNode* dynos_model_store_geo(u32* aId, enum ModelPool aModelPool, void* aAsset, struct GraphNode* aGraphNode);
struct GraphNode* dynos_model_get_geo(u32 aId);
void dynos_model_overwrite_slot(u32 srcSlot, u32 dstSlot);
Gfx *dynos_model_duplicate_displaylist(Gfx* gfx);
u32 dynos_model_get_id_from_asset(void* aAsset);
u32 dynos_model_get_id_from_graph_node(struct GraphNode* aGraphNode);
void dynos_model_clear_pool(enum ModelPool aModelPool);

View file

@ -972,6 +972,7 @@ struct GraphNode* DynOS_Model_GetGeo(u32 aId);
u32 DynOS_Model_GetIdFromAsset(void* asset);
u32 DynOS_Model_GetIdFromGraphNode(struct GraphNode* aNode);
void DynOS_Model_OverwriteSlot(u32 srcSlot, u32 dstSlot);
Gfx *DynOS_Model_Duplicate_DisplayList(Gfx* aGfx);
void DynOS_Model_ClearPool(enum ModelPool aModelPool);
//

View file

@ -270,6 +270,10 @@ void dynos_model_overwrite_slot(u32 srcSlot, u32 dstSlot) {
DynOS_Model_OverwriteSlot(srcSlot, dstSlot);
}
Gfx *dynos_model_duplicate_displaylist(Gfx* gfx) {
return DynOS_Model_Duplicate_DisplayList(gfx);
}
// -- other -- //
void dynos_mod_shutdown(void) {

View file

@ -7,6 +7,7 @@ extern "C" {
#include "engine/graph_node.h"
#include "model_ids.h"
#include "pc/lua/utils/smlua_model_utils.h"
#include "engine/display_list.h"
}
enum ModelLoadType {
@ -20,6 +21,7 @@ struct ModelInfo {
void* asset;
struct GraphNode* graphNode;
enum ModelPool modelPool;
std::vector<void*> *duplicates;
};
static struct DynamicPool* sModelPools[MODEL_POOL_MAX] = { 0 };
@ -28,6 +30,10 @@ static std::map<void*, struct ModelInfo> sAssetMap[MODEL_POOL_MAX];
static std::map<u32, std::vector<struct ModelInfo>> sIdMap;
static std::map<u32, u32> sOverwriteMap;
// An array of display list and/or vertex buffer duplicates for the current model processed in process_geo_layout
static std::vector<void*> *sCurrModelDuplicates = nullptr;
static std::vector<void*> sScheduledFree[MODEL_POOL_MAX];
static u32 find_empty_id(bool aIsPermanent) {
u32 id = aIsPermanent ? 9999 : VANILLA_ID_END + 1;
s8 dir = aIsPermanent ? -1 : 1;
@ -90,6 +96,8 @@ static struct GraphNode* DynOS_Model_LoadCommonInternal(u32* aId, enum ModelPool
return found.graphNode;
}
sCurrModelDuplicates = new std::vector<void*>();
// load geo
struct GraphNode* node = NULL;
switch (mlt) {
@ -103,7 +111,14 @@ static struct GraphNode* DynOS_Model_LoadCommonInternal(u32* aId, enum ModelPool
node = aGraphNode;
break;
}
if (!node) { return NULL; }
if (!node) {
for (auto &duplicate : *sCurrModelDuplicates) {
free(duplicate);
}
delete sCurrModelDuplicates;
sCurrModelDuplicates = nullptr;
return NULL;
}
// figure out id
if (!*aId) { *aId = find_empty_id(aModelPool == MODEL_POOL_PERMANENT); }
@ -113,8 +128,10 @@ static struct GraphNode* DynOS_Model_LoadCommonInternal(u32* aId, enum ModelPool
.id = *aId,
.asset = aAsset,
.graphNode = node,
.modelPool = aModelPool
.modelPool = aModelPool,
.duplicates = sCurrModelDuplicates,
};
sCurrModelDuplicates = nullptr;
// store in maps
sIdMap[*aId].push_back(info);
@ -224,12 +241,53 @@ void DynOS_Model_OverwriteSlot(u32 srcSlot, u32 dstSlot) {
sOverwriteMap[srcSlot] = dstSlot;
}
// Display lists need to be duplicated so they can be modified by mods
// also to prevent trying to write to read only memory for vanilla display lists
Gfx *DynOS_Model_Duplicate_DisplayList(Gfx* aGfx) {
if (!aGfx) { return nullptr; }
u32 size = gfx_get_size(aGfx) * sizeof(Gfx);
Gfx *gfxDuplicate = (Gfx *) malloc(size);
memcpy(gfxDuplicate, aGfx, size);
// Look for other display lists or vertices
for (u32 i = 0; i < size / sizeof(Gfx); i++) {
Gfx *cmd = gfxDuplicate + i;
u32 op = cmd->words.w0 >> 24;
// Duplicate referenced display lists
if (op == G_DL) {
cmd->words.w1 = (uintptr_t) DynOS_Model_Duplicate_DisplayList((Gfx *) cmd->words.w1);
if (C0(cmd, 16, 1) == G_DL_NOPUSH) { break; } // This is a branch (jump), end of display list
}
// Duplicate referenced vertices
if (op == G_VTX) {
u32 size = C0(cmd, 12, 8) * sizeof(Vtx);
Vtx *vtxDuplicate = (Vtx *) malloc(size);
memcpy(vtxDuplicate, (Vtx *) cmd->words.w1, size);
cmd->words.w1 = (uintptr_t) vtxDuplicate;
sCurrModelDuplicates->push_back(vtxDuplicate);
}
}
sCurrModelDuplicates->push_back(gfxDuplicate);
return gfxDuplicate;
}
void DynOS_Model_ClearPool(enum ModelPool aModelPool) {
if (!sModelPools[aModelPool]) { return; }
// schedule pool to be freed
dynamic_pool_free_pool(sModelPools[aModelPool]);
// free scheduled duplicates
for (auto &duplicate : sScheduledFree[aModelPool]) {
free(duplicate);
}
sScheduledFree[aModelPool].clear();
// clear overwrite
if (aModelPool == MODEL_POOL_LEVEL) {
sOverwriteMap.clear();
@ -257,6 +315,15 @@ void DynOS_Model_ClearPool(enum ModelPool aModelPool) {
info2++;
}
}
// schedule duplicates to be freed
if (info.duplicates) {
for (auto &duplicate : *info.duplicates) {
sScheduledFree[aModelPool].push_back(duplicate);
}
delete info.duplicates;
info.duplicates = nullptr;
}
}
assetMap.clear();

25
src/engine/display_list.c Normal file
View file

@ -0,0 +1,25 @@
#include "display_list.h"
// Get the size of a display list by iterating
// until gsSPEndDisplayList or gsSPBranchList is found
u32 gfx_get_size(const Gfx* gfx) {
for (u32 i = 0;;) {
u32 op = (gfx + i)->words.w0 >> 24;
u32 cmdSize = 1;
switch (op) {
case G_DL:
if (C0(gfx + i, 16, 1) == G_DL_NOPUSH) { return i + 1; } // For displaylists that end with branches (jumps)
break;
case G_ENDDL:
return i + 1;
case G_TEXRECT:
case G_TEXRECTFLIP:
cmdSize = 3;
break;
case G_FILLRECT:
cmdSize = 2;
break;
}
i += cmdSize;
}
}

View file

@ -0,0 +1,5 @@
#include <PR/gbi.h>
#define C0(cmd, pos, width) (((cmd)->words.w0 >> (pos)) & ((1U << width) - 1))
u32 gfx_get_size(const Gfx* gfx);

View file

@ -235,7 +235,7 @@ init_graph_node_translation_rotation(struct DynamicPool *pool,
vec3s_copy(graphNode->translation, translation);
vec3s_copy(graphNode->rotation, rotation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;
@ -257,7 +257,7 @@ struct GraphNodeTranslation *init_graph_node_translation(struct DynamicPool *poo
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;
@ -278,7 +278,7 @@ struct GraphNodeRotation *init_graph_node_rotation(struct DynamicPool *pool,
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ROTATION);
vec3s_copy(graphNode->rotation, rotation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;
@ -299,7 +299,7 @@ struct GraphNodeScale *init_graph_node_scale(struct DynamicPool *pool,
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->scale = scale;
graphNode->prevScale = scale;
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;
@ -369,7 +369,7 @@ struct GraphNodeAnimatedPart *init_graph_node_animated_part(struct DynamicPool *
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_ANIMATED_PART);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;
@ -390,7 +390,7 @@ struct GraphNodeBillboard *init_graph_node_billboard(struct DynamicPool *pool,
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_BILLBOARD);
vec3s_copy(graphNode->translation, translation);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;
@ -409,7 +409,7 @@ struct GraphNodeDisplayList *init_graph_node_display_list(struct DynamicPool *po
if (graphNode != NULL) {
init_scene_graph_node_links(&graphNode->node, GRAPH_NODE_TYPE_DISPLAY_LIST);
graphNode->node.flags = (drawingLayer << 8) | (graphNode->node.flags & 0xFF);
graphNode->displayList = displayList;
graphNode->displayList = dynos_model_duplicate_displaylist(displayList);
}
return graphNode;

View file

@ -3,6 +3,7 @@
#include "game/rendering_graph_node.h"
#include "game/skybox.h"
#include "geo_commands.h"
#include "engine/display_list.h"
void set_override_fov(f32 fov) {
gOverrideFOV = fov;
@ -109,8 +110,6 @@ void set_skybox_color(u8 index, u8 value) {
///
#define C0(pos, width) ((cmd->words.w0 >> (pos)) & ((1U << width) - 1))
// Assumes the current microcode is Fast3DEX2 Extended (default for pc port)
void gfx_parse(Gfx* cmd, LuaFunction func) {
if (!cmd) { return; }
@ -121,7 +120,7 @@ void gfx_parse(Gfx* cmd, LuaFunction func) {
u32 op = cmd->words.w0 >> 24;
switch (op) {
case G_DL:
if (C0(16, 1) == 0) {
if (C0(cmd, 16, 1) == G_DL_PUSH) {
gfx_parse((Gfx *) cmd->words.w1, func);
} else {
cmd = (Gfx *) cmd->words.w1;
@ -160,7 +159,7 @@ Vtx *gfx_get_vtx(Gfx* cmd, u16 offset) {
if (op != G_VTX) { return NULL; }
if (cmd->words.w1 == 0) { return NULL; }
u16 numVertices = C0(12, 8);
u16 numVertices = C0(cmd, 12, 8);
if (offset >= numVertices) { return NULL; }
return &((Vtx *) cmd->words.w1)[offset];