Exemple #1
0
        /**
         * Finish command buffer recording and submit it to a queue
         *
         * @param commandBuffer Command buffer to flush
         * @param queue Queue to submit the command buffer to
         * @param free (Optional) Free the command buffer once it has been submitted (Defaults to true)
         *
         * @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from
         * @note Uses a fence to ensure command buffer has finished executing
         */
        public void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true)
        {
            if (commandBuffer.Handle == NullHandle)
            {
                return;
            }

            Util.CheckResult(vkEndCommandBuffer(commandBuffer));

            VkSubmitInfo submitInfo = VkSubmitInfo.New();

            submitInfo.commandBufferCount = 1;
            submitInfo.pCommandBuffers    = &commandBuffer;

            // Create fence to ensure that the command buffer has finished executing
            VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.New();

            fenceInfo.flags = VkFenceCreateFlags.None;
            VkFence fence;

            Util.CheckResult(vkCreateFence(_logicalDevice, &fenceInfo, null, &fence));

            // Submit to the queue
            Util.CheckResult(vkQueueSubmit(queue, 1, &submitInfo, fence));
            // Wait for the fence to signal that command buffer has finished executing
            Util.CheckResult(vkWaitForFences(_logicalDevice, 1, &fence, True, DEFAULT_FENCE_TIMEOUT));

            vkDestroyFence(_logicalDevice, fence, null);

            if (free)
            {
                vkFreeCommandBuffers(_logicalDevice, CommandPool, 1, &commandBuffer);
            }
        }
Exemple #2
0
        private void CreateFences()
        {
            VkFenceCreateInfo fenceCI = VkFenceCreateInfo.New();

            fenceCI.flags = VkFenceCreateFlags.None;
            vkCreateFence(_device, ref fenceCI, null, out _imageAvailableFence);
        }
        /**
         * Finish command buffer recording and submit it to a queue
         *
         * @param commandBuffer Command buffer to flush
         * @param queue Queue to submit the command buffer to
         * @param free (Optional) Free the command buffer once it has been submitted (Defaults to true)
         *
         * @note The queue that the command buffer is submitted to must be from the same family index as the pool it was allocated from
         * @note Uses a fence to ensure command buffer has finished executing
         */
        public void flushCommandBuffer(VkCommandBuffer commandBuffer, VkQueue queue, bool free = true)
        {
            if (commandBuffer.handle == 0)
            {
                return;
            }

            vkEndCommandBuffer(commandBuffer);

            VkSubmitInfo submitInfo = new VkSubmitInfo();

            submitInfo.sType          = SubmitInfo;
            submitInfo.commandBuffers = commandBuffer;

            // Create fence to ensure that the command buffer has finished executing
            VkFenceCreateInfo fenceInfo = new VkFenceCreateInfo();

            fenceInfo.sType = FenceCreateInfo;
            fenceInfo.flags = 0;
            VkFence fence;

            vkCreateFence(_logicalDevice, &fenceInfo, null, &fence);

            // Submit to the queue
            vkQueueSubmit(queue, 1, &submitInfo, fence);
            // Wait for the fence to signal that command buffer has finished executing
            vkWaitForFences(_logicalDevice, 1, &fence, true, DEFAULT_FENCE_TIMEOUT);

            vkDestroyFence(_logicalDevice, fence, null);

            if (free)
            {
                vkFreeCommandBuffers(_logicalDevice, CommandPool, 1, &commandBuffer);
            }
        }
Exemple #4
0
        public unsafe Fence(Device device, bool isSignaled = false)
        {
            _device = device;
            var createInfo = new VkFenceCreateInfo
            {
                sType = VkStructureType.FenceCreateInfo,
                flags = (
                    isSignaled ?
                    VkFenceCreateFlags.Signaled :
                    VkFenceCreateFlags.None
                    )
            };

            VkFence fence;

            if (VulkanNative.vkCreateFence(
                    device.Handle,
                    &createInfo,
                    null,
                    &fence
                    ) != VkResult.Success)
            {
                throw new Exception("failed to create fence");
            }
            _handle = fence;
        }
        public void FlushCommandBuffer(CommandBuffer cmd)
        {
            if (cmd.vkCmd == NullHandle)
            {
                return;
            }
            if (!cmd.ended)
            {
                cmd.End();
            }

            var vkCmd = cmd.vkCmd;

            VkSubmitInfo submitInfo = VkSubmitInfo.New();

            submitInfo.commandBufferCount = 1;
            submitInfo.pCommandBuffers    = &vkCmd;

            VkFenceCreateInfo fenceInfo = VkFenceCreateInfo.New();

            Util.CheckResult(vkCreateFence(device, &fenceInfo, null, out VkFence fence));

            Util.CheckResult(vkQueueSubmit(queue, 1, &submitInfo, fence));

            //Wait for CommandBuffer to finish
            Util.CheckResult(vkWaitForFences(device, 1, ref fence, true, 10000000000));

            vkDestroyFence(device, fence, null);
        }
        private ulong[] CreateFences()
        {
            var fences = new ulong[(uint)_graphicsSurface.BufferCount];

            var fenceCreateInfo = new VkFenceCreateInfo {
                sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
                pNext = null,
                flags = (uint)VK_FENCE_CREATE_SIGNALED_BIT,
            };

            for (var i = 0; i < fences.Length; i++)
            {
                ulong fence;
                var   result = vkCreateFence(Device, &fenceCreateInfo, pAllocator: null, &fence);

                if (result != VK_SUCCESS)
                {
                    ThrowExternalException(nameof(vkCreateFence), (int)result);
                }

                fences[i] = fence;
            }

            return(fences);
        }
Exemple #7
0
        public VkFence CreateFence(bool signaled = false)
        {
            VkFence           tmp;
            VkFenceCreateInfo info = VkFenceCreateInfo.New();

            info.flags = signaled ? VkFenceCreateFlags.Signaled : 0;
            Utils.CheckResult(vkCreateFence(dev, ref info, IntPtr.Zero, out tmp));
            return(tmp);
        }
Exemple #8
0
        public VkFence(VkGraphicsDevice gd, bool signaled)
        {
            _gd = gd;
            VkFenceCreateInfo fenceCI = VkFenceCreateInfo.New();

            fenceCI.flags = signaled ? VkFenceCreateFlags.Signaled : VkFenceCreateFlags.None;
            VkResult result = vkCreateFence(_gd.Device, ref fenceCI, null, out _fence);

            VulkanUtil.CheckResult(result);
        }
        /// <summary>
        /// Executes multiple deferred command lists.
        /// </summary>
        /// <param name="count">Number of command lists to execute.</param>
        /// <param name="commandLists">The deferred command lists.</param>
        public unsafe void ExecuteCommandLists(int count, CompiledCommandList[] commandLists)
        {
            if (commandLists == null)
            {
                throw new ArgumentNullException(nameof(commandLists));
            }
            if (count > commandLists.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(count));
            }

            var fenceValue = NextFenceValue++;

            // Create a fence
            var fenceCreateInfo = new VkFenceCreateInfo {
                sType = VkStructureType.FenceCreateInfo
            };

            vkCreateFence(nativeDevice, &fenceCreateInfo, null, out var fence);

            using (FenceLock.WriteLock())
            {
                nativeFences.Add(new KeyValuePair <long, VkFence>(fenceValue, fence));
            }

            // Collect resources
            var commandBuffers = stackalloc VkCommandBuffer[count];

            for (int i = 0; i < count; i++)
            {
                commandBuffers[i] = commandLists[i].NativeCommandBuffer;
                RecycleCommandListResources(commandLists[i], fenceValue);
            }

            // Submit commands
            var pipelineStageFlags = VkPipelineStageFlags.BottomOfPipe;
            var submitInfo         = new VkSubmitInfo
            {
                sType = VkStructureType.SubmitInfo,
                commandBufferCount = (uint)count,
                pCommandBuffers    = commandBuffers,
                waitSemaphoreCount = 0U,
                pWaitSemaphores    = null,
                pWaitDstStageMask  = &pipelineStageFlags,
            };

            using (QueueLock.ReadLock())
            {
                vkQueueSubmit(NativeCommandQueue, 1, &submitInfo, fence);
            }

            nativeResourceCollector.FastRelease();
            graphicsResourceLinkCollector.FastRelease();
        }
