Skip to content

Commit 0163107

Browse files
committed
Use cached UI render instances in tech menu renderer
1 parent 03b73e5 commit 0163107

6 files changed

Lines changed: 163 additions & 126 deletions

File tree

code/menuui/techmenu.cpp

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "lighting/lighting.h"
2626
#include "localization/localize.h"
2727
#include "menuui/techmenu.h"
28+
#include "model/modelrender.h"
2829
#include "missionui/missionscreencommon.h"
2930
#include "parse/parselo.h"
3031
#include "playerman/player.h"
@@ -213,7 +214,6 @@ static const char *Text_lines[MAX_TEXT_LINES];
213214
static int Cur_entry = -1; // this is the current entry selected, using entry indexing
214215
static int Cur_entry_index = -1; // this is the current entry selected, using master list indexing
215216
static int Techroom_modelnum = -1;
216-
static int Techroom_model_instance = -1;
217217
static float Techroom_ship_rot;
218218
static UI_BUTTON List_buttons[LIST_BUTTONS_MAX]; // buttons for each line of text in list
219219

@@ -332,12 +332,6 @@ void techroom_select_new_entry()
332332

333333
Techroom_modelnum = model_load(sip, true);
334334

335-
if (Techroom_model_instance >= 0) {
336-
model_delete_instance(Techroom_model_instance);
337-
}
338-
Techroom_model_instance = model_create_instance(model_objnum_special::OBJNUM_NONE, Techroom_modelnum);
339-
340-
model_set_up_techroom_instance(sip, Techroom_model_instance);
341335

342336
Current_list->at(Cur_entry).model_num = Techroom_modelnum;
343337

