void FD3D12DynamicRHI::UnlockBuffer(FRHICommandListImmediate* RHICmdList, BufferType* Buffer) { FD3D12LockedResource& LockedData = Buffer->LockedData; check(LockedData.bLocked == true); // Determine whether the buffer is dynamic or not. const bool bIsDynamic = (Buffer->GetUsage() & BUF_AnyDynamic) ? true : false; if (bIsDynamic) { // If the Buffer is dynamic, its upload heap memory can always stay mapped. Don't do anything. } else { if (LockedData.bLockedForReadOnly) { //Nothing to do, just release the locked data at the end of the function } else { // Copy the contents of the temporary memory buffer allocated for writing into the Buffer. BufferType* CurrentBuffer = Buffer; // Update all of the resources in the LDA chain while (CurrentBuffer) { // If we are on the render thread, queue up the copy on the RHIThread so it happens at the correct time. if (ShouldDeferBufferLockOperation(RHICmdList)) { new (RHICmdList->AllocCommand<FRHICommandUpdateBuffer>()) FRHICommandUpdateBuffer(&CurrentBuffer->ResourceLocation, LockedData.ResourceLocation, LockedData.LockedOffset, LockedData.LockedPitch); } else { UpdateBuffer(CurrentBuffer->ResourceLocation.GetResource(), CurrentBuffer->ResourceLocation.GetOffsetFromBaseOfResource() + LockedData.LockedOffset, LockedData.ResourceLocation.GetResource(), LockedData.ResourceLocation.GetOffsetFromBaseOfResource(), LockedData.LockedPitch); } CurrentBuffer = CurrentBuffer->GetNextObject(); } } } LockedData.Reset(); }
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; }