[√]如何拿到label的绘制批次

简介: [√]如何拿到label的绘制批次

label的绘制是在onDraw完成的

void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
    if (!_shadowEnabled && (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP))
    {
    }
    else
    {
        // 项目中使用的是TTF,label绘制走的是CustomCommand
        _customCommand.init(_globalZOrder, transform, flags);
        _customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated);
        renderer->addCommand(&_customCommand);
    }
}
// 处理渲染命令时,碰到CustomCommand的逻辑
void Renderer::processRenderCommand(RenderCommand* command)
{
    if(RenderCommand::Type::CUSTOM_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<CustomCommand*>(command);
        CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_CUSTOM_COMMAND");
        cmd->execute();// 执行绑定的渲染函数: cmd.func()
    }
}
// 进行label的绘制
void Label::onDraw(const Mat4& transform, bool /*transformUpdated*/)
{
    // 最终的绘制结果都会放在BatchNode里面
    for (auto&& batchNode : _batchNodes)
    {
        //          ↓ 借用了Atlas进行渲染
        batchNode->getTextureAtlas()->drawQuads();
    }
}
void TextureAtlas::drawQuads()
{
    this->drawNumberOfQuads(_totalQuads, 0);
}
void TextureAtlas::drawNumberOfQuads(ssize_t numberOfQuads, ssize_t start)
{
    // 完成实际的绘制
    glDrawElements(GL_TRIANGLES, (GLsizei)numberOfQuads*6, GL_UNSIGNED_SHORT, (GLvoid*) (start*6*sizeof(_indices[0])));
    CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,numberOfQuads*6);// 告诉统计增加绘制批次
}

经过以上的分析,那如何知道label的绘制批次呢?也就是label要保存最后调用统计时的dcIndex

label.batchNode身上有BatchCommand,但是label没有使用这个BatchCommand,这个BatchCommand是给Sprite准备的

void Renderer::processRenderCommand(RenderCommand* command)
{
    if(RenderCommand::Type::BATCH_COMMAND == commandType)
    {
        flush();
        auto cmd = static_cast<BatchCommand*>(command);
        CCGL_DEBUG_INSERT_EVENT_MARKER("RENDERER_BATCH_COMMAND");
        cmd->execute();
    }
}
void BatchCommand::execute()
{
    // Set material
    _shader->use();
    _shader->setUniformsForBuiltins(_mv);
    GL::bindTexture2D(_textureID);
    GL::blendFunc(_blendType.src, _blendType.dst);
    // Draw 可以观察到最后还是使用Atlas进行了渲染
    _textureAtlas->drawQuads();
}

既然都是借用了Atlas,那么这个dcIndex还是存在Atlas上比较合适,在执行Atlas->drawQuads()的时候,更新dcIndex,然后统一从Atlas获取这个dcIndex就可以了。

所以最后label获取dcIndex的逻辑就变成这样子

// 我一直认为是基于RenderCommand的,所以架构是基于RenderCommand
cocos2d::RenderCommand* Label::getRenderCommand()
{
    SpriteBatchNode* batchNode = this->_batchNodes.front();
    // 这样就能直接拿到laebl的dcIndex,但是很明显和架构不符
    batchNode->getTextureAtlas()->drawCallIndex;
}

很明显,我们需要将这个dcIndex放到label绑定的CustomCommand。

能直接想到的就是,如果要更新cmd.dcIndex,只能从调用cmd.func的地方下手,2个思路:

// 方式1:在执行渲染回调的时候`cmd.func()`,通过返回值更新cmd.dcIndx
// 这种方式能直观的看到dcIndex的修改逻辑,但是改动func会带来一些未知问题
cmd.dcIndex = cmd.func(); 
// 方式2:传递指针,让fuc内部修改cmd.dcIndex
// 因为fuc可能实现差异很大,所以需要func内部自己处理dcIndex的赋值
cmd.func(cmd); 

这里我采用第二种方式,仔细分析后,我发现

void Label::onDraw(const Mat4& transform, bool /*transformUpdated*/)
{
    // 在执行这个Render的时候,很明显就是_customCommand导致的
    // .... 渲染逻辑
    // 在最后,修改_customCommand的dcIndex即可
    if (_batchNodes.size() > 0)
    {
        unsigned int dcIndex = _batchNodes.front()->getTextureAtlas()->drawCallIndex;
        this->_customCommand.setDrawCallIndex(dcIndex);
    }
}
cocos2d::RenderCommand* Label::getRenderCommand()
{
    if (!_shadowEnabled && (_currentLabelType == LabelType::BMFONT || _currentLabelType == LabelType::CHARMAP))
    {
        return &(this->_quadCommand);
    }
    else
    {
        unsigned int size = this->_batchNodes.size();
        if (size == 0)
        {
            return nullptr;
        }
        else
        {
            return &(this->_customCommand); // 在draw的时候已经正确赋值dcIndex了
        }
    }
    return Node::getRenderCommand();
}

