shareable: Remove (*Image).PutVertex

This commit is contained in:
Hajime Hoshi 2019-09-21 02:35:18 +09:00
parent ea52aa52bb
commit 6095cd965b
4 changed files with 132 additions and 47 deletions

View File

@ -262,29 +262,22 @@ func (i *Image) Size() (width, height int) {
return i.width, i.height return i.width, i.height
} }
// PutVertices puts the given dst with vertices that can be passed to DrawTriangles. // DrawTriangles draws triangles with the given image.
func (i *Image) PutVertex(dst []float32, dx, dy, sx, sy float32, bx0, by0, bx1, by1 float32, cr, cg, cb, ca float32) {
// Specifying a range explicitly here is redundant but this helps optimization
// to eliminate boundary checks.
// //
// VertexFloatNum is better than 12 in terms of code maintenanceability, but in GopherJS, optimization // The vertex floats are:
// might not work. //
vs := dst[0:12] // 0: Destination X in pixels
// 1: Destination Y in pixels
vs[0] = dx // 2: Source X in pixels (the upper-left is (0, 0))
vs[1] = dy // 3: Source Y in pixels
vs[2] = sx // 4: Bounds of the source min X in pixels
vs[3] = sy // 5: Bounds of the source min Y in pixels
vs[4] = bx0 // 6: Bounds of the source max X in pixels
vs[5] = by0 // 7: Bounds of the source max Y in pixels
vs[6] = bx1 // 8: Color R [0.0-1.0]
vs[7] = by1 // 9: Color G
vs[8] = cr // 10: Color B
vs[9] = cg // 11: Color Y
vs[10] = cb
vs[11] = ca
}
func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) { func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()

View File

@ -44,6 +44,23 @@ func TestMain(m *testing.M) {
os.Exit(code) os.Exit(code)
} }
func quadVertices(sw, sh, x, y int, scalex float32) []float32 {
dx0 := float32(x)
dy0 := float32(y)
dx1 := float32(x) + float32(sw)*scalex
dy1 := float32(y) + float32(sh)
sx0 := float32(0)
sy0 := float32(0)
sx1 := float32(sw)
sy1 := float32(sh)
return []float32{
dx0, dy0, sx0, sy0, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
dx1, dy0, sx1, sy0, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
dx0, dy1, sx0, sy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
dx1, dy1, sx1, sy1, sx0, sy0, sx1, sy1, 1, 1, 1, 1,
}
}
const bigSize = 2049 const bigSize = 2049
func TestEnsureNotShared(t *testing.T) { func TestEnsureNotShared(t *testing.T) {
@ -85,8 +102,7 @@ func TestEnsureNotShared(t *testing.T) {
dy1 = size * 3 / 4 dy1 = size * 3 / 4
) )
// img4.ensureNotShared() should be called. // img4.ensureNotShared() should be called.
vs := make([]float32, 4*graphics.VertexFloatNum) vs := quadVertices(size/2, size/2, size/4, size/4, 1)
graphics.PutQuadVertices(vs, img3, 0, 0, size/2, size/2, 1, 0, 0, 1, size/4, size/4, 1, 1, 1, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero) img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
want := false want := false
@ -150,8 +166,7 @@ func TestReshared(t *testing.T) {
} }
// Use img1 as a render target. // Use img1 as a render target.
vs := make([]float32, 4*graphics.VertexFloatNum) vs := quadVertices(size, size, 0, 0, 1)
graphics.PutQuadVertices(vs, img2, 0, 0, size, size, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawTriangles(img2, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero) img1.DrawTriangles(img2, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
if got, want := img1.IsSharedForTesting(), false; got != want { if got, want := img1.IsSharedForTesting(), false; got != want {
@ -277,8 +292,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
} }
src.ReplacePixels(pix) src.ReplacePixels(pix)
vs := make([]float32, 4*graphics.VertexFloatNum) vs := quadVertices(w, h, 0, 0, 1)
graphics.PutQuadVertices(vs, src, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero) dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
dst.ReplacePixels(pix) dst.ReplacePixels(pix)
@ -313,8 +327,7 @@ func TestSmallImages(t *testing.T) {
} }
src.ReplacePixels(pix) src.ReplacePixels(pix)
vs := make([]float32, 4*graphics.VertexFloatNum) vs := quadVertices(w, h, 0, 0, 1)
graphics.PutQuadVertices(vs, src, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero) dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
@ -349,8 +362,7 @@ func TestLongImages(t *testing.T) {
src.ReplacePixels(pix) src.ReplacePixels(pix)
const scale = 120 const scale = 120
vs := make([]float32, 4*graphics.VertexFloatNum) vs := quadVertices(w, h, 0, 0, scale)
graphics.PutQuadVertices(vs, src, 0, 0, w, h, scale, 0, 0, 1, 0, 0, 1, 1, 1, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero) dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)

View File

@ -120,8 +120,7 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
a, b, c, d, tx, ty := geom.elements() a, b, c, d, tx, ty := geom.elements()
if level == 0 { if level == 0 {
vs := vertexSlice(4) vs := quadVertices(bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca)
graphics.PutQuadVertices(vs, src.orig, bounds.Min.X, bounds.Min.Y, bounds.Max.X, bounds.Max.Y, a, b, c, d, tx, ty, cr, cg, cb, ca)
is := graphics.QuadIndices() is := graphics.QuadIndices()
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressClampToZero) m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressClampToZero)
} else if shared := src.level(bounds, level); shared != nil { } else if shared := src.level(bounds, level); shared != nil {
@ -131,8 +130,7 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
b *= s b *= s
c *= s c *= s
d *= s d *= s
vs := vertexSlice(4) vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
graphics.PutQuadVertices(vs, shared, 0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
is := graphics.QuadIndices() is := graphics.QuadIndices()
m.orig.DrawTriangles(shared, vs, is, colorm, mode, filter, driver.AddressClampToZero) m.orig.DrawTriangles(shared, vs, is, colorm, mode, filter, driver.AddressClampToZero)
} }
@ -140,12 +138,28 @@ func (m *mipmap) drawImage(src *mipmap, bounds image.Rectangle, geom *GeoM, colo
} }
func (m *mipmap) drawTriangles(src *mipmap, bounds image.Rectangle, vertices []Vertex, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) { func (m *mipmap) drawTriangles(src *mipmap, bounds image.Rectangle, vertices []Vertex, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address) {
bx0 := float32(bounds.Min.X)
by0 := float32(bounds.Min.Y)
bx1 := float32(bounds.Max.X)
by1 := float32(bounds.Max.Y)
// TODO: Needs boundary check optimization?
// See https://go101.org/article/bounds-check-elimination.html
vs := vertexSlice(len(vertices)) vs := vertexSlice(len(vertices))
for idx, v := range vertices { for i, v := range vertices {
src.orig.PutVertex(vs[idx*graphics.VertexFloatNum:(idx+1)*graphics.VertexFloatNum], vs[i*graphics.VertexFloatNum] = v.DstX
float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY, vs[i*graphics.VertexFloatNum+1] = v.DstY
float32(bounds.Min.X), float32(bounds.Min.Y), float32(bounds.Max.X), float32(bounds.Max.Y), vs[i*graphics.VertexFloatNum+2] = v.SrcX
v.ColorR, v.ColorG, v.ColorB, v.ColorA) vs[i*graphics.VertexFloatNum+3] = v.SrcY
vs[i*graphics.VertexFloatNum+4] = bx0
vs[i*graphics.VertexFloatNum+5] = by0
vs[i*graphics.VertexFloatNum+6] = bx1
vs[i*graphics.VertexFloatNum+7] = by1
vs[i*graphics.VertexFloatNum+8] = v.ColorR
vs[i*graphics.VertexFloatNum+9] = v.ColorG
vs[i*graphics.VertexFloatNum+10] = v.ColorB
vs[i*graphics.VertexFloatNum+11] = v.ColorA
} }
m.orig.DrawTriangles(src.orig, vs, indices, colorm, mode, filter, address) m.orig.DrawTriangles(src.orig, vs, indices, colorm, mode, filter, address)
m.disposeMipmaps() m.disposeMipmaps()
@ -170,12 +184,12 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
} }
var src *shareable.Image var src *shareable.Image
vs := vertexSlice(4) var vs []float32
var filter driver.Filter var filter driver.Filter
switch { switch {
case level == 1: case level == 1:
src = m.orig src = m.orig
graphics.PutQuadVertices(vs, src, r.Min.X, r.Min.Y, r.Max.X, r.Max.Y, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) vs = quadVertices(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1)
filter = driver.FilterLinear filter = driver.FilterLinear
case level > 1: case level > 1:
src = m.level(r, level-1) src = m.level(r, level-1)
@ -184,11 +198,11 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
return nil return nil
} }
w, h := src.Size() w, h := src.Size()
graphics.PutQuadVertices(vs, src, 0, 0, w, h, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1) vs = quadVertices(0, 0, w, h, 0.5, 0, 0, 0.5, 0, 0, 1, 1, 1, 1)
filter = driver.FilterLinear filter = driver.FilterLinear
case level == -1: case level == -1:
src = m.orig src = m.orig
graphics.PutQuadVertices(vs, src, r.Min.X, r.Min.Y, r.Max.X, r.Max.Y, 2, 0, 0, 2, 0, 0, 1, 1, 1, 1) vs = quadVertices(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y, 2, 0, 0, 2, 0, 0, 1, 1, 1, 1)
filter = driver.FilterNearest filter = driver.FilterNearest
case level < -1: case level < -1:
src = m.level(r, level+1) src = m.level(r, level+1)
@ -197,7 +211,7 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
return nil return nil
} }
w, h := src.Size() w, h := src.Size()
graphics.PutQuadVertices(vs, src, 0, 0, w, h, 2, 0, 0, 2, 0, 0, 1, 1, 1, 1) vs = quadVertices(0, 0, w, h, 2, 0, 0, 2, 0, 0, 1, 1, 1, 1)
filter = driver.FilterNearest filter = driver.FilterNearest
default: default:
panic(fmt.Sprintf("ebiten: invalid level: %d", level)) panic(fmt.Sprintf("ebiten: invalid level: %d", level))

