diff --git a/src/graphics/profile_task.c b/src/graphics/profile_task.c index 434c469..97a4409 100644 --- a/src/graphics/profile_task.c +++ b/src/graphics/profile_task.c @@ -110,4 +110,28 @@ void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task) { osSetEventMesg(OS_EVENT_DP, &scheduler->interruptQ, (OSMesg)RDP_DONE_MSG); osViSetEvent(&scheduler->interruptQ, (OSMesg)VIDEO_MSG, 1); osSetThreadPri(currentThread, GAME_PRIORITY); +} + +void profileMapAddress(void* original, void* ramAddress) { +#ifdef PORTAL64_WITH_DEBUGGER + char message[64]; + int messageLen = sprintf( + message, + "addr 0x%08x -> 0x%08x", + (int)original, + (int)ramAddress + ); + gdbSendMessage(GDBDataTypeText, message, messageLen); +#endif +} + +void profileClearAddressMap() { +#ifdef PORTAL64_WITH_DEBUGGER + char message[64]; + int messageLen = sprintf( + message, + "addr clearall" + ); + gdbSendMessage(GDBDataTypeText, message, messageLen); +#endif } \ No newline at end of file diff --git a/src/graphics/profile_task.h b/src/graphics/profile_task.h index d50383d..ca92f2a 100644 --- a/src/graphics/profile_task.h +++ b/src/graphics/profile_task.h @@ -5,5 +5,7 @@ #include void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task); +void profileMapAddress(void* original, void* ramAddress); +void profileClearAddressMap(); #endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index c3e6db5..4b73f5a 100644 --- a/src/main.c +++ b/src/main.c @@ -284,6 +284,7 @@ static void gameProc(void* arg) { portalSurfaceRevert(0); portalSurfaceCleanupQueueInit(); heapInit(_heapStart, memoryEnd); + profileClearAddressMap(); translationsLoad(gSaveData.controls.subtitleLanguage); levelLoadWithCallbacks(levelGetQueued()); rumblePakClipInit(); diff --git a/src/util/dynamic_asset_loader.c b/src/util/dynamic_asset_loader.c index ffd1341..3b8afa7 100644 --- a/src/util/dynamic_asset_loader.c +++ b/src/util/dynamic_asset_loader.c @@ -1,6 +1,7 @@ #include "dynamic_asset_loader.h" #include "memory.h" #include "rom.h" +#include "../graphics/profile_task.h" #include "../build/assets/models/dynamic_model_list.h" #include "../build/assets/models/dynamic_animated_model_list.h" @@ -92,6 +93,8 @@ Gfx* dynamicAssetLoadModel(struct DynamicAssetModel* model, u32* pointerOffset) *pointerOffset = 0; } + profileMapAddress(model->model, result); + return result; } @@ -114,6 +117,8 @@ void dynamicAssetLoadAnimatedModel(struct DynamicAnimatedAssetModel* model, stru } result->clipCount = model->clipCount; + + profileMapAddress(result->armature->displayList, result->armature->displayList); } void dynamicAssetModelPreload(int index) { diff --git a/tools/profile_parser.js b/tools/profile_parser.js index 862ffbd..191ff35 100644 --- a/tools/profile_parser.js +++ b/tools/profile_parser.js @@ -6,6 +6,8 @@ const symbolMapLines = fs.readFileSync(process.argv[3], 'utf-8').split('\n'); const lineParserRegexp = /(\d+)\/\d+ 0x([a-f0-9]{2})([a-f0-9]{6})([a-f0-9]{8}) ms (\d+\.\d+)/ +const lineMappingRegexp = /addr 0x([a-f0-9]{8}) -> 0x([a-f0-9]{8})/ + const symbolParserRegexp = /0x([a-f0-9]{16})\s+(\w+)/ function parseLine(line) { @@ -37,7 +39,38 @@ function parseSymbolLine(line) { }; } -const linesParsed = lines.map(parseLine).filter(Boolean); +const memoryMapping = new Map(); +const profileBatches = []; +let lastIndex = -1; + +for (const line of lines) { + const parsedLine = parseLine(line); + + if (parsedLine) { + if (lastIndex != 0 && parsedLine.index == 0) { + profileBatches.push({ + memoryMapping: new Map(memoryMapping), + lines: [], + }) + } + + const latestBatch = profileBatches[profileBatches.length - 1]; + latestBatch.lines.push(parsedLine); + + lastIndex = parsedLine.index; + continue + } + + if (line.trim() == 'addr clearall') { + memoryMapping.clear(); + } + + const addrMatch = lineMappingRegexp.exec(line); + + if (addrMatch) { + memoryMapping.set(addrMatch[2], addrMatch[1]); + } +} const symbolAddressMapping = new Map(); @@ -55,47 +88,57 @@ for (const symbolLine of symbolMapLines) { } } -const combinedCommands = []; - -for (const parsedLine of linesParsed) { - const existing = combinedCommands[parsedLine.index]; - - if (existing) { - existing.startTime += parsedLine.startTime; - existing.total += 1; - } else { - combinedCommands[parsedLine.index] = { - ...parsedLine, - total: 1, - }; +function calculateAverage(batch) { + const combinedCommands = []; + + for (const parsedLine of batch.lines) { + const existing = combinedCommands[parsedLine.index]; + + if (existing) { + existing.startTime += parsedLine.startTime; + existing.total += 1; + } else { + combinedCommands[parsedLine.index] = { + ...parsedLine, + total: 1, + }; + } } -} - -for (let i = 0; i < combinedCommands.length; ++i) { - const current = combinedCommands[i]; - - if (current) { - current.startTime /= current.total; + + for (let i = 0; i < combinedCommands.length; ++i) { + const current = combinedCommands[i]; + + if (current) { + current.startTime /= current.total; + } } + + for (let i = 0; i + 1 < combinedCommands.length; ++i) { + const current = combinedCommands[i]; + const next = combinedCommands[i + 1]; + + current.elapsedTime = next.startTime - current.startTime; + } + + // the last command is always a pipe sync we dont care about + combinedCommands.pop(); + + combinedCommands.sort((a, b) => b.elapsedTime - a.elapsedTime); + + batch.combinedCommands = combinedCommands; } -for (let i = 0; i + 1 < combinedCommands.length; ++i) { - const current = combinedCommands[i]; - const next = combinedCommands[i + 1]; +profileBatches.forEach(calculateAverage); - current.elapsedTime = next.startTime - current.startTime; -} +function formatAddress(address, batch) { + if (batch.memoryMapping.has(address)) { + address = batch.memoryMapping.get(address); + } -// the last command is always a pipe sync we dont care about -combinedCommands.pop(); - -combinedCommands.sort((a, b) => b.elapsedTime - a.elapsedTime); - -function formatAddress(address) { return symbolAddressMapping.get(address) || `0x${address}`; } -function formatCommandName(command) { +function formatCommandName(command, batch) { switch (command.command) { case 'db': { @@ -103,7 +146,7 @@ function formatCommandName(command) { return `gsSPSegment(0x${segment}, 0x${command.w1})`; } case 'de': - return `gsSPDisplayList(${formatAddress(command.w1)})`; + return `gsSPDisplayList(${formatAddress(command.w1, batch)})`; case 'f6': return `gsDPFillRectangle`; case 'd8': @@ -115,10 +158,14 @@ function formatCommandName(command) { } } -function formatCommand(command) { - return `${command.elapsedTime} ${formatCommandName(command)}`; +function formatCommand(command, batch) { + return `${command.elapsedTime} ${formatCommandName(command, batch)}`; } -for (const command of combinedCommands) { - console.log(formatCommand(command)); +for (const batch of profileBatches) { + console.log('start of batch'); + for (const command of batch.combinedCommands) { + console.log(formatCommand(command, batch)); + } + console.log('end of batch'); } \ No newline at end of file