// Copyright Epic Games, Inc. All Rights Reserved. #pragma once // Metal RHI public headers. #include "MetalRHIPrivate.h" #include "MetalThirdParty.h" #include "MetalState.h" #include "MetalResources.h" #include "MetalViewport.h" #include "MetalDevice.h" #include "MetalCommandList.h" #include "MetalCommandEncoder.h" #include "MetalRHIRenderQuery.h" #include "RHICore.h" #include "Containers/DynamicRHIResourceArray.h" class FMetalEventNode; #if PLATFORM_VISIONOS namespace MetalRHIVisionOS { struct BeginRenderingImmersiveParams; struct PresentImmersiveParams; } #endif struct FMetalParallelRenderPassInfo { MTLParallelRenderCommandEncoderPtr ParallelEncoder; MTL::RenderPassDescriptor* RenderPassDesc = nullptr; }; enum class EMetalFlushFlags { None = 0, // Block the calling thread until the submission thread has dispatched all work. WaitForSubmission = 1, // Both the calling thread until the GPU has signaled completion of all dispatched work. WaitForCompletion = 2 }; ENUM_CLASS_FLAGS(EMetalFlushFlags) /** The interface RHI command context. */ class FMetalRHICommandContext : public IRHICommandContext { public: FMetalRHICommandContext(FMetalDevice& Device, class FMetalProfiler* InProfiler); virtual ~FMetalRHICommandContext(); static inline FMetalRHICommandContext& Get(FRHICommandListBase& CmdList) { check(CmdList.IsBottomOfPipe()); return static_cast(CmdList.GetContext().GetLowestLevelContext()); } void ResetContext(); void BeginComputeEncoder(); void EndComputeEncoder(); void BeginBlitEncoder(); void EndBlitEncoder(); /** Get the profiler pointer */ FORCEINLINE class FMetalProfiler* GetProfiler() const { return Profiler; } virtual void RHISetComputePipelineState(FRHIComputePipelineState* ComputePipelineState) override; virtual void RHIDispatchComputeShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override; virtual void RHIDispatchIndirectComputeShader(FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; // Useful when used with geometry shader (emit polygons to different viewports), otherwise SetViewPort() is simpler // @param Count >0 // @param Data must not be 0 virtual void RHISetMultipleViewports(uint32 Count, const FViewportBounds* Data) final override; /** Clears a UAV to the multi-component value provided. */ virtual void RHIClearUAVFloat(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FVector4f& Values) final override; virtual void RHIClearUAVUint(FRHIUnorderedAccessView* UnorderedAccessViewRHI, const FUintVector4& Values) final override; virtual void RHICopyTexture(FRHITexture* SourceTextureRHI, FRHITexture* DestTextureRHI, const FRHICopyTextureInfo& CopyInfo) final override; virtual void RHICopyBufferRegion(FRHIBuffer* DstBufferRHI, uint64 DstOffset, FRHIBuffer* SrcBufferRHI, uint64 SrcOffset, uint64 NumBytes) final override; virtual void RHICalibrateTimers(FRHITimestampCalibrationQuery* CalibrationQuery) final override; virtual void RHIBeginRenderQuery(FRHIRenderQuery* RenderQuery) final override; virtual void RHIEndRenderQuery(FRHIRenderQuery* RenderQuery) final override; virtual void RHIDiscardRenderTargets(bool Depth, bool Stencil, uint32 ColorBitMask) final override; // This method is queued with an RHIThread, otherwise it will flush after it is queued; without an RHI thread there is no benefit to queuing this frame advance commands virtual void RHIBeginDrawingViewport(FRHIViewport* Viewport, FRHITexture* RenderTargetRHI) override; // This method is queued with an RHIThread, otherwise it will flush after it is queued; without an RHI thread there is no benefit to queuing this frame advance commands virtual void RHIEndDrawingViewport(FRHIViewport* Viewport, bool bPresent, bool bLockToVsync) override; virtual void RHISetStreamSource(uint32 StreamIndex, FRHIBuffer* VertexBuffer, uint32 Offset) final override; // @param MinX including like Win32 RECT // @param MinY including like Win32 RECT // @param MaxX excluding like Win32 RECT // @param MaxY excluding like Win32 RECT virtual void RHISetViewport(float MinX, float MinY, float MinZ, float MaxX, float MaxY, float MaxZ) final override; virtual void RHISetStereoViewport(float LeftMinX, float RightMinX, float LeftMinY, float RightMinY, float MinZ, float LeftMaxX, float RightMaxX, float LeftMaxY, float RightMaxY, float MaxZ) final override; // @param MinX including like Win32 RECT // @param MinY including like Win32 RECT // @param MaxX excluding like Win32 RECT // @param MaxY excluding like Win32 RECT virtual void RHISetScissorRect(bool bEnable, uint32 MinX, uint32 MinY, uint32 MaxX, uint32 MaxY) final override; virtual void RHISetGraphicsPipelineState(FRHIGraphicsPipelineState* GraphicsState, uint32 StencilRef, bool bApplyAdditionalState) final override; virtual void RHISetStaticUniformBuffers(const FUniformBufferStaticBindings& InUniformBuffers) final override; virtual void RHISetStaticUniformBuffer(FUniformBufferStaticSlot Slot, FRHIUniformBuffer* Buffer) final override; virtual void RHISetShaderParameters(FRHIGraphicsShader* Shader, TConstArrayView InParametersData, TConstArrayView InParameters, TConstArrayView InResourceParameters, TConstArrayView InBindlessParameters) final override; virtual void RHISetShaderParameters(FRHIComputeShader* Shader, TConstArrayView InParametersData, TConstArrayView InParameters, TConstArrayView InResourceParameters, TConstArrayView InBindlessParameters) final override; virtual void RHISetStencilRef(uint32 StencilRef) final override; virtual void RHISetBlendFactor(const FLinearColor& BlendFactor) final override; void SetRenderTargets(uint32 NumSimultaneousRenderTargets, const FRHIRenderTargetView* NewRenderTargets, const FRHIDepthRenderTargetView* NewDepthStencilTarget); void SetRenderTargetsAndClear(const FRHISetRenderTargetsInfo& RenderTargetsInfo); virtual void RHIDrawPrimitive(uint32 BaseVertexIndex, uint32 NumPrimitives, uint32 NumInstances) final override; virtual void RHIDrawPrimitiveIndirect(FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; virtual void RHIDrawIndexedIndirect(FRHIBuffer* IndexBufferRHI, FRHIBuffer* ArgumentsBufferRHI, int32 DrawArgumentsIndex, uint32 NumInstances) final override; // @param NumPrimitives need to be >0 virtual void RHIDrawIndexedPrimitive(FRHIBuffer* IndexBuffer, int32 BaseVertexIndex, uint32 FirstInstance, uint32 NumVertices, uint32 StartIndex, uint32 NumPrimitives, uint32 NumInstances) final override; virtual void RHIDrawIndexedPrimitiveIndirect(FRHIBuffer* IndexBuffer, FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; #if PLATFORM_SUPPORTS_MESH_SHADERS virtual void RHIDispatchMeshShader(uint32 ThreadGroupCountX, uint32 ThreadGroupCountY, uint32 ThreadGroupCountZ) final override; virtual void RHIDispatchIndirectMeshShader(FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; #endif /** * Sets Depth Bounds Testing with the given min/max depth. * @param MinDepth The minimum depth for depth bounds test * @param MaxDepth The maximum depth for depth bounds test. * The valid values for fMinDepth and fMaxDepth are such that 0 <= fMinDepth <= fMaxDepth <= 1 */ virtual void RHISetDepthBounds(float MinDepth, float MaxDepth) final override; #if WITH_RHI_BREADCRUMBS virtual void RHIBeginBreadcrumbGPU(FRHIBreadcrumbNode* Breadcrumb) final override; virtual void RHIEndBreadcrumbGPU(FRHIBreadcrumbNode* Breadcrumb) final override; #endif virtual void RHICopyToStagingBuffer(FRHIBuffer* SourceBufferRHI, FRHIStagingBuffer* DestinationStagingBufferRHI, uint32 Offset, uint32 NumBytes) final override; virtual void RHIWriteGPUFence(FRHIGPUFence* FenceRHI) final override; virtual void RHIBeginTransitions(TArrayView Transitions); virtual void RHIEndTransitions(TArrayView Transitions); virtual void RHIBeginParallelRenderPass(TSharedPtr InInfo, const TCHAR* InName) final override; virtual void RHIEndParallelRenderPass() final override; virtual void RHIBeginRenderPass(const FRHIRenderPassInfo& InInfo, const TCHAR* InName) final override; virtual void RHIEndRenderPass() final override; virtual void RHINextSubpass() final override; #if METAL_RHI_RAYTRACING virtual void RHIBindAccelerationStructureMemory(FRHIRayTracingScene* Scene, FRHIBuffer* Buffer, uint32 BufferOffset) final override; virtual void RHIBuildAccelerationStructures(TConstArrayView Params, const FRHIBufferRange& ScratchBufferRange) final override; virtual void RHIBuildAccelerationStructures(TConstArrayView Params) final override; virtual void RHIClearShaderBindingTable(FRHIShaderBindingTable* SBT) final override; virtual void RHIRayTraceDispatch(FRHIRayTracingPipelineState* RayTracingPipelineState, FRHIRayTracingShader* RayGenShader, FRHIRayTracingScene* Scene, const FRayTracingShaderBindings& GlobalResourceBindings, uint32 Width, uint32 Height) final override; virtual void RHIRayTraceDispatch(FRHIRayTracingPipelineState* RayTracingPipelineState, FRHIRayTracingShader* RayGenShader, FRHIShaderBindingTable* SBT, const FRayTracingShaderBindings& GlobalResourceBindings, uint32 Width, uint32 Height) final override; virtual void RHIRayTraceDispatchIndirect(FRHIRayTracingPipelineState* RayTracingPipelineState, FRHIRayTracingShader* RayGenShader, FRHIRayTracingScene* Scene, const FRayTracingShaderBindings& GlobalResourceBindings, FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; virtual void RHIRayTraceDispatchIndirect(FRHIRayTracingPipelineState* RayTracingPipelineState, FRHIRayTracingShader* RayGenShader, FRHIShaderBindingTable* SBT, const FRayTracingShaderBindings& GlobalResourceBindings, FRHIBuffer* ArgumentBuffer, uint32 ArgumentOffset) final override; virtual void RHISetRayTracingBindings( FRHIRayTracingScene* Scene, FRHIRayTracingPipelineState* Pipeline, uint32 NumBindings, const FRayTracingLocalShaderBindings* Bindings, ERayTracingBindingType BindingType) final override; virtual void RHISetBindingsOnShaderBindingTable( FRHIShaderBindingTable* SBT, FRHIRayTracingPipelineState* Pipeline, uint32 NumBindings, const FRayTracingLocalShaderBindings* Bindings, ERayTracingBindingType BindingType) final override; #endif // METAL_RHI_RAYTRACING void FillBuffer(MTL::Buffer* Buffer, NS::Range Range, uint8 Value); void CopyFromTextureToBuffer(MTL::Texture* Texture, uint32 sourceSlice, uint32 sourceLevel, MTL::Origin sourceOrigin, MTL::Size sourceSize, FMetalBufferPtr toBuffer, uint32 destinationOffset, uint32 destinationBytesPerRow, uint32 destinationBytesPerImage, MTL::BlitOption options); void CopyFromBufferToTexture(FMetalBufferPtr Buffer, uint32 sourceOffset, uint32 sourceBytesPerRow, uint32 sourceBytesPerImage, MTL::Size sourceSize, MTL::Texture* toTexture, uint32 destinationSlice, uint32 destinationLevel, MTL::Origin destinationOrigin, MTL::BlitOption options); void CopyFromTextureToTexture(MTL::Texture* Texture, uint32 sourceSlice, uint32 sourceLevel, MTL::Origin sourceOrigin, MTL::Size sourceSize, MTL::Texture* toTexture, uint32 destinationSlice, uint32 destinationLevel, MTL::Origin destinationOrigin); void CopyFromBufferToBuffer(FMetalBufferPtr SourceBuffer, NS::UInteger SourceOffset, FMetalBufferPtr DestinationBuffer, NS::UInteger DestinationOffset, NS::UInteger Size); void CommitRenderResourceTables(void); void PrepareToRender(uint32 PrimitiveType); bool PrepareToDraw(uint32 PrimitiveType); void PrepareToDispatch(); void SetupParallelContext(const FRHIParallelRenderPassInfo* ParallelRenderPassInfo); void Finalize(TArray& OutPayloads); #if PLATFORM_SUPPORTS_BINDLESS_RENDERING void EnqueueDescriptorUpdate(IRDescriptorTableEntry Entry, uint32_t Index) { ComputeDescriptorEntries.Add(Entry); ComputeDescriptorIndices.Add(Index); } #endif void PushDescriptorUpdates(); enum class EPhase { Wait, Execute, Signal } CurrentPhase = EPhase::Wait; FMetalPayload* GetPayload(EPhase Phase) { if (Payloads.Num() == 0 || Phase < CurrentPhase) { NewPayload(); } CurrentPhase = Phase; return Payloads.Last(); } void NewPayload() { Payloads.Add(new FMetalPayload(Device.GetCommandQueue(EMetalQueueType::Direct))); } FMetalSyncPoint* GetContextSyncPoint() { if (!ContextSyncPoint) { ContextSyncPoint = FMetalSyncPoint::Create(EMetalSyncPointType::GPUAndCPU); BatchedSyncPoints.ToSignal.Add(ContextSyncPoint); } return ContextSyncPoint; } // Sync points which are waited at the start / signaled at the end // of the whole batch of command lists this context recorded. struct { TArray ToWait; TArray ToSignal; } BatchedSyncPoints; // Inserts a command to signal the specified sync point void SignalSyncPoint(FMetalSyncPoint* SyncPoint); // Inserts a command that blocks the GPU queue until the specified sync point is signaled. void WaitSyncPoint(FMetalSyncPoint* SyncPoint); void StartTiming(class FMetalEventNode* EventNode); void EndTiming(class FMetalEventNode* EventNode); void SynchronizeResource(MTL::Resource* Resource); void SynchronizeTexture(MTL::Texture* Texture, uint32 Slice, uint32 Level); /** Update the event to capture all GPU work so far enqueued by this encoder. */ void SignalEvent(MTLEventPtr Event, uint32_t SignalCount); /** Prevent further GPU work until the event is reached. */ void WaitForEvent(MTLEventPtr Event, uint32_t SignalCount); #if PLATFORM_VISIONOS void BeginRenderingImmersive(const MetalRHIVisionOS::BeginRenderingImmersiveParams& Params); cp_frame_t SwiftFrame = nullptr; #endif // PLATFORM_VISIONOS void SetCustomPresentViewport(FRHIViewport* Viewport) { CustomPresentViewport = Viewport; } FRHIViewport* CustomPresentViewport = nullptr; FMetalCommandBuffer* GetCurrentCommandBuffer(); void BeginRecursiveCommand() { // Nothing to do } inline const TArray& GetStaticUniformBuffers() const { return GlobalUniformBuffers; } inline void SetProfiler(FMetalProfiler* InProfiler) { Profiler = InProfiler; } inline FMetalProfiler* GetProfiler() { return Profiler; } inline TSharedRef GetQueryBufferPool() { return QueryBuffer.ToSharedRef(); } inline FMetalStateCache& GetStateCache() { return StateCache; } inline FMetalCommandQueue& GetCommandQueue() { return CommandQueue; } inline FMetalDevice& GetDevice() { return Device; } inline bool IsInsideRenderPass() const { return bWithinRenderPass; } void StartCommandBuffer(); void EndCommandBuffer(); #if RHI_NEW_GPU_PROFILER void FlushProfilerStats(); #endif void FlushCommands(EMetalFlushFlags Flags); protected: FMetalDevice& Device; /** The wrapper around the device command-queue for creating & committing command buffers to */ FMetalCommandQueue& CommandQueue; /** The wrapper around command buffers for ensuring correct parallel execution order */ FMetalCommandList CommandList; FMetalCommandEncoder CurrentEncoder; /** The cache of all tracked & accessible state. */ FMetalStateCache StateCache; /** A pool of buffers for writing visibility query results. */ TSharedPtr QueryBuffer; MTL::RenderPassDescriptor* RenderPassDesc = nullptr; /** Profiling implementation details. */ class FMetalProfiler* Profiler = nullptr; TRefCountPtr CurrentEncoderFence; uint64_t UploadSyncCounter = 0; bool bWithinRenderPass = false; bool bIsParallelContext = false; void ResolveTexture(UE::RHICore::FResolveTextureInfo Info); TArray GlobalUniformBuffers; // The array of recorded payloads the submission thread will process. // These are returned when the context is finalized. TArray Payloads; // A sync point signaled when all payloads in this context have completed. FMetalSyncPointRef ContextSyncPoint; FMetalParallelRenderPassInfo* ParallelRenderPassInfo; #if PLATFORM_SUPPORTS_BINDLESS_RENDERING TResourceArray ComputeDescriptorEntries; TResourceArray ComputeDescriptorIndices; #endif private: void RHIClearMRT(bool bClearColor, int32 NumClearColors, const FLinearColor* ColorArray, bool bClearDepth, float Depth, bool bClearStencil, uint32 Stencil); }; class FMetalRHIUploadContext : public IRHIUploadContext { public: FMetalRHIUploadContext(FMetalDevice& Device); ~FMetalRHIUploadContext(); typedef TFunction UploadContextFunction; void Finalize(TArray& OutPayloads); void EnqueueFunction(UploadContextFunction Function) { UploadFunctions.Add(Function); } private: FMetalRHICommandContext* UploadContext; FMetalRHICommandContext* WaitContext; TArray UploadFunctions; MTLEventPtr UploadSyncEvent; uint64_t UploadSyncCounter = 0; }; struct FMetalContextArray : public TRHIPipelineArray { FMetalContextArray(FRHIContextArray const& Contexts); };