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 unsafe void Recreate(IntPtr dataPointer) { // capture vertex information for things less than ~512 verts for possible later easy batching if (dataPointer != IntPtr.Zero && (ViewFlags == BufferFlags.VertexBuffer && bufferDescription.SizeInBytes <= CaptureVertexBuffersOfSize || ViewFlags == BufferFlags.IndexBuffer && bufferDescription.SizeInBytes <= CaptureIndexBuffersOfSize)) { VertIndexData = new byte[Description.SizeInBytes]; fixed(byte *vid = &VertIndexData[0]) { Utilities.CopyMemory((IntPtr)vid, dataPointer, VertIndexData.Length); } } else { VertIndexData = null; } var createInfo = new BufferCreateInfo { StructureType = StructureType.BufferCreateInfo, Size = (ulong)bufferDescription.SizeInBytes, Flags = BufferCreateFlags.None, }; createInfo.Usage |= BufferUsageFlags.TransferSource; // We always fill using transfer //if (bufferDescription.Usage != GraphicsResourceUsage.Immutable) createInfo.Usage |= BufferUsageFlags.TransferDestination; if (Usage == GraphicsResourceUsage.Staging) { NativeAccessMask = AccessFlags.HostRead | AccessFlags.HostWrite; NativePipelineStageMask |= PipelineStageFlags.Host; } else { if ((ViewFlags & BufferFlags.VertexBuffer) != 0) { createInfo.Usage |= BufferUsageFlags.VertexBuffer; NativeAccessMask |= AccessFlags.VertexAttributeRead; NativePipelineStageMask |= PipelineStageFlags.VertexInput; } if ((ViewFlags & BufferFlags.IndexBuffer) != 0) { createInfo.Usage |= BufferUsageFlags.IndexBuffer; NativeAccessMask |= AccessFlags.IndexRead; NativePipelineStageMask |= PipelineStageFlags.VertexInput; } if ((ViewFlags & BufferFlags.ConstantBuffer) != 0) { createInfo.Usage |= BufferUsageFlags.UniformBuffer; NativeAccessMask |= AccessFlags.UniformRead; NativePipelineStageMask |= PipelineStageFlags.VertexShader | PipelineStageFlags.FragmentShader; } if ((ViewFlags & BufferFlags.ShaderResource) != 0) { createInfo.Usage |= BufferUsageFlags.UniformTexelBuffer; NativeAccessMask |= AccessFlags.ShaderRead; NativePipelineStageMask |= PipelineStageFlags.VertexShader | PipelineStageFlags.FragmentShader; if ((ViewFlags & BufferFlags.UnorderedAccess) != 0) { createInfo.Usage |= BufferUsageFlags.StorageTexelBuffer; NativeAccessMask |= AccessFlags.ShaderWrite; } } } // Create buffer NativeBuffer = GraphicsDevice.NativeDevice.CreateBuffer(ref createInfo); // Allocate memory var memoryProperties = MemoryPropertyFlags.DeviceLocal; if (bufferDescription.Usage == GraphicsResourceUsage.Staging || Usage == GraphicsResourceUsage.Dynamic) { memoryProperties = MemoryPropertyFlags.HostVisible | MemoryPropertyFlags.HostCoherent; } MemoryRequirements memoryRequirements; GraphicsDevice.NativeDevice.GetBufferMemoryRequirements(NativeBuffer, out memoryRequirements); AllocateMemory(memoryProperties, memoryRequirements); if (NativeMemory != DeviceMemory.Null) { GraphicsDevice.NativeDevice.BindBufferMemory(NativeBuffer, NativeMemory, 0); } if (SizeInBytes > 0) { // Begin copy command buffer var commandBufferAllocateInfo = new CommandBufferAllocateInfo { StructureType = StructureType.CommandBufferAllocateInfo, CommandPool = GraphicsDevice.NativeCopyCommandPool, CommandBufferCount = 1, Level = CommandBufferLevel.Primary }; CommandBuffer commandBuffer; using (GraphicsDevice.QueueLock.ReadLock()) { GraphicsDevice.NativeDevice.AllocateCommandBuffers(ref commandBufferAllocateInfo, &commandBuffer); } var beginInfo = new CommandBufferBeginInfo { StructureType = StructureType.CommandBufferBeginInfo, Flags = CommandBufferUsageFlags.OneTimeSubmit }; commandBuffer.Begin(ref beginInfo); // Copy to upload buffer if (dataPointer != IntPtr.Zero) { if (Usage == GraphicsResourceUsage.Dynamic) { var uploadMemory = GraphicsDevice.NativeDevice.MapMemory(NativeMemory, 0, (ulong)SizeInBytes, MemoryMapFlags.None); Utilities.CopyMemory(uploadMemory, dataPointer, SizeInBytes); using (GraphicsDevice.QueueLock.ReadLock()) { GraphicsDevice.NativeDevice.UnmapMemory(NativeMemory); } } else { var sizeInBytes = bufferDescription.SizeInBytes; SharpVulkan.Buffer uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(sizeInBytes, out uploadResource, out uploadOffset); Utilities.CopyMemory(uploadMemory, dataPointer, sizeInBytes); // Barrier var memoryBarrier = new BufferMemoryBarrier(uploadResource, AccessFlags.HostWrite, AccessFlags.TransferRead, (ulong)uploadOffset, (ulong)sizeInBytes); commandBuffer.PipelineBarrier(PipelineStageFlags.Host, PipelineStageFlags.Transfer, DependencyFlags.None, 0, null, 1, &memoryBarrier, 0, null); // Copy var bufferCopy = new BufferCopy { SourceOffset = (uint)uploadOffset, DestinationOffset = 0, Size = (uint)sizeInBytes }; using (GraphicsDevice.QueueLock.ReadLock()) { commandBuffer.CopyBuffer(uploadResource, NativeBuffer, 1, &bufferCopy); } } } else { commandBuffer.FillBuffer(NativeBuffer, 0, (uint)bufferDescription.SizeInBytes, 0); } // Barrier var bufferMemoryBarrier = new BufferMemoryBarrier(NativeBuffer, AccessFlags.TransferWrite, NativeAccessMask); commandBuffer.PipelineBarrier(PipelineStageFlags.Transfer, PipelineStageFlags.AllCommands, DependencyFlags.None, 0, null, 1, &bufferMemoryBarrier, 0, null); // Close and submit using (GraphicsDevice.QueueLock.ReadLock()) { commandBuffer.End(); } var submitInfo = new SubmitInfo { StructureType = StructureType.SubmitInfo, CommandBufferCount = 1, CommandBuffers = new IntPtr(&commandBuffer), }; var fenceCreateInfo = new FenceCreateInfo { StructureType = StructureType.FenceCreateInfo }; var fence = GraphicsDevice.NativeDevice.CreateFence(ref fenceCreateInfo); using (GraphicsDevice.QueueLock.WriteLock()) { GraphicsDevice.NativeCommandQueue.Submit(1, &submitInfo, fence); GraphicsDevice.NativeDevice.WaitForFences(1, &fence, true, ulong.MaxValue); } GraphicsDevice.NativeDevice.FreeCommandBuffers(GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); GraphicsDevice.NativeDevice.DestroyFence(fence); InitializeViews(); } }
private unsafe void InitializeImage(DataBox[] dataBoxes) { // Begin copy command buffer var commandBufferAllocateInfo = new VkCommandBufferAllocateInfo { sType = VkStructureType.CommandBufferAllocateInfo, commandPool = GraphicsDevice.NativeCopyCommandPool, commandBufferCount = 1, level = VkCommandBufferLevel.Primary }; VkCommandBuffer commandBuffer; // do some setup outside of the lock var beginInfo = new VkCommandBufferBeginInfo { sType = VkStructureType.CommandBufferBeginInfo, flags = VkCommandBufferUsageFlags.OneTimeSubmit }; var fenceCreateInfo = new VkFenceCreateInfo { sType = VkStructureType.FenceCreateInfo }; vkCreateFence(GraphicsDevice.NativeDevice, &fenceCreateInfo, null, out var fence); var submitInfo = new VkSubmitInfo { sType = VkStructureType.SubmitInfo, commandBufferCount = 1, }; using (GraphicsDevice.QueueLock.ReadLock()) { vkAllocateCommandBuffers(GraphicsDevice.NativeDevice, &commandBufferAllocateInfo, &commandBuffer); } vkBeginCommandBuffer(commandBuffer, &beginInfo); if (dataBoxes != null && dataBoxes.Length > 0) { // Buffer-to-image copies need to be aligned to the pixel size and 4 (always a power of 2) var blockSize = Format.IsCompressed() ? NativeFormat.BlockSizeInBytes() : TexturePixelSize; var alignmentMask = (blockSize < 4 ? 4 : blockSize) - 1; int totalSize = dataBoxes.Length * alignmentMask; for (int i = 0; i < dataBoxes.Length; i++) { totalSize += dataBoxes[i].SlicePitch; } var uploadMemory = GraphicsDevice.AllocateUploadBuffer(totalSize, out var upBuf, out int uploadOffset); // Upload buffer barrier var bufferMemoryBarrier = new VkBufferMemoryBarrier(upBuf, VkAccessFlags.HostWrite, VkAccessFlags.TransferRead, (ulong)uploadOffset, (ulong)totalSize); // Image barrier var initialBarrier = new VkImageMemoryBarrier(NativeImage, new VkImageSubresourceRange(NativeImageAspect, 0, uint.MaxValue, 0, uint.MaxValue), VkAccessFlags.None, VkAccessFlags.TransferWrite, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal); vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer, VkDependencyFlags.None, 0, null, 1, &bufferMemoryBarrier, 1, &initialBarrier); // Copy data boxes to upload buffer var copies = new VkBufferImageCopy[dataBoxes.Length]; for (int i = 0; i < copies.Length; i++) { var slicePitch = dataBoxes[i].SlicePitch; int arraySlice = i / MipLevels; int mipSlice = i % MipLevels; var mipMapDescription = GetMipMapDescription(mipSlice); var alignment = ((uploadOffset + alignmentMask) & ~alignmentMask) - uploadOffset; uploadMemory += alignment; uploadOffset += alignment; Utilities.CopyMemory(uploadMemory, dataBoxes[i].DataPointer, slicePitch); // TODO VULKAN: Check if pitches are valid copies[i] = new VkBufferImageCopy { bufferOffset = (ulong)uploadOffset, imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, (uint)mipSlice, (uint)arraySlice, 1), bufferRowLength = 0, //(uint)(dataBoxes[i].RowPitch / pixelSize), bufferImageHeight = 0, //(uint)(dataBoxes[i].SlicePitch / dataBoxes[i].RowPitch), imageOffset = new Vortice.Vulkan.VkOffset3D(0, 0, 0), imageExtent = new Vortice.Vulkan.VkExtent3D(mipMapDescription.Width, mipMapDescription.Height, mipMapDescription.Depth) }; uploadMemory += slicePitch; uploadOffset += slicePitch; } // Copy from upload buffer to image using (GraphicsDevice.QueueLock.ReadLock()) { fixed(VkBufferImageCopy *copiesPointer = copies) { vkCmdCopyBufferToImage(commandBuffer, upBuf, NativeImage, VkImageLayout.TransferDstOptimal, (uint)copies.Length, copiesPointer); } } IsInitialized = true; } // Transition to default layout var imageMemoryBarrier = new VkImageMemoryBarrier(NativeImage, new VkImageSubresourceRange(NativeImageAspect, 0, uint.MaxValue, 0, uint.MaxValue), dataBoxes == null || dataBoxes.Length == 0 ? VkAccessFlags.None : VkAccessFlags.TransferWrite, NativeAccessMask, dataBoxes == null || dataBoxes.Length == 0 ? VkImageLayout.Undefined : VkImageLayout.TransferDstOptimal, NativeLayout); submitInfo.pCommandBuffers = &commandBuffer; vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier); vkEndCommandBuffer(commandBuffer); // Close and submit using (GraphicsDevice.QueueLock.WriteLock()) { vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, fence); } vkWaitForFences(GraphicsDevice.NativeDevice, 1, &fence, Vortice.Vulkan.VkBool32.True, ulong.MaxValue); vkFreeCommandBuffers(GraphicsDevice.NativeDevice, GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); vkDestroyFence(GraphicsDevice.NativeDevice, fence, null); }
private unsafe void InitializeImage(DataBox[] dataBoxes) { // Begin copy command buffer var commandBufferAllocateInfo = new CommandBufferAllocateInfo { StructureType = StructureType.CommandBufferAllocateInfo, CommandPool = GraphicsDevice.NativeCopyCommandPool, CommandBufferCount = 1, Level = CommandBufferLevel.Primary }; CommandBuffer commandBuffer; lock (GraphicsDevice.QueueLock) { GraphicsDevice.NativeDevice.AllocateCommandBuffers(ref commandBufferAllocateInfo, &commandBuffer); } var beginInfo = new CommandBufferBeginInfo { StructureType = StructureType.CommandBufferBeginInfo, Flags = CommandBufferUsageFlags.OneTimeSubmit }; commandBuffer.Begin(ref beginInfo); if (dataBoxes != null && dataBoxes.Length > 0) { // Buffer-to-image copies need to be aligned to the pixel size and 4 (always a power of 2) var blockSize = Format.IsCompressed() ? NativeFormat.BlockSizeInBytes() : TexturePixelSize; var alignmentMask = (blockSize < 4 ? 4 : blockSize) - 1; int totalSize = dataBoxes.Length * alignmentMask; for (int i = 0; i < dataBoxes.Length; i++) { totalSize += dataBoxes[i].SlicePitch; } SharpVulkan.Buffer uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(totalSize, out uploadResource, out uploadOffset); // Upload buffer barrier var bufferMemoryBarrier = new BufferMemoryBarrier(uploadResource, AccessFlags.HostWrite, AccessFlags.TransferRead, (ulong)uploadOffset, (ulong)totalSize); // Image barrier var initialBarrier = new ImageMemoryBarrier(NativeImage, ImageLayout.Undefined, ImageLayout.TransferDestinationOptimal, AccessFlags.None, AccessFlags.TransferWrite, new ImageSubresourceRange(NativeImageAspect)); commandBuffer.PipelineBarrier(PipelineStageFlags.Host, PipelineStageFlags.Transfer, DependencyFlags.None, 0, null, 1, &bufferMemoryBarrier, 1, &initialBarrier); // Copy data boxes to upload buffer var copies = new BufferImageCopy[dataBoxes.Length]; for (int i = 0; i < copies.Length; i++) { var slicePitch = dataBoxes[i].SlicePitch; int arraySlice = i / MipLevels; int mipSlice = i % MipLevels; var mipMapDescription = GetMipMapDescription(mipSlice); var alignment = ((uploadOffset + alignmentMask) & ~alignmentMask) - uploadOffset; uploadMemory += alignment; uploadOffset += alignment; Utilities.CopyMemory(uploadMemory, dataBoxes[i].DataPointer, slicePitch); // TODO VULKAN: Check if pitches are valid copies[i] = new BufferImageCopy { BufferOffset = (ulong)uploadOffset, ImageSubresource = new ImageSubresourceLayers(ImageAspectFlags.Color, (uint)arraySlice, 1, (uint)mipSlice), BufferRowLength = 0, //(uint)(dataBoxes[i].RowPitch / pixelSize), BufferImageHeight = 0, //(uint)(dataBoxes[i].SlicePitch / dataBoxes[i].RowPitch), ImageOffset = new Offset3D(0, 0, 0), ImageExtent = new Extent3D((uint)mipMapDescription.Width, (uint)mipMapDescription.Height, (uint)mipMapDescription.Depth) }; uploadMemory += slicePitch; uploadOffset += slicePitch; } // Copy from upload buffer to image fixed(BufferImageCopy *copiesPointer = &copies[0]) { commandBuffer.CopyBufferToImage(uploadResource, NativeImage, ImageLayout.TransferDestinationOptimal, (uint)copies.Length, copiesPointer); } IsInitialized = true; } // Transition to default layout var imageMemoryBarrier = new ImageMemoryBarrier(NativeImage, dataBoxes == null || dataBoxes.Length == 0 ? ImageLayout.Undefined : ImageLayout.TransferDestinationOptimal, NativeLayout, dataBoxes == null || dataBoxes.Length == 0 ? AccessFlags.None : AccessFlags.TransferWrite, NativeAccessMask, new ImageSubresourceRange(NativeImageAspect)); commandBuffer.PipelineBarrier(PipelineStageFlags.Transfer, PipelineStageFlags.AllCommands, DependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier); // Close and submit commandBuffer.End(); var submitInfo = new SubmitInfo { StructureType = StructureType.SubmitInfo, CommandBufferCount = 1, CommandBuffers = new IntPtr(&commandBuffer), }; lock (GraphicsDevice.QueueLock) { GraphicsDevice.NativeCommandQueue.Submit(1, &submitInfo, Fence.Null); GraphicsDevice.NativeCommandQueue.WaitIdle(); GraphicsDevice.NativeDevice.FreeCommandBuffers(GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); } }
/// <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); }