UE4之RHI图形API封装
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
// 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(); } };
不同平台实现了各自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平台上,调用堆栈如下:
参考