3.x native渲染分析

115 阅读3分钟

发现之前写的自定义渲染,在win32环境下竟然没有生效

发现整个的batcher-2d.update竟然没有生效,在jsb环境下直接返回了

68794bd5fa9e2329bab452fd523742a.png

追到最后,整个渲染驱动还是来自batch2d

image.png

这个frameMove就是来自jsb层,

还是得先查顶点数据,老办法,从结果倒推。

这里为了调试方便,我构建了win32版本,理论上在c++层的渲染应该是和android大致相同的,只要我们看明白了win32,基本上android也同理。

从glDrawElements下手

思路和之前一样,无论engine的渲染多复杂,最终到opengl这边,他都会使用函数glDrawElements

image.png

发现gles2和gles3中都有,尝试下个断点,就会发现到底使用的是哪个,我这里断点到了gles3里面

因为我们的场景上只有一个Sprite,所以提交到glDrawElements的参数肯定是如下结果:

image.png 这里你需要了解下glDrawElements函数对应的参数,这里不再展开。

图元glPrimitive是4,和gl里面的定义是一致的

#define GL_TRIANGLES                      0x0004

顶点数量是6?为啥是6?2个三角形,6个顶点,所以是6!

image.png

这里检查下参数仅仅是为了二次确认我们的确找到了想要的断点。

顺着drawInfo开始往上推

image.png

走到这里,我们发现数据好像是来自Message,从Agent可以看出来是一个代理。

#define ENQUEUE_MESSAGE_2(queue, MessageName,                      \
                          Param1, Value1,                          \
                          Param2, Value2,                          \
                          Code)                                    \
    {                                                              \
        using Type1 = typename std::decay<decltype(Value1)>::type; \
        using Type2 = typename std::decay<decltype(Value2)>::type; \
                                                                   \
        class MessageName final : public Message {                 \
        public:                                                    \
            MessageName(                                           \
                Type1 In##Param1, Type2 In##Param2)                \
            : Param1(std::move(In##Param1)),                       \
              Param2(std::move(In##Param2)) {                      \
            }                                                      \
            void execute() override {                              \
                Code                                               \
            }                                                      \
            char const *getName() const noexcept override {        \
                return (#MessageName);                             \
            }                                                      \
                                                                   \
        private:                                                   \
            Type1 Param1;                                          \
            Type2 Param2;                                          \
        };                                                         \
        WRITE_MESSAGE(queue, MessageName, (Value1, Value2))        \
    }

用到了c++模版,的确有点晦涩难懂。到这里我们也不需要深究,可以这么理解,通过消息机制保证的数据的安全和有效性

void CommandBufferAgent::draw(const DrawInfo &info) {
    // 在这里派注册一个消息,当消息队列轮训到这个消息时,就执行回调
   ENQUEUE_MESSAGE_2(
       _messageQueue, CommandBufferDraw,
       actor, getActor(),
       info, info,
       {
           // 所以本质上info信息还是来自draw的参数
           actor->draw(info);
       });
}

继续追踪

image.png

再顺着调用栈往上走,发现数据是来自batch->_drawInfo

所以焦点就转向drawInfo的赋值地方,顺着找,发现只有这1个地方,再断点看下何时赋值。

void DrawBatch2D::setInputAssembler(gfx::InputAssembler *ia) {
    _inputAssembler = ia;
    _drawInfo = _inputAssembler->getDrawInfo();
}

image.png

ia走的是下边的逻辑,那么这个ia到底是怎么来的呢

UIMeshBuffer* currMeshBuffer = drawInfo->getMeshBuffer();// 设置meshBuffer也来自jsb
ia = currMeshBuffer->requireFreeIA(getDevice());

gfx::InputAssembler* UIMeshBuffer::requireFreeIA(gfx::Device* device) {
    return createNewIA(device);
}

gfx::InputAssembler* UIMeshBuffer::createNewIA(gfx::Device* device) {
    // 到这里直接返回了,说明drawInfo->getMeshBuffer()里面早就有值了
    if (!_ia) {
        uint32_t vbStride = _vertexFormatBytes;
        uint32_t ibStride = sizeof(uint16_t);

        gfx::InputAssemblerInfo iaInfo = {};
        _vb = device->createBuffer({
            gfx::BufferUsageBit::VERTEX | gfx::BufferUsageBit::TRANSFER_DST,
            gfx::MemoryUsageBit::DEVICE | gfx::MemoryUsageBit::HOST,
            vbStride * 3,
            vbStride,
        });
        _ib = device->createBuffer({
            gfx::BufferUsageBit::INDEX | gfx::BufferUsageBit::TRANSFER_DST,
            gfx::MemoryUsageBit::DEVICE | gfx::MemoryUsageBit::HOST,
            ibStride * 3,
            ibStride,
        });

        iaInfo.attributes = _attributes;
        iaInfo.vertexBuffers.emplace_back(_vb);
        iaInfo.indexBuffer = _ib;
        // 来自这里,有点类似初始化的操作
        _ia = device->createInputAssembler(iaInfo);
    }

    return _ia;
}

void Batcher2d::fillBuffersAndMergeBatches() {
    size_t index = 0;
    for (auto* rootNode : _rootNodeArr) {
        // _batches will add by generateBatch
        walk(rootNode, 1);
        generateBatch(_currEntity, _currDrawInfo);// 我发现currDrawInfo为null

        auto* scene = rootNode->getScene()->getRenderScene();
        size_t const count = _batches.size();
        for (size_t i = index; i < count; i++) {
            scene->addBatch(_batches.at(i));
        }
        index = count;
    }
}

node->userData()

奇了怪了,我写的那个自定义渲染组件,竟然c++层获取不到entry,只有获取到entity,才能执行handleDrawInfo

ad8a1907d0b9caab3b709f11067df63.png

看着像是jsb层在在设置这个userData

0ae890df43d2ced686f84ad8a2a10b8.png

对应的ts层代码在这里

image.png

看到是在onLoad里面设置,我发现是我的代码中没有调用super.onLoad导致的bug