Merge branch 'lambertjamesd:master' into docker-2

This commit is contained in:
hackgrid 2023-12-12 20:56:13 +01:00 committed by GitHub
commit 3ad1c815de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
51 changed files with 506 additions and 220 deletions

3
.gitignore vendored
View file

@ -24,3 +24,6 @@ tools/vtf2png
skelatool64/*.zip*
src/controls/controller-data.h
log.txt

View file

@ -7,14 +7,16 @@ ENV PATH /opt/crashsdk/bin:$PATH
ENV PATH /root/.local/bin:$PATH
ENV ROOT /etc/n64
RUN apt update -y && \
DEBIAN_FRONTEND=noninteractive apt install -y \
RUN apt-get update -y
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
apt-utils 2>/dev/null
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y \
ca-certificates
RUN echo "deb [trusted=yes] https://lambertjamesd.github.io/apt/ ./" | tee /etc/apt/sources.list.d/lambertjamesd.list && \
echo "deb [trusted=yes] https://crashoveride95.github.io/apt/ ./" | tee /etc/apt/sources.list.d/n64sdk.list
RUN apt update -y && \
RUN apt-get update -y && \
dpkg --add-architecture i386 && \
DEBIAN_FRONTEND=noninteractive apt install -y \
DEBIAN_FRONTEND=noninteractive apt-get install -y \
binutils-mips-n64 \
gcc-mips-n64 \
n64sdk \
@ -58,5 +60,5 @@ RUN rm /opt/blender/blender-3.6.1-linux-x64.tar.xz
ENV BLENDER_3_6 /opt/blender/blender-3.6.1-linux-x64/blender
RUN pipx ensurepath
RUN pipx ensurepath --force
RUN pipx install vpk

View file

@ -8,14 +8,13 @@
# --------------------------------------------------------------------
include /usr/include/n64/make/PRdefs
SKELATOOL64:=skelatool64/skeletool64
VTF2PNG:=vtf2png
SFZ2N64:=sfz2n64
$(SKELATOOL64):
skelatool64/setup_dependencies.sh
make -C skelatool64
@$(MAKE) -C skelatool64
OPTIMIZER := -Os
LCDEFS := -DDEBUG -g -Isrc/ -I/usr/include/n64/nustd -Werror -Wall
@ -67,6 +66,7 @@ LDFLAGS = -L/usr/lib/n64 $(N64LIB) -L$(N64_LIBGCCDIR) -lgcc
default: english_audio
english_audio: build/src/audio/subtitles.h portal_pak_dir
@$(MAKE) -C skelatool64
@$(MAKE) buildgame
all_languages: build/src/audio/subtitles.h portal_pak_dir german_audio french_audio russian_audio spanish_audio
@ -359,6 +359,7 @@ build/src/scene/security_camera.o: build/src/audio/clips.h build/assets/models/p
build/src/scene/signage.o: $(MODEL_HEADERS)
build/src/scene/switch.o: build/assets/models/props/switch001.h build/assets/materials/static.h build/assets/models/dynamic_animated_model_list.h
build/src/util/dynamic_asset_data.o: build/assets/models/dynamic_model_list_data.h
build/src/util/dynamic_animated_asset_data.o: build/assets/models/dynamic_animated_model_list_data.h
build/src/util/dynamic_asset_loader.o: build/assets/models/dynamic_model_list.h build/assets/models/dynamic_animated_model_list.h
build/src/menu/audio_options.o: build/src/audio/subtitles.h
build/src/menu/video_options.o: build/src/audio/subtitles.h
@ -572,7 +573,8 @@ clean:
rm -rf build
rm -rf portal_pak_dir
rm -rf portal_pak_modified
rm -rf assets/locales/
rm -rf assets/locales
@$(MAKE) -C skelatool64 clean
clean-src:
rm -rf build/src

View file

@ -1,38 +1,35 @@
all:
docker run -v $$PWD:/usr/src/app -it portal64 make
docker run --rm -v $$PWD:/usr/src/app -it portal64 make
debug:
docker run -v $$PWD:/usr/src/app -it PORTAL64_WITH_GFX_VALIDATOR=1 portal64 make
convert_all_png:
docker run -v $$PWD:/usr/src/app -it portal64 make convert_all_png
docker run --rm -v $$PWD:/usr/src/app -it portal64 make convert_all_png
image:
docker build -t portal64 .
bash:
docker run -v $$PWD:/usr/src/app -it portal64 bash
docker run --rm -v $$PWD:/usr/src/app -it portal64 bash
clean:
sudo rm -rf build
sudo rm -rf portal_pak_dir
sudo rm -rf portal_pak_modified
sudo rm -rf assets/locales/
sudo make clean
english_audio:
docker run -v $$PWD:/usr/src/app -it portal64 make english_audio
docker run --rm -v $$PWD:/usr/src/app -it portal64 make english_audio
all_languages:
docker run -v $$PWD:/usr/src/app -it portal64 make all_languages
docker run --rm -v $$PWD:/usr/src/app -it portal64 make all_languages
german_audio:
docker run -v $$PWD:/usr/src/app -it portal64 make german_audio
docker run --rm -v $$PWD:/usr/src/app -it portal64 make german_audio
french_audio:
docker run -v $$PWD:/usr/src/app -it portal64 make french_audio
docker run --rm -v $$PWD:/usr/src/app -it portal64 make french_audio
russian_audio:
docker run -v $$PWD:/usr/src/app -it portal64 make russian_audio
docker run --rm -v $$PWD:/usr/src/app -it portal64 make russian_audio
spanish_audio:
docker run -v $$PWD:/usr/src/app -it portal64 make spanish_audio
docker run --rm -v $$PWD:/usr/src/app -it portal64 make spanish_audio

View file

@ -68,6 +68,13 @@ pipx install vpk
<br />
Clone the Portal64 repo or download the zip.
```sh
sudo apt install git
git clone https://github.com/lambertjamesd/portal64.git
cd portal64
```
Setup and install dependencies for `skeletool64`
```sh
@ -104,13 +111,6 @@ sudo apt install nodejs
```
<br />
Clone the Portal64 repo or download the zip.
```sh
sudo apt install git
git clone https://github.com/lambertjamesd/portal64.git
cd portal64
```
You then need to add the following files from where Portal is installed to the folder `vpk/` OR create a symbolic link to the `Portal` folder there (see [vpk/add_vpk_here.md](./vpk/add_vpk_here.md) for more details!).
You can add multiple languages if desired.

View file

@ -14,10 +14,10 @@
.word 0x00000000 /* Checksum 2 (OVERWRITTEN BY MAKEMASK)*/
.word 0x00000000 /* Unknown */
.word 0x00000000 /* Unknown */
.ascii " " /* Internal ROM name (Max 20 characters) */
.word 0x01000000 /* Advanced_Homebrew_ROM_Header (controller config) */
/* Game ID (EXAMPLE: NSME) Begins here */
.word 0x0000004E /* Cartridge Type (N)*/
.ascii "ED" /* Cartridge ID (SM)*/
.ascii " " /* Region (E)*/
.byte 0x30 /* Version */
.ascii "Portal 64 " /* Internal ROM name (Max 20 characters) */
.word 0x01000000 /* Unused officially / Advanced homebrew ROM header controller config */
.word 0x0000004E /* Cartridge Type (N; cart)*/
.ascii "ED" /* Cartridge ID (ED) / Advanced homebrew ROM header magic value */
.byte 0x41 /* Region (A; All)*/
.byte 0x32 /* Version / Advanced homebrew ROM header misc. (region-free + 256K SRAM) */

