gomobile bind works

This commit is contained in:
Hajime Hoshi 2016-05-19 23:37:58 +09:00
parent 296282435a
commit 6d06b01cae
10 changed files with 138 additions and 55 deletions

View File

@ -17,8 +17,6 @@
package mobile package mobile
import ( import (
_ "image/jpeg"
"log"
"math" "math"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
@ -49,11 +47,16 @@ func update(screen *ebiten.Image) error {
return nil return nil
} }
func Start() { func Start() error {
var err error var err error
gophersImage, _, err = common.AssetImage("gophers.jpg", ebiten.FilterNearest) gophersImage, _, err = common.AssetImage("gophers.jpg", ebiten.FilterNearest)
if err != nil { if err != nil {
log.Fatal(err) return err
} }
mobile.Start(update, screenWidth, screenHeight, 2, "Mobile (Ebiten Demo)") mobile.Start(update, screenWidth, screenHeight, 2, "Mobile (Ebiten Demo)")
return nil
}
func Render() error {
return mobile.Render()
} }

View File

@ -14,6 +14,11 @@
package ebiten package ebiten
import (
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/ui"
)
func newGraphicsContext(f func(*Image) error) *graphicsContext { func newGraphicsContext(f func(*Image) error) *graphicsContext {
return &graphicsContext{ return &graphicsContext{
f: f, f: f,
@ -25,6 +30,7 @@ type graphicsContext struct {
screen *Image screen *Image
defaultRenderTarget *Image defaultRenderTarget *Image
screenScale int screenScale int
imageTasksDone bool
} }
func (c *graphicsContext) SetSize(screenWidth, screenHeight, screenScale int) error { func (c *graphicsContext) SetSize(screenWidth, screenHeight, screenScale int) error {
@ -49,6 +55,17 @@ func (c *graphicsContext) SetSize(screenWidth, screenHeight, screenScale int) er
} }
func (c *graphicsContext) Update() error { func (c *graphicsContext) Update() error {
if !c.imageTasksDone {
if err := graphics.Initialize(ui.GLContext()); err != nil {
return err
}
// This execution is called here because we can say actual GL function calls
// should be done here (especailly on mobiles).
if err := theDelayedImageTasks.exec(); err != nil {
return err
}
c.imageTasksDone = true
}
if err := c.screen.Clear(); err != nil { if err := c.screen.Clear(); err != nil {
return err return err
} }

View File

@ -561,10 +561,6 @@ func newImageWithZeroFramebuffer(width, height int) (*Image, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// When this is called, OpenGL context should exist.
if err := theDelayedImageTasks.exec(); err != nil {
return nil, err
}
return img, nil return img, nil
} }

View File

@ -42,9 +42,11 @@ func (p Program) id() programID {
type context struct { type context struct {
gl mgl.Context gl mgl.Context
worker mgl.Worker
initialized chan struct{}
} }
func NewContext() *Context { func NewContext() (*Context, error) {
c := &Context{ c := &Context{
Nearest: mgl.NEAREST, Nearest: mgl.NEAREST,
Linear: mgl.LINEAR, Linear: mgl.LINEAR,
@ -65,21 +67,28 @@ func NewContext() *Context {
locationCache: newLocationCache(), locationCache: newLocationCache(),
lastCompositeMode: CompositeModeUnknown, lastCompositeMode: CompositeModeUnknown,
} }
return c c.gl, c.worker = mgl.NewContext()
} c.initialized = make(chan struct{})
go func() {
// GL calls will just enqueue an task to the worker.
// Since the worker is not avaialbe, this enqueuing should be done
// in a goroutine.
func (c *Context) SetContext(gl mgl.Context) {
c.gl = gl
if gl == nil {
return
}
// Textures' pixel formats are alpha premultiplied. // Textures' pixel formats are alpha premultiplied.
gl.Enable(mgl.BLEND) c.gl.Enable(mgl.BLEND)
c.BlendFunc(CompositeModeSourceOver) c.BlendFunc(CompositeModeSourceOver)
close(c.initialized)
}()
return c, nil
} }
func (c *Context) IsGLContextNil() bool { func (c *Context) WaitUntilInitializingDone() {
return c.gl == nil // TODO: Call this function at an approriate place
<-c.initialized
}
func (c *Context) Worker() mgl.Worker {
return c.worker
} }
func (c *Context) BlendFunc(mode CompositeMode) { func (c *Context) BlendFunc(mode CompositeMode) {

View File

@ -150,6 +150,9 @@ func Run(g GraphicsContext, width, height, scale int, title string, fps int) err
beforeForUpdate += int64(tt) * int64(time.Second) / int64(fps) beforeForUpdate += int64(tt) * int64(time.Second) / int64(fps)
frames++ frames++
} }
if err := ui.CurrentUI().FinishRendering(); err != nil {
return err
}
// Calc the current FPS. // Calc the current FPS.
if time.Second <= time.Duration(n2-beforeForFPS) { if time.Second <= time.Duration(n2-beforeForFPS) {

View File

@ -18,6 +18,7 @@ type UserInterface interface {
Start(width, height, scale int, title string) error Start(width, height, scale int, title string) error
Update() (interface{}, error) Update() (interface{}, error)
SwapBuffers() error SwapBuffers() error
FinishRendering() error
Terminate() error Terminate() error
ScreenScale() int ScreenScale() int
SetScreenSize(width, height int) bool SetScreenSize(width, height int) bool

View File

@ -23,7 +23,6 @@ import (
"time" "time"
"github.com/go-gl/glfw/v3.1/glfw" "github.com/go-gl/glfw/v3.1/glfw"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/graphics/opengl" "github.com/hajimehoshi/ebiten/internal/graphics/opengl"
) )
@ -87,9 +86,6 @@ func initialize() (*opengl.Context, error) {
if err := u.context.Init(); err != nil { if err := u.context.Init(); err != nil {
return nil, err return nil, err
} }
if err := graphics.Initialize(u.context); err != nil {
return nil, err
}
return u.context, nil return u.context, nil
} }
@ -262,6 +258,10 @@ func (u *userInterface) swapBuffers() {
}) })
} }
func (u *userInterface) FinishRendering() error {
return nil
}
func (u *userInterface) setScreenSize(width, height, scale int) bool { func (u *userInterface) setScreenSize(width, height, scale int) bool {
if u.width == width && u.height == height && u.scale == scale { if u.width == width && u.height == height && u.scale == scale {
return false return false

View File

@ -20,7 +20,6 @@ import (
"strconv" "strconv"
"github.com/gopherjs/gopherjs/js" "github.com/gopherjs/gopherjs/js"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/graphics/opengl" "github.com/hajimehoshi/ebiten/internal/graphics/opengl"
) )
@ -109,6 +108,10 @@ func (u *userInterface) SwapBuffers() error {
return nil return nil
} }
func (u *userInterface) FinishRendering() error {
return nil
}
func initialize() (*opengl.Context, error) { func initialize() (*opengl.Context, error) {
// Do nothing in node.js. // Do nothing in node.js.
if js.Global.Get("require") != js.Undefined { if js.Global.Get("require") != js.Undefined {
@ -116,9 +119,6 @@ func initialize() (*opengl.Context, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := graphics.Initialize(c); err != nil {
return nil, err
}
return c, nil return c, nil
} }
@ -226,9 +226,6 @@ func initialize() (*opengl.Context, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := graphics.Initialize(c); err != nil {
return nil, err
}
return c, nil return c, nil
} }

View File

@ -17,26 +17,68 @@
package ui package ui
import ( import (
"errors"
"github.com/hajimehoshi/ebiten/internal/graphics/opengl" "github.com/hajimehoshi/ebiten/internal/graphics/opengl"
) )
func initialize() (*opengl.Context, error) { func initialize() (*opengl.Context, error) {
// TODO: Implement return opengl.NewContext()
return nil, nil
} }
func Main() error { func Main() error {
return errors.New("ui: don't call this: use RunWithoutMainLoop instead of Run")
}
func Render(chError <-chan error) error {
if chError == nil {
return errors.New("ui: chError must not be nil")
}
// TODO: Check this is called on the rendering thread
chRender <- struct{}{}
worker := glContext.Worker()
loop:
for {
select {
case err := <-chError:
return err
case <-worker.WorkAvailable():
worker.DoWork()
case <-chRenderEnd:
break loop
}
}
return nil return nil
} }
type userInterface struct { type userInterface struct {
width int
height int
scale int
sizeChanged bool
render chan struct{}
renderEnd chan struct{}
} }
var (
chRender = make(chan struct{})
chRenderEnd = make(chan struct{})
currentUI = &userInterface{
sizeChanged: true,
render: chRender,
renderEnd: chRenderEnd,
}
)
func CurrentUI() UserInterface { func CurrentUI() UserInterface {
return nil return currentUI
} }
func (u *userInterface) Start(width, height, scale int, title string) error { func (u *userInterface) Start(width, height, scale int, title string) error {
u.width = width
u.height = height
u.scale = scale
// title is ignored?
return nil return nil
} }
@ -45,21 +87,46 @@ func (u *userInterface) Terminate() error {
} }
func (u *userInterface) Update() (interface{}, error) { func (u *userInterface) Update() (interface{}, error) {
return nil, nil // TODO: Need lock?
if u.sizeChanged {
u.sizeChanged = false
e := ScreenSizeEvent{
Width: u.width,
Height: u.height,
Scale: u.scale,
ActualScale: u.actualScreenScale(),
}
return e, nil
}
select {
case <-u.render:
return RenderEvent{}, nil
}
} }
func (u *userInterface) SwapBuffers() error { func (u *userInterface) SwapBuffers() error {
return nil return nil
} }
func (u *userInterface) FinishRendering() error {
u.renderEnd <- struct{}{}
return nil
}
func (u *userInterface) SetScreenSize(width, height int) bool { func (u *userInterface) SetScreenSize(width, height int) bool {
// TODO: Implement
return false return false
} }
func (u *userInterface) SetScreenScale(scale int) bool { func (u *userInterface) SetScreenScale(scale int) bool {
// TODO: Implement
return false return false
} }
func (u *userInterface) ScreenScale() int { func (u *userInterface) ScreenScale() int {
return 1 return u.scale
}
func (u *userInterface) actualScreenScale() int {
return u.scale
} }

View File

@ -15,6 +15,7 @@
package mobile package mobile
import ( import (
"errors"
"runtime" "runtime"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
@ -28,16 +29,6 @@ var chError <-chan error
// Different from ebiten.Run, this invokes only the game loop and not the main (UI) loop. // Different from ebiten.Run, this invokes only the game loop and not the main (UI) loop.
func Start(f func(*ebiten.Image) error, width, height, scale int, title string) { func Start(f func(*ebiten.Image) error, width, height, scale int, title string) {
chError = ebiten.RunWithoutMainLoop(f, width, height, scale, title) chError = ebiten.RunWithoutMainLoop(f, width, height, scale, title)
return
}
func LastErrorString() string {
select {
case err := <-chError:
return err.Error()
default:
return ""
}
} }
func SetScreenSize(width, height int) { func SetScreenSize(width, height int) {
@ -48,13 +39,12 @@ func SetScreenScale(scale int) {
ui.CurrentUI().SetScreenScale(scale) ui.CurrentUI().SetScreenScale(scale)
} }
func Render() { func Render() error {
runtime.LockOSThread() runtime.LockOSThread()
// TODO: Implement this defer runtime.UnlockOSThread()
/*select {
case <-workAvailable: if chError == nil {
DoWork() return errors.New("mobile: chError must not be nil: Start is not called yet?")
case <-done: }
return return ui.Render(chError)
}*/
} }