当前位置: 首页 > news >正文

Filament引擎分析--command抽象设备API

1. 前言

Filament中使用了FrameGraph来管理渲染管线,需要准备两点:

  • 设备接口抽象:设备API抽象为Command
  • 资源抽象:使用虚拟资源,在实际用到时再创建,方便剔除无用资源

下面就围绕Filament中设备API抽象为Command代码部分做一个解读:

2. 代码分析

先贴一段创建顶点缓冲的接口调用堆栈:

[Inlined] filament::backend::CommandBase::CommandBase(void (*)(filament::backend::Driver &, filament::backend::CommandBase *, int *)) CommandStream.h:63
[Inlined] filament::backend::CommandType<void (filament::backend::Driver::*)(filament::backend::Handle<filament::backend::HwVertexBuffer>, unsigned char, unsigned char, unsigned int, std::__ndk1::array<filament::backend::Attribute, 16u>)>::Command<&filament::backend::Driver::createVertexBufferR(filament::backend::Handle<filament::backend::HwVertexBuffer>, unsigned char, unsigned char, unsigned int, std::__ndk1::array<filament::backend::Attribute, 16u>)>::Command<filament::backend::Handle<filament::backend::HwVertexBuffer>, unsigned char, unsigned char, unsigned int, std::__ndk1::array<filament::backend::Attribute, 16u>>(void (*)(filament::backend::Driver&, filament::backend::CommandBase*, int*), filament::backend::Handle<filament::backend::HwVertexBuffer>&&, unsigned char&&, unsigned char&&, unsigned int&&, std::__ndk1::array<filament::backend::Attribute, 16u>&&) CommandStream.h:154
[Inlined] filament::backend::CommandStream::createVertexBuffer(unsigned char, unsigned char, unsigned int, std::__ndk1::array<>) DriverAPI.inc:169
filament::FVertexBuffer::FVertexBuffer(filament::FEngine &, const filament::VertexBuffer::Builder &) VertexBuffer.cpp:185
[Inlined] utils::Arena::make<>(filament::FEngine &, const filament::VertexBuffer::Builder &) Allocator.h:647
[Inlined] filament::FEngine::create<>(filament::ResourceList<> &, const filament::FVertexBuffer::Builder &) Engine.cpp:680
filament::FEngine::createVertexBuffer(const filament::VertexBuffer::Builder &) Engine.cpp:690
filament::FEngine::init() Engine.cpp:277
filament::FEngine::create(filament::backend::Backend, filament::backend::Platform *, void *, const filament::Engine::Config *) Engine.cpp:110
[Inlined] FilamentTest::setupFilament() FilamentTest.cpp:98
FilamentTest::init() FilamentTest.cpp:68
boxing::xr::composer::StartBase::instance(ANativeWindow *, int, int) StartBase.h:263
[Inlined] native_OnDrawFrame::$_0::operator()() const JniImpl.cpp:100
[Inlined] std::__ndk1::__invoke<>(native_OnDrawFrame::$_0 &) type_traits:3874
[Inlined] std::__ndk1::__apply_functor<>(native_OnDrawFrame::$_0 &, std::__ndk1::tuple<> &, std::__ndk1::__tuple_indices<>, std::__ndk1::tuple<> &&) functional:2853
[Inlined] std::__ndk1::__bind::operator()<>() functional:2886
[Inlined] std::__ndk1::__invoke<>(std::__ndk1::__bind<> &) type_traits:3874
std::__ndk1::__packaged_task_func::operator()() future:1817
[Inlined] std::__ndk1::__packaged_task_function::operator()() const future:1994
std::__ndk1::packaged_task::operator()() future:2214
[Inlined] std::__ndk1::__function::__value_func::operator()() const functional:1884
[Inlined] std::__ndk1::function::operator()() const functional:2556
<lambda>::operator()() const ThreadPool.h:71
[Inlined] decltype(std::__ndk1::forward<boxing::core::ThreadPool::ThreadPool(unsigned int)::'lambda'()>(fp)()) std::__ndk1::__invoke<boxing::core::ThreadPool::ThreadPool(unsigned int)::'lambda'()>(boxing::core::ThreadPool::ThreadPool(unsigned int)::'lambda'()&&) type_traits:3874
[Inlined] std::__ndk1::__thread_execute<>(std::__ndk1::tuple<> &, std::__ndk1::__tuple_indices<>) thread:273
std::__ndk1::__thread_proxy<>(void *) thread:284
__pthread_start(void*) 0x00000000eab36828
__start_thread 0x00000000eaaed5ce

渲染设备API定义:

filament\filament\backend\include\private\backend\DriverAPI.inc