@@ -348,10 +342,6 @@ void techroom_select_new_entry()
348342
} else {
349343
Techroom_modelnum = -1;
350344

351-
if (Techroom_model_instance >= 0) {
352-
model_delete_instance(Techroom_model_instance);
353-
Techroom_model_instance = -1;
354-
}
355345

356346
Trackball_mode = 0;
357347

@@ -393,11 +383,6 @@ void techroom_select_new_entry()
393383
if (Techroom_modelnum >= 0) {
394384
weaponLoaded = true;
395385

396-
if (Techroom_model_instance >= 0) {
397-
model_delete_instance(Techroom_model_instance);
398-
}
399-
Techroom_model_instance = model_create_instance(model_objnum_special::OBJNUM_NONE, Techroom_modelnum);
400-
401386
// If this ends up being needed for weapon models, a weapon version
402387
// of this method will need to be created. Should only be necessary
403388
// if weapon models start having animations or other advanced model
@@ -611,6 +596,12 @@ void techroom_ships_render(float frametime)
611596
model_clear_instance(Techroom_modelnum);
612597
render_info.set_detail_level_lock(0);
613598

599+
int model_instance = -1;
600+
model_get_cached_ui_render_instance(Techroom_modelnum, &model_instance, cached_ui_render_instance_type::tech_room);
601+
if (Tab == SHIPS_DATA_TAB) {
602+
model_set_up_techroom_instance(&Ship_info[Cur_entry_index], model_instance);
603+
}
604+
614605
if(shadow_maybe_start_frame(Shadow_disable_overrides.disable_techroom))
615606
{
616607
gr_reset_clip();
@@ -620,7 +611,7 @@ void techroom_ships_render(float frametime)
620611
shadows_start_render(&Eye_matrix, &Eye_position, Proj_fov, gr_screen.clip_aspect, -closeup_pos.xyz.z + pm->rad, -closeup_pos.xyz.z + pm->rad + 200.0f, -closeup_pos.xyz.z + pm->rad + 2000.0f, -closeup_pos.xyz.z + pm->rad + 10000.0f);
621612
render_info.set_flags(MR_NO_TEXTURING | MR_NO_LIGHTING | MR_AUTOCENTER);
622613

623-
model_render_immediate(&render_info, Techroom_modelnum, Techroom_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
614+
model_render_immediate(&render_info, Techroom_modelnum, model_instance, &Techroom_ship_orient, &vmd_zero_vector);
624615
shadows_end_render();
625616

626617
gr_set_clip(Tech_ship_display_coords[gr_screen.res][SHIP_X_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_Y_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_W_COORD], Tech_ship_display_coords[gr_screen.res][SHIP_H_COORD], GR_RESIZE_MENU);
@@ -636,7 +627,7 @@ void techroom_ships_render(float frametime)
636627

637628
render_info.set_flags(render_flags);
638629

639-
model_render_immediate(&render_info, Techroom_modelnum, Techroom_model_instance, &Techroom_ship_orient, &vmd_zero_vector);
630+
model_render_immediate(&render_info, Techroom_modelnum, model_instance, &Techroom_ship_orient, &vmd_zero_vector);
640631

641632
Glowpoint_use_depth_buffer = true;
642633

@@ -1314,7 +1305,6 @@ void techroom_lists_reset()
13141305

13151306
model_free_all();
13161307
Techroom_modelnum = -1;
1317-
Techroom_model_instance = -1;
13181308

13191309
// This can be cleared immediately because there are no anims or bitmaps associated.
13201310
Ship_list.clear();

code/missionui/missionscreencommon.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1671,7 +1671,8 @@ void draw_model_rotating(model_render_params *render_info, int ship_class, int m
16711671
return;
16721672

16731673
const auto instance_type = (flags & MR_IS_MISSILE) ? cached_ui_render_instance_type::overhead : cached_ui_render_instance_type::rotating;
1674-
int model_instance = model_get_cached_ui_render_instance(model_id, instance_type);
1674+
int model_instance = -1;
1675+
model_get_cached_ui_render_instance(model_id, &model_instance, instance_type);
16751676
if (!(flags & MR_IS_MISSILE) && SCP_vector_inbounds(Ship_info, ship_class)) {
16761677
model_set_up_techroom_instance(&Ship_info[ship_class], model_instance);
16771678
}

code/missionui/missionweaponchoice.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,8 @@ void draw_3d_overhead_view(int model_num,
824824
Glowpoint_use_depth_buffer = false;
825825

826826
model_clear_instance(model_num);
827-
int model_instance = model_get_cached_ui_render_instance(model_num, cached_ui_render_instance_type::overhead);
827+
int model_instance = -1;
828+
model_get_cached_ui_render_instance(model_num, &model_instance, cached_ui_render_instance_type::overhead);
828829
model_set_up_techroom_instance(sip, model_instance);
829830
polymodel* pm = model_get(model_num);
830831

code/model/modelrender.cpp

Lines changed: 105 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "nebula/neb.h"
2727
#include "particle/particle.h"
2828
#include "prop/prop.h"
29+
#include "parse/encrypt.h"
2930
#include "render/3dinternal.h"
3031
#include "render/batching.h"
3132
#include "ship/ship.h"
@@ -51,6 +52,109 @@ extern void interp_generate_arc_segment(SCP_vector<vec3d> &arc_segment_points, c
5152

5253
int model_render_determine_elapsed_time(int objnum, uint64_t flags);
5354

55+
SCP_unordered_map<cached_ui_render_instance_key, cached_ui_render_instance_entry, cached_ui_render_instance_key_hash>
56+
Cached_ui_render_instance_cache;
57+
UI_TIMESTAMP Ui_render_instance_cache_last_processed_timestamp = UI_TIMESTAMP::invalid();
58+
constexpr int UI_RENDER_INSTANCE_CACHE_UNUSED_MS_GRACE = 100;
59+
60+
uint32_t model_hash_subsystem_name_list_for_cache(const SCP_vector<SCP_string>& subsystem_names)
61+
{
62+
if (subsystem_names.empty()) {
63+
return 0;
64+
}
65+
66+
SCP_vector<SCP_string> normalized_names;
67+
normalized_names.reserve(subsystem_names.size());
68+
69+
for (const auto& name : subsystem_names) {
70+
auto normalized = name;
71+
SCP_tolower(normalized);
72+
normalized_names.push_back(std::move(normalized));
73+
}
74+
75+
std::sort(normalized_names.begin(), normalized_names.end());
76+
77+
size_t seed = 0;
78+
79+
boost::hash_combine(seed, static_cast<uint32_t>(normalized_names.size()));
80+
for (const auto& name : normalized_names) {
81+
boost::hash_combine(seed, hash_fnv1a(name));
82+
}
83+
84+
return seed;
85+
}
86+
87+
// Returns TriStateBool::TRUE_ if a new instance was created, TriStateBool::FALSE_ if an existing instance was returned,
88+
// or TriStateBool::UNKNOWN_ if there was an error (and model_instance_out will be set to -1 in this case)
89+
TriStateBool model_get_cached_ui_render_instance(int model_num, int* model_instance_out, cached_ui_render_instance_type type, uint32_t instance_data_hash)
90+
{
91+
Assertion(model_instance_out != nullptr, "model_instance_out must not be null!");
92+
if (model_instance_out == nullptr) {
93+
return TriStateBool::UNKNOWN_;
94+
}
95+
96+
cached_ui_render_instance_key key;
97+
key.model_num = model_num;
98+
key.type = type;
99+
key.state_instance_id = gameseq_get_state_instance_id();
100+
key.instance_data_hash = instance_data_hash;
101+
102+
auto& entry = Cached_ui_render_instance_cache[key];
103+
104+
auto created_new = false;
105+
106+
if (entry.model_instance < 0) {
107+
entry.model_instance = model_create_instance(model_objnum_special::OBJNUM_NONE, model_num);
108+
if (entry.model_instance < 0) {
109+
Warning(LOCATION, "Failed to create cached UI render model instance for model id %d.", model_num);
110+
Cached_ui_render_instance_cache.erase(key);
111+
*model_instance_out = -1;
112+
return TriStateBool::UNKNOWN_;
113+
}
114+
created_new = true;
115+
}
116+
117+
entry.last_used_timestamp = ui_timestamp();
118+
*model_instance_out = entry.model_instance;
119+
return created_new ? TriStateBool::TRUE_ : TriStateBool::FALSE_;
120+
}
121+
122+
void model_process_cached_ui_render_instances()
123+
{
124+
const auto now = ui_timestamp();
125+
126+
if (Ui_render_instance_cache_last_processed_timestamp.isValid() &&
127+
ui_timestamp_get_delta(Ui_render_instance_cache_last_processed_timestamp, now) == 0) {
128+
return;
129+
}
130+
131+
Ui_render_instance_cache_last_processed_timestamp = now;
132+
133+
for (auto it = Cached_ui_render_instance_cache.begin(); it != Cached_ui_render_instance_cache.end();) {
134+
if (it->second.model_instance < 0 || !it->second.last_used_timestamp.isValid() ||
135+
ui_timestamp_get_delta(it->second.last_used_timestamp, now) > UI_RENDER_INSTANCE_CACHE_UNUSED_MS_GRACE) {
136+
if (it->second.model_instance >= 0) {
137+
model_delete_instance(it->second.model_instance);
138+
}
139+
it = Cached_ui_render_instance_cache.erase(it);
140+
} else {
141+
++it;
142+
}
143+
}
144+
}
145+
146+
void model_clear_cached_ui_render_instances()
147+
{
148+
for (auto& instance : Cached_ui_render_instance_cache) {
149+
if (instance.second.model_instance >= 0) {
150+
model_delete_instance(instance.second.model_instance);
151+
}
152+
}
153+
154+
Cached_ui_render_instance_cache.clear();
155+
Ui_render_instance_cache_last_processed_timestamp = UI_TIMESTAMP::invalid();
156+
}
157+
54158
model_batch_buffer TransformBufferHandler;
55159

56160
model_render_params::model_render_params() :
@@ -3072,64 +3176,6 @@ void model_render_set_wireframe_color(const color* clr)
30723176
Wireframe_color = *clr;
30733177
}
30743178

3075-
3076-
namespace {
3077-
3078-
struct cached_ui_render_instance_key {
3079-
int model_num;
3080-
cached_ui_render_instance_type type;
3081-
int state_instance_id;
3082-
3083-
bool operator==(const cached_ui_render_instance_key& other) const {
3084-
return model_num == other.model_num && type == other.type && state_instance_id == other.state_instance_id;
3085-
}
3086-
};
3087-
3088-
struct cached_ui_render_instance_key_hash {
3089-
size_t operator()(const cached_ui_render_instance_key& key) const {
3090-
size_t seed = 0;
3091-
auto hash_combine = [&seed](size_t value) {
3092-
seed ^= value + 0x9e3779b9 + (seed << 6) + (seed >> 2);
3093-
};
3094-
3095-
hash_combine(std::hash<int>{}(key.model_num));
3096-
hash_combine(std::hash<int>{}(static_cast<int>(key.type)));
3097-
hash_combine(std::hash<int>{}(key.state_instance_id));
3098-
return seed;
3099-
}
3100-
};
3101-
3102-
struct cached_ui_render_instance_entry {
3103-
int model_instance = -1;
3104-
int last_used_frame = -1;
3105-
};
3106-
3107-
SCP_unordered_map<cached_ui_render_instance_key, cached_ui_render_instance_entry, cached_ui_render_instance_key_hash> Cached_ui_render_instance_cache;
3108-
int Cached_ui_render_instance_cache_last_gc_frame = -1;
3109-
constexpr int UI_RENDER_INSTANCE_CACHE_UNUSED_FRAME_GRACE = 2;
3110-
3111-
void model_gc_cached_ui_render_instances()
3112-
{
3113-
if (Cached_ui_render_instance_cache_last_gc_frame == Framecount) {
3114-
return;
3115-
}
3116-
3117-
Cached_ui_render_instance_cache_last_gc_frame = Framecount;
3118-
3119-
for (auto it = Cached_ui_render_instance_cache.begin(); it != Cached_ui_render_instance_cache.end(); ) {
3120-
if (it->second.model_instance < 0 || (Framecount - it->second.last_used_frame) > UI_RENDER_INSTANCE_CACHE_UNUSED_FRAME_GRACE) {
3121-
if (it->second.model_instance >= 0) {
3122-
model_delete_instance(it->second.model_instance);
3123-
}
3124-
it = Cached_ui_render_instance_cache.erase(it);
3125-
} else {
3126-
++it;
3127-
}
3128-
}
3129-
}
3130-
3131-
}
3132-
31333179
void modelinstance_replace_active_texture(polymodel_instance* pmi, const char* old_name, const char* new_name)
31343180
{
31353181
Assert(pmi != nullptr);
@@ -3165,42 +3211,6 @@ void modelinstance_replace_active_texture(polymodel_instance* pmi, const char* o
31653211
Warning(LOCATION, "Invalid texture '%s' used for replacement texture", old_name);
31663212
}
31673213

3168-
int model_get_cached_ui_render_instance(
3169-
int model_num,
3170-
cached_ui_render_instance_type type)
3171-
{
3172-
cached_ui_render_instance_key key;
3173-
key.model_num = model_num;
3174-
key.type = type;
3175-
key.state_instance_id = gameseq_get_state_instance_id();
3176-
3177-
auto& entry = Cached_ui_render_instance_cache[key];
3178-
3179-
if (entry.model_instance < 0) {
3180-
entry.model_instance = model_create_instance(model_objnum_special::OBJNUM_NONE, model_num);
3181-
}
3182-
3183-
entry.last_used_frame = Framecount;
3184-
return entry.model_instance;
3185-
}
3186-
3187-
void model_process_cached_ui_render_instances()
3188-
{
3189-
model_gc_cached_ui_render_instances();
3190-
}
3191-
3192-
void model_clear_cached_ui_render_instances()
3193-
{
3194-
for (auto& instance : Cached_ui_render_instance_cache) {
3195-
if (instance.second.model_instance >= 0) {
3196-
model_delete_instance(instance.second.model_instance);
3197-
}
3198-
}
3199-
3200-
Cached_ui_render_instance_cache.clear();
3201-
Cached_ui_render_instance_cache_last_gc_frame = -1;
3202-
}
3203-
32043214
// renders a model as if in the tech room or briefing UI
32053215
// model_type 1 for ship class, 2 for weapon class, 3 for pof
32063216
bool render_tech_model(tech_render_type model_type, int x1, int y1, int x2, int y2, float zoom, bool lighting, int class_idx, const matrix* orient, const SCP_string &pof_filename, float close_zoom, const vec3d *close_pos, const SCP_string& tcolor)
@@ -3317,7 +3327,7 @@ bool render_tech_model(tech_render_type model_type, int x1, int y1, int x2, int
33173327

33183328
// Get a cached UI render instance for ships so repeated UI/Lua renders avoid per-call allocation churn
33193329
if (model_type == TECH_SHIP) {
3320-
model_instance = model_get_cached_ui_render_instance(model_num, cached_ui_render_instance_type::tech_room);
3330+
model_get_cached_ui_render_instance(model_num, &model_instance, cached_ui_render_instance_type::tech_room);
33213331
model_set_up_techroom_instance(&Ship_info[class_idx], model_instance);
33223332
}
33233333

0 commit comments

Comments
 (0)