Exemple #10
0
        public Fence(Device device, bool signaled = true) : base(device)
        {
            VkFenceCreateInfo fenceCreateInfo = new VkFenceCreateInfo()
            {
                sType = VkStructureType.FenceCreateInfo,
                pNext = null,
                flags = signaled ? VkFenceCreateFlags.Signaled : VkFenceCreateFlags.None,
            };


            vkCreateFence(NativeDevice.handle, &fenceCreateInfo, null, out handle);
        }
        internal unsafe long ExecuteCommandListInternal(CompiledCommandList commandList)
        {
            //if (nativeUploadBuffer != VkBuffer.Null)
            //{
            //    NativeDevice.UnmapMemory(nativeUploadBufferMemory);
            //    TemporaryResources.Enqueue(new BufferInfo(NextFenceValue, nativeUploadBuffer, nativeUploadBufferMemory));

            //    nativeUploadBuffer = VkBuffer.Null;
            //    nativeUploadBufferMemory = VkDeviceMemory.Null;
            //}

            var fenceValue = NextFenceValue++;

            // Create new fence
            var fenceCreateInfo = new VkFenceCreateInfo {
                sType = VkStructureType.FenceCreateInfo
            };

            vkCreateFence(nativeDevice, &fenceCreateInfo, null, out var fence);

            using (FenceLock.WriteLock())
            {
                nativeFences.Add(new KeyValuePair <long, VkFence>(fenceValue, fence));
            }

            // Collect resources
            RecycleCommandListResources(commandList, fenceValue);

            // Submit commands
            var nativeCommandBufferCopy = commandList.NativeCommandBuffer;
            var pipelineStageFlags      = VkPipelineStageFlags.BottomOfPipe;

            var submitInfo = new VkSubmitInfo
            {
                sType = VkStructureType.SubmitInfo,
                commandBufferCount = 1,
                pCommandBuffers    = &nativeCommandBufferCopy,
                waitSemaphoreCount = 0U,
                pWaitSemaphores    = null,
                pWaitDstStageMask  = &pipelineStageFlags,
            };

            using (QueueLock.ReadLock())
            {
                vkQueueSubmit(NativeCommandQueue, 1, &submitInfo, fence);
            }

            nativeResourceCollector.Release();
            graphicsResourceLinkCollector.Release();

            return(fenceValue);
        }
Exemple #12
0
 public Fence(Device dev, VkFenceCreateFlag flags)
 {
     Device = dev;
     unsafe
     {
         var info = new VkFenceCreateInfo()
         {
             SType = VkStructureType.FenceCreateInfo,
             PNext = IntPtr.Zero,
             Flags = flags
         };
         Handle = dev.Handle.CreateFence(&info, Instance.AllocationCallbacks);
     }
 }
Exemple #13
0
        void CreateFence(FenceCreateInfo mInfo)
        {
            VkFenceCreateInfo info = new VkFenceCreateInfo();

            info.sType = VkStructureType.FenceCreateInfo;
            info.flags = mInfo.Flags;

            var result = Device.Commands.createFence(Device.Native, ref info, Device.Instance.AllocationCallbacks, out fence);

            if (result != VkResult.Success)
            {
                throw new FenceException(string.Format("Error creating fence: {0}", result));
            }
        }
Exemple #14
0
        private VkFence CreateVulkanFence()
        {
            ThrowIfDisposedOrDisposing(_state, nameof(VulkanGraphicsFence));

            VkFence vulkanFence;

            var fenceCreateInfo = new VkFenceCreateInfo {
                sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
                pNext = null,
                flags = VK_FENCE_CREATE_SIGNALED_BIT,
            };

            ThrowExternalExceptionIfNotSuccess(vkCreateFence(Device.VulkanDevice, &fenceCreateInfo, pAllocator: null, (ulong *)&vulkanFence), nameof(vkCreateFence));

            return(vulkanFence);
        }
Exemple #15
0
        // Command: 116
        /// <summary>vkCreateFence - Create a new fence object
        /// </summary>
        /// <param name="pCreateInfo"> pCreateInfo is a pointer to an instance of the
        /// VkFenceCreateInfo structure which contains information about how
        /// the fence is to be created.</param>
        /// <param name="pAllocator"> pAllocator controls host memory allocation as described in the
        /// Memory Allocation chapter.</param>
        public VkFence CreateFence(VkFenceCreateInfo *pCreateInfo = null, VkAllocationCallbacks *pAllocator = null)
        {
            VkFence fence;

            if (pCreateInfo == null)
            {
                var info = new VkFenceCreateInfo {
                    sType = VkStructureType.FenceCreateInfo
                };
                pCreateInfo = &info;
            }

            vkAPI.vkCreateFence(this, pCreateInfo, pAllocator, &fence);

            return(fence);
        }
