mirror of
https://github.com/open-goal/jak-project.git
synced 2024-10-20 11:26:18 -04:00
[jak2] improve loader for jak 2 levels (#2206)
Add a debug window for the loader to show levels and fix an issue with jak 2 level unloading. I never really thought about how this works for > 2 levels, and there is a chance that you could unload the wrong level in some cases. The problem is some combination of merc-only levels not counting toward the "in use" detection, and the unloader ignoring what the game wants to load. I don't remember why using merc models doesn't contribute to "in use" but I'm afraid to change this for jak 1. Instead, we can have the unloader avoid unloading levels that the game is telling us are loaded. This is what we should have done originally, but there was a time when Jak 1 didn't tell the loader anything, and we had this stupid detection thing. I think this is the first step toward just getting rid of the "in use" detection and trusting the game for everything.
This commit is contained in:
parent
e41ca8903e
commit
c249dbc437
|
@ -609,6 +609,10 @@ void OpenGLRenderer::render(DmaFollower dma, const RenderOptions& settings) {
|
|||
}
|
||||
}
|
||||
|
||||
if (settings.draw_loader_window) {
|
||||
m_render_state.loader->draw_debug_window();
|
||||
}
|
||||
|
||||
m_profiler.finish();
|
||||
// if (m_profiler.root_time() > 0.018) {
|
||||
// fmt::print("Slow frame: {:.2f} ms\n", m_profiler.root_time() * 1000);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
struct RenderOptions {
|
||||
bool draw_render_debug_window = false;
|
||||
bool draw_profiler_window = false;
|
||||
bool draw_loader_window = false;
|
||||
bool draw_small_profiler_window = false;
|
||||
bool draw_subtitle_editor_window = false;
|
||||
bool draw_filters_window = false;
|
||||
|
|
|
@ -99,6 +99,7 @@ void OpenGlDebugGui::draw(const DmaStats& dma_stats) {
|
|||
ImGui::MenuItem("Render Debug", nullptr, &m_draw_debug);
|
||||
ImGui::MenuItem("Profiler", nullptr, &m_draw_profiler);
|
||||
ImGui::MenuItem("Small Profiler", nullptr, &small_profiler);
|
||||
ImGui::MenuItem("Loader", nullptr, &m_draw_loader);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ class OpenGlDebugGui {
|
|||
bool should_draw_profiler() const { return master_enable && m_draw_profiler; }
|
||||
bool should_draw_subtitle_editor() const { return master_enable && m_subtitle_editor; }
|
||||
bool should_draw_filters_menu() const { return master_enable && m_filters_menu; }
|
||||
bool should_draw_loader_menu() const { return master_enable && m_draw_loader; }
|
||||
const char* screenshot_name() const { return m_screenshot_save_name; }
|
||||
|
||||
bool should_advance_frame() { return m_frame_timer.should_advance_frame(); }
|
||||
|
@ -76,6 +77,7 @@ class OpenGlDebugGui {
|
|||
bool m_draw_frame_time = false;
|
||||
bool m_draw_profiler = false;
|
||||
bool m_draw_debug = false;
|
||||
bool m_draw_loader = false;
|
||||
bool m_subtitle_editor = false;
|
||||
bool m_filters_menu = false;
|
||||
bool m_want_screenshot = false;
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include "game/graphics/opengl_renderer/loader/LoaderStages.h"
|
||||
|
||||
#include "third-party/imgui/imgui.h"
|
||||
|
||||
namespace {
|
||||
std::string uppercase_string(const std::string& s) {
|
||||
std::string result;
|
||||
|
@ -96,6 +98,62 @@ std::vector<LevelData*> Loader::get_in_use_levels() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void Loader::draw_debug_window() {
|
||||
ImGui::Begin("Loader");
|
||||
std::unique_lock<std::mutex> lk(m_loader_mutex);
|
||||
ImVec4 blue(0.3, 0.3, 0.8, 1.0);
|
||||
ImVec4 red(0.8, 0.3, 0.3, 1.0);
|
||||
ImVec4 green(0.3, 0.8, 0.3, 1.0);
|
||||
|
||||
if (!m_desired_levels.empty()) {
|
||||
ImGui::Text("desired levels");
|
||||
for (auto& lev : m_desired_levels) {
|
||||
auto lev_color = red;
|
||||
if (m_initializing_tfrag3_levels.find(lev) != m_initializing_tfrag3_levels.end()) {
|
||||
lev_color = blue;
|
||||
}
|
||||
if (m_loaded_tfrag3_levels.find(lev) != m_loaded_tfrag3_levels.end()) {
|
||||
lev_color = green;
|
||||
}
|
||||
ImGui::TextColored(lev_color, "%s", lev.c_str());
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
if (!m_initializing_tfrag3_levels.empty()) {
|
||||
ImGui::Text("init levels");
|
||||
for (auto& lev : m_initializing_tfrag3_levels) {
|
||||
ImGui::TextColored(blue, "%s", lev.first.c_str());
|
||||
ImGui::SameLine();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
if (!m_loaded_tfrag3_levels.empty()) {
|
||||
ImGui::Text("loaded levels");
|
||||
for (auto& lev : m_loaded_tfrag3_levels) {
|
||||
auto lev_color = green;
|
||||
if (lev.second->frames_since_last_used > 0) {
|
||||
lev_color = blue;
|
||||
}
|
||||
if (lev.second->frames_since_last_used > 180) {
|
||||
lev_color = red;
|
||||
}
|
||||
ImGui::TextColored(lev_color, "%20s : %3d", lev.first.c_str(),
|
||||
lev.second->frames_since_last_used);
|
||||
ImGui::Text(" %d textures", (int)lev.second->textures.size());
|
||||
ImGui::Text(" %d merc", (int)lev.second->merc_model_lookup.size());
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::Separator();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Loader function that runs in a completely separate thread.
|
||||
* This is used for file I/O and unpacking.
|
||||
|
@ -277,6 +335,23 @@ void Loader::update_blocking(TexturePool& tex_pool) {
|
|||
}
|
||||
}
|
||||
|
||||
const std::string* Loader::get_most_unloadable_level() {
|
||||
for (const auto& [name, lev] : m_loaded_tfrag3_levels) {
|
||||
if (lev->frames_since_last_used > 180 &&
|
||||
std::find(m_desired_levels.begin(), m_desired_levels.end(), name) ==
|
||||
m_desired_levels.end()) {
|
||||
return &name;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [name, lev] : m_loaded_tfrag3_levels) {
|
||||
if (lev->frames_since_last_used > 180) {
|
||||
return &name;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Loader::update(TexturePool& texture_pool) {
|
||||
Timer loader_timer;
|
||||
|
||||
|
@ -339,65 +414,62 @@ void Loader::update(TexturePool& texture_pool) {
|
|||
// try to remove levels.
|
||||
Timer unload_timer;
|
||||
if ((int)m_loaded_tfrag3_levels.size() >= m_max_levels) {
|
||||
for (auto& lev : m_loaded_tfrag3_levels) {
|
||||
if (lev.second->frames_since_last_used > 180) {
|
||||
std::unique_lock<std::mutex> lk(texture_pool.mutex());
|
||||
fmt::print("------------------------- PC unloading {}\n", lev.first);
|
||||
for (size_t i = 0; i < lev.second->level->textures.size(); i++) {
|
||||
auto& tex = lev.second->level->textures[i];
|
||||
if (tex.load_to_pool) {
|
||||
texture_pool.unload_texture(PcTextureId::from_combo_id(tex.combo_id),
|
||||
lev.second->textures.at(i));
|
||||
}
|
||||
}
|
||||
lk.unlock();
|
||||
for (auto tex : lev.second->textures) {
|
||||
if (EXTRA_TEX_DEBUG) {
|
||||
for (auto& slot : texture_pool.all_textures()) {
|
||||
if (slot.source) {
|
||||
ASSERT(slot.gpu_texture != tex);
|
||||
} else {
|
||||
ASSERT(slot.gpu_texture != tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
|
||||
for (auto& tie_geo : lev.second->tie_data) {
|
||||
for (auto& tie_tree : tie_geo) {
|
||||
glDeleteBuffers(1, &tie_tree.vertex_buffer);
|
||||
if (tie_tree.has_wind) {
|
||||
glDeleteBuffers(1, &tie_tree.wind_indices);
|
||||
}
|
||||
glDeleteBuffers(1, &tie_tree.index_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& tfrag_geo : lev.second->tfrag_vertex_data) {
|
||||
for (auto& tfrag_buff : tfrag_geo) {
|
||||
glDeleteBuffers(1, &tfrag_buff);
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteBuffers(1, &lev.second->collide_vertices);
|
||||
glDeleteBuffers(1, &lev.second->merc_vertices);
|
||||
glDeleteBuffers(1, &lev.second->merc_indices);
|
||||
|
||||
for (auto& model : lev.second->level->merc_data.models) {
|
||||
auto& mercs = m_all_merc_models.at(model.name);
|
||||
MercRef ref{&model, lev.second->load_id};
|
||||
auto it = std::find(mercs.begin(), mercs.end(), ref);
|
||||
ASSERT_MSG(it != mercs.end(), fmt::format("missing merc: {}\n", model.name));
|
||||
mercs.erase(it);
|
||||
}
|
||||
|
||||
m_loaded_tfrag3_levels.erase(lev.first);
|
||||
break;
|
||||
auto to_unload = get_most_unloadable_level();
|
||||
auto& lev = m_loaded_tfrag3_levels.at(*to_unload);
|
||||
std::unique_lock<std::mutex> lk(texture_pool.mutex());
|
||||
fmt::print("------------------------- PC unloading {}\n", *to_unload);
|
||||
for (size_t i = 0; i < lev->level->textures.size(); i++) {
|
||||
auto& tex = lev->level->textures[i];
|
||||
if (tex.load_to_pool) {
|
||||
texture_pool.unload_texture(PcTextureId::from_combo_id(tex.combo_id),
|
||||
lev->textures.at(i));
|
||||
}
|
||||
}
|
||||
lk.unlock();
|
||||
for (auto tex : lev->textures) {
|
||||
if (EXTRA_TEX_DEBUG) {
|
||||
for (auto& slot : texture_pool.all_textures()) {
|
||||
if (slot.source) {
|
||||
ASSERT(slot.gpu_texture != tex);
|
||||
} else {
|
||||
ASSERT(slot.gpu_texture != tex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glDeleteTextures(1, &tex);
|
||||
}
|
||||
|
||||
for (auto& tie_geo : lev->tie_data) {
|
||||
for (auto& tie_tree : tie_geo) {
|
||||
glDeleteBuffers(1, &tie_tree.vertex_buffer);
|
||||
if (tie_tree.has_wind) {
|
||||
glDeleteBuffers(1, &tie_tree.wind_indices);
|
||||
}
|
||||
glDeleteBuffers(1, &tie_tree.index_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& tfrag_geo : lev->tfrag_vertex_data) {
|
||||
for (auto& tfrag_buff : tfrag_geo) {
|
||||
glDeleteBuffers(1, &tfrag_buff);
|
||||
}
|
||||
}
|
||||
|
||||
glDeleteBuffers(1, &lev->collide_vertices);
|
||||
glDeleteBuffers(1, &lev->merc_vertices);
|
||||
glDeleteBuffers(1, &lev->merc_indices);
|
||||
|
||||
for (auto& model : lev->level->merc_data.models) {
|
||||
auto& mercs = m_all_merc_models.at(model.name);
|
||||
MercRef ref{&model, lev->load_id};
|
||||
auto it = std::find(mercs.begin(), mercs.end(), ref);
|
||||
ASSERT_MSG(it != mercs.end(), fmt::format("missing merc: {}\n", model.name));
|
||||
mercs.erase(it);
|
||||
}
|
||||
|
||||
m_loaded_tfrag3_levels.erase(*to_unload);
|
||||
}
|
||||
|
||||
if (unload_timer.getMs() > 5.f) {
|
||||
|
|
|
@ -25,11 +25,14 @@ class Loader {
|
|||
void load_common(TexturePool& tex_pool, const std::string& name);
|
||||
void set_want_levels(const std::vector<std::string>& levels);
|
||||
std::vector<LevelData*> get_in_use_levels();
|
||||
void draw_debug_window();
|
||||
|
||||
private:
|
||||
void loader_thread();
|
||||
bool upload_textures(Timer& timer, LevelData& data, TexturePool& texture_pool);
|
||||
|
||||
const std::string* get_most_unloadable_level();
|
||||
|
||||
// used by game and loader thread
|
||||
std::unordered_map<std::string, std::unique_ptr<LevelData>> m_initializing_tfrag3_levels;
|
||||
|
||||
|
|
|
@ -459,6 +459,7 @@ void render_game_frame(int game_width,
|
|||
options.msaa_samples = msaa_samples;
|
||||
options.draw_render_debug_window = g_gfx_data->debug_gui.should_draw_render_debug();
|
||||
options.draw_profiler_window = g_gfx_data->debug_gui.should_draw_profiler();
|
||||
options.draw_loader_window = g_gfx_data->debug_gui.should_draw_loader_menu();
|
||||
options.draw_subtitle_editor_window = g_gfx_data->debug_gui.should_draw_subtitle_editor();
|
||||
options.draw_filters_window = g_gfx_data->debug_gui.should_draw_filters_menu();
|
||||
options.save_screenshot = false;
|
||||
|
|
Loading…
Reference in a new issue