shader: Use the fixed number of images for shaders

This changes uses arrays rather than slices in order to avoid heap
allocations.

Updates #1193
This commit is contained in:
Hajime Hoshi 2020-07-18 01:09:58 +09:00
parent 0c13fff62a
commit e0d5763a60
18 changed files with 261 additions and 231 deletions

View File

@ -78,7 +78,7 @@ func (g *Game) Update(screen *ebiten.Image) error {
g.shaders = map[int]*ebiten.Shader{}
}
if _, ok := g.shaders[g.idx]; !ok {
s, err := ebiten.NewShader([]byte(shaderSrcs[g.idx]), 1)
s, err := ebiten.NewShader([]byte(shaderSrcs[g.idx]))
if err != nil {
return err
}
@ -130,7 +130,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
[]float32{float32(cx), float32(cy)}, // Cursor
}
if g.idx != 0 {
op.Images = append(op.Images, gophersImage)
op.Images[0] = gophersImage
}
screen.DrawTrianglesWithShader(vs, is, s, op)

View File

@ -15,6 +15,7 @@
package ebiten
import (
"fmt"
"image"
"image/color"
@ -205,7 +206,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
sy1 := float32(bounds.Max.Y)
vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1, filter == driver.FilterScreen)
is := graphics.QuadIndices()
i.buffered.DrawTriangles(img.buffered, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
i.buffered.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{img.buffered}, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
return nil
}
@ -342,15 +343,22 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
}
}
i.buffered.DrawTriangles(img.buffered, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address), sr, nil, nil, nil)
i.buffered.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{img.buffered}, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address), sr, nil, nil)
}
type DrawTrianglesWithShaderOptions struct {
Uniforms []interface{}
Images []*Image
Images [4]*Image
CompositeMode CompositeMode
}
func init() {
var op DrawTrianglesWithShaderOptions
if got, want := len(op.Images), graphics.ShaderImageNum; got != want {
panic(fmt.Sprintf("ebiten: len((DrawTrianglesWithShaderOptions{}).Images) must be %d but %d", want, got))
}
}
// TODO: Add comments and tests
func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesWithShaderOptions) {
i.copyCheck()
@ -381,8 +389,11 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha
us := append([]interface{}{[]float32{0, 0}}, options.Uniforms...)
var imgw, imgh int
var imgs []*buffered.Image
for _, img := range options.Images {
var imgs [graphics.ShaderImageNum]*buffered.Image
for i, img := range options.Images {
if img == nil {
continue
}
if img.isDisposed() {
panic("ebiten: the given image to DrawTriangles must not be disposed")
}
@ -391,7 +402,7 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha
} else if w, h := img.Size(); imgw != w || imgh != h {
panic("ebiten: all the source images must be the same size")
}
imgs = append(imgs, img.buffered)
imgs[i] = img.buffered
}
vs := make([]float32, len(vertices)*graphics.VertexFloatNum)
@ -408,7 +419,7 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha
is := make([]uint16, len(indices))
copy(is, indices)
i.buffered.DrawTriangles(nil, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, shader.shader, us, imgs)
i.buffered.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, shader.shader, us)
}
// SubImage returns an image representing the portion of the image p visible through r.

View File