Exemple #16
0
        public void Build(int devID)
        {
            if (!locked)
            {
                unsafe
                {
                    var fenceCreateInfo = new VkFenceCreateInfo()
                    {
                        sType = VkStructureType.StructureTypeFenceCreateInfo,
                        flags = CreateSignaled ? VkFenceCreateFlags.FenceCreateSignaledBit : 0
                    };

                    IntPtr hndl_l = IntPtr.Zero;
                    if (vkCreateFence(GraphicsDevice.GetDeviceInfo(devID).Device, fenceCreateInfo.Pointer(), null, &hndl_l) != VkResult.Success)
                    {
                        throw new Exception("Failed to create fence.");
                    }
                    hndl       = hndl_l;
                    this.devID = devID;

                    if (GraphicsDevice.EnableValidation && Name != null)
                    {
                        var objName = new VkDebugUtilsObjectNameInfoEXT()
                        {
                            sType        = VkStructureType.StructureTypeDebugUtilsObjectNameInfoExt,
                            pObjectName  = Name,
                            objectType   = VkObjectType.ObjectTypeFence,
                            objectHandle = (ulong)hndl
                        };
                        GraphicsDevice.SetDebugUtilsObjectNameEXT(GraphicsDevice.GetDeviceInfo(devID).Device, objName.Pointer());
                    }
                }
                locked = true;
            }
            else
            {
                throw new Exception("Fence is locked.");
            }
        }
        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 unsafe void CreateBackBuffers()
        {
            backbuffer.OnDestroyed();

            // Create the texture object
            var backBufferDescription = new TextureDescription
            {
                ArraySize        = 1,
                Dimension        = TextureDimension.Texture2D,
                Height           = Description.BackBufferHeight,
                Width            = Description.BackBufferWidth,
                Depth            = 1,
                Flags            = TextureFlags.RenderTarget,
                Format           = Description.BackBufferFormat,
                MipLevels        = 1,
                MultisampleCount = MultisampleCount.None,
                Usage            = GraphicsResourceUsage.Default
            };

            backbuffer.InitializeWithoutResources(backBufferDescription);

            var createInfo = new VkImageViewCreateInfo
            {
                sType            = VkStructureType.ImageViewCreateInfo,
                subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1),
                format           = backbuffer.NativeFormat,
                viewType         = VkImageViewType.Image2D,
            };

            // We initialize swapchain images to PresentSource, since we swap them out while in this layout.
            backbuffer.NativeAccessMask = VkAccessFlags.MemoryRead;
            backbuffer.NativeLayout     = VkImageLayout.PresentSrcKHR;

            var imageMemoryBarrier = new VkImageMemoryBarrier
            {
                sType            = VkStructureType.ImageMemoryBarrier,
                subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, 1, 0, 1),
                oldLayout        = VkImageLayout.Undefined,
                newLayout        = VkImageLayout.PresentSrcKHR,
                srcAccessMask    = VkAccessFlags.None,
                dstAccessMask    = VkAccessFlags.MemoryRead
            };

            var commandBuffer = GraphicsDevice.NativeCopyCommandBuffer;
            var beginInfo     = new VkCommandBufferBeginInfo {
                sType = VkStructureType.CommandBufferBeginInfo
            };

            vkBeginCommandBuffer(commandBuffer, &beginInfo);

            var buffers = vkGetSwapchainImagesKHR(GraphicsDevice.NativeDevice, swapChain);

            swapchainImages = new SwapChainImageInfo[buffers.Length];

            for (int i = 0; i < buffers.Length; i++)
            {
                // Create image views
                swapchainImages[i].NativeImage = createInfo.image = buffers[i];
                vkCreateImageView(GraphicsDevice.NativeDevice, &createInfo, null, out swapchainImages[i].NativeColorAttachmentView);

                // Transition to default layout
                imageMemoryBarrier.image = buffers[i];
                vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.AllCommands, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier);
            }

            // Close and submit
            vkEndCommandBuffer(commandBuffer);

            var submitInfo = new VkSubmitInfo
            {
                sType = VkStructureType.SubmitInfo,
                commandBufferCount = 1,
                pCommandBuffers    = &commandBuffer,
            };

            vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, VkFence.Null);
            vkQueueWaitIdle(GraphicsDevice.NativeCommandQueue);
            vkResetCommandBuffer(commandBuffer, VkCommandBufferResetFlags.None);

            // need to make a fence, but can immediately reset it, as it acts as a dummy
            var fenceCreateInfo = new VkFenceCreateInfo {
                sType = VkStructureType.FenceCreateInfo
            };

            vkCreateFence(GraphicsDevice.NativeDevice, &fenceCreateInfo, null, out presentFence);

            vkAcquireNextImageKHR(GraphicsDevice.NativeDevice, swapChain, ulong.MaxValue, VkSemaphore.Null, presentFence, out currentBufferIndex);

            fixed(VkFence *fences = &presentFence)
            {
                vkResetFences(GraphicsDevice.NativeDevice, 1, fences);
            }

            // Apply the first swap chain image to the texture
            backbuffer.SetNativeHandles(swapchainImages[currentBufferIndex].NativeImage, swapchainImages[currentBufferIndex].NativeColorAttachmentView);
        }
        /// <summary>
        /// Explicitly recreate buffer with given data. Usually called after a <see cref="GraphicsDevice"/> reset.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dataPointer"></param>
        public unsafe void Recreate(IntPtr dataPointer)
        {
            // capture vertex information for things less than ~512 verts for possible later easy batching
            if (dataPointer != IntPtr.Zero &&
                (ViewFlags == BufferFlags.VertexBuffer && bufferDescription.SizeInBytes <= CaptureVertexBuffersOfSize ||
                 ViewFlags == BufferFlags.IndexBuffer && bufferDescription.SizeInBytes <= CaptureIndexBuffersOfSize))
            {
                VertIndexData = new byte[Description.SizeInBytes];
                fixed(byte *vid = &VertIndexData[0])
                {
                    Utilities.CopyMemory((IntPtr)vid, dataPointer, VertIndexData.Length);
                }
            }
            else
            {
                VertIndexData = null;
            }

            var createInfo = new VkBufferCreateInfo
            {
                sType = VkStructureType.BufferCreateInfo,
                size  = (ulong)bufferDescription.SizeInBytes,
                flags = VkBufferCreateFlags.None,
            };

            createInfo.usage |= VkBufferUsageFlags.TransferSrc;

            // We always fill using transfer
            //if (bufferDescription.Usage != GraphicsResourceUsage.Immutable)
            createInfo.usage |= VkBufferUsageFlags.TransferDst;

            if (Usage == GraphicsResourceUsage.Staging)
            {
                NativeAccessMask         = VkAccessFlags.HostRead | VkAccessFlags.HostWrite;
                NativePipelineStageMask |= VkPipelineStageFlags.Host;
            }
            else
            {
                if ((ViewFlags & BufferFlags.VertexBuffer) != 0)
                {
                    createInfo.usage        |= VkBufferUsageFlags.VertexBuffer;
                    NativeAccessMask        |= VkAccessFlags.VertexAttributeRead;
                    NativePipelineStageMask |= VkPipelineStageFlags.VertexInput;
                }

                if ((ViewFlags & BufferFlags.IndexBuffer) != 0)
                {
                    createInfo.usage        |= VkBufferUsageFlags.IndexBuffer;
                    NativeAccessMask        |= VkAccessFlags.IndexRead;
                    NativePipelineStageMask |= VkPipelineStageFlags.VertexInput;
                }

                if ((ViewFlags & BufferFlags.ConstantBuffer) != 0)
                {
                    createInfo.usage        |= VkBufferUsageFlags.UniformBuffer;
                    NativeAccessMask        |= VkAccessFlags.UniformRead;
                    NativePipelineStageMask |= VkPipelineStageFlags.VertexShader | VkPipelineStageFlags.FragmentShader;
                }

                if ((ViewFlags & BufferFlags.ShaderResource) != 0)
                {
                    createInfo.usage        |= VkBufferUsageFlags.UniformTexelBuffer;
                    NativeAccessMask        |= VkAccessFlags.ShaderRead;
                    NativePipelineStageMask |= VkPipelineStageFlags.VertexShader | VkPipelineStageFlags.FragmentShader;

                    if ((ViewFlags & BufferFlags.UnorderedAccess) != 0)
                    {
                        createInfo.usage |= VkBufferUsageFlags.StorageTexelBuffer;
                        NativeAccessMask |= VkAccessFlags.ShaderWrite;
                    }
                }
            }

            // Create buffer
            vkCreateBuffer(GraphicsDevice.NativeDevice, &createInfo, null, out NativeBuffer);

            // Allocate memory
            var memoryProperties = VkMemoryPropertyFlags.DeviceLocal;

            if (bufferDescription.Usage == GraphicsResourceUsage.Staging || Usage == GraphicsResourceUsage.Dynamic)
            {
                memoryProperties = VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent;
            }

            vkGetBufferMemoryRequirements(GraphicsDevice.NativeDevice, NativeBuffer, out var memoryRequirements);

            AllocateMemory(memoryProperties, memoryRequirements);

            if (NativeMemory != VkDeviceMemory.Null)
            {
                vkBindBufferMemory(GraphicsDevice.NativeDevice, NativeBuffer, NativeMemory, 0);
            }

            if (SizeInBytes > 0)
            {
                // Begin copy command buffer
                var commandBufferAllocateInfo = new VkCommandBufferAllocateInfo
                {
                    sType              = VkStructureType.CommandBufferAllocateInfo,
                    commandPool        = GraphicsDevice.NativeCopyCommandPool,
                    commandBufferCount = 1,
                    level              = VkCommandBufferLevel.Primary
                };
                VkCommandBuffer commandBuffer;

                lock (BufferLocker)
                {
                    vkAllocateCommandBuffers(GraphicsDevice.NativeDevice, &commandBufferAllocateInfo, &commandBuffer);
                }

                var beginInfo = new VkCommandBufferBeginInfo {
                    sType = VkStructureType.CommandBufferBeginInfo, flags = VkCommandBufferUsageFlags.OneTimeSubmit
                };
                vkBeginCommandBuffer(commandBuffer, &beginInfo);

                GraphicsDevice.UploadBuffer?uploadBuffer = null;

                // Copy to upload buffer
                if (dataPointer != IntPtr.Zero)
                {
                    if (Usage == GraphicsResourceUsage.Dynamic)
                    {
                        void *uploadMemory;
                        vkMapMemory(GraphicsDevice.NativeDevice, NativeMemory, 0, (ulong)SizeInBytes, VkMemoryMapFlags.None, &uploadMemory);
                        Utilities.CopyMemory((IntPtr)uploadMemory, dataPointer, SizeInBytes);
                        lock (BufferLocker)
                        {
                            vkUnmapMemory(GraphicsDevice.NativeDevice, NativeMemory);
                        }
                    }
                    else
                    {
                        var sizeInBytes = bufferDescription.SizeInBytes;
                        int uploadOffset;
                        GraphicsDevice.AllocateOneTimeUploadBuffer(sizeInBytes, out var upBuf);
                        uploadBuffer = upBuf;

                        Utilities.CopyMemory(uploadBuffer.Value.address, dataPointer, sizeInBytes);

                        // Barrier
                        var memoryBarrier = new VkBufferMemoryBarrier(uploadBuffer.Value.buffer, VkAccessFlags.HostWrite, VkAccessFlags.TransferRead, 0, (ulong)sizeInBytes);
                        vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer, VkDependencyFlags.None, 0, null, 1, &memoryBarrier, 0, null);

                        // Copy
                        var bufferCopy = new VkBufferCopy
                        {
                            srcOffset = 0,
                            dstOffset = 0,
                            size      = (uint)sizeInBytes
                        };

                        vkCmdCopyBuffer(commandBuffer, uploadBuffer.Value.buffer, NativeBuffer, 1, &bufferCopy);
                    }
                }
                else
                {
                    vkCmdFillBuffer(commandBuffer, NativeBuffer, 0, (uint)bufferDescription.SizeInBytes, 0);
                }

                // Barrier
                var bufferMemoryBarrier = new VkBufferMemoryBarrier(NativeBuffer, VkAccessFlags.TransferWrite, NativeAccessMask);
                vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 1, &bufferMemoryBarrier, 0, null);

                var submitInfo = new VkSubmitInfo
                {
                    sType = VkStructureType.SubmitInfo,
                    commandBufferCount = 1,
                    pCommandBuffers    = &commandBuffer,
                };

                var fenceCreateInfo = new VkFenceCreateInfo {
                    sType = VkStructureType.FenceCreateInfo
                };
                vkCreateFence(GraphicsDevice.NativeDevice, &fenceCreateInfo, null, out var fence);

                // Close and submit
                vkEndCommandBuffer(commandBuffer);

                using (GraphicsDevice.QueueLock.ReadLock())
                {
                    vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, fence);
                }

                vkWaitForFences(GraphicsDevice.NativeDevice, 1, &fence, true, ulong.MaxValue);

                vkFreeCommandBuffers(GraphicsDevice.NativeDevice, GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer);
                vkDestroyFence(GraphicsDevice.NativeDevice, fence, null);

                if (uploadBuffer.HasValue)
                {
                    GraphicsDevice.FreeOneTimeUploadBuffer(uploadBuffer.Value);
                }

                InitializeViews();
            }
        }
Exemple #20
0
 public static extern VkResult CreateFence(
     VkDevice device,
     ref VkFenceCreateInfo pCreateInfo,
     IntPtr pAllocator,
     out VkFence pFence
     );
