From 0c8ff9f648bfa8cc43ec9de8edf3636144cfe1f6 Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Wed, 29 Jul 2015 12:26:32 -0400 Subject: [PATCH] app, event/paint: plumb through paint Generation Change-Id: I82bac0dc24943c64560a0bac4687cbbbb8b5c328 Reviewed-on: https://go-review.googlesource.com/12841 Reviewed-by: Nigel Tao --- app/app.go | 10 ++++++---- app/loop_android.go | 35 ++++++++++++++++------------------- event/paint/paint.go | 5 ++++- example/audio/main.go | 2 +- example/basic/main.go | 2 +- example/network/main.go | 2 +- example/sprite/main.go | 2 +- 7 files changed, 30 insertions(+), 28 deletions(-) diff --git a/app/app.go b/app/app.go index 7c96e3e..b9a11e2 100644 --- a/app/app.go +++ b/app/app.go @@ -9,6 +9,7 @@ package app import ( "golang.org/x/mobile/event/config" "golang.org/x/mobile/event/lifecycle" + "golang.org/x/mobile/event/paint" "golang.org/x/mobile/gl" _ "golang.org/x/mobile/internal/mobileinit" ) @@ -38,7 +39,8 @@ type App interface { Send(event interface{}) // EndPaint flushes any pending OpenGL commands or buffers to the screen. - EndPaint() + // If EndPaint is called with an old generation number, it is ignored. + EndPaint(paint.Event) } var ( @@ -47,7 +49,7 @@ var ( eventsOut = make(chan interface{}) eventsIn = pump(eventsOut) - endPaint = make(chan struct{}, 1) + endPaint = make(chan paint.Event, 1) ) func sendLifecycle(to lifecycle.Stage) { @@ -71,7 +73,7 @@ func (app) Send(event interface{}) { eventsIn <- event } -func (app) EndPaint() { +func (app) EndPaint(e paint.Event) { // gl.Flush is a lightweight (on modern GL drivers) blocking call // that ensures all GL functions pending in the gl package have // been passed onto the GL driver before the app package attempts @@ -80,7 +82,7 @@ func (app) EndPaint() { // This enforces that the final receive (for this paint cycle) on // gl.WorkAvailable happens before the send on endPaint. gl.Flush() - endPaint <- struct{}{} + endPaint <- e } var filters []func(interface{}) interface{} diff --git a/app/loop_android.go b/app/loop_android.go index 02697ef..cda023b 100644 --- a/app/loop_android.go +++ b/app/loop_android.go @@ -113,10 +113,10 @@ func main(f func(App)) { // // When a windowRedrawNeeded request comes in, we increment redrawGen // (Gen is short for generation number), and do not make a paint cycle - // visible on <-endPaint unless paintGen agrees. If possible, + // visible on <-endPaint unless Generation agrees. If possible, // windowRedrawDone is signalled, allowing onNativeWindowRedrawNeeded // to return. - var redrawGen, paintGen uint32 + var redrawGen uint32 for { if q != nil { @@ -151,10 +151,7 @@ func main(f func(App)) { redrawGen++ if newWindow { // New window, begin paint loop. - // TODO(crawshaw): If we get a <-windowCreated in between sending a - // paint.Event and receiving on endPaint, we can double-paint. (BUG) - paintGen = redrawGen - eventsIn <- paint.Event{} + eventsIn <- paint.Event{redrawGen} } case <-windowDestroyed: if C.surface != nil { @@ -167,24 +164,24 @@ func main(f func(App)) { sendLifecycle(lifecycle.StageAlive) case <-gl.WorkAvailable: gl.DoWork() - case <-endPaint: - if paintGen == redrawGen { - if C.surface != nil { - // eglSwapBuffers blocks until vsync. - if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE { - log.Printf("app: failed to swap buffers (%s)", eglGetError()) - } - } - select { - case windowRedrawDone <- struct{}{}: - default: + case p := <-endPaint: + if p.Generation != redrawGen { + continue + } + if C.surface != nil { + // eglSwapBuffers blocks until vsync. + if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE { + log.Printf("app: failed to swap buffers (%s)", eglGetError()) } } + select { + case windowRedrawDone <- struct{}{}: + default: + } if C.surface != nil { redrawGen++ - eventsIn <- paint.Event{} + eventsIn <- paint.Event{redrawGen} } - paintGen = redrawGen } } } diff --git a/event/paint/paint.go b/event/paint/paint.go index e659c59..f63f758 100644 --- a/event/paint/paint.go +++ b/event/paint/paint.go @@ -9,4 +9,7 @@ package paint // import "golang.org/x/mobile/event/paint" // Event indicates that the app is ready to paint the next frame of the GUI. A // frame is completed by calling the App's EndPaint method. -type Event struct{} +type Event struct { + // Generation is a monotonically increasing generation number. + Generation uint32 +} diff --git a/example/audio/main.go b/example/audio/main.go index 7115057..bbe7486 100644 --- a/example/audio/main.go +++ b/example/audio/main.go @@ -84,7 +84,7 @@ func main() { cfg = e case paint.Event: onPaint() - a.EndPaint() + a.EndPaint(e) } } }) diff --git a/example/basic/main.go b/example/basic/main.go index d04c621..666800d 100644 --- a/example/basic/main.go +++ b/example/basic/main.go @@ -72,7 +72,7 @@ func main() { touchLoc = geom.Point{c.WidthPt / 2, c.HeightPt / 2} case paint.Event: onPaint(c) - a.EndPaint() + a.EndPaint(e) case touch.Event: touchLoc = e.Loc } diff --git a/example/network/main.go b/example/network/main.go index e720df3..dd40994 100644 --- a/example/network/main.go +++ b/example/network/main.go @@ -61,7 +61,7 @@ func main() { c = e case paint.Event: onDraw(c) - a.EndPaint() + a.EndPaint(e) } } }) diff --git a/example/sprite/main.go b/example/sprite/main.go index 0922b3a..cdab6b8 100644 --- a/example/sprite/main.go +++ b/example/sprite/main.go @@ -63,7 +63,7 @@ func main() { c = e case paint.Event: onPaint(c) - a.EndPaint() + a.EndPaint(e) } } })