private void CreateLogicalDevice()
        {
            GetQueueFamilyIndices();

            HashSet <uint> familyIndices = new HashSet <uint> {
                _graphicsQueueIndex, _presentQueueIndex
            };
            RawList <VkDeviceQueueCreateInfo> queueCreateInfos = new RawList <VkDeviceQueueCreateInfo>();

            foreach (uint index in familyIndices)
            {
                VkDeviceQueueCreateInfo queueCreateInfo = VkDeviceQueueCreateInfo.New();
                queueCreateInfo.queueFamilyIndex = _graphicsQueueIndex;
                queueCreateInfo.queueCount       = 1;
                float priority = 1f;
                queueCreateInfo.pQueuePriorities = &priority;
                queueCreateInfos.Add(queueCreateInfo);
            }

            VkPhysicalDeviceFeatures deviceFeatures = new VkPhysicalDeviceFeatures();

            deviceFeatures.samplerAnisotropy = true;
            deviceFeatures.fillModeNonSolid  = true;
            deviceFeatures.geometryShader    = true;
            deviceFeatures.depthClamp        = true;

            VkDeviceCreateInfo deviceCreateInfo = VkDeviceCreateInfo.New();

            fixed(VkDeviceQueueCreateInfo *qciPtr = &queueCreateInfos.Items[0])
            {
                deviceCreateInfo.pQueueCreateInfos    = qciPtr;
                deviceCreateInfo.queueCreateInfoCount = queueCreateInfos.Count;

                deviceCreateInfo.pEnabledFeatures = &deviceFeatures;

                StackList <IntPtr> layerNames = new StackList <IntPtr>();

                layerNames.Add(CommonStrings.StandardValidationLayerName);
                deviceCreateInfo.enabledLayerCount   = layerNames.Count;
                deviceCreateInfo.ppEnabledLayerNames = (byte **)layerNames.Data;

                byte *extensionNames = CommonStrings.VK_KHR_SWAPCHAIN_EXTENSION_NAME;

                deviceCreateInfo.enabledExtensionCount   = 1;
                deviceCreateInfo.ppEnabledExtensionNames = &extensionNames;

                vkCreateDevice(_physicalDevice, ref deviceCreateInfo, null, out _device);
            }

            vkGetDeviceQueue(_device, _graphicsQueueIndex, 0, out _graphicsQueue);
            vkGetDeviceQueue(_device, _presentQueueIndex, 0, out _presentQueue);
        }
        private void CopyDataMultiImage(
            IntPtr pixelsFront,
            IntPtr pixelsBack,
            IntPtr pixelsLeft,
            IntPtr pixelsRight,
            IntPtr pixelsTop,
            IntPtr pixelsBottom)
        {
            StackList <IntPtr, Size6IntPtr> faces = new StackList <IntPtr, Size6IntPtr>();

            faces.Add(pixelsRight);
            faces.Add(pixelsLeft);
            faces.Add(pixelsTop);
            faces.Add(pixelsBottom);
            faces.Add(pixelsBack);
            faces.Add(pixelsFront);

            TransitionImageLayout(_image, (uint)MipLevels, 0, 6, _imageLayout, VkImageLayout.TransferDstOptimal);

            for (uint i = 0; i < 6; i++)
            {
                CreateImage(
                    _device,
                    _physicalDevice,
                    _memoryManager,
                    (uint)Width,
                    (uint)Height,
                    1,
                    _vkFormat,
                    VkImageTiling.Linear,
                    VkImageUsageFlags.TransferSrc,
                    VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent,
                    out VkImage stagingImage, out VkMemoryBlock stagingMemory);

                VkImageSubresource subresource;
                subresource.aspectMask = VkImageAspectFlags.Color;
                subresource.arrayLayer = 0;
                subresource.mipLevel   = 0;

                vkGetImageSubresourceLayout(_device, stagingImage, ref subresource, out VkSubresourceLayout stagingLayout);
                void *   mappedPtr;
                VkResult result = vkMapMemory(_device, stagingMemory.DeviceMemory, stagingMemory.Offset, stagingLayout.size, 0, &mappedPtr);
                CheckResult(result);
                IntPtr data            = faces[i];
                ulong  dataSizeInBytes = (ulong)(Width * Height * FormatHelpers.GetPixelSizeInBytes(_format));
                ulong  rowPitch        = stagingLayout.rowPitch;
                if (rowPitch == (ulong)Width)
                {
                    Buffer.MemoryCopy(data.ToPointer(), mappedPtr, dataSizeInBytes, dataSizeInBytes);
                }
                else
                {
                    int pixelSizeInBytes = FormatHelpers.GetPixelSizeInBytes(_format);
                    for (uint yy = 0; yy < Height; yy++)
                    {
                        byte *dstRowStart = ((byte *)mappedPtr) + (rowPitch * yy);
                        byte *srcRowStart = ((byte *)data.ToPointer()) + (Width * yy * pixelSizeInBytes);
                        Unsafe.CopyBlock(dstRowStart, srcRowStart, (uint)(Width * pixelSizeInBytes));
                    }
                }

                vkUnmapMemory(_device, stagingMemory.DeviceMemory);

                TransitionImageLayout(stagingImage, 1, 0, 1, VkImageLayout.Preinitialized, VkImageLayout.TransferSrcOptimal);
                CopyImage(stagingImage, 0, 0, _image, 0, i, (uint)Width, (uint)Height);

                vkDestroyImage(_device, stagingImage, null);
                _memoryManager.Free(stagingMemory);
            }
        }
        private void CopyDataSingleStagingBuffer(IntPtr pixelsFront, IntPtr pixelsBack, IntPtr pixelsLeft, IntPtr pixelsRight, IntPtr pixelsTop, IntPtr pixelsBottom, VkMemoryRequirements memReqs)
        {
            VkBufferCreateInfo bufferCI = VkBufferCreateInfo.New();

            bufferCI.size  = memReqs.size;
            bufferCI.usage = VkBufferUsageFlags.TransferSrc;
            vkCreateBuffer(_device, ref bufferCI, null, out VkBuffer stagingBuffer);

            vkGetBufferMemoryRequirements(_device, stagingBuffer, out VkMemoryRequirements stagingMemReqs);
            VkMemoryBlock stagingMemory = _memoryManager.Allocate(
                FindMemoryType(
                    _physicalDevice,
                    stagingMemReqs.memoryTypeBits,
                    VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent),
                stagingMemReqs.size,
                stagingMemReqs.alignment);

            VkResult result = vkBindBufferMemory(_device, stagingBuffer, stagingMemory.DeviceMemory, 0);

            CheckResult(result);

            StackList <IntPtr, Size6IntPtr> faces = new StackList <IntPtr, Size6IntPtr>();

            faces.Add(pixelsRight);
            faces.Add(pixelsLeft);
            faces.Add(pixelsTop);
            faces.Add(pixelsBottom);
            faces.Add(pixelsBack);
            faces.Add(pixelsFront);

            for (uint i = 0; i < 6; i++)
            {
                VkImageSubresource subresource;
                subresource.aspectMask = VkImageAspectFlags.Color;
                subresource.arrayLayer = i;
                subresource.mipLevel   = 0;
                vkGetImageSubresourceLayout(_device, _image, ref subresource, out VkSubresourceLayout faceLayout);
                void *mappedPtr;
                result = vkMapMemory(_device, stagingMemory.DeviceMemory, faceLayout.offset, faceLayout.size, 0, &mappedPtr);
                CheckResult(result);
                Buffer.MemoryCopy((void *)faces[i], mappedPtr, faceLayout.size, faceLayout.size);
                vkUnmapMemory(_device, stagingMemory.DeviceMemory);
            }

            StackList <VkBufferImageCopy, Size512Bytes> copyRegions = new StackList <VkBufferImageCopy, Size512Bytes>();

            for (uint i = 0; i < 6; i++)
            {
                VkImageSubresource subres;
                subres.aspectMask = VkImageAspectFlags.Color;
                subres.mipLevel   = 0;
                subres.arrayLayer = i;
                vkGetImageSubresourceLayout(_device, _image, ref subres, out VkSubresourceLayout layout);

                VkBufferImageCopy copyRegion;
                copyRegion.bufferOffset       = layout.offset;
                copyRegion.bufferImageHeight  = 0;
                copyRegion.bufferRowLength    = 0;
                copyRegion.imageExtent.width  = (uint)Width;
                copyRegion.imageExtent.height = (uint)Height;
                copyRegion.imageExtent.depth  = 1;
                copyRegion.imageOffset.x      = 0;
                copyRegion.imageOffset.y      = 0;
                copyRegion.imageOffset.z      = 0;
                copyRegion.imageSubresource.baseArrayLayer = i;
                copyRegion.imageSubresource.aspectMask     = VkImageAspectFlags.Color;
                copyRegion.imageSubresource.layerCount     = 1;
                copyRegion.imageSubresource.mipLevel       = 0;

                copyRegions.Add(copyRegion);
            }

            VkFenceCreateInfo fenceCI = VkFenceCreateInfo.New();

            result = vkCreateFence(_device, ref fenceCI, null, out VkFence copyFence);
            CheckResult(result);

            TransitionImageLayout(_image, (uint)MipLevels, 0, 6, _imageLayout, VkImageLayout.TransferDstOptimal);

            VkCommandBuffer copyCmd = _rc.BeginOneTimeCommands();

            vkCmdCopyBufferToImage(copyCmd, stagingBuffer, _image, VkImageLayout.TransferDstOptimal, copyRegions.Count, (IntPtr)copyRegions.Data);
            _rc.EndOneTimeCommands(copyCmd, copyFence);
            result = vkWaitForFences(_device, 1, ref copyFence, true, ulong.MaxValue);
            CheckResult(result);

            vkDestroyBuffer(_device, stagingBuffer, null);
            _memoryManager.Free(stagingMemory);
        }
        private void FlushFrameDrawCommands()
        {
            _scInfo.AcquireNextImage(_device, _imageAvailableSemaphore);
            for (int i = 0; i < _renderPassStates.Count; i++)
            {
                RenderPassInfo renderPassState = _renderPassStates[i];

                VkCommandBuffer primaryCommandBuffer = GetPrimaryCommandBuffer();
                renderPassState.PrimaryCommandBuffer = primaryCommandBuffer;
                VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.New();
                beginInfo.flags = VkCommandBufferUsageFlags.OneTimeSubmit;
                vkBeginCommandBuffer(primaryCommandBuffer, ref beginInfo);
                VkRenderPassBeginInfo renderPassBeginInfo = VkRenderPassBeginInfo.New();
                VkFramebufferBase     fbInfo = renderPassState.Framebuffer;
                renderPassBeginInfo.framebuffer = fbInfo.VkFramebuffer;
                renderPassBeginInfo.renderPass  = renderPassState.ClearBuffer
                    ? fbInfo.RenderPassClearBuffer
                    : fbInfo.RenderPassNoClear;

                if (renderPassState.ClearBuffer)
                {
                    VkClearColorValue colorClear = new VkClearColorValue
                    {
                        float32_0 = renderPassState.ClearColor.R,
                        float32_1 = renderPassState.ClearColor.G,
                        float32_2 = renderPassState.ClearColor.B,
                        float32_3 = renderPassState.ClearColor.A
                    };
                    VkClearDepthStencilValue depthClear = new VkClearDepthStencilValue()
                    {
                        depth = 1f, stencil = 0
                    };
                    StackList <VkClearValue, Size512Bytes> clearValues = new StackList <VkClearValue, Size512Bytes>();
                    if (fbInfo.ColorTexture != null)
                    {
                        clearValues.Add(new VkClearValue()
                        {
                            color = colorClear
                        });
                    }
                    if (fbInfo.DepthTexture != null)
                    {
                        clearValues.Add(new VkClearValue()
                        {
                            depthStencil = depthClear
                        });
                    }

                    renderPassBeginInfo.clearValueCount = clearValues.Count;
                    renderPassBeginInfo.pClearValues    = (VkClearValue *)clearValues.Data;
                }
                renderPassBeginInfo.renderArea.extent = new VkExtent2D(fbInfo.Width, fbInfo.Height);

                vkCmdBeginRenderPass(primaryCommandBuffer, ref renderPassBeginInfo, VkSubpassContents.SecondaryCommandBuffers);
                RawList <VkCommandBuffer> secondaryCBs = renderPassState.SecondaryCommandBuffers;
                if (secondaryCBs.Count > 0)
                {
                    vkCmdExecuteCommands(primaryCommandBuffer, secondaryCBs.Count, ref secondaryCBs[0]);
                }
                vkCmdEndRenderPass(primaryCommandBuffer);
                vkEndCommandBuffer(primaryCommandBuffer);

                VkSubmitInfo         submitInfo    = VkSubmitInfo.New();
                VkSemaphore          waitSemaphore = (i == 0) ? _imageAvailableSemaphore : _renderPassSemaphores[i - 1];
                VkPipelineStageFlags waitStages    = VkPipelineStageFlags.ColorAttachmentOutput;
                submitInfo.waitSemaphoreCount = 1;
                submitInfo.pWaitSemaphores    = &waitSemaphore;
                submitInfo.pWaitDstStageMask  = &waitStages;
                VkCommandBuffer cb = primaryCommandBuffer;
                submitInfo.commandBufferCount = 1;
                submitInfo.pCommandBuffers    = &cb;
                VkSemaphore signalSemaphore = _renderPassSemaphores[i];
                submitInfo.signalSemaphoreCount = 1;
                submitInfo.pSignalSemaphores    = &signalSemaphore;
                vkQueueSubmit(_graphicsQueue, 1, ref submitInfo, VkFence.Null);
            }
        }
        private void DrawPrimitives(int indexCount, int instanceCount, int startingIndex, int startingVertex)
        {
            RenderPassInfo     renderPassState  = GetCurrentRenderPass();
            VkPipelineLayout   layout           = ShaderResourceBindingSlots.PipelineLayout;
            VkPipelineCacheKey pipelineCacheKey = new VkPipelineCacheKey();

            pipelineCacheKey.RenderPass        = renderPassState.Framebuffer.RenderPassClearBuffer;
            pipelineCacheKey.PipelineLayout    = layout;
            pipelineCacheKey.BlendState        = (VkBlendState)BlendState;
            pipelineCacheKey.Framebuffer       = renderPassState.Framebuffer;
            pipelineCacheKey.DepthStencilState = (VkDepthStencilState)DepthStencilState;
            pipelineCacheKey.RasterizerState   = (VkRasterizerState)RasterizerState;
            pipelineCacheKey.PrimitiveTopology = _primitiveTopology;
            pipelineCacheKey.ShaderSet         = ShaderSet;
            pipelineCacheKey.VertexBindings    = VertexBuffers;
            VkPipeline graphicsPipeline = _resourceCache.GetGraphicsPipeline(ref pipelineCacheKey);

            VkDescriptorSetCacheKey descriptorSetCacheKey = new VkDescriptorSetCacheKey();

            descriptorSetCacheKey.ShaderResourceBindingSlots = ShaderResourceBindingSlots;
            descriptorSetCacheKey.ConstantBuffers            = _constantBuffers;
            descriptorSetCacheKey.TextureBindings            = _textureBindings;
            descriptorSetCacheKey.SamplerStates = _samplerStates;
            VkDescriptorSet descriptorSet = _resourceCache.GetDescriptorSet(ref descriptorSetCacheKey);

            VkCommandBuffer          cb        = GetCommandBuffer();
            VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.New();

            beginInfo.flags = VkCommandBufferUsageFlags.OneTimeSubmit | VkCommandBufferUsageFlags.RenderPassContinue;
            VkCommandBufferInheritanceInfo inheritanceInfo = VkCommandBufferInheritanceInfo.New();

            inheritanceInfo.renderPass = renderPassState.Framebuffer.RenderPassClearBuffer;
            beginInfo.pInheritanceInfo = &inheritanceInfo;
            vkBeginCommandBuffer(cb, ref beginInfo);

            vkCmdBindPipeline(cb, VkPipelineBindPoint.Graphics, graphicsPipeline);
            vkCmdBindDescriptorSets(
                cb,
                VkPipelineBindPoint.Graphics,
                layout,
                0,
                1,
                ref descriptorSet,
                0,
                IntPtr.Zero);

            int vbCount = ShaderSet.InputLayout.InputDescriptions.Length;
            StackList <VkBuffer, Size512Bytes> vbs = new StackList <VkBuffer, Size512Bytes>();

            for (int vbIndex = 0; vbIndex < vbCount; vbIndex++)
            {
                vbs.Add(((VkVertexBuffer)VertexBuffers[vbIndex]).DeviceBuffer);
            }

            StackList <VkBuffer, Size512Bytes> offsets = new StackList <VkBuffer, Size512Bytes>();

            vkCmdBindVertexBuffers(cb, 0, vbs.Count, (IntPtr)vbs.Data, (IntPtr)offsets.Data);
            vkCmdBindIndexBuffer(cb, IndexBuffer.DeviceBuffer, 0, IndexBuffer.IndexType);
            VkViewport viewport = new VkViewport()
            {
                x        = Viewport.X,
                y        = Viewport.Y,
                width    = Viewport.Width,
                height   = Viewport.Height,
                minDepth = 0,
                maxDepth = 1
            };

            vkCmdSetViewport(cb, 0, 1, ref viewport);
            vkCmdSetScissor(cb, 0, 1, ref _scissorRect);
            vkCmdDrawIndexed(cb, (uint)indexCount, (uint)instanceCount, (uint)startingIndex, startingVertex, 0);
            vkEndCommandBuffer(cb);

            renderPassState.SecondaryCommandBuffers.Add(cb);
        }
        private void CreateInstance()
        {
            HashSet <string> availableInstanceLayers     = new HashSet <string>(EnumerateInstanceLayers());
            HashSet <string> availableInstanceExtensions = new HashSet <string>(EnumerateInstanceExtensions());

            VkInstanceCreateInfo instanceCI      = VkInstanceCreateInfo.New();
            VkApplicationInfo    applicationInfo = new VkApplicationInfo();

            applicationInfo.apiVersion         = new VkVersion(1, 0, 0);
            applicationInfo.applicationVersion = new VkVersion(1, 0, 0);
            applicationInfo.engineVersion      = new VkVersion(1, 0, 0);
            applicationInfo.pApplicationName   = s_name;
            applicationInfo.pEngineName        = s_name;

            instanceCI.pApplicationInfo = &applicationInfo;

            StackList <IntPtr, Size64Bytes> instanceExtensions = new StackList <IntPtr, Size64Bytes>();
            StackList <IntPtr, Size64Bytes> instanceLayers     = new StackList <IntPtr, Size64Bytes>();

            if (!availableInstanceExtensions.Contains(CommonStrings.VK_KHR_SURFACE_EXTENSION_NAME))
            {
                throw new VeldridException($"The required instance extension was not available: {CommonStrings.VK_KHR_SURFACE_EXTENSION_NAME}");
            }

            instanceExtensions.Add(CommonStrings.VK_KHR_SURFACE_EXTENSION_NAME);

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                if (!availableInstanceExtensions.Contains(CommonStrings.VK_KHR_WIN32_SURFACE_EXTENSION_NAME))
                {
                    throw new VeldridException($"The required instance extension was not available: {CommonStrings.VK_KHR_WIN32_SURFACE_EXTENSION_NAME}");
                }

                instanceExtensions.Add(CommonStrings.VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
            {
                if (!availableInstanceExtensions.Contains(CommonStrings.VK_KHR_XLIB_SURFACE_EXTENSION_NAME))
                {
                    throw new VeldridException($"The required instance extension was not available: {CommonStrings.VK_KHR_XLIB_SURFACE_EXTENSION_NAME}");
                }

                instanceExtensions.Add(CommonStrings.VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
            }
            else
            {
                throw new NotSupportedException("This platform does not support Vulkan.");
            }

            bool debug = false;

#if DEBUG
            debug = true;
#endif
            bool debugReportExtensionAvailable = false;
            if (debug)
            {
                if (availableInstanceExtensions.Contains(CommonStrings.VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
                {
                    debugReportExtensionAvailable = true;
                    instanceExtensions.Add(CommonStrings.VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
                }
                if (availableInstanceLayers.Contains(CommonStrings.StandardValidationLayerName))
                {
                    instanceLayers.Add(CommonStrings.StandardValidationLayerName);
                }
            }

            instanceCI.enabledExtensionCount   = instanceExtensions.Count;
            instanceCI.ppEnabledExtensionNames = (byte **)instanceExtensions.Data;

            instanceCI.enabledLayerCount   = instanceLayers.Count;
            instanceCI.ppEnabledLayerNames = (byte **)instanceLayers.Data;

            VkResult result = vkCreateInstance(ref instanceCI, null, out _instance);
            CheckResult(result);

            if (debug && debugReportExtensionAvailable)
            {
                EnableDebugCallback(VkDebugReportFlagsEXT.WarningEXT | VkDebugReportFlagsEXT.ErrorEXT | VkDebugReportFlagsEXT.PerformanceWarningEXT);
            }
        }
        private VkPipeline CreateNewGraphicsPipeline(ref VkPipelineCacheKey cacheKey)
        {
            VkGraphicsPipelineCreateInfo pipelineCI = VkGraphicsPipelineCreateInfo.New();

            // RenderPass
            pipelineCI.renderPass = cacheKey.RenderPass;
            pipelineCI.subpass    = 0;

            pipelineCI.layout = cacheKey.PipelineLayout;

            // DynamicState
            VkPipelineDynamicStateCreateInfo dynamicStateCI = VkPipelineDynamicStateCreateInfo.New();
            VkDynamicState *dynamicStates = stackalloc VkDynamicState[2];

            dynamicStates[0] = VkDynamicState.Viewport;
            dynamicStates[1] = VkDynamicState.Scissor;
            dynamicStateCI.dynamicStateCount = 2;
            dynamicStateCI.pDynamicStates    = dynamicStates;
            pipelineCI.pDynamicState         = &dynamicStateCI;

            // ColorBlendState
            VkPipelineColorBlendAttachmentState colorBlendAttachementState = new VkPipelineColorBlendAttachmentState();

            colorBlendAttachementState.colorWriteMask      = VkColorComponentFlags.R | VkColorComponentFlags.G | VkColorComponentFlags.B | VkColorComponentFlags.A;
            colorBlendAttachementState.blendEnable         = cacheKey.BlendState.IsBlendEnabled;
            colorBlendAttachementState.srcColorBlendFactor = VkFormats.VeldridToVkBlendFactor(cacheKey.BlendState.SourceColorBlend);
            colorBlendAttachementState.dstColorBlendFactor = VkFormats.VeldridToVkBlendFactor(cacheKey.BlendState.DestinationColorBlend);
            colorBlendAttachementState.colorBlendOp        = VkFormats.VeldridToVkBlendOp(cacheKey.BlendState.ColorBlendFunction);
            colorBlendAttachementState.srcAlphaBlendFactor = VkFormats.VeldridToVkBlendFactor(cacheKey.BlendState.SourceAlphaBlend);
            colorBlendAttachementState.dstAlphaBlendFactor = VkFormats.VeldridToVkBlendFactor(cacheKey.BlendState.DestinationAlphaBlend);
            colorBlendAttachementState.alphaBlendOp        = VkFormats.VeldridToVkBlendOp(cacheKey.BlendState.AlphaBlendFunction);

            VkPipelineColorBlendStateCreateInfo colorBlendStateCI = VkPipelineColorBlendStateCreateInfo.New();

            if (cacheKey.Framebuffer.ColorTexture != null)
            {
                colorBlendStateCI.attachmentCount  = 1;
                colorBlendStateCI.pAttachments     = &colorBlendAttachementState;
                colorBlendStateCI.blendConstants_0 = cacheKey.BlendState.BlendFactor.R;
                colorBlendStateCI.blendConstants_1 = cacheKey.BlendState.BlendFactor.G;
                colorBlendStateCI.blendConstants_2 = cacheKey.BlendState.BlendFactor.B;
                colorBlendStateCI.blendConstants_3 = cacheKey.BlendState.BlendFactor.A;
                pipelineCI.pColorBlendState        = &colorBlendStateCI;
            }

            // DepthStencilState
            VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = VkPipelineDepthStencilStateCreateInfo.New();

            depthStencilStateCI.depthCompareOp   = VkFormats.VeldridToVkDepthComparison(cacheKey.DepthStencilState.DepthComparison);
            depthStencilStateCI.depthWriteEnable = cacheKey.DepthStencilState.IsDepthWriteEnabled;
            depthStencilStateCI.depthTestEnable  = cacheKey.DepthStencilState.IsDepthEnabled;
            pipelineCI.pDepthStencilState        = &depthStencilStateCI;

            // MultisampleState
            VkPipelineMultisampleStateCreateInfo multisampleStateCI = VkPipelineMultisampleStateCreateInfo.New();

            multisampleStateCI.rasterizationSamples = VkSampleCountFlags.Count1;
            pipelineCI.pMultisampleState            = &multisampleStateCI;

            // RasterizationState
            VkPipelineRasterizationStateCreateInfo rasterizationStateCI = ((VkRasterizerState)cacheKey.RasterizerState).RasterizerStateCreateInfo;

            rasterizationStateCI.lineWidth = 1f;
            pipelineCI.pRasterizationState = &rasterizationStateCI;

            // ViewportState
            VkPipelineViewportStateCreateInfo viewportStateCI = VkPipelineViewportStateCreateInfo.New();

            viewportStateCI.viewportCount = 1;
            viewportStateCI.scissorCount  = 1;
            pipelineCI.pViewportState     = &viewportStateCI;

            // InputAssemblyState
            VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = VkPipelineInputAssemblyStateCreateInfo.New();

            inputAssemblyStateCI.topology  = cacheKey.PrimitiveTopology;
            pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;

            // VertexInputState
            VkPipelineVertexInputStateCreateInfo vertexInputStateCI = VkPipelineVertexInputStateCreateInfo.New();

            VertexInputDescription[] inputDescriptions = cacheKey.ShaderSet.InputLayout.InputDescriptions;
            uint bindingCount   = (uint)inputDescriptions.Length;
            uint attributeCount = (uint)inputDescriptions.Sum(desc => desc.Elements.Length);
            VkVertexInputBindingDescription *  bindingDescs   = stackalloc VkVertexInputBindingDescription[(int)bindingCount];
            VkVertexInputAttributeDescription *attributeDescs = stackalloc VkVertexInputAttributeDescription[(int)attributeCount];

            int targetIndex    = 0;
            int targetLocation = 0;

            for (int binding = 0; binding < inputDescriptions.Length; binding++)
            {
                VertexInputDescription inputDesc = inputDescriptions[binding];
                bindingDescs[targetIndex] = new VkVertexInputBindingDescription()
                {
                    binding   = (uint)binding,
                    inputRate = (inputDesc.Elements[0].StorageClassifier == VertexElementInputClass.PerInstance) ? VkVertexInputRate.Instance : VkVertexInputRate.Vertex,
                    stride    = ((VkVertexBuffer)cacheKey.VertexBindings[binding]).Stride
                };

                uint currentOffset = 0;
                for (int location = 0; location < inputDesc.Elements.Length; location++)
                {
                    VertexInputElement inputElement = inputDesc.Elements[location];

                    attributeDescs[targetIndex] = new VkVertexInputAttributeDescription()
                    {
                        format   = VkFormats.VeldridToVkVertexElementFormat(inputElement.ElementFormat),
                        binding  = (uint)binding,
                        location = (uint)(targetLocation + location),
                        offset   = currentOffset
                    };

                    targetIndex   += 1;
                    currentOffset += inputElement.SizeInBytes;
                }

                targetLocation += inputDesc.Elements.Length;
            }

            vertexInputStateCI.vertexBindingDescriptionCount   = bindingCount;
            vertexInputStateCI.pVertexBindingDescriptions      = bindingDescs;
            vertexInputStateCI.vertexAttributeDescriptionCount = attributeCount;
            vertexInputStateCI.pVertexAttributeDescriptions    = attributeDescs;
            pipelineCI.pVertexInputState = &vertexInputStateCI;

            // ShaderStage
            StackList <VkPipelineShaderStageCreateInfo> shaderStageCIs = new StackList <VkPipelineShaderStageCreateInfo>();

            VkPipelineShaderStageCreateInfo vertexStage = VkPipelineShaderStageCreateInfo.New();

            vertexStage.stage  = VkShaderStageFlags.Vertex;
            vertexStage.module = cacheKey.ShaderSet.VertexShader.ShaderModule;
            vertexStage.pName  = CommonStrings.main;
            shaderStageCIs.Add(vertexStage);

            VkPipelineShaderStageCreateInfo fragmentStage = VkPipelineShaderStageCreateInfo.New();

            fragmentStage.stage  = VkShaderStageFlags.Fragment;
            fragmentStage.module = cacheKey.ShaderSet.FragmentShader.ShaderModule;
            fragmentStage.pName  = CommonStrings.main;
            shaderStageCIs.Add(fragmentStage);

            if (cacheKey.ShaderSet.TessellationControlShader != null)
            {
                VkPipelineShaderStageCreateInfo tcStage = VkPipelineShaderStageCreateInfo.New();
                tcStage.stage  = VkShaderStageFlags.TessellationControl;
                tcStage.module = cacheKey.ShaderSet.TessellationControlShader.ShaderModule;
                tcStage.pName  = CommonStrings.main;
                shaderStageCIs.Add(tcStage);
            }

            if (cacheKey.ShaderSet.TessellationEvaluationShader != null)
            {
                VkPipelineShaderStageCreateInfo teStage = VkPipelineShaderStageCreateInfo.New();
                teStage.stage  = VkShaderStageFlags.TessellationEvaluation;
                teStage.module = cacheKey.ShaderSet.TessellationEvaluationShader.ShaderModule;
                teStage.pName  = CommonStrings.main;
                shaderStageCIs.Add(teStage);
            }

            if (cacheKey.ShaderSet.GeometryShader != null)
            {
                VkPipelineShaderStageCreateInfo geometryStage = VkPipelineShaderStageCreateInfo.New();
                geometryStage.stage  = VkShaderStageFlags.Geometry;
                geometryStage.module = cacheKey.ShaderSet.GeometryShader.ShaderModule;
                geometryStage.pName  = CommonStrings.main;
                shaderStageCIs.Add(geometryStage);
            }

            pipelineCI.stageCount = shaderStageCIs.Count;
            pipelineCI.pStages    = (VkPipelineShaderStageCreateInfo *)shaderStageCIs.Data;

            VkResult result = vkCreateGraphicsPipelines(_device, VkPipelineCache.Null, 1, ref pipelineCI, null, out VkPipeline ret);

            CheckResult(result);
            return(ret);
        }