Exemple #21
0
        internal static VulkanImage Texture2D(VulkanContext ctx, TextureData tex2D)
        {
            ulong    size = (ulong)tex2D.Mipmaps[0].Size;
            VkBuffer stagingBuffer;
            var      bufferCreateInfo = new VkBufferCreateInfo
            {
                sType = VkStructureType.BufferCreateInfo,
                pNext = null,
                size  = (ulong)tex2D.Mipmaps[0].Size,
                usage = VkBufferUsageFlags.TransferSrc
            };

            vkCreateBuffer(ctx.Device, &bufferCreateInfo, null, out stagingBuffer);

            vkGetPhysicalDeviceMemoryProperties(ctx.PhysicalDevice, out VkPhysicalDeviceMemoryProperties memoryProperties);
            vkGetBufferMemoryRequirements(ctx.Device, stagingBuffer, out VkMemoryRequirements stagingMemReq);
            uint heapIndex = BufferHelper.GetMemoryTypeIndex(stagingMemReq.memoryTypeBits, VkMemoryPropertyFlags.HostVisible, memoryProperties);

            VkMemoryAllocateInfo memAllocInfo = new VkMemoryAllocateInfo()
            {
                sType           = VkStructureType.MemoryAllocateInfo,
                pNext           = null,
                allocationSize  = stagingMemReq.size,
                memoryTypeIndex = heapIndex
            };

            VkDeviceMemory stagingMemory;
            VkResult       result = vkAllocateMemory(ctx.Device, &memAllocInfo, null, &stagingMemory);

            result.CheckResult();

            result = vkBindBufferMemory(ctx.Device, stagingBuffer, stagingMemory, 0);
            result.CheckResult();

            void *vertexPtr;

            result = vkMapMemory(ctx.Device, stagingMemory, 0, (ulong)tex2D.Mipmaps[0].Size, 0, &vertexPtr);
            result.CheckResult();

            fixed(byte *dataPtr = &tex2D.Mipmaps[0].Data[0])
            {
                Buffer.MemoryCopy(dataPtr, vertexPtr, size, size);
            }

            vkUnmapMemory(ctx.Device, stagingMemory);

            // Setup buffer copy regions for each mip level.
            var bufferCopyRegions = new VkBufferImageCopy[tex2D.Mipmaps.Length]; // TODO: stackalloc
            int offset            = 0;

            for (int i = 0; i < bufferCopyRegions.Length; i++)
            {
                // TODO: from VulkanCore, doesn't look correct (reassigns bufferCopyRegions in each loop)
                bufferCopyRegions = new[]
                {
                    new VkBufferImageCopy
                    {
                        imageSubresource = new VkImageSubresourceLayers(VkImageAspectFlags.Color, (uint)i, 0, 1),
                        imageExtent      = tex2D.Mipmaps[0].Extent,
                        bufferOffset     = (ulong)offset
                    }
                };
                offset += tex2D.Mipmaps[i].Size;
            }

            // Create optimal tiled target image.
            var createInfo = new VkImageCreateInfo
            {
                sType         = VkStructureType.ImageCreateInfo,
                pNext         = null,
                imageType     = VkImageType.Image2D,
                format        = tex2D.Format,
                mipLevels     = (uint)tex2D.Mipmaps.Length,
                arrayLayers   = 1,
                samples       = VkSampleCountFlags.Count1,
                tiling        = VkImageTiling.Optimal,
                sharingMode   = VkSharingMode.Exclusive,
                initialLayout = VkImageLayout.Undefined,
                extent        = tex2D.Mipmaps[0].Extent,
                usage         = VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferDst
            };

            VkImage image;

            result = vkCreateImage(ctx.Device, &createInfo, null, out image);
            result.CheckResult();

            VkMemoryRequirements imageMemReq;

            vkGetImageMemoryRequirements(ctx.Device, image, out imageMemReq);

            vkGetPhysicalDeviceMemoryProperties(ctx.PhysicalDevice, out VkPhysicalDeviceMemoryProperties imageMemoryProperties);

            uint imageHeapIndex = BufferHelper.GetMemoryTypeIndex(imageMemReq.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal, imageMemoryProperties);

            var allocInfo = new VkMemoryAllocateInfo
            {
                sType           = VkStructureType.MemoryAllocateInfo,
                pNext           = null,
                allocationSize  = imageMemReq.size,
                memoryTypeIndex = imageHeapIndex,
            };
            VkDeviceMemory memory;

            result = vkAllocateMemory(ctx.Device, &allocInfo, null, &memory);
            result.CheckResult();

            result = vkBindImageMemory(ctx.Device, image, memory, 0);
            result.CheckResult();

            var subresourceRange = new VkImageSubresourceRange(VkImageAspectFlags.Color, 0, (uint)tex2D.Mipmaps.Length, 0, 1);

            // Copy the data from staging buffers to device local buffers.
            var allocInfo2 = new VkCommandBufferAllocateInfo()
            {
                sType       = VkStructureType.CommandBufferAllocateInfo,
                commandPool = ctx.GraphicsCommandPool,

                level = VkCommandBufferLevel.Primary,
                commandBufferCount = 1,
            };
            VkCommandBuffer cmdBuffer;

            vkAllocateCommandBuffers(ctx.Device, &allocInfo2, &cmdBuffer);

            VkCommandBufferBeginInfo beginInfo = new VkCommandBufferBeginInfo()
            {
                sType = VkStructureType.CommandBufferBeginInfo,
                flags = VkCommandBufferUsageFlags.OneTimeSubmit,
            };

            vkBeginCommandBuffer(cmdBuffer, &beginInfo);

            VkImageMemoryBarrier imageMemoryBarrier = new VkImageMemoryBarrier
            {
                sType               = VkStructureType.ImageMemoryBarrier,
                pNext               = null,
                image               = image,
                subresourceRange    = subresourceRange,
                srcAccessMask       = 0,
                dstAccessMask       = VkAccessFlags.TransferWrite,
                oldLayout           = VkImageLayout.Undefined,
                newLayout           = VkImageLayout.TransferDstOptimal,
                srcQueueFamilyIndex = QueueFamilyIgnored,
                dstQueueFamilyIndex = QueueFamilyIgnored
            };

            vkCmdPipelineBarrier(cmdBuffer, VkPipelineStageFlags.TopOfPipe, VkPipelineStageFlags.Transfer, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier);

            fixed(VkBufferImageCopy *regionsPtr = bufferCopyRegions)
            {
                vkCmdCopyBufferToImage(cmdBuffer, stagingBuffer, image, VkImageLayout.TransferDstOptimal, (uint)bufferCopyRegions.Length, regionsPtr);
            }

            VkImageMemoryBarrier imageMemoryBarrier2 = new VkImageMemoryBarrier
            {
                sType               = VkStructureType.ImageMemoryBarrier,
                pNext               = null,
                image               = image,
                subresourceRange    = subresourceRange,
                srcAccessMask       = VkAccessFlags.TransferWrite,
                dstAccessMask       = VkAccessFlags.ShaderRead,
                oldLayout           = VkImageLayout.TransferDstOptimal,
                newLayout           = VkImageLayout.ShaderReadOnlyOptimal,
                srcQueueFamilyIndex = (uint)QueueFamilyIgnored,
                dstQueueFamilyIndex = (uint)QueueFamilyIgnored
            };

            vkCmdPipelineBarrier(cmdBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier2);

            vkEndCommandBuffer(cmdBuffer);

            // Submit.
            VkFenceCreateInfo fenceCreateInfo = new VkFenceCreateInfo
            {
                sType = VkStructureType.FenceCreateInfo,
                pNext = null
            };
            VkFence fence;

            result = vkCreateFence(ctx.Device, &fenceCreateInfo, null, out fence);
            result.CheckResult();

            var submitInfo = new VkSubmitInfo
            {
                sType = VkStructureType.SubmitInfo,
                pNext = null,
                commandBufferCount = 1,
                pCommandBuffers    = &cmdBuffer
            };

            vkQueueSubmit(ctx.GraphicsQueue, submitInfo, fence);

            result = vkWaitForFences(ctx.Device, 1, &fence, false, ulong.MaxValue);
            result.CheckResult();

            // Cleanup staging resources.
            vkDestroyFence(ctx.Device, fence, null);
            vkFreeMemory(ctx.Device, stagingMemory, null);
            vkDestroyBuffer(ctx.Device, stagingBuffer, null);

            // Create image view.
            VkImageViewCreateInfo imageViewCreateInfo = new VkImageViewCreateInfo()
            {
                sType            = VkStructureType.ImageViewCreateInfo,
                image            = image,
                viewType         = VkImageViewType.Image2D,
                format           = tex2D.Format,
                subresourceRange = subresourceRange
            };

            VkImageView view;

            vkCreateImageView(ctx.Device, &imageViewCreateInfo, null, out view);

            return(new VulkanImage(ctx, image, memory, view, tex2D.Format));
        }
