FD3D11UniformBuffer::~FD3D11UniformBuffer() { // Do not return the allocation to the pool if it is in the dynamic constant buffer! if (!RingAllocation.IsValid() && Resource != nullptr) { check(IsInRenderingThread()); D3D11_BUFFER_DESC Desc; Resource->GetDesc(&Desc); // Return this uniform buffer to the free pool if (Desc.CPUAccessFlags == D3D11_CPU_ACCESS_WRITE && Desc.Usage == D3D11_USAGE_DYNAMIC) { check(IsValidRef(Resource)); FPooledUniformBuffer NewEntry; NewEntry.Buffer = Resource; NewEntry.FrameFreed = GFrameNumberRenderThread; NewEntry.CreatedSize = Desc.ByteWidth; // Add to this frame's array of free uniform buffers const int32 SafeFrameIndex = (GFrameNumberRenderThread - 1) % NumSafeFrames; const uint32 BucketIndex = GetPoolBucketIndex(Desc.ByteWidth); int32 LastNum = SafeUniformBufferPools[SafeFrameIndex][BucketIndex].Num(); check(Desc.ByteWidth <= GetPoolBucketSize(Desc.ByteWidth)); SafeUniformBufferPools[SafeFrameIndex][BucketIndex].Add(NewEntry); INC_DWORD_STAT(STAT_D3D11NumFreeUniformBuffers); INC_MEMORY_STAT_BY(STAT_D3D11FreeUniformBufferMemory, Desc.ByteWidth); FPlatformMisc::MemoryBarrier(); // check for unwanted concurrency check(SafeUniformBufferPools[SafeFrameIndex][BucketIndex].Num() == LastNum + 1); } } }
/*----------------------------------------------------------------------------- FBoneBufferPoolPolicy -----------------------------------------------------------------------------*/ FBoneBufferTypeRef FBoneBufferPoolPolicy::CreateResource(CreationArguments Args) { uint32 BufferSize = GetPoolBucketSize(GetPoolBucketIndex(Args)); FBoneBuffer Buffer; FRHIResourceCreateInfo CreateInfo; Buffer.VertexBufferRHI = RHICreateVertexBuffer( BufferSize, (BUF_Dynamic | BUF_ShaderResource), CreateInfo ); Buffer.VertexBufferSRV = RHICreateShaderResourceView( Buffer.VertexBufferRHI, sizeof(FVector4), PF_A32B32G32R32F ); return Buffer; }
FUniformBufferRHIRef FD3D11DynamicRHI::RHICreateUniformBuffer(const void* Contents,const FRHIUniformBufferLayout& Layout,EUniformBufferUsage Usage) { check(IsInRenderingThread()); 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; }