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

: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赞

好的多谢!

希望官方能关注下这个问题,毕竟这种常见需求非常多见,截全屏尚且这么卡了,截长图什么的都不敢想会多卡

没有报错吗?这边运行到微信上 报
Failed to execute ‘createImageData’ on ‘CanvasRenderingContext2D’: The source width is 0.
Error: Failed to execute ‘createImageData’ on ‘CanvasRenderingContext2D’: The source width is 0.

微信端的话调用微信那边的接口吧https://developers.weixin.qq.com/minigame/dev/api/Canvas.toTempFilePath.html

< 1分钟
大佬,可不可以研究下这个问题https://forum.cocos.com/t/cocoscreator2-0-9/75313/4

大佬 3.6版本 Unit8ClampedArray 生成的时候无法生成一行的数据 得到的是buffer完整的数据 这个有可能是什么原因?