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

View File

@ -44,6 +44,23 @@ func TestMain(m *testing.M) {
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
func TestEnsureNotShared(t *testing.T) {
@ -85,8 +102,7 @@ func TestEnsureNotShared(t *testing.T) {
dy1 = size * 3 / 4
)
// img4.ensureNotShared() should be called.
vs := make([]float32, 4*graphics.VertexFloatNum)
graphics.PutQuadVertices(vs, img3, 0, 0, size/2, size/2, 1, 0, 0, 1, size/4, size/4, 1, 1, 1, 1)
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
is := graphics.QuadIndices()
img4.DrawTriangles(img3, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
want := false
@ -150,8 +166,7 @@ func TestReshared(t *testing.T) {
}
// Use img1 as a render target.
vs := make([]float32, 4*graphics.VertexFloatNum)
graphics.PutQuadVertices(vs, img2, 0, 0, size, size, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices()
img1.DrawTriangles(img2, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
if got, want := img1.IsSharedForTesting(), false; got != want {
@ -277,8 +292,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
}
src.ReplacePixels(pix)
vs := make([]float32, 4*graphics.VertexFloatNum)
graphics.PutQuadVertices(vs, src, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressClampToZero)
dst.ReplacePixels(pix)
@ -313,8 +327,7 @@ func TestSmallImages(t *testing.T) {
}
src.ReplacePixels(pix)
vs := make([]float32, 4*graphics.VertexFloatNum)
graphics.PutQuadVertices(vs, src, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dst.DrawTriangles(src, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressClampToZero)
@ -349,8 +362,7 @@ func TestLongImages(t *testing.T) {
src.ReplacePixels(pix)
const scale = 120
vs := make([]float32, 4*graphics.VertexFloatNum)
graphics.PutQuadVertices(vs, src, 0, 0, w, h, scale, 0, 0, 1, 0, 0, 1, 1, 1, 1)
vs := quadVertices(w, h, 0, 0, scale)
is := graphics.QuadIndices()
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()
if level == 0 {
vs := vertexSlice(4)
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)
vs := quadVertices(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()
m.orig.DrawTriangles(src.orig, vs, is, colorm, mode, filter, driver.AddressClampToZero)
} 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
c *= s
d *= s
vs := vertexSlice(4)
graphics.PutQuadVertices(vs, shared, 0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
vs := quadVertices(0, 0, w, h, a, b, c, d, tx, ty, cr, cg, cb, ca)
is := graphics.QuadIndices()
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) {
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))
for idx, v := range vertices {
src.orig.PutVertex(vs[idx*graphics.VertexFloatNum:(idx+1)*graphics.VertexFloatNum],
float32(v.DstX), float32(v.DstY), v.SrcX, v.SrcY,
float32(bounds.Min.X), float32(bounds.Min.Y), float32(bounds.Max.X), float32(bounds.Max.Y),
v.ColorR, v.ColorG, v.ColorB, v.ColorA)
for i, v := range vertices {
vs[i*graphics.VertexFloatNum] = v.DstX
vs[i*graphics.VertexFloatNum+1] = v.DstY
vs[i*graphics.VertexFloatNum+2] = v.SrcX
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.disposeMipmaps()
@ -170,12 +184,12 @@ func (m *mipmap) level(r image.Rectangle, level int) *shareable.Image {
}
var src *shareable.Image
vs := vertexSlice(4)
var vs []float32
var filter driver.Filter
switch {
case level == 1:
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
case 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
}
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
case level == -1:
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
case 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
}
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
default:
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 {
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
}