public static void SetRenderState(RenderState state)
        {
            GraphicsDevice.CullMode          = state.CullMode;
            GraphicsDevice.ClearColor        = state.ClearColor;
            GraphicsDevice.DepthTest         = state.DepthTest;
            GraphicsDevice.DepthWriteEnabled = state.DepthWrite;
            GraphicsDevice.ColorWriteEnabled = state.ColorWrite;
            GraphicsDevice.ClearDepth        = state.ClearDepth;
            GraphicsDevice.AlphaSrc          = state.Src;
            GraphicsDevice.AlphaDst          = state.Dst;
            if (state.Framebuffer != null)
            {
                GraphicsDevice.Framebuffer = state.Framebuffer;
            }
            GraphicsDevice.SetDepthRange(state.NearPlane, state.FarPlane);


            if (state.IndexBuffer != null)
            {
                SetVertexArray(state.IndexBuffer.varray);
            }
            for (int i = 0; i < state.Viewports.Length; i++)
            {
                GraphicsDevice.SetViewport(i, state.Viewports[i].X, state.Viewports[i].Y, state.Viewports[i].Z, state.Viewports[i].W);
            }

            if (state.ShaderStorageBufferBindings != null)
            {
                StorageBuffer[] pendingBindings = new StorageBuffer[state.ShaderStorageBufferBindings.Length];
                Array.Copy(state.ShaderStorageBufferBindings, pendingBindings, pendingBindings.Length);
                int pendingCnt = pendingBindings.Length;
                while (pendingCnt > 0)
                {
                    for (int i = 0; i < pendingBindings.Length; i++)
                    {
                        if (pendingBindings[i] != null && pendingBindings[i].IsReady)
                        {
                            GraphicsDevice.SetShaderStorageBufferBinding(pendingBindings[i], i);
                            pendingBindings[i] = null;
                            pendingCnt--;
                        }
                    }
                }
            }

            if (state.UniformBufferBindings != null)
            {
                UniformBuffer[] pendingBindings = new UniformBuffer[state.UniformBufferBindings.Length];
                Array.Copy(state.UniformBufferBindings, pendingBindings, pendingBindings.Length);
                int pendingCnt = pendingBindings.Length;
                while (pendingCnt > 0)
                {
                    for (int i = 0; i < pendingBindings.Length; i++)
                    {
                        if (pendingBindings[i] != null && pendingBindings[i].IsReady)
                        {
                            GraphicsDevice.SetUniformBufferBinding(pendingBindings[i], i);
                            pendingBindings[i] = null;
                            pendingCnt--;
                        }
                    }
                }
            }

            if (state.ShaderProgram != null)
            {
                GraphicsDevice.ShaderProgram = state.ShaderProgram;
            }
        }
        public static void Init()
        {
            optionalExtn_avail      = new bool[optionalDeviceExtns.Length];
            Console.BackgroundColor = ConsoleColor.Black;
            Console.ForegroundColor = ConsoleColor.White;
            Window = new GameWindow(AppName);
            fixed(IntPtr *instancePtr = &instanceHndl)
            fixed(IntPtr * surfacePtr = &surfaceHndl)
            {
                VkResult res;
                var      instLayers  = new List <string>();
                var      instExtns   = new List <string>();
                var      devExtns    = new List <string>();
                uint     glfwExtnCnt = 0;
                var      glfwExtns   = glfwGetRequiredInstanceExtensions(&glfwExtnCnt);

                for (int i = 0; i < glfwExtnCnt; i++)
                {
                    instExtns.Add(Marshal.PtrToStringAnsi(glfwExtns[i]));
                }

                instExtns.Add(VkKhrGetPhysicalDeviceProperties2ExtensionName);

                if (EnableValidation)
                {
                    instLayers.Add("VK_LAYER_KHRONOS_validation");
                    instExtns.Add(VkExtDebugUtilsExtensionName);
                }

                var layers = stackalloc IntPtr[instLayers.Count];

                for (int i = 0; i < instLayers.Count; i++)
                {
                    layers[i] = Marshal.StringToHGlobalAnsi(instLayers[i]);
                }

                var extns = stackalloc IntPtr[instExtns.Count];

                for (int i = 0; i < instExtns.Count; i++)
                {
                    extns[i] = Marshal.StringToHGlobalAnsi(instExtns[i]);
                }
                {
                    var appInfo =
                        new VkApplicationInfo()
                    {
                        sType              = VkStructureType.StructureTypeApplicationInfo,
                        pApplicationName   = AppName,
                        pEngineName        = EngineName,
                        apiVersion         = VkApiVersion12,
                        applicationVersion = 1,
                        engineVersion      = 1,
                        pNext              = IntPtr.Zero
                    };
                    var appInfo_ptr = appInfo.Pointer();

                    var instCreatInfo = new VkInstanceCreateInfo()
                    {
                        sType            = VkStructureType.StructureTypeInstanceCreateInfo,
                        pApplicationInfo = appInfo_ptr,
                    };

                    instCreatInfo.ppEnabledLayerNames     = layers;
                    instCreatInfo.enabledLayerCount       = (uint)instLayers.Count;
                    instCreatInfo.ppEnabledExtensionNames = extns;
                    instCreatInfo.enabledExtensionCount   = (uint)instExtns.Count;

                    var instCreatInfo_ptr = instCreatInfo.Pointer();

                    //register instance create debug message handler
                    debugCreatInfo = new VkDebugUtilsMessengerCreateInfoEXT()
                    {
                        sType           = VkStructureType.StructureTypeDebugUtilsMessengerCreateInfoExt,
                        messageSeverity = VkDebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityErrorBitExt | VkDebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityWarningBitExt | VkDebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityVerboseBitExt | VkDebugUtilsMessageSeverityFlagsEXT.DebugUtilsMessageSeverityInfoBitExt,
                        messageType     = VkDebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeGeneralBitExt | VkDebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypePerformanceBitExt | VkDebugUtilsMessageTypeFlagsEXT.DebugUtilsMessageTypeValidationBitExt,
                        pfnUserCallback = DebugCallback
                    };
                    var debugCreatInfo_ptr = debugCreatInfo.Pointer();

                    if (EnableValidation)
                    {
                        instCreatInfo.pNext = debugCreatInfo_ptr;
                    }

                    res = vkCreateInstance(instCreatInfo_ptr, null, instancePtr);
                    if (res != VkResult.Success)
                    {
                        throw new Exception("Failed to create instance.");
                    }
                }

                if (EnableValidation)
                {
                    SetupDebugMessengers(instanceHndl);
                    var debugCreatInfo_ptr = debugCreatInfo.Pointer();

                    fixed(IntPtr *dbg_ptr = &debugMessenger)
                    res = CreateDebugUtilsMessengerEXT(instanceHndl, debugCreatInfo_ptr, IntPtr.Zero, dbg_ptr);

                    if (res != VkResult.Success)
                    {
                        throw new Exception("Failed to register debug callback.");
                    }
                }

                res = Window.CreateSurface(instanceHndl, surfacePtr);
                if (res != VkResult.Success)
                {
                    throw new Exception("Failed to create surface.");
                }

                uint devCount = 0;

                vkEnumeratePhysicalDevices(instanceHndl, &devCount, null);
                if (devCount == 0)
                {
                    throw new Exception("Failed to find Vulkan compatible devices.");
                }

                var devices = new IntPtr[devCount];

                fixed(IntPtr *devicesPtr = devices)
                vkEnumeratePhysicalDevices(instanceHndl, &devCount, devicesPtr);

                var ratedDevices = new List <(uint, IntPtr)>();

                for (int i = 0; i < devices.Length; i++)
                {
                    //rate each device
                    ratedDevices.Add((RateDevice(devices[i]), devices[i]));
                }
                var orderedDevices = ratedDevices.OrderByDescending(a => a.Item1)
                                     .Select(a => a.Item2)
                                     .ToArray();

                DeviceInformation    = new DeviceInfo[orderedDevices.Length];
                DeviceInformation[0] = new DeviceInfo()
                {
                    PhysicalDevice = orderedDevices[0]
                };
                //TODO for now just choose the first device

                {
                    //allocate queues for primary device
                    uint graphicsFamily = ~0u;
                    uint computeFamily  = ~0u;
                    uint transferFamily = ~0u;
                    uint presentFamily  = ~0u;

                    uint qfam_cnt = 0;
                    vkGetPhysicalDeviceQueueFamilyProperties(orderedDevices[0], &qfam_cnt, null);
                    var qFams_ptr = new ManagedPtrArray <VkQueueFamilyProperties>(qfam_cnt);
                    vkGetPhysicalDeviceQueueFamilyProperties(orderedDevices[0], &qfam_cnt, qFams_ptr);
                    var qFams = qFams_ptr.Value;

                    uint qFamIdx = 0;
                    foreach (var qFam in qFams)
                    {
                        bool presentSupport = false;
                        vkGetPhysicalDeviceSurfaceSupportKHR(orderedDevices[0], qFamIdx, surfaceHndl, &presentSupport);

                        if ((qFam.queueFlags & VkQueueFlags.QueueGraphicsBit) != 0 && graphicsFamily == ~0u)
                        {
                            graphicsFamily = qFamIdx;
                            if (presentSupport)
                            {
                                presentFamily = qFamIdx;
                            }
                            qFamIdx++;
                            continue;
                        }

                        if ((qFam.queueFlags & VkQueueFlags.QueueComputeBit) != 0 && computeFamily == ~0u)
                        {
                            computeFamily = qFamIdx;
                            if ((qFam.queueFlags & VkQueueFlags.QueueTransferBit) != 0)
                            {
                                transferFamily = qFamIdx;
                            }
                            qFamIdx++;
                            continue;
                        }

                        if ((qFam.queueFlags & VkQueueFlags.QueueTransferBit) != 0)
                        {
                            transferFamily = qFamIdx;
                        }

                        if (graphicsFamily != ~0u && computeFamily != ~0u && transferFamily != ~0u && presentFamily != ~0u)
                        {
                            break;
                        }
                        qFamIdx++;
                    }

                    if (presentFamily == ~0u)
                    {
                        throw new Exception("Separate present queue support hasn't been implemented.");
                    }

                    var max_q_priority          = stackalloc float[] { 1.0f };
                    var dual_graph_q_priority   = stackalloc float[] { 1.0f };
                    var triple_graph_q_priority = stackalloc float[] { 1.0f, 1.0f, 1.0f };

                    VkDeviceQueueCreateInfo graphics_qCreatInfo = new VkDeviceQueueCreateInfo()
                    {
                        sType            = VkStructureType.StructureTypeDeviceQueueCreateInfo,
                        queueFamilyIndex = graphicsFamily,
                        queueCount       = 1,
                        pQueuePriorities = max_q_priority
                    };

                    VkDeviceQueueCreateInfo compute_qCreatInfo = new VkDeviceQueueCreateInfo
                    {
                        sType            = VkStructureType.StructureTypeDeviceQueueCreateInfo,
                        queueFamilyIndex = computeFamily,
                        queueCount       = 1,
                        pQueuePriorities = max_q_priority
                    };

                    VkDeviceQueueCreateInfo transfer_qCreatInfo = new VkDeviceQueueCreateInfo();
                    if (transferFamily != graphicsFamily)
                    {
                        transfer_qCreatInfo.sType            = VkStructureType.StructureTypeDeviceQueueCreateInfo;
                        transfer_qCreatInfo.queueFamilyIndex = transferFamily;
                        transfer_qCreatInfo.queueCount       = 1;
                        transfer_qCreatInfo.pQueuePriorities = max_q_priority;
                    }
                    else
                    {
                        graphics_qCreatInfo.queueCount       = 2;
                        graphics_qCreatInfo.pQueuePriorities = dual_graph_q_priority;
                    }


                    var qCreatInfos = new VkDeviceQueueCreateInfo[3];
                    qCreatInfos[0] = graphics_qCreatInfo;
                    qCreatInfos[1] = compute_qCreatInfo;
                    if (transferFamily != graphicsFamily)
                    {
                        qCreatInfos[2] = transfer_qCreatInfo;
                    }

                    DeviceInformation[0].GraphicsFamily = graphicsFamily;
                    DeviceInformation[0].ComputeFamily  = computeFamily;
                    DeviceInformation[0].TransferFamily = transferFamily;
                    DeviceInformation[0].PresentFamily  = presentFamily;

                    VkPhysicalDeviceFeatures devFeats = new VkPhysicalDeviceFeatures()
                    {
                        multiDrawIndirect              = true,
                        drawIndirectFirstInstance      = true,
                        fullDrawIndexUint32            = true,
                        tessellationShader             = true,
                        fragmentStoresAndAtomics       = true,
                        vertexPipelineStoresAndAtomics = true,
                        robustBufferAccess             = EnableValidation,
                        shaderInt16       = true,
                        samplerAnisotropy = true,
                        fillModeNonSolid  = true,
                        largePoints       = true,
                    };

                    var devFeats11 = new VkPhysicalDeviceVulkan11Features()
                    {
                        sType = VkStructureType.StructureTypePhysicalDeviceVulkan11Features,
                        shaderDrawParameters = true,
                        //storageBuffer16BitAccess = true,
                        //uniformAndStorageBuffer16BitAccess = true,
                        //storagePushConstant16 = true,
                    };
                    var devFeats11_ptr = devFeats11.Pointer();

                    /*
                     * var depthStenc = new VkPhysicalDeviceSeparateDepthStencilLayoutsFeatures()
                     * {
                     *  sType = VkStructureType.StructureTypePhysicalDeviceSeparateDepthStencilLayoutsFeatures,
                     *  separateDepthStencilLayouts = true,
                     *  pNext = devFeats11_ptr
                     * };
                     * var depthStenc_ptr = depthStenc.Pointer();
                     *
                     * var timelineSems = new VkPhysicalDeviceTimelineSemaphoreFeatures()
                     * {
                     *  sType = VkStructureType.StructureTypePhysicalDeviceTimelineSemaphoreFeatures,
                     *  timelineSemaphore = true,
                     *  pNext = depthStenc_ptr,
                     * };
                     * var timelineSems_ptr = timelineSems.Pointer();
                     *
                     * var indirectCnt = new VkPhysicalDeviceShaderFloat16Int8Features()
                     * {
                     *  sType = VkStructureType.StructureTypePhysicalDeviceShaderFloat16Int8Features,
                     *  shaderFloat16 = true,
                     *  shaderInt8 = true,
                     *  pNext = timelineSems_ptr,
                     * };
                     * var indirectCnt_ptr = indirectCnt.Pointer();
                     *
                     * var uboLayout = new VkPhysicalDeviceUniformBufferStandardLayoutFeatures()
                     * {
                     *  sType = VkStructureType.StructureTypePhysicalDeviceUniformBufferStandardLayoutFeatures,
                     *  uniformBufferStandardLayout = true,
                     *  pNext = indirectCnt_ptr
                     * };
                     * var uboLayout_ptr = uboLayout.Pointer();
                     *
                     * var storageByte = new VkPhysicalDevice8BitStorageFeatures()
                     * {
                     *  sType = VkStructureType.StructureTypePhysicalDevice8bitStorageFeatures,
                     *  storageBuffer8BitAccess = true,
                     *  uniformAndStorageBuffer8BitAccess = true,
                     *  pNext = uboLayout_ptr
                     * };
                     * var storageByte_ptr = storageByte.Pointer();
                     * var descIndexing = new VkPhysicalDeviceDescriptorIndexingFeatures()
                     * {
                     *  sType = VkStructureType.StructureTypePhysicalDeviceDescriptorIndexingFeatures,
                     *  descriptorBindingSampledImageUpdateAfterBind = true,
                     *  descriptorBindingStorageBufferUpdateAfterBind = true,
                     *  descriptorBindingStorageImageUpdateAfterBind = true,
                     *  descriptorBindingStorageTexelBufferUpdateAfterBind = true,
                     *  descriptorBindingUniformBufferUpdateAfterBind = true,
                     *  descriptorBindingUniformTexelBufferUpdateAfterBind = true,
                     *  descriptorBindingUpdateUnusedWhilePending = true,
                     *  descriptorBindingPartiallyBound = true,
                     *  shaderStorageTexelBufferArrayDynamicIndexing = true,
                     *  pNext = storageByte_ptr,
                     * };
                     * var descIndexing_ptr = descIndexing.Pointer();*/

                    //var drawIndirectCount = new VkDrawIndirecCount

                    var devFeats12 = new VkPhysicalDeviceVulkan12Features()
                    {
                        sType = VkStructureType.StructureTypePhysicalDeviceVulkan12Features,
                        separateDepthStencilLayouts = true,
                        timelineSemaphore           = true,
                        drawIndirectCount           = true,
                        shaderFloat16 = true,
                        shaderInt8    = true,
                        uniformBufferStandardLayout       = true,
                        storageBuffer8BitAccess           = true,
                        uniformAndStorageBuffer8BitAccess = true,
                        descriptorIndexing = true,
                        descriptorBindingSampledImageUpdateAfterBind       = true,
                        descriptorBindingStorageBufferUpdateAfterBind      = true,
                        descriptorBindingStorageImageUpdateAfterBind       = true,
                        descriptorBindingStorageTexelBufferUpdateAfterBind = true,
                        descriptorBindingUniformBufferUpdateAfterBind      = true,
                        descriptorBindingUniformTexelBufferUpdateAfterBind = true,
                        descriptorBindingUpdateUnusedWhilePending          = true,
                        descriptorBindingPartiallyBound = true,
                        shaderStorageTexelBufferArrayDynamicIndexing = true,

                        shaderStorageBufferArrayNonUniformIndexing = true,
                        runtimeDescriptorArray = true,
                        descriptorBindingVariableDescriptorCount = true,

                        pNext = devFeats11_ptr
                    };
                    var devFeats12_ptr = devFeats12.Pointer();

                    devExtns.AddRange(requiredDeviceExtns);
                    for (int i = 0; i < optionalExtn_avail.Length; i++)
                    {
                        if (optionalExtn_avail[i])
                        {
                            devExtns.Add(optionalDeviceExtns[i]);
                        }
                    }
                    var devExtns_ptr = stackalloc IntPtr[devExtns.Count];
                    for (int i = 0; i < devExtns.Count; i++)
                    {
                        devExtns_ptr[i] = Marshal.StringToHGlobalAnsi(devExtns[i]);
                    }

                    var qCreatInfos_ptr = qCreatInfos.Pointer();
                    var devFeats_ptr    = devFeats.Pointer();
                    var devCreatInfo    = new VkDeviceCreateInfo()
                    {
                        sType = VkStructureType.StructureTypeDeviceCreateInfo,
                        queueCreateInfoCount    = (uint)(transferFamily != graphicsFamily ? 3 : 2),
                        enabledExtensionCount   = (uint)devExtns.Count,
                        ppEnabledExtensionNames = devExtns_ptr,
                        enabledLayerCount       = (uint)instLayers.Count,
                        ppEnabledLayerNames     = layers,
                        pEnabledFeatures        = devFeats_ptr,
                        pQueueCreateInfos       = qCreatInfos_ptr,
                        pNext = devFeats12_ptr
                    };
                    var    devCreatInfo_ptr = devCreatInfo.Pointer();
                    IntPtr deviceHndl       = IntPtr.Zero;
                    res = vkCreateDevice(orderedDevices[0], devCreatInfo_ptr, null, &deviceHndl);
                    if (res != VkResult.Success)
                    {
                        throw new Exception("Failed to create logical device.");
                    }
                    DeviceInformation[0].Device = deviceHndl;

                    //Setup memory allocator
                    var allocCreatInfo = new VmaAllocatorCreateInfo()
                    {
                        physicalDevice = DeviceInformation[0].PhysicalDevice,
                        device         = DeviceInformation[0].Device
                    };
                    var allocCreatInfo_ptr = allocCreatInfo.Pointer();

                    fixed(IntPtr *vma_alloc_ptr = &DeviceInformation[0].vmaAllocator)
                    res = vmaCreateAllocator(allocCreatInfo_ptr, vma_alloc_ptr);

                    if (res != VkResult.Success)
                    {
                        throw new Exception("Failed to initialize allocator.");
                    }

                    IntPtr graph_q_hndl = IntPtr.Zero;
                    IntPtr trans_q_hndl = IntPtr.Zero;
                    IntPtr comp_q_hndl  = IntPtr.Zero;
                    vkGetDeviceQueue(DeviceInformation[0].Device, graphicsFamily, 0, &graph_q_hndl);
                    vkGetDeviceQueue(DeviceInformation[0].Device, computeFamily, 0, &comp_q_hndl);
                    if (transferFamily != graphicsFamily)
                    {
                        vkGetDeviceQueue(DeviceInformation[0].Device, transferFamily, 0, &trans_q_hndl);
                    }
                    else
                    {
                        vkGetDeviceQueue(DeviceInformation[0].Device, graphicsFamily, 1, &trans_q_hndl);
                    }

                    DeviceInformation[0].GraphicsQueue = new GpuQueue(CommandQueueKind.Graphics, graph_q_hndl, graphicsFamily, 0);
                    DeviceInformation[0].TransferQueue = new GpuQueue(CommandQueueKind.Transfer, trans_q_hndl, transferFamily, 0);
                    DeviceInformation[0].ComputeQueue  = new GpuQueue(CommandQueueKind.Compute, comp_q_hndl, computeFamily, 0);

                    var queue_indices = new List <uint>();
                    if (!queue_indices.Contains(DeviceInformation[0].GraphicsFamily))
                    {
                        queue_indices.Add(DeviceInformation[0].GraphicsFamily);
                    }
                    if (!queue_indices.Contains(DeviceInformation[0].ComputeFamily))
                    {
                        queue_indices.Add(DeviceInformation[0].ComputeFamily);
                    }
                    if (!queue_indices.Contains(DeviceInformation[0].PresentFamily))
                    {
                        queue_indices.Add(DeviceInformation[0].PresentFamily);
                    }
                    if (!queue_indices.Contains(DeviceInformation[0].TransferFamily))
                    {
                        queue_indices.Add(DeviceInformation[0].TransferFamily);
                    }
                    DeviceInformation[0].QueueFamilyIndices = queue_indices.ToArray();

                    var physDeviceProps = new ManagedPtr <VkPhysicalDeviceProperties>();
                    vkGetPhysicalDeviceProperties(DeviceInformation[0].PhysicalDevice, physDeviceProps);
                    DeviceInformation[0].Properties = physDeviceProps.Value;

                    var caps_ptr = new ManagedPtr <VkSurfaceCapabilitiesKHR>();
                    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(DeviceInformation[0].PhysicalDevice, surfaceHndl, caps_ptr);
                    var caps = caps_ptr.Value;

                    VkExtent2D cur_extent = new VkExtent2D();
                    if (caps.currentExtent.width != uint.MaxValue)
                    {
                        cur_extent = caps.currentExtent;
                    }
                    else
                    {
                        cur_extent.width  = System.Math.Clamp((uint)Window.Width, caps.minImageExtent.width, caps.maxImageExtent.width);
                        cur_extent.height = System.Math.Clamp((uint)Window.Height, caps.minImageExtent.height, caps.maxImageExtent.height);
                    }

                    uint img_cnt = caps.minImageCount + 1;

                    VkSwapchainCreateInfoKHR swapCreatInfo = new VkSwapchainCreateInfoKHR()
                    {
                        sType                 = VkStructureType.StructureTypeSwapchainCreateInfoKhr,
                        surface               = surfaceHndl,
                        minImageCount         = img_cnt,
                        imageFormat           = surface_fmt.format,
                        imageColorSpace       = surface_fmt.colorSpace,
                        imageExtent           = cur_extent,
                        imageArrayLayers      = 1,
                        imageUsage            = VkImageUsageFlags.ImageUsageColorAttachmentBit | VkImageUsageFlags.ImageUsageTransferDstBit | VkImageUsageFlags.ImageUsageTransferSrcBit,
                        imageSharingMode      = VkSharingMode.SharingModeExclusive,
                        queueFamilyIndexCount = 0,
                        pQueueFamilyIndices   = null,
                        preTransform          = caps.currentTransform,
                        compositeAlpha        = VkCompositeAlphaFlagsKHR.CompositeAlphaOpaqueBitKhr,
                        presentMode           = present_mode,
                        clipped               = true,
                        oldSwapchain          = IntPtr.Zero
                    };
                    var swapCreatInfo_ptr = swapCreatInfo.Pointer();

                    fixed(IntPtr *swapchain_ptr = &swapChainHndl)
                    res = vkCreateSwapchainKHR(DeviceInformation[0].Device, swapCreatInfo_ptr, null, swapchain_ptr);

                    if (res != VkResult.Success)
                        throw new Exception("Failed to create swapchain.");

                    fixed(uint *swapchain_img_cnt_ptr = &swapchain_img_cnt)
                    {
                        vkGetSwapchainImagesKHR(DeviceInformation[0].Device, swapChainHndl, swapchain_img_cnt_ptr, null);
                        var swapchainImages_l = new IntPtr[swapchain_img_cnt];

                        fixed(IntPtr *swapchain_imgs = swapchainImages_l)
                        vkGetSwapchainImagesKHR(DeviceInformation[0].Device, swapChainHndl, swapchain_img_cnt_ptr, swapchain_imgs);

                        MaxFramesInFlight = swapchain_img_cnt;
                        MaxFrameCount     = swapchain_img_cnt;
                        swapchainImages   = new Image[swapchain_img_cnt];
                        for (int i = 0; i < swapchainImages.Length; i++)
                        {
                            swapchainImages[i] = new Image($"Swapchain_{i}")
                            {
                                Dimensions    = 2,
                                Width         = cur_extent.width,
                                Height        = cur_extent.height,
                                Depth         = 1,
                                Format        = (ImageFormat)surface_fmt.format,
                                Layers        = 1,
                                Levels        = 1,
                                MemoryUsage   = MemoryUsage.GpuOnly,
                                Usage         = ImageUsage.Sampled,
                                InitialLayout = ImageLayout.Undefined,
                                Cubemappable  = false,
                            };
                            swapchainImages[i].Build(0, swapchainImages_l[i]);
                        }


                        surface_extent     = cur_extent;
                        swapchainViews     = new ImageView[swapchain_img_cnt];
                        DefaultFramebuffer = new Framebuffer[swapchain_img_cnt];
                        for (int i = 0; i < swapchainImages.Length; i++)
                        {
                            swapchainViews[i] = new ImageView($"Swapchain_{i}")
                            {
                                BaseLayer  = 0,
                                BaseLevel  = 0,
                                Format     = (ImageFormat)surface_fmt.format,
                                LayerCount = 1,
                                LevelCount = 1,
                                ViewType   = ImageViewType.View2D,
                            };
                            swapchainViews[i].Build(swapchainImages[i]);

                            DefaultFramebuffer[i]                  = new Framebuffer();
                            DefaultFramebuffer[i].Width            = surface_extent.width;
                            DefaultFramebuffer[i].Height           = surface_extent.height;
                            DefaultFramebuffer[i].Name             = $"Swapchain_{i}";
                            DefaultFramebuffer[i].ColorAttachments = new ImageView[] { swapchainViews[i] };
                        }
                    }

                    for (int i = 0; i < instLayers.Count; i++)
                    {
                        Marshal.FreeHGlobal(layers[i]);
                    }
                    for (int i = 0; i < instExtns.Count; i++)
                    {
                        Marshal.FreeHGlobal(extns[i]);
                    }
                    for (int i = 0; i < devExtns.Count; i++)
                    {
                        Marshal.FreeHGlobal(devExtns_ptr[i]);
                    }

                    //TODO allocate compute and trasnfer queues for all secondary devices
                }

                for (int i = 0; i < 1 /*DeviceInformation.Length*/; i++)
                {
                    var    devInfo    = DeviceInformation[i];
                    IntPtr rrCtxtHndl = IntPtr.Zero;

                    if (rrCreateContextVk(RrApiVersion, devInfo.Device, devInfo.PhysicalDevice, devInfo.ComputeQueue.Handle, devInfo.ComputeFamily, &rrCtxtHndl) != RRError.RrSuccess)
                    {
                        Console.WriteLine($"Failed to initialize RadeonRays for device #{i}.");
                    }
                    DeviceInformation[i].RaysContext = rrCtxtHndl;
                }

                FrameFinishedSemaphore  = new GpuSemaphore[MaxFramesInFlight];
                ImageAvailableSemaphore = new GpuSemaphore[MaxFramesInFlight];
                InflightFences          = new Fence[MaxFramesInFlight];
                for (int i = 0; i < MaxFramesInFlight; i++)
                {
                    FrameFinishedSemaphore[i] = new GpuSemaphore();
                    FrameFinishedSemaphore[i].Build(0, false, 0);

                    ImageAvailableSemaphore[i] = new GpuSemaphore();
                    ImageAvailableSemaphore[i].Build(0, false, 0);

                    InflightFences[i] = new Fence
                    {
                        CreateSignaled = true
                    };
                    InflightFences[i].Build(0);
                }

                Width  = (uint)Window.Width;
                Height = (uint)Window.Height;
            }
        }