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{} g.shaders = map[int]*ebiten.Shader{}
} }
if _, ok := g.shaders[g.idx]; !ok { 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 { if err != nil {
return err return err
} }
@ -130,7 +130,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
[]float32{float32(cx), float32(cy)}, // Cursor []float32{float32(cx), float32(cy)}, // Cursor
} }
if g.idx != 0 { if g.idx != 0 {
op.Images = append(op.Images, gophersImage) op.Images[0] = gophersImage
} }
screen.DrawTrianglesWithShader(vs, is, s, op) screen.DrawTrianglesWithShader(vs, is, s, op)

View File

@ -15,6 +15,7 @@
package ebiten package ebiten
import ( import (
"fmt"
"image" "image"
"image/color" "image/color"
@ -205,7 +206,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
sy1 := float32(bounds.Max.Y) 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) vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1, filter == driver.FilterScreen)
is := graphics.QuadIndices() 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 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 { type DrawTrianglesWithShaderOptions struct {
Uniforms []interface{} Uniforms []interface{}
Images []*Image Images [4]*Image
CompositeMode CompositeMode 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 // TODO: Add comments and tests
func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesWithShaderOptions) { func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, shader *Shader, options *DrawTrianglesWithShaderOptions) {
i.copyCheck() i.copyCheck()
@ -381,8 +389,11 @@ func (i *Image) DrawTrianglesWithShader(vertices []Vertex, indices []uint16, sha
us := append([]interface{}{[]float32{0, 0}}, options.Uniforms...) us := append([]interface{}{[]float32{0, 0}}, options.Uniforms...)
var imgw, imgh int var imgw, imgh int
var imgs []*buffered.Image var imgs [graphics.ShaderImageNum]*buffered.Image
for _, img := range options.Images { for i, img := range options.Images {
if img == nil {
continue
}
if img.isDisposed() { if img.isDisposed() {
panic("ebiten: the given image to DrawTriangles must not be disposed") 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 { } else if w, h := img.Size(); imgw != w || imgh != h {
panic("ebiten: all the source images must be the same size") 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) 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)) is := make([]uint16, len(indices))
copy(is, 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. // 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/affine"
"github.com/hajimehoshi/ebiten/internal/driver" "github.com/hajimehoshi/ebiten/internal/driver"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/mipmap" "github.com/hajimehoshi/ebiten/internal/mipmap"
"github.com/hajimehoshi/ebiten/internal/shaderir" "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. // DrawTriangles draws the src image with the given vertices.
// //
// Copying vertices and indices is the caller's responsibility. // 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) { 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 src != nil { for _, src := range srcs {
if i == src {
panic("buffered: Image.DrawTriangles: src must be different from the receiver")
}
}
for _, src := range images {
if i == src { if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver") 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 maybeCanAddDelayedCommand() {
if tryAddDelayedCommand(func() error { if tryAddDelayedCommand(func() error {
// Arguments are not copied. Copying is the caller's responsibility. // 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 nil
}) { }) {
return return
} }
} }
if src != nil { for _, src := range srcs {
src.resolvePendingPixels(true) if src == nil {
continue
} }
for _, src := range images {
src.resolvePendingPixels(true) src.resolvePendingPixels(true)
} }
i.resolvePendingPixels(false) i.resolvePendingPixels(false)
@ -270,17 +266,15 @@ func (i *Image) DrawTriangles(src *Image, vertices []float32, indices []uint16,
s = shader.shader s = shader.shader
} }
var srcImg *mipmap.Mipmap var imgs [graphics.ShaderImageNum]*mipmap.Mipmap
if src != nil { for i, img := range srcs {
srcImg = src.img if img == nil {
continue
}
imgs[i] = img.img
} }
var imgs []*mipmap.Mipmap i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
for _, img := range images {
imgs = append(imgs, img.img)
}
i.img.DrawTriangles(srcImg, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms, imgs)
i.invalidatePendingPixels() i.invalidatePendingPixels()
} }

View File

@ -18,6 +18,7 @@ import (
"errors" "errors"
"github.com/hajimehoshi/ebiten/internal/affine" "github.com/hajimehoshi/ebiten/internal/affine"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/shaderir" "github.com/hajimehoshi/ebiten/internal/shaderir"
"github.com/hajimehoshi/ebiten/internal/thread" "github.com/hajimehoshi/ebiten/internal/thread"
) )
@ -44,6 +45,7 @@ type Graphics interface {
IsGL() bool IsGL() bool
HasHighPrecisionFloat() bool HasHighPrecisionFloat() bool
MaxImageSize() int MaxImageSize() int
InvalidImageID() ImageID
NewShader(program *shaderir.Program) (Shader, error) NewShader(program *shaderir.Program) (Shader, error)
@ -58,7 +60,7 @@ type Graphics interface {
// //
// * float32 // * float32
// * []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. // 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" "github.com/hajimehoshi/ebiten/internal/web"
) )
const ShaderImageNum = 4
const ( const (
IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles. IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles.
VertexFloatNum = 8 VertexFloatNum = 8

View File

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

View File

@ -126,13 +126,6 @@ func (i *Image) InternalSize() (int, int) {
return i.internalWidth, i.internalHeight 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. // DrawTriangles draws triangles with the given image.
// //
// The vertex floats are: // 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 // 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. // 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.lastCommand == lastCommandNone {
if !i.screen && mode != driver.CompositeModeClear { if !i.screen && mode != driver.CompositeModeClear {
panic("graphicscommand: the image must be cleared first") panic("graphicscommand: the image must be cleared first")
} }
} }
if src != nil { for _, src := range srcs {
processSrc(src) if src == nil {
continue
} }
for _, src := range images { if src.screen {
processSrc(src) panic("graphicscommand: the screen image cannot be the rendering source")
}
src.resolveBufferedReplacePixels()
} }
i.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 { if i.lastCommand == lastCommandNone && !i.screen {
i.lastCommand = lastCommandClear i.lastCommand = lastCommandClear

View File

@ -44,7 +44,7 @@ func TestClear(t *testing.T) {
vs := quadVertices(w/2, h/2) vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices() 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() pix, err := dst.Pixels()
if err != nil { if err != nil {
@ -74,8 +74,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
dst := NewImage(w, h) dst := NewImage(w, h)
vs := quadVertices(w/2, h/2) vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices() 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)
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) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
} }
@ -89,14 +89,14 @@ func TestShader(t *testing.T) {
dst := NewImage(w, h) dst := NewImage(w, h)
vs := quadVertices(w, h) vs := quadVertices(w, h)
is := graphics.QuadIndices() 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) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
us := []interface{}{ us := []interface{}{
[]float32{0, 0}, []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() pix, err := dst.Pixels()
if err != nil { if err != nil {

View File

@ -432,6 +432,10 @@ func (g *Graphics) genNextImageID() driver.ImageID {
return id return id
} }
func (g *Graphics) InvalidImageID() driver.ImageID {
return -1
}
func (g *Graphics) NewImage(width, height int) (driver.Image, error) { func (g *Graphics) NewImage(width, height int) (driver.Image, error) {
g.checkSize(width, height) g.checkSize(width, height)
td := mtl.TextureDescriptor{ td := mtl.TextureDescriptor{
@ -796,7 +800,7 @@ func (g *Graphics) NewShader(program *shaderir.Program) (driver.Shader, error) {
panic("metal: NewShader is not implemented") 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") panic("metal: DrawShader is not implemented")
} }

View File

@ -84,6 +84,10 @@ func (g *Graphics) genNextImageID() driver.ImageID {
return id return id
} }
func (g *Graphics) InvalidImageID() driver.ImageID {
return -1
}
func (g *Graphics) genNextShaderID() driver.ShaderID { func (g *Graphics) genNextShaderID() driver.ShaderID {
id := g.nextShaderID id := g.nextShaderID
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 return err
} }
@ -275,7 +287,7 @@ func (g *Graphics) removeShader(shader *Shader) {
delete(g.shaders, shader.id) 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] d := g.images[dst]
s := g.shaders[shader] s := g.shaders[shader]
@ -292,9 +304,13 @@ func (g *Graphics) DrawShader(dst driver.ImageID, shader driver.ShaderID, indexL
us[k].value = v us[k].value = v
} }
ts := make([]textureNative, len(srcs)) var ts [graphics.ShaderImageNum]textureVariable
for k, v := range srcs { for i, src := range srcs {
ts[k] = g.images[v].textureNative 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 { if err := g.useProgram(s.p, us, ts); err != nil {

View File

@ -239,8 +239,13 @@ type uniformVariable struct {
value interface{} value interface{}
} }
type textureVariable struct {
valid bool
native textureNative
}
// useProgram uses the program (programTexture). // 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) { if !g.state.lastProgram.equal(program) {
g.context.useProgram(program) g.context.useProgram(program)
if g.state.lastProgram.equal(zeroProgram) { if g.state.lastProgram.equal(zeroProgram) {
@ -278,13 +283,16 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
} }
for i, t := range textures { for i, t := range textures {
if !t.valid {
continue
}
g.context.uniformInt(program, fmt.Sprintf("T%d", i), i) g.context.uniformInt(program, fmt.Sprintf("T%d", i), i)
if g.state.lastActiveTexture != i { if g.state.lastActiveTexture != i {
g.context.activeTexture(i) g.context.activeTexture(i)
g.state.lastActiveTexture = i g.state.lastActiveTexture = i
} }
// Apparently, a texture must be bound every time. The cache is not used here. // 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 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) 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 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 level = math.MaxInt32
for i := 0; i < len(indices)/3; i++ { for i := 0; i < len(indices)/3; i++ {
const n = graphics.VertexFloatNum const n = graphics.VertexFloatNum
@ -138,30 +139,27 @@ func (m *Mipmap) DrawTriangles(src *Mipmap, vertices []float32, indices []uint16
s = shader.shader s = shader.shader
} }
var srcimg *shareable.Image var imgs [graphics.ShaderImageNum]*shareable.Image
if src != nil { for i, src := range srcs {
srcimg = src.orig if src == nil {
continue
} }
if level != 0 { if level != 0 {
if img := src.level(level); img != nil { if img := src.level(level); img != nil {
srcimg = img
const n = graphics.VertexFloatNum const n = graphics.VertexFloatNum
s := float32(pow2(level)) s := float32(pow2(level))
for i := 0; i < len(vertices)/n; i++ { for i := 0; i < len(vertices)/n; i++ {
vertices[i*n+2] /= s vertices[i*n+2] /= s
vertices[i*n+3] /= s vertices[i*n+3] /= s
} }
imgs[i] = img
continue
} }
} }
imgs[i] = src.orig
}
// TODO: Do we need to consider mipmaps here? m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
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.disposeMipmaps() m.disposeMipmaps()
} }
@ -222,7 +220,7 @@ func (m *Mipmap) level(level int) *shareable.Image {
return nil return nil
} }
s := shareable.NewImage(w2, h2, m.volatile) 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 m.imgs[level] = s
return m.imgs[level] 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. // drawTrianglesHistoryItem is an item for history of draw-image commands.
type drawTrianglesHistoryItem struct { type drawTrianglesHistoryItem struct {
image *Image images [graphics.ShaderImageNum]*Image
vertices []float32 vertices []float32
indices []uint16 indices []uint16
colorm *affine.ColorM colorm *affine.ColorM
@ -79,7 +79,6 @@ type drawTrianglesHistoryItem struct {
sourceRegion driver.Region sourceRegion driver.Region
shader *Shader shader *Shader
uniforms []interface{} uniforms []interface{}
images []*Image
} }
// Image represents an image that can be restored when GL context is lost. // 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. // 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) vs := quadVertices(0, 0, float32(dw), float32(dh), 1, 1, float32(sw-1), float32(sh-1), rf, gf, bf, af)
is := graphics.QuadIndices() 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. // 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 // 5: Color G
// 6: Color B // 6: Color B
// 7: Color Y // 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 { if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image") 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. // TODO: Add tests to confirm this logic.
var srcstale bool var srcstale bool
if img != nil && (img.stale || img.volatile) { for _, src := range srcs {
srcstale = true if src == nil {
continue
} }
if !srcstale { if src.stale || src.volatile {
for _, t := range images {
if t.stale || t.volatile {
srcstale = true srcstale = true
break break
} }
} }
}
if srcstale || i.screen || !needsRestoring() || i.volatile { if srcstale || i.screen || !needsRestoring() || i.volatile {
i.makeStale() i.makeStale()
} else { } 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 var s *graphicscommand.Shader
if shader != nil { if shader != nil {
s = shader.shader s = shader.shader
} }
var gimg *graphicscommand.Image
if img != nil {
gimg = img.image
}
var ts []*graphicscommand.Image var imgs [graphics.ShaderImageNum]*graphicscommand.Image
for _, t := range images { for i, src := range srcs {
ts = append(ts, t.image) 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. // 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 { if i.stale || i.volatile || i.screen {
return return
} }
@ -416,7 +412,7 @@ func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, ind
copy(is, indices) copy(is, indices)
item := &drawTrianglesHistoryItem{ item := &drawTrianglesHistoryItem{
image: image, images: srcs,
vertices: vs, vertices: vs,
indices: is, indices: is,
colorm: colorm, colorm: colorm,
@ -426,7 +422,6 @@ func (i *Image) appendDrawTrianglesHistory(image *Image, vertices []float32, ind
sourceRegion: sourceRegion, sourceRegion: sourceRegion,
shader: shader, shader: shader,
uniforms: uniforms, uniforms: uniforms,
images: images,
} }
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item) i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
} }
@ -515,10 +510,10 @@ func (i *Image) resolveStale() error {
// dependsOn reports whether the image depends on target. // dependsOn reports whether the image depends on target.
func (i *Image) dependsOn(target *Image) bool { func (i *Image) dependsOn(target *Image) bool {
for _, c := range i.drawTrianglesHistory { for _, c := range i.drawTrianglesHistory {
if c.image == target {
return true
}
for _, img := range c.images { for _, img := range c.images {
if img == nil {
continue
}
if img == target { if img == target {
return true return true
} }
@ -541,10 +536,10 @@ func (i *Image) dependsOnShader(shader *Shader) bool {
func (i *Image) dependingImages() map[*Image]struct{} { func (i *Image) dependingImages() map[*Image]struct{} {
r := map[*Image]struct{}{} r := map[*Image]struct{}{}
for _, c := range i.drawTrianglesHistory { for _, c := range i.drawTrianglesHistory {
if c.image != nil {
r[c.image] = struct{}{}
}
for _, img := range c.images { for _, img := range c.images {
if img == nil {
continue
}
r[img] = struct{}{} r[img] = struct{}{}
} }
} }
@ -592,23 +587,22 @@ func (i *Image) restore() error {
i.basePixels.Apply(gimg) i.basePixels.Apply(gimg)
for _, c := range i.drawTrianglesHistory { 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 var s *graphicscommand.Shader
if c.shader != nil { if c.shader != nil {
s = c.shader.shader s = c.shader.shader
} }
var imgs []*graphicscommand.Image
for _, img := range c.images { var imgs [graphics.ShaderImageNum]*graphicscommand.Image
imgs = append(imgs, img.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 { if len(i.drawTrianglesHistory) > 0 {

View File

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

View File

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

View File

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

View File

@ -96,7 +96,7 @@ func TestEnsureNotShared(t *testing.T) {
// img4.ensureNotShared() should be called. // img4.ensureNotShared() should be called.
vs := quadVertices(size/2, size/2, size/4, size/4, 1) vs := quadVertices(size/2, size/2, size/4, size/4, 1)
is := graphics.QuadIndices() 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 want := false
if got := img4.IsSharedForTesting(); got != want { if got := img4.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", 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. // Check further drawing doesn't cause panic.
// This bug was fixed by 03dcd948. // 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) { func TestReshared(t *testing.T) {
@ -166,7 +166,7 @@ func TestReshared(t *testing.T) {
// Use img1 as a render target. // Use img1 as a render target.
vs := quadVertices(size, size, 0, 0, 1) vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices() 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -176,7 +176,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil { if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err) 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 { if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", 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 { if got, want := img1.IsSharedForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want) t.Errorf("got: %v, want: %v", got, want)
} }
@ -231,7 +231,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil { if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err) 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 { if got, want := img3.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", 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) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() 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) dst.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h) pix, err := dst.Pixels(0, 0, w, h)
@ -368,7 +368,7 @@ func TestSmallImages(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1) vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices() 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) pix, err := dst.Pixels(0, 0, w, h)
if err != nil { if err != nil {
@ -410,7 +410,7 @@ func TestLongImages(t *testing.T) {
const scale = 120 const scale = 120
vs := quadVertices(w, h, 0, 0, scale) vs := quadVertices(w, h, 0, 0, scale)
is := graphics.QuadIndices() 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) pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil { if err != nil {

View File

@ -20,6 +20,7 @@ import (
"go/token" "go/token"
"github.com/hajimehoshi/ebiten/internal/buffered" "github.com/hajimehoshi/ebiten/internal/buffered"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/shader" "github.com/hajimehoshi/ebiten/internal/shader"
) )
@ -35,7 +36,7 @@ type Shader struct {
shader *buffered.Shader shader *buffered.Shader
} }
func NewShader(src []byte, textureNum int) (*Shader, error) { func NewShader(src []byte) (*Shader, error) {
var buf bytes.Buffer var buf bytes.Buffer
buf.Write(src) buf.Write(src)
buf.WriteString(shaderSuffix) 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. // 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 { if err != nil {
return nil, err return nil, err
} }