Skip to content

Commit

Permalink
Use glfw.WaitEvents() for more efficient run loop
Browse files Browse the repository at this point in the history
  • Loading branch information
Jacalz committed Aug 21, 2023
1 parent 0a42b57 commit 4b25d12
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 65 deletions.
11 changes: 5 additions & 6 deletions internal/driver/glfw/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"os"
"runtime"
"sync"
"sync/atomic"

"github.com/fyne-io/image/ico"

Expand Down Expand Up @@ -39,7 +40,7 @@ type gLDriver struct {
windowLock sync.RWMutex
windows []fyne.Window
device *glDevice
done chan interface{}
mainDone uint32
drawDone chan interface{}

animation *animation.Runner
Expand Down Expand Up @@ -102,10 +103,9 @@ func (d *gLDriver) Quit() {
}
fyne.CurrentApp().Lifecycle().(*intapp.Lifecycle).TriggerExitedForeground()
}
defer func() {
recover() // we could be called twice - no safe way to check if d.done is closed
}()
close(d.done)

atomic.StoreUint32(&d.mainDone, 1)
PostEmptyEvent()
}

func (d *gLDriver) addWindow(w *window) {
Expand Down Expand Up @@ -173,7 +173,6 @@ func NewGLDriver() fyne.Driver {
repository.Register("file", intRepo.NewFileRepository())

return &gLDriver{
done: make(chan interface{}),
drawDone: make(chan interface{}),
animation: &animation.Runner{},
}
Expand Down
2 changes: 1 addition & 1 deletion internal/driver/glfw/driver_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ func (d *gLDriver) CurrentKeyModifiers() fyne.KeyModifier {

func catchTerm(d *gLDriver) {
terminateSignals := make(chan os.Signal, 1)
signal.Notify(terminateSignals, syscall.SIGINT, syscall.SIGTERM)
signal.Notify(terminateSignals, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)

for range terminateSignals {
d.Quit()
Expand Down
117 changes: 63 additions & 54 deletions internal/driver/glfw/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package glfw
import (
"runtime"
"sync"
"sync/atomic"
"time"

"fyne.io/fyne/v2"
Expand Down Expand Up @@ -58,6 +59,7 @@ func runOnMain(f func()) {
defer donePool.Put(done)

funcQueue <- funcData{f: f, done: done}
PostEmptyEvent()

<-done
}
Expand Down Expand Up @@ -112,74 +114,81 @@ func (d *gLDriver) runGL() {
if d.trayStart != nil {
d.trayStart()
}

fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStarted()
for {
select {
case <-d.done:
eventTick.Stop()
d.drawDone <- nil // wait for draw thread to stop

// Post empty event to not get stuck in the loop on first iteration.
PostEmptyEvent()

for range eventTick.C {
d.waitForEvents()

// Check if we are done with execution.
if atomic.LoadUint32(&d.mainDone) == 1 {
d.drawDone <- nil // Wait for draw thread to stop.
d.Terminate()
fyne.CurrentApp().Lifecycle().(*app.Lifecycle).TriggerStopped()
return
}

select {
case f := <-funcQueue:
f.f()
if f.done != nil {
f.done <- struct{}{}
f.done <- struct{}{}
default: // Don't get stuck waiting for event.
}

newWindows := []fyne.Window{}
reassign := false
for _, win := range d.windowList() {
w := win.(*window)
if w.viewport == nil {
continue
}
case <-eventTick.C:
d.tryPollEvents()
newWindows := []fyne.Window{}
reassign := false
for _, win := range d.windowList() {
w := win.(*window)
if w.viewport == nil {
continue
}

if w.viewport.ShouldClose() {
reassign = true
w.viewLock.Lock()
w.visible = false
v := w.viewport
w.viewLock.Unlock()

// remove window from window list
v.Destroy()
w.destroy(d)
continue
}
if w.viewport.ShouldClose() {
reassign = true
w.viewLock.Lock()
w.visible = false
v := w.viewport
w.viewLock.Unlock()

// remove window from window list
v.Destroy()
w.destroy(d)
continue
}

w.viewLock.RLock()
expand := w.shouldExpand
fullScreen := w.fullScreen
w.viewLock.RUnlock()

if expand && !fullScreen {
w.fitContent()
w.viewLock.Lock()
shouldExpand := w.shouldExpand
w.shouldExpand = false
view := w.viewport
w.viewLock.Unlock()
if shouldExpand {
view.SetSize(w.shouldWidth, w.shouldHeight)
}
w.viewLock.RLock()
expand := w.shouldExpand
fullScreen := w.fullScreen
w.viewLock.RUnlock()

if expand && !fullScreen {
w.fitContent()
w.viewLock.Lock()
shouldExpand := w.shouldExpand
w.shouldExpand = false
view := w.viewport
w.viewLock.Unlock()
if shouldExpand {
view.SetSize(w.shouldWidth, w.shouldHeight)
}
}

newWindows = append(newWindows, win)
newWindows = append(newWindows, win)

if drawOnMainThread {
d.drawSingleFrame()
}
if drawOnMainThread {
d.drawSingleFrame()
}
if reassign {
d.windowLock.Lock()
d.windows = newWindows
d.windowLock.Unlock()
}
if reassign {
d.windowLock.Lock()
d.windows = newWindows
d.windowLock.Unlock()

if len(newWindows) == 0 {
d.Quit()
}
if len(newWindows) == 0 {
d.Quit()
}
}
}
Expand Down
10 changes: 8 additions & 2 deletions internal/driver/glfw/loop_desktop.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,22 @@ func (d *gLDriver) initGLFW() {
})
}

func (d *gLDriver) tryPollEvents() {
func (d *gLDriver) waitForEvents() {
defer func() {
if r := recover(); r != nil {
fyne.LogError(fmt.Sprint("GLFW poll event error: ", r), nil)
}
}()

glfw.PollEvents() // This call blocks while window is being resized, which prevents freeDirtyTextures from being called
glfw.WaitEvents()
}

func (d *gLDriver) Terminate() {
glfw.Terminate()
}

// PostEmptyEvent posts an event to the GLFW event queue.
// This is used to signal execution to continue from glfw.WaitEvents().
func PostEmptyEvent() {
glfw.PostEmptyEvent()
}
10 changes: 8 additions & 2 deletions internal/driver/glfw/loop_goxjs.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,22 @@ func (d *gLDriver) initGLFW() {
})
}

func (d *gLDriver) tryPollEvents() {
func (d *gLDriver) waitForEvents() {
defer func() {
if r := recover(); r != nil {
fyne.LogError(fmt.Sprint("GLFW poll event error: ", r), nil)
}
}()

glfw.PollEvents() // This call blocks while window is being resized, which prevents freeDirtyTextures from being called
glfw.WaitEvents()
}

func (d *gLDriver) Terminate() {
glfw.Terminate()
}

// PostEmptyEvent posts an event to the GLFW event queue.
// This is used to signal execution to continue from glfw.WaitEvents().
func PostEmptyEvent() {
glfw.PostEmptyEvent()
}

0 comments on commit 4b25d12

Please sign in to comment.