~eliasnaur/gio

83cb3835232bbabb1f7f96c473bcca30ae83c46c — Inkeliz 2 years ago e37deed
app,internal/gl: [wasm] fix context lost

Before that change, Gio could crash when the WebGL context was lost
unexpectedly. Now, Gio will properly handle such situation and
recreate the buffers/resources when context is restored and will
wait until context is recovered.

Signed-off-by: Inkeliz <inkeliz@inkeliz.com>
3 files changed, 62 insertions(+), 6 deletions(-)

M app/gl_js.go
M app/os_js.go
M internal/gl/gl_js.go
M app/gl_js.go => app/gl_js.go +14 -4
@@ 13,6 13,7 @@ import (
type glContext struct {
	ctx js.Value
	cnv js.Value
	w   *window
}

func newContext(w *window) (*glContext, error) {


@@ 32,11 33,15 @@ func newContext(w *window) (*glContext, error) {
	c := &glContext{
		ctx: ctx,
		cnv: w.cnv,
		w:   w,
	}
	return c, nil
}

func (c *glContext) RenderTarget() (gpu.RenderTarget, error) {
	if c.w.contextStatus != contextStatusOkay {
		return nil, gpu.ErrDeviceLost
	}
	return gpu.OpenGLRenderTarget{}, nil
}



@@ 48,9 53,6 @@ func (c *glContext) Release() {
}

func (c *glContext) Present() error {
	if c.ctx.Call("isContextLost").Bool() {
		return errors.New("context lost")
	}
	return nil
}



@@ 61,7 63,15 @@ func (c *glContext) Lock() error {
func (c *glContext) Unlock() {}

func (c *glContext) Refresh() error {
	return nil
	switch c.w.contextStatus {
	case contextStatusLost:
		return errOutOfDate
	case contextStatusRestored:
		c.w.contextStatus = contextStatusOkay
		return gpu.ErrDeviceLost
	default:
		return nil
	}
}

func (w *window) NewContext() (context, error) {

M app/os_js.go => app/os_js.go +38 -1
@@ 24,6 24,14 @@ import (

type ViewEvent struct{}

type contextStatus int

const (
	contextStatusOkay contextStatus = iota
	contextStatusLost
	contextStatusRestored
)

type window struct {
	window                js.Value
	document              js.Value


@@ 54,6 62,8 @@ type window struct {
	// is pending.
	animRequested bool
	wakeups       chan struct{}

	contextStatus contextStatus
}

func newWindow(win *callbacks, options []Option) error {


@@ 162,9 172,25 @@ func (w *window) cleanup() {
}

func (w *window) addEventListeners() {
	w.addEventListener(w.cnv, "webglcontextlost", func(this js.Value, args []js.Value) interface{} {
		args[0].Call("preventDefault")
		w.contextStatus = contextStatusLost
		return nil
	})
	w.addEventListener(w.cnv, "webglcontextrestored", func(this js.Value, args []js.Value) interface{} {
		args[0].Call("preventDefault")
		w.contextStatus = contextStatusRestored

		// Resize is required to force update the canvas content when restored.
		w.cnv.Set("width", 0)
		w.cnv.Set("height", 0)
		w.resize()
		w.requestRedraw()
		return nil
	})
	w.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} {
		w.resize()
		w.chanRedraw <- struct{}{}
		w.requestRedraw()
		return nil
	})
	w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} {


@@ 622,10 648,14 @@ func (w *window) resize() {
}

func (w *window) draw(sync bool) {
	if w.contextStatus == contextStatusLost {
		return
	}
	size, insets, metric := w.getConfig()
	if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
		return
	}

	w.w.Event(frameEvent{
		FrameEvent: system.FrameEvent{
			Now:    time.Now(),


@@ 696,6 726,13 @@ func (w *window) navigationColor(c color.NRGBA) {
	theme.Set("content", fmt.Sprintf("#%06X", []uint8{rgba.R, rgba.G, rgba.B}))
}

func (w *window) requestRedraw() {
	select {
	case w.chanRedraw <- struct{}{}:
	default:
	}
}

func osMain() {
	select {}
}

M internal/gl/gl_js.go => internal/gl/gl_js.go +10 -1
@@ 299,7 299,12 @@ func (f *Functions) BufferSubData(target Enum, offset int, src []byte) {
	f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src))
}
func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
	return Enum(f._checkFramebufferStatus.Invoke(int(target)).Int())
	status := Enum(f._checkFramebufferStatus.Invoke(int(target)).Int())
	if status != FRAMEBUFFER_COMPLETE && f.Ctx.Call("isContextLost").Bool() {
		// If the context is lost, we say that everything is fine. That saves internal/opengl/opengl.go from panic.
		return FRAMEBUFFER_COMPLETE
	}
	return status
}
func (f *Functions) Clear(mask Enum) {
	f._clear.Invoke(int(mask))


@@ 633,6 638,10 @@ func paramVal(v js.Value) int {
		}
	case js.TypeNumber:
		return v.Int()
	case js.TypeUndefined:
		return 0
	case js.TypeNull:
		return 0
	default:
		panic("unknown parameter type")
	}