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

Unity SRP学习笔记(二)

Unity SRP学习笔记(二)

主要参考:
https://catlikecoding.com/unity/tutorials/custom-srp/
https://docs.unity.cn/cn/2022.3/ScriptReference/index.html
中文教程部分参考(可选):
https://tuncle.blog/custom_render_pipeline/index.html
https://edu.uwa4d.com/lesson-detail/282/1308/0(依照Unity 2019版的内容翻译,不太适用于Unity 2022)
本文主要是,在参考以上内容学习的过程中,对一些基于已有内容但还是不太能理解的部分进行了一些额外的补充。

.Shader使用HLGL

当然也可以用GLSL在.Shader中写着色器,不过还是首推HLSL,毕竟绝大部分游戏都是在Windows平台下运行的。而且Shader中写的HLSL也不会直接执行,而是会根据目标平台编译成指定图形API。

SRP batcher

https://docs.unity.cn/cn/2019.4/Manual/SRPBatcher.html

初见SRPBatcher,了解到SPRBatcher可以通过合批优化渲染过程中CPU向GPU的数据发送环节。但也就仅限于此了,这种半懂不懂的感觉实在让人难受,于是便计划在实际项目中更深入的认识一下这到底是个什么玩意。
**主要有以下几个问题:**什么是通俗意义上的DrawCall?Unity渲染管线在最原始的情况下会如何进行渲染?经常出现在DrawCall优化中的方法——静态批处理/动态批处理/GPU Instancing都是什么?在Unity中实现后和原来相比到底什么区别?最后,再重新审视一下什么是SPR Batcher?

什么是通俗意义上的DrawCall?

你要写SRP batcher,就不能只写SRP batcher
参考了这篇文章DrawCall,Batches,SetPass calls是什么?,DrawCall就是一个CPU向GPU发出的渲染命令,同时告诉GPU我要渲染哪些数据。渲染管线进行一次渲染的步骤为:1)设置一个Shader为当前渲染状态(设置顶点着色器和片元着色器)。2)传递着色器参数,包括各种变换的矩阵(MVP),自定义的各类变量,以及纹理等。3)调用DrawCall,向GPU发出渲染命令。
文章中描述的一次渲染的流程和OpenGL完全对应的上🤗。
在OpenGL中,while (!glfwWindowShouldClose(window))所包围的部分就是完整的一帧渲染,而通常渲染一帧需要多次调用glDrawElements,glDrawElements就可以看成是DrawCall命令。进行一次glDrawElements,我们需要 1)用glUseProgram设置一个program为当前渲染环境(可选,如果不需要切换Shader就不需要再次设置)。2)设置着色器中Uniform的值,以及绑定VAO,设置layout,绑定纹理。3)调用画图函数(如glDrawElements)渲染目标,函数中指明了要渲染哪些数据。
了解了渲染管线基本步骤和DrawCall是什么,就该进到Unity里了。

为了进行后面的实验,需要能够控制渲染管线是否使用静态批处理/动态批处理/GPU Instancing/SPRBatcher。

Static Batching

我用的Unity版本是2022.3.45f1c1,平台为Windows10,静态批处理可以直接在ProjectSetting中设置。

在这里插入图片描述

Dynamic Batching/GPU Instancing

Dynamic Batching和GPU Instancing在SRP中,都需要在C#中手动设置,在URP中可以直接在内置的渲染管线中设置Dynamic Batching(该选项默认隐藏,需要开启全部可见),HDRP不支持Dynamic Batching。
通过对象初始化器{enableDynamicBatching = useDynamicBatching, enableInstancing = useGPUInstancing}设置m_Flags的值。

DrawingSettings drawingOpaqueSettings = new DrawingSettings(unlitShaderTagId, sortingOpaqueSettings) {enableDynamicBatching = useDynamicBatching, enableInstancing = useGPUInstancing };

这是因为Unity中通过变量m_Flags来控制Dynamic Batching/GPU Instancing的启用,m_Flags是一个枚举类型,其默认值为DrawRendererFlags.EnableInstancing。

internal enum DrawRendererFlags
{None = 0,EnableDynamicBatching = 1,EnableInstancing = 2
}

