internal/graphicsdriver: refactoring: choose the graphics driver at this package

This commit is contained in:
Hajime Hoshi 2022-02-27 23:14:43 +09:00
parent 99f8e335ee
commit dd8900ea48
11 changed files with 105 additions and 56 deletions

View File

@ -26,20 +26,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
) )
var theGraphicsDriver graphicsdriver.Graphics
func SetGraphicsDriver(driver graphicsdriver.Graphics) {
theGraphicsDriver = driver
}
func NeedsInvertY() bool {
return theGraphicsDriver.FramebufferYDirection() != theGraphicsDriver.NDCYDirection()
}
func NeedsRestoring() bool {
return theGraphicsDriver.NeedsRestoring()
}
// command represents a drawing command. // command represents a drawing command.
// //
// A command for drawing that is created when Image functions are called like DrawTriangles, // A command for drawing that is created when Image functions are called like DrawTriangles,
@ -245,7 +231,7 @@ func (q *commandQueue) flush() error {
vs := q.vertices vs := q.vertices
debug.Logf("Graphics commands:\n") debug.Logf("Graphics commands:\n")
if theGraphicsDriver.HasHighPrecisionFloat() { if graphicsDriver().HasHighPrecisionFloat() {
n := q.nvertices / graphics.VertexFloatNum n := q.nvertices / graphics.VertexFloatNum
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
s := q.srcSizes[i] s := q.srcSizes[i]
@ -296,7 +282,7 @@ func (q *commandQueue) flush() error {
} }
} }
theGraphicsDriver.Begin() graphicsDriver().Begin()
var present bool var present bool
cs := q.commands cs := q.commands
for len(cs) > 0 { for len(cs) > 0 {
@ -320,7 +306,7 @@ func (q *commandQueue) flush() error {
nc++ nc++
} }
if 0 < ne { if 0 < ne {
theGraphicsDriver.SetVertices(vs[:nv], es[:ne]) graphicsDriver().SetVertices(vs[:nv], es[:ne])
es = es[ne:] es = es[ne:]
vs = vs[nv:] vs = vs[nv:]
} }
@ -339,7 +325,7 @@ func (q *commandQueue) flush() error {
} }
cs = cs[nc:] cs = cs[nc:]
} }
theGraphicsDriver.End(present) graphicsDriver().End(present)
// Release the commands explicitly (#1803). // Release the commands explicitly (#1803).
// Apparently, the part of a slice between len and cap-1 still holds references. // Apparently, the part of a slice between len and cap-1 still holds references.
@ -488,7 +474,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
imgs[0] = c.srcs[0].image.ID() imgs[0] = c.srcs[0].image.ID()
} }
return theGraphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.offsets, shaderID, c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.dstRegion, c.srcRegion, c.uniforms, c.evenOdd) return graphicsDriver().DrawTriangles(c.dst.image.ID(), imgs, c.offsets, shaderID, c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.dstRegion, c.srcRegion, c.uniforms, c.evenOdd)
} }
func (c *drawTrianglesCommand) numVertices() int { func (c *drawTrianglesCommand) numVertices() int {
@ -662,7 +648,7 @@ func (c *newImageCommand) String() string {
// Exec executes a newImageCommand. // Exec executes a newImageCommand.
func (c *newImageCommand) Exec(indexOffset int) error { func (c *newImageCommand) Exec(indexOffset int) error {
i, err := theGraphicsDriver.NewImage(c.width, c.height) i, err := graphicsDriver().NewImage(c.width, c.height)
if err != nil { if err != nil {
return err return err
} }
@ -684,7 +670,7 @@ func (c *newScreenFramebufferImageCommand) String() string {
// Exec executes a newScreenFramebufferImageCommand. // Exec executes a newScreenFramebufferImageCommand.
func (c *newScreenFramebufferImageCommand) Exec(indexOffset int) error { func (c *newScreenFramebufferImageCommand) Exec(indexOffset int) error {
var err error var err error
c.result.image, err = theGraphicsDriver.NewScreenFramebufferImage(c.width, c.height) c.result.image, err = graphicsDriver().NewScreenFramebufferImage(c.width, c.height)
return err return err
} }
@ -701,14 +687,14 @@ func (c *newShaderCommand) String() string {
// Exec executes a newShaderCommand. // Exec executes a newShaderCommand.
func (c *newShaderCommand) Exec(indexOffset int) error { func (c *newShaderCommand) Exec(indexOffset int) error {
var err error var err error
c.result.shader, err = theGraphicsDriver.NewShader(c.ir) c.result.shader, err = graphicsDriver().NewShader(c.ir)
return err return err
} }
// InitializeGraphicsDriverState initialize the current graphics driver state. // InitializeGraphicsDriverState initialize the current graphics driver state.
func InitializeGraphicsDriverState() (err error) { func InitializeGraphicsDriverState() (err error) {
runOnRenderingThread(func() { runOnRenderingThread(func() {
err = theGraphicsDriver.Initialize() err = graphicsDriver().Initialize()
}) })
return return
} }
@ -716,7 +702,7 @@ func InitializeGraphicsDriverState() (err error) {
// ResetGraphicsDriverState resets the current graphics driver state. // ResetGraphicsDriverState resets the current graphics driver state.
// If the graphics driver doesn't have an API to reset, ResetGraphicsDriverState does nothing. // If the graphics driver doesn't have an API to reset, ResetGraphicsDriverState does nothing.
func ResetGraphicsDriverState() (err error) { func ResetGraphicsDriverState() (err error) {
if r, ok := theGraphicsDriver.(interface{ Reset() error }); ok { if r, ok := graphicsDriver().(interface{ Reset() error }); ok {
runOnRenderingThread(func() { runOnRenderingThread(func() {
err = r.Reset() err = r.Reset()
}) })
@ -728,7 +714,7 @@ func ResetGraphicsDriverState() (err error) {
func MaxImageSize() int { func MaxImageSize() int {
var size int var size int
runOnRenderingThread(func() { runOnRenderingThread(func() {
size = theGraphicsDriver.MaxImageSize() size = graphicsDriver().MaxImageSize()
}) })
return size return size
} }

View File

@ -0,0 +1,59 @@
// Copyright 2022 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 graphicscommand
import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
)
type Drawer interface {
Draw(screenScale float64, offsetX, offsetY float64, needsClearingScreen bool, framebufferYDirection graphicsdriver.YDirection, screenClearedEveryFrame, filterEnabled bool) error
}
func Draw(drawer Drawer, screenScale float64, offsetX, offsetY float64, screenClearedEveryFrame, filterEnabled bool) error {
return drawer.Draw(screenScale, offsetX, offsetY, graphicsDriver().NeedsClearingScreen(), graphicsDriver().FramebufferYDirection(), screenClearedEveryFrame, filterEnabled)
}
// TODO: Reduce these 'getter' global functions if possible.
func NeedsInvertY() bool {
return graphicsDriver().FramebufferYDirection() != graphicsDriver().NDCYDirection()
}
func NeedsRestoring() bool {
return graphicsDriver().NeedsRestoring()
}
func IsGL() bool {
return graphicsDriver().IsGL()
}
func SetVsyncEnabled(enabled bool) {
graphicsDriver().SetVsyncEnabled(enabled)
}
func SetTransparent(transparent bool) {
graphicsDriver().SetTransparent(transparent)
}
func SetFullscreen(fullscreen bool) {
graphicsDriver().SetFullscreen(fullscreen)
}
func SetWindow(window uintptr) {
if g, ok := graphicsDriver().(interface{ SetWindow(uintptr) }); ok {
g.SetWindow(window)
}
}

View File

@ -15,7 +15,7 @@
//go:build !ios && !ebitengl && !ebitencbackend //go:build !ios && !ebitengl && !ebitencbackend
// +build !ios,!ebitengl,!ebitencbackend // +build !ios,!ebitengl,!ebitencbackend
package ui package graphicscommand
// #cgo CFLAGS: -x objective-c // #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation // #cgo LDFLAGS: -framework Foundation
@ -55,7 +55,7 @@ func supportsMetal() bool {
var graphicsOnce sync.Once var graphicsOnce sync.Once
func graphics() graphicsdriver.Graphics { func graphicsDriver() graphicsdriver.Graphics {
graphicsOnce.Do(func() { graphicsOnce.Do(func() {
if supportsMetal() { if supportsMetal() {
theGraphics = metal.Get() theGraphics = metal.Get()

View File

@ -15,14 +15,14 @@
//go:build ios && !ebitengl && !ebitencbackend //go:build ios && !ebitengl && !ebitencbackend
// +build ios,!ebitengl,!ebitencbackend // +build ios,!ebitengl,!ebitencbackend
package ui package graphicscommand
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
func graphics() graphicsdriver.Graphics { func graphicsDriver() graphicsdriver.Graphics {
// Metal might not be supported on emulators on Intel machines. // Metal might not be supported on emulators on Intel machines.
return opengl.Get() return opengl.Get()
} }

View File

@ -15,14 +15,14 @@
//go:build ios && !ebitengl && !ebitencbackend //go:build ios && !ebitengl && !ebitencbackend
// +build ios,!ebitengl,!ebitencbackend // +build ios,!ebitengl,!ebitencbackend
package ui package graphicscommand
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/metal"
) )
func graphics() graphicsdriver.Graphics { func graphicsDriver() graphicsdriver.Graphics {
g := metal.Get() g := metal.Get()
if g == nil { if g == nil {
panic("ui: Metal is not available on this iOS device") panic("ui: Metal is not available on this iOS device")

View File

@ -1,4 +1,4 @@
// Copyright 2020 The Ebiten Authors // Copyright 2022 The Ebiten Authors
// //
// Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
@ -12,13 +12,17 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package ui //go:build android || ios
// +build android ios
package graphicscommand
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "golang.org/x/mobile/gl"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
func init() { func SetGomobileGLContext(ctx gl.Context) {
// TODO: Should graphics() be moved to the graphicscommand package? graphicsDriver().(*opengl.Graphics).SetGomobileGLContext(ctx)
graphicscommand.SetGraphicsDriver(graphics())
} }

View File

@ -15,13 +15,13 @@
//go:build !darwin || ebitencbackend || ebitengl //go:build !darwin || ebitencbackend || ebitengl
// +build !darwin ebitencbackend ebitengl // +build !darwin ebitencbackend ebitengl
package ui package graphicscommand
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
func graphics() graphicsdriver.Graphics { func graphicsDriver() graphicsdriver.Graphics {
return opengl.Get() return opengl.Get()
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/clock" "github.com/hajimehoshi/ebiten/v2/internal/clock"
"github.com/hajimehoshi/ebiten/v2/internal/debug" "github.com/hajimehoshi/ebiten/v2/internal/debug"
graphicspkg "github.com/hajimehoshi/ebiten/v2/internal/graphics" graphicspkg "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/hooks" "github.com/hajimehoshi/ebiten/v2/internal/hooks"
) )
@ -106,7 +107,7 @@ func (c *contextImpl) updateFrameImpl(updateCount int, outsideWidth, outsideHeig
// Draw the game. // Draw the game.
screenScale, offsetX, offsetY := c.screenScaleAndOffsets(deviceScaleFactor) screenScale, offsetX, offsetY := c.screenScaleAndOffsets(deviceScaleFactor)
if err := c.game.Draw(screenScale, offsetX, offsetY, graphics().NeedsClearingScreen(), graphics().FramebufferYDirection(), theGlobalState.isScreenClearedEveryFrame(), theGlobalState.isScreenFilterEnabled()); err != nil { if err := graphicscommand.Draw(c.game, screenScale, offsetX, offsetY, theGlobalState.isScreenClearedEveryFrame(), theGlobalState.isScreenFilterEnabled()); err != nil {
return err return err
} }

View File

@ -28,6 +28,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/devicescale" "github.com/hajimehoshi/ebiten/v2/internal/devicescale"
"github.com/hajimehoshi/ebiten/v2/internal/glfw" "github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/hooks" "github.com/hajimehoshi/ebiten/v2/internal/hooks"
"github.com/hajimehoshi/ebiten/v2/internal/thread" "github.com/hajimehoshi/ebiten/v2/internal/thread"
) )
@ -667,7 +668,7 @@ func (u *UserInterface) createWindow(width, height int) error {
// Ensure to consume this callback. // Ensure to consume this callback.
u.waitForFramebufferSizeCallback(u.window, nil) u.waitForFramebufferSizeCallback(u.window, nil)
if graphics().IsGL() { if graphicscommand.IsGL() {
u.window.MakeContextCurrent() u.window.MakeContextCurrent()
} }
@ -721,7 +722,7 @@ func (u *UserInterface) registerWindowSetSizeCallback() {
u.err = err u.err = err
} }
if graphics().IsGL() { if graphicscommand.IsGL() {
u.swapBuffers() u.swapBuffers()
} }
}) })
@ -815,7 +816,7 @@ event:
} }
func (u *UserInterface) init() error { func (u *UserInterface) init() error {
if graphics().IsGL() { if graphicscommand.IsGL() {
glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI) glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI)
glfw.WindowHint(glfw.ContextVersionMajor, 2) glfw.WindowHint(glfw.ContextVersionMajor, 2)
glfw.WindowHint(glfw.ContextVersionMinor, 1) glfw.WindowHint(glfw.ContextVersionMinor, 1)
@ -836,7 +837,7 @@ func (u *UserInterface) init() error {
transparent = glfw.True transparent = glfw.True
} }
glfw.WindowHint(glfw.TransparentFramebuffer, transparent) glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
graphics().SetTransparent(u.isInitScreenTransparent()) graphicscommand.SetTransparent(u.isInitScreenTransparent())
// Before creating a window, set it unresizable no matter what u.isInitWindowResizable() is (#1987). // Before creating a window, set it unresizable no matter what u.isInitWindowResizable() is (#1987).
// Making the window resizable here doesn't work correctly when switching to enable resizing. // Making the window resizable here doesn't work correctly when switching to enable resizing.
@ -888,9 +889,7 @@ func (u *UserInterface) init() error {
u.window.Show() u.window.Show()
if g, ok := graphics().(interface{ SetWindow(uintptr) }); ok { graphicscommand.SetWindow(u.nativeWindow())
g.SetWindow(u.nativeWindow())
}
return nil return nil
} }
@ -1067,7 +1066,7 @@ func (u *UserInterface) loop() error {
// swapBuffers also checks IsGL, so this condition is redundant. // swapBuffers also checks IsGL, so this condition is redundant.
// However, (*thread).Call is not good for performance due to channels. // However, (*thread).Call is not good for performance due to channels.
// Let's avoid this whenever possible (#1367). // Let's avoid this whenever possible (#1367).
if graphics().IsGL() { if graphicscommand.IsGL() {
u.t.Call(u.swapBuffers) u.t.Call(u.swapBuffers)
} }
@ -1089,7 +1088,7 @@ func (u *UserInterface) loop() error {
// swapBuffers must be called from the main thread. // swapBuffers must be called from the main thread.
func (u *UserInterface) swapBuffers() { func (u *UserInterface) swapBuffers() {
if graphics().IsGL() { if graphicscommand.IsGL() {
u.window.SwapBuffers() u.window.SwapBuffers()
} }
} }
@ -1146,7 +1145,7 @@ func (u *UserInterface) adjustWindowSizeBasedOnSizeLimitsInDIP(width, height int
func (u *UserInterface) setWindowSizeInDIP(width, height int, fullscreen bool) { func (u *UserInterface) setWindowSizeInDIP(width, height int, fullscreen bool) {
width, height = u.adjustWindowSizeBasedOnSizeLimitsInDIP(width, height) width, height = u.adjustWindowSizeBasedOnSizeLimitsInDIP(width, height)
graphics().SetFullscreen(fullscreen) graphicscommand.SetFullscreen(fullscreen)
scale := u.deviceScaleFactor(u.currentMonitor()) scale := u.deviceScaleFactor(u.currentMonitor())
if u.windowWidthInDIP == width && u.windowHeightInDIP == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == scale { if u.windowWidthInDIP == width && u.windowHeightInDIP == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == scale {
@ -1220,7 +1219,7 @@ func (u *UserInterface) setWindowSizeInDIPImpl(width, height int, fullscreen boo
// Swapping buffer is necesary to prevent the image lag (#1004). // Swapping buffer is necesary to prevent the image lag (#1004).
// TODO: This might not work when vsync is disabled. // TODO: This might not work when vsync is disabled.
if graphics().IsGL() { if graphicscommand.IsGL() {
glfw.PollEvents() glfw.PollEvents()
u.swapBuffers() u.swapBuffers()
} }
@ -1267,7 +1266,7 @@ func (u *UserInterface) setWindowSizeInDIPImpl(width, height int, fullscreen boo
// updateVsync must be called on the main thread. // updateVsync must be called on the main thread.
func (u *UserInterface) updateVsync() { func (u *UserInterface) updateVsync() {
if graphics().IsGL() { if graphicscommand.IsGL() {
// SwapInterval is affected by the current monitor of the window. // SwapInterval is affected by the current monitor of the window.
// This needs to be called at least after SetMonitor. // This needs to be called at least after SetMonitor.
// Without SwapInterval after SetMonitor, vsynch doesn't work (#375). // Without SwapInterval after SetMonitor, vsynch doesn't work (#375).
@ -1281,7 +1280,7 @@ func (u *UserInterface) updateVsync() {
glfw.SwapInterval(0) glfw.SwapInterval(0)
} }
} }
graphics().SetVsyncEnabled(u.fpsMode == FPSModeVsyncOn) graphicscommand.SetVsyncEnabled(u.fpsMode == FPSModeVsyncOn)
} }
// currentMonitor returns the current active monitor. // currentMonitor returns the current active monitor.

View File

@ -227,6 +227,7 @@ import "C"
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/glfw" "github.com/hajimehoshi/ebiten/v2/internal/glfw"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
) )
// clearVideoModeScaleCache must be called from the main thread. // clearVideoModeScaleCache must be called from the main thread.
@ -316,7 +317,7 @@ func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
} }
func (u *UserInterface) adjustViewSize() { func (u *UserInterface) adjustViewSize() {
if graphics().IsGL() { if graphicscommand.IsGL() {
return return
} }
C.adjustViewSize(C.uintptr_t(u.window.GetCocoaWindow())) C.adjustViewSize(C.uintptr_t(u.window.GetCocoaWindow()))

View File

@ -36,7 +36,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/devicescale" "github.com/hajimehoshi/ebiten/v2/internal/devicescale"
"github.com/hajimehoshi/ebiten/v2/internal/gamepad" "github.com/hajimehoshi/ebiten/v2/internal/gamepad"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
"github.com/hajimehoshi/ebiten/v2/internal/hooks" "github.com/hajimehoshi/ebiten/v2/internal/hooks"
"github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/restorable"
"github.com/hajimehoshi/ebiten/v2/internal/thread" "github.com/hajimehoshi/ebiten/v2/internal/thread"
@ -277,7 +276,7 @@ func (u *UserInterface) run(game Game, mainloop bool) (err error) {
// When mainloop is true, gomobile-build is used. In this case, GL functions must be called via // When mainloop is true, gomobile-build is used. In this case, GL functions must be called via
// gl.Context so that they are called on the appropriate thread. // gl.Context so that they are called on the appropriate thread.
ctx := <-glContextCh ctx := <-glContextCh
graphics().(*opengl.Graphics).SetGomobileGLContext(ctx) graphicscommand.SetGomobileGLContext(ctx)
} else { } else {
u.t = thread.NewOSThread() u.t = thread.NewOSThread()
graphicscommand.SetRenderingThread(u.t) graphicscommand.SetRenderingThread(u.t)