app: avoid relocking contexts every frame

Signed-off-by: Joe Julian <me@joejulian.name>
This commit is contained in:
Joe Julian
2026-04-21 06:32:24 -07:00
parent 7bb7a1407f
commit d9b0c8c1c5
+39 -10
View File
@@ -46,6 +46,10 @@ type Window struct {
ctx context
gpu gpu.GPU
// ctxNeedsLock tracks whether the rendering context must be made
// current again before the next GPU operation. Refresh paths, surface
// loss, and explicit unlocks all invalidate the current binding.
ctxNeedsLock bool
// timer tracks the delayed invalidate goroutine.
timer struct {
// quit is shuts down the goroutine.
@@ -146,15 +150,14 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
if err != nil {
return err
}
w.ctxNeedsLock = true
sync = true
}
}
if w.ctx != nil {
if err := w.ctx.Lock(); err != nil {
if err := w.lockContext(); err != nil {
w.destroyGPU()
return err
}
}
if sync && w.ctx != nil {
if err := w.ctx.Refresh(); err != nil {
if errors.Is(err, errOutOfDate) {
@@ -168,11 +171,16 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
}
return err
}
w.unlockContext()
if err := w.lockContext(); err != nil {
w.destroyGPU()
return err
}
}
if w.gpu == nil && !w.nocontext {
gpu, err := gpu.New(w.ctx.API())
if err != nil {
w.ctx.Unlock()
w.unlockContext()
w.destroyGPU()
return err
}
@@ -180,7 +188,7 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
}
if w.gpu != nil {
if err := w.frame(frame, size); err != nil {
w.ctx.Unlock()
w.unlockContext()
if errors.Is(err, errOutOfDate) {
// GPU surface needs refreshing.
sync = true
@@ -200,7 +208,6 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
var err error
if w.gpu != nil {
err = w.ctx.Present()
w.ctx.Unlock()
}
return err
}
@@ -503,16 +510,37 @@ func (c *callbacks) ActionAt(p f32.Point) (system.Action, bool) {
return c.w.queue.ActionAt(p)
}
func (w *Window) lockContext() error {
if w.ctx == nil || !w.ctxNeedsLock {
return nil
}
if err := w.ctx.Lock(); err != nil {
return err
}
w.ctxNeedsLock = false
return nil
}
func (w *Window) unlockContext() {
if w.ctx == nil || w.ctxNeedsLock {
return
}
w.ctx.Unlock()
w.ctxNeedsLock = true
}
func (w *Window) destroyGPU() {
if w.gpu != nil {
w.ctx.Lock()
if err := w.lockContext(); err == nil {
w.gpu.Release()
w.ctx.Unlock()
w.unlockContext()
}
w.gpu = nil
}
if w.ctx != nil {
w.ctx.Release()
w.ctx = nil
w.ctxNeedsLock = false
}
}
@@ -655,10 +683,11 @@ func (w *Window) processEvent(e event.Event) bool {
w.coalesced.destroy = &e2
case ViewEvent:
if !e2.Valid() && w.gpu != nil {
w.ctx.Lock()
if err := w.lockContext(); err == nil {
w.gpu.Release()
w.unlockContext()
}
w.gpu = nil
w.ctx.Unlock()
}
w.coalesced.view = &e2
case ConfigEvent: