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")
}