/// <summary> /// Construct a Graphics object belonging to a window /// </summary> /// <param name="window"></param> public Graphics(WyvernWindow window) { // Argument checks if (window is null) { throw new ArgumentNullException(nameof(window)); } // Initialize the static part of Graphics if necessary InitializeStatic(); // Store window Window = window; // Create window surface { AllocationCallbacks?allocationCallbacks = null; Surface = new SurfaceKhr( Instance, ref allocationCallbacks, VkGLFW3.VkGlfw.CreateWindowSurface(Instance.Handle, Window, IntPtr.Zero) ); Surface.PrintDebug(); } // Choose a physical device { PhysicalDevice = Instance.EnumeratePhysicalDevices() .Where(e => GetDeviceMeetsRequirements(e, Surface)) .OrderByDescending(GetDeviceScore) .FirstOrDefault(); if (PhysicalDevice is null) { throw new InvalidOperationException("No physical device found that meets the requirements for the application"); } MemoryProperties = PhysicalDevice.GetMemoryProperties(); } // Create default queue families { ComputeQueueFamily = new QueueFamily( $"{Name}'s {nameof(ComputeQueueFamily)}", this, QueueFamily.QueueType.Compute, 2, true, ComputeCommandPoolFlags ); GraphicsQueueFamily = new QueueFamily( $"{Name}'s {nameof(GraphicsQueueFamily)}", this, QueueFamily.QueueType.Graphics, 2, true, GraphicsCommandPoolFlags ); TransferQueueFamily = new QueueFamily( $"{Name}'s {nameof(TransferQueueFamily)}", this, QueueFamily.QueueType.Transfer, 2, true, TransferCommandPoolFlags ); PresentQueueFamily = new QueueFamily( $"{Name}'s {nameof(PresentQueueFamily)}", this, QueueFamily.QueueType.Present, 1, false ); } // Create a logical device { // Generate queue create info structs var queueCreateInfos = QueueFamilies.Select(queueFamily => { // Generate queue priorities var priorities = new float[queueFamily.Count]; for (var i = 0; i < priorities.Length; i++) { priorities[i] = 1f - (i / (float)(priorities.Length - 1)); } // Create create info var createInfo = new DeviceQueueCreateInfo() { QueueFamilyIndex = queueFamily.Index, QueueCount = queueFamily.Count, QueuePriorities = priorities }; return(createInfo); }); // Merge multiple queue families' queue create infos { var alreadyHave = new List <int>(); var uniqueCreateInfos = new List <DeviceQueueCreateInfo>(); foreach (var createInfo in queueCreateInfos.OrderByDescending(e => e.QueueCount)) { if (!alreadyHave.Contains(createInfo.QueueFamilyIndex)) { alreadyHave.Add(createInfo.QueueFamilyIndex); uniqueCreateInfos.Add(createInfo); } } queueCreateInfos = uniqueCreateInfos; foreach (var createInfo in queueCreateInfos) { createInfo.PrintDebug(); } } // Create device Device = PhysicalDevice.CreateDevice(new DeviceCreateInfo() { EnabledExtensionNames = EnabledDeviceExtensions.ToArray(), QueueCreateInfos = queueCreateInfos.ToArray(), EnabledFeatures = EnabledPhysicalDeviceFeatures }); // Set the queues in the queue families, using those created with the device foreach (var family in QueueFamilies) { var queues = new Queue[family.Count]; for (var i = 0; i < queues.Length; i++) { queues[i] = Device.GetQueue(family.Index, i); } family.SetQueues(queues); } // Create and set the command pools in the queue families foreach (var family in QueueFamilies) { // Skip family that shouldn't have a command pool if (!family.HasCommandPool) { continue; } // Create command pool var commandPool = Device.CreateCommandPool(new CommandPoolCreateInfo() { QueueFamilyIndex = family.Index, Flags = family.CommandPoolFlags }); family.SetCommandPool(commandPool); } } // Create swapchain { // Query supported capabilities and formats var surfaceCapabilities = SurfaceCapabilities; var surfaceFormats = SurfaceFormats; var surfacePresentModes = SurfacePresentModes; surfaceCapabilities.PrintDebug(); // Choose the best image format and color space { var imageFormat = Array.Find( PreferredSwapchainFormats, preferred => surfaceFormats.Any(available => available.Format == preferred) ); if (imageFormat == Format.Undefined) { imageFormat = surfaceFormats.FirstOrDefault().Format; } if (imageFormat == Format.Undefined) { throw new InvalidOperationException("Surface somehow does not support any known image formats"); } SwapchainImageFormat = imageFormat; SwapchainColorSpace = surfaceFormats.First(e => e.Format == SwapchainImageFormat).ColorSpace; } // Choose the best present mode { var presentMode = Array.Find( PreferredPresentModes, preferred => surfacePresentModes.Any(available => available == preferred) ); SwapchainPresentMode = presentMode; } // Create the swapchain SwapchainExtent = surfaceCapabilities.CurrentExtent; Swapchain = Device.CreateSwapchainKhr(new SwapchainCreateInfoKhr( surface: Surface, imageFormat: SwapchainImageFormat, imageExtent: SwapchainExtent, preTransform: surfaceCapabilities.CurrentTransform, presentMode: SwapchainPresentMode, minImageCount: SurfaceCapabilities.MinImageCount, imageArrayLayers: SurfaceCapabilities.MaxImageArrayLayers, imageSharingMode: SharingMode.Exclusive, imageColorSpace: SwapchainColorSpace )); SwapchainAttachmentImages = Swapchain.GetImages().Select( e => new VKImage( e, SwapchainImageFormat, SwapchainExtent, new ImageSubresourceRange(ImageAspects.Color, 0, 1, 0, 1) ) ).ToArray(); Swapchain.PrintDebug(); } // Create semaphores & fences { // Image available semaphore ImageAvailableSemaphore = Device.CreateSemaphore(); ReadyToPresentSemaphore = Device.CreateSemaphore(); // Swapchain image rendering fences RenderToImageFences = new Fence[SwapchainAttachmentImages.Length]; for (var i = 0; i < RenderToImageFences.Length; i++) { RenderToImageFences[i] = Device.CreateFence(new FenceCreateInfo(flags: FenceCreateFlags.Signaled)); } } // Create content collection { Content = new ContentCollection(this); } // Misc Stopwatch = Stopwatch.StartNew(); CurrentTime = 0.0; }