// 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. // // The loop processes GL calls until a publish event appears. // Then it runs any remaining GL calls and flushes the screen. // // As NSOpenGLCPSwapInterval is set to 1, the call to CGLFlushDrawable // blocks until the screen refresh. func (a *app) loop(ctx C.GLintptr) { runtime.LockOSThread() C.makeCurrentContext(ctx) workAvailable := a.worker.WorkAvailable() for { select { case <-workAvailable: a.worker.DoWork() case <-theApp.publish: loop1: for { select { case <-workAvailable: a.worker.DoWork() default: break loop1 } } C.CGLFlushDrawable(C.CGLGetCurrentContext()) theApp.publishResult <- PublishResult{} select { case drawDone <- struct{}{}: default: } } } }
// 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{}{} } } }
// 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) for range draw { eventsIn <- paint.Event{} loop1: for { select { case <-gl.WorkAvailable: gl.DoWork() case <-endPaint: C.CGLFlushDrawable(C.CGLGetCurrentContext()) break loop1 } } drawDone <- struct{}{} } }
// drawLoop is the primary drawing loop. // // After Cocoa has created an NSWindow on the initial OS thread for // processing Cocoa events in doNewWindow, it starts drawLoop on another // goroutine. It is locked to an OS thread for its OpenGL context. // // Two Cocoa threads deliver draw signals to drawLoop. 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 drawLoop(w *windowImpl) { runtime.LockOSThread() // TODO(crawshaw): there are several problematic issues around having // a draw loop per window, but resolving them requires some thought. // Firstly, nothing should race on gl.DoWork, so only one person can // do that at a time. Secondly, which GL ctx we use matters. A ctx // carries window-specific state (for example, the current glViewport // value), so we only want to run GL commands on the right context // between a <-w.draw and a <-w.drawDone. Thirdly, some GL functions // can be legitimately called outside of a window draw cycle, for // example, gl.CreateTexture. It doesn't matter which GL ctx we use // for that, but we have to use a valid one. So if a window gets // closed, it's important we swap the default ctx. More work needed. // // Similarly, should each window have its own endPaint channel, or should // the single dedicated draw loop have a single dedicated channel? C.makeCurrentContext(C.uintptr_t(w.ctx)) workAvailable := w.worker.WorkAvailable() // TODO(crawshaw): exit this goroutine on Release. for { select { case <-workAvailable: w.worker.DoWork() case <-w.draw: // TODO(crawshaw): don't send a paint.Event unconditionally. Only // send one if the window actually needs redrawing. w.Send(paint.Event{}) loop: for { select { case <-workAvailable: w.worker.DoWork() case <-w.publish: C.CGLFlushDrawable(C.CGLGetCurrentContext()) break loop } } w.drawDone <- struct{}{} } } }
// 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) for { select { case <-gl.WorkAvailable: gl.DoWork() case <-draw: loop1: for { select { case <-gl.WorkAvailable: gl.DoWork() case <-publish: C.CGLFlushDrawable(C.CGLGetCurrentContext()) publishResult <- PublishResult{} break loop1 } } drawDone <- struct{}{} } } }
// drawLoop is the primary drawing loop. // // After Cocoa has created an NSWindow on the initial OS thread for // processing Cocoa events in newWindow, it starts drawLoop on another // goroutine. It is locked to an OS thread for its OpenGL context. // // Two Cocoa threads deliver draw signals to drawLoop. 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 (w *windowImpl) drawLoop(ctx uintptr) { runtime.LockOSThread() // TODO(crawshaw): there are several problematic issues around having // a draw loop per window, but resolving them requires some thought. // Firstly, nothing should race on gl.DoWork, so only one person can // do that at a time. Secondly, which GL ctx we use matters. A ctx // carries window-specific state (for example, the current glViewport // value), so we only want to run GL commands on the right context // between a <-w.draw and a <-w.drawDone. Thirdly, some GL functions // can be legitimately called outside of a window draw cycle, for // example, gl.CreateTexture. It doesn't matter which GL ctx we use // for that, but we have to use a valid one. So if a window gets // closed, it's important we swap the default ctx. More work needed. C.makeCurrentContext(C.uintptr_t(ctx)) // TODO(crawshaw): exit this goroutine on Release. for { select { case <-gl.WorkAvailable: gl.DoWork() case <-w.draw: w.Send(paint.Event{}) loop: for { select { case <-gl.WorkAvailable: gl.DoWork() case <-w.endPaint: C.CGLFlushDrawable(C.CGLGetCurrentContext()) break loop } } w.drawDone <- struct{}{} } } }
func (this *Window) _flushContext() { C.CGLFlushDrawable(C.CGLGetCurrentContext()) }