Decker icon indicating copy to clipboard operation
Decker copied to clipboard

High energy usage on Mac

Open geoffbeier opened this issue 1 year ago • 5 comments

I haven't yet really looked into why this is so, but it's very pronounced.

I've been playing with Decker in odd spare moments on my Mac, which means I'm often using it on battery power. If I switch away from Decker because I need to answer a few emails, I have noticed that my battery drains much more quickly than normal while Decker is running, even if it's just in the background doing practically nothing.

Activity Monitor shows Decker as having very high energy usage.

I have also noticed that my mac won't automatically sleep when Decker is running.

I'm using the build downloaded from:

https://internet-janitor.itch.io/decker/devlog/838108/decker-152

on an M1 Pro running MacOS Sonoma Version 14.7 (23H124).

I may be able to chase this down at some point. Right now, I'm just interested in trying to use Decker to make a thing for Dec(k) Month, and my brain won't let me do that until I capture this somewhere. This issue tracker seemed like the right place.

geoffbeier avatar Dec 02 '24 14:12 geoffbeier

Self-replying to note:

CPU usage is high. Decker, in the background with an empty deck open is reported by Activity Monitor as using 47.5% of my CPU (much more than anything else currently running on my system, including Chrome, PyCharm, Safari and Affinity Designer), and it's an Intel build.

I should make an ARM build and see if that helps, but if I do that right now, I'll wind up fiddling with this instead of participating in the jam :-)

geoffbeier avatar Dec 02 '24 14:12 geoffbeier

This also affects Linux: I've also noticed that an empty deck will use about half a CPU on my x86 Linux laptop.

According to some perf record runs on an -O2 optimized build, running in the background with a blank deck open:

  • The majority of the samples (~62%) are while running void finish_flip(void){SDL_RenderPresent(ren);}
  • The runner-up (~27%) is draw_frame(), which converts Decker's internal frame format (8 bits per pixel, indicating color/pattern/animation/etc) into SDL's format (32-bit RGBA).

Since SDL is taking up the largest share, I tried changing the SDL renderer mode to SDL_RENDERER_ACCELERATED. I also fiddled with the logic in draw_frame(), trying to speed it up (specifically, instead of calculating x+y*size.x and x+y*stride as array indexes, maintain pointers *src and *dest which get incremented).

  • Baseline: 43.9% CPU usage
  • SDL hardware rendering: 19.2% CPU
  • Alternate implementation of draw_frame(): 42.4% CPU
  • Both hardware rendering and an alternate draw_frame(): 16.8% CPU

So switching from SDL_RENDERER_SOFTWARE to SDL_RENDERER_ACCELERATED seems worthwhile to do. Unless for some reason we need the software renderer, but the default setting should probably be accelerated.

cmounce avatar Dec 21 '24 11:12 cmounce

I've benefited on Linux from using the accelerated renderer, but it still hurts for Decker to constantly render in the background when using it as a kind of journal: normally not displayed, but ready for me to switch to it to tick a checkbox or add a note. With this patch CPU seems to drop to about 14% of foreground usage:

diff --git a/c/decker.c b/c/decker.c
index 6dcd5b5..bb03fa5 100644
--- a/c/decker.c
+++ b/c/decker.c
@@ -3418,6 +3418,7 @@ void sync(void){
 	if(wid.change_timer){wid.change_timer--;if(wid.change_timer==0)field_change();}
 	for(int z=0;z<256;z++)ev.shortcuts[z]=0;memset(keyup,0,sizeof(keyup));
 	process_events(disp,size,scale);
+	if (unfocused) return;
 	pointer_down=ev.md,pointer_up=ev.mu;
 	if(toggle_fullscreen){toggle_fullscreen=0;windowed=!windowed;window_set_fullscreen(!windowed);}
 	if(!windowed||uimode!=mode_draw)set_tracing=0;
diff --git a/c/io_sdl2.h b/c/io_sdl2.h
index 11e8ad9..6445222 100644
--- a/c/io_sdl2.h
+++ b/c/io_sdl2.h
@@ -2,6 +2,7 @@
 #include <SDL.h>
 
 SDL_Cursor*CURSORS[4];
+int unfocused = 0;
 
 // keyboard keys
 
@@ -131,6 +132,7 @@ void field_input(char*text);
 void process_events(pair disp,pair size,int scale){
 	SDL_Event e;
 	while(SDL_WaitEvent(&e)){
+		if(e.type==SDL_WINDOWEVENT){if(e.window.event==SDL_WINDOWEVENT_FOCUS_GAINED||e.window.event==SDL_WINDOWEVENT_SHOWN){unfocused=0;}else if(e.window.event==SDL_WINDOWEVENT_FOCUS_LOST||e.window.event==SDL_WINDOWEVENT_HIDDEN){unfocused=1;}}
 		if(e.type==SDL_QUIT     )event_quit();
 		if(e.type==SDL_USEREVENT)break;
 		if(e.type==SDL_TEXTINPUT)field_input(e.text.text);
@@ -254,5 +256,5 @@ void io_init(void){
 	SDL_AddTimer((1000/60),tick_pump,NULL);
 }
 void io_run(lv*env){
-	while(!should_exit){tick(env);sync();}
+	while(!should_exit){if(!unfocused)tick(env);sync();}
 }

I haven't tested this thoroughly. It's easy to confirm with some animated pixels that the animation stops when the window loses focus.

jrfondren avatar Mar 12 '25 15:03 jrfondren

There are plenty of legitimate reasons to want Decker to continue executing while unfocused; consider any sort of program that produces audio. At the very least it should avoid "parking" if any scripts are currently executing, events are pending to process, or the background "loop" is active.

JohnEarnest avatar Mar 13 '25 02:03 JohnEarnest

Even the animation that's stopped would be desirable if Decker's being displayed alongside other windows and still intended to be viewable when unfocused. As a proper feature I'd want this as an option under the Decker menu.

This minimal patch seems to work OK with sound.deck, not interrupting sound or scripts manipulating sound, but it freezes the pinwheel animation in a way that suggests that this patch would be bad for loosely synchronized effects.

jrfondren avatar Mar 13 '25 12:03 jrfondren

same here. uses 40% cpu while doing nothing. the code in io_sdl2.h does the right thing in that it uses SDL_WaitEvent (which blocks until something happens) instead of SDL_PollEvent, however then it creates a timer which generates events every 1/60 seconds which wakes up the waitevent. not good. even if in foreground (focused), a repaint should only happen if something changes, like the mouse position.

rofl0r avatar Apr 10 '25 14:04 rofl0r

Another data point: I mostly use Decker on Linux, on an old ~2015 era netbook with a Celeron CPU (~4000 bogomips). A stock build of Decker doing nothing but displaying the default deck takes 80% of a CPU core. If I switch the code to use SDL_RENDERER_ACCELERATED as described by @cmounce, it goes down to 50% or so, with no visual change (that is, it doesn't use bilinear filtering or anything like that).

Screwtapello avatar Sep 24 '25 13:09 Screwtapello