Exemple #22
0
        private unsafe void InitializeImage(DataBox[] dataBoxes)
        {
            // Begin copy command buffer
            var commandBufferAllocateInfo = new VkCommandBufferAllocateInfo
            {
                sType              = VkStructureType.CommandBufferAllocateInfo,
                commandPool        = GraphicsDevice.NativeCopyCommandPool,
                commandBufferCount = 1,
                level              = VkCommandBufferLevel.Primary
            };
            VkCommandBuffer commandBuffer;

            // do some setup outside of the lock
            var beginInfo = new VkCommandBufferBeginInfo {
                sType = VkStructureType.CommandBufferBeginInfo, flags = VkCommandBufferUsageFlags.OneTimeSubmit
            };
            var fenceCreateInfo = new VkFenceCreateInfo {
                sType = VkStructureType.FenceCreateInfo
            };

            vkCreateFence(GraphicsDevice.NativeDevice, &fenceCreateInfo, null, out var fence);
            var submitInfo = new VkSubmitInfo
            {
                sType = VkStructureType.SubmitInfo,
                commandBufferCount = 1,
            };

            using (GraphicsDevice.QueueLock.ReadLock())
            {
                vkAllocateCommandBuffers(GraphicsDevice.NativeDevice, &commandBufferAllocateInfo, &commandBuffer);
            }

            vkBeginCommandBuffer(commandBuffer, &beginInfo);

            if (dataBoxes != null && dataBoxes.Length > 0)
            {
                // Buffer-to-image copies need to be aligned to the pixel size and 4 (always a power of 2)
                var blockSize     = Format.IsCompressed() ? NativeFormat.BlockSizeInBytes() : TexturePixelSize;
                var alignmentMask = (blockSize < 4 ? 4 : blockSize) - 1;

                int totalSize = dataBoxes.Length * alignmentMask;
                for (int i = 0; i < dataBoxes.Length; i++)
                {
                    totalSize += dataBoxes[i].SlicePitch;
                }

                var uploadMemory = GraphicsDevice.AllocateUploadBuffer(totalSize, out var upBuf, out int uploadOffset);

                // Upload buffer barrier
                var bufferMemoryBarrier = new VkBufferMemoryBarrier(upBuf, VkAccessFlags.HostWrite, VkAccessFlags.TransferRead, (ulong)uploadOffset, (ulong)totalSize);

                // Image barrier
                var initialBarrier = new VkImageMemoryBarrier(NativeImage, new VkImageSubresourceRange(NativeImageAspect, 0, uint.MaxValue, 0, uint.MaxValue), VkAccessFlags.None, VkAccessFlags.TransferWrite, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal);
                vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Host, VkPipelineStageFlags.Transfer, VkDependencyFlags.None, 0, null, 1, &bufferMemoryBarrier, 1, &initialBarrier);

                // Copy data boxes to upload buffer
                var copies = new VkBufferImageCopy[dataBoxes.Length];
                for (int i = 0; i < copies.Length; i++)
                {
                    var slicePitch = dataBoxes[i].SlicePitch;

                    int arraySlice        = i / MipLevels;
                    int mipSlice          = i % MipLevels;
                    var mipMapDescription = GetMipMapDescription(mipSlice);

                    var alignment = ((uploadOffset + alignmentMask) & ~alignmentMask) - uploadOffset;
                    uploadMemory += alignment;
                    uploadOffset += alignment;

                    Utilities.CopyMemory(uploadMemory, dataBoxes[i].DataPointer, slicePitch);

                    // TODO VULKAN: Check if pitches are valid
                    copies[i] = new VkBufferImageCopy
                    {
                        bufferOffset      = (ulong)uploadOffset,
                        imageSubresource  = new VkImageSubresourceLayers(VkImageAspectFlags.Color, (uint)mipSlice, (uint)arraySlice, 1),
                        bufferRowLength   = 0, //(uint)(dataBoxes[i].RowPitch / pixelSize),
                        bufferImageHeight = 0, //(uint)(dataBoxes[i].SlicePitch / dataBoxes[i].RowPitch),
                        imageOffset       = new Vortice.Vulkan.VkOffset3D(0, 0, 0),
                        imageExtent       = new Vortice.Vulkan.VkExtent3D(mipMapDescription.Width, mipMapDescription.Height, mipMapDescription.Depth)
                    };

                    uploadMemory += slicePitch;
                    uploadOffset += slicePitch;
                }

                // Copy from upload buffer to image
                using (GraphicsDevice.QueueLock.ReadLock())
                {
                    fixed(VkBufferImageCopy *copiesPointer = copies)
                    {
                        vkCmdCopyBufferToImage(commandBuffer, upBuf, NativeImage, VkImageLayout.TransferDstOptimal, (uint)copies.Length, copiesPointer);
                    }
                }

                IsInitialized = true;
            }

            // Transition to default layout
            var imageMemoryBarrier = new VkImageMemoryBarrier(NativeImage,
                                                              new VkImageSubresourceRange(NativeImageAspect, 0, uint.MaxValue, 0, uint.MaxValue),
                                                              dataBoxes == null || dataBoxes.Length == 0 ? VkAccessFlags.None : VkAccessFlags.TransferWrite, NativeAccessMask,
                                                              dataBoxes == null || dataBoxes.Length == 0 ? VkImageLayout.Undefined : VkImageLayout.TransferDstOptimal, NativeLayout);

            submitInfo.pCommandBuffers = &commandBuffer;
            vkCmdPipelineBarrier(commandBuffer, VkPipelineStageFlags.Transfer, VkPipelineStageFlags.AllCommands, VkDependencyFlags.None, 0, null, 0, null, 1, &imageMemoryBarrier);
            vkEndCommandBuffer(commandBuffer);

            // Close and submit
            using (GraphicsDevice.QueueLock.WriteLock())
            {
                vkQueueSubmit(GraphicsDevice.NativeCommandQueue, 1, &submitInfo, fence);
            }

            vkWaitForFences(GraphicsDevice.NativeDevice, 1, &fence, Vortice.Vulkan.VkBool32.True, ulong.MaxValue);
            vkFreeCommandBuffers(GraphicsDevice.NativeDevice, GraphicsDevice.NativeCopyCommandPool, 1, &commandBuffer);
            vkDestroyFence(GraphicsDevice.NativeDevice, fence, null);
        }
        public static void CreateTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, byte[] imageSource, out VkImage image, out VkDeviceMemory memory, VkQueue queue, VkCommandBuffer workCommandBuffer)
        {
            var imageCreateInfo = new VkImageCreateInfo()
            {
                imageType = VkImageType.VK_IMAGE_TYPE_2D,
                format    = VkFormat.VK_FORMAT_B8G8R8A8_UNORM,
                extent    = new VkExtent3D(width, height),
                mipLevels = 1,
                usage     = VkImageUsageFlags.VK_IMAGE_USAGE_TRANSFER_DST_BIT | VkImageUsageFlags.VK_IMAGE_USAGE_SAMPLED_BIT,
            };

            VulkanAPI.vkCreateImage(device, ref imageCreateInfo, out image);
            VkMemoryRequirements requirements;

            VulkanAPI.vkGetImageMemoryRequirements(device, image, out requirements);
            VkMemoryPropertyFlags memoryFlags = VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

            VulkanAPI.vkAllocateMemory(device, physicalDevice, ref requirements, memoryFlags, out memory);
            VulkanAPI.vkBindImageMemory(device, image, memory, 0);

            // ステージングバッファ経由で転送.
            VkBuffer       staging;
            VkDeviceMemory stagingMemory;
            var            stagingFlags = VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VkMemoryPropertyFlags.VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;

            CreateBuffer(device, physicalDevice, imageSource.Length, VkBufferUsageFlags.VK_BUFFER_USAGE_TRANSFER_SRC_BIT, stagingFlags, out staging, out stagingMemory);
            MappedMemoryStream mappedStream;

            VulkanAPI.vkMapMemory(device, stagingMemory, 0, VkDeviceSize.VK_WHOLE_SIZE, 0, out mappedStream);
            mappedStream.Write(imageSource);
            VulkanAPI.vkUnmapMemory(device, stagingMemory);

            var copyRegion = new VkBufferImageCopy()
            {
                imageExtent       = new VkExtent3D(width, height),
                bufferImageHeight = (uint)height,
                imageSubresource  = new VkImageSubresourceLayers()
                {
                    aspectMask     = VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT,
                    mipLevel       = 0,
                    baseArrayLayer = 0,
                    layerCount     = 1,
                }
            };

            // 一時的なコマンドバッファで転送処理.
            var command = workCommandBuffer;

            VulkanAPI.vkBeginCommandBuffer(command);
            setImageMemoryBarrier(command,
                                  0, VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT,
                                  VkImageLayout.VK_IMAGE_LAYOUT_UNDEFINED, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                                  image, VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT);
            VulkanAPI.vkCmdCopyBufferToImage(command, staging, image, VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, new[] { copyRegion });
            setImageMemoryBarrier(command,
                                  VkAccessFlags.VK_ACCESS_TRANSFER_WRITE_BIT, VkAccessFlags.VK_ACCESS_SHADER_READ_BIT,
                                  VkImageLayout.VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VkImageLayout.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                                  image, VkImageAspectFlags.VK_IMAGE_ASPECT_COLOR_BIT);
            VulkanAPI.vkEndCommandBuffer(command);

            var submitInfo = new VkSubmitInfo()
            {
                commandBuffers = new[] { command },
            };
            var fenceCreateInfo = new VkFenceCreateInfo()
            {
            };
            VkFence fence;

            VulkanAPI.vkCreateFence(device, fenceCreateInfo, out fence);
            VulkanAPI.vkQueueSubmit(queue, new[] { submitInfo }, fence);

            VulkanAPI.vkWaitForFences(device, new[] { fence }, true, ulong.MaxValue);
            VulkanAPI.vkDestroyFence(device, fence);
            VulkanAPI.vkDestroyBuffer(device, staging);
            VulkanAPI.vkFreeMemory(device, stagingMemory);
        }
