mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
app: don't delay FrameEvent.Frame by v-sync latency
We should return as soon as possible from FrameEvent.Frame to allow the main
goroutine to continue processing other tasks. Whereas GPU.Frame may touch
the frame ops, GPU.Present will not, so this change moves Present to after
returning from FrameEvent.Frame.
This is a variant of 38ff78df5d that
works on OpenGL where context locking is required.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+13
-13
@@ -174,7 +174,7 @@ func (w *Window) update(frame *op.Ops) {
|
||||
<-w.frameAck
|
||||
}
|
||||
|
||||
func (w *Window) validateAndProcess(d driver, size image.Point, sync bool, frame *op.Ops) error {
|
||||
func (w *Window) validateAndProcess(d driver, size image.Point, sync bool, frame *op.Ops, signal chan<- struct{}) error {
|
||||
for {
|
||||
if w.gpu == nil && !w.nocontext {
|
||||
var err error
|
||||
@@ -214,7 +214,7 @@ func (w *Window) validateAndProcess(d driver, size image.Point, sync bool, frame
|
||||
w.gpu = gpu
|
||||
}
|
||||
if w.gpu != nil {
|
||||
if err := w.render(frame, size); err != nil {
|
||||
if err := w.frame(frame, size, signal); err != nil {
|
||||
if errors.Is(err, errOutOfDate) {
|
||||
// GPU surface needs refreshing.
|
||||
sync = true
|
||||
@@ -227,12 +227,11 @@ func (w *Window) validateAndProcess(d driver, size image.Point, sync bool, frame
|
||||
return err
|
||||
}
|
||||
}
|
||||
w.queue.q.Frame(frame)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Window) render(frame *op.Ops, viewport image.Point) error {
|
||||
func (w *Window) frame(frame *op.Ops, viewport image.Point, signal chan<- struct{}) error {
|
||||
if err := w.ctx.Lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -251,6 +250,11 @@ func (w *Window) render(frame *op.Ops, viewport image.Point) error {
|
||||
if err := w.gpu.Frame(frame, target, viewport); err != nil {
|
||||
return err
|
||||
}
|
||||
w.queue.q.Frame(frame)
|
||||
// We're done with frame, let the client continue.
|
||||
if signal != nil {
|
||||
signal <- struct{}{}
|
||||
}
|
||||
return w.ctx.Present()
|
||||
}
|
||||
|
||||
@@ -672,18 +676,18 @@ func (w *Window) destroyGPU() {
|
||||
// waitFrame waits for the client to either call FrameEvent.Frame
|
||||
// or to continue event handling. It returns whether the client
|
||||
// called Frame or not.
|
||||
func (w *Window) waitFrame(d driver) (*op.Ops, bool) {
|
||||
func (w *Window) waitFrame(d driver) (*op.Ops, chan<- struct{}) {
|
||||
for {
|
||||
select {
|
||||
case f := <-w.driverFuncs:
|
||||
f(d)
|
||||
case frame := <-w.frames:
|
||||
// The client called FrameEvent.Frame.
|
||||
return frame, true
|
||||
return frame, w.frameAck
|
||||
case w.out <- ackEvent:
|
||||
// The client ignored FrameEvent and continued processing
|
||||
// events.
|
||||
return nil, false
|
||||
return nil, nil
|
||||
case <-w.immediateRedraws:
|
||||
// Invalidate was called during frame processing.
|
||||
w.setNextFrame(time.Time{})
|
||||
@@ -799,15 +803,11 @@ func (w *Window) processEvent(d driver, e event.Event) {
|
||||
size := e2.Size // save the initial window size as the decorations will change it.
|
||||
e2.FrameEvent.Size = w.decorate(d, e2.FrameEvent, wrapper)
|
||||
w.out <- e2.FrameEvent
|
||||
frame, gotFrame := w.waitFrame(d)
|
||||
frame, signal := w.waitFrame(d)
|
||||
cl := clip.Rect(image.Rectangle{Max: e2.FrameEvent.Size}).Push(wrapper)
|
||||
ops.AddCall(&wrapper.Internal, &frame.Internal, ops.PC{}, ops.PCFor(&frame.Internal))
|
||||
cl.Pop()
|
||||
err := w.validateAndProcess(d, size, e2.Sync, wrapper)
|
||||
if gotFrame {
|
||||
// We're done with frame, let the client continue.
|
||||
w.frameAck <- struct{}{}
|
||||
}
|
||||
err := w.validateAndProcess(d, size, e2.Sync, wrapper, signal)
|
||||
if err != nil {
|
||||
w.destroyGPU()
|
||||
w.out <- system.DestroyEvent{Err: err}
|
||||
|
||||
Reference in New Issue
Block a user