func showWindow(w *windowImpl) { w.glctxMu.Lock() w.glctx, w.worker = gl.NewContext() w.glctxMu.Unlock() C.doShowWindow(C.uintptr_t(w.id)) }
// loop is the primary drawing loop. // // After Cocoa has captured the initial OS thread for processing Cocoa // events in runApp, it starts loop on another goroutine. It is locked // to an OS thread for its OpenGL context. // // Two Cocoa threads deliver draw signals to loop. The primary source of // draw events is the CVDisplayLink timer, which is tied to the display // vsync. Secondary draw events come from [NSView drawRect:] when the // window is resized. func loop(ctx C.GLintptr) { runtime.LockOSThread() C.makeCurrentContext(ctx) var worker gl.Worker glctx, worker = gl.NewContext() workAvailable := worker.WorkAvailable() for { select { case <-workAvailable: worker.DoWork() case <-draw: loop1: for { select { case <-workAvailable: worker.DoWork() case <-theApp.publish: C.CGLFlushDrawable(C.CGLGetCurrentContext()) theApp.publishResult <- PublishResult{} break loop1 } } drawDone <- struct{}{} } } }
//export drawgl func drawgl(ctx uintptr) { if glctx == nil { C.setContext(unsafe.Pointer(ctx)) glctx, worker = gl.NewContext() workAvailable = worker.WorkAvailable() // TODO(crawshaw): not just on process start. theApp.sendLifecycle(lifecycle.StageFocused) } // TODO(crawshaw): don't send a paint.Event unconditionally. Only send one // if the window actually needs redrawing. theApp.eventsIn <- paint.Event{} for { select { case <-workAvailable: worker.DoWork() case <-theApp.publish: theApp.publishResult <- PublishResult{} return } } }
func showWindow(w *windowImpl) { w.glctxMu.Lock() w.glctx, w.worker = gl.NewContext() w.glctxMu.Unlock() // Show makes an initial call to sizeEvent (via win32.SizeEvent), where // we setup the EGL surface and GL context. win32.Show(syscall.Handle(w.id)) }
func (this *Window) Init() error { wm := TheApp.GetDefaultDisplay().GetWindowManager() wm.AddWindow(this) this.mGL, this.mGLWorker = gl.NewContext() TheApp.RegisterEventCallback(this) this._createWindow() return nil }
func main(f func(screen.Screen)) error { if gl.Version() == "GL_ES_2_0" { return errors.New("gldriver: ES 3 required on X11") } C.startDriver() glctx, worker = gl.NewContext() closec := make(chan struct{}) go func() { f(theScreen) close(closec) }() // heartbeat is a channel that, at regular intervals, directs the select // below to also consider X11 events, not just Go events (channel // communications). // // TODO: select instead of poll. Note that knowing whether to call // C.processEvents needs to select on a file descriptor, and the other // cases below select on Go channels. heartbeat := time.NewTicker(time.Second / 60) workAvailable := worker.WorkAvailable() for { select { case <-closec: return nil case ctx := <-glcontextc: // TODO: do we need to synchronize with seeing a size event for // this window's context before or after calling makeCurrent? // Otherwise, are we racing with the gl.Viewport call? I've // occasionally seen a stale viewport, if the window manager sets // the window width and height to something other than that // requested by XCreateWindow, but it's not easily reproducible. C.makeCurrent(C.uintptr_t(ctx)) case w := <-publishc: C.swapBuffers(C.uintptr_t(w.ctx.(uintptr))) w.publishDone <- screen.PublishResult{} case req := <-uic: ret := req.f() if req.retc != nil { req.retc <- ret } case <-heartbeat.C: C.processEvents() case <-workAvailable: worker.DoWork() } } }
func main(f func(App)) { runtime.LockOSThread() var worker gl.Worker glctx, worker = gl.NewContext() workAvailable := worker.WorkAvailable() C.createWindow() // TODO: send lifecycle events when e.g. the X11 window is iconified or moved off-screen. theApp.sendLifecycle(lifecycle.StageFocused) // TODO: translate X11 expose events to shiny paint events, instead of // sending this synthetic paint event as a hack. theApp.eventsIn <- paint.Event{} donec := make(chan struct{}) go func() { f(theApp) close(donec) }() // TODO: can we get the actual vsync signal? ticker := time.NewTicker(time.Second / 60) defer ticker.Stop() var tc <-chan time.Time for { select { case <-donec: return case <-workAvailable: worker.DoWork() case <-theApp.publish: C.swapBuffers() tc = ticker.C case <-tc: tc = nil theApp.publishResult <- PublishResult{} } C.processEvents() } }
// NewContext creates an OpenGL ES context with a dedicated processing thread. func NewContext() (gl.Context, error) { glctx, worker := gl.NewContext() errCh := make(chan error) workAvailable := worker.WorkAvailable() go func() { runtime.LockOSThread() err := surfaceCreate() errCh <- err if err != nil { return } for range workAvailable { worker.DoWork() } }() if err := <-errCh; err != nil { return nil, err } return glctx, nil }
func init() { theApp.eventsIn = pump(theApp.eventsOut) theApp.glctx, theApp.worker = gl.NewContext() }
func mainUI(vm, jniEnv, ctx uintptr) error { var worker gl.Worker glctx, worker = gl.NewContext() workAvailable := worker.WorkAvailable() donec := make(chan struct{}) go func() { mainUserFn(theApp) close(donec) }() var pixelsPerPt float32 var orientation size.Orientation // Android can send a windowRedrawNeeded event any time, including // in the middle of a paint cycle. The redraw event may have changed // the size of the screen, so any partial painting is now invalidated. // We must also not return to Android (via sending on windowRedrawDone) // until a complete paint with the new configuration is complete. // // 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 Generation agrees. If possible, // windowRedrawDone is signalled, allowing onNativeWindowRedrawNeeded // to return. // // TODO: is this still needed? var redrawGen uint32 for { select { case <-donec: return nil case cfg := <-windowConfigChange: pixelsPerPt = cfg.pixelsPerPt orientation = cfg.orientation case w := <-windowRedrawNeeded: if C.surface == nil { if errStr := C.createEGLSurface(w); errStr != nil { return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError()) } } theApp.sendLifecycle(lifecycle.StageFocused) widthPx := int(C.ANativeWindow_getWidth(w)) heightPx := int(C.ANativeWindow_getHeight(w)) theApp.eventsIn <- size.Event{ WidthPx: widthPx, HeightPx: heightPx, WidthPt: geom.Pt(float32(widthPx) / pixelsPerPt), HeightPt: geom.Pt(float32(heightPx) / pixelsPerPt), PixelsPerPt: pixelsPerPt, Orientation: orientation, } redrawGen++ theApp.eventsIn <- paint.Event{redrawGen} case <-windowDestroyed: if C.surface != nil { if errStr := C.destroyEGLSurface(); errStr != nil { return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError()) } } C.surface = nil theApp.sendLifecycle(lifecycle.StageAlive) case <-workAvailable: worker.DoWork() case <-theApp.publish: // TODO: compare a generation number to redrawGen for stale paints? 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: } theApp.publishResult <- PublishResult{} } } }
func NewContext() (*Context, error) { c := &Context{} c.gl, c.worker = mgl.NewContext() return c, nil }