Compare commits

...

12 Commits

Author SHA1 Message Date
Hajime Hoshi
af9bd6a282 all: specify src-regions correctly
This parameter was not important and actually not used with usual
shaders. However, now this information will be important for the
restorable package optimization later.

There was a performance issue that was caused by srcRegions, but
now this should not happen thanks to FilterUniformVariables.

Updates #1293
2024-09-08 22:24:15 +09:00
Hajime Hoshi
73565034a9 internal/restorable: refactoring 2024-09-08 19:29:41 +09:00
Hajime Hoshi
bbe3cba110 internal/restorable: remove draw-triangles history items at WritePixels 2024-09-08 18:20:40 +09:00
Hajime Hoshi
f32648b144 internal/restorable: skip adding stale regions when the image doesn't need to be restored 2024-09-08 17:51:09 +09:00
Hajime Hoshi
aa8e112414 internal/restorable: restore a stale image at clearing pixels 2024-09-08 17:48:23 +09:00
Hajime Hoshi
26f0479f16 internal/restorable: typo 2024-09-08 17:06:25 +09:00
Hajime Hoshi
9ef9ea0469 internal/restorable: refactoring 2024-09-08 17:03:18 +09:00
Hajime Hoshi
b36160d9e7 internal/restorable: bug fix: make all the images stale correctly 2024-09-08 16:11:57 +09:00
Hajime Hoshi
20f8df7fc1 internal/restorable: refactoring: remove lastTarget
This didn't improve performance very much. Rather, this made the code
a little hard to read and could have a potential issue.
2024-09-08 16:08:36 +09:00
Hajime Hoshi
167c3435f7 internal/restorable: refactoring 2024-09-08 16:03:56 +09:00
Hajime Hoshi
d84b030300 ebiten: give HintOverwriteDstRegion when BlendClear is used 2024-09-08 14:57:20 +09:00
Hajime Hoshi
4824dc0360 internal/restorable: resolve a stale state when possible 2024-09-08 14:28:16 +09:00
12 changed files with 241 additions and 124 deletions

View File

@ -267,7 +267,32 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
})
}
i.image.DrawTriangles(srcs, vs, is, blend, i.adjustedBounds(), [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false, restorable.HintNone)
dr := i.adjustedBounds()
hint := restorable.HintNone
if overwritesDstRegion(options.Blend, dr, geoM, sx0, sy0, sx1, sy1) {
hint = restorable.HintOverwriteDstRegion
}
i.image.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{img.adjustedBounds()}, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, canSkipMipmap(geoM, filter), false, hint)
}
// overwritesDstRegion reports whether the given parameters overwrite the destination region completely.
func overwritesDstRegion(blend Blend, dstRegion image.Rectangle, geoM GeoM, sx0, sy0, sx1, sy1 int) bool {
// TODO: More precisely, BlendFactorDestinationRGB, BlendFactorDestinationAlpha, and operations should be checked.
if blend != BlendCopy && blend != BlendClear {
return false
}
// Check the result vertices is not a rotated rectangle.
if geoM.b != 0 || geoM.c != 0 {
return false
}
// Check the result vertices completely covers dstRegion.
x0, y0 := geoM.Apply(float64(sx0), float64(sy0))
x1, y1 := geoM.Apply(float64(sx1), float64(sy1))
if float64(dstRegion.Min.X) < x0 || float64(dstRegion.Min.Y) < y0 || float64(dstRegion.Max.X) > x1 || float64(dstRegion.Max.Y) > y1 {
return false
}
return true
}
// Vertex represents a vertex passed to DrawTriangles.
@ -856,7 +881,14 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
i.tmpUniforms = i.tmpUniforms[:0]
i.tmpUniforms = shader.appendUniforms(i.tmpUniforms, options.Uniforms)
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false, restorable.HintNone)
dr := i.adjustedBounds()
hint := restorable.HintNone
// Do not use srcRegions[0].Dx() and srcRegions[0].Dy() as these might be empty.
if overwritesDstRegion(options.Blend, dr, geoM, srcRegions[0].Min.X, srcRegions[0].Min.Y, srcRegions[0].Min.X+width, srcRegions[0].Min.Y+height) {
hint = restorable.HintOverwriteDstRegion
}
i.image.DrawTriangles(imgs, vs, is, blend, i.adjustedBounds(), srcRegions, shader.shader, i.tmpUniforms, graphicsdriver.FillRuleFillAll, true, false, hint)
}
// SubImage returns an image representing the portion of the image p visible through r.

