void FDrawEvent::Start(FRHICommandList& InRHICmdList, const FColor& Color, const TCHAR* Fmt, ...) { check(IsInParallelRenderingThread() || IsInRHIThread()); { va_list ptr; va_start(ptr, Fmt); TCHAR TempStr[256]; // Build the string in the temp buffer FCString::GetVarArgs(TempStr, ARRAY_COUNT(TempStr), ARRAY_COUNT(TempStr) - 1, Fmt, ptr); InRHICmdList.PushEvent(TempStr); RHICmdList = &InRHICmdList; } }
void FDrawEventRHIExecute::Start(IRHIComputeContext& InRHICommandContext, FColor Color, const TCHAR* Fmt, ...) { check(IsInParallelRenderingThread() || IsInRHIThread() || (!GRHIThread && IsInRenderingThread())); { va_list ptr; va_start(ptr, Fmt); TCHAR TempStr[256]; // Build the string in the temp buffer FCString::GetVarArgs(TempStr, ARRAY_COUNT(TempStr), ARRAY_COUNT(TempStr) - 1, Fmt, ptr); RHICommandContext = &InRHICommandContext; RHICommandContext->RHIPushEvent(TempStr, Color); } }
/** * Creates a bound shader state instance which encapsulates a decl, vertex shader, and pixel shader * @param VertexDeclaration - existing vertex decl * @param StreamStrides - optional stream strides * @param VertexShader - existing vertex shader * @param HullShader - existing hull shader * @param DomainShader - existing domain shader * @param PixelShader - existing pixel shader * @param GeometryShader - existing geometry shader */ FBoundShaderStateRHIRef FD3D12DynamicRHI::RHICreateBoundShaderState( FVertexDeclarationRHIParamRef VertexDeclarationRHI, FVertexShaderRHIParamRef VertexShaderRHI, FHullShaderRHIParamRef HullShaderRHI, FDomainShaderRHIParamRef DomainShaderRHI, FPixelShaderRHIParamRef PixelShaderRHI, FGeometryShaderRHIParamRef GeometryShaderRHI ) { SCOPE_CYCLE_COUNTER(STAT_D3D12CreateBoundShaderStateTime); checkf(GIsRHIInitialized && GetRHIDevice()->GetCommandListManager().IsReady(), (TEXT("Bound shader state RHI resource was created without initializing Direct3D first"))); #if D3D12_SUPPORTS_PARALLEL_RHI_EXECUTE // Check for an existing bound shader state which matches the parameters FBoundShaderStateRHIRef CachedBoundShaderState = GetCachedBoundShaderState_Threadsafe( VertexDeclarationRHI, VertexShaderRHI, PixelShaderRHI, HullShaderRHI, DomainShaderRHI, GeometryShaderRHI ); if(CachedBoundShaderState.GetReference()) { // If we've already created a bound shader state with these parameters, reuse it. return CachedBoundShaderState; } #else check(IsInRenderingThread() || IsInRHIThread()); // Check for an existing bound shader state which matches the parameters FCachedBoundShaderStateLink* CachedBoundShaderStateLink = GetCachedBoundShaderState( VertexDeclarationRHI, VertexShaderRHI, PixelShaderRHI, HullShaderRHI, DomainShaderRHI, GeometryShaderRHI ); if(CachedBoundShaderStateLink) { // If we've already created a bound shader state with these parameters, reuse it. return CachedBoundShaderStateLink->BoundShaderState; } #endif else { SCOPE_CYCLE_COUNTER(STAT_D3D12NewBoundShaderStateTime); return new FD3D12BoundShaderState(VertexDeclarationRHI, VertexShaderRHI, PixelShaderRHI, HullShaderRHI, DomainShaderRHI, GeometryShaderRHI); } }
FUniformBufferRHIRef FD3D11DynamicRHI::RHICreateUniformBuffer(const void* Contents,const FRHIUniformBufferLayout& Layout,EUniformBufferUsage Usage) { check(IsInRenderingThread() || IsInRHIThread()); FD3D11UniformBuffer* NewUniformBuffer = nullptr; const uint32 NumBytes = Layout.ConstantBufferSize; if (NumBytes > 0) { // Constant buffers must also be 16-byte aligned. check(Align(NumBytes,16) == NumBytes); check(Align(Contents,16) == Contents); check(NumBytes <= D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * 16); check(NumBytes < (1 << NumPoolBuckets)); SCOPE_CYCLE_COUNTER(STAT_D3D11UpdateUniformBufferTime); if (IsPoolingEnabled()) { TRefCountPtr<ID3D11Buffer> UniformBufferResource; FRingAllocation RingAllocation; if (!RingAllocation.IsValid()) { // Find the appropriate bucket based on size const uint32 BucketIndex = GetPoolBucketIndex(NumBytes); TArray<FPooledUniformBuffer>& PoolBucket = UniformBufferPool[BucketIndex]; if (PoolBucket.Num() > 0) { // Reuse the last entry in this size bucket FPooledUniformBuffer FreeBufferEntry = PoolBucket.Pop(); check(IsValidRef(FreeBufferEntry.Buffer)); UniformBufferResource = FreeBufferEntry.Buffer; checkf(FreeBufferEntry.CreatedSize >= NumBytes, TEXT("%u %u %u %u"), NumBytes, BucketIndex, FreeBufferEntry.CreatedSize, GetPoolBucketSize(NumBytes)); DEC_DWORD_STAT(STAT_D3D11NumFreeUniformBuffers); DEC_MEMORY_STAT_BY(STAT_D3D11FreeUniformBufferMemory, FreeBufferEntry.CreatedSize); } // Nothing usable was found in the free pool, create a new uniform buffer if (!IsValidRef(UniformBufferResource)) { D3D11_BUFFER_DESC Desc; // Allocate based on the bucket size, since this uniform buffer will be reused later Desc.ByteWidth = GetPoolBucketSize(NumBytes); // Use D3D11_USAGE_DYNAMIC, which allows multiple CPU writes for pool reuses // This is method of updating is vastly superior to creating a new constant buffer each time with D3D11_USAGE_IMMUTABLE, // Since that inserts the data into the command buffer which causes GPU flushes Desc.Usage = D3D11_USAGE_DYNAMIC; Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; Desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; Desc.MiscFlags = 0; Desc.StructureByteStride = 0; VERIFYD3D11RESULT(Direct3DDevice->CreateBuffer(&Desc, NULL, UniformBufferResource.GetInitReference())); UpdateBufferStats(UniformBufferResource, true); } check(IsValidRef(UniformBufferResource)); D3D11_MAPPED_SUBRESOURCE MappedSubresource; // Discard previous results since we always do a full update VERIFYD3D11RESULT(Direct3DDeviceIMContext->Map(UniformBufferResource, 0, D3D11_MAP_WRITE_DISCARD, 0, &MappedSubresource)); check(MappedSubresource.RowPitch >= NumBytes); FMemory::Memcpy(MappedSubresource.pData, Contents, NumBytes); Direct3DDeviceIMContext->Unmap(UniformBufferResource, 0); } NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, UniformBufferResource, RingAllocation); } else { // No pooling D3D11_BUFFER_DESC Desc; Desc.ByteWidth = NumBytes; Desc.Usage = D3D11_USAGE_IMMUTABLE; Desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; Desc.CPUAccessFlags = 0; Desc.MiscFlags = 0; Desc.StructureByteStride = 0; D3D11_SUBRESOURCE_DATA ImmutableData; ImmutableData.pSysMem = Contents; ImmutableData.SysMemPitch = ImmutableData.SysMemSlicePitch = 0; TRefCountPtr<ID3D11Buffer> UniformBufferResource; VERIFYD3D11RESULT(Direct3DDevice->CreateBuffer(&Desc,&ImmutableData,UniformBufferResource.GetInitReference())); NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, UniformBufferResource, FRingAllocation()); } } else { // This uniform buffer contains no constants, only a resource table. NewUniformBuffer = new FD3D11UniformBuffer(this, Layout, nullptr, FRingAllocation()); } if (Layout.Resources.Num()) { int32 NumResources = Layout.Resources.Num(); FRHIResource** InResources = (FRHIResource**)((uint8*)Contents + Layout.ResourceOffset); NewUniformBuffer->ResourceTable.Empty(NumResources); NewUniformBuffer->ResourceTable.AddZeroed(NumResources); for (int32 i = 0; i < NumResources; ++i) { check(InResources[i]); NewUniformBuffer->ResourceTable[i] = InResources[i]; } NewUniformBuffer->RawResourceTable.Empty(NumResources); NewUniformBuffer->RawResourceTable.AddZeroed(NumResources); } return NewUniformBuffer; }
void* FD3D12DynamicRHI::LockBuffer(FRHICommandListImmediate* RHICmdList, BufferType* Buffer, uint32 Offset, uint32 Size, EResourceLockMode LockMode) { FD3D12LockedData LockedData; // 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); TRefCountPtr<FD3D12ResourceLocation> newLocation = new FD3D12ResourceLocation(GetRHIDevice()); // Allocate a new resource Data = GetRHIDevice()->GetDefaultUploadHeapAllocator().AllocUploadResource(Buffer->ResourceLocation->GetEffectiveBufferSize(), Buffer->BufferAlignment, newLocation); // If on the RenderThread, queue up a command on the RHIThread to rename this buffer at the correct time if (ShouldDeferBufferLockOperation(RHICmdList)) { new (RHICmdList->AllocCommand<FRHICommandRenameUploadBuffer<BufferType>>()) FRHICommandRenameUploadBuffer<BufferType>(Buffer, newLocation); } else { Buffer->Rename(newLocation); } } 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) { // If the static buffer is being locked for reading, create a staging buffer. TRefCountPtr<FD3D12Resource> StagingBuffer; VERIFYD3D11RESULT(GetRHIDevice()->GetResourceHelper().CreateBuffer(D3D12_HEAP_TYPE_READBACK, Offset + Size, StagingBuffer.GetInitReference())); LockedData.StagingResource = StagingBuffer; // Copy the contents of the buffer to the staging buffer. { const auto& pfnCopyContents = [&]() { FD3D12CommandContext& DefaultContext = GetRHIDevice()->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); 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(); } } // Map the staging buffer's memory for reading. VERIFYD3D11RESULT(StagingBuffer->GetResource()->Map(0, nullptr, &Data)); } else { // If the static buffer is being locked for writing, allocate memory for the contents to be written to. TRefCountPtr<FD3D12ResourceLocation> UploadBufferLocation = new FD3D12ResourceLocation(GetRHIDevice()); Data = GetRHIDevice()->GetDefaultFastAllocator().Allocate(Offset + Size, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT, UploadBufferLocation); // Keep track of the underlying resource for the upload heap so it can be referenced during Unmap. LockedData.UploadHeapLocation = UploadBufferLocation; } } check(Data); // Add the lock to the lock map. LockedData.SetData(Data); LockedData.Pitch = Offset + Size; // Add the lock to the lock map. FD3D12LockedKey LockedKey(Buffer); AddToOutstandingLocks(LockedKey, LockedData); // Return the offset pointer return (void*)((uint8*)LockedData.GetData() + Offset); }
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; }