Exemple #24
0
        private static VulkanBuffer GetBuffer <T>(VulkanContext ctx, T[] data, VkBufferUsageFlags usage) where T : unmanaged
        {
            long size = data.Length * Unsafe.SizeOf <T>();

            // Create a staging buffer that is writable by host.
            var stagingCreateInfo = new VkBufferCreateInfo
            {
                sType       = VkStructureType.BufferCreateInfo,
                pNext       = null,
                usage       = VkBufferUsageFlags.TransferSrc,
                sharingMode = VkSharingMode.Exclusive,
                size        = (ulong)size
            };

            VkBuffer stagingBuffer;
            VkResult result = vkCreateBuffer(
                ctx.Device,
                &stagingCreateInfo,
                null,
                out stagingBuffer);

            result.CheckResult();

            VkMemoryRequirements stagingReq;

            vkGetBufferMemoryRequirements(ctx.Device, stagingBuffer, out stagingReq);

            vkGetPhysicalDeviceMemoryProperties(ctx.PhysicalDevice, out VkPhysicalDeviceMemoryProperties memoryProperties);

            uint stagingMemoryTypeIndex = BufferHelper.GetMemoryTypeIndex(stagingReq.memoryTypeBits, VkMemoryPropertyFlags.HostVisible | VkMemoryPropertyFlags.HostCoherent, memoryProperties);

            VkMemoryAllocateInfo allocateInfo = new VkMemoryAllocateInfo
            {
                sType           = VkStructureType.MemoryAllocateInfo,
                pNext           = null,
                allocationSize  = stagingReq.size,
                memoryTypeIndex = stagingMemoryTypeIndex
            };

            VkDeviceMemory stagingMemory;

            result = vkAllocateMemory(ctx.Device, &allocateInfo, null, &stagingMemory);
            result.CheckResult();

            void *vertexPtr;

            result = vkMapMemory(ctx.Device, stagingMemory, 0, (ulong)stagingReq.size, 0, &vertexPtr);
            result.CheckResult();

            fixed(T *dataPtr = &data[0])
            {
                System.Buffer.MemoryCopy(dataPtr, vertexPtr, size, size);
            }

            vkUnmapMemory(ctx.Device, stagingMemory);

            result = vkBindBufferMemory(ctx.Device, stagingBuffer, stagingMemory, 0);

            // Create a device local buffer where the data will be copied and which will be used for rendering.
            var bufferCreateInfo = new VkBufferCreateInfo
            {
                sType       = VkStructureType.BufferCreateInfo,
                pNext       = null,
                usage       = usage | VkBufferUsageFlags.TransferDst,
                sharingMode = VkSharingMode.Exclusive,
                size        = (ulong)size
            };

            VkBuffer buffer;

            result = vkCreateBuffer(
                ctx.Device,
                &bufferCreateInfo,
                null,
                out buffer);
            result.CheckResult();

            VkMemoryRequirements req;

            vkGetBufferMemoryRequirements(ctx.Device, buffer, out req);

            vkGetPhysicalDeviceMemoryProperties(ctx.PhysicalDevice, out VkPhysicalDeviceMemoryProperties memProps);

            uint memoryTypeIndex = BufferHelper.GetMemoryTypeIndex(req.memoryTypeBits, VkMemoryPropertyFlags.DeviceLocal, memProps);

            VkMemoryAllocateInfo bufferAllocInfo = new VkMemoryAllocateInfo
            {
                sType           = VkStructureType.MemoryAllocateInfo,
                pNext           = null,
                allocationSize  = req.size,
                memoryTypeIndex = memoryTypeIndex
            };

            VkDeviceMemory memory;

            result = vkAllocateMemory(ctx.Device, &bufferAllocInfo, null, &memory);
            result.CheckResult();

            result = vkBindBufferMemory(ctx.Device, buffer, memory, 0);

            // Copy the data from staging buffers to device local buffers.

            VkCommandBufferAllocateInfo allocInfo = new VkCommandBufferAllocateInfo()
            {
                sType       = VkStructureType.CommandBufferAllocateInfo,
                commandPool = ctx.GraphicsCommandPool,

                level = VkCommandBufferLevel.Primary,
                commandBufferCount = 1,
            };

            VkCommandBuffer cmdBuffer;

            vkAllocateCommandBuffers(ctx.Device, &allocInfo, &cmdBuffer);

            VkCommandBufferBeginInfo beginInfo = new VkCommandBufferBeginInfo()
            {
                sType = VkStructureType.CommandBufferBeginInfo,
                flags = VkCommandBufferUsageFlags.OneTimeSubmit,
            };

            result = vkBeginCommandBuffer(cmdBuffer, &beginInfo);
            result.CheckResult();

            VkBufferCopy bufferCopy = new VkBufferCopy
            {
                size = (ulong)size
            };

            vkCmdCopyBuffer(cmdBuffer, stagingBuffer, buffer, 1, &bufferCopy);

            result = vkEndCommandBuffer(cmdBuffer);
            result.CheckResult();

            // Submit.
            VkFenceCreateInfo fenceCreateInfo = new VkFenceCreateInfo
            {
                sType = VkStructureType.FenceCreateInfo,
                pNext = null
            };

            VkFence fence;

            result = vkCreateFence(ctx.Device, &fenceCreateInfo, null, out fence);
            result.CheckResult();

            VkSubmitInfo submitInfo = new VkSubmitInfo
            {
                sType = VkStructureType.SubmitInfo,
                pNext = null,
                commandBufferCount = 1,
                pCommandBuffers    = &cmdBuffer
            };

            result = vkQueueSubmit(ctx.GraphicsQueue, 1, &submitInfo, fence);

            result = vkWaitForFences(ctx.Device, 1, &fence, false, ulong.MaxValue);
            result.CheckResult();

            // Cleanup.
            vkDestroyFence(ctx.Device, fence, null);
            vkFreeCommandBuffers(ctx.Device, ctx.GraphicsCommandPool, 1, &cmdBuffer);
            vkDestroyBuffer(ctx.Device, stagingBuffer, null);
            vkFreeMemory(ctx.Device, stagingMemory, null);

            return(new VulkanBuffer(ctx.Device, ctx.GraphicsCommandPool, buffer, memory, data.Length));
        }
Exemple #25
0
        public void Initialize(IVulkanAppHost host)
        {
            Host = host;
#if DEBUG
            const bool debug = true;
#else
            const bool debug = false;
#endif
            _initializingPermanent = true;

            VkResult result = vkInitialize();
            result.CheckResult();

            // Calling ToDispose here registers the resource to be automatically disposed on exit.
            Instance = CreateInstance(debug);
            Surface  = CreateSurface();
            Context  = new VulkanContext(Instance, Surface, Host.Platform);
            Content  = new ContentManager(Host, Context, "Content");
            ImageAvailableSemaphore    = CreateSemaphore(Context.Device);
            RenderingFinishedSemaphore = CreateSemaphore(Context.Device);

            _initializingPermanent = false;
            // Calling ToDispose here registers the resource to be automatically disposed on events
            // such as window resize.
            var swapchain = CreateSwapchain();
            Swapchain = swapchain;
            ToDispose(new ActionDisposable(() =>
            {
                vkDestroySwapchainKHR(Context.Device, swapchain, null);
            }));

            // Acquire underlying images of the freshly created swapchain.
            uint swapchainImageCount;
            result = vkGetSwapchainImagesKHR(Context.Device, Swapchain, &swapchainImageCount, null);
            result.CheckResult();

            var swapchainImages = stackalloc VkImage[(int)swapchainImageCount];
            result = vkGetSwapchainImagesKHR(Context.Device, Swapchain, &swapchainImageCount, swapchainImages);
            result.CheckResult();

            SwapchainImages = new VkImage[swapchainImageCount];
            for (int i = 0; i < swapchainImageCount; i++)
            {
                SwapchainImages[i] = swapchainImages[i];
            }

            VkCommandBufferAllocateInfo allocInfo = new VkCommandBufferAllocateInfo()
            {
                sType       = VkStructureType.CommandBufferAllocateInfo,
                commandPool = Context.GraphicsCommandPool,

                level = VkCommandBufferLevel.Primary,
                commandBufferCount = (uint)SwapchainImages.Length,
            };

            VkCommandBuffer[] commandBuffers = new VkCommandBuffer[SwapchainImages.Length];
            fixed(VkCommandBuffer *commandBuffersPtr = &commandBuffers[0])
            {
                vkAllocateCommandBuffers(Context.Device, &allocInfo, commandBuffersPtr).CheckResult();
            }

            CommandBuffers = commandBuffers;

            // Create a fence for each commandbuffer so that we can wait before using it again
            _initializingPermanent = true; //We need our fences to be there permanently
            SubmitFences           = new VkFence[SwapchainImages.Length];
            for (int i = 0; i < SubmitFences.Length; i++)
            {
                VkFenceCreateInfo fenceCreateInfo = new VkFenceCreateInfo()
                {
                    sType = VkStructureType.FenceCreateInfo,
                    pNext = null,
                    flags = VkFenceCreateFlags.Signaled
                };

                VkFence handle;

                vkCreateFence(Context.Device, &fenceCreateInfo, null, out handle);

                SubmitFences[i] = handle;
                ToDispose(new ActionDisposable(() =>
                {
                    vkDestroyFence(Context.Device, handle, null);
                }));
            }

            // Allow concrete samples to initialize their resources.
            InitializePermanent();
            _initializingPermanent = false;
            InitializeFrame();

            // Record commands for execution by Vulkan.
            RecordCommandBuffers();
        }
