shader: Enable to get pixels from multiple images

Updates #1193
This commit is contained in:
Hajime Hoshi 2020-07-18 19:56:22 +09:00
parent 5dd073fcbb
commit 36e9803cea
15 changed files with 199 additions and 80 deletions

View File

@ -60,7 +60,7 @@ type Graphics interface {
// //
// * float32 // * float32
// * []float32 // * []float32
DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, uniforms []interface{}) error DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, 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,7 +18,18 @@ import (
"github.com/hajimehoshi/ebiten/internal/web" "github.com/hajimehoshi/ebiten/internal/web"
) )
const ShaderImageNum = 4 const (
ShaderImageNum = 4
// PreservedUniformVariablesNum represents the number of preserved uniform variables.
// Any shaders in Ebiten must have these uniform variables.
//
// The first one is for the viewport size.
// The second and the followings are for offsets of the second and the following images.
//
// All the preversed uniform variables are vec2 so far.
PreservedUniformVariablesNum = 1 + (ShaderImageNum - 1)
)
const ( const (
IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles. IndicesNum = (1 << 16) / 3 * 3 // Adjust num for triangles.

View File

@ -144,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 *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{}) { func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, 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))
} }
@ -171,6 +171,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
// 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) {
// TODO: Pass offsets and uniforms when merging considers the shader.
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, 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))
@ -178,17 +179,24 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
} }
} }
if firstSrc != nil && address != driver.AddressUnsafe { if firstSrc != nil {
w, h := firstSrc.InternalSize() w, h := firstSrc.InternalSize()
if address != driver.AddressUnsafe {
sourceRegion.X /= float32(w) sourceRegion.X /= float32(w)
sourceRegion.Y /= float32(h) sourceRegion.Y /= float32(h)
sourceRegion.Width /= float32(w) sourceRegion.Width /= float32(w)
sourceRegion.Height /= float32(h) sourceRegion.Height /= float32(h)
} }
for i := range offsets {
offsets[i][0] /= float32(w)
offsets[i][1] /= float32(h)
}
}
c := &drawTrianglesCommand{ c := &drawTrianglesCommand{
dst: dst, dst: dst,
srcs: srcs, srcs: srcs,
offsets: offsets,
nvertices: len(vertices), nvertices: len(vertices),
nindices: len(indices), nindices: len(indices),
color: color, color: color,
@ -315,6 +323,7 @@ func FlushCommands() error {
type drawTrianglesCommand struct { type drawTrianglesCommand struct {
dst *Image dst *Image
srcs [graphics.ShaderImageNum]*Image srcs [graphics.ShaderImageNum]*Image
offsets [graphics.ShaderImageNum - 1][2]float32
nvertices int nvertices int
nindices int nindices int
color *affine.ColorM color *affine.ColorM
@ -424,7 +433,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
imgs[i] = src.image.ID() imgs[i] = src.image.ID()
} }
return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.shader.shader.ID(), c.nindices, indexOffset, c.mode, c.uniforms) return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.mode, c.uniforms)
} }
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) 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)
} }

View File

