internal/graphicscommand: introduce commandQueuePool and commandQueueManager

This removes the restriction of the number of command queues.

Updates #1704
This commit is contained in:
Hajime Hoshi 2023-10-08 16:55:29 +09:00
parent d2dd62b6db
commit 810b62f83e
4 changed files with 98 additions and 32 deletions

View File

@ -71,5 +71,5 @@ func (t *temporaryBytes) reset() {
// //
// Be careful that the returned pixels might not be zero-cleared. // Be careful that the returned pixels might not be zero-cleared.
func AllocBytes(size int) []byte { func AllocBytes(size int) []byte {
return currentCommandQueue().temporaryBytes.alloc(size) return theCommandQueueManager.allocBytes(size)
} }

View File

@ -19,6 +19,7 @@ import (
"image" "image"
"math" "math"
"strings" "strings"
"sync"
"sync/atomic" "sync/atomic"
"github.com/hajimehoshi/ebiten/v2/internal/debug" "github.com/hajimehoshi/ebiten/v2/internal/debug"
@ -81,24 +82,6 @@ type commandQueue struct {
err atomic.Value err atomic.Value
} }
// theCommandQueues is the set of command queues for the current process.
var (
theCommandQueues = [...]*commandQueue{
{},
{},
}
commandQueueIndex int
)
func currentCommandQueue() *commandQueue {
return theCommandQueues[commandQueueIndex]
}
func switchCommandQueue() {
commandQueueIndex++
commandQueueIndex = commandQueueIndex % len(theCommandQueues)
}
func (q *commandQueue) appendIndices(indices []uint16, offset uint16) { func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
n := len(q.indices) n := len(q.indices)
q.indices = append(q.indices, indices...) q.indices = append(q.indices, indices...)
@ -217,6 +200,8 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo
if endFrame && swapBuffersForGL != nil { if endFrame && swapBuffersForGL != nil {
swapBuffersForGL() swapBuffersForGL()
} }
theCommandQueueManager.putCommandQueue(q)
}, sync) }, sync)
if sync && flushErr != nil { if sync && flushErr != nil {
@ -312,9 +297,7 @@ func (q *commandQueue) flush(graphicsDriver graphicsdriver.Graphics, endFrame bo
// If endFrame is true, the current screen might be used to present. // If endFrame is true, the current screen might be used to present.
func FlushCommands(graphicsDriver graphicsdriver.Graphics, endFrame bool, swapBuffersForGL func()) error { func FlushCommands(graphicsDriver graphicsdriver.Graphics, endFrame bool, swapBuffersForGL func()) error {
flushImageBuffers() flushImageBuffers()
q := currentCommandQueue() if err := theCommandQueueManager.flush(graphicsDriver, endFrame, swapBuffersForGL); err != nil {
switchCommandQueue()
if err := q.Flush(graphicsDriver, endFrame, swapBuffersForGL); err != nil {
return err return err
} }
return nil return nil
@ -651,6 +634,89 @@ func MaxImageSize(graphicsDriver graphicsdriver.Graphics) int {
return size return size
} }
type commandQueuePool struct {
cache []*commandQueue
m sync.Mutex
}
func (c *commandQueuePool) get() (*commandQueue, error) {
c.m.Lock()
defer c.m.Unlock()
if len(c.cache) == 0 {
return &commandQueue{}, nil
}
for _, q := range c.cache {
if err := q.err.Load(); err != nil {
return nil, err.(error)
}
}
q := c.cache[len(c.cache)-1]
c.cache[len(c.cache)-1] = nil
c.cache = c.cache[:len(c.cache)-1]
return q, nil
}
func (c *commandQueuePool) put(queue *commandQueue) {
c.m.Lock()
defer c.m.Unlock()
c.cache = append(c.cache, queue)
}
type commandQueueManager struct {
pool commandQueuePool
current *commandQueue
}
var theCommandQueueManager commandQueueManager
func (c *commandQueueManager) allocBytes(size int) []byte {
if c.current == nil {
c.current, _ = c.pool.get()
}
return c.current.temporaryBytes.alloc(size)
}
func (c *commandQueueManager) enqueueCommand(command command) {
if c.current == nil {
c.current, _ = c.pool.get()
}
c.current.Enqueue(command)
}
// put can be called from any goroutines.
func (c *commandQueueManager) putCommandQueue(commandQueue *commandQueue) {
c.pool.put(commandQueue)
}
func (c *commandQueueManager) enqueueDrawTrianglesCommand(dst *Image, 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) {
if c.current == nil {
c.current, _ = c.pool.get()
}
c.current.EnqueueDrawTrianglesCommand(dst, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, evenOdd)
}
func (c *commandQueueManager) flush(graphicsDriver graphicsdriver.Graphics, endFrame bool, swapBuffersForGL func()) error {
// Switch the command queue.
prev := c.current
q, err := c.pool.get()
if err != nil {
return err
}
c.current = q
if prev == nil {
return nil
}
if err := prev.Flush(graphicsDriver, endFrame, swapBuffersForGL); err != nil {
return err
}
return nil
}
func max(a, b int) int { func max(a, b int) int {
if a < b { if a < b {
return b return b

View File

@ -88,7 +88,7 @@ func NewImage(width, height int, screenFramebuffer bool) *Image {
height: height, height: height,
screen: screenFramebuffer, screen: screenFramebuffer,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
return i return i
} }
@ -100,7 +100,7 @@ func (i *Image) flushBufferedWritePixels() {
dst: i, dst: i,
args: i.bufferedWritePixelsArgs, args: i.bufferedWritePixelsArgs,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
i.bufferedWritePixelsArgs = nil i.bufferedWritePixelsArgs = nil
} }
@ -110,7 +110,7 @@ func (i *Image) Dispose() {
c := &disposeImageCommand{ c := &disposeImageCommand{
target: i, target: i,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
} }
func (i *Image) InternalSize() (int, int) { func (i *Image) InternalSize() (int, int) {
@ -159,7 +159,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageCount]*Image, vertices [
} }
i.flushBufferedWritePixels() i.flushBufferedWritePixels()
currentCommandQueue().EnqueueDrawTrianglesCommand(i, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, evenOdd) theCommandQueueManager.enqueueDrawTrianglesCommand(i, srcs, vertices, indices, blend, dstRegion, srcRegions, shader, uniforms, evenOdd)
} }
// ReadPixels reads the image's pixels. // ReadPixels reads the image's pixels.
@ -170,8 +170,8 @@ func (i *Image) ReadPixels(graphicsDriver graphicsdriver.Graphics, args []graphi
img: i, img: i,
args: args, args: args,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
if err := currentCommandQueue().Flush(graphicsDriver, false, nil); err != nil { if err := theCommandQueueManager.flush(graphicsDriver, false, nil); err != nil {
return err return err
} }
return nil return nil
@ -200,8 +200,8 @@ func (i *Image) IsInvalidated(graphicsDriver graphicsdriver.Graphics) (bool, err
c := &isInvalidatedCommand{ c := &isInvalidatedCommand{
image: i, image: i,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
if err := currentCommandQueue().Flush(graphicsDriver, false, nil); err != nil { if err := theCommandQueueManager.flush(graphicsDriver, false, nil); err != nil {
return false, err return false, err
} }
return c.result, nil return c.result, nil

View File

@ -32,7 +32,7 @@ func NewShader(ir *shaderir.Program) *Shader {
result: s, result: s,
ir: ir, ir: ir,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
return s return s
} }
@ -40,7 +40,7 @@ func (s *Shader) Dispose() {
c := &disposeShaderCommand{ c := &disposeShaderCommand{
target: s, target: s,
} }
currentCommandQueue().Enqueue(c) theCommandQueueManager.enqueueCommand(c)
} }
func (s *Shader) unit() shaderir.Unit { func (s *Shader) unit() shaderir.Unit {