Codeusa / Borderless-Gaming

Play your favorite games in a borderless window; no more time consuming alt-tabs.
GNU General Public License v2.0
5.43k stars 468 forks source link

Excessive CPU usage, never idles. #148

Closed AeliusSaionji closed 9 years ago

AeliusSaionji commented 9 years ago

On my laptop, this program is always using 2% of the CPU, even when I tell check "pause automatic processing". A similarly significant percentage is always chewed up on my desktop, too. Using ProcessHacker I can see borderless gaming is aggressively looping the following thread over and over "clr.dll!GetMetaDataInternalInterfaceFromPublic+0x1b03b" (not necessarily the same hex address each time).

If I had to guess, I'd say borderless gaming is aggressively trying to find the names and classes of open windows in an a) inefficient way and b) never stops searching.

Maybe it takes this much CPU to create a real-ish time list of windows (I don't think it does, but for the sake of argument...)- however I KNOW it does not take this much processing power to wait for a specified window to come into existence (eg the favorites list). When borderless gaming is minimized or hidden, the user does not need a real time list of windows, so a quick fix is to have the polling routine stop while the program is minimized or hidden. I still think you should also address the efficiency of your polling routine, but allowing the program to actually idle is a good start.

Keep up the good work!

psouza4 commented 9 years ago

Thank you for your thoughtful and thorough issue report, however there is already an open issue for high CPU usage right here:

https://github.com/Codeusa/Borderless-Gaming/issues/51

An explanation of what the exact issue is and that we intend on working around the problem is included, however you raise one other question that isn't specifically answered in that other issue: why grab the window caption on every poll (class is not checked ever, so skipping that).

The answer to that is fairly simple: the primary window handle regularly changes for games, especially in the first few minutes when a game is launched. Games include splash screens, launchers, and all sorts of transitions between a standard GDI window to DX or other graphical layer, so it's necessary for Borderless Gaming to recognize and track when window handles change so we don't miss triggering automatic processing. Even if that weren't the case, window title in some games change too as a game progresses from loading stages (more prevalent in online games), which would not be triggered correctly if the caption wasn't regularly fetched.

Please have a read in the original high-CPU usage issue, especially my latest reply near the bottom to understand where the actual CPU use is and why it's not an issue we can directly solve (but again, will find a way to work around).

AeliusSaionji commented 9 years ago

You've demonstrated why the approach you use to detect games causes this issue for automatic processing. I would be perfectly content with turning automatic processing off and using Win+F6, but as mentioned in the other issue thread, this doesn't actually reduce CPU usage.

I'm not exactly following all the technical details of the other thread, but I have some relevant first hand experiences. I can write a powershell script that waits for a window to pop into existence á la the favorites list (and in fact, I have https://github.com/Link-Satonaka/scripts/blob/master/General.ps1), and that script averages under 1%. And I still considered it too high, because I can also write this script using AHK (and AHK can make windows borderless actually) and have the average be virtually 0%. I use an AHK script equivalent of the above to wait for a "disconnected" prompt on my CarPC and the script repeatedly press the "reconnect" button. Dinky mobile processor, still almost, if not exactly 0% while idling.

I understand changing handles throws a wrench in the situation, but that makes me wonder why you don't identify by the binary name and path?

http://sourceforge.net/projects/mmfh/ manages to idle 0% even with automatic processing (though somewhat less reliable in idle mode and much less expedient). My guess is that they take the more efficient approach of binary name + path (wildcard path supported), plus really long waits between polling times, complete with the option to actually make it stop thinking entirely. Compatibility with games in my uses has been equal to borderless gaming, I don't think the approach is any less effective. They might extract the window ID more efficiently by targeting the binary, rather than polling all windows? I'm just guessing here.

Borderless gaming has more polish, hot keys, UI is more convenient, and now uses steam as a Linux style package manager! You blow "window manager" out of the water in all other aspects, I like what you're doing here. I hope in my ramblings I managed to say something helpful.

psouza4 commented 9 years ago

Hmm, you're starting to ask a lot of questions that don't really amount to much now: why we don't do this or that (when, in a lot of cases, we do) and talking about automatic processing disabling (which doesn't affect this issue at all -- it only prevents triggering from when new windows and processes are refreshed, but does not stop the refreshing).

Again, the core of the issue here is a disconnect between Windows and a non-responsive application, but as I've said before: we are working on a solution for this already. We have a few approaches we can take with each their pros and cons and are carefully weighing and testing the solutions before deploying them.

Don't get too worked up -- this won't be an issue for long.

AeliusSaionji commented 9 years ago

I'll take your word for it, but regarding automatic processing disabling (which doesn't affect this issue at all -- it only prevents triggering from when new windows and processes are refreshed, but does not stop the refreshing), yes, clearly. I'm suggesting that it should. If you're not operating on windows it makes zero sense to continue polling them, particularly when the program is minimized or hidden to the tray.

psouza4 commented 9 years ago

No, it shouldn't -- not because there aren't merits for having such an option, but because the feature is regularly used to manually move windows in/out of borderless state, especially when fine-tuning settings on a favorited process. Otherwise the user has to shut down and restart the game multiple times any time they want to test an adjustment to window bounds, position, etc. Causing both automatic favorite processing and automatic process/window list refreshment to attach to the same setting will break that behavior.

Since you have an environment where Borderless Gaming has an issue playing nicely with other applications, would you be receptive to testing debug builds I'm working on? I do not have any issues with my current array of running processes, so I can't actually verify that I've made any progress so far.

