您好,登錄后才能下訂單哦!
本篇內容主要講解“NanoVG優化方法是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“NanoVG優化方法是什么”吧!
nanovg正如其名稱所示的那樣,是一個非常小巧的矢量繪圖函數庫。相比cairo和skia的數十萬行代碼,nanovg不足5000行的C語言代碼,稱為nano也是名副其實了。nanovg的設計、接口和代碼質量都堪稱典范,唯一美中不足的就是性能不太理想。特別是在Android的低端機型和大屏幕的機型上,一個簡單的界面每秒只能畫十幾幀。最近我把AWTK移植到Android上時,就碰到了這個尷尬的問題。
經過優化之后,AWTK在低端機型上,整體渲染性能有了3到5倍的提升。這里做個筆記,供有需要的朋友參考。
nanovg的性能瓶頸在于片段著色器(fragment shader),片段著色器可以認為是為GPU提供的一個回調函數,該回調函數在處理每個像素時被調用,在每一幀繪制時都會執行數百萬次,可見該函數的對性能的影響是很大的。
我們先看看nanovg的片段著色器(fragment shader)代碼:
static const char* fillFragShader = "#ifdef GL_ES\n" "#if defined(GL_FRAGMENT_PRECISION_HIGH) || defined(NANOVG_GL3)\n" " precision highp float;\n" "#else\n" " precision mediump float;\n" "#endif\n" "#endif\n" "#ifdef NANOVG_GL3\n" "#ifdef USE_UNIFORMBUFFER\n" " layout(std140) uniform frag {\n" " mat3 scissorMat;\n" " mat3 paintMat;\n" " vec4 innerCol;\n" " vec4 outerCol;\n" " vec2 scissorExt;\n" " vec2 scissorScale;\n" " vec2 extent;\n" " float radius;\n" " float feather;\n" " float strokeMult;\n" " float strokeThr;\n" " int texType;\n" " int type;\n" " };\n" "#else\n" // NANOVG_GL3 && !USE_UNIFORMBUFFER " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" "#endif\n" " uniform sampler2D tex;\n" " in vec2 ftcoord;\n" " in vec2 fpos;\n" " out vec4 outColor;\n" "#else\n" // !NANOVG_GL3 " uniform vec4 frag[UNIFORMARRAY_SIZE];\n" " uniform sampler2D tex;\n" " varying vec2 ftcoord;\n" " varying vec2 fpos;\n" "#endif\n" "#ifndef USE_UNIFORMBUFFER\n" " #define scissorMat mat3(frag[0].xyz, frag[1].xyz, frag[2].xyz)\n" " #define paintMat mat3(frag[3].xyz, frag[4].xyz, frag[5].xyz)\n" " #define innerCol frag[6]\n" " #define outerCol frag[7]\n" " #define scissorExt frag[8].xy\n" " #define scissorScale frag[8].zw\n" " #define extent frag[9].xy\n" " #define radius frag[9].z\n" " #define feather frag[9].w\n" " #define strokeMult frag[10].x\n" " #define strokeThr frag[10].y\n" " #define texType int(frag[10].z)\n" " #define type int(frag[10].w)\n" "#endif\n" "\n" "float sdroundrect(vec2 pt, vec2 ext, float rad) {\n" " vec2 ext2 = ext - vec2(rad,rad);\n" " vec2 d = abs(pt) - ext2;\n" " return min(max(d.x,d.y),0.0) + length(max(d,0.0)) - rad;\n" "}\n" "\n" "// Scissoring\n" "float scissorMask(vec2 p) {\n" " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" " sc = vec2(0.5,0.5) - sc * scissorScale;\n" " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" "}\n" "#ifdef EDGE_AA\n" "// Stroke - from [0..1] to clipped pyramid, where the slope is 1px.\n" "float strokeMask() {\n" " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" "}\n" "#endif\n" "\n" "void main(void) {\n" " vec4 result;\n" " float scissor = scissorMask(fpos);\n" "#ifdef EDGE_AA\n" " float strokeAlpha = strokeMask();\n" " if (strokeAlpha < strokeThr) discard;\n" "#else\n" " float strokeAlpha = 1.0;\n" "#endif\n" " if (type == 0) { // Gradient\n" " // Calculate gradient color using box gradient\n" " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" " vec4 color = mix(innerCol,outerCol,d);\n" " // Combine alpha\n" " color *= strokeAlpha * scissor;\n" " result = color;\n" " } else if (type == 1) { // Image\n" " // Calculate color fron texture\n" " vec2 pt = (paintMat * vec3(fpos,1.0)).xy / extent;\n" "#ifdef NANOVG_GL3\n" " vec4 color = texture(tex, pt);\n" "#else\n" " vec4 color = texture2D(tex, pt);\n" "#endif\n" " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" " if (texType == 2) color = vec4(color.x);" " // Apply color tint and alpha.\n" " color *= innerCol;\n" " // Combine alpha\n" " color *= strokeAlpha * scissor;\n" " result = color;\n" " } else if (type == 2) { // Stencil fill\n" " result = vec4(1,1,1,1);\n" " } else if (type == 3) { // Textured tris\n" "#ifdef NANOVG_GL3\n" " vec4 color = texture(tex, ftcoord);\n" "#else\n" " vec4 color = texture2D(tex, ftcoord);\n" "#endif\n" " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" " if (texType == 2) color = vec4(color.x);" " color *= scissor;\n" " result = color * innerCol;\n" " }\n" "#ifdef NANOVG_GL3\n" " outColor = result;\n" "#else\n" " gl_FragColor = result;\n" "#endif\n" "}\n";
它的功能很完整也很復雜,裁剪和反走樣都做了處理。仔細分析之后,我發現了幾個性能問題:
簡單顏色填充和漸變顏色填充使用了相同的代碼:
" if (type == 0) { // Gradient\n" " // Calculate gradient color using box gradient\n" " vec2 pt = (paintMat * vec3(fpos,1.0)).xy;\n" " float d = clamp((sdroundrect(pt, extent, radius) + feather*0.5) / feather, 0.0, 1.0);\n" " vec4 color = mix(innerCol,outerCol,d);\n" " // Combine alpha\n" " color *= strokeAlpha * scissor;\n" " result = color;\n"
簡單顏色填充只需一條指令,而漸變顏色填充則需要數十條指令。這兩種情況重用一段代碼,會讓簡單顏色填充慢10倍以上。
把顏色填充分成以下幾種情況,分別進行優化:
矩形簡單顏色填充。
對于無需裁剪的矩形(這是最常見的情況),直接賦值即可,性能提高20倍以上。
" if (type == 5) { //fast fill color\n" " result = innerCol;\n"
通用多邊形簡單顏色填充。
去掉漸變的采樣函數,性能會提高一倍以上:
" } else if(type == 7) { // fill color\n" " strokeAlpha = strokeMask();\n" " if (strokeAlpha < strokeThr) discard;\n" " float scissor = scissorMask(fpos);\n" " vec4 color = innerCol;\n" " color *= strokeAlpha * scissor;\n" " result = color;\n"
漸變顏色填充(只占極小的部分)。
這種情況非常少見,還是使用之前的代碼。
平均情況,填充性能提高10倍以上!
對于文字而言,需要顯示的像素和不顯示的像素,平均算下來在1:1左右。
" } else if (type == 3) { // Textured tris\n" "#ifdef NANOVG_GL3\n" " vec4 color = texture(tex, ftcoord);\n" "#else\n" " vec4 color = texture2D(tex, ftcoord);\n" "#endif\n" " if (texType == 1) color = vec4(color.xyz*color.w,color.w);" " if (texType == 2) color = vec4(color.x);" " color *= scissor;\n" " result = color * innerCol;\n" " }\n"
如果顯示的像素和不顯示的像素都走完整的流程,會浪費調一半的時間。
當color.x < 0.02時直接跳過。
裁剪和反走樣放到判斷語句之后。
" } else if (type == 3) { // Textured tris\n" "#ifdef NANOVG_GL3\n" " vec4 color = texture(tex, ftcoord);\n" "#else\n" " vec4 color = texture2D(tex, ftcoord);\n" "#endif\n" " if(color.x < 0.02) discard;\n" " strokeAlpha = strokeMask();\n" " if (strokeAlpha < strokeThr) discard;\n" " float scissor = scissorMask(fpos);\n" " color = vec4(color.x);" " color *= scissor;\n" " result = color * innerCol;\n" " }\n"
字體渲染性能提高一倍!
反走樣的實現函數如下(其實我也不懂):
"float strokeMask() {\n" " return min(1.0, (1.0-abs(ftcoord.x*2.0-1.0))*strokeMult) * min(1.0, ftcoord.y);\n" "}\n"
與簡單的賦值操作相比,加上反走樣功能,性能會下降5-10倍。但是不加反走樣功能,繪制多邊形時邊緣效果比較差。不加不好看,加了又太慢,看起來是個兩難的選擇。
矩形填充是可以不用反走樣功能的。而90%以上的情況都是矩形填充。矩形填充單獨處理,一條指令搞定,性能提高20倍以上:
" if (type == 5) { //fast fill color\n" " result = innerCol;\n"
配合裁剪和矩形的優化,性能提高10倍以上。
裁剪放到Shader中雖然合理,但是性能就要大大折扣了。
"// Scissoring\n" "float scissorMask(vec2 p) {\n" " vec2 sc = (abs((scissorMat * vec3(p,1.0)).xy) - scissorExt);\n" " sc = vec2(0.5,0.5) - sc * scissorScale;\n" " return clamp(sc.x,0.0,1.0) * clamp(sc.y,0.0,1.0);\n" "}\n"
與簡單的賦值操作相比,加上裁剪功能,性能會下降10以上倍。但是不加裁剪功能,像滾動視圖這樣的控件就沒法實現,這看起來也是個兩難的選擇。
而90%以上的填充都是在裁剪區域的內部的,沒有必要每個像素都去判斷,放在Shader之外進行判斷即可。
static int glnvg__pathInScissor(const NVGpath* path, NVGscissor* scissor) { int32_t i = 0; float cx = scissor->xform[4]; float cy = scissor->xform[5]; float hw = scissor->extent[0]; float hh = scissor->extent[1]; float l = cx - hw; float t = cy - hh; float r = l + 2 * hw - 1; float b = t + 2 * hh - 1; const NVGvertex* verts = path->fill; for (i = 0; i < path->nfill; i++) { const NVGvertex* iter = verts + i; int x = iter->x; int y = iter->y; if (x < l || x > r || y < t || y > b) { return 0; } } return 1; }
配合裁剪和矩形的優化,性能提高10倍以上。
到此,相信大家對“NanoVG優化方法是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。