hajimehoshi / ebiten

Ebitengine - A dead simple 2D game engine for Go
https://ebitengine.org
Apache License 2.0
11.02k stars 660 forks source link

ebiten: `dfdx` and `dfdy` don't work with an unmanaged image #2583

Closed hajimehoshi closed 1 year ago

hajimehoshi commented 1 year ago

Ebitengine Version

62e08e735665709c4e1dde205a2a4539e721be77

Operating System

Go Version (go version)

go version go1.20 darwin/amd64

What steps will reproduce the problem?

Apply this patch and run the tests

diff --git a/shader_test.go b/shader_test.go
index e1d63ff86..718f714b1 100644
--- a/shader_test.go
+++ b/shader_test.go
@@ -469,7 +469,11 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
                t.Fatal(err)
        }

-       dst := ebiten.NewImage(w, h)
+       // Make the destination image without a text atlas to get reliable results.
+       // A managed image can be on a texture atlas and it is not predictable where the pixels are.
+       dstOp := &ebiten.NewImageOptions{}
+       dstOp.Unmanaged = true
+       dst := ebiten.NewImageWithOptions(image.Rect(0, 0, w, h), dstOp)
        src := ebiten.NewImage(w, h)
        pix := make([]byte, 4*w*h)
        for j := 0; j < h; j++ {
@@ -527,7 +531,11 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
                t.Fatal(err)
        }

-       dst := ebiten.NewImage(w, h)
+       // Make the destination image without a text atlas to get reliable results.
+       // A managed image can be on a texture atlas and it is not predictable where the pixels are.
+       dstOp := &ebiten.NewImageOptions{}
+       dstOp.Unmanaged = true
+       dst := ebiten.NewImageWithOptions(image.Rect(0, 0, w, h), dstOp)
        src := ebiten.NewImage(w, h)
        pix := make([]byte, 4*w*h)
        for j := 0; j < h; j++ {

What is the expected result?

The tests pass

What happens instead?

The tests fail:

--- FAIL: TestShaderDerivatives (0.01s)
    shader_test.go:508: dst.At(7, 1): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 1): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 2): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 2): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 3): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 3): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 4): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 4): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 5): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 5): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 6): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 6): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(1, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(2, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(3, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(4, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(5, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(6, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(7, 7): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:508: dst.At(8, 7): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:508: dst.At(9, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(10, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(11, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(12, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(13, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(14, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(1, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(2, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(3, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(4, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(5, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(6, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(7, 8): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:508: dst.At(8, 8): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:508: dst.At(9, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(10, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(11, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(12, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(13, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(14, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:508: dst.At(7, 9): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 9): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 10): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 10): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 11): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 11): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 12): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 12): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 13): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 13): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(7, 14): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:508: dst.At(8, 14): got: {0 0 0 255}, want: {255 0 0 255}
--- FAIL: TestShaderDerivatives2 (0.00s)
    shader_test.go:570: dst.At(7, 1): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 1): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 2): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 2): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 3): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 3): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 4): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 4): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 5): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 5): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 6): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 6): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(1, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(2, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(3, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(4, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(5, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(6, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(7, 7): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:570: dst.At(8, 7): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:570: dst.At(9, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(10, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(11, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(12, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(13, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(14, 7): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(1, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(2, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(3, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(4, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(5, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(6, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(7, 8): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:570: dst.At(8, 8): got: {0 0 0 255}, want: {255 255 0 255}
    shader_test.go:570: dst.At(9, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(10, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(11, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(12, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(13, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(14, 8): got: {0 0 0 255}, want: {0 255 0 255}
    shader_test.go:570: dst.At(7, 9): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 9): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 10): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 10): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 11): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 11): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 12): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 12): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 13): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 13): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(7, 14): got: {0 0 0 255}, want: {255 0 0 255}
    shader_test.go:570: dst.At(8, 14): got: {0 0 0 255}, want: {255 0 0 255}
FAIL
2023-02-28 03:06:03.592 ebiten.test[82785:2036775] [CAMetalLayer nextDrawable] returning nil because allocation failed.
FAIL    github.com/hajimehoshi/ebiten/v2    0.506s
FAIL

Anything else you feel useful to add?

No response

hajimehoshi commented 1 year ago

Currently an unmanged image has one pixel padding in its texture atlas. Testing these functions heavily depends on the internal details.