需要像URP一样在渲染管线中控制Dynamic Batching/GPU Instancing,需要在CustomRenderPipelineAsset中创建两个bool型变量并序列化,然后赋值到DrawingSettings中即可。具体实现在DrawCall后半章有。

疑问🤔:对已经通过某个变量完成实例化的类,在inspector中修改该变量为什么还能影响到已经完成实例化的类?
要么是检测如果有实例使用过该变量构造,则重新创建实例。
要么是Unity有什么特殊的办法能够修改readonly的变量。

public class CustomRenderPipelineAsset : RenderPipelineAsset
{[SerializeField]bool useDynamicBatching = true, useGPUInstancing = true, useSRPBatcher = true;protected override RenderPipeline CreatePipeline(){return new CustomRenderPipeline(useDynamicBatching, useGPUInstancing, useSRPBatcher);}
}
SRP Batcher

同样创建一个bool型变量并序列化,但不需要在DrawingSettings中设置,而只需要在CustomRenderPipeline.cs中设置Render函数的GraphicsSettings.useScriptableRenderPipelineBatching即可。

同时还需要提前将变量保存到常量缓冲区中:常量缓冲区介绍
常量缓冲区也需要字节对齐:cbuffer布局介绍,在Unity中如果不手动对齐的话,也能自动对齐。
在这里插入图片描述

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"可能会提示cannot open source file,说是历史遗留问题,除了写代码不太方便外不影响编译。
In Unity2018 and before, the editor feature ShaderIncludePath was used to configure the relative root of the shader, which is now obsolete.The current method is that #include “Packages//”,ShaderLab will parse and configure by itself when linking, but VS does not recognize this writing method and still links according to the default relative path of HLSL files, so an error will be reported, but compilation will not be affected.

以下代码会无法正常使用cbuffer,Shader文件会显示buildin property offset in cbuffer overlap other stages(UnityPerDraw)

CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
CBUFFER_END

UnityPerDraw必须定义特定的值组,cbuffer如下,这里的组名可以理解为更新方式,UnityPerDraw表示每次DrawCall更新,而unity_ObjectToWorld在不同DrawCall(即不同物体)时会改变,所以属于PerDraw。而相机的UnityPerFrame在每一帧内都是不变的,所以是PerFrame,不同材质对同一个Shader有不同的参数,即PerMaterial。在使用时Unity会自动检查所以不能混用,

CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
real4 unity_WorldTransformParams;
CBUFFER_ENDCBUFFER_START(UnityPerFrame)
float4x4 unity_MatrixVP;
CBUFFER_ENDCBUFFER_START(UnityPerMaterial)
float4 _BaseColor;
CBUFFER_END

In this context, “update” refers to the process of initializing a constant buffer with data that will remain constant throughout the execution of the shader programs that access it. 基于上述内容理解,the execution of the shader programs可以理解为一次DrawCall的过程中cbuffer内的值会保持不变。

至此,在渲染管线中实现了对合批方法的控制。接下来用RenderDoc和Frame Debugger进行实验。

在这里插入图片描述

Unity渲染管线在最原始的情况下会如何进行渲染?

场景,40个物体(均使用默认网格),黄色为内置Shader Unlit/Color,绿色和红色为自定义Shader,仅材质参数BaseColor不同。
在这里插入图片描述
无合批优化情况下(RenderDoc能捕获到的内容)的Main Camera:

1)RSSetViewports 设置视口大小,ClearDepthStencilView 清除模板和深度缓冲。
2)绘制第一个物体,设置OM阶段混合状态,设置IA阶段顶点缓冲,布局及索引缓冲,设置OM阶段深度和模板缓冲状态,设置RS阶段光栅状态,设置VS和PS的着色器,UnityPerFrame,UnityPerDraw映射到CPU内存空间,修改后释放绑定到VS,UnityPerMaterial同样操作,绑定到PS,DrawIndexed调用绘制。
具体设置的情况在Pipeline State的OM阶段中可知。
在这里插入图片描述
cbuffer在Pipeline State的VS阶段和PS阶段的ConstantBuffers栏可见,需要HLSL中添加#pragma enable_d3d11_debug_symbols(提供有关着色器内部状态的附加信息),否则Slot只能看到cbuffer0,cbuffer1…
在这里插入图片描述
3)绘制下一个物体,结论为如果 a.同材质,无论是否使用同一个网格,都重新设置顶点,索引缓冲,修改UnityPerDraw。b.同Shader,不同材质,重新设置顶点,索引缓冲,修改UnityPerDraw和UnityPerMaterial。c.不同Shader,和第 2)步一样,所有内容重新设置。