View file

@ -150,6 +150,10 @@ materials:
filename: "../../portal_pak_modified/materials/gamepadui/portal_logo.png"
siz: G_IM_SIZ_4b
fmt: G_IM_FMT_I
s:
wrap: false
t:
wrap: false
gDPSetCombineMode:
color: ["0", "0", "0", ENVIRONMENT]
alpha: [TEXEL0, "0", ENVIRONMENT, "0"]

View file

@ -32,7 +32,9 @@ skeletool64: $(OBJ_FILES) $(LUA_OBJ_FILES)
g++ -g -o skeletool64 $(OBJ_FILES) $(LUA_OBJ_FILES) $(LINKER_FLAGS)
clean:
rm -r build/
rm -rf build/
rm -f skeletool64
rm -f CImg_*
init:
@ -48,4 +50,4 @@ build/skeletool.deb: skeletool64 control
dpkg-deb --build build/skeletool
docs:
ldoc .
ldoc .

View file

@ -28,7 +28,7 @@ Bone* Bone::GetParent() {
return mParent;
}
std::unique_ptr<DataChunk> Bone::GenerateRestPosiitonData() {
std::unique_ptr<DataChunk> Bone::GenerateRestPositionData() {
std::unique_ptr<StructureDataChunk> result(new StructureDataChunk());
result->Add(std::unique_ptr<DataChunk>(new StructureDataChunk(mRestPosition)));
@ -190,13 +190,14 @@ Bone* BoneHierarchy::BoneForName(std::string name) {
}
}
void BoneHierarchy::GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName) {
void BoneHierarchy::GenerateRestPositionData(CFileDefinition& fileDef, const std::string& variableName) {
if (mBones.size() == 0) return;
std::unique_ptr<StructureDataChunk> transformData(new StructureDataChunk());
for (unsigned int boneIndex = 0; boneIndex < mBones.size(); ++boneIndex) {
transformData->Add(std::move(mBones[boneIndex]->GenerateRestPosiitonData()));
auto restPositionData = mBones[boneIndex]->GenerateRestPositionData();
transformData->Add(std::move(restPositionData));
std::string boneName = fileDef.GetUniqueName(mBones[boneIndex]->GetName() + "_BONE");
std::transform(boneName.begin(), boneName.end(), boneName.begin(), ::toupper);
@ -215,4 +216,4 @@ bool BoneHierarchy::HasData() const {
unsigned int BoneHierarchy::GetBoneCount() const {
return mBones.size();
}
}

View file

@ -36,7 +36,7 @@ public:
const std::string& GetName();
Bone* GetParent();
std::unique_ptr<DataChunk> GenerateRestPosiitonData();
std::unique_ptr<DataChunk> GenerateRestPositionData();
static Bone* FindCommonAncestor(Bone* a, Bone* b);
/**
@ -72,7 +72,7 @@ public:
bool HasData() const;
unsigned int GetBoneCount() const;
void GenerateRestPosiitonData(CFileDefinition& fileDef, const std::string& variableName);
void GenerateRestPositionData(CFileDefinition& fileDef, const std::string& variableName);
private:
std::vector<std::unique_ptr<Bone>> mBones;
std::map<std::string, Bone*> mBoneByName;

View file

@ -212,7 +212,8 @@ DisplayList::DisplayList(std::string name):
}
void DisplayList::AddCommand(std::unique_ptr<DisplayListCommand> command) {
mDataChunk->Add(std::move(command->GenerateCommand()));
auto generatedCommand = command->GenerateCommand();
mDataChunk->Add(std::move(generatedCommand));
}
StructureDataChunk& DisplayList::GetDataChunk() {
@ -237,4 +238,4 @@ std::unique_ptr<FileDefinition> DisplayList::Generate(const std::string& fileSuf
result->AddTypeHeader("<ultra64.h>");
return result;
}
}

View file

@ -15,8 +15,8 @@ void useTexture(std::set<std::shared_ptr<TextureDefinition>>& usedTextures, std:
}
usedTextures.insert(texture);
fileDefinition.AddDefinition(std::move(texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), fileSuffix)));
auto textureDefinition = texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), fileSuffix);
fileDefinition.AddDefinition(std::move(textureDefinition));
std::shared_ptr<PalleteDefinition> pallete = texture->GetPallete();
if (!pallete || usedPalletes.find(pallete) != usedPalletes.end()) {
@ -24,7 +24,8 @@ void useTexture(std::set<std::shared_ptr<TextureDefinition>>& usedTextures, std:
}
usedPalletes.insert(pallete);
fileDefinition.AddDefinition(std::move(pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), fileSuffix)));
auto palleteDefinition = pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), fileSuffix);
fileDefinition.AddDefinition(std::move(palleteDefinition));
}
void MaterialCollector::UseMaterial(const std::string& material, DisplayListSettings& settings) {
@ -150,4 +151,4 @@ std::string generateMesh(const aiScene* scene, CFileDefinition& fileDefinition,
fileDefinition.AddDefinition(std::move(dlResult));
return displayList.GetName();
}
}

View file

@ -266,7 +266,7 @@ AnimationResults generateAnimationForScene(const aiScene* scene, CFileDefinition
std::string bonesName = fileDefinition.GetUniqueName("default_bones");
std::string boneParentName = fileDefinition.GetUniqueName("bone_parent");
bones.GenerateRestPosiitonData(fileDefinition, bonesName);
bones.GenerateRestPositionData(fileDefinition, bonesName);
std::string boneCountName = bonesName + "_COUNT";
std::transform(boneCountName.begin(), boneCountName.end(), boneCountName.begin(), ::toupper);
fileDefinition.AddMacro(boneCountName, std::to_string(bones.GetBoneCount()));

View file

@ -83,8 +83,9 @@ std::shared_ptr<CollisionGeneratorOutput> generateCollision(const aiScene* scene
if (namedEntry != "") {
fileDefinition.AddMacro(fileDefinition.GetMacroName(namedEntry + "_COLLISION_INDEX"), std::to_string(output->quads.size()));
}
collidersChunk->Add(std::move(collider.Generate()));
auto generatedCollider = collider.Generate();
collidersChunk->Add(std::move(generatedCollider));
std::unique_ptr<StructureDataChunk> colliderType(new StructureDataChunk());
colliderType->AddPrimitive<const char*>("CollisionShapeTypeQuad");
@ -174,4 +175,4 @@ void generateMeshCollider(CFileDefinition& fileDefinition, CollisionGeneratorOut
std::unique_ptr<FileDefinition> definition(new DataFileDefinition("struct MeshCollider", colliderName, false, "_geo", std::move(meshColliderChunk)));
definition->AddTypeHeader("\"physics/mesh_collider.h\"");
fileDefinition.AddDefinition(std::move(definition));
}
}

View file

@ -54,11 +54,13 @@ void MaterialGenerator::GenerateDefinitions(const aiScene* scene, CFileDefinitio
}
for (auto& texture : textures) {
fileDefinition.AddDefinition(std::move(texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), "_mat")));
auto textureDefinition = texture->GenerateDefinition(fileDefinition.GetUniqueName(texture->Name()), "_mat");
fileDefinition.AddDefinition(std::move(textureDefinition));
}
for (auto& pallete : palletes) {
fileDefinition.AddDefinition(std::move(pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), "_mat")));
auto palleteDefinition = pallete->GenerateDefinition(fileDefinition.GetUniqueName(pallete->Name()), "_mat");
fileDefinition.AddDefinition(std::move(palleteDefinition));
}
int index = 0;
@ -130,4 +132,4 @@ std::string MaterialGenerator::MaterialIndexMacroName(const std::string& materia
std::transform(materialName.begin(), materialName.end(), result.begin(), ::toupper);
makeCCompatible(result);
return result + "_INDEX";
}
}

View file

@ -20,7 +20,8 @@ std::unique_ptr<DataChunk> buildMacroChunk(lua_State* L) {
lua_pushnil(L); /* first key */
while (lua_next(L, args) != 0) {
result->Add(std::move(buildDataChunk(L)));
auto dataChunk = buildDataChunk(L);
result->Add(std::move(dataChunk));
lua_pop(L, 1);
}
lua_pop(L, 1);
@ -39,7 +40,8 @@ std::unique_ptr<DataChunk> buildStructureChunk(lua_State* L) {
while (lua_next(L, topStart) != 0) {
int keyType = lua_type(L, -2);
if (keyType == LUA_TNUMBER) {
result->Add(std::move(buildDataChunk(L)));
auto dataChunk = buildDataChunk(L);
result->Add(std::move(dataChunk));
} else if (keyType == LUA_TSTRING) {
namedEntries.push_back(std::pair<std::string, std::unique_ptr<DataChunk>>(
lua_tostring(L, -2),
@ -308,4 +310,4 @@ int luaDefinitonWriterAppend(lua_State* L) {
void populateLuaDefinitionWrite(lua_State* L, CFileDefinition& fileDef) {
lua_pushlightuserdata(L, &fileDef);
luaChainModuleLoader(L, "sk_definition_writer", luaDefinitonWriterAppend, 1);
}
}

View file

@ -601,17 +601,20 @@ void generateMaterial(CFileDefinition& fileDef, const MaterialState& from, const
generateEnumMacro((int)from.alphaCompare, (int)to.alphaCompare, "gsDPSetAlphaCompare", gAlphaCompareNames, output);
generateEnumMacro((int)from.depthSource, (int)to.depthSource, "gsDPSetDepthSource", gDepthSourceNames, output);
std::unique_ptr<DataChunk> geometryModes = std::move(generateGeometryModes(from, to));
auto generatedGeometryModes = generateGeometryModes(from, to);
std::unique_ptr<DataChunk> geometryModes = std::move(generatedGeometryModes);
if (geometryModes) {
output.Add(std::move(geometryModes));
}
std::unique_ptr<DataChunk> combineMode = std::move(generateCombineMode(from, to));
auto generatedCombineMode = generateCombineMode(from, to);
std::unique_ptr<DataChunk> combineMode = std::move(generatedCombineMode);
if (combineMode) {
output.Add(std::move(combineMode));
}
std::unique_ptr<DataChunk> renderMode = std::move(generateRenderMode(from, to));
auto generatedRenderMode = generateRenderMode(from, to);
std::unique_ptr<DataChunk> renderMode = std::move(generatedRenderMode);
if (renderMode) {
output.Add(std::move(renderMode));
}
@ -869,4 +872,4 @@ double materialTransitionTime(const MaterialState& from, const MaterialState& to
}
return result;
}
}

View file

@ -122,7 +122,7 @@ struct DecorObjectDefinition gDecorObjectDefinitions[] = {
NULL,
},
0.0f,
1.0f,
1.3f,
PROPS_LIGHT_RAIL_ENDCAP_DYNAMIC_MODEL,
.materialIndex = LIGHT_RAIL_ENDCAP_INDEX,
.soundClipId = -1,

View file

@ -15,6 +15,7 @@
#define CONTROLLER_PRIORITY 11
#define AUDIO_PRIORITY 12
#define SCHEDULER_PRIORITY 13
#define RSP_PROFILE_PRIORITY 14
#define NUM_FIELDS 1
#define LEVEL_SEGMENT 2

View file

@ -47,107 +47,6 @@ struct FontSymbol* fontFindSymbol(struct Font* font, short id) {
return NULL;
}
Gfx* fontRender(struct Font* font, char* message, int x, int y, Gfx* dl) {
int startX = x;
char prev = 0;
for (; *message; prev = *message, ++message) {
char curr = *message;
if (curr == '\n') {
y += font->charHeight;
x = startX;
continue;
}
// TODO utf-8 decode
struct FontSymbol* symbol = fontFindSymbol(font, (short)curr);
if (!symbol) {
continue;
}
x += fontDetermineKerning(font, prev, curr);
int finalX = x + symbol->xoffset;
int finalY = y + symbol->yoffset;
gSPTextureRectangle(
dl++,
finalX << 2, finalY << 2,
(finalX + symbol->width) << 2,
(finalY + symbol->height) << 2,
G_TX_RENDERTILE,
symbol->x << 5, symbol->y << 5,
0x400, 0x400
);
x += symbol->xadvance;
}
return dl;
}
int fontCountGfx(struct Font* font, char* message) {
int result = 0;
for (; *message; ++message) {
char curr = *message;
if (curr == '\n') {
continue;
}
// TODO utf-8 decode
struct FontSymbol* symbol = fontFindSymbol(font, (short)curr);
if (!symbol) {
continue;
}
result += 3;
}
return result;
}
struct Vector2s16 fontMeasure(struct Font* font, char* message) {
int startX = 0;
char prev = 0;
int x = 0;
int y = 0;
struct Vector2s16 result;
result.x = 0;
for (; *message; prev = *message, ++message) {
char curr = *message;
if (curr == '\n') {
y += font->charHeight;
x = startX;
continue;
}
// TODO utf-8 decode
struct FontSymbol* symbol = fontFindSymbol(font, (short)curr);
if (!symbol) {
continue;
}
x += fontDetermineKerning(font, prev, curr);
x += symbol->xadvance;
result.x = MAX(result.x, x);
}
result.y = y + font->charHeight;
return result;
}
short fontNextUtf8Character(char** strPtr) {
char* curr = *strPtr;

View file

@ -35,11 +35,6 @@ struct Font {
unsigned short kerningMaxCollisions;
};
// legacy methods for a font that fits into a single page
Gfx* fontRender(struct Font* font, char* message, int x, int y, Gfx* dl);
int fontCountGfx(struct Font* font, char* message);
struct Vector2s16 fontMeasure(struct Font* font, char* message);
struct SymbolLocation {
short x;
short y;

143
src/graphics/profile_task.c Normal file
View file

@ -0,0 +1,143 @@
#include "profile_task.h"
#include "../defs.h"
#ifdef PORTAL64_WITH_DEBUGGER
#include "../debugger/serial.h"
#endif
#include <string.h>
#define VIDEO_MSG 666
#define RSP_DONE_MSG 667
#define RDP_DONE_MSG 668
#define SAMPLES_PER_STEP 10
void copyGfx(Gfx* from, Gfx* to, int count) {
Gfx* max = from + count;
while (from < max) {
*to++ = *from++;
}
}
void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task) {
// block scheduler thread
osSetThreadPri(currentThread, RSP_PROFILE_PRIORITY);
// wait for DP to be available
while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID));
OSMesgQueue messageQueue;
OSMesg messages[4];
osCreateMesgQueue(&messageQueue, messages, 4);
// take over event queues
osSetEventMesg(OS_EVENT_SP, &messageQueue, (OSMesg)RSP_DONE_MSG);
osSetEventMesg(OS_EVENT_DP, &messageQueue, (OSMesg)RDP_DONE_MSG);
osViSetEvent(&messageQueue, (OSMesg)VIDEO_MSG, 1);
Gfx* curr = (Gfx*)task->t.data_ptr;
Gfx* end = curr;
while (end[1].words.w0 != _SHIFTL(G_RDPFULLSYNC, 24, 8)) {
++end;
}
#ifdef PORTAL64_WITH_DEBUGGER
int total = end - curr;
#endif
Gfx tmp[3];
while (curr <= end) {
for (int sample = 0; sample < SAMPLES_PER_STEP; ++sample) {
// wait for DP to be available
while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID));
copyGfx(curr, tmp, 3);
Gfx* dl = curr;
gDPPipeSync(dl++);
gDPFullSync(dl++);
gSPEndDisplayList(dl++);
// not very precise, but it seems to work
osWritebackDCacheAll();
#ifdef PORTAL64_WITH_DEBUGGER
OSTime start = osGetTime();
#endif
osSpTaskStart(task);
OSMesg recv;
do {
(void)osRecvMesg(&messageQueue, &recv, OS_MESG_BLOCK);
} while ((int)recv != RDP_DONE_MSG);
#ifdef PORTAL64_WITH_DEBUGGER
OSTime result = osGetTime() - start;
u64 us = OS_CYCLES_TO_NSEC(result);
#endif
// wait for DP to be available
while (IO_READ(DPC_STATUS_REG) & (DPC_STATUS_DMA_BUSY | DPC_STATUS_END_VALID | DPC_STATUS_START_VALID));
copyGfx(tmp, curr, 3);
osWritebackDCacheAll();
#ifdef PORTAL64_WITH_DEBUGGER
char message[64];
int messageLen = sprintf(
message,
"%d/%d 0x%08x%08x ms %d.%d",
curr - (Gfx*)task->t.data_ptr,
total,
curr->words.w0,
curr->words.w1,
(int)(us / 1000000),
(int)(us % 1000000)
);
gdbSendMessage(GDBDataTypeText, message, messageLen);
#endif
}
++curr;
}
osSetEventMesg(OS_EVENT_SP, &scheduler->interruptQ, (OSMesg)RSP_DONE_MSG);
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
}

View file

@ -0,0 +1,11 @@
#ifndef __PROFILE_TASK_H__
#define __PROFILE_TASK_H__
#include <ultra64.h>
#include <sched.h>
void profileTask(OSSched* scheduler, OSThread* currentThread, OSTask* task);
void profileMapAddress(void* original, void* ramAddress);
void profileClearAddressMap();
#endif

View file

@ -65,14 +65,16 @@ void staticRenderPopulateRooms(struct FrustrumCullingInformation* cullingInfo, M
struct RotatedBox rotatedBox;
rotatedBoxTransform(&staticTransforms[staticElement->transformIndex], animatedBox, &rotatedBox);
struct Transform* transform = &staticTransforms[staticElement->transformIndex];
rotatedBoxTransform(transform, animatedBox, &rotatedBox);
if (isRotatedBoxOutsideFrustrum(cullingInfo, &rotatedBox)) {
continue;
}
struct Vector3 center;
vector3Scale(&rotatedBox.origin, &center, 1.0f / SCENE_SCALE);
vector3AddScaled(&staticElement->center, &transform->position, 1.0f / SCENE_SCALE, &center);
renderSceneAdd(
renderScene,

View file

@ -25,6 +25,7 @@
#include "util/profile.h"
#include "util/rom.h"
#include "util/time.h"
#include "graphics/profile_task.h"
#include "levels/levels.h"
#include "savefile/checkpoint.h"
@ -182,6 +183,7 @@ int updateSchedulerModeAndGetFPS(int interlacedMode) {
schedulerMode = HIGH_RES ? (interlacedMode ? OS_VI_MPAL_HPF1 : OS_VI_MPAL_HPN1) : (interlacedMode ? OS_VI_MPAL_LPF1 : OS_VI_MPAL_LPN1);
break;
}
return fps;
}
@ -253,6 +255,7 @@ static void gameProc(void* arg) {
controllersInit();
rumblePakClipInit();
initAudio(fps);
timeSetFrameRate(fps);
soundPlayerInit();
translationsLoad(gSaveData.controls.subtitleLanguage);
skSetSegmentLocation(CHARACTER_ANIMATION_SEGMENT, (unsigned)_animation_segmentSegmentRomStart);
@ -281,6 +284,7 @@ static void gameProc(void* arg) {
portalSurfaceRevert(0);
portalSurfaceCleanupQueueInit();
heapInit(_heapStart, memoryEnd);
profileClearAddressMap();
translationsLoad(gSaveData.controls.subtitleLanguage);
levelLoadWithCallbacks(levelGetQueued());
rumblePakClipInit();
@ -325,6 +329,14 @@ static void gameProc(void* arg) {
profileEnd(updateStart, 0);
drawingEnabled = 1;
}
#if PORTAL64_WITH_RSP_PROFILER
if (controllerGetButtonDown(2, START_BUTTON)) {
struct GraphicsTask* task = &gGraphicsTasks[drawBufferIndex];
zeroMemory(task->framebuffer, sizeof(u16) * SCREEN_WD * SCREEN_HT);
profileTask(&scheduler, &gameThread, &task->task.list);
}
#endif
timeUpdateDelta();
soundPlayerUpdate();
controllersSavePreviousState();

View file

@ -44,8 +44,9 @@ void mainMenuInit(struct GameMenu* gameMenu) {
mainMenuPlayAmbientSound();
for (int i = 0; i < gScene.signageCount; ++i) {
signageActivate(&gScene.signage[i]);
for (int i = 0; i < gScene.clockCount; ++i) {
// this shows the pause time for the main menu
gScene.clocks[i].timeLeft = -1.0f;
}
}
@ -67,6 +68,12 @@ void mainMenuUpdate(struct GameMenu* gameMenu) {
mainMenuPlayAmbientSound();
if (gScene.animator.animators[0].currentTime > 5.0f) {
for (int i = 0; i < gScene.signageCount; ++i) {
signageActivate(&gScene.signage[i]);
}
}
for (int i = 0; i < gScene.signageCount; ++i) {
signageUpdate(&gScene.signage[i]);
}

View file

@ -16,16 +16,6 @@ struct PrerenderedText* menuBuildPrerenderedText(struct Font* font, char* messag
return result;
}
Gfx* menuBuildText(struct Font* font, char* message, int x, int y) {
Gfx* result = malloc(sizeof(Gfx) * (fontCountGfx(font, message) + 1));
Gfx* dl = result;
dl = fontRender(font, message, x, y, dl);
gSPEndDisplayList(dl++);
return result;
}
Gfx* menuBuildBorder(int x, int y, int width, int height) {
Gfx* result = malloc(sizeof(Gfx) * 7 * 3 + 1);
Gfx* dl = result;
@ -211,7 +201,7 @@ void menuSetRenderColor(struct RenderState* renderState, int isSelected, struct
}
}
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y, int shouldUsePrerendered) {
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y) {
struct MenuCheckbox result;
result.x = x;
@ -227,12 +217,7 @@ struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, i
dl = menuRenderOutline(x, y, CHECKBOX_SIZE, CHECKBOX_SIZE, 1, dl);
gSPEndDisplayList(dl++);
if (shouldUsePrerendered) {
result.prerenderedText = menuBuildPrerenderedText(font, message, x + CHECKBOX_SIZE + 6, y, SCREEN_WD);
} else {
result.text = menuBuildText(font, message, x + CHECKBOX_SIZE + 6, y);
}
result.prerenderedText = menuBuildPrerenderedText(font, message, x + CHECKBOX_SIZE + 6, y, SCREEN_WD);
result.checked = 0;
return result;

View file

@ -17,10 +17,7 @@ struct MenuButton {
struct MenuCheckbox {
Gfx* outline;
union {
Gfx* text;
struct PrerenderedText* prerenderedText;
};
struct PrerenderedText* prerenderedText;
Gfx* checkedIndicator;
short x, y;
short checked;
@ -48,7 +45,6 @@ extern struct Coloru8 gSelectionGray;
extern struct Coloru8 gBorderHighlight;
extern struct Coloru8 gBorderDark;
Gfx* menuBuildText(struct Font* font, char* message, int x, int y);
struct PrerenderedText* menuBuildPrerenderedText(struct Font* font, char* message, int x, int y, int maxWidth);
Gfx* menuBuildBorder(int x, int y, int width, int height);
@ -61,7 +57,7 @@ struct MenuButton menuBuildButton(struct Font* font, char* message, int x, int y
void menuSetRenderColor(struct RenderState* renderState, int isSelected, struct Coloru8* selected, struct Coloru8* defaultColor);
void menuRebuildButtonText(struct MenuButton* button, struct Font* font, char* message, int rightAlign);
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y, int shouldUsePrerendered);
struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y);
Gfx* menuCheckboxRender(struct MenuCheckbox* checkbox, Gfx* dl);
struct MenuSlider menuBuildSlider(int x, int y, int w, int tickCount);

View file

@ -44,8 +44,7 @@ void checkboxMenuItemInit(struct MenuBuilderElement* element) {
element->params->params.checkbox.font,
translationsGet(element->params->params.checkbox.messageId),
element->params->x,
element->params->y,
1
element->params->y
);
element->data = checkbox;
}

View file

@ -41,8 +41,6 @@ struct Tab gOptionTabs[] = {
void optionsMenuInit(struct OptionsMenu* options) {
options->menuOutline = menuBuildBorder(MENU_LEFT, MENU_TOP, MENU_WIDTH, MENU_HEIGHT);
options->optionsText = menuBuildText(&gDejaVuSansFont, "OPTIONS", 48, 48);
tabsInit(
&options->tabs,
gOptionTabs,

View file

@ -22,7 +22,6 @@ enum OptionsMenuTabs {
struct OptionsMenu {
Gfx* menuOutline;
Gfx* optionsText;
struct Tabs tabs;

View file

@ -570,7 +570,7 @@ int epaSolveSwept(struct Simplex* startingSimplex, void* objectA, MinkowsiSum ob
vector3Scale(&raycastDir, &planePos, distance);
float moveOffset = vector3DistSqrd(bStart, bEnd);
if (distance * distance > moveOffset + 0.1f) {
if (distance * distance >= moveOffset + 0.01f) {
goto error;
}

View file

@ -525,5 +525,6 @@ void sceneDeserialize(struct Serializer* serializer, struct Scene* scene) {
if (scene->player.flags & (PlayerHasFirstPortalGun | PlayerHasSecondPortalGun)) {
scene->portalGun.portalGunVisible = 1;
scene->portalGun.rotation = scene->player.lookTransform.rotation;
}
}

View file

@ -19,8 +19,10 @@
#define DIGIT_WIDTH 512
u8 gCurrentClockDigits[5];
u8 gCurrentClockDigits[7];
Vtx* gClockDigits[] = {
signage_clock_digits_clock_digits_hour_01_color,
signage_clock_digits_clock_digits_minute_10_color,
signage_clock_digits_clock_digits_minute_01_color,
signage_clock_digits_clock_digits_second_10_color,
signage_clock_digits_clock_digits_second_01_color,
@ -45,31 +47,34 @@ void clockSetDigit(int digitIndex, int currDigit) {
}
void clockSetTime(float timeInSeconds) {
clockSetDigit(0, 0);
clockSetDigit(1, 0);
float minutes = floor(timeInSeconds * (1.0f / 60.0f));
clockSetDigit(0, (int)minutes);
clockSetDigit(2, (int)minutes);
timeInSeconds -= minutes *= 60.0f;
float tenSeconds = floor(timeInSeconds * 0.1f);
clockSetDigit(1, (int)tenSeconds);
clockSetDigit(3, (int)tenSeconds);
timeInSeconds -= tenSeconds * 10.0f;
float seconds = floor(timeInSeconds);
clockSetDigit(2, (int)seconds);
clockSetDigit(4, (int)seconds);
timeInSeconds -= seconds;
float tenthsOfSecond = floor(timeInSeconds * 10.0f);
clockSetDigit(3, (int)tenthsOfSecond);
clockSetDigit(5, (int)tenthsOfSecond);
timeInSeconds -= tenthsOfSecond * 0.1f;
clockSetDigit(4, (int)floor(timeInSeconds * 100.0f));
clockSetDigit(6, (int)floor(timeInSeconds * 100.0f));
}
void clockRenderRender(void* data, struct DynamicRenderDataList* renderList, struct RenderState* renderState) {
@ -81,7 +86,18 @@ void clockRenderRender(void* data, struct DynamicRenderDataList* renderList, str
return;
}
clockSetTime(clock->timeLeft);
// main menu clock time
if (clock->timeLeft == -1.0f) {
clockSetDigit(0, 1);
clockSetDigit(1, 5);
clockSetDigit(2, 9);
clockSetDigit(3, 5);
clockSetDigit(4, 0);
clockSetDigit(5, 0);
clockSetDigit(6, 0);
} else {
clockSetTime(clock->timeLeft);
}
transformToMatrixL(&clock->transform, matrix, SCENE_SCALE);

View file

@ -216,4 +216,6 @@ void fizzlerUpdate(struct Fizzler* fizzler) {
fizzler->oldestParticleIndex = 0;
}
}
osWritebackDCache(fizzler->modelVertices, sizeof(Vtx) * maxVertex);
}

View file

@ -49,6 +49,9 @@ void portalGunInit(struct PortalGun* portalGun, struct Transform* at, int isFres
if (isFreshStart) {
skAnimatorRunClip(&portalGun->animator, &portal_gun_v_portalgun_Armature_draw_clip, 0.0f, 0);
// the first time the scene renders, the animation clip hasn't started yet
// this just hides the gun offscreen so it doesn't show up for a single frame
portalGun->armature.pose[0].position.y = -1.0f;
} else {
skAnimatorRunClip(&portalGun->animator, &portal_gun_v_portalgun_Armature_idle_clip, 0.0f, 0);
}

View file

@ -272,7 +272,8 @@ int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform*
for (iteration = 0; iteration < MAX_POS_ADJUST_ITERATIONS; ++iteration) {
int minOverlap = PORTAL_SURFACE_OVERLAP;
struct Vector2s16 minOverlapOffset;
int minIsOutside = 0;
struct Vector2s16 minOverlapOffset = {{{0, 0}}};
struct Vector2s16 portalMin;
portalMin.x = output->x - halfSize.x;
@ -310,40 +311,49 @@ int portalSurfaceAdjustPosition(struct PortalSurface* surface, struct Transform*
vector2s16Sub(&b, &a, &edgeDir);
struct Vector2s16 offset;
int distance = edgeDir.y >= 0 ? portalMax.x - edgeMin.x : PORTAL_SURFACE_OVERLAP;
int distance = portalMax.x - edgeMin.x;
int isOutside = edgeDir.y < 0;
offset.x = -distance;
offset.y = 0;
int distanceCheck = edgeMax.x - portalMin.x;
if (distanceCheck < distance && edgeDir.y <= 0) {
if (distanceCheck < distance) {
distance = distanceCheck;
offset.x = distance;
offset.y = 0;
isOutside = edgeDir.y > 0;
}
distanceCheck = portalMax.y - edgeMin.y;
if (distanceCheck < distance && edgeDir.x <= 0) {
if (distanceCheck < distance) {
distance = distanceCheck;
offset.x = 0;
offset.y = -distance;
isOutside = edgeDir.x > 0;
}
distanceCheck = edgeMax.y - portalMin.y;
if (distanceCheck < distance && edgeDir.x >= 0) {
if (distanceCheck < distance) {
distance = distanceCheck;
offset.x = 0;
offset.y = distance;
isOutside = edgeDir.x < 0;
}
if (distance < minOverlap) {
minOverlap = distance;
minIsOutside = isOutside;
minOverlapOffset = offset;
}
}
if (minIsOutside) {
return 0;
}
if (minOverlap == PORTAL_SURFACE_OVERLAP) {
break;
}

View file

@ -326,12 +326,13 @@ void sceneRender(struct Scene* scene, struct RenderState* renderState, struct Gr
gDPSetRenderMode(renderState->dl++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
gSPGeometryMode(renderState->dl++, G_ZBUFFER | G_LIGHTING | G_CULL_BOTH, G_SHADE);
if (gGameMenu.state == GameMenuStateResumeGame || scene->hud.fadeInTimer > 0.0f) {
hudRender(&scene->hud, &scene->player, renderState);
}
if (gGameMenu.state != GameMenuStateResumeGame) {
gameMenuRender(&gGameMenu, renderState, task);
}
else{
hudRender(&scene->hud, &scene->player, renderState);
}
// sceneRenderPerformanceMetrics(scene, renderState, task);

View file

@ -250,5 +250,7 @@ void signageUpdate(struct Signage* signage) {
}
void signageActivate(struct Signage* signage) {
signage->currentFrame = 0;
if (signage->currentFrame == -1) {
signage->currentFrame = 0;
}
}

View file

@ -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) {

View file

@ -3,9 +3,14 @@
float gTimePassed = 0.0f;
int gCurrentFrame = 0;
float gFixedDeltaTime = ((1.0f + FRAME_SKIP) / 60.0f);
void timeUpdateDelta() {
OSTime currTime = osGetTime();
gTimePassed = (float)OS_CYCLES_TO_USEC(currTime) / 1000000.0f;
++gCurrentFrame;
}
void timeSetFrameRate(int fps) {
gFixedDeltaTime = ((1.0f + FRAME_SKIP) / (float)fps);
}

View file

@ -6,10 +6,12 @@
extern float gTimePassed;
extern OSTime gLastTime;
extern int gCurrentFrame;
extern float gFixedDeltaTime;
#define FRAME_SKIP 1
#define FIXED_DELTA_TIME ((1.0f + FRAME_SKIP) / 60.0f)
#define FIXED_DELTA_TIME gFixedDeltaTime
void timeUpdateDelta();
void timeSetFrameRate(int fps);
#endif

171
tools/profile_parser.js Normal file
View file

@ -0,0 +1,171 @@
const fs = require('fs');
const lines = fs.readFileSync(process.argv[2], 'utf-8').split('\n');
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) {
const match = lineParserRegexp.exec(line);
if (!match) {
return null;
}
return {
index: parseInt(match[1]),
command: match[2],
w0: match[3],
w1: match[4],
startTime: parseFloat(match[5]),
}
}
function parseSymbolLine(line) {
const match = symbolParserRegexp.exec(line);
if (!match) {
return null;
}
return {
address: match[1].substring(8),
name: match[2],
};
}
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();
for (const symbolLine of symbolMapLines) {
const parsedLine = parseSymbolLine(symbolLine);
if (!parsedLine) {
continue;
}
if (symbolAddressMapping.has(parsedLine.address)) {
symbolAddressMapping.set(parsedLine.address, symbolAddressMapping.get(parsedLine.address) + ',' + parsedLine.name);
} else {
symbolAddressMapping.set(parsedLine.address, parsedLine.name);
}
}
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 + 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;
}
profileBatches.forEach(calculateAverage);
function formatAddress(address, batch) {
if (batch.memoryMapping.has(address)) {
address = batch.memoryMapping.get(address);
}
return symbolAddressMapping.get(address) || `0x${address}`;
}
function formatCommandName(command, batch) {
switch (command.command) {
case 'db':
{
const segment = parseInt(command.w0.substring(4), 16) / 4;
return `gsSPSegment(0x${segment}, 0x${command.w1})`;
}
case 'de':
return `gsSPDisplayList(${formatAddress(command.w1, batch)})`;
case 'f6':
return `gsDPFillRectangle`;
case 'd8':
return `gsSPPopMatrix`;
case 'da':
return `gsSPMatrix`;
default:
return `unknown 0x${command.command} 0x${command.w0}${command.w1}`;
}
}
function formatCommand(command, batch) {
return `${command.elapsedTime} ${formatCommandName(command, batch)}`;
}
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');
}