mirror of
https://github.com/mwpenny/portal64-still-alive.git
synced 2024-10-19 22:27:36 -04:00
work on rsp profiler
This commit is contained in:
parent
eae2f6d3b1
commit
af2240298f
|
@ -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
|
||||
}
|
|
@ -5,5 +5,7 @@
|
|||
#include <sched.h>
|
||||
|
||||
void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task);
|
||||
void profileMapAddress(void* original, void* ramAddress);
|
||||
void profileClearAddressMap();
|
||||
|
||||
#endif
|
|
@ -284,6 +284,7 @@ static void gameProc(void* arg) {
|
|||
portalSurfaceRevert(0);
|
||||
portalSurfaceCleanupQueueInit();
|
||||
heapInit(_heapStart, memoryEnd);
|
||||
profileClearAddressMap();
|
||||
translationsLoad(gSaveData.controls.subtitleLanguage);
|
||||
levelLoadWithCallbacks(levelGetQueued());
|
||||
rumblePakClipInit();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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');
|
||||
}
|
Loading…
Reference in a new issue