virtual memory and actor spawner

This commit is contained in:
madeline
2026-04-28 02:25:18 -07:00
parent 5f33489465
commit c71272af05
23 changed files with 649 additions and 53 deletions
+2
View File
@@ -1453,6 +1453,7 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiProcessOverlay.cpp
src/dusk/imgui/ImGuiCameraOverlay.cpp
src/dusk/imgui/ImGuiHeapOverlay.cpp
src/dusk/imgui/ImGuiActorSpawner.cpp
src/dusk/imgui/ImGuiDebugPad.cpp
src/dusk/imgui/ImGuiControllerOverlay.cpp
src/dusk/imgui/ImGuiStubLog.cpp
@@ -1466,6 +1467,7 @@ set(DUSK_FILES
src/dusk/iso_validate.cpp
src/dusk/livesplit.cpp
src/dusk/offset_ptr.cpp
src/dusk/vmem.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
src/dusk/OSMutex.cpp
-10
View File
@@ -1,10 +0,0 @@
#ifndef DUSK_MEMORY_H
#define DUSK_MEMORY_H
#if TARGET_PC
#define HEAP_SIZE(original, dusk) (dusk)
#else
#define HEAP_SIZE(original, dusk) (original)
#endif
#endif
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include <stddef.h>
#ifndef __cplusplus
#include <stdbool.h>
#endif
#ifdef __cplusplus
namespace dusk {
#endif
// Reserve a contiguous virtual address range without committing physical pages
void* vmem_reserve(size_t size);
// Commit physical backing for pages in a previously reserved range, ptr and size should be page-aligned
bool vmem_commit(void* ptr, size_t size);
// Decommit physical pages in a reserved range, releasing RAM without releasing address space
void vmem_decommit(void* ptr, size_t size);
// Release an entire virtual reservation obtained from vmem_reserve
void vmem_release(void* ptr, size_t size);
// Returns the OS page size
size_t vmem_page_size();
// Shared vmem arena
// All JKR heap vmem reservations are sub-allocated from a single large reservation,
// keeping the total entry count at 1 regardless of how many heaps exist
// Must be called once before any JKR heap is created
void vmem_arena_init();
// Allocate a slot of size bytes (page-aligned) from the arena
void* vmem_arena_alloc(size_t size);
// Return a slot to the arena and decommit its physical pages
void vmem_arena_free(void* ptr, size_t size);
#ifdef __cplusplus
} // namespace dusk
// Total virtual address space reserved for the shared JKR heap arena
inline constexpr size_t JKR_VMEM_ARENA_SIZE = 128ULL * 1024 * 1024 * 1024; // 128 GB
// Virtual address space reserved per JKR heap (one slot in the shared arena)
inline constexpr size_t JKR_HEAP_VIRTUAL_RESERVE = 64ULL * 1024 * 1024; // 64 MB
// Minimum growth increment when a JKR heap expands into reserved but uncommitted pages
inline constexpr size_t JKR_HEAP_GROW_CHUNK = 16ULL * 1024 * 1024; // 16 MB
// Maximum number of free slots the arena can track (= total slots in the arena)
inline constexpr size_t JKR_VMEM_MAX_FREE_SLOTS = JKR_VMEM_ARENA_SIZE / JKR_HEAP_VIRTUAL_RESERVE;
#endif
@@ -127,6 +127,13 @@ public:
[[nodiscard]] const CMemBlock* getFreeHead() const { return mHeadFreeList; }
[[nodiscard]] CMemBlock* getUsedHead() { return mHeadUsedList; }
[[nodiscard]] const CMemBlock* getUsedHead() const { return mHeadUsedList; }
void* mVmemBase; // base of VM reservation
size_t mVmemCapacity; // total reserved bytes
size_t mVmemCommitted; // page-aligned committed bytes so far
// Commit more pages and splice them into the free list
bool growHeap(u32 needed);
#endif
};
@@ -61,6 +61,15 @@ public:
static JKRSolidHeap* create(u32, JKRHeap*, bool);
static void* getState_(TState* state) { return getState_buf_(state); }
#if TARGET_PC
void* mVmemBase; // base of VM reservation
size_t mVmemCapacity; // total reserved bytes
size_t mVmemCommitted; // page-aligned committed bytes so far
// Commit more pages and extend the free region
bool growHeap(u32 needed);
#endif
};
inline JKRSolidHeap* JKRCreateSolidHeap(u32 param_0, JKRHeap* heap, bool param_2) {
+121 -6
View File
@@ -10,6 +10,11 @@
#include "JSystem/JUtility/JUTConsole.h"
#include "JSystem/JUtility/JUTException.h"
#include <cstdlib>
#if TARGET_PC
#include "dusk/vmem.h"
#include <algorithm>
#include "dusk/logging.h"
#endif
JKRExpHeap* JKRExpHeap::createRoot(int maxHeaps, bool errorFlag) {
JKRExpHeap* heap = NULL;
@@ -71,21 +76,49 @@ JKRExpHeap* JKRExpHeap::create(u32 size, JKRHeap* parent, bool errorFlag) {
u32 alignedSize = ALIGN_PREV(size, 0x10);
if (alignedSize < expHeapSize + blockSize)
if (alignedSize < expHeapSize + blockSize) {
return NULL;
}
u8* memory = (u8*)JKRAllocFromHeap(parent, alignedSize, 0x10);
u8* dataPtr = (memory + expHeapSize);
#if TARGET_PC
u8* vmemBase = (u8*)dusk::vmem_arena_alloc(JKR_HEAP_VIRTUAL_RESERVE);
if (!vmemBase) {
return NULL;
}
const size_t pageSize = dusk::vmem_page_size();
size_t commitSize = ALIGN_NEXT((size_t)alignedSize, pageSize);
if (!dusk::vmem_commit(vmemBase, commitSize)) {
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
return NULL;
}
u8* memory = vmemBase;
u8* dataPtr = memory + expHeapSize;
newHeap = JKR_NEW_ARGS(memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);
if (newHeap == NULL) {
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
return NULL;
}
newHeap->mVmemBase = vmemBase;
newHeap->mVmemCapacity = JKR_HEAP_VIRTUAL_RESERVE;
newHeap->mVmemCommitted = commitSize;
#else
u8* memory = (u8*)JKRAllocFromHeap(parent, alignedSize, 0x10);
u8* dataPtr = memory + expHeapSize;
if (!memory) {
return NULL;
}
newHeap = JKR_NEW_ARGS (memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);
newHeap = JKR_NEW_ARGS(memory) JKRExpHeap(dataPtr, alignedSize - expHeapSize, parent, errorFlag);
if (newHeap == NULL) {
JKRFree(memory);
return NULL;
}
#endif
#if DEBUG
if (newHeap) {
u8* local_30 = dataPtr + sizeof(CMemBlock);
@@ -102,9 +135,16 @@ JKRExpHeap* JKRExpHeap::create(u32 size, JKRHeap* parent, bool errorFlag) {
JKRExpHeap* JKRExpHeap::create(void* ptr, u32 size, JKRHeap* parent, bool errorFlag) {
JKRHeap* parent2;
if (parent == NULL) {
#if TARGET_PC
// VM-backed heaps live outside the root heap's address range, so find() fails
// findAllHeap() searches the full tree
parent2 = getRootHeap()->findAllHeap(ptr);
#else
parent2 = getRootHeap()->find(ptr);
if (!parent2)
#endif
if (!parent2) {
return NULL;
}
} else {
parent2 = parent;
}
@@ -136,6 +176,15 @@ JKRExpHeap* JKRExpHeap::create(void* ptr, u32 size, JKRHeap* parent, bool errorF
}
void JKRExpHeap::do_destroy() {
#if TARGET_PC
if (mVmemBase) {
void* vmemBase = mVmemBase;
size_t vmemCapacity = mVmemCapacity;
this->~JKRExpHeap();
dusk::vmem_arena_free(vmemBase, vmemCapacity);
return;
}
#endif
if (!field_0x6e) {
JKRHeap* heap = getParent();
if (heap) {
@@ -163,6 +212,11 @@ JKRExpHeap::JKRExpHeap(void* data, u32 size, JKRHeap* parent, bool errorFlag)
mHeadFreeList->initiate(NULL, NULL, size - sizeof(CMemBlock), 0, 0);
mHeadUsedList = NULL;
mTailUsedList = NULL;
#if TARGET_PC
mVmemBase = nullptr;
mVmemCapacity = 0;
mVmemCommitted = 0;
#endif
}
JKRExpHeap::~JKRExpHeap() {
@@ -214,6 +268,24 @@ void* JKRExpHeap::do_alloc(u32 size, int alignment) {
#endif
#if TARGET_PC
if (!ptr && mVmemBase) {
// Heap is full, commit the next chunk of reserved VM and retry
if (growHeap(size)) {
if (alignment >= 0) {
if (alignment <= 4) {
ptr = allocFromHead(size);
} else {
ptr = allocFromHead(size, alignment);
}
} else {
if (-alignment <= 4) {
ptr = allocFromTail(size);
} else {
ptr = allocFromTail(size, -alignment);
}
}
}
}
if (!ptr) {
// Allocation failed.
OSReport_Error(
@@ -491,6 +563,49 @@ static void dummy() {
OS_REPORT("newSize > 0");
}
#if TARGET_PC
bool JKRExpHeap::growHeap(u32 needed) {
// Determine how much to commit
// Always grow by at least JKR_HEAP_GROW_CHUNK
const size_t pageSize = dusk::vmem_page_size();
size_t wantBytes = (size_t)needed + sizeof(CMemBlock);
size_t growAmount = std::max(wantBytes, JKR_HEAP_GROW_CHUNK);
growAmount = ALIGN_NEXT(growAmount, pageSize);
size_t remaining = mVmemCapacity - mVmemCommitted;
if (growAmount > remaining) {
// Clamp to whatever reservation is left
growAmount = ALIGN_PREV(remaining, pageSize);
if (growAmount < wantBytes) {
return false;
}
}
void* commitBase = (u8*)mVmemBase + mVmemCommitted;
if (!dusk::vmem_commit(commitBase, growAmount)) {
return false;
}
// Splice the new committed region into the free list as a single block at mEnd
CMemBlock* newBlock = (CMemBlock*)mEnd;
newBlock->size = (u32)(growAmount - sizeof(CMemBlock));
newBlock->mFlags = 0;
mEnd = (u8*)mEnd + growAmount;
mSize += (u32)growAmount;
mVmemCommitted += growAmount;
recycleFreeBlock(newBlock);
DuskLog.debug("[JKRExpHeap] '{}' grew by {} MB (committed: {} MB / reserved: {} MB)\n",
getName(),
growAmount / (1024 * 1024),
mVmemCommitted / (1024 * 1024),
mVmemCapacity / (1024 * 1024));
return true;
}
#endif
void JKRExpHeap::do_freeAll() {
lock();
JKRHeap::callAllDisposer();
+115 -3
View File
@@ -1,4 +1,4 @@
#include "JSystem/JSystem.h" // IWYU pragma: keep
#include "JSystem/JSystem.h" // IWYU pragma: keep
#include "JSystem/JKernel/JKRSolidHeap.h"
#include "JSystem/JGadget/binary.h"
@@ -7,6 +7,11 @@
#include "global.h"
#include <stdint.h>
#include <cstdlib>
#if TARGET_PC
#include "dusk/vmem.h"
#include <algorithm>
#include "dusk/logging.h"
#endif
JKRSolidHeap* JKRSolidHeap::create(u32 size, JKRHeap* heap, bool useErrorHandler) {
if (!heap) {
@@ -19,18 +24,56 @@ JKRSolidHeap* JKRSolidHeap::create(u32 size, JKRHeap* heap, bool useErrorHandler
}
u32 alignedSize = ALIGN_PREV(size, 0x10);
if (alignedSize < solidHeapSize)
if (alignedSize < solidHeapSize) {
return NULL;
}
#if TARGET_PC
u8* vmemBase = (u8*)dusk::vmem_arena_alloc(JKR_HEAP_VIRTUAL_RESERVE);
if (!vmemBase) {
return NULL;
}
const size_t pageSize = dusk::vmem_page_size();
size_t commitSize = ALIGN_NEXT((size_t)alignedSize, pageSize);
if (!dusk::vmem_commit(vmemBase, commitSize)) {
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
return NULL;
}
u8* mem = vmemBase;
void* dataPtr = mem + solidHeapSize;
JKRSolidHeap* newHeap = JKR_NEW_ARGS(mem) JKRSolidHeap(dataPtr, alignedSize - solidHeapSize, heap, useErrorHandler);
if (newHeap == NULL) {
dusk::vmem_arena_free(vmemBase, JKR_HEAP_VIRTUAL_RESERVE);
return NULL;
}
newHeap->mVmemBase = vmemBase;
newHeap->mVmemCapacity = JKR_HEAP_VIRTUAL_RESERVE;
newHeap->mVmemCommitted = commitSize;
return newHeap;
#else
u8* mem = (u8*)JKRAllocFromHeap(heap, alignedSize, 0x10);
void* dataPtr = mem + solidHeapSize;
if (!mem)
if (!mem) {
return NULL;
}
return JKR_NEW_ARGS (mem) JKRSolidHeap(dataPtr, alignedSize - solidHeapSize, heap, useErrorHandler);
#endif
}
void JKRSolidHeap::do_destroy(void) {
#if TARGET_PC
if (mVmemBase) {
void* vmemBase = mVmemBase;
size_t vmemCapacity = mVmemCapacity;
this->~JKRSolidHeap();
dusk::vmem_arena_free(vmemBase, vmemCapacity);
return;
}
#endif
JKRHeap* parent = getParent();
if (parent) {
this->~JKRSolidHeap();
@@ -44,6 +87,11 @@ JKRSolidHeap::JKRSolidHeap(void* start, u32 size, JKRHeap* parent, bool useError
mSolidHead = (u8*)mStart;
mSolidTail = (u8*)mEnd;
field_0x78 = NULL;
#if TARGET_PC
mVmemBase = nullptr;
mVmemCapacity = 0;
mVmemCommitted = 0;
#endif
#if DEBUG
if (mDebugFill) {
JKRFillMemory(mStart, mSize, JKRValue_DEBUGFILL_NOTUSE);
@@ -59,6 +107,15 @@ s32 JKRSolidHeap::adjustSize(void) {
int r25 = 0;
JKRHeap* parent = getParent();
if (parent) {
#if TARGET_PC
if (mVmemBase) {
// VM-backed heap, can't resize in parent, but this is not a failure
// Return what the trimmed size would have been so the caller doesn't log an error
u32 thisSize = (uintptr_t)mStart - (uintptr_t)this;
u32 newSize = ALIGN_NEXT(mSolidHead - mStart, 0x20);
return (s32)(thisSize + newSize);
}
#endif
lock();
u32 thisSize = (uintptr_t)mStart - (uintptr_t)this;
u32 newSize = ALIGN_NEXT(mSolidHead - mStart, 0x20);
@@ -110,6 +167,11 @@ void* JKRSolidHeap::allocFromHead(u32 size, int alignment) {
void* ptr = NULL;
uintptr_t alignedStart = (alignment - 1 + (uintptr_t)mSolidHead) & ~(alignment - 1);
u32 totalSize = size + (alignedStart - (uintptr_t)mSolidHead);
#if TARGET_PC
if (totalSize > mFreeSize && mVmemBase) {
growHeap(totalSize);
}
#endif
if (totalSize <= mFreeSize) {
#if DEBUG
if (mCheckMemoryFilled) {
@@ -137,6 +199,15 @@ void* JKRSolidHeap::allocFromTail(u32 size, int alignment) {
void* ptr = NULL;
uintptr_t alignedStart = ALIGN_PREV((uintptr_t)mSolidTail - size, alignment);
u32 totalSize = (uintptr_t)mSolidTail - (uintptr_t)alignedStart;
#if TARGET_PC
if (totalSize > mFreeSize && mVmemBase) {
if (growHeap(totalSize)) {
// mSolidTail moved to new mEnd; recompute from the new tail position
alignedStart = ALIGN_PREV((uintptr_t)mSolidTail - size, alignment);
totalSize = (uintptr_t)mSolidTail - (uintptr_t)alignedStart;
}
}
#endif
if (totalSize <= mFreeSize) {
ptr = (void*)alignedStart;
mSolidTail -= totalSize;
@@ -158,6 +229,47 @@ void* JKRSolidHeap::allocFromTail(u32 size, int alignment) {
return ptr;
}
#if TARGET_PC
bool JKRSolidHeap::growHeap(u32 needed) {
// Growth is only safe when no tail allocations exist yet
if (mSolidTail != mEnd) {
return false;
}
const size_t pageSize = dusk::vmem_page_size();
size_t wantBytes = (size_t)needed;
size_t growAmount = std::max(wantBytes, JKR_HEAP_GROW_CHUNK);
growAmount = ALIGN_NEXT(growAmount, pageSize);
size_t remaining = mVmemCapacity - mVmemCommitted;
if (growAmount > remaining) {
growAmount = ALIGN_PREV(remaining, pageSize);
if (growAmount < wantBytes) {
return false;
}
}
void* commitBase = (u8*)mVmemBase + mVmemCommitted;
if (!dusk::vmem_commit(commitBase, growAmount)) {
return false;
}
// Extend the heap end and the tail pointer
mEnd = (u8*)mEnd + growAmount;
mSolidTail = mEnd;
mFreeSize += (u32)growAmount;
mSize += (u32)growAmount;
mVmemCommitted += growAmount;
DuskLog.debug("[JKRSolidHeap] '{}' grew by {} MB (committed: {} MB / reserved: {} MB)\n",
getName(),
growAmount / (1024 * 1024),
mVmemCommitted / (1024 * 1024),
mVmemCapacity / (1024 * 1024));
return true;
}
#endif
void JKRSolidHeap::do_free(void* ptr) {
JUTWarningConsole_f("free: cannot free memory block (%08x)\n", ptr);
}
+1 -3
View File
@@ -14,8 +14,6 @@
#include "JSystem/J2DGraph/J2DAnmLoader.h"
#include <cstring>
#include "dusk/memory.h"
class daCoach2D_HIO_c : public mDoHIO_entry_c {
public:
struct Param {
@@ -155,7 +153,7 @@ int daCoach2D_c::createHeap() {
int daCoach2D_c::create() {
int phase_state = dComIfG_resLoad(this, l_arcName);
if (phase_state == cPhs_COMPLEATE_e) {
if (!fopAcM_entrySolidHeap(this, daCoach2D_createHeap, HEAP_SIZE(0x5050, 0x6000))) {
if (!fopAcM_entrySolidHeap(this, daCoach2D_createHeap, 0x5050)) {
return cPhs_ERROR_e;
}
+7
View File
@@ -78,7 +78,14 @@ void dEyeHL_mng_c::remove(dEyeHL_c* i_obj) {
next = m_obj;
}
#if TARGET_PC
// Skip the write if the heap owning m_timg was already destroyed
if (JKRHeap::findFromRoot(i_obj->m_timg) != nullptr) {
i_obj->m_timg->LODBias = i_obj->m_lodBias;
}
#else
i_obj->m_timg->LODBias = i_obj->m_lodBias;
#endif
i_obj->m_timg = NULL;
i_obj->m_pre = NULL;
i_obj->m_next = NULL;
+1 -2
View File
@@ -6,7 +6,6 @@
#include "d/dolzel.h" // IWYU pragma: keep
#include "d/d_k_wmark.h"
#include "dusk/memory.h"
#include "JSystem/J3DGraphBase/J3DMaterial.h"
#include "SSystem/SComponent/c_math.h"
#include "d/actor/d_a_player.h"
@@ -34,7 +33,7 @@ int dkWmark_c::create() {
mColorType = this->parameters;
}
mpHeap = mDoExt_createSolidHeapFromGameToCurrent(HEAP_SIZE(0x880, 0x1100), 0x20);
mpHeap = mDoExt_createSolidHeapFromGameToCurrent(0x880, 0x20);
if (mpHeap != NULL) {
JKRHEAP_NAME(mpHeap, "dkWmark_c::mpHeap");
J3DModelData* modelData = (J3DModelData*)dComIfG_getObjectRes("Alink", 0x23);
+1 -2
View File
@@ -1,7 +1,6 @@
#include "d/dolzel.h" // IWYU pragma: keep
#include "d/d_kankyo.h"
#include "dusk/memory.h"
#ifdef __REVOLUTION_SDK__
#include <revolution.h>
#else
@@ -1186,7 +1185,7 @@ static void undwater_init() {
J3DModelData* modelData2 = (J3DModelData*)dComIfG_getObjectRes("Always", 0x1D);
JUT_ASSERT(1867, modelData2 != NULL);
g_env_light.undwater_ef_heap = mDoExt_createSolidHeapFromGameToCurrent(HEAP_SIZE(0x600, 0xC00), 0x20);
g_env_light.undwater_ef_heap = mDoExt_createSolidHeapFromGameToCurrent(0x600, 0x20);
JKRHEAP_NAME(g_env_light.undwater_ef_heap, "g_env_light.undwater_ef_heap");
if (g_env_light.undwater_ef_heap != NULL) {
+1 -2
View File
@@ -21,7 +21,6 @@
#include "d/d_msg_object.h"
#include "d/d_msg_scrn_explain.h"
#include "d/d_stage.h"
#include "dusk/memory.h"
#include "f_op/f_op_msg_mng.h"
static dMf_HIO_c g_fmHIO;
@@ -190,7 +189,7 @@ dMenu_Fmap_c::dMenu_Fmap_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i
field_0x148[i] = 0.0f;
}
mpTalkHeap = JKRCreateExpHeap(HEAP_SIZE(0x32000, 0x40000), mpHeap, false);
mpTalkHeap = JKRCreateExpHeap(0x32000, mpHeap, false);
JUT_ASSERT(359, mpTalkHeap != NULL);
JKRHEAP_NAME(mpTalkHeap, "dMenu_Fmap_c::mpTalkHeap");
field_0x200 = 0;
+3 -7
View File
@@ -24,16 +24,12 @@
#include "d/actor/d_a_horse.h"
#include <cstring>
#include "dusk/memory.h"
#include "dusk/memory.h"
int dMeter2_c::_create() {
stage_stag_info_class* stag_info = dComIfGp_getStageStagInfo();
if (dStage_stagInfo_GetUpButton(stag_info) == 1) {
mpHeap = fopMsgM_createExpHeap(HEAP_SIZE(0x5A400, 0xA0000), NULL);
mpHeap = fopMsgM_createExpHeap(0x5A400, NULL);
} else {
mpHeap = fopMsgM_createExpHeap(HEAP_SIZE(0x60800, 0xC1000), NULL);
mpHeap = fopMsgM_createExpHeap(0x60800, NULL);
}
JKRHEAP_NAME(mpHeap, "dMeter2_c");
@@ -236,7 +232,7 @@ int dMeter2_c::_create() {
dMeter2Info_setMeterMapClass(mpMap);
mpHeap->getTotalFreeSize();
mpSubHeap = fopMsgM_createExpHeap(HEAP_SIZE(0x5000, 0x6500), mpHeap);
mpSubHeap = fopMsgM_createExpHeap(0x5000, mpHeap);
JKRHEAP_NAME(mpSubHeap, "dMeter2_c mpSubHeap");
field_0x108 = NULL;
mpSubContents = NULL;
+18 -6
View File
@@ -36,15 +36,27 @@ dRes_info_c::dRes_info_c() {
dRes_info_c::~dRes_info_c() {
if (mDMCommand != NULL) {
mDMCommand->destroy();
#if TARGET_PC
if (JKRHeap::findFromRoot(mDMCommand) != nullptr) {
#endif
mDMCommand->destroy();
#if TARGET_PC
}
#endif
mDMCommand = NULL;
} else if (mArchive != NULL) {
deleteArchiveRes();
if (mDataHeap != NULL) {
mDoExt_destroySolidHeap(mDataHeap);
mDataHeap = NULL;
mArchive->unmount();
#if TARGET_PC
if (JKRHeap::findFromRoot(mArchive) != nullptr) {
#endif
deleteArchiveRes();
if (mDataHeap != NULL) {
mDoExt_destroySolidHeap(mDataHeap);
mDataHeap = NULL;
mArchive->unmount();
}
#if TARGET_PC
}
#endif
mRes = NULL;
mArchive = NULL;
}
+1 -2
View File
@@ -10,7 +10,6 @@
#include "d/d_meter2_info.h"
#include "d/d_s_name.h"
#include "dusk/imgui/ImGuiConsole.hpp"
#include "dusk/memory.h"
#include "dusk/settings.h"
#include "f_op/f_op_overlap_mng.h"
#include "f_op/f_op_scene_mng.h"
@@ -77,7 +76,7 @@ static s32 resLoad(request_of_phase_process_class* i_phase, char* i_resName) {
s32 dScnName_c::create() {
int phase_state = resLoad(&phase, "fileSel");
if (phase_state == cPhs_COMPLEATE_e) {
mHeap = JKRCreateExpHeap(HEAP_SIZE(0x180000, 0x1C0000), mDoExt_getGameHeap(), false);
mHeap = JKRCreateExpHeap(0x180000, mDoExt_getGameHeap(), false);
JUT_ASSERT(289, mHeap != NULL);
JKRHEAP_NAME(mHeap, "File select");
+2 -6
View File
@@ -39,10 +39,6 @@
#include "JSystem/JKernel/JKRAram.h"
#include "JSystem/JKernel/JKRAramArchive.h"
#if TARGET_PC
#include "dusk/memory.h"
#endif
#if DEBUG
#include "d/d_s_menu.h"
#include "d/d_debug_pad.h"
@@ -1424,7 +1420,7 @@ static int phase_4(dScnPly_c* i_this) {
dComIfGd_setViewport(NULL);
dComIfGd_setView(NULL);
JKRExpHeap* heap = fopMsgM_createExpHeap(HEAP_SIZE(0xBB800, 0x177000), NULL);
JKRExpHeap* heap = fopMsgM_createExpHeap(0xBB800, NULL);
#if TARGET_PC
heap->setName("Scene2DHeap");
#endif
@@ -1432,7 +1428,7 @@ static int phase_4(dScnPly_c* i_this) {
JUT_ASSERT(2704, heap != NULL);
dComIfGp_setExpHeap2D(heap);
JKRExpHeap* heap2 = fopMsgM_createExpHeap(HEAP_SIZE(0xA800, 0x15000), NULL);
JKRExpHeap* heap2 = fopMsgM_createExpHeap(0xA800, NULL);
#if TARGET_PC
heap2->setName("SceneMsgHeap");
#endif
+142
View File
@@ -0,0 +1,142 @@
#include "imgui.h"
#include "ImGuiMenuTools.hpp"
#include "d/actor/d_a_alink.h"
#include "d/d_com_inf_game.h"
#include "f_op/f_op_actor_mng.h"
#include "SSystem/SComponent/c_sxyz.h"
#include "SSystem/SComponent/c_xyz.h"
namespace dusk {
namespace {
struct ActorSpawnerState {
int actorId = 0;
int params = -1;
int argument = -1;
int angleX = 0;
int angleY = 0;
int angleZ = 0;
float scaleX = 1.0f;
float scaleY = 1.0f;
float scaleZ = 1.0f;
bool usePlayerRoom = true;
int manualRoom = 0;
int spawnCount = 1;
bool hasResult = false;
unsigned int lastResult = 0;
int lastAttempted = 0;
};
ActorSpawnerState s_state;
} // namespace
void ImGuiMenuTools::ShowActorSpawner() {
if (!m_showActorSpawner) {
return;
}
if (!ImGui::Begin("Actor Spawner", &m_showActorSpawner)) {
ImGui::End();
return;
}
daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0);
ImGui::SeparatorText("Actor");
ImGui::InputInt("Actor ID", &s_state.actorId);
ImGui::InputInt("Params (hex)", &s_state.params, 0, 0, ImGuiInputTextFlags_CharsHexadecimal);
ImGui::InputInt("Argument", &s_state.argument);
s_state.argument = (s_state.argument < -128) ? -128 : (s_state.argument > 127) ? 127 : s_state.argument;
ImGui::SeparatorText("Angle");
ImGui::InputInt("Angle X", &s_state.angleX);
ImGui::InputInt("Angle Y", &s_state.angleY);
ImGui::InputInt("Angle Z", &s_state.angleZ);
ImGui::SeparatorText("Scale");
ImGui::InputFloat("Scale X", &s_state.scaleX, 0.1f, 1.0f);
ImGui::InputFloat("Scale Y", &s_state.scaleY, 0.1f, 1.0f);
ImGui::InputFloat("Scale Z", &s_state.scaleZ, 0.1f, 1.0f);
ImGui::SeparatorText("Spawn");
ImGui::InputInt("Count", &s_state.spawnCount);
if (s_state.spawnCount < 1) {
s_state.spawnCount = 1;
}
ImGui::SeparatorText("Position");
ImGui::Checkbox("Use player room", &s_state.usePlayerRoom);
if (!s_state.usePlayerRoom) {
ImGui::InputInt("Room No", &s_state.manualRoom);
}
if (player != nullptr) {
ImGui::Text("Spawn pos: %.2f, %.2f, %.2f",
player->current.pos.x, player->current.pos.y, player->current.pos.z);
} else {
ImGui::TextDisabled("Player not available");
}
ImGui::Separator();
bool canSpawn = player != nullptr;
if (!canSpawn) {
ImGui::BeginDisabled();
}
if (ImGui::Button("Spawn", ImVec2(-1, 0))) {
cXyz pos = player->current.pos;
csXyz angle;
angle.set((s16)s_state.angleX, (s16)s_state.angleY, (s16)s_state.angleZ);
cXyz scale(s_state.scaleX, s_state.scaleY, s_state.scaleZ);
int roomNo = s_state.usePlayerRoom ? player->current.roomNo : s_state.manualRoom;
layer_class* savedLayer = fpcLy_CurrentLayer();
base_process_class* playScene = fpcM_SearchByName(fpcNm_PLAY_SCENE_e);
if (playScene != nullptr) {
fpcLy_SetCurrentLayer(&((process_node_class*)playScene)->layer);
}
s_state.lastResult = 0;
s_state.lastAttempted = s_state.spawnCount;
for (int i = 0; i < s_state.spawnCount; ++i) {
unsigned int result = fopAcM_create(
(s16)s_state.actorId,
(u32)s_state.params,
&pos,
roomNo,
&angle,
&scale,
(s8)s_state.argument
);
if (result != 0) {
s_state.lastResult = result;
}
}
s_state.hasResult = true;
fpcLy_SetCurrentLayer(savedLayer);
}
if (!canSpawn) {
ImGui::EndDisabled();
}
if (s_state.hasResult) {
if (s_state.lastResult != 0) {
if (s_state.lastAttempted == 1) {
ImGui::Text("Spawned: proc ID %u", s_state.lastResult);
} else {
ImGui::Text("Spawned %d (last proc ID %u)", s_state.lastAttempted, s_state.lastResult);
}
} else {
ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "Spawn failed (returned 0)");
}
}
ImGui::End();
}
} // namespace dusk
+1
View File
@@ -430,6 +430,7 @@ namespace dusk {
m_menuTools.ShowAudioDebug();
m_menuTools.ShowSaveEditor();
m_menuTools.ShowStateShare();
m_menuTools.ShowActorSpawner();
}
m_menuTools.ShowAchievements();
DuskDebugPad(); // temporary, remove later
+28 -3
View File
@@ -6,6 +6,7 @@
#include "JSystem/JFramework/JFWSystem.h"
#include "JSystem/JKernel/JKRExpHeap.h"
#include "JSystem/JKernel/JKRHeap.h"
#include "JSystem/JKernel/JKRSolidHeap.h"
#include "absl/container/flat_hash_map.h"
#include "imgui.h"
@@ -178,11 +179,11 @@ namespace dusk {
}
void ShowHeapDetailed(JKRHeap* heap, OpenHeapData& data, bool& open) {
const char* heapName = data.Safe ? heap->getName() : "INVALID";
char title[128];
const char* name = data.Safe ? heap->getName() : "INVALID";
snprintf(title, sizeof(title), "Heap %s##%p", heap->getName(), static_cast<const void*>(heap));
snprintf(title, sizeof(title), "Heap %s##%p", heapName, static_cast<const void*>(heap));
if (!ImGui::Begin(name, &open)) {
if (!ImGui::Begin(title, &open)) {
ImGui::End();
return;
}
@@ -200,6 +201,30 @@ namespace dusk {
const auto freeSize = BytesToString(heap->getFreeSize());
ImGui::Text("Size: %08X (%s), free: %08X (%s)", heap->getSize(), size.c_str(), heap->getFreeSize(), freeSize.c_str());
{
void* vmemBase = nullptr;
size_t vmemCapacity = 0;
size_t vmemCommitted = 0;
if (heap->getHeapType() == 'EXPH') {
auto* h = static_cast<JKRExpHeap*>(heap);
vmemBase = h->mVmemBase; vmemCapacity = h->mVmemCapacity; vmemCommitted = h->mVmemCommitted;
} else if (heap->getHeapType() == 'SLID') {
auto* h = static_cast<JKRSolidHeap*>(heap);
vmemBase = h->mVmemBase; vmemCapacity = h->mVmemCapacity; vmemCommitted = h->mVmemCommitted;
}
if (vmemBase) {
ImGui::SeparatorText("Virtual Memory");
ImGui::Text("Base: %p", vmemBase);
const auto committedStr = BytesToString(vmemCommitted);
const auto capacityStr = BytesToString(vmemCapacity);
ImGui::Text("Committed: %s / %s reserved", committedStr.c_str(), capacityStr.c_str());
float pct = vmemCapacity > 0 ? (float)vmemCommitted / (float)vmemCapacity : 0.0f;
char overlayBuf[32];
snprintf(overlayBuf, sizeof(overlayBuf), "%.1f%%", pct * 100.0f);
ImGui::ProgressBar(pct, ImVec2(-1.0f, 0.0f), overlayBuf);
}
}
if (ImGui::Button("Check")) {
data.HeapCheckFailed = !heap->check();
data.HeapCheckRan = true;
+1
View File
@@ -119,6 +119,7 @@ namespace dusk {
ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug);
ImGui::MenuItem("Bloom", nullptr, &m_showBloomWindow);
ImGui::MenuItem("Stub Log", nullptr, &m_showStubLog);
ImGui::MenuItem("Actor Spawner", nullptr, &m_showActorSpawner);
if (!dusk::IsGameLaunched) {
ImGui::EndDisabled();
+3
View File
@@ -29,6 +29,7 @@ namespace dusk {
void ShowStateShare();
void ShowAchievements();
void notifyAchievement(std::string name);
void ShowActorSpawner();
private:
bool m_showDebugOverlay = false;
@@ -71,6 +72,8 @@ namespace dusk {
bool m_showAchievements = false;
ImGuiAchievements m_achievementsWindow;
bool m_showActorSpawner = false;
};
}
+122
View File
@@ -0,0 +1,122 @@
#include "dusk/vmem.h"
#if _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <sys/mman.h>
#include <unistd.h>
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#endif
#include <atomic>
#include <mutex>
#include <cstdint>
namespace dusk {
size_t vmem_page_size() {
#if _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
return (size_t)sysconf(_SC_PAGESIZE);
#endif
}
void* vmem_reserve(size_t size) {
#if _WIN32
return VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_NOACCESS);
#else
void* p = mmap(nullptr, size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
return (p == MAP_FAILED) ? nullptr : p;
#endif
}
bool vmem_commit(void* ptr, size_t size) {
#if _WIN32
return VirtualAlloc(ptr, size, MEM_COMMIT, PAGE_READWRITE) != nullptr;
#else
return mprotect(ptr, size, PROT_READ | PROT_WRITE) == 0;
#endif
}
void vmem_decommit(void* ptr, size_t size) {
#if _WIN32
VirtualFree(ptr, size, MEM_DECOMMIT);
#else
mprotect(ptr, size, PROT_NONE);
madvise(ptr, size, MADV_DONTNEED);
#endif
}
void vmem_release(void* ptr, size_t size) {
#if _WIN32
(void)size;
VirtualFree(ptr, 0, MEM_RELEASE);
#else
munmap(ptr, size);
#endif
}
namespace {
static void* s_arenaBase = nullptr;
static size_t s_arenaTotal = 0;
static std::atomic<size_t> s_arenaBump{0};
struct FreeSlot { void* ptr; size_t size; };
static FreeSlot s_free[JKR_VMEM_MAX_FREE_SLOTS];
static size_t s_freeCount = 0;
static std::mutex s_freeMutex;
} // namespace
void vmem_arena_init() {
s_arenaBase = vmem_reserve(JKR_VMEM_ARENA_SIZE);
s_arenaTotal = JKR_VMEM_ARENA_SIZE;
}
void* vmem_arena_alloc(size_t size) {
const size_t pageSize = vmem_page_size();
size = (size + pageSize - 1) & ~(pageSize - 1);
{
std::lock_guard<std::mutex> lock(s_freeMutex);
for (size_t i = 0; i < s_freeCount; ++i) {
if (s_free[i].size >= size) {
void* ptr = s_free[i].ptr;
s_free[i] = s_free[--s_freeCount];
return ptr;
}
}
}
size_t offset = s_arenaBump.fetch_add(size);
if (offset + size > s_arenaTotal) {
s_arenaBump.fetch_sub(size);
return nullptr;
}
return static_cast<uint8_t*>(s_arenaBase) + offset;
}
void vmem_arena_free(void* ptr, size_t size) {
if (!ptr) {
return;
}
const size_t pageSize = vmem_page_size();
size = (size + pageSize - 1) & ~(pageSize - 1);
vmem_decommit(ptr, size);
std::lock_guard<std::mutex> lock(s_freeMutex);
if (s_freeCount < JKR_VMEM_MAX_FREE_SLOTS) {
s_free[s_freeCount++] = {ptr, size};
}
}
} // namespace dusk
+9 -1
View File
@@ -71,6 +71,7 @@
#include "dusk/config.hpp"
#include "dusk/imgui/ImGuiConsole.hpp"
#include "dusk/settings.h"
#include "dusk/vmem.h"
#include "dusk/version.hpp"
#include "dusk/discord_presence.hpp"
#include "tracy/Tracy.hpp"
@@ -551,12 +552,19 @@ int game_main(int argc, char* argv[]) {
config.desiredBackend = ResolveDesiredBackend(parsed_arg_options);
config.logCallback = &aurora_log_callback;
config.logLevel = startupLogLevel;
config.mem1Size = 256 * 1024 * 1024;
// Child heaps use independent vmem reservations on PC
config.mem1Size = DUSK_IF_ELSE(16, 256) * 1024 * 1024;
config.mem2Size = 24 * 1024 * 1024;
config.allowJoystickBackgroundEvents = true;
config.imGuiInitCallback = &aurora_imgui_init_callback;
config.allowTextureReplacements = true;
config.allowTextureDumps = false;
#if TARGET_PC
dusk::vmem_arena_init();
#endif
auroraInfo = aurora_initialize(argc, argv, &config);
}