@ -147,7 +147,7 @@ func (i *Image) InternalSize() (int, int) {
// //
// 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(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{}) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, 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")
@ -165,7 +165,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
} }
i.resolveBufferedReplacePixels() i.resolveBufferedReplacePixels()
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms) theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, 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([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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([graphics.ShaderImageNum]*Image{clr}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, 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.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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,11 +89,11 @@ 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([graphics.ShaderImageNum]*Image{clr}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, 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)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
pix, err := dst.Pixels() pix, err := dst.Pixels()
if err != nil { if err != nil {

View File

@ -802,7 +802,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, srcs [graphics.ShaderImageNum]driver.ImageID, shader driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, uniforms []interface{}) error { func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, 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

@ -287,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, srcs [graphics.ShaderImageNum]driver.ImageID, shader driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, uniforms []interface{}) error { func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, 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]
@ -298,14 +298,22 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]
} }
g.context.blendFunc(mode) g.context.blendFunc(mode)
us := make([]uniformVariable, 1+len(uniforms)) us := make([]uniformVariable, graphics.PreservedUniformVariablesNum+len(uniforms))
vw := graphics.InternalImageSize(d.width) vw := graphics.InternalImageSize(d.width)
vh := graphics.InternalImageSize(d.height) vh := graphics.InternalImageSize(d.height)
us[0].name = "U0" us[0].name = "U0"
us[0].value = []float32{float32(vw), float32(vh)} us[0].value = []float32{float32(vw), float32(vh)}
for k, v := range uniforms {
us[k+1].name = fmt.Sprintf("U%d", k+1) for i, o := range offsets {
us[k+1].value = v o := o
us[i+1].name = fmt.Sprintf("U%d", i+1)
us[i+1].value = o[:]
}
for i, v := range uniforms {
const offset = graphics.PreservedUniformVariablesNum
us[i+offset].name = fmt.Sprintf("U%d", i+offset)
us[i+offset].value = v
} }
var ts [graphics.ShaderImageNum]textureVariable var ts [graphics.ShaderImageNum]textureVariable

View File

@ -262,6 +262,8 @@ func (g *Graphics) useProgram(program program, uniforms []uniformVariable, textu
for _, u := range uniforms { for _, u := range uniforms {
switch v := u.value.(type) { switch v := u.value.(type) {
case nil:
// Do nothing.
case float32: case float32:
cached, ok := g.state.lastUniforms[u.name].(float32) cached, ok := g.state.lastUniforms[u.name].(float32)
if ok && cached == v { if ok && cached == v {

View File

@ -70,6 +70,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 {
images [graphics.ShaderImageNum]*Image images [graphics.ShaderImageNum]*Image
offsets [graphics.ShaderImageNum - 1][2]float32
vertices []float32 vertices []float32
indices []uint16 indices []uint16
colorm *affine.ColorM colorm *affine.ColorM
@ -259,7 +260,9 @@ 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([graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image}, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) srcs := [graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image}
var offsets [graphics.ShaderImageNum - 1][2]float32
i.DrawTriangles(srcs, offsets, 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.
@ -351,7 +354,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(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{}) { func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, 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")
} }
@ -375,7 +378,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
if srcstale || i.screen || !needsRestoring() || i.volatile { if srcstale || i.screen || !needsRestoring() || i.volatile {
i.makeStale() i.makeStale()
} else { } else {
i.appendDrawTrianglesHistory(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms) i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms)
} }
var s *graphicscommand.Shader var s *graphicscommand.Shader
if shader != nil { if shader != nil {
@ -389,11 +392,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
} }
imgs[i] = src.image imgs[i] = src.image
} }
i.image.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms) i.image.DrawTriangles(imgs, offsets, 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(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{}) { func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, 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
} }
@ -413,6 +416,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
item := &drawTrianglesHistoryItem{ item := &drawTrianglesHistoryItem{
images: srcs, images: srcs,
offsets: offsets,
vertices: vs, vertices: vs,
indices: is, indices: is,
colorm: colorm, colorm: colorm,
@ -602,7 +606,7 @@ func (i *Image) restore() error {
} }
imgs[i] = img.image imgs[i] = img.image
} }
gimg.DrawTriangles(imgs, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.sourceRegion, s, c.uniforms) gimg.DrawTriangles(imgs, c.offsets, 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([graphics.ShaderImageNum]*Image{imgs[i]}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, 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([graphics.ShaderImageNum]*Image{imgs[7]}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, 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) imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, 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([graphics.ShaderImageNum]*Image{imgs[i]}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, 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([graphics.ShaderImageNum]*Image{img1}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, 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) img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, 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([graphics.ShaderImageNum]*Image{img0}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, 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,24 @@ 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([graphics.ShaderImageNum]*Image{img0}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) var offsets [graphics.ShaderImageNum - 1][2]float32
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, 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([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, 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([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, 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([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, 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([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, 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([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, 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([graphics.ShaderImageNum]*Image{img4}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, 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([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, 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([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, 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 +407,8 @@ func TestRestoreRecursive(t *testing.T) {
img0.Dispose() img0.Dispose()
}() }()
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, 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) img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +502,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([graphics.ShaderImageNum]*Image{img0}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +539,8 @@ func TestDispose(t *testing.T) {
defer img2.Dispose() defer img2.Dispose()
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, 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) img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +648,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([graphics.ShaderImageNum]*Image{img0}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +701,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([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +722,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([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +740,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([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +829,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([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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 +868,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([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, 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

@ -35,7 +35,7 @@ func TestShader(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
if err := ResolveStaleImages(); err != nil { if err := ResolveStaleImages(); err != nil {
t.Fatal(err) t.Fatal(err)
@ -69,7 +69,7 @@ func TestShaderChain(t *testing.T) {
ir := etesting.ShaderProgramImages(1) ir := etesting.ShaderProgramImages(1)
s := NewShader(&ir) s := NewShader(&ir)
for i := 0; i < num-1; i++ { for i := 0; i < num-1; i++ {
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, nil) imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
} }
if err := ResolveStaleImages(); err != nil { if err := ResolveStaleImages(); err != nil {
@ -105,7 +105,48 @@ func TestShaderMultipleSources(t *testing.T) {
ir := etesting.ShaderProgramImages(3) ir := etesting.ShaderProgramImages(3)
s := NewShader(&ir) s := NewShader(&ir)
dst.DrawTriangles(srcs, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) var offsets [graphics.ShaderImageNum - 1][2]float32
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
// Clear one of the sources after DrawTriangles. dst should not be affected.
srcs[0].Fill(color.RGBA{})
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
if err := RestoreIfNeeded(); err != nil {
t.Fatal(err)
}
want := color.RGBA{0x40, 0x80, 0xc0, 0xff}
got := pixelsToColor(dst.BasePixelsForTesting(), 0, 0)
if !sameColors(got, want, 1) {
t.Errorf("got %v, want %v", got, want)
}
}
func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
if !graphicscommand.IsShaderAvailable() {
t.Skip("shader is not available on this environment")
}
var srcs [graphics.ShaderImageNum]*Image
srcs[0] = NewImage(3, 1, false)
srcs[0].ReplacePixels([]byte{
0x40, 0, 0, 0xff,
0, 0x80, 0, 0xff,
0, 0, 0xc0, 0xff,
}, 0, 0, 3, 1)
dst := NewImage(1, 1, false)
ir := etesting.ShaderProgramImages(3)
s := NewShader(&ir)
offsets := [graphics.ShaderImageNum - 1][2]float32{
{1, 0},
{2, 0},
}
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
// 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{})
@ -134,7 +175,7 @@ func TestShaderDispose(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff) ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir) s := NewShader(&ir)
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil) img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
// 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

@ -181,6 +181,12 @@ func (cs *compileState) parse(f *ast.File) {
utypes = append(utypes, cs.ir.Uniforms[i]) utypes = append(utypes, cs.ir.Uniforms[i])
} }
} }
// TODO: Check len(unames) == graphics.PreservedUniformVariablesNum. Unfortunately this is not true on tests.
for _, t := range utypes {
if got, want := t.Main, shaderir.Vec2; got != want {
panic(fmt.Sprintf("shader: all the preserved uniform variables' types must be %v but %v", want, got))
}
}
for i, u := range cs.uniforms { for i, u := range cs.uniforms {
if !strings.HasPrefix(u, "__") { if !strings.HasPrefix(u, "__") {
unames = append(unames, u) unames = append(unames, u)

View File

@ -220,7 +220,9 @@ 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([graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}
var offsets [graphics.ShaderImageNum - 1][2]float32
newImg.DrawTriangles(srcs, offsets, 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{
@ -322,7 +324,6 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
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 srcs[0] != nil { if srcs[0] != nil {
ox, oy, _, _ := srcs[0].regionWithPadding() ox, oy, _, _ := srcs[0].regionWithPadding()
@ -342,6 +343,19 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
} }
} }
var offsets [graphics.ShaderImageNum - 1][2]float32
for i := range offsets {
src := srcs[i+1]
if src == nil {
continue
}
ox, oy, _, _ := src.regionWithPadding()
ox += paddingSize
oy += paddingSize
offsets[i][0] = float32(ox) - oxf
offsets[i][1] = float32(oy) - oyf
}
var s *restorable.Shader var s *restorable.Shader
if shader != nil { if shader != nil {
s = shader.shader s = shader.shader
@ -355,7 +369,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
imgs[i] = src.backend.restorable imgs[i] = src.backend.restorable
} }
i.backend.restorable.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms) i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
i.nonUpdatedCount = 0 i.nonUpdatedCount = 0
delete(imagesToMakeShared, i) delete(imagesToMakeShared, i)

View File

@ -17,6 +17,7 @@ package testing
import ( import (
"go/constant" "go/constant"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/shaderir" "github.com/hajimehoshi/ebiten/internal/shaderir"
) )
@ -210,10 +211,7 @@ var (
) )
func defaultProgram() shaderir.Program { func defaultProgram() shaderir.Program {
return shaderir.Program{ p := shaderir.Program{
Uniforms: []shaderir.Type{
{Main: shaderir.Vec2}, // Viewport size. This must be the first uniform variable, and the values are set at graphicscommand driver.
},
Attributes: []shaderir.Type{ Attributes: []shaderir.Type{
{Main: shaderir.Vec2}, // Local var (0) in the vertex shader {Main: shaderir.Vec2}, // Local var (0) in the vertex shader
{Main: shaderir.Vec2}, // Local var (1) in the vertex shader {Main: shaderir.Vec2}, // Local var (1) in the vertex shader
@ -225,6 +223,12 @@ func defaultProgram() shaderir.Program {
}, },
VertexFunc: defaultVertexFunc, VertexFunc: defaultVertexFunc,
} }
p.Uniforms = make([]shaderir.Type, graphics.PreservedUniformVariablesNum)
for i := range p.Uniforms {
p.Uniforms[i] = shaderir.Type{Main: shaderir.Vec2}
}
return p
} }
// ShaderProgramFill returns a shader intermediate representation to fill the frambuffer. // ShaderProgramFill returns a shader intermediate representation to fill the frambuffer.
@ -340,6 +344,17 @@ func ShaderProgramImages(imageNum int) shaderir.Program {
}, },
} }
} else { } else {
texPos2 := shaderir.Expr{
Type: shaderir.Binary,
Op: shaderir.Add,
Exprs: []shaderir.Expr{
texPos,
{
Type: shaderir.UniformVariable,
Index: 1 + i - 1,
},
},
}
rhs = shaderir.Expr{ rhs = shaderir.Expr{
Type: shaderir.Binary, Type: shaderir.Binary,
Op: shaderir.Add, Op: shaderir.Add,
@ -356,7 +371,7 @@ func ShaderProgramImages(imageNum int) shaderir.Program {
Type: shaderir.TextureVariable, Type: shaderir.TextureVariable,
Index: i, Index: i,
}, },
texPos, texPos2,
}, },
}, },
}, },

View File

@ -19,14 +19,16 @@ import (
"fmt" "fmt"
"go/parser" "go/parser"
"go/token" "go/token"
"strings"
"github.com/hajimehoshi/ebiten/internal/buffered" "github.com/hajimehoshi/ebiten/internal/buffered"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/shader" "github.com/hajimehoshi/ebiten/internal/shader"
) )
var shaderSuffix = ` var shaderSuffix string
func init() {
shaderSuffix = `
var __viewportSize vec2 var __viewportSize vec2
func viewportSize() vec2 { func viewportSize() vec2 {
@ -34,18 +36,24 @@ func viewportSize() vec2 {
} }
` `
func init() { for i := 1; i < graphics.ShaderImageNum; i++ {
// __t%d is a special variable for a texture variable. shaderSuffix += fmt.Sprintf(`
// TODO: Add appropriate offsets for second and following images. var __offset%d vec2
`, i)
var fs []string }
for i := 0; i < graphics.ShaderImageNum; i++ {
fs = append(fs, fmt.Sprintf(`func texture%[1]dAt(pos vec2) vec4 { for i := 0; i < graphics.ShaderImageNum; i++ {
return texture2D(__t%[1]d, pos) var offset string
} if i >= 1 {
`, i)) offset = fmt.Sprintf(" + __offset%d", i)
}
// __t%d is a special variable for a texture variable.
shaderSuffix += fmt.Sprintf(`
func texture%[1]dAt(pos vec2) vec4 {
return texture2D(__t%[1]d, pos%[2]s)
}
`, i, offset)
} }
shaderSuffix += "\n" + strings.Join(fs, "\n")
} }
type Shader struct { type Shader struct {