@ -21,6 +21,7 @@ import (
"github.com/hajimehoshi/ebiten/internal/affine"
"github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/mipmap"
"github.com/hajimehoshi/ebiten/internal/shaderir"
)
@ -235,13 +236,8 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) {
// DrawTriangles draws the src image with the given vertices.
//
// Copying vertices and indices is the caller's responsibility.
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Image) {
if src != nil {
if i == src {
panic("buffered: Image.DrawTriangles: src must be different from the receiver")
}
}
for _, src := range images {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
for _, src := range srcs {
if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
}
@ -250,17 +246,17 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
if maybeCanAddDelayedCommand() {
if tryAddDelayedCommand(func() error {
// Arguments are not copied. Copying is the caller's responsibility.
i.DrawTriangles(src, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms, images)
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms)
return nil
}) {
return
}
}
if src != nil {
src.resolvePendingPixels(true)
for _, src := range srcs {
if src == nil {
continue
}
for _, src := range images {
src.resolvePendingPixels(true)
}
i.resolvePendingPixels(false)
@ -270,17 +266,15 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
s = shader.shader
}
var srcImg *mipmap.Mipmap
if src != nil {
srcImg = src.img
var imgs [graphics.ShaderImageNum]*mipmap.Mipmap
for i, img := range srcs {
if img == nil {
continue
}
imgs[i] = img.img
}
var imgs []*mipmap.Mipmap
for _, img := range images {
imgs = append(imgs, img.img)
}
i.img.DrawTriangles(srcImg, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms, imgs)
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
i.invalidatePendingPixels()
}

View File

@ -18,6 +18,7 @@ import (
"errors"
"github.com/hajimehoshi/ebiten/internal/affine"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/shaderir"
"github.com/hajimehoshi/ebiten/internal/thread"
)
@ -44,6 +45,7 @@ type Graphics interface {
IsGL() bool
HasHighPrecisionFloat() bool
MaxImageSize() int
InvalidImageID() ImageID
NewShader(program *shaderir.Program) (Shader, error)
@ -58,7 +60,7 @@ type Graphics interface {
//
// * float32
// * []float32
DrawShader(dst ImageID, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, uniforms []interface{}, srcs []ImageID) error
DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, uniforms []interface{}) error
}
// GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost.

View File

@ -18,6 +18,8 @@ import (
"github.com/hajimehoshi/ebiten/internal/web"
)
const ShaderImageNum = 4
const (
IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles.
VertexFloatNum = 8

View File

@ -17,6 +17,7 @@ package graphicscommand
import (
"fmt"
"math"
"strings"
"github.com/hajimehoshi/ebiten/internal/affine"
"github.com/hajimehoshi/ebiten/internal/driver"
@ -65,7 +66,7 @@ type command interface {
NumIndices() int
AddNumVertices(n int)
AddNumIndices(n int)
CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool
CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool
}
type size struct {
@ -143,7 +144,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
}
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Image) {
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
if len(indices) > graphics.IndicesNum {
panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum))
}
@ -155,28 +156,30 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []f
split = true
}
// Assume that all the image sizes are same.
var firstSrc *Image
for _, src := range srcs {
if src != nil {
q.appendVertices(vertices, src)
} else if len(images) > 0 {
q.appendVertices(vertices, images[0])
} else {
q.appendVertices(vertices, nil)
firstSrc = src
break
}
}
q.appendVertices(vertices, firstSrc)
q.appendIndices(indices, uint16(q.nextIndex))
q.nextIndex += len(vertices) / graphics.VertexFloatNum
q.tmpNumIndices += len(indices)
// TODO: If dst is the screen, reorder the command to be the last.
if !split && 0 < len(q.commands) {
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, src, color, mode, filter, address, sourceRegion, shader) {
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, sourceRegion, shader) {
last.AddNumVertices(len(vertices))
last.AddNumIndices(len(indices))
return
}
}
if address != driver.AddressUnsafe {
w, h := src.InternalSize()
if firstSrc != nil && address != driver.AddressUnsafe {
w, h := firstSrc.InternalSize()
sourceRegion.X /= float32(w)
sourceRegion.Y /= float32(h)
sourceRegion.Width /= float32(w)
@ -185,7 +188,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []f
c := &drawTrianglesCommand{
dst: dst,
src: src,
srcs: srcs,
nvertices: len(vertices),
nindices: len(indices),
color: color,
@ -195,7 +198,6 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst, src *Image, vertices []f
sourceRegion: sourceRegion,
shader: shader,
uniforms: uniforms,
images: images,
}
q.commands = append(q.commands, c)
}
@ -312,7 +314,7 @@ func FlushCommands() error {
// drawTrianglesCommand represents a drawing command to draw an image on another image.
type drawTrianglesCommand struct {
dst *Image
src *Image
srcs [graphics.ShaderImageNum]*Image
nvertices int
nindices int
color *affine.ColorM
@ -322,7 +324,6 @@ type drawTrianglesCommand struct {
sourceRegion driver.Region
shader *Shader
uniforms []interface{}
images []*Image
}
func (c *drawTrianglesCommand) String() string {
@ -391,12 +392,19 @@ func (c *drawTrianglesCommand) String() string {
panic(fmt.Sprintf("graphicscommand: invalid address: %d", c.address))
}
src := fmt.Sprintf("%d", c.src.id)
if c.src.screen {
src += " (screen)"
var srcstrs [graphics.ShaderImageNum]string
for i, src := range c.srcs {
if src == nil {
srcstrs[i] = "(nil)"
continue
}
srcstrs[i] = fmt.Sprintf("%d", src.id)
if src.screen {
srcstrs[i] += " (screen)"
}
}
return fmt.Sprintf("draw-triangles: dst: %s <- src: %s, num of indices: %d, colorm: %v, mode %s, filter: %s, address: %s", dst, src, c.nindices, c.color, mode, filter, address)
return fmt.Sprintf("draw-triangles: dst: %s <- src: [%s], num of indices: %d, colorm: %v, mode %s, filter: %s, address: %s", dst, strings.Join(srcstrs[:], ", "), c.nindices, c.color, mode, filter, address)
}
// Exec executes the drawTrianglesCommand.
@ -407,9 +415,13 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
}
if c.shader != nil {
var imgs []driver.ImageID
for _, img := range c.images {
imgs = append(imgs, img.image.ID())
var imgs [graphics.ShaderImageNum]driver.ImageID
for i, src := range c.srcs {
if src == nil {
imgs[i] = theGraphicsDriver.InvalidImageID()
continue
}
imgs[i] = src.image.ID()
}
// The last uniform variables are added at /shader.go and represents a viewport size.
@ -418,9 +430,9 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
viewport[0] = float32(w)
viewport[1] = float32(h)
return theGraphicsDriver.DrawShader(c.dst.image.ID(), c.shader.shader.ID(), c.nindices, indexOffset, c.mode, c.uniforms, imgs)
return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.shader.shader.ID(), c.nindices, indexOffset, c.mode, c.uniforms)
}
return theGraphicsDriver.Draw(c.dst.image.ID(), c.src.image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.sourceRegion)
return theGraphicsDriver.Draw(c.dst.image.ID(), c.srcs[0].image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.sourceRegion)
}
func (c *drawTrianglesCommand) NumVertices() int {
@ -441,7 +453,7 @@ func (c *drawTrianglesCommand) AddNumIndices(n int) {
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
// with the drawTrianglesCommand c.
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
// If a shader is used, commands are not merged.
//
// TODO: Merge shader commands considering uniform variables.
@ -451,7 +463,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image,
if c.dst != dst {
return false
}
if c.src != src {
if c.srcs != srcs {
return false
}
if !c.color.Equals(color) {
@ -502,7 +514,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) {
func (c *replacePixelsCommand) AddNumIndices(n int) {
}
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}
@ -539,7 +551,7 @@ func (c *pixelsCommand) AddNumVertices(n int) {
func (c *pixelsCommand) AddNumIndices(n int) {
}
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}
@ -572,7 +584,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) {
func (c *disposeImageCommand) AddNumIndices(n int) {
}
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}
@ -605,7 +617,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) {
func (c *disposeShaderCommand) AddNumIndices(n int) {
}
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}
@ -644,7 +656,7 @@ func (c *newImageCommand) AddNumVertices(n int) {
func (c *newImageCommand) AddNumIndices(n int) {
}
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}
@ -680,7 +692,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) {
func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) {
}
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}
@ -715,7 +727,7 @@ func (c *newShaderCommand) AddNumVertices(n int) {
func (c *newShaderCommand) AddNumIndices(n int) {
}
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst, src *Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
return false
}

View File

@ -126,13 +126,6 @@ func (i *Image) InternalSize() (int, int) {
return i.internalWidth, i.internalHeight
}
func processSrc(src *Image) {
if src.screen {
panic("graphicscommand: the screen image cannot be the rendering source")
}
src.resolveBufferedReplacePixels()
}
// DrawTriangles draws triangles with the given image.
//
// The vertex floats are:
@ -154,22 +147,25 @@ func processSrc(src *Image) {
//
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
// elements for the source image are not used.
func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Image) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
if i.lastCommand == lastCommandNone {
if !i.screen && mode != driver.CompositeModeClear {
panic("graphicscommand: the image must be cleared first")
}
}
if src != nil {
processSrc(src)
for _, src := range srcs {
if src == nil {
continue
}
for _, src := range images {
processSrc(src)
if src.screen {
panic("graphicscommand: the screen image cannot be the rendering source")
}
src.resolveBufferedReplacePixels()
}
i.resolveBufferedReplacePixels()
theCommandQueue.EnqueueDrawTrianglesCommand(i, src, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms, images)
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms)
if i.lastCommand == lastCommandNone && !i.screen {
i.lastCommand = lastCommandClear

View File

@ -44,7 +44,7 @@ func TestClear(t *testing.T) {
vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
pix, err := dst.Pixels()
if err != nil {
@ -74,8 +74,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
dst := NewImage(w, h)
vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices()
dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
}
@ -89,14 +89,14 @@ func TestShader(t *testing.T) {
dst := NewImage(w, h)
vs := quadVertices(w, h)
is := graphics.QuadIndices()
dst.DrawTriangles(clr, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir)
us := []interface{}{
[]float32{0, 0},
}
dst.DrawTriangles(nil, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
pix, err := dst.Pixels()
if err != nil {

View File

@ -432,6 +432,10 @@ func (g *Graphics) genNextImageID() driver.ImageID {
return id
}
func (g *Graphics) InvalidImageID() driver.ImageID {
return -1
}
func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
g.checkSize(width, height)
td := mtl.TextureDescriptor{
@ -796,7 +800,7 @@ func (g *Graphics) NewShader(program *shaderir.Program) (driver.Shader, error) {
panic("metal: NewShader is not implemented")
}
func (g *Graphics) DrawShader(dst driver.ImageID, shader driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, uniforms []interface{}, srcs []driver.ImageID) error {
func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, shader driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, uniforms []interface{}) error {
panic("metal: DrawShader is not implemented")
}

View File

@ -84,6 +84,10 @@ func (g *Graphics) genNextImageID() driver.ImageID {
return id
}
func (g *Graphics) InvalidImageID() driver.ImageID {
return -1
}
func (g *Graphics) genNextShaderID() driver.ShaderID {
id := g.nextShaderID
g.nextShaderID++
@ -215,7 +219,15 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int,
})
}
if err := g.useProgram(program, uniforms, []textureNative{source.textureNative}); err != nil {
var imgs [graphics.ShaderImageNum]textureVariable
for i := range imgs {
if i == 0 {
imgs[i].valid = true
imgs[i].native = source.textureNative
}
}
if err := g.useProgram(program, uniforms, imgs); err != nil {
return err
}
@ -275,7 +287,7 @@ func (g *Graphics) removeShader(shader *Shader) {
delete(g.shaders, shader.id)
}
func (g *Graphics) DrawShader(dst driver.ImageID, shader driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, uniforms []interface{}, srcs []driver.ImageID) error {
func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, shader driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, uniforms []interface{}) error {
d := g.images[dst]
s := g.shaders[shader]
@ -292,9 +304,13 @@ func (g *Graphics) DrawShader(dst driver.ImageID, shader driver.ShaderID, indexL
us[k].value = v
}
ts := make([]textureNative, len(srcs))
for k, v := range srcs {
ts[k] = g.images[v].textureNative
var ts [graphics.ShaderImageNum]textureVariable
for i, src := range srcs {
if src == g.InvalidImageID() {
continue
}
ts[i].valid = true
ts[i].native = g.images[src].textureNative
}
if err := g.useProgram(s.p, us, ts); err != nil {

View File

@ -239,8 +239,13 @@ type uniformVariable struct {
value interface{}
}
type textureVariable struct {
valid bool
native textureNative
}
// useProgram uses the program (programTexture).
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures []textureNative) error {
func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textures [graphics.ShaderImageNum]textureVariable) error {
if !g.state.lastProgram.equal(program) {
g.context.useProgram(program)
if g.state.lastProgram.equal(zeroProgram) {
@ -278,13 +283,16 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
}
for i, t := range textures {
if !t.valid {
continue
}
g.context.uniformInt(program, fmt.Sprintf("T%d", i), i)
if g.state.lastActiveTexture != i {
g.context.activeTexture(i)
g.state.lastActiveTexture = i
}
// Apparently, a texture must be bound every time. The cache is not used here.
g.context.bindTexture(t)
g.context.bindTexture(t.native)
}
return nil

View File

@ -87,9 +87,10 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height)
}
func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Mipmap) {
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
level := 0
if src != nil && !src.volatile && filter != driver.FilterScreen {
// TODO: Do we need to check all the sources' states of being volatile?
if srcs[0] != nil && !srcs[0].volatile && filter != driver.FilterScreen {
level = math.MaxInt32
for i := 0; i < len(indices)/3; i++ {
const n = graphics.VertexFloatNum
@ -138,30 +139,27 @@ func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16
s = shader.shader
}
var srcimg *shareable.Image
if src != nil {
srcimg = src.orig
var imgs [graphics.ShaderImageNum]*shareable.Image
for i, src := range srcs {
if src == nil {
continue
}
if level != 0 {
if img := src.level(level); img != nil {
srcimg = img
const n = graphics.VertexFloatNum
s := float32(pow2(level))
for i := 0; i < len(vertices)/n; i++ {
vertices[i*n+2] /= s
vertices[i*n+3] /= s
}
imgs[i] = img
continue
}
}
imgs[i] = src.orig
}
// TODO: Do we need to consider mipmaps here?
var imgs []*shareable.Image
for _, img := range images {
imgs = append(imgs, img.orig)
}
m.orig.DrawTriangles(srcimg, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms, imgs)
m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
m.disposeMipmaps()
}
@ -222,7 +220,7 @@ func (m *Mipmap) level(level int) *shareable.Image {
return nil
}
s := shareable.NewImage(w2, h2, m.volatile)
s.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
s.DrawTriangles([graphics.ShaderImageNum]*shareable.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, nil, nil)
m.imgs[level] = s
return m.imgs[level]

View File

@ -69,7 +69,7 @@ func (p *Pixels) At(i, j int) (byte, byte, byte, byte) {
// drawTrianglesHistoryItem is an item for history of draw-image commands.
type drawTrianglesHistoryItem struct {
image *Image
images [graphics.ShaderImageNum]*Image
vertices []float32
indices []uint16
colorm *affine.ColorM
@ -79,7 +79,6 @@ type drawTrianglesHistoryItem struct {
sourceRegion driver.Region
shader *Shader
uniforms []interface{}
images []*Image
}
// Image represents an image that can be restored when GL context is lost.
@ -260,7 +259,7 @@ func fillImage(i *graphicscommand.Image, clr color.RGBA) {
// Add 1 pixels for paddings.
vs := quadVertices(0, 0, float32(dw), float32(dh), 1, 1, float32(sw-1), float32(sh-1), rf, gf, bf, af)
is := graphics.QuadIndices()
i.DrawTriangles(emptyImage.image, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
i.DrawTriangles([graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image}, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
}
// BasePixelsForTesting returns the image's basePixels for testing.
@ -352,7 +351,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
// 5: Color G
// 6: Color B
// 7: Color Y
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Image) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image")
}
@ -363,41 +362,38 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
// TODO: Add tests to confirm this logic.
var srcstale bool
if img != nil && (img.stale || img.volatile) {
srcstale = true
for _, src := range srcs {
if src == nil {
continue
}
if !srcstale {
for _, t := range images {
if t.stale || t.volatile {
if src.stale || src.volatile {
srcstale = true
break
}
}
}
if srcstale || i.screen || !needsRestoring() || i.volatile {
i.makeStale()
} else {
i.appendDrawTrianglesHistory(img, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms, images)
i.appendDrawTrianglesHistory(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms)
}
var s *graphicscommand.Shader
if shader != nil {
s = shader.shader
}
var gimg *graphicscommand.Image
if img != nil {
gimg = img.image
}
var ts []*graphicscommand.Image
for _, t := range images {
ts = append(ts, t.image)
var imgs [graphics.ShaderImageNum]*graphicscommand.Image
for i, src := range srcs {
if src == nil {
continue
}
i.image.DrawTriangles(gimg, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms, ts)
imgs[i] = src.image
}
i.image.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
}
// appendDrawTrianglesHistory appends a draw-image history item to the image.
func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Image) {
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
if i.stale || i.volatile || i.screen {
return
}
@ -416,7 +412,7 @@ func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, ind
copy(is, indices)
item := &drawTrianglesHistoryItem{
image: image,
images: srcs,
vertices: vs,
indices: is,
colorm: colorm,
@ -426,7 +422,6 @@ func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, ind
sourceRegion: sourceRegion,
shader: shader,
uniforms: uniforms,
images: images,
}
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
}
@ -515,10 +510,10 @@ func (i *Image) resolveStale() error {
// dependsOn reports whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool {
for _, c := range i.drawTrianglesHistory {
if c.image == target {
return true
}
for _, img := range c.images {
if img == nil {
continue
}
if img == target {
return true
}
@ -541,10 +536,10 @@ func (i *Image) dependsOnShader(shader *Shader) bool {
func (i *Image) dependingImages() map[*Image]struct{} {
r := map[*Image]struct{}{}
for _, c := range i.drawTrianglesHistory {
if c.image != nil {
r[c.image] = struct{}{}
}
for _, img := range c.images {
if img == nil {
continue
}
r[img] = struct{}{}
}
}
@ -592,23 +587,22 @@ func (i *Image) restore() error {
i.basePixels.Apply(gimg)
for _, c := range i.drawTrianglesHistory {
if c.image != nil && c.image.hasDependency() {
panic("restorable: all dependencies must be already resolved but not")
}
// TODO: Check the uniform variable's images.
var img *graphicscommand.Image
if c.image != nil {
img = c.image.image
}
var s *graphicscommand.Shader
if c.shader != nil {
s = c.shader.shader
}
var imgs []*graphicscommand.Image
for _, img := range c.images {
imgs = append(imgs, img.image)
var imgs [graphics.ShaderImageNum]*graphicscommand.Image
for i, img := range c.images {
if img == nil {
continue
}
gimg.DrawTriangles(img, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.sourceRegion, s, c.uniforms, imgs)
if img.hasDependency() {
panic("restorable: all dependencies must be already resolved but not")
}
imgs[i] = img.image
}
gimg.DrawTriangles(imgs, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.sourceRegion, s, c.uniforms)
}
if len(i.drawTrianglesHistory) > 0 {

View File

@ -131,7 +131,7 @@ func TestRestoreChain(t *testing.T) {
for i := 0; i < num-1; i++ {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
imgs[i+1].DrawTriangles(imgs[i], vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
}
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
@ -173,10 +173,10 @@ func TestRestoreChain2(t *testing.T) {
imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
is := graphics.QuadIndices()
imgs[8].DrawTriangles(imgs[7], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
imgs[9].DrawTriangles(imgs[8], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
for i := 0; i < 7; i++ {
imgs[i+1].DrawTriangles(imgs[i], quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
}
if err := ResolveStaleImages(); err != nil {
@ -216,10 +216,10 @@ func TestRestoreOverrideSource(t *testing.T) {
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
is := graphics.QuadIndices()
img2.DrawTriangles(img1, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img3.DrawTriangles(img2, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
img1.DrawTriangles(img0, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -298,23 +298,23 @@ func TestRestoreComplexGraph(t *testing.T) {
}()
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
img3.DrawTriangles(img0, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 1, 0)
img3.DrawTriangles(img1, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 1, 0)
img4.DrawTriangles(img1, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 2, 0)
img4.DrawTriangles(img2, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 0, 0)
img5.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 0, 0)
img6.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 1, 0)
img6.DrawTriangles(img4, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 0, 0)
img7.DrawTriangles(img2, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
vs = quadVertices(w, h, 2, 0)
img7.DrawTriangles(img3, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -406,8 +406,8 @@ func TestRestoreRecursive(t *testing.T) {
img0.Dispose()
}()
is := graphics.QuadIndices()
img1.DrawTriangles(img0, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img0.DrawTriangles(img1, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -501,7 +501,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
img1.DrawTriangles(img0, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := ResolveStaleImages(); err != nil {
@ -538,8 +538,8 @@ func TestDispose(t *testing.T) {
defer img2.Dispose()
is := graphics.QuadIndices()
img1.DrawTriangles(img2, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img0.DrawTriangles(img1, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img1.Dispose()
if err := ResolveStaleImages(); err != nil {
@ -647,7 +647,7 @@ func TestReplacePixelsOnly(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
img1.DrawTriangles(img0, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
// BasePixelsForTesting is available without GPU accessing.
@ -700,7 +700,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
src.ReplacePixels(pix, 0, 0, w, h)
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
// stale.
@ -721,7 +721,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
// ReplacePixels for a whole image doesn't panic.
}
@ -739,7 +739,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
}
@ -828,7 +828,7 @@ func TestFill2(t *testing.T) {
dst := NewImage(w, h, false)
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
// Fill src with a different color. This should not affect dst.
src.Fill(color.RGBA{0, 0xff, 0, 0xff})
@ -867,7 +867,7 @@ func TestMutateSlices(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := make([]uint16, len(graphics.QuadIndices()))
copy(is, graphics.QuadIndices())
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
for i := range vs {
vs[i] = 0
}

View File

@ -38,7 +38,7 @@ func TestShader(t *testing.T) {
us := []interface{}{
[]float32{0, 0},
}
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us, nil)
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
@ -75,7 +75,7 @@ func TestShaderChain(t *testing.T) {
us := []interface{}{
[]float32{0, 0},
}
imgs[i+1].DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us, imgs[i:i+1])
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
}
if err := ResolveStaleImages(); err != nil {
@ -99,7 +99,7 @@ func TestShaderMultipleSources(t *testing.T) {
t.Skip("shader is not available on this environment")
}
srcs := make([]*Image, 3)
var srcs [graphics.ShaderImageNum]*Image
for i := range srcs {
srcs[i] = NewImage(1, 1, false)
}
@ -114,7 +114,7 @@ func TestShaderMultipleSources(t *testing.T) {
us := []interface{}{
[]float32{0, 0},
}
dst.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us, srcs)
dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
// Clear one of the sources after DrawTriangles. dst should not be affected.
srcs[0].Fill(color.RGBA{})
@ -146,7 +146,7 @@ func TestShaderDispose(t *testing.T) {
us := []interface{}{
[]float32{0, 0},
}
img.DrawTriangles(nil, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us, nil)
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, us)
// Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
// stale.

View File

@ -220,7 +220,7 @@ func (i *Image) ensureNotShared() {
dx1, dy1, sx1, sy1, 1, 1, 1, 1,
}
is := graphics.QuadIndices()
newImg.DrawTriangles(i.backend.restorable, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
newImg.DrawTriangles([graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
i.dispose(false)
i.backend = &backend{
@ -273,6 +273,9 @@ func (i *Image) regionWithPadding() (x, y, width, height int) {
}
func (i *Image) processSrc(src *Image) {
if src == nil {
return
}
if src.disposed {
panic("shareable: the drawing source image must not be disposed (DrawTriangles)")
}
@ -287,12 +290,6 @@ func (i *Image) processSrc(src *Image) {
}
}
func makeSharedIfNeeded(src *Image) {
if !src.isShared() && src.shareable() {
imagesToMakeShared[src] = struct{}{}
}
}
// DrawTriangles draws triangles with the given image.
//
// The vertex floats are:
@ -305,7 +302,7 @@ func makeSharedIfNeeded(src *Image) {
// 5: Color G
// 6: Color B
// 7: Color Y
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}, images []*Image) {
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) {
backendsM.Lock()
// Do not use defer for performance.
@ -314,14 +311,7 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
}
i.ensureNotShared()
if img != nil {
i.processSrc(img)
}
firstImg := img
for _, src := range images {
if firstImg == nil {
firstImg = src
}
for _, src := range srcs {
i.processSrc(src)
}
@ -332,9 +322,10 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
dy = paddingSize
}
// TODO: Pass the offsets as uniform variables for second and following images.
var oxf, oyf float32
if firstImg != nil {
ox, oy, _, _ := firstImg.regionWithPadding()
if srcs[0] != nil {
ox, oy, _, _ := srcs[0].regionWithPadding()
ox += paddingSize
oy += paddingSize
oxf, oyf = float32(ox), float32(oy)
@ -356,25 +347,26 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16,
s = shader.shader
}
var imgs []*restorable.Image
for _, img := range images {
imgs = append(imgs, img.backend.restorable)
var imgs [graphics.ShaderImageNum]*restorable.Image
for i, src := range srcs {
if src == nil {
continue
}
imgs[i] = src.backend.restorable
}
var r *restorable.Image
if img != nil {
r = img.backend.restorable
}
i.backend.restorable.DrawTriangles(r, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms, imgs)
i.backend.restorable.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
i.nonUpdatedCount = 0
delete(imagesToMakeShared, i)
if img != nil {
makeSharedIfNeeded(img)
for _, src := range srcs {
if src == nil {
continue
}
if !src.isShared() && src.shareable() {
imagesToMakeShared[src] = struct{}{}
}
for _, src := range images {
makeSharedIfNeeded(src)
}
backendsM.Unlock()

View File

@ -96,7 +96,7 @@ func TestEnsureNotShared(t *testing.T) {
// img4.ensureNotShared() should be called.
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
is := graphics.QuadIndices()
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
want := false
if got := img4.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", got, want)
@ -126,7 +126,7 @@ func TestEnsureNotShared(t *testing.T) {
// Check further drawing doesn't cause panic.
// This bug was fixed by 03dcd948.
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
}
func TestReshared(t *testing.T) {
@ -166,7 +166,7 @@ func TestReshared(t *testing.T) {
// Use img1 as a render target.
vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices()
img1.DrawTriangles(img2, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -176,7 +176,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err)
}
img0.DrawTriangles(img1, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -203,7 +203,7 @@ func TestReshared(t *testing.T) {
}
}
img0.DrawTriangles(img1, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if got, want := img1.IsSharedForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -231,7 +231,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err)
}
img0.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
if got, want := img3.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -326,7 +326,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dst.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h)
@ -368,7 +368,7 @@ func TestSmallImages(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
pix, err := dst.Pixels(0, 0, w, h)
if err != nil {
@ -410,7 +410,7 @@ func TestLongImages(t *testing.T) {
const scale = 120
vs := quadVertices(w, h, 0, 0, scale)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil {

View File

@ -20,6 +20,7 @@ import (
"go/token"
"github.com/hajimehoshi/ebiten/internal/buffered"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/shader"
)
@ -35,7 +36,7 @@ type Shader struct {
shader *buffered.Shader
}
func NewShader(src []byte, textureNum int) (*Shader, error) {
func NewShader(src []byte) (*Shader, error) {
var buf bytes.Buffer
buf.Write(src)
buf.WriteString(shaderSuffix)
@ -47,7 +48,7 @@ func NewShader(src []byte, textureNum int) (*Shader, error) {
}
// TODO: Create a pseudo vertex entrypoint to treat the attribute values correctly.
s, err := shader.Compile(fs, f, "Vertex", "Fragment", textureNum)
s, err := shader.Compile(fs, f, "Vertex", "Fragment", graphics.ShaderImageNum)
if err != nil {
return nil, err
}