public void BuildMipmaps(CommandBuffer cmd) { VkImageSubresourceRange mipSubRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, info.arrayLayers); SetLayout(cmd, VkImageLayout.Undefined, VkImageLayout.TransferSrcOptimal, mipSubRange, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer); for (int i = 1; i < info.mipLevels; i++) { VkImageBlit imageBlit = new VkImageBlit { srcSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, info.arrayLayers, (uint)i - 1), srcOffsets_1 = new VkOffset3D((int)info.extent.width >> (i - 1), (int)info.extent.height >> (i - 1), 1), dstSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, info.arrayLayers, (uint)i), dstOffsets_1 = new VkOffset3D((int)info.extent.width >> i, (int)info.extent.height >> i, 1) }; mipSubRange.baseMipLevel = (uint)i; SetLayout(cmd, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, mipSubRange, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer); vkCmdBlitImage(cmd.Handle, handle, VkImageLayout.TransferSrcOptimal, handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear); SetLayout(cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.TransferSrcOptimal, mipSubRange, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer); } SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); }
public ImageView(Image image, VkImageSubresourceRange range, VkImageViewType type, VkFormat?format = null, VkComponentMapping?swizzle = null) { Device = image.Device; Image = image; unsafe { var info = new VkImageViewCreateInfo() { SType = VkStructureType.ImageViewCreateInfo, Flags = 0, PNext = IntPtr.Zero, Format = format ?? image.Format, Components = swizzle ?? new VkComponentMapping() { R = VkComponentSwizzle.Identity, G = VkComponentSwizzle.Identity, B = VkComponentSwizzle.Identity, A = VkComponentSwizzle.Identity }, Image = image.Handle, ViewType = type, SubresourceRange = range }; Handle = Device.Handle.CreateImageView(&info, Instance.AllocationCallbacks); } }
private VkImageView[] CreateImageViews() { var imageViews = new VkImageView[SwapchainImages.Length]; for (int i = 0; i < SwapchainImages.Length; i++) { VkImageSubresourceRange range = new VkImageSubresourceRange { aspectMask = VkImageAspectFlags.Color, baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1 }; VkImageViewCreateInfo createInfo = new VkImageViewCreateInfo { sType = VkStructureType.ImageViewCreateInfo, pNext = null, format = SwapchainFormat, viewType = VkImageViewType.Image2D, subresourceRange = range, image = SwapchainImages[i] }; VkImageView imageView; vkCreateImageView(Context.Device, &createInfo, null, out imageView); imageViews[i] = imageView; } return(imageViews); }
public void SetLayout( CommandBuffer cmdbuffer, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange, VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands, uint srcQueueFamilyIndex = Vk.QueueFamilyIgnored, uint dstQueueFamilyIndex = Vk.QueueFamilyIgnored) { VkImageMemoryBarrier imageMemoryBarrier = VkImageMemoryBarrier.New(); imageMemoryBarrier.srcQueueFamilyIndex = srcQueueFamilyIndex; imageMemoryBarrier.dstQueueFamilyIndex = dstQueueFamilyIndex; imageMemoryBarrier.oldLayout = oldImageLayout; imageMemoryBarrier.newLayout = newImageLayout; imageMemoryBarrier.image = handle; imageMemoryBarrier.subresourceRange = subresourceRange; imageMemoryBarrier.srcAccessMask = srcAccessMask; imageMemoryBarrier.dstAccessMask = dstAccessMask; Vk.vkCmdPipelineBarrier( cmdbuffer.Handle, srcStageMask, dstStageMask, 0, 0, IntPtr.Zero, 0, IntPtr.Zero, 1, ref imageMemoryBarrier); }
public void Clear(RawColor color) { VkClearColorValue clearValue = new VkClearColorValue(color.R, color.G, color.B, color.A); VkImageSubresourceRange clearRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1); vkCmdClearColorImage(CommandBuffer, NativeDevice.NativeSwapChain.Images[(int)imageIndex], VkImageLayout.ColorAttachmentOptimal, &clearValue, 1, &clearRange); }
public void Clear(float R, float G, float B, float A = 1.0f) { VkClearColorValue clearValue = new VkClearColorValue(R, G, B); VkImageSubresourceRange clearRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1); vkCmdClearColorImage(CommandBuffer, NativeDevice.NativeSwapChain.Images[(int)imageIndex], VkImageLayout.ColorAttachmentOptimal, &clearValue, 1, &clearRange); }
private void Vkctrl_VulkanRendering(object sender, SharpVulkanWpf.VulkanEventArgs args) { var device = args.Device; var image = vkctrl.AcquireNextImage(); var command = m_commandBuffers[0]; VulkanAPI.vkBeginCommandBuffer(command); VkClearColorValue clearColor = new VkClearColorValue(); clearColor.valF32.R = 0.125f; clearColor.valF32.G = 0.25f; clearColor.valF32.B = (float)(0.5f * Math.Sin(m_frameCount * 0.1) + 0.5); clearColor.valF32.A = 1.0f; VkImageSubresourceRange range = new VkImageSubresourceRange() { baseMipLevel = 0, levelCount = 1, baseArrayLayer = 0, layerCount = 1, aspectMask = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT }; setImageMemoryBarrier(command, VkAccessFlags.VK_ACCESS_MEMORY_READ_BIT, VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT, VkImageLayout.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, image, VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT); VulkanAPI.vkCmdClearColorImage(command, image, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ref clearColor, new VkImageSubresourceRange[] { range }); // Present のためにレイアウト変更 setImageMemoryBarrier(command, VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT, VkAccessFlags.VK_ACCESS_MEMORY_READ_BIT, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VkImageLayout.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, image, VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT); VulkanAPI.vkEndCommandBuffer(command); var submitInfo = new VkSubmitInfo() { commandBuffers = new[] { command } }; VulkanAPI.vkQueueSubmit(args.GraphicsQueue, new VkSubmitInfo[] { submitInfo }, null); vkctrl.SwapBuffers(); m_frameCount++; }
/// <summary> /// Must be called outside of a renderpass scope. /// </summary> /// <param name="depthStencil"></param> /// <param name="clearValue"></param> public void ClearDepthStencil(DepthStencil depthStencil, VkClearDepthStencilValue clearValue) { CheckBegun(); CheckNotInRenderPass(); VkImageSubresourceRange imageRange = new VkImageSubresourceRange(); imageRange.aspectMask = VkImageAspectFlags.Depth; imageRange.levelCount = 1; imageRange.layerCount = 1; vkCmdClearDepthStencilImage(vkCmd, depthStencil.vkImage, VkImageLayout.General, &clearValue, 1, &imageRange); }
/// <summary> /// Must be called outside of a renderpass scope. /// </summary> /// <param name="depthStencil"></param> /// <param name="clearValue"></param> public void ClearColor(Swapchain swapchain, uint swapchainImageIndex, VkClearColorValue clearColor) { CheckBegun(); CheckNotInRenderPass(); VkClearValue clearValue = new VkClearValue(); clearValue.color = clearColor; VkImageSubresourceRange imageRange = new VkImageSubresourceRange(); imageRange.aspectMask = VkImageAspectFlags.Color; imageRange.levelCount = 1; imageRange.layerCount = 1; vkCmdClearColorImage(vkCmd, swapchain.vkSwapchain.Images[swapchainImageIndex], VkImageLayout.General, &clearColor, 1, &imageRange); }
// Fixed sub resource on first mip level and layer public static void setImageLayout( VkCommandBuffer cmdbuffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange(); subresourceRange.aspectMask = aspectMask; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = 1; subresourceRange.layerCount = 1; setImageLayout(cmdbuffer, image, aspectMask, oldImageLayout, newImageLayout, subresourceRange); }
public void SetLayout( CommandBuffer cmdbuffer, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange { aspectMask = aspectMask, baseMipLevel = 0, levelCount = CreateInfo.mipLevels, layerCount = CreateInfo.arrayLayers, }; SetLayout(cmdbuffer, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask); }
public unsafe VkImageViewCreateInfo( VkImage image, VkImageViewType viewType, VkFormat format, VkComponentMapping components, VkImageSubresourceRange subresourceRange, VkImageViewCreateFlags flags = VkImageViewCreateFlags.None, void *pNext = default) { sType = VkStructureType.ImageViewCreateInfo; this.pNext = pNext; this.flags = flags; this.image = image; this.viewType = viewType; this.format = format; this.components = components; this.subresourceRange = subresourceRange; }
public VkImageMemoryBarrier( VkImage image, VkImageSubresourceRange subresourceRange, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageLayout oldLayout, VkImageLayout newLayout, uint srcQueueFamilyIndex = VulkanNative.QueueFamilyIgnored, uint dstQueueFamilyIndex = VulkanNative.QueueFamilyIgnored) { sType = VkStructureType.ImageMemoryBarrier; pNext = null; this.srcAccessMask = srcAccessMask; this.dstAccessMask = dstAccessMask; this.oldLayout = oldLayout; this.newLayout = newLayout; this.srcQueueFamilyIndex = srcQueueFamilyIndex; this.dstQueueFamilyIndex = dstQueueFamilyIndex; this.image = image; this.subresourceRange = subresourceRange; }
public unsafe VkImageMemoryBarrier( VkImage image, VkImageSubresourceRange subresourceRange, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageLayout oldLayout, VkImageLayout newLayout, uint srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, uint dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, void *pNext = default) { sType = VkStructureType.ImageMemoryBarrier; this.pNext = pNext; this.srcAccessMask = srcAccessMask; this.dstAccessMask = dstAccessMask; this.oldLayout = oldLayout; this.newLayout = newLayout; this.srcQueueFamilyIndex = srcQueueFamilyIndex; this.dstQueueFamilyIndex = dstQueueFamilyIndex; this.image = image; this.subresourceRange = subresourceRange; }
public void SetLayout( CommandBuffer cmdbuffer, VkImageAspectFlags aspectMask, VkAccessFlags srcAccessMask, VkAccessFlags dstAccessMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands, uint srcQueueFamilyIndex = Vk.QueueFamilyIgnored, uint dstQueueFamilyIndex = Vk.QueueFamilyIgnored) { VkImageSubresourceRange subresourceRange = new VkImageSubresourceRange { aspectMask = aspectMask, baseMipLevel = 0, levelCount = CreateInfo.mipLevels, layerCount = CreateInfo.arrayLayers, }; SetLayout(cmdbuffer, srcAccessMask, dstAccessMask, oldImageLayout, newImageLayout, subresourceRange, srcStageMask, dstStageMask, srcQueueFamilyIndex, dstQueueFamilyIndex); }
void CreateImageView(ImageViewCreateInfo mInfo) { VkImageViewCreateInfo info = new VkImageViewCreateInfo(); info.sType = VkStructureType.ImageViewCreateInfo; info.image = mInfo.image.Native; info.viewType = mInfo.viewType; info.format = mInfo.format; info.components = mInfo.components; info.subresourceRange = mInfo.subresourceRange; var result = Device.Commands.createImageView(Device.Native, ref info, Device.Instance.AllocationCallbacks, out imageView); if (result != VkResult.Success) { throw new ImageViewException(string.Format("Error creating image view: {0}", result)); } Image = mInfo.image; ViewType = mInfo.viewType; Format = mInfo.format; Components = mInfo.components; SubresourceRange = mInfo.subresourceRange; }
/// <summary> /// Records the an image memory barrier /// </summary> /// <param name="img">image</param> /// <param name="format">format</param> /// <param name="old">old layout</param> /// <param name="new">new layout</param> /// <param name="range">resource range</param> /// <param name="srcQueue">source queue</param> /// <param name="dstQueue">destination queue</param> /// <param name="flags">dependency flags</param> /// <returns>this</returns> public void ImageMemoryBarrier(VkImage img, VkFormat format, VkImageLayout old, VkImageLayout @new, VkImageSubresourceRange range, VkDependencyFlag flags = 0, uint srcQueue = Vulkan.QueueFamilyIgnored, uint dstQueue = Vulkan.QueueFamilyIgnored) { var spec = new VkImageMemoryBarrier() { SType = VkStructureType.ImageMemoryBarrier, PNext = IntPtr.Zero, OldLayout = old, NewLayout = @new, SrcQueueFamilyIndex = srcQueue, DstQueueFamilyIndex = dstQueue, Image = img, SubresourceRange = range, SrcAccessMask = VkAccessFlag.None, DstAccessMask = VkAccessFlag.None }; GetStageInfo(old, out VkPipelineStageFlag srcFlag, out spec.SrcAccessMask); GetStageInfo(@new, out VkPipelineStageFlag dstFlag, out spec.DstAccessMask); PipelineBarrier(srcFlag, dstFlag, flags, default(VkMemoryBarrier), default(VkBufferMemoryBarrier), spec); }
public Image generateCubeMap(Queue staggingQ, CommandPool cmdPool, CBTarget target) { const float deltaPhi = (2.0f * (float)Math.PI) / 180.0f; const float deltaTheta = (0.5f * (float)Math.PI) / 64.0f; VkFormat format = VkFormat.R32g32b32a32Sfloat; uint dim = 64; if (target == CBTarget.PREFILTEREDENV) { format = VkFormat.R16g16b16a16Sfloat; dim = 512; } uint numMips = (uint)Math.Floor(Math.Log(dim, 2)) + 1; Image imgFbOffscreen = new Image(Dev, format, VkImageUsageFlags.TransferSrc | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, dim, dim); imgFbOffscreen.SetName("offscreenfb"); imgFbOffscreen.CreateView(); Image cmap = new Image(Dev, format, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, dim, dim, VkImageType.Image2D, VkSampleCountFlags.SampleCount1, VkImageTiling.Optimal, numMips, 6, 1, VkImageCreateFlags.CubeCompatible); if (target == CBTarget.PREFILTEREDENV) { cmap.SetName("prefilterenvmap"); } else { cmap.SetName("irradianceCube"); } cmap.CreateView(VkImageViewType.Cube, VkImageAspectFlags.Color, 6, 0, numMips); cmap.CreateSampler(VkSamplerAddressMode.ClampToEdge); DescriptorPool dsPool = new DescriptorPool(Dev, 2, new VkDescriptorPoolSize(VkDescriptorType.CombinedImageSampler)); DescriptorSetLayout dsLayout = new DescriptorSetLayout(Dev, new VkDescriptorSetLayoutBinding(0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault(VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false); cfg.Layout = new PipelineLayout(Dev, dsLayout); cfg.Layout.AddPushConstants( new VkPushConstantRange(VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf <Matrix4x4> () + 8)); cfg.RenderPass = new RenderPass(Dev); cfg.RenderPass.AddAttachment(format, VkImageLayout.ColorAttachmentOptimal); cfg.RenderPass.ClearValues.Add(new VkClearValue { color = new VkClearColorValue(0, 0, 0) }); cfg.RenderPass.AddSubpass(new SubPass(VkImageLayout.ColorAttachmentOptimal)); cfg.AddVertexBinding(0, 3 * sizeof(float)); cfg.AddVertexAttributes(0, VkFormat.R32g32b32Sfloat); cfg.AddShader(VkShaderStageFlags.Vertex, "shaders/filtercube.vert.spv"); if (target == CBTarget.PREFILTEREDENV) { cfg.AddShader(VkShaderStageFlags.Fragment, "shaders/prefilterenvmap.frag.spv"); } else { cfg.AddShader(VkShaderStageFlags.Fragment, "shaders/irradiancecube.frag.spv"); } Matrix4x4[] matrices = { // POSITIVE_X Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(90)), // NEGATIVE_X Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(-90)), // POSITIVE_Y Matrix4x4.CreateRotationX(Utils.DegreesToRadians(-90)), // NEGATIVE_Y Matrix4x4.CreateRotationX(Utils.DegreesToRadians(90)), // POSITIVE_Z Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)), // NEGATIVE_Z Matrix4x4.CreateRotationZ(Utils.DegreesToRadians(180)) }; VkImageSubresourceRange subRes = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, numMips, 0, 6); using (GraphicPipeline pl = new GraphicPipeline(cfg)) { DescriptorSet dset = dsPool.Allocate(dsLayout); DescriptorSetWrites dsUpdate = new DescriptorSetWrites(dsLayout); dsUpdate.Write(Dev, dset, cubemap.Descriptor); Dev.WaitIdle(); using (Framebuffer fb = new Framebuffer(pl.RenderPass, dim, dim, imgFbOffscreen)) { CommandBuffer cmd = cmdPool.AllocateCommandBuffer(); cmd.Start(VkCommandBufferUsageFlags.OneTimeSubmit); cmap.SetLayout(cmd, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subRes); float roughness = 0; cmd.SetScissor(dim, dim); cmd.SetViewport((float)(dim), (float)dim); for (int m = 0; m < numMips; m++) { roughness = (float)m / ((float)numMips - 1f); for (int f = 0; f < 6; f++) { pl.RenderPass.Begin(cmd, fb); pl.Bind(cmd); float viewPortSize = (float)Math.Pow(0.5, m) * dim; cmd.SetViewport(viewPortSize, viewPortSize); cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, matrices[f] * Matrix4x4.CreatePerspectiveFieldOfView(Utils.DegreesToRadians(90), 1f, 0.1f, 512f)); if (target == CBTarget.IRRADIANCE) { cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaPhi, (uint)Marshal.SizeOf <Matrix4x4> ()); cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaTheta, (uint)Marshal.SizeOf <Matrix4x4> () + 4); } else { cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, roughness, (uint)Marshal.SizeOf <Matrix4x4> ()); cmd.PushConstant(pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 64u, (uint)Marshal.SizeOf <Matrix4x4> () + 4); } cmd.BindDescriptorSet(pl.Layout, dset); cmd.BindVertexBuffer(vboSkybox); cmd.Draw(36); pl.RenderPass.End(cmd); imgFbOffscreen.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal); VkImageCopy region = new VkImageCopy(); region.srcSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, 1); region.dstSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, 1, (uint)m, (uint)f); region.extent = new VkExtent3D { width = (uint)viewPortSize, height = (uint)viewPortSize, depth = 1 }; Vk.vkCmdCopyImage(cmd.Handle, imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal, cmap.Handle, VkImageLayout.TransferDstOptimal, 1, region.Pin()); region.Unpin(); imgFbOffscreen.SetLayout(cmd, VkImageAspectFlags.Color, VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal); } } cmap.SetLayout(cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, subRes); cmd.End(); staggingQ.Submit(cmd); staggingQ.WaitIdle(); cmd.Free(); } } cmap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; dsLayout.Dispose(); imgFbOffscreen.Dispose(); dsPool.Dispose(); return(cmap); }
// Setup the offscreen framebuffer for rendering the blurred scene // The color attachment of this framebuffer will then be used to sample frame in the fragment shader of the final pass void prepareOffscreen() { offscreenPass.width = FB_DIM; offscreenPass.height = FB_DIM; // Find a suitable depth format VkFormat fbDepthFormat; VkBool32 validDepthFormat = Tools.getSupportedDepthFormat(physicalDevice, &fbDepthFormat); Debug.Assert(validDepthFormat); // Color attachment var imageInfo = VkImageCreateInfo.Alloc(); imageInfo->imageType = VkImageType._2d;// VK_IMAGE_TYPE_2D; imageInfo->format = FB_COLOR_FORMAT; imageInfo->extent.width = (uint)offscreenPass.width; imageInfo->extent.height = (uint)offscreenPass.height; imageInfo->extent.depth = 1; imageInfo->mipLevels = 1; imageInfo->arrayLayers = 1; imageInfo->samples = VkSampleCountFlagBits._1; // VK_SAMPLE_COUNT_1_BIT; imageInfo->tiling = VkImageTiling.Optimal; // VK_IMAGE_TILING_OPTIMAL; // We will sample directly from the color attachment imageInfo->usage = VkImageUsageFlagBits.ColorAttachment | VkImageUsageFlagBits.Sampled; //VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; var memAlloc = VkMemoryAllocateInfo.Alloc(); VkMemoryRequirements memReqs; { VkImage image; vkCreateImage(device, imageInfo, null, &image); offscreenPass.colorAttachment.image = image; } vkGetImageMemoryRequirements(device, offscreenPass.colorAttachment.image, &memReqs); memAlloc->allocationSize = memReqs.size; memAlloc->memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); //VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); { VkDeviceMemory memory; vkAllocateMemory(device, memAlloc, null, &memory); offscreenPass.colorAttachment.mem = memory; } vkBindImageMemory(device, offscreenPass.colorAttachment.image, offscreenPass.colorAttachment.mem, 0); var colorViewInfo = VkImageViewCreateInfo.Alloc(); colorViewInfo->viewType = VkImageViewType._2d;// VK_IMAGE_VIEW_TYPE_2D; colorViewInfo->format = FB_COLOR_FORMAT; colorViewInfo->subresourceRange = new VkImageSubresourceRange(); colorViewInfo->subresourceRange.aspectMask = VkImageAspectFlagBits.Color;// VK_IMAGE_ASPECT_COLOR_BIT; colorViewInfo->subresourceRange.baseMipLevel = 0; colorViewInfo->subresourceRange.levelCount = 1; colorViewInfo->subresourceRange.baseArrayLayer = 0; colorViewInfo->subresourceRange.layerCount = 1; colorViewInfo->image = offscreenPass.colorAttachment.image; { VkImageView view; vkCreateImageView(device, colorViewInfo, null, &view); offscreenPass.colorAttachment.view = view; } // Create sampler to sample from the attachment in the fragment shader var samplerInfo = VkSamplerCreateInfo.Alloc(); samplerInfo->magFilter = VkFilter.Linear; // VK_FILTER_LINEAR; samplerInfo->minFilter = VkFilter.Linear; // VK_FILTER_LINEAR; samplerInfo->mipmapMode = VkSamplerMipmapMode.Linear; // VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerInfo->addressModeU = VkSamplerAddressMode.ClampToEdge; // VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerInfo->addressModeV = samplerInfo->addressModeU; samplerInfo->addressModeW = samplerInfo->addressModeU; samplerInfo->mipLodBias = 0.0f; samplerInfo->maxAnisotropy = 0; samplerInfo->minLod = 0.0f; samplerInfo->maxLod = 1.0f; samplerInfo->borderColor = VkBorderColor.FloatOpaqueWhite;// VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; { VkSampler sampler; vkCreateSampler(device, samplerInfo, null, &sampler); offscreenPass.sampler = sampler; } // Depth stencil attachment imageInfo->format = fbDepthFormat; imageInfo->usage = VkImageUsageFlagBits.DepthStencilAttachment;// VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; { VkImage image; vkCreateImage(device, imageInfo, null, &image); offscreenPass.depthAttachment.image = image; } vkGetImageMemoryRequirements(device, offscreenPass.depthAttachment.image, &memReqs); memAlloc->allocationSize = memReqs.size; memAlloc->memoryTypeIndex = vulkanDevice.getMemoryType(memReqs.memoryTypeBits, VkMemoryPropertyFlagBits.DeviceLocal); //VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); { VkDeviceMemory memory; vkAllocateMemory(device, memAlloc, null, &memory); offscreenPass.depthAttachment.mem = memory; } vkBindImageMemory(device, offscreenPass.depthAttachment.image, offscreenPass.depthAttachment.mem, 0); var depthViewInfo = VkImageViewCreateInfo.Alloc(); depthViewInfo->viewType = VkImageViewType._2d;// VK_IMAGE_VIEW_TYPE_2D; depthViewInfo->format = fbDepthFormat; depthViewInfo->flags = 0; depthViewInfo->subresourceRange = new VkImageSubresourceRange(); depthViewInfo->subresourceRange.aspectMask = VkImageAspectFlagBits.Depth | VkImageAspectFlagBits.Stencil; //VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; depthViewInfo->subresourceRange.baseMipLevel = 0; depthViewInfo->subresourceRange.levelCount = 1; depthViewInfo->subresourceRange.baseArrayLayer = 0; depthViewInfo->subresourceRange.layerCount = 1; depthViewInfo->image = offscreenPass.depthAttachment.image; { VkImageView view; vkCreateImageView(device, depthViewInfo, null, &view); offscreenPass.depthAttachment.view = view; } // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering var attchmentDescriptions = new VkAttachmentDescription[2]; // Color attachment attchmentDescriptions[0].format = FB_COLOR_FORMAT; attchmentDescriptions[0].samples = VkSampleCountFlagBits._1; // VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[0].loadOp = VkAttachmentLoadOp.Clear; // VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[0].storeOp = VkAttachmentStoreOp.Store; // VK_ATTACHMENT_STORE_OP_STORE; attchmentDescriptions[0].stencilLoadOp = VkAttachmentLoadOp.DontCare; // VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[0].stencilStoreOp = VkAttachmentStoreOp.DontCare; // VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[0].initialLayout = VkImageLayout.Undefined; // VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[0].finalLayout = VkImageLayout.ShaderReadOnlyOptimal; // VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Depth attachment attchmentDescriptions[1].format = fbDepthFormat; attchmentDescriptions[1].samples = VkSampleCountFlagBits._1; // VK_SAMPLE_COUNT_1_BIT; attchmentDescriptions[1].loadOp = VkAttachmentLoadOp.Clear; // VK_ATTACHMENT_LOAD_OP_CLEAR; attchmentDescriptions[1].storeOp = VkAttachmentStoreOp.DontCare; // VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].stencilLoadOp = VkAttachmentLoadOp.DontCare; // VK_ATTACHMENT_LOAD_OP_DONT_CARE; attchmentDescriptions[1].stencilStoreOp = VkAttachmentStoreOp.DontCare; // VK_ATTACHMENT_STORE_OP_DONT_CARE; attchmentDescriptions[1].initialLayout = VkImageLayout.Undefined; // VK_IMAGE_LAYOUT_UNDEFINED; attchmentDescriptions[1].finalLayout = VkImageLayout.DepthStencilAttachmentOptimal; // VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; var colorReference = new VkAttachmentReference { attachment = 0, layout = VkImageLayout.ColorAttachmentOptimal// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; var depthReference = new VkAttachmentReference { attachment = 1, layout = VkImageLayout.DepthStencilAttachmentOptimal// VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; VkSubpassDescription subpassDescription = new VkSubpassDescription(); subpassDescription.pipelineBindPoint = VkPipelineBindPoint.Graphics;// VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescription.colorResolveAttachments.SetColorAttachments(colorReference); subpassDescription.pDepthStencilAttachment = &depthReference; // Use subpass dependencies for layout transitions var dependencies = new VkSubpassDependency[2]; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].dstSubpass = 0; dependencies[0].srcStageMask = VkPipelineStageFlagBits.BottomOfPipe; // VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].dstStageMask = VkPipelineStageFlagBits.ColorAttachmentOutput; // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].srcAccessMask = VkAccessFlagBits.MemoryRead; // VK_ACCESS_MEMORY_READ_BIT; dependencies[0].dstAccessMask = VkAccessFlagBits.ColorAttachmentRead | VkAccessFlagBits.ColorAttachmentWrite; //VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dependencyFlags = VkDependencyFlagBits.ByRegion;// VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].srcSubpass = 0; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].srcStageMask = VkPipelineStageFlagBits.ColorAttachmentOutput; // VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].dstStageMask = VkPipelineStageFlagBits.BottomOfPipe; // VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].srcAccessMask = VkAccessFlagBits.ColorAttachmentRead | VkAccessFlagBits.ColorAttachmentWrite; //VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].dstAccessMask = VkAccessFlagBits.MemoryRead; // VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dependencyFlags = VkDependencyFlagBits.ByRegion; // VK_DEPENDENCY_BY_REGION_BIT; // Create the actual renderpass var renderPassInfo = VkRenderPassCreateInfo.Alloc(); renderPassInfo->attachments = attchmentDescriptions; renderPassInfo->subpasses = subpassDescription; renderPassInfo->dependencies = dependencies; { VkRenderPass renderPass; vkCreateRenderPass(device, renderPassInfo, null, &renderPass); offscreenPass.renderPass = renderPass; } var attachments = new VkImageView[] { offscreenPass.colorAttachment.view, offscreenPass.depthAttachment.view }; var framebufferInfo = VkFramebufferCreateInfo.Alloc(); framebufferInfo->renderPass = offscreenPass.renderPass; framebufferInfo->attachments = attachments; framebufferInfo->width = (uint)offscreenPass.width; framebufferInfo->height = (uint)offscreenPass.height; framebufferInfo->layers = 1; { VkFramebuffer framebuffer; vkCreateFramebuffer(device, framebufferInfo, null, &framebuffer); offscreenPass.framebuffer = framebuffer; } // Fill a descriptor for later use in a descriptor set offscreenPass.descriptorImage.imageLayout = VkImageLayout.ShaderReadOnlyOptimal;// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; offscreenPass.descriptorImage.imageView = offscreenPass.colorAttachment.view; offscreenPass.descriptorImage.sampler = offscreenPass.sampler; }
// Create an image memory barrier for changing the layout of // an image and put it into an active command buffer // See chapter 11.4 "Image Layout" for details public static void setImageLayout( VkCommandBuffer cmdbuffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange, VkPipelineStageFlags srcStageMask = VkPipelineStageFlags.AllCommands, VkPipelineStageFlags dstStageMask = VkPipelineStageFlags.AllCommands) { // Create an image barrier object VkImageMemoryBarrier imageMemoryBarrier = Initializers.imageMemoryBarrier(); imageMemoryBarrier.oldLayout = oldImageLayout; imageMemoryBarrier.newLayout = newImageLayout; imageMemoryBarrier.image = image; imageMemoryBarrier.subresourceRange = subresourceRange; // Source layouts (old) // Source access mask controls actions that have to be finished on the old layout // before it will be transitioned to the new layout switch (oldImageLayout) { case VkImageLayout.Undefined: // Image layout is undefined (or does not matter) // Only valid as initial layout // No flags required, listed only for completeness imageMemoryBarrier.srcAccessMask = 0; break; case VkImageLayout.Preinitialized: // Image is preinitialized // Only valid as initial layout for linear images, preserves memory contents // Make sure host writes have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite; break; case VkImageLayout.ColorAttachmentOptimal: // Image is a color attachment // Make sure any writes to the color buffer have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.ColorAttachmentWrite; break; case VkImageLayout.DepthStencilAttachmentOptimal: // Image is a depth/stencil attachment // Make sure any writes to the depth/stencil buffer have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.DepthStencilAttachmentWrite; break; case VkImageLayout.TransferSrcOptimal: // Image is a transfer source // Make sure any reads from the image have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead; break; case VkImageLayout.TransferDstOptimal: // Image is a transfer destination // Make sure any writes to the image have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferWrite; break; case VkImageLayout.ShaderReadOnlyOptimal: // Image is read by a shader // Make sure any shader reads from the image have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.ShaderRead; break; } // Target layouts (new) // Destination access mask controls the dependency for the new image layout switch (newImageLayout) { case VkImageLayout.TransferDstOptimal: // Image will be used as a transfer destination // Make sure any writes to the image have been finished imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferWrite; break; case VkImageLayout.TransferSrcOptimal: // Image will be used as a transfer source // Make sure any reads from and writes to the image have been finished imageMemoryBarrier.srcAccessMask = imageMemoryBarrier.srcAccessMask | VkAccessFlags.TransferRead; imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferRead; break; case VkImageLayout.ColorAttachmentOptimal: // Image will be used as a color attachment // Make sure any writes to the color buffer have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferRead; imageMemoryBarrier.dstAccessMask = VkAccessFlags.ColorAttachmentWrite; break; case VkImageLayout.DepthStencilAttachmentOptimal: // Image layout will be used as a depth/stencil attachment // Make sure any writes to depth/stencil buffer have been finished imageMemoryBarrier.dstAccessMask = imageMemoryBarrier.dstAccessMask | VkAccessFlags.DepthStencilAttachmentWrite; break; case VkImageLayout.ShaderReadOnlyOptimal: // Image will be read in a shader (sampler, input attachment) // Make sure any writes to the image have been finished if (imageMemoryBarrier.srcAccessMask == 0) { imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite | VkAccessFlags.TransferWrite; } imageMemoryBarrier.dstAccessMask = VkAccessFlags.ShaderRead; break; } // Put barrier inside setup command buffer vkCmdPipelineBarrier( cmdbuffer, srcStageMask, dstStageMask, 0, 0, null, 0, null, 1, &imageMemoryBarrier); }
/** * 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 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 void InitializeFromImpl(DataBox[] dataBoxes = null) { NativeFormat = VulkanConvertExtensions.ConvertPixelFormat(ViewFormat); HasStencil = IsStencilFormat(ViewFormat); NativeImageAspect = IsDepthStencil ? VkImageAspectFlags.Depth : VkImageAspectFlags.Color; if (HasStencil) { NativeImageAspect |= VkImageAspectFlags.Stencil; } var arraySlice = ArraySlice; var mipLevel = MipLevel; GetViewSliceBounds(ViewType, ref arraySlice, ref mipLevel, out var arrayOrDepthCount, out var mipCount); var arrayCount = Dimension == TextureDimension.Texture3D ? 1 : arrayOrDepthCount; NativeResourceRange = new VkImageSubresourceRange(NativeImageAspect, (uint)mipLevel, (uint)mipCount, (uint)arraySlice, (uint)arrayCount); // For depth-stencil formats, automatically fall back to a supported one if (IsDepthStencil && HasStencil) { NativeFormat = GetFallbackDepthStencilFormat(GraphicsDevice, NativeFormat); } if (Usage == GraphicsResourceUsage.Staging) { if (NativeImage != VkImage.Null) { throw new InvalidOperationException(); } if (isNotOwningResources) { throw new InvalidOperationException(); } NativeAccessMask = VkAccessFlags.HostRead | VkAccessFlags.HostWrite; NativePipelineStageMask = VkPipelineStageFlags.Host; if (ParentTexture != null) { // Create only a view NativeBuffer = ParentTexture.NativeBuffer; NativeMemory = ParentTexture.NativeMemory; } else { CreateBuffer(); if (dataBoxes != null && dataBoxes.Length > 0) { InitializeData(dataBoxes); } } } else { if (NativeImage != VkImage.Null) { throw new InvalidOperationException(); } NativeLayout = IsRenderTarget ? VkImageLayout.ColorAttachmentOptimal : IsDepthStencil ? VkImageLayout.DepthStencilAttachmentOptimal : IsShaderResource ? VkImageLayout.ShaderReadOnlyOptimal : VkImageLayout.General; if (NativeLayout == VkImageLayout.TransferDstOptimal) { NativeAccessMask = VkAccessFlags.TransferRead; } if (NativeLayout == VkImageLayout.ColorAttachmentOptimal) { NativeAccessMask = VkAccessFlags.ColorAttachmentWrite; } if (NativeLayout == VkImageLayout.DepthStencilAttachmentOptimal) { NativeAccessMask = VkAccessFlags.DepthStencilAttachmentWrite; } if (NativeLayout == VkImageLayout.ShaderReadOnlyOptimal) { NativeAccessMask = VkAccessFlags.ShaderRead | VkAccessFlags.InputAttachmentRead; } NativePipelineStageMask = IsRenderTarget ? VkPipelineStageFlags.ColorAttachmentOutput : IsDepthStencil ? VkPipelineStageFlags.ColorAttachmentOutput | VkPipelineStageFlags.EarlyFragmentTests | VkPipelineStageFlags.LateFragmentTests : IsShaderResource ? VkPipelineStageFlags.VertexInput | VkPipelineStageFlags.FragmentShader : VkPipelineStageFlags.None; if (ParentTexture != null) { // Create only a view NativeImage = ParentTexture.NativeImage; NativeMemory = ParentTexture.NativeMemory; } else { if (!isNotOwningResources) { CreateImage(); InitializeData(dataBoxes); } } if (!isNotOwningResources) { NativeImageView = GetImageView(ViewType, ArraySlice, MipLevel); NativeColorAttachmentView = GetColorAttachmentView(ViewType, ArraySlice, MipLevel); NativeDepthStencilView = GetDepthStencilView(); } } }
/// <summary> /// Puts all pending transfers in the transfer queue /// </summary> /// <returns>true if anything was queued</returns> public bool Flush() { var flushed = 0; Interlocked.Increment(ref _activeFlushers); CommandBufferPooledExclusiveUse buffer = null; try { CommandBufferRecorder recorder = default; while (_pendingFlush.TryDequeue(out var temp)) { if (buffer == null) { buffer = _cmdBufferPool.Borrow(); recorder = buffer.RecordCommands(VkCommandBufferUsageFlag.OneTimeSubmit); } flushed++; buffer.SubmissionFinished += temp.Finished; switch (temp) { case PendingFlushBuffer pendingBuffer: { _log.Trace( $"Transferring {pendingBuffer.Size} bytes to {pendingBuffer.Destination}\t({_pendingFlush.Count} remain)"); var target = (IBindableBuffer)pendingBuffer; if (temp.Arguments.SrcQueueFamily != _transferQueue.FamilyIndex) { recorder.BufferMemoryBarrier(target.BindingHandle, VkAccessFlag.AllExceptExt, pendingBuffer.Arguments.SrcQueueFamily, VkAccessFlag.AllExceptExt, _transferQueue.FamilyIndex, VkPipelineStageFlag.AllCommands, VkPipelineStageFlag.AllCommands, VkDependencyFlag.None, target.Offset, target.Size); } recorder.CopyBuffer(pendingBuffer.Handle.BackingBuffer, target.BindingHandle, new VkBufferCopy { SrcOffset = pendingBuffer.Handle.Offset, DstOffset = target.Offset, Size = target.Size }); if (temp.Arguments.DstQueueFamily != _transferQueue.FamilyIndex) { recorder.BufferMemoryBarrier(pendingBuffer.Destination.BindingHandle, VkAccessFlag.AllExceptExt, _transferQueue.FamilyIndex, VkAccessFlag.AllExceptExt, temp.Arguments.DstQueueFamily, VkPipelineStageFlag.AllCommands, VkPipelineStageFlag.AllCommands, VkDependencyFlag.None, target.Offset, target.Size); } if (temp.Arguments.SetEvent != null) { recorder.Handle.SetEvent(temp.Arguments.SetEvent.Handle, VkPipelineStageFlag.AllCommands); } break; } case PendingFlushImage pendingImage: { _log.Trace( $"Transferring {pendingImage.CopyData.BufferRowLength * pendingImage.CopyData.ImageExtent.Height} pixels to {pendingImage.Destination}\t({_pendingFlush.Count} remain)"); var range = new VkImageSubresourceRange { AspectMask = pendingImage.CopyData.ImageSubresource.AspectMask, BaseMipLevel = pendingImage.CopyData.ImageSubresource.MipLevel, LevelCount = 1, BaseArrayLayer = pendingImage.CopyData.ImageSubresource.BaseArrayLayer, LayerCount = pendingImage.CopyData.ImageSubresource.LayerCount }; if (temp.Arguments.SrcQueueFamily != _transferQueue.FamilyIndex) { recorder.ImageMemoryBarrier(pendingImage.Destination.Handle, pendingImage.Destination.Format, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, range, VkDependencyFlag.None, pendingImage.Arguments.SrcQueueFamily, _transferQueue.FamilyIndex); } recorder.CopyBufferToImage(pendingImage.Handle.BackingBuffer.Handle, pendingImage.Destination.Handle, new VkBufferImageCopy { BufferOffset = pendingImage.Handle.Offset + pendingImage.CopyData.BufferOffset, BufferRowLength = pendingImage.CopyData.BufferRowLength, BufferImageHeight = pendingImage.CopyData.BufferImageHeight, ImageSubresource = pendingImage.CopyData.ImageSubresource, ImageOffset = pendingImage.CopyData.ImageOffset, ImageExtent = pendingImage.CopyData.ImageExtent }); if (temp.Arguments.DstQueueFamily != _transferQueue.FamilyIndex) { recorder.ImageMemoryBarrier(pendingImage.Destination.Handle, pendingImage.Destination.Format, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, range, VkDependencyFlag.None, _transferQueue.FamilyIndex, temp.Arguments.DstQueueFamily); } if (temp.Arguments.SetEvent != null) { recorder.Handle.SetEvent(temp.Arguments.SetEvent.Handle, VkPipelineStageFlag.AllCommands); } break; } default: throw new InvalidOperationException( $"Can't handle pending data {temp.GetType().Name}"); } } if (buffer != null) { recorder.Commit(); _transferQueue.Submit(buffer); } } finally { if (buffer != null) { _cmdBufferPool.Return(buffer); } Interlocked.Decrement(ref _activeFlushers); } return(flushed > 0); }
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); }
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)); }
// Create an image memory barrier for changing the layout of // an image and put it into an active command buffer void setImageLayout( VkCommandBuffer cmdBuffer, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkImageSubresourceRange subresourceRange) { // Create an image barrier object VkImageMemoryBarrier imageMemoryBarrier = Initializers.imageMemoryBarrier();; imageMemoryBarrier.oldLayout = oldImageLayout; imageMemoryBarrier.newLayout = newImageLayout; imageMemoryBarrier.image = image; imageMemoryBarrier.subresourceRange = subresourceRange; // Only sets masks for layouts used in this example // For a more complete version that can be used with other layouts see vks::tools::setImageLayout // Source layouts (old) switch (oldImageLayout) { case VkImageLayout.Undefined: // Only valid as initial layout, memory contents are not preserved // Can be accessed directly, no source dependency required imageMemoryBarrier.srcAccessMask = 0; break; case VkImageLayout.Preinitialized: // Only valid as initial layout for linear images, preserves memory contents // Make sure host writes to the image have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.HostWrite; break; case VkImageLayout.TransferDstOptimal: // Old layout is transfer destination // Make sure any writes to the image have been finished imageMemoryBarrier.srcAccessMask = VkAccessFlags.TransferWrite; break; } // Target layouts (new) switch (newImageLayout) { case VkImageLayout.TransferSrcOptimal: // Transfer source (copy, blit) // Make sure any reads from the image have been finished imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferRead; break; case VkImageLayout.TransferDstOptimal: // Transfer destination (copy, blit) // Make sure any writes to the image have been finished imageMemoryBarrier.dstAccessMask = VkAccessFlags.TransferWrite; break; case VkImageLayout.ShaderReadOnlyOptimal: // Shader read (sampler, input attachment) imageMemoryBarrier.dstAccessMask = VkAccessFlags.ShaderRead; break; } // Put barrier on top of pipeline VkPipelineStageFlags srcStageFlags = VkPipelineStageFlags.TopOfPipe; VkPipelineStageFlags destStageFlags = VkPipelineStageFlags.TopOfPipe; // Put barrier inside setup command buffer vkCmdPipelineBarrier( cmdBuffer, srcStageFlags, destStageFlags, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier); }
public unsafe VkImageMemoryBarrier( VkImage image, VkImageLayout oldLayout, VkImageLayout newLayout, VkImageSubresourceRange subresourceRange, void *pNext = default) { sType = VkStructureType.ImageMemoryBarrier; this.pNext = pNext; srcAccessMask = 0; dstAccessMask = 0; // Source layouts (old) // Source access mask controls actions that have to be finished on the old layout // before it will be transitioned to the new layout switch (oldLayout) { case VkImageLayout.Undefined: // Image layout is undefined (or does not matter) // Only valid as initial layout // No flags required, listed only for completeness srcAccessMask = 0; break; case VkImageLayout.Preinitialized: // Image is preinitialized // Only valid as initial layout for linear images, preserves memory contents // Make sure host writes have been finished srcAccessMask = VkAccessFlags.HostWrite; break; case VkImageLayout.ColorAttachmentOptimal: // Image is a color attachment // Make sure any writes to the color buffer have been finished srcAccessMask = VkAccessFlags.ColorAttachmentWrite; break; case VkImageLayout.DepthStencilAttachmentOptimal: // Image is a depth/stencil attachment // Make sure any writes to the depth/stencil buffer have been finished srcAccessMask = VkAccessFlags.DepthStencilAttachmentWrite; break; case VkImageLayout.TransferSrcOptimal: // Image is a transfer source // Make sure any reads from the image have been finished srcAccessMask = VkAccessFlags.TransferRead; break; case VkImageLayout.TransferDstOptimal: // Image is a transfer destination // Make sure any writes to the image have been finished srcAccessMask = VkAccessFlags.TransferWrite; break; case VkImageLayout.ShaderReadOnlyOptimal: // Image is read by a shader // Make sure any shader reads from the image have been finished srcAccessMask = VkAccessFlags.ShaderWrite; break; default: // Other source layouts aren't handled (yet) break; } // Target layouts (new) // Destination access mask controls the dependency for the new image layout switch (newLayout) { case VkImageLayout.TransferDstOptimal: // Image will be used as a transfer destination // Make sure any writes to the image have been finished dstAccessMask = VkAccessFlags.TransferWrite; break; case VkImageLayout.TransferSrcOptimal: // Image will be used as a transfer source // Make sure any reads from the image have been finished dstAccessMask = VkAccessFlags.TransferRead; break; case VkImageLayout.ColorAttachmentOptimal: // Image will be used as a color attachment // Make sure any writes to the color buffer have been finished dstAccessMask = VkAccessFlags.ColorAttachmentWrite; break; case VkImageLayout.DepthStencilAttachmentOptimal: // Image layout will be used as a depth/stencil attachment // Make sure any writes to depth/stencil buffer have been finished dstAccessMask |= VkAccessFlags.DepthStencilAttachmentWrite; break; case VkImageLayout.ShaderReadOnlyOptimal: // Image will be read in a shader (sampler, input attachment) // Make sure any writes to the image have been finished if (srcAccessMask == 0) { srcAccessMask = VkAccessFlags.HostWrite | VkAccessFlags.TransferWrite; } dstAccessMask = VkAccessFlags.ShaderRead; break; default: // Other source layouts aren't handled (yet) break; } this.oldLayout = oldLayout; this.newLayout = newLayout; srcQueueFamilyIndex = Vulkan.QueueFamilyIgnored; dstQueueFamilyIndex = Vulkan.QueueFamilyIgnored; this.image = image; this.subresourceRange = subresourceRange; }
///// <summary> ///// build texture array ///// </summary> ///// <returns>The images.</returns> ///// <param name="textureSize">Uniformized Texture size for all images</param> public void BuildTexArray (ref Image texArray, uint firstImg = 0) { int texDim = (int)texArray.CreateInfo.extent.width; PrimaryCommandBuffer cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); texArray.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.Transfer); transferQ.EndSubmitAndWait (cmd, true); VkImageBlit imageBlit = new VkImageBlit { srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, 0), dstOffsets_1 = new VkOffset3D (texDim, texDim, 1) }; for (int l = 0; l < gltf.Images.Length; l++) { GL.Image img = gltf.Images[l]; Image vkimg = null; if (img.BufferView != null) {//load image from gltf buffer view GL.BufferView bv = gltf.BufferViews[(int)img.BufferView]; ensureBufferIsLoaded (bv.Buffer); vkimg = Image.Load (dev, loadedBuffers[bv.Buffer].Slice (bv.ByteOffset), (ulong)bv.ByteLength, VkImageUsageFlags.TransferSrc); } else if (img.Uri.StartsWith ("data:", StringComparison.Ordinal)) {//load base64 encoded image Debug.WriteLine ("loading embedded image {0} : {1}", img.Name, img.MimeType); vkimg = Image.Load (dev, glTFLoader.loadDataUri (img), VkImageUsageFlags.TransferSrc); } else { Debug.WriteLine ("loading image {0} : {1} : {2}", img.Name, img.MimeType, img.Uri);//load image from file path in uri vkimg = Image.Load (dev, Path.Combine (baseDirectory, img.Uri), VkImageUsageFlags.TransferSrc); } imageBlit.srcOffsets_1 = new VkOffset3D ((int)vkimg.CreateInfo.extent.width, (int)vkimg.CreateInfo.extent.height, 1); imageBlit.dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, 0, (uint)l + firstImg); cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); vkimg.SetLayout (cmd, VkImageAspectFlags.Color, VkAccessFlags.HostWrite, VkAccessFlags.TransferRead, VkImageLayout.Undefined, VkImageLayout.TransferSrcOptimal, VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer); Vk.vkCmdBlitImage (cmd.Handle, vkimg.Handle, VkImageLayout.TransferSrcOptimal, texArray.Handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear); transferQ.EndSubmitAndWait (cmd, true); vkimg.Dispose (); } cmd = cmdPool.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); uint imgCount = (uint)gltf.Images.Length; VkImageSubresourceRange mipSubRange = new VkImageSubresourceRange (VkImageAspectFlags.Color, 0, 1, firstImg, imgCount); for (int i = 1; i < texArray.CreateInfo.mipLevels; i++) { imageBlit = new VkImageBlit { srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, imgCount, (uint)i - 1, firstImg), srcOffsets_1 = new VkOffset3D ((int)texDim >> (i - 1), (int)texDim >> (i - 1), 1), dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, imgCount, (uint)i, firstImg), dstOffsets_1 = new VkOffset3D ((int)texDim >> i, (int)texDim >> i, 1) }; texArray.SetLayout (cmd, VkAccessFlags.TransferWrite, VkAccessFlags.TransferRead, VkImageLayout.TransferDstOptimal, VkImageLayout.TransferSrcOptimal, mipSubRange, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.Transfer); Vk.vkCmdBlitImage (cmd.Handle, texArray.Handle, VkImageLayout.TransferSrcOptimal, texArray.Handle, VkImageLayout.TransferDstOptimal, 1, ref imageBlit, VkFilter.Linear); texArray.SetLayout (cmd, VkImageLayout.TransferSrcOptimal, VkImageLayout.ShaderReadOnlyOptimal, mipSubRange, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); mipSubRange.baseMipLevel = (uint)i; } mipSubRange.baseMipLevel = texArray.CreateInfo.mipLevels - 1; texArray.SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, mipSubRange, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); cmd.End (); transferQ.Submit (cmd); transferQ.WaitIdle (); cmd.Free (); }
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 };