virtual void SubmitAndFreeContextContainer(int32 Index, int32 Num) override { if (Index == 0) { check((IsInRenderingThread() || IsInRHIThread())); OwningDevice->GetDefaultCommandContext().FlushCommands(); } // Add the current lists for execution (now or possibly later) for (int32 i = 0; i < CommandLists.Num(); ++i) { OwningDevice->PendingCommandLists.Add(CommandLists[i]); OwningDevice->PendingCommandListsTotalWorkCommands += CommandLists[i].GetCurrentOwningContext()->numClears + CommandLists[i].GetCurrentOwningContext()->numCopies + CommandLists[i].GetCurrentOwningContext()->numDraws; } CommandLists.Reset(); // Submission occurs when a batch is finished const bool FinalCommandListInBatch = Index == (Num - 1); if (FinalCommandListInBatch && OwningDevice->PendingCommandLists.Num() > 0) { #if SUPPORTS_MEMORY_RESIDENCY OwningDevice->GetOwningRHI()->GetResourceResidencyManager().MakeResident(); #endif OwningDevice->GetCommandListManager().ExecuteCommandLists(OwningDevice->PendingCommandLists); OwningDevice->PendingCommandLists.Reset(); OwningDevice->PendingCommandListsTotalWorkCommands = 0; } delete this; }
void FD3D12CommandContext::RHIBeginFrame() { check(IsDefaultContext()); RHIPrivateBeginFrame(); FD3D12Device* Device = GetParentDevice(); FD3D12GlobalOnlineHeap& SamplerHeap = Device->GetGlobalSamplerHeap(); const uint32 NumContexts = Device->GetNumContexts(); if (SamplerHeap.DescriptorTablesDirty()) { //Rearrange the set for better look-up performance SamplerHeap.GetUniqueDescriptorTables().Compact(); } for (uint32 i = 0; i < NumContexts; ++i) { Device->GetCommandContext(i).StateCache.GetDescriptorCache()->BeginFrame(); } Device->GetGlobalSamplerHeap().ToggleDescriptorTablesDirtyFlag(false); UniformBufferBeginFrame(); OwningRHI.GPUProfilingData.BeginFrame(&OwningRHI); }
virtual void FinishContext() override { // We never "Finish" the default context. It gets submitted when FlushCommands() is called. check(!CmdContext->IsDefaultContext()); CmdContext->Finish(CommandLists); OwningDevice->ReleaseCommandContext(CmdContext); CmdContext = nullptr; }
FD3D12CommandListHandle FD3D12CommandContext::FlushCommands(bool WaitForCompletion) { // We should only be flushing the default context check(IsDefaultContext()); FD3D12Device* Device = GetParentDevice(); const bool bExecutePendingWork = GCommandListBatchingMode == CLB_AggressiveBatching || GetParentDevice()->IsGPUIdle(); const bool bHasPendingWork = bExecutePendingWork && (Device->PendingCommandListsTotalWorkCommands > 0); const bool bHasDoneWork = HasDoneWork() || bHasPendingWork; // Only submit a command list if it does meaningful work or the flush is expected to wait for completion. if (WaitForCompletion || bHasDoneWork) { #if SUPPORTS_MEMORY_RESIDENCY Device->GetOwningRHI()->GetResourceResidencyManager().MakeResident(); #endif // Close the current command list CloseCommandList(); if (bHasPendingWork) { // Submit all pending command lists and the current command list Device->PendingCommandLists.Add(CommandListHandle); GetCommandListManager().ExecuteCommandLists(Device->PendingCommandLists, WaitForCompletion); Device->PendingCommandLists.Reset(); Device->PendingCommandListsTotalWorkCommands = 0; } else { // Just submit the current command list CommandListHandle.Execute(WaitForCompletion); } // Get a new command list to replace the one we submitted for execution. // Restore the state from the previous command list. OpenCommandList(true); } return CommandListHandle; }
void FD3D12BuddyAllocator::Initialize() { FD3D12Device* Device = GetParentDevice(); FD3D12Adapter* Adapter = Device->GetParentAdapter(); if (AllocationStrategy == eBuddyAllocationStrategy::kPlacedResourceStrategy) { D3D12_HEAP_PROPERTIES HeapProps = CD3DX12_HEAP_PROPERTIES(HeapType); HeapProps.CreationNodeMask = GetNodeMask(); HeapProps.VisibleNodeMask = GetVisibilityMask(); D3D12_HEAP_DESC Desc = {}; Desc.SizeInBytes = MaxBlockSize; Desc.Properties = HeapProps; Desc.Alignment = 0; Desc.Flags = HeapFlags; ID3D12Heap* Heap = nullptr; VERIFYD3D12RESULT(Adapter->GetD3DDevice()->CreateHeap(&Desc, IID_PPV_ARGS(&Heap))); SetName(Heap, L"Placed Resource Allocator Backing Heap"); BackingHeap = new FD3D12Heap(GetParentDevice(), GetVisibilityMask()); BackingHeap->SetHeap(Heap); if (IsCPUWritable(HeapType) == false) { BackingHeap->BeginTrackingResidency(Desc.SizeInBytes); } } else { VERIFYD3D12RESULT(Adapter->CreateBuffer(HeapType, GetNodeMask(), GetVisibilityMask(), MaxBlockSize, BackingResource.GetInitReference(), ResourceFlags)); SetName(BackingResource, L"Resource Allocator Underlying Buffer"); if (IsCPUWritable(HeapType)) { BackingResource->Map(); } } }
inline FD3D12UnorderedAccessView* CreateUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC& Desc, ResourceType* Resource, bool bNeedsCounterResource) { if (Resource == nullptr) { return nullptr; } FD3D12Adapter* Adapter = Resource->GetParentDevice()->GetParentAdapter(); return Adapter->CreateLinkedViews<ResourceType, FD3D12UnorderedAccessView>(Resource, [bNeedsCounterResource, &Desc](ResourceType* Resource) { FD3D12Device* Device = Resource->GetParentDevice(); FD3D12Resource* CounterResource = nullptr; if (bNeedsCounterResource) { const GPUNodeMask Node = Device->GetNodeMask(); Device->GetParentAdapter()->CreateBuffer(D3D12_HEAP_TYPE_DEFAULT, Node, Node, 4, &CounterResource, D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS); } return new FD3D12UnorderedAccessView(Device, &Desc, &Resource->ResourceLocation, CounterResource); }); }
virtual IRHICommandContext* GetContext() override { check(CmdContext == nullptr); CmdContext = OwningDevice->ObtainCommandContext(); check(CmdContext != nullptr); if (CmdContext->CommandListHandle == nullptr) { CmdContext->OpenCommandList(); } CmdContext->ClearState(); return CmdContext; }
void FD3D12CommandContext::RHIEndFrame() { check(IsDefaultContext()); OwningRHI.GPUProfilingData.EndFrame(); FD3D12Device* Device = GetParentDevice(); Device->FirstFrameSeen = true; Device->GetDeferredDeletionQueue().ReleaseResources(); Device->GetDefaultBufferAllocator().CleanupFreeBlocks(); const uint32 NumContexts = Device->GetNumContexts(); for (uint32 i = 0; i < NumContexts; ++i) { Device->GetCommandContext(i).EndFrame(); } Device->GetDefaultUploadHeapAllocator().CleanUpAllocations(); Device->GetTextureAllocator().CleanUpAllocations(); Device->GetDefaultBufferAllocator().CleanupFreeBlocks(); Device->GetDefaultFastAllocatorPool().CleanUpPages(10); { FScopeLock Lock(Device->GetBufferInitFastAllocator().GetCriticalSection()); Device->GetBufferInitFastAllocatorPool().CleanUpPages(10); } // The Texture streaming threads share a pool { FD3D12ThreadSafeFastAllocator* pCurrentHelperThreadDynamicHeapAllocator = nullptr; for (uint32 i = 0; i < FD3D12DynamicRHI::GetD3DRHI()->NumThreadDynamicHeapAllocators; ++i) { pCurrentHelperThreadDynamicHeapAllocator = FD3D12DynamicRHI::GetD3DRHI()->ThreadDynamicHeapAllocatorArray[i]; if (pCurrentHelperThreadDynamicHeapAllocator) { FScopeLock Lock(pCurrentHelperThreadDynamicHeapAllocator->GetCriticalSection()); pCurrentHelperThreadDynamicHeapAllocator->GetPool()->CleanUpPages(10); break; } } } #if SUPPORTS_MEMORY_RESIDENCY GetResourceResidencyManager().Process(); #endif DXGI_QUERY_VIDEO_MEMORY_INFO LocalVideoMemoryInfo; FD3D12DynamicRHI::GetD3DRHI()->GetLocalVideoMemoryInfo(&LocalVideoMemoryInfo); int64 budget = LocalVideoMemoryInfo.Budget; int64 AvailableSpace = budget - int64(LocalVideoMemoryInfo.CurrentUsage); SET_MEMORY_STAT(STAT_D3D12UsedVideoMemory, LocalVideoMemoryInfo.CurrentUsage); SET_MEMORY_STAT(STAT_D3D12AvailableVideoMemory, AvailableSpace); SET_MEMORY_STAT(STAT_D3D12TotalVideoMemory, budget); }
void* FD3D12DynamicRHI::LockBuffer(FRHICommandListImmediate* RHICmdList, BufferType* Buffer, uint32 Offset, uint32 Size, EResourceLockMode LockMode) { #if STATS LockBufferCalls++; SCOPE_CYCLE_COUNTER(STAT_D3D12LockBufferTime); INC_DWORD_STAT_BY(STAT_D3D12LockBufferCalls, LockBufferCalls); #endif FD3D12LockedResource& LockedData = Buffer->LockedData; check(LockedData.bLocked == false); FD3D12Device* Device = GetRHIDevice(); FD3D12Adapter& Adapter = GetAdapter(); // Determine whether the buffer is dynamic or not. const bool bIsDynamic = (Buffer->GetUsage() & BUF_AnyDynamic) ? true : false; void* Data = nullptr; if (bIsDynamic) { check(LockMode == RLM_WriteOnly); BufferType* CurrentBuffer = Buffer; // Update all of the resources in the LDA chain while (CurrentBuffer) { // Allocate a new resource // If on the RenderThread, queue up a command on the RHIThread to rename this buffer at the correct time if (ShouldDeferBufferLockOperation(RHICmdList)) { FRHICommandRenameUploadBuffer<BufferType>* Command = new (RHICmdList->AllocCommand<FRHICommandRenameUploadBuffer<BufferType>>()) FRHICommandRenameUploadBuffer<BufferType>(CurrentBuffer, Device); Data = Adapter.GetUploadHeapAllocator().AllocUploadResource(Buffer->GetSize(), Buffer->BufferAlignment, Command->NewResource); } else { FD3D12ResourceLocation Location(CurrentBuffer->GetParentDevice()); Data = Adapter.GetUploadHeapAllocator().AllocUploadResource(Buffer->GetSize(), Buffer->BufferAlignment, Location); CurrentBuffer->Rename(Location); } CurrentBuffer = CurrentBuffer->GetNextObject(); } } else { FD3D12Resource* pResource = Buffer->ResourceLocation.GetResource(); // Locking for read must occur immediately so we can't queue up the operations later. if (LockMode == RLM_ReadOnly) { LockedData.bLockedForReadOnly = true; // If the static buffer is being locked for reading, create a staging buffer. FD3D12Resource* StagingBuffer = nullptr; const GPUNodeMask Node = Device->GetNodeMask(); VERIFYD3D12RESULT(Adapter.CreateBuffer(D3D12_HEAP_TYPE_READBACK, Node, Node, Offset + Size, &StagingBuffer)); // Copy the contents of the buffer to the staging buffer. { const auto& pfnCopyContents = [&]() { FD3D12CommandContext& DefaultContext = Device->GetDefaultCommandContext(); FD3D12CommandListHandle& hCommandList = DefaultContext.CommandListHandle; FScopeResourceBarrier ScopeResourceBarrierSource(hCommandList, pResource, pResource->GetDefaultResourceState(), D3D12_RESOURCE_STATE_COPY_SOURCE, 0); // Don't need to transition upload heaps DefaultContext.numCopies++; hCommandList->CopyBufferRegion( StagingBuffer->GetResource(), 0, pResource->GetResource(), Offset, Size); hCommandList.UpdateResidency(StagingBuffer); hCommandList.UpdateResidency(pResource); DefaultContext.FlushCommands(true); }; if (ShouldDeferBufferLockOperation(RHICmdList)) { // Sync when in the render thread implementation check(IsInRHIThread() == false); RHICmdList->ImmediateFlush(EImmediateFlushType::FlushRHIThread); pfnCopyContents(); } else { check(IsInRHIThread()); pfnCopyContents(); } } LockedData.ResourceLocation.AsStandAlone(StagingBuffer, Size); Data = LockedData.ResourceLocation.GetMappedBaseAddress(); } else { // If the static buffer is being locked for writing, allocate memory for the contents to be written to. Data = Device->GetDefaultFastAllocator().Allocate<FD3D12ScopeLock>(Size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT, &LockedData.ResourceLocation); } } LockedData.LockedOffset = Offset; LockedData.LockedPitch = Size; LockedData.bLocked = true; // Return the offset pointer check(Data != nullptr); return Data; }
BufferType* FD3D12Adapter::CreateRHIBuffer(FRHICommandListImmediate* RHICmdList, const D3D12_RESOURCE_DESC& InDesc, uint32 Alignment, uint32 Stride, uint32 Size, uint32 InUsage, FRHIResourceCreateInfo& CreateInfo, bool SkipCreate) { const bool bIsDynamic = (InUsage & BUF_AnyDynamic) ? true : false; BufferType* BufferOut = CreateLinkedObject<BufferType>([&](FD3D12Device* Device) { BufferType* NewBuffer = new BufferType(Device, Stride, Size, InUsage); NewBuffer->BufferAlignment = Alignment; if (SkipCreate == false) { AllocateBuffer(Device, InDesc, Size, InUsage, CreateInfo, Alignment, NewBuffer->ResourceLocation); } return NewBuffer; }); if (CreateInfo.ResourceArray) { if (bIsDynamic == false && BufferOut->ResourceLocation.IsValid()) { check(Size == CreateInfo.ResourceArray->GetResourceDataSize()); // Get an upload heap and initialize data FD3D12ResourceLocation SrcResourceLoc(BufferOut->GetParentDevice()); void* pData = SrcResourceLoc.GetParentDevice()->GetDefaultFastAllocator().Allocate<FD3D12ScopeLock>(Size, 4UL, &SrcResourceLoc); check(pData); FMemory::Memcpy(pData, CreateInfo.ResourceArray->GetResourceData(), Size); const auto& pfnUpdateBuffer = [&]() { BufferType* CurrentBuffer = BufferOut; while (CurrentBuffer != nullptr) { FD3D12Resource* Destination = CurrentBuffer->ResourceLocation.GetResource(); FD3D12Device* Device = Destination->GetParentDevice(); FD3D12CommandListHandle& hCommandList = Device->GetDefaultCommandContext().CommandListHandle; // Copy from the temporary upload heap to the default resource { // Writable structured bufferes are sometimes initialized with inital data which means they sometimes need tracking. FConditionalScopeResourceBarrier ConditionalScopeResourceBarrier(hCommandList, Destination, D3D12_RESOURCE_STATE_COPY_DEST, 0); Device->GetDefaultCommandContext().numCopies++; hCommandList->CopyBufferRegion( Destination->GetResource(), CurrentBuffer->ResourceLocation.GetOffsetFromBaseOfResource(), SrcResourceLoc.GetResource()->GetResource(), SrcResourceLoc.GetOffsetFromBaseOfResource(), Size); hCommandList.UpdateResidency(Destination); } CurrentBuffer = CurrentBuffer->GetNextObject(); } }; //TODO: This should be a deferred op like the buffer lock/unlocks // We only need to synchronize when creating default resource buffers (because we need a command list to initialize them) if (RHICmdList) { FScopedRHIThreadStaller StallRHIThread(*RHICmdList); pfnUpdateBuffer(); } else { pfnUpdateBuffer(); } } // Discard the resource array's contents. CreateInfo.ResourceArray->Discard(); } return BufferOut; }