上述是仅使用cbuffer情况下的渲染管线,如果把cbuffer去掉呢O.o。实际上如果去掉显式声明的cbuffer,Unity也会自动把所有的着色器参数放到为$Globals的常量缓冲区中。但是这样的话,因为每一次DrawCall都需要更新unity_ObjectToWorld的值,所以需要对整个Globals常量缓冲区进行映射,不好不好。
在这里插入图片描述

静态批处理/动态批处理/GPU Instancing

Static batching

静态批处理是一种绘制调用批处理方法,它将不会移动的网格组合在一起,以减少绘制调用。 它将组合网格转换到世界空间,并为它们建立一个共享顶点和索引缓冲区。 然后,对于可见的网格,Unity 会执行一系列简单的绘制调用,每次调用之间几乎不会改变状态。 静态批处理并不会减少绘制调用的次数,反而会减少它们之间渲染状态变化的次数。
Static batching会将不会移动的网格合并在一起(最主要的作用!),需要静态批处理的物体可在Inspector中设置为Stratic。
注意:Static Batching只有在Game窗口运行时才会生效!!!同时,因为是在运行模式下,默认的Clear flag:Skybox不会清除上一帧的颜色缓冲,虽然在运行时没有问题,但是会不便于观察调试,所以修改了一下让Skybox也清除颜色缓冲。
在这里插入图片描述
虽然文档说不会减少DrawCall,但是在RenderDoc中看DrawIndexed的次数是会减少的。Static batching只对同材质的物体渲染能有效提升(可不同Mesh)。如果材质不同,Mesh依然会合并且共用一个顶点缓冲区,但依然需要设置顶点缓冲,这样相对于不设置Static batching反而增加了每次DrawCall设置的顶点缓冲大小。
对于组合后的网格能否一次DrawCall绘制还得看是否在索引缓冲区上连续,如果不连续,即使是同材质的物体也需要多次DrawCall。但是不连续也只需要调用一次DrawIndexed,无需再次设置顶点缓冲和索引缓冲,渲染速度也是会更快的。

Dynamic batching

动态批处理是一种绘制调用批处理方法,可批处理移动的游戏对象以减少绘制调用。 动态批处理在网格和 Unity 在运行时动态生成的几何体(如粒子系统)之间的工作方式不同。 有关网格和动态几何体之间内部差异的信息,请参阅网格的动态批处理和动态生成几何体的动态批处理。 注:网格的动态批处理是为了优化老式低端设备的性能而设计的。 在现代消费级硬件上,动态批处理在 CPU 上的工作可能大于绘制调用的开销。 这会对性能产生负面影响。 更多信息,请参阅网格的动态批处理。
Dynamic batching会在每一帧动态计算哪些对象可以合批,哪些对象需要合批,针对需要合批的对象还需要在CPU中做模型变换(转到世界坐标下),这个是要CPU开销的。如果这个开销比不合批时多出来的DrawCall的开销还要大,就成负优化了(所以这种方法现在基本都不用了)。所以尽量还是用Static Batching吧,Static Batching虽然也要将合批对象在CPU中变换到世界坐标下组合,但是是在Build阶段实现的,而不是在运行时每一帧场景绘制之前。
在这里插入图片描述

GPU Instancing

