diff --git a/imagedumper_desktop.go b/imagedumper_desktop.go index 207abc7c6..fb0833805 100644 --- a/imagedumper_desktop.go +++ b/imagedumper_desktop.go @@ -22,7 +22,7 @@ import ( "os" "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. @@ -72,7 +72,7 @@ func dumpInternalImages() error { return err } - if err := atlas.DumpImages(dir); err != nil { + if err := ui.DumpImages(dir); err != nil { return err } diff --git a/internal/atlas/export_test.go b/internal/atlas/export_test.go index 7799eebf2..cf42bd4fd 100644 --- a/internal/atlas/export_test.go +++ b/internal/atlas/export_test.go @@ -14,13 +14,17 @@ package atlas +import ( + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" +) + const ( BaseCountToPutOnAtlas = baseCountToPutOnAtlas PaddingSize = paddingSize ) -func PutImagesOnAtlasForTesting() error { - return putImagesOnAtlas() +func PutImagesOnAtlasForTesting(graphicsDriver graphicsdriver.Graphics) error { + return putImagesOnAtlas(graphicsDriver) } var ( diff --git a/internal/atlas/image.go b/internal/atlas/image.go index f24c54a61..0028d4ce2 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -117,11 +117,11 @@ func resolveDeferred() { // Actual time duration is increased in an exponential way for each usages as a rendering target. const baseCountToPutOnAtlas = 10 -func putImagesOnAtlas() error { +func putImagesOnAtlas(graphicsDriver graphicsdriver.Graphics) error { for i := range imagesToPutOnAtlas { i.usedAsSourceCount++ if i.usedAsSourceCount >= baseCountToPutOnAtlas*(1< 0 || i.stale { - if err := graphicscommand.FlushCommands(); err != nil { + if err := graphicscommand.FlushCommands(graphicsDriver); err != nil { return err } - if err := i.readPixelsFromGPU(); err != nil { + if err := i.readPixelsFromGPU(graphicsDriver); err != nil { return err } } @@ -462,12 +462,12 @@ func (i *Image) readPixelsFromGPUIfNeeded() error { // At returns a color value at (x, y). // // 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 { 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 } @@ -496,9 +496,9 @@ func (i *Image) makeStaleIfDependingOnShader(shader *Shader) { } // 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) - if err := i.image.ReadPixels(pix); err != nil { + if err := i.image.ReadPixels(graphicsDriver, pix); err != nil { return err } i.basePixels = Pixels{} @@ -509,7 +509,7 @@ func (i *Image) readPixelsFromGPU() error { } // resolveStale resolves the image's 'stale' state. -func (i *Image) resolveStale() error { +func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error { if !NeedsRestoring() { return nil } @@ -523,7 +523,7 @@ func (i *Image) resolveStale() error { if !i.stale { return nil } - return i.readPixelsFromGPU() + return i.readPixelsFromGPU(graphicsDriver) } // 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. -func (i *Image) restore() error { +func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error { w, h := i.width, i.height // 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 { i.basePixels = Pixels{} pix := make([]byte, 4*w*h) - if err := gimg.ReadPixels(pix); err != nil { + if err := gimg.ReadPixels(graphicsDriver, pix); err != nil { return err } 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. // // 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. - if err := graphicscommand.FlushCommands(); err != nil { + if err := graphicscommand.FlushCommands(graphicsDriver); err != nil { return false, err } return i.image.IsInvalidated(), nil } -func (i *Image) Dump(path string, blackbg bool, rect image.Rectangle) error { - return i.image.Dump(path, blackbg, rect) +func (i *Image) Dump(graphicsDriver graphicsdriver.Graphics, path string, blackbg bool, rect image.Rectangle) error { + return i.image.Dump(graphicsDriver, path, blackbg, rect) } func (i *Image) clearDrawTrianglesHistory() { diff --git a/internal/restorable/images.go b/internal/restorable/images.go index 3b901cb93..218dc060a 100644 --- a/internal/restorable/images.go +++ b/internal/restorable/images.go @@ -26,12 +26,11 @@ import ( // forceRestoring reports whether restoring forcely happens or not. var forceRestoring = false +var needsRestoringByGraphicsDriver bool + // NeedsRestoring reports whether restoring process works or not. func NeedsRestoring() bool { - if forceRestoring { - return true - } - return graphicscommand.NeedsRestoring() + return forceRestoring || needsRestoringByGraphicsDriver } // EnableRestoringForTesting forces to enable restoring for testing. @@ -57,7 +56,7 @@ var theImages = &images{ // all stale images. // // ResolveStaleImages is intended to be called at the end of a frame. -func ResolveStaleImages() error { +func ResolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { if debug.IsDebug { debug.Logf("Internal image sizes:\n") imgs := make([]*graphicscommand.Image, 0, len(theImages.images)) @@ -67,19 +66,19 @@ func ResolveStaleImages() error { graphicscommand.LogImagesInfo(imgs) } - if err := graphicscommand.FlushCommands(); err != nil { + if err := graphicscommand.FlushCommands(graphicsDriver); err != nil { return err } if !NeedsRestoring() { return nil } - return theImages.resolveStaleImages() + return theImages.resolveStaleImages(graphicsDriver) } // RestoreIfNeeded restores the images. // // Restoring means to make all *graphicscommand.Image objects have their textures and framebuffers. -func RestoreIfNeeded() error { +func RestoreIfNeeded(graphicsDriver graphicsdriver.Graphics) error { if !NeedsRestoring() { return nil } @@ -98,7 +97,7 @@ func RestoreIfNeeded() error { continue } var err error - r, err = img.isInvalidated() + r, err = img.isInvalidated(graphicsDriver) if err != nil { return err } @@ -111,22 +110,22 @@ func RestoreIfNeeded() error { } } - err := graphicscommand.ResetGraphicsDriverState() + err := graphicscommand.ResetGraphicsDriverState(graphicsDriver) if err == graphicsdriver.GraphicsNotReady { return nil } if err != nil { return err } - return theImages.restore() + return theImages.restore(graphicsDriver) } // DumpImages dumps all the current images to the specified directory. // // This is for testing usage. -func DumpImages(dir string) error { +func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) error { 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 } } @@ -154,10 +153,10 @@ func (i *images) removeShader(shader *Shader) { } // resolveStaleImages resolves stale images. -func (i *images) resolveStaleImages() error { +func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) error { i.lastTarget = nil for img := range i.images { - if err := img.resolveStale(); err != nil { + if err := img.resolveStale(graphicsDriver); err != nil { return err } } @@ -194,7 +193,7 @@ func (i *images) makeStaleIfDependingOnShader(shader *Shader) { // restore restores the images. // // 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() { panic("restorable: restore cannot be called when restoring is disabled") } @@ -267,7 +266,7 @@ func (i *images) restore() error { } for _, img := range sorted { - if err := img.restore(); err != nil { + if err := img.restore(graphicsDriver); err != nil { return err } } @@ -280,14 +279,15 @@ func (i *images) restore() error { var graphicsDriverInitialized bool // InitializeGraphicsDriverState initializes the graphics driver state. -func InitializeGraphicsDriverState() error { +func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) error { graphicsDriverInitialized = true - return graphicscommand.InitializeGraphicsDriverState() + needsRestoringByGraphicsDriver = graphicsDriver.NeedsRestoring() + return graphicscommand.InitializeGraphicsDriverState(graphicsDriver) } // MaxImageSize returns the maximum size of an image. -func MaxImageSize() int { - return graphicscommand.MaxImageSize() +func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int { + return graphicscommand.MaxImageSize(graphicsDriver) } // OnContextLost is called when the context lost is detected in an explicit way. diff --git a/internal/restorable/images_test.go b/internal/restorable/images_test.go index 1a747162d..bae051bb1 100644 --- a/internal/restorable/images_test.go +++ b/internal/restorable/images_test.go @@ -24,6 +24,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/restorable" etesting "github.com/hajimehoshi/ebiten/v2/internal/testing" + "github.com/hajimehoshi/ebiten/v2/internal/ui" ) func TestMain(m *testing.M) { @@ -61,10 +62,10 @@ func TestRestore(t *testing.T) { clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff} 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) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } 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 err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { 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) } - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } 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) } - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } 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) 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) - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } 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) 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) - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } 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) 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) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } testCases := []struct { @@ -488,7 +489,7 @@ func TestReplacePixels(t *testing.T) { // Check the region (5, 7)-(9, 11). Outside state is indeterministic. for j := 7; j < 11; j++ { 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 { 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) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } for j := 7; j < 11; j++ { 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 { 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.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) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { 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 { 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) img1.Dispose() - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { 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 { 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) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } 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 // stale. want := byte(0xff) - got, _, _, _, err := dst.At(0, 0) + got, _, _, _, err := dst.At(ui.GraphicsDriverForTesting(), 0, 0) if err != nil { t.Fatal(err) } @@ -836,7 +837,7 @@ func TestExtend(t *testing.T) { for j := 0; j < h*2; j++ { for i := 0; i < w*2; i++ { - got, _, _, _, err := extended.At(i, j) + got, _, _, _, err := extended.At(ui.GraphicsDriverForTesting(), i, j) if err != nil { t.Fatal(err) } @@ -892,21 +893,21 @@ func TestMutateSlices(t *testing.T) { for i := range is { is[i] = 0 } - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } for j := 0; j < h; j++ { 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 { t.Fatal(err) } 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 { t.Fatal(err) } diff --git a/internal/restorable/shader_test.go b/internal/restorable/shader_test.go index 30282aaad..92e5b0699 100644 --- a/internal/restorable/shader_test.go +++ b/internal/restorable/shader_test.go @@ -20,12 +20,17 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/affine" "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/restorable" 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) { emptyImage := restorable.NewImage(3, 3) defer emptyImage.Dispose() @@ -58,7 +63,7 @@ func TestShader(t *testing.T) { img := restorable.NewImage(1, 1) defer img.Dispose() - ir := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0xff, 0, 0, 0xff) + ir := etesting.ShaderProgramFill(needsInvertY(), 0xff, 0, 0, 0xff) s := restorable.NewShader(&ir) dr := graphicsdriver.Region{ 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) - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -93,7 +98,7 @@ func TestShaderChain(t *testing.T) { 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) for i := 0; i < num-1; i++ { 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) } - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -132,7 +137,7 @@ func TestShaderMultipleSources(t *testing.T) { dst := restorable.NewImage(1, 1) - ir := etesting.ShaderProgramImages(graphicscommand.NeedsInvertY(), 3) + ir := etesting.ShaderProgramImages(needsInvertY(), 3) s := restorable.NewShader(&ir) var offsets [graphics.ShaderImageNum - 1][2]float32 dr := graphicsdriver.Region{ @@ -146,10 +151,10 @@ func TestShaderMultipleSources(t *testing.T) { // Clear one of the sources after DrawTriangles. dst should not be affected. clearImage(srcs[0], 1, 1) - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -171,7 +176,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { dst := restorable.NewImage(1, 1) - ir := etesting.ShaderProgramImages(graphicscommand.NeedsInvertY(), 3) + ir := etesting.ShaderProgramImages(needsInvertY(), 3) s := restorable.NewShader(&ir) offsets := [graphics.ShaderImageNum - 1][2]float32{ {1, 0}, @@ -188,10 +193,10 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) { // Clear one of the sources after DrawTriangles. dst should not be affected. clearImage(srcs[0], 3, 1) - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } @@ -206,7 +211,7 @@ func TestShaderDispose(t *testing.T) { img := restorable.NewImage(1, 1) defer img.Dispose() - ir := etesting.ShaderProgramFill(graphicscommand.NeedsInvertY(), 0xff, 0, 0, 0xff) + ir := etesting.ShaderProgramFill(needsInvertY(), 0xff, 0, 0, 0xff) s := restorable.NewShader(&ir) dr := graphicsdriver.Region{ X: 0, @@ -220,10 +225,10 @@ func TestShaderDispose(t *testing.T) { // stale. s.Dispose() - if err := restorable.ResolveStaleImages(); err != nil { + if err := restorable.ResolveStaleImages(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } - if err := restorable.RestoreIfNeeded(); err != nil { + if err := restorable.RestoreIfNeeded(ui.GraphicsDriverForTesting()); err != nil { t.Fatal(err) } diff --git a/internal/ui/context.go b/internal/ui/context.go index 1950f2fb3..f609d793e 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -23,7 +23,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/clock" "github.com/hajimehoshi/ebiten/v2/internal/debug" 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/hooks" ) @@ -83,7 +82,7 @@ func (c *contextImpl) updateFrameImpl(updateCount int, outsideWidth, outsideHeig debug.Logf("----\n") - if err := buffered.BeginFrame(); err != nil { + if err := buffered.BeginFrame(graphicsDriver()); err != nil { return err } @@ -111,14 +110,14 @@ func (c *contextImpl) updateFrameImpl(updateCount int, outsideWidth, outsideHeig // Draw the game. 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 } // 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. return graphicspkg.LockAndResetVertices(func() error { - if err := buffered.EndFrame(); err != nil { + if err := buffered.EndFrame(graphicsDriver()); err != nil { return err } return nil diff --git a/internal/graphicscommand/graphics_mobile.go b/internal/ui/graphics.go similarity index 69% rename from internal/graphicscommand/graphics_mobile.go rename to internal/ui/graphics.go index ca64a731a..444e239cc 100644 --- a/internal/graphicscommand/graphics_mobile.go +++ b/internal/ui/graphics.go @@ -12,17 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build android || ios -// +build android ios - -package graphicscommand +package ui import ( - "golang.org/x/mobile/gl" - - "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver/opengl" + "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" ) -func SetGomobileGLContext(ctx gl.Context) { - graphicsDriver().(*opengl.Graphics).SetGomobileGLContext(ctx) +func GraphicsDriverForTesting() graphicsdriver.Graphics { + return graphicsDriver() } diff --git a/internal/graphicscommand/graphics_darwin.go b/internal/ui/graphics_darwin.go similarity index 98% rename from internal/graphicscommand/graphics_darwin.go rename to internal/ui/graphics_darwin.go index b7bc1c30c..7967b2321 100644 --- a/internal/graphicscommand/graphics_darwin.go +++ b/internal/ui/graphics_darwin.go @@ -15,7 +15,7 @@ //go:build !ios && !ebitengl && !ebitencbackend // +build !ios,!ebitengl,!ebitencbackend -package graphicscommand +package ui // #cgo CFLAGS: -x objective-c // #cgo LDFLAGS: -framework Foundation diff --git a/internal/graphicscommand/graphics_ios_amd64.go b/internal/ui/graphics_ios_amd64.go similarity index 97% rename from internal/graphicscommand/graphics_ios_amd64.go rename to internal/ui/graphics_ios_amd64.go index dd48ddf50..dcdc452f6 100644 --- a/internal/graphicscommand/graphics_ios_amd64.go +++ b/internal/ui/graphics_ios_amd64.go @@ -15,7 +15,7 @@ //go:build ios && !ebitengl && !ebitencbackend // +build ios,!ebitengl,!ebitencbackend -package graphicscommand +package ui import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" diff --git a/internal/graphicscommand/graphics_ios_arm64.go b/internal/ui/graphics_ios_arm64.go similarity index 97% rename from internal/graphicscommand/graphics_ios_arm64.go rename to internal/ui/graphics_ios_arm64.go index 8490ed1c6..d6533f216 100644 --- a/internal/graphicscommand/graphics_ios_arm64.go +++ b/internal/ui/graphics_ios_arm64.go @@ -15,7 +15,7 @@ //go:build ios && !ebitengl && !ebitencbackend // +build ios,!ebitengl,!ebitencbackend -package graphicscommand +package ui import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" diff --git a/internal/graphicscommand/graphics_opengl.go b/internal/ui/graphics_opengl.go similarity index 97% rename from internal/graphicscommand/graphics_opengl.go rename to internal/ui/graphics_opengl.go index 50ab5a670..6ed81a887 100644 --- a/internal/graphicscommand/graphics_opengl.go +++ b/internal/ui/graphics_opengl.go @@ -15,7 +15,7 @@ //go:build !darwin || ebitencbackend || ebitengl // +build !darwin ebitencbackend ebitengl -package graphicscommand +package ui import ( "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" diff --git a/internal/ui/image.go b/internal/ui/image.go index e5cf6f471..f808dd442 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -16,6 +16,7 @@ package ui import ( "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/graphicsdriver" "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 { - 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) { - 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 { - return i.mipmap.DumpScreenshot(name, blackbg) + return i.mipmap.DumpScreenshot(graphicsDriver(), name, blackbg) } func (i *Image) SetIndependent(independent bool) { @@ -78,3 +79,7 @@ func (i *Image) SetIndependent(independent bool) { func (i *Image) SetVolatile(volatile bool) { i.mipmap.SetVolatile(volatile) } + +func DumpImages(dir string) error { + return atlas.DumpImages(graphicsDriver(), dir) +} diff --git a/internal/ui/shader.go b/internal/ui/shader.go index 2ca87bd37..25a7a23d3 100644 --- a/internal/ui/shader.go +++ b/internal/ui/shader.go @@ -22,7 +22,6 @@ import ( "strings" "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/shader" "github.com/hajimehoshi/ebiten/v2/internal/shaderir" @@ -117,7 +116,7 @@ func NewShader(src []byte) (*Shader, error) { var buf bytes.Buffer buf.Write(src) buf.WriteString(shaderSuffix) - if graphicscommand.NeedsInvertY() { + if graphicsDriver().FramebufferYDirection() != graphicsDriver().NDCYDirection() { buf.WriteString(` func __vertex(position vec2, texCoord vec2, color vec4) (vec4, vec2, vec4) { return mat4( diff --git a/internal/ui/ui_glfw.go b/internal/ui/ui_glfw.go index 806e799ce..06156bdee 100644 --- a/internal/ui/ui_glfw.go +++ b/internal/ui/ui_glfw.go @@ -28,7 +28,6 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/devicescale" "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/thread" ) @@ -668,7 +667,7 @@ func (u *UserInterface) createWindow(width, height int) error { // Ensure to consume this callback. u.waitForFramebufferSizeCallback(u.window, nil) - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { u.window.MakeContextCurrent() } @@ -722,7 +721,7 @@ func (u *UserInterface) registerWindowSetSizeCallback() { u.err = err } - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { u.swapBuffers() } }) @@ -816,7 +815,7 @@ event: } func (u *UserInterface) init() error { - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { glfw.WindowHint(glfw.ClientAPI, glfw.OpenGLAPI) glfw.WindowHint(glfw.ContextVersionMajor, 2) glfw.WindowHint(glfw.ContextVersionMinor, 1) @@ -837,7 +836,7 @@ func (u *UserInterface) init() error { transparent = glfw.True } 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). // 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() - graphicscommand.SetWindow(u.nativeWindow()) + if g, ok := graphicsDriver().(interface{ SetWindow(uintptr) }); ok { + g.SetWindow(u.nativeWindow()) + } return nil } @@ -1066,7 +1067,7 @@ func (u *UserInterface) loop() error { // swapBuffers also checks IsGL, so this condition is redundant. // However, (*thread).Call is not good for performance due to channels. // Let's avoid this whenever possible (#1367). - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { u.t.Call(u.swapBuffers) } @@ -1088,7 +1089,7 @@ func (u *UserInterface) loop() error { // swapBuffers must be called from the main thread. func (u *UserInterface) swapBuffers() { - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { u.window.SwapBuffers() } } @@ -1145,7 +1146,7 @@ func (u *UserInterface) adjustWindowSizeBasedOnSizeLimitsInDIP(width, height int func (u *UserInterface) setWindowSizeInDIP(width, height int, fullscreen bool) { width, height = u.adjustWindowSizeBasedOnSizeLimitsInDIP(width, height) - graphicscommand.SetFullscreen(fullscreen) + graphicsDriver().SetFullscreen(fullscreen) scale := u.deviceScaleFactor(u.currentMonitor()) 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). // TODO: This might not work when vsync is disabled. - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { glfw.PollEvents() u.swapBuffers() } @@ -1266,7 +1267,7 @@ func (u *UserInterface) setWindowSizeInDIPImpl(width, height int, fullscreen boo // updateVsync must be called on the main thread. func (u *UserInterface) updateVsync() { - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { // SwapInterval is affected by the current monitor of the window. // This needs to be called at least after SetMonitor. // Without SwapInterval after SetMonitor, vsynch doesn't work (#375). @@ -1280,7 +1281,7 @@ func (u *UserInterface) updateVsync() { glfw.SwapInterval(0) } } - graphicscommand.SetVsyncEnabled(u.fpsMode == FPSModeVsyncOn) + graphicsDriver().SetVsyncEnabled(u.fpsMode == FPSModeVsyncOn) } // currentMonitor returns the current active monitor. diff --git a/internal/ui/ui_glfw_darwin.go b/internal/ui/ui_glfw_darwin.go index 06a91fbe2..fb9411b8e 100644 --- a/internal/ui/ui_glfw_darwin.go +++ b/internal/ui/ui_glfw_darwin.go @@ -227,7 +227,6 @@ import "C" import ( "github.com/hajimehoshi/ebiten/v2/internal/glfw" - "github.com/hajimehoshi/ebiten/v2/internal/graphicscommand" ) // clearVideoModeScaleCache must be called from the main thread. @@ -317,7 +316,7 @@ func (u *UserInterface) setNativeFullscreen(fullscreen bool) { } func (u *UserInterface) adjustViewSize() { - if graphicscommand.IsGL() { + if graphicsDriver().IsGL() { return } C.adjustViewSize(C.uintptr_t(u.window.GetCocoaWindow())) diff --git a/internal/ui/ui_mobile.go b/internal/ui/ui_mobile.go index 1e7c21bf8..27467fd9f 100644 --- a/internal/ui/ui_mobile.go +++ b/internal/ui/ui_mobile.go @@ -36,6 +36,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/internal/devicescale" "github.com/hajimehoshi/ebiten/v2/internal/gamepad" "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/restorable" "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 // gl.Context so that they are called on the appropriate thread. ctx := <-glContextCh - graphicscommand.SetGomobileGLContext(ctx) + graphicsDriver().(*opengl.Graphics).SetGomobileGLContext(ctx) } else { u.t = thread.NewOSThread() graphicscommand.SetRenderingThread(u.t)