-
Notifications
You must be signed in to change notification settings - Fork 83
/
drawLoop.go
128 lines (118 loc) · 3.45 KB
/
drawLoop.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package oak
import (
"image"
"image/draw"
)
// A Background can be used as a background draw layer. Backgrounds will be drawn as the first
// element in each frame, and are expected to cover up data drawn on the previous frame.
type Background interface {
GetRGBA() *image.RGBA
}
// DrawLoop
// Unless told to stop, the draw channel will repeatedly
// 1. draw the background color to a temporary buffer
// 2. draw all visible rendered elements onto the temporary buffer.
// 3. draw the buffer's data at the viewport's position to the screen.
// 4. publish the screen to display in window.
func (w *Window) drawLoop() {
<-w.drawCh
draw.Draw(w.winBuffers[w.bufferIdx].RGBA(), w.winBuffers[w.bufferIdx].Bounds(), w.bkgFn(), zeroPoint, draw.Src)
w.publish()
drawFrame := func() {
buff := w.winBuffers[w.bufferIdx]
if buff.RGBA() != nil {
// Publish what was drawn last frame to screen, then work on preparing the next frame.
w.publish()
draw.Draw(buff.RGBA(), buff.Bounds(), w.bkgFn(), zeroPoint, draw.Src)
w.DrawStack.PreDraw()
p := w.viewPos
w.DrawStack.DrawToScreen(buff.RGBA(), &p, w.ScreenWidth, w.ScreenHeight)
}
}
drawLoadingFrame := func() {
buff := w.winBuffers[w.bufferIdx]
w.publish()
draw.Draw(buff.RGBA(), w.winBuffers[w.bufferIdx].Bounds(), w.bkgFn(), zeroPoint, draw.Src)
if w.LoadingR != nil {
w.LoadingR.Draw(w.winBuffers[w.bufferIdx].RGBA(), 0, 0)
}
}
if w.config.UnlimitedDrawFrameRate {
// this code is duplicated as an optimization: it's much faster
// to have a 'default' case than to flood a channel, and we can't conditionally
// add a default case to a select.
for {
select {
case <-w.quitCh:
return
case <-w.drawCh:
<-w.drawCh
loadingSelectUnlimited:
for {
select {
case <-w.quitCh:
return
case <-w.drawCh:
break loadingSelectUnlimited
case <-w.animationFrame:
drawLoadingFrame()
case <-w.DrawTicker.C:
drawLoadingFrame()
default:
drawLoadingFrame()
}
}
case f := <-w.betweenDrawCh:
f()
case <-w.animationFrame:
drawFrame()
case <-w.DrawTicker.C:
drawFrame()
default:
drawFrame()
}
}
}
for {
select {
case <-w.quitCh:
return
case <-w.drawCh:
<-w.drawCh
loadingSelect:
for {
select {
case <-w.quitCh:
return
case <-w.drawCh:
break loadingSelect
case <-w.animationFrame:
drawLoadingFrame()
case <-w.DrawTicker.C:
drawLoadingFrame()
}
}
case f := <-w.betweenDrawCh:
f()
case <-w.animationFrame:
drawFrame()
case <-w.DrawTicker.C:
drawFrame()
}
}
}
func (w *Window) publish() {
w.prePublish(w.winBuffers[w.bufferIdx].RGBA())
w.windowTextures[w.bufferIdx].Upload(zeroPoint, w.winBuffers[w.bufferIdx], w.winBuffers[w.bufferIdx].Bounds())
w.Window.Scale(w.windowRect, w.windowTextures[w.bufferIdx], w.windowTextures[w.bufferIdx].Bounds(), draw.Src)
w.Window.Publish()
// every frame, swap buffers. This enables drivers which might hold on to the rgba buffers we publish as if they
// were immutable.
w.bufferIdx = (w.bufferIdx + 1) % bufferCount
}
// DoBetweenDraws will execute the given function in-between draw frames. It will prevent draws from happening until
// the provided function has terminated. DoBetweenDraws will block until the provided function is called within the
// draw loop's schedule, but will not wait for that function itself to terminate.
func (w *Window) DoBetweenDraws(f func()) {
w.betweenDrawCh <- f
}