cocoscreator 官方截图部分高端安卓机特别卡,大佬有什么优化方法

  • Creator 版本:2.0.4

  • 目标平台: h5

下图代码,基于官方代码小修改了下,普通手机和苹果手机都很流畅,反而比较高端点的安卓机,小米6一加5等会特别特别卡,甚至会卡停音乐。


capture-web.rar (1.8 KB)

2赞

对比过不同手机渲染方式,都是webgl的渲染方式(除了苹果咯),也改过opengl的渲染方式还是依旧卡顿。

真的卡顿巨型严重好几秒的那种,奇怪的是low点的手机反而不会卡,用同样的手机测白鹭的截图一点也不会卡顿。

刚刚试了升级cocoscreator到2.07还是一样的卡。

大家都没遇到过这种问题么,截图这种需求挺常见,不然好多项目都用不了cocos了:sob:

:joy:不好意思,没有很明白是什么意思,能详细说下么,我这里给renderTexture是设置的this.node的高宽呀。

我之前的表述不是问题所在,实际问题出现在this.node.width 和 this.node.height这两位数是浮点数值
texture.readPixels()获取到的像素数组元素是非常多的。

let start = srowwidth4;
for (let i = 0; i < rowBytes; i++) {
imageData.data[i] = data[start+i];
//这里由于浮点数的下标所以这个元素是undefined的,双重for循环下imageData.data的值一会变成undefined,一会变成0,已造成运算压力。
}

建议向下取整:let start = Math.floor(srowwidth4);

2赞

改过来了,但是并未改善,这个奇怪的地方是部分高端的安卓机会卡,其他的手机其实都很快。
感觉是这里绘图导致的,我尝试直接截取canvas不绘制就一点都不卡。

我一会再安卓上测试看下

多谢!截的图片越大卡顿越明显,我这边已知会卡的手机有 一加6,魅蓝Note6,小米5,还有一个华为的忘记什么型号了。

http://game.flyh5.cn/resources/game/hjl_game/2018/12/aiaitie/index.html
这个是我现在做的你可以试试

我在小米 MIX 2S上测试没有出现问题,在安卓高版本和低版本的机型截图耗时都差不多

不是安卓版本的问题,是手机类型,很明显的卡顿好几秒的那种仅仅只是手机不同,大佬可以试试我说的那几款手机。

遇到过同样的问题,顶了一下旧贴好像也没有反应,在关联一下

https://forum.cocos.com/t/cocos-creator2-0/66023/3

1赞

我也遇到过同样问题,以下是直接的部分分析见解(主要针对FB Instant Game)

耗时分析

截图耗时主要有几个地方(微信好像不用第二、三步的样子)

  1. 摄像机渲染耗时,视乎节点内容多少
  2. 将截图内容写入到CanvasRenderingContext2D的过程,视乎写入数据到Canvas2d时的方法不同,耗时也不同
  3. canvas.toDataUrl 方法
    • 部分机型可以在这里耗上1200ms,因为js为单线程,所谓异步操作也只是插入到队列中比较前的位置,可以立即执行,但是如果上一个待执行的任务要占用很久,比如1200ms,那么这期间所有内容都会卡死,比如UI的Loading菊花不会转了,音乐暂停了之类

写入图像到 CanvasRenderingContext2D 的不同方法

其中,第2步,参考两种对比写法

// 接下来就可以对这些数据进行操作了
// 创建一个新的画布
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
canvas.width = width;
canvas.height = height;

// 将刚刚的渲染数据渲染到新的画布上
// 图片用32bit位表示一个像素,所以一行有width个像素,一共是width*32/8bit = width*4字节
let rowBytes = width * 4;
let s3 = new Date().getTime();

