mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-26 18:52:44 +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 is a mutex for critical sections of the backend and packing.Node objects.
|
||||||
backendsM sync.Mutex
|
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
|
initOnce sync.Once
|
||||||
|
|
||||||
// theBackends is a set of atlases.
|
// theBackends is a set of atlases.
|
||||||
@ -135,18 +139,6 @@ var (
|
|||||||
deferredM sync.Mutex
|
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
|
type ImageType int
|
||||||
|
|
||||||
const (
|
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) {
|
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()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
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)
|
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) {
|
func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
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)
|
i.writePixels(pix, region)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,6 +543,10 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, pixels []byte
|
|||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
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).
|
// 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.
|
// To prevent memory leaks, flush the deferred functions here.
|
||||||
flushDeferred()
|
flushDeferred()
|
||||||
@ -715,12 +741,19 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri
|
|||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
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))
|
return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(0, 0, i.width, i.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error {
|
func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error {
|
||||||
// Flushing draw commands starts. Stop queuing new graphics commands until the next frame.
|
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
|
defer backendsM.Unlock()
|
||||||
|
defer func() {
|
||||||
|
inFrame = true
|
||||||
|
}()
|
||||||
|
|
||||||
if err := restorable.EndFrame(graphicsDriver, swapBuffersForGL); err != nil {
|
if err := restorable.EndFrame(graphicsDriver, swapBuffersForGL); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -745,7 +778,9 @@ func floorPowerOf2(x int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
||||||
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
defer backendsM.Unlock()
|
||||||
|
inFrame = true
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
initOnce.Do(func() {
|
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 {
|
func BeginFrame(graphicsDriver graphicsdriver.Graphics) error {
|
||||||
if err := atlas.BeginFrame(graphicsDriver); err != nil {
|
return atlas.BeginFrame(graphicsDriver)
|
||||||
return err
|
|
||||||
}
|
|
||||||
flushDelayedCommands()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func EndFrame(graphicsDriver graphicsdriver.Graphics, swapBuffersForGL func()) error {
|
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 {
|
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() returns false, the pixel data is cached in the restorable package.
|
||||||
if !restorable.AlwaysReadPixelsFromGPU() {
|
if !restorable.AlwaysReadPixelsFromGPU() {
|
||||||
if err := i.img.ReadPixels(graphicsDriver, pixels, region); err != nil {
|
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) {
|
func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) (string, error) {
|
||||||
checkDelayedCommandsFlushed("Dump")
|
|
||||||
return i.img.DumpScreenshot(graphicsDriver, name, blackbg)
|
return i.img.DumpScreenshot(graphicsDriver, name, blackbg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,19 +94,6 @@ func (i *Image) WritePixels(pix []byte, region image.Rectangle) {
|
|||||||
}
|
}
|
||||||
i.invalidatePixels()
|
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)
|
i.img.WritePixels(pix, region)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,23 +108,6 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
|
|||||||
}
|
}
|
||||||
i.invalidatePixels()
|
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
|
var imgs [graphics.ShaderImageCount]*atlas.Image
|
||||||
for i, img := range srcs {
|
for i, img := range srcs {
|
||||||
if img == nil {
|
if img == nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user