/// <summary> /// Create a new Image. /// </summary> /// <remarks>Initial layout will be automatically set to Undefined if tiling is optimal and Preinitialized if tiling is linear.</remarks> /// <param name="device">The logical device that create the image.</param> /// <param name="format">format and type of the texel blocks that will be contained in the image</param> /// <param name="usage">bitmask describing the intended usage of the image.</param> /// <param name="_memoryPropertyFlags">Memory property flags.</param> /// <param name="width">number of data in the X dimension of the image.</param> /// <param name="height">number of data in the Y dimension of the image.</param> /// <param name="type">value specifying the basic dimensionality of the image. Layers in array textures do not count as a dimension for the purposes of the image type.</param> /// <param name="samples">number of sample per texel.</param> /// <param name="tiling">tiling arrangement of the texel blocks in memory.</param> /// <param name="mipsLevels">describes the number of levels of detail available for minified sampling of the image.</param> /// <param name="layers">number of layers in the image.</param> /// <param name="depth">number of data in the Z dimension of the image</param> /// <param name="createFlags">bitmask describing additional parameters of the image.</param> /// <param name="sharingMode">value specifying the sharing mode of the image when it will be accessed by multiple queue families.</param> /// <param name="queuesFamillies">list of queue families that will access this image (ignored if sharingMode is not CONCURRENT).</param> public Image(Device device, VkFormat format, VkImageUsageFlags usage, VkMemoryPropertyFlags _memoryPropertyFlags, uint width, uint height, VkImageType type = VkImageType.Image2D, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1, VkImageTiling tiling = VkImageTiling.Optimal, uint mipsLevels = 1, uint layers = 1, uint depth = 1, VkImageCreateFlags createFlags = 0, VkSharingMode sharingMode = VkSharingMode.Exclusive, params uint[] queuesFamillies) : base(device, _memoryPropertyFlags) { info.imageType = type; info.format = format; info.extent.width = width; info.extent.height = height; info.extent.depth = depth; info.mipLevels = mipsLevels; info.arrayLayers = layers; info.samples = samples; info.tiling = tiling; info.usage = usage; info.initialLayout = (tiling == VkImageTiling.Optimal) ? VkImageLayout.Undefined : VkImageLayout.Preinitialized; info.sharingMode = sharingMode; info.flags = createFlags; this.queueFalillies = queuesFamillies; lastKnownLayout = info.initialLayout; Activate(); }
public static extern VkResult GetPhysicalDeviceImageFormatProperties( VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, IntPtr pImageFormatProperties );
public Image(Device device, VkFormat format, VkImageUsageFlags usage, VkMemoryPropertyFlags _memoryPropertyFlags, uint width, uint height, VkImageType type = VkImageType.Image2D, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1, VkImageTiling tiling = VkImageTiling.Optimal, uint mipsLevels = 1, uint layers = 1, uint depth = 1, VkImageCreateFlags createFlags = 0) : base(device, _memoryPropertyFlags) { info.imageType = type; info.format = format; info.extent.width = width; info.extent.height = height; info.extent.depth = depth; info.mipLevels = mipsLevels; info.arrayLayers = layers; info.samples = samples; info.tiling = tiling; info.usage = usage; info.initialLayout = (tiling == VkImageTiling.Optimal) ? VkImageLayout.Undefined : VkImageLayout.Preinitialized; info.sharingMode = VkSharingMode.Exclusive; info.flags = createFlags; Activate(); //DONT OVERRIDE Activate in derived classes!!!! }
public unsafe static Image Create(uint width, uint height, VkImageCreateFlags flags, uint layers, uint levels, VkFormat format, VkSampleCountFlags samples, VkImageUsageFlags usage) { var imageType = height == 1 ? width > 1 ? VkImageType.Image1D : VkImageType.Image2D : VkImageType.Image2D; var createInfo = new VkImageCreateInfo { sType = VkStructureType.ImageCreateInfo, flags = flags, imageType = imageType, format = format, extent = new VkExtent3D(width, height, 1), mipLevels = levels, arrayLayers = layers, samples = samples, tiling = VkImageTiling.Optimal, usage = usage, sharingMode = VkSharingMode.Exclusive, initialLayout = VkImageLayout.Undefined }; Image image = new Image(ref createInfo); return(image); }
internal extern static Result vkGetPhysicalDeviceImageFormatProperties(IntPtr physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, ref VkImageFormatProperties pImageFormatProperties);
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); }
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 VkImageFormatProperties GetImageFormatProperties(VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags) { using (var prop = new Marshalled <VkImageFormatProperties>()) { Instance.Commands.getPhysicalDeviceImageFormatProperties(physicalDevice, format, type, tiling, usage, flags, prop.Address); return(prop.Value); } }