mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-12 20:18:59 +01:00
parent
da979a3ab2
commit
7d517bfb63
@ -120,6 +120,10 @@ var (
|
||||
// backendsM is a mutex for critical sections of the backend and packing.Node objects.
|
||||
backendsM sync.Mutex
|
||||
|
||||
// inFrame indicates whether the current state is in between BeginFrame and EndFrame or not.
|
||||
// If inFrame is false, function calls on an image should be deferred until the next BeginFrame.
|
||||
inFrame bool
|
||||
|
||||
initOnce sync.Once
|
||||
|
||||
// theBackends is a set of atlases.
|
||||
@ -135,18 +139,6 @@ var (
|
||||
deferredM sync.Mutex
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Lock the mutex before a frame begins.
|
||||
//
|
||||
// In each frame, restoring images and resolving images happen respectively:
|
||||
//
|
||||
// [Restore -> Resolve] -> [Restore -> Resolve] -> ...
|
||||
//
|
||||
// Between each frame, any image operations are not permitted, or stale images would remain when restoring
|
||||
// (#913).
|
||||
backendsM.Lock()
|
||||
}
|
||||
|
||||
type ImageType int
|
||||
|
||||
const (
|
||||
@ -341,6 +333,23 @@ func (i *Image) regionWithPadding() image.Rectangle {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *Shader, uniforms []uint32, evenOdd bool) {
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
|
||||
if !inFrame {
|
||||
vs := make([]float32, len(vertices))
|
||||
copy(vs, vertices)
|
||||
is := make([]uint16, len(indices))
|
||||
copy(is, indices)
|
||||
us := make([]uint32, len(uniforms))
|
||||
copy(us, uniforms)
|
||||
|
||||
deferredM.Lock()
|
||||
deferred = append(deferred, func() {
|
||||
i.drawTriangles(srcs, vs, is, blend, dstRegion, srcRegions, shader, us, evenOdd, false)
|
||||
})
|
||||
deferredM.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
i.drawTriangles(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, evenOdd, false)
|
||||
}
|
||||
|
||||
@ -449,6 +458,19 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
|
||||
if !inFrame {
|
||||
copied := make([]byte, len(pix))
|
||||
copy(copied, pix)
|
||||
|
||||
deferredM.Lock()
|
||||
deferred = append(deferred, func() {
|
||||
i.writePixels(copied, region)
|
||||
})
|
||||
deferredM.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
i.writePixels(pix, region)
|
||||
}
|
||||
|
||||
@ -521,6 +543,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
|
||||
if !inFrame {
|
||||
panic("atlas: ReadPixels must be called in between BeginFrame and EndFrame")
|
||||
}
|
||||
|
||||
// In the tests, BeginFrame might not be called often and then images might not be disposed (#2292).
|
||||
// To prevent memory leaks, flush the deferred functions here.
|
||||
flushDeferred()
|
||||
@ -715,12 +741,19 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
|
||||
if !inFrame {
|
||||
panic("atlas: ReadPixels must be called in between BeginFrame and EndFrame")
|
||||
}
|
||||
|
||||
return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height))
|
||||
}
|
||||
|
||||
func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error {
|
||||
// Flushing draw commands starts. Stop queuing new graphics commands until the next frame.
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
defer func() {
|
||||
inFrame = true
|
||||
}()
|
||||
|
||||
if err := restorable.EndFrame(graphicsDriver, swapBuffersForGL); err != nil {
|
||||
return err
|
||||
@ -745,7 +778,9 @@ func floorPowerOf2(x int) int {
|
||||
}
|
||||
|
||||
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
inFrame = true
|
||||
|
||||
var err error
|
||||
initOnce.Do(func() {
|
||||
|
@ -1,75 +0,0 @@
|
||||
// Copyright 2019 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 buffered
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
// delayedCommands represents a queue for image operations that are ordered before the game starts
|
||||
// (BeginFrame). Before the game starts, the package shareable doesn't determine the minimum/maximum texture
|
||||
// sizes (#879).
|
||||
delayedCommands = []func(){}
|
||||
|
||||
delayedCommandsM sync.Mutex
|
||||
delayedCommandsFlushed uint32
|
||||
)
|
||||
|
||||
func flushDelayedCommands() {
|
||||
if atomic.LoadUint32(&delayedCommandsFlushed) == 0 {
|
||||
// Outline the slow-path to expect the fast-path is inlined.
|
||||
flushDelayedCommandsSlow()
|
||||
}
|
||||
}
|
||||
|
||||
func flushDelayedCommandsSlow() {
|
||||
delayedCommandsM.Lock()
|
||||
defer delayedCommandsM.Unlock()
|
||||
|
||||
if delayedCommandsFlushed == 0 {
|
||||
for _, f := range delayedCommands {
|
||||
f()
|
||||
}
|
||||
delayedCommands = nil
|
||||
delayedCommandsFlushed = 1
|
||||
}
|
||||
}
|
||||
|
||||
// maybeCanAddDelayedCommand returns false if the delayed commands cannot be added.
|
||||
// Otherwise, maybeCanAddDelayedCommand's returning value is not determined.
|
||||
// For example, maybeCanAddDelayedCommand can return true even when flushing is being processed.
|
||||
func maybeCanAddDelayedCommand() bool {
|
||||
return atomic.LoadUint32(&delayedCommandsFlushed) == 0
|
||||
}
|
||||
|
||||
func tryAddDelayedCommand(f func()) bool {
|
||||
delayedCommandsM.Lock()
|
||||
defer delayedCommandsM.Unlock()
|
||||
|
||||
if delayedCommandsFlushed == 0 {
|
||||
delayedCommands = append(delayedCommands, f)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func checkDelayedCommandsFlushed(fname string) {
|
||||
if atomic.LoadUint32(&delayedCommandsFlushed) == 0 {
|
||||
panic("buffered: the command queue is not available yet at " + fname)
|
||||
}
|
||||
}
|
@ -34,11 +34,7 @@ type Image struct {
|
||||
}
|
||||
|
||||
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
||||
if err := atlas.BeginFrame(graphicsDriver); err != nil {
|
||||
return err
|
||||
}
|
||||
flushDelayedCommands()
|
||||
return nil
|
||||
return atlas.BeginFrame(graphicsDriver)
|
||||
}
|
||||
|
||||
func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error {
|
||||
@ -62,8 +58,6 @@ func (i *Image) MarkDisposed() {
|
||||
}
|
||||
|
||||
func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte, region image.Rectangle) error {
|
||||
checkDelayedCommandsFlushed("ReadPixels")
|
||||
|
||||
// If restorable.AlwaysReadPixelsFromGPU() returns false, the pixel data is cached in the restorable package.
|
||||
if !restorable.AlwaysReadPixelsFromGPU() {
|
||||
if err := i.img.ReadPixels(graphicsDriver, pixels, region); err != nil {
|
||||
@ -90,7 +84,6 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
|
||||
}
|
||||
|
||||
func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) (string, error) {
|
||||
checkDelayedCommandsFlushed("Dump")
|
||||
return i.img.DumpScreenshot(graphicsDriver, name, blackbg)
|
||||
}
|
||||
|
||||
@ -101,19 +94,6 @@ func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
|
||||
}
|
||||
i.invalidatePixels()
|
||||
|
||||
if maybeCanAddDelayedCommand() {
|
||||
copied := make([]byte, len(pix))
|
||||
copy(copied, pix)
|
||||
if tryAddDelayedCommand(func() {
|
||||
i.writePixelsImpl(copied, region)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
i.writePixelsImpl(pix, region)
|
||||
}
|
||||
|
||||
func (i *Image) writePixelsImpl(pix []byte, region image.Rectangle) {
|
||||
i.img.WritePixels(pix, region)
|
||||
}
|
||||
|
||||
@ -128,23 +108,6 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
||||
}
|
||||
i.invalidatePixels()
|
||||
|
||||
if maybeCanAddDelayedCommand() {
|
||||
vs := make([]float32, len(vertices))
|
||||
copy(vs, vertices)
|
||||
is := make([]uint16, len(indices))
|
||||
copy(is, indices)
|
||||
us := make([]uint32, len(uniforms))
|
||||
copy(us, uniforms)
|
||||
if tryAddDelayedCommand(func() {
|
||||
i.drawTrianglesImpl(srcs, vs, is, blend, dstRegion, srcRegions, shader, us, evenOdd)
|
||||
}) {
|
||||
return
|
||||
}
|
||||
}
|
||||
i.drawTrianglesImpl(srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, evenOdd)
|
||||
}
|
||||
|
||||
func (i *Image) drawTrianglesImpl(srcs [graphics.ShaderImageCount]*Image, vertices []float32, indices []uint16, blend graphicsdriver.Blend, dstRegion image.Rectangle, srcRegions [graphics.ShaderImageCount]image.Rectangle, shader *atlas.Shader, uniforms []uint32, evenOdd bool) {
|
||||
var imgs [graphics.ShaderImageCount]*atlas.Image
|
||||
for i, img := range srcs {
|
||||
if img == nil {
|
||||
|
Loading…
Reference in New Issue
Block a user