mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
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>
This commit is contained in:
+14
-4
@@ -13,6 +13,7 @@ import (
|
|||||||
type glContext struct {
|
type glContext struct {
|
||||||
ctx js.Value
|
ctx js.Value
|
||||||
cnv js.Value
|
cnv js.Value
|
||||||
|
w *window
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContext(w *window) (*glContext, error) {
|
func newContext(w *window) (*glContext, error) {
|
||||||
@@ -32,11 +33,15 @@ func newContext(w *window) (*glContext, error) {
|
|||||||
c := &glContext{
|
c := &glContext{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cnv: w.cnv,
|
cnv: w.cnv,
|
||||||
|
w: w,
|
||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *glContext) RenderTarget() (gpu.RenderTarget, error) {
|
func (c *glContext) RenderTarget() (gpu.RenderTarget, error) {
|
||||||
|
if c.w.contextStatus != contextStatusOkay {
|
||||||
|
return nil, gpu.ErrDeviceLost
|
||||||
|
}
|
||||||
return gpu.OpenGLRenderTarget{}, nil
|
return gpu.OpenGLRenderTarget{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,9 +53,6 @@ func (c *glContext) Release() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *glContext) Present() error {
|
func (c *glContext) Present() error {
|
||||||
if c.ctx.Call("isContextLost").Bool() {
|
|
||||||
return errors.New("context lost")
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +63,15 @@ func (c *glContext) Lock() error {
|
|||||||
func (c *glContext) Unlock() {}
|
func (c *glContext) Unlock() {}
|
||||||
|
|
||||||
func (c *glContext) Refresh() error {
|
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) {
|
func (w *window) NewContext() (context, error) {
|
||||||
|
|||||||
+38
-1
@@ -24,6 +24,14 @@ import (
|
|||||||
|
|
||||||
type ViewEvent struct{}
|
type ViewEvent struct{}
|
||||||
|
|
||||||
|
type contextStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
contextStatusOkay contextStatus = iota
|
||||||
|
contextStatusLost
|
||||||
|
contextStatusRestored
|
||||||
|
)
|
||||||
|
|
||||||
type window struct {
|
type window struct {
|
||||||
window js.Value
|
window js.Value
|
||||||
document js.Value
|
document js.Value
|
||||||
@@ -54,6 +62,8 @@ type window struct {
|
|||||||
// is pending.
|
// is pending.
|
||||||
animRequested bool
|
animRequested bool
|
||||||
wakeups chan struct{}
|
wakeups chan struct{}
|
||||||
|
|
||||||
|
contextStatus contextStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWindow(win *callbacks, options []Option) error {
|
func newWindow(win *callbacks, options []Option) error {
|
||||||
@@ -162,9 +172,25 @@ func (w *window) cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *window) addEventListeners() {
|
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.addEventListener(w.visualViewport, "resize", func(this js.Value, args []js.Value) interface{} {
|
||||||
w.resize()
|
w.resize()
|
||||||
w.chanRedraw <- struct{}{}
|
w.requestRedraw()
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
w.addEventListener(w.window, "contextmenu", func(this js.Value, args []js.Value) interface{} {
|
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) {
|
func (w *window) draw(sync bool) {
|
||||||
|
if w.contextStatus == contextStatusLost {
|
||||||
|
return
|
||||||
|
}
|
||||||
size, insets, metric := w.getConfig()
|
size, insets, metric := w.getConfig()
|
||||||
if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
|
if metric == (unit.Metric{}) || size.X == 0 || size.Y == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.w.Event(frameEvent{
|
w.w.Event(frameEvent{
|
||||||
FrameEvent: system.FrameEvent{
|
FrameEvent: system.FrameEvent{
|
||||||
Now: time.Now(),
|
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}))
|
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() {
|
func osMain() {
|
||||||
select {}
|
select {}
|
||||||
}
|
}
|
||||||
|
|||||||
+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))
|
f._bufferSubData.Invoke(int(target), offset, f.byteArrayOf(src))
|
||||||
}
|
}
|
||||||
func (f *Functions) CheckFramebufferStatus(target Enum) Enum {
|
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) {
|
func (f *Functions) Clear(mask Enum) {
|
||||||
f._clear.Invoke(int(mask))
|
f._clear.Invoke(int(mask))
|
||||||
@@ -633,6 +638,10 @@ func paramVal(v js.Value) int {
|
|||||||
}
|
}
|
||||||
case js.TypeNumber:
|
case js.TypeNumber:
|
||||||
return v.Int()
|
return v.Int()
|
||||||
|
case js.TypeUndefined:
|
||||||
|
return 0
|
||||||
|
case js.TypeNull:
|
||||||
|
return 0
|
||||||
default:
|
default:
|
||||||
panic("unknown parameter type")
|
panic("unknown parameter type")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user