2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(五)
作者:互联网
2021SC@SDUSC
目录
一.概述
本文将继续讨论DrawImage()方法中嵌套调用的DrawTriangles()方法。
首先是mipmap类调用的,定义在internal/buffered/image.go的DrawTriangles方法。
该方法又调用了定义在internal/atlas/image.go中的DrawTriangles()方法。
然后atlas中的DrawTriangles()方法又调用了同文件下的drawTriangles()方法。
drawTriangles()方法又调用了位于internal/restorable/image.go中的DrawTriangles()方法。
接下来是internal/grasphiccommand/image.go方法中的DrawTriangles()方法且该方法最终调用了同文件夹下的command.go中的EnqueueDrawTrianglesCommand()方法调用请求绘制三角形命令。
下面将逐一分析上述函数的实现过程。
二.代码分析
1.internal/buffered/image.go
代码如下:
//DrawTriangles绘制具有给定顶点的src图像。
//。
//复制顶点和索引是调用者的责任
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
for _, src := range srcs {
if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
}
}
if maybeCanAddDelayedCommand() {
if tryAddDelayedCommand(func() error {
//不复制参数。抄袭是呼叫者的责任。
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd)
return nil
}) {
return
}
}
var s *atlas.Shader
var imgs [graphics.ShaderImageNum]*atlas.Image
if shader == nil {
//无着色器渲染的快速路径(#1355)
img := srcs[0]
img.resolvePendingPixels(true)
imgs[0] = img.img
} else {
for i, img := range srcs {
if img == nil {
continue
}
img.resolvePendingPixels(true)
imgs[i] = img.img
}
s = shader.shader
}
i.resolvePendingPixels(false)
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms, evenOdd)
i.invalidatePendingPixels()
}
该方法首先判断传入的srcs图像数组中的每一个图像的源图像都必须与接收方图像不同;
然后判断是否能够添加延迟的命令,如果能,则尝试添加延迟命令即本身的DrawTriangles命令,相当于递归;
如果不能添加延迟的命令,则先定义一个atlas类的shader对象,然后根据函数参数列表中的shader是否为空,如果为空,那么执行无着色器渲染的快速路径,不为空则先循环解析图像数值srcs中每一个图像挂起的像素,再将shader对象的shader属性赋给刚刚新定义的atlas.shader对象;
然后再解析接收方图像挂起的像素,执行atlas中的DrawTriangles方法,执行完毕再使挂起的像素无效化。
2.internal/atlas/image.go
代码如下:
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
backendsM.Lock()
defer backendsM.Unlock()
i.drawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false)
}
func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, keepOnAtlas bool) {
if i.disposed {
panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
}
if keepOnAtlas {
if i.backend == nil {
i.allocate(true)
}
} else {
i.ensureIsolated()
}
for _, src := range srcs {
i.processSrc(src)
}
cr := float32(1)
cg := float32(1)
cb := float32(1)
ca := float32(1)
if !colorm.IsIdentity() && colorm.ScaleOnly() {
cr = colorm.At(0, 0)
cg = colorm.At(1, 1)
cb = colorm.At(2, 2)
ca = colorm.At(3, 3)
colorm = affine.ColorMIdentity{}
}
var dx, dy float32
//屏幕画面没有填充。
if !i.screen {
x, y, _, _ := i.regionWithPadding()
dx = float32(x) + paddingSize
dy = float32(y) + paddingSize
//TODO:检查dstRegion是否不违反区域。
}
dstRegion.X += dx
dstRegion.Y += dy
var oxf, oyf float32
if srcs[0] != nil {
ox, oy, _, _ := srcs[0].regionWithPadding()
ox += paddingSize
oy += paddingSize
oxf, oyf = float32(ox), float32(oy)
n := len(vertices)
for i := 0; i < n; i += graphics.VertexFloatNum {
vertices[i] += dx
vertices[i+1] += dy
vertices[i+2] += oxf
vertices[i+3] += oyf
vertices[i+4] *= cr
vertices[i+5] *= cg
vertices[i+6] *= cb
vertices[i+7] *= ca
}
//srcRegion在不需要时可以强制为空,以避免意外。
//性能问题(#1293)。
if srcRegion.Width != 0 && srcRegion.Height != 0 {
srcRegion.X += oxf
srcRegion.Y += oyf
}
} else {
n := len(vertices)
for i := 0; i < n; i += graphics.VertexFloatNum {
vertices[i] += dx
vertices[i+1] += dy
vertices[i+4] *= cr
vertices[i+5] *= cg
vertices[i+6] *= cb
vertices[i+7] *= ca
}
}
var offsets [graphics.ShaderImageNum - 1][2]float32
var s *restorable.Shader
var imgs [graphics.ShaderImageNum]*restorable.Image
if shader == nil {
//无着色器渲染的快速路径(#1355)
imgs[0] = srcs[0].backend.restorable
} else {
for i, subimageOffset := range subimageOffsets {
src := srcs[i+1]
if src == nil {
continue
}
ox, oy, _, _ := src.regionWithPadding()
offsets[i][0] = float32(ox) + paddingSize - oxf + subimageOffset[0]
offsets[i][1] = float32(oy) + paddingSize - oyf + subimageOffset[1]
}
s = shader.shader
for i, src := range srcs {
if src == nil {
continue
}
imgs[i] = src.backend.restorable
}
}
i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms, evenOdd)
for _, src := range srcs {
if src == nil {
continue
}
if !src.isOnAtlas() && src.canBePutOnAtlas() {
//src可能已经注册,但重新赋值并不有害。
imagesToPutOnAtlas[src] = struct{}{}
}
}
}
DrawTriangles使用给定图像绘制三角形。
顶点浮点数为:
0:目标X,单位为像素。
1:目标Y,单位为像素。
2:源X,单位为像素(左上角为(0,0))。
3:源Y,单位为像素。
4:颜色R[0.0-1.0]。
5:颜色G。
6:颜色B。
7:颜色Y。
DrawTriangles相当于再drawTriangles的基础上增加了一个互斥锁,重点分析drawTriangles的实现方法。
首先判断目标图像是否已经被处理过了,接下来判断是否有KeepOnAtlas,此处我们没有传入该参数,所以直接执行下一步ensureIsolated()方法确保img像素是隔离的,具体方法如下:
func (i *Image) ensureIsolated() {
i.resetUsedAsSourceCount()
if i.backend == nil {
i.allocate(false)
return
}
if !i.isOnAtlas() {
return
}
ox, oy, w, h := i.regionWithPadding()
dx0 := float32(0)
dy0 := float32(0)
dx1 := float32(w)
dy1 := float32(h)
sx0 := float32(ox)
sy0 := float32(oy)
sx1 := float32(ox + w)
sy1 := float32(oy + h)
newImg := restorable.NewImage(w, h)
newImg.SetVolatile(i.volatile)
vs := []float32{
dx0, dy0, sx0, sy0, 1, 1, 1, 1,
dx1, dy0, sx1, sy0, 1, 1, 1, 1,
dx0, dy1, sx0, sy1, 1, 1, 1, 1,
dx1, dy1, sx1, sy1, 1, 1, 1, 1,
}
is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}
var offsets [graphics.ShaderImageNum - 1][2]float32
dstRegion := driver.Region{
X: paddingSize,
Y: paddingSize,
Width: float32(w - 2*paddingSize),
Height: float32(h - 2*paddingSize),
}
newImg.DrawTriangles(srcs, offsets, vs, is, affine.ColorMIdentity{}, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
i.dispose(false)
i.backend = &backend{
restorable: newImg,
}
i.isolatedCount++
}
然后经过一系列变换操作,得到新的参数,与上述过程类似,在此不详细介绍了,然后新建一个restorable类型的shader,获取到新的shader之后执行restorable文件夹下的DrawTriangles方法。
3.internal/restorable/image.go
代码如下:
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, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image")
}
if len(vertices) == 0 {
return
}
theImages.makeStaleIfDependingOn(i)
// TODO: Add tests to confirm this logic.
var srcstale bool
for _, src := range srcs {
if src == nil {
continue
}
if src.stale || src.volatile {
srcstale = true
break
}
}
if srcstale || i.screen || !NeedsRestoring() || i.volatile {
i.makeStale()
} else {
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, shader, uniforms, evenOdd)
}
var s *graphicscommand.Shader
var imgs [graphics.ShaderImageNum]*graphicscommand.Image
if shader == nil {
// Fast path for rendering without a shader (#1355).
imgs[0] = srcs[0].image
} else {
for i, src := range srcs {
if src == nil {
continue
}
imgs[i] = src.image
}
s = shader.shader
}
i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms, evenOdd)
}
首先判断是否是优先级图像,因为不能在优先级图像上调用该方法,然后检查传入顶点长度是否正确,接下来调用makeStaleIfDependingOn()方法使所有依赖于目标的镜像都失效。接下来同上述两个方法,获取graphicscommand.Shader类型的着色器,并将其传入下一级DrawTriangles方法中。
4.internal/grasphiccommand/image.go
代码如下:
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, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
if shader == nil {
//无着色器渲染的快速路径(#1355)
img := srcs[0]
if img.screen {
panic("graphicscommand: the screen image cannot be the rendering source")
}
img.resolveBufferedReplacePixels()
} else {
for _, src := range srcs {
if src == nil {
continue
}
if src.screen {
panic("graphicscommand: the screen image cannot be the rendering source")
}
src.resolveBufferedReplacePixels()
}
}
i.resolveBufferedReplacePixels()
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, dstRegion, srcRegion, shader, uniforms, evenOdd)
}
首先还是判断是否存在传入的着色器,然后调用resolveBufferedReplacePixels()方法解析缓冲的替换像素,一切操作完毕后将这处理过的一系列参数传给一个drawTrianglesCommand对象,并将其添加到命令队列中等待调用。
drawTrianglesCommand对象定义如下:
//drawTrianglesCommand表示在另一个图像上绘制图像的绘制命令。
type drawTrianglesCommand struct {
dst *Image
srcs [graphics.ShaderImageNum]*Image
offsets [graphics.ShaderImageNum - 1][2]float32
vertices []float32
nindices int
color affine.ColorM
mode driver.CompositeMode
filter driver.Filter
address driver.Address
dstRegion driver.Region
srcRegion driver.Region
shader *Shader
uniforms []interface{}
evenOdd bool
}
以上就是ebiten的核心方法,不断的在图像上绘制图像,完成屏幕的刷新渲染。
标签:src,2021SC,--,driver,vertices,shader,srcs,源码,float32 来源: https://blog.csdn.net/qq_48181962/article/details/121050063