至此就完美拿到ttf的label绘制批次了

std::function绑定的小细节

//                                                                                      ↓可变参数绑定的方式
#define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
//  _customCommand.func的函数是无参的: std::function<void()> func;
_customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated);
// 但是绑定的onDraw函数是有参数的,原因是可变参数绑定
void Label::onDraw(const Mat4& transform, bool /*transformUpdated*/){}
// 可以看到其他的onDraw函数参数类型不同
void DrawNode::onDraw(const Mat4 &transform, uint32_t /*flags*/) 

std::bind 是 C++ 标准库中的函数对象绑定器,用于将函数和其参数绑定为一个可调用的对象。

在给定的代码片段中,std::bind 函数被使用,它接受一个函数指针或可调用对象(如函数、函数对象、成员函数等),并将其与一组参数绑定在一起。这些参数可以是直接传递的值,也可以是占位符(_1_2_3 等)。

  • &__selector__ 是要绑定的函数或可调用对象的名称(或地址)。
  • __target__ 是绑定的目标对象。
  • ##__VA_ARGS__ 是可变参数,可以是一系列要绑定的参数。

这样的绑定操作旨在创建一个可调用的对象,使得在调用该对象时,被绑定的函数会以预先指定的目标对象和参数进行调用。这种方式在一些场景中非常有用,例如回调函数、事件处理等。

目录
相关文章
|
7天前
|
人工智能 JSON 机器人
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
本文带你零成本玩转OpenClaw:学生认证白嫖6个月阿里云服务器,手把手配置飞书机器人、接入免费/高性价比AI模型(NVIDIA/通义),并打造微信公众号“全自动分身”——实时抓热榜、AI选题拆解、一键发布草稿,5分钟完成热点→文章全流程!
10921 83
让龙虾成为你的“公众号分身” | 阿里云服务器玩Openclaw
|
6天前
|
人工智能 IDE API
2026年国内 Codex 安装教程和使用教程:GPT-5.4 完整指南
Codex已进化为AI编程智能体,不仅能补全代码,更能理解项目、自动重构、执行任务。本文详解国内安装、GPT-5.4接入、cc-switch中转配置及实战开发流程,助你从零掌握“描述需求→AI实现”的新一代工程范式。(239字)
4092 129
|
2天前
|
人工智能 Kubernetes 供应链
深度解析:LiteLLM 供应链投毒事件——TeamPCP 三阶段后门全链路分析
阿里云云安全中心和云防火墙已在第一时间上线相关检测与拦截策略!
1381 5
|
3天前
|
人工智能 自然语言处理 供应链
【最新】阿里云ClawHub Skill扫描:3万个AI Agent技能中的安全度量
阿里云扫描3万+AI Skill,发现AI检测引擎可识别80%+威胁,远高于传统引擎。
1283 3
|
12天前
|
人工智能 JavaScript API
解放双手!OpenClaw Agent Browser全攻略(阿里云+本地部署+免费API+网页自动化场景落地)
“让AI聊聊天、写代码不难,难的是让它自己打开网页、填表单、查数据”——2026年,无数OpenClaw用户被这个痛点困扰。参考文章直击核心:当AI只能“纸上谈兵”,无法实际操控浏览器,就永远成不了真正的“数字员工”。而Agent Browser技能的出现,彻底打破了这一壁垒——它给OpenClaw装上“上网的手和眼睛”,让AI能像真人一样打开网页、点击按钮、填写表单、提取数据,24小时不间断完成网页自动化任务。
2719 6
|
5天前
|
人工智能 机器人 API
从零搭建OpenClaw多智能体系统:部署、API配置+飞书多机器人管理手册
在团队协作场景中,单一AI智能体往往难以满足多部门、多场景的差异化需求——研发团队需要代码专家,运营团队需要内容策划助手,客服团队需要高效问答机器人,若所有需求都由同一个智能体承接,不仅会导致响应质量下降,还可能出现记忆混乱、权限失控等问题。2026年,OpenClaw(曾用名Clawdbot)的多Agent架构完美解决了这一痛点,通过“多飞书机器人账号+多独立Agent+路由绑定”的配置,可实现不同机器人对应专属AI大脑,各司其职、精准响应。
1345 1