mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-05 09:25:38 +00:00
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:
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user