[√]如何拿到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__ 是可变参数,可以是一系列要绑定的参数。

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

目录
相关文章
|
6天前
|
云安全 监控 安全
|
11天前
|
机器学习/深度学习 人工智能 自然语言处理
Z-Image:冲击体验上限的下一代图像生成模型
通义实验室推出全新文生图模型Z-Image,以6B参数实现“快、稳、轻、准”突破。Turbo版本仅需8步亚秒级生成,支持16GB显存设备,中英双语理解与文字渲染尤为出色,真实感和美学表现媲美国际顶尖模型,被誉为“最值得关注的开源生图模型之一”。
1240 7
|
5天前
|
人工智能 安全 前端开发
AgentScope Java v1.0 发布,让 Java 开发者轻松构建企业级 Agentic 应用
AgentScope 重磅发布 Java 版本,拥抱企业开发主流技术栈。
369 10
|
3天前
|
人工智能
自动化读取内容,不会写爆款的普通人也能产出好内容,附coze工作流
陌晨分享AI内容二创工作流,通过采集爆款文案、清洗文本、智能改写,实现高效批量生产。五步完成从选题到输出,助力内容创作者提升效率,适合多场景应用。
213 104
|
17天前
|
人工智能 Java API
Java 正式进入 Agentic AI 时代:Spring AI Alibaba 1.1 发布背后的技术演进
Spring AI Alibaba 1.1 正式发布,提供极简方式构建企业级AI智能体。基于ReactAgent核心,支持多智能体协作、上下文工程与生产级管控,助力开发者快速打造可靠、可扩展的智能应用。
1200 43
|
17天前
|
人工智能 前端开发 算法
大厂CIO独家分享:AI如何重塑开发者未来十年
在 AI 时代,若你还在紧盯代码量、执着于全栈工程师的招聘,或者仅凭技术贡献率来评判价值,执着于业务提效的比例而忽略产研价值,你很可能已经被所谓的“常识”困住了脚步。
996 84
大厂CIO独家分享:AI如何重塑开发者未来十年
|
13天前
|
存储 自然语言处理 测试技术
一行代码,让 Elasticsearch 集群瞬间雪崩——5000W 数据压测下的性能避坑全攻略
本文深入剖析 Elasticsearch 中模糊查询的三大陷阱及性能优化方案。通过5000 万级数据量下做了高压测试,用真实数据复刻事故现场,助力开发者规避“查询雪崩”,为您的业务保驾护航。
595 32