Skip to content

Commit

Permalink
Parallelize icon parsing
Browse files Browse the repository at this point in the history
Spawn one thread per custom launcher in an attempt to make icon retrieval faster.
While it is much faster, it's still unusably slow, and WB hit 2.4GB RAM usage at some point, so maybe this is not the way to do it.
  • Loading branch information
BGazotti committed Mar 29, 2024
1 parent 30d4e23 commit e556aa0
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 22 deletions.
50 changes: 30 additions & 20 deletions Mopy/bash/basher/links_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
also defined in these functions."""
import os
import shlex
import threading

import pefile
import wx
Expand All @@ -49,7 +50,7 @@
from .. import bass, bush
from ..balt import BashStatusBar, MenuLink, SeparatorLink, UIList_Delete, \
UIList_Hide, UIList_OpenItems, UIList_OpenStore, UIList_Rename
from ..bolt import GPath_no_norm, os_name
from ..bolt import GPath_no_norm, os_name, deprint
from ..game import MergeabilityCheck
from ..game.patch_game import PatchGame
from ..gui import GuiImage, get_image
Expand Down Expand Up @@ -140,29 +141,38 @@ def _tool_args(app_key, app_path_data, clazz=AppButton, **kwargs):
'ShowAudioToolLaunchers']) for at in audio_tools.items())
all_links.extend(_tool_args(*mt) for mt in misc_tools.items())

# FIXME this is slow. Unusably slow. Even in parallel, there seems to be
# something about pefile that's incredibly slow and memory-hungry
# TODO native win32 handling for this
local_PE_threads = list()
local_lock = threading.Lock()
from ..gui.extract_icon import ExtractIcon
for launcher_name in bass.settings['bash.launchers']:
launcher_path, launcher_args = bass.settings['bash.launchers'][launcher_name]
# TODO pretty icons -> pefile?
# This is not trivial at all for non-Windows platforms, as it requires
# reading through a PE object file, counting how many icons it has
# embedded in it (there may be more than one)
# then appropriately retrieving one after calculating offsets
# and sending it over to wx or whatever so that it can be displayed.
#
# How about a big bold initial for the launcher's name instead? :')
# see: wx.IconLocation(path), might help
def create_launcher_button(lpath: str, lname, largs): # TODO get this out of here

icons = (get_image('error_cross.16'),) * 3
deprint(f'Thread for {launcher_name} started')
try:
if icon_data := ExtractIcon(filepath = launcher_path).get_raw_windows_preferred_icon():
# TODO what happens if there's no icon? None? Error?
launcher_icon = []
if icon_data := ExtractIcon(launcher_path).get_raw_windows_preferred_icon():
icons = []
for value in (16, 24, 32):
launcher_icon.append(wx.ImageFromBuffer(value, value, icon_data))
else: launcher_icon = [get_image('error_cross.16')] * 3
icons.append(wx.Bitmap(wx.Image(value, value, icon_data)))
except pefile.PEFormatError:
launcher_icon = [get_image('error_cross.16')] * 3
all_links.append(AppButton(GPath_no_norm(launcher_path), launcher_icon,
_(f'Run {launcher_name}'), launcher_name,cli_args=shlex.split(launcher_args), canHide=False))
pass # no icon in this file
btn = AppButton(GPath_no_norm(lpath), icons,
_(f'Run {lname}'), lname,
cli_args=shlex.split(largs), canHide=False)
local_lock.acquire()
all_links.append(btn)
local_lock.release()

for launcher_name in bass.settings['bash.launchers']:
launcher_path, launcher_args = bass.settings['bash.launchers'][launcher_name]
local_PE_threads.append((lthread := threading.Thread(target=create_launcher_button,
args=(launcher_path, launcher_name, launcher_args))))
lthread.start()

for lthread in local_PE_threads:
lthread.join()
#--Final couple
all_links.append(DocBrowserButton('DocBrowser'))
all_links.append(PluginCheckerButton('ModChecker'))
Expand Down
4 changes: 2 additions & 2 deletions Mopy/bash/gui/extract_icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def get_raw_windows_preferred_icon(self):
If no suitable icon was found, None is returned.
"""
group, icon = self.get_windows_preferred_icon()
if group != None and icon != None:
if group and icon:
return self.export_raw(group, icon)
else:
return None
Expand All @@ -182,7 +182,7 @@ def get_icon(self, index):
return data

def export_raw(self, entries, index = None):
if index is not None:
if index:
entries = entries[index:index+1]

ico = struct.pack('<HHH', 0, self.RES_ICON, len(entries))
Expand Down

0 comments on commit e556aa0

Please sign in to comment.