public PendingFlushImage(BufferPools.MemoryHandle src, TransferArguments arguments, Image dst, VkBufferImageCopy copyDesc) : base(src, arguments) { Destination = dst; CopyData = copyDesc; Destination.IncreasePins(); }
void CopyBufferToImage(VkBuffer buffer, VkImage image, int width, int height) { VkCommandBuffer commandBuffer = BeginSingleTimeCommands(); VkBufferImageCopy region = new VkBufferImageCopy(); region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource.aspectMask = VkImageAspectFlags.ColorBit; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = 0; region.imageSubresource.layerCount = 1; region.imageOffset = new VkOffset3D(); region.imageExtent = new VkExtent3D { width = width, height = height, depth = 1 }; commandBuffer.CopyBufferToImage(buffer, image, VkImageLayout.TransferDstOptimal, new VkBufferImageCopy[] { region }); EndSingleTimeCommand(commandBuffer); }
public void CopyBufferToImage(VkBuffer buffer, VkImage image, VkBufferImageCopy copyData) { unsafe { var local = copyData; VkCommandBuffer.vkCmdCopyBufferToImage(Handle, buffer, image, VkImageLayout.TransferDstOptimal, 1, &local); } }
public unsafe void Transfer(Image dest, void *data, ulong count, VkBufferImageCopy copyInfo, TransferArguments?arguments = null) { CheckArgs(arguments); var handle = Device.BufferPools.Allocate(_transferType, Usage, Flags, count); Buffer.MemoryCopy(data, handle.MappedMemory.Handle.ToPointer(), handle.Size, count); if (!handle.BackingMemory.MemoryType.HostCoherent) { handle.MappedMemory.FlushRange(0, count); } _pendingFlush.Enqueue(new PendingFlushImage(handle, arguments ?? TransferArguments.Null, dest, copyInfo)); _pendingFlushQueued.Set(); }
public void CopyTo(CommandBuffer cmd, Image img, VkImageLayout finalLayout = VkImageLayout.ShaderReadOnlyOptimal) { img.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.Transfer); VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy { imageExtent = img.CreateInfo.extent, imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color) }; vkCmdCopyBufferToImage(cmd.Handle, handle, img.handle, VkImageLayout.TransferDstOptimal, 1, ref bufferCopyRegion); img.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, finalLayout, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllGraphics); }
public unsafe static Texture Create2D(uint w, uint h, VkFormat format, IntPtr tex2DDataPtr, bool dynamic = false) { var texture = new Texture { extent = new VkExtent3D(w, h, 1), mipLevels = 1, format = format }; texture.image = Image.Create(w, h, VkImageCreateFlags.None, 1, 1, format, VkSampleCountFlags.Count1, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled); ulong totalBytes = texture.image.AllocationSize; using (Buffer stagingBuffer = Buffer.CreateStagingBuffer(totalBytes, tex2DDataPtr)) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy { imageSubresource = new VkImageSubresourceLayers { aspectMask = VkImageAspectFlags.Color, mipLevel = 0, baseArrayLayer = 0, layerCount = 1, }, imageExtent = new VkExtent3D(w, h, 1), bufferOffset = 0 }; // The sub resource range describes the regions of the image we will be transition var subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1); CommandBuffer copyCmd = Graphics.BeginPrimaryCmd(); copyCmd.SetImageLayout(texture.image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); copyCmd.CopyBufferToImage(stagingBuffer, texture.image, VkImageLayout.TransferDstOptimal, ref bufferCopyRegion); copyCmd.SetImageLayout(texture.image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, texture.imageLayout, subresourceRange); Graphics.EndPrimaryCmd(copyCmd); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; } texture.imageView = ImageView.Create(texture.image, VkImageViewType.Image2D, format, VkImageAspectFlags.Color, 0, texture.mipLevels); texture.sampler = new Sampler(VkFilter.Linear, VkSamplerMipmapMode.Linear, VkSamplerAddressMode.Repeat, texture.mipLevels, Device.Features.samplerAnisotropy == true); texture.UpdateDescriptor(); return(texture); }
public unsafe void CopyBufferToImage( Buffer buffer, Image image, uint mipLevel = 0 ) { var region = new VkBufferImageCopy { bufferOffset = 0, bufferRowLength = image.Width, bufferImageHeight = image.Height, imageOffset = new VkOffset3D { x = 0, y = 0, z = 0 }, imageExtent = new VkExtent3D { width = image.Width, height = image.Height, depth = 1 }, imageSubresource = new VkImageSubresourceLayers { aspectMask = GetAspectFlags( image.Handle, image.Format, image.Layout[mipLevel] ), mipLevel = mipLevel, baseArrayLayer = 0, layerCount = 1 } }; VulkanNative.vkCmdCopyBufferToImage( _handle, buffer.Handle, image.Handle, image.Layout[mipLevel], 1, ®ion ); }
public void Download(Image src, uint mipLevel, uint baseArrayLayer, uint layerCount, int x, int y, int z, uint w, uint h, uint d, GpuBuffer dst, ulong dst_off) { if (locked) { unsafe { var bufCopy = new VkBufferImageCopy() { bufferOffset = dst_off, bufferRowLength = 0, bufferImageHeight = 0, imageSubresource = new VkImageSubresourceLayers() { aspectMask = VkImageAspectFlags.ImageAspectColorBit, mipLevel = mipLevel, baseArrayLayer = baseArrayLayer, layerCount = layerCount }, imageOffset = new VkOffset3D() { x = x, y = y, z = z }, imageExtent = new VkExtent3D() { width = w, height = h, depth = d } }; vkCmdCopyImageToBuffer(hndl, src.hndl, (VkImageLayout)src.CurrentLayout, dst.hndl, 1, bufCopy.Pointer()); IsEmpty = false; } } else { throw new Exception("Command buffer not built."); } }
public unsafe void SetImageData(MipmapLevel[] imageData) { this.imageData = imageData; this.extent = new VkExtent3D(imageData[0].Width, imageData[0].Height, 1); mipLevels = (uint)imageData.Length; layers = (uint)imageData[0].ArrayElements.Length; faceCount = (uint)imageData[0].ArrayElements[0].Faces.Length; ulong totalSize = 0; foreach (var mip in imageData) { totalSize += mip.TotalSize * layers * faceCount; } Buffer stagingBuffer = Buffer.CreateStagingBuffer(totalSize, IntPtr.Zero); image = Image.Create(width, height, imageCreateFlags, layers * faceCount, mipLevels, format, VkSampleCountFlags.Count1, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled); IntPtr mapped = stagingBuffer.Map(); // Setup buffer copy regions for each face including all of it's miplevels Span <VkBufferImageCopy> bufferCopyRegions = stackalloc VkBufferImageCopy[(int)(mipLevels * layers * faceCount)]; uint offset = 0; int index = 0; for (int layer = 0; layer < layers; layer++) { for (int face = 0; face < faceCount; face++) { for (uint level = 0; level < mipLevels; level++) { var mipLevel = imageData[level]; var layerElement = mipLevel.ArrayElements[layer]; var faceElement = layerElement.Faces[face]; Unsafe.CopyBlock((void *)(mapped + (int)offset), Unsafe.AsPointer(ref faceElement.Data[0]), (uint)faceElement.Data.Length); VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy { imageSubresource = new VkImageSubresourceLayers { aspectMask = VkImageAspectFlags.Color, mipLevel = level, baseArrayLayer = (uint)(layer * faceCount + face), layerCount = 1 }, imageExtent = new VkExtent3D(mipLevel.Width, mipLevel.Height, mipLevel.Depth), bufferOffset = offset }; bufferCopyRegions[index++] = bufferCopyRegion; offset += (uint)faceElement.Data.Length; } } } stagingBuffer.Unmap(); var subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, mipLevels, 0, layers * faceCount); CommandBuffer copyCmd = Graphics.BeginPrimaryCmd(); copyCmd.SetImageLayout(image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); copyCmd.CopyBufferToImage(stagingBuffer, image, VkImageLayout.TransferDstOptimal, bufferCopyRegions); copyCmd.SetImageLayout(image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, imageLayout, subresourceRange); Graphics.EndPrimaryCmd(copyCmd); imageLayout = VkImageLayout.ShaderReadOnlyOptimal; stagingBuffer.Dispose(); imageView = ImageView.Create(image, ImageViewType, format, VkImageAspectFlags.Color, 0, mipLevels, 0, layers); sampler = new Sampler(VkFilter.Linear, VkSamplerMipmapMode.Linear, samplerAddressMode, mipLevels, Device.Features.samplerAnisotropy == true, borderColor); UpdateDescriptor(); }
void loadCubemap(string filename, VkFormat format, bool forceLinearTiling) { KtxFile texCube; using (var fs = File.OpenRead(filename)) { texCube = KtxFile.Load(fs, readKeyValuePairs: false); } cubeMap.width = texCube.Header.PixelWidth; cubeMap.height = texCube.Header.PixelHeight; cubeMap.mipLevels = texCube.Header.NumberOfMipmapLevels; VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = texCube.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(vkCreateBuffer(device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte* data; Util.CheckResult(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void**)&data)); byte[] allTextureData = texCube.GetAllTextureData(); fixed (byte* texCubeDataPtr = &allTextureData[0]) { Unsafe.CopyBlock(data, texCubeDataPtr, (uint)allTextureData.Length); } vkUnmapMemory(device, stagingMemory); // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VkImageType.Image2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = cubeMap.mipLevels; imageCreateInfo.samples = VkSampleCountFlags.Count1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.usage = VkImageUsageFlags.Sampled; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = cubeMap.width, height = cubeMap.height, depth = 1 }; imageCreateInfo.usage = VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled; // Cube faces count as array layers in Vulkan imageCreateInfo.arrayLayers = 6; // This flag is required for cube map images imageCreateInfo.flags = VkImageCreateFlags.CubeCompatible; Util.CheckResult(vkCreateImage(device, &imageCreateInfo, null, out cubeMap.image)); vkGetImageMemoryRequirements(device, cubeMap.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, out cubeMap.deviceMemory)); Util.CheckResult(vkBindImageMemory(device, cubeMap.image, cubeMap.deviceMemory, 0)); VkCommandBuffer copyCmd = createCommandBuffer(VkCommandBufferLevel.Primary, true); // Setup buffer copy regions for each face including all of it's miplevels NativeList<VkBufferImageCopy> bufferCopyRegions = new NativeList<VkBufferImageCopy>(); uint offset = 0; for (uint face = 0; face < 6; face++) { for (uint level = 0; level < cubeMap.mipLevels; level++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.Color; bufferCopyRegion.imageSubresource.mipLevel = level; bufferCopyRegion.imageSubresource.baseArrayLayer = face; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = texCube.Faces[face].Mipmaps[level].Width; bufferCopyRegion.imageExtent.height = texCube.Faces[face].Mipmaps[level].Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); // Increase offset into staging buffer for next level / face offset += texCube.Faces[face].Mipmaps[level].SizeInBytes; } } // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlags.Color; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = cubeMap.mipLevels; subresourceRange.layerCount = 6; Tools.setImageLayout( copyCmd, cubeMap.image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, cubeMap.image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all faces have been copied cubeMap.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; Tools.setImageLayout( copyCmd, cubeMap.image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, cubeMap.imageLayout, subresourceRange); flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = Initializers.samplerCreateInfo(); sampler.magFilter = VkFilter.Linear; sampler.minFilter = VkFilter.Linear; sampler.mipmapMode = VkSamplerMipmapMode.Linear; sampler.addressModeU = VkSamplerAddressMode.ClampToEdge; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.compareOp = VkCompareOp.Never; sampler.minLod = 0.0f; sampler.maxLod = cubeMap.mipLevels; sampler.borderColor = VkBorderColor.FloatOpaqueWhite; sampler.maxAnisotropy = 1.0f; if (vulkanDevice.features.samplerAnisotropy == 1) { sampler.maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = True; } Util.CheckResult(vkCreateSampler(device, &sampler, null, out cubeMap.sampler)); // Create image view VkImageViewCreateInfo view = Initializers.imageViewCreateInfo(); // Cube map view type view.viewType = VkImageViewType.ImageCube; view.format = format; view.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; view.subresourceRange = new VkImageSubresourceRange { aspectMask = VkImageAspectFlags.Color, baseMipLevel = 0, layerCount = 1, baseArrayLayer = 0, levelCount = 1 }; // 6 array layers (faces) view.subresourceRange.layerCount = 6; // Set number of mip levels view.subresourceRange.levelCount = cubeMap.mipLevels; view.image = cubeMap.image; Util.CheckResult(vkCreateImageView(device, &view, null, out cubeMap.view)); // Clean up staging resources vkFreeMemory(device, stagingMemory, null); vkDestroyBuffer(device, stagingBuffer, null); }
public static Image Load(Queue staggingQ, CommandPool staggingCmdPool, string ktxPath, VkImageUsageFlags usage = VkImageUsageFlags.Sampled, VkMemoryPropertyFlags memoryProperty = VkMemoryPropertyFlags.DeviceLocal, bool generateMipmaps = true, VkImageTiling tiling = VkImageTiling.Optimal) { Image img = null; using (Stream ktxStream = File.Open(ktxPath, FileMode.Open, FileAccess.Read)) { using (BinaryReader br = new BinaryReader(ktxStream)) { if (!br.ReadBytes(12).AreEquals(ktxSignature)) { throw new KtxException("Not a ktx file: " + ktxPath); } UInt32 endianness = br.ReadUInt32(); UInt32 glType = br.ReadUInt32(); UInt32 glTypeSize = br.ReadUInt32(); UInt32 glFormat = br.ReadUInt32(); UInt32 glInternalFormat = br.ReadUInt32(); UInt32 glBaseInternalFormat = br.ReadUInt32(); UInt32 pixelWidth = br.ReadUInt32(); UInt32 pixelHeight = br.ReadUInt32(); UInt32 pixelDepth = Math.Max(1, br.ReadUInt32()); UInt32 numberOfArrayElements = br.ReadUInt32(); //only for array text, else 0 UInt32 numberOfFaces = br.ReadUInt32(); //only for cube map, else 1 UInt32 numberOfMipmapLevels = Math.Max(1, br.ReadUInt32()); UInt32 bytesOfKeyValueData = br.ReadUInt32(); VkFormat vkFormat = GLHelper.vkGetFormatFromOpenGLInternalFormat(glInternalFormat); if (vkFormat == VkFormat.Undefined) { vkFormat = GLHelper.vkGetFormatFromOpenGLFormat(glFormat, glType); if (vkFormat == VkFormat.Undefined) { throw new KtxException("Undefined format: " + ktxPath); } } VkFormatProperties formatProperties = staggingQ.Dev.phy.GetFormatProperties(vkFormat); VkFormatFeatureFlags phyFormatSupport = (tiling == VkImageTiling.Linear) ? formatProperties.linearTilingFeatures : formatProperties.optimalTilingFeatures; uint requestedMipsLevels = numberOfMipmapLevels; if (numberOfMipmapLevels == 1) { requestedMipsLevels = (generateMipmaps && phyFormatSupport.HasFlag(VkFormatFeatureFlags.BlitSrc | VkFormatFeatureFlags.BlitDst)) ? (uint)Math.Floor(Math.Log(Math.Max(pixelWidth, pixelHeight))) + 1 : 1; } if (tiling == VkImageTiling.Optimal) { usage |= VkImageUsageFlags.TransferDst; } if (generateMipmaps) { usage |= (VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst); } VkImageCreateFlags createFlags = 0; VkImageType imgType = (pixelWidth == 0) ? throw new KtxException("pixelWidth must be > 0") : (pixelHeight == 0) ? imgType = VkImageType.Image1D : (pixelDepth == 1) ? imgType = VkImageType.Image2D : imgType = VkImageType.Image3D; VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; if (numberOfFaces > 1) { if (imgType != VkImageType.Image2D) { throw new KtxException("cubemap faces must be 2D textures"); } createFlags = VkImageCreateFlags.CubeCompatible; samples = VkSampleCountFlags.SampleCount1; numberOfArrayElements = numberOfFaces; } else { numberOfFaces = 1; if (numberOfArrayElements == 0) { numberOfArrayElements = 1; } } if (!Image.CheckFormatIsSupported(usage, phyFormatSupport)) { throw new Exception($"Unsupported image format: {vkFormat}, {tiling}, {usage}"); } img = new Image(staggingQ.Dev, vkFormat, usage, memoryProperty, pixelWidth, pixelHeight, imgType, samples, tiling, requestedMipsLevels, numberOfArrayElements, pixelDepth, createFlags); byte[] keyValueDatas = br.ReadBytes((int)bytesOfKeyValueData); uint blockW, blockH; bool isCompressed = vkFormat.TryGetCompressedFormatBlockSize(out blockW, out blockH); uint blockSize = blockW * blockH; if (memoryProperty.HasFlag(VkMemoryPropertyFlags.DeviceLocal)) { ulong staggingSize = img.AllocatedDeviceMemorySize; Console.WriteLine($"KtxStream size = {ktxStream.Length}, img Allocation = {img.AllocatedDeviceMemorySize}"); using (HostBuffer stagging = new HostBuffer(staggingQ.Dev, VkBufferUsageFlags.TransferSrc, staggingSize)) { stagging.Map(); PrimaryCommandBuffer cmd = staggingCmdPool.AllocateAndStart(VkCommandBufferUsageFlags.OneTimeSubmit); img.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.Transfer); List <VkBufferImageCopy> buffCopies = new List <VkBufferImageCopy> (); VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy { imageExtent = img.CreateInfo.extent, imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, img.CreateInfo.arrayLayers, 0) }; ulong bufferOffset = 0; uint imgWidth = img.CreateInfo.extent.width; uint imgHeight = img.CreateInfo.extent.height; for (int mips = 0; mips < numberOfMipmapLevels; mips++) { UInt32 imgSize = br.ReadUInt32(); bufferCopyRegion.bufferRowLength = imgWidth; bufferCopyRegion.bufferImageHeight = imgHeight; if (isCompressed && (imgWidth % blockW > 0 || imgHeight % blockH > 0)) { bufferCopyRegion.bufferRowLength += blockW - imgWidth % blockW; bufferCopyRegion.bufferImageHeight += blockH - imgHeight % blockH; } bufferCopyRegion.bufferOffset = bufferOffset; bufferCopyRegion.imageSubresource.mipLevel = (uint)mips; bufferCopyRegion.imageExtent.width = imgWidth; bufferCopyRegion.imageExtent.height = imgHeight; if (createFlags.HasFlag(VkImageCreateFlags.CubeCompatible)) { //TODO:handle compressed formats for (uint face = 0; face < numberOfFaces; face++) { Marshal.Copy(br.ReadBytes((int)imgSize), 0, stagging.MappedData + (int)bufferOffset, (int)imgSize); uint faceOffset = imgSize + (imgSize % 4); //cube padding bufferOffset += faceOffset; } buffCopies.Add(bufferCopyRegion); bufferCopyRegion.bufferOffset = bufferOffset; } else if (isCompressed && (imgWidth % blockW > 0 || imgHeight % blockH > 0)) { for (int line = 0; line < imgHeight; line++) { Marshal.Copy(br.ReadBytes((int)imgWidth), 0, stagging.MappedData + (int)bufferOffset, (int)imgWidth); bufferOffset += bufferCopyRegion.bufferRowLength; } buffCopies.Add(bufferCopyRegion); } else { Marshal.Copy(br.ReadBytes((int)imgSize), 0, stagging.MappedData + (int)bufferOffset, (int)imgSize); buffCopies.Add(bufferCopyRegion); bufferOffset += imgSize; } if (isCompressed && bufferOffset % blockSize > 0) { bufferOffset += blockSize - bufferOffset % blockSize; } imgWidth /= 2; imgHeight /= 2; } stagging.Unmap(); Vk.vkCmdCopyBufferToImage(cmd.Handle, stagging.handle, img.handle, VkImageLayout.TransferDstOptimal, (uint)buffCopies.Count, buffCopies.Pin()); buffCopies.Unpin(); if (requestedMipsLevels > numberOfMipmapLevels) { img.BuildMipmaps(cmd); } else { img.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); } cmd.End(); staggingQ.Submit(cmd); staggingQ.WaitIdle(); cmd.Free(); } } else { } } } return(img); }
public static void CreateTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, byte[] imageSource, out VkImage image, out VkDeviceMemory memory, VkQueue queue, VkCommandBuffer workCommandBuffer) { var imageCreateInfo = new VkImageCreateInfo() { imageType = VkImageType.VK_IMAGE_TYPE_2D, format = VkFormat.VK_FORMAT_B8G8R8A8_UNORM, extent = new VkExtent3D(width, height), mipLevels = 1, usage = VkImageUsageFlags.VK_IMAGE_USAGE_TRANSFER_DST_BIT | VkImageUsageFlags.VK_IMAGE_USAGE_SAMPLED_BIT, }; VulkanAPI.vkCreateImage(device, ref imageCreateInfo, out image); VkMemoryRequirements requirements; VulkanAPI.vkGetImageMemoryRequirements(device, image, out requirements); VkMemoryPropertyFlags memoryFlags = VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; VulkanAPI.vkAllocateMemory(device, physicalDevice, ref requirements, memoryFlags, out memory); VulkanAPI.vkBindImageMemory(device, image, memory, 0); // ステージングバッファ経由で転送. VkBuffer staging; VkDeviceMemory stagingMemory; var stagingFlags = VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; CreateBuffer(device, physicalDevice, imageSource.Length, VkBufferUsageFlags.VK_BUFFER_USAGE_TRANSFER_SRC_BIT, stagingFlags, out staging, out stagingMemory); MappedMemoryStream mappedStream; VulkanAPI.vkMapMemory(device, stagingMemory, 0, VkDeviceSize.VK_WHOLE_SIZE, 0, out mappedStream); mappedStream.Write(imageSource); VulkanAPI.vkUnmapMemory(device, stagingMemory); var copyRegion = new VkBufferImageCopy() { imageExtent = new VkExtent3D(width, height), bufferImageHeight = (uint)height, imageSubresource = new VkImageSubresourceLayers() { aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT, mipLevel = 0, baseArrayLayer = 0, layerCount = 1, } }; // 一時的なコマンドバッファで転送処理. var command = workCommandBuffer; VulkanAPI.vkBeginCommandBuffer(command); setImageMemoryBarrier(command, 0, VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT, VkImageLayout.VK_IMAGE_LAYOUT_UNDEFINED, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image, VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT); VulkanAPI.vkCmdCopyBufferToImage(command, staging, image, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, new[] { copyRegion }); setImageMemoryBarrier(command, VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT, VkAccessFlags.VK_ACCESS_SHADER_READ_BIT, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VkImageLayout.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, image, VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT); VulkanAPI.vkEndCommandBuffer(command); var submitInfo = new VkSubmitInfo() { commandBuffers = new[] { command }, }; var fenceCreateInfo = new VkFenceCreateInfo() { }; VkFence fence; VulkanAPI.vkCreateFence(device, fenceCreateInfo, out fence); VulkanAPI.vkQueueSubmit(queue, new[] { submitInfo }, fence); VulkanAPI.vkWaitForFences(device, new[] { fence }, true, ulong.MaxValue); VulkanAPI.vkDestroyFence(device, fence); VulkanAPI.vkDestroyBuffer(device, staging); VulkanAPI.vkFreeMemory(device, stagingMemory); }
internal static VulkanImage Texture2D(VulkanContext ctx, TextureData tex2D) { ulong size = (ulong)tex2D.Mipmaps[0].Size; VkBuffer stagingBuffer; var bufferCreateInfo = new VkBufferCreateInfo { sType = VkStructureType.BufferCreateInfo, pNext = null, size = (ulong)tex2D.Mipmaps[0].Size, usage = VkBufferUsageFlags.TransferSrc }; vkCreateBuffer(ctx.Device, &bufferCreateInfo, null, out stagingBuffer); vkGetPhysicalDeviceMemoryProperties(ctx.PhysicalDevice, out VkPhysicalDeviceMemoryProperties memoryProperties); vkGetBufferMemoryRequirements(ctx.Device, stagingBuffer, out VkMemoryRequirements stagingMemReq); uint heapIndex = BufferHelper.GetMemoryTypeIndex(stagingMemReq.memoryTypeBits, VkMemoryPropertyFlags.HostVisible, memoryProperties); VkMemoryAllocateInfo memAllocInfo = new VkMemoryAllocateInfo() { sType = VkStructureType.MemoryAllocateInfo, pNext = null, allocationSize = stagingMemReq.size, memoryTypeIndex = heapIndex }; VkDeviceMemory stagingMemory; VkResult result = vkAllocateMemory(ctx.Device, &memAllocInfo, null, &stagingMemory); result.CheckResult(); result = vkBindBufferMemory(ctx.Device, stagingBuffer, stagingMemory, 0); result.CheckResult(); void *vertexPtr; result = vkMapMemory(ctx.Device, stagingMemory, 0, (ulong)tex2D.Mipmaps[0].Size, 0, &vertexPtr); result.CheckResult(); fixed(byte *dataPtr = &tex2D.Mipmaps[0].Data[0]) { Buffer.MemoryCopy(dataPtr, vertexPtr, size, size); } vkUnmapMemory(ctx.Device, stagingMemory); // Setup buffer copy regions for each mip level. var bufferCopyRegions = new VkBufferImageCopy[tex2D.Mipmaps.Length]; // TODO: stackalloc int offset = 0; for (int i = 0; i < bufferCopyRegions.Length; i++) { // TODO: from VulkanCore, doesn't look correct (reassigns bufferCopyRegions in each loop) bufferCopyRegions = new[] { new VkBufferImageCopy { imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, (uint)i, 0, 1), imageExtent = tex2D.Mipmaps[0].Extent, bufferOffset = (ulong)offset } }; offset += tex2D.Mipmaps[i].Size; } // Create optimal tiled target image. var createInfo = new VkImageCreateInfo { sType = VkStructureType.ImageCreateInfo, pNext = null, imageType = VkImageType.Image2D, format = tex2D.Format, mipLevels = (uint)tex2D.Mipmaps.Length, arrayLayers = 1, samples = VkSampleCountFlags.Count1, tiling = VkImageTiling.Optimal, sharingMode = VkSharingMode.Exclusive, initialLayout = VkImageLayout.Undefined, extent = tex2D.Mipmaps[0].Extent, usage = VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferDst }; VkImage image; result = vkCreateImage(ctx.Device, &createInfo, null, out image); result.CheckResult(); VkMemoryRequirements imageMemReq; vkGetImageMemoryRequirements(ctx.Device, image, out imageMemReq); vkGetPhysicalDeviceMemoryProperties(ctx.PhysicalDevice, out VkPhysicalDeviceMemoryProperties imageMemoryProperties); uint imageHeapIndex = BufferHelper.GetMemoryTypeIndex(imageMemReq.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal, imageMemoryProperties); var allocInfo = new VkMemoryAllocateInfo { sType = VkStructureType.MemoryAllocateInfo, pNext = null, allocationSize = imageMemReq.size, memoryTypeIndex = imageHeapIndex, }; VkDeviceMemory memory; result = vkAllocateMemory(ctx.Device, &allocInfo, null, &memory); result.CheckResult(); result = vkBindImageMemory(ctx.Device, image, memory, 0); result.CheckResult(); var subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, (uint)tex2D.Mipmaps.Length, 0, 1); // Copy the data from staging buffers to device local buffers. var allocInfo2 = new VkCommandBufferAllocateInfo() { sType = VkStructureType.CommandBufferAllocateInfo, commandPool = ctx.GraphicsCommandPool, level = VkCommandBufferLevel.Primary, commandBufferCount = 1, }; VkCommandBuffer cmdBuffer; vkAllocateCommandBuffers(ctx.Device, &allocInfo2, &cmdBuffer); VkCommandBufferBeginInfo beginInfo = new VkCommandBufferBeginInfo() { sType = VkStructureType.CommandBufferBeginInfo, flags = VkCommandBufferUsageFlags.OneTimeSubmit, }; vkBeginCommandBuffer(cmdBuffer, &beginInfo); VkImageMemoryBarrier imageMemoryBarrier = new VkImageMemoryBarrier { sType = VkStructureType.ImageMemoryBarrier, pNext = null, image = image, subresourceRange = subresourceRange, srcAccessMask = 0, dstAccessMask = VkAccessFlags.TransferWrite, oldLayout = VkImageLayout.Undefined, newLayout = VkImageLayout.TransferDstOptimal, srcQueueFamilyIndex = QueueFamilyIgnored, dstQueueFamilyIndex = QueueFamilyIgnored }; vkCmdPipelineBarrier(cmdBuffer, VkPipelineStageFlags.TopOfPipe, VkPipelineStageFlags.Transfer, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier); fixed(VkBufferImageCopy *regionsPtr = bufferCopyRegions) { vkCmdCopyBufferToImage(cmdBuffer, stagingBuffer, image, VkImageLayout.TransferDstOptimal, (uint)bufferCopyRegions.Length, regionsPtr); } VkImageMemoryBarrier imageMemoryBarrier2 = new VkImageMemoryBarrier { sType = VkStructureType.ImageMemoryBarrier, pNext = null, image = image, subresourceRange = subresourceRange, srcAccessMask = VkAccessFlags.TransferWrite, dstAccessMask = VkAccessFlags.ShaderRead, oldLayout = VkImageLayout.TransferDstOptimal, newLayout = VkImageLayout.ShaderReadOnlyOptimal, srcQueueFamilyIndex = (uint)QueueFamilyIgnored, dstQueueFamilyIndex = (uint)QueueFamilyIgnored }; vkCmdPipelineBarrier(cmdBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier2); vkEndCommandBuffer(cmdBuffer); // Submit. VkFenceCreateInfo fenceCreateInfo = new VkFenceCreateInfo { sType = VkStructureType.FenceCreateInfo, pNext = null }; VkFence fence; result = vkCreateFence(ctx.Device, &fenceCreateInfo, null, out fence); result.CheckResult(); var submitInfo = new VkSubmitInfo { sType = VkStructureType.SubmitInfo, pNext = null, commandBufferCount = 1, pCommandBuffers = &cmdBuffer }; vkQueueSubmit(ctx.GraphicsQueue, submitInfo, fence); result = vkWaitForFences(ctx.Device, 1, &fence, false, ulong.MaxValue); result.CheckResult(); // Cleanup staging resources. vkDestroyFence(ctx.Device, fence, null); vkFreeMemory(ctx.Device, stagingMemory, null); vkDestroyBuffer(ctx.Device, stagingBuffer, null); // Create image view. VkImageViewCreateInfo imageViewCreateInfo = new VkImageViewCreateInfo() { sType = VkStructureType.ImageViewCreateInfo, image = image, viewType = VkImageViewType.Image2D, format = tex2D.Format, subresourceRange = subresourceRange }; VkImageView view; vkCreateImageView(ctx.Device, &imageViewCreateInfo, null, out view); return(new VulkanImage(ctx, image, memory, view, tex2D.Format)); }
/// <inheritdoc cref="Copy(GraphicsTexture, GraphicsBuffer)" /> public void Copy(VulkanGraphicsTexture destination, VulkanGraphicsBuffer source) { ThrowIfNull(destination, nameof(destination)); ThrowIfNull(source, nameof(source)); var vulkanCommandBuffer = VulkanCommandBuffer; var vulkanImage = destination.VulkanImage; BeginCopy(); var vulkanBufferImageCopy = new VkBufferImageCopy { imageSubresource = new VkImageSubresourceLayers { aspectMask = (uint)VK_IMAGE_ASPECT_COLOR_BIT, layerCount = 1, }, imageExtent = new VkExtent3D { width = (uint)destination.Width, height = destination.Height, depth = destination.Depth, }, }; vkCmdCopyBufferToImage(vulkanCommandBuffer, source.VulkanBuffer, vulkanImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &vulkanBufferImageCopy); EndCopy(); void BeginCopy() { var vulkanImageMemoryBarrier = new VkImageMemoryBarrier { sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, dstAccessMask = (uint)VK_ACCESS_TRANSFER_WRITE_BIT, oldLayout = VK_IMAGE_LAYOUT_UNDEFINED, newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image = vulkanImage, subresourceRange = new VkImageSubresourceRange { aspectMask = (uint)VK_IMAGE_ASPECT_COLOR_BIT, levelCount = 1, layerCount = 1, }, }; vkCmdPipelineBarrier(vulkanCommandBuffer, (uint)VK_PIPELINE_STAGE_HOST_BIT, (uint)VK_PIPELINE_STAGE_TRANSFER_BIT, dependencyFlags: 0, memoryBarrierCount: 0, pMemoryBarriers: null, bufferMemoryBarrierCount: 0, pBufferMemoryBarriers: null, imageMemoryBarrierCount: 1, &vulkanImageMemoryBarrier); } void EndCopy() { var vulkanImageMemoryBarrier = new VkImageMemoryBarrier { sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, srcAccessMask = (uint)VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask = (uint)VK_ACCESS_SHADER_READ_BIT, oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, image = vulkanImage, subresourceRange = new VkImageSubresourceRange { aspectMask = (uint)VK_IMAGE_ASPECT_COLOR_BIT, levelCount = 1, layerCount = 1, }, }; vkCmdPipelineBarrier(vulkanCommandBuffer, (uint)VK_PIPELINE_STAGE_TRANSFER_BIT, (uint)VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dependencyFlags: 0, memoryBarrierCount: 0, pMemoryBarriers: null, bufferMemoryBarrierCount: 0, pBufferMemoryBarriers: null, imageMemoryBarrierCount: 1, &vulkanImageMemoryBarrier); } }
void loadTexture(string fileName, VkFormat format, bool forceLinearTiling) { KtxFile tex2D; using (var fs = File.OpenRead(fileName)) { tex2D = KtxFile.Load(fs, false); } VkFormatProperties formatProperties; texture.width = tex2D.Header.PixelWidth; texture.height = tex2D.Header.PixelHeight; texture.mipLevels = tex2D.Header.NumberOfMipmapLevels; // Get Device properites for the requested texture format vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); // Only use linear tiling if requested (and supported by the Device) // Support for linear tiling is mostly limited, so prefer to use // optimal tiling instead // On most implementations linear tiling will only support a very // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) uint useStaging = 1; // Only use linear tiling if forced if (forceLinearTiling) { // Don't use linear if format is not supported for (linear) shader sampling useStaging = ((formatProperties.linearTilingFeatures & VkFormatFeatureFlags.SampledImage) != VkFormatFeatureFlags.SampledImage) ? 1u : 0u; } VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs = new VkMemoryRequirements(); if (useStaging == 1) { // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(vkCreateBuffer(device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); byte[] allData = tex2D.GetAllTextureData(); fixed(byte *tex2DDataPtr = &allData[0]) { Unsafe.CopyBlock(data, tex2DDataPtr, (uint)allData.Length); } vkUnmapMemory(device, stagingMemory); // Setup buffer copy regions for each mip level NativeList <VkBufferImageCopy> bufferCopyRegions = new NativeList <VkBufferImageCopy>(); uint offset = 0; for (uint i = 0; i < texture.mipLevels; i++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.Color; bufferCopyRegion.imageSubresource.mipLevel = i; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = tex2D.Faces[0].Mipmaps[i].Width; bufferCopyRegion.imageExtent.height = tex2D.Faces[0].Mipmaps[i].Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); offset += tex2D.Faces[0].Mipmaps[i].SizeInBytes; } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VkImageType.Image2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = texture.mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VkSampleCountFlags.Count1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; // Set initial layout of the image to undefined imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = texture.width, height = texture.height, depth = 1 }; imageCreateInfo.usage = VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled; Util.CheckResult(vkCreateImage(device, &imageCreateInfo, null, out texture.image)); vkGetImageMemoryRequirements(device, texture.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, out texture.DeviceMemory)); Util.CheckResult(vkBindImageMemory(device, texture.image, texture.DeviceMemory, 0)); VkCommandBuffer copyCmd = base.createCommandBuffer(VkCommandBufferLevel.Primary, true); // Image barrier for optimal image // The sub resource range describes the regions of the image we will be transition VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); // Image only contains color data subresourceRange.aspectMask = VkImageAspectFlags.Color; // Start at first mip level subresourceRange.baseMipLevel = 0; // We will transition on all mip levels subresourceRange.levelCount = texture.mipLevels; // The 2D texture only has one layer subresourceRange.layerCount = 1; // Optimal image will be used as destination for the copy, so we must transfer from our // initial undefined image layout to the transfer destination layout setImageLayout( copyCmd, texture.image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copyCmd, stagingBuffer, texture.image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all mip levels have been copied texture.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; setImageLayout( copyCmd, texture.image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, texture.imageLayout, subresourceRange); flushCommandBuffer(copyCmd, queue, true); // Clean up staging resources vkFreeMemory(device, stagingMemory, null); vkDestroyBuffer(device, stagingBuffer, null); } else { throw new NotImplementedException(); /* * // Prefer using optimal tiling, as linear tiling * // may support only a small set of features * // depending on implementation (e.g. no mip maps, only one layer, etc.) * * VkImage mappableImage; * VkDeviceMemory mappableMemory; * * // Load mip map level 0 to linear tiling image * VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); * imageCreateInfo.imageType = VkImageType._2d; * imageCreateInfo.format = format; * imageCreateInfo.mipLevels = 1; * imageCreateInfo.arrayLayers = 1; * imageCreateInfo.samples = VkSampleCountFlags._1; * imageCreateInfo.tiling = VkImageTiling.Linear; * imageCreateInfo.usage = VkImageUsageFlags.Sampled; * imageCreateInfo.sharingMode = VkSharingMode.Exclusive; * imageCreateInfo.initialLayout = VkImageLayout.Preinitialized; * imageCreateInfo.extent = new VkExtent3D { width = texture.width, height = texture.height, depth = 1 }; * Util.CheckResult(vkCreateImage(Device, &imageCreateInfo, null, &mappableImage)); * * // Get memory requirements for this image * // like size and alignment * vkGetImageMemoryRequirements(Device, mappableImage, &memReqs); * // Set memory allocation size to required memory size * memAllocInfo.allocationSize = memReqs.size; * * // Get memory type that can be mapped to host memory * memAllocInfo.memoryTypeIndex = VulkanDevice.GetMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); * * // Allocate host memory * Util.CheckResult(vkAllocateMemory(Device, &memAllocInfo, null, &mappableMemory)); * * // Bind allocated image for use * Util.CheckResult(vkBindImageMemory(Device, mappableImage, mappableMemory, 0)); * * // Get sub resource layout * // Mip map count, array layer, etc. * VkImageSubresource subRes = new VkImageSubresource(); * subRes.aspectMask = VkImageAspectFlags.Color; * * VkSubresourceLayout subResLayout; * void* data; * * // Get sub resources layout * // Includes row pitch, size offsets, etc. * vkGetImageSubresourceLayout(Device, mappableImage, &subRes, &subResLayout); * * // Map image memory * Util.CheckResult(vkMapMemory(Device, mappableMemory, 0, memReqs.size, 0, &data)); * * // Copy image data into memory * memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()); * * vkUnmapMemory(Device, mappableMemory); * * // Linear tiled images don't need to be staged * // and can be directly used as textures * texture.image = mappableImage; * texture.DeviceMemory = mappableMemory; * texture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; * * VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); * * // Setup image memory barrier transfer image to shader read layout * * // The sub resource range describes the regions of the image we will be transition * VkImageSubresourceRange subresourceRange = { }; * // Image only contains color data * subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; * // Start at first mip level * subresourceRange.baseMipLevel = 0; * // Only one mip level, most implementations won't support more for linear tiled images * subresourceRange.levelCount = 1; * // The 2D texture only has one layer * subresourceRange.layerCount = 1; * * setImageLayout( * copyCmd, * texture.image, * VK_IMAGE_ASPECT_COLOR_BIT, * VK_IMAGE_LAYOUT_PREINITIALIZED, * texture.imageLayout, * subresourceRange); * * VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); */ } // Create sampler // In Vulkan textures are accessed by samplers // This separates all the sampling information from the // texture data // This means you could have multiple sampler objects // for the same texture with different settings // Similar to the samplers available with OpenGL 3.3 VkSamplerCreateInfo sampler = Initializers.samplerCreateInfo(); sampler.magFilter = VkFilter.Linear; sampler.minFilter = VkFilter.Linear; sampler.mipmapMode = VkSamplerMipmapMode.Linear; sampler.addressModeU = VkSamplerAddressMode.Repeat; sampler.addressModeV = VkSamplerAddressMode.Repeat; sampler.addressModeW = VkSamplerAddressMode.Repeat; sampler.mipLodBias = 0.0f; sampler.compareOp = VkCompareOp.Never; sampler.minLod = 0.0f; // Set max level-of-detail to mip level count of the texture sampler.maxLod = (useStaging == 1) ? (float)texture.mipLevels : 0.0f; // Enable anisotropic filtering // This feature is optional, so we must check if it's supported on the Device if (vulkanDevice.features.samplerAnisotropy == 1) { // Use max. level of anisotropy for this example sampler.maxAnisotropy = vulkanDevice.properties.limits.maxSamplerAnisotropy; sampler.anisotropyEnable = True; } else { // The Device does not support anisotropic filtering sampler.maxAnisotropy = 1.0f; sampler.anisotropyEnable = False; } sampler.borderColor = VkBorderColor.FloatOpaqueWhite; Util.CheckResult(vkCreateSampler(device, ref sampler, null, out texture.sampler)); // Create image view // Textures are not directly accessed by the shaders and // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo view = Initializers.imageViewCreateInfo(); view.viewType = VkImageViewType.Image2D; view.format = format; view.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; // The subresource range describes the set of mip levels (and array layers) that can be accessed through this image view // It's possible to create multiple image views for a single image referring to different (and/or overlapping) ranges of the image view.subresourceRange.aspectMask = VkImageAspectFlags.Color; view.subresourceRange.baseMipLevel = 0; view.subresourceRange.baseArrayLayer = 0; view.subresourceRange.layerCount = 1; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used view.subresourceRange.levelCount = (useStaging == 1) ? texture.mipLevels : 1; // The view will be based on the texture's image view.image = texture.image; Util.CheckResult(vkCreateImageView(device, &view, null, out texture.view)); }
public static Image Load(Queue staggingQ, CommandPool staggingCmdPool, string ktxPath, VkImageUsageFlags usage = VkImageUsageFlags.Sampled, VkMemoryPropertyFlags memoryProperty = VkMemoryPropertyFlags.DeviceLocal, bool generateMipmaps = true, VkImageTiling tiling = VkImageTiling.Optimal) { Image img = null; using (Stream ktxStream = File.Open(ktxPath, FileMode.Open, FileAccess.Read)) { using (BinaryReader br = new BinaryReader(ktxStream)) { if (!br.ReadBytes(12).AreEquals(ktxSignature)) { throw new KtxException("Not a ktx file: " + ktxPath); } UInt32 endianness = br.ReadUInt32(); UInt32 glType = br.ReadUInt32(); UInt32 glTypeSize = br.ReadUInt32(); UInt32 glFormat = br.ReadUInt32(); UInt32 glInternalFormat = br.ReadUInt32(); UInt32 glBaseInternalFormat = br.ReadUInt32(); UInt32 pixelWidth = br.ReadUInt32(); UInt32 pixelHeight = br.ReadUInt32(); UInt32 pixelDepth = Math.Min(1, br.ReadUInt32()); UInt32 numberOfArrayElements = br.ReadUInt32(); //only for array text, else 0 UInt32 numberOfFaces = br.ReadUInt32(); //only for cube map, else UInt32 numberOfMipmapLevels = Math.Min(1, br.ReadUInt32()); UInt32 bytesOfKeyValueData = br.ReadUInt32(); VkFormat vkFormat = GLHelper.vkGetFormatFromOpenGLInternalFormat(glInternalFormat); if (vkFormat == VkFormat.Undefined) { vkFormat = GLHelper.vkGetFormatFromOpenGLFormat(glFormat, glType); if (vkFormat == VkFormat.Undefined) { throw new KtxException("Undefined format: " + ktxPath); } } VkFormatFeatureFlags phyFormatSupport = (tiling == VkImageTiling.Linear) ? staggingQ.Dev.phy.GetFormatProperties(vkFormat).linearTilingFeatures : staggingQ.Dev.phy.GetFormatProperties(vkFormat).optimalTilingFeatures; uint requestedMipsLevels = numberOfMipmapLevels; if (numberOfMipmapLevels == 1) { requestedMipsLevels = (generateMipmaps && phyFormatSupport.HasFlag(VkFormatFeatureFlags.BlitSrc | VkFormatFeatureFlags.BlitDst)) ? (uint)Math.Floor(Math.Log(Math.Max(pixelWidth, pixelHeight))) + 1 : 1; } if (tiling == VkImageTiling.Optimal) { usage |= VkImageUsageFlags.TransferDst; } if (generateMipmaps) { usage |= (VkImageUsageFlags.TransferSrc | VkImageUsageFlags.TransferDst); } VkImageCreateFlags createFlags = 0; VkImageType imgType = (pixelWidth == 0) ? throw new KtxException("pixelWidth must be > 0") : (pixelHeight == 0) ? imgType = VkImageType.Image1D : (pixelDepth == 0) ? imgType = VkImageType.Image2D : imgType = VkImageType.Image3D; VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; if (numberOfFaces > 1) { if (imgType != VkImageType.Image2D) { throw new KtxException("cubemap faces must be 2D textures"); } createFlags = VkImageCreateFlags.CubeCompatible; samples = VkSampleCountFlags.SampleCount1; numberOfArrayElements = numberOfFaces; } else { numberOfFaces = 1; if (numberOfArrayElements == 0) { numberOfArrayElements = 1; } } if (imgType != VkImageType.Image3D) { pixelDepth = 1; } img = new Image(staggingQ.Dev, vkFormat, usage, memoryProperty, pixelWidth, pixelHeight, imgType, samples, tiling, requestedMipsLevels, numberOfArrayElements, pixelDepth, createFlags); byte[] keyValueDatas = br.ReadBytes((int)bytesOfKeyValueData); if (memoryProperty.HasFlag(VkMemoryPropertyFlags.DeviceLocal)) { ulong staggingSize = img.AllocatedDeviceMemorySize; using (HostBuffer stagging = new HostBuffer(staggingQ.Dev, VkBufferUsageFlags.TransferSrc, staggingSize)) { stagging.Map(); CommandBuffer cmd = staggingCmdPool.AllocateCommandBuffer(); cmd.Start(VkCommandBufferUsageFlags.OneTimeSubmit); img.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.Transfer); List <VkBufferImageCopy> buffCopies = new List <VkBufferImageCopy> (); VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy { imageExtent = img.CreateInfo.extent, imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, img.CreateInfo.arrayLayers, 0) }; ulong bufferOffset = 0; uint imgWidth = img.CreateInfo.extent.height; uint imgHeight = img.CreateInfo.extent.width; for (int mips = 0; mips < numberOfMipmapLevels; mips++) { UInt32 imgSize = br.ReadUInt32(); bufferCopyRegion.bufferImageHeight = imgWidth; bufferCopyRegion.bufferRowLength = imgHeight; bufferCopyRegion.bufferOffset = bufferOffset; if (createFlags.HasFlag(VkImageCreateFlags.CubeCompatible)) { IntPtr ptrFace = img.MappedData; bufferCopyRegion.imageSubresource.layerCount = 1; for (uint face = 0; face < numberOfFaces; face++) { bufferCopyRegion.imageSubresource.baseArrayLayer = face; Marshal.Copy(br.ReadBytes((int)imgSize), 0, stagging.MappedData + (int)bufferOffset, (int)imgSize); buffCopies.Add(bufferCopyRegion); uint faceOffset = imgSize + (imgSize % 4); ptrFace += (int)faceOffset; //cube padding bufferOffset += faceOffset; bufferCopyRegion.bufferOffset = bufferOffset; } } else { Marshal.Copy(br.ReadBytes((int)imgSize), 0, stagging.MappedData, (int)imgSize); buffCopies.Add(bufferCopyRegion); } bufferOffset += imgSize; imgWidth /= 2; imgHeight /= 2; } stagging.Unmap(); Vk.vkCmdCopyBufferToImage(cmd.Handle, stagging.handle, img.handle, VkImageLayout.TransferDstOptimal, (uint)buffCopies.Count, buffCopies.Pin()); buffCopies.Unpin(); if (requestedMipsLevels > numberOfMipmapLevels) { img.BuildMipmaps(cmd); } else { img.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllGraphics); } cmd.End(); staggingQ.Submit(cmd); staggingQ.WaitIdle(); cmd.Free(); } //for (int mips = 0; mips < numberOfMipmapLevels; mips++) { //UInt32 imgSize = br.ReadUInt32 (); /*VkImageBlit imageBlit = new VkImageBlit { * srcSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, numberOfArrayElements, (uint)mips - 1), * srcOffsets_1 = new VkOffset3D((int)pixelWidth >> (mips - 1), (int)pixelHeight >> (mips - 1),1), * dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, numberOfArrayElements, (uint)mips), * dstOffsets_1 = new VkOffset3D ((int)pixelWidth >> mips, (int)pixelHeight >> mips, 1), * };*/ //for (int layer = 0; layer < numberOfArrayElements; layer++) { //for (int face = 0; face < numberOfFaces; face++) { //for (int slice = 0; slice < pixelDepth; slice++) { /*for (int y = 0; y < pixelHeight; y++) { * for (int x = 0; x < pixelWidth; x++) { * //Uncompressed texture data matches a GL_UNPACK_ALIGNMENT of 4. * } * }*/ //} //Byte cubePadding[0-3] //} //} //Byte mipPadding[0-3] //} } } } return(img); }
/** * Load a 2D texture including all mip levels * * @param filename File to load (supports .ktx and .dds) * @param format Vulkan format of the image data stored in the file * @param device Vulkan device to create the texture on * @param copyQueue Queue used for the texture staging copy commands (must support transfer) * @param (Optional) imageUsageFlags Usage flags for the texture's image (defaults to VK_IMAGE_USAGE_SAMPLED_BIT) * @param (Optional) imageLayout Usage layout for the texture (defaults VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) * @param (Optional) forceLinear Force linear tiling (not advised, defaults to false) * */ public void loadFromFile( string filename, VkFormat format, vksVulkanDevice device, VkQueue copyQueue, VkImageUsageFlagBits imageUsageFlags = VkImageUsageFlagBits.Sampled, VkImageLayout imageLayout = VkImageLayout.ShaderReadOnlyOptimal, bool forceLinear = false) { KtxFile tex2D; using (var fs = File.OpenRead(filename)) { tex2D = KtxFile.Load(fs, false); } this.device = device; width = tex2D.Header.PixelWidth; height = tex2D.Header.PixelHeight; if (height == 0) { height = width; } mipLevels = tex2D.Header.NumberOfMipmapLevels; // Get device properites for the requested texture format VkFormatProperties formatProperties; vkGetPhysicalDeviceFormatProperties(device.PhysicalDevice, format, &formatProperties); // Only use linear tiling if requested (and supported by the device) // Support for linear tiling is mostly limited, so prefer to use // optimal tiling instead // On most implementations linear tiling will only support a very // limited amount of formats and features (mip maps, cubemaps, arrays, etc.) bool useStaging = !forceLinear; VkMemoryAllocateInfo memAllocInfo = new VkMemoryAllocateInfo(); memAllocInfo.sType = MemoryAllocateInfo; VkMemoryRequirements memReqs; // Use a separate command buffer for texture loading VkCommandBuffer copyCmd = device.createCommandBuffer(VkCommandBufferLevel.Primary, true); if (useStaging) { // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = new VkBufferCreateInfo(); bufferCreateInfo.sType = BufferCreateInfo; bufferCreateInfo.size = tex2D.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlagBits.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; vkCreateBuffer(device.LogicalDevice, &bufferCreateInfo, null, &stagingBuffer); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device.LogicalDevice, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = device.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.HostVisible | VkMemoryPropertyFlagBits.HostCoherent); vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &stagingMemory); vkBindBufferMemory(device.LogicalDevice, stagingBuffer, stagingMemory, 0); // Copy texture data into staging buffer IntPtr data; vkMapMemory(device.LogicalDevice, stagingMemory, 0, memReqs.size, 0, &data); byte[] pixelData = tex2D.GetAllTextureData(); fixed(byte *pixelDataPtr = &pixelData[0]) { Unsafe.CopyBlock(data, pixelDataPtr, (uint)pixelData.Length); } vkUnmapMemory(device.LogicalDevice, stagingMemory); // Setup buffer copy regions for each mip level var bufferCopyRegions = new List <VkBufferImageCopy>(); uint offset = 0; for (uint i = 0; i < mipLevels; i++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlagBits.Color; bufferCopyRegion.imageSubresource.mipLevel = i; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = tex2D.Faces[0].Mipmaps[i].Width; bufferCopyRegion.imageExtent.height = tex2D.Faces[0].Mipmaps[i].Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); offset += tex2D.Faces[0].Mipmaps[i].SizeInBytes; } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = new VkImageCreateInfo(); imageCreateInfo.sType = ImageCreateInfo; imageCreateInfo.imageType = VkImageType._2d; imageCreateInfo.format = format; imageCreateInfo.mipLevels = mipLevels; imageCreateInfo.arrayLayers = 1; imageCreateInfo.samples = VkSampleCountFlagBits._1; imageCreateInfo.tiling = VkImageTiling.Optimal; imageCreateInfo.sharingMode = VkSharingMode.Exclusive; imageCreateInfo.initialLayout = VkImageLayout.Undefined; imageCreateInfo.extent = new VkExtent3D { width = width, height = height, depth = 1 }; imageCreateInfo.usage = imageUsageFlags; // Ensure that the TRANSFER_DST bit is set for staging if ((imageCreateInfo.usage & VkImageUsageFlagBits.TransferDst) == 0) { imageCreateInfo.usage |= VkImageUsageFlagBits.TransferDst; } { VkImage vkImage; vkCreateImage(device.LogicalDevice, &imageCreateInfo, null, &vkImage); this.image = vkImage; } vkGetImageMemoryRequirements(device.LogicalDevice, image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = device.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); { VkDeviceMemory memory; vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &memory); this.deviceMemory = memory; } vkBindImageMemory(device.LogicalDevice, image, deviceMemory, 0); VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlagBits.Color; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = mipLevels; subresourceRange.layerCount = 1; // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy Tools.setImageLayout( copyCmd, image, VkImageAspectFlagBits.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer fixed(VkBufferImageCopy *pointer = bufferCopyRegions.ToArray()) { vkCmdCopyBufferToImage( copyCmd, stagingBuffer, image, VkImageLayout.TransferDstOptimal, (UInt32)bufferCopyRegions.Count, pointer); } // Change texture image layout to shader read after all mip levels have been copied this.imageLayout = imageLayout; Tools.setImageLayout( copyCmd, image, VkImageAspectFlagBits.Color, VkImageLayout.TransferDstOptimal, imageLayout, subresourceRange); device.flushCommandBuffer(copyCmd, copyQueue); // Clean up staging resources vkFreeMemory(device.LogicalDevice, stagingMemory, null); vkDestroyBuffer(device.LogicalDevice, stagingBuffer, null); } else { throw new NotImplementedException(); /* * // Prefer using optimal tiling, as linear tiling * // may support only a small set of features * // depending on implementation (e.g. no mip maps, only one layer, etc.) * * // Check if this support is supported for linear tiling * Debug.Assert((formatProperties.linearTilingFeatures & VkFormatFeatureFlags.SampledImage) != 0); * * VkImage mappableImage; * VkDeviceMemory mappableMemory; * * VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); * imageCreateInfo.imageType = VkImageType._2d; * imageCreateInfo.format = format; * imageCreateInfo.extent = new VkExtent3D { width = width, height = height, depth = 1 }; * imageCreateInfo.mipLevels = 1; * imageCreateInfo.arrayLayers = 1; * imageCreateInfo.samples = VkSampleCountFlags._1; * imageCreateInfo.tiling = VkImageTiling.Linear; * imageCreateInfo.usage = imageUsageFlags; * imageCreateInfo.sharingMode = VkSharingMode.Exclusive; * imageCreateInfo.initialLayout = VkImageLayout.Undefined; * * // Load mip map level 0 to linear tiling image * Util.CheckResult(vkCreateImage(device.LogicalDevice, &imageCreateInfo, null, &mappableImage)); * * // Get memory requirements for this image * // like size and alignment * vkGetImageMemoryRequirements(device.LogicalDevice, mappableImage, &memReqs); * // Set memory allocation size to required memory size * memAllocInfo.allocationSize = memReqs.size; * * // Get memory type that can be mapped to host memory * memAllocInfo.memoryTypeIndex = device.GetMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); * * // Allocate host memory * Util.CheckResult(vkAllocateMemory(device.LogicalDevice, &memAllocInfo, null, &mappableMemory)); * * // Bind allocated image for use * Util.CheckResult(vkBindImageMemory(device.LogicalDevice, mappableImage, mappableMemory, 0)); * * // Get sub resource layout * // Mip map count, array layer, etc. * VkImageSubresource subRes = new VkImageSubresource(); * subRes.aspectMask = VkImageAspectFlags.Color; * subRes.mipLevel = 0; * * VkSubresourceLayout subResLayout; * void* data; * * // Get sub resources layout * // Includes row pitch, size offsets, etc. * vkGetImageSubresourceLayout(device.LogicalDevice, mappableImage, &subRes, &subResLayout); * * // Map image memory * Util.CheckResult(vkMapMemory(device.LogicalDevice, mappableMemory, 0, memReqs.size, 0, &data)); * * // Copy image data into memory * memcpy(data, tex2D[subRes.mipLevel].data(), tex2D[subRes.mipLevel].size()); * * vkUnmapMemory(device.LogicalDevice, mappableMemory); * * // Linear tiled images don't need to be staged * // and can be directly used as textures * image = mappableImage; * deviceMemory = mappableMemory; * imageLayout = imageLayout; * * // Setup image memory barrier * vks::tools::setImageLayout(copyCmd, image, VkImageAspectFlags.Color, VkImageLayout.Undefined, imageLayout); * * device.flushCommandBuffer(copyCmd, copyQueue); */ } // Create a defaultsampler VkSamplerCreateInfo samplerCreateInfo = new VkSamplerCreateInfo(); samplerCreateInfo.sType = SamplerCreateInfo; samplerCreateInfo.magFilter = VkFilter.Linear; samplerCreateInfo.minFilter = VkFilter.Linear; samplerCreateInfo.mipmapMode = VkSamplerMipmapMode.Linear; samplerCreateInfo.addressModeU = VkSamplerAddressMode.Repeat; samplerCreateInfo.addressModeV = VkSamplerAddressMode.Repeat; samplerCreateInfo.addressModeW = VkSamplerAddressMode.Repeat; samplerCreateInfo.mipLodBias = 0.0f; samplerCreateInfo.compareOp = VkCompareOp.Never; samplerCreateInfo.minLod = 0.0f; // Max level-of-detail should match mip level count samplerCreateInfo.maxLod = (useStaging) ? (float)mipLevels : 0.0f; // Enable anisotropic filtering samplerCreateInfo.maxAnisotropy = 8; samplerCreateInfo.anisotropyEnable = true; samplerCreateInfo.borderColor = VkBorderColor.FloatOpaqueWhite; { VkSampler vkSampler; vkCreateSampler(device.LogicalDevice, &samplerCreateInfo, null, &vkSampler); this.sampler = vkSampler; } // Create image view // Textures are not directly accessed by the shaders and // are abstracted by image views containing additional // information and sub resource ranges VkImageViewCreateInfo viewCreateInfo = new VkImageViewCreateInfo(); viewCreateInfo.sType = ImageViewCreateInfo; viewCreateInfo.viewType = VkImageViewType._2d; viewCreateInfo.format = format; viewCreateInfo.components = new VkComponentMapping { r = VkComponentSwizzle.R, g = VkComponentSwizzle.G, b = VkComponentSwizzle.B, a = VkComponentSwizzle.A }; viewCreateInfo.subresourceRange = new VkImageSubresourceRange { aspectMask = VkImageAspectFlagBits.Color, baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1 }; // Linear tiling usually won't support mip maps // Only set mip map count if optimal tiling is used viewCreateInfo.subresourceRange.levelCount = (useStaging) ? mipLevels : 1; viewCreateInfo.image = image; { VkImageView vkImageView; vkCreateImageView(device.LogicalDevice, &viewCreateInfo, null, &vkImageView); this.view = vkImageView; } // Update descriptor image info member that can be used for setting up descriptor sets updateDescriptor(); }
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; lock (GraphicsDevice.QueueLock) { vkAllocateCommandBuffers(GraphicsDevice.NativeDevice, &commandBufferAllocateInfo, &commandBuffer); } var beginInfo = new VkCommandBufferBeginInfo { sType = VkStructureType.CommandBufferBeginInfo, flags = VkCommandBufferUsageFlags.OneTimeSubmit }; 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; } VkBuffer uploadResource; int uploadOffset; var uploadMemory = GraphicsDevice.AllocateUploadBuffer(totalSize, out uploadResource, out uploadOffset); // Upload buffer barrier var bufferMemoryBarrier = new VkBufferMemoryBarrier(uploadResource, 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.Mathematics.Point3(0, 0, 0), imageExtent = new Vortice.Mathematics.Size3(mipMapDescription.Width, mipMapDescription.Height, mipMapDescription.Depth) }; uploadMemory += slicePitch; uploadOffset += slicePitch; } // Copy from upload buffer to image fixed(VkBufferImageCopy *copiesPointer = &copies[0]) { vkCmdCopyBufferToImage(commandBuffer, uploadResource, 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); vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier); // Close and submit vkEndCommandBuffer(commandBuffer); var submitInfo = new VkSubmitInfo { sType = VkStructureType.SubmitInfo, commandBufferCount = 1, pCommandBuffers = &commandBuffer, }; lock (GraphicsDevice.QueueLock) { vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, VkFence.Null); vkQueueWaitIdle(GraphicsDevice.NativeCommandQueue); vkFreeCommandBuffers(GraphicsDevice.NativeDevice, GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer); } }
private void TransferData(Image <Rgba32> tex2D) { using CommandBuffer copyCmd = device.GetCommandPool().Rent(); VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs; copyCmd.Begin(); // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; var pixels = tex2D.GetPixelSpan(); var byteCount = (ulong)(pixels.Length * Unsafe.SizeOf <Rgba32>()); VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = byteCount; // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VkBufferUsageFlags.TransferSrc; bufferCreateInfo.sharingMode = VkSharingMode.Exclusive; Util.CheckResult(vkCreateBuffer(device.device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device.device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = device.vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent); Util.CheckResult(vkAllocateMemory(device.device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device.device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(device.device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); fixed(Rgba32 *pixelDataPtr = &pixels[0]) { Unsafe.CopyBlock(data, pixelDataPtr, (uint)byteCount); } vkUnmapMemory(device.device, stagingMemory); // Setup buffer copy regions for each mip level using NativeList <VkBufferImageCopy> bufferCopyRegions = new NativeList <VkBufferImageCopy>(); uint offset = 0; VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VkImageAspectFlags.Color; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = 0; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = (uint)tex2D.Width; bufferCopyRegion.imageExtent.height = (uint)tex2D.Height; bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.Add(bufferCopyRegion); VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = VkImageAspectFlags.Color; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = mipLevels; subresourceRange.layerCount = 1; // Image barrier for optimal image (target) // Optimal image will be used as destination for the copy Tools.setImageLayout( copyCmd.vkCmd, image, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subresourceRange); // Copy mip levels from staging buffer vkCmdCopyBufferToImage( copyCmd.vkCmd, stagingBuffer, image, VkImageLayout.TransferDstOptimal, bufferCopyRegions.Count, bufferCopyRegions.Data); // Change texture image layout to shader read after all mip levels have been copied Tools.setImageLayout( copyCmd.vkCmd, image, VkImageAspectFlags.Color, VkImageLayout.TransferDstOptimal, imageLayout, subresourceRange); copyCmd.End(); device.FlushCommandBuffer(copyCmd); //device.flushCommandBuffer(copyCmd, copyQueue); // Clean up staging resources vkFreeMemory(device.device, stagingMemory, null); vkDestroyBuffer(device.device, stagingBuffer, null); }
private static VkImage LoadTexture(VkDevice device, VkPhysicalDevice physicalDevice, int cmdPoolID, VkQueue queue, string[] paths, uint mipLevels) { Bitmap[] bitmaps = new Bitmap[paths.Length]; FDataBuffer <byte>[] tempBuffer = new FDataBuffer <byte> [paths.Length]; uint width = 0, height = 0; for (int j = 0; j < paths.Length; j++) { bitmaps[j] = new Bitmap(System.Drawing.Image.FromFile(paths[j])); var data = bitmaps[j].LockBits(new Rectangle(0, 0, bitmaps[j].Width, bitmaps[j].Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bitmaps[j].PixelFormat); width = (uint)data.Width; height = (uint)data.Height;//TODO add size check Span <byte> img = new Span <byte>((void *)data.Scan0, data.Stride * data.Height); tempBuffer[j] = new FDataBuffer <byte>(device, physicalDevice, img.Length, VkBufferUsageFlags.TransferSrc, VkSharingMode.Exclusive); Span <byte> buffer = tempBuffer[j].Map(); for (int i = 0; i < img.Length; i += 4) { buffer[i + 2] = img[i]; buffer[i + 1] = img[i + 1]; buffer[i] = img[i + 2]; buffer[i + 3] = img[i + 3]; } buffer = tempBuffer[j].UnMap(); } VkImage texture = VkImage.Null; VkDeviceMemory memory = VkDeviceMemory.Null; VkImageCreateInfo createInfo = VkImageCreateInfo.New(); createInfo.imageType = VkImageType.Image2D; createInfo.extent.width = width; createInfo.extent.height = height; createInfo.extent.depth = 1; createInfo.mipLevels = mipLevels; createInfo.arrayLayers = (uint)paths.Length; createInfo.format = VkFormat.R8g8b8a8Unorm; createInfo.tiling = VkImageTiling.Optimal; createInfo.initialLayout = VkImageLayout.Undefined; createInfo.usage = VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferSrc; createInfo.sharingMode = VkSharingMode.Exclusive; createInfo.samples = VkSampleCountFlags.Count1; Assert(vkCreateImage(device, &createInfo, null, &texture)); VkMemoryRequirements memoryRequirements; vkGetImageMemoryRequirements(device, texture, &memoryRequirements); VkPhysicalDeviceMemoryProperties memoryProperties = new VkPhysicalDeviceMemoryProperties(); vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memoryProperties); VkMemoryAllocateInfo allocateInfo = VkMemoryAllocateInfo.New(); allocateInfo.allocationSize = memoryRequirements.size; allocateInfo.memoryTypeIndex = FDataBuffer <byte> .SelectMemoryType(memoryProperties, memoryRequirements.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal); Assert(vkAllocateMemory(device, &allocateInfo, null, &memory)); vkBindImageMemory(device, texture, memory, 0); VkCommandBufferAllocateInfo pAllocateInfo = VkCommandBufferAllocateInfo.New(); pAllocateInfo.commandPool = CommandPoolManager.GetPool(cmdPoolID); pAllocateInfo.level = VkCommandBufferLevel.Primary; pAllocateInfo.commandBufferCount = 1; VkCommandBuffer cmdBuffer = VkCommandBuffer.Null; Assert(vkAllocateCommandBuffers(device, &pAllocateInfo, &cmdBuffer)); VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.New(); beginInfo.flags = VkCommandBufferUsageFlags.OneTimeSubmit; Assert(vkBeginCommandBuffer(cmdBuffer, &beginInfo)); VkImageMemoryBarrier imageMemoryBarrier = VkImageMemoryBarrier.New(); imageMemoryBarrier.srcAccessMask = VkAccessFlags.None; imageMemoryBarrier.dstAccessMask = VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite; imageMemoryBarrier.oldLayout = VkImageLayout.Undefined; imageMemoryBarrier.newLayout = VkImageLayout.TransferDstOptimal; imageMemoryBarrier.srcQueueFamilyIndex = VulkanNative.QueueFamilyIgnored; imageMemoryBarrier.dstQueueFamilyIndex = VulkanNative.QueueFamilyIgnored; imageMemoryBarrier.image = texture; imageMemoryBarrier.subresourceRange = new VkImageSubresourceRange() { baseMipLevel = 0, levelCount = mipLevels, baseArrayLayer = 0, layerCount = (uint)paths.Length, aspectMask = VkImageAspectFlags.Color }; vkCmdPipelineBarrier(cmdBuffer, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.AllCommands, VkDependencyFlags.ByRegion, 0, null, 0, null, 1, &imageMemoryBarrier); for (int j = 0; j < tempBuffer.Length; j++) { VkBufferImageCopy region = new VkBufferImageCopy(); region.bufferOffset = 0; region.bufferRowLength = 0; region.bufferImageHeight = 0; region.imageSubresource.aspectMask = VkImageAspectFlags.Color; region.imageSubresource.mipLevel = 0; region.imageSubresource.baseArrayLayer = (uint)j; region.imageSubresource.layerCount = 1; region.imageOffset = new VkOffset3D(); region.imageExtent = new VkExtent3D() { width = width, height = height, depth = 1 }; vkCmdCopyBufferToImage(cmdBuffer, tempBuffer[j].Buffer, texture, VkImageLayout.TransferDstOptimal, 1, ®ion); } imageMemoryBarrier.oldLayout = VkImageLayout.TransferDstOptimal; imageMemoryBarrier.newLayout = VkImageLayout.ShaderReadOnlyOptimal; vkCmdPipelineBarrier(cmdBuffer, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.AllCommands, VkDependencyFlags.ByRegion, 0, null, 0, null, 1, &imageMemoryBarrier); Assert(vkEndCommandBuffer(cmdBuffer)); VkSubmitInfo submitInfo = VkSubmitInfo.New(); submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &cmdBuffer; Assert(vkQueueSubmit(queue, 1, &submitInfo, VkFence.Null)); Assert(vkQueueWaitIdle(queue)); vkFreeCommandBuffers(device, CommandPoolManager.GetPool(cmdPoolID), 1, &cmdBuffer); return(texture); }
internal static void CopyTextureCore_VkCommandBuffer( VkCommandBuffer cb, Texture source, uint srcX, uint srcY, uint srcZ, uint srcMipLevel, uint srcBaseArrayLayer, Texture destination, uint dstX, uint dstY, uint dstZ, uint dstMipLevel, uint dstBaseArrayLayer, uint width, uint height, uint depth, uint layerCount) { VkTexture srcVkTexture = Util.AssertSubtype <Texture, VkTexture>(source); VkTexture dstVkTexture = Util.AssertSubtype <Texture, VkTexture>(destination); bool sourceIsStaging = (source.Usage & TextureUsage.Staging) == TextureUsage.Staging; bool destIsStaging = (destination.Usage & TextureUsage.Staging) == TextureUsage.Staging; if (!sourceIsStaging && !destIsStaging) { VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers { aspectMask = VkImageAspectFlags.Color, layerCount = layerCount, mipLevel = srcMipLevel, baseArrayLayer = srcBaseArrayLayer }; VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers { aspectMask = VkImageAspectFlags.Color, layerCount = layerCount, mipLevel = dstMipLevel, baseArrayLayer = dstBaseArrayLayer }; VkImageCopy region = new VkImageCopy { srcOffset = new VkOffset3D { x = (int)srcX, y = (int)srcY, z = (int)srcZ }, dstOffset = new VkOffset3D { x = (int)dstX, y = (int)dstY, z = (int)dstZ }, srcSubresource = srcSubresource, dstSubresource = dstSubresource, extent = new VkExtent3D { width = width, height = height, depth = depth } }; srcVkTexture.TransitionImageLayout( cb, srcMipLevel, 1, srcBaseArrayLayer, layerCount, VkImageLayout.TransferSrcOptimal); dstVkTexture.TransitionImageLayout( cb, dstMipLevel, 1, dstBaseArrayLayer, layerCount, VkImageLayout.TransferDstOptimal); vkCmdCopyImage( cb, srcVkTexture.OptimalDeviceImage, VkImageLayout.TransferSrcOptimal, dstVkTexture.OptimalDeviceImage, VkImageLayout.TransferDstOptimal, 1, ref region); } else if (sourceIsStaging && !destIsStaging) { Vulkan.VkBuffer srcBuffer = srcVkTexture.StagingBuffer; VkSubresourceLayout srcLayout = srcVkTexture.GetSubresourceLayout( srcVkTexture.CalculateSubresource(srcMipLevel, srcBaseArrayLayer)); VkImage dstImage = dstVkTexture.OptimalDeviceImage; dstVkTexture.TransitionImageLayout( cb, dstMipLevel, 1, dstBaseArrayLayer, layerCount, VkImageLayout.TransferDstOptimal); VkImageSubresourceLayers dstSubresource = new VkImageSubresourceLayers { aspectMask = VkImageAspectFlags.Color, layerCount = layerCount, mipLevel = dstMipLevel, baseArrayLayer = dstBaseArrayLayer }; Util.GetMipDimensions(srcVkTexture, srcMipLevel, out uint mipWidth, out uint mipHeight, out uint mipDepth); uint blockSize = FormatHelpers.IsCompressedFormat(srcVkTexture.Format) ? 4u : 1u; uint bufferRowLength = Math.Max(mipWidth, blockSize); uint bufferImageHeight = Math.Max(mipHeight, blockSize); uint compressedX = srcX / blockSize; uint compressedY = srcY / blockSize; uint blockSizeInBytes = blockSize == 1 ? FormatHelpers.GetSizeInBytes(srcVkTexture.Format) : FormatHelpers.GetBlockSizeInBytes(srcVkTexture.Format); uint rowPitch = FormatHelpers.GetRowPitch(bufferRowLength, srcVkTexture.Format); uint depthPitch = FormatHelpers.GetDepthPitch(rowPitch, bufferImageHeight, srcVkTexture.Format); VkBufferImageCopy regions = new VkBufferImageCopy { bufferOffset = srcLayout.offset + (srcZ * depthPitch) + (compressedY * rowPitch) + (compressedX * blockSizeInBytes), bufferRowLength = bufferRowLength, bufferImageHeight = bufferImageHeight, imageExtent = new VkExtent3D { width = width, height = height, depth = depth }, imageOffset = new VkOffset3D { x = (int)dstX, y = (int)dstY, z = (int)dstZ }, imageSubresource = dstSubresource }; vkCmdCopyBufferToImage(cb, srcBuffer, dstImage, VkImageLayout.TransferDstOptimal, 1, ref regions); } else if (!sourceIsStaging && destIsStaging) { VkImage srcImage = srcVkTexture.OptimalDeviceImage; srcVkTexture.TransitionImageLayout( cb, srcMipLevel, 1, srcBaseArrayLayer, layerCount, VkImageLayout.TransferSrcOptimal); Vulkan.VkBuffer dstBuffer = dstVkTexture.StagingBuffer; VkSubresourceLayout dstLayout = dstVkTexture.GetSubresourceLayout( dstVkTexture.CalculateSubresource(dstMipLevel, dstBaseArrayLayer)); VkImageSubresourceLayers srcSubresource = new VkImageSubresourceLayers { aspectMask = VkImageAspectFlags.Color, layerCount = layerCount, mipLevel = srcMipLevel, baseArrayLayer = srcBaseArrayLayer }; Util.GetMipDimensions(dstVkTexture, dstMipLevel, out uint mipWidth, out uint mipHeight, out uint mipDepth); VkBufferImageCopy region = new VkBufferImageCopy { bufferRowLength = mipWidth, bufferImageHeight = mipHeight, bufferOffset = dstLayout.offset + (dstX * FormatHelpers.GetSizeInBytes(dstVkTexture.Format)), imageExtent = new VkExtent3D { width = width, height = height, depth = depth }, imageOffset = new VkOffset3D { x = (int)dstX, y = (int)dstY, z = (int)dstZ }, imageSubresource = srcSubresource }; vkCmdCopyImageToBuffer(cb, srcImage, VkImageLayout.TransferSrcOptimal, dstBuffer, 1, ref region); } else { Debug.Assert(sourceIsStaging && destIsStaging); Vulkan.VkBuffer srcBuffer = srcVkTexture.StagingBuffer; VkSubresourceLayout srcLayout = srcVkTexture.GetSubresourceLayout( srcVkTexture.CalculateSubresource(srcMipLevel, srcBaseArrayLayer)); Vulkan.VkBuffer dstBuffer = dstVkTexture.StagingBuffer; VkSubresourceLayout dstLayout = dstVkTexture.GetSubresourceLayout( dstVkTexture.CalculateSubresource(dstMipLevel, dstBaseArrayLayer)); uint zLimit = Math.Max(depth, layerCount); if (!FormatHelpers.IsCompressedFormat(source.Format)) { uint pixelSize = FormatHelpers.GetSizeInBytes(srcVkTexture.Format); for (uint zz = 0; zz < zLimit; zz++) { for (uint yy = 0; yy < height; yy++) { VkBufferCopy region = new VkBufferCopy { srcOffset = srcLayout.offset + srcLayout.depthPitch * (zz + srcZ) + srcLayout.rowPitch * (yy + srcY) + pixelSize * srcX, dstOffset = dstLayout.offset + dstLayout.depthPitch * (zz + dstZ) + dstLayout.rowPitch * (yy + dstY) + pixelSize * dstX, size = width * pixelSize, }; vkCmdCopyBuffer(cb, srcBuffer, dstBuffer, 1, ref region); } } } else // IsCompressedFormat { uint denseRowSize = FormatHelpers.GetRowPitch(width, source.Format); uint numRows = FormatHelpers.GetNumRows(height, source.Format); uint compressedSrcX = srcX / 4; uint compressedSrcY = srcY / 4; uint compressedDstX = dstX / 4; uint compressedDstY = dstY / 4; uint blockSizeInBytes = FormatHelpers.GetBlockSizeInBytes(source.Format); for (uint zz = 0; zz < zLimit; zz++) { for (uint row = 0; row < numRows; row++) { VkBufferCopy region = new VkBufferCopy { srcOffset = srcLayout.offset + srcLayout.depthPitch * (zz + srcZ) + srcLayout.rowPitch * (row + compressedSrcY) + blockSizeInBytes * compressedSrcX, dstOffset = dstLayout.offset + dstLayout.depthPitch * (zz + dstZ) + dstLayout.rowPitch * (row + compressedDstY) + blockSizeInBytes * compressedDstX, size = denseRowSize, }; vkCmdCopyBuffer(cb, srcBuffer, dstBuffer, 1, ref region); } } } } }
void loadTextureArray(string filename, VkFormat format) { KtxFile tex2DArray; using (var fs = File.OpenRead(filename)) { tex2DArray = KtxFile.Load(fs, false); } textureArray.width = tex2DArray.Header.PixelWidth; textureArray.height = tex2DArray.Header.PixelHeight; layerCount = tex2DArray.Header.NumberOfArrayElements; VkMemoryAllocateInfo memAllocInfo = Initializers.memoryAllocateInfo(); VkMemoryRequirements memReqs; // Create a host-visible staging buffer that contains the raw image data VkBuffer stagingBuffer; VkDeviceMemory stagingMemory; VkBufferCreateInfo bufferCreateInfo = Initializers.bufferCreateInfo(); bufferCreateInfo.size = tex2DArray.GetTotalSize(); // This buffer is used as a transfer source for the buffer copy bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; Util.CheckResult(vkCreateBuffer(device, &bufferCreateInfo, null, &stagingBuffer)); // Get memory requirements for the staging buffer (alignment, memory type bits) vkGetBufferMemoryRequirements(device, stagingBuffer, &memReqs); memAllocInfo.allocationSize = memReqs.size; // Get memory type index for a host visible buffer memAllocInfo.memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, &stagingMemory)); Util.CheckResult(vkBindBufferMemory(device, stagingBuffer, stagingMemory, 0)); // Copy texture data into staging buffer byte *data; Util.CheckResult(vkMapMemory(device, stagingMemory, 0, memReqs.size, 0, (void **)&data)); byte[] allTextureData = tex2DArray.GetAllTextureData(); fixed(byte *texPtr = allTextureData) { Unsafe.CopyBlock(data, texPtr, (uint)allTextureData.Length); } vkUnmapMemory(device, stagingMemory); // Setup buffer copy regions for array layers NativeList <VkBufferImageCopy> bufferCopyRegions; IntPtr offset = IntPtr.Zero; for (uint layer = 0; layer < layerCount; layer++) { VkBufferImageCopy bufferCopyRegion = new VkBufferImageCopy(); bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; bufferCopyRegion.imageSubresource.mipLevel = 0; bufferCopyRegion.imageSubresource.baseArrayLayer = layer; bufferCopyRegion.imageSubresource.layerCount = 1; bufferCopyRegion.imageExtent.width = (uint)(tex2DArray[layer][0].extent().x); bufferCopyRegion.imageExtent.height = (uint)(tex2DArray[layer][0].extent().y); bufferCopyRegion.imageExtent.depth = 1; bufferCopyRegion.bufferOffset = offset; bufferCopyRegions.push_back(bufferCopyRegion); // Increase offset into staging buffer for next level / face offset += tex2DArray[layer][0].Count; } // Create optimal tiled target image VkImageCreateInfo imageCreateInfo = Initializers.imageCreateInfo(); imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; imageCreateInfo.format = format; imageCreateInfo.mipLevels = 1; imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; imageCreateInfo.extent = new { textureArray.width, textureArray.height, 1 }; imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageCreateInfo.arrayLayers = layerCount; Util.CheckResult(vkCreateImage(device, &imageCreateInfo, null, &textureArray.image)); vkGetImageMemoryRequirements(device, textureArray.image, &memReqs); memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); Util.CheckResult(vkAllocateMemory(device, &memAllocInfo, null, &textureArray.deviceMemory)); Util.CheckResult(vkBindImageMemory(device, textureArray.image, textureArray.deviceMemory, 0)); VkCommandBuffer copyCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); // Image barrier for optimal image (target) // Set initial layout for all array layers (faces) of the optimal (target) tiled texture VkImageSubresourceRange subresourceRange = { }; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = layerCount; vkstools::setImageLayout( copyCmd, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); // Copy the cube map faces from the staging buffer to the optimal tiled image vkCmdCopyBufferToImage( copyCmd, stagingBuffer, textureArray.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, bufferCopyRegions.Count, bufferCopyRegions.Data ); // Change texture image layout to shader read after all faces have been copied textureArray.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; vkstools::setImageLayout( copyCmd, textureArray.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, textureArray.imageLayout, subresourceRange); VulkanExampleBase::flushCommandBuffer(copyCmd, queue, true); // Create sampler VkSamplerCreateInfo sampler = Initializers.samplerCreateInfo(); sampler.magFilter = VK_FILTER_LINEAR; sampler.minFilter = VK_FILTER_LINEAR; sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sampler.addressModeV = sampler.addressModeU; sampler.addressModeW = sampler.addressModeU; sampler.mipLodBias = 0.0f; sampler.maxAnisotropy = 8; sampler.compareOp = VK_COMPARE_OP_NEVER; sampler.minLod = 0.0f; sampler.maxLod = 0.0f; sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; Util.CheckResult(vkCreateSampler(device, &sampler, null, &textureArray.sampler)); // Create image view VkImageViewCreateInfo view = Initializers.imageViewCreateInfo(); view.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; view.format = format; view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };