opengl: Hide OpenGL context usages into internal/graphics package

This commit is contained in:
Hajime Hoshi 2017-05-31 02:09:27 +09:00
parent 62b364de2d
commit 7d181e3182
20 changed files with 131 additions and 190 deletions

View File

@ -16,12 +16,9 @@ package ebiten
import (
"math"
"sync/atomic"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/restorable"
"github.com/hajimehoshi/ebiten/internal/ui"
)
func newGraphicsContext(f func(*Image) error) *graphicsContext {
@ -36,17 +33,10 @@ type graphicsContext struct {
offscreen2 *Image // TODO: better name
screen *Image
screenScale float64
initialized int32
initialized bool
invalidated bool // browser only
}
func (c *graphicsContext) GLContext() *opengl.Context {
if atomic.LoadInt32(&c.initialized) == 0 {
return nil
}
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
@ -100,21 +90,21 @@ func (c *graphicsContext) SetSize(screenWidth, screenHeight int, screenScale flo
return nil
}
func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error {
if atomic.LoadInt32(&c.initialized) == 0 {
if err := graphics.Reset(context); err != nil {
func (c *graphicsContext) initializeIfNeeded() error {
if !c.initialized {
if err := graphics.Reset(); err != nil {
return err
}
atomic.StoreInt32(&c.initialized, 1)
c.initialized = true
}
r, err := c.needsRestoring(context)
r, err := c.needsRestoring()
if err != nil {
return err
}
if !r {
return nil
}
if err := c.restore(context); err != nil {
if err := c.restore(); err != nil {
return err
}
return nil
@ -130,17 +120,17 @@ func drawWithFittingScale(dst *Image, src *Image) {
_ = dst.DrawImage(src, op)
}
func (c *graphicsContext) drawToDefaultRenderTarget(context *opengl.Context) error {
func (c *graphicsContext) drawToDefaultRenderTarget() error {
_ = c.screen.Clear()
drawWithFittingScale(c.screen, c.offscreen2)
if err := graphics.FlushCommands(context); err != nil {
if err := graphics.FlushCommands(); err != nil {
return err
}
return nil
}
func (c *graphicsContext) UpdateAndDraw(context *opengl.Context, updateCount int) error {
if err := c.initializeIfNeeded(context); err != nil {
func (c *graphicsContext) UpdateAndDraw(updateCount int) error {
if err := c.initializeIfNeeded(); err != nil {
return err
}
for i := 0; i < updateCount; i++ {
@ -153,21 +143,21 @@ func (c *graphicsContext) UpdateAndDraw(context *opengl.Context, updateCount int
if 0 < updateCount {
drawWithFittingScale(c.offscreen2, c.offscreen)
}
if err := c.drawToDefaultRenderTarget(context); err != nil {
if err := c.drawToDefaultRenderTarget(); err != nil {
return err
}
// TODO: Add tests to check if this behavior is correct (#357)
if err := restorable.ResolveStalePixels(context); err != nil {
if err := restorable.ResolveStalePixels(); err != nil {
return err
}
return nil
}
func (c *graphicsContext) restore(context *opengl.Context) error {
if err := graphics.Reset(context); err != nil {
func (c *graphicsContext) restore() error {
if err := graphics.Reset(); err != nil {
return err
}
if err := restorable.Restore(context); err != nil {
if err := restorable.Restore(); err != nil {
return err
}
c.invalidated = false

View File

@ -16,10 +16,6 @@
package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/opengl"
)
func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) {
func (c *graphicsContext) needsRestoring() (bool, error) {
return c.invalidated, nil
}

View File

@ -18,13 +18,12 @@ package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/opengl"
)
func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error) {
func (c *graphicsContext) needsRestoring() (bool, error) {
// FlushCommands is required because c.offscreen.impl might not have an actual texture.
if err := graphics.FlushCommands(context); err != nil {
if err := graphics.FlushCommands(); err != nil {
return false, err
}
return c.offscreen.restorable.IsInvalidated(context), nil
return c.offscreen.restorable.IsInvalidated(), nil
}

View File

@ -25,18 +25,6 @@ import (
"github.com/hajimehoshi/ebiten/internal/restorable"
)
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()
}
// Image represents an image.
// The pixel format is alpha-premultiplied.
// Image implements image.Image.
@ -166,7 +154,7 @@ func (i *Image) At(x, y int) color.Color {
return color.Transparent
}
// TODO: Error should be delayed until flushing. Do not panic here.
clr, err := i.restorable.At(x, y, glContext())
clr, err := i.restorable.At(x, y)
if err != nil {
panic(err)
}

View File

@ -27,7 +27,7 @@ import (
)
type command interface {
Exec(context *opengl.Context, indexOffsetInBytes int) error
Exec(indexOffsetInBytes int) error
}
type commandQueue struct {
@ -113,11 +113,11 @@ func (q *commandQueue) commandGroups() [][]command {
return gs
}
func (q *commandQueue) Flush(context *opengl.Context) error {
func (q *commandQueue) Flush() error {
q.m.Lock()
defer q.m.Unlock()
// glViewport must be called at least at every frame on iOS.
context.ResetViewportSize()
opengl.GetContext().ResetViewportSize()
n := 0
lastN := 0
for _, g := range q.commandGroups() {
@ -128,7 +128,7 @@ func (q *commandQueue) Flush(context *opengl.Context) error {
}
}
if 0 < n-lastN {
context.BufferSubData(opengl.ArrayBuffer, q.vertices[lastN:n])
opengl.GetContext().BufferSubData(opengl.ArrayBuffer, q.vertices[lastN:n])
}
// NOTE: WebGL doesn't seem to have Check gl.MAX_ELEMENTS_VERTICES or gl.MAX_ELEMENTS_INDICES so far.
// Let's use them to compare to len(quads) in the future.
@ -138,7 +138,7 @@ func (q *commandQueue) Flush(context *opengl.Context) error {
numc := len(g)
indexOffsetInBytes := 0
for _, c := range g {
if err := c.Exec(context, indexOffsetInBytes); err != nil {
if err := c.Exec(indexOffsetInBytes); err != nil {
return err
}
if c, ok := c.(*drawImageCommand); ok {
@ -148,7 +148,7 @@ func (q *commandQueue) Flush(context *opengl.Context) error {
}
if 0 < numc {
// Call glFlush to prevent black flicking (especially on Android (#226) and iOS).
context.Flush()
opengl.GetContext().Flush()
}
lastN = n
}
@ -157,8 +157,8 @@ func (q *commandQueue) Flush(context *opengl.Context) error {
return nil
}
func FlushCommands(context *opengl.Context) error {
return theCommandQueue.Flush(context)
func FlushCommands() error {
return theCommandQueue.Flush()
}
type fillCommand struct {
@ -166,12 +166,12 @@ type fillCommand struct {
color color.RGBA
}
func (c *fillCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
f, err := c.dst.createFramebufferIfNeeded(context)
func (c *fillCommand) Exec(indexOffsetInBytes int) error {
f, err := c.dst.createFramebufferIfNeeded()
if err != nil {
return err
}
if err := f.setAsViewport(context); err != nil {
if err := f.setAsViewport(); err != nil {
return err
}
cr, cg, cb, ca := c.color.R, c.color.G, c.color.B, c.color.A
@ -180,7 +180,7 @@ func (c *fillCommand) Exec(context *opengl.Context, indexOffsetInBytes int) erro
g := float64(cg) / max
b := float64(cb) / max
a := float64(ca) / max
return context.FillFramebuffer(r, g, b, a)
return opengl.GetContext().FillFramebuffer(r, g, b, a)
}
type drawImageCommand struct {
@ -195,15 +195,15 @@ func QuadVertexSizeInBytes() int {
return 4 * theArrayBufferLayout.totalBytes()
}
func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
f, err := c.dst.createFramebufferIfNeeded(context)
func (c *drawImageCommand) Exec(indexOffsetInBytes int) error {
f, err := c.dst.createFramebufferIfNeeded()
if err != nil {
return err
}
if err := f.setAsViewport(context); err != nil {
if err := f.setAsViewport(); err != nil {
return err
}
context.BlendFunc(c.mode)
opengl.GetContext().BlendFunc(c.mode)
n := c.quadsNum()
if n == 0 {
@ -214,7 +214,6 @@ func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int)
p := &programContext{
state: &theOpenGLState,
program: theOpenGLState.programTexture,
context: context,
projectionMatrix: proj,
texture: c.src.texture.native,
colorM: c.color,
@ -224,7 +223,7 @@ func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int)
}
// TODO: We should call glBindBuffer here?
// The buffer is already bound at begin() but it is counterintuitive.
context.DrawElements(opengl.Triangles, 6*n, indexOffsetInBytes)
opengl.GetContext().DrawElements(opengl.Triangles, 6*n, indexOffsetInBytes)
return nil
}
@ -263,29 +262,29 @@ type replacePixelsCommand struct {
pixels []uint8
}
func (c *replacePixelsCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
f, err := c.dst.createFramebufferIfNeeded(context)
func (c *replacePixelsCommand) Exec(indexOffsetInBytes int) error {
f, err := c.dst.createFramebufferIfNeeded()
if err != nil {
return err
}
if err := f.setAsViewport(context); err != nil {
if err := f.setAsViewport(); err != nil {
return err
}
// Filling with non black or white color is required here for glTexSubImage2D.
// Very mysterious but this actually works (Issue #186).
// This is needed even after fixing a shader bug at f537378f2a6a8ef56e1acf1c03034967b77c7b51.
if err := context.FillFramebuffer(0, 0, 0.5, 1); err != nil {
if err := opengl.GetContext().FillFramebuffer(0, 0, 0.5, 1); err != nil {
return err
}
// This is necessary on Android. We can't call glClear just before glTexSubImage2D without
// glFlush. glTexSubImage2D didn't work without this hack at least on Nexus 5x (#211).
// This also happens when a fillCommand precedes a replacePixelsCommand.
// TODO: Can we have a better way like optimizing commands?
context.Flush()
if err := context.BindTexture(c.dst.texture.native); err != nil {
opengl.GetContext().Flush()
if err := opengl.GetContext().BindTexture(c.dst.texture.native); err != nil {
return err
}
context.TexSubImage2D(c.pixels, NextPowerOf2Int(c.dst.width), NextPowerOf2Int(c.dst.height))
opengl.GetContext().TexSubImage2D(c.pixels, NextPowerOf2Int(c.dst.width), NextPowerOf2Int(c.dst.height))
return nil
}
@ -293,12 +292,12 @@ type disposeCommand struct {
target *Image
}
func (c *disposeCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
func (c *disposeCommand) Exec(indexOffsetInBytes int) error {
if c.target.framebuffer != nil {
context.DeleteFramebuffer(c.target.framebuffer.native)
opengl.GetContext().DeleteFramebuffer(c.target.framebuffer.native)
}
if c.target.texture != nil {
context.DeleteTexture(c.target.texture.native)
opengl.GetContext().DeleteTexture(c.target.texture.native)
}
return nil
}
@ -309,7 +308,7 @@ type newImageFromImageCommand struct {
filter opengl.Filter
}
func (c *newImageFromImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
func (c *newImageFromImageCommand) Exec(indexOffsetInBytes int) error {
origSize := c.img.Bounds().Size()
if origSize.X < 1 {
return errors.New("graphics: width must be equal or more than 1.")
@ -321,7 +320,7 @@ func (c *newImageFromImageCommand) Exec(context *opengl.Context, indexOffsetInBy
if c.img.Bounds() != image.Rect(0, 0, NextPowerOf2Int(w), NextPowerOf2Int(h)) {
panic(fmt.Sprintf("graphics: invalid image bounds: %v", c.img.Bounds()))
}
native, err := context.NewTexture(w, h, c.img.Pix, c.filter)
native, err := opengl.GetContext().NewTexture(w, h, c.img.Pix, c.filter)
if err != nil {
return err
}
@ -338,7 +337,7 @@ type newImageCommand struct {
filter opengl.Filter
}
func (c *newImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
func (c *newImageCommand) Exec(indexOffsetInBytes int) error {
w := NextPowerOf2Int(c.width)
h := NextPowerOf2Int(c.height)
if w < 1 {
@ -347,7 +346,7 @@ func (c *newImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int)
if h < 1 {
return errors.New("graphics: height must be equal or more than 1.")
}
native, err := context.NewTexture(w, h, nil, c.filter)
native, err := opengl.GetContext().NewTexture(w, h, nil, c.filter)
if err != nil {
return err
}
@ -363,7 +362,7 @@ type newScreenFramebufferImageCommand struct {
height int
}
func (c *newScreenFramebufferImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) error {
func (c *newScreenFramebufferImageCommand) Exec(indexOffsetInBytes int) error {
if c.width < 1 {
return errors.New("graphics: width must be equal or more than 1.")
}
@ -371,7 +370,7 @@ func (c *newScreenFramebufferImageCommand) Exec(context *opengl.Context, indexOf
return errors.New("graphics: height must be equal or more than 1.")
}
f := &framebuffer{
native: context.ScreenFramebuffer(),
native: opengl.GetContext().ScreenFramebuffer(),
flipY: true,
}
c.result.framebuffer = f

View File

@ -38,8 +38,8 @@ type framebuffer struct {
proMatrix []float32
}
func newFramebufferFromTexture(context *opengl.Context, texture *texture) (*framebuffer, error) {
native, err := context.NewFramebuffer(opengl.Texture(texture.native))
func newFramebufferFromTexture(texture *texture) (*framebuffer, error) {
native, err := opengl.GetContext().NewFramebuffer(opengl.Texture(texture.native))
if err != nil {
return nil, err
}
@ -50,10 +50,10 @@ func newFramebufferFromTexture(context *opengl.Context, texture *texture) (*fram
const viewportSize = 4096
func (f *framebuffer) setAsViewport(context *opengl.Context) error {
func (f *framebuffer) setAsViewport() error {
width := viewportSize
height := viewportSize
return context.SetViewport(f.native, width, height)
return opengl.GetContext().SetViewport(f.native, width, height)
}
func (f *framebuffer) projectionMatrix(height int) []float32 {

View File

@ -145,16 +145,16 @@ func (i *Image) DrawImage(src *Image, vertices []float32, clr *affine.ColorM, mo
theCommandQueue.EnqueueDrawImageCommand(i, src, vertices, clr, mode)
}
func (i *Image) Pixels(context *opengl.Context) ([]uint8, error) {
func (i *Image) Pixels() ([]uint8, error) {
// Flush the enqueued commands so that pixels are certainly read.
if err := theCommandQueue.Flush(context); err != nil {
if err := theCommandQueue.Flush(); err != nil {
return nil, err
}
f, err := i.createFramebufferIfNeeded(context)
f, err := i.createFramebufferIfNeeded()
if err != nil {
return nil, err
}
return context.FramebufferPixels(f.native, NextPowerOf2Int(i.width), NextPowerOf2Int(i.height))
return opengl.GetContext().FramebufferPixels(f.native, NextPowerOf2Int(i.width), NextPowerOf2Int(i.height))
}
func (i *Image) ReplacePixels(p []uint8) {
@ -167,15 +167,15 @@ func (i *Image) ReplacePixels(p []uint8) {
theCommandQueue.Enqueue(c)
}
func (i *Image) IsInvalidated(context *opengl.Context) bool {
return !context.IsTexture(i.texture.native)
func (i *Image) IsInvalidated() bool {
return !opengl.GetContext().IsTexture(i.texture.native)
}
func (i *Image) createFramebufferIfNeeded(context *opengl.Context) (*framebuffer, error) {
func (i *Image) createFramebufferIfNeeded() (*framebuffer, error) {
if i.framebuffer != nil {
return i.framebuffer, nil
}
f, err := newFramebufferFromTexture(context, i.texture)
f, err := newFramebufferFromTexture(i.texture)
if err != nil {
return nil, err
}

View File

@ -46,26 +46,26 @@ func (a *arrayBufferLayout) totalBytes() int {
return a.total
}
func (a *arrayBufferLayout) newArrayBuffer(c *opengl.Context) opengl.Buffer {
return c.NewBuffer(opengl.ArrayBuffer, a.totalBytes()*4*maxQuads, opengl.DynamicDraw)
func (a *arrayBufferLayout) newArrayBuffer() opengl.Buffer {
return opengl.GetContext().NewBuffer(opengl.ArrayBuffer, a.totalBytes()*4*maxQuads, opengl.DynamicDraw)
}
func (a *arrayBufferLayout) enable(c *opengl.Context, program opengl.Program) {
func (a *arrayBufferLayout) enable(program opengl.Program) {
for _, p := range a.parts {
c.EnableVertexAttribArray(program, p.name)
opengl.GetContext().EnableVertexAttribArray(program, p.name)
}
total := a.totalBytes()
offset := 0
for _, p := range a.parts {
c.VertexAttribPointer(program, p.name, p.num, p.dataType, p.normalize, total, offset)
opengl.GetContext().VertexAttribPointer(program, p.name, p.num, p.dataType, p.normalize, total, offset)
offset += p.dataType.SizeInBytes() * p.num
}
}
func (a *arrayBufferLayout) disable(c *opengl.Context, program opengl.Program) {
func (a *arrayBufferLayout) disable(program opengl.Program) {
// TODO: Disabling should be done in reversed order?
for _, p := range a.parts {
c.DisableVertexAttribArray(program, p.name)
opengl.GetContext().DisableVertexAttribArray(program, p.name)
}
}
@ -125,12 +125,12 @@ const (
maxQuads = indicesNum / 6
)
func Reset(context *opengl.Context) error {
return theOpenGLState.reset(context)
func Reset() error {
return theOpenGLState.reset()
}
func (s *openGLState) reset(context *opengl.Context) error {
if err := context.Reset(); err != nil {
func (s *openGLState) reset() error {
if err := opengl.GetContext().Reset(); err != nil {
return err
}
s.lastProgram = zeroProgram
@ -138,19 +138,19 @@ func (s *openGLState) reset(context *opengl.Context) error {
s.lastColorMatrix = nil
s.lastColorMatrixTranslation = nil
shaderVertexModelviewNative, err := context.NewShader(opengl.VertexShader, shader(context, shaderVertexModelview))
shaderVertexModelviewNative, err := opengl.GetContext().NewShader(opengl.VertexShader, shader(shaderVertexModelview))
if err != nil {
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
}
defer context.DeleteShader(shaderVertexModelviewNative)
defer opengl.GetContext().DeleteShader(shaderVertexModelviewNative)
shaderFragmentTextureNative, err := context.NewShader(opengl.FragmentShader, shader(context, shaderFragmentTexture))
shaderFragmentTextureNative, err := opengl.GetContext().NewShader(opengl.FragmentShader, shader(shaderFragmentTexture))
if err != nil {
panic(fmt.Sprintf("graphics: shader compiling error:\n%s", err))
}
defer context.DeleteShader(shaderFragmentTextureNative)
defer opengl.GetContext().DeleteShader(shaderFragmentTextureNative)
s.programTexture, err = context.NewProgram([]opengl.Shader{
s.programTexture, err = opengl.GetContext().NewProgram([]opengl.Shader{
shaderVertexModelviewNative,
shaderFragmentTextureNative,
})
@ -158,7 +158,7 @@ func (s *openGLState) reset(context *opengl.Context) error {
return err
}
s.arrayBuffer = theArrayBufferLayout.newArrayBuffer(context)
s.arrayBuffer = theArrayBufferLayout.newArrayBuffer()
indices := make([]uint16, 6*maxQuads)
for i := uint16(0); i < maxQuads; i++ {
@ -169,7 +169,7 @@ func (s *openGLState) reset(context *opengl.Context) error {
indices[6*i+4] = 4*i + 2
indices[6*i+5] = 4*i + 3
}
s.indexBufferQuads = context.NewBuffer(opengl.ElementArrayBuffer, indices, opengl.StaticDraw)
s.indexBufferQuads = opengl.GetContext().NewBuffer(opengl.ElementArrayBuffer, indices, opengl.StaticDraw)
return nil
}
@ -189,20 +189,19 @@ func areSameFloat32Array(a, b []float32) bool {
type programContext struct {
state *openGLState
program opengl.Program
context *opengl.Context
projectionMatrix []float32
texture opengl.Texture
colorM affine.ColorM
}
func (p *programContext) begin() error {
c := p.context
c := opengl.GetContext()
if p.state.lastProgram != p.program {
c.UseProgram(p.program)
if p.state.lastProgram != zeroProgram {
theArrayBufferLayout.disable(c, p.state.lastProgram)
theArrayBufferLayout.disable(p.state.lastProgram)
}
theArrayBufferLayout.enable(c, p.program)
theArrayBufferLayout.enable(p.program)
p.state.lastProgram = p.state.programTexture
p.state.lastProjectionMatrix = nil

View File

@ -27,9 +27,9 @@ const (
shaderFragmentTexture
)
func shader(c *opengl.Context, id shaderId) string {
func shader(id shaderId) string {
str := shaders[id]
if !c.GlslHighpSupported() {
if !opengl.GetContext().GlslHighpSupported() {
str = strings.Replace(str, "highp ", "", -1)
str = strings.Replace(str, "lowp ", "", -1)
}

View File

@ -18,7 +18,6 @@ import (
"errors"
"time"
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/sync"
"github.com/hajimehoshi/ebiten/internal/ui"
)
@ -76,7 +75,7 @@ func (c *runContext) updateFPS(fps float64) {
type GraphicsContext interface {
SetSize(width, height int, scale float64) error
UpdateAndDraw(context *opengl.Context, updateCount int) error
UpdateAndDraw(updateCount int) error
Invalidate()
}
@ -150,7 +149,7 @@ func (c *runContext) render(g GraphicsContext) error {
if tt == 0 && (int64(time.Second)/int64(fps)-int64(5*time.Millisecond)) < t {
tt = 1
}
if err := g.UpdateAndDraw(ui.GLContext(), tt); err != nil {
if err := g.UpdateAndDraw(tt); err != nil {
return err
}
c.lastUpdated += int64(tt) * int64(time.Second) / int64(fps)

View File

@ -47,6 +47,12 @@ type Context struct {
context
}
var theContext *Context
func GetContext() *Context {
return theContext
}
func (c *Context) BindTexture(t Texture) error {
if c.lastTexture == t {
return nil

View File

@ -73,10 +73,10 @@ type context struct {
runOnMainThread func(func() error) error
}
func NewContext(runOnMainThread func(func() error) error) (*Context, error) {
func Init(runOnMainThread func(func() error) error) {
c := &Context{}
c.runOnMainThread = runOnMainThread
return c, nil
theContext = c
}
func (c *Context) runOnContextThread(f func() error) error {

View File

@ -91,7 +91,7 @@ type context struct {
lastProgramID programID
}
func NewContext() (*Context, error) {
func Init() error {
var gl *webgl.Context
if js.Global.Get("require") == js.Undefined {
@ -103,7 +103,7 @@ func NewContext() (*Context, error) {
PremultipliedAlpha: true,
})
if err != nil {
return nil, err
return err
}
} else {
// TODO: Now Ebiten with headless-gl doesn't work well (#141).
@ -126,7 +126,8 @@ func NewContext() (*Context, error) {
c.loseContext.Call("loseContext")
})
}
return c, nil
theContext = c
return nil
}
func (c *Context) Reset() error {

View File

@ -72,10 +72,10 @@ type context struct {
worker mgl.Worker
}
func NewContext() (*Context, error) {
func Init() {
c := &Context{}
c.gl, c.worker = mgl.NewContext()
return c, nil
theContext = c
}
func (c *Context) DoWork(chError <-chan error, chDone <-chan struct{}) error {

View File

@ -167,14 +167,14 @@ func (p *Image) appendDrawImageHistory(image *Image, vertices []float32, colorm
// At returns a color value at (x, y).
//
// Note that this must not be called until context is available.
func (p *Image) At(x, y int, context *opengl.Context) (color.RGBA, error) {
func (p *Image) At(x, y int) (color.RGBA, error) {
w, h := p.image.Size()
w2, h2 := graphics.NextPowerOf2Int(w), graphics.NextPowerOf2Int(h)
if x < 0 || y < 0 || w2 <= x || h2 <= y {
return color.RGBA{}, nil
}
if p.basePixels == nil || p.drawImageHistory != nil || p.stale {
if err := p.readPixelsFromGPU(p.image, context); err != nil {
if err := p.readPixelsFromGPU(p.image); err != nil {
return color.RGBA{}, err
}
}
@ -195,9 +195,9 @@ func (p *Image) makeStaleIfDependingOn(target *Image) {
}
}
func (p *Image) readPixelsFromGPU(image *graphics.Image, context *opengl.Context) error {
func (p *Image) readPixelsFromGPU(image *graphics.Image) error {
var err error
p.basePixels, err = image.Pixels(context)
p.basePixels, err = image.Pixels()
if err != nil {
return err
}
@ -207,14 +207,14 @@ func (p *Image) readPixelsFromGPU(image *graphics.Image, context *opengl.Context
return nil
}
func (p *Image) resolveStalePixels(context *opengl.Context) error {
func (p *Image) resolveStalePixels() error {
if p.volatile {
return nil
}
if !p.stale {
return nil
}
return p.readPixelsFromGPU(p.image, context)
return p.readPixelsFromGPU(p.image)
}
func (p *Image) hasDependency() bool {
@ -225,7 +225,7 @@ func (p *Image) hasDependency() bool {
}
// Restore restores *graphics.Image from the pixels using its state.
func (p *Image) restore(context *opengl.Context) error {
func (p *Image) restore() error {
w, h := p.image.Size()
if p.screen {
// The screen image should also be recreated because framebuffer might
@ -273,7 +273,7 @@ func (p *Image) restore(context *opengl.Context) error {
p.image = gimg
var err error
p.basePixels, err = gimg.Pixels(context)
p.basePixels, err = gimg.Pixels()
if err != nil {
return err
}
@ -295,6 +295,6 @@ func (p *Image) Dispose() {
runtime.SetFinalizer(p, nil)
}
func (p *Image) IsInvalidated(context *opengl.Context) bool {
return p.image.IsInvalidated(context)
func (p *Image) IsInvalidated() bool {
return p.image.IsInvalidated()
}

View File

@ -15,7 +15,6 @@
package restorable
import (
"github.com/hajimehoshi/ebiten/internal/opengl"
"github.com/hajimehoshi/ebiten/internal/sync"
)
@ -29,12 +28,12 @@ var theImages = &images{
images: map[*Image]struct{}{},
}
func ResolveStalePixels(context *opengl.Context) error {
return theImages.resolveStalePixels(context)
func ResolveStalePixels() error {
return theImages.resolveStalePixels()
}
func Restore(context *opengl.Context) error {
return theImages.restore(context)
func Restore() error {
return theImages.restore()
}
func ClearVolatileImages() {
@ -53,12 +52,12 @@ func (i *images) remove(img *Image) {
delete(i.images, img)
}
func (i *images) resolveStalePixels(context *opengl.Context) error {
func (i *images) resolveStalePixels() error {
i.m.Lock()
defer i.m.Unlock()
i.lastChecked = nil
for img := range i.images {
if err := img.resolveStalePixels(context); err != nil {
if err := img.resolveStalePixels(); err != nil {
return err
}
}
@ -86,7 +85,7 @@ func (i *images) resetPixelsIfDependingOn(target *Image) {
i.m.Unlock()
}
func (i *images) restore(context *opengl.Context) error {
func (i *images) restore() error {
i.m.Lock()
defer i.m.Unlock()
// Framebuffers/textures cannot be disposed since framebuffers/textures that
@ -102,12 +101,12 @@ func (i *images) restore(context *opengl.Context) error {
}
// Images depending on other images should be processed first.
for _, img := range imagesWithoutDependency {
if err := img.restore(context); err != nil {
if err := img.restore(); err != nil {
return err
}
}
for _, img := range imagesWithDependency {
if err := img.restore(context); err != nil {
if err := img.restore(); err != nil {
return err
}
}

View File

@ -1,25 +0,0 @@
// Copyright 2016 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ui
import (
"github.com/hajimehoshi/ebiten/internal/opengl"
)
var glContext *opengl.Context
func GLContext() *opengl.Context {
return glContext
}

View File

@ -178,11 +178,7 @@ func Run(width, height int, scale float64, title string, g GraphicsContext) erro
u := currentUI
// GLContext must be created before setting the screen size, which requires
// swapping buffers.
var err error
glContext, err = opengl.NewContext(currentUI.runOnMainThread)
if err != nil {
return err
}
opengl.Init(currentUI.runOnMainThread)
if err := u.runOnMainThread(func() error {
m := glfw.GetPrimaryMonitor()
v := m.GetVideoMode()
@ -275,7 +271,7 @@ func (u *userInterface) loop(g GraphicsContext) error {
return err
}
// The bound framebuffer must be the default one (0) before swapping buffers.
if err := glContext.BindScreenFramebuffer(); err != nil {
if err := opengl.GetContext().BindScreenFramebuffer(); err != nil {
return err
}
_ = u.runOnMainThread(func() error {

View File

@ -72,8 +72,8 @@ func (u *userInterface) update(g GraphicsContext) error {
if !u.windowFocus {
return nil
}
if glContext.IsContextLost() {
glContext.RestoreContext()
if opengl.GetContext().IsContextLost() {
opengl.GetContext().RestoreContext()
g.Invalidate()
}
currentInput.updateGamepads()
@ -280,9 +280,7 @@ func Run(width, height int, scale float64, title string, g GraphicsContext) erro
doc.Set("title", title)
u.setScreenSize(width, height, scale)
canvas.Call("focus")
var err error
glContext, err = opengl.NewContext()
if err != nil {
if err := opengl.Init(); err != nil {
return err
}
return u.loop(g)

View File

@ -38,7 +38,7 @@ func Render(chError <-chan error) error {
// TODO: Check this is called on the rendering thread
select {
case chRender <- struct{}{}:
return glContext.DoWork(chError, chRenderEnd)
return opengl.GetContext().DoWork(chError, chRenderEnd)
case <-time.After(500 * time.Millisecond):
// This function must not be blocked. We need to break for timeout.
return nil
@ -66,11 +66,7 @@ func Run(width, height int, scale float64, title string, g GraphicsContext) erro
u.height = height
u.scale = scale
// title is ignored?
var err error
glContext, err = opengl.NewContext()
if err != nil {
return err
}
opengl.Init()
for {
if err := u.update(g); err != nil {
return err