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); GraphicsDevice.UploadBuffer?uploadBuffer = null; 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; } int uploadOffset = 0; GraphicsDevice.AllocateOneTimeUploadBuffer(totalSize, out var upBuf); // Upload buffer barrier var bufferMemoryBarrier = new VkBufferMemoryBarrier(upBuf.buffer, VkAccessFlags.HostWrite, VkAccessFlags.TransferRead, 0, (ulong)totalSize); // Image barrier NativeLayout = VkImageLayout.TransferDstOptimal; 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; upBuf.address += alignment; uploadOffset += alignment; Utilities.CopyMemory(upBuf.address, 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.Mathematics.Point3(0, 0, 0), imageExtent = new Vortice.Mathematics.Size3(mipMapDescription.Width, mipMapDescription.Height, mipMapDescription.Depth) }; upBuf.address += slicePitch; uploadOffset += slicePitch; } // Copy from upload buffer to image using (GraphicsDevice.QueueLock.ReadLock()) { fixed(VkBufferImageCopy *copiesPointer = copies) { vkCmdCopyBufferToImage(commandBuffer, upBuf.buffer, NativeImage, VkImageLayout.TransferDstOptimal, (uint)copies.Length, copiesPointer); } } uploadBuffer = upBuf; 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; // Close and submit using (GraphicsDevice.QueueLock.WriteLock()) { vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier); vkEndCommandBuffer(commandBuffer); vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, fence); vkWaitForFences(GraphicsDevice.NativeDevice, 1, &fence, true, ulong.MaxValue); } vkFreeCommandBuffers(GraphicsDevice.NativeDevice, GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); vkDestroyFence(GraphicsDevice.NativeDevice, fence, null); if (uploadBuffer.HasValue) { GraphicsDevice.FreeOneTimeUploadBuffer(uploadBuffer.Value); } }
/// <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 VkBufferCreateInfo { sType = VkStructureType.BufferCreateInfo, size = (ulong)bufferDescription.SizeInBytes, flags = VkBufferCreateFlags.None, }; createInfo.usage |= VkBufferUsageFlags.TransferSrc; // We always fill using transfer //if (bufferDescription.Usage != GraphicsResourceUsage.Immutable) createInfo.usage |= VkBufferUsageFlags.TransferDst; if (Usage == GraphicsResourceUsage.Staging) { NativeAccessMask = VkAccessFlags.HostRead | VkAccessFlags.HostWrite; NativePipelineStageMask |= VkPipelineStageFlags.Host; } else { if ((ViewFlags & BufferFlags.VertexBuffer) != 0) { createInfo.usage |= VkBufferUsageFlags.VertexBuffer; NativeAccessMask |= VkAccessFlags.VertexAttributeRead; NativePipelineStageMask |= VkPipelineStageFlags.VertexInput; } if ((ViewFlags & BufferFlags.IndexBuffer) != 0) { createInfo.usage |= VkBufferUsageFlags.IndexBuffer; NativeAccessMask |= VkAccessFlags.IndexRead; NativePipelineStageMask |= VkPipelineStageFlags.VertexInput; } if ((ViewFlags & BufferFlags.ConstantBuffer) != 0) { createInfo.usage |= VkBufferUsageFlags.UniformBuffer; NativeAccessMask |= VkAccessFlags.UniformRead; NativePipelineStageMask |= VkPipelineStageFlags.VertexShader | VkPipelineStageFlags.FragmentShader; } if ((ViewFlags & BufferFlags.ShaderResource) != 0) { createInfo.usage |= VkBufferUsageFlags.UniformTexelBuffer; NativeAccessMask |= VkAccessFlags.ShaderRead; NativePipelineStageMask |= VkPipelineStageFlags.VertexShader | VkPipelineStageFlags.FragmentShader; if ((ViewFlags & BufferFlags.UnorderedAccess) != 0) { createInfo.usage |= VkBufferUsageFlags.StorageTexelBuffer; NativeAccessMask |= VkAccessFlags.ShaderWrite; } } } // Create buffer vkCreateBuffer(GraphicsDevice.NativeDevice, &createInfo, null, out NativeBuffer); // Allocate memory var memoryProperties = VkMemoryPropertyFlags.DeviceLocal; if (bufferDescription.Usage == GraphicsResourceUsage.Staging || Usage == GraphicsResourceUsage.Dynamic) { memoryProperties = VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent; } vkGetBufferMemoryRequirements(GraphicsDevice.NativeDevice, NativeBuffer, out var memoryRequirements); AllocateMemory(memoryProperties, memoryRequirements); if (NativeMemory != VkDeviceMemory.Null) { vkBindBufferMemory(GraphicsDevice.NativeDevice, NativeBuffer, NativeMemory, 0); } if (SizeInBytes > 0) { // Begin copy command buffer var commandBufferAllocateInfo = new VkCommandBufferAllocateInfo { sType = VkStructureType.CommandBufferAllocateInfo, commandPool = GraphicsDevice.NativeCopyCommandPool, commandBufferCount = 1, level = VkCommandBufferLevel.Primary }; VkCommandBuffer commandBuffer; lock (BufferLocker) { vkAllocateCommandBuffers(GraphicsDevice.NativeDevice, &commandBufferAllocateInfo, &commandBuffer); } var beginInfo = new VkCommandBufferBeginInfo { sType = VkStructureType.CommandBufferBeginInfo, flags = VkCommandBufferUsageFlags.OneTimeSubmit }; vkBeginCommandBuffer(commandBuffer, &beginInfo); GraphicsDevice.UploadBuffer?uploadBuffer = null; // Copy to upload buffer if (dataPointer != IntPtr.Zero) { if (Usage == GraphicsResourceUsage.Dynamic) { void *uploadMemory; vkMapMemory(GraphicsDevice.NativeDevice, NativeMemory, 0, (ulong)SizeInBytes, VkMemoryMapFlags.None, &uploadMemory); Utilities.CopyMemory((IntPtr)uploadMemory, dataPointer, SizeInBytes); lock (BufferLocker) { vkUnmapMemory(GraphicsDevice.NativeDevice, NativeMemory); } } else { var sizeInBytes = bufferDescription.SizeInBytes; int uploadOffset; GraphicsDevice.AllocateOneTimeUploadBuffer(sizeInBytes, out var upBuf); uploadBuffer = upBuf; Utilities.CopyMemory(uploadBuffer.Value.address, dataPointer, sizeInBytes); // Barrier var memoryBarrier = new VkBufferMemoryBarrier(uploadBuffer.Value.buffer, VkAccessFlags.HostWrite, VkAccessFlags.TransferRead, 0, (ulong)sizeInBytes); vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer, VkDependencyFlags.None, 0, null, 1, &memoryBarrier, 0, null); // Copy var bufferCopy = new VkBufferCopy { srcOffset = 0, dstOffset = 0, size = (uint)sizeInBytes }; vkCmdCopyBuffer(commandBuffer, uploadBuffer.Value.buffer, NativeBuffer, 1, &bufferCopy); } } else { vkCmdFillBuffer(commandBuffer, NativeBuffer, 0, (uint)bufferDescription.SizeInBytes, 0); } // Barrier var bufferMemoryBarrier = new VkBufferMemoryBarrier(NativeBuffer, VkAccessFlags.TransferWrite, NativeAccessMask); vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 1, &bufferMemoryBarrier, 0, null); var submitInfo = new VkSubmitInfo { sType = VkStructureType.SubmitInfo, commandBufferCount = 1, pCommandBuffers = &commandBuffer, }; var fenceCreateInfo = new VkFenceCreateInfo { sType = VkStructureType.FenceCreateInfo }; vkCreateFence(GraphicsDevice.NativeDevice, &fenceCreateInfo, null, out var fence); // Close and submit vkEndCommandBuffer(commandBuffer); using (GraphicsDevice.QueueLock.ReadLock()) { vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, fence); } vkWaitForFences(GraphicsDevice.NativeDevice, 1, &fence, true, ulong.MaxValue); vkFreeCommandBuffers(GraphicsDevice.NativeDevice, GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); vkDestroyFence(GraphicsDevice.NativeDevice, fence, null); if (uploadBuffer.HasValue) { GraphicsDevice.FreeOneTimeUploadBuffer(uploadBuffer.Value); } InitializeViews(); } }