forked from joejulian/gio
app: avoid relocking contexts every frame
Signed-off-by: Joe Julian <me@joejulian.name>
This commit is contained in:
+33
-15
@@ -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,10 @@ 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 {
|
||||
w.destroyGPU()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if sync && w.ctx != nil {
|
||||
if err := w.ctx.Refresh(); err != nil {
|
||||
if errors.Is(err, errOutOfDate) {
|
||||
@@ -168,11 +167,19 @@ func (w *Window) validateAndProcess(size image.Point, sync bool, frame *op.Ops,
|
||||
}
|
||||
return err
|
||||
}
|
||||
w.ctxNeedsLock = true
|
||||
}
|
||||
if w.ctx != nil && w.ctxNeedsLock {
|
||||
if err := w.ctx.Lock(); err != nil {
|
||||
w.destroyGPU()
|
||||
return err
|
||||
}
|
||||
w.ctxNeedsLock = false
|
||||
}
|
||||
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 +187,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 +207,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 +509,26 @@ func (c *callbacks) ActionAt(p f32.Point) (system.Action, bool) {
|
||||
return c.w.queue.ActionAt(p)
|
||||
}
|
||||
|
||||
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()
|
||||
w.gpu.Release()
|
||||
w.ctx.Unlock()
|
||||
if err := w.ctx.Lock(); err == nil {
|
||||
w.gpu.Release()
|
||||
w.ctx.Unlock()
|
||||
}
|
||||
w.gpu = nil
|
||||
}
|
||||
if w.ctx != nil {
|
||||
w.ctx.Release()
|
||||
w.ctx = nil
|
||||
w.ctxNeedsLock = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,10 +671,12 @@ func (w *Window) processEvent(e event.Event) bool {
|
||||
w.coalesced.destroy = &e2
|
||||
case ViewEvent:
|
||||
if !e2.Valid() && w.gpu != nil {
|
||||
w.ctx.Lock()
|
||||
w.gpu.Release()
|
||||
if err := w.ctx.Lock(); err == nil {
|
||||
w.gpu.Release()
|
||||
w.ctx.Unlock()
|
||||
}
|
||||
w.gpu = nil
|
||||
w.ctx.Unlock()
|
||||
w.ctxNeedsLock = true
|
||||
}
|
||||
w.coalesced.view = &e2
|
||||
case ConfigEvent:
|
||||
|
||||
+26
-4
@@ -11,18 +11,40 @@ import (
|
||||
"gioui.org/op"
|
||||
)
|
||||
|
||||
func TestValidateAndProcessLocksBeforeRefresh(t *testing.T) {
|
||||
func TestValidateAndProcessRelocksAfterRefresh(t *testing.T) {
|
||||
ctx := &testContext{}
|
||||
w := &Window{
|
||||
ctx: ctx,
|
||||
gpu: &testGPU{},
|
||||
ctx: ctx,
|
||||
gpu: &testGPU{},
|
||||
ctxNeedsLock: false,
|
||||
}
|
||||
|
||||
if err := w.validateAndProcess(image.Pt(320, 240), true, new(op.Ops), nil); err != nil {
|
||||
t.Fatalf("validateAndProcess returned error: %v", err)
|
||||
}
|
||||
|
||||
want := []string{"lock", "refresh", "render-target", "present", "unlock"}
|
||||
want := []string{"refresh", "lock", "render-target", "present"}
|
||||
if got := ctx.ops; !equalStringSlices(got, want) {
|
||||
t.Fatalf("unexpected call order:\n got %v\n want %v", got, want)
|
||||
}
|
||||
if w.ctxNeedsLock {
|
||||
t.Fatalf("context should remain current after a successful frame")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAndProcessSkipsRedundantRelock(t *testing.T) {
|
||||
ctx := &testContext{}
|
||||
w := &Window{
|
||||
ctx: ctx,
|
||||
gpu: &testGPU{},
|
||||
ctxNeedsLock: false,
|
||||
}
|
||||
|
||||
if err := w.validateAndProcess(image.Pt(320, 240), false, new(op.Ops), nil); err != nil {
|
||||
t.Fatalf("validateAndProcess returned error: %v", err)
|
||||
}
|
||||
|
||||
want := []string{"render-target", "present"}
|
||||
if got := ctx.ops; !equalStringSlices(got, want) {
|
||||
t.Fatalf("unexpected call order:\n got %v\n want %v", got, want)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user