Skip to content

Commit 367f63e

Browse files
committed
Reduce energy consumption when screen is not changing
For static editors too much energy is being consumed in order to draw the screen pixels each frame. This commit changes how often screen pixels are replaced in graphics card memory.
1 parent f592ff6 commit 367f63e

File tree

1 file changed

+59
-26
lines changed

1 file changed

+59
-26
lines changed

ebitengine/game.go

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,22 @@ import (
1111
"github.com/hajimehoshi/ebiten/v2"
1212

1313
"github.com/elgopher/pi"
14+
"github.com/elgopher/pi/image"
1415
"github.com/elgopher/pi/key"
1516
)
1617

1718
type game struct {
1819
ready atomic.Bool
19-
screenDataRGBA []byte // reused RGBA pixels
20-
screenChanged bool
2120
shouldSkipNextDraw bool
21+
screenFrame screenFrame
22+
}
23+
24+
type screenFrame struct {
25+
changed bool
26+
pix []byte
27+
palette [256]image.RGB
28+
pald pi.PalMapping
29+
screenDataRGBA []byte // reused RGBA pixels
2230
}
2331

2432
func (e *game) Update() error {
@@ -58,11 +66,39 @@ func (e *game) Update() error {
5866
}
5967
}
6068

61-
e.screenChanged = true
69+
e.screenFrame.update()
6270

6371
return nil
6472
}
6573

74+
func (f *screenFrame) update() {
75+
scrPix := pi.Scr().Pix()
76+
screenChanged := !slicesEqual(f.pix, scrPix) || f.palette != pi.Palette || f.pald != pi.Pald
77+
if screenChanged {
78+
f.changed = true
79+
80+
if len(f.pix) != len(scrPix) {
81+
f.pix = make([]byte, len(scrPix))
82+
}
83+
copy(f.pix, scrPix)
84+
f.pald = pi.Pald
85+
f.palette = pi.Palette
86+
}
87+
}
88+
89+
// replace with slices.Equal after upgrading to go 1.21
90+
func slicesEqual[S ~[]E, E comparable](s1, s2 S) bool {
91+
if len(s1) != len(s2) {
92+
return false
93+
}
94+
for i := range s1 {
95+
if s1[i] != s2[i] {
96+
return false
97+
}
98+
}
99+
return true
100+
}
101+
66102
func handleKeyboardShortcuts() {
67103
f11 := key.Duration[key.F11] == 1
68104
altEnter := key.Duration[key.Enter] == 1 && key.Duration[key.Alt] > 0
@@ -77,33 +113,30 @@ func (e *game) Draw(screen *ebiten.Image) {
77113
return
78114
}
79115

80-
// Ebitengine executes Draw based on display frequency.
81-
// But the screen is changed at most 30 times per second.
82-
// That's why there is no need to write pixels more often
83-
// than 30 times per second.
84-
if e.screenChanged {
85-
e.writeScreenPixels(screen)
86-
e.screenChanged = false
87-
}
116+
e.screenFrame.writeScreenPixels(screen)
88117
}
89118

90-
func (e *game) writeScreenPixels(screen *ebiten.Image) {
91-
pix := pi.Scr().Pix()
92-
if e.screenDataRGBA == nil || len(e.screenDataRGBA)/4 != len(pix) {
93-
e.screenDataRGBA = make([]byte, len(pix)*4)
94-
}
119+
func (f *screenFrame) writeScreenPixels(screen *ebiten.Image) {
120+
if f.changed {
121+
f.changed = false
95122

96-
offset := 0
97-
for _, col := range pix {
98-
rgb := pi.Palette[pi.Pald[col]]
99-
e.screenDataRGBA[offset] = rgb.R
100-
e.screenDataRGBA[offset+1] = rgb.G
101-
e.screenDataRGBA[offset+2] = rgb.B
102-
e.screenDataRGBA[offset+3] = 0xff
103-
offset += 4
104-
}
123+
pix := pi.Scr().Pix()
124+
if f.screenDataRGBA == nil || len(f.screenDataRGBA)/4 != len(pix) {
125+
f.screenDataRGBA = make([]byte, len(pix)*4)
126+
}
127+
128+
offset := 0
129+
for _, col := range pix {
130+
rgb := pi.Palette[pi.Pald[col]]
131+
f.screenDataRGBA[offset] = rgb.R
132+
f.screenDataRGBA[offset+1] = rgb.G
133+
f.screenDataRGBA[offset+2] = rgb.B
134+
f.screenDataRGBA[offset+3] = 0xff
135+
offset += 4
136+
}
105137

106-
screen.WritePixels(e.screenDataRGBA)
138+
screen.WritePixels(f.screenDataRGBA)
139+
}
107140
}
108141

109142
func (e *game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {

0 commit comments

Comments
 (0)