GPU 实例化是一种绘制调用优化方法,可在一次绘制调用中渲染具有相同材质的多个网格副本。 每个网格副本称为一个实例。 这对于绘制场景中多次出现的物体非常有用,例如树木或灌木丛。 GPU 实例化可以在同一个绘制调用中渲染相同的网格。 为了增加变化和减少重复,每个实例可以有不同的属性,如颜色或比例。 渲染多个实例的绘制调用会在帧调试器中显示为 “渲染网格(实例化)”。
**用于优化相同材质,相同网格的物体渲染速度。**可以理解为,在所有相同材质,相同网格的物体中取其中一个实例,对于其他物体将会变化的部分以数组的方式存储在GPU常量缓冲区中,每个物体都有一个唯一InstanceID,通过这个ID就可以索引其数据在数组中的数据。
例如,假如每个实例在世界坐标下的位置(即模型变换矩阵)都不一样,那么GPU中就会有一个常量缓冲区(名为PerDraw0)用来存储其模型变换矩阵数组。启用GPU Instancing时,顶点着色器中传入的数据(即Attributes)带有UNITY_VERTEX_INPUT_INSTANCE_ID,通过UNITY_SETUP_INSTANCE_ID(input)设置InstanceID后GetObjectToWorldMatrix()就能获取对应实例的模型变换矩阵(这个部分在UnityInstancing.hlsl中实现,当然如果想变的麻烦的话也可以自己实现。比如这里的_BaseColor就是自己实现的,可以对不同实例实现不同颜色参数)。
PS:使用_BaseColor时,如果想改颜色需要用MaterialPropertyBlock(教程默认用这个,可能不会遇到这种问题),而不是直接改Material。因为直接改Material会创建新的材质,而MaterialPropertyBlock是重写材质,还是原来的材质所以符合使用相同材质的条件。

//.hlsl
//避免多次include导致重定义问题
#ifndef CUSTOM_UNLIT_SHADER#define CUSTOM_UNLIT_SHADER//包含了对cbuffer的宏定义替换
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"#define UNITY_MATRIX_M unity_ObjectToWorld;//包含了对GPU实例所需的很多内容,可以简单的使用GPU Instancing
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
float4 unity_LODFade;
real4 unity_WorldTransformParams;
CBUFFER_ENDfloat4x4 GetObjectToWorldMatrix()
{return UNITY_MATRIX_M;
}CBUFFER_START(UnityPerFrame)
float4x4 unity_MatrixVP;
CBUFFER_ENDUNITY_INSTANCING_BUFFER_START(BaseColorCB)  //可随意命名,用于在UNITY_ACCESS_INSTANCED_PROP中使用UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)
UNITY_INSTANCING_BUFFER_END(BaseColorCB)struct Attributes
{UNITY_VERTEX_INPUT_INSTANCE_IDfloat3 positionOS : POSITION;
};struct Varyings
{UNITY_VERTEX_INPUT_INSTANCE_IDfloat4 positionCS : SV_POSITION;
};Varyings UnlitPassVertex(Attributes input)
{Varyings output;UNITY_SETUP_INSTANCE_ID(input);UNITY_TRANSFER_INSTANCE_ID(input, output); //因为这里的片元着色器中有根据InstanceID设置的变量所以需要传递InstanceID,如果没有可以去掉这部分output.positionCS = mul(unity_MatrixVP, mul(GetObjectToWorldMatrix(), float4(input.positionOS, 1.0)));return output;
}float4 UnlitPassFragment(Varyings input) : SV_TARGET
{UNITY_SETUP_INSTANCE_ID(input);return UNITY_ACCESS_INSTANCED_PROP(BaseColorCB, _BaseColor); //根据InstanceID在名为UnityPerMaterial的常量缓冲区中的_BaseColor数组内知道当前实例的_BaseColor
}#endif

综上所述,GPU Instancing其实就是把使用同一个Mesh和同一个材质的所有对象会变化的部分以数组的形式存储在常量缓冲区中,再通过InstanceID进行索引。同一个Mesh保证传入的顶点坐标是通用的(即在每个实例渲染中,Attributes的positionOS是通用的),同一个材质大概是确保无需切换渲染状态吧。所以GPU Instancing可以规避合并Mesh导致的内存与性能上升的问题。例如对于大量的树木或者杂草,使用GPU Instancing不会像Static Batching会导致生成一个巨大的顶点缓冲和索引缓冲,只需将模型变换矩阵以数组的形式加载到常量缓冲区中即可。
同样,常量缓冲区是有大小限制的(64kb),Unity中也限制了UNITY_MAX_INSTANCE_COUNT为500,最多一次DrawCall支持500个实例。
【Unity笔记】ShaderLab与其底层原理浅谈

SRP Batcher

