diff --git a/Example/01-General/main.cpp b/Example/01-General/main.cpp index 8dd0a34..0bff91c 100644 --- a/Example/01-General/main.cpp +++ b/Example/01-General/main.cpp @@ -5,8 +5,10 @@ #include #include #include - -#include +#include +#include +#include +#include using namespace MAGE; @@ -46,27 +48,72 @@ int main() Owned cQueue = device->CreateQueue(vk::QueueFlagBits::eCompute); Owned tQueue = device->CreateQueue(vk::QueueFlagBits::eTransfer); - Owned swapchain = MakeOwned(SwapchainProps(vk::Format::eR8G8B8A8Unorm, + Owned swapchain = MakeOwned(SwapchainProps(vk::Format::eR8G8B8A8Unorm, vk::PresentModeKHR::eFifoRelaxed, { 1280, 720 }, 2, &*gQueue), &*device); - BufferProps bufferTestProp = BufferProps(); - bufferTestProp.memory = device->GetAllocator()->GetAvailableMemory(AllocProps(bufferTestProp.sizeInBytes, vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible)); - Owned bufferTest1 = MakeOwned(bufferTestProp, &*device); - bufferTest1->Update({ square.data(), square.size() * sizeof(Vertex) }); + CmdPoolProps gPoolProps = + { + .queue = &*gQueue, + .createFlags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer + }; + Owned gPool = MakeOwned(gPoolProps, &*device); + Owned gPrimBuffer = gPool->CreateBuffer(); - bufferTestProp.memory = device->GetAllocator()->GetAvailableMemory(AllocProps(bufferTestProp.sizeInBytes, vk::MemoryPropertyFlagBits::eDeviceLocal | vk::MemoryPropertyFlagBits::eHostVisible)); - Owned bufferTest2 = MakeOwned(bufferTestProp, &*device); - bufferTest2->Update({ square.data(), square.size() * sizeof(Vertex) }); + Owned imgFence = MakeOwned(&*device); + Owned presentSem = MakeOwned(&*device); window.Show(); while (!window.IsClosed()) { window.PollEvents(); + + u32 imgIndex = swapchain->AcquireNextImage(nullptr, &*imgFence); + imgFence->Wait(); + imgFence->Reset(); + + gPrimBuffer->BeginRecord(vk::CommandBufferUsageFlagBits::eOneTimeSubmit); + + // Barrier swapchain + ImageBarrierProps barrier = {}; + barrier.srcPipeline = vk::PipelineStageFlagBits::eColorAttachmentOutput; + barrier.dstPipeline = vk::PipelineStageFlagBits::eColorAttachmentOutput; + barrier.srcAccessMask = {}; + barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; + barrier.oldLayout = vk::ImageLayout::ePresentSrcKHR; + barrier.newLayout = vk::ImageLayout::eColorAttachmentOptimal; + barrier.image = swapchain->GetSwapchainImage(); + + gPrimBuffer->ImageBarrier(barrier); + + RenderProps renderProp = {}; + renderProp.colorAttachment = { {swapchain->GetSwapchainView(), vk::ImageLayout::eColorAttachmentOptimal, vk::AttachmentLoadOp::eClear, + vk::AttachmentStoreOp::eStore, vk::ClearColorValue(.39f, .58f, .92f, 1.f), vk::ClearDepthStencilValue(1.f, 0)} }; + renderProp.extent = swapchain->GetImageSize(); + renderProp.layerCount = 1; + renderProp.viewMask = 0; + renderProp.renderFlags = vk::RenderingFlagBits::eContentsSecondaryCommandBuffers; + gPrimBuffer->BeginRendering(renderProp); + gPrimBuffer->EndRendering(); + + // Barrier again + barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite; + barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead; + barrier.oldLayout = vk::ImageLayout::eColorAttachmentOptimal; + barrier.newLayout = vk::ImageLayout::ePresentSrcKHR; + gPrimBuffer->ImageBarrier(barrier); + gPrimBuffer->EndRecord(); + + gQueue->Submit({ &*gPrimBuffer }, { &*presentSem }, {}, { vk::PipelineStageFlagBits::eColorAttachmentOutput }, nullptr); + swapchain->Present({ &*presentSem }); } window.Hide(); - bufferTest2->Destroy(); - bufferTest1->Destroy(); + device->WaitForIdle(); + + imgFence->Destroy(); + presentSem->Destroy(); + gPrimBuffer->Destroy(); + gPool->Destroy(); swapchain->Destroy(); device->Destroy(); instance->Destroy(); diff --git a/README.md b/README.md index 61d870c..36760ff 100644 --- a/README.md +++ b/README.md @@ -3,32 +3,23 @@ ![example event parameter](https://github.com/mtuncbilek95/MAGE/actions/workflows/windows-build.yml/badge.svg) # M.A.G.E -## NOTES ON PROJECT -- Project continuously gets rework on Graphics Implementation. Because I'm trying new things. If you want results please check older commits. -- Current version is using vulkan.hpp -- I can change anything at anytime. Which means I can always start working from scratch on master. So I don't care what you think. I'm trying to learn. -- Tried descriptor buffers. And its gonna take a long time to find a good implementation for it on general usage. -- I have my own memory allocator design. It has issues but mostly its working fine with a little bit over allocation. But I will try to fix those on packaging since editor side has quite unexpected behaviours. - Modern & Advanced Graphics Engine ## GOOD TO KNOW WHILE WORKING - The Engine is written in C++20 and uses Vulkan API `v1.3.290.0` for rendering (Will support DX12 in future). +- Current version is using vulkan.hpp +- I have my own memory allocator design. It has issues but mostly its working fine with a little bit over allocation. - The Engine is still in development and not ready for production. -- While running build generator for cmake, you will see a part where the cmake shows the excluded files. When working with Linux the excluded files needs to belong Win32API and -vice versa. This has been added to CMake with the aim of reducing macro controllers and boosting the compilation speed. **Edit:** It is still there but it's not that important anymore. -Because I literally deep dived into not having layers on platform specific implementations. Couple things are still there for this excluding purpose but most of them are gone. ## IMPLEMENTED FEATURES - [x] Unit Test Environment (Catch2) - [x] Win32 Platform - [ ] Linux Platform - [x] Vulkan Implementation -- [ ] Job System for multi-thread behaviour +- [x] Job System for multi-thread behaviour - [x] Runtime Shader Compiler - [x] Custom Memory Allocator - [ ] JSON Serialization -- [ ] Encryption - [ ] Resource Control Mechanism - [ ] Entity Component System - [ ] Editor Integration with ImGui @@ -61,10 +52,18 @@ Because I literally deep dived into not having layers on platform specific imple - [ ] DX12 (Graphics SDK) - [ ] Nvidia Physx (Physics SDK) + +## ACTIVE VULKAN FEATURES +- [x] Dynamic Rendering +- [x] RenderPass & FrameBuffer +- [x] Descriptor Buffer +- [x] Descriptor Set +- [x] Bindless Shaders + ## THE AIM OF THE SYSTEM -- Windows should work with DX12 no matter what. But for now I don't want the burnout with DX12, so VulkanSDK continues. If I could go the past, I would choose DX12 though. +- I believe the only reason for DX12 would be Xbox support. I will continue my journey with Vulkan as much as possible. - Linux has no option other than VulkanSDK. -- I already made cross platform support for both Linux and Windows. It's not that hard to make it. The hard part is to make them work together while doing lots of feature +- I already made cross platform support for both Linux and Windows a time ago. It's not that hard to make it. The hard part is to make them work together while doing lots of feature implementations. So till I get the satisfaction on Win32, I won't touch the Linux part. The system already has the capability to run on Linux. - Main optimization workflow mostly will be on Windows since buying PS5 Dev Kit is not easy. - Current goal is not to create showreels but creating crash-less environment diff --git a/Source/Engine/Vulkan/Core/VkUtils.cpp b/Source/Engine/Vulkan/Core/VkUtils.cpp new file mode 100644 index 0000000..6d0321d --- /dev/null +++ b/Source/Engine/Vulkan/Core/VkUtils.cpp @@ -0,0 +1,103 @@ +#include "VkUtils.h" + +#include + +namespace MAGE +{ + HashMap MakeFormatSizeMap() + { + HashMap map; + for (auto format : magic_enum::enum_values()) + { + u32 size = 0; + if (format == vk::Format::eR4G4UnormPack8 || format == vk::Format::eS8Uint) size = 1; + else if (format >= vk::Format::eR4G4B4A4UnormPack16 && format <= vk::Format::eA1R5G5B5UnormPack16) + size = 2; + else if (format >= vk::Format::eR8Unorm && format <= vk::Format::eR8Srgb) size = 1; + else if (format >= vk::Format::eR8G8Unorm && format <= vk::Format::eR8G8Srgb) size = 2; + else if (format >= vk::Format::eR8G8B8Unorm && format <= vk::Format::eB8G8R8Srgb) size = 3; + else if (format >= vk::Format::eR8G8B8A8Unorm && format <= vk::Format::eA2B10G10R10SintPack32) + size = 4; + else if (format >= vk::Format::eR16Unorm && format <= vk::Format::eR16Sfloat) size = 2; + else if (format >= vk::Format::eR16G16Unorm && format <= vk::Format::eR16G16Sfloat) size = 4; + else if (format >= vk::Format::eR16G16B16Unorm && format <= vk::Format::eR16G16B16Sfloat) size = 6; + else if (format >= vk::Format::eR16G16B16A16Unorm && format <= vk::Format::eR16G16B16A16Sfloat) + size = 8; + else if (format >= vk::Format::eR32Uint && format <= vk::Format::eR32Sfloat) size = 4; + else if (format >= vk::Format::eR32G32Uint && format <= vk::Format::eR32G32Sfloat) size = 8; + else if (format >= vk::Format::eR32G32B32Uint && format <= vk::Format::eR32G32B32Sfloat) size = 12; + else if (format >= vk::Format::eR32G32B32A32Uint && format <= vk::Format::eR32G32B32A32Sfloat) + size = 16; + else if (format >= vk::Format::eR64Uint && format <= vk::Format::eR64Sfloat) size = 8; + else if (format >= vk::Format::eR64G64Uint && format <= vk::Format::eR64G64Sfloat) size = 16; + else if (format >= vk::Format::eR64G64B64Uint && format <= vk::Format::eR64G64B64Sfloat) size = 24; + else if (format >= vk::Format::eR64G64B64A64Uint && format <= vk::Format::eR64G64B64A64Sfloat) + size = 32; + else if (format == vk::Format::eB10G11R11UfloatPack32 || format == vk::Format::eE5B9G9R9UfloatPack32 + || format == vk::Format::eX8D24UnormPack32 || format == vk::Format::eD32Sfloat + || format == vk::Format::eD24UnormS8Uint) + size = 4; + else if (format == vk::Format::eD16Unorm) size = 2; + else if (format == vk::Format::eD16UnormS8Uint) size = 3; + else if (format == vk::Format::eD32SfloatS8Uint) size = 5; + else if (format == vk::Format::eBc1RgbUnormBlock || format == vk::Format::eBc1RgbSrgbBlock + || format == vk::Format::eBc1RgbaUnormBlock || format == vk::Format::eBc1RgbaSrgbBlock + || format == vk::Format::eBc4SnormBlock || format == vk::Format::eBc4UnormBlock) + size = 8; + else if (format == vk::Format::eBc2SrgbBlock || format == vk::Format::eBc2UnormBlock + || format == vk::Format::eBc3SrgbBlock || format == vk::Format::eBc3UnormBlock + || format == vk::Format::eBc5SnormBlock || format == vk::Format::eBc5UnormBlock + || format == vk::Format::eBc6HSfloatBlock || format == vk::Format::eBc6HUfloatBlock + || format == vk::Format::eBc7SrgbBlock || format == vk::Format::eBc7UnormBlock) + size = 16; + else if (format == vk::Format::eEtc2R8G8B8UnormBlock || format == vk::Format::eEtc2R8G8B8SrgbBlock + || format == vk::Format::eEtc2R8G8B8A1UnormBlock + || format == vk::Format::eEtc2R8G8B8A1SrgbBlock + || format == vk::Format::eEtc2R8G8B8A8UnormBlock + || format == vk::Format::eEtc2R8G8B8A8SrgbBlock || format == vk::Format::eEacR11UnormBlock + || format == vk::Format::eEacR11SnormBlock || format == vk::Format::eEacR11G11UnormBlock + || format == vk::Format::eEacR11G11SnormBlock) + size = 16; + else if (format == vk::Format::eAstc4x4UnormBlock || format == vk::Format::eAstc4x4SfloatBlock + || format == vk::Format::eAstc4x4SrgbBlock || format == vk::Format::eAstc5x4UnormBlock + || format == vk::Format::eAstc5x4SfloatBlock || format == vk::Format::eAstc5x4SrgbBlock + || format == vk::Format::eAstc5x5UnormBlock || format == vk::Format::eAstc5x5SfloatBlock + || format == vk::Format::eAstc5x5SrgbBlock || format == vk::Format::eAstc6x5UnormBlock + || format == vk::Format::eAstc6x5SfloatBlock || format == vk::Format::eAstc6x5SrgbBlock + || format == vk::Format::eAstc6x6UnormBlock || format == vk::Format::eAstc6x6SfloatBlock + || format == vk::Format::eAstc6x6SrgbBlock || format == vk::Format::eAstc8x5UnormBlock + || format == vk::Format::eAstc8x5SfloatBlock || format == vk::Format::eAstc8x5SrgbBlock + || format == vk::Format::eAstc8x6UnormBlock || format == vk::Format::eAstc8x6SfloatBlock + || format == vk::Format::eAstc8x6SrgbBlock || format == vk::Format::eAstc8x8UnormBlock + || format == vk::Format::eAstc8x8SfloatBlock || format == vk::Format::eAstc8x8SrgbBlock + || format == vk::Format::eAstc10x5UnormBlock || format == vk::Format::eAstc10x5SfloatBlock + || format == vk::Format::eAstc10x5SrgbBlock || format == vk::Format::eAstc10x6UnormBlock + || format == vk::Format::eAstc10x6SfloatBlock || format == vk::Format::eAstc10x6SrgbBlock + || format == vk::Format::eAstc10x8UnormBlock || format == vk::Format::eAstc10x8SfloatBlock + || format == vk::Format::eAstc10x8SrgbBlock || format == vk::Format::eAstc10x10UnormBlock + || format == vk::Format::eAstc10x10SfloatBlock || format == vk::Format::eAstc10x10SrgbBlock + || format == vk::Format::eAstc12x10UnormBlock + || format == vk::Format::eAstc12x10SfloatBlock || format == vk::Format::eAstc12x10SrgbBlock + || format == vk::Format::eAstc12x12UnormBlock + || format == vk::Format::eAstc12x12SfloatBlock || format == vk::Format::eAstc12x12SrgbBlock) + size = 16; + + map[format] = size; + } + + return map; + } + + const HashMap FORMAT_SIZE_MAP = MakeFormatSizeMap(); + + u32 VkUtils::GetVkTextureSize(vk::Format format) + { + auto size = FORMAT_SIZE_MAP.find(format); + if (size == FORMAT_SIZE_MAP.end() || size->second == 0) [[unlikely]] + { + spdlog::critical("VkFormat not found in the map: {}", magic_enum::enum_name(format)); + exit(-1); + } + return size->second; + } +} diff --git a/Source/Engine/Vulkan/Core/VkUtils.h b/Source/Engine/Vulkan/Core/VkUtils.h new file mode 100644 index 0000000..bd39f19 --- /dev/null +++ b/Source/Engine/Vulkan/Core/VkUtils.h @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include + +namespace MAGE +{ + namespace VkUtils + { + u32 GetVkTextureSize(vk::Format format); + } +} diff --git a/Source/Engine/Vulkan/RHI/Buffer/VBuffer.cpp b/Source/Engine/Vulkan/RHI/Buffer/VBuffer.cpp index fa9c8b8..4ee7bac 100644 --- a/Source/Engine/Vulkan/RHI/Buffer/VBuffer.cpp +++ b/Source/Engine/Vulkan/RHI/Buffer/VBuffer.cpp @@ -6,21 +6,13 @@ namespace MAGE { - VBuffer::VBuffer(const BufferProps& desc, VDevice* device) : VkObject(device), m_props(desc) + VBuffer::VBuffer(const BufferProps& desc, VDevice* device) : VkObject(device), m_props(desc), m_memoryOffset(0), m_memory(nullptr) { vk::BufferCreateInfo bufferInfo = vk::BufferCreateInfo(); bufferInfo.size = desc.sizeInBytes; bufferInfo.usage = vk::BufferUsageFlagBits::eShaderDeviceAddress | desc.usageFlags; ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().createBuffer(&bufferInfo, nullptr, &m_buffer), "VBuffer"); - - vk::MemoryRequirements memReq = vk::MemoryRequirements(); - m_rootDevice->GetVkDevice().getBufferMemoryRequirements(m_buffer, &memReq); - - u64 offset = desc.memory->Allocate(memReq.size + memReq.alignment); - m_memoryOffset = offset + (offset % memReq.alignment == 0 ? 0 : (memReq.alignment - (offset % memReq.alignment))); - - m_rootDevice->GetVkDevice().bindBufferMemory(m_buffer, m_props.memory->m_memory, m_memoryOffset); } VBuffer::~VBuffer() @@ -32,13 +24,33 @@ namespace MAGE { vk::BufferDeviceAddressInfo addressInfo = {}; addressInfo.buffer = m_buffer; - m_props.memory->m_address = m_rootDevice->GetVkDevice().getBufferAddress(&addressInfo); - return m_props.memory->m_address; + m_memory->m_address = m_rootDevice->GetVkDevice().getBufferAddress(&addressInfo); + return m_memory->m_address; + } + + usize VBuffer::GetRequestedSize() const + { + vk::MemoryRequirements memReq = vk::MemoryRequirements(); + m_rootDevice->GetVkDevice().getBufferMemoryRequirements(m_buffer, &memReq); + return memReq.size + memReq.alignment; + } + + void VBuffer::BindMemory(VMemory* memory) + { + vk::MemoryRequirements memReq = vk::MemoryRequirements(); + m_rootDevice->GetVkDevice().getBufferMemoryRequirements(m_buffer, &memReq); + + u64 offset = memory->Allocate(memReq.size + memReq.alignment); + m_memoryOffset = offset + (offset % memReq.alignment == 0 ? 0 : (memReq.alignment - (offset % memReq.alignment))); + + m_rootDevice->GetVkDevice().bindBufferMemory(m_buffer, memory->m_memory, m_memoryOffset); + + m_memory = memory; } void VBuffer::Update(RawBuffer buffer) const { - memcpy(m_props.memory->m_mappedData + m_memoryOffset, buffer.Data(), buffer.Size()); + memcpy(m_memory->m_mappedData + m_memoryOffset, buffer.Data(), buffer.Size()); } void VBuffer::Destroy() @@ -48,7 +60,7 @@ namespace MAGE m_rootDevice->GetVkDevice().destroyBuffer(m_buffer); m_buffer = VK_NULL_HANDLE; - m_props.memory->Free(0, m_memoryOffset); + m_memory->Free(0, m_memoryOffset); } } } diff --git a/Source/Engine/Vulkan/RHI/Buffer/VBuffer.h b/Source/Engine/Vulkan/RHI/Buffer/VBuffer.h index 040a5f8..8cefbaf 100644 --- a/Source/Engine/Vulkan/RHI/Buffer/VBuffer.h +++ b/Source/Engine/Vulkan/RHI/Buffer/VBuffer.h @@ -20,13 +20,11 @@ namespace MAGE struct BufferProps final { BufferProps(usize size = 0, - vk::BufferUsageFlags flag = {}, - VMemory* mem = nullptr) : sizeInBytes(size), usageFlags(flag), memory(mem) + vk::BufferUsageFlags flag = {}) : sizeInBytes(size), usageFlags(flag) {} usize sizeInBytes; vk::BufferUsageFlags usageFlags; - VMemory* memory; }; class VBuffer final : public VkObject @@ -36,22 +34,24 @@ namespace MAGE ~VBuffer() override final; inline vk::Buffer GetVkBuffer() const { return m_buffer; } - inline vk::DeviceMemory GetVkMemory() const { return m_props.memory->m_memory; } + inline vk::DeviceMemory GetVkMemory() const { return m_memory->m_memory; } inline vk::BufferUsageFlags GetVkUsage() const { return m_props.usageFlags; } vk::DeviceAddress GetVkAddress() const; inline usize GetMemoryOffset() const { return m_memoryOffset; } inline usize GetTotalSize() const { return m_props.sizeInBytes; } + inline u8* GetMappedData() const { return m_memory->m_mappedData; } - inline u8* GetMappedData() const { return m_props.memory->m_mappedData; } + usize GetRequestedSize() const; + void BindMemory(VMemory* memory); void Update(RawBuffer buffer) const; - void Destroy() override final; private: BufferProps m_props; + VMemory* m_memory; vk::Buffer m_buffer; usize m_memoryOffset; diff --git a/Source/Engine/Vulkan/RHI/Command/VCmdBuffer.cpp b/Source/Engine/Vulkan/RHI/Command/VCmdBuffer.cpp new file mode 100644 index 0000000..b11daef --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Command/VCmdBuffer.cpp @@ -0,0 +1,146 @@ +#include "VCmdBuffer.h" + +#include "../Device/VDevice.h" +#include "../Command/VCmdPool.h" +#include "../Image/VImageView.h" + +#include "Engine/Vulkan/Core/VkAssert.h" + +namespace MAGE +{ + VCmdBuffer::VCmdBuffer(const CmdBufferProps& desc, VDevice* device) : VkObject(device), m_props(desc) + { + vk::CommandBufferAllocateInfo info = {}; + info.commandBufferCount = 1; + info.commandPool = desc.pool->GetVkCmdPool(); + info.level = desc.level; + + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().allocateCommandBuffers(&info, &m_buffer), "VCmdBuffer"); + } + + VCmdBuffer::~VCmdBuffer() + { + Destroy(); + } + + void VCmdBuffer::BeginRecord(vk::CommandBufferUsageFlags flags) const + { + m_buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources); + + vk::CommandBufferBeginInfo info = {}; + info.flags = flags; + + ErrorUtils::VkAssert(m_buffer.begin(&info), "VCmdBUffer"); + } + + void VCmdBuffer::BeginRecord(const InheritanceProps& desc) const + { + m_buffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources); + + vk::CommandBufferInheritanceRenderingInfo rendering = {}; + rendering.colorAttachmentCount = desc.colorAttachments.size(); + rendering.pColorAttachmentFormats = desc.colorAttachments.data(); + rendering.depthAttachmentFormat = desc.depthAttachment; + rendering.stencilAttachmentFormat = desc.stencilAttachment; + rendering.flags = desc.renderFlags; + rendering.viewMask = desc.viewMask; + rendering.rasterizationSamples = desc.msaaCount; + + vk::CommandBufferInheritanceInfo inheritance = {}; + inheritance.framebuffer = nullptr; + inheritance.renderPass = nullptr; + inheritance.occlusionQueryEnable = false; + inheritance.pNext = &rendering; + + vk::CommandBufferBeginInfo info = {}; + info.flags = desc.usageFlags; + info.pInheritanceInfo = &inheritance; + + ErrorUtils::VkAssert(m_buffer.begin(&info), "VCmdBUffer"); + } + + void VCmdBuffer::EndRecord() const + { + m_buffer.end(); + } + + void VCmdBuffer::BeginRendering(const RenderProps& desc) const + { + Vector colorAttachments; + + for (u32 i = 0; i < desc.colorAttachment.size(); i++) + { + const RenderAttachment& att = desc.colorAttachment[i]; + + vk::RenderingAttachmentInfoKHR info = vk::RenderingAttachmentInfo(); + info.imageView = att.view->GetVkView(); + info.imageLayout = att.layout; + info.loadOp = att.loadOp; + info.storeOp = att.storeOp; + info.clearValue.setColor(att.color); + colorAttachments.push_back(info); + } + + vk::RenderingAttachmentInfoKHR depth = {}; + vk::RenderingAttachmentInfoKHR stencil = {}; + if (desc.hasDepth) + { + depth.imageView = desc.depthAttachment.view->GetVkView(); + depth.imageLayout = desc.depthAttachment.layout; + depth.loadOp = desc.depthAttachment.loadOp; + depth.storeOp = desc.depthAttachment.storeOp; + depth.clearValue.depthStencil = desc.depthAttachment.depthStencil; + } + + if (desc.hasStencil) + { + stencil.imageView = desc.stencilAttachment.view->GetVkView(); + stencil.imageLayout = desc.stencilAttachment.layout; + stencil.loadOp = desc.stencilAttachment.loadOp; + stencil.storeOp = desc.stencilAttachment.storeOp; + stencil.clearValue.depthStencil = desc.stencilAttachment.depthStencil; + } + + vk::RenderingInfoKHR info = {}; + info.colorAttachmentCount = colorAttachments.size(); + info.pColorAttachments = colorAttachments.data(); + info.pDepthAttachment = desc.hasDepth ? &depth : nullptr; + info.pStencilAttachment = desc.hasStencil ? &stencil : nullptr; + info.renderArea.extent = desc.extent; + info.renderArea.offset = desc.offset; + info.viewMask = desc.viewMask; + info.layerCount = desc.layerCount; + info.flags = desc.renderFlags; + + m_buffer.beginRendering(&info); + } + + void VCmdBuffer::EndRendering() const + { + m_buffer.endRendering(); + } + + void VCmdBuffer::ImageBarrier(const ImageBarrierProps& desc) const + { + vk::ImageMemoryBarrier barrier = {}; + barrier.srcAccessMask = desc.srcAccessMask; + barrier.dstAccessMask = desc.dstAccessMask; + barrier.oldLayout = desc.oldLayout; + barrier.newLayout = desc.newLayout; + barrier.srcQueueFamilyIndex = desc.srcQueueFamilyIndex; + barrier.dstQueueFamilyIndex = desc.dstQueueFamilyIndex; + barrier.image = desc.image->GetVkImage(); + barrier.subresourceRange = vk::ImageSubresourceRange(desc.aspectMask, desc.baseMipLevel, desc.levelCount, desc.baseArrayLayer, desc.layerCount); + + m_buffer.pipelineBarrier(desc.srcPipeline, desc.dstPipeline, vk::DependencyFlags(), 0, nullptr, 0, nullptr, 1, &barrier); + } + + void VCmdBuffer::Destroy() + { + if (m_buffer != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().freeCommandBuffers(m_props.pool->GetVkCmdPool(), m_buffer); + m_buffer = VK_NULL_HANDLE; + } + } +} diff --git a/Source/Engine/Vulkan/RHI/Command/VCmdBuffer.h b/Source/Engine/Vulkan/RHI/Command/VCmdBuffer.h new file mode 100644 index 0000000..155af83 --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Command/VCmdBuffer.h @@ -0,0 +1,109 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include "Engine/Vulkan/Core/VkObject.h" + +#include + +namespace MAGE +{ + class VCmdPool; + class VPipeline; + class VImageView; + class VImage; + + struct CmdBufferProps final + { + VCmdPool* pool = nullptr; + vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary; + }; + + struct InheritanceProps final + { + Vector colorAttachments; + vk::Format depthAttachment = vk::Format::eUndefined; + vk::Format stencilAttachment = vk::Format::eUndefined; + vk::RenderingFlags renderFlags = vk::RenderingFlagBits::eContentsSecondaryCommandBuffers; + vk::SampleCountFlagBits msaaCount = vk::SampleCountFlagBits::e1; + u32 viewMask = 0; + + vk::CommandBufferUsageFlags usageFlags = vk::CommandBufferUsageFlagBits::eRenderPassContinue; + }; + + struct RenderAttachment final + { + VImageView* view = nullptr; + vk::ImageLayout layout = vk::ImageLayout::eUndefined; + vk::AttachmentLoadOp loadOp = vk::AttachmentLoadOp::eLoad; + vk::AttachmentStoreOp storeOp = vk::AttachmentStoreOp::eStore; + vk::ClearColorValue color = vk::ClearColorValue(0.1f, 0.2f, 0.3f, 1.0f); + vk::ClearDepthStencilValue depthStencil = vk::ClearDepthStencilValue(1.0f, 0); + }; + + struct RenderProps final + { + Vector colorAttachment; + b8 hasDepth = false; + RenderAttachment depthAttachment; + b8 hasStencil = false; + RenderAttachment stencilAttachment; + vk::Extent2D extent; + vk::Offset2D offset = { 0, 0 }; + u32 layerCount = 1; + u32 viewMask = 0; + vk::RenderingFlags renderFlags; + }; + + struct ImageBarrierProps final + { + vk::PipelineStageFlags srcPipeline; + vk::PipelineStageFlags dstPipeline; + + vk::AccessFlags srcAccessMask; + vk::AccessFlags dstAccessMask; + vk::ImageLayout oldLayout; + vk::ImageLayout newLayout; + u32 srcQueueFamilyIndex = vk::QueueFamilyIgnored; + u32 dstQueueFamilyIndex = vk::QueueFamilyIgnored; + VImage* image; + + vk::ImageAspectFlags aspectMask = vk::ImageAspectFlagBits::eColor; + u32 baseMipLevel = 0; + u32 levelCount = 1; + u32 baseArrayLayer = 0; + u32 layerCount = 1; + }; + + class VCmdBuffer final : public VkObject + { + public: + VCmdBuffer(const CmdBufferProps& desc, VDevice* device); + ~VCmdBuffer() override final; + + inline vk::CommandBuffer GetVkCmdBuffer() const { return m_buffer; } + + void BeginRecord(vk::CommandBufferUsageFlags flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit) const; + void BeginRecord(const InheritanceProps& desc) const; + void EndRecord() const; + + void BeginRendering(const RenderProps& desc) const; + void EndRendering() const; + + void ImageBarrier(const ImageBarrierProps& desc) const; + + void Destroy() override final; + + private: + CmdBufferProps m_props; + + vk::CommandBuffer m_buffer; + }; +} diff --git a/Source/Engine/Vulkan/RHI/Command/VCmdPool.cpp b/Source/Engine/Vulkan/RHI/Command/VCmdPool.cpp new file mode 100644 index 0000000..9bf6e51 --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Command/VCmdPool.cpp @@ -0,0 +1,40 @@ +#include "VCmdPool.h" + +#include "../Device/VDevice.h" +#include "../Queue/VQueue.h" +#include "../Command/VCmdBuffer.h" +#include "Engine/Vulkan/Core/VkAssert.h" + +namespace MAGE +{ + VCmdPool::VCmdPool(const CmdPoolProps& desc, VDevice* device) : VkObject(device), m_props(desc) + { + vk::CommandPoolCreateInfo createInfo = {}; + createInfo.queueFamilyIndex = desc.queue->GetFamilyIndex(); + createInfo.flags = desc.createFlags; + + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().createCommandPool(&createInfo, nullptr, &m_pool), "VCmdPool"); + } + + VCmdPool::~VCmdPool() + { + Destroy(); + } + + Owned VCmdPool::CreateBuffer(vk::CommandBufferLevel level) + { + CmdBufferProps prop = {}; + prop.level = level; + prop.pool = this; + return MakeOwned(prop, m_rootDevice); + } + + void VCmdPool::Destroy() + { + if (m_pool != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().destroyCommandPool(m_pool); + m_pool = VK_NULL_HANDLE; + } + } +} diff --git a/Source/Engine/Vulkan/RHI/Command/VCmdPool.h b/Source/Engine/Vulkan/RHI/Command/VCmdPool.h new file mode 100644 index 0000000..85b345c --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Command/VCmdPool.h @@ -0,0 +1,44 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include "Engine/Vulkan/Core/VkObject.h" + +#include + +namespace MAGE +{ + class VQueue; + class VCmdBuffer; + + struct CmdPoolProps final + { + VQueue* queue = nullptr; + vk::CommandPoolCreateFlags createFlags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer; + }; + + class VCmdPool final : public VkObject + { + public: + VCmdPool(const CmdPoolProps& desc, VDevice* device); + ~VCmdPool() override final; + + Owned CreateBuffer(vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary); + + inline vk::CommandPool GetVkCmdPool() const { return m_pool; } + + void Destroy() override final; + + private: + CmdPoolProps m_props; + + vk::CommandPool m_pool; + }; +} diff --git a/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.cpp b/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.cpp index 05a1dd5..b121767 100644 --- a/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.cpp +++ b/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.cpp @@ -6,7 +6,7 @@ namespace MAGE { - VDescLayout::VDescLayout(const DescLayoutProps& desc, VDevice* device) : VkObject(device) + VDescLayout::VDescLayout(const DescLayoutProps& desc, VDevice* device) : VkObject(device), m_props(desc) { Vector bindings(desc.bindings.size()); diff --git a/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.h b/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.h index d6a97d4..fc0f6c1 100644 --- a/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.h +++ b/Source/Engine/Vulkan/RHI/Descriptor/VDescLayout.h @@ -44,6 +44,8 @@ namespace MAGE void Destroy() override final; private: + DescLayoutProps m_props; + vk::DescriptorSetLayout m_layout; }; } diff --git a/Source/Engine/Vulkan/RHI/Device/VDevice.cpp b/Source/Engine/Vulkan/RHI/Device/VDevice.cpp index 0ab774b..f8216aa 100644 --- a/Source/Engine/Vulkan/RHI/Device/VDevice.cpp +++ b/Source/Engine/Vulkan/RHI/Device/VDevice.cpp @@ -1,6 +1,7 @@ #include "VDevice.h" #include "../Instance/VInstance.h" #include "../Queue/VQueue.h" +#include "../Sync/VFence.h" #include "Engine/Vulkan/Core/VkAssert.h" @@ -234,6 +235,31 @@ namespace MAGE } } + void VDevice::WaitForFences(const Vector& fences) const + { + Vector ctx(fences.size(), VK_NULL_HANDLE); + + for (u32 i = 0; i < ctx.size(); i++) + ctx[i] = fences[i]->GetVkFence(); + + ErrorUtils::VkAssert(m_device.waitForFences(ctx.size(), ctx.data(), false, u64_max), "VFence"); + } + + void VDevice::ResetFences(const Vector& fences) const + { + Vector ctx(fences.size(), VK_NULL_HANDLE); + + for (u32 i = 0; i < ctx.size(); i++) + ctx[i] = fences[i]->GetVkFence(); + + ErrorUtils::VkAssert(m_device.resetFences(ctx.size(), ctx.data()), "VFence"); + } + + void VDevice::WaitForIdle() const + { + m_device.waitIdle(); + } + void VDevice::Destroy() { m_memAllocator->Destroy(); diff --git a/Source/Engine/Vulkan/RHI/Device/VDevice.h b/Source/Engine/Vulkan/RHI/Device/VDevice.h index 1a904c3..708e2d4 100644 --- a/Source/Engine/Vulkan/RHI/Device/VDevice.h +++ b/Source/Engine/Vulkan/RHI/Device/VDevice.h @@ -17,7 +17,7 @@ namespace MAGE { class VInstance; class VQueue; - + class VFence; struct DeviceProps final { @@ -71,11 +71,15 @@ namespace MAGE inline vk::Instance GetVkInstance() const { return m_instance; } inline vk::PhysicalDevice GetVkAdapter() const { return m_adapter; } + MemoryAllocator* GetAllocator() const { return &*m_memAllocator; } u32 FindMemoryType(vk::MemoryPropertyFlags properties); Owned CreateQueue(vk::QueueFlagBits queueType); - MemoryAllocator* GetAllocator() const { return &*m_memAllocator; } + void WaitForFences(const Vector& fences) const; + void ResetFences(const Vector& fences) const; + void WaitForIdle() const; + void Destroy() override final; private: diff --git a/Source/Engine/Vulkan/RHI/Instance/VInstance.cpp b/Source/Engine/Vulkan/RHI/Instance/VInstance.cpp index 0966c7f..950405b 100644 --- a/Source/Engine/Vulkan/RHI/Instance/VInstance.cpp +++ b/Source/Engine/Vulkan/RHI/Instance/VInstance.cpp @@ -144,7 +144,7 @@ namespace MAGE vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose | vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning | vk::DebugUtilsMessageSeverityFlagBitsEXT::eError, vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral | vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | - vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance, + vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance | vk::DebugUtilsMessageTypeFlagBitsEXT::eDeviceAddressBinding, DebugCallback); m_debugger = m_instance.createDebugUtilsMessengerEXT(debugCreateInfo, nullptr, m_loader); diff --git a/Source/Engine/Vulkan/RHI/Pipeline/VPipeline.cpp b/Source/Engine/Vulkan/RHI/Pipeline/VPipeline.cpp new file mode 100644 index 0000000..c1c4d2a --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Pipeline/VPipeline.cpp @@ -0,0 +1,201 @@ +#include "VPipeline.h" + +#include "../Device/VDevice.h" +#include "../Descriptor/VDescLayout.h" +#include "../Shader/VShader.h" + +#include "Engine/Vulkan/Core/VkAssert.h" +#include "Engine/Vulkan/Core/VkUtils.h" + +namespace MAGE +{ + VPipeline::VPipeline(const GraphicsPipelineProps& desc, VDevice* device) : VkObject(device), m_gProps(desc), m_bindPoint(vk::PipelineBindPoint::eGraphics) + { + Vector dLayouts(desc.layouts.size()); + for (u32 i = 0; i < dLayouts.size(); i++) + dLayouts[i] = desc.layouts[i]->GetVkLayout(); + + vk::PipelineLayoutCreateInfo layoutInfo = {}; + layoutInfo.setLayoutCount = static_cast(dLayouts.size()); + layoutInfo.pSetLayouts = dLayouts.data(); + + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().createPipelineLayout(&layoutInfo, nullptr, &m_layout), "VGraphicsPipeline"); + + Vector sStages; + + for (u32 i = 0; i < desc.shaderStages.size(); i++) + { + auto& shader = desc.shaderStages[i]; + vk::PipelineShaderStageCreateInfo info = {}; + info.module = shader->GetVkShaderModule(); + info.pName = shader->GetEntryPoint().CharString(); + info.stage = shader->GetVkShaderStage(); + sStages.push_back(info); + } + + Vector bindings; + Vector attributes; + for (u8 bindIndex = 0; bindIndex < desc.inputAssembler.bindings.size(); bindIndex++) + { + const auto& element = desc.inputAssembler.bindings[bindIndex]; + vk::VertexInputBindingDescription binding = {}; + binding.binding = bindIndex; + binding.inputRate = element.inputRate; + + u32 offset = 0; + for (u8 attIndex = 0; attIndex < element.attributes.size(); attIndex++) + { + vk::VertexInputAttributeDescription attr = {}; + attr.binding = bindIndex; + attr.location = attIndex; + attr.format = element.attributes[attIndex]; + attr.offset = offset; + attributes.push_back(attr); + + offset += VkUtils::GetVkTextureSize(element.attributes[attIndex]); + } + binding.stride = offset; + bindings.push_back(binding); + } + + // VertexInputState + vk::PipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.vertexBindingDescriptionCount = static_cast(bindings.size()); + vertexInputInfo.pVertexBindingDescriptions = bindings.data(); + vertexInputInfo.vertexAttributeDescriptionCount = static_cast(attributes.size()); + vertexInputInfo.pVertexAttributeDescriptions = attributes.data(); + + // InputAssemblyState + vk::PipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.topology = desc.inputAssembler.topology; + inputAssembly.primitiveRestartEnable = desc.inputAssembler.primitiveRestartEnable; + + Vector dStates; + if (desc.viewportState.dynamicView) + dStates.push_back(vk::DynamicState::eViewport); + if (desc.viewportState.dynamicScissor) + dStates.push_back(vk::DynamicState::eScissor); + + // DynamicPipelineState + vk::PipelineDynamicStateCreateInfo dynamicState = {}; + dynamicState.dynamicStateCount = static_cast(dStates.size()); + dynamicState.pDynamicStates = dStates.data(); + + // ViewportState + vk::PipelineViewportStateCreateInfo viewportState = {}; + viewportState.viewportCount = 1; + viewportState.pViewports = &desc.viewportState.viewport; + viewportState.scissorCount = 1; + viewportState.pScissors = &desc.viewportState.scissor; + + // RasterizationState + vk::PipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = desc.rasterizerState.polygonMode; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = desc.rasterizerState.cullMode; + rasterizer.frontFace = desc.rasterizerState.frontFace; + rasterizer.depthBiasEnable = desc.rasterizerState.depthBiasEnable; + rasterizer.depthBiasConstantFactor = desc.rasterizerState.depthBiasConstantFactor; + rasterizer.depthBiasClamp = desc.rasterizerState.depthBiasClamp; + rasterizer.depthBiasSlopeFactor = desc.rasterizerState.depthBiasSlopeFactor; + + // MultisampleState + vk::PipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1; + + // ColorBlendAttachmentState + Vector colorBlendAttachments; + for (auto& attachment : desc.blendState.attachments) + { + vk::PipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = attachment.colorWriteMask; + + colorBlendAttachment.blendEnable = attachment.blendEnable; + + colorBlendAttachment.srcColorBlendFactor = attachment.srcColorBlendFactor; + colorBlendAttachment.dstColorBlendFactor = attachment.dstColorBlendFactor; + colorBlendAttachment.colorBlendOp = attachment.colorBlendOp; + + colorBlendAttachment.srcAlphaBlendFactor = attachment.srcAlphaBlendFactor; + colorBlendAttachment.dstAlphaBlendFactor = attachment.dstAlphaBlendFactor; + colorBlendAttachment.alphaBlendOp = attachment.alphaBlendOp; + + colorBlendAttachments.push_back(colorBlendAttachment); + } + + // ColorBlendState + vk::PipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.logicOpEnable = desc.blendState.logicOpEnable; + colorBlending.logicOp = desc.blendState.logicOp; + colorBlending.attachmentCount = static_cast(colorBlendAttachments.size()); + colorBlending.pAttachments = colorBlendAttachments.data(); + colorBlending.blendConstants[0] = 0.0f; + colorBlending.blendConstants[1] = 0.0f; + colorBlending.blendConstants[2] = 0.0f; + colorBlending.blendConstants[3] = 0.0f; + + // DepthStencilState + vk::PipelineDepthStencilStateCreateInfo depthstencil = {}; + depthstencil.depthWriteEnable = desc.depthStencilState.depthWriteEnable; + depthstencil.depthTestEnable = desc.depthStencilState.depthTestEnable; + depthstencil.depthCompareOp = desc.depthStencilState.depthCompareOp; + depthstencil.depthBoundsTestEnable = VK_FALSE; + depthstencil.stencilTestEnable = VK_FALSE; + depthstencil.minDepthBounds = 0.0f; + depthstencil.maxDepthBounds = 1.0f; + depthstencil.front = desc.depthStencilState.front; + depthstencil.back = desc.depthStencilState.back; + + // Dynamic Rendering Info + // It will be added to pNext of VkGraphicsPipelineCreateInfo + vk::PipelineRenderingCreateInfo renderingInfo = {}; + renderingInfo.colorAttachmentCount = desc.dynamicRendering.colorAttachments.size(); + renderingInfo.pColorAttachmentFormats = desc.dynamicRendering.colorAttachments.data(); + renderingInfo.depthAttachmentFormat = desc.dynamicRendering.depthAttachment; + renderingInfo.stencilAttachmentFormat = desc.dynamicRendering.stencilAttachment; + renderingInfo.pNext = nullptr; + renderingInfo.viewMask = desc.dynamicRendering.viewMask; + + vk::GraphicsPipelineCreateInfo pipelineInfo{}; + pipelineInfo.stageCount = static_cast(sStages.size()); + pipelineInfo.pStages = sStages.data(); + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pDepthStencilState = &depthstencil; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = dStates.size() > 0 ? &dynamicState : nullptr; + pipelineInfo.layout = m_layout; + pipelineInfo.renderPass = nullptr; + pipelineInfo.subpass = 0; + pipelineInfo.flags = desc.createFlag; + pipelineInfo.pNext = &renderingInfo; + + ErrorUtils::VkAssert(device->GetVkDevice().createGraphicsPipelines({}, 1, &pipelineInfo, nullptr, &m_pipeline), "VulkanPipeline"); + } + + VPipeline::~VPipeline() + { + Destroy(); + } + + void VPipeline::Destroy() + { + if (m_pipeline != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().destroyPipeline(m_pipeline); + m_pipeline = VK_NULL_HANDLE; + } + + if (m_layout != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().destroyPipelineLayout(m_layout); + m_layout = VK_NULL_HANDLE; + } + } +} diff --git a/Source/Engine/Vulkan/RHI/Pipeline/VPipeline.h b/Source/Engine/Vulkan/RHI/Pipeline/VPipeline.h new file mode 100644 index 0000000..70c4902 --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Pipeline/VPipeline.h @@ -0,0 +1,125 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include "Engine/Vulkan/Core/VkObject.h" +#include + +namespace MAGE +{ + class VDescLayout; + class VShader; + + struct InputBinding final + { + vk::VertexInputRate inputRate = vk::VertexInputRate::eInstance; + Vector attributes; + }; + + struct InputAssembler final + { + vk::PrimitiveTopology topology = vk::PrimitiveTopology::eTriangleList; + b8 primitiveRestartEnable = false; + Vector bindings; + }; + + struct ViewportState final + { + b8 dynamicView = false; + vk::Viewport viewport; + b8 dynamicScissor = false; + vk::Rect2D scissor; + }; + + struct RasterizerState final + { + b8 depthBiasEnable = true; + f32 depthBiasConstantFactor = 0.0f; + f32 depthBiasClamp = 0.0f; + f32 depthBiasSlopeFactor = 0.0f; + vk::PolygonMode polygonMode = vk::PolygonMode::eFill; + vk::CullModeFlags cullMode = vk::CullModeFlagBits::eBack; + vk::FrontFace frontFace = vk::FrontFace::eClockwise; + }; + + struct BlendStateAttachment final + { + b8 blendEnable = true; + vk::BlendFactor srcColorBlendFactor = vk::BlendFactor::eZero; + vk::BlendFactor dstColorBlendFactor = vk::BlendFactor::eOne; + vk::BlendOp colorBlendOp = vk::BlendOp::eAdd; + vk::BlendFactor srcAlphaBlendFactor = vk::BlendFactor::eZero; + vk::BlendFactor dstAlphaBlendFactor = vk::BlendFactor::eOne; + vk::BlendOp alphaBlendOp = vk::BlendOp::eAdd; + vk::ColorComponentFlags colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA; + }; + + struct BlendState final + { + b8 logicOpEnable = true; + vk::LogicOp logicOp = vk::LogicOp::eClear; + Vector attachments; + }; + + struct DepthStencilState final + { + b8 depthTestEnable = true; + b8 depthWriteEnable = true; + vk::CompareOp depthCompareOp = vk::CompareOp::eNever; + b8 depthBoundsTestEnable = false; + b8 stencilTestEnable = false; + vk::StencilOpState front = vk::StencilOpState(); + vk::StencilOpState back = vk::StencilOpState(); + f32 minDepthBounds = 0.0f; + f32 maxDepthBounds = 1.0f; + }; + + struct DynamicAttachments final + { + Vector colorAttachments; + vk::Format depthAttachment = vk::Format::eUndefined; + vk::Format stencilAttachment = vk::Format::eUndefined; + u32 viewMask = 0; + }; + + struct GraphicsPipelineProps final + { + Vector layouts; + Vector shaderStages; + InputAssembler inputAssembler; + ViewportState viewportState; + RasterizerState rasterizerState; + BlendState blendState; + DepthStencilState depthStencilState; + DynamicAttachments dynamicRendering; + + vk::PipelineCreateFlags createFlag; + }; + + class VPipeline final : public VkObject + { + public: + VPipeline(const GraphicsPipelineProps& desc, VDevice* device); + ~VPipeline() override final; + + inline vk::PipelineLayout GetVkLayout() const { return m_layout; } + inline vk::Pipeline GetVkPipeline() const { return m_pipeline; } + + void Destroy() override final; + + private: + GraphicsPipelineProps m_gProps; + + vk::PipelineLayout m_layout; + vk::Pipeline m_pipeline; + + vk::PipelineBindPoint m_bindPoint; + }; +} diff --git a/Source/Engine/Vulkan/RHI/Queue/VQueue.cpp b/Source/Engine/Vulkan/RHI/Queue/VQueue.cpp index c6a26f5..20ace52 100644 --- a/Source/Engine/Vulkan/RHI/Queue/VQueue.cpp +++ b/Source/Engine/Vulkan/RHI/Queue/VQueue.cpp @@ -1,10 +1,43 @@ #include "VQueue.h" #include "../Device/VDevice.h" +#include "../Command/VCmdBuffer.h" +#include "../Sync/VSemaphore.h" +#include "../Sync/VFence.h" + +#include "Engine/Vulkan/Core/VkAssert.h" namespace MAGE { VQueue::VQueue(const QueueProps& desc, vk::Queue queue, VDevice* device) : VkObject(device), m_props(desc), m_queue(queue) {} VQueue::~VQueue() { Destroy(); } + + void VQueue::Submit(const Vector& cmdBuffers, const Vector& signals, const Vector& waits, const Vector& dstFlags, VFence* fence) const + { + Vector cmds(cmdBuffers.size(), VK_NULL_HANDLE); + Vector signalSems(signals.size(), VK_NULL_HANDLE); + Vector waitSems(waits.size(), VK_NULL_HANDLE); + + for (u32 i = 0; i < cmds.size(); i++) + cmds[i] = cmdBuffers[i]->GetVkCmdBuffer(); + + for (u32 i = 0; i < signalSems.size(); i++) + signalSems[i] = signals[i]->GetVkSemaphore(); + + for (u32 i = 0; i < waitSems.size(); i++) + waitSems[i] = waits[i]->GetVkSemaphore(); + + vk::SubmitInfo info = {}; + info.commandBufferCount = cmdBuffers.size(); + info.pCommandBuffers = cmds.data(); + info.waitSemaphoreCount = waits.size(); + info.pWaitSemaphores = waitSems.data(); + info.signalSemaphoreCount = signals.size(); + info.pSignalSemaphores = signalSems.data(); + info.pWaitDstStageMask = dstFlags.data(); + + ErrorUtils::VkAssert(m_queue.submit(1, &info, fence ? fence->GetVkFence() : vk::Fence()), "VSemaphore"); + } + void VQueue::Destroy() {} } diff --git a/Source/Engine/Vulkan/RHI/Queue/VQueue.h b/Source/Engine/Vulkan/RHI/Queue/VQueue.h index d8e5b54..e7b0ca0 100644 --- a/Source/Engine/Vulkan/RHI/Queue/VQueue.h +++ b/Source/Engine/Vulkan/RHI/Queue/VQueue.h @@ -14,6 +14,10 @@ namespace MAGE { + class VCmdBuffer; + class VSemaphore; + class VFence; + struct QueueProps final { QueueProps(u32 index = 255, vk::QueueFlagBits flag = vk::QueueFlagBits::eGraphics) : @@ -34,6 +38,8 @@ namespace MAGE inline u32 GetFamilyIndex() const { return m_props.familyIndex; } inline vk::QueueFlagBits GetType() const { return m_props.flags; } + void Submit(const Vector& cmdBuffers, const Vector& signals, const Vector& waits, const Vector& dstFlags, VFence* fence) const; + void Destroy() override final; private: diff --git a/Source/Engine/Vulkan/RHI/Sampler/VSampler.h b/Source/Engine/Vulkan/RHI/Sampler/VSampler.h index 904eca2..8af60fe 100644 --- a/Source/Engine/Vulkan/RHI/Sampler/VSampler.h +++ b/Source/Engine/Vulkan/RHI/Sampler/VSampler.h @@ -16,11 +16,11 @@ namespace MAGE { struct SamplerProps final { - SamplerProps(vk::Filter magF, vk::Filter minF, - vk::SamplerMipmapMode mipMode , vk::SamplerAddressMode addressU, - vk::SamplerAddressMode addressV, vk::SamplerAddressMode addressW, - f32 mipBias, b8 aEnable, f32 maxA, b8 cEnable, - vk::CompareOp compOp, f32 minLod, f32 maxLod) : magFilter(magF), minFilter(minF), + SamplerProps(vk::Filter magF = vk::Filter::eNearest, vk::Filter minF = vk::Filter::eNearest, + vk::SamplerMipmapMode mipMode = vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode addressU = vk::SamplerAddressMode::eRepeat, + vk::SamplerAddressMode addressV = vk::SamplerAddressMode::eRepeat, vk::SamplerAddressMode addressW = vk::SamplerAddressMode::eRepeat, + f32 mipBias = 0.0f, b8 aEnable = true, f32 maxA = 1.0f, b8 cEnable = true, + vk::CompareOp compOp = vk::CompareOp::eAlways, f32 minLod = 0.0f, f32 maxLod = 1.0f) : magFilter(magF), minFilter(minF), mipmapMode(mipMode), addressModeU(addressU), addressModeV(addressV), addressModeW(addressW), mipLodBias(mipBias), anisotropyEnable(aEnable), maxAnisotropy(maxA), compareEnable(cEnable), compareOp(compOp), minLod(minLod), maxLod(maxLod) diff --git a/Source/Engine/Vulkan/RHI/Shader/VShader.cpp b/Source/Engine/Vulkan/RHI/Shader/VShader.cpp new file mode 100644 index 0000000..fd76298 --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Shader/VShader.cpp @@ -0,0 +1,35 @@ +#include "VShader.h" + +#include "../Device/VDevice.h" +#include "Engine/Vulkan/Core/VkAssert.h" + +#include "Engine/ShaderCompiler/ShaderCompiler.h" +#include "Engine/Data/Memory/OwnedBuffer.h" + +namespace MAGE +{ + VShader::VShader(const ShaderProps& desc, VDevice* device) : VkObject(device), m_props(desc), m_shaderModule(VK_NULL_HANDLE) + { + OwnedBuffer data = ShaderCompiler::CompileShader(desc.shaderPath, desc.includePath, desc.entryPoint); + + vk::ShaderModuleCreateInfo createInfo{}; + createInfo.codeSize = static_cast(data.Size()); + createInfo.pCode = reinterpret_cast(data.Data()); + + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().createShaderModule(&createInfo, nullptr, &m_shaderModule), "VulkanShader"); + } + + VShader::~VShader() + { + Destroy(); + } + + void MAGE::VShader::Destroy() + { + if (m_shaderModule != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().destroyShaderModule(m_shaderModule); + m_shaderModule = VK_NULL_HANDLE; + } + } +} diff --git a/Source/Engine/Vulkan/RHI/Shader/VShader.h b/Source/Engine/Vulkan/RHI/Shader/VShader.h new file mode 100644 index 0000000..d04267b --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Shader/VShader.h @@ -0,0 +1,44 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include "Engine/Vulkan/Core/VkObject.h" +#include + +namespace MAGE +{ + struct ShaderProps final + { + String shaderPath; + String includePath = ""; + String entryPoint = "main"; + vk::ShaderStageFlagBits shaderStage; + }; + + class VShader final : public VkObject + { + public: + VShader(const ShaderProps& desc, VDevice* device); + ~VShader(); + + inline vk::ShaderModule GetVkShaderModule() const { return m_shaderModule; } + inline vk::ShaderStageFlagBits GetVkShaderStage() const { return m_props.shaderStage; } + + const String& GetShaderPath() const { return m_props.shaderPath; } + const String& GetIncludePath() const { return m_props.includePath; } + const String& GetEntryPoint() const { return m_props.entryPoint; } + + void Destroy() override final; + + private: + vk::ShaderModule m_shaderModule; + ShaderProps m_props; + }; +} \ No newline at end of file diff --git a/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.cpp b/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.cpp index d77e22c..7e9dc7d 100644 --- a/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.cpp +++ b/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.cpp @@ -4,6 +4,8 @@ #include "../Image/VImage.h" #include "../Image/VImageView.h" #include "../Queue/VQueue.h" +#include "../Sync/VFence.h" +#include "../Sync/VSemaphore.h" #include "Engine/Vulkan/Core/VkAssert.h" #include "Engine/Window/WindowManager.h" @@ -124,6 +126,35 @@ namespace MAGE } } + u32 VSwapchain::AcquireNextImage(VSemaphore* semaphore, VFence* fence) + { + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().acquireNextImageKHR(m_swapchain, u64_max, + semaphore ? semaphore->GetVkSemaphore() : vk::Semaphore(), fence ? fence->GetVkFence() : vk::Fence(), &m_requestedIndex), "VSwapchain"); + + return m_requestedIndex; + } + + void VSwapchain::Present(const Vector& waitSems) const + { + Vector waits(waitSems.size(), VK_NULL_HANDLE); + + for (u32 i = 0; i < waits.size(); i++) + waits[i] = waitSems[i]->GetVkSemaphore(); + + vk::Result returnResult = {}; + + vk::PresentInfoKHR present = {}; + present.pImageIndices = &m_requestedIndex; + present.swapchainCount = 1; + present.pSwapchains = &m_swapchain; + present.waitSemaphoreCount = waits.size(); + present.pWaitSemaphores = waits.data(); + present.pResults = &returnResult; + + ErrorUtils::VkAssert(m_props.graphicsQueue->GetVkQueue().presentKHR(&present), "VSwapchain"); + ErrorUtils::VkAssert(returnResult, "VSwapchain - Present"); + } + void VSwapchain::Destroy() { if (m_barrierFence != VK_NULL_HANDLE) diff --git a/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.h b/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.h index 134866d..5c8fae3 100644 --- a/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.h +++ b/Source/Engine/Vulkan/RHI/Swapchain/VSwapchain.h @@ -47,14 +47,16 @@ namespace MAGE inline vk::SurfaceKHR GetVkSurface() const { return m_surface; } inline vk::SwapchainKHR GetVkSwapchain() const { return m_swapchain; } - inline vk::Format GetFormat() const { return m_props.imageFormat; } - inline vk::PresentModeKHR GetPresentMode() const { return m_props.presentMode; } + inline vk::Format GetVkFormat() const { return m_props.imageFormat; } + inline vk::PresentModeKHR GetVkPresentMode() const { return m_props.presentMode; } inline const vk::Extent2D& GetImageSize() const { return m_props.imageSize; } inline u32 GetBufferCount() const { return m_props.imageCount; } VImage* GetSwapchainImage() const { return &*m_images[m_requestedIndex]; } VImageView* GetSwapchainView() const { return &*m_views[m_requestedIndex]; } + u32 AcquireNextImage(VSemaphore* semaphore, VFence* fence); + void Present(const Vector& waitSems) const; void Destroy() override final; private: diff --git a/Source/Engine/Vulkan/RHI/Sync/VFence.cpp b/Source/Engine/Vulkan/RHI/Sync/VFence.cpp new file mode 100644 index 0000000..2693224 --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Sync/VFence.cpp @@ -0,0 +1,37 @@ +#include "VFence.h" + +#include "../Device/VDevice.h" +#include "Engine/Vulkan/Core/VkAssert.h" + +namespace MAGE +{ + VFence::VFence(VDevice* device) : VkObject(device) + { + vk::FenceCreateInfo info = {}; + ErrorUtils::VkAssert(device->GetVkDevice().createFence(&info, nullptr, &m_fence), "VFence"); + } + + VFence::~VFence() + { + Destroy(); + } + + void VFence::Wait() const + { + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().waitForFences(1, &m_fence, false, u64_max), "VFence"); + } + + void VFence::Reset() const + { + ErrorUtils::VkAssert(m_rootDevice->GetVkDevice().resetFences(1, &m_fence), "VFence"); + } + + void VFence::Destroy() + { + if (m_fence != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().destroyFence(m_fence); + m_fence = VK_NULL_HANDLE; + } + } +} diff --git a/Source/Engine/Vulkan/RHI/Sync/VFence.h b/Source/Engine/Vulkan/RHI/Sync/VFence.h new file mode 100644 index 0000000..6f0b97c --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Sync/VFence.h @@ -0,0 +1,33 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include "Engine/Vulkan/Core/VkObject.h" +#include + +namespace MAGE +{ + class VFence final : public VkObject + { + public: + VFence(VDevice* device); + ~VFence() override final; + + inline vk::Fence GetVkFence() const { return m_fence; } + + void Wait() const; + void Reset() const; + + void Destroy() override final; + + private: + vk::Fence m_fence; + }; +} diff --git a/Source/Engine/Vulkan/RHI/Sync/VSemaphore.cpp b/Source/Engine/Vulkan/RHI/Sync/VSemaphore.cpp new file mode 100644 index 0000000..a731e0c --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Sync/VSemaphore.cpp @@ -0,0 +1,27 @@ +#include "VSemaphore.h" + +#include "../Device/VDevice.h" +#include "Engine/Vulkan/Core/VkAssert.h" + +namespace MAGE +{ + VSemaphore::VSemaphore(VDevice* device) : VkObject(device) + { + vk::SemaphoreCreateInfo info = {}; + ErrorUtils::VkAssert(device->GetVkDevice().createSemaphore(&info, nullptr, &m_semaphore), "VFence"); + } + + VSemaphore::~VSemaphore() + { + Destroy(); + } + + void VSemaphore::Destroy() + { + if (m_semaphore != VK_NULL_HANDLE) + { + m_rootDevice->GetVkDevice().destroySemaphore(m_semaphore); + m_semaphore = VK_NULL_HANDLE; + } + } +} diff --git a/Source/Engine/Vulkan/RHI/Sync/VSemaphore.h b/Source/Engine/Vulkan/RHI/Sync/VSemaphore.h new file mode 100644 index 0000000..21985ab --- /dev/null +++ b/Source/Engine/Vulkan/RHI/Sync/VSemaphore.h @@ -0,0 +1,30 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) 2024 Metehan Tuncbilek + */ + +#pragma once + +#include "Engine/Core/Core.h" +#include "Engine/Vulkan/Core/VkObject.h" +#include + +namespace MAGE +{ + class VSemaphore final : public VkObject + { + public: + VSemaphore(VDevice* device); + ~VSemaphore() override final; + + inline vk::Semaphore GetVkSemaphore() const { return m_semaphore; } + + void Destroy() override final; + + private: + vk::Semaphore m_semaphore; + }; +}