RHI全称是Render Hardware Interface(渲染硬件接口),封装了众多图形API(DirectX、OpenGL、Vulkan、Metal)之间的差异
基于D3D11 API设计而成,包含了资源管理(Shader、Texture、VertexBuffer等)和图形API封装(DrawIndexedPrimitive、Clear、SetTexture等)。
对Game和Renderer模块提供了简便且一致的概念、数据、资源和接口,实现一份渲染代码跑在多个平台的目标。
RHI相关的测试代码:UnrealEngine\Engine\Plugins\Tests\RHITests\Source\RHITests
资源管理部分详见:UE4之RHI资源管理。本文重点讲 图形API封装。
IRHICommandContext
IRHICommandContext是RHI的命令上下文接口类,定义了一组图形API相关的操作。在可以并行处理命令列表的平台上,它是一个单独的对象。它和相关继承类型定义如下:
// Engine\Source\Runtime\RHI\Public\RHIContext.h
// 能够执行计算工作的上下文。可以在gfx管道上执行异步或计算.
class IRHIComputeContext
{
public:
virtual ~IRHIComputeContext();
// 设置/派发计算着色器.
virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) = 0;
virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState);
virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) = 0;
virtual void RHIDispatchIndirectComputeShader(FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
virtual void RHISetAsyncComputeBudget(EAsyncComputeBudget Budget) {}
// 转换资源.
virtual void RHIBeginTransitions(TArrayView<const FRHITransition*> Transitions) = 0;
virtual void RHIEndTransitions(TArrayView<const FRHITransition*> Transitions) = 0;
// UAV
virtual void RHIClearUAVFloat(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FVector4& Values) = 0;
virtual void RHIClearUAVUint(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FUintVector4& Values) = 0;
virtual void RHIBeginUAVOverlap() {}
virtual void RHIEndUAVOverlap() {}
virtual void RHIBeginUAVOverlap(TArrayView<FRHIUnorderedAccessView* const> UAVs) {}
virtual void RHIEndUAVOverlap(TArrayView<FRHIUnorderedAccessView* const> UAVs) {}
// 着色器参数.
virtual void RHISetShaderTexture(FRHIComputeShader* PixelShader, uint32 TextureIndex, FRHITexture* NewTexture) = 0;
virtual void RHISetShaderSampler(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHISamplerState* NewState) = 0;
virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV) = 0;
virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV, uint32 InitialCount) = 0;
virtual void RHISetShaderResourceViewParameter(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHIShaderResourceView* SRV) = 0;
virtual void RHISetShaderUniformBuffer(FRHIComputeShader* ComputeShader, uint32 BufferIndex, FRHIUniformBuffer* Buffer) = 0;
virtual void RHISetShaderParameter(FRHIComputeShader* ComputeShader, uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue) = 0;
virtual void RHISetGlobalUniformBuffers(const FUniformBufferStaticBindings& InUniformBuffers);
// 压入/弹出事件.
virtual void RHIPushEvent(const TCHAR* Name, FColor Color) = 0;
virtual void RHIPopEvent() = 0;
// 其它接口.
virtual void RHISubmitCommandsHint() = 0;
virtual void RHIInvalidateCachedState() {}
virtual void RHICopyToStagingBuffer(FRHIVertexBuffer* SourceBufferRHI, FRHIStagingBuffer* DestinationStagingBufferRHI, uint32 InOffset, uint32 InNumBytes);
virtual void RHIWriteGPUFence(FRHIGPUFence* FenceRHI);
virtual void RHISetGPUMask(FRHIGPUMask GPUMask);
// 加速结构.
virtual void RHIBuildAccelerationStructure(FRHIRayTracingGeometry* Geometry);
virtual void RHIBuildAccelerationStructures(const TArrayView<const FAccelerationStructureBuildParams> Params);
virtual void RHIBuildAccelerationStructure(FRHIRayTracingScene* Scene);
// 获取计算上下文.
inline IRHIComputeContext& GetLowestLevelContext() { return *this; }
inline IRHIComputeContext& GetHighestLevelContext() { return *this; }
};
// 命令上下文.
class IRHICommandContext : public IRHIComputeContext
{
public:
virtual ~IRHICommandContext();
// 派发计算.
virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) = 0;
virtual void RHIDispatchIndirectComputeShader(FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
// 渲染查询.
virtual void RHIBeginRenderQuery(FRHIRenderQuery* RenderQuery) = 0;
virtual void RHIEndRenderQuery(FRHIRenderQuery* RenderQuery) = 0;
virtual void RHIPollOcclusionQueries();
// 开启/结束接口.
virtual void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) = 0;
virtual void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) = 0;
virtual void RHIBeginFrame() = 0;
virtual void RHIEndFrame() = 0;
virtual void RHIBeginScene() = 0;
virtual void RHIEndScene() = 0;
virtual void RHIBeginUpdateMultiFrameResource(FRHITexture* Texture);
virtual void RHIEndUpdateMultiFrameResource(FRHITexture* Texture);
virtual void RHIBeginUpdateMultiFrameResource(FRHIUnorderedAccessView* UAV);
virtual void RHIEndUpdateMultiFrameResource(FRHIUnorderedAccessView* UAV);
// 设置数据.
virtual void RHISetStreamSource(uint32 StreamIndex, FRHIVertexBuffer* VertexBuffer, uint32 Offset) = 0;
virtual void RHISetViewport(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ) = 0;
virtual void RHISetStereoViewport(...);
virtual void RHISetScissorRect(bool bEnable, uint32 MinX, uint32 MinY, uint32 MaxX, uint32 MaxY) = 0;
virtual void RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, bool bApplyAdditionalState) = 0;
// 设置着色器参数.
virtual void RHISetShaderTexture(FRHIGraphicsShader* Shader, uint32 TextureIndex, FRHITexture* NewTexture) = 0;
virtual void RHISetShaderTexture(FRHIComputeShader* PixelShader, uint32 TextureIndex, FRHITexture* NewTexture) = 0;
virtual void RHISetShaderSampler(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHISamplerState* NewState) = 0;
virtual void RHISetShaderSampler(FRHIGraphicsShader* Shader, uint32 SamplerIndex, FRHISamplerState* NewState) = 0;
virtual void RHISetUAVParameter(FRHIPixelShader* PixelShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV) = 0;
virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV) = 0;
virtual void RHISetUAVParameter(FRHIComputeShader* ComputeShader, uint32 UAVIndex, FRHIUnorderedAccessView* UAV, uint32 InitialCount) = 0;
virtual void RHISetShaderResourceViewParameter(FRHIComputeShader* ComputeShader, uint32 SamplerIndex, FRHIShaderResourceView* SRV) = 0;
virtual void RHISetShaderResourceViewParameter(FRHIGraphicsShader* Shader, uint32 SamplerIndex, FRHIShaderResourceView* SRV) = 0;
virtual void RHISetShaderUniformBuffer(FRHIGraphicsShader* Shader, uint32 BufferIndex, FRHIUniformBuffer* Buffer) = 0;
virtual void RHISetShaderUniformBuffer(FRHIComputeShader* ComputeShader, uint32 BufferIndex, FRHIUniformBuffer* Buffer) = 0;
virtual void RHISetShaderParameter(FRHIGraphicsShader* Shader, uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue) = 0;
virtual void RHISetShaderParameter(FRHIComputeShader* ComputeShader, uint32 BufferIndex, uint32 BaseIndex, uint32 NumBytes, const void* NewValue) = 0;
virtual void RHISetStencilRef(uint32 StencilRef) {}
virtual void RHISetBlendFactor(const FLinearColor& BlendFactor) {}
// 绘制图元.
virtual void RHIDrawPrimitive(uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances) = 0;
virtual void RHIDrawPrimitiveIndirect(FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
virtual void RHIDrawIndexedIndirect(FRHIIndexBuffer* IndexBufferRHI, FRHIStructuredBuffer* ArgumentsBufferRHI, int32 DrawArgumentsIndex, uint32 NumInstances) = 0;
virtual void RHIDrawIndexedPrimitive(FRHIIndexBuffer* IndexBuffer, int32 BaseVertexIndex, uint32 FirstInstance, uint32 NumVertices, uint32 StartIndex, uint32 NumPrimitives, uint32 NumInstances) = 0;
virtual void RHIDrawIndexedPrimitiveIndirect(FRHIIndexBuffer* IndexBuffer, FRHIVertexBuffer* ArgumentBuffer, uint32 ArgumentOffset) = 0;
// 其它接口
virtual void RHISetDepthBounds(float MinDepth, float MaxDepth) = 0;
virtual void RHISetShadingRate(EVRSShadingRate ShadingRate, EVRSRateCombiner Combiner);
virtual void RHISetShadingRateImage(FRHITexture* RateImageTexture, EVRSRateCombiner Combiner);
virtual void RHISetMultipleViewports(uint32 Count, const FViewportBounds* Data) = 0;
virtual void RHICopyToResolveTarget(FRHITexture* SourceTexture, FRHITexture* DestTexture, const FResolveParams& ResolveParams) = 0;
virtual void RHIResummarizeHTile(FRHITexture2D* DepthTexture);
virtual void RHICalibrateTimers();
virtual void RHICalibrateTimers(FRHITimestampCalibrationQuery* CalibrationQuery);
virtual void RHIDiscardRenderTargets(bool Depth, bool Stencil, uint32 ColorBitMask) {}
// 纹理
virtual void RHIUpdateTextureReference(FRHITextureReference* TextureRef, FRHITexture* NewTexture) = 0;
virtual void RHICopyTexture(FRHITexture* SourceTexture, FRHITexture* DestTexture, const FRHICopyTextureInfo& CopyInfo);
virtual void RHICopyBufferRegion(FRHIVertexBuffer* DestBuffer, ...);
// Pass相关.
virtual void RHIBeginRenderPass(const FRHIRenderPassInfo& InInfo, const TCHAR* InName) = 0;
virtual void RHIEndRenderPass() = 0;
virtual void RHINextSubpass();
// 光线追踪.
virtual void RHIClearRayTracingBindings(FRHIRayTracingScene* Scene);
virtual void RHIBuildAccelerationStructures(const TArrayView<const FAccelerationStructureBuildParams> Params);
virtual void RHIBuildAccelerationStructure(FRHIRayTracingGeometry* Geometry) final override;
virtual void RHIBuildAccelerationStructure(FRHIRayTracingScene* Scene);
virtual void RHIRayTraceOcclusion(FRHIRayTracingScene* Scene, ...);
virtual void RHIRayTraceIntersection(FRHIRayTracingScene* Scene, ...);
virtual void RHIRayTraceDispatch(FRHIRayTracingPipelineState* RayTracingPipelineState, ...);
virtual void RHISetRayTracingHitGroups(FRHIRayTracingScene* Scene, ...);
virtual void RHISetRayTracingHitGroup(FRHIRayTracingScene* Scene, ...);
virtual void RHISetRayTracingCallableShader(FRHIRayTracingScene* Scene, ...);
virtual void RHISetRayTracingMissShader(FRHIRayTracingScene* Scene, ...);
(......)
protected:
// 渲染Pass信息.
FRHIRenderPassInfo RenderPassInfo;
};
IRHICommandContext还有许多子类:
- IRHICommandContextPSOFallback:不支持真正的图形管道的RHI命令上下文。
- FNullDynamicRHI:空实现的动态绑定RHI。
- FOpenGLDynamicRHI:OpenGL的动态RHI。
- FD3D11DynamicRHI:D3D11的动态RHI。
- FMetalRHICommandContext:Metal平台的命令上下文。
- FD3D12CommandContextBase:D3D12的命令上下文。
- FVulkanCommandListContext:Vulkan平台的命令队列上下文。
- FEmptyDynamicRHI:动态绑定的RHI实现的接口。
- FValidationContext:校验上下文。
上述的子类中,平台相关的部分子类还继承了FDynamicRHI。
IRHICommandContextPSOFallback比较特殊,它的子类都是不支持并行绘制的图形API(如:OpenGL、D3D11)。
IRHICommandContextPSOFallback定义如下:
class IRHICommandContextPSOFallback : public IRHICommandContext
{
public:
// 设置渲染状态.
virtual void RHISetBoundShaderState(FRHIBoundShaderState* BoundShaderState) = 0;
virtual void RHISetDepthStencilState(FRHIDepthStencilState* NewState, uint32 StencilRef) = 0;
virtual void RHISetRasterizerState(FRHIRasterizerState* NewState) = 0;
virtual void RHISetBlendState(FRHIBlendState* NewState, const FLinearColor& BlendFactor) = 0;
virtual void RHIEnableDepthBoundsTest(bool bEnable) = 0;
// 管线状态.
virtual void RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, bool bApplyAdditionalState) override;
};
IRHICommandContext的核心继承UML图如下:
IRHICommandContextContainer
IRHICommandContextContainer就是包含了IRHICommandContext对象的类型,它和核心继承子类的定义如下:
// Engine\Source\Runtime\RHI\Public\RHICommandList.h
class IRHICommandContextContainer
{
public:
virtual ~IRHICommandContextContainer();
// 获取IRHICommandContext实例.
virtual IRHICommandContext* GetContext();
virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num);
virtual void FinishContext();
};
// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalContext.cpp
class FMetalCommandContextContainer : public IRHICommandContextContainer
{
// FMetalRHICommandContext列表的下一个.
FMetalRHICommandContext* CmdContext;
int32 Index;
int32 Num;
public:
void* operator new(size_t Size);
void operator delete(void *RawMemory);
FMetalCommandContextContainer(int32 InIndex, int32 InNum);
virtual ~FMetalCommandContextContainer() override final;
virtual IRHICommandContext* GetContext() override final;
virtual void FinishContext() override final;
// 提交并释放自己.
virtual void SubmitAndFreeContextContainer(int32 NewIndex, int32 NewNum) override final;
};
// FMetalCommandContextContainer分配器.
static TLockFreeFixedSizeAllocator<sizeof(FMetalCommandContextContainer), PLATFORM_CACHE_LINE_SIZE, FThreadSafeCounter> FMetalCommandContextContainerAllocator;
// Engine\Source\Runtime\D3D12RHI\Private\D3D12CommandContext.cpp
class FD3D12CommandContextContainer : public IRHICommandContextContainer
{
// 适配器.
FD3D12Adapter* Adapter;
// 命令上下文.
FD3D12CommandContext* CmdContext;
// 上下文重定向器.
FD3D12CommandContextRedirector* CmdContextRedirector;
FRHIGPUMask GPUMask;
// 命令队列列表.
TArray<FD3D12CommandListHandle> CommandLists;
public:
void* operator new(size_t Size);
void operator delete(void* RawMemory);
FD3D12CommandContextContainer(FD3D12Adapter* InAdapter, FRHIGPUMask InGPUMask);
virtual ~FD3D12CommandContextContainer() override
virtual IRHICommandContext* GetContext() override;
virtual void FinishContext() override;
virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override;
};
// Engine\Source\Runtime\VulkanRHI\Private\VulkanContext.h
struct FVulkanCommandContextContainer : public IRHICommandContextContainer, public VulkanRHI::FDeviceChild
{
// 命令队列上下文.
FVulkanCommandListContext* CmdContext;
FVulkanCommandContextContainer(FVulkanDevice* InDevice);
virtual IRHICommandContext* GetContext() override final;
virtual void FinishContext() override final;
virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override final;
void* operator new(size_t Size);
void operator delete(void* RawMemory);
};
IRHICommandContextContainer相当于存储了一个或一组命令上下文的容器,以支持并行化地提交命令队列,只在D3D12、Metal、Vulkan等现代图形API中有实现。
完整继承UML图如下:
FDynamicRHI
FDynamicRHI是由动态绑定的RHI实现的接口,它定义的接口和CommandContext比较相似,部分如下:
class RHI_API FDynamicRHI
{
public:
virtual ~FDynamicRHI() {}
virtual void Init() = 0;
virtual void PostInit() {}
virtual void Shutdown() = 0;
void InitPixelFormatInfo(const TArray<uint32>& PixelFormatBlockBytesIn);
// ---- RHI接口 ----
// 下列接口要求FlushType: Thread safe
virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) = 0;
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) = 0;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) = 0;
virtual FBlendStateRHIRef RHICreateBlendState(const FBlendStateInitializerRHI& Initializer) = 0;
// 下列接口要求FlushType: Wait RHI Thread
virtual FVertexDeclarationRHIRef RHICreateVertexDeclaration(const FVertexDeclarationElementList& Elements) = 0;
virtual FPixelShaderRHIRef RHICreatePixelShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
virtual FVertexShaderRHIRef RHICreateVertexShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
virtual FHullShaderRHIRef RHICreateHullShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
virtual FDomainShaderRHIRef RHICreateDomainShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
virtual FGeometryShaderRHIRef RHICreateGeometryShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
virtual FComputeShaderRHIRef RHICreateComputeShader(TArrayView<const uint8> Code, const FSHAHash& Hash) = 0;
// FlushType: Must be Thread-Safe.
virtual FRenderQueryPoolRHIRef RHICreateRenderQueryPool(ERenderQueryType QueryType, uint32 NumQueries = UINT32_MAX);
inline FComputeFenceRHIRef RHICreateComputeFence(const FName& Name);
virtual FGPUFenceRHIRef RHICreateGPUFence(const FName &Name);
virtual void RHICreateTransition(FRHITransition* Transition, ERHIPipeline SrcPipelines, ERHIPipeline DstPipelines, ERHICreateTransitionFlags CreateFlags, TArrayView<const FRHITransitionInfo> Infos);
virtual void RHIReleaseTransition(FRHITransition* Transition);
// FlushType: Thread safe.
virtual FStagingBufferRHIRef RHICreateStagingBuffer();
virtual void* RHILockStagingBuffer(FRHIStagingBuffer* StagingBuffer, FRHIGPUFence* Fence, uint32 Offset, uint32 SizeRHI);
virtual void RHIUnlockStagingBuffer(FRHIStagingBuffer* StagingBuffer);
// FlushType: Thread safe, but varies depending on the RHI
virtual FBoundShaderStateRHIRef RHICreateBoundShaderState(FRHIVertexDeclaration* VertexDeclaration, FRHIVertexShader* VertexShader, FRHIHullShader* HullShader, FRHIDomainShader* DomainShader, FRHIPixelShader* PixelShader, FRHIGeometryShader* GeometryShader) = 0;
// FlushType: Thread safe
virtual FGraphicsPipelineStateRHIRef RHICreateGraphicsPipelineState(const FGraphicsPipelineStateInitializer& Initializer);
// FlushType: Thread safe, but varies depending on the RHI
virtual FUniformBufferRHIRef RHICreateUniformBuffer(const void* Contents, const FRHIUniformBufferLayout& Layout, EUniformBufferUsage Usage, EUniformBufferValidation Validation) = 0;
virtual void RHIUpdateUniformBuffer(FRHIUniformBuffer* UniformBufferRHI, const void* Contents) = 0;
// FlushType: Wait RHI Thread
virtual FIndexBufferRHIRef RHICreateIndexBuffer(uint32 Stride, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo) = 0;
virtual void* RHILockIndexBuffer(FRHICommandListImmediate& RHICmdList, FRHIIndexBuffer* IndexBuffer, uint32 Offset, uint32 Size, EResourceLockMode LockMode);
virtual void RHIUnlockIndexBuffer(FRHICommandListImmediate& RHICmdList, FRHIIndexBuffer* IndexBuffer);
virtual void RHITransferIndexBufferUnderlyingResource(FRHIIndexBuffer* DestIndexBuffer, FRHIIndexBuffer* SrcIndexBuffer);
// FlushType: Wait RHI Thread
virtual FVertexBufferRHIRef RHICreateVertexBuffer(uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo) = 0;
// FlushType: Flush RHI Thread
virtual void* RHILockVertexBuffer(FRHICommandListImmediate& RHICmdList, FRHIVertexBuffer* VertexBuffer, uint32 Offset, uint32 SizeRHI, EResourceLockMode LockMode);
virtual void RHIUnlockVertexBuffer(FRHICommandListImmediate& RHICmdList, FRHIVertexBuffer* VertexBuffer);
// FlushType: Flush Immediate (seems dangerous)
virtual void RHICopyVertexBuffer(FRHIVertexBuffer* SourceBuffer, FRHIVertexBuffer* DestBuffer) = 0;
virtual void RHITransferVertexBufferUnderlyingResource(FRHIVertexBuffer* DestVertexBuffer, FRHIVertexBuffer* SrcVertexBuffer);
// FlushType: Wait RHI Thread
virtual FStructuredBufferRHIRef RHICreateStructuredBuffer(uint32 Stride, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo) = 0;
// FlushType: Flush RHI Thread
virtual void* RHILockStructuredBuffer(FRHICommandListImmediate& RHICmdList, FRHIStructuredBuffer* StructuredBuffer, uint32 Offset, uint32 SizeRHI, EResourceLockMode LockMode);
virtual void RHIUnlockStructuredBuffer(FRHICommandListImmediate& RHICmdList, FRHIStructuredBuffer* StructuredBuffer);
// FlushType: Wait RHI Thread
virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(FRHIStructuredBuffer* StructuredBuffer, bool bUseUAVCounter, bool bAppendBuffer) = 0;
// FlushType: Wait RHI Thread
virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel) = 0;
// FlushType: Wait RHI Thread
virtual FUnorderedAccessViewRHIRef RHICreateUnorderedAccessView(FRHITexture* Texture, uint32 MipLevel, uint8 Format);
(......)
// RHI帧更新,须从主线程调用,FlushType: Thread safe
virtual void RHITick(float DeltaTime) = 0;
// 阻塞CPU直到GPU执行完成变成空闲. FlushType: Flush Immediate (seems wrong)
virtual void RHIBlockUntilGPUIdle() = 0;
// 开始当前帧,并确保GPU正在积极地工作 FlushType: Flush Immediate (copied from RHIBlockUntilGPUIdle)
virtual void RHISubmitCommandsAndFlushGPU() {};
// 通知RHI准备暂停它.
virtual void RHIBeginSuspendRendering() {};
// 暂停RHI渲染并将控制权交给系统的操作, FlushType: Thread safe
virtual void RHISuspendRendering() {};
// 继续RHI渲染, FlushType: Thread safe
virtual void RHIResumeRendering() {};
// FlushType: Flush Immediate
virtual bool RHIIsRenderingSuspended() { return false; };
// FlushType: called from render thread when RHI thread is flushed
// 仅在FRHIResource::FlushPendingDeletes内的延迟删除之前每帧调用.
virtual void RHIPerFrameRHIFlushComplete();
// 执行命令队列, FlushType: Wait RHI Thread
virtual void RHIExecuteCommandList(FRHICommandList* CmdList) = 0;
// FlushType: Flush RHI Thread
virtual void* RHIGetNativeDevice() = 0;
// FlushType: Flush RHI Thread
virtual void* RHIGetNativeInstance() = 0;
// 获取命令上下文. FlushType: Thread safe
virtual IRHICommandContext* RHIGetDefaultContext() = 0;
// 获取计算上下文. FlushType: Thread safe
virtual IRHIComputeContext* RHIGetDefaultAsyncComputeContext();
// FlushType: Thread safe
virtual class IRHICommandContextContainer* RHIGetCommandContextContainer(int32 Index, int32 Num) = 0;
// 直接由渲染线程调用的接口, 以优化RHI调用.
virtual FVertexBufferRHIRef CreateAndLockVertexBuffer_RenderThread(class FRHICommandListImmediate& RHICmdList, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo, void*& OutDataBuffer);
virtual FIndexBufferRHIRef CreateAndLockIndexBuffer_RenderThread(class FRHICommandListImmediate& RHICmdList, uint32 Stride, uint32 Size, uint32 InUsage, ERHIAccess InResourceState, FRHIResourceCreateInfo& CreateInfo, void*& OutDataBuffer);
(......)
// Buffer Lock/Unlock
virtual void* LockVertexBuffer_BottomOfPipe(class FRHICommandListImmediate& RHICmdList, ...);
virtual void* LockIndexBuffer_BottomOfPipe(class FRHICommandListImmediate& RHICmdList, ...);
(......)
};
以上只显示了部分接口,其中部分接口要求从渲染线程调用,部分须从游戏线程调用。大多数接口在被调用前需刷新指定类型的命令,比如:
class RHI_API FDynamicRHI
{
// FlushType: Wait RHI Thread
void RHIExecuteCommandList(FRHICommandList* CmdList);
// FlushType: Flush Immediate
void RHIBlockUntilGPUIdle();
// FlushType: Thread safe
void RHITick(float DeltaTime);
};
那么调用以上接口的代码如下:
class RHI_API FRHICommandListImmediate : public FRHICommandList
{
void ExecuteCommandList(FRHICommandList* CmdList)
{
// 等待RHI线程.
FScopedRHIThreadStaller StallRHIThread(*this);
GDynamicRHI->RHIExecuteCommandList(CmdList);
}
void BlockUntilGPUIdle()
{
// 调用FDynamicRHI::RHIBlockUntilGPUIdle须刷新RHI.
ImmediateFlush(EImmediateFlushType::FlushRHIThread);
GDynamicRHI->RHIBlockUntilGPUIdle();
}
void Tick(float DeltaTime)
{
// 由于FDynamicRHI::RHITick是Thread Safe(线程安全), 所以不需要调用ImmediateFlush或等待事件.
GDynamicRHI->RHITick(DeltaTime);
}
};
我们继续看FDynamicRHI的子类定义:
// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalDynamicRHI.h
class FMetalDynamicRHI : public FDynamicRHI
{
public:
FMetalDynamicRHI(ERHIFeatureLevel::Type RequestedFeatureLevel);
~FMetalDynamicRHI();
// 设置必要的内部资源
void SetupRecursiveResources();
// FDynamicRHI interface.
virtual void Init();
virtual void Shutdown() {}
virtual const TCHAR* GetName() override { return TEXT("Metal"); }
virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(...) final override;
(......)
private:
// 立即模式上下文.
FMetalRHIImmediateCommandContext ImmediateContext;
// 异步计算上下文.
FMetalRHICommandContext* AsyncComputeContext;
// 顶点声明缓存.
TMap<uint32, FVertexDeclarationRHIRef> VertexDeclarationCache;
};
// Engine\Source\Runtime\D3D12RHI\Private\D3D12RHIPrivate.h
class FD3D12DynamicRHI : public FDynamicRHI
{
static FD3D12DynamicRHI* SingleD3DRHI;
public:
static D3D12RHI_API FD3D12DynamicRHI* GetD3DRHI() { return SingleD3DRHI; }
FD3D12DynamicRHI(const TArray<TSharedPtr<FD3D12Adapter>>& ChosenAdaptersIn, bool bInPixEventEnabled);
virtual ~FD3D12DynamicRHI();
// FDynamicRHI interface.
virtual void Init() override;
virtual void PostInit() override;
virtual void Shutdown() override;
virtual const TCHAR* GetName() override { return TEXT("D3D12"); }
virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
(......)
protected:
// 已选择的适配器.
TArray<TSharedPtr<FD3D12Adapter>> ChosenAdapters;
// AMD AGS工具库上下文.
AGSContext* AmdAgsContext;
// D3D12设备.
inline FD3D12Device* GetRHIDevice(uint32 GPUIndex)
{
return GetAdapter().GetDevice(GPUIndex);
}
(......)
};
// Engine\Source\Runtime\EmptyRHI\Public\EmptyRHI.h
class FEmptyDynamicRHI : public FDynamicRHI, public IRHICommandContext
{
(......)
};
// Engine\Source\Runtime\NullDrv\Public\NullRHI.h
class FNullDynamicRHI : public FDynamicRHI , public IRHICommandContextPSOFallback
{
(......)
};
class OPENGLDRV_API FOpenGLDynamicRHI final : public FDynamicRHI, public IRHICommandContextPSOFallback
{
public:
FOpenGLDynamicRHI();
~FOpenGLDynamicRHI();
// FDynamicRHI interface.
virtual void Init();
virtual void PostInit();
virtual void Shutdown();
virtual const TCHAR* GetName() override { return TEXT("OpenGL"); }
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
virtual FBlendStateRHIRef RHICreateBlendState(const FBlendStateInitializerRHI& Initializer) final override;
(......)
private:
// 计数器.
uint32 SceneFrameCounter;
uint32 ResourceTableFrameCounter;
// RHI设备状态, 独立于使用的底层OpenGL上下文.
FOpenGLRHIState PendingState;
FOpenGLStreamedVertexBufferArray DynamicVertexBuffers;
FOpenGLStreamedIndexBufferArray DynamicIndexBuffers;
FSamplerStateRHIRef PointSamplerState;
// 已创建的视口.
TArray<FOpenGLViewport*> Viewports;
TRefCountPtr<FOpenGLViewport> DrawingViewport;
bool bRevertToSharedContextAfterDrawingViewport;
// 已绑定的着色器状态历史.
TGlobalResource< TBoundShaderStateHistory<10000> > BoundShaderStateHistory;
// 逐上下文状态缓存.
FOpenGLContextState InvalidContextState;
FOpenGLContextState SharedContextState;
FOpenGLContextState RenderingContextState;
// 统一缓冲区.
TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
TMap<GLuint, TPair<GLenum, GLenum>> TextureMipLimits;
// 底层平台相关的数据.
FPlatformOpenGLDevice* PlatformDevice;
// 查询相关.
TArray<FOpenGLRenderQuery*> Queries;
FCriticalSection QueriesListCriticalSection;
// 配置和呈现数据.
FOpenGLGPUProfiler GPUProfilingData;
FCriticalSection CustomPresentSection;
TRefCountPtr<class FRHICustomPresent> CustomPresent;
(......)
};
// Engine\Source\Runtime\RHI\Public\RHIValidation.h
class FValidationRHI : public FDynamicRHI
{
public:
RHI_API FValidationRHI(FDynamicRHI* InRHI);
RHI_API virtual ~FValidationRHI();
virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) override final;
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) override final;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) override final;
(......)
// RHI实例.
FDynamicRHI* RHI;
// 所属的上下文.
TIndirectArray<IRHIComputeContext> OwnedContexts;
// 深度模板状态列表.
TMap<FRHIDepthStencilState*, FDepthStencilStateInitializerRHI> DepthStencilStates;
};
// Engine\Source\Runtime\VulkanRHI\Public\VulkanDynamicRHI.h
class FVulkanDynamicRHI : public FDynamicRHI
{
public:
FVulkanDynamicRHI();
~FVulkanDynamicRHI();
// FDynamicRHI interface.
virtual void Init() final override;
virtual void PostInit() final override;
virtual void Shutdown() final override;;
virtual const TCHAR* GetName() final override { return TEXT("Vulkan"); }
void InitInstance();
virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
(......)
protected:
// 实例.
VkInstance Instance;
TArray<const ANSICHAR*> InstanceExtensions;
TArray<const ANSICHAR*> InstanceLayers;
// 设备.
TArray<FVulkanDevice*> Devices;
FVulkanDevice* Device;
// 视口.
TArray<FVulkanViewport*> Viewports;
TRefCountPtr<FVulkanViewport> DrawingViewport;
// 缓存.
IConsoleObject* SavePipelineCacheCmd = nullptr;
IConsoleObject* RebuildPipelineCacheCmd = nullptr;
// 临界区.
FCriticalSection LockBufferCS;
// 内部接口.
void CreateInstance();
void SelectAndInitDevice();
void InitGPU(FVulkanDevice* Device);
void InitDevice(FVulkanDevice* Device);
(......)
};
// Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11RHIPrivate.h
class D3D11RHI_API FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContextPSOFallback
{
public:
FD3D11DynamicRHI(IDXGIFactory1* InDXGIFactory1,D3D_FEATURE_LEVEL InFeatureLevel,int32 InChosenAdapter, const DXGI_ADAPTER_DESC& ChosenDescription);
virtual ~FD3D11DynamicRHI();
virtual void InitD3DDevice();
// FDynamicRHI interface.
virtual void Init() override;
virtual void PostInit() override;
virtual void Shutdown() override;
virtual const TCHAR* GetName() override { return TEXT("D3D11"); }
// HDR display output
virtual void EnableHDR();
virtual void ShutdownHDR();
virtual FSamplerStateRHIRef RHICreateSamplerState(const FSamplerStateInitializerRHI& Initializer) final override;
virtual FRasterizerStateRHIRef RHICreateRasterizerState(const FRasterizerStateInitializerRHI& Initializer) final override;
virtual FDepthStencilStateRHIRef RHICreateDepthStencilState(const FDepthStencilStateInitializerRHI& Initializer) final override;
(......)
ID3D11Device* GetDevice() const
{
return Direct3DDevice;
}
FD3D11DeviceContext* GetDeviceContext() const
{
return Direct3DDeviceIMContext;
}
IDXGIFactory1* GetFactory() const
{
return DXGIFactory1;
}
protected:
// D3D工厂(接口).
TRefCountPtr<IDXGIFactory1> DXGIFactory1;
// D3D设备.
TRefCountPtr<FD3D11Device> Direct3DDevice;
// D3D设备的立即上下文.
TRefCountPtr<FD3D11DeviceContext> Direct3DDeviceIMContext;
// 线程锁.
FD3D11LockTracker LockTracker;
FCriticalSection LockTrackerCS;
// 视口.
TArray<FD3D11Viewport*> Viewports;
TRefCountPtr<FD3D11Viewport> DrawingViewport;
// AMD AGS工具库上下文.
AGSContext* AmdAgsContext;
// RT, UAV, 着色器等资源.
TRefCountPtr<ID3D11RenderTargetView> CurrentRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT];
TRefCountPtr<FD3D11UnorderedAccessView> CurrentUAVs[D3D11_PS_CS_UAV_REGISTER_COUNT];
ID3D11UnorderedAccessView* UAVBound[D3D11_PS_CS_UAV_REGISTER_COUNT];
TRefCountPtr<ID3D11DepthStencilView> CurrentDepthStencilTarget;
TRefCountPtr<FD3D11TextureBase> CurrentDepthTexture;
FD3D11BaseShaderResource* CurrentResourcesBoundAsSRVs[SF_NumStandardFrequencies][D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT];
FD3D11BaseShaderResource* CurrentResourcesBoundAsVBs[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT];
FD3D11BaseShaderResource* CurrentResourceBoundAsIB;
int32 MaxBoundShaderResourcesIndex[SF_NumStandardFrequencies];
FUniformBufferRHIRef BoundUniformBuffers[SF_NumStandardFrequencies][MAX_UNIFORM_BUFFERS_PER_SHADER_STAGE];
uint16 DirtyUniformBuffers[SF_NumStandardFrequencies];
TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
// 已创建的常量缓冲区.
TArray<TRefCountPtr<FD3D11ConstantBuffer> > VSConstantBuffers;
TArray<TRefCountPtr<FD3D11ConstantBuffer> > HSConstantBuffers;
TArray<TRefCountPtr<FD3D11ConstantBuffer> > DSConstantBuffers;
TArray<TRefCountPtr<FD3D11ConstantBuffer> > PSConstantBuffers;
TArray<TRefCountPtr<FD3D11ConstantBuffer> > GSConstantBuffers;
TArray<TRefCountPtr<FD3D11ConstantBuffer> > CSConstantBuffers;
// 已绑定的着色器状态历史.
TGlobalResource< TBoundShaderStateHistory<10000> > BoundShaderStateHistory;
FComputeShaderRHIRef CurrentComputeShader;
(......)
};
它们的核心继承UML图如下:
需要注意的是,传统图形API(D3D11、OpenGL)除了继承FDynamicRHI,还需要继承IRHICommandContextPSOFallback,因为需要借助后者的接口处理PSO的数据和行为,以保证传统和现代API对PSO的一致处理行为。
也正因为此,现代图形API(D3D12、Vulkan、Metal)不需要继承IRHICommandContext的任何继承体系的类型,单单直接继承FDynamicRHI就可以处理RHI层的所有数据和操作。
既然现代图形API(D3D12、Vulkan、Metal)的DynamicRHI没有继承IRHICommandContext的任何继承体系的类型,那么它们是如何实现FDynamicRHI::RHIGetDefaultContext的接口?
下面以FD3D12DynamicRHI为例:
IRHICommandContext* FD3D12DynamicRHI::RHIGetDefaultContext()
{
FD3D12Adapter& Adapter = GetAdapter();
IRHICommandContext* DefaultCommandContext = nullptr;
if (GNumExplicitGPUsForRendering > 1) // 多GPU
{
DefaultCommandContext = static_cast<IRHICommandContext*>(&Adapter.GetDefaultContextRedirector());
}
else // 单GPU
{
FD3D12Device* Device = Adapter.GetDevice(0);
DefaultCommandContext = static_cast<IRHICommandContext*>(&Device->GetDefaultCommandContext());
}
return DefaultCommandContext;
}
无论是单GPU还是多GPU,都是从FD3D12CommandContext强制转换而来,而FD3D12CommandContext又是IRHICommandContext的子子子类,因此静态类型转换完全没问题。
FD3D11DynamicRHI
FD3D11DynamicRHI包含或引用了若干D3D11平台相关的核心类型,它们的定义如下所示:
// Engine\Source\Runtime\Windows\D3D11RHI\Private\D3D11RHIPrivate.h
class D3D11RHI_API FD3D11DynamicRHI : public FDynamicRHI, public IRHICommandContextPSOFallback
{
(......)
protected:
// D3D工厂(接口).
TRefCountPtr<IDXGIFactory1> DXGIFactory1;
// D3D设备.
TRefCountPtr<FD3D11Device> Direct3DDevice;
// D3D设备的立即上下文.
TRefCountPtr<FD3D11DeviceContext> Direct3DDeviceIMContext;
// 视口.
TArray<FD3D11Viewport*> Viewports;
TRefCountPtr<FD3D11Viewport> DrawingViewport;
// AMD AGS工具库上下文.
AGSContext* AmdAgsContext;
(......)
};
// Engine\Source\Runtime\Windows\D3D11RHI\Private\Windows\D3D11RHIBasePrivate.h
typedef ID3D11DeviceContext FD3D11DeviceContext;
typedef ID3D11Device FD3D11Device;
// Engine\Source\Runtime\Windows\D3D11RHI\Public\D3D11Viewport.h
class FD3D11Viewport : public FRHIViewport
{
public:
FD3D11Viewport(class FD3D11DynamicRHI* InD3DRHI) : D3DRHI(InD3DRHI), PresentFailCount(0), ValidState (0), FrameSyncEvent(InD3DRHI);
FD3D11Viewport(class FD3D11DynamicRHI* InD3DRHI, HWND InWindowHandle, uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat InPreferredPixelFormat);
~FD3D11Viewport();
virtual void Resize(uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat PreferredPixelFormat);
void ConditionalResetSwapChain(bool bIgnoreFocus);
void CheckHDRMonitorStatus();
// 呈现交换链.
bool Present(bool bLockToVsync);
// Accessors.
FIntPoint GetSizeXY() const;
FD3D11Texture2D* GetBackBuffer() const;
EColorSpaceAndEOTF GetPixelColorSpace() const;
void WaitForFrameEventCompletion();
void IssueFrameEvent()
IDXGISwapChain* GetSwapChain() const;
virtual void* GetNativeSwapChain() const override;
virtual void* GetNativeBackBufferTexture() const override;
virtual void* GetNativeBackBufferRT() const overrid;
virtual void SetCustomPresent(FRHICustomPresent* InCustomPresent) override
virtual FRHICustomPresent* GetCustomPresent() const;
virtual void* GetNativeWindow(void** AddParam = nullptr) const override;
static FD3D11Texture2D* GetSwapChainSurface(FD3D11DynamicRHI* D3DRHI, EPixelFormat PixelFormat, uint32 SizeX, uint32 SizeY, IDXGISwapChain* SwapChain);
protected:
// 动态RHI.
FD3D11DynamicRHI* D3DRHI;
// 交换链.
TRefCountPtr<IDXGISwapChain> SwapChain;
// 后渲染缓冲.
TRefCountPtr<FD3D11Texture2D> BackBuffer;
FD3D11EventQuery FrameSyncEvent;
FCustomPresentRHIRef CustomPresent;
(......)
};
FD3D11DynamicRHI绘制成UML图之后如下所示:
FOpenGLDynamicRHI
FOpenGLDynamicRHI相关的核心类型定义如下:
class OPENGLDRV_API FOpenGLDynamicRHI final : public FDynamicRHI, public IRHICommandContextPSOFallback
{
(......)
private:
// 已创建的视口.
TArray<FOpenGLViewport*> Viewports;
// 底层平台相关的数据.
FPlatformOpenGLDevice* PlatformDevice;
};
// Engine\Source\Runtime\OpenGLDrv\Public\OpenGLResources.h
class FOpenGLViewport : public FRHIViewport
{
public:
FOpenGLViewport(class FOpenGLDynamicRHI* InOpenGLRHI,void* InWindowHandle,uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen,EPixelFormat PreferredPixelFormat);
~FOpenGLViewport();
void Resize(uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen);
// Accessors.
FIntPoint GetSizeXY() const;
FOpenGLTexture2D *GetBackBuffer() const;
bool IsFullscreen( void ) const;
void WaitForFrameEventCompletion();
void IssueFrameEvent();
virtual void* GetNativeWindow(void** AddParam) const override;
struct FPlatformOpenGLContext* GetGLContext() const;
FOpenGLDynamicRHI* GetOpenGLRHI() const;
virtual void SetCustomPresent(FRHICustomPresent* InCustomPresent) override;
FRHICustomPresent* GetCustomPresent() const;
private:
FOpenGLDynamicRHI* OpenGLRHI;
struct FPlatformOpenGLContext* OpenGLContext;
uint32 SizeX;
uint32 SizeY;
bool bIsFullscreen;
EPixelFormat PixelFormat;
bool bIsValid;
TRefCountPtr<FOpenGLTexture2D> BackBuffer;
FOpenGLEventQuery FrameSyncEvent;
FCustomPresentRHIRef CustomPresent;
};
// Engine\Source\Runtime\OpenGLDrv\Private\Android\AndroidOpenGL.cpp
// 安卓系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
bool TargetDirty;
void SetCurrentSharedContext();
void SetCurrentRenderingContext();
void SetupCurrentContext();
void SetCurrentNULLContext();
FPlatformOpenGLDevice();
~FPlatformOpenGLDevice();
void Init();
void LoadEXT();
void Terminate();
void ReInit();
};
// Engine\Source\Runtime\OpenGLDrv\Private\Windows\OpenGLWindows.cpp
// Windows系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
FPlatformOpenGLContext SharedContext;
FPlatformOpenGLContext RenderingContext;
TArray<FPlatformOpenGLContext*> ViewportContexts;
bool TargetDirty;
/** Guards against operating on viewport contexts from more than one thread at the same time. */
FCriticalSection* ContextUsageGuard;
};
// Engine\Source\Runtime\OpenGLDrv\Private\Lumin\LuminOpenGL.cpp
// Lumin系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
void SetCurrentSharedContext();
void SetCurrentRenderingContext();
void SetCurrentNULLContext();
FPlatformOpenGLDevice();
~FPlatformOpenGLDevice();
void Init();
void LoadEXT();
void Terminate();
void ReInit();
};
// Engine\Source\Runtime\OpenGLDrv\Private\Linux\OpenGLLinux.cpp
// Linux系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
FPlatformOpenGLContext SharedContext;
FPlatformOpenGLContext RenderingContext;
int32 NumUsedContexts;
FCriticalSection* ContextUsageGuard;
};
// Engine\Source\Runtime\OpenGLDrv\Private\Lumin\LuminGL4.cpp
// Lumin系统的OpenGL设备.
struct FPlatformOpenGLDevice
{
FPlatformOpenGLContext SharedContext;
FPlatformOpenGLContext RenderingContext;
TArray<FPlatformOpenGLContext*> ViewportContexts;
bool TargetDirty;
FCriticalSection* ContextUsageGuard;
};
以上显示不同操作系统,OpenGL设备对象的定义有所不同。实际上,OpenGL上下文也因操作系统而异,下面以Windows为例:
// Engine\Source\Runtime\OpenGLDrv\Private\Windows\OpenGLWindows.cpp
struct FPlatformOpenGLContext
{
// 窗口句柄
HWND WindowHandle;
// 设备上下文.
HDC DeviceContext;
// OpenGL上下文.
HGLRC OpenGLContext;
// 其它实际.
bool bReleaseWindowOnDestroy;
int32 SyncInterval;
GLuint ViewportFramebuffer;
GLuint VertexArrayObject; // one has to be generated and set for each context (OpenGL 3.2 Core requirements)
GLuint BackBufferResource;
GLenum BackBufferTarget;
};
FOpenGLDynamicRHI绘制成的UML图如下所示:
FD3D12DynamicRHI
FD3D12DynamicRHI的核心类型定义如下:
// Engine\Source\Runtime\D3D12RHI\Private\D3D12RHIPrivate.h
class FD3D12DynamicRHI : public FDynamicRHI
{
(......)
protected:
// 已选择的适配器.
TArray<TSharedPtr<FD3D12Adapter>> ChosenAdapters;
// D3D12设备.
inline FD3D12Device* GetRHIDevice(uint32 GPUIndex)
{
return GetAdapter().GetDevice(GPUIndex);
}
(......)
};
// Engine\Source\Runtime\D3D12RHI\Private\D3D12Adapter.h
class FD3D12Adapter : public FNoncopyable
{
public:
void Initialize(FD3D12DynamicRHI* RHI);
void InitializeDevices();
void InitializeRayTracing();
// 资源创建.
HRESULT CreateCommittedResource(...)
HRESULT CreateBuffer(...);
template <typename BufferType>
BufferType* CreateRHIBuffer(...);
inline FD3D12CommandContextRedirector& GetDefaultContextRedirector();
inline FD3D12CommandContextRedirector& GetDefaultAsyncComputeContextRedirector();
FD3D12FastConstantAllocator& GetTransientUniformBufferAllocator();
void BlockUntilIdle();
(......)
protected:
virtual void CreateRootDevice(bool bWithDebug);
FD3D12DynamicRHI* OwningRHI;
// LDA设置拥有一个ID3D12Device
TRefCountPtr<ID3D12Device> RootDevice;
TRefCountPtr<ID3D12Device1> RootDevice1;
TRefCountPtr<IDXGIAdapter> DxgiAdapter;
TRefCountPtr<IDXGIFactory> DxgiFactory;
TRefCountPtr<IDXGIFactory2> DxgiFactory2;
// 每个设备代表一个物理GPU“节点”.
FD3D12Device* Devices[MAX_NUM_GPUS];
FD3D12CommandContextRedirector DefaultContextRedirector;
FD3D12CommandContextRedirector DefaultAsyncComputeContextRedirector;
TArray<FD3D12Viewport*> Viewports;
TRefCountPtr<FD3D12Viewport> DrawingViewport;
(......)
};
// Engine\Source\Runtime\D3D12RHI\Private\D3D12RHICommon.h
class FD3D12AdapterChild
{
protected:
FD3D12Adapter* ParentAdapter;
(......)
};
class FD3D12DeviceChild
{
protected:
FD3D12Device* Parent;
(......)
};
// Engine\Source\Runtime\D3D12RHI\Private\D3D12Device.h
class FD3D12Device : public FD3D12SingleNodeGPUObject, public FNoncopyable, public FD3D12AdapterChild
{
public:
TArray<FD3D12CommandListHandle> PendingCommandLists;
void Initialize();
void CreateCommandContexts();
void InitPlatformSpecific();
virtual void Cleanup();
bool GetQueryData(FD3D12RenderQuery& Query, bool bWait);
ID3D12Device* GetDevice();
void BlockUntilIdle();
bool IsGPUIdle();
FD3D12SamplerState* CreateSampler(const FSamplerStateInitializerRHI& Initializer);
(......)
protected:
// CommandListManager
FD3D12CommandListManager* CommandListManager;
FD3D12CommandListManager* CopyCommandListManager;
FD3D12CommandListManager* AsyncCommandListManager;
FD3D12CommandAllocatorManager TextureStreamingCommandAllocatorManager;
// Allocator
FD3D12OfflineDescriptorManager RTVAllocator;
FD3D12OfflineDescriptorManager DSVAllocator;
FD3D12OfflineDescriptorManager SRVAllocator;
FD3D12OfflineDescriptorManager UAVAllocator;
FD3D12DefaultBufferAllocator DefaultBufferAllocator;
// FD3D12CommandContext
TArray<FD3D12CommandContext*> CommandContextArray;
TArray<FD3D12CommandContext*> FreeCommandContexts;
TArray<FD3D12CommandContext*> AsyncComputeContextArray;
(......)
};
// Engine\Source\Runtime\D3D12RHI\Public\D3D12Viewport.h
class FD3D12Viewport : public FRHIViewport, public FD3D12AdapterChild
{
public:
void Init();
void Resize(uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat PreferredPixelFormat);
void ConditionalResetSwapChain(bool bIgnoreFocus);
bool Present(bool bLockToVsync);
void WaitForFrameEventCompletion();
bool CurrentOutputSupportsHDR() const;
(......)
private:
HWND WindowHandle;
#if D3D12_VIEWPORT_EXPOSES_SWAP_CHAIN
TRefCountPtr<IDXGISwapChain1> SwapChain1;
TRefCountPtr<IDXGISwapChain4> SwapChain4;
#endif
TArray<TRefCountPtr<FD3D12Texture2D>> BackBuffers;
TRefCountPtr<FD3D12Texture2D> DummyBackBuffer_RenderThread;
uint32 CurrentBackBufferIndex_RHIThread;
FD3D12Texture2D* BackBuffer_RHIThread;
TArray<TRefCountPtr<FD3D12Texture2D>> SDRBackBuffers;
TRefCountPtr<FD3D12Texture2D> SDRDummyBackBuffer_RenderThread;
FD3D12Texture2D* SDRBackBuffer_RHIThread;
bool CheckHDRSupport();
void EnableHDR();
void ShutdownHDR();
(......)
};
// Engine\Source\Runtime\D3D12RHI\Private\D3D12CommandContext.h
class FD3D12CommandContextBase : public IRHICommandContext, public FD3D12AdapterChild
{
public:
FD3D12CommandContextBase(class FD3D12Adapter* InParent, FRHIGPUMask InGPUMask, bool InIsDefaultContext, bool InIsAsyncComputeContext);
void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) final override;
void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) final override;
void RHIBeginFrame() final override;
void RHIEndFrame() final override;
(......)
protected:
virtual FD3D12CommandContext* GetContext(uint32 InGPUIndex) = 0;
FRHIGPUMask GPUMask;
(......)
};
class FD3D12CommandContext : public FD3D12CommandContextBase, public FD3D12DeviceChild
{
public:
FD3D12CommandContext(class FD3D12Device* InParent, bool InIsDefaultContext, bool InIsAsyncComputeContext);
virtual ~FD3D12CommandContext();
void EndFrame();
void ConditionalObtainCommandAllocator();
void ReleaseCommandAllocator();
FD3D12CommandListManager& GetCommandListManager();
void OpenCommandList();
void CloseCommandList();
FD3D12CommandListHandle FlushCommands(bool WaitForCompletion = false, EFlushCommandsExtraAction ExtraAction = FCEA_None);
void Finish(TArray<FD3D12CommandListHandle>& CommandLists);
FD3D12FastConstantAllocator ConstantsAllocator;
FD3D12CommandListHandle CommandListHandle;
FD3D12CommandAllocator* CommandAllocator;
FD3D12CommandAllocatorManager CommandAllocatorManager;
FD3D12DynamicRHI& OwningRHI;
// State Block.
FD3D12RenderTargetView* CurrentRenderTargets[D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT];
FD3D12DepthStencilView* CurrentDepthStencilTarget;
FD3D12TextureBase* CurrentDepthTexture;
uint32 NumSimultaneousRenderTargets;
// Uniform Buffer.
FD3D12UniformBuffer* BoundUniformBuffers[SF_NumStandardFrequencies][MAX_CBS];
FUniformBufferRHIRef BoundUniformBufferRefs[SF_NumStandardFrequencies][MAX_CBS];
uint16 DirtyUniformBuffers[SF_NumStandardFrequencies];
// 常量缓冲区.
FD3D12ConstantBuffer VSConstantBuffer;
FD3D12ConstantBuffer HSConstantBuffer;
FD3D12ConstantBuffer DSConstantBuffer;
FD3D12ConstantBuffer PSConstantBuffer;
FD3D12ConstantBuffer GSConstantBuffer;
FD3D12ConstantBuffer CSConstantBuffer;
template <class ShaderType> void SetResourcesFromTables(const ShaderType* RESTRICT);
template <class ShaderType> uint32 SetUAVPSResourcesFromTables(const ShaderType* RESTRICT Shader);
void CommitGraphicsResourceTables();
void CommitComputeResourceTables(FD3D12ComputeShader* ComputeShader);
void ValidateExclusiveDepthStencilAccess(FExclusiveDepthStencil Src) const;
void CommitRenderTargetsAndUAVs();
virtual void SetDepthBounds(float MinDepth, float MaxDepth);
virtual void SetShadingRate(EVRSShadingRate ShadingRate, EVRSRateCombiner Combiner);
(......)
protected:
FD3D12CommandContext* GetContext(uint32 InGPUIndex) final override;
TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
};
class FD3D12CommandContextRedirector final : public FD3D12CommandContextBase
{
public:
FD3D12CommandContextRedirector(class FD3D12Adapter* InParent, bool InIsDefaultContext, bool InIsAsyncComputeContext);
virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) final override;
virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) final override;
virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override;
(......)
private:
FRHIGPUMask PhysicalGPUMask;
FD3D12CommandContext* PhysicalContexts[MAX_NUM_GPUS];
};
// Engine\Source\Runtime\D3D12RHI\Private\D3D12CommandContext.cpp
class FD3D12CommandContextContainer : public IRHICommandContextContainer
{
FD3D12Adapter* Adapter;
FD3D12CommandContext* CmdContext;
FD3D12CommandContextRedirector* CmdContextRedirector;
FRHIGPUMask GPUMask;
TArray<FD3D12CommandListHandle> CommandLists;
(......)
};
以上可知,D3D12涉及的核心类型非常多,涉及多层级的复杂的数据结构链,其内存布局如下所示:
[Engine]--
|
|-[RHI]--
|
|-[Adapter]-- (LDA)
| |
| |- [Device]
| |
| |- [Device]
|
|-[Adapter]--
|
|- [Device]--
|
|-[CommandContext]
|
|-[CommandContext]---
|
|-[StateCache]
在这种方案下,FD3D12Device表示1个节点,属于1个物理适配器。这种结构允许一个RHI控制几个不同类型的硬件设置,例如:
- 单GPU系统(常规案例)。
- 多GPU系统,如LDA(Crossfire/SLI)。
- 非对称多GPU系统,如分离、集成GPU协作系统。
将D3D12的核心类抽象成UML图之后,如下所示:
FVulkanDynamicRHI
FVulkanDynamicRHI涉及的核心类如下:
// Engine\Source\Runtime\VulkanRHI\Public\VulkanDynamicRHI.h
class FVulkanDynamicRHI : public FDynamicRHI
{
public:
// FDynamicRHI interface.
virtual void Init() final override;
virtual void PostInit() final override;
virtual void Shutdown() final override;;
void InitInstance();
(......)
protected:
// 实例.
VkInstance Instance;
// 设备.
TArray<FVulkanDevice*> Devices;
FVulkanDevice* Device;
// 视口.
TArray<FVulkanViewport*> Viewports;
(......)
};
// Engine\Source\Runtime\VulkanRHI\Private\VulkanDevice.h
class FVulkanDevice
{
public:
FVulkanDevice(FVulkanDynamicRHI* InRHI, VkPhysicalDevice Gpu);
~FVulkanDevice();
bool QueryGPU(int32 DeviceIndex);
void InitGPU(int32 DeviceIndex);
void CreateDevice();
void PrepareForDestroy();
void Destroy();
void WaitUntilIdle();
void PrepareForCPURead();
void SubmitCommandsAndFlushGPU();
(......)
private:
void SubmitCommands(FVulkanCommandListContext* Context);
// vk设备.
VkDevice Device;
// vk物理设备.
VkPhysicalDevice Gpu;
VkPhysicalDeviceProperties GpuProps;
VkPhysicalDeviceFeatures PhysicalFeatures;
// 管理器.
VulkanRHI::FDeviceMemoryManager DeviceMemoryManager;
VulkanRHI::FMemoryManager MemoryManager;
VulkanRHI::FDeferredDeletionQueue2 DeferredDeletionQueue;
VulkanRHI::FStagingManager StagingManager;
VulkanRHI::FFenceManager FenceManager;
FVulkanDescriptorPoolsManager* DescriptorPoolsManager = nullptr;
FVulkanDescriptorSetCache* DescriptorSetCache = nullptr;
FVulkanShaderFactory ShaderFactory;
// 队列.
FVulkanQueue* GfxQueue;
FVulkanQueue* ComputeQueue;
FVulkanQueue* TransferQueue;
FVulkanQueue* PresentQueue;
// GPU品牌.
EGpuVendorId VendorId = EGpuVendorId::NotQueried;
// 命令队列上下文.
FVulkanCommandListContextImmediate* ImmediateContext;
FVulkanCommandListContext* ComputeContext;
TArray<FVulkanCommandListContext*> CommandContexts;
FVulkanDynamicRHI* RHI = nullptr;
class FVulkanPipelineStateCacheManager* PipelineStateCache;
(......)
};
// Engine\Source\Runtime\VulkanRHI\Private\VulkanQueue.h
class FVulkanQueue
{
public:
FVulkanQueue(FVulkanDevice* InDevice, uint32 InFamilyIndex);
~FVulkanQueue();
void Submit(FVulkanCmdBuffer* CmdBuffer, uint32 NumSignalSemaphores = 0, VkSemaphore* SignalSemaphores = nullptr);
void Submit(FVulkanCmdBuffer* CmdBuffer, VkSemaphore SignalSemaphore);
void GetLastSubmittedInfo(FVulkanCmdBuffer*& OutCmdBuffer, uint64& OutFenceCounter) const;
(......)
private:
// vk队列
VkQueue Queue;
// 家族索引.
uint32 FamilyIndex;
// 队列索引.
uint32 QueueIndex;
FVulkanDevice* Device;
// vk命令缓冲.
FVulkanCmdBuffer* LastSubmittedCmdBuffer;
uint64 LastSubmittedCmdBufferFenceCounter;
uint64 SubmitCounter;
mutable FCriticalSection CS;
void UpdateLastSubmittedCommandBuffer(FVulkanCmdBuffer* CmdBuffer);
};
// Engine\Source\Runtime\VulkanRHI\Public\VulkanMemory.h
// 设备子节点.
class FDeviceChild
{
public:
FDeviceChild(FVulkanDevice* InDevice = nullptr);
(......)
protected:
FVulkanDevice* Device;
};
// Engine\Source\Runtime\VulkanRHI\Private\VulkanContext.h
class FVulkanCommandListContext : public IRHICommandContext
{
public:
FVulkanCommandListContext(FVulkanDynamicRHI* InRHI, FVulkanDevice* InDevice, FVulkanQueue* InQueue, FVulkanCommandListContext* InImmediate);
virtual ~FVulkanCommandListContext();
static inline FVulkanCommandListContext& GetVulkanContext(IRHICommandContext& CmdContext);
inline bool IsImmediate() const;
virtual void RHISetStreamSource(uint32 StreamIndex, FRHIVertexBuffer* VertexBuffer, uint32 Offset) final override;
virtual void RHISetViewport(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ) final override;
virtual void RHISetScissorRect(bool bEnable, uint32 MinX, uint32 MinY, uint32 MaxX, uint32 MaxY) final override;
(......)
inline FVulkanDevice* GetDevice() const;
void PrepareParallelFromBase(const FVulkanCommandListContext& BaseContext);
protected:
FVulkanDynamicRHI* RHI;
FVulkanCommandListContext* Immediate;
FVulkanDevice* Device;
FVulkanQueue* Queue;
FVulkanUniformBufferUploader* UniformBufferUploader;
FVulkanCommandBufferManager* CommandBufferManager;
static FVulkanLayoutManager LayoutManager;
private:
FVulkanGPUProfiler GpuProfiler;
TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
(......)
};
// 立即模式的命令队列上下文.
class FVulkanCommandListContextImmediate : public FVulkanCommandListContext
{
public:
FVulkanCommandListContextImmediate(FVulkanDynamicRHI* InRHI, FVulkanDevice* InDevice, FVulkanQueue* InQueue);
};
// 命令上下文容器.
struct FVulkanCommandContextContainer : public IRHICommandContextContainer, public VulkanRHI::FDeviceChild
{
FVulkanCommandListContext* CmdContext;
FVulkanCommandContextContainer(FVulkanDevice* InDevice);
virtual IRHICommandContext* GetContext() override final;
virtual void FinishContext() override final;
virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override final;
void* operator new(size_t Size);
void operator delete(void* RawMemory);
(......)
};
// Engine\Source\Runtime\VulkanRHI\Private\VulkanViewport.h
class FVulkanViewport : public FRHIViewport, public VulkanRHI::FDeviceChild
{
public:
FVulkanViewport(FVulkanDynamicRHI* InRHI, FVulkanDevice* InDevice, void* InWindowHandle, uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen, EPixelFormat InPreferredPixelFormat);
~FVulkanViewport();
void AdvanceBackBufferFrame(FRHICommandListImmediate& RHICmdList);
void WaitForFrameEventCompletion();
virtual void SetCustomPresent(FRHICustomPresent* InCustomPresent) override final;
virtual FRHICustomPresent* GetCustomPresent() const override final;
virtual void Tick(float DeltaTime) override final;
bool Present(FVulkanCommandListContext* Context, FVulkanCmdBuffer* CmdBuffer, FVulkanQueue* Queue, FVulkanQueue* PresentQueue, bool bLockToVsync);
(......)
protected:
TArray<VkImage, TInlineAllocator<NUM_BUFFERS*2>> BackBufferImages;
TArray<VulkanRHI::FSemaphore*, TInlineAllocator<NUM_BUFFERS*2>> RenderingDoneSemaphores;
TArray<FVulkanTextureView, TInlineAllocator<NUM_BUFFERS*2>> TextureViews;
TRefCountPtr<FVulkanBackBuffer> RHIBackBuffer;
TRefCountPtr<FVulkanTexture2D> RenderingBackBuffer;
/** narrow-scoped section that locks access to back buffer during its recreation*/
FCriticalSection RecreatingSwapchain;
FVulkanDynamicRHI* RHI;
FVulkanSwapChain* SwapChain;
void* WindowHandle;
VulkanRHI::FSemaphore* AcquiredSemaphore;
FCustomPresentRHIRef CustomPresent;
FVulkanCmdBuffer* LastFrameCommandBuffer = nullptr;
(......)
};
若将Vulkan RHI的核心类型绘制成UML图,则是如下图所示:
FMetalDynamicRHI
FMetalDynamicRHI的核心类型定义如下:
// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalDynamicRHI.h
class FMetalDynamicRHI : public FDynamicRHI
{
public:
// FDynamicRHI interface.
virtual void Init();
virtual void Shutdown() {}
(......)
private:
// 立即模式上下文.
FMetalRHIImmediateCommandContext ImmediateContext;
// 异步计算上下文.
FMetalRHICommandContext* AsyncComputeContext;
(......)
};
// Engine\Source\Runtime\Apple\MetalRHI\Public\MetalRHIContext.h
class FMetalRHICommandContext : public IRHICommandContext
{
public:
FMetalRHICommandContext(class FMetalProfiler* InProfiler, FMetalContext* WrapContext);
virtual ~FMetalRHICommandContext();
virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) override;
virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) override;
virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override;
(......)
protected:
// Metal上下文.
FMetalContext* Context;
TSharedPtr<FMetalCommandBufferFence, ESPMode::ThreadSafe> CommandBufferFence;
class FMetalProfiler* Profiler;
FMetalBuffer PendingVertexBuffer;
TArray<FRHIUniformBuffer*> GlobalUniformBuffers;
(......)
};
class FMetalRHIComputeContext : public FMetalRHICommandContext
{
public:
FMetalRHIComputeContext(class FMetalProfiler* InProfiler, FMetalContext* WrapContext);
virtual ~FMetalRHIComputeContext();
virtual void RHISetAsyncComputeBudget(EAsyncComputeBudget Budget) final override;
virtual void RHISetComputeShader(FRHIComputeShader* ComputeShader) final override;
virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) final override;
virtual void RHISubmitCommandsHint() final override;
};
class FMetalRHIImmediateCommandContext : public FMetalRHICommandContext
{
public:
FMetalRHIImmediateCommandContext(class FMetalProfiler* InProfiler, FMetalContext* WrapContext);
// FRHICommandContext API accessible only on the immediate device context
virtual void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) final override;
virtual void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) final override;
(......)
};
// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalContext.h
// 上下文.
class FMetalContext
{
public:
FMetalContext(mtlpp::Device InDevice, FMetalCommandQueue& Queue, bool const bIsImmediate);
virtual ~FMetalContext();
mtlpp::Device& GetDevice();
bool PrepareToDraw(uint32 PrimitiveType, EMetalIndexType IndexType = EMetalIndexType_None);
void SetRenderPassInfo(const FRHIRenderPassInfo& RenderTargetsInfo, bool const bRestart = false);
void SubmitCommandsHint(uint32 const bFlags = EMetalSubmitFlagsCreateCommandBuffer);
void SubmitCommandBufferAndWait();
void ResetRenderCommandEncoder();
void DrawPrimitive(uint32 PrimitiveType, uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances);
void DrawPrimitiveIndirect(uint32 PrimitiveType, FMetalVertexBuffer* VertexBuffer, uint32 ArgumentOffset);
void DrawIndexedPrimitive(FMetalBuffer const& IndexBuffer, ...);
void DrawIndexedIndirect(FMetalIndexBuffer* IndexBufferRHI, ...);
void DrawIndexedPrimitiveIndirect(uint32 PrimitiveType, ...);
void DrawPatches(uint32 PrimitiveType, ...);
(......)
protected:
// Metal底层设备.
mtlpp::Device Device;
FMetalCommandQueue& CommandQueue;
FMetalCommandList CommandList;
FMetalStateCache StateCache;
FMetalRenderPass RenderPass;
dispatch_semaphore_t CommandBufferSemaphore;
TSharedPtr<FMetalQueryBufferPool, ESPMode::ThreadSafe> QueryBuffer;
TRefCountPtr<FMetalFence> StartFence;
TRefCountPtr<FMetalFence> EndFence;
int32 NumParallelContextsInPass;
(......)
};
// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalCommandQueue.h
class FMetalCommandQueue
{
public:
FMetalCommandQueue(mtlpp::Device Device, uint32 const MaxNumCommandBuffers = 0);
~FMetalCommandQueue(void);
mtlpp::CommandBuffer CreateCommandBuffer(void);
void CommitCommandBuffer(mtlpp::CommandBuffer& CommandBuffer);
void SubmitCommandBuffers(TArray<mtlpp::CommandBuffer> BufferList, uint32 Index, uint32 Count);
FMetalFence* CreateFence(ns::String const& Label) const;
void GetCommittedCommandBufferFences(TArray<mtlpp::CommandBufferFence>& Fences);
mtlpp::Device& GetDevice(void);
static mtlpp::ResourceOptions GetCompatibleResourceOptions(mtlpp::ResourceOptions Options);
static inline bool SupportsFeature(EMetalFeatures InFeature);
static inline bool SupportsSeparateMSAAAndResolveTarget();
(......)
private:
// 设备.
mtlpp::Device Device;
// 命令队列.
mtlpp::CommandQueue CommandQueue;
// 命令缓存区列表.(注意是数组的数组)
TArray<TArray<mtlpp::CommandBuffer>> CommandBuffers;
TLockFreePointerListLIFO<mtlpp::CommandBufferFence> CommandBufferFences;
uint64 ParallelCommandLists;
};
// Engine\Source\Runtime\Apple\MetalRHI\Private\MetalCommandList.h
class FMetalCommandList
{
public:
FMetalCommandList(FMetalCommandQueue& InCommandQueue, bool const bInImmediate);
~FMetalCommandList(void);
void Commit(mtlpp::CommandBuffer& Buffer, TArray<ns::Object<mtlpp::CommandBufferHandler>> CompletionHandlers, bool const bWait, bool const bIsLastCommandBuffer);
void Submit(uint32 Index, uint32 Count);
bool IsImmediate(void) const;
bool IsParallel(void) const;
void SetParallelIndex(uint32 Index, uint32 Num);
uint32 GetParallelIndex(void) const;
uint32 GetParallelNum(void) const;
(......)
private:
// 所属的FMetalCommandQueue.
FMetalCommandQueue& CommandQueue;
// 已提交的命令缓冲列表.
TArray<mtlpp::CommandBuffer> SubmittedBuffers;
};
相比其它现代图形API而言,FMetalDynamicRHI的概念和接口都简介多了。其UML图如下:
不同平台加载FDynamicRHI流程
IDynamicRHIModule
IDynamicRHIModule从IModuleInterface派生,可通过FModuleManager::LoadModule()等函数动态地加载,是各个DynamicRHIModule的公共接口类。
FNullDynamicRHIModule、FOpenGLDynamicRHIModule、FD3D11DynamicRHIModule、FD3D12DynamicRHIModule、FVulkanDynamicRHIModule、FMetalDynamicRHIModule、FEmptyDynamicRHIModule通过重写CreateRHI函数,
创建并返回对应的FDynamicRHI类型,相关的代码如下:
// FNullDynamicRHIModule模块
class FNullDynamicRHIModule
: public IDynamicRHIModule
{
public:
// IDynamicRHIModule
virtual bool SupportsDynamicReloading() override { return false; }
virtual bool IsSupported() override { return true; }
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
{
return new FNullDynamicRHI();
}
};
// FOpenGLDynamicRHIModule模块
class FOpenGLDynamicRHIModule : public IDynamicRHIModule
{
public:
// IModuleInterface
virtual bool SupportsDynamicReloading() override { return false; }
// IDynamicRHIModule
virtual bool IsSupported() override { return true; }
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
{
GRequestedFeatureLevel = InRequestedFeatureLevel;
return new FOpenGLDynamicRHI();
}
};
// FD3D11DynamicRHIModule模块
class FD3D11DynamicRHIModule : public IDynamicRHIModule
{
public:
// IModuleInterface
virtual bool SupportsDynamicReloading() override { return false; }
virtual void StartupModule() override;
// IDynamicRHIModule
virtual bool IsSupported() override
{
// if not computed yet
if(!ChosenAdapter.IsValid())
{
FindAdapter();
}
// The hardware must support at least 10.0 (usually 11_0, 10_0 or 10_1).
return ChosenAdapter.IsValid()
&& ChosenAdapter.MaxSupportedFeatureLevel != D3D_FEATURE_LEVEL_9_1
&& ChosenAdapter.MaxSupportedFeatureLevel != D3D_FEATURE_LEVEL_9_2
&& ChosenAdapter.MaxSupportedFeatureLevel != D3D_FEATURE_LEVEL_9_3;
}
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
{
{
#if PLATFORM_HOLOLENS
GMaxRHIFeatureLevel = ERHIFeatureLevel::ES3_1;
GMaxRHIShaderPlatform = SP_PCD3D_ES3_1;
#endif
TRefCountPtr<IDXGIFactory1> DXGIFactory1;
SafeCreateDXGIFactory(DXGIFactory1.GetInitReference());
check(DXGIFactory1);
GD3D11RHI = new FD3D11DynamicRHI(DXGIFactory1,ChosenAdapter.MaxSupportedFeatureLevel,ChosenAdapter.AdapterIndex,ChosenDescription);
FDynamicRHI* FinalRHI = GD3D11RHI;
#if ENABLE_RHI_VALIDATION
if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
{
FinalRHI = new FValidationRHI(FinalRHI);
}
#endif
return FinalRHI;
}
}
private:
FD3D11Adapter ChosenAdapter;
// we don't use GetDesc().Description as there is a bug with Optimus where it can report the wrong name
DXGI_ADAPTER_DESC ChosenDescription;
// set MaxSupportedFeatureLevel and ChosenAdapter
void FindAdapter();
};
// FD3D12DynamicRHIModule模块
class FD3D12DynamicRHIModule : public IDynamicRHIModule
{
public:
FD3D12DynamicRHIModule()
{
}
~FD3D12DynamicRHIModule()
{
}
// IModuleInterface
virtual bool SupportsDynamicReloading() override { return false; }
virtual void StartupModule() override;
virtual void ShutdownModule() override;
// IDynamicRHIModule
virtual bool IsSupported() override
{
#if !PLATFORM_HOLOLENS
if (!FPlatformMisc::VerifyWindowsVersion(10, 0))
{
return false;
}
#endif
// If not computed yet
if (ChosenAdapters.Num() == 0)
{
FindAdapter();
}
// The hardware must support at least 11.0.
return ChosenAdapters.Num() > 0
&& ChosenAdapters[0]->GetDesc().IsValid()
&& ChosenAdapters[0]->GetDesc().MaxSupportedFeatureLevel >= D3D_FEATURE_LEVEL_11_0;
}
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
{
ERHIFeatureLevel::Type PreviewFeatureLevel;
if (!GIsEditor && RHIGetPreviewFeatureLevel(PreviewFeatureLevel))
{
check(PreviewFeatureLevel == ERHIFeatureLevel::ES3_1);
// ES3.1 feature level emulation in D3D
GMaxRHIFeatureLevel = PreviewFeatureLevel;
if (GMaxRHIFeatureLevel == ERHIFeatureLevel::ES3_1)
{
GMaxRHIShaderPlatform = SP_PCD3D_ES3_1;
}
}
else
{
GMaxRHIFeatureLevel = ERHIFeatureLevel::SM5;
GMaxRHIShaderPlatform = SP_PCD3D_SM5;
}
#if USE_PIX
bool bPixEventEnabled = (WindowsPixDllHandle != nullptr);
#else
bool bPixEventEnabled = false;
#endif // USE_PIX
GD3D12RHI = new FD3D12DynamicRHI(ChosenAdapters, bPixEventEnabled);
#if ENABLE_RHI_VALIDATION
if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
{
return new FValidationRHI(GD3D12RHI);
}
#endif
return GD3D12RHI;
}
private:
#if USE_PIX && (PLATFORM_WINDOWS || PLATFORM_HOLOLENS)
void* WindowsPixDllHandle = nullptr;
#endif // USE_PIX && (PLATFORM_WINDOWS || PLATFORM_HOLOLENS)
TArray<TSharedPtr<FD3D12Adapter>> ChosenAdapters;
// set MaxSupportedFeatureLevel and ChosenAdapter
void FindAdapter();
};
// FVulkanDynamicRHIModule模块
class FVulkanDynamicRHIModule : public IDynamicRHIModule
{
public:
// IDynamicRHIModule
virtual bool IsSupported() override
{
return FVulkanPlatform::IsSupported();
}
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
{
FVulkanPlatform::SetupMaxRHIFeatureLevelAndShaderPlatform(InRequestedFeatureLevel);
check(GMaxRHIFeatureLevel != ERHIFeatureLevel::Num);
GVulkanRHI = new FVulkanDynamicRHI();
FDynamicRHI* FinalRHI = GVulkanRHI;
#if ENABLE_RHI_VALIDATION
if (FParse::Param(FCommandLine::Get(), TEXT("RHIValidation")))
{
FinalRHI = new FValidationRHI(FinalRHI);
}
#endif
return FinalRHI;
}
};
// FMetalDynamicRHIModule模块
class FMetalDynamicRHIModule : public IDynamicRHIModule
{
public:
virtual bool IsSupported() override final { return true; }
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override final
{
LLM(MetalLLM::Initialise());
return new FMetalDynamicRHI(RequestedFeatureLevel);
}
};
// FEmptyDynamicRHIModule模块
class FEmptyDynamicRHIModule : public IDynamicRHIModule
{
public:
// IDynamicRHIModule
virtual bool IsSupported() override { return true; }
virtual FDynamicRHI* CreateRHI(ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num) override
{
return new FEmptyDynamicRHI();
}
};
DynamicRHIModule的继承关系如下:
不同平台实现了各自FDynamicRHI* PlatformCreateDynamicRHI全局函数版本,依据命令行参数或ini配置文件来确定当前应该加载的IDynamicRHIModule,最后调用CreateRHI函数创建出最终使用的FDynamicRHI实例。
并将该实例保存到全局变量extern RHI_API FDynamicRHI* GDynamicRHI中 // UnrealEngine\Engine\Source\Runtime\RHI\Public\DynamicRHI.h
/* UnrealEngine\Engine\Source\Runtime\RHI\Private\Windows\WindowsDynamicRHI.cpp */
// windows平台
FDynamicRHI* PlatformCreateDynamicRHI()
{
FDynamicRHI* DynamicRHI = nullptr;
#if UE_BUILD_DEBUG || UE_BUILD_DEVELOPMENT
if (!FPlatformMisc::IsDebuggerPresent())
{
if (FParse::Param(FCommandLine::Get(), TEXT("AttachDebugger")))
{
// Wait to attach debugger
do
{
FPlatformProcess::Sleep(0);
}
while (!FPlatformMisc::IsDebuggerPresent());
}
}
#endif
ERHIFeatureLevel::Type RequestedFeatureLevel;
const TCHAR* LoadedRHIModuleName;
IDynamicRHIModule* DynamicRHIModule = LoadDynamicRHIModule(RequestedFeatureLevel, LoadedRHIModuleName);
if (DynamicRHIModule)
{
// Create the dynamic RHI.
DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
GLoadedRHIModuleName = LoadedRHIModuleName;
}
return DynamicRHI;
}
/* UnrealEngine\Engine\Source\Runtime\RHI\Private\Android\AndroidDynamicRHI.cpp */
// Android平台
FDynamicRHI* PlatformCreateDynamicRHI()
{
FDynamicRHI* DynamicRHI = NULL;
// Load the dynamic RHI module.
IDynamicRHIModule* DynamicRHIModule = NULL;
ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num;
FString GraphicsRHI;
if (FPlatformMisc::ShouldUseVulkan() || FPlatformMisc::ShouldUseDesktopVulkan())
{
// Vulkan is required, release the EGL created by FAndroidAppEntry::PlatformInit.
FAndroidAppEntry::ReleaseEGL();
DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("VulkanRHI"));
if (!DynamicRHIModule->IsSupported())
{
DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("OpenGLDrv"));
GraphicsRHI = TEXT("OpenGL");
}
else
{
RequestedFeatureLevel = FPlatformMisc::ShouldUseDesktopVulkan() ? ERHIFeatureLevel::SM5 : ERHIFeatureLevel::ES3_1;
GraphicsRHI = TEXT("Vulkan");
}
}
else
{
DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("OpenGLDrv"));
GraphicsRHI = TEXT("OpenGL");
}
if (!DynamicRHIModule->IsSupported())
{
// FMessageDialog::Open(EAppMsgType::Ok, TEXT("OpenGL 3.2 is required to run the engine."));
FPlatformMisc::RequestExit(1);
DynamicRHIModule = NULL;
}
if (DynamicRHIModule)
{
FApp::SetGraphicsRHI(GraphicsRHI);
// Create the dynamic RHI.
DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
}
#if !PLATFORM_LUMIN
FPlatformMisc::UnlockAndroidWindow();
#endif
return DynamicRHI;
}
/* UnrealEngine\Engine\Source\Runtime\RHI\Private\Apple\AppleDynamicRHI.cpp */
// macOS、iOS平台
FDynamicRHI* PlatformCreateDynamicRHI()
{
SCOPED_AUTORELEASE_POOL;
FDynamicRHI* DynamicRHI = NULL;
IDynamicRHIModule* DynamicRHIModule = NULL;
bool const bIsMetalSupported = FPlatformMisc::HasPlatformFeature(TEXT("Metal"));
// Must be Metal!
if(!bIsMetalSupported)
{
FText Title = NSLOCTEXT("AppleDynamicRHI", "OpenGLNotSupportedTitle","Metal Not Supported");
#if PLATFORM_MAC
FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("MacPlatformCreateDynamicRHI", "OpenGLNotSupported.", "You must have a Metal compatible graphics card and be running Mac OS X 10.11.6 or later to launch this process."), &Title);
#else
FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("AppleDynamicRHI", "OpenGLNotSupported.", "You must have a Metal compatible iOS or tvOS device with iOS 8 or later to launch this app."), &Title);
#endif
FPlatformMisc::RequestExit(true);
}
if (FParse::Param(FCommandLine::Get(),TEXT("opengl")))
{
UE_LOG(LogRHI, Log, TEXT("OpenGL command line option ignored; Apple platforms only support Metal."));
}
ERHIFeatureLevel::Type RequestedFeatureLevel = ERHIFeatureLevel::Num;
{
// Check the list of targeted shader platforms and decide an RHI based off them
TArray<FString> TargetedShaderFormats;
#if PLATFORM_MAC
GConfig->GetArray(TEXT("/Script/MacTargetPlatform.MacTargetSettings"), TEXT("TargetedRHIs"), TargetedShaderFormats, GEngineIni);
#else
bool bSupportsMetalMRT = false;
GConfig->GetBool(TEXT("/Script/IOSRuntimeSettings.IOSRuntimeSettings"), TEXT("bSupportsMetalMRT"), bSupportsMetalMRT, GEngineIni);
if (bSupportsMetalMRT)
{
#if PLATFORM_TVOS
TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL_MRT_TVOS).ToString());
#else
TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL_MRT).ToString());
#endif
}
#if PLATFORM_TVOS
TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL_TVOS).ToString());
#else
TargetedShaderFormats.Add(LegacyShaderPlatformToShaderFormat(SP_METAL).ToString());
#endif
#endif // else branch of PLATFORM_MAC
// Metal is not always available, so don't assume that we can use the first platform
for (FString Name : TargetedShaderFormats)
{
FName ShaderFormatName(*Name);
EShaderPlatform TargetedPlatform = ShaderFormatToLegacyShaderPlatform(ShaderFormatName);
// Instead use the first platform that *could* work
if (IsMetalPlatform(TargetedPlatform))
{
RequestedFeatureLevel = GetMaxSupportedFeatureLevel(TargetedPlatform);
break;
}
}
}
// Load the dynamic RHI module.
{
DynamicRHIModule = &FModuleManager::LoadModuleChecked<IDynamicRHIModule>(TEXT("MetalRHI"));
{
#if PLATFORM_MAC
if (FParse::Param(FCommandLine::Get(),TEXT("metal")))
{
RequestedFeatureLevel = ERHIFeatureLevel::SM5;
}
else if (FParse::Param(FCommandLine::Get(),TEXT("metalsm5")) || FParse::Param(FCommandLine::Get(),TEXT("metalmrt")))
{
RequestedFeatureLevel = ERHIFeatureLevel::SM5;
}
#else
if (FParse::Param(FCommandLine::Get(),TEXT("metal")))
{
RequestedFeatureLevel = ERHIFeatureLevel::ES3_1;
}
else if (FParse::Param(FCommandLine::Get(),TEXT("metalmrt")))
{
RequestedFeatureLevel = ERHIFeatureLevel::SM5;
}
#endif
}
FApp::SetGraphicsRHI(TEXT("Metal"));
}
// Create the dynamic RHI.
DynamicRHI = DynamicRHIModule->CreateRHI(RequestedFeatureLevel);
return DynamicRHI;
}
在windows平台上,调用堆栈如下:
参考
剖析虚幻渲染体系(10)- RHI