app: recover from transient Present errors

Some GPU APIs such as Direct3D can return an error after drawing
a frame indicating a transient device error. Recreate device and
retry if it happens.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2020-02-26 22:50:05 +01:00
parent 29d36e11ee
commit 6213daa3e9
2 changed files with 40 additions and 29 deletions
+5
View File
@@ -40,6 +40,11 @@ type Context interface {
Unlock() Unlock()
} }
// ErrDeviceLost is returned from Context.Present when
// the underlying GPU device is gone and should be
// recreated.
var ErrDeviceLost = errors.New("GPU device lost")
// Driver is the interface for the platform implementation // Driver is the interface for the platform implementation
// of a window. // of a window.
type Driver interface { type Driver interface {
+35 -29
View File
@@ -252,8 +252,7 @@ func (w *Window) run(opts *window.Options) {
case system.StageEvent: case system.StageEvent:
if w.loop != nil { if w.loop != nil {
if e2.Stage < system.StageRunning { if e2.Stage < system.StageRunning {
w.loop.Release() w.destroyGPU()
w.loop = nil
} else { } else {
w.loop.Refresh() w.loop.Refresh()
} }
@@ -274,24 +273,10 @@ func (w *Window) run(opts *window.Options) {
w.hasNextFrame = false w.hasNextFrame = false
e2.Frame = w.update e2.Frame = w.update
w.out <- e2.FrameEvent w.out <- e2.FrameEvent
var err error
if w.loop != nil { if w.loop != nil {
if e2.Sync { if e2.Sync {
w.loop.Refresh() w.loop.Refresh()
} }
if err = w.loop.Flush(); err != nil {
w.loop.Release()
w.loop = nil
}
} else {
var ctx window.Context
ctx, err = w.driver.NewContext()
if err == nil {
w.loop, err = newLoop(ctx)
if err != nil {
ctx.Release()
}
}
} }
var frame *op.Ops var frame *op.Ops
// Wait for either a frame or an ack event to go // Wait for either a frame or an ack event to go
@@ -303,25 +288,46 @@ func (w *Window) run(opts *window.Options) {
gotFrame = true gotFrame = true
case w.out <- ackEvent: case w.out <- ackEvent:
} }
if err != nil { var err error
if gotFrame { for {
w.frameAck <- struct{}{} if w.loop != nil {
if err = w.loop.Flush(); err != nil {
w.destroyGPU()
if err != window.ErrDeviceLost {
break
}
}
}
if w.loop == nil {
var ctx window.Context
ctx, err = w.driver.NewContext()
if err != nil {
break
}
w.loop, err = newLoop(ctx)
if err != nil {
ctx.Release()
break
}
}
w.draw(frameStart, e2.Size, frame)
if e2.Sync {
if err = w.loop.Flush(); err != nil {
w.destroyGPU()
}
}
if err != window.ErrDeviceLost {
break
} }
w.destroy(err)
return
} }
w.draw(frameStart, e2.Size, frame)
if gotFrame { if gotFrame {
// We're done with frame, let the client continue. // We're done with frame, let the client continue.
w.frameAck <- struct{}{} w.frameAck <- struct{}{}
} }
if e2.Sync { if err != nil {
if err := w.loop.Flush(); err != nil { w.destroyGPU()
w.loop.Release() w.destroy(err)
w.loop = nil return
w.destroy(err)
return
}
} }
case *system.CommandEvent: case *system.CommandEvent:
w.out <- e w.out <- e