UE4 RHI(2)
在上篇简单说明RHI的作用后, 我们在引擎中探索一下RHI的种种细节与实现. 在解决方案资源管理器中搜索RHI, 会有这些文件:
(1)对应不同运行平台的PlatformDynamicRHI.cpp(上篇有提到, 主要进行硬件接口创建前的配置并创建不同绘图接口对应的RHI). 由于目标平台在打包时就确定了, 在打包时会选择编译这些PlatformDynamicRHI.cpp中的一个, 而其他平台的.cpp会被绕过编译.
                    
(2)对应不同绘图接口的DynamicRHI. 可以看到有DX11, DX12, Vulkan, Metal的RHI模块.
      

  一顿找却没有找到OpenGL的DynamicRHI, 在管理器搜索发现被集成进了OpenGLDrv.h中:
  (3)RHI的具体功能实现部分:
                    
重点探索第三部分.
1.首先看RHI.h/RHI.cpp:
在这部分, 引擎会定义各种RHI的公共变量, 比如当前显卡硬件的代号以及驱动版本:
/**
* only set if RHI has the information (after init of the RHI and only if RHI has that information, never changes after that)
* e.g. "NVIDIA GeForce GTX 670"
*/
extern RHI_API FString GRHIAdapterName;
extern RHI_API FString GRHIAdapterInternalDriverVersion;
extern RHI_API FString GRHIAdapterUserDriverVersion;
extern RHI_API FString GRHIAdapterDriverDate;
extern RHI_API uint32 GRHIDeviceId;
extern RHI_API uint32 GRHIDeviceRevision; // 0 means not defined yet, use functions like IsRHIDeviceAMD() to access
extern RHI_API uint32 GRHIVendorId; // to trigger GPU specific optimizations and fallbacks
RHI_API bool IsRHIDeviceAMD(); // to trigger GPU specific optimizations and fallbacks
RHI_API bool IsRHIDeviceIntel(); // to trigger GPU specific optimizations and fallbacks
RHI_API bool IsRHIDeviceNVIDIA();
以及各项着色/绘图特性, 例如在深度测试时获取深度, 体纹理, 硬件合并渲染, 对MSAA的支持, 对各种RenderTarget格式的支持程度, 乃至于光栅化等. 这些特性因硬件平台、绘图接口以及显示驱动的不同会有种种差异.
2.DynamicRHI
在这部分会有FDynamicRHI接口的定义以及图形渲染所需要的各种基础接口如创建Buffer, 创建Texture以及创建着色器等:
/** The interface which is implemented by the dynamically bound RHI. */
class RHI_API FDynamicRHI
{
public: ....
}
例如创建PixelShader和VertexShader这一类操作:
// FlushType: Wait RHI Thread
virtual FPixelShaderRHIRef RHICreatePixelShader(FRHIShaderLibraryParamRef Library, FSHAHash Hash)
{
return nullptr;
} // FlushType: Wait RHI Thread
virtual FVertexShaderRHIRef RHICreateVertexShader(const TArray<uint8>& Code) = ; // FlushType: Wait RHI Thread
virtual FVertexShaderRHIRef RHICreateVertexShader(FRHIShaderLibraryParamRef Library, FSHAHash Hash)
{
return nullptr;
}
3.FRenderResource
这也是我们应用中实际操作RHI时较多使用的部分, 定义了许多创建渲染资源时需要的类, 以及众多选择进行实现的函数. 首先看资源类基类FRenderResource:
/**
* A rendering resource which is owned by the rendering thread.
*/
class RENDERCORE_API FRenderResource
{
public:
/** @return The global initialized resource list. */
static TLinkedList<FRenderResource*>*& GetResourceList();
...
/**
* Initializes the RHI resources used by this resource.
* Called when entering the state where both the resource and the RHI have been initialized.
* This is only called by the rendering thread.
*/
virtual void InitRHI() {} /**
* Releases the RHI resources used by this resource.
* Called when leaving the state where both the resource and the RHI have been initialized.
* This is only called by the rendering thread.
*/
virtual void ReleaseRHI() {}
...
}
会有众多纯虚函数暴露出来供我们选择重写. 具体接口功能在注释中有很完备的写出. 如果要具体地操作RHI, 这个资源类也会是我们进行了解与学习的入口, 这些接口是首先要了解的内容.
例如创建一个简单的IndexBuffer, 我们会在RenderResource中发现IndexBuffer的基类:
/** An index buffer resource. */
class FIndexBuffer : public FRenderResource
{
public:
FIndexBufferRHIRef IndexBufferRHI; /** Destructor. */
virtual ~FIndexBuffer() {} // FRenderResource interface.
virtual void ReleaseRHI() override
{
IndexBufferRHI.SafeRelease();
}
virtual FString GetFriendlyName() const override { return TEXT("FIndexBuffer"); }
};
会了解到对于这个IndexBuffer对应的Resource, 引擎为我们实现了Realease, 而Init的工作就需要我们亲力完成. 即可如此创建子类:
/* Index Buffer */
class FMyMeshIndexBuffer : public FIndexBuffer
{
public:
int32 NumIndices;
virtual void InitRHI() override
{
FRHIResourceCreateInfo CreateInfo;
IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), NumIndices * sizeof(int32), BUF_Dynamic, CreateInfo);
}
};
此外会有常用接口如:
//开始创建Resource
void BeginInitResource(FRenderResource* Resource)
{
ENQUEUE_RENDER_COMMAND(InitCommand)(
[Resource](FRHICommandListImmediate& RHICmdList)
{
Resource->InitResource();
});
}
//开始更新ResouceRHI
void BeginUpdateResourceRHI(FRenderResource* Resource)
{
ENQUEUE_RENDER_COMMAND(UpdateCommand)(
[Resource](FRHICommandListImmediate& RHICmdList)
{
Resource->UpdateRHI();
});
}
...
4. RHICommandList
在看源码之前先要了解一下RHICommandList的机制: RHI的指令会压入RHICommandList中, 而这个List本身是存放在渲染线程中的. RHI的工作是管理各个RHI对象, 在不干扰渲染线程工作的情况下把这些对象的Command压入到渲染线程的CommanList中, 以及为渲染线程做各种复杂的异步操作而不干涉到渲染线程的正常工作.(皇上我把文件都给你整理好了你批就行了...)
FRHICommandBase & FRHICommand: 初看到FRHICommandBase时会略感懵逼...
struct FRHICommandBase
{
FRHICommandBase* Next = nullptr;
virtual void ExecuteAndDestruct(FRHICommandListBase& CmdList, FRHICommandListDebugContext& DebugContext) = ;
};
其实看类名和虚函数名可以看出这样一个结构体是一条RHI指令的一个基类, 其中的CommanBase空指针就是为了构成CommanList链表而生的. 每个Command下的函数指针会携带一条指令, 而Next指针又会指向下一条Command, 渲染线程只需不停地读这个链表执行这些指令进行工作就可以了.
而在其之下会有一个类模板子类FRHICommand:
template<typename TCmd>
struct FRHICommand : public FRHICommandBase
{
void ExecuteAndDestruct(FRHICommandListBase& CmdList, FRHICommandListDebugContext& Context) override final
{
TCmd *ThisCmd = static_cast<TCmd*>(this);
#if RHI_COMMAND_LIST_DEBUG_TRACES
ThisCmd->StoreDebugInfo(Context);
#endif
ThisCmd->Execute(CmdList);
ThisCmd->~TCmd();
} virtual void StoreDebugInfo(FRHICommandListDebugContext& Context) {};
};
需要模板TCmd实现方法Execute. 而此子类在执行ExecuteAndDestruct时会退化到参数类型TCmd, 执行TCmd下的Execute方法后自动析构, 这个过程也就实现了所谓"ExecuteAndDestruct".
UE4 RHI(2)的更多相关文章
- UE4 RHI与Render模块简解
		