Unity SRP Batcher的工作原理
关于静态批处理/动态批处理/GPU Instancing /SRP Batcher的详细剖析
内容可以参照这些文章👆,相比于GPU Instancing,SPR Batcher不会减少DrawCall,但条件更加宽松,只要是同一个Shader即可。
**数据提前加载到GPU:**从RenderDoc中看,所有需要进行渲染的对象的各种信息已经提前保存在GPU缓冲区中(例如顶点信息【不过就算不使用SRP Batcher,默认网格的顶点数据也会在初始化的时候加载到GPU】,模型变换矩阵,材质参数等)了,在DrawCall的准备阶段只需要进行绑定。即便DrawCall没有减少,但是由于每次DrawCall都只需要绑定资源,同时DrawCall本身也只是个绘制调用,所以每次DrawCall的时间大大降低了。
在这里插入图片描述
**数据不再每帧被重新创建:**这部分在RenderDoc中没能找到佐证的部分,可能是我用的方法有问题。
❗使用MaterialPropertyBlock重写的材质无法被SRP Bathcer合批处理。

unity/tutorials/custom-srp/draw-calls

简单看下结果,使用GPU Instancing绘制大量网格,只需要3个Batches即可绘制完毕。

在这里插入图片描述

不使用GPU Instancing,需要501次Batches(500次绘制球,一次绘制天空盒)。

在这里插入图片描述

cutoff / blend

在这里插入图片描述

❗记得在新创建的材质中勾选Enable GPU Instancing。

相关文章:

Unity SRP学习笔记(二)

Unity SRP学习笔记(二) 主要参考: https://catlikecoding.com/unity/tutorials/custom-srp/ https://docs.unity.cn/cn/2022.3/ScriptReference/index.html 中文教程部分参考(可选): https://tuncle.blog/c…...

数据库第五次作业

一要求 二建库建表 触发器 存储过程 三查询 触发器 1 建立触发器,订单表中增加订单数量后,商品表商品数量同步减少对应的商品订单出数量,并测试 测试 2 建立触发器,实现功能:客户取消订单,恢复商品表对应商品的数量 测试 3…...

健身房业务流程优化:SpringBoot解决方案

3系统分析 3.1可行性分析 通过对本健身房管理系统实行的目的初步调查和分析,提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本健身房管理系统采用SSM框架,JAVA作为开发语言&a…...

【产品经理】工业互联网企业上市之路

树根互联2022年6月2日提交招股书之后,因财务资料超过六个月有效期加三个月延长期,2022年9月30日上市审核中止;2022年12月26日树根互联更新了2022年半年度财务资料,又九个月过去了,其上市进程将面临再一次中止。 处于上…...

Java学习教程,从入门到精通,Java对象和类语法知识点(20)

