It's something that Lor Dric must do, or that we users must do to have a decent performance on the map ?gsanders said:Buckethell said:..., PLEASE, fix the performance issues.
The game in my opinion is currently unplayable, and i really want to play it, because it looks amazing.
The lag is real and the loadings are too much long, even if you enter and exit from a village it takes 15 secs everytime, and i can't even fast-forwarding, so the map it's really unplayable.
I must say that the battles are smooth and the framerate it's very good, so the problem it's only on the map.
@Lor Dric mainly, but any modder might find it jogs their thinking:
good luck on this, in my experience you're one of the few that would do this well.I had these issues in Perisno, then pre-test "A" Warsword Conquest, and the map lag came down to 3 areas of tuning:
1) replace every instance of
(rest_for_hours, ":hours", 5, 0), #kill some time and coincidentally set the speed multiplier until changed
with
(rest_for_hours, ":hours", "$g_warsword_wait_multiplier", 0), # waiting is now scaleable.
I'd set that multiplier at 3 for example. Then when someone ctrl-spacebar to speed time passing the CPU doesn't have to process the 220 or so background tasks at 5x normal rate, now just 3. For kicks and giggles let the user set it with xgm_options
from a range of 2 to 4. I'd say this value needs to be low considering the number of parties you have on map.
2) I became concerned about parties on the map and ended adding a report to manually check if the parties on map had too many disabled, how many parties total a try_for_parties loop has to iterate through, and to count a template that I expected to find in large numbers and see if the Warband tool telling how many of those parties there were was accurate, or it there were disabled parties etc wasting bandwidth during a try_for_parties loop.
it looked like this:
Code:("metrics_for_parties", [ ], "{!}Measure parties counts.", [ (jump_to_menu, "mnu_metrics_for_parties1"), ] ), # ... sometime later in game_menus.py ("metrics_for_parties1", 0, "{s16}^{s17}", "none", [ (assign, ":total_parties", 0), (assign, ":inactive", 0), (assign, ":count_looters", 0), (assign, ":count_deserters", 0), (assign, ":count_royal_traders", 0), (assign, ":inactive_royal_traders", 0), (assign, ":count_patrol", 0), (try_for_parties, ":party_no"), (val_add, ":total_parties", 1), (gt, ":party_no", "p_spawn_points_end"), (party_get_template_id, ":party_template", ":party_no"), (try_begin), (party_is_active,":party_no"), (try_begin), (party_slot_eq, ":party_no", slot_party_type, spt_kingdom_caravan), (val_add, ":count_royal_traders", 1), (else_try), (eq, ":party_template", "pt_looters"), (val_add, ":count_looters", 1), (else_try), (eq, ":party_template", "pt_deserters"), (val_add, ":count_deserters", 1), (else_try), (eq, ":party_template", "pt_patrol_party"), (val_add, ":count_patrol", 1), (try_end), (else_try), (val_add, ":inactive", 1), (try_begin), (party_slot_eq, ":party_no", slot_party_type, spt_kingdom_caravan), (val_add, ":inactive_royal_traders", 1), (try_end), (try_end), (try_end), (assign, reg3, ":total_parties"), (assign, reg4, ":inactive"), (store_num_parties_of_template, reg5, "pt_looters"), (assign, reg6, ":count_looters"), (str_store_string, s16, "@Total Parties: {reg3} Inactive: {reg4}^Looters: {reg5} active: {reg6}^"), (store_num_parties_of_template, reg3, "pt_deserters"), (assign, reg4, ":count_deserters"), (assign, reg5, ":count_royal_traders"), (assign, reg6, ":inactive_royal_traders"), (store_num_parties_of_template, reg7, "pt_patrol_party"), (assign, reg8, ":count_patrol"), (str_store_string, s17, "@Deserters: {reg3} counted: {reg4}^Royal Traders: {reg5} inactive: {reg6}^Patrols: {reg7} counted: {reg8}"), ], [ ("return", [], "Return", [(jump_to_menu, "mnu_reports"),]), ]), # close this menu
3) then I wanted bandwidth wasting loops such as icon flip over water to be called less often. I used 0.35 hours for my timer, to effectively cut wasted bandwidth from that ONE simple_triggers by almost 50%.
note I use the list processing system to further reduce the number of parties to examine.
Code:# Updating player icon in every frame # simple_triggers number 64 (0.35, [ (try_for_parties, ":cur_party"), (party_is_active, ":cur_party"), # don't waste time on disabled parties (party_get_current_terrain, ":terrain", ":cur_party"), (party_get_template_id, ":cur_template", ":cur_party"), (this_or_next|neq, ":cur_template", "pt_sea_traders"), (this_or_next|neq, ":cur_template", "pt_sea_raiders_ships"), (this_or_next|neq, ":cur_template", "pt_raiders_ships"), ( neq, ":cur_template", "pt_pirate_ships"), (assign, ":continue", 1), (try_begin), (eq, ":cur_template", "pt_none"), (party_get_icon, ":test_party_icon", ":cur_party"), (eq, ":test_party_icon", "icon_ship_on_land"), (assign, ":continue", 0), # GS we want to exclude from the list beached player ships (try_end), (eq, ":continue", 1), (try_begin), (this_or_next|eq, ":terrain",rt_bridge), (this_or_next|eq, ":terrain",rt_river), # terrain type 15 isnt defined in header_terrain.py (eq, ":terrain",rt_water), # could only apply to p_main_party (player) (call_script, "script_list_index_of", "trp_list_04", ":cur_party"), # reg1 index, -1 not found (try_begin), (eq, reg1, -1), # its not in the list already (party_get_icon, ":cur_icon", ":cur_party"), (party_set_icon, ":cur_party", "icon_ship"), (call_script, "script_list_add", "trp_list_04", ":cur_party"), # stuff the party_id (call_script, "script_list_add", "trp_list_05", ":cur_icon"), # stuff the icon (try_end), (try_end), (try_end), (call_script, "script_list_count", "trp_list_04"), (assign, ":list_end", reg1), (val_add, ":list_end", 1), (gt, ":list_end", 1), # we can skip any empty list (actual length = 0) (try_for_range, ":list_index", 1, ":list_end"), (call_script, "script_list_at_nc", "trp_list_04", ":list_index"), (assign, ":cur_party", reg1), (try_begin), (neg|party_is_active, ":cur_party"), # party must have been defeated very recently (call_script, "script_list_remove_at", "trp_list_04", ":list_index"), (call_script, "script_list_remove_at", "trp_list_05", ":list_index"), (val_sub, ":list_end", 1), (val_sub, ":list_index", 1), # because you need to test that index again next iteration... (else_try), (party_get_current_terrain, ":terrain", ":cur_party"), (neq, ":terrain",rt_bridge), # (neq, ":terrain",rt_river), # terrain type 15 isnt defined in header_terrain.py (neq, ":terrain",rt_water), # (party_get_template_id, ":cur_template", ":cur_party"), (neq, ":cur_template", "pt_sea_traders"), (call_script, "script_list_at_nc", "trp_list_05", ":list_index"), (assign, ":cur_icon", reg1), (party_set_icon,":cur_party",":cur_icon"), (call_script, "script_list_remove_at", "trp_list_04", ":list_index"), # partyID (call_script, "script_list_remove_at", "trp_list_05", ":list_index"), # icon (val_sub, ":list_end", 1), (val_sub, ":list_index", 1), # because you need to test that index again next iteration... (try_end), (try_end), ]),
list management system at https://forums.taleworlds.com/index.php/topic,341079.0.html
This use of a list was originally intended to put the party on water into a list to check more often if it hits land, while skipping river crossings, so as to use try_for_parties less often. In this case you could for example run the above trigger less often, say 0.7 hours, then every .35 hours check if dry land was hit only within that list, so as to react faster but go through your huge number of parties far less often, reducing apparent stutter. The added overhead of a list manager only makes sense if we assume for example about 1 in 8 parties are on the water, at most, so if we could not check all the other parties as often we save ourselves a processing spike. If 40% of the parties were on water, I'd reckon we're at break-even, and with more we'd suffer a net LOSS of bandwidth. I'd especially reconsider the use of this line (call_script, "script_list_remove_at", "trp_list_04", ":list_index"), # partyID
and try to avoid it completely if possible, for a very big list. A very small list (50 parties for example out of 2000) the added overhead isn't a burden.
Optimize every try_for_parties firing every 2 hours or less. But actually Lor Dric knows this. I suppose I'm reviewing for any other modder...
3) battles are already smooth per the OP so actually these few tricks are probably the ones that pay the quickest. With just tuning icon checking as above Perisno .772 was sped up by 1/3rd for .773 for overland map travel, as was Warsword Conquest for (still unreleased) patch 2. Its proven to work for over two weeks on Warsword Rigale test, so I'm not just making up numbers. In WC I have 1000 parties on map the first few seconds and this grows to about 2000 parties after bandits, kingdom parties, traders, and such have popped for around 1 week. I changed the frequency of kingdom_party spawns from 1 check every hour down to 1 check every 2.31 hours, the weird size to not have it go off at the same time as all the other 1 hour and 2 hour triggers. Picking a weird frequency reduces a standing wave in terms of smoothing a spike in CPU useage. I suppose its sort of like using spread spectrum on a clock timer circuit.
I haven't shared much but you're one of the few I trust, and this little bit of open discussion doesn't really hurt anyone. You have an impressive number of parties on map; they probably need to fit into some fast lists and be less often try_for_parties (which I use just for filling lists once and then drive the list which I assume is much much smaller, else the list overhead doesn't get profit in bandwidth. I wouldn't remove anything from a list that was huge its almost faster to build a fresh list if its done too often. But you'd see that.
Well, all this you know, but its useful review for anyone else.
- GS