Exemple #26
0
        static void Main(string[] args)
        {
            Console.WriteLine(AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES").ToString());
            Console.WriteLine($"Hello Vulkan!");
            Init();

            if (!GLFW.Vulkan.IsSupported)
            {
                Console.Error.WriteLine("GLFW says that vulkan is not supported.");
                return;
            }

            WindowHint(Hint.ClientApi, ClientApi.None);
            NativeWindow window = new GLFW.NativeWindow(width, height, "Fabricor");

            Glfw.SetKeyCallback(window, (a, b, c, d, e) => {
                GLFWInput.KeyCallback(a, b, c, d, e);
            });

            FInstance      finst      = new FInstance();
            VkSurfaceKHR   surface    = CreateSurface(finst.instance, window);
            VkDevice       device     = CreateDevice(finst.instance, out var physicalDevice, surface, out var queueFamilyIndex);
            VkSwapchainKHR swapchain  = CreateSwapchain(VkSwapchainKHR.Null, finst.instance, device, physicalDevice, surface, queueFamilyIndex);
            VkRenderPass   renderPass = CreateRenderPass(device);

            uint swapchainImageCount = 0;

            Assert(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, null));////////////IMAGES
            VkImage[] swapchainImages = new VkImage[swapchainImageCount];

            fixed(VkImage *ptr = &swapchainImages[0])
            Assert(vkGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, ptr));

            CommandPoolManager.Init(device, queueFamilyIndex);
            int poolId = CommandPoolManager.CreateCommandPool(VkCommandPoolCreateFlags.ResetCommandBuffer);


            VkSemaphoreCreateInfo pCreateInfo = VkSemaphoreCreateInfo.New();

            VkSemaphore acquireSemaphore = new VkSemaphore();

            vkCreateSemaphore(device, &pCreateInfo, null, &acquireSemaphore);

            VkSemaphore releaseSemaphore = new VkSemaphore();

            vkCreateSemaphore(device, &pCreateInfo, null, &releaseSemaphore);

            VkQueue graphicsQueue = VkQueue.Null;

            vkGetDeviceQueue(device, queueFamilyIndex, 0, &graphicsQueue);

            string[] textures = new string[] {
                "res/Linus.png",
                "res/Alex.png",
                "res/Victor.png",
                "res/Alex2.png",
                //"res/Cyan.png",
                "res/Alex3.png",
                //"res/Red.png",
            };

            FTexture texture = new FTexture(device, physicalDevice, poolId, graphicsQueue, textures, VkFormat.R8g8b8a8Unorm,
                                            512, 512, (uint)(Math.Log(512) / Math.Log(2)) + 1);

            VkPipelineCache   pipelineCache = VkPipelineCache.Null;//This is critcal for performance.
            FGraphicsPipeline voxelPipeline =
                new FGraphicsPipeline(device, physicalDevice, pipelineCache, renderPass, "shaders/voxel", swapchainImageCount, texture);

            voxelPipeline.CreateDepthBuffer(physicalDevice, (uint)width, (uint)height);

            VkImageView[] swapchainImageViews = new VkImageView[swapchainImageCount];
            for (int i = 0; i < swapchainImageCount; i++)
            {
                swapchainImageViews[i] = FTexture.CreateColourImageView(device, swapchainImages[i], surfaceFormat.format);
            }
            VkFramebuffer[] frambuffers = new VkFramebuffer[swapchainImageCount];
            for (int i = 0; i < swapchainImageCount; i++)
            {
                frambuffers[i] = CreateFramebuffer(device, renderPass, swapchainImageViews[i], voxelPipeline.depthImageView);
            }

            MeshWrapper <VoxelVertex> mesh = VoxelMeshFactory.GenerateMesh(device, physicalDevice);

            Action updateMesh = delegate {
                VoxelMeshFactory.UpdateMesh(device, physicalDevice, mesh);
            };

            GLFWInput.Subscribe(Keys.U, updateMesh, InputState.Press);

            Action changeTexture = delegate
            {
                Span <VoxelVertex> span = mesh.Mesh.vertices.Map();
                for (int j = 0; j < span.Length; j++)
                {
                    span[j].textureId++;
                }
                span = mesh.Mesh.vertices.UnMap();
            };

            GLFWInput.Subscribe(Keys.F, changeTexture, InputState.Press);

            FCommandBuffer[] cmdBuffers = new FCommandBuffer[swapchainImageCount];
            VkFence[]        fences     = new VkFence[swapchainImageCount];
            for (int i = 0; i < swapchainImageCount; i++)
            {
                cmdBuffers[i] = new FCommandBuffer(device, poolId);

                VkFenceCreateInfo createInfo = VkFenceCreateInfo.New();
                createInfo.flags = VkFenceCreateFlags.Signaled;
                VkFence fence = VkFence.Null;
                Assert(vkCreateFence(device, &createInfo, null, &fence));
                fences[i] = fence;
            }

            FCamera camera = new FCamera();

            camera.AspectWidth  = width;
            camera.AspectHeight = height;
            camera.position.Z   = -1f;
            //camera.rotation=Quaternion.CreateFromYawPitchRoll(MathF.PI,0,0);

            double lastTime = Glfw.Time;
            int    nbFrames = 0;

            while (!WindowShouldClose(window))
            {
                PollEvents();
                GLFWInput.Update();

                // Measure speed
                double currentTime = Glfw.Time;
                nbFrames++;
                if (currentTime - lastTime >= 1.0)
                { // If last prinf() was more than 1 sec ago
                  // printf and reset timer
                    Console.WriteLine($"ms/frame: {1000.0 / nbFrames}");
                    nbFrames  = 0;
                    lastTime += 1.0;
                }

                if (GLFWInput.TimeKeyPressed(Keys.D) > 0)
                {
                    camera.position += Vector3.Transform(Vector3.UnitX * 0.00015f, camera.rotation);
                }
                if (GLFWInput.TimeKeyPressed(Keys.A) > 0)
                {
                    camera.position -= Vector3.Transform(Vector3.UnitX * 0.00015f, camera.rotation);
                }

                if (GLFWInput.TimeKeyPressed(Keys.W) > 0)
                {
                    camera.position += Vector3.Transform(Vector3.UnitZ * 0.00015f, camera.rotation);
                }
                if (GLFWInput.TimeKeyPressed(Keys.S) > 0)
                {
                    camera.position -= Vector3.Transform(Vector3.UnitZ * 0.00015f, camera.rotation);
                }

                if (GLFWInput.TimeKeyPressed(Keys.Space) > 0)
                {
                    camera.position += Vector3.Transform(Vector3.UnitY * 0.00015f, camera.rotation);
                }
                if (GLFWInput.TimeKeyPressed(Keys.LeftShift) > 0)
                {
                    camera.position -= Vector3.Transform(Vector3.UnitY * 0.00015f, camera.rotation);
                }

                if (GLFWInput.TimeKeyPressed(Keys.Right) > 0)
                {
                    camera.rotation *= Quaternion.CreateFromAxisAngle(Vector3.UnitY, 0.00015f);
                }
                if (GLFWInput.TimeKeyPressed(Keys.Left) > 0)
                {
                    camera.rotation *= Quaternion.CreateFromAxisAngle(Vector3.UnitY, -0.00015f);
                }


                uint imageIndex = 0;

                Assert(vkAcquireNextImageKHR(device, swapchain, ulong.MaxValue, acquireSemaphore, VkFence.Null, &imageIndex));


                VkCommandBufferBeginInfo beginInfo = VkCommandBufferBeginInfo.New();
                beginInfo.flags = VkCommandBufferUsageFlags.OneTimeSubmit;

                voxelPipeline.swapchainFramebuffer = frambuffers[imageIndex];
                voxelPipeline.swapchainImage       = swapchainImages[imageIndex];
                voxelPipeline.swapchainImageIndex  = imageIndex;

                voxelPipeline.mesh   = mesh;
                voxelPipeline.camera = camera;

                fixed(VkFence *ptr = &(fences[imageIndex]))
                {
                    vkWaitForFences(device, 1, ptr, VkBool32.False, ulong.MaxValue);
                    vkResetFences(device, 1, ptr);
                }

                cmdBuffers[imageIndex].RecordCommandBuffer(new Action <VkCommandBuffer>[] {
                    voxelPipeline.Execute,
                });

                VkPipelineStageFlags submitStageMask = VkPipelineStageFlags.ColorAttachmentOutput;

                VkSubmitInfo submitInfo = VkSubmitInfo.New();
                submitInfo.waitSemaphoreCount = 1;
                submitInfo.pWaitSemaphores    = &acquireSemaphore;
                submitInfo.pWaitDstStageMask  = &submitStageMask;
                submitInfo.commandBufferCount = 1;

                fixed(VkCommandBuffer *ptr = &(cmdBuffers[imageIndex].buffer))
                submitInfo.pCommandBuffers = ptr;

                submitInfo.signalSemaphoreCount = 1;
                submitInfo.pSignalSemaphores    = &releaseSemaphore;

                Assert(vkQueueSubmit(graphicsQueue, 1, &submitInfo, fences[imageIndex]));

                VkPresentInfoKHR presentInfoKHR = VkPresentInfoKHR.New();
                presentInfoKHR.swapchainCount = 1;
                presentInfoKHR.pSwapchains    = &swapchain;
                presentInfoKHR.pImageIndices  = &imageIndex;

                presentInfoKHR.waitSemaphoreCount = 1;
                presentInfoKHR.pWaitSemaphores    = &releaseSemaphore;

                Assert(vkQueuePresentKHR(graphicsQueue, &presentInfoKHR));
                vkDeviceWaitIdle(device);
            }
            finst.Destroy();
            DestroyWindow(window);
            Terminate();
        }
