diff --git a/common/custom_data/TFrag3Data.cpp b/common/custom_data/TFrag3Data.cpp index 24d316589e..9b1e9b1474 100644 --- a/common/custom_data/TFrag3Data.cpp +++ b/common/custom_data/TFrag3Data.cpp @@ -3,6 +3,8 @@ #include #include +#include "xmmintrin.h" + #include "common/util/Assert.h" namespace tfrag3 { @@ -166,6 +168,7 @@ std::array tie_normal_transform_v2(const std::array& 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& 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++; + } } } } diff --git a/common/custom_data/Tfrag3Data.h b/common/custom_data/Tfrag3Data.h index 4fa0f302c8..6a30802713 100644 --- a/common/custom_data/Tfrag3Data.h +++ b/common/custom_data/Tfrag3Data.h @@ -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 color_indices; diff --git a/decompiler/level_extractor/extract_tie.cpp b/decompiler/level_extractor/extract_tie.cpp index abb22c4201..e247cc5850 100644 --- a/decompiler/level_extractor/extract_tie.cpp +++ b/decompiler/level_extractor/extract_tie.cpp @@ -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& grps) { std::swap(result, grps); } +void merge_groups(std::vector& grps) { + std::vector 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& 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)); } diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt index e34cbdfee9..70fec8ec06 100644 --- a/game/CMakeLists.txt +++ b/game/CMakeLists.txt @@ -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 diff --git a/game/graphics/opengl_renderer/LightningRenderer.cpp b/game/graphics/opengl_renderer/LightningRenderer.cpp deleted file mode 100644 index d821a18b42..0000000000 --- a/game/graphics/opengl_renderer/LightningRenderer.cpp +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/game/graphics/opengl_renderer/LightningRenderer.h b/game/graphics/opengl_renderer/LightningRenderer.h deleted file mode 100644 index df3c5e4625..0000000000 --- a/game/graphics/opengl_renderer/LightningRenderer.h +++ /dev/null @@ -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; -}; diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.cpp b/game/graphics/opengl_renderer/OpenGLRenderer.cpp index e89786b089..21558f27bd 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.cpp +++ b/game/graphics/opengl_renderer/OpenGLRenderer.cpp @@ -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 texture_pool, lg::debug("OpenGL context information: {}", (const char*)glGetString(GL_VERSION)); + m_merc2 = std::make_shared(m_render_state.shaders); + m_generic2 = std::make_shared(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( + init_bucket_renderer( 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( + GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_TFRAG, BucketId::MERC_L1_TFRAG, i), m_merc2); + init_bucket_renderer( 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( fmt::format("tex-l{}-shrub", i), BucketCategory::TEX, @@ -134,12 +139,13 @@ void OpenGLRenderer::init_bucket_renderers_jak2() { init_bucket_renderer( 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( + init_bucket_renderer( 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( + GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_SHRUB, BucketId::MERC_L1_SHRUB, i), m_merc2); + init_bucket_renderer( 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( 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( + init_bucket_renderer( 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( + GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_ALPHA, BucketId::MERC_L1_ALPHA, i), m_merc2); + init_bucket_renderer( 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( 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( + init_bucket_renderer( 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( + GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS, BucketId::MERC_L1_PRIS, i), m_merc2); + init_bucket_renderer( 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( 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( + init_bucket_renderer( 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( + GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_PRIS2, BucketId::MERC_L1_PRIS2, i), m_merc2); + init_bucket_renderer( 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( 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( + init_bucket_renderer( 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( + GET_BUCKET_ID_FOR_LIST(BucketId::MERC_L0_WATER, BucketId::MERC_L1_WATER, i), m_merc2); + init_bucket_renderer( 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( 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("tex-lcom-tfrag", BucketCategory::TEX, BucketId::TEX_LCOM_TFRAG); - init_bucket_renderer("merc-lcom-tfrag", BucketCategory::MERC, BucketId::MERC_LCOM_TFRAG); + init_bucket_renderer("merc-lcom-tfrag", BucketCategory::MERC, + BucketId::MERC_LCOM_TFRAG, m_merc2); // 190 init_bucket_renderer("tex-lcom-shrub", BucketCategory::TEX, BucketId::TEX_LCOM_SHRUB); - init_bucket_renderer("merc-lcom-shrub", BucketCategory::MERC, BucketId::MERC_LCOM_SHRUB); - init_bucket_renderer("gmerc-lcom-tfrag", BucketCategory::GENERIC, - BucketId::GMERC_LCOM_TFRAG); + init_bucket_renderer("merc-lcom-shrub", BucketCategory::MERC, + BucketId::MERC_LCOM_SHRUB, m_merc2); + init_bucket_renderer("gmerc-lcom-tfrag", BucketCategory::GENERIC, + BucketId::GMERC_LCOM_TFRAG, m_generic2, + Generic2::Mode::NORMAL); init_bucket_renderer("shadow", BucketCategory::OTHER, BucketId::SHADOW); // 220 init_bucket_renderer("tex-lcom-pris", BucketCategory::TEX, BucketId::TEX_LCOM_PRIS); - init_bucket_renderer("merc-lcom-pris", BucketCategory::MERC, BucketId::MERC_LCOM_PRIS); + init_bucket_renderer("merc-lcom-pris", BucketCategory::MERC, + BucketId::MERC_LCOM_PRIS, m_merc2); init_bucket_renderer("tex-lcom-water", BucketCategory::TEX, BucketId::TEX_LCOM_WATER); - init_bucket_renderer("merc-lcom-water", BucketCategory::MERC, BucketId::MERC_LCOM_WATER); + init_bucket_renderer("merc-lcom-water", BucketCategory::MERC, + BucketId::MERC_LCOM_WATER, m_merc2); init_bucket_renderer("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("particles", BucketCategory::SPRITE, BucketId::PARTICLES); init_bucket_renderer("shadow2", BucketCategory::OTHER, BucketId::SHADOW2); - init_bucket_renderer("effects", BucketCategory::OTHER, BucketId::EFFECTS); + init_bucket_renderer("effects", BucketCategory::OTHER, BucketId::EFFECTS, + m_generic2, Generic2::Mode::LIGHTNING); init_bucket_renderer("tex-all-warp", BucketCategory::TEX, BucketId::TEX_ALL_WARP); - init_bucket_renderer("warp", BucketCategory::GENERIC, BucketId::GMERC_WARP); + init_bucket_renderer("warp", BucketCategory::GENERIC, BucketId::GMERC_WARP, m_generic2); init_bucket_renderer("debug-no-zbuf1", BucketCategory::OTHER, BucketId::DEBUG_NO_ZBUF1, 0x8000); init_bucket_renderer("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(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(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("l0-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL0, 0); // 10 : MERC_TFRAG_TEX_LEVEL0 - init_bucket_renderer("l0-tfrag-merc", BucketCategory::MERC, - BucketId::MERC_TFRAG_TEX_LEVEL0); + init_bucket_renderer("l0-tfrag-merc", BucketCategory::MERC, + BucketId::MERC_TFRAG_TEX_LEVEL0, m_merc2); // 11 : GMERC_TFRAG_TEX_LEVEL0 - init_bucket_renderer("l0-tfrag-generic", BucketCategory::GENERIC, - BucketId::GENERIC_TFRAG_TEX_LEVEL0, 1500000, 10000, 10000, 800); + init_bucket_renderer("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("l1-tfrag-tie", BucketCategory::TIE, BucketId::TIE_LEVEL1, 1); // 17 : MERC_TFRAG_TEX_LEVEL1 - init_bucket_renderer("l1-tfrag-merc", BucketCategory::MERC, - BucketId::MERC_TFRAG_TEX_LEVEL1); + init_bucket_renderer("l1-tfrag-merc", BucketCategory::MERC, + BucketId::MERC_TFRAG_TEX_LEVEL1, m_merc2); // 18 : GMERC_TFRAG_TEX_LEVEL1 - init_bucket_renderer("l1-tfrag-generic", BucketCategory::GENERIC, - BucketId::GENERIC_TFRAG_TEX_LEVEL1, 1500000, 10000, 10000, 800); + init_bucket_renderer("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("l0-shrub-generic", BucketCategory::GENERIC, - BucketId::SHRUB_GENERIC_LEVEL0); + init_bucket_renderer("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("l1-shrub-generic", BucketCategory::GENERIC, - BucketId::SHRUB_GENERIC_LEVEL1); + init_bucket_renderer("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("common-alpha-merc", BucketCategory::MERC, - BucketId::MERC_AFTER_ALPHA); + init_bucket_renderer("common-alpha-merc", BucketCategory::MERC, + BucketId::MERC_AFTER_ALPHA, m_merc2); - init_bucket_renderer("common-alpha-generic", BucketCategory::GENERIC, - BucketId::GENERIC_ALPHA); // 46 + init_bucket_renderer("common-alpha-generic", BucketCategory::GENERIC, + BucketId::GENERIC_ALPHA, m_generic2, + Generic2::Mode::NORMAL); // 46 init_bucket_renderer("shadow", BucketCategory::OTHER, BucketId::SHADOW); // 47 //----------------------- @@ -417,50 +441,55 @@ void OpenGLRenderer::init_bucket_renderers_jak1() { //----------------------- init_bucket_renderer("l0-pris-tex", BucketCategory::TEX, BucketId::PRIS_TEX_LEVEL0); // 48 - init_bucket_renderer("l0-pris-merc", BucketCategory::MERC, - BucketId::MERC_PRIS_LEVEL0); // 49 - init_bucket_renderer("l0-pris-generic", BucketCategory::GENERIC, - BucketId::GENERIC_PRIS_LEVEL0); // 50 + init_bucket_renderer("l0-pris-merc", BucketCategory::MERC, + BucketId::MERC_PRIS_LEVEL0, m_merc2); // 49 + init_bucket_renderer("l0-pris-generic", BucketCategory::GENERIC, + BucketId::GENERIC_PRIS_LEVEL0, m_generic2, + Generic2::Mode::NORMAL); // 50 //----------------------- // LEVEL 1 pris texture //----------------------- init_bucket_renderer("l1-pris-tex", BucketCategory::TEX, BucketId::PRIS_TEX_LEVEL1); // 51 - init_bucket_renderer("l1-pris-merc", BucketCategory::MERC, - BucketId::MERC_PRIS_LEVEL1); // 52 - init_bucket_renderer("l1-pris-generic", BucketCategory::GENERIC, - BucketId::GENERIC_PRIS_LEVEL1); // 53 + init_bucket_renderer("l1-pris-merc", BucketCategory::MERC, + BucketId::MERC_PRIS_LEVEL1, m_merc2); // 52 + init_bucket_renderer("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( "common-pris-eyes", BucketCategory::OTHER, BucketId::MERC_EYES_AFTER_PRIS); // 54 // hack: set to merc2 for debugging - init_bucket_renderer("common-pris-merc", BucketCategory::MERC, - BucketId::MERC_AFTER_PRIS); // 55 - init_bucket_renderer("common-pris-generic", BucketCategory::GENERIC, - BucketId::GENERIC_PRIS); // 56 + init_bucket_renderer("common-pris-merc", BucketCategory::MERC, + BucketId::MERC_AFTER_PRIS, m_merc2); // 55 + init_bucket_renderer("common-pris-generic", BucketCategory::GENERIC, + BucketId::GENERIC_PRIS, m_generic2, + Generic2::Mode::NORMAL); // 56 //----------------------- // LEVEL 0 water texture //----------------------- init_bucket_renderer("l0-water-tex", BucketCategory::TEX, BucketId::WATER_TEX_LEVEL0); // 57 - init_bucket_renderer("l0-water-merc", BucketCategory::MERC, - BucketId::MERC_WATER_LEVEL0); // 58 - init_bucket_renderer("l0-water-generic", BucketCategory::GENERIC, - BucketId::GENERIC_WATER_LEVEL0); // 59 + init_bucket_renderer("l0-water-merc", BucketCategory::MERC, + BucketId::MERC_WATER_LEVEL0, m_merc2); // 58 + init_bucket_renderer("l0-water-generic", BucketCategory::GENERIC, + BucketId::GENERIC_WATER_LEVEL0, m_generic2, + Generic2::Mode::NORMAL); // 59 //----------------------- // LEVEL 1 water texture //----------------------- init_bucket_renderer("l1-water-tex", BucketCategory::TEX, BucketId::WATER_TEX_LEVEL1); // 60 - init_bucket_renderer("l1-water-merc", BucketCategory::MERC, - BucketId::MERC_WATER_LEVEL1); // 61 - init_bucket_renderer("l1-water-generic", BucketCategory::GENERIC, - BucketId::GENERIC_WATER_LEVEL1); // 62 + init_bucket_renderer("l1-water-merc", BucketCategory::MERC, + BucketId::MERC_WATER_LEVEL1, m_merc2); // 61 + init_bucket_renderer("l1-water-generic", BucketCategory::GENERIC, + BucketId::GENERIC_WATER_LEVEL1, m_generic2, + Generic2::Mode::NORMAL); // 62 init_bucket_renderer("ocean-near", BucketCategory::OCEAN, BucketId::OCEAN_NEAR); // 63 diff --git a/game/graphics/opengl_renderer/OpenGLRenderer.h b/game/graphics/opengl_renderer/OpenGLRenderer.h index 3754b9ab9b..92b39c1ee7 100644 --- a/game/graphics/opengl_renderer/OpenGLRenderer.h +++ b/game/graphics/opengl_renderer/OpenGLRenderer.h @@ -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 m_merc2; + std::shared_ptr m_generic2; std::vector> m_bucket_renderers; std::vector m_bucket_categories; diff --git a/game/graphics/opengl_renderer/Warp.cpp b/game/graphics/opengl_renderer/Warp.cpp index a996a4e842..3b1e789813 100644 --- a/game/graphics/opengl_renderer/Warp.cpp +++ b/game/graphics/opengl_renderer/Warp.cpp @@ -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 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); } \ No newline at end of file diff --git a/game/graphics/opengl_renderer/Warp.h b/game/graphics/opengl_renderer/Warp.h index c9287e70fa..4db5fd5f6f 100644 --- a/game/graphics/opengl_renderer/Warp.h +++ b/game/graphics/opengl_renderer/Warp.h @@ -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 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 m_generic; FramebufferCopier m_fb_copier; GpuTexture* m_warp_src_tex = nullptr; u32 m_tbp = 1216; // hack, jak 2 diff --git a/game/graphics/opengl_renderer/foreground/Generic2.cpp b/game/graphics/opengl_renderer/foreground/Generic2.cpp index 9a2bd156b3..16ecffba57 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2.cpp +++ b/game/graphics/opengl_renderer/foreground/Generic2.cpp @@ -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 diff --git a/game/graphics/opengl_renderer/foreground/Generic2.h b/game/graphics/opengl_renderer/foreground/Generic2.h index 67e7510707..907c1cac26 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2.h +++ b/game/graphics/opengl_renderer/foreground/Generic2.h @@ -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 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}; diff --git a/game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.cpp b/game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.cpp new file mode 100644 index 0000000000..57ceae2af5 --- /dev/null +++ b/game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.cpp @@ -0,0 +1,25 @@ +#include "Generic2BucketRenderer.h" + +Generic2BucketRenderer::Generic2BucketRenderer(const std::string& name, + int id, + std::shared_ptr 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); +} diff --git a/game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.h b/game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.h new file mode 100644 index 0000000000..839111ec82 --- /dev/null +++ b/game/graphics/opengl_renderer/foreground/Generic2BucketRenderer.h @@ -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 renderer, + Generic2::Mode mode); + void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; + void draw_debug_window() override; + + private: + std::shared_ptr m_generic; + Generic2::Mode m_mode; +}; diff --git a/game/graphics/opengl_renderer/foreground/Generic2_DMA.cpp b/game/graphics/opengl_renderer/foreground/Generic2_DMA.cpp index aa4cdb013d..02d7d59277 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2_DMA.cpp +++ b/game/graphics/opengl_renderer/foreground/Generic2_DMA.cpp @@ -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; diff --git a/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp b/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp index f954dacae2..93e0741384 100644 --- a/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp +++ b/game/graphics/opengl_renderer/foreground/Generic2_OpenGL.cpp @@ -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(); } diff --git a/game/graphics/opengl_renderer/foreground/Merc2.cpp b/game/graphics/opengl_renderer/foreground/Merc2.cpp index c4cfb612f2..62460f6c0b 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.cpp +++ b/game/graphics/opengl_renderer/foreground/Merc2.cpp @@ -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), diff --git a/game/graphics/opengl_renderer/foreground/Merc2.h b/game/graphics/opengl_renderer/foreground/Merc2.h index 96b70b4ef4..e8487a950a 100644 --- a/game/graphics/opengl_renderer/foreground/Merc2.h +++ b/game/graphics/opengl_renderer/foreground/Merc2.h @@ -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 effects; }; - struct { - std::vector model_list; - } m_debug; + + std::vector 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); }; diff --git a/game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp b/game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp new file mode 100644 index 0000000000..eb8a9844fb --- /dev/null +++ b/game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.cpp @@ -0,0 +1,24 @@ +#include "Merc2BucketRenderer.h" + +Merc2BucketRenderer::Merc2BucketRenderer(const std::string& name, + int my_id, + std::shared_ptr 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); +} \ No newline at end of file diff --git a/game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.h b/game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.h new file mode 100644 index 0000000000..5dddd665a7 --- /dev/null +++ b/game/graphics/opengl_renderer/foreground/Merc2BucketRenderer.h @@ -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 merc); + void draw_debug_window() override; + void render(DmaFollower& dma, SharedRenderState* render_state, ScopedProfilerNode& prof) override; + + private: + std::shared_ptr m_renderer; + MercDebugStats m_debug_stats; +}; diff --git a/game/graphics/opengl_renderer/loader/Loader.cpp b/game/graphics/opengl_renderer/loader/Loader.cpp index 1f802a0be3..efd77e1a8b 100644 --- a/game/graphics/opengl_renderer/loader/Loader.cpp +++ b/game/graphics/opengl_renderer/loader/Loader.cpp @@ -161,6 +161,7 @@ void Loader::draw_debug_window() { void Loader::loader_thread() { try { while (!m_want_shutdown) { + prof().root_event(); std::unique_lock 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(); 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()); diff --git a/game/graphics/pipelines/opengl.cpp b/game/graphics/pipelines/opengl.cpp index 8cd1cff0d2..6aff7ab105 100644 --- a/game/graphics/pipelines/opengl.cpp +++ b/game/graphics/pipelines/opengl.cpp @@ -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; } diff --git a/game/kernel/jak2/kdgo.cpp b/game/kernel/jak2/kdgo.cpp index d409c9f02d..572ec31837 100644 --- a/game/kernel/jak2/kdgo.cpp +++ b/game/kernel/jak2/kdgo.cpp @@ -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) { diff --git a/game/kernel/jak2/kmachine.cpp b/game/kernel/jak2/kmachine.cpp index d59d965be3..68984ad2ce 100644 --- a/game/kernel/jak2/kmachine.cpp +++ b/game/kernel/jak2/kmachine.cpp @@ -5,6 +5,7 @@ #include #include +#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()), 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! } } diff --git a/game/kernel/jak2/kscheme.cpp b/game/kernel/jak2/kscheme.cpp index cea08453c1..6c14b1e26a 100644 --- a/game/kernel/jak2/kscheme.cpp +++ b/game/kernel/jak2/kscheme.cpp @@ -5,6 +5,7 @@ #include #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; } diff --git a/game/runtime.cpp b/game/runtime.cpp index 255e08ec2a..145030b7e8 100644 --- a/game/runtime.cpp +++ b/game/runtime.cpp @@ -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 = diff --git a/goal_src/jak2/pc/debug/vag-player.gc b/goal_src/jak2/pc/debug/vag-player.gc index 48871c362f..21999c5c6a 100644 --- a/goal_src/jak2/pc/debug/vag-player.gc +++ b/goal_src/jak2/pc/debug/vag-player.gc @@ -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))