private void InitializeFromImpl(DataBox[] dataBoxes = null) { bool hasInitData = dataBoxes != null && dataBoxes.Length > 0; if (ParentTexture != null) { ParentResource = ParentTexture; NativeDeviceChild = ParentTexture.NativeDeviceChild; } if (NativeDeviceChild == null) { ClearValue?clearValue = GetClearValue(); ResourceDescription nativeDescription; switch (Dimension) { case TextureDimension.Texture1D: nativeDescription = ConvertToNativeDescription1D(); break; case TextureDimension.Texture2D: case TextureDimension.TextureCube: nativeDescription = ConvertToNativeDescription2D(); break; case TextureDimension.Texture3D: nativeDescription = ConvertToNativeDescription3D(); break; default: throw new ArgumentOutOfRangeException(); } var initialResourceState = ResourceStates.GenericRead; var heapType = HeapType.Default; var currentResourceState = initialResourceState; if (Usage == GraphicsResourceUsage.Staging) { heapType = HeapType.Readback; NativeResourceState = ResourceStates.CopyDestination; int totalSize = ComputeBufferTotalSize(); nativeDescription = ResourceDescription.Buffer(totalSize); // Staging textures on DirectX 12 use buffer internally NativeDeviceChild = GraphicsDevice.NativeDevice.CreateCommittedResource(new HeapProperties(heapType), HeapFlags.None, nativeDescription, NativeResourceState); if (hasInitData) { var commandList = GraphicsDevice.NativeCopyCommandList; commandList.Reset(GraphicsDevice.NativeCopyCommandAllocator, null); Resource uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(totalSize, out uploadResource, out uploadOffset, TextureSubresourceAlignment); // Copy data to the upload buffer int dataBoxIndex = 0; var uploadMemoryMipStart = uploadMemory; for (int arraySlice = 0; arraySlice < ArraySize; arraySlice++) { for (int mipLevel = 0; mipLevel < MipLevels; mipLevel++) { var databox = dataBoxes[dataBoxIndex++]; var mipHeight = CalculateMipSize(Width, mipLevel); var mipRowPitch = ComputeRowPitch(mipLevel); var uploadMemoryCurrent = uploadMemoryMipStart; var dataPointerCurrent = databox.DataPointer; for (int rowIndex = 0; rowIndex < mipHeight; rowIndex++) { Utilities.CopyMemory(uploadMemoryCurrent, dataPointerCurrent, mipRowPitch); uploadMemoryCurrent += mipRowPitch; dataPointerCurrent += databox.RowPitch; } uploadMemoryMipStart += ComputeSubresourceSize(mipLevel); } } // Copy from upload heap to actual resource commandList.CopyBufferRegion(NativeResource, 0, uploadResource, uploadOffset, totalSize); commandList.Close(); StagingFenceValue = 0; GraphicsDevice.WaitCopyQueue(); } return; } if (hasInitData) { currentResourceState = ResourceStates.CopyDestination; } // TODO D3D12 move that to a global allocator in bigger committed resources NativeDeviceChild = GraphicsDevice.NativeDevice.CreateCommittedResource(new HeapProperties(heapType), HeapFlags.None, nativeDescription, currentResourceState, clearValue); GraphicsDevice.RegisterTextureMemoryUsage(SizeInBytes); if (hasInitData) { // Trigger copy var commandList = GraphicsDevice.NativeCopyCommandList; commandList.Reset(GraphicsDevice.NativeCopyCommandAllocator, null); long textureCopySize; var placedSubresources = new PlacedSubResourceFootprint[dataBoxes.Length]; var rowCounts = new int[dataBoxes.Length]; var rowSizeInBytes = new long[dataBoxes.Length]; GraphicsDevice.NativeDevice.GetCopyableFootprints(ref nativeDescription, 0, dataBoxes.Length, 0, placedSubresources, rowCounts, rowSizeInBytes, out textureCopySize); SharpDX.Direct3D12.Resource uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer((int)textureCopySize, out uploadResource, out uploadOffset, TextureSubresourceAlignment); for (int i = 0; i < dataBoxes.Length; ++i) { var databox = dataBoxes[i]; var dataPointer = databox.DataPointer; var rowCount = rowCounts[i]; var sliceCount = placedSubresources[i].Footprint.Depth; var rowSize = (int)rowSizeInBytes[i]; var destRowPitch = placedSubresources[i].Footprint.RowPitch; // Memcpy data for (int z = 0; z < sliceCount; ++z) { var uploadMemoryCurrent = uploadMemory + (int)placedSubresources[i].Offset + z * destRowPitch * rowCount; var dataPointerCurrent = dataPointer + z * databox.SlicePitch; for (int y = 0; y < rowCount; ++y) { Utilities.CopyMemory(uploadMemoryCurrent, dataPointerCurrent, rowSize); uploadMemoryCurrent += destRowPitch; dataPointerCurrent += databox.RowPitch; } } // Adjust upload offset (circular dependency between GetCopyableFootprints and AllocateUploadBuffer) placedSubresources[i].Offset += uploadOffset; commandList.CopyTextureRegion(new TextureCopyLocation(NativeResource, i), 0, 0, 0, new TextureCopyLocation(uploadResource, placedSubresources[i]), null); } commandList.ResourceBarrierTransition(NativeResource, ResourceStates.CopyDestination, initialResourceState); commandList.Close(); GraphicsDevice.WaitCopyQueue(); } NativeResourceState = initialResourceState; } NativeShaderResourceView = GetShaderResourceView(ViewType, ArraySlice, MipLevel); NativeRenderTargetView = GetRenderTargetView(ViewType, ArraySlice, MipLevel); NativeDepthStencilView = GetDepthStencilView(out HasStencil); NativeUnorderedAccessView = GetUnorderedAccessView(ViewType, ArraySlice, MipLevel); }
/// <summary> /// Explicitly recreate buffer with given data. Usually called after a <see cref="GraphicsDevice"/> reset. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dataPointer"></param> public void Recreate(IntPtr dataPointer) { // TODO D3D12 where should that go longer term? should it be precomputed for future use? (cost would likely be additional check on SetDescriptorSets/Draw) NativeResourceState = ResourceStates.Common; var bufferFlags = bufferDescription.BufferFlags; if ((bufferFlags & BufferFlags.ConstantBuffer) != 0) { NativeResourceState |= ResourceStates.VertexAndConstantBuffer; } if ((bufferFlags & BufferFlags.IndexBuffer) != 0) { NativeResourceState |= ResourceStates.IndexBuffer; } if ((bufferFlags & BufferFlags.VertexBuffer) != 0) { NativeResourceState |= ResourceStates.VertexAndConstantBuffer; } if ((bufferFlags & BufferFlags.ShaderResource) != 0) { NativeResourceState |= ResourceStates.PixelShaderResource | ResourceStates.NonPixelShaderResource; } if ((bufferFlags & BufferFlags.StructuredBuffer) != 0) { if (bufferDescription.StructureByteStride <= 0) { throw new ArgumentException("Element size cannot be less or equal 0 for structured buffer"); } } if ((bufferFlags & BufferFlags.ArgumentBuffer) == BufferFlags.ArgumentBuffer) { NativeResourceState |= ResourceStates.IndirectArgument; } var heapType = HeapType.Default; if (Usage == GraphicsResourceUsage.Staging) { if (dataPointer != IntPtr.Zero) { throw new NotImplementedException("D3D12: Staging buffers can't be created with initial data."); } heapType = HeapType.Readback; NativeResourceState = ResourceStates.CopyDestination; } else if (Usage == GraphicsResourceUsage.Dynamic) { heapType = HeapType.Upload; NativeResourceState = ResourceStates.GenericRead; } // TODO D3D12 move that to a global allocator in bigger committed resources NativeDeviceChild = GraphicsDevice.NativeDevice.CreateCommittedResource(new HeapProperties(heapType), HeapFlags.None, nativeDescription, dataPointer != IntPtr.Zero ? ResourceStates.CopyDestination : NativeResourceState); GPUVirtualAddress = NativeResource.GPUVirtualAddress; if (dataPointer != IntPtr.Zero) { if (heapType == HeapType.Upload) { var uploadMemory = NativeResource.Map(0); Utilities.CopyMemory(uploadMemory, dataPointer, SizeInBytes); NativeResource.Unmap(0); } else { // Copy data in upload heap for later copy // TODO D3D12 move that to a shared upload heap SharpDX.Direct3D12.Resource uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(SizeInBytes, out uploadResource, out uploadOffset); Utilities.CopyMemory(uploadMemory, dataPointer, SizeInBytes); // TODO D3D12 lock NativeCopyCommandList usages var commandList = GraphicsDevice.NativeCopyCommandList; commandList.Reset(GraphicsDevice.NativeCopyCommandAllocator, null); // Copy from upload heap to actual resource commandList.CopyBufferRegion(NativeResource, 0, uploadResource, uploadOffset, SizeInBytes); // Switch resource to proper read state commandList.ResourceBarrierTransition(NativeResource, 0, ResourceStates.CopyDestination, NativeResourceState); commandList.Close(); GraphicsDevice.WaitCopyQueue(); } } NativeShaderResourceView = GetShaderResourceView(ViewFormat); NativeUnorderedAccessView = GetUnorderedAccessView(ViewFormat); }