UE4中的RHI指的是Render hardware interface,作用像Ogre里的RenderSystem,针对Dx11,Dx12,Opengl等等平台抽象出相同的接口,我们能方便能使用相同 ...
 - UE4 RHI与条件式编译
		
RHI即RenderHardwareInterface, 即渲染硬件接口, 是UE为实现跨平台而实现的一套API. 每个RHI接口都为OpenGL, Vulkan, DX11等做了不同的实现. 在引擎 ...
 - UE4入门与精通
		
由于目前在使用UE4引擎,多少也有一些心得,比如在日常使用中会遇到一些问题.坑(潜规则)或者一些使用技巧等.本人决定开一个大坑,主要有两个目的:一是可以自己做个记录,二是可以给大家提供一些参考吧.主要 ...
 - UE4 在C++ 动态生成几何、BSP体、Brush ---- Mesh_Generation
		
截至UE4 4.10 runtime 无法生成BSP类 ,只能通过自定义的Mesh的Vertex 进行绘制 ( Google 考证,能改UE4源码的请忽略 ) 可用到的 UE4 集成的Render ...
 - 移植UE4的模型操作到Unity中
		
最近在Unity上要写一个东东,功能差不多就是在Unity编辑器上的旋转,移动这些,在手机上也能比较容易操作最好,原来用Axiom3D写过一个类似的,有许多位置并不好用,刚好在研究UE4的源码,在模型 ...
 - UE4/Unity3D中同时捕获多高清摄像头的高效插件
		
