graphics: Optimize commands: call glBufferSubData as less times as possible

This commit is contained in:
Hajime Hoshi 2016-06-03 12:41:18 +09:00
parent 208b27d5de
commit 3e912ce7f8
5 changed files with 50 additions and 49 deletions

View File

@ -15,6 +15,8 @@
package graphics package graphics
import ( import (
"errors"
"fmt"
"image/color" "image/color"
"math" "math"
@ -27,6 +29,7 @@ type command interface {
type commandQueue struct { type commandQueue struct {
commands []command commands []command
indexOffsetInBytes int
} }
var theCommandQueue = &commandQueue{ var theCommandQueue = &commandQueue{
@ -38,7 +41,18 @@ func (q *commandQueue) Enqueue(command command) {
} }
func (q *commandQueue) Flush(context *opengl.Context) error { func (q *commandQueue) Flush(context *opengl.Context) error {
// TODO: Do optimizing before executing q.indexOffsetInBytes = 0
vertices := []int16{}
for _, c := range q.commands {
switch c := c.(type) {
case *drawImageCommand:
vertices = append(vertices, c.vertices...)
}
}
// TODO: Check if len(vertices) is too big
if 0 < len(vertices) {
context.BufferSubData(context.ArrayBuffer, vertices)
}
for _, c := range q.commands { for _, c := range q.commands {
if err := c.Exec(context); err != nil { if err := c.Exec(context); err != nil {
return err return err
@ -79,8 +93,34 @@ func (c *drawImageCommand) Exec(context *opengl.Context) error {
if err := c.dst.setAsViewport(context); err != nil { if err := c.dst.setAsViewport(context); err != nil {
return err return err
} }
p := c.dst.projectionMatrix() context.BlendFunc(c.mode)
return drawTexture(context, c.src.native, p, c.vertices, c.geo, c.color, c.mode)
// 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.
n := len(c.vertices) / 16
if n == 0 {
return nil
}
if MaxQuads < n/16 {
return errors.New(fmt.Sprintf("len(quads) must be equal to or less than %d", MaxQuads))
}
p := programContext{
state: &theOpenGLState,
program: theOpenGLState.programTexture,
context: context,
projectionMatrix: glMatrix(c.dst.projectionMatrix()),
texture: c.src.native,
geoM: c.geo,
colorM: c.color,
}
p.begin()
defer p.end()
// TODO: We should call glBindBuffer here?
// The buffer is already bound at begin() but it is counterintuitive.
context.DrawElements(context.Triangles, 6*n, theCommandQueue.indexOffsetInBytes)
theCommandQueue.indexOffsetInBytes += 6 * n * 2
return nil
} }
type replacePixelsCommand struct { type replacePixelsCommand struct {

View File

@ -14,13 +14,6 @@
package graphics package graphics
import (
"errors"
"fmt"
"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)
func glMatrix(m *[4][4]float64) []float32 { func glMatrix(m *[4][4]float64) []float32 {
return []float32{ return []float32{
float32(m[0][0]), float32(m[1][0]), float32(m[2][0]), float32(m[3][0]), float32(m[0][0]), float32(m[1][0]), float32(m[2][0]), float32(m[3][0]),
@ -33,35 +26,3 @@ func glMatrix(m *[4][4]float64) []float32 {
type Matrix interface { type Matrix interface {
Element(i, j int) float64 Element(i, j int) float64
} }
func drawTexture(c *opengl.Context, texture opengl.Texture, projectionMatrix *[4][4]float64, vertices []int16, geo Matrix, color Matrix, mode opengl.CompositeMode) error {
c.BlendFunc(mode)
// 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.
n := len(vertices) / 16
if n == 0 {
return nil
}
if MaxQuads < n/16 {
return errors.New(fmt.Sprintf("len(quads) must be equal to or less than %d", MaxQuads))
}
p := programContext{
state: &theOpenGLState,
program: theOpenGLState.programTexture,
context: c,
projectionMatrix: glMatrix(projectionMatrix),
texture: texture,
geoM: geo,
colorM: color,
}
p.begin()
defer p.end()
// TODO: We should call glBindBuffer here?
// The buffer is already bound at begin() but it is counterintuitive.
c.BufferSubData(c.ArrayBuffer, vertices)
c.DrawElements(c.Triangles, 6*n)
return nil
}

View File

@ -438,9 +438,9 @@ func (c *Context) DeleteBuffer(b Buffer) {
}) })
} }
func (c *Context) DrawElements(mode Mode, len int) { func (c *Context) DrawElements(mode Mode, len int, offsetInBytes int) {
c.RunOnContextThread(func() error { c.RunOnContextThread(func() error {
gl.DrawElements(uint32(mode), int32(len), gl.UNSIGNED_SHORT, gl.PtrOffset(0)) gl.DrawElements(uint32(mode), int32(len), gl.UNSIGNED_SHORT, gl.PtrOffset(offsetInBytes))
return nil return nil
}) })
} }

View File

@ -356,7 +356,7 @@ func (c *Context) DeleteBuffer(b Buffer) {
gl.DeleteBuffer(b.Object) gl.DeleteBuffer(b.Object)
} }
func (c *Context) DrawElements(mode Mode, len int) { func (c *Context) DrawElements(mode Mode, len int, offsetInBytes int) {
gl := c.gl gl := c.gl
gl.DrawElements(int(mode), len, gl.UNSIGNED_SHORT, 0) gl.DrawElements(int(mode), len, gl.UNSIGNED_SHORT, offsetInBytes)
} }

View File

@ -359,7 +359,7 @@ func (c *Context) DeleteBuffer(b Buffer) {
gl.DeleteBuffer(mgl.Buffer(b)) gl.DeleteBuffer(mgl.Buffer(b))
} }
func (c *Context) DrawElements(mode Mode, len int) { func (c *Context) DrawElements(mode Mode, len int, offsetInBytes int) {
gl := c.gl gl := c.gl
gl.DrawElements(mgl.Enum(mode), len, mgl.UNSIGNED_SHORT, 0) gl.DrawElements(mgl.Enum(mode), len, mgl.UNSIGNED_SHORT, offsetInBytes)
} }