internal static VulkanImage Texture2D(VulkanContext ctx, TextureData tex2D) { Buffer stagingBuffer = ctx.Device.CreateBuffer( new BufferCreateInfo(tex2D.Mipmaps[0].Size, BufferUsages.TransferSrc)); MemoryRequirements stagingMemReq = stagingBuffer.GetMemoryRequirements(); int heapIndex = ctx.MemoryProperties.MemoryTypes.IndexOf( stagingMemReq.MemoryTypeBits, MemoryProperties.HostVisible); DeviceMemory stagingMemory = ctx.Device.AllocateMemory( new MemoryAllocateInfo(stagingMemReq.Size, heapIndex)); stagingBuffer.BindMemory(stagingMemory); IntPtr ptr = stagingMemory.Map(0, stagingMemReq.Size); Interop.Write(ptr, tex2D.Mipmaps[0].Data); stagingMemory.Unmap(); // Setup buffer copy regions for each mip level. var bufferCopyRegions = new BufferImageCopy[tex2D.Mipmaps.Length]; int offset = 0; for (int i = 0; i < bufferCopyRegions.Length; i++) { bufferCopyRegions = new[] { new BufferImageCopy { ImageSubresource = new ImageSubresourceLayers(ImageAspects.Color, i, 0, 1), ImageExtent = tex2D.Mipmaps[0].Extent, BufferOffset = offset } }; offset += tex2D.Mipmaps[i].Size; } // Create optimal tiled target image. Image image = ctx.Device.CreateImage(new ImageCreateInfo { ImageType = ImageType.Image2D, Format = tex2D.Format, MipLevels = tex2D.Mipmaps.Length, ArrayLayers = 1, Samples = SampleCounts.Count1, Tiling = ImageTiling.Optimal, SharingMode = SharingMode.Exclusive, InitialLayout = ImageLayout.Undefined, Extent = tex2D.Mipmaps[0].Extent, Usage = ImageUsages.Sampled | ImageUsages.TransferDst }); MemoryRequirements imageMemReq = image.GetMemoryRequirements(); int imageHeapIndex = ctx.MemoryProperties.MemoryTypes.IndexOf( imageMemReq.MemoryTypeBits, MemoryProperties.DeviceLocal); DeviceMemory memory = ctx.Device.AllocateMemory(new MemoryAllocateInfo(imageMemReq.Size, imageHeapIndex)); image.BindMemory(memory); var subresourceRange = new ImageSubresourceRange(ImageAspects.Color, 0, tex2D.Mipmaps.Length, 0, 1); // Copy the data from staging buffers to device local buffers. CommandBuffer cmdBuffer = ctx.GraphicsCommandPool.AllocateBuffers(new CommandBufferAllocateInfo(CommandBufferLevel.Primary, 1))[0]; cmdBuffer.Begin(new CommandBufferBeginInfo(CommandBufferUsages.OneTimeSubmit)); cmdBuffer.CmdPipelineBarrier(PipelineStages.TopOfPipe, PipelineStages.TopOfPipe, imageMemoryBarriers: new[] { new ImageMemoryBarrier( image, subresourceRange, 0, Accesses.TransferWrite, ImageLayout.Undefined, ImageLayout.TransferDstOptimal) }); cmdBuffer.CmdCopyBufferToImage(stagingBuffer, image, ImageLayout.TransferDstOptimal, bufferCopyRegions); cmdBuffer.CmdPipelineBarrier(PipelineStages.TopOfPipe, PipelineStages.TopOfPipe, imageMemoryBarriers: new[] { new ImageMemoryBarrier( image, subresourceRange, Accesses.TransferWrite, Accesses.ShaderRead, ImageLayout.TransferDstOptimal, ImageLayout.ShaderReadOnlyOptimal) }); cmdBuffer.End(); // Submit. Fence fence = ctx.Device.CreateFence(); ctx.GraphicsQueue.Submit(new SubmitInfo(commandBuffers: new[] { cmdBuffer }), fence); fence.Wait(); // Cleanup staging resources. fence.Dispose(); stagingMemory.Dispose(); stagingBuffer.Dispose(); // Create image view. ImageView view = image.CreateView(new ImageViewCreateInfo(tex2D.Format, subresourceRange)); return(new VulkanImage(image, memory, view, tex2D.Format)); }