NDK OpenGL离屏渲染与工程代码整合
NDK系列之OpenGL离屏渲染与工程代码整合,本节主要是对上一节OpenGL渲染画面效果代码进行封装设计,将各种特效代码进行分离解耦,便于后期增加其他特效。
实现效果:

实现逻辑:

1.封装BaseFilter过滤器基类,实现通用的顶点着色器、片元着色器和着色器程序的初始化,赋值和绘制操作;
2.编写CamreaFilter相机过滤器,实现FBO离屏渲染和变换矩阵等相机相关操作,绘制操作完成后,将相机过滤器实现的效果的纹理ID传递给下一个过滤器,在相机过滤器效果上进行叠加其他效果。
本节主要内容:
1.封装BaseFilter过滤器基类;
2.实现CamreaFilter相机过滤器;
3.实现ScreenFilter屏幕过滤器;
4.自定义渲染器MyGlRenderer;
源码:
NdkOpenGLPlay: NDK OpenGL渲染画面效果
一、封装BaseFilter过滤器基类
1)BaseFilter构造方法,保存顶点着色器和片元着色器代码资源文件id,保存坐标系到数据缓冲区;
public BaseFilter(Context context, int vertexSourceId, int fragmentSourceId) {this.mVertexSourceId = vertexSourceId; // 子类传递过来的顶点着色器代码IDthis.mFragmentSourceId = fragmentSourceId; // 子类传递过来的片元着色器代码ID// 顶点相关 坐标系float[] VERTEX = {-1.0f, -1.0f,1.0f, -1.0f,-1.0f, 1.0f,1.0f, 1.0f,};mVertexBuffer = BufferHelper.getFloatBuffer(VERTEX); // 保存到 顶点坐标数据缓冲区// 纹理相关 坐标系float[] TEXTURE = {0.0f, 0.0f,1.0f, 0.0f,0.0f, 1.0f,1.0f, 1.0f,};mTextureBuffer = BufferHelper.getFloatBuffer(TEXTURE); // 保存到 纹理坐标数据缓冲区init(context);
}
2)加载并编译着色器代码,链接色器ID,输出着色器程序,并获取着色器的索引值;
private void init(Context context) {String vertexSource = TextResourceReader.readTextFileFromResource(context, mVertexSourceId); // 顶点着色器代码字符串String fragmentSource = TextResourceReader.readTextFileFromResource(context, mFragmentSourceId); // 片元着色器代码字符串int vertexShaderId = ShaderHelper.compileVertexShader(vertexSource); // 编译顶点着色器代码字符串int fragmentShaderId = ShaderHelper.compileFragmentShader(fragmentSource); // 编译片元着色器代码字符串mProgramId = ShaderHelper.linkProgram(vertexShaderId, fragmentShaderId); // 链接 顶点着色器ID,片元着色器ID 最终输出着色器程序// 删除 顶点 片元 着色器IDglDeleteShader(vertexShaderId);glDeleteShader(fragmentShaderId);// 顶点着色器里面的如下:vPosition = glGetAttribLocation(mProgramId, "vPosition"); // 顶点着色器:的索引值vCoord = glGetAttribLocation(mProgramId, "vCoord"); // 顶点着色器:纹理坐标,采样器采样图片的坐标 的索引值vMatrix = glGetUniformLocation(mProgramId, "vMatrix"); // 顶点着色器:变换矩阵 的索引值// 片元着色器里面的如下:vTexture = glGetUniformLocation(mProgramId, "vTexture"); // 片元着色器:采样器
}
3)完成绘制操作,顶点坐标赋值,纹理坐标赋值,采样器赋值,通知opengl绘制,将过滤器实现的效果的纹理ID返回给下一个过滤器;
public int onDrawFrame(int textureId) {// 设置视窗大小glViewport(0, 0, mWidth, mHeight);glUseProgram(mProgramId); // 必须要使用着色器程序一次// TODO 画画,绘制 等工作// TODO 1.顶点坐标赋值mVertexBuffer.position(0);// 传值glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, mVertexBuffer);// 激活glEnableVertexAttribArray(vPosition);// TODO 2.纹理坐标赋值mTextureBuffer.position(0);// 传值glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, mTextureBuffer);// 激活glEnableVertexAttribArray(vCoord);// 只需要把OpenGL的纹理ID,渲染到屏幕上就可以了,不需要矩阵数据传递给顶点着色器了// 变换矩阵 把mtx矩阵数据 传递到 vMatrix// glUniformMatrix4fv(vMatrix, 1, false, mtx, 0);// TODO 3.片元 vTextureglActiveTexture(GL_TEXTURE0); // 激活图层// 不需要关心摄像头 和 矩阵// 绑定图层,为什么不需要GL_TEXTURE_EXTERNAL_OES?答:目前拿到的textureId已经是纹理ID了,不是摄像头直接采集到的纹理IDglBindTexture(GL_TEXTURE_2D ,textureId);// 因为CameraFilter已经做过了,我就直接显示,我用OepnGL 2D GL_TEXTURE_2D 显示就行了// glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); // 由于这种方式并不是通用的,所以先去除glUniform1i(vTexture, 0); // 传递采样器glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 通知 opengl 绘制return textureId; // 返回纹理ID,可以告诉下一个过滤器
}
二、实现CamreaFilter相机过滤器
1)CameraFilter构造方法,将顶点着色器和片元着色器代码资源文件id传给父类,完成顶点着色器、片元着色器和着色器程序的初始化工作;
public CameraFilter(Context context) {super(context, R.raw.camera_vertex, R.raw.camera_fragment);
}
2)准备工作,创建FBO帧缓冲区和FBO的纹理ID,并绑定起来;
public void onReady(int width, int height) {super.onReady(width,height);// TODO 准备工作// TODO 第一步:创建 FBO (看不见的离屏的屏幕)mFrameBuffers = new int[1];// 参数1:int n, fbo 个数// 参数2:int[] framebuffers, 用来保存 fbo id 的数组// 参数3:int offset 从数组中第几个id来保存,从零下标开始glGenFramebuffers(mFrameBuffers.length, mFrameBuffers, 0); // 实例化创建帧缓冲区,FBO缓冲区// TODO 第二步:创建属于 fbo 纹理(第一节课是没有配置的,但是这个是FOB纹理,所以需要配置纹理)// 既然上面的 FBO(看不见的离屏的屏幕),下面的目的就是要把画面显示到FBO中mFrameBufferTextures = new int[1]; // 记录FBO纹理的IDTextureHelper.genTextures(mFrameBufferTextures); // 生成并配置纹理// TODO 第三步:上面的 FBO缓冲区 与 FBO纹理 还没有任何关系,现在要让他们绑定起来glBindTexture(GL_TEXTURE_2D, mFrameBufferTextures[0]);// 生产2D纹理图像/*int target, 要绑定的纹理目标int level, level一般都是0int internalformat, 纹理图像内部处理的格式是什么,rgbaint width, 宽int height, 高int border, 边界int format, 纹理图像格式是什么,rgbaint type, 无符号字节的类型java.nio.Buffer pixels*/glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers[0]); // 发生关系/*int target, fbo的纹理目标int attachment, 附属到哪里int textarget, 要绑定的纹理目标int texture, 纹理int level level一般都是0*/glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mFrameBufferTextures[0], 0);// TODO 第四步:解绑操作glBindTexture(GL_TEXTURE_2D, 0);glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
3)完成绘制操作,渲染到FBO离线缓存中,顶点坐标赋值,纹理坐标赋值,变换矩阵把mtx矩阵数据传递到vMatrix,相机过滤器处理过后,其他过滤器不需要再变换矩阵,采样器赋值,通知opengl绘制,将FBO的纹理ID返回给下一个过滤器;
public int onDrawFrame(int textureId) {// 不能调用super,因为父类做的事情,和子类是很大区别的// super.onDrawFrame(textureId);glViewport(0, 0, mWidth, mHeight); // 设置视窗大小// TODO 渲染到 FBO离线缓存中// 绑定FBO缓存(否则会绘制到屏幕上) 我们最终的效果是 离屏渲染glBindFramebuffer(GL_FRAMEBUFFER, mFrameBuffers[0]);glUseProgram(mProgramId); // 必须要使用着色器程序一次// 画画 绘制操作mVertexBuffer.position(0); // 顶点坐标赋值glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, mVertexBuffer); // 传值glEnableVertexAttribArray(vPosition); // 激活mTextureBuffer.position(0); // 纹理坐标赋值glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, mTextureBuffer); // 传值glEnableVertexAttribArray(vCoord); // 激活// TODO 变换矩阵,在我们CameraFilter这里就需要处理了,后面的BaseFilter就不需要了glUniformMatrix4fv(vMatrix, 1, false, matrix, 0);// 片元 vTextureglActiveTexture(GL_TEXTURE0); // 激活图层// glBindTexture(GL_TEXTURE_2D ,textureId); // 公用的那个 1glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); // 摄像头打交道采样器:使用额外拓展的,不能使用公用的那个 2glUniform1i(vTexture, 0);glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 通知 opengl 绘制// 解绑 fboglBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);glBindFramebuffer(GL_FRAMEBUFFER, 0);// FBO的纹理ID,返回了return mFrameBufferTextures[0]; // 你没有学过C,你就不明白,如果你学过,无需多言
}
4)相机过滤器顶点着色器代码:
attribute vec4 vPosition; // 顶点坐标,相当于:相机的四个点位置排版attribute vec4 vCoord; // 纹理坐标,用来图形上色的uniform mat4 vMatrix; // 变换矩阵,4*4的格式的varying vec2 aCoord; // 把这个最终的计算成果,给片元着色器 【不需要Java传递,他是计算出来的】void main() {gl_Position = vPosition; // 确定好位置排版aCoord = (vMatrix * vCoord).xy; // 兼容所有设备
}
5)相机过滤器片元着色器代码:
// 导入 samplerExternalOES
#extension GL_OES_EGL_image_external : require// float 数据的精度 (precision lowp = 低精度) (precision mediump = 中精度) (precision highp = 高精度)
precision mediump float;// 根据上面的数据的精度,写下面的 采样器 相机的数据
// uniform sampler2D vTexture; 由于我们用的是 安卓的相机,就不能用他
uniform samplerExternalOES vTexture; // samplerExternalOES才能采样相机的数据varying vec2 aCoord; // 把这个最终的计算成果,给片元着色器,拿到最终的成果,我才能上色void main() {// 底片效果vec4 rgba = texture2D(vTexture,aCoord); // rgbagl_FragColor = vec4(1.-rgba.r, 1.-rgba.g, 1.-rgba.b, rgba.a);
}
三、实现ScreenFilter屏幕过滤器
1)ScreenFilter构造方法,将顶点着色器和片元着色器代码资源文件id传给父类,完成顶点着色器、片元着色器和着色器程序的初始化工作;
public ScreenFilter(Context context) {super(context, R.raw.base_vertex, R.raw.base_fragment); // base_vertex(没有矩阵) base_fragment(没有OES 是sampler2D)
}
2)准备工作和绘制操作复用父类完成;
3)屏幕过滤器顶点着色器代码:
attribute vec4 vPosition; // 顶点坐标,相当于:相机的四个点位置排版attribute vec2 vCoord; // 纹理坐标,用来图形上色的varying vec2 aCoord; // 把这个最终的计算成果,给片元着色器 【不需要Java传递,他是计算出来的】void main() {gl_Position = vPosition; // 确定好位置排版 gl_Position OpenGL着色器语言内置的变量// 着色器语言基础语法// aCoord = vCoord.xy;// aCoord是2个分量的 vCoord是四个分量的.xy取出两个分量aCoord = vCoord;
}
4)屏幕过滤器片元着色器代码
// float 数据的精度 (precision lowp = 低精度) (precision mediump = 中精度) (precision highp = 高精度)
precision mediump float;// 根据上面的数据的精度,写下面的 采样器 相机的数据
uniform sampler2D vTexture;// 由于我们用的是 安卓的相机,就不能用他(用OpenGL 2D显示就行了,不需要摄像头了,sampler2D)varying vec2 aCoord;// 把这个最终的计算成果,给片元着色器,拿到最终的成果,我才能上色void main() {// texture2D (采样器, 坐标) gl_FragColor OpenGL着色器语言内置的变量gl_FragColor = texture2D(vTexture, aCoord);// 直接上色,你直接上色,是上camera_fragment.glsl的底片效果
}
四、自定义渲染器MyGlRenderer
从上一节中我们知道,MyGlRenderer实现GLSurfaceView.Renderer接口,实现接口的onSurfaceCreated()、onSurfaceChanged()和onDrawFrame()方法;
1)当Surface创建时,回调onSurfaceCreated()函数,创建ScreenFilter屏幕过滤器,同样在创建ScreenFilter屏幕过滤器之前,我们要先创建CamreaFilter相机过滤器;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {mCameraFilter = new CameraFilter(myGLSurfaceView.getContext()); // 先 FBOmScreenFilter = new ScreenFilter(myGLSurfaceView.getContext()); // 后 渲染屏幕
}
2)当Surface改变时,回调onSurfaceChanged()函数,完成过滤器准备工作;
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {mCameraFilter.onReady(width, height);mScreenFilter.onReady(width, height);
}
3)绘制一帧图像时,回调onDrawFrame()函数,获取纹理对象的图像数据,先通过CamreaFilter相机过滤器,实现相关效果,再将其FBO的纹理ID传递给下一个过滤器,这里由于暂无实现其他特效,就传递给了ScreenFilter屏幕过滤器,将最终成果的纹理ID通过OpenGL渲染到屏幕;
@Override
public void onDrawFrame(GL10 gl) {// 相机过滤器,绘制一帧图像,不可见mCameraFilter.setMatrix(mtx);int textureId = mCameraFilter.onDrawFrame(mTextureID[0]); // 摄像头,矩阵,都已经做了// 增加其他特效/*textureId = 美白.onDrawFrame(textureId);textureId = 大眼.onDrawFrame(textureId);textureId = xxx.onDrawFrame(textureId);*/// 屏幕过滤器,绘制一帧图像,屏幕显示mScreenFilter.onDrawFrame(textureId); // textureId == 最终成果的纹理ID
}
注:后续将会在CamreaFilter相机过滤器和ScreenFilter屏幕过滤器中间增加各种效果过滤器,实现其他特效,如:美白,大眼,兔耳朵等。
至此,OpenGL离屏渲染与工程代码整合已完成。
源码:
NdkOpenGLPlay: NDK OpenGL渲染画面效果
相关文章:
NDK OpenGL离屏渲染与工程代码整合
NDK系列之OpenGL离屏渲染与工程代码整合,本节主要是对上一节OpenGL渲染画面效果代码进行封装设计,将各种特效代码进行分离解耦,便于后期增加其他特效。 实现效果: 实现逻辑: 1.封装BaseFilter过滤器基类,…...
Python基础入门编程代码练习(二)
一、求1~100之间不能被3整除的数之和 循环条件:i<100循环操作 实现代码如下: def sums():sum 0for num in range(1, 101):if num % 3 ! 0:sum numprint("1~100之间不能被3整除的数之和为:%s" % (sum))sums() print("1~…...
C# | 对象池
对象池 文章目录 对象池前言什么是对象池对象池的优点对象池的缺点 实现思路示例代码 结束语 前言 当我们开发一个系统或者应用程序时,我们通常需要创建很多的对象,这些对象可能是线程、内存、数据库连接、文件句柄等等。在某些情况下,我们需…...
CSS小技巧之圆形虚线边框
虚线相信大家日常都用的比较多,常见的用法就是使用 border-style 控制不同的样式,比如设置如下边框代码: border-style: dotted dashed solid double;这将设置顶部的边框样式为点状,右边的边框样式为虚线,底部的边框样…...
QString与QByteArray互相转换的方法
QString与QByteArray互相转换的方法 [1] QString与QByteArray互相转换的方法QString转QByteArray方法QByteArray转QString方法QByteArray类同样不以’\0’为结尾QByteArray转QString,主要用buf.toHex()即可 [2] Qt开发串口通讯软件中的数据转换问题1.读取串口命令-Q…...
Springboot +Flowable,设置流程变量的方式(一)
一.简介 为什么需要流程变量。 举个例子,假设有如下一个流程,截图如下: 这是一个请假流程,那么谁请假、请几天、起始时间、请假理由等等,这些都需要说明,不然领导审批的依据是啥?那么如何传递…...
机器学习13(正则化)
文章目录 简介正则化经验风险和结构风险过拟合正则化建模策略 逻辑回归逻辑回归评估器 练习评估器训练与过拟合实验评估器的手动调参 简介 这一节详细探讨关于正则化的相关内容,并就 sklearn 中逻辑回归(评估器)的参数进行详细解释由于 skle…...
并发编程学习(十一):原子数组、
1、数组类型的原子类 原子数组类型,这个其实和AtomicInteger等类似,只不过在修改时需要指明数组下标。 CAS是按照来根据地址进行比较。数组比较地址,肯定是不行的,只能比较下标元素。而比较下标元素,就和元素的…...
递归到动态规划:省去枚举行为
如果在动态规划的过程中没有枚举行为,那严格位置依赖和傻缓存的方式并没有太大区别,但是当有枚举行为的时候(一个位置依赖于多个位置),那严格位置依赖是有优化空间的,枚举行为也许可以省去,题目…...
服务(第二十一篇)mysql高级查询语句(二)
①视图表: 视图表是虚拟表,用来存储SQL语句的定义 如果视图表和原表的字段相同,是可以进行数据修改的; 如果两者的字段不通,不可以修改数据。 语法: 创建:create view 试图表名 as ... 查…...
MYSQL高可用配置(MHA)
1、什么是MHA MHA(Master High Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中,MHA能做到0-30秒内自动完成故障切换操作。 MHA能在故障切换的过程中最大…...
单精度浮点数与十进制数据相互转换
一、float基础: Float类型占4个字节,也就是32bit,其中最高位是符号位,2~9位是指数位,后边的23bit是数值位.如下所示 大部分数据的二进制形式都可以用科学计数法表示,即1.m*2^n这种形式,只要知道m和n,就能确定一个数值 二、小数位如何转变为二进制: 下面…...
PMP敏捷-4大价值观、12原则
宣言及4大价值观 个体及互动 胜于 流程和工具 以人为本 工作的软件 胜于 完整的文档 以价值为导向 客户合作 胜于 合同谈判 合作共赢 应对变更 胜于 遵循计划 拥抱变化 12原则 工作原则:精益、至简,实现这种原则的方式是“定期反省”。9、10、12 …...
K8S—Helm
一、Helm介绍 helm通过打包的方式,支持发布的版本管理和控制,很大程度上简化了Kubernetes应用的部署和管理。 Helm本质就是让k8s的应用管理(Deployment、Service等)可配置,能动态生成。通过动态生成K8S资源清单文件&a…...
ALSA内部函数调用流程
ALSA内部函数调用流程 一直都有这样的一个疑问 就是在linux系统中我们调用snd_pcm_open后,就不知道alsa内部是怎么运行的了 用户的pcm_open()相当于先对ASoC各个驱动模块startup(),再做hw_params()。 pcm_open()pcm->fd open("/dev/snd/pcm…...
Python正则表达式详解,保姆式教学,0基础也能掌握正则
正则作为处理字符串的一个实用工具,在Python中经常会用到,比如爬虫爬取数据时常用正则来检索字符串等等。正则表达式已经内嵌在Python中,通过导入re模块就可以使用,作为刚学Python的新手大多数都听说”正则“这个术语。 今天来给…...
ChatGPT 接入飞书教程,创建自己的聊天机器人
ChatGPT 接入飞书教程,创建自己的聊天机器人 一、飞书进入开发者平台。点击创建应用。二、打开Aircode,点击创建应用,上面输入名字,下面选择Node.js v16三、配置环境,点击Environments,创建四个变量,全部要大写本教程收集于: AIGC从入门到精通教程 首先,准备三个账号…...
JS生成随机数(多种解决方案)
JS生成随机数 概述 随机数是编程语言中的重要组成部分。在JavaScript中,生成随机数是一项简单的任务。本文将介绍生成随机数的各种方法。 Math.random() Math.random()是JavaScript中生成随机数最常见的方法。该方法返回介于0和1之间的随机数。例如,…...
文件IO 函数 静态库和动态库的创建 5.11
5.11 文件IO函数 1.数据读写 ssize_t read(int fd,void *buf,size_t count); 功能: 从fd对应的文件中 读取前count个字节的数据到buf缓冲区中 头文件: #include <unistd.h> 参数: fd :文件描述符 buf…...
考研日语-详解ている、てある、ていく、てくる用法
目录 一、ている用法 1. 表示现在状态 2. 表示持续动作 3. 表示经验或习惯 4. 表示结果或效果 二、てある用法 1. 表示已经完成的动作 2. 表示现在状态 3. 表示被动 三、ていく用法 1. 表示未来的动作 2. 表示逐渐变化的过程 四、てくる用法 1. 表示过去到现在的…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
从零手写Java版本的LSM Tree (一):LSM Tree 概述
🔥 推荐一个高质量的Java LSM Tree开源项目! https://github.com/brianxiadong/java-lsm-tree java-lsm-tree 是一个从零实现的Log-Structured Merge Tree,专为高并发写入场景设计。 核心亮点: ⚡ 极致性能:写入速度超…...
【笔记】AI Agent 项目 SUNA 部署 之 Docker 构建记录
#工作记录 构建过程记录 Microsoft Windows [Version 10.0.27871.1000] (c) Microsoft Corporation. All rights reserved.(suna-py3.12) F:\PythonProjects\suna>python setup.py --admin███████╗██╗ ██╗███╗ ██╗ █████╗ ██╔════╝…...
简约商务通用宣传年终总结12套PPT模版分享
IOS风格企业宣传PPT模版,年终工作总结PPT模版,简约精致扁平化商务通用动画PPT模版,素雅商务PPT模版 简约商务通用宣传年终总结12套PPT模版分享:商务通用年终总结类PPT模版https://pan.quark.cn/s/ece1e252d7df...