本文主要讲实现过程的一些坑. 先说下要实现的目标,主要功能在UE4/Unity中都要用,能同时捕获多个摄像头,并且捕获的图片要达到1080p25桢上,并且需要经过复杂的图片处理后丢给UE4/Unity ...
 - UE4命令行使用,解释
		
命令行在外部 从命令行运行编辑项目 1 导航到您的[LauncherInstall][VersionNumber]\Engine\Binaries\Win64 目录中. 2 右键单击上 UE4Edit ...
 - 初探UE4中的Profiling【转】
		
http://blog.ch-wind.com/ue4-profiling-preview/ Profililng是成品制作过程中非常重要的一个步骤,通过Profiling才能提高运行效率使得作品达到 ...
 - 探究光线追踪技术及UE4的实现
		
目录 一.光线追踪概述 1.1 光线追踪是什么 1.2 光线追踪的特点 1.3 光线追踪的历史 1.4 光线追踪的应用 二.光线追踪的原理 2.1 光线追踪的物理原理 2.2 光线追踪算法 2.3 R ...
 
随机推荐
- ftp定时下载指定目录或文件脚本
			
#! /bin/bash rpm -qa lftp &>/dev/null || yum install -y lftp lftp 160.106.0.34 << EOF c ...
 - 关于STM32的I2C硬件DMA实现
			
关于STM32的I2C硬件DMA实现 网上看到很多说STM32的I2C很难用,但我觉得还是理解上的问题,STM32的I2C确实很复杂,但只要基础牢靠,并没有想象中的那么困难. 那么就先从基础说起,只说 ...
 - Anaconda 下 Jupyter 更改默认启动路径和默认浏览器
			
1.Jupyter 更改默认启动路径方法 输入jupyter notebook --generate-config 会生成jupyter_notebook_config.py 找到文件,并打开 将 # ...
 - retrying failed action with response code: 403 错误解决
			
[2019-06-10T06:52:51,610][INFO ][logstash.outputs.elasticsearch] retrying failed action with respons ...
 - 小tip: 使用SVG寥寥数行实现圆环loading进度效果(转载)
			
设计师设计了一个图片上传圆环loading进度效果.如下截图: 首先,CSS3是可以实现的,以前写过一篇转大饼的文章:“CSS3实现鸡蛋饼饼状图loading等待转转转”.原理跟这个一模一样,两个半区 ...
 - IntelliJ IDEA 创建 Git 分支并且 Push 到远程
			
在 IntelliJ 的右下角,你可以看到当前的 Git 分支,然后你可以单击这个分支后,在弹出的界面的最上方有一个新建分支的选项. 然后再弹出的界面中,输入你要创建的分支名称后回车输入. 然后从项目 ...
 - 小米oj 帮小学生排队(排序+插入)
			
帮小学生排队 序号:#18难度:有挑战时间限制:1000ms内存限制:10M 描述 用一个数组表示一群正在排队的小学生,每个小学生用一对整数 H, K 来表示:H 表示这个小学生的身高,K 表示这个 ...
 - 【CUDA 基础】5.3 减少全局内存访问
			
title: [CUDA 基础]5.3 减少全局内存访问 categories: - CUDA - Freshman tags: - 共享内存 - 归约 toc: true date: 2018-06 ...
 - 2016 ACM-ICPC NEERC F. Foreign Postcards (概率DP)
			
2016 ACM-ICPC NEERC F. Foreign Postcards 题意:有一串由C.W组成的字符串,每次截取长度为k(1<=k<=n且k随机)的前缀,如果该前缀首位为W,则 ...
 - npm修改源
			
获取原来的镜像地址 npm get registry 修改源 npm config set registry http://registry.npm.taobao.org/