Skia深入分析8——Skia的GPU绘图

  • Post author:
  • Post category:其他




Skia的GPU绘图



一、Skia-GPU概述

在Android4.2到Android5.0的过程中,skia中开发较频繁的部分莫过于GPU加速部分和延迟渲染机制,尽管目前来看几乎没有用到,但后续很可能会在Frameworks层引入。




Android

上面,只可能使用OpenGL,因此作为使用OpenGL的绘图引擎,关注如下要点即可:


1、OpenGL上下文如何建立(关系到如何显示绘制结果)


2、顶点如何生成


3、着色器如何管理,特效怎么设置


4、纹理、vbo、字体cache等缓存管理机制


由于OpenGL编程本身很复杂,东西也很多,这里只是介绍一下用法和流程框架,有兴趣研究的可按上述问题细看。



二、用法

<code class="hljs lasso has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*获取OpenGL上下文*/</span>
GrContextFactory contextFactory;
GrContext<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">*</span> context <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> contextFactory<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>get(GrContextFactory<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;">::kNative_GLContextType</span>);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*创建指定大小格式Surface,并由Surface中取出Canvas*/</span>
const SkImageInfo info <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> SkImageInfo<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;">::MakeN32Premul</span>(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">720</span>,<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1080</span>);
SkSurface<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">*</span> surface <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> SkSurface<span class="hljs-tag" style="color: rgb(0, 102, 102); box-sizing: border-box;">::NewRenderTarget</span>(context, info);<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//实际上是创建一个纹理,并创建相应的fbo与之绑定,以作为渲染目标</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//或者用 NewScratchRenderTarget,这个会用缓存过的目标纹理</span>
SkCanvas<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">*</span> canvas <span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">=</span> surface<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">-></span>getCanvas();
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*执行绘制*/</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*canvas->drawColor(0x0);*/</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*..........*/</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*..........*/</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*..........*/</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*绘制完成,取出像素*/</span>
SkBitmap output;
output<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>setInfo(info);
canvas<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">-></span>readPixels(<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">&</span>output);
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*又或者读到GraphicBuffer上*/</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*输入 ANativeWindow_Buffer outBuffer*/</span>
canvas<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">-></span>readPixels(info, outputBuffer<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>bits, outputBuffer<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">.</span>stride<span class="hljs-subst" style="color: rgb(0, 0, 0); box-sizing: border-box;">*</span><span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">4</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*ARGB*/</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li></ul><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li></ul>

Skia创建GPU上下文时,其Surface并不关联Android里面的显示系统,因此是无法直接渲染上屏的,在绘制完成之后需要额外的一次readPixels,也即内存拷贝,这使其

不适合做实时渲染

。只是在做比较复杂的效果,如Bicubic插值、光照、模糊时,可以用一用。


关于 Skia的特效,可以看 include/effects 和 src/effects 目录下面的代码,这里面是CPU方式实现的。由于很少见用到,之前并没有介绍。


对应的gpu特效实现见 include/gpu 和 src/gpu/effects目录下的代码。



三、流程与框架

SkGpu的一次绘制基本流程如下:

SkCanvasSkGpuDeviceGrContextGrDrawTargetGrGpuGrGLInterface

SkCanvas:如之前章节所述,下发命令,保留Layer


SkGpuDevice:处理退化情况,将SkPaint转化为GrPaint


GrContext:构建形(顶点Vertex)且处理抗锯齿,描述色(GrDrawState)


GrDrawTarget:描述绘图目标,起接口作用,将材料打包为Drawinfo,由子类执行 onDraw方法。


GrGpu:设定Shader、设定顶点,调用OpenGL接口渲染


GrGLInterFace:由于各个厂商支持的OpenGL标准版本有所不同,且一些厂商会增加一些接口,这一层做一个API适配,这样平台相关的代码就集中在此层级。