View File

@ -57,3 +57,69 @@ func (v *verticesBackend) slice(n int) []float32 {
func vertexSlice(n int) []float32 { func vertexSlice(n int) []float32 {
return theVerticesBackend.slice(n) return theVerticesBackend.slice(n)
} }
func quadVertices(sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) []float32 {
x := float32(sx1 - sx0)
y := float32(sy1 - sy0)
ax, by, cx, dy := a*x, b*y, c*x, d*y
u0, v0, u1, v1 := float32(sx0), float32(sy0), float32(sx1), float32(sy1)
// This function is very performance-sensitive and implement in a very dumb way.
vs := vertexSlice(4)
_ = vs[:48]
// For each values, see the comment at shareable.(*Image).DrawTriangles.
vs[0] = tx
vs[1] = ty
vs[2] = u0
vs[3] = v0
vs[4] = u0
vs[5] = v0
vs[6] = u1
vs[7] = v1
vs[8] = cr
vs[9] = cg
vs[10] = cb
vs[11] = ca
vs[12] = ax + tx
vs[13] = cx + ty
vs[14] = u1
vs[15] = v0
vs[16] = u0
vs[17] = v0
vs[18] = u1
vs[19] = v1
vs[20] = cr
vs[21] = cg
vs[22] = cb
vs[23] = ca
vs[24] = by + tx
vs[25] = dy + ty
vs[26] = u0
vs[27] = v1
vs[28] = u0
vs[29] = v0
vs[30] = u1
vs[31] = v1
vs[32] = cr
vs[33] = cg
vs[34] = cb
vs[35] = ca
vs[36] = ax + by + tx
vs[37] = cx + dy + ty
vs[38] = u1
vs[39] = v1
vs[40] = u0
vs[41] = v0
vs[42] = u1
vs[43] = v1
vs[44] = cr
vs[45] = cg
vs[46] = cb
vs[47] = ca
return vs
}