1825 lines
56 KiB
C++
1825 lines
56 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
MetalCommandEncoder.cpp: Metal command encoder wrapper.
|
|
=============================================================================*/
|
|
|
|
#include "MetalCommandEncoder.h"
|
|
#include "MetalCommandBuffer.h"
|
|
#include "MetalCommandList.h"
|
|
#include "MetalRHIPrivate.h"
|
|
#include "MetalShaderTypes.h"
|
|
#include "MetalGraphicsPipelineState.h"
|
|
#include "MetalProfiler.h"
|
|
#include "MetalShaderResources.h"
|
|
#include "MetalStateCache.h"
|
|
#include "MetalRHIContext.h"
|
|
|
|
const uint32 EncoderRingBufferSize = 1024 * 1024;
|
|
|
|
#if METAL_DEBUG_OPTIONS
|
|
extern int32 GMetalBufferScribble;
|
|
#endif
|
|
|
|
static TCHAR const* const GMetalCommandDataTypeName[] = {
|
|
TEXT("DrawPrimitive"),
|
|
TEXT("DrawPrimitiveIndexed"),
|
|
TEXT("DrawPrimitivePatch"),
|
|
TEXT("DrawPrimitiveIndirect"),
|
|
TEXT("DrawPrimitiveIndexedIndirect"),
|
|
TEXT("Dispatch"),
|
|
TEXT("DispatchIndirect"),
|
|
};
|
|
|
|
FString FMetalCommandData::ToString() const
|
|
{
|
|
FString Result;
|
|
if ((uint32)CommandType < (uint32)FMetalCommandData::Type::Num)
|
|
{
|
|
Result = GMetalCommandDataTypeName[(uint32)CommandType];
|
|
switch(CommandType)
|
|
{
|
|
case FMetalCommandData::Type::DrawPrimitive:
|
|
Result += FString::Printf(TEXT(" BaseInstance: %u InstanceCount: %u VertexCount: %u VertexStart: %u"), Draw.baseInstance, Draw.instanceCount, Draw.vertexCount, Draw.vertexStart);
|
|
break;
|
|
case FMetalCommandData::Type::DrawPrimitiveIndexed:
|
|
Result += FString::Printf(TEXT(" BaseInstance: %u BaseVertex: %u IndexCount: %u IndexStart: %u InstanceCount: %u"), DrawIndexed.baseInstance, DrawIndexed.baseVertex, DrawIndexed.indexCount, DrawIndexed.indexStart, DrawIndexed.instanceCount);
|
|
break;
|
|
case FMetalCommandData::Type::DrawPrimitivePatch:
|
|
Result += FString::Printf(TEXT(" BaseInstance: %u InstanceCount: %u PatchCount: %u PatchStart: %u"), DrawPatch.baseInstance, DrawPatch.instanceCount, DrawPatch.patchCount, DrawPatch.patchStart);
|
|
break;
|
|
case FMetalCommandData::Type::Dispatch:
|
|
Result += FString::Printf(TEXT(" X: %u Y: %u Z: %u"), (uint32)Dispatch.threadgroupsPerGrid[0], (uint32)Dispatch.threadgroupsPerGrid[1], (uint32)Dispatch.threadgroupsPerGrid[2]);
|
|
break;
|
|
case FMetalCommandData::Type::DispatchIndirect:
|
|
Result += FString::Printf(TEXT(" Buffer: %p Offset: %u"), (void*)DispatchIndirect.ArgumentBuffer, (uint32)DispatchIndirect.ArgumentOffset);
|
|
break;
|
|
case FMetalCommandData::Type::DrawPrimitiveIndirect:
|
|
case FMetalCommandData::Type::DrawPrimitiveIndexedIndirect:
|
|
case FMetalCommandData::Type::Num:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return Result;
|
|
};
|
|
|
|
#pragma mark - Public C++ Boilerplate -
|
|
|
|
FMetalCommandEncoder::FMetalCommandEncoder(FMetalDevice& MetalDevice, FMetalCommandList& CmdList)
|
|
: Device(MetalDevice)
|
|
, CommandList(CmdList)
|
|
, bSupportsMetalFeaturesSetBytes(Device.SupportsFeature(EMetalFeaturesSetBytes))
|
|
, RingBuffer(Device, EncoderRingBufferSize, BufferOffsetAlignment, FMetalCommandQueue::GetCompatibleResourceOptions((MTL::ResourceOptions)(MTL::ResourceHazardTrackingModeUntracked | BUFFER_RESOURCE_STORAGE_MANAGED)))
|
|
, RenderPassDesc(nullptr)
|
|
, EncoderFence(nullptr)
|
|
#if ENABLE_METAL_GPUPROFILE
|
|
, CommandBufferStats(nullptr)
|
|
#endif
|
|
, EncoderNum(0)
|
|
, CmdBufIndex(0)
|
|
{
|
|
for (uint32 Frequency = 0; Frequency < uint32(MTL::FunctionTypeObject)+1; Frequency++)
|
|
{
|
|
FMemory::Memzero(ShaderBuffers[Frequency].ReferencedResources);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Bytes);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Offsets);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Lengths);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Usage);
|
|
ShaderBuffers[Frequency].SideTable = new FMetalBufferData;
|
|
ShaderBuffers[Frequency].SideTable->Data = (uint8*)(&ShaderBuffers[Frequency].Lengths[0]);
|
|
ShaderBuffers[Frequency].SideTable->Len = sizeof(ShaderBuffers[Frequency].Lengths);
|
|
|
|
ShaderBuffers[Frequency].Bound = 0;
|
|
}
|
|
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
ColorStoreActions[i] = MTL::StoreActionUnknown;
|
|
}
|
|
DepthStoreAction = MTL::StoreActionUnknown;
|
|
StencilStoreAction = MTL::StoreActionUnknown;
|
|
}
|
|
|
|
void FMetalCommandEncoder::Release(void)
|
|
{
|
|
check(!IsRenderCommandEncoderActive());
|
|
check(!IsComputeCommandEncoderActive());
|
|
check(!IsBlitCommandEncoderActive());
|
|
#if METAL_RHI_RAYTRACING
|
|
check(!IsAccelerationStructureCommandEncoderActive());
|
|
#endif // METAL_RHI_RAYTRACING
|
|
|
|
RenderPassDesc = nullptr;
|
|
|
|
for(NS::String* Str : DebugGroups)
|
|
{
|
|
Str->release();
|
|
}
|
|
DebugGroups.Empty();
|
|
|
|
for (uint32 Frequency = 0; Frequency < uint32(MTL::FunctionTypeObject)+1; Frequency++)
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].Buffers[i] = nullptr;
|
|
}
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Bytes);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].ReferencedResources);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Offsets);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Lengths);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Usage);
|
|
ShaderBuffers[Frequency].SideTable->Data = nullptr;
|
|
delete ShaderBuffers[Frequency].SideTable;
|
|
ShaderBuffers[Frequency].SideTable = nullptr;
|
|
ShaderBuffers[Frequency].Bound = 0;
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::Reset(void)
|
|
{
|
|
check(IsRenderCommandEncoderActive() == false
|
|
&& IsComputeCommandEncoderActive() == false
|
|
&& IsBlitCommandEncoderActive() == false
|
|
#if METAL_RHI_RAYTRACING
|
|
&& IsAccelerationStructureCommandEncoderActive() == false
|
|
#endif // METAL_RHI_RAYTRACING
|
|
);
|
|
|
|
RenderPassDesc = nullptr;
|
|
|
|
{
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
ColorStoreActions[i] = MTL::StoreActionUnknown;
|
|
}
|
|
DepthStoreAction = MTL::StoreActionUnknown;
|
|
StencilStoreAction = MTL::StoreActionUnknown;
|
|
}
|
|
|
|
for (uint32 Frequency = 0; Frequency < uint32(MTL::FunctionTypeObject)+1; Frequency++)
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].Buffers[i] = nullptr;
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].AccelerationStructure[i] = nullptr;
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Bytes);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].ReferencedResources);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Offsets);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Lengths);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Usage);
|
|
ShaderBuffers[Frequency].Bound = 0;
|
|
}
|
|
|
|
for(NS::String* Str : DebugGroups)
|
|
{
|
|
Str->release();
|
|
}
|
|
DebugGroups.Empty();
|
|
}
|
|
|
|
void FMetalCommandEncoder::ResetLive(void)
|
|
{
|
|
for (uint32 Frequency = 0; Frequency < uint32(MTL::FunctionTypeObject)+1; Frequency++)
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].Buffers[i] = nullptr;
|
|
}
|
|
FMemory::Memzero(ShaderBuffers[Frequency].ReferencedResources);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Bytes);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Offsets);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Lengths);
|
|
ShaderBuffers[Frequency].Bound = 0;
|
|
}
|
|
|
|
if (IsRenderCommandEncoderActive())
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
RenderCommandEncoder->setVertexBuffer(nullptr, 0, i);
|
|
RenderCommandEncoder->setFragmentBuffer(nullptr, 0, i);
|
|
}
|
|
|
|
for (uint32 i = 0; i < ML_MaxTextures; i++)
|
|
{
|
|
RenderCommandEncoder->setVertexTexture(nullptr, i);
|
|
RenderCommandEncoder->setFragmentTexture(nullptr, i);
|
|
}
|
|
}
|
|
else if (IsComputeCommandEncoderActive())
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ComputeCommandEncoder->setBuffer(nullptr, 0, i);
|
|
}
|
|
|
|
for (uint32 i = 0; i < ML_MaxTextures; i++)
|
|
{
|
|
ComputeCommandEncoder->setTexture(nullptr, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
#pragma mark - Public Command Buffer Mutators -
|
|
|
|
void FMetalCommandEncoder::StartCommandBuffer(void)
|
|
{
|
|
check(!CommandBuffer || EncoderNum == 0);
|
|
check(IsRenderCommandEncoderActive() == false
|
|
&& IsComputeCommandEncoderActive() == false
|
|
&& IsBlitCommandEncoderActive() == false
|
|
#if METAL_RHI_RAYTRACING
|
|
&& IsAccelerationStructureCommandEncoderActive() == false
|
|
#endif // METAL_RHI_RAYTRACING
|
|
);
|
|
|
|
if (!CommandBuffer)
|
|
{
|
|
CmdBufIndex++;
|
|
CommandBuffer = CommandList.GetCommandQueue().CreateCommandBuffer();
|
|
|
|
if (DebugGroups.Num())
|
|
{
|
|
CommandBuffer->GetMTLCmdBuffer()->setLabel(DebugGroups.Last());
|
|
}
|
|
|
|
#if ENABLE_METAL_GPUPROFILE && RHI_NEW_GPU_PROFILER == 0
|
|
FMetalProfiler* Profiler = FMetalProfiler::GetProfiler();
|
|
if (Profiler)
|
|
{
|
|
CommandBufferStats = Profiler->AllocateCommandBuffer(CommandBuffer->GetMTLCmdBuffer(), 0);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::EndCommandBuffer(FMetalRHICommandContext* Context)
|
|
{
|
|
check(CommandBuffer);
|
|
check(IsRenderCommandEncoderActive() == false
|
|
&& IsComputeCommandEncoderActive() == false
|
|
&& IsBlitCommandEncoderActive() == false
|
|
#if METAL_RHI_RAYTRACING
|
|
&& IsAccelerationStructureCommandEncoderActive() == false
|
|
#endif // METAL_RHI_RAYTRACING
|
|
);
|
|
|
|
if(CommandBuffer->GetMTLCmdBuffer()->label() == nullptr && DebugGroups.Num() > 0)
|
|
{
|
|
CommandBuffer->GetMTLCmdBuffer()->setLabel(DebugGroups.Last());
|
|
}
|
|
|
|
RingBuffer.Commit(CommandBuffer);
|
|
|
|
#if METAL_DEBUG_OPTIONS
|
|
if(Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation)
|
|
{
|
|
for (FMetalBufferPtr Buffer : ActiveBuffers)
|
|
{
|
|
Device.AddActiveBuffer(Buffer->GetMTLBuffer(), Buffer->GetRange());
|
|
}
|
|
|
|
TSet<FMetalBufferPtr> NewActiveBuffers = MoveTemp(ActiveBuffers);
|
|
|
|
Context->GetContextSyncPoint()->OnCompletionCallback([&InDevice = Device, NewActiveBuffers]()
|
|
{
|
|
for (FMetalBufferPtr Buffer : NewActiveBuffers)
|
|
{
|
|
InDevice.RemoveActiveBuffer(Buffer->GetMTLBuffer(), Buffer->GetRange());
|
|
}
|
|
});
|
|
}
|
|
#endif
|
|
|
|
#if ENABLE_METAL_GPUPROFILE && RHI_NEW_GPU_PROFILER == 0
|
|
if(CommandBufferStats)
|
|
{
|
|
CommandBufferStats->End(CommandBuffer->GetMTLCmdBuffer());
|
|
CommandBufferStats = nullptr;
|
|
}
|
|
#endif
|
|
|
|
CommandList.FinalizeCommandBuffer(CommandBuffer);
|
|
|
|
CommandBuffer = nullptr;
|
|
EncoderNum = 0;
|
|
}
|
|
|
|
#pragma mark - Public Command Encoder Accessors -
|
|
|
|
#if METAL_RHI_RAYTRACING
|
|
bool FMetalCommandEncoder::IsAccelerationStructureCommandEncoderActive(void) const
|
|
{
|
|
return AccelerationStructureCommandEncoder.get() != nullptr;
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
|
|
bool FMetalCommandEncoder::IsRenderPassDescriptorValid(void) const
|
|
{
|
|
return (RenderPassDesc != nullptr);
|
|
}
|
|
|
|
const MTL::RenderPassDescriptor* FMetalCommandEncoder::GetRenderPassDescriptor(void) const
|
|
{
|
|
return RenderPassDesc;
|
|
}
|
|
|
|
MTL::RenderCommandEncoder* FMetalCommandEncoder::GetRenderCommandEncoder(void)
|
|
{
|
|
check(IsRenderCommandEncoderActive() && RenderCommandEncoder);
|
|
return RenderCommandEncoder.get();
|
|
}
|
|
|
|
MTL::ComputeCommandEncoder* FMetalCommandEncoder::GetComputeCommandEncoder(void)
|
|
{
|
|
check(IsComputeCommandEncoderActive());
|
|
return ComputeCommandEncoder.get();
|
|
}
|
|
|
|
MTL::BlitCommandEncoder* FMetalCommandEncoder::GetBlitCommandEncoder(void)
|
|
{
|
|
check(IsBlitCommandEncoderActive());
|
|
return BlitCommandEncoder.get();
|
|
}
|
|
|
|
#if METAL_RHI_RAYTRACING
|
|
MTL::AccelerationStructureCommandEncoder* FMetalCommandEncoder::GetAccelerationStructureCommandEncoder(void)
|
|
{
|
|
check(IsAccelerationStructureCommandEncoderActive());
|
|
return AccelerationStructureCommandEncoder.get();
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
|
|
TRefCountPtr<FMetalFence> const& FMetalCommandEncoder::GetEncoderFence(void) const
|
|
{
|
|
return EncoderFence;
|
|
}
|
|
|
|
#pragma mark - Public Command Encoder Mutators -
|
|
|
|
void FMetalCommandEncoder::BeginRenderCommandEncoding(FMetalCounterSampler* Sampler)
|
|
{
|
|
check(RenderPassDesc);
|
|
check(CommandBuffer);
|
|
check(!IsAnyCommandEncoderActive());
|
|
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
if (bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation))
|
|
{
|
|
CommandEncoderFence.FenceResources = MoveTemp(TransitionedResources);
|
|
|
|
// Update fence state if current pass and prologue pass render to the same render targets
|
|
MTL::RenderPassColorAttachmentDescriptorArray* ColorAttachments = RenderPassDesc->colorAttachments();
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
MTL::RenderPassColorAttachmentDescriptor* ColorDesc = ColorAttachments->object(i);
|
|
if (ColorDesc->texture())
|
|
{
|
|
FenceResource(ColorDesc->texture(), MTL::FunctionTypeFragment, true);
|
|
}
|
|
}
|
|
if (RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
FenceResource(RenderPassDesc->depthAttachment()->texture(), MTL::FunctionTypeFragment, true);
|
|
}
|
|
if (RenderPassDesc->stencilAttachment()->texture() && RenderPassDesc->stencilAttachment()->texture() != RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
FenceResource(RenderPassDesc->stencilAttachment()->texture(), MTL::FunctionTypeFragment, true);
|
|
}
|
|
}
|
|
|
|
// Clear Residency Cache (TODO: Move this to a separate function)
|
|
ResourceUsage.Empty();
|
|
|
|
if(Sampler)
|
|
{
|
|
CommandBuffer->AddCounterSample(Sampler->SetupStageCounters(RenderPassDesc));
|
|
}
|
|
|
|
RenderCommandEncoder = NS::RetainPtr(CommandBuffer->GetMTLCmdBuffer()->renderCommandEncoder(RenderPassDesc));
|
|
EncoderNum++;
|
|
|
|
check(!EncoderFence);
|
|
NS::String* Label = nullptr;
|
|
|
|
if(GetEmitDrawEvents())
|
|
{
|
|
Label = FStringToNSString(FString::Printf(TEXT("RenderEncoder: %s"), DebugGroups.Num() > 0 ? *NSStringToFString(DebugGroups.Last()) : TEXT("InitialPass")));
|
|
RenderCommandEncoder->setLabel(Label);
|
|
|
|
for (NS::String* Group : DebugGroups)
|
|
{
|
|
RenderCommandEncoder->pushDebugGroup(Group);
|
|
}
|
|
}
|
|
|
|
EncoderFence = CommandList.GetCommandQueue().CreateFence(Label);
|
|
}
|
|
|
|
void FMetalCommandEncoder::BeginRenderCommandEncoding(MTL::RenderPassDescriptor* InRenderPassDesc, MTLParallelRenderCommandEncoderPtr ParallelEncoder)
|
|
{
|
|
ResourceUsage.Empty();
|
|
|
|
RenderPassDesc = InRenderPassDesc;
|
|
RenderCommandEncoder = NS::RetainPtr(ParallelEncoder->renderCommandEncoder());
|
|
bIsParallelEncoding = true;
|
|
|
|
EncoderNum++;
|
|
|
|
check(!EncoderFence);
|
|
NS::String* Label = nullptr;
|
|
|
|
if(GetEmitDrawEvents())
|
|
{
|
|
Label = FStringToNSString(FString::Printf(TEXT("RenderEncoder: %s"), DebugGroups.Num() > 0 ? *NSStringToFString(DebugGroups.Last()) : TEXT("InitialPass")));
|
|
RenderCommandEncoder->setLabel(Label);
|
|
|
|
for (NS::String* Group : DebugGroups)
|
|
{
|
|
RenderCommandEncoder->pushDebugGroup(Group);
|
|
}
|
|
}
|
|
|
|
//EncoderFence = CommandList.GetCommandQueue().CreateFence(Label);
|
|
}
|
|
|
|
MTLParallelRenderCommandEncoderPtr FMetalCommandEncoder::BeginParallelRenderCommandEncoding(FMetalCounterSampler* Sampler)
|
|
{
|
|
check(RenderPassDesc);
|
|
check(CommandBuffer);
|
|
check(!IsAnyCommandEncoderActive());
|
|
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
if (bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation))
|
|
{
|
|
CommandEncoderFence.FenceResources = MoveTemp(TransitionedResources);
|
|
|
|
// Update fence state if current pass and prologue pass render to the same render targets
|
|
MTL::RenderPassColorAttachmentDescriptorArray* ColorAttachments = RenderPassDesc->colorAttachments();
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
MTL::RenderPassColorAttachmentDescriptor* ColorDesc = ColorAttachments->object(i);
|
|
if (ColorDesc->texture())
|
|
{
|
|
FenceResource(ColorDesc->texture(), MTL::FunctionTypeFragment, true);
|
|
}
|
|
}
|
|
if (RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
FenceResource(RenderPassDesc->depthAttachment()->texture(), MTL::FunctionTypeFragment, true);
|
|
}
|
|
if (RenderPassDesc->stencilAttachment()->texture() && RenderPassDesc->stencilAttachment()->texture() != RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
FenceResource(RenderPassDesc->stencilAttachment()->texture(), MTL::FunctionTypeFragment, true);
|
|
}
|
|
}
|
|
|
|
// Clear Residency Cache (TODO: Move this to a separate function)
|
|
ResourceUsage.Empty();
|
|
|
|
if(Sampler)
|
|
{
|
|
CommandBuffer->AddCounterSample(Sampler->SetupStageCounters(RenderPassDesc));
|
|
}
|
|
|
|
ParallelRenderCommandEncoder = NS::RetainPtr(CommandBuffer->GetMTLCmdBuffer()->parallelRenderCommandEncoder(RenderPassDesc));
|
|
EncoderNum++;
|
|
|
|
check(!EncoderFence);
|
|
NS::String* Label = nullptr;
|
|
|
|
if(GetEmitDrawEvents())
|
|
{
|
|
Label = FStringToNSString(FString::Printf(TEXT("RenderEncoder: %s"), DebugGroups.Num() > 0 ? *NSStringToFString(DebugGroups.Last()) : TEXT("InitialPass")));
|
|
RenderCommandEncoder->setLabel(Label);
|
|
|
|
for (NS::String* Group : DebugGroups)
|
|
{
|
|
RenderCommandEncoder->pushDebugGroup(Group);
|
|
}
|
|
}
|
|
|
|
EncoderFence = CommandList.GetCommandQueue().CreateFence(Label);
|
|
|
|
return ParallelRenderCommandEncoder;
|
|
}
|
|
|
|
void FMetalCommandEncoder::BeginComputeCommandEncoding(MTL::DispatchType DispatchType, FMetalCounterSampler* Sampler)
|
|
{
|
|
check(CommandBuffer);
|
|
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
if (bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation))
|
|
{
|
|
CommandEncoderFence.FenceResources = MoveTemp(TransitionedResources);
|
|
}
|
|
|
|
// Clear Residency Cache (TODO: Move this to a separate function)
|
|
ResourceUsage.Empty();
|
|
|
|
MTL::ComputePassDescriptor* ComputePassDesc = MTL::ComputePassDescriptor::alloc()->init();
|
|
ComputePassDesc->setDispatchType(DispatchType);
|
|
|
|
if(Sampler)
|
|
{
|
|
CommandBuffer->AddCounterSample(Sampler->SetupStageCounters(ComputePassDesc));
|
|
}
|
|
|
|
ComputeCommandEncoder = NS::RetainPtr(CommandBuffer->GetMTLCmdBuffer()->computeCommandEncoder(ComputePassDesc));
|
|
|
|
ComputePassDesc->release();
|
|
|
|
EncoderNum++;
|
|
|
|
check(!EncoderFence);
|
|
NS::String* Label = nullptr;
|
|
|
|
if(GetEmitDrawEvents())
|
|
{
|
|
Label = FStringToNSString(FString::Printf(TEXT("ComputeEncoder: %s"), DebugGroups.Num() > 0 ? *NSStringToFString(DebugGroups.Last()) : TEXT("InitialPass")));
|
|
ComputeCommandEncoder->setLabel(Label);
|
|
|
|
for (NS::String* Group : DebugGroups)
|
|
{
|
|
ComputeCommandEncoder->pushDebugGroup(Group);
|
|
}
|
|
}
|
|
|
|
EncoderFence = CommandList.GetCommandQueue().CreateFence(Label);
|
|
}
|
|
|
|
void FMetalCommandEncoder::BeginBlitCommandEncoding(FMetalCounterSampler* Sampler)
|
|
{
|
|
check(CommandBuffer);
|
|
check(IsRenderCommandEncoderActive() == false && IsComputeCommandEncoderActive() == false && IsBlitCommandEncoderActive() == false
|
|
#if METAL_RHI_RAYTRACING
|
|
&& IsAccelerationStructureCommandEncoderActive() == false
|
|
#endif // METAL_RHI_RAYTRACING
|
|
);
|
|
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
if (bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation))
|
|
{
|
|
CommandEncoderFence.FenceResources = MoveTemp(TransitionedResources);
|
|
}
|
|
|
|
MTL::BlitPassDescriptor* BlitPassDesc = MTL::BlitPassDescriptor::alloc()->init();
|
|
|
|
if(Sampler)
|
|
{
|
|
CommandBuffer->AddCounterSample(Sampler->SetupStageCounters(BlitPassDesc));
|
|
}
|
|
|
|
BlitCommandEncoder = NS::RetainPtr(CommandBuffer->GetMTLCmdBuffer()->blitCommandEncoder(BlitPassDesc));
|
|
|
|
BlitPassDesc->release();
|
|
|
|
EncoderNum++;
|
|
|
|
check(!EncoderFence);
|
|
NS::String* Label = nullptr;
|
|
|
|
if(GetEmitDrawEvents())
|
|
{
|
|
Label = FStringToNSString(FString::Printf(TEXT("BlitEncoder: %s"), DebugGroups.Num() > 0 ? *NSStringToFString(DebugGroups.Last()) : TEXT("InitialPass")));
|
|
BlitCommandEncoder->setLabel(Label);
|
|
|
|
for (NS::String* Group : DebugGroups)
|
|
{
|
|
BlitCommandEncoder->pushDebugGroup(Group);
|
|
}
|
|
}
|
|
|
|
EncoderFence = CommandList.GetCommandQueue().CreateFence(Label);
|
|
}
|
|
|
|
#if METAL_RHI_RAYTRACING
|
|
void FMetalCommandEncoder::BeginAccelerationStructureCommandEncoding(void)
|
|
{
|
|
check(CommandBuffer);
|
|
check(IsRenderCommandEncoderActive() == false && IsComputeCommandEncoderActive() == false && IsBlitCommandEncoderActive() == false && IsAccelerationStructureCommandEncoderActive() == false);
|
|
|
|
static bool bSupportsFences = CommandList.GetCommandQueue().SupportsFeature(EMetalFeaturesFences);
|
|
if (bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation))
|
|
{
|
|
CommandEncoderFence.FenceResources = MoveTemp(TransitionedResources);
|
|
}
|
|
|
|
AccelerationStructureCommandEncoder = CommandBuffer.AccelerationStructureCommandEncoder();
|
|
EncoderNum++;
|
|
|
|
check(!EncoderFence);
|
|
NSString* Label = nullptr;
|
|
|
|
if(GetEmitDrawEvents())
|
|
{
|
|
Label = FStringToNSString(FString::Printf(TEXT("AccelerationStructureCommandEncoder: %s"), DebugGroups.Num() > 0 ? *FString(DebugGroups.Last()) : TEXT("InitialPass")));
|
|
AccelerationStructureCommandEncoder.SetLabel(Label);
|
|
|
|
for (NS::String* Group : DebugGroups)
|
|
{
|
|
AccelerationStructureCommandEncoder.PushDebugGroup(Group);
|
|
}
|
|
}
|
|
|
|
EncoderFence = CommandList.GetCommandQueue().CreateFence(Label);
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
|
|
TRefCountPtr<FMetalFence> FMetalCommandEncoder::EndEncoding()
|
|
{
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
TRefCountPtr<FMetalFence> Fence = nullptr;
|
|
MTL_SCOPED_AUTORELEASE_POOL;
|
|
|
|
if(IsRenderCommandEncoderActive())
|
|
{
|
|
if (RenderCommandEncoder)
|
|
{
|
|
check(!bSupportsFences || EncoderFence);
|
|
check(RenderPassDesc);
|
|
|
|
if(!bIsParallelEncoding)
|
|
{
|
|
MTL::RenderPassColorAttachmentDescriptorArray* ColorAttachments = RenderPassDesc->colorAttachments();
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
MTL::RenderPassColorAttachmentDescriptor* ColorAttachment = ColorAttachments->object(i);
|
|
if (ColorAttachment->texture())
|
|
{
|
|
if (ColorAttachment->storeAction() == MTL::StoreActionUnknown)
|
|
{
|
|
MTL::StoreAction Action = ColorStoreActions[i];
|
|
check(Action != MTL::StoreActionUnknown);
|
|
RenderCommandEncoder->setColorStoreAction((MTL::StoreAction)Action, i);
|
|
}
|
|
// Recorded in case the epilogue pass renders to the same render targets
|
|
TransitionResources(ColorAttachment->texture());
|
|
}
|
|
}
|
|
|
|
if (RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
if (RenderPassDesc->depthAttachment()->storeAction() == MTL::StoreActionUnknown)
|
|
{
|
|
MTL::StoreAction Action = DepthStoreAction;
|
|
check(Action != MTL::StoreActionUnknown);
|
|
RenderCommandEncoder->setDepthStoreAction((MTL::StoreAction)Action);
|
|
}
|
|
// Recorded in case the epilogue pass renders to the same render targets
|
|
TransitionResources(RenderPassDesc->depthAttachment()->texture());
|
|
}
|
|
if (RenderPassDesc->stencilAttachment()->texture())
|
|
{
|
|
if (RenderPassDesc->stencilAttachment()->storeAction() == MTL::StoreActionUnknown)
|
|
{
|
|
MTL::StoreAction Action = StencilStoreAction;
|
|
check(Action != MTL::StoreActionUnknown);
|
|
RenderCommandEncoder->setStencilStoreAction((MTL::StoreAction)Action);
|
|
}
|
|
// Recorded in case the epilogue pass renders to the same render targets
|
|
if (RenderPassDesc->stencilAttachment()->texture() != RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
TransitionResources(RenderPassDesc->stencilAttachment()->texture());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
check(!CommandBuffer);
|
|
}
|
|
|
|
// Wait the prologue fence
|
|
{
|
|
EMetalFenceWaitStage::Type FenceWaitStage = CommandEncoderFence.BarrierScope.GetFenceWaitStage();
|
|
FMetalFence* PrologueFence = CommandEncoderFence.Fence;
|
|
if (PrologueFence)
|
|
{
|
|
MTL::RenderStages FenceStage = FenceWaitStage == EMetalFenceWaitStage::BeforeVertex ? MTL::RenderStageVertex : MTL::RenderStageFragment;
|
|
MTL::Fence* MTLFence = PrologueFence->Get();
|
|
RenderCommandEncoder->waitForFence(MTLFence, FenceStage);
|
|
PrologueFence->Wait();
|
|
}
|
|
CommandEncoderFence.Reset();
|
|
}
|
|
|
|
Fence = EncoderFence;
|
|
UpdateFence(EncoderFence);
|
|
|
|
RenderCommandEncoder->endEncoding();
|
|
RenderCommandEncoder.reset();
|
|
EncoderFence = nullptr;
|
|
bIsParallelEncoding = false;
|
|
}
|
|
}
|
|
else if(IsParallelRenderCommandEncoderActive())
|
|
{
|
|
if (ParallelRenderCommandEncoder)
|
|
{
|
|
check(!bSupportsFences || EncoderFence);
|
|
check(RenderPassDesc);
|
|
|
|
MTL::RenderPassColorAttachmentDescriptorArray* ColorAttachments = RenderPassDesc->colorAttachments();
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
MTL::RenderPassColorAttachmentDescriptor* ColorAttachment = ColorAttachments->object(i);
|
|
if (ColorAttachment->texture())
|
|
{
|
|
if (ColorAttachment->storeAction() == MTL::StoreActionUnknown)
|
|
{
|
|
MTL::StoreAction Action = ColorStoreActions[i];
|
|
check(Action != MTL::StoreActionUnknown);
|
|
ParallelRenderCommandEncoder->setColorStoreAction((MTL::StoreAction)Action, i);
|
|
}
|
|
// Recorded in case the epilogue pass renders to the same render targets
|
|
TransitionResources(ColorAttachment->texture());
|
|
}
|
|
}
|
|
|
|
if (RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
if (RenderPassDesc->depthAttachment()->storeAction() == MTL::StoreActionUnknown)
|
|
{
|
|
MTL::StoreAction Action = DepthStoreAction;
|
|
check(Action != MTL::StoreActionUnknown);
|
|
ParallelRenderCommandEncoder->setDepthStoreAction((MTL::StoreAction)Action);
|
|
}
|
|
// Recorded in case the epilogue pass renders to the same render targets
|
|
TransitionResources(RenderPassDesc->depthAttachment()->texture());
|
|
}
|
|
if (RenderPassDesc->stencilAttachment()->texture())
|
|
{
|
|
if (RenderPassDesc->stencilAttachment()->storeAction() == MTL::StoreActionUnknown)
|
|
{
|
|
MTL::StoreAction Action = StencilStoreAction;
|
|
check(Action != MTL::StoreActionUnknown);
|
|
ParallelRenderCommandEncoder->setStencilStoreAction((MTL::StoreAction)Action);
|
|
}
|
|
// Recorded in case the epilogue pass renders to the same render targets
|
|
if (RenderPassDesc->stencilAttachment()->texture() != RenderPassDesc->depthAttachment()->texture())
|
|
{
|
|
TransitionResources(RenderPassDesc->stencilAttachment()->texture());
|
|
}
|
|
}
|
|
|
|
{
|
|
EMetalFenceWaitStage::Type FenceWaitStage = CommandEncoderFence.BarrierScope.GetFenceWaitStage();
|
|
check(!CommandEncoderFence.Fence);
|
|
CommandEncoderFence.Reset();
|
|
}
|
|
|
|
Fence = EncoderFence;
|
|
UpdateFence(EncoderFence);
|
|
|
|
ParallelRenderCommandEncoder->endEncoding();
|
|
ParallelRenderCommandEncoder.reset();
|
|
EncoderFence = nullptr;
|
|
}
|
|
}
|
|
else if(IsComputeCommandEncoderActive())
|
|
{
|
|
check(!bSupportsFences || EncoderFence);
|
|
|
|
// Wait the prologue fence
|
|
{
|
|
EMetalFenceWaitStage::Type FenceWaitStage = CommandEncoderFence.BarrierScope.GetFenceWaitStage();
|
|
FMetalFence* PrologueFence = CommandEncoderFence.Fence;
|
|
if (PrologueFence)
|
|
{
|
|
MTL::Fence* MTLFence = PrologueFence->Get();
|
|
ComputeCommandEncoder->waitForFence(MTLFence);
|
|
PrologueFence->Wait();
|
|
}
|
|
CommandEncoderFence.Reset();
|
|
}
|
|
|
|
Fence = EncoderFence;
|
|
UpdateFence(EncoderFence);
|
|
|
|
ComputeCommandEncoder->endEncoding();
|
|
ComputeCommandEncoder.reset();
|
|
EncoderFence = nullptr;
|
|
}
|
|
else if(IsBlitCommandEncoderActive())
|
|
{
|
|
// check(!bSupportsFences || EncoderFence);
|
|
// Wait the prologue fence
|
|
{
|
|
EMetalFenceWaitStage::Type FenceWaitStage = CommandEncoderFence.BarrierScope.GetFenceWaitStage();
|
|
FMetalFence* PrologueFence = CommandEncoderFence.Fence;
|
|
if (PrologueFence)
|
|
{
|
|
MTL::Fence* MTLFence = PrologueFence->Get();
|
|
BlitCommandEncoder->waitForFence(MTLFence);
|
|
PrologueFence->Wait();
|
|
}
|
|
CommandEncoderFence.Reset();
|
|
}
|
|
|
|
Fence = EncoderFence;
|
|
UpdateFence(EncoderFence);
|
|
|
|
BlitCommandEncoder->endEncoding();
|
|
BlitCommandEncoder.reset();
|
|
EncoderFence = nullptr;
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
else if(IsAccelerationStructureCommandEncoderActive())
|
|
{
|
|
UpdateFence(EncoderFence);
|
|
|
|
AccelerationStructureCommandEncoder.EndEncoding();
|
|
AccelerationStructureCommandEncoder.reset();
|
|
EncoderFence = nullptr;
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
|
|
for (uint32 Frequency = 0; Frequency < uint32(MTL::FunctionTypeObject)+1; Frequency++)
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].Buffers[i] = nullptr;
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].AccelerationStructure[i] = nullptr;
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Bytes);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Offsets);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Lengths);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Usage);
|
|
ShaderBuffers[Frequency].Bound = 0;
|
|
}
|
|
|
|
return Fence;
|
|
}
|
|
|
|
void FMetalCommandEncoder::UpdateFence(FMetalFence* Fence)
|
|
{
|
|
check(IsAnyCommandEncoderActive());
|
|
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
if ((bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation)) && Fence)
|
|
{
|
|
MTL::Fence* MTLFence = Fence->Get();
|
|
if (RenderCommandEncoder)
|
|
{
|
|
RenderCommandEncoder->updateFence(MTLFence, MTL::RenderStageFragment);
|
|
Fence->Write();
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->updateFence(MTLFence);
|
|
Fence->Write();
|
|
}
|
|
else if (BlitCommandEncoder)
|
|
{
|
|
BlitCommandEncoder->updateFence(MTLFence);
|
|
Fence->Write();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::WaitForFence(FMetalFence* Fence)
|
|
{
|
|
check(IsAnyCommandEncoderActive());
|
|
|
|
static bool bSupportsFences = Device.SupportsFeature(EMetalFeaturesFences);
|
|
if ((bSupportsFences METAL_DEBUG_OPTION(|| Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation)) && Fence)
|
|
{
|
|
// Will be waited at encoder end recording
|
|
CommandEncoderFence.Fence = Fence;
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SignalEvent(MTLEventPtr Event, uint32_t SignalCount)
|
|
{
|
|
check(CommandBuffer);
|
|
|
|
if(IsAnyCommandEncoderActive())
|
|
{
|
|
EndEncoding();
|
|
}
|
|
CommandBuffer->GetMTLCmdBuffer()->encodeSignalEvent(Event.get(), SignalCount);
|
|
}
|
|
|
|
void FMetalCommandEncoder::WaitForEvent(MTLEventPtr Event, uint32_t SignalCount)
|
|
{
|
|
check(!IsAnyCommandEncoderActive());
|
|
check(CommandBuffer);
|
|
|
|
CommandBuffer->GetMTLCmdBuffer()->encodeWait(Event.get(), SignalCount);
|
|
}
|
|
|
|
#pragma mark - Public Debug Support -
|
|
|
|
void FMetalCommandEncoder::InsertDebugSignpost(NS::String* String)
|
|
{
|
|
if (String)
|
|
{
|
|
if (RenderCommandEncoder)
|
|
{
|
|
RenderCommandEncoder->insertDebugSignpost(String);
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->insertDebugSignpost(String);
|
|
}
|
|
else if (BlitCommandEncoder)
|
|
{
|
|
BlitCommandEncoder->insertDebugSignpost(String);
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
else if (AccelerationStructureCommandEncoder)
|
|
{
|
|
AccelerationStructureCommandEncoder.InsertDebugSignpost(String);
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::PushDebugGroup(NS::String* String)
|
|
{
|
|
if (String)
|
|
{
|
|
String->retain();
|
|
DebugGroups.Add(String);
|
|
|
|
if (RenderCommandEncoder)
|
|
{
|
|
RenderCommandEncoder->pushDebugGroup(String);
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->pushDebugGroup(String);
|
|
}
|
|
else if (BlitCommandEncoder)
|
|
{
|
|
BlitCommandEncoder->pushDebugGroup(String);
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
else if (AccelerationStructureCommandEncoder)
|
|
{
|
|
AccelerationStructureCommandEncoder.PushDebugGroup(String);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::PopDebugGroup(void)
|
|
{
|
|
if (DebugGroups.Num() > 0)
|
|
{
|
|
DebugGroups.Pop()->release();
|
|
if (RenderCommandEncoder)
|
|
{
|
|
RenderCommandEncoder->popDebugGroup();
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->popDebugGroup();
|
|
}
|
|
else if (BlitCommandEncoder)
|
|
{
|
|
BlitCommandEncoder->popDebugGroup();
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
else if (AccelerationStructureCommandEncoder)
|
|
{
|
|
AccelerationStructureCommandEncoder->popDebugGroup();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if ENABLE_METAL_GPUPROFILE
|
|
FMetalCommandBufferStats* FMetalCommandEncoder::GetCommandBufferStats(void)
|
|
{
|
|
return CommandBufferStats;
|
|
}
|
|
#endif
|
|
|
|
#pragma mark - Public Render State Mutators -
|
|
|
|
void FMetalCommandEncoder::SetRenderPassDescriptor(MTL::RenderPassDescriptor* RenderPass)
|
|
{
|
|
check(RenderPass);
|
|
|
|
if(RenderPass != RenderPassDesc)
|
|
{
|
|
RenderPassDesc = RenderPass;
|
|
{
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
ColorStoreActions[i] = MTL::StoreActionUnknown;
|
|
}
|
|
DepthStoreAction = MTL::StoreActionUnknown;
|
|
StencilStoreAction = MTL::StoreActionUnknown;
|
|
}
|
|
}
|
|
check(RenderPassDesc);
|
|
|
|
for (uint32 Frequency = 0; Frequency < uint32(MTL::FunctionTypeObject)+1; Frequency++)
|
|
{
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].Buffers[i] = nullptr;
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
for (uint32 i = 0; i < ML_MaxBuffers; i++)
|
|
{
|
|
ShaderBuffers[Frequency].AccelerationStructure[i] = nullptr;
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Bytes);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Offsets);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Lengths);
|
|
FMemory::Memzero(ShaderBuffers[Frequency].Usage);
|
|
ShaderBuffers[Frequency].Bound = 0;
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetRenderPassStoreActions(MTL::StoreAction const* const ColorStore, MTL::StoreAction const DepthStore, MTL::StoreAction const StencilStore)
|
|
{
|
|
check(RenderPassDesc);
|
|
{
|
|
for (uint32 i = 0; i < MaxSimultaneousRenderTargets; i++)
|
|
{
|
|
ColorStoreActions[i] = ColorStore[i];
|
|
}
|
|
DepthStoreAction = DepthStore;
|
|
StencilStoreAction = StencilStore;
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetRenderPipelineState(FMetalShaderPipeline* PipelineState)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setRenderPipelineState(PipelineState->RenderPipelineState.get());
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetViewport(MTL::Viewport const Viewport[], uint32 NumActive)
|
|
{
|
|
check(RenderCommandEncoder);
|
|
check(NumActive >= 1 && NumActive < ML_MaxViewports);
|
|
if (NumActive == 1)
|
|
{
|
|
RenderCommandEncoder->setViewport(Viewport[0]);
|
|
}
|
|
#if PLATFORM_MAC
|
|
else
|
|
{
|
|
check(Device.SupportsFeature(EMetalFeaturesMultipleViewports));
|
|
RenderCommandEncoder->setViewports(Viewport, NumActive);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetFrontFacingWinding(MTL::Winding const InFrontFacingWinding)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setFrontFacingWinding(InFrontFacingWinding);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetCullMode(MTL::CullMode const InCullMode)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setCullMode(InCullMode);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetDepthBias(float const InDepthBias, float const InSlopeScale, float const InClamp)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setDepthBias(InDepthBias, InSlopeScale, InClamp);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetScissorRect(MTL::ScissorRect const Rect[], uint32 NumActive)
|
|
{
|
|
check(RenderCommandEncoder);
|
|
check(NumActive >= 1 && NumActive < ML_MaxViewports);
|
|
if (NumActive == 1)
|
|
{
|
|
RenderCommandEncoder->setScissorRect(Rect[0]);
|
|
}
|
|
#if PLATFORM_MAC
|
|
else
|
|
{
|
|
check(Device.SupportsFeature(EMetalFeaturesMultipleViewports));
|
|
RenderCommandEncoder->setScissorRects(Rect, NumActive);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetTriangleFillMode(MTL::TriangleFillMode const InFillMode)
|
|
{
|
|
check(RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setTriangleFillMode(InFillMode);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetDepthClipMode(MTL::DepthClipMode const InDepthClipMode)
|
|
{
|
|
check(RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setDepthClipMode(InDepthClipMode);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetBlendColor(float const Red, float const Green, float const Blue, float const Alpha)
|
|
{
|
|
check(RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setBlendColor(Red, Green, Blue, Alpha);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetDepthStencilState(MTL::DepthStencilState* InDepthStencilState)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setDepthStencilState(InDepthStencilState);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetStencilReferenceValue(uint32 const ReferenceValue)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
RenderCommandEncoder->setStencilReferenceValue(ReferenceValue);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetVisibilityResultMode(MTL::VisibilityResultMode const Mode, NS::UInteger const Offset)
|
|
{
|
|
check (RenderCommandEncoder);
|
|
{
|
|
check(Mode == MTL::VisibilityResultModeDisabled || RenderPassDesc->visibilityResultBuffer());
|
|
RenderCommandEncoder->setVisibilityResultMode(Mode, Offset);
|
|
}
|
|
}
|
|
|
|
#pragma mark - Public Shader Resource Mutators -
|
|
#if METAL_RHI_RAYTRACING
|
|
void FMetalCommandEncoder::SetShaderAccelerationStructure(MTL::FunctionType const FunctionType, MTL::AccelerationStructure const& AccelerationStructure, NS::UInteger const index)
|
|
{
|
|
if (AccelerationStructure)
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound |= (1 << index);
|
|
}
|
|
else
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound &= ~(1 << index);
|
|
}
|
|
|
|
ShaderBuffers[uint32(FunctionType)].AccelerationStructure[index] = AccelerationStructure;
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[index] = nullptr;
|
|
ShaderBuffers[uint32(FunctionType)].Bytes[index] = nullptr;
|
|
ShaderBuffers[uint32(FunctionType)].Offsets[index] = 0;
|
|
ShaderBuffers[uint32(FunctionType)].Usage[index] = MTL::ResourceUsage(0);
|
|
|
|
SetShaderBufferInternal(FunctionType, index);
|
|
}
|
|
#endif // METAL_RHI_RAYTRACING
|
|
|
|
void FMetalCommandEncoder::SetShaderBuffer(MTL::FunctionType const FunctionType, FMetalBufferPtr Buffer, NS::UInteger const Offset, NS::UInteger const Length, NS::UInteger index, MTL::ResourceUsage const Usage, EPixelFormat const Format, NS::UInteger const ElementRowPitch, TArray<TTuple<MTL::Resource*, MTL::ResourceUsage>> ReferencedResources)
|
|
{
|
|
FenceResource(Buffer->GetMTLBuffer(), FunctionType);
|
|
check(index < ML_MaxBuffers);
|
|
|
|
if(Device.SupportsFeature(EMetalFeaturesSetBufferOffset) &&
|
|
Buffer && (ShaderBuffers[uint32(FunctionType)].Bound & (1 << index)) &&
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[index] == Buffer)
|
|
{
|
|
if(ShaderBuffers[uint32(FunctionType)].Offsets[index] != Offset ||
|
|
ShaderBuffers[uint32(FunctionType)].Usage[index] != Usage)
|
|
{
|
|
SetShaderBufferOffset(FunctionType, Offset, Length, index);
|
|
ShaderBuffers[uint32(FunctionType)].Usage[index] = Usage;
|
|
ShaderBuffers[uint32(FunctionType)].ReferencedResources[index] = ReferencedResources;
|
|
ShaderBuffers[uint32(FunctionType)].SetBufferMetaData(index, Length, GMetalBufferFormats[Format].DataFormat, ElementRowPitch);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(Buffer)
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound |= (1 << index);
|
|
}
|
|
else
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound &= ~(1 << index);
|
|
}
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[index] = Buffer;
|
|
ShaderBuffers[uint32(FunctionType)].Bytes[index] = nullptr;
|
|
#if METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].AccelerationStructure[index] = nullptr;
|
|
#endif // METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].ReferencedResources[index] = ReferencedResources;
|
|
ShaderBuffers[uint32(FunctionType)].Offsets[index] = Offset;
|
|
ShaderBuffers[uint32(FunctionType)].Usage[index] = Usage;
|
|
ShaderBuffers[uint32(FunctionType)].SetBufferMetaData(index, Length, GMetalBufferFormats[Format].DataFormat, ElementRowPitch);
|
|
|
|
SetShaderBufferInternal(FunctionType, index);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderData(MTL::FunctionType const FunctionType, FMetalBufferData* Data, NS::UInteger const Offset, NS::UInteger const Index, EPixelFormat const Format, NS::UInteger const ElementRowPitch)
|
|
{
|
|
check(Index < ML_MaxBuffers);
|
|
|
|
if(Data)
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound |= (1 << Index);
|
|
}
|
|
else
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound &= ~(1 << Index);
|
|
}
|
|
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[Index] = nullptr;
|
|
#if METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].AccelerationStructure[Index] = nullptr;
|
|
#endif // METAL_RHI_RAYTRACINGs
|
|
ShaderBuffers[uint32(FunctionType)].ReferencedResources[Index].Empty();
|
|
ShaderBuffers[uint32(FunctionType)].Bytes[Index] = Data;
|
|
ShaderBuffers[uint32(FunctionType)].Offsets[Index] = Offset;
|
|
ShaderBuffers[uint32(FunctionType)].Usage[Index] = MTL::ResourceUsageRead;
|
|
ShaderBuffers[uint32(FunctionType)].SetBufferMetaData(Index, Data ? (Data->Len - Offset) : 0, GMetalBufferFormats[Format].DataFormat, ElementRowPitch);
|
|
SetShaderBufferInternal(FunctionType, Index);
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderBytes(MTL::FunctionType const FunctionType, uint8 const* Bytes, NS::UInteger const Length, NS::UInteger const Index)
|
|
{
|
|
check(Index < ML_MaxBuffers);
|
|
|
|
if(Bytes && Length)
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound |= (1 << Index);
|
|
|
|
if (bSupportsMetalFeaturesSetBytes)
|
|
{
|
|
switch (FunctionType)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setVertexBytes(Bytes, Length, Index);
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setFragmentBytes(Bytes, Length, Index);
|
|
break;
|
|
case MTL::FunctionTypeKernel:
|
|
check(ComputeCommandEncoder);
|
|
ComputeCommandEncoder->setBytes(Bytes, Length, Index);
|
|
break;
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
case MTL::FunctionTypeMesh:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setMeshBytes(Bytes, Length, Index);
|
|
break;
|
|
case MTL::FunctionTypeObject:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setObjectBytes(Bytes, Length, Index);
|
|
break;
|
|
#endif
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[Index] = nullptr;
|
|
}
|
|
else
|
|
{
|
|
FMetalBufferPtr Buffer = RingBuffer.NewBuffer(Length, BufferOffsetAlignment);
|
|
FMemory::Memcpy(((uint8*)Buffer->Contents()), Bytes, Length);
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[Index] = Buffer;
|
|
}
|
|
#if METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].AccelerationStructure[Index] = nullptr;
|
|
#endif // METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].ReferencedResources[Index].Empty();
|
|
ShaderBuffers[uint32(FunctionType)].Bytes[Index] = nullptr;
|
|
ShaderBuffers[uint32(FunctionType)].Offsets[Index] = 0;
|
|
ShaderBuffers[uint32(FunctionType)].Usage[Index] = MTL::ResourceUsageRead;
|
|
ShaderBuffers[uint32(FunctionType)].SetBufferMetaData(Index, Length, GMetalBufferFormats[PF_Unknown].DataFormat, 0);
|
|
}
|
|
else
|
|
{
|
|
ShaderBuffers[uint32(FunctionType)].Bound &= ~(1 << Index);
|
|
|
|
#if METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].AccelerationStructure[Index] = nullptr;
|
|
#endif // METAL_RHI_RAYTRACING
|
|
ShaderBuffers[uint32(FunctionType)].ReferencedResources[Index].Empty();
|
|
ShaderBuffers[uint32(FunctionType)].Buffers[Index] = nullptr;
|
|
ShaderBuffers[uint32(FunctionType)].Bytes[Index] = nullptr;
|
|
ShaderBuffers[uint32(FunctionType)].Offsets[Index] = 0;
|
|
ShaderBuffers[uint32(FunctionType)].Usage[Index] = MTL::ResourceUsage(0);
|
|
ShaderBuffers[uint32(FunctionType)].SetBufferMetaData(Index, 0, GMetalBufferFormats[PF_Unknown].DataFormat, 0);
|
|
}
|
|
|
|
SetShaderBufferInternal(FunctionType, Index);
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderBufferOffset(MTL::FunctionType FunctionType, NS::UInteger const Offset, NS::UInteger const Length, NS::UInteger const index)
|
|
{
|
|
check(index < ML_MaxBuffers);
|
|
checkf(ShaderBuffers[uint32(FunctionType)].Buffers[index] && (ShaderBuffers[uint32(FunctionType)].Bound & (1 << index)), TEXT("Buffer must already be bound"));
|
|
check(Device.SupportsFeature(EMetalFeaturesSetBufferOffset));
|
|
ShaderBuffers[uint32(FunctionType)].Offsets[index] = Offset;
|
|
ShaderBuffers[uint32(FunctionType)].SetBufferMetaData(index, Length, GMetalBufferFormats[PF_Unknown].DataFormat, 0);
|
|
|
|
switch (FunctionType)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
check (RenderCommandEncoder);
|
|
RenderCommandEncoder->setVertexBufferOffset(Offset + ShaderBuffers[uint32(FunctionType)].Buffers[index]->GetOffset(), index);
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setFragmentBufferOffset(Offset + ShaderBuffers[uint32(FunctionType)].Buffers[index]->GetOffset(), index);
|
|
break;
|
|
case MTL::FunctionTypeKernel:
|
|
check (ComputeCommandEncoder);
|
|
ComputeCommandEncoder->setBufferOffset(Offset + ShaderBuffers[uint32(FunctionType)].Buffers[index]->GetOffset(), index);
|
|
break;
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
case MTL::FunctionTypeObject:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setObjectBufferOffset(Offset + ShaderBuffers[uint32(FunctionType)].Buffers[index]->GetOffset(), index);
|
|
break;
|
|
case MTL::FunctionTypeMesh:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setMeshBufferOffset(Offset + ShaderBuffers[uint32(FunctionType)].Buffers[index]->GetOffset(), index);
|
|
break;
|
|
#endif
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderTexture(MTL::FunctionType FunctionType, MTL::Texture* Texture, NS::UInteger index, MTL::ResourceUsage Usage)
|
|
{
|
|
FenceResource(Texture, FunctionType);
|
|
check(index < ML_MaxTextures);
|
|
switch (FunctionType)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
check (RenderCommandEncoder);
|
|
RenderCommandEncoder->setVertexTexture(Texture, index);
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setFragmentTexture(Texture, index);
|
|
break;
|
|
case MTL::FunctionTypeKernel:
|
|
check (ComputeCommandEncoder);
|
|
ComputeCommandEncoder->setTexture(Texture, index);
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
|
|
if (Texture)
|
|
{
|
|
uint8 Swizzle[4] = {0,0,0,0};
|
|
assert(sizeof(Swizzle) == sizeof(uint32));
|
|
if (Texture->pixelFormat() == MTL::PixelFormatX32_Stencil8
|
|
#if PLATFORM_MAC
|
|
|| Texture->pixelFormat() == MTL::PixelFormatX24_Stencil8
|
|
#endif
|
|
)
|
|
{
|
|
Swizzle[0] = Swizzle[1] = Swizzle[2] = Swizzle[3] = 1;
|
|
}
|
|
|
|
ShaderBuffers[uint32(FunctionType)].SetTextureSwizzle(index, Swizzle);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderSamplerState(MTL::FunctionType FunctionType, MTL::SamplerState* Sampler, NS::UInteger index)
|
|
{
|
|
check(index < ML_MaxSamplers);
|
|
switch (FunctionType)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
check (RenderCommandEncoder);
|
|
RenderCommandEncoder->setVertexSamplerState(Sampler, index);
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
check (RenderCommandEncoder);
|
|
RenderCommandEncoder->setFragmentSamplerState(Sampler, index);
|
|
break;
|
|
case MTL::FunctionTypeKernel:
|
|
check (ComputeCommandEncoder);
|
|
ComputeCommandEncoder->setSamplerState(Sampler, index);
|
|
break;
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderSideTable(MTL::FunctionType const FunctionType, NS::UInteger const Index)
|
|
{
|
|
if (Index < ML_MaxBuffers)
|
|
{
|
|
SetShaderData(FunctionType, ShaderBuffers[uint32(FunctionType)].SideTable, 0, Index);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::UseIndirectArgumentResource(MTL::Texture* Texture, MTL::ResourceUsage const Usage)
|
|
{
|
|
FenceResource(Texture, MTL::FunctionTypeVertex);
|
|
UseResource(Texture, Usage);
|
|
}
|
|
|
|
void FMetalCommandEncoder::UseIndirectArgumentResource(FMetalBufferPtr Buffer, MTL::ResourceUsage const Usage)
|
|
{
|
|
MTL::Buffer* MTLBuffer = Buffer->GetMTLBuffer();
|
|
FenceResource(MTLBuffer, MTL::FunctionTypeVertex);
|
|
UseResource(MTLBuffer, Usage);
|
|
}
|
|
|
|
void FMetalCommandEncoder::TransitionResources(MTL::Resource* Resource)
|
|
{
|
|
TransitionedResources.Add(Resource);
|
|
}
|
|
|
|
#pragma mark - Public Compute State Mutators -
|
|
|
|
void FMetalCommandEncoder::SetComputePipelineState(FMetalShaderPipelinePtr State)
|
|
{
|
|
check (ComputeCommandEncoder);
|
|
{
|
|
ComputeCommandEncoder->setComputePipelineState(State->ComputePipelineState.get());
|
|
}
|
|
}
|
|
|
|
#pragma mark - Public Ring-Buffer Accessor -
|
|
|
|
FMetalSubBufferRing& FMetalCommandEncoder::GetRingBuffer(void)
|
|
{
|
|
return RingBuffer;
|
|
}
|
|
|
|
#pragma mark - Public Resource query Access -
|
|
|
|
#pragma mark - Private Functions -
|
|
|
|
void FMetalCommandEncoder::FenceResource(MTL::Texture* Resource, const MTL::FunctionType Function, bool bIsRenderTarget/* = false*/)
|
|
{
|
|
MTL::Resource* Res = Resource;
|
|
MTL::Texture* Parent = Resource->parentTexture();
|
|
MTL::Buffer* Buffer = Resource->buffer();
|
|
if (Parent)
|
|
{
|
|
Res = Parent;
|
|
}
|
|
else if (Buffer)
|
|
{
|
|
Res = Buffer;
|
|
}
|
|
|
|
FMetalBarrierScope& BarrierScope = CommandEncoderFence.BarrierScope;
|
|
if (CommandEncoderFence.FenceResources.Contains(Res))
|
|
{
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeKernel:
|
|
BarrierScope.TexturesWaitStage = EMetalFenceWaitStage::BeforeVertex;
|
|
break;
|
|
case MTL::FunctionTypeVertex:
|
|
BarrierScope.TexturesWaitStage = EMetalFenceWaitStage::BeforeVertex;
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
if (bIsRenderTarget)
|
|
{
|
|
BarrierScope.RenderTargetsWaitStage = EMetalFenceWaitStage::BeforeFragment;
|
|
}
|
|
else if (BarrierScope.TexturesWaitStage == EMetalFenceWaitStage::None)
|
|
{
|
|
BarrierScope.TexturesWaitStage = EMetalFenceWaitStage::BeforeFragment;
|
|
}
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::FenceResource(MTL::Buffer* Resource, const MTL::FunctionType Function)
|
|
{
|
|
MTL::Resource* Res = Resource;
|
|
FMetalBarrierScope& BarrierScope = CommandEncoderFence.BarrierScope;
|
|
if (CommandEncoderFence.FenceResources.Contains(Res))
|
|
{
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeKernel:
|
|
BarrierScope.BuffersWaitStage = EMetalFenceWaitStage::BeforeVertex;
|
|
break;
|
|
case MTL::FunctionTypeVertex:
|
|
BarrierScope.BuffersWaitStage = EMetalFenceWaitStage::BeforeVertex;
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
if (BarrierScope.BuffersWaitStage == EMetalFenceWaitStage::None)
|
|
{
|
|
BarrierScope.BuffersWaitStage = EMetalFenceWaitStage::BeforeFragment;
|
|
}
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::UseHeaps(TArray<MTL::Heap*> const& Heaps, const MTL::FunctionType Function)
|
|
{
|
|
if (RenderCommandEncoder)
|
|
{
|
|
MTL::RenderStages RenderStage = (MTL::RenderStages)0;
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
RenderStage |= MTLRenderStageVertex;
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
RenderStage |= MTLRenderStageFragment;
|
|
break;
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
case MTL::FunctionTypeMesh:
|
|
RenderStage |= MTLRenderStageMesh;
|
|
break;
|
|
case MTL::FunctionTypeObject:
|
|
RenderStage |= MTLRenderStageObject;
|
|
break;
|
|
#endif
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
}
|
|
|
|
RenderCommandEncoder->useHeaps(Heaps.GetData(), Heaps.Num(), RenderStage);
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->useHeaps(Heaps.GetData(), Heaps.Num());
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::UseResource(MTL::Resource* Resource, MTL::ResourceUsage const Usage)
|
|
{
|
|
// TODO: Rework residency caching (current one is broken)
|
|
auto IsAlreadyResident = ResourceUsage.Find(Resource);
|
|
if (!IsAlreadyResident)
|
|
{
|
|
ResourceUsage.Add(Resource, Usage);
|
|
}
|
|
else if (Usage != *IsAlreadyResident)
|
|
{
|
|
ResourceUsage[Resource] = Usage;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (RenderCommandEncoder)
|
|
{
|
|
RenderCommandEncoder->useResource(Resource, Usage);
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->useResource(Resource, Usage);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::UseResources(TArray<MTL::Resource*> const& Resources, MTL::ResourceUsage const Usage, MTL::RenderStages RenderStages)
|
|
{
|
|
if (RenderCommandEncoder)
|
|
{
|
|
checkSlow(RenderStages != 0);
|
|
RenderCommandEncoder->useResources(Resources.GetData(), Resources.Num(), Usage, RenderStages);
|
|
}
|
|
else if (ComputeCommandEncoder)
|
|
{
|
|
ComputeCommandEncoder->useResources(Resources.GetData(), Resources.Num(), Usage);
|
|
}
|
|
}
|
|
|
|
void FMetalCommandEncoder::SetShaderBufferInternal(MTL::FunctionType Function, uint32 Index)
|
|
{
|
|
FMetalBufferBindings& Binding = ShaderBuffers[uint32(Function)];
|
|
|
|
NS::UInteger Offset = Binding.Offsets[Index];
|
|
|
|
bool bBufferHasBytes = Binding.Bytes[Index] != nullptr;
|
|
if (!Binding.Buffers[Index] && bBufferHasBytes && !bSupportsMetalFeaturesSetBytes)
|
|
{
|
|
uint8 const* Bytes = (((uint8 const*)Binding.Bytes[Index]->Data) + Binding.Offsets[Index]);
|
|
uint32 Len = Binding.Bytes[Index]->Len - Binding.Offsets[Index];
|
|
|
|
Offset = 0;
|
|
Binding.Buffers[Index] = RingBuffer.NewBuffer(Len, BufferOffsetAlignment);
|
|
|
|
FMemory::Memcpy(((uint8*)Binding.Buffers[Index]->Contents()) + Offset, Bytes, Len);
|
|
}
|
|
|
|
TArray<TTuple<MTL::Resource*, MTL::ResourceUsage>> ReferencedResources = ShaderBuffers[uint32(Function)].ReferencedResources[Index];
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeKernel:
|
|
for (TTuple<MTL::Resource*, MTL::ResourceUsage>& ReferencedResource : ReferencedResources)
|
|
{
|
|
ComputeCommandEncoder->useResource(
|
|
ReferencedResource.Key,
|
|
ReferencedResource.Value
|
|
);
|
|
}
|
|
break;
|
|
case MTL::FunctionTypeVertex:
|
|
for (TTuple<MTL::Resource*, MTL::ResourceUsage>& ReferencedResource : ReferencedResources)
|
|
{
|
|
RenderCommandEncoder->useResource(
|
|
ReferencedResource.Key,
|
|
ReferencedResource.Value,
|
|
MTL::RenderStageVertex
|
|
);
|
|
}
|
|
break;
|
|
case MTL::FunctionTypeFragment:
|
|
for (TTuple<MTL::Resource*, MTL::ResourceUsage>& ReferencedResource : ReferencedResources)
|
|
{
|
|
RenderCommandEncoder->useResource(
|
|
ReferencedResource.Key,
|
|
ReferencedResource.Value,
|
|
MTL::RenderStageFragment
|
|
);
|
|
}
|
|
break;
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
case MTL::FunctionTypeObject:
|
|
for (TTuple<MTL::Resource*, MTL::ResourceUsage>& ReferencedResource : ReferencedResources)
|
|
{
|
|
RenderCommandEncoder->useResource(
|
|
ReferencedResource.Key,
|
|
ReferencedResource.Value,
|
|
MTL::RenderStageObject
|
|
);
|
|
}
|
|
break;
|
|
case MTL::FunctionTypeMesh:
|
|
for (TTuple<MTL::Resource*, MTL::ResourceUsage>& ReferencedResource : ReferencedResources)
|
|
{
|
|
RenderCommandEncoder->useResource(
|
|
ReferencedResource.Key,
|
|
ReferencedResource.Value,
|
|
MTL::RenderStageMesh
|
|
);
|
|
}
|
|
break;
|
|
#endif // PLATFORM_SUPPORTS_MESH_SHADERS
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
};
|
|
|
|
FMetalBufferPtr Buffer = Binding.Buffers[Index];
|
|
#if METAL_RHI_RAYTRACING
|
|
MTL::AccelerationStructure* AS = ShaderBuffers[uint32(Function)].AccelerationStructure[Index];
|
|
if (AS)
|
|
{
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeKernel:
|
|
ShaderBuffers[uint32(Function)].Bound |= (1 << Index);
|
|
check(ComputeCommandEncoder);
|
|
ComputeCommandEncoder.UseResource(AS, MTL::ResourceUsage::Read);
|
|
ComputeCommandEncoder.SetAccelerationStructure(AS, Index);
|
|
break;
|
|
default:
|
|
checkNoEntry();
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
#endif // METAL_RHI_RAYTRACING
|
|
if (Buffer)
|
|
{
|
|
#if METAL_DEBUG_OPTIONS
|
|
if(Device.GetRuntimeDebuggingLevel() >= EMetalDebugLevelValidation)
|
|
{
|
|
ActiveBuffers.Add(Buffer);
|
|
}
|
|
#endif
|
|
MTL::Buffer* MTLBuffer = Buffer->GetMTLBuffer();
|
|
FenceResource(MTLBuffer, Function);
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setVertexBuffer(MTLBuffer, Offset + Buffer->GetOffset(), Index);
|
|
break;
|
|
|
|
case MTL::FunctionTypeFragment:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setFragmentBuffer(MTLBuffer, Offset + Buffer->GetOffset(), Index);
|
|
break;
|
|
|
|
case MTL::FunctionTypeKernel:
|
|
Binding.Bound |= (1 << Index);
|
|
check(ComputeCommandEncoder);
|
|
ComputeCommandEncoder->setBuffer(MTLBuffer, Offset + Buffer->GetOffset(), Index);
|
|
break;
|
|
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
case MTL::FunctionTypeObject:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setObjectBuffer(MTLBuffer, Offset + Buffer->GetOffset(), Index);
|
|
break;
|
|
|
|
case MTL::FunctionTypeMesh:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setMeshBuffer(MTLBuffer, Offset + Buffer->GetOffset(), Index);
|
|
break;
|
|
#endif // PLATFORM_SUPPORTS_MESH_SHADERS
|
|
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
else if (bBufferHasBytes && bSupportsMetalFeaturesSetBytes)
|
|
{
|
|
uint8 const* Bytes = (((uint8 const*)Binding.Bytes[Index]->Data) + Binding.Offsets[Index]);
|
|
uint32 Len = Binding.Bytes[Index]->Len - Binding.Offsets[Index];
|
|
|
|
switch (Function)
|
|
{
|
|
case MTL::FunctionTypeVertex:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setVertexBytes(Bytes, Len, Index);
|
|
break;
|
|
|
|
case MTL::FunctionTypeFragment:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setFragmentBytes(Bytes, Len, Index);
|
|
break;
|
|
|
|
case MTL::FunctionTypeKernel:
|
|
Binding.Bound |= (1 << Index);
|
|
check(ComputeCommandEncoder);
|
|
ComputeCommandEncoder->setBytes(Bytes, Len, Index);
|
|
break;
|
|
|
|
#if PLATFORM_SUPPORTS_MESH_SHADERS
|
|
case MTL::FunctionTypeObject:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setObjectBytes(Bytes, Len, Index);
|
|
break;
|
|
|
|
case MTL::FunctionTypeMesh:
|
|
Binding.Bound |= (1 << Index);
|
|
check(RenderCommandEncoder);
|
|
RenderCommandEncoder->setMeshBytes(Bytes, Len, Index);
|
|
break;
|
|
#endif // PLATFORM_SUPPORTS_MESH_SHADERS
|
|
|
|
default:
|
|
check(false);
|
|
break;
|
|
}
|
|
}
|
|
}
|