void FWinD3D11ConstantBuffer::InitDynamicRHI() { TRefCountPtr<ID3D11Buffer> CBuffer = NULL; D3D11_BUFFER_DESC BufferDesc; ZeroMemory( &BufferDesc, sizeof( D3D11_BUFFER_DESC ) ); // Verify constant buffer ByteWidth requirements check(MaxSize <= D3D11_REQ_CONSTANT_BUFFER_ELEMENT_COUNT && (MaxSize % 16) == 0); BufferDesc.ByteWidth = MaxSize; BufferDesc.Usage = D3D11_USAGE_DEFAULT; BufferDesc.CPUAccessFlags = 0; BufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; BufferDesc.MiscFlags = 0; Buffers = new TRefCountPtr<ID3D11Buffer>[NumSubBuffers]; CurrentSubBuffer = 0; for(uint32 s = 0;s < NumSubBuffers;s++) { VERIFYD3D11RESULT_EX(D3DRHI->GetDevice()->CreateBuffer(&BufferDesc, NULL, Buffers[s].GetInitReference()), D3DRHI->GetDevice()); UpdateBufferStats(Buffers[s],true); BufferDesc.ByteWidth = Align(BufferDesc.ByteWidth / 2, 16); } FD3D11ConstantBuffer::InitDynamicRHI(); }
FIndexBufferRHIRef FD3D11DynamicRHI::RHICreateIndexBuffer(uint32 Stride,uint32 Size,uint32 InUsage, FRHIResourceCreateInfo& CreateInfo) { // Explicitly check that the size is nonzero before allowing CreateIndexBuffer to opaquely fail. check(Size > 0); // Describe the index buffer. D3D11_BUFFER_DESC Desc; ZeroMemory( &Desc, sizeof( D3D11_BUFFER_DESC ) ); Desc.ByteWidth = Size; Desc.Usage = (InUsage & BUF_AnyDynamic) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; Desc.BindFlags = D3D11_BIND_INDEX_BUFFER; Desc.CPUAccessFlags = (InUsage & BUF_AnyDynamic) ? D3D11_CPU_ACCESS_WRITE : 0; Desc.MiscFlags = 0; if (InUsage & BUF_UnorderedAccess) { Desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; } if(InUsage & BUF_DrawIndirect) { Desc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; } if (InUsage & BUF_ShaderResource) { Desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; } // If a resource array was provided for the resource, create the resource pre-populated D3D11_SUBRESOURCE_DATA InitData; D3D11_SUBRESOURCE_DATA* pInitData = NULL; if(CreateInfo.ResourceArray) { check(Size == CreateInfo.ResourceArray->GetResourceDataSize()); InitData.pSysMem = CreateInfo.ResourceArray->GetResourceData(); InitData.SysMemPitch = Size; InitData.SysMemSlicePitch = 0; pInitData = &InitData; } TRefCountPtr<ID3D11Buffer> IndexBufferResource; VERIFYD3D11RESULT(Direct3DDevice->CreateBuffer(&Desc,pInitData,IndexBufferResource.GetInitReference())); UpdateBufferStats(IndexBufferResource, true); if(CreateInfo.ResourceArray) { // Discard the resource array's contents. CreateInfo.ResourceArray->Discard(); } return new FD3D11IndexBuffer(IndexBufferResource, Stride, Size, InUsage); }
void FWinD3D11ConstantBuffer::ReleaseDynamicRHI() { FD3D11ConstantBuffer::ReleaseDynamicRHI(); if(Buffers) { for(uint32 s = 0;s < NumSubBuffers;s++) { UpdateBufferStats(Buffers[s],false); } delete [] Buffers; Buffers = NULL; } }
FStructuredBufferRHIRef FD3D12DynamicRHI::RHICreateStructuredBuffer(uint32 Stride, uint32 Size, uint32 InUsage, FRHIResourceCreateInfo& CreateInfo) { // Check for values that will cause D3D calls to fail check(Size / Stride > 0 && Size % Stride == 0); const D3D12_RESOURCE_DESC Desc = CreateStructuredBufferResourceDesc(Size, InUsage); // Structured buffers, non-byte address buffers, need to be aligned to their stride to ensure that they // can be addressed correctly with element based offsets. const uint32 Alignment = ((InUsage & (BUF_ByteAddressBuffer | BUF_DrawIndirect)) == 0) ? Stride : 4; FD3D12StructuredBuffer* NewBuffer = GetAdapter().CreateRHIBuffer<FD3D12StructuredBuffer>(nullptr, Desc, Alignment, Stride, Size, InUsage, CreateInfo, false); UpdateBufferStats(&NewBuffer->ResourceLocation, true, D3D12_BUFFER_TYPE_STRUCTURED); return NewBuffer; }
/** Does per-frame global updating for the uniform buffer pool. */ void UniformBufferBeginFrame() { int32 NumCleaned = 0; SCOPE_CYCLE_COUNTER(STAT_D3D11CleanUniformBufferTime); // Clean a limited number of old entries to reduce hitching when leaving a large level for (int32 BucketIndex = 0; BucketIndex < NumPoolBuckets; BucketIndex++) { for (int32 EntryIndex = UniformBufferPool[BucketIndex].Num() - 1; EntryIndex >= 0 && NumCleaned < 10; EntryIndex--) { FPooledUniformBuffer& PoolEntry = UniformBufferPool[BucketIndex][EntryIndex]; check(IsValidRef(PoolEntry.Buffer)); // Clean entries that are unlikely to be reused if (GFrameNumberRenderThread - PoolEntry.FrameFreed > 30) { DEC_DWORD_STAT(STAT_D3D11NumFreeUniformBuffers); DEC_MEMORY_STAT_BY(STAT_D3D11FreeUniformBufferMemory, PoolEntry.CreatedSize); NumCleaned++; UpdateBufferStats(PoolEntry.Buffer, false); UniformBufferPool[BucketIndex].RemoveAtSwap(EntryIndex); } } } // Index of the bucket that is now old enough to be reused const int32 SafeFrameIndex = GFrameNumberRenderThread % NumSafeFrames; // Merge the bucket into the free pool array for (int32 BucketIndex = 0; BucketIndex < NumPoolBuckets; BucketIndex++) { int32 LastNum = UniformBufferPool[BucketIndex].Num(); UniformBufferPool[BucketIndex].Append(SafeUniformBufferPools[SafeFrameIndex][BucketIndex]); #if DO_CHECK while (LastNum < UniformBufferPool[BucketIndex].Num()) { check(IsValidRef(UniformBufferPool[BucketIndex][LastNum].Buffer)); LastNum++; } #endif SafeUniformBufferPools[SafeFrameIndex][BucketIndex].Reset(); } }
FStructuredBufferRHIRef FD3D11DynamicRHI::RHICreateStructuredBuffer(uint32 Stride,uint32 Size,uint32 InUsage, FRHIResourceCreateInfo& CreateInfo) { // Explicitly check that the size is nonzero before allowing CreateStructuredBuffer to opaquely fail. check(Size > 0); // Check for values that will cause D3D calls to fail check(Size / Stride > 0 && Size % Stride == 0); D3D11_BUFFER_DESC Desc; ZeroMemory( &Desc, sizeof( D3D11_BUFFER_DESC ) ); Desc.ByteWidth = Size; Desc.Usage = (InUsage & BUF_AnyDynamic) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; Desc.BindFlags = 0; if(InUsage & BUF_ShaderResource) { // Setup bind flags so we can create a view to read from the buffer in a shader. Desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; } if (InUsage & BUF_UnorderedAccess) { // Setup bind flags so we can create a writeable UAV to the buffer Desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; } if (InUsage & BUF_StreamOutput) { Desc.BindFlags |= D3D11_BIND_STREAM_OUTPUT; } Desc.CPUAccessFlags = (InUsage & BUF_AnyDynamic) ? D3D11_CPU_ACCESS_WRITE : 0; Desc.MiscFlags = 0; if (InUsage & BUF_DrawIndirect) { Desc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; } else { if (InUsage & BUF_ByteAddressBuffer) { Desc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; } else { Desc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_STRUCTURED; } } Desc.StructureByteStride = Stride; if (FPlatformProperties::SupportsFastVRAMMemory()) { if (InUsage & BUF_FastVRAM) { FFastVRAMAllocator::GetFastVRAMAllocator()->AllocUAVBuffer(Desc); } } // If a resource array was provided for the resource, create the resource pre-populated D3D11_SUBRESOURCE_DATA InitData; D3D11_SUBRESOURCE_DATA* pInitData = NULL; if(CreateInfo.ResourceArray) { check(Size == CreateInfo.ResourceArray->GetResourceDataSize()); InitData.pSysMem = CreateInfo.ResourceArray->GetResourceData(); InitData.SysMemPitch = Size; InitData.SysMemSlicePitch = 0; pInitData = &InitData; } TRefCountPtr<ID3D11Buffer> StructuredBufferResource; VERIFYD3D11RESULT_EX(Direct3DDevice->CreateBuffer(&Desc,pInitData,StructuredBufferResource.GetInitReference()), Direct3DDevice); UpdateBufferStats(StructuredBufferResource, true); if(CreateInfo.ResourceArray) { // Discard the resource array's contents. CreateInfo.ResourceArray->Discard(); } return new FD3D11StructuredBuffer(StructuredBufferResource,Stride,Size,InUsage); }
FD3D12StructuredBuffer::~FD3D12StructuredBuffer() { UpdateBufferStats(&ResourceLocation, false, D3D12_BUFFER_TYPE_STRUCTURED); }
FVertexBufferRHIRef FD3D11DynamicRHI::RHICreateVertexBuffer(uint32 Size,uint32 InUsage, FRHIResourceCreateInfo& CreateInfo) { // Explicitly check that the size is nonzero before allowing CreateVertexBuffer to opaquely fail. check(Size > 0); D3D11_BUFFER_DESC Desc; ZeroMemory( &Desc, sizeof( D3D11_BUFFER_DESC ) ); Desc.ByteWidth = Size; Desc.Usage = (InUsage & BUF_AnyDynamic) ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT; Desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; Desc.CPUAccessFlags = (InUsage & BUF_AnyDynamic) ? D3D11_CPU_ACCESS_WRITE : 0; Desc.MiscFlags = 0; Desc.StructureByteStride = 0; if (InUsage & BUF_UnorderedAccess) { Desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS; static bool bRequiresRawView = (GMaxRHIFeatureLevel < ERHIFeatureLevel::SM5); if (bRequiresRawView) { Desc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; } } if (InUsage & BUF_ByteAddressBuffer) { Desc.MiscFlags |= D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; } if (InUsage & BUF_StreamOutput) { Desc.BindFlags |= D3D11_BIND_STREAM_OUTPUT; } if(InUsage & BUF_DrawIndirect) { Desc.MiscFlags |= D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; } if (InUsage & BUF_ShaderResource) { Desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE; } if (FPlatformProperties::SupportsFastVRAMMemory()) { if (InUsage & BUF_FastVRAM) { FFastVRAMAllocator::GetFastVRAMAllocator()->AllocUAVBuffer(Desc); } } // If a resource array was provided for the resource, create the resource pre-populated D3D11_SUBRESOURCE_DATA InitData; D3D11_SUBRESOURCE_DATA* pInitData = NULL; if(CreateInfo.ResourceArray) { check(Size == CreateInfo.ResourceArray->GetResourceDataSize()); InitData.pSysMem = CreateInfo.ResourceArray->GetResourceData(); InitData.SysMemPitch = Size; InitData.SysMemSlicePitch = 0; pInitData = &InitData; } TRefCountPtr<ID3D11Buffer> VertexBufferResource; VERIFYD3D11RESULT(Direct3DDevice->CreateBuffer(&Desc,pInitData,VertexBufferResource.GetInitReference())); UpdateBufferStats(VertexBufferResource, true); if(CreateInfo.ResourceArray) { // Discard the resource array's contents. CreateInfo.ResourceArray->Discard(); } return new FD3D11VertexBuffer(VertexBufferResource,Size,InUsage); }
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; }