Skip to content

Commit

Permalink
internal/ui: refactoring: move some logics to internal/ui
Browse files Browse the repository at this point in the history
  • Loading branch information
hajimehoshi committed Feb 13, 2022
1 parent b282b18 commit 2609d73
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 94 deletions.
99 changes: 89 additions & 10 deletions internal/ui/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ package ui

import (
"sync/atomic"

"github.com/hajimehoshi/ebiten/v2/internal/clock"
)

const DefaultTPS = 60

type Context interface {
UpdateFrame() error
ForceUpdateFrame() error
UpdateFrame(updateCount int) error
Layout(outsideWidth, outsideHeight float64)

// AdjustPosition can be called from a different goroutine from Update's or Layout's.
Expand All @@ -30,21 +33,36 @@ type Context interface {
type contextImpl struct {
context Context

err atomic.Value
outsideWidth float64
outsideHeight float64
}

func newContextImpl(context Context) *contextImpl {
return &contextImpl{
context: context,
}
}

func (c *contextImpl) updateFrame() error {
if err, ok := c.err.Load().(error); ok && err != nil {
if err := theGlobalState.err(); err != nil {
return err
}
return c.context.UpdateFrame()

// TODO: If updateCount is 0 and vsync is disabled, swapping buffers can be skipped.
return c.context.UpdateFrame(clock.Update(theGlobalState.maxTPS()))
}

func (c *contextImpl) forceUpdateFrame() error {
if err, ok := c.err.Load().(error); ok && err != nil {
if err := theGlobalState.err(); err != nil {
return err
}
return c.context.ForceUpdateFrame()

// ForceUpdate can be invoked even if the context is not initialized yet (#1591).
if c.outsideWidth == 0 || c.outsideHeight == 0 {
return nil
}

return c.context.UpdateFrame(1)
}

func (c *contextImpl) layout(outsideWidth, outsideHeight float64) {
Expand All @@ -54,17 +72,78 @@ func (c *contextImpl) layout(outsideWidth, outsideHeight float64) {
return
}

c.outsideWidth = outsideWidth
c.outsideHeight = outsideHeight
c.context.Layout(outsideWidth, outsideHeight)
}

func (c *contextImpl) adjustPosition(x, y float64, deviceScaleFactor float64) (float64, float64) {
return c.context.AdjustPosition(x, y, deviceScaleFactor)
}

func (c *contextImpl) setError(err error) {
c.err.Store(err)
var theGlobalState = globalState{
currentMaxTPS: DefaultTPS,
}

// globalState represents a global state in this package.
// This is available even before the game loop starts.
type globalState struct {
currentErr atomic.Value
currentFPSMode int32
currentMaxTPS int32
}

func (g *globalState) err() error {
err, ok := g.currentErr.Load().(error)
if !ok {
return nil
}
return err
}

func (g *globalState) setError(err error) {
g.currentErr.Store(err)
}

func (g *globalState) fpsMode() FPSModeType {
return FPSModeType(atomic.LoadInt32(&g.currentFPSMode))
}

func (g *globalState) setFPSMode(fpsMode FPSModeType) {
atomic.StoreInt32(&g.currentFPSMode, int32(fpsMode))
}

func (g *globalState) maxTPS() int {
if g.fpsMode() == FPSModeVsyncOffMinimum {
return clock.SyncWithFPS
}
return int(atomic.LoadInt32(&g.currentMaxTPS))
}

func (g *globalState) setMaxTPS(tps int) {
if tps < 0 && tps != clock.SyncWithFPS {
panic("ebiten: tps must be >= 0 or SyncWithFPS")
}
atomic.StoreInt32(&g.currentMaxTPS, int32(tps))
}

func SetError(err error) {
Get().context.setError(err)
theGlobalState.setError(err)
}

func FPSMode() FPSModeType {
return theGlobalState.fpsMode()
}

func SetFPSMode(fpsMode FPSModeType) {
theGlobalState.setFPSMode(fpsMode)
Get().SetFPSMode(fpsMode)
}

func MaxTPS() int {
return theGlobalState.maxTPS()
}

func SetMaxTPS(tps int) {
theGlobalState.setMaxTPS(tps)
}
6 changes: 2 additions & 4 deletions internal/ui/run_notsinglethread.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/thread"
)

func (u *UserInterface) Run(uicontext Context) error {
u.context = &contextImpl{
context: uicontext,
}
func (u *UserInterface) Run(context Context) error {
u.context = newContextImpl(context)

// Initialize the main thread first so the thread is available at u.run (#809).
u.t = thread.NewOSThread()
Expand Down
6 changes: 2 additions & 4 deletions internal/ui/run_singlethread.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,8 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/thread"
)

func (u *UserInterface) Run(uicontext Context) error {
u.context = &contextImpl{
context: uicontext,
}
func (u *UserInterface) Run(context Context) error {
u.context = newContextImpl(context)

// Initialize the main thread first so the thread is available at u.run (#809).
u.t = thread.NewNoopThread()
Expand Down
4 changes: 2 additions & 2 deletions internal/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ type TouchID int
// the game loop should be terminated as soon as possible.
var RegularTermination = errors.New("regular termination")

type FPSMode int
type FPSModeType int

const (
FPSModeVsyncOn FPSMode = iota
FPSModeVsyncOn FPSModeType = iota
FPSModeVsyncOffMaximum
FPSModeVsyncOffMinimum
)
Expand Down
10 changes: 2 additions & 8 deletions internal/ui/ui_cbackend.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ func Get() *UserInterface {
}

func (u *UserInterface) Run(context Context) error {
u.context = &contextImpl{
context: context,
}
u.context = newContextImpl(context)
cbackend.InitializeGame()
for {
w, h := cbackend.ScreenSize()
Expand Down Expand Up @@ -107,11 +105,7 @@ func (*UserInterface) IsRunnableOnUnfocused() bool {
func (*UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
}

func (*UserInterface) FPSMode() FPSMode {
return FPSModeVsyncOn
}

func (*UserInterface) SetFPSMode(mode FPSMode) {
func (*UserInterface) SetFPSMode(mode FPSModeType) {
}

func (*UserInterface) ScheduleFrame() {
Expand Down
20 changes: 3 additions & 17 deletions internal/ui/ui_glfw.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ type UserInterface struct {
origPosX int
origPosY int
runnableOnUnfocused bool
fpsMode FPSMode
fpsMode FPSModeType
iconImages []image.Image
cursorShape CursorShape
windowClosingHandled bool
Expand Down Expand Up @@ -536,7 +536,7 @@ func (u *UserInterface) IsRunnableOnUnfocused() bool {
return u.isRunnableOnUnfocused()
}

func (u *UserInterface) SetFPSMode(mode FPSMode) {
func (u *UserInterface) SetFPSMode(mode FPSModeType) {
if !u.isRunning() {
u.m.Lock()
u.fpsMode = mode
Expand All @@ -553,20 +553,6 @@ func (u *UserInterface) SetFPSMode(mode FPSMode) {
})
}

func (u *UserInterface) FPSMode() FPSMode {
if !u.isRunning() {
u.m.Lock()
m := u.fpsMode
u.m.Unlock()
return m
}
var v FPSMode
u.t.Call(func() {
v = u.fpsMode
})
return v
}

func (u *UserInterface) ScheduleFrame() {
if !u.isRunning() {
return
Expand Down Expand Up @@ -943,7 +929,7 @@ func (u *UserInterface) updateSize() (float64, float64) {
}

// setFPSMode must be called from the main thread.
func (u *UserInterface) setFPSMode(fpsMode FPSMode) {
func (u *UserInterface) setFPSMode(fpsMode FPSModeType) {
needUpdate := u.fpsMode != fpsMode || !u.fpsModeInited
u.fpsMode = fpsMode
u.fpsModeInited = true
Expand Down
12 changes: 3 additions & 9 deletions internal/ui/ui_js.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func driverCursorShapeToCSSCursor(cursor CursorShape) string {

type UserInterface struct {
runnableOnUnfocused bool
fpsMode FPSMode
fpsMode FPSModeType
renderingScheduled bool
running bool
initFocused bool
Expand Down Expand Up @@ -152,14 +152,10 @@ func (u *UserInterface) IsRunnableOnUnfocused() bool {
return u.runnableOnUnfocused
}

func (u *UserInterface) SetFPSMode(mode FPSMode) {
func (u *UserInterface) SetFPSMode(mode FPSModeType) {
u.fpsMode = mode
}

func (u *UserInterface) FPSMode() FPSMode {
return u.fpsMode
}

func (u *UserInterface) ScheduleFrame() {
u.renderingScheduled = true
}
Expand Down Expand Up @@ -319,9 +315,7 @@ func (u *UserInterface) needsUpdate() bool {
}

func (u *UserInterface) loop(context Context) <-chan error {
u.context = &contextImpl{
context: context,
}
u.context = newContextImpl(context)

errCh := make(chan error, 1)
reqStopAudioCh := make(chan struct{})
Expand Down
12 changes: 3 additions & 9 deletions internal/ui/ui_mobile.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ type UserInterface struct {

input Input

fpsMode FPSMode
fpsMode FPSModeType
renderRequester RenderRequester

t *thread.OSThread
Expand Down Expand Up @@ -273,9 +273,7 @@ func (u *UserInterface) run(context Context, mainloop bool) (err error) {
u.sizeChanged = true
u.m.Unlock()

u.context = &contextImpl{
context: context,
}
u.context = newContextImpl(context)

if mainloop {
// When mainloop is true, gomobile-build is used. In this case, GL functions must be called via
Expand Down Expand Up @@ -410,11 +408,7 @@ func (u *UserInterface) SetRunnableOnUnfocused(runnableOnUnfocused bool) {
// Do nothing
}

func (u *UserInterface) FPSMode() FPSMode {
return u.fpsMode
}

func (u *UserInterface) SetFPSMode(mode FPSMode) {
func (u *UserInterface) SetFPSMode(mode FPSModeType) {
u.fpsMode = mode
u.updateExplicitRenderingModeIfNeeded()
}
Expand Down
Loading

0 comments on commit 2609d73

Please sign in to comment.