bsnes-emu / bsnes

bsnes is a Super Nintendo (SNES) emulator focused on performance, features, and ease of use.
Other
1.67k stars 154 forks source link

"Load ROM..." dialog uses too much CPU #188

Open Screwtapello opened 3 years ago

Screwtapello commented 3 years ago

Steps to reproduce

  1. Start bsnes
  2. disable the animated snow, it's enabled
  3. From the System menu, choose Load Game...

Expected results

Near-zero CPU usage

Actual results

bsnes consumes a CPU core

Notes

This was reported on the Discord under Windows, but reproduced on Linux/GTK+3.

perf top report `perf top` isn't great at digging out symbol names for some reason. ``` 62.54% 0.00% bsnes [unknown] 27.64% 25.28% bsnes libpango-1.0.so.0.4800.2 17.44% 7.14% bsnes libc-2.31.so 17.26% 15.31% bsnes libglib-2.0.so.0.6600.8 16.22% 15.59% bsnes libharfbuzz.so.0.20704.0 12.91% 12.86% bsnes [kernel.kallsyms] 7.50% 6.82% bsnes libgobject-2.0.so.0.6600.8 6.64% 5.52% bsnes bsnes 4.55% 1.69% bsnes libpthread-2.31.so 1.98% 1.76% bsnes libpangocairo-1.0.so.0.4800.2 1.69% 1.56% bsnes libpangoft2-1.0.so.0.4800.2 1.52% 1.38% bsnes libgdk-3.so.0.2404.20 1.19% 1.06% bsnes libxcb.so.1.1.0 1.06% 0.92% bsnes libcairo.so.2.11600.0 0.98% 0.94% bsnes libX11.so.6.4.0 0.75% 0.74% bsnes libfontconfig.so.1.12.0 0.49% 0.44% bsnes libgtk-3.so.0.2404.20 0.44% 0.40% bsnes libdbus-1.so.3.19.13 0.36% 0.31% bsnes libfribidi.so.0.4.0 0.13% 0.13% bsnes libstdc++.so.6.0.28 0.09% 0.09% bsnes [vdso] 0.07% 0.05% bsnes libatspi.so.0.0.1 0.00% 0.00% bsnes [scsi_mod] ```
gdb traceback Captured at some arbitrary point in the proceedings ``` #0 0x00007f2baaa02780 in g_slice_free_chain_with_offset () at /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0 #1 0x00007f2baac8c72c in () at /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 #2 0x00007f2baac8e6a4 in () at /usr/lib/x86_64-linux-gnu/libpango-1.0.so.0 #3 0x00007f2baaada11e in g_object_unref () at /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 #4 0x000055898d2ef4d8 in hiro::pFont::size(_PangoFontDescription*, nall::string const&) (font=font@entry=0x55899439f580, text=...) at ../hiro/core/../gtk/font.cpp:19 #5 0x000055898d2ffd5e in hiro::pFont::size(hiro::Font const&, nall::string const&) (font=..., text=...) at ../hiro/core/../gtk/font.cpp:7 #6 0x000055898d3007e3 in hiro::Font::size(nall::string const&) const (this=this@entry=0x7ffcc942b6b0, text=...) at ../hiro/core/font.cpp:71 #7 0x000055898d308932 in hiro::pWindow::_menuTextHeight() const (this=this@entry=0x558991ee6ce0) at ../hiro/core/../gtk/window.cpp:463 #8 0x000055898d3089d1 in hiro::pWindow::_menuHeight() const (this=this@entry=0x558991ee6ce0) at ../hiro/core/../gtk/window.cpp:453 #9 0x000055898d30976a in hiro::pWindow::_synchronizeGeometry() (this=0x558991ee6ce0) at ../hiro/core/../gtk/window.cpp:574 #10 0x000055898d30f266 in hiro::pApplication::processEvents() () at ../hiro/core/../gtk/application.cpp:49 #11 0x000055898d30f2c9 in hiro::Application::processEvents() () at ../hiro/core/application.cpp:46 #12 0x000055898d310035 in hiro::pWindow::setModal(bool) (this=0x5589945bb450, modal=modal@entry=true) at ../hiro/core/../gtk/window.cpp:399 #13 0x000055898d3100a5 in hiro::mWindow::setModal(bool) (this=0x5589942e1f30, modal=) at ../hiro/core/window.hpp:3 #14 0x000055898d3558d5 in hiro::Window::setModal(bool) (this=0x7ffcc942c018, modal=) at ../hiro/core/shared.hpp:946 #15 0x000055898d338893 in hiro::BrowserDialogWindow::run() (this=this@entry=0x7ffcc942bff0) at ../hiro/extension/browser-dialog.cpp:362 #16 0x000055898d3390ce in hiro::BrowserDialog::_run() (this=this@entry=0x7ffcc942c460) at ../hiro/extension/browser-dialog.cpp:524 #17 0x000055898d33994a in hiro::BrowserDialog::openObject() (this=this@entry=0x7ffcc942c460) at ../hiro/extension/browser-dialog.cpp:449 #18 0x000055898d4dd0ec in Program::openGame(hiro::BrowserDialog&) (this=this@entry=0x558990438260 , dialog=...) ```

It looks like when bsnes opens the file dialog as a modal dialog, it doesn't just use the GTK "make modal dialog" function (whatever that is), it tries to make its own event loop and pump events (see pWindow::setModal in hiro/gtk/window.cpp). However, it doesn't have any way to wait for an event, so it busy-loops calling bsnes' Program::main() method and proactively updating the size of every window. Updating window sizes means a whole bunch of text measurement, which is why pango and harfbuzz show up.

I don't know why hiro doesn't use GTK's (or Windows') native modal feature; if it's a quirk of GTK2 or Qt4 perhaps we can be rid of it, but if it's a quirk of Windows or macOS or GTK3 or Qt5, I think we're stuck with it.