View File

@ -284,8 +284,9 @@ func (i *Image) ensureIsolatedFromSource(backends []*backend) {
graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, i.width, i.height)
sr := image.Rect(0, 0, i.width, i.height)
newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
newI.moveTo(i)
}
@ -315,7 +316,8 @@ func (i *Image) putOnSourceBackend() {
graphics.QuadVerticesFromDstAndSrc(vs, 0, 0, w, h, 0, 0, w, h, 1, 1, 1, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, i.width, i.height)
newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
sr := image.Rect(0, 0, i.width, i.height)
newI.drawTriangles([graphics.ShaderSrcImageCount]*Image{i}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
newI.moveTo(i)
i.usedAsSourceCount = 0
@ -433,6 +435,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice
// A source region can be deliberately empty when this is not needed in order to avoid unexpected
// performance issue (#1293).
// TODO: This should no longer be needed but is kept just in case. Remove this later.
if srcRegions[i].Empty() {
continue
}

View File

@ -103,7 +103,8 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, size, size)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, size/2, size/2)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img4.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -111,7 +112,8 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
// img5 is not allocated now, but is allocated at DrawTriangles.
vs = quadVertices(0, 0, size/2, size/2, 1)
dr = image.Rect(0, 0, size/2, size/2)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr = image.Rect(0, 0, size/2, size/2)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img5}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -145,7 +147,9 @@ func TestEnsureIsolatedFromSourceBackend(t *testing.T) {
// Check further drawing doesn't cause panic.
// This bug was fixed by 03dcd948.
vs = quadVertices(0, 0, size/2, size/2, 1)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dr = image.Rect(0, 0, size, size)
sr = image.Rect(0, 0, size/2, size/2)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
func TestReputOnSourceBackend(t *testing.T) {
@ -186,10 +190,11 @@ func TestReputOnSourceBackend(t *testing.T) {
// Use img1 as a render target.
is := graphics.QuadIndices()
dr := image.Rect(0, 0, size, size)
sr := image.Rect(0, 0, size, size)
// Render onto img1. The count should not matter.
for i := 0; i < 5; i++ {
vs := quadVertices(size, size, 0, 0, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -201,7 +206,7 @@ func TestReputOnSourceBackend(t *testing.T) {
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ {
atlas.PutImagesOnSourceBackendForTesting()
vs := quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -209,7 +214,7 @@ func TestReputOnSourceBackend(t *testing.T) {
// Finally, img1 is on a source backend.
atlas.PutImagesOnSourceBackendForTesting()
vs := quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -238,7 +243,7 @@ func TestReputOnSourceBackend(t *testing.T) {
}
vs = quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -268,7 +273,7 @@ func TestReputOnSourceBackend(t *testing.T) {
// Use img1 as a render target again. The count should not matter.
for i := 0; i < 5; i++ {
vs := quadVertices(size, size, 0, 0, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -280,7 +285,7 @@ func TestReputOnSourceBackend(t *testing.T) {
atlas.PutImagesOnSourceBackendForTesting()
img1.WritePixels(make([]byte, 4*size*size), image.Rect(0, 0, size, size))
vs := quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -289,7 +294,7 @@ func TestReputOnSourceBackend(t *testing.T) {
// img1 is not on an atlas due to WritePixels.
vs = quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img1.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -298,7 +303,7 @@ func TestReputOnSourceBackend(t *testing.T) {
for i := 0; i < atlas.BaseCountToPutOnSourceBackend*2; i++ {
atlas.PutImagesOnSourceBackendForTesting()
vs := quadVertices(size, size, 0, 0, 1)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img3}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := img3.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -401,7 +406,8 @@ func TestWritePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.WritePixels(pix, image.Rect(0, 0, w, h))
pix = make([]byte, 4*w*h)
@ -448,7 +454,8 @@ func TestSmallImages(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
pix = make([]byte, 4*w*h)
ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h))
@ -495,7 +502,8 @@ func TestLongImages(t *testing.T) {
vs := quadVertices(w, h, 0, 0, scale)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, dstW, dstH)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
pix = make([]byte, 4*dstW*dstH)
ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, dstW, dstH))
@ -611,7 +619,8 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) {
vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, size, size)
src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, size, size)
src.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := src.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -620,7 +629,7 @@ func TestDeallocatedAndReputOnSourceBackend(t *testing.T) {
for i := 0; i < atlas.BaseCountToPutOnSourceBackend/2; i++ {
atlas.PutImagesOnSourceBackendForTesting()
vs := quadVertices(size, size, 0, 0, 1)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := src.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -648,13 +657,14 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) {
vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, size, size)
sr := image.Rect(0, 0, size, size)
// Use src2 as a rendering target, and make src2 on a destination backend.
//
// Call DrawTriangles multiple times.
// The number of DrawTriangles doesn't matter as long as these are called in one frame.
for i := 0; i < 2; i++ {
src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
src2.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
@ -673,7 +683,7 @@ func TestImageIsNotReputOnSourceBackendWithoutUsingAsSource(t *testing.T) {
for i := 0; i < atlas.BaseCountToPutOnSourceBackend; i++ {
atlas.PutImagesOnSourceBackendForTesting()
vs := quadVertices(size, size, 0, 0, 1)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src2}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if got, want := src2.IsOnSourceBackendForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -796,17 +806,18 @@ func TestDestinationCountOverflow(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
sr := image.Rect(0, 0, w, h)
// Use dst0 as a destination for a while.
for i := 0; i < 31; i++ {
dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
atlas.PutImagesOnSourceBackendForTesting()
}
// Use dst0 as a source for a while.
// As dst0 is used as a destination too many times (31 is a maximum), dst0's backend should never be a source backend.
for i := 0; i < 100; i++ {
dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst1.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{dst0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
atlas.PutImagesOnSourceBackendForTesting()
if dst0.IsOnSourceBackendForTesting() {
t.Errorf("dst0 cannot be on a source backend: %d", i)
@ -831,8 +842,9 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
sr := image.Rect(0, 0, w, h)
for _, img := range srcs {
img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
atlas.PutImagesOnSourceBackendForTesting()
@ -840,7 +852,7 @@ func TestIteratingImagesToPutOnSourceBackend(t *testing.T) {
// Check iterating the registered image works correctly.
for i := 0; i < 100; i++ {
for _, src := range srcs {
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
atlas.PutImagesOnSourceBackendForTesting()
}
@ -888,7 +900,8 @@ func TestDallocateUnmanagedImageBackends(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{img1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// Get the difference of the number of backends before and after the images are deallocated.
c := atlas.BackendCountForTesting()

View File

@ -70,11 +70,12 @@ func TestImageDrawTwice(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// Vertices must be recreated (#1755)
vs = quadVertices(w, h, 0, 0, 1)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*atlas.Image{src1}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
pix := make([]byte, 4*w*h)
ok, err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h))

View File

@ -310,8 +310,9 @@ func (i *Image) syncPixelsIfNeeded() {
srcs := [graphics.ShaderSrcImageCount]*atlas.Image{whiteImage.img}
dr := image.Rect(0, 0, i.width, i.height)
sr := image.Rect(0, 0, whiteImage.width, whiteImage.height)
blend := graphicsdriver.BlendCopy
i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
i.img.DrawTriangles(srcs, vs, is, blend, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, atlas.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// TODO: Use clear if Go 1.21 is available.
for pos := range i.dotsBuffer {

View File

@ -56,7 +56,8 @@ func TestClear(t *testing.T) {
vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
sr := image.Rect(0, 0, w/2, h/2)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), []graphicsdriver.PixelsArgs{
@ -87,8 +88,10 @@ func TestWritePixelsPartAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
sr0 := image.Rect(0, 0, w, h)
sr1 := image.Rect(0, 0, w/2, h/2)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr0}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr1}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
bs := graphics.NewManagedBytes(4, func(bs []byte) {
for i := range bs {
bs[i] = 0
@ -106,7 +109,8 @@ func TestShader(t *testing.T) {
vs := quadVertices(w, h)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*graphicscommand.Image{clr}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, nearestFilterShader, nil, graphicsdriver.FillRuleFillAll)
g := ui.Get().GraphicsDriverForTesting()
s := graphicscommand.NewShader(etesting.ShaderProgramFill(0xff, 0, 0, 0xff), "")

View File

@ -185,7 +185,10 @@ func (m *Mipmap) level(level int) *buffered.Image {
s := buffered.NewImage(w2, h2, m.imageType)
dstRegion := image.Rect(0, 0, w2, h2)
s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
w := sizeForLevel(m.width, level-1)
h := sizeForLevel(m.height, level-1)
srcRegion := image.Rect(0, 0, w, h)
s.DrawTriangles([graphics.ShaderSrcImageCount]*buffered.Image{src}, vs, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{srcRegion}, atlas.LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
m.setImg(level, s)
return m.imgs[level]

View File

@ -80,7 +80,7 @@ func (p *Pixels) Dispose() {
// drawTrianglesHistoryItem is an item for history of draw-image commands.
type drawTrianglesHistoryItem struct {
images [graphics.ShaderSrcImageCount]*Image
srcImages [graphics.ShaderSrcImageCount]*Image
vertices []float32
indices []uint32
blend graphicsdriver.Blend
@ -233,6 +233,10 @@ func (i *Image) makeStale(rect image.Rectangle) {
return
}
if !i.needsRestoration() {
return
}
origSize := len(i.staleRegions)
i.staleRegions = i.appendRegionsForDrawTriangles(i.staleRegions)
if !rect.Empty() {
@ -283,8 +287,7 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl
clearImage(i.image, region)
}
// Even if the image is already stale, call makeStale to extend the stale region.
if !needsRestoration() || !i.needsRestoration() || i.stale {
if !needsRestoration() || !i.needsRestoration() {
i.makeStale(region)
return
}
@ -292,9 +295,9 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl
if region.Eq(image.Rect(0, 0, w, h)) {
if pixels != nil {
// Clone a ManagedBytes as the package graphicscommand has a different lifetime management.
i.basePixels.AddOrReplace(pixels.Clone(), image.Rect(0, 0, w, h))
i.basePixels.AddOrReplace(pixels.Clone(), region)
} else {
i.basePixels.Clear(image.Rect(0, 0, w, h))
i.basePixels.Clear(region)
}
i.clearDrawTrianglesHistory()
i.stale = false
@ -302,6 +305,14 @@ func (i *Image) WritePixels(pixels *graphics.ManagedBytes, region image.Rectangl
return
}
if i.stale {
// Even if the image is already stale, call makeStale to extend the stale region.
i.makeStale(region)
return
}
i.removeDrawTrianglesHistoryItems(region)
// Records for DrawTriangles cannot come before records for WritePixels.
if len(i.drawTrianglesHistory) > 0 {
i.makeStale(region)
@ -347,10 +358,25 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice
}
// Even if the image is already stale, call makeStale to extend the stale region.
if srcstale || !needsRestoration() || !i.needsRestoration() || i.stale {
if srcstale || !needsRestoration() || !i.needsRestoration() {
i.makeStale(dstRegion)
} else if i.stale {
var overwrite bool
if hint == HintOverwriteDstRegion {
overwrite = i.areStaleRegionsIncludedIn(dstRegion)
}
if overwrite {
i.basePixels.Clear(dstRegion)
i.clearDrawTrianglesHistory()
i.stale = false
i.staleRegions = i.staleRegions[:0]
} else {
// TODO: Consider overwritten.
// Even if the image is already stale, call makeStale to extend the stale region.
i.makeStale(dstRegion)
}
}
if !i.stale {
i.appendDrawTrianglesHistory(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, fillRule, hint)
}
@ -364,19 +390,23 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderSrcImageCount]*Image, vertice
i.image.DrawTriangles(imgs, vertices, indices, blend, dstRegion, srcRegions, shader.shader, uniforms, fillRule)
}
// appendDrawTrianglesHistory appends a draw-image history item to the image.
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint Hint) {
if i.stale || !i.needsRestoration() {
panic("restorable: an image must not be stale or need restoration at appendDrawTrianglesHistory")
func (i *Image) areStaleRegionsIncludedIn(r image.Rectangle) bool {
if !i.stale {
return false
}
if AlwaysReadPixelsFromGPU() {
panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true")
for _, sr := range i.staleRegions {
if !sr.In(r) {
return false
}
}
return true
}
// If the command overwrites the destination region, remove the history items that are in the region.
if hint == HintOverwriteDstRegion {
// removeDrawTrianglesHistoryItems removes draw-image history items whose destination regions are in the given region.
// This is useful when the destination region is overwritten soon later.
func (i *Image) removeDrawTrianglesHistoryItems(region image.Rectangle) {
for idx, c := range i.drawTrianglesHistory {
if c.dstRegion.In(dstRegion) {
if c.dstRegion.In(region) {
i.drawTrianglesHistory[idx] = nil
}
}
@ -391,6 +421,20 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*I
i.drawTrianglesHistory = i.drawTrianglesHistory[:n]
}
// appendDrawTrianglesHistory appends a draw-image history item to the image.
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*Image, vertices []float32, indices []uint32, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderSrcImageCount]image.Rectangle, shader *Shader, uniforms []uint32, fillRule graphicsdriver.FillRule, hint Hint) {
if i.stale || !i.needsRestoration() {
panic("restorable: an image must not be stale or need restoration at appendDrawTrianglesHistory")
}
if AlwaysReadPixelsFromGPU() {
panic("restorable: appendDrawTrianglesHistory must not be called when AlwaysReadPixelsFromGPU() returns true")
}
// If the command overwrites the destination region, remove the history items that are in the region.
if hint == HintOverwriteDstRegion {
i.removeDrawTrianglesHistoryItems(dstRegion)
}
// TODO: Would it be possible to merge draw image history items?
const maxDrawTrianglesHistoryCount = 1024
if len(i.drawTrianglesHistory)+1 > maxDrawTrianglesHistoryCount {
@ -410,7 +454,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderSrcImageCount]*I
copy(us, uniforms)
item := &drawTrianglesHistoryItem{
images: srcs,
srcImages: srcs,
vertices: vs,
indices: is,
blend: blend,
@ -433,7 +477,7 @@ func (i *Image) readPixelsFromGPUIfNeeded(graphicsDriver graphicsdriver.Graphics
}
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error {
if AlwaysReadPixelsFromGPU() {
if AlwaysReadPixelsFromGPU() || !i.needsRestoration() {
if err := i.image.ReadPixels(graphicsDriver, []graphicsdriver.PixelsArgs{
{
Pixels: pixels,
@ -455,12 +499,12 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
return nil
}
// makeStaleIfDependingOn makes the image stale if the image depends on target.
func (i *Image) makeStaleIfDependingOn(target *Image) {
// makeStaleIfDependingOn makes the image stale if the image depends on src.
func (i *Image) makeStaleIfDependingOn(src *Image) {
if i.stale {
return
}
if i.dependsOn(target) {
if i.dependsOn(src) {
// There is no new region to make stale.
i.makeStale(image.Rectangle{})
}
@ -546,18 +590,16 @@ func (i *Image) resolveStale(graphicsDriver graphicsdriver.Graphics) error {
return i.readPixelsFromGPU(graphicsDriver)
}
// dependsOn reports whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
// dependsOn reports whether the image depends on src.
func (i *Image) dependsOn(src *Image) bool {
for _, c := range i.drawTrianglesHistory {
for _, img := range c.images {
if img == nil {
for _, img := range c.srcImages {
if img != src {
continue
}
if img == target {
return true
}
}
}
return false
}
@ -575,7 +617,7 @@ func (i *Image) dependsOnShader(shader *Shader) bool {
func (i *Image) dependingImages() map[*Image]struct{} {
r := map[*Image]struct{}{}
for _, c := range i.drawTrianglesHistory {
for _, img := range c.images {
for _, img := range c.srcImages {
if img == nil {
continue
}
@ -629,7 +671,7 @@ func (i *Image) restore(graphicsDriver graphicsdriver.Graphics) error {
for _, c := range i.drawTrianglesHistory {
var imgs [graphics.ShaderSrcImageCount]*graphicscommand.Image
for i, img := range c.images {
for i, img := range c.srcImages {
if img == nil {
continue
}

View File

@ -62,7 +62,6 @@ func AlwaysReadPixelsFromGPU() bool {
type images struct {
images map[*Image]struct{}
shaders map[*Shader]struct{}
lastTarget *Image
contextLost atomic.Bool
}
@ -91,7 +90,7 @@ func resolveStaleImages(graphicsDriver graphicsdriver.Graphics, endFrame bool) e
if disabled.Load() {
disabledOnce.Do(func() {
for img := range theImages.images {
img.makeStale(image.Rectangle{})
img.makeStale(image.Rect(0, 0, img.width, img.height))
}
})
}
@ -157,7 +156,6 @@ func (i *images) removeShader(shader *Shader) {
// resolveStaleImages resolves stale images.
func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) error {
i.lastTarget = nil
for img := range i.images {
if err := img.resolveStale(graphicsDriver); err != nil {
return err
@ -166,20 +164,16 @@ func (i *images) resolveStaleImages(graphicsDriver graphicsdriver.Graphics) erro
return nil
}
// makeStaleIfDependingOn makes all the images stale that depend on target.
// makeStaleIfDependingOn makes all the images stale that depend on src.
//
// When target is modified, all images depending on target can't be restored with target.
// When src is modified, all images depending on src can't be restored with src.
// makeStaleIfDependingOn is called in such situation.
func (i *images) makeStaleIfDependingOn(target *Image) {
if target == nil {
panic("restorable: target must not be nil at makeStaleIfDependingOn")
func (i *images) makeStaleIfDependingOn(src *Image) {
if src == nil {
panic("restorable: src must not be nil at makeStaleIfDependingOn")
}
if i.lastTarget == target {
return
}
i.lastTarget = target
for img := range i.images {
img.makeStaleIfDependingOn(target)
img.makeStaleIfDependingOn(src)
}
}
@ -242,10 +236,8 @@ func (i *images) restore(graphicsDriver graphicsdriver.Graphics) error {
current[i] = struct{}{}
}
for e := range edges {
if _, ok := current[e.target]; ok {
delete(current, e.target)
}
}
for i := range current {
delete(images, i)
sorted = append(sorted, i)

View File

@ -140,7 +140,8 @@ func TestRestoreChain(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, 1, 1)
imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, 1, 1)
imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
@ -183,10 +184,11 @@ func TestRestoreChain2(t *testing.T) {
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
imgs[8].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[7]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
imgs[9].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[8]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
for i := 0; i < 7; i++ {
imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
@ -227,10 +229,11 @@ func TestRestoreOverrideSource(t *testing.T) {
img1.WritePixels(bytesToManagedBytes([]byte{clr0.R, clr0.G, clr0.B, clr0.A}), image.Rect(0, 0, w, h))
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
img2.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.WritePixels(bytesToManagedBytes([]byte{clr1.R, clr1.G, clr1.B, clr1.A}), image.Rect(0, 0, w, h))
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 0, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
@ -309,24 +312,25 @@ func TestRestoreComplexGraph(t *testing.T) {
}()
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
sr := image.Rect(0, 0, w, h)
vs := quadVertices(w, h, 0, 0)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 1, 0)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img3.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 1, 0)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 2, 0)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img4.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 0, 0)
img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img5.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 0, 0)
img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 1, 0)
img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img6.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img4}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 0, 0)
img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
vs = quadVertices(w, h, 2, 0)
img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img7.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img3}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
@ -419,8 +423,9 @@ func TestRestoreRecursive(t *testing.T) {
}()
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(w, h, 1, 0), is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
t.Fatal(err)
}
@ -519,7 +524,8 @@ func TestDrawTrianglesAndWritePixels(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, 2, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, 1, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img1.WritePixels(bytesToManagedBytes([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}), image.Rect(0, 0, 2, 1))
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
@ -557,8 +563,9 @@ func TestDispose(t *testing.T) {
is := graphics.QuadIndices()
dr := image.Rect(0, 0, 1, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, 1, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img2}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img1}, quadVertices(1, 1, 0, 0), is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img1.Dispose()
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
@ -667,7 +674,8 @@ func TestWritePixelsOnly(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, 1, 1)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
img1.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{img0}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
img0.WritePixels(bytesToManagedBytes([]byte{5, 6, 7, 8}), image.Rect(0, 0, 1, 1))
// BasePixelsForTesting is available without GPU accessing.
@ -721,7 +729,8 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
// stale.
@ -745,7 +754,8 @@ func TestAllowWritePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.WritePixels(bytesToManagedBytes(make([]byte, 4*w*h)), image.Rect(0, 0, w, h))
// WritePixels for a whole image doesn't panic.
}
@ -764,7 +774,8 @@ func TestAllowWritePixelsForPartAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
dst.WritePixels(bytesToManagedBytes(make([]byte, 4*2*2)), image.Rect(0, 0, 2, 2))
// WritePixels for a part of image doesn't panic.
@ -858,7 +869,8 @@ func TestDrawTrianglesAndExtend(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
orig.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
extended := orig.Extend(w*2, h*2) // After this, orig is already disposed.
result := make([]byte, 4*(w*2)*(h*2))
@ -908,7 +920,8 @@ func TestMutateSlices(t *testing.T) {
is := make([]uint32, len(graphics.QuadIndices()))
copy(is, graphics.QuadIndices())
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
for i := range vs {
vs[i] = 0
}
@ -1100,7 +1113,8 @@ func TestDrawTrianglesAndReadPixels(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
pix := make([]byte, 4*w*h)
if err := dst.ReadPixels(ui.Get().GraphicsDriverForTesting(), pix, image.Rect(0, 0, w, h)); err != nil {
@ -1124,7 +1138,8 @@ func TestWritePixelsAndDrawTriangles(t *testing.T) {
vs := quadVertices(1, 1, 1, 0)
is := graphics.QuadIndices()
dr := image.Rect(1, 0, 2, 1)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, 1, 1)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// Get the pixels.
pix := make([]byte, 4*2*1)
@ -1151,10 +1166,11 @@ func TestOverwriteDstRegion(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, w, h)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src0}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// This tests that HintOverwriteDstRegion removes the previous DrawTriangles.
// In practice, BlendCopy should be used instead of BlendSourceOver in this case.
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
dst.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{src1}, vs, is, graphicsdriver.BlendSourceOver, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintOverwriteDstRegion)
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
t.Fatal(err)

View File

@ -46,7 +46,8 @@ func clearImage(img *restorable.Image, w, h int) {
}
is := graphics.QuadIndices()
dr := image.Rect(0, 0, w, h)
img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, 3, 3)
img.DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{emptyImage}, vs, is, graphicsdriver.BlendClear, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, restorable.NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
func TestShader(t *testing.T) {
@ -85,7 +86,8 @@ func TestShaderChain(t *testing.T) {
s := restorable.NewShader(etesting.ShaderProgramImages(1), "")
for i := 0; i < num-1; i++ {
dr := image.Rect(0, 0, 1, 1)
imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
sr := image.Rect(0, 0, 1, 1)
imgs[i+1].DrawTriangles([graphics.ShaderSrcImageCount]*restorable.Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
}
if err := restorable.ResolveStaleImages(ui.Get().GraphicsDriverForTesting()); err != nil {
@ -117,7 +119,12 @@ func TestShaderMultipleSources(t *testing.T) {
s := restorable.NewShader(etesting.ShaderProgramImages(3), "")
dr := image.Rect(0, 0, 1, 1)
dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, [graphics.ShaderSrcImageCount]image.Rectangle{}, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
srs := [graphics.ShaderSrcImageCount]image.Rectangle{
image.Rect(0, 0, 1, 1),
image.Rect(0, 0, 1, 1),
image.Rect(0, 0, 1, 1),
}
dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), graphicsdriver.BlendCopy, dr, srs, s, nil, graphicsdriver.FillRuleFillAll, restorable.HintNone)
// Clear one of the sources after DrawTriangles. dst should not be affected.
clearImage(srcs[0], 1, 1)

View File

@ -183,8 +183,9 @@ func (i *Image) Fill(r, g, b, a float32, region image.Rectangle) {
if a == 1 && i.lastBlend == graphicsdriver.BlendSourceOver {
blend = graphicsdriver.BlendSourceOver
}
sr := image.Rect(0, 0, i.ui.whiteImage.width, i.ui.whiteImage.height)
// i.lastBlend is updated in DrawTriangles.
i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion)
i.DrawTriangles(srcs, i.tmpVerticesForFill, is, blend, region, [graphics.ShaderSrcImageCount]image.Rectangle{sr}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion)
}
type bigOffscreenImage struct {
@ -253,7 +254,8 @@ func (i *bigOffscreenImage) drawTriangles(srcs [graphics.ShaderSrcImageCount]*Im
1, 1, 1, 1)
is := graphics.QuadIndices()
dstRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale)
i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion)
srcRegion := i.region
i.image.DrawTriangles(srcs, i.tmpVerticesForCopying, is, graphicsdriver.BlendCopy, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{srcRegion}, NearestFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, restorable.HintOverwriteDstRegion)
}
for idx := 0; idx < len(vertices); idx += graphics.VertexFloatCount {
@ -297,13 +299,14 @@ func (i *bigOffscreenImage) flush() {
1, 1, 1, 1)
is := graphics.QuadIndices()
dstRegion := i.region
srcRegion := image.Rect(0, 0, i.region.Dx()*bigOffscreenScale, i.region.Dy()*bigOffscreenScale)
blend := graphicsdriver.BlendSourceOver
hint := restorable.HintNone
if i.blend != graphicsdriver.BlendSourceOver {
blend = graphicsdriver.BlendCopy
hint = restorable.HintOverwriteDstRegion
}
i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, hint)
i.orig.DrawTriangles(srcs, i.tmpVerticesForFlushing, is, blend, dstRegion, [graphics.ShaderSrcImageCount]image.Rectangle{srcRegion}, LinearFilterShader, nil, graphicsdriver.FillRuleFillAll, true, false, hint)
i.image.clear()
i.dirty = false