Cocos2dx多重纹理技术细节逻辑

357 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

很久之前,我在cocos2dx的基础上,实现了一个多纹理合批的功能。

在后续项目使用中,也发现了一些没有注意到的细节问题,导致了一些很隐晦的bug

问题:渲染错误

因为我是将纹理ID存储在顶点的z中,所以直接排查顶点数据就发现了问题。

void Renderer::fillVerticesAndIndices(const TrianglesCommand* cmd)
{
    // fill vertex, and convert them to world coordinates
    const Mat4& modelView = cmd->getModelView();
    for(ssize_t i=0; i < cmd->getVertexCount(); ++i)
    {
        Vec3* point = &(_verts[i + _filledVertex].vertices);
        modelView.transformPoint(point);
    }
}

因为提交的顶点数据是在CPU进行了矩阵转换,直接观察经过矩阵转换后的顶点数据,发现Z发生了变化

看来还不能在sprite顶点组装阶段将Z写入到顶点信息中,因为经过矩阵计算,z会变化,解决办法只需要在transformPoint之后再次写入z就行了,很暴力但是很见效。

目前也没想到有啥更好的方案,因为cocos2dx 不支持自定义vertices数据的解析,因为底层解析顶点数据的结构是定死的(V3F_C4B_T2F)

如果MVP矩阵计算放在GPU,估计这个问题会更加隐晦,其实最初我就是从shader开始排查的。

gl_Position = MVP * a_position

问题:读取位置 0xCDCDCDCD 时发生访问冲突

直接表现为:

0x0F6F395E (vcruntime140d.dll)处(位于 client_tank5.exe 中)引发的异常: 0xC0000005:

image.png image.png

image.png 网上大部分都是说野指针的问题,但是我不知道为什么会产生野指针。

通过IsBadReadPtr判断野指针

#include <iostream>
#include <windows.h>
int main(int, char**) {

    int* p = new int;
    delete p;
    p = nullptr;
    if (IsBadReadPtr(p, sizeof(int)) == TRUE)
    {
        std::cout << "bad ptr";
        return 1;
    }
    std::cout << *p;
}

image.png

我尝试着这么做,去排查野指针的问题,但是我这个野指针直接跳过了判断,看来还得找为啥会产生野指针。

多重纹理置灰shader导致的bug

最终排查TrianglesCommand,发现它是在getVertices()的时候返回的顶点指针是野指针,排查思路就从这里切入:

 struct Triangles
{
    /**Vertex data pointer.*/
    V3F_C4B_T2F* verts;
    /**Index data pointer.*/
    unsigned short* indices;
    /**The number of vertices.*/
    int vertCount;
    /**The number of indices.*/
    int indexCount;
};

classs TrianglesCommand{
   Triangles _triangles;
}

问题就出在这里,TrianglesCommand_triangles是个结构体,不是指针,而Triangles并没有对结构体就行初始化,所以直接使用

this->_triangles->verts;

返回的当然是野指针,坑就在这里。

因为cocos默认的渲染会重新赋值_triangles的,所以不存在这个问题,很明显我没有更新_triangles

为什么没有更新?

因为node当前的program不能转换为多纹理program不能转换,why?

因为node进行了置灰操作,更新了program!

问题终于找到了,怎么解决嘞?

当发现program无法正常转换时,就使用原来的program逻辑进行初始化triangleCommand。

总结

一定要对值进行初始化,非常重要!