Exemple #27
0
        public GraphicsDevice(string applicationName, bool enableValidation, Window?window)
        {
            VkString name    = applicationName;
            var      appInfo = new VkApplicationInfo
            {
                sType              = VkStructureType.ApplicationInfo,
                pApplicationName   = name,
                applicationVersion = new VkVersion(1, 0, 0),
                pEngineName        = s_EngineName,
                engineVersion      = new VkVersion(1, 0, 0),
                apiVersion         = vkEnumerateInstanceVersion()
            };

            List <string> instanceExtensions = new List <string>
            {
                KHRSurfaceExtensionName
            };

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                instanceExtensions.Add(KHRWin32SurfaceExtensionName);
            }

            List <string> instanceLayers = new List <string>();

            if (enableValidation)
            {
                FindValidationLayers(instanceLayers);
            }

            if (instanceLayers.Count > 0)
            {
                instanceExtensions.Add(EXTDebugUtilsExtensionName);
            }

            using var vkInstanceExtensions = new VkStringArray(instanceExtensions);

            var instanceCreateInfo = new VkInstanceCreateInfo
            {
                sType                   = VkStructureType.InstanceCreateInfo,
                pApplicationInfo        = &appInfo,
                enabledExtensionCount   = vkInstanceExtensions.Length,
                ppEnabledExtensionNames = vkInstanceExtensions
            };

            using var vkLayerNames = new VkStringArray(instanceLayers);
            if (instanceLayers.Count > 0)
            {
                instanceCreateInfo.enabledLayerCount   = vkLayerNames.Length;
                instanceCreateInfo.ppEnabledLayerNames = vkLayerNames;
            }

            var debugUtilsCreateInfo = new VkDebugUtilsMessengerCreateInfoEXT
            {
                sType = VkStructureType.DebugUtilsMessengerCreateInfoEXT
            };

            if (instanceLayers.Count > 0)
            {
                _debugMessengerCallbackFunc          = DebugMessengerCallback;
                debugUtilsCreateInfo.messageSeverity = VkDebugUtilsMessageSeverityFlagsEXT.Error | VkDebugUtilsMessageSeverityFlagsEXT.Warning;
                debugUtilsCreateInfo.messageType     = VkDebugUtilsMessageTypeFlagsEXT.Validation | VkDebugUtilsMessageTypeFlagsEXT.Performance;
                debugUtilsCreateInfo.pfnUserCallback = Marshal.GetFunctionPointerForDelegate(_debugMessengerCallbackFunc);

                instanceCreateInfo.pNext = &debugUtilsCreateInfo;
            }

            VkResult result = vkCreateInstance(&instanceCreateInfo, null, out VkInstance);

            if (result != VkResult.Success)
            {
                throw new InvalidOperationException($"Failed to create vulkan instance: {result}");
            }

            vkLoadInstance(VkInstance);

            if (instanceLayers.Count > 0)
            {
                vkCreateDebugUtilsMessengerEXT(VkInstance, &debugUtilsCreateInfo, null, out _debugMessenger).CheckResult();
            }

            Log.Info($"Created VkInstance with version: {appInfo.apiVersion.Major}.{appInfo.apiVersion.Minor}.{appInfo.apiVersion.Patch}");
            if (instanceLayers.Count > 0)
            {
                foreach (var layer in instanceLayers)
                {
                    Log.Info($"Instance layer '{layer}'");
                }
            }

            foreach (string extension in instanceExtensions)
            {
                Log.Info($"Instance extension '{extension}'");
            }

            _surface = CreateSurface(window);

            // Find physical device, setup queue's and create device.
            var physicalDevices = vkEnumeratePhysicalDevices(VkInstance);

            foreach (var physicalDevice in physicalDevices)
            {
                vkGetPhysicalDeviceProperties(physicalDevice, out var properties);
                var deviceName = properties.GetDeviceName();
            }

            PhysicalDevice = physicalDevices[0];

            var queueFamilies = FindQueueFamilies(PhysicalDevice, _surface);

            var priority        = 1.0f;
            var queueCreateInfo = new VkDeviceQueueCreateInfo
            {
                sType            = VkStructureType.DeviceQueueCreateInfo,
                queueFamilyIndex = queueFamilies.graphicsFamily,
                queueCount       = 1,
                pQueuePriorities = &priority
            };

            List <string> deviceExtensions = new List <string>
            {
                KHRSwapchainExtensionName
            };
            var deviceCreateInfo = new VkDeviceCreateInfo
            {
                sType                = VkStructureType.DeviceCreateInfo,
                pQueueCreateInfos    = &queueCreateInfo,
                queueCreateInfoCount = 1,
                pEnabledFeatures     = null,
            };

            using var deviceExtensionNames           = new VkStringArray(deviceExtensions);
            deviceCreateInfo.enabledExtensionCount   = deviceExtensionNames.Length;
            deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;

            result = vkCreateDevice(PhysicalDevice, &deviceCreateInfo, null, out VkDevice);
            if (result != VkResult.Success)
            {
                throw new Exception($"Failed to create Vulkan Logical Device, {result}");
            }

            vkGetDeviceQueue(VkDevice, queueFamilies.graphicsFamily, 0, out GraphicsQueue);
            vkGetDeviceQueue(VkDevice, queueFamilies.presentFamily, 0, out PresentQueue);

            // Create swap chain
            Swapchain = new Swapchain(this, window);
            _perFrame = new PerFrame[Swapchain.ImageCount];
            for (var i = 0; i < _perFrame.Length; i++)
            {
                VkFenceCreateInfo fenceCreateInfo = new VkFenceCreateInfo(VkFenceCreateFlags.Signaled);
                vkCreateFence(VkDevice, &fenceCreateInfo, null, out _perFrame[i].QueueSubmitFence).CheckResult();

                VkCommandPoolCreateInfo poolCreateInfo = new VkCommandPoolCreateInfo
                {
                    sType            = VkStructureType.CommandPoolCreateInfo,
                    flags            = VkCommandPoolCreateFlags.Transient,
                    queueFamilyIndex = queueFamilies.graphicsFamily,
                };
                vkCreateCommandPool(VkDevice, &poolCreateInfo, null, out _perFrame[i].PrimaryCommandPool).CheckResult();

                VkCommandBufferAllocateInfo commandBufferInfo = new VkCommandBufferAllocateInfo
                {
                    sType              = VkStructureType.CommandBufferAllocateInfo,
                    commandPool        = _perFrame[i].PrimaryCommandPool,
                    level              = VkCommandBufferLevel.Primary,
                    commandBufferCount = 1
                };
                vkAllocateCommandBuffers(VkDevice, &commandBufferInfo, out _perFrame[i].PrimaryCommandBuffer).CheckResult();
            }
        }