/// <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) { 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; lock (GraphicsDevice.QueueLock) { 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); 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 }; 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 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(); //commandBuffer.Reset(CommandBufferResetFlags.None); GraphicsDevice.NativeDevice.FreeCommandBuffers(GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); } InitializeViews(); } }
private unsafe void InitializeImage(DataBox[] dataBoxes) { var commandBuffer = GraphicsDevice.NativeCopyCommandBuffer; var beginInfo = new CommandBufferBeginInfo { StructureType = StructureType.CommandBufferBeginInfo }; commandBuffer.Begin(ref beginInfo); if (dataBoxes != null && dataBoxes.Length > 0) { int totalSize = dataBoxes.Length * 4; 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.TopOfPipe, 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); SubresourceLayout layout; GraphicsDevice.NativeDevice.GetImageSubresourceLayout(NativeImage, new ImageSubresource(NativeImageAspect, (uint)arraySlice, (uint)mipSlice), out layout); var alignment = ((uploadOffset + 3) & ~3) - 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, arraySlice), ImageExtent = new Extent3D((uint)mipMapDescription.Width, (uint)mipMapDescription.Height, 1) }; 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), }; GraphicsDevice.NativeCommandQueue.Submit(1, &submitInfo, Fence.Null); GraphicsDevice.NativeCommandQueue.WaitIdle(); commandBuffer.Reset(CommandBufferResetFlags.None); }
internal void UpdateSubresource(GraphicsResource resource, int subResourceIndex, DataBox databox, ResourceRegion region) { var texture = resource as Texture; if (texture != null) { var width = region.Right - region.Left; var height = region.Bottom - region.Top; var depth = region.Back - region.Front; ResourceDescription resourceDescription; switch (texture.Dimension) { case TextureDimension.Texture1D: resourceDescription = ResourceDescription.Texture1D((SharpDX.DXGI.Format)texture.Format, width, 1, 1); break; case TextureDimension.Texture2D: case TextureDimension.TextureCube: resourceDescription = ResourceDescription.Texture2D((SharpDX.DXGI.Format)texture.Format, width, height, 1, 1); break; case TextureDimension.Texture3D: resourceDescription = ResourceDescription.Texture3D((SharpDX.DXGI.Format)texture.Format, width, height, (short)depth, 1); break; default: throw new ArgumentOutOfRangeException(); } // TODO D3D12 allocate in upload heap (placed resources?) var nativeUploadTexture = NativeDevice.CreateCommittedResource(new HeapProperties(CpuPageProperty.WriteBack, MemoryPool.L0), HeapFlags.None, resourceDescription, ResourceStates.GenericRead); GraphicsDevice.TemporaryResources.Enqueue(new KeyValuePair <long, Pageable>(GraphicsDevice.NextFenceValue, nativeUploadTexture)); nativeUploadTexture.WriteToSubresource(0, null, databox.DataPointer, databox.RowPitch, databox.SlicePitch); var parentResource = resource.ParentResource ?? resource; // Trigger copy NativeCommandList.ResourceBarrierTransition(resource.NativeResource, parentResource.NativeResourceState, ResourceStates.CopyDestination); NativeCommandList.CopyTextureRegion(new TextureCopyLocation(resource.NativeResource, subResourceIndex), region.Left, region.Top, region.Front, new TextureCopyLocation(nativeUploadTexture, 0), null); NativeCommandList.ResourceBarrierTransition(resource.NativeResource, ResourceStates.CopyDestination, parentResource.NativeResourceState); } else { var buffer = resource as Buffer; if (buffer != null) { SharpDX.Direct3D12.Resource uploadResource; int uploadOffset; var uploadSize = region.Right - region.Left; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(region.Right - region.Left, out uploadResource, out uploadOffset); Utilities.CopyMemory(uploadMemory, databox.DataPointer, uploadSize); NativeCommandList.ResourceBarrierTransition(resource.NativeResource, resource.NativeResourceState, ResourceStates.CopyDestination); NativeCommandList.CopyBufferRegion(resource.NativeResource, region.Left, uploadResource, uploadOffset, uploadSize); NativeCommandList.ResourceBarrierTransition(resource.NativeResource, ResourceStates.CopyDestination, resource.NativeResourceState); } else { throw new InvalidOperationException("Unknown resource type"); } } }
// TODO GRAPHICS REFACTOR what should we do with this? /// <summary> /// Maps a subresource. /// </summary> /// <param name="resource">The resource.</param> /// <param name="subResourceIndex">Index of the sub resource.</param> /// <param name="mapMode">The map mode.</param> /// <param name="doNotWait">if set to <c>true</c> this method will return immediately if the resource is still being used by the GPU for writing. Default is false</param> /// <param name="offsetInBytes">The offset information in bytes.</param> /// <param name="lengthInBytes">The length information in bytes.</param> /// <returns>Pointer to the sub resource to map.</returns> public MappedResource MapSubresource(GraphicsResource resource, int subResourceIndex, MapMode mapMode, bool doNotWait = false, int offsetInBytes = 0, int lengthInBytes = 0) { if (resource == null) { throw new ArgumentNullException("resource"); } var rowPitch = 0; var depthStride = 0; var usage = GraphicsResourceUsage.Default; var texture = resource as Texture; if (texture != null) { usage = texture.Usage; if (lengthInBytes == 0) { lengthInBytes = texture.ComputeSubresourceSize(subResourceIndex); } rowPitch = texture.ComputeRowPitch(subResourceIndex % texture.MipLevels); depthStride = texture.ComputeSlicePitch(subResourceIndex % texture.MipLevels); if (texture.Usage == GraphicsResourceUsage.Staging) { // Internally it's a buffer, so adapt resource index and offset offsetInBytes = texture.ComputeBufferOffset(subResourceIndex, 0); subResourceIndex = 0; } } else { var buffer = resource as Buffer; if (buffer != null) { usage = buffer.Usage; if (lengthInBytes == 0) { lengthInBytes = buffer.SizeInBytes; } } } // TODO D3D12 WriteDiscard should just reallocate new buffer, and WriteNoOverwrite shouldn't use that path (for now we defer to upload buffer) if (mapMode == MapMode.WriteDiscard || mapMode == MapMode.WriteNoOverwrite) { SharpDX.Direct3D12.Resource uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(lengthInBytes, out uploadResource, out uploadOffset); return(new MappedResource(resource, subResourceIndex, new DataBox(uploadMemory, rowPitch, depthStride), offsetInBytes, lengthInBytes) { UploadResource = uploadResource, UploadOffset = uploadOffset, }); } else if (mapMode == MapMode.Read || mapMode == MapMode.ReadWrite || mapMode == MapMode.Write) { // Is non-staging ever possible? if (usage != GraphicsResourceUsage.Staging) { throw new InvalidOperationException(); } if (mapMode != MapMode.WriteNoOverwrite) { // Need to wait? if (!GraphicsDevice.IsFenceCompleteInternal(resource.StagingFenceValue)) { if (doNotWait) { return(new MappedResource(resource, subResourceIndex, new DataBox(IntPtr.Zero, 0, 0))); } // Need to flush (part of current command list) if (resource.StagingFenceValue == GraphicsDevice.NextFenceValue) { FlushInternal(false); } GraphicsDevice.WaitForFenceInternal(resource.StagingFenceValue); } } var mappedMemory = resource.NativeResource.Map(subResourceIndex) + offsetInBytes; return(new MappedResource(resource, subResourceIndex, new DataBox(mappedMemory, rowPitch, depthStride), offsetInBytes, lengthInBytes)); } else { throw new NotImplementedException(); } }
/// <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.UnorderedAccess) != 0) { NativeResourceState |= ResourceStates.UnorderedAccess; } if ((bufferFlags & BufferFlags.StructuredBuffer) != 0) { throw new NotImplementedException(); if (bufferDescription.StructureByteStride == 0) { throw new ArgumentException("Element size cannot be set to 0 for structured buffer"); } } if ((bufferFlags & BufferFlags.RawBuffer) == BufferFlags.RawBuffer) { throw new NotImplementedException(); } if ((bufferFlags & BufferFlags.ArgumentBuffer) == BufferFlags.ArgumentBuffer) { NativeResourceState |= ResourceStates.IndirectArgument; } // TODO D3D12 move that to a global allocator in bigger committed resources NativeDeviceChild = GraphicsDevice.NativeDevice.CreateCommittedResource(new HeapProperties(HeapType.Default), HeapFlags.None, nativeDescription, dataPointer != IntPtr.Zero ? ResourceStates.CopyDestination : NativeResourceState); if (dataPointer != IntPtr.Zero) { // 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.NativeCommandQueue.ExecuteCommandList(commandList); // TODO D3D12 release uploadResource (using a fence to know when copy is done) } }
/// <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); }
private void InitializeFromImpl(DataBox[] dataBoxes = null) { if (ParentTexture != null) { ParentResource = ParentTexture; NativeDeviceChild = ParentTexture.NativeDeviceChild; } if (NativeDeviceChild == null) { 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) { if (dataBoxes != null) { throw new NotImplementedException(); } heapType = HeapType.Readback; initialResourceState = ResourceStates.CopyDestination; currentResourceState = ResourceStates.CopyDestination; nativeDescription = ResourceDescription.Buffer(ComputeBufferTotalSize()); // TODO: Alloc in readback heap as a buffer //return; } if (dataBoxes != null && dataBoxes.Length > 0) { 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); GraphicsDevice.TextureMemory += (Depth * DepthStride) / (float)0x100000; if (dataBoxes != null && dataBoxes.Length > 0) { if (Usage == GraphicsResourceUsage.Staging) { throw new NotImplementedException("D3D12: Staging textures can't be created with initial data"); } // 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); }
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.TextureMemory += (Depth * DepthStride) / (float)0x100000; 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); }
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.TopOfPipe, 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); } }