AeliusSaionji commented 9 years ago

...will break that behavior This isn't an insurmountable dilemma. You can add a manual refresh button, or make "stop refreshing" a distinct new option. But even before going that far, notice that I've said (repeatedly) this should happen while minimized or hidden to the tray. You're defending the logic wasting CPU cycles on a list even while the list is not in use and completely hidden from user view.

Even with that aside, I'm not trying to tell you exactly what to do, I'm providing ideas for achieving efficiency- if there's some reason you're dismissing these suggestions without giving a moment's thought for how you could implement it, please let me know. You've established that you're working on improving the efficiency of the window polling, I'm trying to take it one step further by having the function not run when it is not desired by the user, or when it doesn't make sense to.

madpew commented 9 years ago

could also add an option to stop refreshing when 1 process was matched and made borderless (e.g: check whole list until 1 process is a match, then continue to check/update that process, and ignore the others until process is gone again). This could improve performance for people that only have 1 game borderless at the same time (I estimate that would be about 95% at least; who really runs multiple games borderless at the same time)

psouza4 commented 9 years ago

@Link-Satonaka: I disagree with your reasoning again. Most gamers set up their favorites once, then minimize. For example, on my home theater PC, I have Borderless Gaming running on startup, minimized to tray and it automatically triggers on dozens of favorites I have added over the last year or so. Forcing it to only poll for new processes when the application is actively being used defeats the purpose of that automation.

Let's stop going round and round this path and get to the actual issue: high CPU usage. I offered a test build for you to provide feedback on. Were you not interested?

@madpew: that's a thought -- honestly there's a few ways to go about it, like an option to have it work the way Link-Satonaka proposed as well (but not automatically: through an option), but ideally neither should be necessary. I have a more optimized version that I'd like for someone experiencing this issue to test, currently wasting away on my hard drive. :)

AeliusSaionji commented 9 years ago

@Psouza4 again, you failed to properly read my comments. Please do more than skim the words.

WHILE ("automatic processing disabled" AND "program minimized") { DISABLE REFRESH }

The user has disabled automatic processing, but the refresh is still running. All. The. Time. The user only needs the refresh while looking at the program, if they have chosen to disable automatic processing.

Yes, I'll try the test build for you.

psouza4 commented 9 years ago

No need to become hostile -- I did in fact read your comments thoroughly before responding. Sorry if I misunderstood that you were proposing a joint conditional scenario. It makes sense and I'll implement it.

As for the testing, it looks like the project founder, Andrew, has come across this problem on his machine and has been testing it with me. We're in good shape and should be pushing out a new release shortly.

psouza4 commented 9 years ago

I encourage you to update to Borderless Gaming 8.2:

https://github.com/Codeusa/Borderless-Gaming/releases/tag/8.2

Several optimizations have been made to reduce CPU use and improve performance and responsiveness. One of the changes I made includes your request to suspend all process/window polling when the main window is minimized or hidden and favorites processing is paused.

AeliusSaionji commented 9 years ago

It's perfect, thank you. I did not expect such a quick turn around.

Sorry I got frustrated at our encounter earlier, I went back and reread my statements (composed on a tiny phone screen) and realize the double condition wasn't as explicitly clear as I thought I was making it, my bad.

Is "use slower window detection" new? When I have it enabled, my CPU usage has the following three point pattern over 3 seconds: 0.50%, 0.08%, 0.02%, repeat. When I have it disabled: 0.22%, 0.02%, 0.02%, repeat. Is there a chance you reversed the enabled state of that option? Or is my machine just handling one better than expected? I'm happy with the second set, it doesn't seem to be noticeably slower.

I'll keep an eye on RAM usage, it's probably nothing but in just 20 minutes I already observed a trend of slowly consuming more memory. untitled

psouza4 commented 9 years ago

The slower detection method is what Borderless Gaming was previously using. It would enumerate through all threads in a process to find a visible and valid window handle (required to be compatible with some games) but was a costly operation versus the default quicker method which simply uses the main process thread. The slow setting also has longer timeouts/tolerance for windows that aren't responding to requests for their caption text before giving up. It's ironic that your early tests show it taking nearly the same amount of time for either method now.

As far as memory usage, it should step up and down several times while it collects and compares data on active processes. the .NET framework handles garbage collection somewhat mysteriously at times and so we can observe an occasional larger shift up and then eventual shift down over long enough time. It shouldn't leak anything though and you should be able to leave the process running as long as you want.

AeliusSaionji commented 9 years ago

I see, it's the slower detection method, not a method by which window detection occurs more slowly. At my time of testing it I had no favorites established; adding a favorite bumped up the CPU load a bit.

psouza4 commented 9 years ago

That option can't be enabled using version 8.2 because of a bug that's preventing the checkbox from showing, so you're getting the faster method whether you like it or not right now. Oops.

karasuhebi commented 9 years ago

@Link-Satonaka What program is that featured in your last screenshot? Looks useful.

psouza4 commented 9 years ago

Looks like Process Explorer to me

karasuhebi commented 9 years ago

I don't think so, I have Process Explorer and the tab that looks like that is called Performance Graph, not Performance: http://i.imgur.com/OtD87bw.png

AeliusSaionji commented 9 years ago

@karasuhebi http://processhacker.sourceforge.net/

psouza4 commented 9 years ago

I've used Process Hacker too in situations where Process Explorer is stubborn. It's a fantastic resource, too.

karasuhebi commented 9 years ago

Thanks!