diff --git a/graphicscontext.go b/graphicscontext.go index fa05ad9c0..c1e78d1c3 100644 --- a/graphicscontext.go +++ b/graphicscontext.go @@ -16,9 +16,11 @@ package ebiten import ( "math" + "sync/atomic" "github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics/opengl" + "github.com/hajimehoshi/ebiten/internal/ui" ) func newGraphicsContext(f func(*Image) error) *graphicsContext { @@ -33,7 +35,14 @@ type graphicsContext struct { offscreen2 *Image // TODO: better name screen *Image screenScale float64 - initialized bool + initialized int32 +} + +func (c *graphicsContext) GLContext() *opengl.Context { + if atomic.LoadInt32(&c.initialized) == 0 { + return nil + } + return ui.GLContext() } func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale float64) error { @@ -82,11 +91,11 @@ func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) } func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error { - if !c.initialized { + if atomic.LoadInt32(&c.initialized) == 0 { if err := graphics.Reset(context); err != nil { return err } - c.initialized = true + atomic.StoreInt32(&c.initialized, 1) } r, err := c.needsRestoring(context) if err != nil { diff --git a/image.go b/image.go index 58c52c61c..8613d1689 100644 --- a/image.go +++ b/image.go @@ -21,9 +21,20 @@ import ( "sync" "github.com/hajimehoshi/ebiten/internal/graphics/opengl" - "github.com/hajimehoshi/ebiten/internal/ui" ) +func glContext() *opengl.Context { + // This is called from finalizers even when the context or the program is not set. + g, ok := theGraphicsContext.Load().(*graphicsContext) + if !ok { + return nil + } + if g == nil { + return nil + } + return g.GLContext() +} + type images struct { images map[*imageImpl]struct{} m sync.Mutex @@ -141,7 +152,7 @@ func (i *Image) Size() (width, height int) { // // This function is concurrent-safe. func (i *Image) Clear() error { - if err := theImagesForRestoring.flushPixelsIfNeeded(i, ui.GLContext()); err != nil { + if err := theImagesForRestoring.flushPixelsIfNeeded(i, glContext()); err != nil { return err } return i.impl.Fill(color.Transparent) @@ -151,7 +162,7 @@ func (i *Image) Clear() error { // // This function is concurrent-safe. func (i *Image) Fill(clr color.Color) error { - if err := theImagesForRestoring.flushPixelsIfNeeded(i, ui.GLContext()); err != nil { + if err := theImagesForRestoring.flushPixelsIfNeeded(i, glContext()); err != nil { return err } return i.impl.Fill(clr) @@ -174,7 +185,7 @@ func (i *Image) Fill(clr color.Color) error { // // This function is concurrent-safe. func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error { - if err := theImagesForRestoring.flushPixelsIfNeeded(i, ui.GLContext()); err != nil { + if err := theImagesForRestoring.flushPixelsIfNeeded(i, glContext()); err != nil { return err } return i.impl.DrawImage(image, options) @@ -202,7 +213,7 @@ func (i *Image) ColorModel() color.Model { // // This function is concurrent-safe. func (i *Image) At(x, y int) color.Color { - return i.impl.At(x, y, ui.GLContext()) + return i.impl.At(x, y, glContext()) } // Dispose disposes the image data. After disposing, the image becomes invalid. @@ -212,7 +223,7 @@ func (i *Image) At(x, y int) color.Color { // // This function is concurrent-safe. func (i *Image) Dispose() error { - if err := theImagesForRestoring.flushPixelsIfNeeded(i, ui.GLContext()); err != nil { + if err := theImagesForRestoring.flushPixelsIfNeeded(i, glContext()); err != nil { return err } if i.impl.isDisposed() { @@ -229,7 +240,7 @@ func (i *Image) Dispose() error { // // This function is concurrent-safe. func (i *Image) ReplacePixels(p []uint8) error { - if err := theImagesForRestoring.flushPixelsIfNeeded(i, ui.GLContext()); err != nil { + if err := theImagesForRestoring.flushPixelsIfNeeded(i, glContext()); err != nil { return err } return i.impl.ReplacePixels(p) diff --git a/run.go b/run.go index b8c2d7054..d485a9231 100644 --- a/run.go +++ b/run.go @@ -57,6 +57,8 @@ func IsRunningSlowly() bool { return atomic.LoadInt32(&isRunningSlowly) != 0 } +var theGraphicsContext atomic.Value + // Run runs the game. // f is a function which is called at every frame. // The argument (*Image) is the render target that represents the screen. @@ -73,6 +75,7 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e ch := make(chan error) go func() { g := newGraphicsContext(f) + theGraphicsContext.Store(g) if err := loop.Run(g, width, height, scale, title, FPS); err != nil { ch <- err } @@ -93,6 +96,7 @@ func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64, ch := make(chan error) go func() { g := newGraphicsContext(f) + theGraphicsContext.Store(g) if err := loop.Run(g, width, height, scale, title, FPS); err != nil { ch <- err }