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"
"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
}

View File

@ -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 (

View File

@ -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<<uint(min(i.isolatedCount, 31))) {
if err := i.putOnAtlas(); err != nil {
if err := i.putOnAtlas(graphicsDriver); err != nil {
return err
}
i.usedAsSourceCount = 0
@ -304,7 +304,7 @@ func (i *Image) ensureIsolated() {
i.isolatedCount++
}
func (i *Image) putOnAtlas() error {
func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
if i.backend == nil {
i.allocate(true)
return nil
@ -327,7 +327,7 @@ func (i *Image) putOnAtlas() error {
pixels := make([]byte, 4*i.width*i.height)
for y := 0; y < i.height; y++ {
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 {
return err
}
@ -598,7 +598,7 @@ func (i *Image) replacePixels(pix []byte) {
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()
defer backendsM.Unlock()
@ -609,7 +609,7 @@ func (img *Image) Pixels(x, y, width, height int) ([]byte, error) {
idx := 0
for j := y; j < y+height; j++ {
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 {
return nil, err
}
@ -623,7 +623,7 @@ func (img *Image) Pixels(x, y, width, height int) ([]byte, error) {
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 {
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 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.
@ -790,11 +790,11 @@ func (i *Image) allocate(putOnAtlas bool) {
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()
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 {
@ -807,20 +807,20 @@ func NewScreenFramebufferImage(width, height int) *Image {
return i
}
func EndFrame() error {
func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
backendsM.Lock()
theTemporaryPixels.resetAtFrameEnd()
return restorable.ResolveStaleImages()
return restorable.ResolveStaleImages(graphicsDriver)
}
func BeginFrame() error {
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
defer backendsM.Unlock()
var err error
initOnce.Do(func() {
err = restorable.InitializeGraphicsDriverState()
err = restorable.InitializeGraphicsDriverState(graphicsDriver)
if err != nil {
return
}
@ -828,22 +828,22 @@ func BeginFrame() error {
panic("atlas: all the images must be not on an atlas before the game starts")
}
minSize = 1024
maxSize = restorable.MaxImageSize()
maxSize = restorable.MaxImageSize(graphicsDriver)
})
if err != nil {
return err
}
resolveDeferred()
if err := putImagesOnAtlas(); err != nil {
if err := putImagesOnAtlas(graphicsDriver); err != nil {
return err
}
return restorable.RestoreIfNeeded()
return restorable.RestoreIfNeeded(graphicsDriver)
}
func DumpImages(dir string) error {
func DumpImages(graphicsDriver graphicsdriver.Graphics, dir string) error {
backendsM.Lock()
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/graphicsdriver"
t "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
const (
@ -109,7 +110,7 @@ func TestEnsureIsolated(t *testing.T) {
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 {
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.
// Then, img1 requires longer time to recover to be on a textur atlas again.
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)
}
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)
}
}
if err := atlas.PutImagesOnAtlasForTesting(); err != nil {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
pix, err := img1.Pixels(0, 0, size, size)
pix, err := img1.Pixels(ui.GraphicsDriverForTesting(), 0, 0, size, size)
if err != nil {
t.Fatal(err)
}
@ -225,7 +226,7 @@ func TestReputOnAtlas(t *testing.T) {
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 {
t.Fatal(err)
}
@ -252,7 +253,7 @@ func TestReputOnAtlas(t *testing.T) {
// Use img1 as a render source, but call ReplacePixels.
// Now use 4x count as img1 became an isolated image again.
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)
}
img1.ReplacePixels(make([]byte, 4*size*size))
@ -261,7 +262,7 @@ func TestReputOnAtlas(t *testing.T) {
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)
}
@ -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.
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)
}
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
img1.ReplacePixels(p1)
pix0, err := img0.Pixels(0, 0, w0, h0)
pix0, err := img0.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w0, h0)
if err != nil {
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 {
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.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h)
pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w, h)
if err != nil {
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)
pix, err := dst.Pixels(0, 0, w, h)
pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w, h)
if err != nil {
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)
pix, err := dst.Pixels(0, 0, dstW, dstH)
pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, dstW, dstH)
if err != nil {
t.Fatal(err)
}
@ -568,7 +569,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
// Use src as a render source.
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)
}
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()
// Confirm that PutImagesOnAtlasForTesting doesn't panic.
if err := atlas.PutImagesOnAtlasForTesting(); err != nil {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
}
@ -619,7 +620,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
// Update the count without using src2 as a rendering source.
// This should not affect whether src2 is on an atlas or not.
for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
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.
for i := 0; i < atlas.BaseCountToPutOnAtlas; i++ {
if err := atlas.PutImagesOnAtlasForTesting(); err != nil {
if err := atlas.PutImagesOnAtlasForTesting(ui.GraphicsDriverForTesting()); err != nil {
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)
@ -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)
}
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/atlas"
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
etesting "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
func TestShaderFillTwice(t *testing.T) {
@ -39,17 +39,19 @@ func TestShaderFillTwice(t *testing.T) {
Width: w,
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)
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)
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)
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 {
t.Error(err)
}
@ -81,7 +83,7 @@ func TestImageDrawTwice(t *testing.T) {
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)
pix, err := dst.Pixels(0, 0, w, h)
pix, err := dst.Pixels(ui.GraphicsDriverForTesting(), 0, 0, w, h)
if err != nil {
t.Error(err)
}

View File

@ -34,15 +34,15 @@ type Image struct {
needsToResolvePixels bool
}
func BeginFrame() error {
if err := atlas.BeginFrame(); err != nil {
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
if err := atlas.BeginFrame(graphicsDriver); err != nil {
return err
}
return flushDelayedCommands()
}
func EndFrame() error {
return atlas.EndFrame()
func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
return atlas.EndFrame(graphicsDriver)
}
func NewImage(width, height int) *Image {
@ -138,7 +138,7 @@ func (i *Image) 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")
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)
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 {
return nil, err
}
@ -161,12 +161,12 @@ func (img *Image) Pixels(x, y, width, height int) (pix []byte, err error) {
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")
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 {
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))
copy(copied, pix)
if tryAddDelayedCommand(func() error {
i.ReplacePixels(copied, x, y, width, height)
i.ReplacePixels(graphicsDriver, copied, x, y, width, height)
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?
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 {
return err
}

View File

@ -345,8 +345,8 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics) error {
}
// FlushCommands flushes the command queue.
func FlushCommands() error {
return theCommandQueue.Flush(graphicsDriver())
func FlushCommands(graphicsDriver graphicsdriver.Graphics) error {
return theCommandQueue.Flush(graphicsDriver)
}
// 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.
func InitializeGraphicsDriverState() (err error) {
func InitializeGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) (err error) {
runOnRenderingThread(func() {
err = graphicsDriver().Initialize()
err = graphicsDriver.Initialize()
})
return
}
// ResetGraphicsDriverState resets the current graphics driver state.
// If the graphics driver doesn't have an API to reset, ResetGraphicsDriverState does nothing.
func ResetGraphicsDriverState() (err error) {
if r, ok := graphicsDriver().(interface{ Reset() error }); ok {
func ResetGraphicsDriverState(graphicsDriver graphicsdriver.Graphics) (err error) {
if r, ok := graphicsDriver.(interface{ Reset() error }); ok {
runOnRenderingThread(func() {
err = r.Reset()
})
@ -711,10 +711,10 @@ func ResetGraphicsDriverState() (err error) {
}
// MaxImageSize returns the maximum size of an image.
func MaxImageSize() int {
func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int {
var size int
runOnRenderingThread(func() {
size = graphicsDriver().MaxImageSize()
size = graphicsDriver.MaxImageSize()
})
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 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()
c := &pixelsCommand{
img: i,
result: buf,
}
theCommandQueue.Enqueue(c)
if err := theCommandQueue.Flush(graphicsDriver()); err != nil {
if err := theCommandQueue.Flush(graphicsDriver); err != nil {
return err
}
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.
//
// 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.
if i.screen {
return nil
@ -224,7 +224,7 @@ func (i *Image) Dump(path string, blackbg bool, rect image.Rectangle) error {
defer f.Close()
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
}

View File

@ -23,6 +23,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/graphicscommand"
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
etesting "github.com/hajimehoshi/ebiten/v2/internal/testing"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
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)
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)
}
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)
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)
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)
if err := dst.ReadPixels(pix); err != nil {
if err := dst.ReadPixels(g, pix); err != nil {
t.Fatal(err)
}
for j := 0; j < h; j++ {

View File

@ -67,20 +67,20 @@ func (m *Mipmap) SetVolatile(volatile bool) {
m.orig.SetVolatile(volatile)
}
func (m *Mipmap) DumpScreenshot(name string, blackbg bool) error {
return m.orig.DumpScreenshot(name, blackbg)
func (m *Mipmap) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) error {
return m.orig.DumpScreenshot(graphicsDriver, name, blackbg)
}
func (m *Mipmap) ReplacePixels(pix []byte, x, y, width, height int) error {
if err := m.orig.ReplacePixels(pix, x, y, width, height); err != nil {
func (m *Mipmap) ReplacePixels(graphicsDriver graphicsdriver.Graphics, pix []byte, x, y, width, height int) error {
if err := m.orig.ReplacePixels(graphicsDriver, pix, x, y, width, height); err != nil {
return err
}
m.disposeMipmaps()
return nil
}
func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height)
func (m *Mipmap) Pixels(graphicsDriver graphicsdriver.Graphics, x, y, width, height int) ([]byte, error) {
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) {

View File

@ -447,12 +447,12 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
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 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() {

View File

@ -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.

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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)
}

View File

@ -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(

View File

@ -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.

View File

@ -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()))

View File

@ -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)