internal/graphicscommand: move the choice of graphics drivers to internal/ui

This commit is contained in:
Hajime Hoshi 2022-03-19 23:55:14 +09:00
parent 1d9982ee6d
commit 4cbce71b2b
26 changed files with 214 additions and 258 deletions

View File

@ -22,7 +22,7 @@ import (
"os" "os"
"time" "time"
"github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
// availableFilename returns a filename that is valid as a new file or directory. // availableFilename returns a filename that is valid as a new file or directory.
@ -72,7 +72,7 @@ func dumpInternalImages() error {
return err return err
} }
if err := atlas.DumpImages(dir); err != nil { if err := ui.DumpImages(dir); err != nil {
return err return err
} }

View File

@ -14,13 +14,17 @@
package atlas package atlas
import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
)
const ( const (
BaseCountToPutOnAtlas = baseCountToPutOnAtlas BaseCountToPutOnAtlas = baseCountToPutOnAtlas
PaddingSize = paddingSize PaddingSize = paddingSize
) )
func PutImagesOnAtlasForTesting() error { func PutImagesOnAtlasForTesting(graphicsDriver graphicsdriver.Graphics) error {
return putImagesOnAtlas() return putImagesOnAtlas(graphicsDriver)
} }
var ( var (

View File

@ -117,11 +117,11 @@ func resolveDeferred() {
// Actual time duration is increased in an exponential way for each usages as a rendering target. // Actual time duration is increased in an exponential way for each usages as a rendering target.
const baseCountToPutOnAtlas = 10 const baseCountToPutOnAtlas = 10
func putImagesOnAtlas() error { func putImagesOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
for i := range imagesToPutOnAtlas { for i := range imagesToPutOnAtlas {
i.usedAsSourceCount++ i.usedAsSourceCount++
if i.usedAsSourceCount >= baseCountToPutOnAtlas*(1<<uint(min(i.isolatedCount, 31))) { if i.usedAsSourceCount >= baseCountToPutOnAtlas*(1<<uint(min(i.isolatedCount, 31))) {
if err := i.putOnAtlas(); err != nil { if err := i.putOnAtlas(graphicsDriver); err != nil {
return err return err
} }
i.usedAsSourceCount = 0 i.usedAsSourceCount = 0
@ -304,7 +304,7 @@ func (i *Image) ensureIsolated() {
i.isolatedCount++ i.isolatedCount++
} }
func (i *Image) putOnAtlas() error { func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
if i.backend == nil { if i.backend == nil {
i.allocate(true) i.allocate(true)
return nil return nil
@ -327,7 +327,7 @@ func (i *Image) putOnAtlas() error {
pixels := make([]byte, 4*i.width*i.height) pixels := make([]byte, 4*i.width*i.height)
for y := 0; y < i.height; y++ { for y := 0; y < i.height; y++ {
for x := 0; x < i.width; x++ { for x := 0; x < i.width; x++ {
r, g, b, a, err := i.at(x+paddingSize, y+paddingSize) r, g, b, a, err := i.at(graphicsDriver, x+paddingSize, y+paddingSize)
if err != nil { if err != nil {
return err return err
} }
@ -598,7 +598,7 @@ func (i *Image) replacePixels(pix []byte) {
i.backend.restorable.ReplacePixels(pixb, x, y, w, h) i.backend.restorable.ReplacePixels(pixb, x, y, w, h)
} }
func (img *Image) Pixels(x, y, width, height int) ([]byte, error) { func (img *Image) Pixels(graphicsDriver graphicsdriver.Graphics, x, y, width, height int) ([]byte, error) {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
@ -609,7 +609,7 @@ func (img *Image) Pixels(x, y, width, height int) ([]byte, error) {
idx := 0 idx := 0
for j := y; j < y+height; j++ { for j := y; j < y+height; j++ {
for i := x; i < x+width; i++ { for i := x; i < x+width; i++ {
r, g, b, a, err := img.at(i, j) r, g, b, a, err := img.at(graphicsDriver, i, j)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -623,7 +623,7 @@ func (img *Image) Pixels(x, y, width, height int) ([]byte, error) {
return bs, nil return bs, nil
} }
func (i *Image) at(x, y int) (byte, byte, byte, byte, error) { func (i *Image) at(graphicsDriver graphicsdriver.Graphics, x, y int) (byte, byte, byte, byte, error) {
if i.backend == nil { if i.backend == nil {
return 0, 0, 0, 0, nil return 0, 0, 0, 0, nil
} }
@ -633,7 +633,7 @@ func (i *Image) at(x, y int) (byte, byte, byte, byte, error) {
return 0, 0, 0, 0, nil return 0, 0, 0, 0, nil
} }
return i.backend.restorable.At(x+ox, y+oy) return i.backend.restorable.At(graphicsDriver, x+ox, y+oy)
} }
// MarkDisposed marks the image as disposed. The actual operation is deferred. // MarkDisposed marks the image as disposed. The actual operation is deferred.
@ -790,11 +790,11 @@ func (i *Image) allocate(putOnAtlas bool) {
i.node = n i.node = n
} }
func (i *Image) DumpScreenshot(path string, blackbg bool) error { func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool) error {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
return i.backend.restorable.Dump(path, blackbg, image.Rect(paddingSize, paddingSize, paddingSize+i.width, paddingSize+i.height)) return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(paddingSize, paddingSize, paddingSize+i.width, paddingSize+i.height))
} }
func NewScreenFramebufferImage(width, height int) *Image { func NewScreenFramebufferImage(width, height int) *Image {
@ -807,20 +807,20 @@ func NewScreenFramebufferImage(width, height int) *Image {
return i return i
} }
func EndFrame() error { func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
backendsM.Lock() backendsM.Lock()
theTemporaryPixels.resetAtFrameEnd() theTemporaryPixels.resetAtFrameEnd()
return restorable.ResolveStaleImages() return restorable.ResolveStaleImages(graphicsDriver)
} }
func BeginFrame() error { func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
defer backendsM.Unlock() defer backendsM.Unlock()
var err error var err error
initOnce.Do(func() { initOnce.Do(func() {
err = restorable.InitializeGraphicsDriverState() err = restorable.InitializeGraphicsDriverState(graphicsDriver)
if err != nil { if err != nil {
return return
} }
@ -828,22 +828,22 @@ func BeginFrame() error {
panic("atlas: all the images must be not on an atlas before the game starts") panic("atlas: all the images must be not on an atlas before the game starts")
} }
minSize = 1024 minSize = 1024
maxSize = restorable.MaxImageSize() maxSize = restorable.MaxImageSize(graphicsDriver)
}) })
if err != nil { if err != nil {
return err return err
} }
resolveDeferred() resolveDeferred()
if err := putImagesOnAtlas(); err != nil { if err := putImagesOnAtlas(graphicsDriver); err != nil {
return err return err
} }
return restorable.RestoreIfNeeded() return restorable.RestoreIfNeeded(graphicsDriver)
} }
func DumpImages(dir string) error { func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) error {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
return restorable.DumpImages(dir) return restorable.DumpImages(graphicsDriver, dir)
} }

View File

@ -24,6 +24,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
t "github.com/hajimehoshi/ebiten/v2/internal/testing" t "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
const ( const (
@ -109,7 +110,7 @@ func TestEnsureIsolated(t *testing.T) {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
pix, err := img4.Pixels(0, 0, size, size) pix, err := img4.Pixels(ui.GraphicsDriverForTesting(), 0, 0, size, size)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -189,7 +190,7 @@ func TestReputOnAtlas(t *testing.T) {
// Use the doubled count since img1 was on a texture atlas and became an isolated image once. // Use the doubled count since img1 was on a texture atlas and became an isolated image once.
// Then, img1 requires longer time to recover to be on a textur atlas again. // Then, img1 requires longer time to recover to be on a textur atlas again.
for i := 0; i < atlas.BaseCountToPutOnAtlas*2; i++ { for i := 0; i < atlas.BaseCountToPutOnAtlas*2; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
img0.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{img1}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) img0.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{img1}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
@ -197,11 +198,11 @@ func TestReputOnAtlas(t *testing.T) {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
} }
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
pix, err := img1.Pixels(0, 0, size, size) pix, err := img1.Pixels(ui.GraphicsDriverForTesting(), 0, 0, size, size)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -225,7 +226,7 @@ func TestReputOnAtlas(t *testing.T) {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
pix, err = img1.Pixels(0, 0, size, size) pix, err = img1.Pixels(ui.GraphicsDriverForTesting(), 0, 0, size, size)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -252,7 +253,7 @@ func TestReputOnAtlas(t *testing.T) {
// Use img1 as a render source, but call ReplacePixels. // Use img1 as a render source, but call ReplacePixels.
// Now use 4x count as img1 became an isolated image again. // Now use 4x count as img1 became an isolated image again.
for i := 0; i < atlas.BaseCountToPutOnAtlas*4; i++ { for i := 0; i < atlas.BaseCountToPutOnAtlas*4; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
img1.ReplacePixels(make([]byte, 4*size*size)) img1.ReplacePixels(make([]byte, 4*size*size))
@ -261,7 +262,7 @@ func TestReputOnAtlas(t *testing.T) {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
} }
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -273,7 +274,7 @@ func TestReputOnAtlas(t *testing.T) {
// Use img3 as a render source. As img3 is volatile, img3 is never on an atlas. // Use img3 as a render source. As img3 is volatile, img3 is never on an atlas.
for i := 0; i < atlas.BaseCountToPutOnAtlas*2; i++ { for i := 0; i < atlas.BaseCountToPutOnAtlas*2; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
img0.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{img3}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) img0.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{img3}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
@ -313,7 +314,7 @@ func TestExtend(t *testing.T) {
// Ensure to allocate // Ensure to allocate
img1.ReplacePixels(p1) img1.ReplacePixels(p1)
pix0, err := img0.Pixels(0, 0, w0, h0) pix0, err := img0.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w0, h0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -332,7 +333,7 @@ func TestExtend(t *testing.T) {
} }
} }
pix1, err := img1.Pixels(0, 0, w1, h1) pix1, err := img1.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w1, h1)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -379,7 +380,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
dst.ReplacePixels(pix) dst.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h) pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w, h)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -426,7 +427,7 @@ func TestSmallImages(t *testing.T) {
} }
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
pix, err := dst.Pixels(0, 0, w, h) pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w, h)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -474,7 +475,7 @@ func TestLongImages(t *testing.T) {
} }
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
pix, err := dst.Pixels(0, 0, dstW, dstH) pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, dstW, dstH)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -568,7 +569,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
// Use src as a render source. // Use src as a render source.
for i := 0; i < atlas.BaseCountToPutOnAtlas/2; i++ { for i := 0; i < atlas.BaseCountToPutOnAtlas/2; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
@ -584,7 +585,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
atlas.ResolveDeferredForTesting() atlas.ResolveDeferredForTesting()
// Confirm that PutImagesOnAtlasForTesting doesn't panic. // Confirm that PutImagesOnAtlasForTesting doesn't panic.
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
} }
@ -619,7 +620,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
// Update the count without using src2 as a rendering source. // Update the count without using src2 as a rendering source.
// This should not affect whether src2 is on an atlas or not. // This should not affect whether src2 is on an atlas or not.
for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ { for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if got, want := src2.IsOnAtlasForTesting(), false; got != want { if got, want := src2.IsOnAtlasForTesting(), false; got != want {
@ -629,7 +630,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
// Update the count with using src2 as a rendering source. // Update the count with using src2 as a rendering source.
for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ { for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src2}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src2}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
@ -638,7 +639,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
} }
} }
if err := atlas.PutImagesOnAtlasForTesting(); err != nil { if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if got, want := src2.IsOnAtlasForTesting(), true; got != want { if got, want := src2.IsOnAtlasForTesting(), true; got != want {

View File

@ -21,9 +21,9 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "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"
etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" etesting "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
func TestShaderFillTwice(t *testing.T) { func TestShaderFillTwice(t *testing.T) {
@ -39,17 +39,19 @@ func TestShaderFillTwice(t *testing.T) {
Width: w, Width: w,
Height: h, Height: h,
} }
p0 := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0xff, 0xff, 0xff, 0xff) g := ui.GraphicsDriverForTesting()
needsInvertY := g.FramebufferYDirection() != g.NDCYDirection()
p0 := etesting.ShaderProgramFill(needsInvertY, 0xff, 0xff, 0xff, 0xff)
s0 := atlas.NewShader(&p0) s0 := atlas.NewShader(&p0)
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, s0, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, s0, nil, false)
// Vertices must be recreated (#1755) // Vertices must be recreated (#1755)
vs = quadVertices(w, h, 0, 0, 1) vs = quadVertices(w, h, 0, 0, 1)
p1 := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0x80, 0x80, 0x80, 0xff) p1 := etesting.ShaderProgramFill(needsInvertY, 0x80, 0x80, 0x80, 0xff)
s1 := atlas.NewShader(&p1) s1 := atlas.NewShader(&p1)
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, s1, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, s1, nil, false)
pix, err := dst.Pixels(0, 0, w, h) pix, err := dst.Pixels(g, 0, 0, w, h)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
@ -81,7 +83,7 @@ func TestImageDrawTwice(t *testing.T) {
vs = quadVertices(w, h, 0, 0, 1) vs = quadVertices(w, h, 0, 0, 1)
dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src1}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*atlas.Image{src1}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
pix, err := dst.Pixels(0, 0, w, h) pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w, h)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }

View File

@ -34,15 +34,15 @@ type Image struct {
needsToResolvePixels bool needsToResolvePixels bool
} }
func BeginFrame() error { func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
if err := atlas.BeginFrame(); err != nil { if err := atlas.BeginFrame(graphicsDriver); err != nil {
return err return err
} }
return flushDelayedCommands() return flushDelayedCommands()
} }
func EndFrame() error { func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
return atlas.EndFrame() return atlas.EndFrame(graphicsDriver)
} }
func NewImage(width, height int) *Image { func NewImage(width, height int) *Image {
@ -138,7 +138,7 @@ func (i *Image) MarkDisposed() {
i.img.MarkDisposed() i.img.MarkDisposed()
} }
func (img *Image) Pixels(x, y, width, height int) (pix []byte, err error) { func (img *Image) Pixels(graphicsDriver graphicsdriver.Graphics, x, y, width, height int) (pix []byte, err error) {
checkDelayedCommandsFlushed("Pixels") checkDelayedCommandsFlushed("Pixels")
if !image.Rect(x, y, x+width, y+height).In(image.Rect(0, 0, img.width, img.height)) { if !image.Rect(x, y, x+width, y+height).In(image.Rect(0, 0, img.width, img.height)) {
@ -148,7 +148,7 @@ func (img *Image) Pixels(x, y, width, height int) (pix []byte, err error) {
pix = make([]byte, 4*width*height) pix = make([]byte, 4*width*height)
if img.pixels == nil { if img.pixels == nil {
pix, err := img.img.Pixels(0, 0, img.width, img.height) pix, err := img.img.Pixels(graphicsDriver, 0, 0, img.width, img.height)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -161,12 +161,12 @@ func (img *Image) Pixels(x, y, width, height int) (pix []byte, err error) {
return pix, nil return pix, nil
} }
func (i *Image) DumpScreenshot(name string, blackbg bool) error { func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) error {
checkDelayedCommandsFlushed("Dump") checkDelayedCommandsFlushed("Dump")
return i.img.DumpScreenshot(name, blackbg) return i.img.DumpScreenshot(graphicsDriver, name, blackbg)
} }
func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error { func (i *Image) ReplacePixels(graphicsDriver graphicsdriver.Graphics, pix []byte, x, y, width, height int) error {
if l := 4 * width * height; len(pix) != l { if l := 4 * width * height; len(pix) != l {
panic(fmt.Sprintf("buffered: len(pix) was %d but must be %d", len(pix), l)) panic(fmt.Sprintf("buffered: len(pix) was %d but must be %d", len(pix), l))
} }
@ -175,7 +175,7 @@ func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
copied := make([]byte, len(pix)) copied := make([]byte, len(pix))
copy(copied, pix) copy(copied, pix)
if tryAddDelayedCommand(func() error { if tryAddDelayedCommand(func() error {
i.ReplacePixels(copied, x, y, width, height) i.ReplacePixels(graphicsDriver, copied, x, y, width, height)
return nil return nil
}) { }) {
return nil return nil
@ -194,7 +194,7 @@ func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
// TODO: Can we use (*restorable.Image).ReplacePixels? // TODO: Can we use (*restorable.Image).ReplacePixels?
if i.pixels == nil { if i.pixels == nil {
pix, err := i.img.Pixels(0, 0, i.width, i.height) pix, err := i.img.Pixels(graphicsDriver, 0, 0, i.width, i.height)
if err != nil { if err != nil {
return err return err
} }

View File

@ -345,8 +345,8 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics) error {
} }
// FlushCommands flushes the command queue. // FlushCommands flushes the command queue.
func FlushCommands() error { func FlushCommands(graphicsDriver graphicsdriver.Graphics) error {
return theCommandQueue.Flush(graphicsDriver()) return theCommandQueue.Flush(graphicsDriver)
} }
// drawTrianglesCommand represents a drawing command to draw an image on another image. // drawTrianglesCommand represents a drawing command to draw an image on another image.
@ -692,17 +692,17 @@ func (c *newShaderCommand) Exec(graphicsDriver graphicsdriver.Graphics, indexOff
} }
// InitializeGraphicsDriverState initialize the current graphics driver state. // InitializeGraphicsDriverState initialize the current graphics driver state.
func InitializeGraphicsDriverState() (err error) { func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) (err error) {
runOnRenderingThread(func() { runOnRenderingThread(func() {
err = graphicsDriver().Initialize() err = graphicsDriver.Initialize()
}) })
return return
} }
// 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(graphicsDriver graphicsdriver.Graphics) (err error) {
if r, ok := graphicsDriver().(interface{ Reset() error }); ok { if r, ok := graphicsDriver.(interface{ Reset() error }); ok {
runOnRenderingThread(func() { runOnRenderingThread(func() {
err = r.Reset() err = r.Reset()
}) })
@ -711,10 +711,10 @@ func ResetGraphicsDriverState() (err error) {
} }
// MaxImageSize returns the maximum size of an image. // MaxImageSize returns the maximum size of an image.
func MaxImageSize() int { func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int {
var size int var size int
runOnRenderingThread(func() { runOnRenderingThread(func() {
size = graphicsDriver().MaxImageSize() size = graphicsDriver.MaxImageSize()
}) })
return size return size
} }

View File

@ -1,59 +0,0 @@
// 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

@ -167,14 +167,14 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
// ReadPixels reads the image's pixels. // ReadPixels reads the image's pixels.
// ReadPixels returns an error when an error happens in the graphics driver. // ReadPixels returns an error when an error happens in the graphics driver.
func (i *Image) ReadPixels(buf []byte) error { func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, buf []byte) error {
i.resolveBufferedReplacePixels() i.resolveBufferedReplacePixels()
c := &pixelsCommand{ c := &pixelsCommand{
img: i, img: i,
result: buf, result: buf,
} }
theCommandQueue.Enqueue(c) theCommandQueue.Enqueue(c)
if err := theCommandQueue.Flush(graphicsDriver()); err != nil { if err := theCommandQueue.Flush(graphicsDriver); err != nil {
return err return err
} }
return nil return nil
@ -210,7 +210,7 @@ func (i *Image) IsInvalidated() bool {
// If blackbg is true, any alpha values in the dumped image will be 255. // If blackbg is true, any alpha values in the dumped image will be 255.
// //
// This is for testing usage. // This is for testing usage.
func (i *Image) Dump(path string, blackbg bool, rect image.Rectangle) error { func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) error {
// Screen image cannot be dumped. // Screen image cannot be dumped.
if i.screen { if i.screen {
return nil return nil
@ -224,7 +224,7 @@ func (i *Image) Dump(path string, blackbg bool, rect image.Rectangle) error {
defer f.Close() defer f.Close()
pix := make([]byte, 4*i.width*i.height) pix := make([]byte, 4*i.width*i.height)
if err := i.ReadPixels(pix); err != nil { if err := i.ReadPixels(graphicsDriver, pix); err != nil {
return err return err
} }

View File

@ -23,6 +23,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" etesting "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -54,7 +55,7 @@ func TestClear(t *testing.T) {
dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(pix); err != nil { if err := dst.ReadPixels(ui.GraphicsDriverForTesting(), pix); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h/2; j++ { for j := 0; j < h/2; j++ {
@ -103,12 +104,14 @@ func TestShader(t *testing.T) {
} }
dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeClear, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
ir := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0xff, 0, 0, 0xff) g := ui.GraphicsDriverForTesting()
needsInvertY := g.FramebufferYDirection() != g.NDCYDirection()
ir := etesting.ShaderProgramFill(needsInvertY, 0xff, 0, 0, 0xff)
s := graphicscommand.NewShader(&ir) s := graphicscommand.NewShader(&ir)
dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false) dst.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false)
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(pix); err != nil { if err := dst.ReadPixels(g, pix); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h; j++ { for j := 0; j < h; j++ {

View File

@ -67,20 +67,20 @@ func (m *Mipmap) SetVolatile(volatile bool) {
m.orig.SetVolatile(volatile) m.orig.SetVolatile(volatile)
} }
func (m *Mipmap) DumpScreenshot(name string, blackbg bool) error { func (m *Mipmap) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) error {
return m.orig.DumpScreenshot(name, blackbg) return m.orig.DumpScreenshot(graphicsDriver, name, blackbg)
} }
func (m *Mipmap) ReplacePixels(pix []byte, x, y, width, height int) error { func (m *Mipmap) ReplacePixels(graphicsDriver graphicsdriver.Graphics, pix []byte, x, y, width, height int) error {
if err := m.orig.ReplacePixels(pix, x, y, width, height); err != nil { if err := m.orig.ReplacePixels(graphicsDriver, pix, x, y, width, height); err != nil {
return err return err
} }
m.disposeMipmaps() m.disposeMipmaps()
return nil return nil
} }
func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) { func (m *Mipmap) Pixels(graphicsDriver graphicsdriver.Graphics, x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height) return m.orig.Pixels(graphicsDriver, x, y, width, height)
} }
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm affine.ColorM, mode graphicsdriver.CompositeMode, filter graphicsdriver.Filter, address graphicsdriver.Address, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) { func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm affine.ColorM, mode graphicsdriver.CompositeMode, filter graphicsdriver.Filter, address graphicsdriver.Address, dstRegion, srcRegion graphicsdriver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms [][]float32, evenOdd bool, canSkipMipmap bool) {

View File

@ -447,12 +447,12 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item) i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
} }
func (i *Image) readPixelsFromGPUIfNeeded() error { func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics) error {
if len(i.drawTrianglesHistory) > 0 || i.stale { if len(i.drawTrianglesHistory) > 0 || i.stale {
if err := graphicscommand.FlushCommands(); err != nil { if err := graphicscommand.FlushCommands(graphicsDriver); err != nil {
return err return err
} }
if err := i.readPixelsFromGPU(); err != nil { if err := i.readPixelsFromGPU(graphicsDriver); err != nil {
return err return err
} }
} }
@ -462,12 +462,12 @@ func (i *Image) readPixelsFromGPUIfNeeded() error {
// At returns a color value at (x, y). // At returns a color value at (x, y).
// //
// Note that this must not be called until context is available. // Note that this must not be called until context is available.
func (i *Image) At(x, y int) (byte, byte, byte, byte, error) { func (i *Image) At(graphicsDriver graphicsdriver.Graphics, x, y int) (byte, byte, byte, byte, error) {
if x < 0 || y < 0 || i.width <= x || i.height <= y { if x < 0 || y < 0 || i.width <= x || i.height <= y {
return 0, 0, 0, 0, nil return 0, 0, 0, 0, nil
} }
if err := i.readPixelsFromGPUIfNeeded(); err != nil { if err := i.readPixelsFromGPUIfNeeded(graphicsDriver); err != nil {
return 0, 0, 0, 0, err return 0, 0, 0, 0, err
} }
@ -496,9 +496,9 @@ func (i *Image) makeStaleIfDependingOnShader(shader *Shader) {
} }
// readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state. // readPixelsFromGPU reads the pixels from GPU and resolves the image's 'stale' state.
func (i *Image) readPixelsFromGPU() error { func (i *Image) readPixelsFromGPU(graphicsDriver graphicsdriver.Graphics) error {
pix := make([]byte, 4*i.width*i.height) pix := make([]byte, 4*i.width*i.height)
if err := i.image.ReadPixels(pix); err != nil { if err := i.image.ReadPixels(graphicsDriver, pix); err != nil {
return err return err
} }
i.basePixels = Pixels{} i.basePixels = Pixels{}
@ -509,7 +509,7 @@ func (i *Image) readPixelsFromGPU() error {
} }
// resolveStale resolves the image's 'stale' state. // resolveStale resolves the image's 'stale' state.
func (i *Image) resolveStale() error { func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error {
if !NeedsRestoring() { if !NeedsRestoring() {
return nil return nil
} }
@ -523,7 +523,7 @@ func (i *Image) resolveStale() error {
if !i.stale { if !i.stale {
return nil return nil
} }
return i.readPixelsFromGPU() return i.readPixelsFromGPU(graphicsDriver)
} }
// dependsOn reports whether the image depends on target. // dependsOn reports whether the image depends on target.
@ -574,7 +574,7 @@ func (i *Image) hasDependency() bool {
} }
// Restore restores *graphicscommand.Image from the pixels using its state. // Restore restores *graphicscommand.Image from the pixels using its state.
func (i *Image) restore() error { func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
w, h := i.width, i.height w, h := i.width, i.height
// Do not dispose the image here. The image should be already disposed. // Do not dispose the image here. The image should be already disposed.
@ -627,7 +627,7 @@ func (i *Image) restore() error {
if len(i.drawTrianglesHistory) > 0 { if len(i.drawTrianglesHistory) > 0 {
i.basePixels = Pixels{} i.basePixels = Pixels{}
pix := make([]byte, 4*w*h) pix := make([]byte, 4*w*h)
if err := gimg.ReadPixels(pix); err != nil { if err := gimg.ReadPixels(graphicsDriver, pix); err != nil {
return err return err
} }
i.basePixels.AddOrReplace(pix, 0, 0, w, h) i.basePixels.AddOrReplace(pix, 0, 0, w, h)
@ -654,16 +654,16 @@ func (i *Image) Dispose() {
// isInvalidated returns a boolean value indicating whether the image is invalidated. // isInvalidated returns a boolean value indicating whether the image is invalidated.
// //
// If an image is invalidated, GL context is lost and all the images should be restored asap. // If an image is invalidated, GL context is lost and all the images should be restored asap.
func (i *Image) isInvalidated() (bool, error) { func (i *Image) isInvalidated(graphicsDriver graphicsdriver.Graphics) (bool, error) {
// FlushCommands is required because c.offscreen.impl might not have an actual texture. // FlushCommands is required because c.offscreen.impl might not have an actual texture.
if err := graphicscommand.FlushCommands(); err != nil { if err := graphicscommand.FlushCommands(graphicsDriver); err != nil {
return false, err return false, err
} }
return i.image.IsInvalidated(), nil return i.image.IsInvalidated(), nil
} }
func (i *Image) Dump(path string, blackbg bool, rect image.Rectangle) error { func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) error {
return i.image.Dump(path, blackbg, rect) return i.image.Dump(graphicsDriver, path, blackbg, rect)
} }
func (i *Image) clearDrawTrianglesHistory() { func (i *Image) clearDrawTrianglesHistory() {

View File

@ -26,12 +26,11 @@ import (
// forceRestoring reports whether restoring forcely happens or not. // forceRestoring reports whether restoring forcely happens or not.
var forceRestoring = false var forceRestoring = false
var needsRestoringByGraphicsDriver bool
// NeedsRestoring reports whether restoring process works or not. // NeedsRestoring reports whether restoring process works or not.
func NeedsRestoring() bool { func NeedsRestoring() bool {
if forceRestoring { return forceRestoring || needsRestoringByGraphicsDriver
return true
}
return graphicscommand.NeedsRestoring()
} }
// EnableRestoringForTesting forces to enable restoring for testing. // EnableRestoringForTesting forces to enable restoring for testing.
@ -57,7 +56,7 @@ var theImages = &images{
// all stale images. // all stale images.
// //
// ResolveStaleImages is intended to be called at the end of a frame. // ResolveStaleImages is intended to be called at the end of a frame.
func ResolveStaleImages() error { func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error {
if debug.IsDebug { if debug.IsDebug {
debug.Logf("Internal image sizes:\n") debug.Logf("Internal image sizes:\n")
imgs := make([]*graphicscommand.Image, 0, len(theImages.images)) imgs := make([]*graphicscommand.Image, 0, len(theImages.images))
@ -67,19 +66,19 @@ func ResolveStaleImages() error {
graphicscommand.LogImagesInfo(imgs) graphicscommand.LogImagesInfo(imgs)
} }
if err := graphicscommand.FlushCommands(); err != nil { if err := graphicscommand.FlushCommands(graphicsDriver); err != nil {
return err return err
} }
if !NeedsRestoring() { if !NeedsRestoring() {
return nil return nil
} }
return theImages.resolveStaleImages() return theImages.resolveStaleImages(graphicsDriver)
} }
// RestoreIfNeeded restores the images. // RestoreIfNeeded restores the images.
// //
// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
func RestoreIfNeeded() error { func RestoreIfNeeded(graphicsDriver graphicsdriver.Graphics) error {
if !NeedsRestoring() { if !NeedsRestoring() {
return nil return nil
} }
@ -98,7 +97,7 @@ func RestoreIfNeeded() error {
continue continue
} }
var err error var err error
r, err = img.isInvalidated() r, err = img.isInvalidated(graphicsDriver)
if err != nil { if err != nil {
return err return err
} }
@ -111,22 +110,22 @@ func RestoreIfNeeded() error {
} }
} }
err := graphicscommand.ResetGraphicsDriverState() err := graphicscommand.ResetGraphicsDriverState(graphicsDriver)
if err == graphicsdriver.GraphicsNotReady { if err == graphicsdriver.GraphicsNotReady {
return nil return nil
} }
if err != nil { if err != nil {
return err return err
} }
return theImages.restore() return theImages.restore(graphicsDriver)
} }
// DumpImages dumps all the current images to the specified directory. // DumpImages dumps all the current images to the specified directory.
// //
// This is for testing usage. // This is for testing usage.
func DumpImages(dir string) error { func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) error {
for img := range theImages.images { for img := range theImages.images {
if err := img.Dump(filepath.Join(dir, "*.png"), false, image.Rect(0, 0, img.width, img.height)); err != nil { if err := img.Dump(graphicsDriver, filepath.Join(dir, "*.png"), false, image.Rect(0, 0, img.width, img.height)); err != nil {
return err return err
} }
} }
@ -154,10 +153,10 @@ func (i *images) removeShader(shader *Shader) {
} }
// resolveStaleImages resolves stale images. // resolveStaleImages resolves stale images.
func (i *images) resolveStaleImages() error { func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) error {
i.lastTarget = nil i.lastTarget = nil
for img := range i.images { for img := range i.images {
if err := img.resolveStale(); err != nil { if err := img.resolveStale(graphicsDriver); err != nil {
return err return err
} }
} }
@ -194,7 +193,7 @@ func (i *images) makeStaleIfDependingOnShader(shader *Shader) {
// restore restores the images. // restore restores the images.
// //
// Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers.
func (i *images) restore() error { func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error {
if !NeedsRestoring() { if !NeedsRestoring() {
panic("restorable: restore cannot be called when restoring is disabled") panic("restorable: restore cannot be called when restoring is disabled")
} }
@ -267,7 +266,7 @@ func (i *images) restore() error {
} }
for _, img := range sorted { for _, img := range sorted {
if err := img.restore(); err != nil { if err := img.restore(graphicsDriver); err != nil {
return err return err
} }
} }
@ -280,14 +279,15 @@ func (i *images) restore() error {
var graphicsDriverInitialized bool var graphicsDriverInitialized bool
// InitializeGraphicsDriverState initializes the graphics driver state. // InitializeGraphicsDriverState initializes the graphics driver state.
func InitializeGraphicsDriverState() error { func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) error {
graphicsDriverInitialized = true graphicsDriverInitialized = true
return graphicscommand.InitializeGraphicsDriverState() needsRestoringByGraphicsDriver = graphicsDriver.NeedsRestoring()
return graphicscommand.InitializeGraphicsDriverState(graphicsDriver)
} }
// MaxImageSize returns the maximum size of an image. // MaxImageSize returns the maximum size of an image.
func MaxImageSize() int { func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int {
return graphicscommand.MaxImageSize() return graphicscommand.MaxImageSize(graphicsDriver)
} }
// OnContextLost is called when the context lost is detected in an explicit way. // OnContextLost is called when the context lost is detected in an explicit way.

View File

@ -24,6 +24,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/restorable"
etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" etesting "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -61,10 +62,10 @@ func TestRestore(t *testing.T) {
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff} clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
img0.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, 1, 1) img0.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, 1, 1)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := clr0 want := clr0
@ -80,10 +81,10 @@ func TestRestoreWithoutDraw(t *testing.T) {
// If there is no drawing command on img0, img0 is cleared when restored. // If there is no drawing command on img0, img0 is cleared when restored.
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -140,10 +141,10 @@ func TestRestoreChain(t *testing.T) {
} }
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*restorable.Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*restorable.Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
} }
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := clr want := clr
@ -192,10 +193,10 @@ func TestRestoreChain2(t *testing.T) {
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*restorable.Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*restorable.Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
} }
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for i, img := range imgs { for i, img := range imgs {
@ -239,10 +240,10 @@ func TestRestoreOverrideSource(t *testing.T) {
img3.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img3.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h) img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
testCases := []struct { testCases := []struct {
@ -341,10 +342,10 @@ func TestRestoreComplexGraph(t *testing.T) {
img7.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img2}, offsets, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img7.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img2}, offsets, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
vs = quadVertices(w, h, 2, 0) vs = quadVertices(w, h, 2, 0)
img7.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img3}, offsets, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img7.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img3}, offsets, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
testCases := []struct { testCases := []struct {
@ -440,10 +441,10 @@ func TestRestoreRecursive(t *testing.T) {
} }
img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
img0.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img0.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
testCases := []struct { testCases := []struct {
@ -488,7 +489,7 @@ func TestReplacePixels(t *testing.T) {
// Check the region (5, 7)-(9, 11). Outside state is indeterministic. // Check the region (5, 7)-(9, 11). Outside state is indeterministic.
for j := 7; j < 11; j++ { for j := 7; j < 11; j++ {
for i := 5; i < 9; i++ { for i := 5; i < 9; i++ {
r, g, b, a, err := img.At(i, j) r, g, b, a, err := img.At(ui.GraphicsDriverForTesting(), i, j)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -499,15 +500,15 @@ func TestReplacePixels(t *testing.T) {
} }
} }
} }
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 7; j < 11; j++ { for j := 7; j < 11; j++ {
for i := 5; i < 9; i++ { for i := 5; i < 9; i++ {
r, g, b, a, err := img.At(i, j) r, g, b, a, err := img.At(ui.GraphicsDriverForTesting(), i, j)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -542,13 +543,13 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img1.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1) img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, g, b, a, err := img1.At(0, 0) r, g, b, a, err := img1.At(ui.GraphicsDriverForTesting(), 0, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -586,13 +587,13 @@ func TestDispose(t *testing.T) {
img0.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false) img0.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, nil, nil, false)
img1.Dispose() img1.Dispose()
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
r, g, b, a, err := img0.At(0, 0) r, g, b, a, err := img0.At(ui.GraphicsDriverForTesting(), 0, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -718,10 +719,10 @@ func TestReplacePixelsOnly(t *testing.T) {
} }
} }
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := color.RGBA{1, 2, 3, 4} want := color.RGBA{1, 2, 3, 4}
@ -762,7 +763,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being // Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
// stale. // stale.
want := byte(0xff) want := byte(0xff)
got, _, _, _, err := dst.At(0, 0) got, _, _, _, err := dst.At(ui.GraphicsDriverForTesting(), 0, 0)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -836,7 +837,7 @@ func TestExtend(t *testing.T) {
for j := 0; j < h*2; j++ { for j := 0; j < h*2; j++ {
for i := 0; i < w*2; i++ { for i := 0; i < w*2; i++ {
got, _, _, _, err := extended.At(i, j) got, _, _, _, err := extended.At(ui.GraphicsDriverForTesting(), i, j)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -892,21 +893,21 @@ func TestMutateSlices(t *testing.T) {
for i := range is { for i := range is {
is[i] = 0 is[i] = 0
} }
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
for j := 0; j < h; j++ { for j := 0; j < h; j++ {
for i := 0; i < w; i++ { for i := 0; i < w; i++ {
r, g, b, a, err := src.At(i, j) r, g, b, a, err := src.At(ui.GraphicsDriverForTesting(), i, j)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
want := color.RGBA{r, g, b, a} want := color.RGBA{r, g, b, a}
r, g, b, a, err = dst.At(i, j) r, g, b, a, err = dst.At(ui.GraphicsDriverForTesting(), i, j)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -20,12 +20,17 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "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/restorable" "github.com/hajimehoshi/ebiten/v2/internal/restorable"
etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" etesting "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
) )
func needsInvertY() bool {
g := ui.GraphicsDriverForTesting()
return g.FramebufferYDirection() != g.NDCYDirection()
}
func clearImage(img *restorable.Image, w, h int) { func clearImage(img *restorable.Image, w, h int) {
emptyImage := restorable.NewImage(3, 3) emptyImage := restorable.NewImage(3, 3)
defer emptyImage.Dispose() defer emptyImage.Dispose()
@ -58,7 +63,7 @@ func TestShader(t *testing.T) {
img := restorable.NewImage(1, 1) img := restorable.NewImage(1, 1)
defer img.Dispose() defer img.Dispose()
ir := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(needsInvertY(), 0xff, 0, 0, 0xff)
s := restorable.NewShader(&ir) s := restorable.NewShader(&ir)
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
X: 0, X: 0,
@ -68,10 +73,10 @@ func TestShader(t *testing.T) {
} }
img.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false) img.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -93,7 +98,7 @@ func TestShaderChain(t *testing.T) {
imgs[0].ReplacePixels([]byte{0xff, 0, 0, 0xff}, 0, 0, 1, 1) imgs[0].ReplacePixels([]byte{0xff, 0, 0, 0xff}, 0, 0, 1, 1)
ir := etesting.ShaderProgramImages(graphicscommand.NeedsInvertY(), 1) ir := etesting.ShaderProgramImages(needsInvertY(), 1)
s := restorable.NewShader(&ir) s := restorable.NewShader(&ir)
for i := 0; i < num-1; i++ { for i := 0; i < num-1; i++ {
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
@ -105,10 +110,10 @@ func TestShaderChain(t *testing.T) {
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*restorable.Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false) imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*restorable.Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dr, graphicsdriver.Region{}, s, nil, false)
} }
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -132,7 +137,7 @@ func TestShaderMultipleSources(t *testing.T) {
dst := restorable.NewImage(1, 1) dst := restorable.NewImage(1, 1)
ir := etesting.ShaderProgramImages(graphicscommand.NeedsInvertY(), 3) ir := etesting.ShaderProgramImages(needsInvertY(), 3)
s := restorable.NewShader(&ir) s := restorable.NewShader(&ir)
var offsets [graphics.ShaderImageNum - 1][2]float32 var offsets [graphics.ShaderImageNum - 1][2]float32
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
@ -146,10 +151,10 @@ func TestShaderMultipleSources(t *testing.T) {
// Clear one of the sources after DrawTriangles. dst should not be affected. // Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 1, 1) clearImage(srcs[0], 1, 1)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -171,7 +176,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
dst := restorable.NewImage(1, 1) dst := restorable.NewImage(1, 1)
ir := etesting.ShaderProgramImages(graphicscommand.NeedsInvertY(), 3) ir := etesting.ShaderProgramImages(needsInvertY(), 3)
s := restorable.NewShader(&ir) s := restorable.NewShader(&ir)
offsets := [graphics.ShaderImageNum - 1][2]float32{ offsets := [graphics.ShaderImageNum - 1][2]float32{
{1, 0}, {1, 0},
@ -188,10 +193,10 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
// Clear one of the sources after DrawTriangles. dst should not be affected. // Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 3, 1) clearImage(srcs[0], 3, 1)
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -206,7 +211,7 @@ func TestShaderDispose(t *testing.T) {
img := restorable.NewImage(1, 1) img := restorable.NewImage(1, 1)
defer img.Dispose() defer img.Dispose()
ir := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(needsInvertY(), 0xff, 0, 0, 0xff)
s := restorable.NewShader(&ir) s := restorable.NewShader(&ir)
dr := graphicsdriver.Region{ dr := graphicsdriver.Region{
X: 0, X: 0,
@ -220,10 +225,10 @@ func TestShaderDispose(t *testing.T) {
// stale. // stale.
s.Dispose() s.Dispose()
if err := restorable.ResolveStaleImages(); err != nil { if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if err := restorable.RestoreIfNeeded(); err != nil { if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -23,7 +23,6 @@ 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"
) )
@ -83,7 +82,7 @@ func (c *contextImpl) updateFrameImpl(updateCount int, outsideWidth, outsideHeig
debug.Logf("----\n") debug.Logf("----\n")
if err := buffered.BeginFrame(); err != nil { if err := buffered.BeginFrame(graphicsDriver()); err != nil {
return err return err
} }
@ -111,14 +110,14 @@ 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 := graphicscommand.Draw(c.game, screenScale, offsetX, offsetY, theGlobalState.isScreenClearedEveryFrame(), theGlobalState.isScreenFilterEnabled()); err != nil { if err := c.game.Draw(screenScale, offsetX, offsetY, graphicsDriver().NeedsClearingScreen(), graphicsDriver().FramebufferYDirection(), theGlobalState.isScreenClearedEveryFrame(), theGlobalState.isScreenFilterEnabled()); err != nil {
return err return err
} }
// All the vertices data are consumed at the end of the frame, and the data backend can be // All the vertices data are consumed at the end of the frame, and the data backend can be
// available after that. Until then, lock the vertices backend. // available after that. Until then, lock the vertices backend.
return graphicspkg.LockAndResetVertices(func() error { return graphicspkg.LockAndResetVertices(func() error {
if err := buffered.EndFrame(); err != nil { if err := buffered.EndFrame(graphicsDriver()); err != nil {
return err return err
} }
return nil return nil

View File

@ -12,17 +12,12 @@
// 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.
//go:build android || ios package ui
// +build android ios
package graphicscommand
import ( import (
"golang.org/x/mobile/gl" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl"
) )
func SetGomobileGLContext(ctx gl.Context) { func GraphicsDriverForTesting() graphicsdriver.Graphics {
graphicsDriver().(*opengl.Graphics).SetGomobileGLContext(ctx) return graphicsDriver()
} }

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 graphicscommand package ui
// #cgo CFLAGS: -x objective-c // #cgo CFLAGS: -x objective-c
// #cgo LDFLAGS: -framework Foundation // #cgo LDFLAGS: -framework Foundation

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 graphicscommand package ui
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"

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 graphicscommand package ui
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"

View File

@ -15,7 +15,7 @@
//go:build !darwin || ebitencbackend || ebitengl //go:build !darwin || ebitencbackend || ebitengl
// +build !darwin ebitencbackend ebitengl // +build !darwin ebitencbackend ebitengl
package graphicscommand package ui
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"

View File

@ -16,6 +16,7 @@ package ui
import ( import (
"github.com/hajimehoshi/ebiten/v2/internal/affine" "github.com/hajimehoshi/ebiten/v2/internal/affine"
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/mipmap"
@ -60,15 +61,15 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
} }
func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error { func (i *Image) ReplacePixels(pix []byte, x, y, width, height int) error {
return i.mipmap.ReplacePixels(pix, x, y, width, height) return i.mipmap.ReplacePixels(graphicsDriver(), pix, x, y, width, height)
} }
func (i *Image) Pixels(x, y, width, height int) ([]byte, error) { func (i *Image) Pixels(x, y, width, height int) ([]byte, error) {
return i.mipmap.Pixels(x, y, width, height) return i.mipmap.Pixels(graphicsDriver(), x, y, width, height)
} }
func (i *Image) DumpScreenshot(name string, blackbg bool) error { func (i *Image) DumpScreenshot(name string, blackbg bool) error {
return i.mipmap.DumpScreenshot(name, blackbg) return i.mipmap.DumpScreenshot(graphicsDriver(), name, blackbg)
} }
func (i *Image) SetIndependent(independent bool) { func (i *Image) SetIndependent(independent bool) {
@ -78,3 +79,7 @@ func (i *Image) SetIndependent(independent bool) {
func (i *Image) SetVolatile(volatile bool) { func (i *Image) SetVolatile(volatile bool) {
i.mipmap.SetVolatile(volatile) i.mipmap.SetVolatile(volatile)
} }
func DumpImages(dir string) error {
return atlas.DumpImages(graphicsDriver(), dir)
}

View File

@ -22,7 +22,6 @@ import (
"strings" "strings"
"github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/mipmap" "github.com/hajimehoshi/ebiten/v2/internal/mipmap"
"github.com/hajimehoshi/ebiten/v2/internal/shader" "github.com/hajimehoshi/ebiten/v2/internal/shader"
"github.com/hajimehoshi/ebiten/v2/internal/shaderir" "github.com/hajimehoshi/ebiten/v2/internal/shaderir"
@ -117,7 +116,7 @@ func NewShader(src []byte) (*Shader, error) {
var buf bytes.Buffer var buf bytes.Buffer
buf.Write(src) buf.Write(src)
buf.WriteString(shaderSuffix) buf.WriteString(shaderSuffix)
if graphicscommand.NeedsInvertY() { if graphicsDriver().FramebufferYDirection() != graphicsDriver().NDCYDirection() {
buf.WriteString(` buf.WriteString(`
func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) {
return mat4( return mat4(

View File

@ -28,7 +28,6 @@ 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"
) )
@ -668,7 +667,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 graphicscommand.IsGL() { if graphicsDriver().IsGL() {
u.window.MakeContextCurrent() u.window.MakeContextCurrent()
} }
@ -722,7 +721,7 @@ func (u *UserInterface) registerWindowSetSizeCallback() {
u.err = err u.err = err
} }
if graphicscommand.IsGL() { if graphicsDriver().IsGL() {
u.swapBuffers() u.swapBuffers()
} }
}) })
@ -816,7 +815,7 @@ event:
} }
func (u *UserInterface) init() error { func (u *UserInterface) init() error {
if graphicscommand.IsGL() { if graphicsDriver().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)
@ -837,7 +836,7 @@ func (u *UserInterface) init() error {
transparent = glfw.True transparent = glfw.True
} }
glfw.WindowHint(glfw.TransparentFramebuffer, transparent) glfw.WindowHint(glfw.TransparentFramebuffer, transparent)
graphicscommand.SetTransparent(u.isInitScreenTransparent()) graphicsDriver().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.
@ -889,7 +888,9 @@ func (u *UserInterface) init() error {
u.window.Show() u.window.Show()
graphicscommand.SetWindow(u.nativeWindow()) if g, ok := graphicsDriver().(interface{ SetWindow(uintptr) }); ok {
g.SetWindow(u.nativeWindow())
}
return nil return nil
} }
@ -1066,7 +1067,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 graphicscommand.IsGL() { if graphicsDriver().IsGL() {
u.t.Call(u.swapBuffers) u.t.Call(u.swapBuffers)
} }
@ -1088,7 +1089,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 graphicscommand.IsGL() { if graphicsDriver().IsGL() {
u.window.SwapBuffers() u.window.SwapBuffers()
} }
} }
@ -1145,7 +1146,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)
graphicscommand.SetFullscreen(fullscreen) graphicsDriver().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 {
@ -1219,7 +1220,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 graphicscommand.IsGL() { if graphicsDriver().IsGL() {
glfw.PollEvents() glfw.PollEvents()
u.swapBuffers() u.swapBuffers()
} }
@ -1266,7 +1267,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 graphicscommand.IsGL() { if graphicsDriver().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).
@ -1280,7 +1281,7 @@ func (u *UserInterface) updateVsync() {
glfw.SwapInterval(0) glfw.SwapInterval(0)
} }
} }
graphicscommand.SetVsyncEnabled(u.fpsMode == FPSModeVsyncOn) graphicsDriver().SetVsyncEnabled(u.fpsMode == FPSModeVsyncOn)
} }
// currentMonitor returns the current active monitor. // currentMonitor returns the current active monitor.

View File

@ -227,7 +227,6 @@ 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.
@ -317,7 +316,7 @@ func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
} }
func (u *UserInterface) adjustViewSize() { func (u *UserInterface) adjustViewSize() {
if graphicscommand.IsGL() { if graphicsDriver().IsGL() {
return return
} }
C.adjustViewSize(C.uintptr_t(u.window.GetCocoaWindow())) C.adjustViewSize(C.uintptr_t(u.window.GetCocoaWindow()))

View File

@ -36,6 +36,7 @@ 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"
@ -276,7 +277,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
graphicscommand.SetGomobileGLContext(ctx) graphicsDriver().(*opengl.Graphics).SetGomobileGLContext(ctx)
} else { } else {
u.t = thread.NewOSThread() u.t = thread.NewOSThread()
graphicscommand.SetRenderingThread(u.t) graphicscommand.SetRenderingThread(u.t)