diff --git a/examples/sprites/main.go b/examples/sprites/main.go index b4661d74a..e7b5472ad 100644 --- a/examples/sprites/main.go +++ b/examples/sprites/main.go @@ -98,20 +98,20 @@ func (s Sprites) Src(i int) (x0, y0, x1, y1 int) { const ( MinSprites = 0 - MaxSprites = 10000 + MaxSprites = 50000 ) var sprites = &Sprites{make([]*Sprite, MaxSprites), 500} func update(screen *ebiten.Image) error { if ebiten.IsKeyPressed(ebiten.KeyLeft) { - sprites.num -= 20 + sprites.num -= 100 if sprites.num < MinSprites { sprites.num = MinSprites } } if ebiten.IsKeyPressed(ebiten.KeyRight) { - sprites.num += 20 + sprites.num += 100 if MaxSprites < sprites.num { sprites.num = MaxSprites } diff --git a/internal/graphics/command.go b/internal/graphics/command.go index 527fbce19..a5c603014 100644 --- a/internal/graphics/command.go +++ b/internal/graphics/command.go @@ -58,41 +58,72 @@ func (q *commandQueue) Enqueue(command command) { q.commands = append(q.commands, command) } +func (q *commandQueue) commandGroups() [][]command { + cs := make([]command, len(q.commands)) + copy(cs, q.commands) + gs := [][]command{} + quads := 0 + for 0 < len(cs) { + if len(gs) == 0 { + gs = append(gs, []command{}) + } + c := cs[0] + switch c := c.(type) { + case *drawImageCommand: + if maxQuads >= quads+c.quadsNum() { + quads += c.quadsNum() + break + } + cc := c.split(maxQuads - quads) + gs[len(gs)-1] = append(gs[len(gs)-1], cc[0]) + cs[0] = cc[1] + quads = 0 + gs = append(gs, []command{}) + continue + } + gs[len(gs)-1] = append(gs[len(gs)-1], c) + cs = cs[1:] + } + return gs +} + func (q *commandQueue) Flush(context *opengl.Context) error { q.m.Lock() defer q.m.Unlock() // glViewport must be called at least at every frame on iOS. context.ResetViewportSize() - vertices := []int16{} - for _, c := range q.commands { - switch c := c.(type) { - case *drawImageCommand: - vertices = append(vertices, c.vertices...) + for _, g := range q.commandGroups() { + vertices := []int16{} + for _, c := range g { + switch c := c.(type) { + case *drawImageCommand: + vertices = append(vertices, c.vertices...) + } } - } - if 0 < len(vertices) { - context.BufferSubData(opengl.ArrayBuffer, vertices) - } - // NOTE: WebGL doesn't seem to have Check gl.MAX_ELEMENTS_VERTICES or gl.MAX_ELEMENTS_INDICES so far. - // Let's use them to compare to len(quads) in the future. - if maxQuads < len(vertices)/16 { - return errors.New(fmt.Sprintf("len(quads) must be equal to or less than %d", maxQuads)) - } - numc := len(q.commands) - indexOffsetInBytes := 0 - for _, c := range q.commands { - if err := c.Exec(context, indexOffsetInBytes); err != nil { - return err + if 0 < len(vertices) { + context.BufferSubData(opengl.ArrayBuffer, vertices) } - if c, ok := c.(*drawImageCommand); ok { - indexOffsetInBytes += 6 * len(c.vertices) / 16 * 2 + // NOTE: WebGL doesn't seem to have Check gl.MAX_ELEMENTS_VERTICES or gl.MAX_ELEMENTS_INDICES so far. + // Let's use them to compare to len(quads) in the future. + if maxQuads < len(vertices)/16 { + return errors.New(fmt.Sprintf("len(quads) must be equal to or less than %d", maxQuads)) + } + numc := len(g) + indexOffsetInBytes := 0 + for _, c := range g { + if err := c.Exec(context, indexOffsetInBytes); err != nil { + return err + } + if c, ok := c.(*drawImageCommand); ok { + indexOffsetInBytes += 6 * len(c.vertices) / 16 * 2 + } + } + if 0 < numc { + // Call glFlush to prevent black flicking (especially on Android (#226) and iOS). + context.Flush() } } q.commands = []command{} - if 0 < numc { - // Call glFlush to prevent black flicking (especially on Android (#226) and iOS). - context.Flush() - } return nil } @@ -133,7 +164,7 @@ func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) } context.BlendFunc(c.mode) - n := len(c.vertices) / 16 + n := c.quadsNum() if n == 0 { return nil } @@ -156,6 +187,18 @@ func (c *drawImageCommand) Exec(context *opengl.Context, indexOffsetInBytes int) return nil } +func (c *drawImageCommand) split(quadsNum int) [2]*drawImageCommand { + c1 := *c + c2 := *c + c1.vertices = c.vertices[:quadsNum*16] + c2.vertices = c.vertices[quadsNum*16:] + return [2]*drawImageCommand{&c1, &c2} +} + +func (c *drawImageCommand) quadsNum() int { + return len(c.vertices) / 16 +} + type replacePixelsCommand struct { dst *Image pixels []uint8 diff --git a/pixels.go b/pixels.go index 2059f30b5..b294ebca5 100644 --- a/pixels.go +++ b/pixels.go @@ -61,7 +61,6 @@ func (p *pixels) fill(clr color.Color) { func (p *pixels) appendDrawImageHistory(item *drawImageHistoryItem) { p.drawImageHistory = append(p.drawImageHistory, item) - // TODO: Consider the number of the vertices, which should not exceed the max number (#245). } func (p *pixels) at(image *graphics.Image, idx int, context *opengl.Context) (color.Color, error) {