日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

NanoVG 优化笔记:性能提高5倍的秘密

發布時間:2024/5/14 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NanoVG 优化笔记:性能提高5倍的秘密 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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倍以上。

五、綜合

綜合裁剪、反走樣和矩形,新增3個類型,進行特殊處理:

  • 快速填充無需裁剪的矩形:NSVG_SHADER_FAST_FILLCOLOR
  • 快速填充無需裁剪的圖片:NSVG_SHADER_FAST_FILLIMG
  • 快速用簡單顏色填充多邊形:NSVG_SHADER_FILLCOLOR

裁剪、反走樣和矩形可以組合更多類型,進行更精細的優化。但即使只作這三種情況處理,AWTK在Android平臺的整體性能已經有了3-5倍的提高,demoui在我們測試的機型上,都穩穩的保持在60FPS,沒有必要為了性能增加它的復雜度了。

詳細情況和完整代碼請參考AWTK

轉載于:https://my.oschina.net/u/3979680/blog/3084047

總結

以上是生活随笔為你收集整理的NanoVG 优化笔记:性能提高5倍的秘密的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。