DriverAPI.inc中使用大量的宏替换操作,将设备接口进行封装,或打包,这部分代码可读性极差,不过可从其调用逻辑来进行拆解和理解:
先来分析其中一个接口: createVertexBuffer 创建一个顶点缓冲

DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,uint8_t, bufferCount,uint8_t, attributeCount,uint32_t, vertexCount,backend::AttributeArray, attributes)

这里不是真的创建,而要看这个宏接口在哪里使用,我们主要看看这两个地方:

  CommandStream.h  //命令流Driver.h   //设备接口

这两个文件中都对DriverAPI.inc进行了include,但是意义完全不一样,先看DECL_DRIVER_API_R_N:

#define DECL_DRIVER_API_R_N(R, N, ...) \DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))

关键在DECL_DRIVER_API_RETURN这个宏,在CommandStream.h和Driver.h头文件中include文件DriverAPI.inc 之前分别定义了自己的DECL_DRIVER_API_RETURN宏,看看CommandStream.h中:

#define DECL_DRIVER_API(methodName, paramsDecl, params)                                         \inline void methodName(paramsDecl) {                                                        \DEBUG_COMMAND_BEGIN(methodName, false, params);                                         \using Cmd = COMMAND_TYPE(methodName);                                                   \void* const p = allocateCommand(CommandBase::align(sizeof(Cmd)));                       \new(p) Cmd(mDispatcher.methodName##_, APPLY(std::move, params));                        \DEBUG_COMMAND_END(methodName, false);                                                   \}#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params)                    \inline RetType methodName(paramsDecl) {                                                     \DEBUG_COMMAND_BEGIN(methodName, true, params);                                          \AutoExecute callOnExit([=](){                                                           \DEBUG_COMMAND_END(methodName, true);                                                \});                                                                                     \return apply(&Driver::methodName, mDriver, std::forward_as_tuple(params));              \}#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params)                         \inline RetType methodName(paramsDecl) {                                                     \DEBUG_COMMAND_BEGIN(methodName, false, params);                                         \RetType result = mDriver.methodName##S();                                               \using Cmd = COMMAND_TYPE(methodName##R);                                                \void* const p = allocateCommand(CommandBase::align(sizeof(Cmd)));                       \new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params));       \DEBUG_COMMAND_END(methodName, false);                                                   \return result;                                                                          \}

上面三个宏的作用基本是一样的,都将要调用的函数和参数封装为了Command,不同之处在于DECL_DRIVER_API是command无返回值的,DECL_DRIVER_API_SYNCHRONOUS是封装为command后同步执行的,DECL_DRIVER_API_RETURN是需要返回值的
主要看看DECL_DRIVER_API_RETURN:

RetType result = mDriver.methodName##S();    

将方法名后面拼接了S,调用拿到返回类型
看看拼接S后的实现:

Handle<HwVertexBuffer> OpenGLDriver::createVertexBufferS() noexcept {return initHandle<GLVertexBuffer>();
}

initHandle()这句在filament内存池HandleArena上创建了一个GLVertexBuffer对象,然后根据内存地址创建了对象的唯一handeID
再看下面这句:
using Cmd = COMMAND_TYPE(methodName##R);
方法名后面拼接了R,然后获取了command的类型,没有执行方法,看看拼接R后的实现:

void OpenGLDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,uint8_t bufferCount,uint8_t attributeCount,uint32_t elementCount,AttributeArray attributes) {DEBUG_MARKER()construct<GLVertexBuffer>(vbh, bufferCount, attributeCount, elementCount, attributes);
}

内存池HandleArena上创建了一个GLVertexBuffer对象
再看下面一句

void* const p = allocateCommand(CommandBase::align(sizeof(Cmd)));   
new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params));   

在CommandStream内部的环形缓冲上申请了一块Command对象的内存p,然后在内存p上new了对象Command
看看CommandBase* execute执行函数的实现:

inline CommandBase* execute(Driver& driver) {// returning the next command by output parameter allows the compiler to perform the// tail-call optimization in the function called by mExecute, however that comes at// a cost here (writing and reading the stack at each iteration), in the end it's// probably better to pay the cost at just one location.intptr_t next;mExecute(driver, this, &next);return reinterpret_cast<CommandBase*>(reinterpret_cast<intptr_t>(this) + next);
}