1、Java对象和类语法知识点 类的定义 使用class关键字定义类。类名通常使用大写驼峰命名法(PascalCase)。类与对象 类是创建对象的模板或蓝图,它定义了对象的属性和行为。对象是类的实例,它包含了类定义的数据(属性&am…...

金融场中的量化交易:民锋数据驱动策略的优势解析市

随着科技的发展,量化交易成为金融市场的重要组成部分。民锋公司通过智能算法和大数据分析,设计了一系列量化交易策略,帮助投资者实现科学投资。本文将探讨民锋在数据驱动策略上的优势,并展示如何通过量化模型在复杂的市场中获得收…...

Docker 配置镜像加速

docker 拉取代码时出现 ERROR: failed to solve: node:16: unexpected status from HEAD request to https:// xxxxxx.mirror.aliyuncs.com/v2/library/node/m…...

HTTP慢速攻击原理及解决办法

目录 引言 HTTP慢速攻击原理 解决办法 Nginx Tomcat 华宇TAS IIS 结论 引言 HTTP慢速攻击(Slow HTTP Attack)是一种拒绝服务攻击(DoS),攻击者通过故意缓慢地发送HTTP请求来耗尽服务器资源,导致合法…...

【系统面试篇】进程和线程类(1)(笔记)——区别、通讯方式、同步、互斥、锁分类

目录 一、问题综述 1. 进程和线程的区别? 2. 进程的状态有哪些? 3. 进程之间的通信方式? (1)管道 (2)消息队列 (3)共享内存 (4)信号量 &#xff08…...

[C++]——哈希(附源码)

目录 ​编辑 ​编辑 一、前言 二、正文 2.1 unorder系列关联式容器 2.1.1 unordered_map 2.1.1.1 unorderer_map的介绍 ①unordered_map的构造 ②unordered_map的容量 ③unordered_map的迭代器 ④unordered_map的元素访问 ⑤unordered_map的查询 ⑥unordered_map的修改操…...

2024中国自动化大会(CAC2024)“智慧化工及复合人才培养”平行会议圆满落幕

2024中国自动化大会于11月1-3日在青岛举行,本次大会由中国自动化学会主办,青岛科技大学(简称“青科大”)承办。北京和隆优化科技股份有限公司(简称“和隆优化”)承办了重要的“智慧化工及复合人才培养”平行…...

计算机毕业设计——ssm基于JAVA的求职招聘网站的设计与实现演示录像 2021

作者:程序媛9688开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等。 🌟文末获取源码数据库🌟感兴趣的可以先收藏起来,还有大家在毕设选题(免费咨询指导选题)&#xff0…...

跨平台Flutter 、ReactNative 开发原理

一、跨平台Flutter开发原理 Flutter是一个跨平台的应用程序开发框架,它允许你使用一组代码库来构建同时运行在Android和iOS上的应用程序。 1.1.Flutter的核心原理基于以下几点: Dart异步、Widget构建块灵活配置、自工化工具链、热重载、Skia图库、Dar…...

qt QToolBar详解

1、概述 QToolBar是Qt框架中的一个控件,用于在工具栏中显示一组操作按钮和其他控件。它提供了一种方便的方式来组织和管理应用程序中的工具和操作。工具栏通常位于软件或应用程序界面的上方,包含一系列常用工具和命令按钮,用于快速访问和执行…...

MongoDB基础介绍以及从0~1语法介绍

目录 MongoDB 教程导读 NoSQL 简介 关系型数据库遵循ACID规则 分布式系统 分布式计算的优点 分布式计算的缺点 什么是NoSQL? 为什么使用NoSQL ? RDBMS vs NoSQL NoSQL 简史 CAP定理(CAP theorem) NoSQL的优点/缺点 BASE ACID vs BASE N…...

利用Docker Compose构建微服务架构

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 利用Docker Compose构建微服务架构 引言 Docker Compose 简介 安装 Docker Compose 创建项目结构 编写 Dockerfile 前端 Dockerf…...

数据中台一键大解析!

自从互联玩企业掀起了数据中台风,数据中台这个点马上就火起来了,短短几年数据中台就得到了极高的热度,一大堆企业也在跟风做数据中台,都把数据中台作为企业数字化转型的救命稻草,可是如果我告诉你数据中台并不是万能钥…...

MySQL45讲 第十六讲 “order by”是怎么工作的?

文章目录 MySQL45讲 第十六讲 “order by”是怎么工作的?一、引言二、全字段排序(一)索引创建与执行情况分析(二)执行流程(三)查看是否使用临时文件 三、rowid 排序(一)参…...

智慧商城项目-VUE2

实现效果 项目收获 通过本项目的练习,可以掌握以下内容: 创建项目 ##基本创建 基于 VueCli 自定义创建项目架子,并对相关的配置进行选择 vue create demo-shopping调整目录 删除文件 删除初始化的一些默认文件 src/assets/logo.pngsrc/components…...

音视频入门基础:FLV专题(22)——FFmpeg源码中,获取FLV文件音频信息的实现(中)

本文接着《音视频入门基础:FLV专题(21)——FFmpeg源码中,获取FLV文件音频信息的实现(上)》,继续讲解FFmpeg获取FLV文件的音频信息到底是从哪个地方获取的。本文的一级标题从“四”开始。 四、音…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)

前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 ​…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键&#xff…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

Axure Rp 11 安装、汉化、授权

Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接:https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...

多模态学习路线(2)——DL基础系列

目录 前言 一、归一化 1. Layer Normalization (LN) 2. Batch Normalization (BN) 3. Instance Normalization (IN) 4. Group Normalization (GN) 5. Root Mean Square Normalization(RMSNorm) 二、激活函数 1. Sigmoid激活函数(二分类&…...