ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

2021SC@SDUSC山东大学软件学院软件工程应用与实践--Ebiten代码分析 源码分析(五)

2021-10-30 15:09:56  阅读:194  来源: 互联网

标签:src 2021SC -- driver vertices shader srcs 源码 float32


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有