diff --git a/internal/atlas/image.go b/internal/atlas/image.go index ddf77298c..78af38203 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -39,7 +39,19 @@ type temporaryBytes struct { notFullyUsedTime int } -var theTemporaryBytes temporaryBytes +var ( + theTemporaryBytesSet [2]temporaryBytes + temporaryBytesIndex int +) + +func currentTemporaryBytes() *temporaryBytes { + return &theTemporaryBytesSet[temporaryBytesIndex] +} + +func switchTemporaryBytes() { + temporaryBytesIndex++ + temporaryBytesIndex %= len(theTemporaryBytesSet) +} func temporaryBytesSize(size int) int { l := 16 @@ -551,13 +563,13 @@ func (i *Image) writePixels(pix []byte, region image.Rectangle) { } // Copy pixels in the case when pix is modified before the graphics command is executed. - pix2 := theTemporaryBytes.alloc(len(pix)) + pix2 := currentTemporaryBytes().alloc(len(pix)) copy(pix2, pix) i.backend.restorable.WritePixels(pix2, region) return } - pixb := theTemporaryBytes.alloc(4 * r.Dx() * r.Dy()) + pixb := currentTemporaryBytes().alloc(4 * r.Dx() * r.Dy()) // Clear the edges. pixb might not be zero-cleared. // TODO: These loops assume that paddingSize is 1. @@ -795,7 +807,8 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) e return err } - theTemporaryBytes.resetAtFrameEnd() + currentTemporaryBytes().resetAtFrameEnd() + switchTemporaryBytes() for b := range theSourceBackendsForOneFrame { delete(theSourceBackendsForOneFrame, b) diff --git a/internal/processtest/testdata/issue2716.go b/internal/processtest/testdata/issue2716.go new file mode 100644 index 000000000..760113eac --- /dev/null +++ b/internal/processtest/testdata/issue2716.go @@ -0,0 +1,79 @@ +// Copyright 2023 The Ebitengine 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. + +//go:build ignore + +package main + +import ( + "fmt" + "image/color" + + "github.com/hajimehoshi/ebiten/v2" +) + +type Game struct { + count int + + imgs []*ebiten.Image +} + +func (g *Game) Update() error { + const ( + w = 16 + h = 16 + ) + + g.count++ + if g.count >= 16 { + for c, img := range g.imgs { + for j := 0; j < h; j++ { + for i := 0; i < h; i++ { + got := img.At(i, j).(color.RGBA) + want := color.RGBA{byte(c), byte(c), byte(c), byte(c)} + if got != want { + return fmt.Errorf("index: %d, got: %v, want: %v", c, got, want) + } + } + } + } + return ebiten.Termination + } + + pix := make([]byte, 4*w*h) + c := byte(len(g.imgs)) + for i := range pix { + pix[i] = c + } + + img := ebiten.NewImage(w, h) + img.WritePixels(pix) + + g.imgs = append(g.imgs, img) + + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { +} + +func (g *Game) Layout(width, height int) (int, int) { + return width, height +} + +func main() { + if err := ebiten.RunGame(&Game{}); err != nil { + panic(err) + } +}