[jak2] faster startup (#2738)

Trying to make up for some of the startup speed lost in the SDL
transition. This saves about 1s from start (from ~3s), and about 500 MB
of RAM.

- Faster TIE unpack by merging matrix groups, more efficient vertex
transforms, and skipping normal transforms on groups with no normals.
- Refactor generic merc and merc to use a single renderer with multiple
interfaces, rather than many renderers. Removed "LightningRenderer" as a
special thing, but Warp is still special
- Add more profiling stuff to startup and the loader.
- Remove `SDL_INIT_HAPTIC` - this turned out to be needed for
force-feedback steering wheels, and not needed for controller vibration
- Switched `vag-player` to use quicksort instead of the default GOAL
sort (very slow)
This commit is contained in:
water111 2023-06-17 17:23:25 -04:00 committed by GitHub
parent 1512c6bd9c
commit 6e779d1f1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 540 additions and 354 deletions

View file

@ -3,6 +3,8 @@
#include <algorithm>
#include <functional>
#include "xmmintrin.h"
#include "common/util/Assert.h"
namespace tfrag3 {
@ -166,6 +168,7 @@ std::array<math::Vector3f, 3> tie_normal_transform_v2(const std::array<math::Vec
// vmadday.xyzw acc, vf25, vf18
// vmaddz.xyzw vf12, vf26, vf18
result[2] = vf18;
return result;
//
// sqc2 vf10, -112(t8)
@ -199,6 +202,22 @@ u32 unpack_tie_normal(const std::array<math::Vector3f, 3>& mat, s8 nx, s8 ny, s8
return pack_to_gl_normal(as_int.x(), as_int.y(), as_int.z());
}
/*
void tie_normal_v3(__m128* out, const std::array<math::Vector4f, 4>& in) {
math::Vector3f x_row = in[0].xyz();
math::Vector3f y_row = in[1].xyz();
math::Vector3f z_row = in[2].xyz();
x_row.normalize();
y_row = x_row.cross(y_row.cross(x_row)).normalized();
z_row = x_row.cross(y_row);
out[0] = _mm_setr_ps(x_row[0], x_row[1], x_row[2], 0);
out[1] = _mm_setr_ps(y_row[0], y_row[1], y_row[2], 0);
out[2] = _mm_setr_ps(z_row[0], z_row[1], z_row[2], 0);
}
*/
void TieTree::unpack() {
unpacked.vertices.resize(packed_vertices.color_indices.size());
size_t i = 0;
@ -222,24 +241,53 @@ void TieTree::unpack() {
}
} else {
const auto& mat = packed_vertices.matrices[grp.matrix_idx];
auto nmat = tie_normal_transform_v2(mat);
for (u32 src_idx = grp.start_vert; src_idx < grp.end_vert; src_idx++) {
auto& vtx = unpacked.vertices[i];
vtx.color_index = packed_vertices.color_indices[i];
const auto& proto_vtx = packed_vertices.vertices[src_idx];
auto temp = mat[0] * proto_vtx.x + mat[1] * proto_vtx.y + mat[2] * proto_vtx.z + mat[3];
vtx.x = temp.x();
vtx.y = temp.y();
vtx.z = temp.z();
vtx.s = proto_vtx.s;
vtx.t = proto_vtx.t;
vtx.nor = unpack_tie_normal(nmat, proto_vtx.nx, proto_vtx.ny, proto_vtx.nz);
vtx.r = proto_vtx.r;
vtx.g = proto_vtx.g;
vtx.b = proto_vtx.b;
vtx.a = proto_vtx.a;
i++;
__m128 mat0 = _mm_loadu_ps(mat[0].data());
__m128 mat1 = _mm_loadu_ps(mat[1].data());
__m128 mat2 = _mm_loadu_ps(mat[2].data());
__m128 mat3 = _mm_loadu_ps(mat[3].data());
if (grp.has_normals) {
auto nmat = tie_normal_transform_v2(mat);
for (u32 src_idx = grp.start_vert; src_idx < grp.end_vert; src_idx++) {
auto& vtx = unpacked.vertices[i];
vtx.color_index = packed_vertices.color_indices[i];
const auto& proto_vtx = packed_vertices.vertices[src_idx];
// auto temp = mat[0] * proto_vtx.x + mat[1] * proto_vtx.y + mat[2] * proto_vtx.z +
// mat[3];
__m128 transformed = mat3;
transformed = _mm_add_ps(transformed, _mm_mul_ps(_mm_set1_ps(proto_vtx.x), mat0));
transformed = _mm_add_ps(transformed, _mm_mul_ps(_mm_set1_ps(proto_vtx.y), mat1));
transformed = _mm_add_ps(transformed, _mm_mul_ps(_mm_set1_ps(proto_vtx.z), mat2));
_mm_storeu_ps(&vtx.x, transformed);
vtx.s = proto_vtx.s;
vtx.t = proto_vtx.t;
vtx.nor = unpack_tie_normal(nmat, proto_vtx.nx, proto_vtx.ny, proto_vtx.nz);
vtx.r = proto_vtx.r;
vtx.g = proto_vtx.g;
vtx.b = proto_vtx.b;
vtx.a = proto_vtx.a;
i++;
}
} else {
for (u32 src_idx = grp.start_vert; src_idx < grp.end_vert; src_idx++) {
auto& vtx = unpacked.vertices[i];
vtx.color_index = packed_vertices.color_indices[i];
const auto& proto_vtx = packed_vertices.vertices[src_idx];
__m128 transformed = mat3;
transformed = _mm_add_ps(transformed, _mm_mul_ps(_mm_set1_ps(proto_vtx.x), mat0));
transformed = _mm_add_ps(transformed, _mm_mul_ps(_mm_set1_ps(proto_vtx.y), mat1));
transformed = _mm_add_ps(transformed, _mm_mul_ps(_mm_set1_ps(proto_vtx.z), mat2));
_mm_storeu_ps(&vtx.x, transformed);
vtx.s = proto_vtx.s;
vtx.t = proto_vtx.t;
vtx.nor = 0;
vtx.r = proto_vtx.r;
vtx.g = proto_vtx.g;
vtx.b = proto_vtx.b;
vtx.a = proto_vtx.a;
i++;
}
}
}
}

View file

@ -18,7 +18,7 @@ namespace tfrag3 {
// - if changing any large things (vertices, vis, bvh, colors, textures) update get_memory_usage
// - if adding a new category to the memory usage, update extract_level to print it.
constexpr int TFRAG3_VERSION = 36;
constexpr int TFRAG3_VERSION = 37;
enum MemoryUsageCategory {
TEXTURE,
@ -115,6 +115,7 @@ struct PackedTieVertices {
s32 matrix_idx;
u32 start_vert;
u32 end_vert;
bool has_normals = false;
};
std::vector<u16> color_indices;

View file

@ -2489,6 +2489,14 @@ void handle_draw_for_strip(tfrag3::TieTree& tree,
grp.matrix_idx = matrix_idx;
grp.start_vert = packed_vert_indices.at(frag_idx).at(strip_idx).first;
grp.end_vert = packed_vert_indices.at(frag_idx).at(strip_idx).second;
grp.has_normals = false;
for (auto i = grp.start_vert; i < grp.end_vert; i++) {
auto& v = tree.packed_vertices.vertices.at(i);
if (v.nx || v.ny || v.nz) {
grp.has_normals = true;
break;
}
}
tree.packed_vertices.matrix_groups.push_back(grp);
tfrag3::StripDraw::VertexRun run;
@ -2697,6 +2705,25 @@ void merge_groups(std::vector<tfrag3::StripDraw::VisGroup>& grps) {
std::swap(result, grps);
}
void merge_groups(std::vector<tfrag3::PackedTieVertices::MatrixGroup>& grps) {
std::vector<tfrag3::PackedTieVertices::MatrixGroup> result;
result.push_back(grps.at(0));
for (size_t i = 1; i < grps.size(); i++) {
auto& this_group = grps[i];
auto& maybe_merge = result.back();
if (this_group.start_vert == maybe_merge.end_vert &&
this_group.matrix_idx == maybe_merge.matrix_idx &&
this_group.has_normals == maybe_merge.has_normals) {
maybe_merge.end_vert = this_group.end_vert;
} else {
result.push_back(this_group);
}
}
std::swap(result, grps);
}
void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
const std::string& debug_name,
const std::vector<level_tools::TextureRemap>& tex_map,
@ -2798,6 +2825,8 @@ void extract_tie(const level_tools::DrawableTreeInstanceTie* tree,
merge_groups(draw.instance_groups);
}
merge_groups(this_tree.packed_vertices.matrix_groups);
this_tree.colors = full_palette.colors;
out.tie_trees[geo].push_back(std::move(this_tree));
}

View file

@ -30,9 +30,10 @@ set(RUNTIME_SOURCE
graphics/opengl_renderer/foreground/Generic2_DMA.cpp
graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp
graphics/opengl_renderer/foreground/Generic2.cpp
graphics/opengl_renderer/foreground/Generic2BucketRenderer.cpp
graphics/opengl_renderer/foreground/Merc2.cpp
graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp
graphics/opengl_renderer/foreground/Shadow2.cpp
graphics/opengl_renderer/LightningRenderer.cpp
graphics/opengl_renderer/loader/Loader.cpp
graphics/opengl_renderer/loader/LoaderStages.cpp
graphics/opengl_renderer/ocean/CommonOceanRenderer.cpp

View file

@ -1,20 +0,0 @@
#include "LightningRenderer.h"
LightningRenderer::LightningRenderer(const std::string& name, int id)
: BucketRenderer(name, id), m_generic(name, id) {}
LightningRenderer::~LightningRenderer() {}
void LightningRenderer::draw_debug_window() {
m_generic.draw_debug_window();
}
void LightningRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
m_generic.render_in_mode(dma, render_state, prof, Generic2::Mode::LIGHTNING);
}
void LightningRenderer::init_shaders(ShaderLibrary& shaders) {
m_generic.init_shaders(shaders);
}

View file

@ -1,16 +0,0 @@
#pragma once
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/foreground/Generic2.h"
class LightningRenderer : public BucketRenderer {
public:
LightningRenderer(const std::string& name, int id);
~LightningRenderer();
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
private:
Generic2 m_generic;
};

View file

@ -8,7 +8,6 @@
#include "game/graphics/opengl_renderer/DepthCue.h"
#include "game/graphics/opengl_renderer/DirectRenderer.h"
#include "game/graphics/opengl_renderer/EyeRenderer.h"
#include "game/graphics/opengl_renderer/LightningRenderer.h"
#include "game/graphics/opengl_renderer/ProgressRenderer.h"
#include "game/graphics/opengl_renderer/ShadowRenderer.h"
#include "game/graphics/opengl_renderer/SkyRenderer.h"
@ -19,7 +18,8 @@
#include "game/graphics/opengl_renderer/background/TFragment.h"
#include "game/graphics/opengl_renderer/background/Tie3.h"
#include "game/graphics/opengl_renderer/foreground/Generic2.h"
#include "game/graphics/opengl_renderer/foreground/Merc2.h"
#include "game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.h"
#include "game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.h"
#include "game/graphics/opengl_renderer/foreground/Shadow2.h"
#include "game/graphics/opengl_renderer/ocean/OceanMidAndFar.h"
#include "game/graphics/opengl_renderer/ocean/OceanNear.h"
@ -78,7 +78,11 @@ OpenGLRenderer::OpenGLRenderer(std::shared_ptr<TexturePool> texture_pool,
lg::debug("OpenGL context information: {}", (const char*)glGetString(GL_VERSION));
m_merc2 = std::make_shared<Merc2>(m_render_state.shaders);
m_generic2 = std::make_shared<Generic2>(m_render_state.shaders);
// initialize all renderers
auto p = scoped_prof("init-bucket-renderers");
switch (m_version) {
case GameVersion::Jak1:
init_bucket_renderers_jak1();
@ -121,12 +125,13 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
fmt::format("etie-l{}-tfrag", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::ETIE_L0_TFRAG, BucketId::ETIE_L1_TFRAG, i), tie,
tfrag3::TieCategory::NORMAL_ENVMAP);
init_bucket_renderer<Merc2>(
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-tfrag", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_TFRAG, BucketId::MERC_L1_TFRAG, i));
init_bucket_renderer<Generic2>(
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_TFRAG, BucketId::MERC_L1_TFRAG, i), m_merc2);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc-l{}-tfrag", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_TFRAG, BucketId::GMERC_L1_TFRAG, i));
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_TFRAG, BucketId::GMERC_L1_TFRAG, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-shrub", i), BucketCategory::TEX,
@ -134,12 +139,13 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
init_bucket_renderer<Shrub>(
fmt::format("shrub-l{}-shrub", i), BucketCategory::SHRUB,
GET_BUCKET_ID_FOR_LIST(BucketId::SHRUB_L0_SHRUB, BucketId::SHRUB_L1_SHRUB, i));
init_bucket_renderer<Merc2>(
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-shrub", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_SHRUB, BucketId::MERC_L1_SHRUB, i));
init_bucket_renderer<Generic2>(
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_SHRUB, BucketId::MERC_L1_SHRUB, i), m_merc2);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc-l{}-shrub", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_SHRUB, BucketId::GMERC_L1_SHRUB, i));
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_SHRUB, BucketId::GMERC_L1_SHRUB, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-alpha", i), BucketCategory::TEX,
@ -156,42 +162,46 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
fmt::format("etie-t-l{}-alpha", i), BucketCategory::TIE,
GET_BUCKET_ID_FOR_LIST(BucketId::ETIE_T_L0_ALPHA, BucketId::ETIE_T_L1_ALPHA, i), tie,
tfrag3::TieCategory::TRANS_ENVMAP);
init_bucket_renderer<Merc2>(
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-alpha", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_ALPHA, BucketId::MERC_L1_ALPHA, i));
init_bucket_renderer<Generic2>(
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_ALPHA, BucketId::MERC_L1_ALPHA, i), m_merc2);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc-l{}-alpha", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_ALPHA, BucketId::GMERC_L1_ALPHA, i));
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_ALPHA, BucketId::GMERC_L1_ALPHA, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-pris", i), BucketCategory::TEX,
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_PRIS, BucketId::TEX_L1_PRIS, i));
init_bucket_renderer<Merc2>(
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-pris", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS, BucketId::MERC_L1_PRIS, i));
init_bucket_renderer<Generic2>(
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS, BucketId::MERC_L1_PRIS, i), m_merc2);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc-l{}-pris", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_PRIS, BucketId::GMERC_L1_PRIS, i));
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_PRIS, BucketId::GMERC_L1_PRIS, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-pris2", i), BucketCategory::TEX,
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_PRIS2, BucketId::TEX_L1_PRIS2, i));
init_bucket_renderer<Merc2>(
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-pris2", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS2, BucketId::MERC_L1_PRIS2, i));
init_bucket_renderer<Generic2>(
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS2, BucketId::MERC_L1_PRIS2, i), m_merc2);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc-l{}-pris2", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_PRIS2, BucketId::GMERC_L1_PRIS2, i));
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_PRIS2, BucketId::GMERC_L1_PRIS2, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<TextureUploadHandler>(
fmt::format("tex-l{}-water", i), BucketCategory::TEX,
GET_BUCKET_ID_FOR_LIST(BucketId::TEX_L0_WATER, BucketId::TEX_L1_WATER, i));
init_bucket_renderer<Merc2>(
init_bucket_renderer<Merc2BucketRenderer>(
fmt::format("merc-l{}-water", i), BucketCategory::MERC,
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_WATER, BucketId::MERC_L1_WATER, i));
init_bucket_renderer<Generic2>(
GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_WATER, BucketId::MERC_L1_WATER, i), m_merc2);
init_bucket_renderer<Generic2BucketRenderer>(
fmt::format("gmerc-l{}-water", i), BucketCategory::GENERIC,
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_WATER, BucketId::GMERC_L1_WATER, i));
GET_BUCKET_ID_FOR_LIST(BucketId::GMERC_L0_WATER, BucketId::GMERC_L1_WATER, i), m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<TFragment>(
fmt::format("tfrag-w-l{}-alpha", i), BucketCategory::TFRAG,
GET_BUCKET_ID_FOR_LIST(BucketId::TFRAG_W_L0_WATER, BucketId::TFRAG_W_L1_WATER, i),
@ -209,21 +219,26 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
// 180
init_bucket_renderer<TextureUploadHandler>("tex-lcom-tfrag", BucketCategory::TEX,
BucketId::TEX_LCOM_TFRAG);
init_bucket_renderer<Merc2>("merc-lcom-tfrag", BucketCategory::MERC, BucketId::MERC_LCOM_TFRAG);
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-tfrag", BucketCategory::MERC,
BucketId::MERC_LCOM_TFRAG, m_merc2);
// 190
init_bucket_renderer<TextureUploadHandler>("tex-lcom-shrub", BucketCategory::TEX,
BucketId::TEX_LCOM_SHRUB);
init_bucket_renderer<Merc2>("merc-lcom-shrub", BucketCategory::MERC, BucketId::MERC_LCOM_SHRUB);
init_bucket_renderer<Generic2>("gmerc-lcom-tfrag", BucketCategory::GENERIC,
BucketId::GMERC_LCOM_TFRAG);
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-shrub", BucketCategory::MERC,
BucketId::MERC_LCOM_SHRUB, m_merc2);
init_bucket_renderer<Generic2BucketRenderer>("gmerc-lcom-tfrag", BucketCategory::GENERIC,
BucketId::GMERC_LCOM_TFRAG, m_generic2,
Generic2::Mode::NORMAL);
init_bucket_renderer<Shadow2>("shadow", BucketCategory::OTHER, BucketId::SHADOW);
// 220
init_bucket_renderer<TextureUploadHandler>("tex-lcom-pris", BucketCategory::TEX,
BucketId::TEX_LCOM_PRIS);
init_bucket_renderer<Merc2>("merc-lcom-pris", BucketCategory::MERC, BucketId::MERC_LCOM_PRIS);
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-pris", BucketCategory::MERC,
BucketId::MERC_LCOM_PRIS, m_merc2);
init_bucket_renderer<TextureUploadHandler>("tex-lcom-water", BucketCategory::TEX,
BucketId::TEX_LCOM_WATER);
init_bucket_renderer<Merc2>("merc-lcom-water", BucketCategory::MERC, BucketId::MERC_LCOM_WATER);
init_bucket_renderer<Merc2BucketRenderer>("merc-lcom-water", BucketCategory::MERC,
BucketId::MERC_LCOM_WATER, m_merc2);
init_bucket_renderer<TextureUploadHandler>("tex-lcom-sky-post", BucketCategory::TEX,
BucketId::TEX_LCOM_SKY_POST);
// 310
@ -232,10 +247,11 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
BucketId::TEX_ALL_SPRITE);
init_bucket_renderer<Sprite3>("particles", BucketCategory::SPRITE, BucketId::PARTICLES);
init_bucket_renderer<Shadow2>("shadow2", BucketCategory::OTHER, BucketId::SHADOW2);
init_bucket_renderer<LightningRenderer>("effects", BucketCategory::OTHER, BucketId::EFFECTS);
init_bucket_renderer<Generic2BucketRenderer>("effects", BucketCategory::OTHER, BucketId::EFFECTS,
m_generic2, Generic2::Mode::LIGHTNING);
init_bucket_renderer<TextureUploadHandler>("tex-all-warp", BucketCategory::TEX,
BucketId::TEX_ALL_WARP);
init_bucket_renderer<Warp>("warp", BucketCategory::GENERIC, BucketId::GMERC_WARP);
init_bucket_renderer<Warp>("warp", BucketCategory::GENERIC, BucketId::GMERC_WARP, m_generic2);
init_bucket_renderer<DirectRenderer>("debug-no-zbuf1", BucketCategory::OTHER,
BucketId::DEBUG_NO_ZBUF1, 0x8000);
init_bucket_renderer<TextureUploadHandler>("tex-all-map", BucketCategory::TEX,
@ -256,20 +272,23 @@ void OpenGLRenderer::init_bucket_renderers_jak2() {
m_render_state.eye_renderer = eye_renderer.get();
m_jak2_eye_renderer = std::move(eye_renderer);
// for now, for any unset renderers, just set them to an EmptyBucketRenderer.
for (size_t i = 0; i < m_bucket_renderers.size(); i++) {
if (!m_bucket_renderers[i]) {
init_bucket_renderer<EmptyBucketRenderer>(fmt::format("bucket-{}", i), BucketCategory::OTHER,
i);
}
{
auto p = scoped_prof("render-inits");
// for now, for any unset renderers, just set them to an EmptyBucketRenderer.
for (size_t i = 0; i < m_bucket_renderers.size(); i++) {
if (!m_bucket_renderers[i]) {
init_bucket_renderer<EmptyBucketRenderer>(fmt::format("bucket-{}", i),
BucketCategory::OTHER, i);
}
m_bucket_renderers[i]->init_shaders(m_render_state.shaders);
m_bucket_renderers[i]->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
m_bucket_renderers[i]->init_shaders(m_render_state.shaders);
m_bucket_renderers[i]->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
}
m_jak2_eye_renderer->init_shaders(m_render_state.shaders);
m_jak2_eye_renderer->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
}
m_jak2_eye_renderer->init_shaders(m_render_state.shaders);
m_jak2_eye_renderer->init_textures(*m_render_state.texture_pool, GameVersion::Jak2);
auto p = scoped_prof("load-common");
m_render_state.loader->load_common(*m_render_state.texture_pool, "GAME");
}
/*!
@ -314,11 +333,12 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
init_bucket_renderer<Tie3WithEnvmapJak1>("l0-tfrag-tie", BucketCategory::TIE,
BucketId::TIE_LEVEL0, 0);
// 10 : MERC_TFRAG_TEX_LEVEL0
init_bucket_renderer<Merc2>("l0-tfrag-merc", BucketCategory::MERC,
BucketId::MERC_TFRAG_TEX_LEVEL0);
init_bucket_renderer<Merc2BucketRenderer>("l0-tfrag-merc", BucketCategory::MERC,
BucketId::MERC_TFRAG_TEX_LEVEL0, m_merc2);
// 11 : GMERC_TFRAG_TEX_LEVEL0
init_bucket_renderer<Generic2>("l0-tfrag-generic", BucketCategory::GENERIC,
BucketId::GENERIC_TFRAG_TEX_LEVEL0, 1500000, 10000, 10000, 800);
init_bucket_renderer<Generic2BucketRenderer>("l0-tfrag-generic", BucketCategory::GENERIC,
BucketId::GENERIC_TFRAG_TEX_LEVEL0, m_generic2,
Generic2::Mode::NORMAL);
//-----------------------
// LEVEL 1 tfrag texture
@ -335,11 +355,12 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
init_bucket_renderer<Tie3WithEnvmapJak1>("l1-tfrag-tie", BucketCategory::TIE,
BucketId::TIE_LEVEL1, 1);
// 17 : MERC_TFRAG_TEX_LEVEL1
init_bucket_renderer<Merc2>("l1-tfrag-merc", BucketCategory::MERC,
BucketId::MERC_TFRAG_TEX_LEVEL1);
init_bucket_renderer<Merc2BucketRenderer>("l1-tfrag-merc", BucketCategory::MERC,
BucketId::MERC_TFRAG_TEX_LEVEL1, m_merc2);
// 18 : GMERC_TFRAG_TEX_LEVEL1
init_bucket_renderer<Generic2>("l1-tfrag-generic", BucketCategory::GENERIC,
BucketId::GENERIC_TFRAG_TEX_LEVEL1, 1500000, 10000, 10000, 800);
init_bucket_renderer<Generic2BucketRenderer>("l1-tfrag-generic", BucketCategory::GENERIC,
BucketId::GENERIC_TFRAG_TEX_LEVEL1, m_generic2,
Generic2::Mode::NORMAL);
//-----------------------
// LEVEL 0 shrub texture
@ -353,8 +374,9 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
// 22 : SHRUB_BILLBOARD_LEVEL0
// 23 : SHRUB_TRANS_LEVEL0
// 24 : SHRUB_GENERIC_LEVEL0
init_bucket_renderer<Generic2>("l0-shrub-generic", BucketCategory::GENERIC,
BucketId::SHRUB_GENERIC_LEVEL0);
init_bucket_renderer<Generic2BucketRenderer>("l0-shrub-generic", BucketCategory::GENERIC,
BucketId::SHRUB_GENERIC_LEVEL0, m_generic2,
Generic2::Mode::NORMAL);
//-----------------------
// LEVEL 1 shrub texture
@ -368,8 +390,9 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
// 28 : SHRUB_BILLBOARD_LEVEL1
// 29 : SHRUB_TRANS_LEVEL1
// 30 : SHRUB_GENERIC_LEVEL1
init_bucket_renderer<Generic2>("l1-shrub-generic", BucketCategory::GENERIC,
BucketId::SHRUB_GENERIC_LEVEL1);
init_bucket_renderer<Generic2BucketRenderer>("l1-shrub-generic", BucketCategory::GENERIC,
BucketId::SHRUB_GENERIC_LEVEL1, m_generic2,
Generic2::Mode::NORMAL);
//-----------------------
// LEVEL 0 alpha texture
@ -405,11 +428,12 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
BucketId::TFRAG_ICE_LEVEL1, ice_tfrags, false, 1);
// 44
init_bucket_renderer<Merc2>("common-alpha-merc", BucketCategory::MERC,
BucketId::MERC_AFTER_ALPHA);
init_bucket_renderer<Merc2BucketRenderer>("common-alpha-merc", BucketCategory::MERC,
BucketId::MERC_AFTER_ALPHA, m_merc2);
init_bucket_renderer<Generic2>("common-alpha-generic", BucketCategory::GENERIC,
BucketId::GENERIC_ALPHA); // 46
init_bucket_renderer<Generic2BucketRenderer>("common-alpha-generic", BucketCategory::GENERIC,
BucketId::GENERIC_ALPHA, m_generic2,
Generic2::Mode::NORMAL); // 46
init_bucket_renderer<ShadowRenderer>("shadow", BucketCategory::OTHER, BucketId::SHADOW); // 47
//-----------------------
@ -417,50 +441,55 @@ void OpenGLRenderer::init_bucket_renderers_jak1() {
//-----------------------
init_bucket_renderer<TextureUploadHandler>("l0-pris-tex", BucketCategory::TEX,
BucketId::PRIS_TEX_LEVEL0); // 48
init_bucket_renderer<Merc2>("l0-pris-merc", BucketCategory::MERC,
BucketId::MERC_PRIS_LEVEL0); // 49
init_bucket_renderer<Generic2>("l0-pris-generic", BucketCategory::GENERIC,
BucketId::GENERIC_PRIS_LEVEL0); // 50
init_bucket_renderer<Merc2BucketRenderer>("l0-pris-merc", BucketCategory::MERC,
BucketId::MERC_PRIS_LEVEL0, m_merc2); // 49
init_bucket_renderer<Generic2BucketRenderer>("l0-pris-generic", BucketCategory::GENERIC,
BucketId::GENERIC_PRIS_LEVEL0, m_generic2,
Generic2::Mode::NORMAL); // 50
//-----------------------
// LEVEL 1 pris texture
//-----------------------
init_bucket_renderer<TextureUploadHandler>("l1-pris-tex", BucketCategory::TEX,
BucketId::PRIS_TEX_LEVEL1); // 51
init_bucket_renderer<Merc2>("l1-pris-merc", BucketCategory::MERC,
BucketId::MERC_PRIS_LEVEL1); // 52
init_bucket_renderer<Generic2>("l1-pris-generic", BucketCategory::GENERIC,
BucketId::GENERIC_PRIS_LEVEL1); // 53
init_bucket_renderer<Merc2BucketRenderer>("l1-pris-merc", BucketCategory::MERC,
BucketId::MERC_PRIS_LEVEL1, m_merc2); // 52
init_bucket_renderer<Generic2BucketRenderer>("l1-pris-generic", BucketCategory::GENERIC,
BucketId::GENERIC_PRIS_LEVEL1, m_generic2,
Generic2::Mode::NORMAL); // 53
// other renderers may output to the eye renderer
m_render_state.eye_renderer = init_bucket_renderer<EyeRenderer>(
"common-pris-eyes", BucketCategory::OTHER, BucketId::MERC_EYES_AFTER_PRIS); // 54
// hack: set to merc2 for debugging
init_bucket_renderer<Merc2>("common-pris-merc", BucketCategory::MERC,
BucketId::MERC_AFTER_PRIS); // 55
init_bucket_renderer<Generic2>("common-pris-generic", BucketCategory::GENERIC,
BucketId::GENERIC_PRIS); // 56
init_bucket_renderer<Merc2BucketRenderer>("common-pris-merc", BucketCategory::MERC,
BucketId::MERC_AFTER_PRIS, m_merc2); // 55
init_bucket_renderer<Generic2BucketRenderer>("common-pris-generic", BucketCategory::GENERIC,
BucketId::GENERIC_PRIS, m_generic2,
Generic2::Mode::NORMAL); // 56
//-----------------------
// LEVEL 0 water texture
//-----------------------
init_bucket_renderer<TextureUploadHandler>("l0-water-tex", BucketCategory::TEX,
BucketId::WATER_TEX_LEVEL0); // 57
init_bucket_renderer<Merc2>("l0-water-merc", BucketCategory::MERC,
BucketId::MERC_WATER_LEVEL0); // 58
init_bucket_renderer<Generic2>("l0-water-generic", BucketCategory::GENERIC,
BucketId::GENERIC_WATER_LEVEL0); // 59
init_bucket_renderer<Merc2BucketRenderer>("l0-water-merc", BucketCategory::MERC,
BucketId::MERC_WATER_LEVEL0, m_merc2); // 58
init_bucket_renderer<Generic2BucketRenderer>("l0-water-generic", BucketCategory::GENERIC,
BucketId::GENERIC_WATER_LEVEL0, m_generic2,
Generic2::Mode::NORMAL); // 59
//-----------------------
// LEVEL 1 water texture
//-----------------------
init_bucket_renderer<TextureUploadHandler>("l1-water-tex", BucketCategory::TEX,
BucketId::WATER_TEX_LEVEL1); // 60
init_bucket_renderer<Merc2>("l1-water-merc", BucketCategory::MERC,
BucketId::MERC_WATER_LEVEL1); // 61
init_bucket_renderer<Generic2>("l1-water-generic", BucketCategory::GENERIC,
BucketId::GENERIC_WATER_LEVEL1); // 62
init_bucket_renderer<Merc2BucketRenderer>("l1-water-merc", BucketCategory::MERC,
BucketId::MERC_WATER_LEVEL1, m_merc2); // 61
init_bucket_renderer<Generic2BucketRenderer>("l1-water-generic", BucketCategory::GENERIC,
BucketId::GENERIC_WATER_LEVEL1, m_generic2,
Generic2::Mode::NORMAL); // 62
init_bucket_renderer<OceanNear>("ocean-near", BucketCategory::OCEAN, BucketId::OCEAN_NEAR); // 63

View file

@ -9,6 +9,8 @@
#include "game/graphics/opengl_renderer/CollideMeshRenderer.h"
#include "game/graphics/opengl_renderer/Profiler.h"
#include "game/graphics/opengl_renderer/Shader.h"
#include "game/graphics/opengl_renderer/foreground/Generic2.h"
#include "game/graphics/opengl_renderer/foreground/Merc2.h"
#include "game/graphics/opengl_renderer/opengl_utils.h"
#include "game/tools/filter_menu/filter_menu.h"
#include "game/tools/subtitles/subtitle_editor.h"
@ -146,6 +148,8 @@ class OpenGLRenderer {
Subtitle2Editor* m_subtitle2_editor = nullptr;
FiltersMenu m_filters_menu;
std::shared_ptr<Merc2> m_merc2;
std::shared_ptr<Generic2> m_generic2;
std::vector<std::unique_ptr<BucketRenderer>> m_bucket_renderers;
std::vector<BucketCategory> m_bucket_categories;

View file

@ -1,11 +1,10 @@
#include "Warp.h"
Warp::Warp(const std::string& name, int id) : BucketRenderer(name, id), m_generic(name, id) {}
Warp::~Warp() {}
Warp::Warp(const std::string& name, int id, std::shared_ptr<Generic2> generic)
: BucketRenderer(name, id), m_generic(generic) {}
void Warp::draw_debug_window() {
m_generic.draw_debug_window();
m_generic->draw_debug_window();
}
void Warp::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
@ -13,11 +12,7 @@ void Warp::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfi
render_state->render_fb_x, render_state->render_fb_y,
render_state->render_fb);
render_state->texture_pool->move_existing_to_vram(m_warp_src_tex, m_tbp);
m_generic.render_in_mode(dma, render_state, prof, Generic2::Mode::NORMAL);
}
void Warp::init_shaders(ShaderLibrary& shaders) {
m_generic.init_shaders(shaders);
m_generic->render_in_mode(dma, render_state, prof, Generic2::Mode::NORMAL);
}
void Warp::init_textures(TexturePool& tex_pool, GameVersion version) {
@ -30,6 +25,4 @@ void Warp::init_textures(TexturePool& tex_pool, GameVersion version) {
in.debug_name = "PC-WARP";
in.id = tex_pool.allocate_pc_port_texture(version);
m_warp_src_tex = tex_pool.give_texture_and_load_to_vram(in, m_tbp);
m_generic.init_textures(tex_pool, version);
}

View file

@ -7,15 +7,13 @@
class Warp : public BucketRenderer {
public:
Warp(const std::string& name, int id);
~Warp();
Warp(const std::string& name, int id, std::shared_ptr<Generic2> generic);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
void init_textures(TexturePool& tex_pool, GameVersion version) override;
private:
Generic2 m_generic;
std::shared_ptr<Generic2> m_generic;
FramebufferCopier m_fb_copier;
GpuTexture* m_warp_src_tex = nullptr;
u32 m_tbp = 1216; // hack, jak 2

View file

@ -4,20 +4,18 @@
#include "third-party/imgui/imgui.h"
Generic2::Generic2(const std::string& name,
int my_id,
Generic2::Generic2(ShaderLibrary& shaders,
u32 num_verts,
u32 num_frags,
u32 num_adgif,
u32 num_buckets)
: BucketRenderer(name, my_id) {
u32 num_buckets) {
m_verts.resize(num_verts);
m_fragments.resize(num_frags);
m_adgifs.resize(num_adgif);
m_buckets.resize(num_buckets);
m_indices.resize(num_verts * 3);
opengl_setup();
opengl_setup(shaders);
}
Generic2::~Generic2() {
@ -52,28 +50,10 @@ void Generic2::draw_debug_window() {
* generic renderer. This renderer is expected to follow the chain until it reaches "next_bucket"
* and then return.
*/
void Generic2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
render_in_mode(dma, render_state, prof, Mode::NORMAL);
}
void Generic2::render_in_mode(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
Mode mode) {
// completely clear out state. These will get populated by the rendering functions, then displayed
// by draw_debug_window() if the user opens that window
m_debug.clear();
m_stats = Stats();
// if the user has asked to disable the renderer, just advance the dma follower to the next
// bucket and return immediately.
if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
return;
}
// Generic2 has 3 passes.
{
// our first pass is to go over the DMA chain from the game and extract the data into buffers

View file

@ -2,16 +2,14 @@
#include "game/graphics/opengl_renderer/BucketRenderer.h"
class Generic2 : public BucketRenderer {
class Generic2 {
public:
Generic2(const std::string& name,
int my_id,
u32 num_verts = 200000,
u32 num_frags = 2000,
u32 num_adgif = 6000,
Generic2(ShaderLibrary& shaders,
u32 num_verts = 500000,
u32 num_frags = 10000,
u32 num_adgif = 10000,
u32 num_buckets = 800);
~Generic2();
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
enum class Mode { NORMAL, LIGHTNING };
@ -20,8 +18,7 @@ class Generic2 : public BucketRenderer {
ScopedProfilerNode& prof,
Mode mode);
void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
void draw_debug_window();
struct Vertex {
math::Vector<float, 3> xyz;
@ -56,7 +53,7 @@ class Generic2 : public BucketRenderer {
void final_vertex_update();
bool handle_bucket_setup_dma(DmaFollower& dma, u32 next_bucket);
void opengl_setup();
void opengl_setup(ShaderLibrary& shaders);
void opengl_cleanup();
void opengl_bind_and_setup_proj(SharedRenderState* render_state);
void setup_opengl_for_draw_mode(const DrawMode& draw_mode,
@ -193,12 +190,6 @@ class Generic2 : public BucketRenderer {
ASSERT(m_next_free_vert < m_verts.size());
}
std::string m_debug;
struct Stats {
u32 dma_tags = 0;
} m_stats;
static constexpr int ALPHA_MODE_COUNT = 7;
bool m_alpha_draw_enable[ALPHA_MODE_COUNT] = {true, true, true, true, true, true, true};

View file

@ -0,0 +1,25 @@
#include "Generic2BucketRenderer.h"
Generic2BucketRenderer::Generic2BucketRenderer(const std::string& name,
int id,
std::shared_ptr<Generic2> renderer,
Generic2::Mode mode)
: BucketRenderer(name, id), m_generic(renderer), m_mode(mode) {}
void Generic2BucketRenderer::draw_debug_window() {
m_generic->draw_debug_window();
}
void Generic2BucketRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
// if the user has asked to disable the renderer, just advance the dma follower to the next
// bucket and return immediately.
if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
return;
}
m_generic->render_in_mode(dma, render_state, prof, m_mode);
}

View file

@ -0,0 +1,18 @@
#pragma once
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/foreground/Generic2.h"
class Generic2BucketRenderer : public BucketRenderer {
public:
Generic2BucketRenderer(const std::string& name,
int id,
std::shared_ptr<Generic2> renderer,
Generic2::Mode mode);
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
void draw_debug_window() override;
private:
std::shared_ptr<Generic2> m_generic;
Generic2::Mode m_mode;
};

View file

@ -16,12 +16,10 @@ bool Generic2::check_for_end_of_generic_data(DmaFollower& dma, u32 next_bucket)
if (dma.current_tag().kind == DmaTag::Kind::CALL) {
for (int i = 0; i < 4; i++) {
dma.read_and_advance();
m_stats.dma_tags++;
}
ASSERT(dma.current_tag_offset() == next_bucket);
return true;
}
m_stats.dma_tags++;
dma.read_and_advance();
}
return false;

View file

@ -2,7 +2,7 @@
#include "Generic2.h"
void Generic2::opengl_setup() {
void Generic2::opengl_setup(ShaderLibrary& shaders) {
// create OpenGL objects
glGenBuffers(1, &m_ogl.vertex_buffer);
glGenBuffers(1, &m_ogl.index_buffer);
@ -56,15 +56,7 @@ void Generic2::opengl_setup() {
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void Generic2::opengl_cleanup() {
glDeleteBuffers(1, &m_ogl.vertex_buffer);
glDeleteBuffers(1, &m_ogl.index_buffer);
glDeleteVertexArrays(1, &m_ogl.vao);
}
void Generic2::init_shaders(ShaderLibrary& shaders) {
const auto& shader = shaders[ShaderId::GENERIC];
auto id = shader.id();
@ -83,6 +75,12 @@ void Generic2::init_shaders(ShaderLibrary& shaders) {
m_ogl.warp_sample_mode = glGetUniformLocation(id, "warp_sample_mode");
}
void Generic2::opengl_cleanup() {
glDeleteBuffers(1, &m_ogl.vertex_buffer);
glDeleteBuffers(1, &m_ogl.index_buffer);
glDeleteVertexArrays(1, &m_ogl.vao);
}
void Generic2::opengl_bind_and_setup_proj(SharedRenderState* render_state) {
render_state->shaders[ShaderId::GENERIC].activate();
glUniform4f(m_ogl.fog_color, render_state->fog_color[0] / 255.f,
@ -224,8 +222,7 @@ void Generic2::setup_opengl_tex(u16 unit,
}
if (!tex) {
lg::warn("Failed to find texture at {}, using random (generic2: {})", tbp_to_lookup,
name_and_id());
lg::warn("Failed to find texture at {}, using random (generic2)", tbp_to_lookup);
tex = render_state->texture_pool->get_placeholder_texture();
}

View file

@ -48,7 +48,7 @@
std::mutex g_merc_data_mutex;
Merc2::Merc2(const std::string& name, int my_id) : BucketRenderer(name, my_id) {
Merc2::Merc2(ShaderLibrary& shaders) {
// Set up main vertex array. This will point to the data stored in the .FR3 level file, and will
// be uploaded to the GPU by the Loader.
glGenVertexArrays(1, &m_vao);
@ -93,6 +93,10 @@ Merc2::Merc2(const std::string& name, int my_id) : BucketRenderer(name, my_id) {
for (auto& x : m_effect_debug_mask) {
x = true;
}
init_shader_common(shaders[ShaderId::MERC2], &m_merc_uniforms, true);
init_shader_common(shaders[ShaderId::EMERC], &m_emerc_uniforms, false);
m_emerc_uniforms.fade = glGetUniformLocation(shaders[ShaderId::EMERC].id(), "fade");
}
Merc2::~Merc2() {
@ -161,7 +165,8 @@ void Merc2::model_mod_blerc_draws(int num_effects,
const tfrag3::MercModel* model,
const LevelData* lev,
ModBuffers* mod_opengl_buffers,
const float* blerc_weights) {
const float* blerc_weights,
MercDebugStats* stats) {
// loop over effects.
for (int ei = 0; ei < num_effects; ei++) {
const auto& effect = model->effects[ei];
@ -192,8 +197,8 @@ void Merc2::model_mod_blerc_draws(int num_effects,
blerc_avx(i_data, i_data_end, f_data, blerc_weights, m_mod_vtx_temp.data(), blerc_multiplier);
// and upload to GPU
m_stats.num_uploads++;
m_stats.num_upload_bytes += effect.mod.vertices.size() * sizeof(tfrag3::MercVertex);
stats->num_uploads++;
stats->num_upload_bytes += effect.mod.vertices.size() * sizeof(tfrag3::MercVertex);
{
glBindBuffer(GL_ARRAY_BUFFER, opengl_buffers.vertex);
glBufferData(GL_ARRAY_BUFFER, effect.mod.vertices.size() * sizeof(tfrag3::MercVertex),
@ -212,7 +217,8 @@ void Merc2::model_mod_draws(int num_effects,
const LevelData* lev,
const u8* input_data,
const DmaTransfer& setup,
ModBuffers* mod_opengl_buffers) {
ModBuffers* mod_opengl_buffers,
MercDebugStats* stats) {
auto p = scoped_prof("update-verts");
// loop over effects. Mod vertices are done per effect (possibly a bad idea?)
@ -368,8 +374,8 @@ void Merc2::model_mod_draws(int num_effects,
}
// and upload to GPU
m_stats.num_uploads++;
m_stats.num_upload_bytes += effect.mod.vertices.size() * sizeof(tfrag3::MercVertex);
stats->num_uploads++;
stats->num_upload_bytes += effect.mod.vertices.size() * sizeof(tfrag3::MercVertex);
{
auto pp = scoped_prof("update-verts-upload");
glBindBuffer(GL_ARRAY_BUFFER, opengl_buffers.vertex);
@ -384,7 +390,8 @@ void Merc2::model_mod_draws(int num_effects,
*/
void Merc2::handle_pc_model(const DmaTransfer& setup,
SharedRenderState* render_state,
ScopedProfilerNode& proff) {
ScopedProfilerNode& proff,
MercDebugStats* stats) {
auto p = scoped_prof("init-pc");
// the format of the data is:
@ -409,7 +416,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
auto model_ref = render_state->loader->get_merc_model(name);
if (!model_ref) {
// it can fail, if the game is faster than the loader. In this case, we just don't draw.
m_stats.num_missing_models++;
stats->num_missing_models++;
return;
}
@ -420,7 +427,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// each model uses only 1 light.
if (m_next_free_light >= MAX_LIGHTS) {
fmt::print("MERC2 out of lights, consider increasing MAX_LIGHTS\n");
flush_draw_buckets(render_state, proff);
flush_draw_buckets(render_state, proff, stats);
}
// models use many bones. First check if we need to flush:
@ -428,7 +435,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
if (m_next_free_bone_vector + m_opengl_buffer_alignment + bone_count * 8 >
MAX_SHADER_BONE_VECTORS) {
fmt::print("MERC2 out of bones, consider increasing MAX_SHADER_BONE_VECTORS\n");
flush_draw_buckets(render_state, proff);
flush_draw_buckets(render_state, proff, stats);
}
// also sanity check that we have enough to draw the model
@ -453,7 +460,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
if (m_next_free_level_bucket >= m_level_draw_buckets.size()) {
// out of room, flush
// fmt::print("MERC2 out of levels, consider increasing MAX_LEVELS\n");
flush_draw_buckets(render_state, proff);
flush_draw_buckets(render_state, proff, stats);
}
// alloc a new one
lev_bucket = &m_level_draw_buckets[m_next_free_level_bucket++];
@ -465,7 +472,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
if (lev_bucket->next_free_draw + model->max_draws >= lev_bucket->draws.size()) {
// out of room, flush
fmt::print("MERC2 out of draws, consider increasing MAX_DRAWS_PER_LEVEL\n");
flush_draw_buckets(render_state, proff);
flush_draw_buckets(render_state, proff, stats);
if (model->max_draws >= lev_bucket->draws.size()) {
ASSERT_NOT_REACHED_MSG("MERC2 draw buffer not big enough");
}
@ -475,7 +482,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
if (lev_bucket->next_free_envmap_draw + model->max_draws >= lev_bucket->envmap_draws.size()) {
// out of room, flush
fmt::print("MERC2 out of envmap draws, consider increasing MAX_ENVMAP_DRAWS_PER_LEVEL\n");
flush_draw_buckets(render_state, proff);
flush_draw_buckets(render_state, proff, stats);
if (model->max_draws >= lev_bucket->envmap_draws.size()) {
ASSERT_NOT_REACHED_MSG("MERC2 envmap draw buffer not big enough");
}
@ -550,31 +557,31 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// will hold opengl buffers for the updated vertices
ModBuffers mod_opengl_buffers[kMaxEffect];
if (model_uses_pc_blerc) {
model_mod_blerc_draws(num_effects, model, lev, mod_opengl_buffers, blerc_weights);
model_mod_blerc_draws(num_effects, model, lev, mod_opengl_buffers, blerc_weights, stats);
} else if (model_uses_mod) { // only if we've enabled, this path is slow.
model_mod_draws(num_effects, model, lev, input_data, setup, mod_opengl_buffers);
model_mod_draws(num_effects, model, lev, input_data, setup, mod_opengl_buffers, stats);
}
// stats
m_stats.num_models++;
stats->num_models++;
for (const auto& effect : model_ref->model->effects) {
bool envmap = effect.has_envmap;
m_stats.num_effects++;
m_stats.num_predicted_draws += effect.all_draws.size();
stats->num_effects++;
stats->num_predicted_draws += effect.all_draws.size();
if (envmap) {
m_stats.num_envmap_effects++;
m_stats.num_predicted_draws += effect.all_draws.size();
stats->num_envmap_effects++;
stats->num_predicted_draws += effect.all_draws.size();
}
for (const auto& draw : effect.all_draws) {
m_stats.num_predicted_tris += draw.num_triangles;
stats->num_predicted_tris += draw.num_triangles;
if (envmap) {
m_stats.num_predicted_tris += draw.num_triangles;
stats->num_predicted_tris += draw.num_triangles;
}
}
}
if (m_debug_mode) {
auto& d = m_debug.model_list.emplace_back();
if (stats->collect_debug_model_list) {
auto& d = stats->model_list.emplace_back();
d.name = model->name;
d.level = model_ref->level->level->level_name;
for (auto& e : model->effects) {
@ -594,6 +601,7 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
// allocate lights
u32 lights = alloc_lights(current_lights);
stats->num_lights++;
// loop over effects, creating draws for each
for (size_t ei = 0; ei < model->effects.size(); ei++) {
@ -657,31 +665,31 @@ void Merc2::handle_pc_model(const DmaTransfer& setup,
}
}
void Merc2::draw_debug_window() {
ImGui::Text("Models : %d", m_stats.num_models);
ImGui::Text("Effects : %d", m_stats.num_effects);
ImGui::Text("Draws (p): %d", m_stats.num_predicted_draws);
ImGui::Text("Tris (p): %d", m_stats.num_predicted_tris);
ImGui::Text("Bones : %d", m_stats.num_bones_uploaded);
ImGui::Text("Lights : %d", m_stats.num_lights);
ImGui::Text("Dflush : %d", m_stats.num_draw_flush);
void Merc2::draw_debug_window(MercDebugStats* stats) {
ImGui::Text("Models : %d", stats->num_models);
ImGui::Text("Effects : %d", stats->num_effects);
ImGui::Text("Draws (p): %d", stats->num_predicted_draws);
ImGui::Text("Tris (p): %d", stats->num_predicted_tris);
ImGui::Text("Bones : %d", stats->num_bones_uploaded);
ImGui::Text("Lights : %d", stats->num_lights);
ImGui::Text("Dflush : %d", stats->num_draw_flush);
ImGui::Text("EEffects : %d", m_stats.num_envmap_effects);
ImGui::Text("ETris : %d", m_stats.num_envmap_tris);
ImGui::Text("EEffects : %d", stats->num_envmap_effects);
ImGui::Text("ETris : %d", stats->num_envmap_tris);
ImGui::Text("Uploads : %d", m_stats.num_uploads);
ImGui::Text("Upload kB: %d", m_stats.num_upload_bytes / 1024);
ImGui::Text("Uploads : %d", stats->num_uploads);
ImGui::Text("Upload kB: %d", stats->num_upload_bytes / 1024);
ImGui::Checkbox("Debug", &m_debug_mode);
ImGui::Checkbox("Debug", &stats->collect_debug_model_list);
ImGui::SliderFloat("blerc-nightmare", &blerc_multiplier, -3, 3);
if (m_debug_mode) {
if (stats->collect_debug_model_list) {
for (int i = 0; i < kMaxEffect; i++) {
ImGui::Checkbox(fmt::format("e{:02d}", i).c_str(), &m_effect_debug_mask[i]);
}
for (const auto& model : m_debug.model_list) {
for (const auto& model : stats->model_list) {
if (ImGui::TreeNode(model.name.c_str())) {
ImGui::Text("Level: %s\n", model.level.c_str());
for (const auto& e : model.effects) {
@ -696,12 +704,6 @@ void Merc2::draw_debug_window() {
}
}
void Merc2::init_shaders(ShaderLibrary& shaders) {
init_shader_common(shaders[ShaderId::MERC2], &m_merc_uniforms, true);
init_shader_common(shaders[ShaderId::EMERC], &m_emerc_uniforms, false);
m_emerc_uniforms.fade = glGetUniformLocation(shaders[ShaderId::EMERC].id(), "fade");
}
void Merc2::init_shader_common(Shader& shader, Uniforms* uniforms, bool include_lights) {
auto id = shader.id();
shader.activate();
@ -749,37 +751,32 @@ void Merc2::switch_to_emerc(SharedRenderState* render_state) {
/*!
* Main merc2 rendering.
*/
void Merc2::render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) {
m_stats = {};
if (m_debug_mode) {
m_debug = {};
void Merc2::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
MercDebugStats* stats) {
*stats = {};
if (stats->collect_debug_model_list) {
stats->model_list.clear();
}
// skip if disabled
if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
return;
}
switch_to_merc2(render_state);
{
auto pp = scoped_prof("handle-all-dma");
// iterate through the dma chain, filling buckets
handle_all_dma(dma, render_state, prof);
handle_all_dma(dma, render_state, prof, stats);
}
{
auto pp = scoped_prof("flush-buckets");
// flush buckets to draws
flush_draw_buckets(render_state, prof);
flush_draw_buckets(render_state, prof, stats);
}
}
u32 Merc2::alloc_lights(const VuLights& lights) {
ASSERT(m_next_free_light < MAX_LIGHTS);
m_stats.num_lights++;
u32 light_idx = m_next_free_light;
m_lights_buffer[m_next_free_light++] = lights;
static_assert(sizeof(VuLights) == 7 * 16);
@ -797,7 +794,8 @@ std::string Merc2::ShaderMercMat::to_string() const {
*/
void Merc2::handle_all_dma(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
ScopedProfilerNode& prof,
MercDebugStats* stats) {
// process the first tag. this is just jumping to the merc-specific dma.
auto data0 = dma.read_and_advance();
ASSERT(data0.vif1() == 0 || data0.vifcode1().kind == VifCode::Kind::NOP);
@ -822,7 +820,7 @@ void Merc2::handle_all_dma(DmaFollower& dma,
// handle each merc transfer
while (dma.current_tag_offset() != render_state->next_bucket) {
handle_merc_chain(dma, render_state, prof);
handle_merc_chain(dma, render_state, prof, stats);
}
ASSERT(dma.current_tag_offset() == render_state->next_bucket);
}
@ -930,7 +928,8 @@ bool tag_is_nothing_next(const DmaFollower& dma) {
void Merc2::handle_merc_chain(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
ScopedProfilerNode& prof,
MercDebugStats* stats) {
while (tag_is_nothing_next(dma)) {
auto nothing = dma.read_and_advance();
ASSERT(nothing.size_bytes == 0);
@ -950,7 +949,7 @@ void Merc2::handle_merc_chain(DmaFollower& dma,
while (init.vifcode1().kind == VifCode::Kind::PC_PORT) {
// flush_pending_model(render_state, prof);
handle_pc_model(init, render_state, prof);
handle_pc_model(init, render_state, prof, stats);
for (int i = 0; i < skip_count; i++) {
auto link = dma.read_and_advance();
ASSERT(link.vifcode0().kind == VifCode::Kind::NOP);
@ -1157,8 +1156,10 @@ void Merc2::setup_merc_vao() {
);
}
void Merc2::flush_draw_buckets(SharedRenderState* render_state, ScopedProfilerNode& prof) {
m_stats.num_draw_flush++;
void Merc2::flush_draw_buckets(SharedRenderState* render_state,
ScopedProfilerNode& prof,
MercDebugStats* stats) {
stats->num_draw_flush++;
for (u32 li = 0; li < m_next_free_level_bucket; li++) {
const auto& lev_bucket = m_level_draw_buckets[li];
const auto* lev = lev_bucket.level;
@ -1166,7 +1167,7 @@ void Merc2::flush_draw_buckets(SharedRenderState* render_state, ScopedProfilerNo
glBindBuffer(GL_ARRAY_BUFFER, lev->merc_vertices);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lev->merc_indices);
setup_merc_vao();
m_stats.num_bones_uploaded += m_next_free_bone_vector;
stats->num_bones_uploaded += m_next_free_bone_vector;
glBindBuffer(GL_UNIFORM_BUFFER, m_bones_buffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, m_next_free_bone_vector * sizeof(math::Vector4f),

View file

@ -1,17 +1,23 @@
#pragma once
#include "game/graphics/opengl_renderer/BucketRenderer.h"
class Merc2 : public BucketRenderer {
public:
Merc2(const std::string& name, int my_id);
~Merc2();
void draw_debug_window() override;
void init_shaders(ShaderLibrary& shaders) override;
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
static constexpr int kMaxBlerc = 40;
struct MercDebugStats {
int num_models = 0;
int num_missing_models = 0;
int num_chains = 0;
int num_effects = 0;
int num_predicted_draws = 0;
int num_predicted_tris = 0;
int num_bones_uploaded = 0;
int num_lights = 0;
int num_draw_flush = 0;
int num_envmap_effects = 0;
int num_envmap_tris = 0;
int num_upload_bytes = 0;
int num_uploads = 0;
private:
bool m_debug_mode = false;
struct DrawDebug {
DrawMode mode;
int num_tris;
@ -26,9 +32,24 @@ class Merc2 : public BucketRenderer {
std::string level;
std::vector<EffectDebug> effects;
};
struct {
std::vector<ModelDebug> model_list;
} m_debug;
std::vector<ModelDebug> model_list;
bool collect_debug_model_list = false;
};
class Merc2 {
public:
Merc2(ShaderLibrary& shaders);
~Merc2();
void draw_debug_window(MercDebugStats* stats);
void render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
MercDebugStats* stats);
static constexpr int kMaxBlerc = 40;
private:
enum MercDataMemory {
LOW_MEMORY = 0,
BUFFER_BASE = 442,
@ -60,7 +81,8 @@ class Merc2 : public BucketRenderer {
void handle_pc_model(const DmaTransfer& setup,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
ScopedProfilerNode& prof,
MercDebugStats* stats);
u32 alloc_lights(const VuLights& lights);
struct ModBuffers {
@ -118,10 +140,14 @@ class Merc2 : public BucketRenderer {
void init_shader_common(Shader& shader, Uniforms* uniforms, bool include_lights);
void handle_setup_dma(DmaFollower& dma, SharedRenderState* render_state);
void handle_all_dma(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof);
void handle_all_dma(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof,
MercDebugStats* stats);
void handle_merc_chain(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof);
ScopedProfilerNode& prof,
MercDebugStats* stats);
void switch_to_merc2(SharedRenderState* render_state);
void switch_to_emerc(SharedRenderState* render_state);
@ -147,24 +173,6 @@ class Merc2 : public BucketRenderer {
GLuint m_bones_buffer;
struct Stats {
int num_models = 0;
int num_missing_models = 0;
int num_chains = 0;
int num_effects = 0;
int num_predicted_draws = 0;
int num_predicted_tris = 0;
int num_bones_uploaded = 0;
int num_lights = 0;
int num_draw_flush = 0;
int num_envmap_effects = 0;
int num_envmap_tris = 0;
int num_upload_bytes = 0;
int num_uploads = 0;
} m_stats;
enum DrawFlags {
IGNORE_ALPHA = 1,
MOD_VTX = 2,
@ -230,16 +238,20 @@ class Merc2 : public BucketRenderer {
u32 m_next_free_bone_vector = 0;
size_t m_opengl_buffer_alignment = 0;
void flush_draw_buckets(SharedRenderState* render_state, ScopedProfilerNode& prof);
void flush_draw_buckets(SharedRenderState* render_state,
ScopedProfilerNode& prof,
MercDebugStats* stats);
void model_mod_draws(int num_effects,
const tfrag3::MercModel* model,
const LevelData* lev,
const u8* input_data,
const DmaTransfer& setup,
ModBuffers* mod_opengl_buffers);
ModBuffers* mod_opengl_buffers,
MercDebugStats* stats);
void model_mod_blerc_draws(int num_effects,
const tfrag3::MercModel* model,
const LevelData* lev,
ModBuffers* mod_opengl_buffers,
const float* blerc_weights);
const float* blerc_weights,
MercDebugStats* stats);
};

View file

@ -0,0 +1,24 @@
#include "Merc2BucketRenderer.h"
Merc2BucketRenderer::Merc2BucketRenderer(const std::string& name,
int my_id,
std::shared_ptr<Merc2> merc)
: BucketRenderer(name, my_id), m_renderer(merc) {}
void Merc2BucketRenderer::render(DmaFollower& dma,
SharedRenderState* render_state,
ScopedProfilerNode& prof) {
// skip if disabled
if (!m_enabled) {
while (dma.current_tag_offset() != render_state->next_bucket) {
dma.read_and_advance();
}
return;
}
m_renderer->render(dma, render_state, prof, &m_debug_stats);
}
void Merc2BucketRenderer::draw_debug_window() {
m_renderer->draw_debug_window(&m_debug_stats);
}

View file

@ -0,0 +1,15 @@
#pragma once
#include "game/graphics/opengl_renderer/BucketRenderer.h"
#include "game/graphics/opengl_renderer/foreground/Merc2.h"
class Merc2BucketRenderer : public BucketRenderer {
public:
Merc2BucketRenderer(const std::string& name, int my_id, std::shared_ptr<Merc2> merc);
void draw_debug_window() override;
void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override;
private:
std::shared_ptr<Merc2> m_renderer;
MercDebugStats m_debug_stats;
};

View file

@ -161,6 +161,7 @@ void Loader::draw_debug_window() {
void Loader::loader_thread() {
try {
while (!m_want_shutdown) {
prof().root_event();
std::unique_lock<std::mutex> lk(m_loader_mutex);
// this will keep us asleep until we've got a level to load.
@ -176,39 +177,57 @@ void Loader::loader_thread() {
// std::this_thread::sleep_for(std::chrono::milliseconds(1500));
// load the fr3 file
prof().begin_event("read-file");
Timer disk_timer;
auto data =
file_util::read_binary_file(m_base_path / fmt::format("{}.fr3", uppercase_string(lev)));
double disk_load_time = disk_timer.getSeconds();
prof().end_event();
// the FR3 files are compressed
prof().begin_event("decompress-file");
Timer decomp_timer;
auto decomp_data = compression::decompress_zstd(data.data(), data.size());
double decomp_time = decomp_timer.getSeconds();
prof().end_event();
// Read back into the tfrag3::Level structure
prof().begin_event("deserialize");
Timer import_timer;
auto result = std::make_unique<tfrag3::Level>();
Serializer ser(decomp_data.data(), decomp_data.size());
result->serialize(ser);
double import_time = import_timer.getSeconds();
prof().end_event();
// and finally "unpack", which creates the vertex data we'll upload to the GPU
Timer unpack_timer;
for (auto& tie_tree : result->tie_trees) {
for (auto& tree : tie_tree) {
tree.unpack();
}
}
for (auto& t_tree : result->tfrag_trees) {
for (auto& tree : t_tree) {
tree.unpack();
{
auto p = scoped_prof("tie-unpack");
for (auto& tie_tree : result->tie_trees) {
for (auto& tree : tie_tree) {
tree.unpack();
}
}
}
for (auto& shrub_tree : result->shrub_trees) {
shrub_tree.unpack();
{
auto p = scoped_prof("tfrag-unpack");
for (auto& t_tree : result->tfrag_trees) {
for (auto& tree : t_tree) {
tree.unpack();
}
}
}
{
auto p = scoped_prof("shrub-unpack");
for (auto& shrub_tree : result->shrub_trees) {
shrub_tree.unpack();
}
}
fmt::print(
"------------> Load from file: {:.3f}s, import {:.3f}s, decomp {:.3f}s unpack {:.3f}s\n",
disk_load_time, import_time, decomp_time, unpack_timer.getSeconds());

View file

@ -97,7 +97,7 @@ static int gl_init(GfxGlobalSettings& settings) {
auto p = scoped_prof("startup::sdl::init_sdl");
// remove SDL garbage from hooking signal handler.
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) != 0) {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) != 0) {
sdl_util::log_error("Could not initialize SDL, exiting");
return 1;
}

View file

@ -1,5 +1,6 @@
#include "kdgo.h"
#include "common/global_profiler/GlobalProfiler.h"
#include "common/log/log.h"
#include "game/kernel/common/Ptr.h"
@ -105,7 +106,10 @@ void load_and_link_dgo_from_c(const char* name,
lg::debug("[link and exec] {:18s} {} {:6d} heap-use {:8d} {:8d}: 0x{:x}", objName,
lastObjectLoaded, objSize, kheapused(kglobalheap),
kdebugheap.offset ? kheapused(kdebugheap) : 0, kglobalheap->current.offset);
link_and_exec(obj, objName, objSize, heap, linkFlag, jump_from_c_to_goal); // link now!
{
auto p = scoped_prof(fmt::format("link-{}", objName).c_str());
link_and_exec(obj, objName, objSize, heap, linkFlag, jump_from_c_to_goal); // link now!
}
// inform IOP we are done
if (!lastObjectLoaded) {

View file

@ -5,6 +5,7 @@
#include <stdexcept>
#include <string>
#include "common/global_profiler/GlobalProfiler.h"
#include "common/log/log.h"
#include "common/symbols.h"
#include "common/util/FileUtil.h"
@ -400,7 +401,10 @@ int InitMachine() {
InitRPC();
reset_output();
clear_print();
prof().begin_event("init-heap-and-symbol");
auto status = InitHeapAndSymbol();
prof().end_event();
if (status >= 0) {
printf("InitListenerConnect\n");
InitListenerConnect();
@ -774,9 +778,13 @@ void InitMachineScheme() {
intern_from_c("*kernel-boot-art-group*")->value() = make_string_from_c(DebugBootArtGroup);
if (DiskBoot) {
*EnableMethodSet = *EnableMethodSet + 1;
load_and_link_dgo_from_c("game", kglobalheap,
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
0x400000, true);
{
auto p = scoped_prof("load-game-dgo");
load_and_link_dgo_from_c("game", kglobalheap,
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
0x400000, true);
}
*EnableMethodSet = *EnableMethodSet + -1;
using namespace jak2_symbols;
kernel_packages->value() =
@ -789,6 +797,7 @@ void InitMachineScheme() {
new_pair(s7.offset + FIX_SYM_GLOBAL_HEAP, *((s7 + FIX_SYM_PAIR_TYPE - 1).cast<u32>()),
make_string_from_c("common"), kernel_packages->value());
printf("calling play-boot!\n");
auto p = scoped_prof("play-boot-func");
call_goal_function_by_name("play-boot"); // new function for jak2!
}
}

View file

@ -5,6 +5,7 @@
#include <cstring>
#include "common/common_types.h"
#include "common/global_profiler/GlobalProfiler.h"
#include "common/goal_constants.h"
#include "common/log/log.h"
#include "common/symbols.h"
@ -1718,6 +1719,7 @@ int InitHeapAndSymbol() {
// load kernel!
if (MasterUseKernel) {
auto p = scoped_prof("load-kernel-dgo");
*EnableMethodSet = *EnableMethodSet + 1;
load_and_link_dgo_from_c("kernel", kglobalheap,
LINK_FLAG_OUTPUT_LOAD | LINK_FLAG_EXECUTE | LINK_FLAG_PRINT_LOGIN,
@ -1747,7 +1749,10 @@ int InitHeapAndSymbol() {
InitListener();
// Do final initialization, including loading and initializing the engine.
InitMachineScheme();
{
auto p = scoped_prof("init-machine-scheme");
InitMachineScheme();
}
kmemclose();
return 0;
}

View file

@ -142,6 +142,7 @@ void deci2_runner(SystemThreadInterface& iface) {
* SystemThread Function for the EE (PS2 Main CPU)
*/
void ee_runner(SystemThreadInterface& iface) {
prof().root_event();
// Allocate Main RAM. Must have execute enabled.
if (EE_MEM_LOW_MAP) {
g_ee_main_mem =

View file

@ -63,35 +63,55 @@
;; each representing the name of a vag stream. convert using alloc-vag-list
(define *vagdir-names-list* (alloc-vagdir-names 'debug))
(defun sort-string-array ((arr (array string)) (compare-func (function string string object)))
"Sort an array, using compare-func to compare elements.
The comparison function can return either an integer or a true/false.
For integers, use a positive number to represent first > second
Ex: (sort lst -) will sort in ascending order
For booleans, you must explicitly use #t and not a truthy value.
Ex: (sort my-list (lambda ((x int) (y int)) (< x y))) will sort ascending.
NOTE: if you use an integer, don't accidentally return #t"
(let ((sorted -1))
(while (nonzero? sorted)
(set! sorted 0)
(dotimes (i (1- (-> arr allocated-length)))
(let* ((cur (-> arr i))
(next (-> arr (1+ i)))
(result (compare-func cur next))
)
(when (and (or (not result) (> (the-as int result) 0)) (!= result #t))
(+! sorted 1)
(set! (-> arr i) next)
(set! (-> arr (1+ i)) cur)
)
)
)
(defun strcmp ((a string) (b string))
"C-style strcmp. Unlike GOAL's string comparison functions, these actually work:
(strcmp 'ab' 'a') and (strcmp 'a' 'ab') give the opposite result."
(let ((a-ptr (-> a data))
(b-ptr (-> b data)))
(while (and (nonzero? (-> a-ptr))
(= (-> a-ptr) (-> b-ptr)))
(&+! a-ptr 1)
(&+! b-ptr 1)
)
(- (the int (-> a-ptr)) (the int (-> b-ptr)))
)
)
(defun string-quicksort-partition ((arr (array string)) (lo int) (hi int))
(let ((pivot (-> arr hi))
(i (- lo 1))
(j lo)
)
(while (< j hi)
(when (< (strcmp (-> arr j) pivot) 0)
(+! i 1)
(swap! (-> arr i) (-> arr j))
)
(+! j 1)
)
(+! i 1)
(swap! (-> arr i) (-> arr hi))
i
)
)
(defun-recursive string-quicksort-run (array string) ((arr (array string)) (lo int) (hi int))
(when (or (>= lo hi) (< lo 0))
(return arr)
)
(let ((p (string-quicksort-partition arr lo hi)))
(string-quicksort-run arr lo (- p 1))
(string-quicksort-run arr (+ p 1) hi)
)
arr
)
(defun string-quicksort ((arr (array string)))
"Sort an array of strings alphabetically using quicksort.
This is about 100x faster than the normal GOAL sort."
(string-quicksort-run arr 0 (- (-> arr length) 1))
)
(defun alloc-vag-list ()
"allocates and returns a boxed array with all of the vag names as strings, sorted"
@ -112,7 +132,7 @@
(set! (-> list i) (new 'debug 'string 0 *temp-string*)))
;; return the allocated, filled and sorted array
(sort-string-array list string<=?))
(string-quicksort list))
)
@ -179,7 +199,7 @@
(defstate vag-player-playing (vag-player)
:event (behavior ((from process) (argc int) (msg symbol) (block event-message-block))
(case msg
(('play)
@ -195,13 +215,13 @@
#t)
)
)
:enter (behavior ((index int))
(set! (-> self master-mode) *master-mode*)
(set! (-> self debug-menu-hidden) (-> *debug-menu-context* is-hidden))
(set! (-> self display-art-control) *display-art-control*)
(set! (-> self gui-kick-str) *gui-kick-str*)
(set-master-mode 'menu) ;; put us in menu mode first
(true! *display-art-control*) ;; force this on
(true! (-> *debug-menu-context* is-hidden)) ;; hide debug menu
@ -309,7 +329,7 @@
(set! (-> self old-speed) (-> self speed))))
(suspend))
)
(go vag-player-idle)
(none))
)
@ -382,14 +402,14 @@
"play a vag from its index in the vag list"
(if (not *vag-player*)
(vag-player-start))
(send-event (ppointer->process *vag-player*) 'play-index index))
(defun vag-player-play-from-name ((name string))
"play a vag from its name"
(if (not *vag-player*)
(vag-player-start))
(send-event (ppointer->process *vag-player*) 'play name))
(defun vag-list-to-file ((file-name string))