graphcis: Bug fix: Can't dispose textures/framebuffers after the context is lost (fixing #305)

This commit is contained in:
Hajime Hoshi 2017-01-22 01:37:08 +09:00
parent d3e1f91511
commit 6e897d1479
7 changed files with 24 additions and 19 deletions

View File

@ -36,6 +36,7 @@ type graphicsContext struct {
screen *Image screen *Image
screenScale float64 screenScale float64
initialized int32 initialized int32
invalidated bool
} }
func (c *graphicsContext) GLContext() *opengl.Context { func (c *graphicsContext) GLContext() *opengl.Context {
@ -45,6 +46,14 @@ func (c *graphicsContext) GLContext() *opengl.Context {
return ui.GLContext() return ui.GLContext()
} }
func (c *graphicsContext) Invalidate() {
// Note that this is called only on browsers so far.
// TODO: On mobiles, this function is not called and instead IsTexture is called
// to detect if the context is lost. This is simple but might not work on some platforms.
// Should Invalidate be called explicitly?
c.invalidated = true
}
func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error { func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error {
if c.screen != nil { if c.screen != nil {
if err := c.screen.Dispose(); err != nil { if err := c.screen.Dispose(); err != nil {
@ -91,6 +100,9 @@ func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale flo
} }
func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) { func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) {
if c.invalidated {
return true, nil
}
// FlushCommands is required because c.offscreen.impl might not have an actual texture. // FlushCommands is required because c.offscreen.impl might not have an actual texture.
if err := graphics.FlushCommands(context); err != nil { if err := graphics.FlushCommands(context); err != nil {
return false, err return false, err
@ -178,5 +190,6 @@ func (c *graphicsContext) restore(context *opengl.Context) error {
if err := theImagesForRestoring.restore(context); err != nil { if err := theImagesForRestoring.restore(context); err != nil {
return err return err
} }
c.invalidated = false
return nil return nil
} }

View File

@ -94,17 +94,8 @@ func (i *images) resetPixelsIfDependingOn(target *Image, context *opengl.Context
func (i *images) restore(context *opengl.Context) error { func (i *images) restore(context *opengl.Context) error {
i.m.Lock() i.m.Lock()
defer i.m.Unlock() defer i.m.Unlock()
// Dispose all images first because framebuffer/texture numbers can be reused. // Framebuffers/textures cannot be disposed since framebuffers/textures that
// If framebuffers/textures are not disposed here, a newly created framebuffer/texture // don't belong to the current context.
// number can be a same number as existing one.
for img := range i.images {
if img.isDisposed() {
continue
}
if err := img.restorable.DisposeOnlyImage(); err != nil {
return err
}
}
imagesWithoutDependency := []*imageImpl{} imagesWithoutDependency := []*imageImpl{}
imagesWithDependency := []*imageImpl{} imagesWithDependency := []*imageImpl{}
for img := range i.images { for img := range i.images {

View File

@ -77,6 +77,7 @@ func (c *runContext) updateFPS(fps float64) {
type GraphicsContext interface { type GraphicsContext interface {
SetSize(width, height int, scale float64) error SetSize(width, height int, scale float64) error
UpdateAndDraw(context *opengl.Context, updateCount int) error UpdateAndDraw(context *opengl.Context, updateCount int) error
Invalidate()
} }
type loopGraphicsContext struct { type loopGraphicsContext struct {
@ -92,6 +93,10 @@ func (g *loopGraphicsContext) Update() error {
return g.runContext.render(g.graphicsContext) return g.runContext.render(g.graphicsContext)
} }
func (g *loopGraphicsContext) Invalidate() {
g.graphicsContext.Invalidate()
}
func Run(g GraphicsContext, width, height int, scale float64, title string, fps int) (err error) { func Run(g GraphicsContext, width, height int, scale float64, title string, fps int) (err error) {
if currentRunContext != nil { if currentRunContext != nil {
return errors.New("loop: The game is already running") return errors.New("loop: The game is already running")

View File

@ -220,7 +220,8 @@ func (c *Context) DeleteTexture(t Texture) {
func (c *Context) IsTexture(t Texture) bool { func (c *Context) IsTexture(t Texture) bool {
gl := c.gl gl := c.gl
return gl.IsTexture(t.Object) b := gl.IsTexture(t.Object)
return b
} }
func (c *Context) TexSubImage2D(p []uint8, width, height int) { func (c *Context) TexSubImage2D(p []uint8, width, height int) {

View File

@ -304,13 +304,6 @@ func (p *Image) Dispose() error {
return nil return nil
} }
func (p *Image) DisposeOnlyImage() error {
if err := p.image.Dispose(); err != nil {
return err
}
return nil
}
func (p *Image) IsInvalidated(context *opengl.Context) bool { func (p *Image) IsInvalidated(context *opengl.Context) bool {
return p.image.IsInvalidated(context) return p.image.IsInvalidated(context)
} }

View File

@ -17,6 +17,7 @@ package ui
type GraphicsContext interface { type GraphicsContext interface {
SetSize(width, height int, scale float64) error SetSize(width, height int, scale float64) error
Update() error Update() error
Invalidate()
} }
type RegularTermination struct { type RegularTermination struct {

View File

@ -74,6 +74,7 @@ func (u *userInterface) update(g GraphicsContext) error {
} }
if glContext.IsContextLost() { if glContext.IsContextLost() {
glContext.RestoreContext() glContext.RestoreContext()
g.Invalidate()
} }
currentInput.updateGamepads() currentInput.updateGamepads()
if u.sizeChanged { if u.sizeChanged {