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(); } } }
private unsafe void CreateSwapChain() { var formats = new[] { PixelFormat.B8G8R8A8_UNorm_SRgb, PixelFormat.R8G8B8A8_UNorm_SRgb, PixelFormat.B8G8R8A8_UNorm, PixelFormat.R8G8B8A8_UNorm }; foreach (var format in formats) { var nativeFromat = VulkanConvertExtensions.ConvertPixelFormat(format); vkGetPhysicalDeviceFormatProperties(GraphicsDevice.NativePhysicalDevice, nativeFromat, out var formatProperties); if ((formatProperties.optimalTilingFeatures & VkFormatFeatureFlags.ColorAttachment) != 0) { Description.BackBufferFormat = format; break; } } // Queue // TODO VULKAN: Queue family is needed when creating the Device, so here we can just do a sanity check? var queueNodeIndex = vkGetPhysicalDeviceQueueFamilyProperties(GraphicsDevice.NativePhysicalDevice).ToArray(). Where((properties, index) => (properties.queueFlags & VkQueueFlags.Graphics) != 0 && vkGetPhysicalDeviceSurfaceSupportKHR(GraphicsDevice.NativePhysicalDevice, (uint)index, surface, out var supported) == VkResult.Success && supported). Select((properties, index) => index).First(); // Surface format var backBufferFormat = VulkanConvertExtensions.ConvertPixelFormat(Description.BackBufferFormat); var surfaceFormats = vkGetPhysicalDeviceSurfaceFormatsKHR(GraphicsDevice.NativePhysicalDevice, surface).ToArray(); if ((surfaceFormats.Length != 1 || surfaceFormats[0].format != VkFormat.Undefined) && !surfaceFormats.Any(x => x.format == backBufferFormat)) { backBufferFormat = surfaceFormats[0].format; } // Create swapchain vkGetPhysicalDeviceSurfaceCapabilitiesKHR(GraphicsDevice.NativePhysicalDevice, surface, out var surfaceCapabilities); // Buffer count uint desiredImageCount = Math.Max(surfaceCapabilities.minImageCount, 2); if (surfaceCapabilities.maxImageCount > 0 && desiredImageCount > surfaceCapabilities.maxImageCount) { desiredImageCount = surfaceCapabilities.maxImageCount; } // Transform VkSurfaceTransformFlagsKHR preTransform; if ((surfaceCapabilities.supportedTransforms & VkSurfaceTransformFlagsKHR.Identity) != 0) { preTransform = VkSurfaceTransformFlagsKHR.Identity; } else { preTransform = surfaceCapabilities.currentTransform; } // Find present mode var presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(GraphicsDevice.NativePhysicalDevice, surface); var swapChainPresentMode = VkPresentModeKHR.Fifo; // Always supported foreach (var presentMode in presentModes) { // TODO VULKAN: Handle PresentInterval.Two if (Description.PresentationInterval == PresentInterval.Immediate) { // Prefer mailbox to immediate if (presentMode == VkPresentModeKHR.Immediate) { swapChainPresentMode = VkPresentModeKHR.Immediate; } else if (presentMode == VkPresentModeKHR.Mailbox) { swapChainPresentMode = VkPresentModeKHR.Mailbox; break; } } } // Create swapchain var swapchainCreateInfo = new VkSwapchainCreateInfoKHR { sType = VkStructureType.SwapchainCreateInfoKHR, surface = surface, imageArrayLayers = 1, imageSharingMode = VkSharingMode.Exclusive, imageExtent = new Vortice.Mathematics.Size(Description.BackBufferWidth, Description.BackBufferHeight), imageFormat = backBufferFormat, imageColorSpace = Description.ColorSpace == ColorSpace.Gamma ? VkColorSpaceKHR.SrgbNonLinear : 0, imageUsage = VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferDst | (surfaceCapabilities.supportedUsageFlags & VkImageUsageFlags.TransferSrc), // TODO VULKAN: Use off-screen buffer to emulate presentMode = swapChainPresentMode, compositeAlpha = VkCompositeAlphaFlagsKHR.Opaque, minImageCount = desiredImageCount, preTransform = preTransform, oldSwapchain = swapChain, clipped = true }; vkCreateSwapchainKHR(GraphicsDevice.NativeDevice, &swapchainCreateInfo, null, out var newSwapChain); DestroySwapchain(); swapChain = newSwapChain; CreateBackBuffers(); }