// 依次读取图片里的每行数据,放入到ctx中。
for (let row = 0; row < height; row++) {
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // DEMO 写法(高性能,兼容性好)

    // 设置开始行为最下面那行
    // 因为 RenderTexture 得到的纹理是上下翻转的,所以需要旋转回来
    // 在这里几位从最下面的行开始读取图片数据
    let srow = height - 1 - row;

    // 从图片的所有数据中,获取一行的图片字节数据
    let oneLineImageData = new Uint8ClampedArray(textureData.buffer, Math.floor(srow * width * 4), rowBytes);

    // 设置图片数据对象
    // 设置一个一行的图片,所以就是 width, 1
    let imageData = new ImageData(oneLineImageData, width, 1);

    ctx.putImageData(imageData, 0, row);

    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // // 官网文档 写法(在iPhone5机器上 ctx.createImageData 得到的对象是不能直接通过读取 .data 属性赋值的, 因为该属性为只读,所以是不能写入到数据导致没有截图数据)

    // // 设置开始行为最下面那行
    // // 因为 RenderTexture 得到的纹理是上下翻转的,所以需要旋转回来
    // // 在这里几位从最下面的行开始读取图片数据
    // let srow = height - 1 - row;

    // // 设置图片数据对象
    // // 设置一个一行的图片,所以就是 width, 1
    // let imageData = ctx.createImageData(width, 1);

    // // 从最下面那行的第一个字节开始读
    // // 下标是从0开始, srow* width * 4 则刚刚好为最下面那行的第一个字节
    // let start = srow * width * 4;
    // for (let i = 0; i < rowBytes; i++) {
    //     imageData.data[i] = data[start + i];
    // }
    // ctx.putImageData(imageData, 0, row);
    // if (row == 0) {
    //     console.log(`imageData:`);
    //     console.log(imageData);
    // }
}
if (CC_DEBUG) {
    cc.log(`生成截图耗时:  ${new Date().getTime() - s3} ms`);
}

同一个手机

  1. 用 Unit8ClampedArray 最快可以100ms
  2. 用 createImageData 方法需要1200ms

没仔细测试,只是简单对比了手头上的几台手机,其他手机情况未知

Canvas.toDataUrl 方法

这个是MDN统一方法,没解的样子,除非我们自己重写一遍,不然就是完全阻塞,想到一个绕一点的方法,截图数据分几次读取,比如截好图之后,分多次去读取数据,每次读取数据耗时控制在8ms左右,一帧还剩下8ms交给Loading菊花去转,或者音乐播放之类

3赞

感谢大佬,刚刚用大佬修改了下,速度的确快了不少,还没上真机测试等会看看能不能解决部分手机卡的问题。

刚刚上真机试了试,可惜还是卡顿,但是已经快了大约40%了。同时对比了调整了Canvas.toDataUrl的质量到0.3和默认对真机的影响,对卡的那几种手机影响几乎感觉不到。感觉不是这个方向的问题,:joy:怕是手机渲染硬件导致的问题

补充一点,一开始设置截图大小的时候就要向下取整截图尺寸,这是因为截图尺寸的宽高有可能是浮点数,那么读出来的内容就可能出现越界问题,即便后面 start 那里向下取整调整回来,也会有问题,我也是找了好久才知道,Anyway

using the following code, you may see the screenshot on iPhone 5 like this:

// Getting view rect size.
let visibleSize = cc.view.getVisibleSize();
let width =visibleSize.width;
let height = visibleSize.height;

// 新建一个 RenderTexture,并且设置 camera 的 targetTexture 为新建的 RenderTexture,这样 camera 的内容将会渲染到新建的 RenderTexture 中。
let renderTexture = new cc.RenderTexture();
// 初始化纹理大小,如果截图内容中不包含 Mask 组件,可以不用传递第三个参数
renderTexture.initWithSize(width, height, cc.game._renderContext.STENCIL_INDEX8);
this.camera.targetTexture = renderTexture;

after using Math.floor , everything is fine:

// Getting view rect size.
let visibleSize = cc.view.getVisibleSize();
let width = Math.floor(visibleSize.width);
let height = Math.floor(visibleSize.height);

// 新建一个 RenderTexture,并且设置 camera 的 targetTexture 为新建的 RenderTexture,这样 camera 的内容将会渲染到新建的 RenderTexture 中。
let renderTexture = new cc.RenderTexture();
// 初始化纹理大小,如果截图内容中不包含 Mask 组件,可以不用传递第三个参数
renderTexture.initWithSize(width, height, cc.game._renderContext.STENCIL_INDEX8);
this.camera.targetTexture = renderTexture;

4赞

剩下的应该是 Canvas.toDataUrl 那块的卡顿了,你可以测试一下那里的耗时,如果真的是这里耗时了,你可以参考下我上面说到的应对思路,就是比较绕。。。

1赞

好的多谢!