当前位置: 首页 > 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文件的音频信息到底是从哪个地方获取的。本文的一级标题从“四”开始。 四、音…...

Chrome与火狐哪个浏览器的性能表现更好

在数字时代,浏览器是我们日常生活中不可或缺的工具。无论是工作、学习还是娱乐,一个好的浏览器都能显著提高我们的效率和体验。市场上有许多优秀的浏览器,其中Google Chrome和Mozilla Firefox无疑是最受欢迎的两款。本文将比较这两款浏览器的…...

uniapp在js方法中,获取当前用户的uid(uni-id-user)表中的用户id

// 1.判断当前用的权限 let uid uniCloud.getCurrentUserInfo().uid //获取当前用户的uid // 用户uid等于发布者id或者用户权限等于admin或者用户角色等于webmaster if (uid this.item.user_id[0]._id || this.uniIDHasRole…...

影响神经网络速度的因素- FLOPs、MAC、并行度以及计算平台

影响神经网络速度的四个主要因素分别是 FLOPs(浮点操作数)、MAC(内存访问成本)、并行度以及计算平台。这些因素共同作用,直接影响到神经网络的计算速度和资源需求。 1. FLOPs(Floating Point Operations&a…...

【万字详解】如何在微信小程序的 Taro 框架中设置静态图片 assets/image 的 Base64 转换上限值

设置方法 mini 中提供了 imageUrlLoaderOption 和 postcss.url 。 其中: config.limit 和 imageUrlLoaderOption.limit 服务于 Taro 的 MiniWebpackModule.js , 值的写法要 ()KB * 1024。 config.maxSize 服务于 postcss-url 的…...

复合选择器,CSS特性,背景属性,显示模式(HTML)

目录 复合选择器,CSS特性,背景属性,显示模式知识点: 练习一: 练习二: 复合选择器,CSS特性,背景属性,显示模式知识点: 复合选择器:后代选择器 :父选择器 子选择器(中间用空格隔开) eg:对div中的span进行设置,会对后代中的所有span都进行设置 选中所有后代(后代选择器.html)…...

加密货币行业与2024年美国大选

加密货币行业经历了近十年的飞速发展,尤其是在比特币、以太坊等主要加密资产的兴起之后,越来越多的美国人开始将其视为一种财富积累或交易的工具。然而,尽管这一新兴行业的市场规模在持续扩大,但加密货币仍面临着重重监管难题&…...

Hive SQL中判断内容包含情况的全面指南

Hive SQL中判断内容包含情况的实用指南 在 Hive SQL 的数据处理与分析世界里,判断字段是否包含特定内容是一项非常重要的操作。今天,我将为大家详细介绍 Hive SQL 中实现这一功能的多种方法,并附上相应的表创建和数据插入语句。 一、准备工作 - 表创建与数据插入 首先,我…...

匿名管道 Linux

目录 管道 pipe创建一个管道 让子进程写入,父进程读取 如何把消息发送/写入给父进程 父进程该怎么读取呢 管道本质 结论:管道的特征: 测试管道大小 写端退了,测试结果 测试子进程一直写,父进程读一会就退出 …...

苍穹外卖WebSocket无法建立连接 (修改前端代码)

我在部署nginx 反向代理服务器时,把80端口改成了90端口(不与80端口的Tomcat冲突)。 但黑马的资料里定义了前端连接nginx的端口号默认为80,造成连接不上的问题,此时只需要修改前端的端口号,使其知道如何连接到修改后的后端端口。 …...

音频内容理解

音频内容理解是音频处理和理解领域的一个重要方向,它涉及到从环境声音中提取语义信息,并能够对这些声音进行解释和描述。以下是音频内容理解的几个关键应用: 1. 音频问答(Audio Question Answering, AQA) 在这个任务…...