mExecute就是上面new(p) Cmd(mDispatcher.methodName##_, RetType(result), APPLY(std::move, params)); 后的函数和参数的封装体,然后拿到了下一个圆形缓冲中下一个command的地址偏移量next,返回下一个command地址
CommandStream中执行command,执行完然后获取下一个执行。。。

mDriver.execute([this, buffer]() {Driver& UTILS_RESTRICT driver = mDriver;CommandBase* UTILS_RESTRICT base = static_cast<CommandBase*>(buffer);while (UTILS_LIKELY(base)) {base = base->execute(driver);}
});

相关文章:

Filament引擎分析--command抽象设备API

1. 前言 Filament中使用了FrameGraph来管理渲染管线&#xff0c;需要准备两点&#xff1a; 设备接口抽象&#xff1a;设备API抽象为Command资源抽象&#xff1a;使用虚拟资源&#xff0c;在实际用到时再创建&#xff0c;方便剔除无用资源 下面就围绕Filament中设备API抽象为…...

网络协议与响应码

http&#xff1a;超文本&#xff08;不止文本&#xff09;传输协议&#xff0c;底层是tcp/ip get&#xff0c;post&#xff0c;put&#xff0c;delete GET把参数包含在 URL 中&#xff0c; POST 通过 request body 传递参数 请求都是tcp链接&#xff0c;HTTP规定&#xff0c;当…...

彻底删除VsCode配置和安装过的插件与缓存

前言 当你准备对 Visual Studio Code&#xff08;VSCode&#xff09;进行重新安装时&#xff0c;可能遇到一个常见问题&#xff1a;重新安装后&#xff0c;新的安装似乎仍然保留了旧的配置信息&#xff0c;这可能会导致一些麻烦。这种情况通常是由于卸载不彻底所致&#xff0c…...

【XILINX】ISE chipscope出现错误 Can‘t load jre bin client jvm.dll

记录一个ISE软件使用过程中遇到的问题及解决方案。 问题 ISE chipscope出现错误 Cant load jre bin client jvm.dll C:\Xilinx\14.7\ISE_DS\ISE\bin\nt C:\Xilinx\14.7\ISE_DS\.xinstall\bin\nt C:\Xilinx\14.7\ISE_DS\.xinstall\bin\nt64 C:\Xilinx\14.7\ISE_DS\ISE\bin\nt6…...

并发编程(已整理,已废弃)

这一块知识&#xff0c;那真是有的啃了。 直接先看速成基础&#xff0c;再直接吃掉高频考点。 每个小知识点&#xff0c;直接看短视频&#xff0c;浅浅了解&#xff0c;在写下来就是自己的资料。 # 基础 一个进程有多个线程&#xff0c;多个线程共享进程的堆和方法区&#xf…...

第一篇:MongoDB的安装、启动、关闭、链接shell

目录 简介 安装 安装遇到的问题 查看brew 当前使用的源&#xff1a; 更换brew 源。更换成清华大学镜像源 版本查看 MongoDB 数据目录与日志目录 启动方式一&#xff1a; 启动MongoDB 验证MongoDB 是否正常运行 停止或重新启动 停止MongoDB 服务 重新启动MongoDB服…...

Python爬虫之重放攻击详解

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 重放攻击是一种网络攻击方式&#xff0c;攻击者通过截获合法用户的请求&#xff0c;并将其重新发送&#xff0c;以模拟合法用户的行为。在Python爬虫领域&#xff0c;了解重放攻击的原理和防范方法至关重要。本文…...

构建一个语音转文字的WebApi服务

构建一个语音转文字的WebApi服务 简介 由于业务需要&#xff0c;我们需要提供一个语音输入功能&#xff0c;以便更方便用户的使用&#xff0c;所以我们需要提供语音转文本的功能&#xff0c;下面我们将讲解使用Whisper将语音转换文本&#xff0c;并且封装成WebApi提供web服务…...

在开发软件KEIL MDK和IAR开发工程里面打印行号、文件名、函数名、时间

最近应用固件没有时间记录&#xff0c;分别请那个是最新的&#xff08;在没有版本区别的情况下&#xff09;&#xff0c;有个办法记录编译时间即可&#xff0c;记录笔记以便查看 在软件工程里面直接用宏 __FILE __ 当前程序文件名的字符串 __FUNCTION __ 当前函数的名字字符串 …...

springboot(ssm健身器材用品网 健身用品商城Java(codeLW)

springboot(ssm必录德健身器材用品网 健身用品商城Java(code&LW) 开发语言&#xff1a;Java 框架&#xff1a;ssm/springboot vue JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09; 服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.7&#xff08;或8.0&am…...

idea git合并推送分支

远端代码合并到当前分支 1.本地切换到当前分支 2.远端目标分支右键合并到当前分支(使用合并拉入) 本地当前分支推送合并到远端分支 1.切换到远端本地分支 2.合并本地其他分支(想要推送的分支)到当前分支 3.推送分支 注意:这里的合并是:将XXX合并到XXX中 …...

AntDesignBlazor示例——创建列表页

本示例是AntDesign Blazor的入门示例&#xff0c;在学习的同时分享出来&#xff0c;以供新手参考。 示例代码仓库&#xff1a;https://gitee.com/known/AntDesignDemo 1. 学习目标 使用Table组件创建列表页面使用DisplayName特性显示中文表头使用模板和Tag组件显示高温数据使…...

微信小程序模板选择指南:如何找到靠谱的平台?

随着移动互联网的快速发展&#xff0c;越来越多的企业和商家都在微信小程序上开展业务。而他们也希望可以通过微信小程序模板快速搭建小程序&#xff0c;那么如何才能找到一个靠谱的微信小程序模板平台呢&#xff1f;下面给大家简单讲解一下。 首先要知道的是&#xff0c;微信小…...

es常用查询编辑

查询指定id信息 GET /index_name/_doc/1074266245查询指定信息并降序 GET /index_name/_search {"query": {"term": {"deviceId": {"value": "1074266245"}}}, "sort": [{"timestamp": {"order&qu…...

记录 | linux静态库和动态库的理解

hello.cpp&#xff1a; #include <cstdio>void hello() {printf("Hello, world!\n"); }main.cpp&#xff1a; #include <cstdio>void hello();int main() {hello();return 0; }静态库编译配置&#xff1a; cmake_minimum_required(VERSION 3.12) proj…...

ParBFT: Faster Asynchronous BFT Consensus with a Parallel Optimistic Path

目录 笔记后续的研究方向摘要引言 ParBFT: Faster Asynchronous BFT Consensus with a Parallel Optimistic Path CCS 2023 笔记 后续的研究方向 摘要 为了减少异步拜占庭容错&#xff08;BFT&#xff09;共识的延迟和通信开销&#xff0c;通常会添加一条乐观的路径&#xf…...

java小工具util系列3:JSON转实体类对象工具

文章目录 准备工作1.JSONObject获取所有的key2.集合中实体对象转换 list中Enrey转Dto3.字符串转List<BusyTimeIndicatorAlarmThreshold>4.json字符串转JSONObject5.list根据ids数组过滤list6.json字符串转JavaBean对象7.json对象转javabean8.jsonObject转map9.List\<U…...

MySQL:找回root密码

一、情景描述 我们在日常学习中&#xff0c;经常会忘记自己的虚拟机中MySQL的root密码。 这个时候&#xff0c;我们要想办法重置root密码&#xff0c;从而&#xff0c;解决root登陆问题。 二、解决办法 1、修改my.cnf配置文件并重启MySQL 通过修改配置文件&#xff0c;来跳…...

计算机网络扫盲(1)——因特网

一、概述 因特网是一个世界范围的计算机网络&#xff0c;即它是一个互联了遍及全世界数十亿计算设备的网络。大家对此应该并不陌生&#xff0c;我们身边有着不计其数的计算机设备被接入了因特网&#xff0c;如今计算机网络这个术语似乎已经有点过时了&#xff0c;用因特网的术语…...

C语言 if语句有无(;)分号问题

在C语言中&#xff0c;if语句后面不带分号&#xff08;;&#xff09;的情况有两种主要形式&#xff1a; 1. 带有大括号的代码块&#xff1a;如果if语句后面跟随一个由大括号&#xff08;{}&#xff09;包围的代码块&#xff0c;那么这个代码块中的语句只有在if条件为真时才会执…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

Vue ③-生命周期 || 脚手架

生命周期 思考&#xff1a;什么时候可以发送初始化渲染请求&#xff1f;&#xff08;越早越好&#xff09; 什么时候可以开始操作dom&#xff1f;&#xff08;至少dom得渲染出来&#xff09; Vue生命周期&#xff1a; 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

数据结构:递归的种类(Types of Recursion)

目录 尾递归&#xff08;Tail Recursion&#xff09; 什么是 Loop&#xff08;循环&#xff09;&#xff1f; 复杂度分析 头递归&#xff08;Head Recursion&#xff09; 树形递归&#xff08;Tree Recursion&#xff09; 线性递归&#xff08;Linear Recursion&#xff09;…...

边缘计算网关提升水产养殖尾水处理的远程运维效率

一、项目背景 随着水产养殖行业的快速发展&#xff0c;养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下&#xff0c;而且难以实现精准监控和管理。为了提升尾水处理的效果和效率&#xff0c;同时降低人力成本&#xff0c;某大型水产养殖企业决定…...