2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(五)
2021/10/30 17:14:56
本文主要是介绍2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(五),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
2021SC@SDUSC
目录
- 一.概述
- 二.代码分析
- 1.internal/buffered/image.go
- 2.internal/atlas/image.go
- 3.internal/restorable/image.go
- 4.internal/grasphiccommand/image.go
一.概述
本文将继续讨论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的核心方法,不断的在图像上绘制图像,完成屏幕的刷新渲染。
这篇关于2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(五)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-11国产医疗级心电ECG采集处理模块
- 2025-01-10Rakuten 乐天积分系统从 Cassandra 到 TiDB 的选型与实战
- 2025-01-09CMS内容管理系统是什么?如何选择适合你的平台?
- 2025-01-08CCPM如何缩短项目周期并降低风险?
- 2025-01-08Omnivore 替代品 Readeck 安装与使用教程
- 2025-01-07Cursor 收费太贵?3分钟教你接入超低价 DeepSeek-V3,代码质量逼近 Claude 3.5
- 2025-01-06PingCAP 连续两年入选 Gartner 云数据库管理系统魔力象限“荣誉提及”
- 2025-01-05Easysearch 可搜索快照功能,看这篇就够了
- 2025-01-04BOT+EPC模式在基础设施项目中的应用与优势
- 2025-01-03用LangChain构建会检索和搜索的智能聊天机器人指南