diff --git a/SHADE_Application/SHADE_Application.vcxproj b/SHADE_Application/SHADE_Application.vcxproj
index d83a0c64..11bbb48f 100644
--- a/SHADE_Application/SHADE_Application.vcxproj
+++ b/SHADE_Application/SHADE_Application.vcxproj
@@ -71,7 +71,7 @@
Windows
true
- WinMainCRTStartup
+ wWinMainCRTStartup
@@ -94,7 +94,7 @@
Windows
true
true
- WinMainCRTStartup
+ wWinMainCRTStartup
diff --git a/SHADE_Application/premake5.lua b/SHADE_Application/premake5.lua
index 35440432..c10b1002 100644
--- a/SHADE_Application/premake5.lua
+++ b/SHADE_Application/premake5.lua
@@ -8,7 +8,7 @@ project "SHADE_Application"
pchheader "SBpch.h"
pchsource "%{prj.location}/src/SBpch.cpp"
staticruntime "on"
- entrypoint "WinMainCRTStartup"
+ entrypoint "wWinMainCRTStartup"
system ("windows")
files
diff --git a/SHADE_Application/src/Application/SBApplication.cpp b/SHADE_Application/src/Application/SBApplication.cpp
index c420a37f..184b9611 100644
--- a/SHADE_Application/src/Application/SBApplication.cpp
+++ b/SHADE_Application/src/Application/SBApplication.cpp
@@ -13,9 +13,16 @@
namespace Sandbox
{
bool paused = false;
- void SBApplication::Initialize(void)
+ void SBApplication::Initialize
+ (
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPWSTR lpCmdLine,
+ _In_ INT nCmdShow
+ )
{
+ window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
#ifdef SHEDITOR
#else
@@ -26,7 +33,7 @@ namespace Sandbox
void SBApplication::Update(void)
{
//TODO: Change true to window is open
- while(true)
+ while (!window.WindowShouldClose())
{
#ifdef SHEDITOR
#else
diff --git a/SHADE_Application/src/Application/SBApplication.h b/SHADE_Application/src/Application/SBApplication.h
index c8e02863..a1bf11eb 100644
--- a/SHADE_Application/src/Application/SBApplication.h
+++ b/SHADE_Application/src/Application/SBApplication.h
@@ -1,6 +1,6 @@
#ifndef SB_APPLICATION_H
#define SB_APPLICATION_H
-
+#include
//using namespace SHADE;
namespace Sandbox
@@ -8,10 +8,14 @@ namespace Sandbox
class SBApplication
{
private:
+ SHADE::SHWindow window;
//SHAppConfig config;
public:
SBApplication() = default;
- void Initialize(void);
+ void Initialize(_In_ HINSTANCE /*hInstance*/,
+ _In_opt_ HINSTANCE /*hPrevInstance*/,
+ _In_ LPWSTR /*lpCmdLine*/,
+ _In_ INT /*nCmdShow*/);
void Update(void);
void Exit(void);
private:
diff --git a/SHADE_Application/src/WinMain.cpp b/SHADE_Application/src/WinMain.cpp
index 50be1289..ea1ac5fe 100644
--- a/SHADE_Application/src/WinMain.cpp
+++ b/SHADE_Application/src/WinMain.cpp
@@ -17,12 +17,12 @@
#define DBG_NEW new
#endif
-INT WINAPI WinMain
+INT WINAPI wWinMain
(
- _In_ HINSTANCE /*hInstance*/,
- _In_opt_ HINSTANCE /*hPrevInstance*/,
- _In_ PSTR /*lpCmdLine*/,
- _In_ INT /*nCmdShow*/
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPWSTR lpCmdLine,
+ _In_ INT nCmdShow
)
{
const SHADE::SHLogger::Config LOGGER_CONFIG{ .directoryPath = "./logs/" };
@@ -36,7 +36,7 @@ INT WINAPI WinMain
SHLOG_INFO("sup")
- SHADE::SHEngine::Run();
+ SHADE::SHEngine::Run(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
}
catch(...)
diff --git a/SHADE_Engine/SHADE_Engine.vcxproj b/SHADE_Engine/SHADE_Engine.vcxproj
index dcc0f3c8..71ffb25f 100644
--- a/SHADE_Engine/SHADE_Engine.vcxproj
+++ b/SHADE_Engine/SHADE_Engine.vcxproj
@@ -57,8 +57,8 @@
Use
SHpch.h
Level4
- _LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;_DEBUG;%(PreprocessorDefinitions)
- src;..\Dependencies\assimp\include;..\Dependencies\imgui;..\Dependencies\imguizmo;..\Dependencies\imnodes;..\Dependencies\msdf;..\Dependencies\msdf\msdfgen;..\Dependencies\spdlog\include;..\Dependencies\tracy;..\Dependencies\VMA\include;..\Dependencies\yamlcpp\include;..\Dependencies\ktx\include;..\Dependencies\RTTR\include;..\Dependencies\reactphysics3d\include;$(VULKAN_SDK)\include;%(AdditionalIncludeDirectories)
+ _LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;NOMINMAX;_DEBUG;%(PreprocessorDefinitions)
+ src;..\Dependencies\assimp\include;..\Dependencies\imgui;..\Dependencies\imguizmo;..\Dependencies\imnodes;..\Dependencies\msdf;..\Dependencies\msdf\msdfgen;..\Dependencies\spdlog\include;..\Dependencies\tracy;..\Dependencies\VMA\include;..\Dependencies\yamlcpp\include;..\Dependencies\ktx\include;..\Dependencies\RTTR\include;..\Dependencies\reactphysics3d\include;$(VULKAN_SDK)\include;$(VULKAN_SDK)\Source\SPIRV-Reflect;%(AdditionalIncludeDirectories)
EditAndContinue
Disabled
false
@@ -80,8 +80,8 @@
Use
SHpch.h
Level4
- _LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;_RELEASE;%(PreprocessorDefinitions)
- src;..\Dependencies\assimp\include;..\Dependencies\imgui;..\Dependencies\imguizmo;..\Dependencies\imnodes;..\Dependencies\msdf;..\Dependencies\msdf\msdfgen;..\Dependencies\spdlog\include;..\Dependencies\tracy;..\Dependencies\VMA\include;..\Dependencies\yamlcpp\include;..\Dependencies\ktx\include;..\Dependencies\RTTR\include;..\Dependencies\reactphysics3d\include;$(VULKAN_SDK)\include;%(AdditionalIncludeDirectories)
+ _LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;NOMINMAX;_RELEASE;%(PreprocessorDefinitions)
+ src;..\Dependencies\assimp\include;..\Dependencies\imgui;..\Dependencies\imguizmo;..\Dependencies\imnodes;..\Dependencies\msdf;..\Dependencies\msdf\msdfgen;..\Dependencies\spdlog\include;..\Dependencies\tracy;..\Dependencies\VMA\include;..\Dependencies\yamlcpp\include;..\Dependencies\ktx\include;..\Dependencies\RTTR\include;..\Dependencies\reactphysics3d\include;$(VULKAN_SDK)\include;$(VULKAN_SDK)\Source\SPIRV-Reflect;%(AdditionalIncludeDirectories)
Full
true
true
@@ -116,7 +116,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -132,6 +190,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Create
@@ -163,4 +268,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/SHADE_Engine/SHADE_Engine.vcxproj.filters b/SHADE_Engine/SHADE_Engine.vcxproj.filters
index 8d62d05b..f38a78a9 100644
--- a/SHADE_Engine/SHADE_Engine.vcxproj.filters
+++ b/SHADE_Engine/SHADE_Engine.vcxproj.filters
@@ -19,9 +19,87 @@
{B3E3FAFD-9FDD-2350-884A-BA6074E389BC}
+
+ {1653CE33-0220-293F-2B39-17E717655ECD}
+
+
+ {92C817CE-7EC1-3620-A7F3-1BA5934B162C}
+
+
+ {17C745C0-83DD-4356-CC54-CF7738AA14DE}
+
+
+ {51443AC7-3D28-FB1C-A688-F56F928BE59E}
+
+
+ {573A6CF2-43C9-F5BB-ECE7-09B7D8550662}
+
+
+ {08DBDC43-F4D3-FB95-1D06-E11A095EDBA1}
+
+
+ {4AD5CA42-3664-540C-DF82-6807CBF064B2}
+
+
+ {FB5EE099-67EA-4D5E-70FB-D052DC05AA5E}
+
+
+ {BA26540B-263D-52A1-6FB4-DDC2DB092329}
+
+
+ {4B204703-3704-0859-A064-02AC8C67F2DA}
+
+
+ {EBA1D3FF-D75C-C3AB-8014-3CF66CAE0D3C}
+
+
+ {8CDBA7C9-F8E8-D5AF-81CF-D19AEDDBA166}
+
+
+ {2460C057-1070-6C28-7929-D14665585BC1}
+
+
+ {FBD334F8-67EA-328E-B061-BEAF1CB70316}
+
+
+ {1DD51CAD-8960-8A71-9271-0D66FE7BE671}
+
+
+ {57DAB30C-4369-3DD6-EC87-51D1D8F54D7C}
+
+
+ {9C0DAFD9-086F-8CE7-91DC-D299FD3CC3A6}
+
+
+ {EF2D07CC-DB26-261E-0459-0BA3F0B0052A}
+
+
+ {3AEF06DD-A6D2-151D-AFD5-43591B38DC6D}
+
+
+ {245F5AB0-1085-2417-F9CA-A9E2E58F49E3}
+
+
+ {03DB39DE-EFBE-FA33-581F-F5864422E5B5}
+
+
+ {576DF841-4392-47C2-6CDD-2C52586146E0}
+
+
+ {75F29FE5-6102-4CB6-CABB-B0D4B6EA3A4F}
+
+
+ {5BAB2A92-478F-EBE7-B0EF-E53A9CF2D569}
+
+
+ {B3B14D12-9FC1-F9E2-087B-5E01F4A9E87B}
+
{AC05897C-983C-8A0D-4129-70102D3F060F}
+
+ {ED6CDF9B-D939-3AA7-0253-284FEE7E6F35}
+
{B3F7140E-1F0C-3DBF-E88D-E01E546139F0}
@@ -72,9 +150,183 @@
Engine
+
+ Graphics\Buffers
+
+
+ Graphics\Commands
+
+
+ Graphics\Commands
+
+
+ Graphics\Commands
+
+
+ Graphics\Debugging
+
+
+ Graphics\Debugging
+
+
+ Graphics\Debugging
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Devices
+
+
+ Graphics\Devices
+
+
+ Graphics\Devices
+
+
+ Graphics\Framebuffer
+
+
+ Graphics\Images
+
+
+ Graphics\Images
+
+
+ Graphics\Images
+
+
+ Graphics\Instance
+
+
+ Graphics\MiddleEnd\Interface
+
+
+ Graphics\MiddleEnd\Interface
+
+
+ Graphics\MiddleEnd\PerFrame
+
+
+ Graphics\MiddleEnd\PerFrame
+
+
+ Graphics\MiddleEnd\Shaders
+
+
+ Graphics\MiddleEnd\Shaders
+
+
+ Graphics\MiddleEnd\Shaders
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Queues
+
+
+ Graphics\RenderGraph
+
+
+ Graphics\Renderpass
+
+
+ Graphics\Renderpass
+
+
+ Graphics\Renderpass
+
+
+ Graphics\Renderpass
+
+
+ Graphics\Renderpass
+
+
+ Graphics
+
+
+ Graphics
+
+
+ Graphics
+
+
+ Graphics\Shaders\BlockInterface
+
+
+ Graphics\Shaders
+
+
+ Graphics\Shaders
+
+
+ Graphics\Shaders\spirv-reflect
+
+
+ Graphics\Swapchain
+
+
+ Graphics\Swapchain
+
+
+ Graphics\Synchronization
+
+
+ Graphics\Synchronization
+
+
+ Graphics\VertexDescriptors
+
+
+ Graphics\Windowing
+
+
+ Graphics\Windowing
+
+
+ Graphics\Windowing\Surface
+
Meta
+
+ Resource
+
+
+ Resource
+
+
+ Resource
+
Scene
@@ -114,6 +366,147 @@
Engine
+
+ Graphics\Buffers
+
+
+ Graphics\Commands
+
+
+ Graphics\Commands
+
+
+ Graphics\Debugging
+
+
+ Graphics\Debugging
+
+
+ Graphics\Debugging
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Descriptors
+
+
+ Graphics\Devices
+
+
+ Graphics\Devices
+
+
+ Graphics\Devices
+
+
+ Graphics\Framebuffer
+
+
+ Graphics\Images
+
+
+ Graphics\Images
+
+
+ Graphics\Instance
+
+
+ Graphics\MiddleEnd\Interface
+
+
+ Graphics\MiddleEnd\Interface
+
+
+ Graphics\MiddleEnd\PerFrame
+
+
+ Graphics\MiddleEnd\PerFrame
+
+
+ Graphics\MiddleEnd\Shaders
+
+
+ Graphics\MiddleEnd\Shaders
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Pipeline
+
+
+ Graphics\Queues
+
+
+ Graphics\RenderGraph
+
+
+ Graphics\Renderpass
+
+
+ Graphics\Renderpass
+
+
+ Graphics\Renderpass
+
+
+ Graphics
+
+
+ Graphics
+
+
+ Graphics\Shaders\BlockInterface
+
+
+ Graphics\Shaders
+
+
+ Graphics\Shaders
+
+
+ Graphics\Shaders\spirv-reflect
+
+
+ Graphics\Swapchain
+
+
+ Graphics\Synchronization
+
+
+ Graphics\Synchronization
+
+
+ Graphics\VertexDescriptors
+
+
+ Graphics\Windowing
+
+
+ Graphics\Windowing
+
+
+ Graphics\Windowing\Surface
+
+
+ Resource
+
Scene
diff --git a/SHADE_Engine/premake5.lua b/SHADE_Engine/premake5.lua
index 39d5039f..a94284df 100644
--- a/SHADE_Engine/premake5.lua
+++ b/SHADE_Engine/premake5.lua
@@ -34,7 +34,8 @@ project "SHADE_Engine"
"%{IncludeDir.ktx}/include",
"%{IncludeDir.RTTR}/include",
"%{IncludeDir.reactphysics3d}/include",
- "%{IncludeDir.VULKAN}/include"
+ "%{IncludeDir.VULKAN}/include",
+ "%{IncludeDir.VULKAN}/Source/SPIRV-Reflect"
}
libdirs
@@ -63,7 +64,8 @@ project "SHADE_Engine"
{
"_LIB",
"_GLFW_INCLUDE_NONE",
- "MSDFGEN_USE_CPP11"
+ "MSDFGEN_USE_CPP11",
+ "NOMINMAX"
}
flags
diff --git a/SHADE_Engine/src/Engine/SHEngine.h b/SHADE_Engine/src/Engine/SHEngine.h
index 87e1e28d..d6a50b32 100644
--- a/SHADE_Engine/src/Engine/SHEngine.h
+++ b/SHADE_Engine/src/Engine/SHEngine.h
@@ -20,16 +20,16 @@ namespace SHADE
{
public:
- template
- static void Run(void)
+ template
+ static void Run(Args&&...args)
{
- static_assert(SHIsDetected::value, "Init Not Detected");
+ //static_assert(SHIsDetected::value, "Init Not Detected");
static_assert(SHIsDetected::value, "Update Not Detected");
static_assert(SHIsDetected::value, "Exit Not Detected");
static Application application;
- application.Initialize();
+ application.Initialize(std::forward(args)...);
application.Update();
application.Exit();
};
diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp
new file mode 100644
index 00000000..4158d3c3
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.cpp
@@ -0,0 +1,460 @@
+#include "SHPch.h"
+#include "SHVkBuffer.h"
+#include "Graphics/Instance/SHVkInstance.h"
+#include "Graphics/Commands/SHVkCommandBuffer.h"
+
+namespace SHADE
+{
+ /***************************************************************************/
+ /*!
+
+ \brief
+
+
+ \param data
+ \param sizeToWrite
+ \param srcOffset
+ \param dstOffset
+ \param cmdBufferHdl
+
+ \return
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::TransferToDeviceResource(Handle const& cmdBufferHdl) noexcept
+ {
+ if (cmdBufferHdl && (bufferUsageFlags & vk::BufferUsageFlagBits::eTransferDst))
+ {
+ vk::BufferCopy copyRegion
+ {
+ .srcOffset = 0,
+ .dstOffset = 0,
+ .size = sizeStored,
+ };
+ cmdBufferHdl->GetVkCommandBuffer().copyBuffer(stagingBuffer, vkBuffer, 1, ©Region);
+ }
+ }
+
+ vk::Buffer SHVkBuffer::GetVkBuffer(void) const noexcept
+ {
+ return vkBuffer;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Maps the mappedPtr member pointer.
+
+ \param vmaAllocator
+ Required VmaAllocator for mapping.
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::Map(void) noexcept
+ {
+ if (!boundToCoherent)
+ vmaMapMemory(vmaAllocator, alloc, &mappedPtr);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Unmaps the mappedPtr member pointer. Does not work if pointer is mapped
+ persistently (i.e. buffer is bound to coherent memory).
+
+ \param vmaAllocator
+ Required VmaAllocator for unmapping.
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::Unmap(void) noexcept
+ {
+ if (!boundToCoherent)
+ {
+ vmaUnmapMemory(vmaAllocator, alloc);
+ mappedPtr = nullptr;
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Writes to the mapped pointer.
+
+ \param data
+ Pointer to source data.
+
+ \param sizeToWrite
+ Amount to write to the buffer.
+
+ \param srcOffset
+ byte offset into the source data.
+
+ \param dstOffset
+ byte offset into the destination data.
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::WriteToMemory(void* data, uint32_t sizeToWrite, uint32_t srcOffset, uint32_t dstOffset) noexcept
+ {
+ if (mappedPtr)
+ std::memcpy(static_cast(mappedPtr) + dstOffset, static_cast(data) + srcOffset, sizeToWrite);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Simply writes data to a region of memory in the buffer. Mapping and
+ unmapping is also done if memory the buffer is bound to is HOST_VISIBLE.
+ Otherwise, only the copying is carried out.
+
+ In the instance where memory is non-coherent but HOST_VISIBLE, we want to
+ write to data and then unmap and flush it immediately. If you want to write
+ to memory in random-access fashion, consider, mapping, writing a few
+ things, unmapping then flushing.
+
+ \param vmaAllocator
+ The VMA allocator object.
+
+ \param data
+ Pointer to source data.
+
+ \param sizeToWrite
+ Amount to write to the buffer.
+
+ \param srcOffset
+ byte offset into the source data.
+
+ \param dstOffset
+ byte offset into the destination data.
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::MapWriteUnmap(void* data, uint32_t sizeToWrite, uint32_t srcOffset, uint32_t dstOffset) noexcept
+ {
+ if (!boundToCoherent)
+ {
+ // map from host visible memory to pointer, do a DMA, and then unmap
+ Map();
+ WriteToMemory(data, sizeToWrite, srcOffset, dstOffset);
+ Unmap();
+ }
+ else
+ {
+ if (mappedPtr)
+ std::memcpy(static_cast(mappedPtr) + dstOffset, static_cast(data) + srcOffset, sizeToWrite);
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Prepares the staging buffer. Called only during initialization when it is
+ known that the buffer will be a GPU only resource. We want to prep the
+ staging buffer in advance so that transfers later will be straightforward.
+
+ \param data
+ The data to write to the staging buffer.
+
+ \param srcSize
+ The amount of data to transfer.
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::PrepStagingBuffer(void* data, uint32_t srcSize) noexcept
+ {
+ // For creation of buffer
+ vk::BufferCreateInfo bufferInfo{};
+
+ // size stored same as GPU buffer
+ bufferInfo.size = sizeStored;
+
+ // We just want to set the transfer bit
+ bufferInfo.usage = vk::BufferUsageFlagBits::eTransferSrc;
+
+ // sharing mode exclusive
+ bufferInfo.sharingMode = vk::SharingMode::eExclusive;
+
+ // Set to auto detect bits
+ VmaAllocationCreateInfo allocCreateInfo{};
+ allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
+
+ // We want to just write all at once. Using random access bit could make this slow
+ allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
+
+ // parameters of a vmaAllocation retrieved via vmaGetAllocationInfo
+ VmaAllocationInfo allocInfo;
+
+ // results of allocation
+ VmaAllocation stagingAlloc;
+
+ // To get around VMA's usage for C version of vulkan, create a temp first...,
+ VkBuffer tempBuffer{};
+
+ // Create the buffer...
+ vmaCreateBuffer(vmaAllocator,
+ &bufferInfo.operator VkBufferCreateInfo & (), // TODO: Verify if this works (can use renderdoc to check buffer variables?)
+ &allocCreateInfo,
+ &tempBuffer, &stagingAlloc, &allocInfo);
+
+ // then assign it to the hpp version
+ stagingBuffer = tempBuffer;
+
+ // Just map, copy then unmap
+ void* stagingBufferMappedPtr = nullptr;
+ vmaMapMemory(vmaAllocator, stagingAlloc, &stagingBufferMappedPtr);
+
+ if (stagingBufferMappedPtr)
+ std::memcpy(static_cast(stagingBufferMappedPtr), static_cast(data), srcSize);
+
+ const VkDeviceSize offsets = 0;
+ const VkDeviceSize sizes = srcSize;
+ vmaFlushAllocations(vmaAllocator, 1, &stagingAlloc, &offsets, &sizes);
+
+ vmaUnmapMemory(vmaAllocator, stagingAlloc);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Default ctor. Initializes everything to 0 or false.
+
+ */
+ /***************************************************************************/
+ SHVkBuffer::SHVkBuffer(std::reference_wrapper allocator) noexcept
+ : vkBuffer{}
+ , stagingBuffer{}
+ , sizeStored{ 0 }
+ , mappedPtr{ nullptr }
+ , alloc {nullptr}
+ , randomAccessOptimized{false}
+ , boundToCoherent {false}
+ , vmaAllocator{allocator}
+ {
+
+ }
+
+ SHVkBuffer::SHVkBuffer(
+ uint32_t inSize,
+ void* data,
+ uint32_t srcSize,
+ std::reference_wrapper allocator,
+ vk::BufferUsageFlags bufferUsage,
+ VmaMemoryUsage memUsage,
+ VmaAllocationCreateFlags allocFlags
+ ) noexcept
+ : SHVkBuffer(allocator)
+ {
+ Init(inSize, data, srcSize, bufferUsage, memUsage, allocFlags);
+ }
+
+ SHVkBuffer::SHVkBuffer(SHVkBuffer&& rhs) noexcept
+ : vkBuffer{std::move (rhs.vkBuffer)}
+ , stagingBuffer{ std::move (rhs.stagingBuffer)}
+ , sizeStored{ std::move (rhs.sizeStored) }
+ , mappedPtr{ nullptr }
+ , alloc{ std::move (rhs.alloc) }
+ , randomAccessOptimized{ rhs.randomAccessOptimized }
+ , boundToCoherent{ rhs.boundToCoherent}
+ , vmaAllocator{ rhs.vmaAllocator }
+ , bufferUsageFlags {rhs.bufferUsageFlags}
+
+ {
+ rhs.vkBuffer = VK_NULL_HANDLE;
+ }
+
+ SHVkBuffer& SHVkBuffer::operator=(SHVkBuffer&& rhs) noexcept
+ {
+ if (&rhs == this)
+ return *this;
+
+ vkBuffer = std::move(rhs.vkBuffer);
+ stagingBuffer = std::move(rhs.stagingBuffer);
+ sizeStored = std::move(rhs.sizeStored);
+ mappedPtr = nullptr;
+ alloc = std::move(rhs.alloc);
+ randomAccessOptimized = rhs.randomAccessOptimized;
+ boundToCoherent = rhs.boundToCoherent;
+ vmaAllocator = std::move (rhs.vmaAllocator);
+ rhs.vkBuffer = VK_NULL_HANDLE;
+ bufferUsageFlags = rhs.bufferUsageFlags;
+
+ return *this;
+ }
+
+ SHVkBuffer::~SHVkBuffer(void) noexcept
+ {
+ Destroy();
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Initialize function. Mainly creates a vulkan buffer bound to a certain
+ region in a VMA memory chunk based on the size passed in. What memory
+ chunk this buffer is bound to is also dependent on the flag.
+
+ \param inSize
+ The size of the buffer.
+
+ \param data
+ Optional data for the user to pass in to perform immediate data transfer.
+
+ \param srcSize
+ Amount of (optional) data to copy to the buffer.
+
+ \param vmaAllocator
+ VMA allocator required for allocation to occur.
+
+ \param bufferUsage
+ Simple and easy. Can be used as a combination
+ (e.g. eVertexBuffer | eTransferDst).
+
+ \param memUsage
+ Usually set to VMA_MEMORY_USAGE_AUTO.
+
+ \param allocFlags
+ Most complicated parameter. Using VMA_RANDOM_ACCESS or VMA_SEQUENTIAL
+ would make the buffer bind to HOST_VISIBLE memory. Using
+ VMA_ALLOCATION_CREATE_MAPPED_BIT will map a pointer immediately (TODO:
+ Need to check if this bounds the buffer to coherent virtual memory. To
+ make this task simple, remember to check every instance of a VMA flag,
+ that the Vulkan flags are set accordingly: like if with sequential,
+ will the coherent bit be set).
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::Init (
+ uint32_t inSize,
+ void* data,
+ uint32_t srcSize,
+ vk::BufferUsageFlags bufferUsage,
+ VmaMemoryUsage memUsage,
+ VmaAllocationCreateFlags allocFlags
+ ) noexcept
+ {
+ sizeStored = inSize;
+
+ // For creation of buffer
+ vk::BufferCreateInfo bufferInfo{};
+
+ // initialize size and usage (vertex, index, uniform, etc)
+ bufferInfo.size = sizeStored;
+ bufferInfo.usage = bufferUsage;
+ bufferInfo.sharingMode = vk::SharingMode::eExclusive;
+
+ // Prepare allocation parameters for call to create buffers later
+ VmaAllocationCreateInfo allocCreateInfo{};
+ allocCreateInfo.usage = memUsage;
+
+ // If vma allocation flags include dedicated bit, immediately activate dst bit
+ if (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT)
+ bufferInfo.usage |= vk::BufferUsageFlagBits::eTransferDst;
+
+ allocCreateInfo.flags = allocFlags;
+
+ // parameters of a vmaAllocation retrieved via vmaGetAllocationInfo
+ VmaAllocationInfo allocInfo;
+
+ // To get around VMA's usage for C version of vulkan, create a temp first...,
+ VkBuffer tempBuffer{};
+
+ // Create the buffer...
+ auto result = vmaCreateBuffer(vmaAllocator,
+ &bufferInfo.operator VkBufferCreateInfo &(),
+ &allocCreateInfo,
+ &tempBuffer, &alloc, &allocInfo);
+
+ if (result != VK_SUCCESS)
+ SHVulkanDebugUtil::ReportVkError(vk::Result (result), "Failed to create vulkan buffer. ");
+ else
+ SHVulkanDebugUtil::ReportVkSuccess("Successfully created buffer. ");
+
+ // ...then assign it to the hpp version
+ vkBuffer = tempBuffer;
+
+ // Set the buffer flags
+ bufferUsageFlags = bufferInfo.usage;
+
+ // This probably means that a HOST_CACHED memory type is used on allocation
+ if (allocFlags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)
+ randomAccessOptimized = true;
+
+ /* There are 3 instances where buffers can be a GPU only resource :
+ 1. When its for a DEDICATED large chunk of memory that is usually not touch ever again
+ like images that get destroyed and recreated every time window resizes.
+ 2. When its for a memory type that is BOTH HOST_VISIBLE and DEVICE_LOCAL. This is known
+ base address register (BAR). If this address space is not available (which it should be on
+ most machines), fall back to use DEVICE_LOCAL memory and do the usual staging buffer memory transfer to
+ GPU resource.
+ 3. When the buffer is initialized with a usage dst bit.
+ */
+
+ // Get the memory property flags
+ VkMemoryPropertyFlags memPropFlags;
+ vmaGetAllocationMemoryProperties(vmaAllocator, alloc, &memPropFlags);
+
+ // mainly host visible. Can be cached (need to flush/invalidate), uncached (always coherent) and coherent (virtual).
+ if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
+ {
+ // If memory is marked to be coherent between CPU and GPU (no need flush/invalidate) (TODO: Verify if VMA_ALLOCATION_CREATE_MAPPED_BIT is used when VMA_MEMORY_USAGE_AUTO is set)
+ // TODO: also verify that coherent bit = pointer is already mapped
+ if (memPropFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
+ {
+ boundToCoherent = true;
+ mappedPtr = allocInfo.pMappedData;
+ }
+ else
+ mappedPtr = nullptr;
+
+ if (data)
+ MapWriteUnmap(data, srcSize, 0, 0);
+ }
+ else
+ {
+ // We can prep first so that we can do transfers later via 1 cmd buffer recording
+ PrepStagingBuffer(data, srcSize);
+
+ // #NoteToSelf: Command buffers used to be an optional argument to perform the transfer immediately but it was removed because we
+ // don't want to allow such scenarios. Ideally, we want users to be transferring data to device memory together with
+ // other buffers that perform similar transfers.
+ //if (cmdBufferHdl) // check for null handle
+ //{
+ // // case 1 and case 3
+ // if (bufferInfo.usage & vk::BufferUsageFlagBits::eTransferDst)
+ // {
+ // TransferToDeviceResource(cmdBufferHdl);
+ // }
+ // //case 2
+ // else if (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT)
+ // {
+ // // Need to check if BAR exists, if it doesn't, fall back to DEVICE_LOCAL, and do regular explicit transfers from staging
+ // }
+ //}
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Destroys the buffer.
+
+ \param allocator
+ VMA allocator required to destroy the buffer.
+
+ */
+ /***************************************************************************/
+ void SHVkBuffer::Destroy(void) noexcept
+ {
+ vmaDestroyBuffer(vmaAllocator, vkBuffer, alloc);
+ }
+
+}
diff --git a/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h
new file mode 100644
index 00000000..2ad3e4e9
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Buffers/SHVkBuffer.h
@@ -0,0 +1,106 @@
+#ifndef SH_VK_BUFFER_H
+#define SH_VK_BUFFER_H
+
+#include "Graphics/SHVulkanIncludes.h"
+#include "vk_mem_alloc.h"
+#include "Resource/Handle.h"
+
+namespace SHADE
+{
+ // Mainly so that middle end doesn't need to call "vk::"
+ //using SHVkBufferUsage = vk::BufferUsageFlags;
+ //using SHVkBufferUsageBits = vk::BufferUsageFlagBits;
+
+ class SHVkCommandBuffer;
+
+ class SHVkBuffer
+ {
+ private:
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER VARIABLES */
+ /*-----------------------------------------------------------------------*/
+ //! Vulkan handle to a vkBuffer
+ vk::Buffer vkBuffer;
+
+ //! When the buffer initialized to be a GPU only resource, this buffer will
+ //! be necessary to perform transfer to GPU either during initialization
+ //! or whenever the user wants it.
+ vk::Buffer stagingBuffer;
+
+ //! The amount of memory this buffer is bound to in device/host memory
+ uint32_t sizeStored;
+
+ //! Persistently mapped pointer if applicable (will be void if buffer is
+ //! not created with the correct flags). Note that this is only used for
+ //! persistent mapping. One time updates do not use this pointer.
+ void* mappedPtr;
+
+ //! allocation object containing details of an allocation
+ VmaAllocation alloc;
+
+ //! If initialized with vma random access flag, this is true
+ bool randomAccessOptimized;
+
+ //! Whether or not this buffer is bound to coherent memory
+ bool boundToCoherent;
+
+ //! buffer usage info flags
+ vk::BufferUsageFlags bufferUsageFlags;
+
+ //! Reference to the allocator
+ //VmaAllocator const& vmaAllocator;
+ std::reference_wrapper vmaAllocator;
+
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ void PrepStagingBuffer (void* data, uint32_t srcSize) noexcept;
+
+ public:
+ /*-----------------------------------------------------------------------*/
+ /* CTORS AND DTORS */
+ /*-----------------------------------------------------------------------*/
+ SHVkBuffer (void) noexcept = delete;
+ SHVkBuffer (std::reference_wrapper allocator) noexcept;
+ SHVkBuffer (
+ uint32_t inSize,
+ void* data,
+ uint32_t srcSize,
+ std::reference_wrapper allocator,
+ vk::BufferUsageFlags bufferUsage,
+ VmaMemoryUsage memUsage,
+ VmaAllocationCreateFlags allocFlags
+ ) noexcept;
+ SHVkBuffer(SHVkBuffer&& rhs) noexcept;
+ SHVkBuffer& operator=(SHVkBuffer&& rhs) noexcept;
+ ~SHVkBuffer(void) noexcept;
+
+
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC MEMBER VARIABLES */
+ /*-----------------------------------------------------------------------*/
+ void Init (
+ uint32_t inSize,
+ void* data,
+ uint32_t srcSize,
+ vk::BufferUsageFlags bufferUsage,
+ VmaMemoryUsage memUsage,
+ VmaAllocationCreateFlags allocFlags
+ ) noexcept;
+ void Destroy (void) noexcept;
+
+ void Map (void) noexcept;
+ void Unmap (void) noexcept;
+ void WriteToMemory (void* data, uint32_t sizeToWrite, uint32_t srcOffset, uint32_t dstOffset) noexcept;
+ void MapWriteUnmap (void* data, uint32_t sizeToWrite, uint32_t srcOffset, uint32_t dstOffset) noexcept;
+ void TransferToDeviceResource(Handle const& cmdBufferHdl) noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* SETTERS AND GETTERS */
+ /*-----------------------------------------------------------------------*/
+ vk::Buffer GetVkBuffer (void) const noexcept;
+
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Commands/SHCommandPoolResetMode.h b/SHADE_Engine/src/Graphics/Commands/SHCommandPoolResetMode.h
new file mode 100644
index 00000000..4ecc0098
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Commands/SHCommandPoolResetMode.h
@@ -0,0 +1,13 @@
+#ifndef SH_COMMAND_POOL_RESET_H
+#define SH_COMMAND_POOL_RESET_H
+
+namespace SHADE
+{
+ enum class SH_CMD_POOL_RESET
+ {
+ POOL_BASED,
+ BUFFER_BASED,
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp
new file mode 100644
index 00000000..6a307230
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.cpp
@@ -0,0 +1,641 @@
+#include "SHPch.h"
+#include "SHVkCommandBuffer.h"
+#include "SHVkCommandPool.h"
+#include "Graphics/Devices/SHVkLogicalDevice.h"
+#include "SHVkCommandPool.h"
+#include "Tools/SHLogger.h"
+#include "Graphics/Renderpass/SHVkRenderpass.h"
+#include "Graphics/Framebuffer/SHVkFramebuffer.h"
+#include "Graphics/Pipeline/SHVkPipeline.h"
+#include "Graphics/Buffers/SHVkBuffer.h"
+
+
+namespace SHADE
+{
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Frees the command buffer.
+
+ */
+ /***************************************************************************/
+ SHVkCommandBuffer::~SHVkCommandBuffer(void) noexcept
+ {
+ if (vkCommandBuffer)
+ parentPool->GetLogicalDevice()->GetVkLogicalDevice().freeCommandBuffers(parentPool->GetVkCommandPool(), commandBufferCount, &vkCommandBuffer);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Only the command buffer is allocated using
+ VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT bit, is resetting
+ individually permitted. Otherwise, throw exception. IMPORTANT NOTE:
+ the command buffer cannot be in the pending state!!!
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::Reset(void)
+ {
+ if (cmdBufferState == SH_CMD_BUFFER_STATE::PENDING)
+ {
+ SHLOG_ERROR("Command buffer in pending state, could not reset. ");
+ return;
+ }
+
+ if (parentPool->GetPoolResetMode() != SH_CMD_POOL_RESET::BUFFER_BASED)
+ {
+ SHLOG_ERROR("Parent Command Pool was not initialized with VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT. Cannot reset independently. ");
+ return;
+ }
+
+ vkCommandBuffer.reset(vk::CommandBufferResetFlagBits::eReleaseResources);
+
+ if (cmdBufferState == SH_CMD_BUFFER_STATE::RECORDING || cmdBufferState == SH_CMD_BUFFER_STATE::EXECUTABLE)
+ {
+ cmdBufferState = SH_CMD_BUFFER_STATE::INVALID;
+ return;
+ }
+
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::INVALID)
+ cmdBufferState = SH_CMD_BUFFER_STATE::INITIAL;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Begins the command buffer.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::BeginRecording(void) noexcept
+ {
+ // Check if command buffer is ready to record.
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::INITIAL)
+ {
+ SHLOG_ERROR("Command buffer not in initial state, cannot begin recording. ");
+ return;
+ }
+
+ // Struct for recording
+ vk::CommandBufferBeginInfo beginInfo{};
+
+ beginInfo.flags = usageFlags;
+ // TODO: Allow passing in inheritance info.
+
+ // Attempt to begin recording
+ if (auto result = vkCommandBuffer.begin(&beginInfo); result != vk::Result::eSuccess)
+ {
+ SHVulkanDebugUtil::ReportVkError(result, "Failed to begin command buffer. ");
+ return;
+ }
+
+ // We don't need to print record success
+ //else
+ // SHVulkanDebugUtil::ReportVkSuccess("Command buffer successfully begun recording... ");
+
+ // Set the state to recording if the call above succeeded.
+ cmdBufferState = SH_CMD_BUFFER_STATE::RECORDING;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ End the recording of a command buffer.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::EndRecording(void) noexcept
+ {
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ SHLOG_ERROR("Command Buffer not in recording state, cannot end recording. ");
+ return;
+ }
+
+ vkCommandBuffer.end();
+ cmdBufferState = SH_CMD_BUFFER_STATE::EXECUTABLE;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Begins a renderpass in the command buffer. 2 important things to note
+ here, the command buffer used MUST be a primary command buffer and
+ command buffer MUST be in a recording state.
+
+ \param renderpassHdl
+ Renderpass for obvious reasons.
+
+ \param framebufferHdl
+ Framebuffer required in the begin info.
+
+ \param offset
+ Offset of the render area in the framebuffer.
+
+ \param extent
+ Extent of the render area in the framebuffer.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::BeginRenderpass(Handle const& renderpassHdl, Handle const& framebufferHdl, vk::Offset2D offset, vk::Extent2D extent) noexcept
+ {
+ // cannot begin renderpass if command buffer is primary
+ if (commandBufferType != SH_CMD_BUFFER_TYPE::PRIMARY)
+ {
+ SHLOG_ERROR("Cannot begin renderpass. Command buffer is not a primary command buffer. ");
+ return;
+ }
+
+ // cannot begin renderpass if command buffer is not recording
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ SHLOG_ERROR("Command buffer must have started recording before a renderpass instance can begin. ");
+ return;
+ }
+
+ vk::Extent2D framebufferExtent{ framebufferHdl->GetWidth(), framebufferHdl->GetHeight() };
+
+ // Prepare renderpass begin
+ vk::RenderPassBeginInfo renderPassInfo{};
+ renderPassInfo.renderPass = renderpassHdl->GetVkRenderpass();
+ renderPassInfo.framebuffer = framebufferHdl->GetVkFramebuffer();
+
+ // If the extent passed in is 0, use the framebuffer dimensions instead.
+ if (extent.width == 0 && extent.height == 0)
+ renderPassInfo.renderArea.extent = framebufferExtent;
+ else
+ renderPassInfo.renderArea.extent = extent;
+
+ // assign offset
+ renderPassInfo.renderArea.offset = offset;
+
+ // prepare clear colors
+ auto const& clearColors = renderpassHdl->GetClearColors();
+ renderPassInfo.clearValueCount = static_cast(clearColors.size());
+ renderPassInfo.pClearValues = clearColors.data();
+
+ // Check if render area is optimal
+ if (!IsRenderAreaOptimal(renderpassHdl, framebufferExtent, renderPassInfo.renderArea))
+ SHLOG_ERROR("Render area in renderpass begin info is not optimal. See Vulkan vkGetRenderAreaGranularity for details.");
+
+ // Begin the render pass
+ vkCommandBuffer.beginRenderPass (&renderPassInfo, vk::SubpassContents::eInline);
+
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Ends a renderpass.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::EndRenderpass(void) noexcept
+ {
+ vkCommandBuffer.endRenderPass();
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Sets the viewport dynamically for the command buffer. #NoteToSelf:
+ Dynamic state will not affect pipelines that don't use dynamic state
+ so there isn't a need to do any checks. Also, setting dynamic state like
+ this only needs to happen ONCE per command buffer UNLESS a different
+ viewport is to be used for different drawing commands.
+
+ \param vpWidth
+ viewport width
+
+ \param vpHeight
+ viewport height
+
+ \param sWidth
+ Scissor extent width
+
+ \param sHeight
+ Scissor extent height
+
+ \param vpX
+ Viewport offset X.
+
+ \param vpY
+ Viewport offset Y.
+
+ \param sX
+ Scissor offset X.
+
+ \param sY
+ Scissor offset Y.
+
+ \param vpMinDepth
+ viewport minimum depth value
+
+ \param vpMaxDepth
+ viewport maximum depth value
+
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::SetviewportScissor(float vpWidth, float vpHeight, uint32_t sWidth, uint32_t sHeight, float vpX /*= 0.0f*/, float vpY /*= 0.0f*/, int32_t sX /*= 0.0f*/, int32_t sY /*= 0.0f*/, float vpMinDepth /*= 0.0f*/, float vpMaxDepth /*= 1.0f*/) noexcept
+ {
+ vk::Viewport dynamicViewport
+ {
+ .x = vpX,
+ .y = vpY,
+ .width = vpWidth,
+ .height = vpHeight,
+ .minDepth = vpMinDepth,
+ .maxDepth = vpMaxDepth,
+ };
+
+ vk::Rect2D dynamicScissor
+ {
+ .offset = vk::Offset2D{sX, sY},
+ .extent = vk::Extent2D{sWidth, sHeight}
+ };
+
+ // Dynamic state set viewport and scissor
+ vkCommandBuffer.setScissor(0, 1, &dynamicScissor);
+ vkCommandBuffer.setViewport(0, 1, &dynamicViewport);
+
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Binds a pipeline object to the command buffer.
+
+ \param pipelineHdl
+ The pipeline to bind.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::BindPipeline(Handle const& pipelineHdl) noexcept
+ {
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ SHLOG_ERROR("Command buffer must have started recording before a pipeline can be bound. ");
+ return;
+ }
+ boundPipelineLayoutHdl = pipelineHdl->GetPipelineLayout();
+ vkCommandBuffer.bindPipeline(pipelineHdl->GetPipelineBindPoint(), pipelineHdl->GetVkPipeline());
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Binds a buffer to the vertex buffer binding point specified in
+ bindingPoint.
+
+ \param bindingPoint
+ The binding point to initialize a vertex
+
+ \param buffer
+ The buffer to bind
+
+ \param offset
+ The offset to start from in the buffer for the first binding point.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::BindVertexBuffer (uint32_t bindingPoint, Handle const& buffer, vk::DeviceSize offset) noexcept
+ {
+ if (cmdBufferState == SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ auto bufferHandle = buffer->GetVkBuffer();
+ vkCommandBuffer.bindVertexBuffers (bindingPoint, 1, &bufferHandle, &offset);
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Binds an index buffer to the pipeline.
+
+ \param buffer
+ The buffer to bind.
+
+ \param startingIndex
+ The starting index in the index buffer. For example, 0 would mean
+ starting at the beginning. 5 would mean starting at byte offset
+ size(uint32_t) * 5.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::BindIndexBuffer(Handle const& buffer, uint32_t startingIndex) const noexcept
+ {
+ if (cmdBufferState == SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ auto bufferHandle = buffer->GetVkBuffer();
+ vkCommandBuffer.bindIndexBuffer (bufferHandle, sizeof (uint32_t) * startingIndex, vk::IndexType::eUint32);
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Calls vkCmdDraw.
+
+ \param vertexCount
+ How many vertices to draw
+
+ \param instanceCount
+ Number of instances to draw
+
+ \param firstVertex
+ First vertex in the buffer of vertices to start from
+
+ \param firstInstance
+ First instance to start from.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::DrawArrays(uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept
+ {
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ SHLOG_ERROR("Command buffer must have started recording before a pipeline can be bound. ");
+ return;
+ }
+ vkCommandBuffer.draw (vertexCount, instanceCount, firstVertex, firstInstance);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Issues a non-instanced indexed draw call.
+
+ \param indexCount
+ Number of indices to draw.
+
+ \param firstIndex
+ Starting index. if the array was 0, 2, 5, 4, and we indicated this to be
+ 1. The draw call would start from index 2.
+
+ \param vertexOffset
+ Starting vertex offset. This would indicate that vertex pulling should
+ start from a certain vertex. So a vertex offset of 3 (for example) would
+ mean an index of 0 would mean the 3rd vertex.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept
+ {
+ if (cmdBufferState != SH_CMD_BUFFER_STATE::RECORDING)
+ {
+ SHLOG_ERROR("Command buffer must have started recording before a pipeline can be bound. ");
+ return;
+ }
+
+ // not an instanced call so we want to make the instanced related stuff hard coded
+ vkCommandBuffer.drawIndexed(indexCount, 1, firstIndex, vertexOffset, 0);
+
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Calls vkCmdPushConstants and submits data stored in command buffer.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::SubmitPushConstants(void) const noexcept
+ {
+ vkCommandBuffer.pushConstants(boundPipelineLayoutHdl->GetVkPipelineLayout(),
+ boundPipelineLayoutHdl->GetPushConstantInterface().GetShaderStageFlags(),
+ 0,
+ boundPipelineLayoutHdl->GetPushConstantInterface().GetSize(), pushConstantData);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Simply returns the command buffer handle.
+
+ \return
+ The command buffer handle.
+
+ */
+ /***************************************************************************/
+ vk::CommandBuffer const& SHVkCommandBuffer::GetVkCommandBuffer(void) const noexcept
+ {
+ return vkCommandBuffer;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ See https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkGetRenderAreaGranularity.html
+ or look up vkGetRenderAreaGranularity.
+
+ \param renderpassHdl
+ Renderpass to get info from.
+
+ \param framebufferExtent
+ For the comparison. Again, look it up on the webpage.
+
+ \param renderArea
+ For the comparison. Again, look it up on the webpage.
+
+ \return
+ If optimal, true. otherwise false.
+
+ */
+ /***************************************************************************/
+ bool SHVkCommandBuffer::IsRenderAreaOptimal(Handle const& renderpassHdl, vk::Extent2D const& framebufferExtent, vk::Rect2D const& renderArea) const noexcept
+ {
+ vk::Extent2D granularity = parentPool->GetLogicalDevice()->GetVkLogicalDevice().getRenderAreaGranularity(renderpassHdl->GetVkRenderpass());
+
+ return (renderArea.offset.x % granularity.width == 0 && renderArea.offset.y % granularity.height == 0 &&
+ (renderArea.extent.width % granularity.width || renderArea.offset.x + renderArea.extent.width == framebufferExtent.width) &&
+ (renderArea.extent.height % granularity.height || renderArea.offset.y + renderArea.extent.height == framebufferExtent.height));
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Setter for the state of the command buffer.
+
+ \param state
+
+ \return
+
+ */
+ /***************************************************************************/
+ void SHVkCommandBuffer::SetState(SH_CMD_BUFFER_STATE state) noexcept
+ {
+ cmdBufferState = state;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns the state of the command buffer.
+
+ \return
+
+ */
+ /***************************************************************************/
+ SH_CMD_BUFFER_STATE SHVkCommandBuffer::GetState(void) const noexcept
+ {
+ return cmdBufferState;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a command buffer. Cmd buffer can be primary or secondary. If
+ secondary, flags will automatically have renderpass continue bit. Command
+ pool used to create this command buffer will determine whether or not
+ this buffer will be allocated with
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT using the reset mode.
+
+ \param logicalDevice
+ Need a logical device to create a buffer.
+
+ \param commandPool
+ Command buffer to allocate from.
+
+ \param type
+ Type of the command buffer; primary or secondary.
+
+ */
+ /***************************************************************************/
+ SHVkCommandBuffer::SHVkCommandBuffer(Handle const& commandPool, SH_CMD_BUFFER_TYPE type) noexcept
+ : vkCommandBuffer{ nullptr }
+ , cmdBufferState{ SH_CMD_BUFFER_STATE::INVALID } // Command buffer will be in initial state
+ , commandBufferType{ SH_CMD_BUFFER_TYPE::PRIMARY }
+ , parentPoolResetMode{ SH_CMD_POOL_RESET::POOL_BASED }
+ , usageFlags{}
+ , commandBufferCount{ 0 }
+ , parentPool{commandPool}
+
+ {
+ vk::CommandBufferAllocateInfo allocateInfo{};
+ allocateInfo.commandPool = commandPool->GetVkCommandPool();
+
+ // don't know if there are performance implications if this is set to always be 1. Could be better to create more at once
+ allocateInfo.commandBufferCount = 1;
+
+ // Set the type of command buffer (primary or secondary)
+ switch (type)
+ {
+ case SH_CMD_BUFFER_TYPE::PRIMARY:
+ allocateInfo.level = vk::CommandBufferLevel::ePrimary;
+ break;
+ case SH_CMD_BUFFER_TYPE::SECONDARY:
+ allocateInfo.level = vk::CommandBufferLevel::eSecondary;
+ break;
+ }
+
+ // Attempt to allocate command buffers
+ if (auto result = commandPool->GetLogicalDevice()->GetVkLogicalDevice().allocateCommandBuffers(&allocateInfo, &vkCommandBuffer); result != vk::Result::eSuccess)
+ {
+ SHVulkanDebugUtil::ReportVkError(result, "Failed to create Command Buffer. ");
+ return;
+ }
+ else
+ SHVulkanDebugUtil::ReportVkSuccess("Successfully created Command Buffer.");
+
+ // Set the state only after successfully allocating
+ cmdBufferState = SH_CMD_BUFFER_STATE::INITIAL;
+
+ // Parent command pool
+ parentPool = commandPool;
+
+ // Save some important info
+ parentPoolResetMode = parentPool->GetPoolResetMode();
+ commandBufferType = type;
+ commandBufferCount = allocateInfo.commandBufferCount;
+
+ if (parentPool->GetIsTransient ())
+ usageFlags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;
+
+ if (commandBufferType == SH_CMD_BUFFER_TYPE::SECONDARY)
+ usageFlags |= vk::CommandBufferUsageFlagBits::eRenderPassContinue;
+
+ // Reset all the push constant data to 0
+ memset (pushConstantData, 0, PUSH_CONSTANT_SIZE);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Move ctor. Invalidates Vulkan handles.
+
+ \param rhs
+ the other command buffer.
+
+ */
+ /***************************************************************************/
+ SHVkCommandBuffer::SHVkCommandBuffer(SHVkCommandBuffer&& rhs) noexcept
+ : vkCommandBuffer {std::move (rhs.vkCommandBuffer)}
+ , cmdBufferState {rhs.cmdBufferState}
+ , commandBufferType {rhs.commandBufferType}
+ , parentPoolResetMode {rhs.parentPoolResetMode}
+ , usageFlags {rhs.usageFlags}
+ , commandBufferCount {rhs.commandBufferCount}
+ , parentPool {rhs.parentPool}
+ , boundPipelineLayoutHdl{rhs.boundPipelineLayoutHdl }
+ {
+ memcpy (pushConstantData, rhs.pushConstantData, PUSH_CONSTANT_SIZE);
+
+ rhs.vkCommandBuffer = VK_NULL_HANDLE;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Move assignment operator. Invalidates Vulkan handles.
+
+ \param rhs
+ The other Vulkan Handle.
+
+ \return
+ a reference itself.
+
+ */
+ /***************************************************************************/
+ SHVkCommandBuffer& SHVkCommandBuffer::operator=(SHVkCommandBuffer&& rhs) noexcept
+ {
+ if (&rhs == this)
+ return *this;
+
+ vkCommandBuffer = std::move(rhs.vkCommandBuffer);
+ cmdBufferState = rhs.cmdBufferState;
+ commandBufferType = rhs.commandBufferType;
+ parentPoolResetMode = rhs.parentPoolResetMode;
+ usageFlags = rhs.usageFlags;
+ commandBufferCount = rhs.commandBufferCount;
+ parentPool = rhs.parentPool;
+ boundPipelineLayoutHdl = rhs.boundPipelineLayoutHdl;
+ memcpy(pushConstantData, rhs.pushConstantData, PUSH_CONSTANT_SIZE);
+
+ rhs.vkCommandBuffer = VK_NULL_HANDLE;
+
+ return *this;
+ }
+
+}
diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h
new file mode 100644
index 00000000..08fc45f7
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandBuffer.h
@@ -0,0 +1,134 @@
+#ifndef SH_COMMAND_BUFFER_H
+#define SH_COMMAND_BUFFER_H
+
+#include "Graphics/SHVulkanIncludes.h"
+#include "Graphics/SHVulkanDefines.h"
+#include "SHCommandPoolResetMode.h"
+#include "Resource/ResourceLibrary.h"
+#include "Graphics/Pipeline/SHVkPipelineLayout.h"
+
+namespace SHADE
+{
+ class SHVkCommandPool;
+ class SHVkLogicalDevice;
+ class SHVkRenderpass;
+ class SHVkFramebuffer;
+ class SHVkPipeline;
+ class SHVkBuffer;
+
+ enum class SH_CMD_BUFFER_TYPE
+ {
+ PRIMARY,
+ SECONDARY,
+ };
+
+ enum class SH_CMD_BUFFER_STATE
+ {
+ INITIAL, // Can be moved to recording state or freed
+ RECORDING, // vkBeginCommandBuffer changes state from initial state to recording state.
+ EXECUTABLE, // Can be submitted, reset or recorded to another command buffer.
+ INVALID, // Enters invalid state when buffer created with ONE_TIME_SUBMIT_BIT finishes execution. Primaries also enter the invalid state when secondaries enter invalid or initial state
+ PENDING // Cmd buffer enters this state after being submitted to queue.
+ };
+
+ class SHVkCommandBuffer
+ {
+ friend class SHVkCommandPool;
+ friend class ResourceLibrary;
+
+ static constexpr uint16_t PUSH_CONSTANT_SIZE = 512;
+ private:
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER VARIABLES */
+ /*-----------------------------------------------------------------------*/
+ //! Vulkan handle to a command buffer
+ vk::CommandBuffer vkCommandBuffer;
+
+ //! state of the command buffer. TODO: Test some state changes.
+ SH_CMD_BUFFER_STATE cmdBufferState;
+
+ //! Command buffer type
+ SH_CMD_BUFFER_TYPE commandBufferType;
+
+ //! Stores the reset mode of the command pool. This is so that when we
+ //! try to reset command buffers individually, we want to do it only when
+ //! the reset mode is set to COMMAND_BUFFER_RESET only.
+ SH_CMD_POOL_RESET parentPoolResetMode;
+
+ //! Flags of the command buffer (ONE_TIME_USE, RENDERPASS_CONTINUE, SIMULTANEOUS_USAGE)
+ vk::CommandBufferUsageFlags usageFlags;
+
+ //! When allocating, you can specify how many command buffers to allocate
+ uint32_t commandBufferCount;
+
+ //! The command pool that this command buffer belongs to
+ Handle parentPool;
+
+ //! The currently bound pipeline
+ Handle boundPipelineLayoutHdl;
+
+ //! The push constant data for the command buffer
+ uint8_t pushConstantData[PUSH_CONSTANT_SIZE];
+
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ bool IsRenderAreaOptimal(Handle const& renderpassHdl, vk::Extent2D const& framebufferExtent, vk::Rect2D const& renderArea) const noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE GETTERS AND SETTERS */
+ /*-----------------------------------------------------------------------*/
+ void SetState(SH_CMD_BUFFER_STATE state) noexcept;
+ SH_CMD_BUFFER_STATE GetState(void) const noexcept;
+
+ public:
+ /*-----------------------------------------------------------------------*/
+ /* CTORS AND DTORS */
+ /*-----------------------------------------------------------------------*/
+ SHVkCommandBuffer (Handle const& commandPool, SH_CMD_BUFFER_TYPE type) noexcept;
+ SHVkCommandBuffer (SHVkCommandBuffer&& rhs) noexcept;
+ SHVkCommandBuffer (void) noexcept = delete;
+ ~SHVkCommandBuffer (void) noexcept;
+ SHVkCommandBuffer& operator= (SHVkCommandBuffer&& rhs) noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC MEMBER FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ void Reset(void);
+
+ // Begins and Ends
+ void BeginRecording (void) noexcept;
+ void EndRecording (void) noexcept;
+ void BeginRenderpass (Handle const& renderpassHdl, Handle const& framebufferHdl, vk::Offset2D offset = {0, 0}, vk::Extent2D extent = {0, 0}) noexcept;
+ void EndRenderpass (void) noexcept;
+
+ // Dynamic State
+ void SetviewportScissor (float vpWidth, float vpHeight, uint32_t sWidth, uint32_t sHeight, float vpX = 0.0f, float vpY = 0.0f, int32_t sX = 0.0f, int32_t sY = 0.0f, float vpMinDepth = 0.0f, float vpMaxDepth = 1.0f) noexcept;
+
+ // Binding Commands
+ void BindPipeline (Handle const& pipelineHdl) noexcept;
+ void BindVertexBuffer (uint32_t bindingPoint, Handle const& buffer, vk::DeviceSize offset) noexcept;
+ void BindIndexBuffer (Handle const& buffer, uint32_t startingIndex) const noexcept;
+
+ // Draw Commands
+ void DrawArrays (uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance) const noexcept;
+ void DrawIndexed (uint32_t indexCount, uint32_t firstIndex, uint32_t vertexOffset) const noexcept;
+
+ // Push Constant variable setting
+ template
+ void SetPushConstantVariable(std::string variableName, T const& data) noexcept
+ {
+ memcpy (static_cast(pushConstantData) + boundPipelineLayoutHdl->GetPushConstantInterface().GetOffset(variableName), &data, sizeof (T));
+ };
+
+ void SubmitPushConstants (void) const noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* GETTERS AND SETTERS */
+ /*-----------------------------------------------------------------------*/
+ vk::CommandBuffer const& GetVkCommandBuffer(void) const noexcept;
+
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp
new file mode 100644
index 00000000..5cf4bea4
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.cpp
@@ -0,0 +1,269 @@
+#include "SHPch.h"
+#include "SHVkCommandPool.h"
+#include "Graphics/Devices/SHVkLogicalDevice.h"
+#include "Graphics/Instance/SHVkInstance.h"
+#include "Resource/ResourceLibrary.h"
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Simply creates a command pool. Flags passed in are also saved.
+
+ \param logicalDevice
+ Logical device required to create the pool
+
+ \param queueFamilyType
+ Every pool needs to be created from a queue family
+
+ \param inResetMode
+ Whether or not the reset bit that allows resetting command buffers
+ individually should be set or not.
+
+ \param inTransient
+ Whether or not buffers created from the pool are short-lived. IT IS
+ STRONGLY RECOMMENDED for this to be true. From "Writing an efficient
+ Vulkan renderer: In general approaches that pre-record command buffers for
+ parts of the scene are counter-productive since they can result in
+ excessive GPU load due to inefficient culling required to keep command
+ buffer workload large and can trigger inefficient code paths on some
+ tiled renderers, and instead applications should focus on improving the
+ threading and draw call submission cost on the CPU. NOTE: it is possible
+ to create transient command buffers from pools that are BUFFER_BASED,
+ hence why POOL_BASED != transient.
+
+ */
+ /***************************************************************************/
+ SHVkCommandPool::SHVkCommandPool(Handle const& inLogicalDeviceHdl, SH_QUEUE_FAMILY_ARRAY_INDEX queueFamilyType, SH_CMD_POOL_RESET inResetMode, bool inTransient) noexcept
+ : vkCommandPool{ nullptr }
+ , queueFamilyIndex{ 0 }
+ , resetMode{ SH_CMD_POOL_RESET::POOL_BASED }
+ , primaries{}
+ , secondaries{}
+ , logicalDeviceHdl { inLogicalDeviceHdl }
+ , transient {false}
+ {
+ // Command pool create info
+ vk::CommandPoolCreateInfo poolCreateInfo{};
+
+ // Get the queue family index based on the queue family type passed in
+ poolCreateInfo.queueFamilyIndex = logicalDeviceHdl->GetQueueFamilyIndex(queueFamilyType);
+
+ if (inResetMode == SH_CMD_POOL_RESET::BUFFER_BASED)
+ poolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
+
+ if (inTransient)
+ poolCreateInfo.flags |= vk::CommandPoolCreateFlagBits::eTransient;
+
+
+ // Actually create the command pool and check for errors
+ if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createCommandPool(&poolCreateInfo, nullptr, &vkCommandPool); result != vk::Result::eSuccess)
+ {
+ SHVulkanDebugUtil::ReportVkError(result, "Failed to create Command Pool! ");
+ return;
+ }
+ else
+ SHVulkanDebugUtil::ReportVkSuccess("Successfully created Command Pool.");
+
+ // Save the queue family index
+ queueFamilyIndex = poolCreateInfo.queueFamilyIndex;
+
+ // Save the reset mode
+ resetMode = inResetMode;
+ transient = inTransient;
+ }
+
+ SHVkCommandPool::SHVkCommandPool(SHVkCommandPool&& rhs) noexcept
+ : ISelfHandle(rhs)
+ , vkCommandPool{ rhs.vkCommandPool }
+ , queueFamilyIndex{ rhs.queueFamilyIndex }
+ , resetMode{ rhs.resetMode }
+ , primaries{ std::move(rhs.primaries) }
+ , secondaries{ std::move(rhs.secondaries) }
+ , logicalDeviceHdl{ rhs.logicalDeviceHdl }
+ , transient{ rhs.transient }
+ {
+ rhs.vkCommandPool = VK_NULL_HANDLE;
+ }
+
+ SHVkCommandPool& SHVkCommandPool::operator=(SHVkCommandPool&& rhs) noexcept
+ {
+ if (&rhs == this)
+ return *this;
+
+ vkCommandPool = rhs.vkCommandPool;
+ queueFamilyIndex = rhs.queueFamilyIndex;
+ resetMode = rhs.resetMode;
+ primaries = std::move(rhs.primaries);
+ secondaries = std::move(rhs.secondaries);
+ logicalDeviceHdl = rhs.logicalDeviceHdl;
+ transient = rhs.transient;
+
+ static_cast&>(*this) = static_cast&>(rhs);
+
+ rhs.vkCommandPool = VK_NULL_HANDLE;
+
+ return *this;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Destroys the command pool.
+
+ */
+ /***************************************************************************/
+ SHVkCommandPool::~SHVkCommandPool(void) noexcept
+ {
+ // TODO: Destroy all command buffers first. Should implement a check for command buffers that are still recording or pending (submitted to queue)?
+ if (vkCommandPool)
+ {
+
+ for (auto& pri : primaries)
+ pri.Free();
+ for (auto& sec : secondaries)
+ sec.Free();
+
+ vkDestroyCommandPool(logicalDeviceHdl->GetVkLogicalDevice(), vkCommandPool, nullptr);
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Resets the command pool.
+
+ \param logicalDevice
+ Device required to reset the command pool.
+
+ */
+ /***************************************************************************/
+ void SHVkCommandPool::Reset(void) noexcept
+ {
+ // TODO: hopefully some sort of validation layer pops up here when we attempt to reset while the command buffers
+ // are in the pending state. Need to do a test.
+ logicalDeviceHdl->GetVkLogicalDevice().resetCommandPool(vkCommandPool, vk::CommandPoolResetFlagBits::eReleaseResources);
+ for (auto& primary : primaries)
+ {
+ if (primary->GetState() != SH_CMD_BUFFER_STATE::PENDING)
+ primary->SetState(SH_CMD_BUFFER_STATE::INITIAL);
+ else
+ SHLOG_ERROR("Primary command buffer in pending state, could not reset. ");
+ // From the spec: Any primary command buffer allocated from another VkCommandPool that is in the recording or
+ // executable state and has a secondary command buffer allocated from commandPool recorded into it,
+ // becomes invalid. TODO: Might want to check and throw exception for these conditions after making sure this actually happens using validation layers.
+ }
+ for (auto& secondary : secondaries)
+ {
+ if (secondary->GetState() != SH_CMD_BUFFER_STATE::PENDING)
+ secondary->SetState(SH_CMD_BUFFER_STATE::INITIAL);
+ else
+ SHLOG_ERROR("Secondary command buffer in pending state, could not reset. ");
+
+ // TODO: Ditto from TODO in primary check
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Request a command buffer from the command pool. User can choose for it
+ to be secondary or primary/
+
+ \param parentPool
+ This is solely for command buffers created to know what command pool
+ they originated from.
+
+ \param type
+ The type of the command buffer.
+
+ */
+ /***************************************************************************/
+ Handle SHVkCommandPool::RequestCommandBuffer(SH_CMD_BUFFER_TYPE type)
+ {
+ // If type requested for is primary
+ if (type == SH_CMD_BUFFER_TYPE::PRIMARY)
+ {
+ primaries.emplace_back(SHVkInstance::GetResourceManager().Create(GetHandle(), type));
+ return primaries.back();
+ }
+ // If type requested for is secondary
+ else
+ {
+ secondaries.emplace_back(SHVkInstance::GetResourceManager().Create(GetHandle(), type));
+ return secondaries.back();
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Get the reset mode of the command pool.
+
+ \return
+ Returns the reset mode of the command pool.
+
+ */
+ /***************************************************************************/
+ SH_CMD_POOL_RESET SHVkCommandPool::GetPoolResetMode(void) const noexcept
+ {
+ return resetMode;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns the vulkan command pool handle.
+
+ \return
+ The vulkan command pool handle.
+
+ */
+ /***************************************************************************/
+ vk::CommandPool SHVkCommandPool::GetVkCommandPool(void) const noexcept
+ {
+ return vkCommandPool;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns the logical device handle stored in the command pool.
+
+ \return
+ The logical device handle.
+
+ */
+ /***************************************************************************/
+ Handle const& SHVkCommandPool::GetLogicalDevice(void) const noexcept
+ {
+ return logicalDeviceHdl;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns whether or not the buffers created from this pool are
+ short-lived.
+
+ \return
+ The transient flag.
+
+ */
+ /***************************************************************************/
+ bool SHVkCommandPool::GetIsTransient(void) const noexcept
+ {
+ return transient;
+ }
+
+}
diff --git a/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h
new file mode 100644
index 00000000..2bb290a7
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Commands/SHVkCommandPool.h
@@ -0,0 +1,73 @@
+#ifndef SH_COMMAND_POOL_H
+#define SH_COMMAND_POOL_H
+
+#include "Graphics/SHVulkanIncludes.h"
+#include "Graphics/SHVulkanDefines.h"
+#include "Graphics/Queues/SHVkQueue.h"
+#include "SHCommandPoolResetMode.h"
+#include "SHVkCommandBuffer.h"
+#include "Resource/ResourceLibrary.h"
+
+namespace SHADE
+{
+ class SHVkLogicalDevice;
+
+ class SHVkCommandPool : public ISelfHandle
+ {
+ private:
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER VARIABLES */
+ /*-----------------------------------------------------------------------*/
+ //! Vulkan handle to a command pool
+ vk::CommandPool vkCommandPool;
+
+ //! The queue family index that was used to initialize this pool
+ SHQueueFamilyIndex queueFamilyIndex;
+
+ //! The reset mode of the command buffer. For optimal usage, make the reset
+ //! mode POOL_BASED so that pools can recycle buffer memory all at once
+ //! instead of resetting 1 by 1. In general, we also want to re-record
+ //! buffers every frame instead of pre-recording, so resetting pools each
+ //! frame supports this concept.
+ SH_CMD_POOL_RESET resetMode;
+
+ //! primary command buffers
+ std::vector> primaries;
+
+ //! secondary command buffers
+ std::vector> secondaries;
+
+ //! Handle to logical device for convenience
+ Handle logicalDeviceHdl;
+
+ //! Whether or not buffers created from here are to be short lived
+ bool transient;
+
+ public:
+ /*-----------------------------------------------------------------------*/
+ /* CTOR AND DTOR */
+ /*-----------------------------------------------------------------------*/
+ SHVkCommandPool (Handle const& inLogicalDeviceHdl, SH_QUEUE_FAMILY_ARRAY_INDEX queueFamilyType, SH_CMD_POOL_RESET inResetMode, bool inTransient) noexcept;
+ SHVkCommandPool (void) noexcept = delete;
+ SHVkCommandPool(SHVkCommandPool&& rhs) noexcept;
+ SHVkCommandPool& operator=(SHVkCommandPool&& rhs) noexcept;
+ ~SHVkCommandPool(void) noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC MEMBER FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ void Reset (void) noexcept;
+ Handle RequestCommandBuffer (SH_CMD_BUFFER_TYPE type);
+
+ /*-----------------------------------------------------------------------*/
+ /* SETTERS AND GETTERS */
+ /*-----------------------------------------------------------------------*/
+ SH_CMD_POOL_RESET GetPoolResetMode(void) const noexcept;
+ vk::CommandPool GetVkCommandPool(void) const noexcept;
+ Handle const& GetLogicalDevice(void) const noexcept;
+ bool GetIsTransient (void) const noexcept;
+
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Debugging/SHValidationLayersQuery.cpp b/SHADE_Engine/src/Graphics/Debugging/SHValidationLayersQuery.cpp
new file mode 100644
index 00000000..420fa9e5
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Debugging/SHValidationLayersQuery.cpp
@@ -0,0 +1,86 @@
+#include "SHPch.h"
+#include "SHValidationLayersQuery.h"
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ /*-------------------------------------------------------------------------*/
+ /* STATIC VARIABLE DECLARATION */
+ /*-------------------------------------------------------------------------*/
+ std::vector SHValidationLayersQuery::queriedLayers;
+ std::vector SHValidationLayersQuery::availableLayers;
+
+ // TODO: Not sure if necessary but it would be nice to load validation layers
+ // from a file.
+ std::vector SHValidationLayersQuery::requiredLayers =
+ {
+ "VK_LAYER_KHRONOS_validation" // standard validation layer
+ };
+
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Uses the hard coded required layer above to check against the validation
+ layers retrieved upon querying from Vulkan. The final list of layers are
+ ones that are both in the required container and the queried container.
+
+ \param printInfo
+ If this is true, validation layer names are printed.
+
+ \param renderdocEnabled
+ Pushes a validation layer for renderdoc support if true.
+
+ */
+ /***************************************************************************/
+ void SHValidationLayersQuery::GenerateAvailableLayers(bool renderdocEnabled) noexcept
+ {
+ availableLayers.clear();
+
+ // Get the layers from vulkan
+ queriedLayers = vk::enumerateInstanceLayerProperties();
+
+ // Cherrypick those we want from the required list
+ for (char const* const requiredLayer : requiredLayers)
+ {
+ bool layerFound = false;
+ for (auto const& layer : queriedLayers)
+ {
+ if (strcmp (requiredLayer, layer.layerName) == 0)
+ {
+ availableLayers.push_back(requiredLayer);
+ layerFound = true;
+ break;
+ }
+ }
+
+ if (!layerFound)
+ {
+ SHLOG_ERROR(std::string("validation Layer: ") + requiredLayer + std::string ("could not be found. "));
+ }
+ }
+
+ // if renderdoc is requested, add to validation layer.
+ if (renderdocEnabled)
+ availableLayers.push_back("VK_LAYER_RENDERDOC_Capture");
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Gets the available layers after using QueryAvailableLayers.
+
+ \return
+ Returns the available layers.
+
+
+ */
+ /***************************************************************************/
+ std::vector const& SHValidationLayersQuery::GetAvailableLayers(void) noexcept
+ {
+ return availableLayers;
+ }
+
+}
diff --git a/SHADE_Engine/src/Graphics/Debugging/SHValidationLayersQuery.h b/SHADE_Engine/src/Graphics/Debugging/SHValidationLayersQuery.h
new file mode 100644
index 00000000..555dc720
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Debugging/SHValidationLayersQuery.h
@@ -0,0 +1,57 @@
+/**************************************************************************//*!
+\file SHValidationLayersQuery.h
+\author Brandon Mak
+\par email: brandon.hao@digipen.edu
+\date 17th May 2022
+\brief Stores the declaration of SHValidationLayersQuery class.
+
+Copyright (C) 2022 DigiPen Institute of Technology.
+Reproduction or disclosure of this file or its contents without the prior
+written consent of DigiPen Institute of Technology is prohibited.
+
+*//***************************************************************************/
+
+#ifndef SH_VALIDATION_LAYERS_QUERY_H
+#define SH_VALIDATION_LAYERS_QUERY_H
+
+#include // std::vector
+#include "Graphics/SHVulkanIncludes.h"
+
+namespace SHADE
+{
+ /*!**************************************************************************
+
+ \class SHValidationLLayersQuery
+
+ \brief
+ Static class to query for validation layers. Only used during Vulkan
+ instance creation to enable validation layers.
+
+ ***************************************************************************/
+ class SHValidationLayersQuery
+ {
+ private:
+ /*-----------------------------------------------------------------------*/
+ /* STATIC VARIABLES */
+ /*-----------------------------------------------------------------------*/
+
+ //! required validation layers
+ static std::vector requiredLayers;
+
+ //! Queried layers
+ static std::vector queriedLayers;
+
+ //! Final list that should be used to see what validation layers are available
+ static std::vector availableLayers;
+
+ public:
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC STATIC FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ static void GenerateAvailableLayers (bool renderdocEnabled) noexcept;
+ static std::vector const& GetAvailableLayers (void) noexcept;
+
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Debugging/SHVkDebugMessenger.cpp b/SHADE_Engine/src/Graphics/Debugging/SHVkDebugMessenger.cpp
new file mode 100644
index 00000000..d5f0dfe0
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Debugging/SHVkDebugMessenger.cpp
@@ -0,0 +1,84 @@
+#include "SHPch.h"
+#include
+#include "SHVkDebugMessenger.h"
+#include "SHVulkanDebugUtil.h"
+#include "Graphics/Instance/SHVkInstance.h"
+#include "Tools/SHLogger.h"
+//#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Initializes a struct for debug messenger creation.
+
+ \param[in, out] createInfo
+ The create info to initialize.
+
+ \param severityFlags
+ The severity flags we want to have for the messenger.
+
+ \param typeFlags
+ The type flags we want to have for the messenger.
+
+ */
+ /***************************************************************************/
+ void SHVkDebugMessenger::InitializeDebugCreateInfo(vk::DebugUtilsMessengerCreateInfoEXT& createInfo,
+ VkDebugUtilsMessageSeverityFlagsEXT severityFlags,
+ VkDebugUtilsMessageTypeFlagsEXT typeFlags) noexcept
+ {
+ createInfo.messageSeverity = vk::DebugUtilsMessageSeverityFlagsEXT (severityFlags);
+ createInfo.messageType = vk::DebugUtilsMessageTypeFlagsEXT (typeFlags);
+ createInfo.pfnUserCallback = SHVulkanDebugUtil::GenericDebugCallback;
+ createInfo.pNext = nullptr;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Does everything required to create a debug messenger. This includes its
+ create info and calling the function to create a debug messenger.
+
+ \param vulkanInstance
+ Instance is required to create messenger.
+
+ */
+ /***************************************************************************/
+ void SHVkDebugMessenger::Initialize(void) noexcept
+ {
+ vk::DebugUtilsMessengerCreateInfoEXT debMsgCreateInfo;
+ InitializeDebugCreateInfo(debMsgCreateInfo,
+ GenMessengerSeverity(SH_DEBUG_MSG_SEV::S_VERBOSE, SH_DEBUG_MSG_SEV::S_WARNING, SH_DEBUG_MSG_SEV::S_ERROR),
+ GenMessengerType(SH_DEBUG_MSG_TYPE::T_GENERAL, SH_DEBUG_MSG_TYPE::T_VALIDATION, SH_DEBUG_MSG_TYPE::T_PERFORMANCE));
+
+ if (vk::Result result = SHVkInstance::GetVkInstance().createDebugUtilsMessengerEXT(&debMsgCreateInfo, nullptr, &debugMessenger); result != vk::Result::eSuccess)
+ {
+ SHVulkanDebugUtil::ReportVkError(result, "Failed to create Debug Messenger! ");
+ }
+ else
+ {
+ SHVulkanDebugUtil::ReportVkSuccess("Successfully created a Debug Messenger. ");
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Simply calls a Vulkan function to destroy the debug messenger.
+
+ \param vulkanInstance
+ Instance is required to destroy messenger.
+
+ */
+ /***************************************************************************/
+ void SHVkDebugMessenger::Destroy(void) noexcept
+ {
+ SHVkInstance::GetVkInstance().destroyDebugUtilsMessengerEXT(debugMessenger, nullptr);
+ }
+
+}
\ No newline at end of file
diff --git a/SHADE_Engine/src/Graphics/Debugging/SHVkDebugMessenger.h b/SHADE_Engine/src/Graphics/Debugging/SHVkDebugMessenger.h
new file mode 100644
index 00000000..577090a5
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Debugging/SHVkDebugMessenger.h
@@ -0,0 +1,111 @@
+#ifndef SH_DEBUG_MESSENGER_H
+#define SH_DEBUG_MESSENGER_H
+
+#include "SHVulkanDebugUtil.h"
+
+namespace SHADE
+{
+ enum class SH_DEBUG_MSG_SEV : VkDebugUtilsMessageSeverityFlagsEXT
+ {
+ S_VERBOSE = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
+ S_INFO = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
+ S_WARNING = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
+ S_ERROR = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
+ S_FLAG = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
+ };
+
+ enum class SH_DEBUG_MSG_TYPE : VkDebugUtilsMessageTypeFlagsEXT
+ {
+ T_GENERAL = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT,
+ T_VALIDATION = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
+ T_PERFORMANCE = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
+ T_FLAG_BITS = VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT,
+ };
+
+ /*!**************************************************************************
+
+ \class SHVkDebugMessenger
+
+ \brief
+ Vulkan provides debugging functionality through a messenger. This class
+ helps to create and initialize one.
+
+ ***************************************************************************/
+ class SHVkDebugMessenger
+ {
+ private:
+ /*-----------------------------------------------------------------------*/
+ /* DATA MEMBERS */
+ /*-----------------------------------------------------------------------*/
+
+ //! The vulkan debug messenger object
+ vk::DebugUtilsMessengerEXT debugMessenger;
+
+ public:
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC MEMBER FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ void Initialize(void) noexcept;
+ void Destroy(void) noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC STATIC FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ static void InitializeDebugCreateInfo(vk::DebugUtilsMessengerCreateInfoEXT& createInfo,
+ VkDebugUtilsMessageSeverityFlagsEXT severityFlags,
+ VkDebugUtilsMessageTypeFlagsEXT typeFlags) noexcept;
+
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ When using flags to specify debug message severity, the process is
+ extremely cumbersome as each flag has a lengthy name. This function
+ along with the enum SHDebMsgSeverity helps make it more readable. Use
+ this function any time a VkDebugUtilsMessageSeverityFlagsEXT argument
+ is requested.
+
+ \param ...severities
+ The combined severities.
+
+ \return
+ Returns a bitfield indicated the debug severity flags. Gets passed to
+ Vulkan when created debug create info.
+
+ */
+ /***************************************************************************/
+ template && ...)>>
+ static VkDebugUtilsMessageSeverityFlagsEXT GenMessengerSeverity(Sevs... severities)
+ {
+ return (static_cast(severities) | ...);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ When using flags to specify debug message types, the process is
+ extremely cumbersome as each flag has a lengthy name. This function
+ along with the enum SHDebMsgType helps make it more readable. Use
+ this function any time a VkDebugUtilsMessageTypeFlagsEXT argument
+ is requested.
+
+ \param ...types
+ The combined types.
+
+ \return
+ Returns a bitfield indicated the debug severity flags. Gets passed to
+ Vulkan when created debug create info.
+
+ */
+ /***************************************************************************/
+ template && ...)>>
+ static VkDebugUtilsMessageTypeFlagsEXT GenMessengerType(Types... types)
+ {
+ return (static_cast(types) | ...);
+ }
+ };
+}
+
+#endif
\ No newline at end of file
diff --git a/SHADE_Engine/src/Graphics/Debugging/SHVulkanDebugUtil.cpp b/SHADE_Engine/src/Graphics/Debugging/SHVulkanDebugUtil.cpp
new file mode 100644
index 00000000..d63a65f7
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Debugging/SHVulkanDebugUtil.cpp
@@ -0,0 +1,154 @@
+#include "SHPch.h"
+#include "SHVulkanDebugUtil.h"
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Generic debug function callback to print error messages that are warnings
+ or errors.
+
+ \param messageSeverity
+ Severity of the message
+
+ \param messageType
+ Type of the message
+
+ \param pCallbackData
+ Vulkan object containing useful information regarding the message.
+
+ \param pUserData
+ A pointer to any kind of user data.
+
+ \return
+ returns false anyway.
+
+ */
+ /***************************************************************************/
+ VKAPI_ATTR VkBool32 VKAPI_CALL SHVulkanDebugUtil::GenericDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
+ [[maybe_unused]] VkDebugUtilsMessageSeverityFlagsEXT messageType,
+ const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
+ [[maybe_unused]] void* pUserData)
+ {
+ if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
+ ReportVkWarning(static_cast (pCallbackData->messageIdNumber), pCallbackData->pMessage);
+ else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
+ ReportVkError(static_cast (pCallbackData->messageIdNumber), pCallbackData->pMessage);
+
+ return VK_FALSE;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Prints warnings in a formatted manner. Includes vkResult.
+
+ \param vkResult
+ The vkResult to display.
+
+ \param message
+ The custom user message to display.
+
+ \param location
+ location contains information about the function that calls this function,
+ the column/row of the callsite and the filename.
+
+ */
+ /***************************************************************************/
+ void SHVulkanDebugUtil::ReportVkWarning(vk::Result vkResult, std::string_view message, std::source_location const& location /*= std::source_location::current()*/) noexcept
+ {
+ //std::cout << location.file_name() << ": " << location.function_name() << "|" << location.line() << "|" <<
+ // location.column() << "|: Warning: " << SHDebugUtil::VkResultToString(vkResult) << " | " << message << std::endl;
+
+ std::string toLogger = "Vulkan Warning: " + std::string(SHVulkanDebugUtil::VkResultToString(vkResult)) + " | " + std::string(message);
+
+ SHLOGV_WARNING(toLogger);
+ }
+
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Prints errors in a formatted manner. Includes vkResult.
+
+ \param vkResult
+ The vkResult to display.
+
+ \param message
+ The custom user message to display.
+
+ \param location
+ location contains information about the function that calls this function,
+ the column/row of the callsite and the filename.
+
+ */
+ /***************************************************************************/
+ void SHVulkanDebugUtil::ReportVkError(vk::Result vkResult, std::string_view message, std::source_location const& location /*= std::source_location::current()*/) noexcept
+ {
+ std::string toLogger = "Vulkan Warning: " + std::string(SHVulkanDebugUtil::VkResultToString(vkResult)) + " | " + std::string(message);
+
+ SHLOGV_ERROR(toLogger);
+ }
+
+
+ void SHVulkanDebugUtil::ReportVkSuccess(std::string_view message, std::source_location const& location /*= std::source_location::current()*/) noexcept
+ {
+ SHLOGV_INFO(message);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Converts a vkResult enum field to a string. Function is copied entirely
+ from xGPU: https://github.com/LIONant-depot/xGPU/blob/d546fc202d2acddb6beeb79947860fd437046cb3/Src/Details/Vulkan/xgpu_vulkan_instance.cpp#L19
+ Could not find a more straightforward way to convert enum to string.
+
+ \param vkResult
+ The Vulkan result to convert to string.
+
+ \return
+ returns a string_view that represents the vulkan result enum value.
+
+ */
+ /***************************************************************************/
+ std::string_view SHVulkanDebugUtil::VkResultToString(vk::Result vkResult) noexcept
+ {
+ VkResult cStyleResult = static_cast(vkResult);
+ switch (cStyleResult)
+ {
+ // Macro to form the enum value using '##', and stringify the enum using '#'
+#define STR(r) case VK_ ##r : return #r
+ STR(TIMEOUT);
+ STR(EVENT_SET);
+ STR(EVENT_RESET);
+ STR(INCOMPLETE);
+ STR(ERROR_OUT_OF_HOST_MEMORY);
+ STR(ERROR_OUT_OF_DEVICE_MEMORY);
+ STR(ERROR_INITIALIZATION_FAILED);
+ STR(ERROR_DEVICE_LOST);
+ STR(ERROR_MEMORY_MAP_FAILED);
+ STR(ERROR_LAYER_NOT_PRESENT);
+ STR(ERROR_EXTENSION_NOT_PRESENT);
+ STR(ERROR_FEATURE_NOT_PRESENT);
+ STR(ERROR_INCOMPATIBLE_DRIVER);
+ STR(ERROR_TOO_MANY_OBJECTS);
+ STR(ERROR_FORMAT_NOT_SUPPORTED);
+ STR(ERROR_SURFACE_LOST_KHR);
+ STR(ERROR_NATIVE_WINDOW_IN_USE_KHR);
+ STR(SUBOPTIMAL_KHR);
+ STR(ERROR_OUT_OF_DATE_KHR);
+ STR(ERROR_INCOMPATIBLE_DISPLAY_KHR);
+ STR(ERROR_VALIDATION_FAILED_EXT);
+ STR(ERROR_INVALID_SHADER_NV);
+#undef STR
+ default:
+ return "UNKNOWN_ERROR";
+ }
+ }
+}
diff --git a/SHADE_Engine/src/Graphics/Debugging/SHVulkanDebugUtil.h b/SHADE_Engine/src/Graphics/Debugging/SHVulkanDebugUtil.h
new file mode 100644
index 00000000..7bf583bb
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Debugging/SHVulkanDebugUtil.h
@@ -0,0 +1,30 @@
+#ifndef SH_DEBUG_UTIL_H
+#define SH_DEBUG_UTIL_H
+
+#include "Graphics/SHVulkanIncludes.h"
+//#include
+#include
+
+namespace SHADE
+{
+ class SHVulkanDebugUtil
+ {
+ private:
+ static std::string_view VkResultToString(vk::Result vkResult) noexcept;
+
+ public:
+ static VKAPI_ATTR VkBool32 VKAPI_CALL GenericDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageSeverityFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData);
+
+ static void ReportVkWarning(vk::Result vkResult, std::string_view message, std::source_location const& location = std::source_location::current()) noexcept;
+ static void ReportVkError(vk::Result vkResult, std::string_view message, std::source_location const& location = std::source_location::current()) noexcept;
+ static void ReportVkSuccess(std::string_view message, std::source_location const& location = std::source_location::current()) noexcept;
+
+ };
+
+ class SHVkDebugOn
+ {
+
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolManager.cpp b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolManager.cpp
new file mode 100644
index 00000000..fcbedc07
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolManager.cpp
@@ -0,0 +1,2 @@
+#include "SHPch.h"
+#include "SHDescriptorPoolManager.h"
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolManager.h b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolManager.h
new file mode 100644
index 00000000..cbd64982
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolManager.h
@@ -0,0 +1,5 @@
+#pragma once
+class SHDescriptorPoolManager
+{
+};
+
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolStorage.cpp b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolStorage.cpp
new file mode 100644
index 00000000..e5f7c394
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolStorage.cpp
@@ -0,0 +1,2 @@
+#include "SHPch.h"
+#include "SHDescriptorPoolStorage.h"
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolStorage.h b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolStorage.h
new file mode 100644
index 00000000..7e421006
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHDescriptorPoolStorage.h
@@ -0,0 +1,5 @@
+#pragma once
+class SHDescriptorPoolStorage
+{
+};
+
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.cpp b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.cpp
new file mode 100644
index 00000000..87d43255
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.cpp
@@ -0,0 +1,38 @@
+#include "SHPch.h"
+#include "SHVkDescriptorPool.h"
+
+#include "Graphics/Devices/SHVkLogicalDevice.h"
+#include "Graphics/Instance/SHVkInstance.h"
+#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Constructor/Destructor */
+ /*---------------------------------------------------------------------------------*/
+ SHVkDescriptorPool::SHVkDescriptorPool(Handle device, const Config& config)
+ : device { device }
+ {
+ // Create the Pool
+ const vk::DescriptorPoolCreateInfo POOL_CREATE_INFO
+ {
+ .flags = config.Flags,
+ .maxSets = config.MaxSets,
+ .poolSizeCount = static_cast(config.Limits.size()),
+ .pPoolSizes = config.Limits.data()
+ };
+ pool = device->GetVkLogicalDevice().createDescriptorPool(POOL_CREATE_INFO);
+ }
+
+ SHVkDescriptorPool::~SHVkDescriptorPool() noexcept
+ {
+ if (pool)
+ device->GetVkLogicalDevice().destroyDescriptorPool(pool);
+ }
+
+ std::vector> SHVkDescriptorPool::Allocate(const std::vector>& layouts, std::vector const& variableDescCounts)
+ {
+ SHVkInstance::GetResourceManager().Create(device, GetHandle(), layouts, variableDescCounts);
+ return {};
+ }
+}
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h
new file mode 100644
index 00000000..c3059b8b
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorPool.h
@@ -0,0 +1,114 @@
+#pragma once
+
+// Project Includes
+#include "Graphics/SHVulkanIncludes.h"
+#include "Resource/Handle.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Forward Declarations */
+ /*---------------------------------------------------------------------------------*/
+ class SHVkLogicalDevice;
+ class SHVkDescriptorSetGroup;
+ class SHVkDescriptorSetLayout;
+
+ /*---------------------------------------------------------------------------------*/
+ /* Type Definitions */
+ /*---------------------------------------------------------------------------------*/
+ ///
+ ///
+ ///
+ class SHVkDescriptorPool : public ISelfHandle
+ {
+ public:
+ /*-----------------------------------------------------------------------------*/
+ /* Type Definitions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Configuration object for the Pool Manager.
+ ///
+ struct Config
+ {
+ public:
+ ///
+ /// Describe the resource limitations of the Descriptor Pool can construct.
+ /// The specified type and count pairs indicate how many Descriptors will be
+ /// constructed in the Descriptor Pool.
+ ///
+ std::vector Limits =
+ {
+ { vk::DescriptorType::eCombinedImageSampler, 100 },
+ { vk::DescriptorType::eUniformBuffer, 100 }
+ };
+ ///
+ /// Maximum number of descriptor sets allowed
+ ///
+ uint32_t MaxSets = 100;
+ ///
+ /// Flags used to create the DescriptorPool
+ ///
+ vk::DescriptorPoolCreateFlags Flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
+ };
+
+ /*-----------------------------------------------------------------------------*/
+ /* Constructor/Destructor */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Constructor for a Descriptor Pool.
+ ///
+ ///
+ /// The Vulkan logical device that is used to create a Descriptor Pool.
+ ///
+ ///
+ /// Configuration object that describes how to construct the Descriptor Pool.
+ ///
+ SHVkDescriptorPool(Handle device, const Config& config = {});
+ SHVkDescriptorPool(const SHVkDescriptorPool&) = delete;
+ SHVkDescriptorPool(SHVkDescriptorPool&& rhs) noexcept = default;
+ ///
+ /// Destructor which will unload and deallocate all resources for this Pool.
+ ///
+ ~SHVkDescriptorPool() noexcept;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Overloaded Operators */
+ /*-----------------------------------------------------------------------------*/
+ SHVkDescriptorPool& operator=(const SHVkDescriptorPool&) = delete;
+ SHVkDescriptorPool& operator=(SHVkDescriptorPool&& rhs) noexcept = default;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Getter Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Retrieves the handle to the Vulkan Descriptor Pool handle.
+ ///
+ /// Handle to the Vulkan Descriptor Pool.
+ [[nodiscard]]
+ inline const vk::DescriptorPool& GetVkHandle() const { return pool; }
+
+ /*-----------------------------------------------------------------------------*/
+ /* Usage Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Allocates multiple DescriptorSets based on the provided layouts.
+ ///
+ /// Layouts of DescriptorSets to create.
+ ///
+ /// Thrown if an incompatible layout was provided to this Descriptor Pool.
+ ///
+ ///
+ /// Handles to the created Descriptor Sets. If this DescriptorPool has run out of
+ /// space, lesser number of Handles will be returned.
+ ///
+ std::vector> Allocate(const std::vector>& layouts, std::vector const& variableDescCounts);
+
+ private:
+ /*-----------------------------------------------------------------------------*/
+ /* Data Members */
+ /*-----------------------------------------------------------------------------*/
+ Handle device;
+ vk::DescriptorPool pool;
+ };
+}
+
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.cpp b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.cpp
new file mode 100644
index 00000000..6bdc5601
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.cpp
@@ -0,0 +1,85 @@
+#include "SHPch.h"
+#include "SHVkDescriptorSetGroup.h"
+
+#include
+
+#include "SHVkDescriptorPool.h"
+#include "Graphics/Devices/SHVkLogicalDevice.h"
+#include "Graphics/Descriptors/SHVkDescriptorSetLayout.h"
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Allocates a bunch of descriptor sets based on how many layouts are
+ being passed in.
+
+ \param deviceHdl
+ Logical device required for the descriptor set creation.
+
+
+ \param pool
+ Pool to allocate from.
+
+ \param layouts
+ With N layouts provided, the function will allocate N descriptor sets.
+ All of which will be stored in this class.
+
+ \param variableDescCounts
+ Represents the variable descriptor count for every layout passed in.
+ If the binding used in the layout is not marked as using a variable count,
+ the value for the particular layout is ignored.
+
+ */
+ /***************************************************************************/
+ SHVkDescriptorSetGroup::SHVkDescriptorSetGroup(Handle deviceHdl, Handle pool, std::vector> const& layouts, std::vector const& variableDescCounts)
+ : device{deviceHdl}
+ , descPool {pool}
+ , descSets{}
+ {
+ // Create the layout for each concurrent frame
+ std::vector vkLayouts{ layouts.size() };
+ for (auto& layout : layouts)
+ {
+ vkLayouts.push_back(layout->GetVkHandle());
+ }
+
+ // Check for variable descriptor count
+ if (variableDescCounts.size() != layouts.size())
+ SHLOG_ERROR("Number of variable descriptor counts does not match number of layouts. If a layout does not use variable counts, pass in 0. ");
+
+ // Prepare variable descriptor counts
+ vk::DescriptorSetVariableDescriptorCountAllocateInfo variableAllocInfo{};
+ variableAllocInfo.descriptorSetCount = static_cast(variableDescCounts.size());
+ variableAllocInfo.pDescriptorCounts = variableDescCounts.data();
+
+ // Prepare allocation information
+ const vk::DescriptorSetAllocateInfo DESC_SET_LAYOUT_CREATE_INFO
+ {
+ .pNext = &variableAllocInfo,
+ .descriptorPool = descPool->GetVkHandle(),
+ .descriptorSetCount = static_cast(vkLayouts.size()),
+ .pSetLayouts = vkLayouts.data(),
+ };
+
+ // allocate descriptor sets
+ descSets = device->GetVkLogicalDevice().allocateDescriptorSets(DESC_SET_LAYOUT_CREATE_INFO);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Destroys all the descriptor sets in the group.
+
+ */
+ /***************************************************************************/
+ SHVkDescriptorSetGroup::~SHVkDescriptorSetGroup() noexcept
+ {
+ if (!descSets.empty())
+ device->GetVkLogicalDevice().freeDescriptorSets(descPool->GetVkHandle(), descSets);
+ }
+}
\ No newline at end of file
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h
new file mode 100644
index 00000000..b95859bb
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetGroup.h
@@ -0,0 +1,71 @@
+ #pragma once
+
+// Project Includes
+#include "Graphics/SHVulkanIncludes.h"
+#include "Resource/Handle.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Forward Declarations */
+ /*---------------------------------------------------------------------------------*/
+ class SHVkLogicalDevice;
+ class SHVkDescriptorPool;
+ class SHVkDescriptorSetLayout;
+
+ /*---------------------------------------------------------------------------------*/
+ /* Type Definitions */
+ /*---------------------------------------------------------------------------------*/
+ ///
+ ///
+ ///
+ class SHVkDescriptorSetGroup
+ {
+ public:
+ /*-----------------------------------------------------------------------------*/
+ /* Constructor/Destructors */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Constructs a Descriptor Set with the specified layout using the specified
+ /// pool meant for use with the specified surface. This Set will be created with
+ /// multiple Vulkan Descriptor Set objects based on the max number of concurrent
+ /// frames for the specified surface.
+ ///
+ /// Vulkan logical device used to create the Set.
+ /// Descriptor Pool used to create the Set.
+ /// Descriptor Set Layout to create the Set with.
+ SHVkDescriptorSetGroup(Handle deviceHdl, Handle pool,
+ std::vector> const& layouts,
+ std::vector const& variableDescCounts);
+ SHVkDescriptorSetGroup(const SHVkDescriptorSetGroup&) = delete;
+ SHVkDescriptorSetGroup(SHVkDescriptorSetGroup&& rhs) noexcept = default;
+ ///
+ /// Destructor which will unload and deallocate all resources for this Descriptor Set.
+ ///
+ ~SHVkDescriptorSetGroup() noexcept;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Overloaded Operators */
+ /*-----------------------------------------------------------------------------*/
+ SHVkDescriptorSetGroup& operator=(const SHVkDescriptorSetGroup&) = delete;
+ SHVkDescriptorSetGroup& operator=(SHVkDescriptorSetGroup&& rhs) noexcept = default;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Getter Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Retrieves the handle to the Vulkan Descriptor Set handle.
+ ///
+ /// Handle to the Vulkan Descriptor Set.
+ [[nodiscard]]
+ inline const std::vector& GetVkHandle() { return descSets; }
+
+ private:
+ /*-----------------------------------------------------------------------------*/
+ /* Data Members */
+ /*-----------------------------------------------------------------------------*/
+ Handle device;
+ Handle descPool;
+ std::vector descSets;
+ };
+}
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp
new file mode 100644
index 00000000..36eaa8e8
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.cpp
@@ -0,0 +1,102 @@
+#include "SHPch.h"
+#include "SHVkDescriptorSetLayout.h"
+#include "Graphics/Devices/SHVkLogicalDevice.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Constructor/Destructor */
+ /*---------------------------------------------------------------------------------*/
+ SHVkDescriptorSetLayout::SHVkDescriptorSetLayout(Handle device, const std::vector& bindings)
+ : device { device }
+ , layoutDesc { bindings }
+ {
+ // Check if auto-binding point calculation configuration is valid
+ bool autoCalc = false;
+ for (const auto& binding : bindings)
+ {
+ if (binding.BindPoint == Binding::AUTO_CALC_BINDING)
+ {
+ autoCalc = true;
+ }
+ else if (autoCalc)
+ {
+ throw std::invalid_argument("For auto calculation of bindings, all bindings must be set to AUTO_CALC_BINDING!");
+ }
+ }
+
+ // Fill up VK bindings with auto calculated bind points if needed
+ std::vector layoutBindings;
+ layoutBindings.reserve(bindings.size());
+ int bindCount = 0;
+ for (const auto& binding : bindings)
+ {
+ const uint32_t CURR_BIND_POINT = autoCalc ? bindCount : binding.BindPoint;
+ const vk::DescriptorSetLayoutBinding VK_BINDING =
+ {
+ .binding = CURR_BIND_POINT,
+ .descriptorType = binding.Type,
+ .descriptorCount = binding.DescriptorCount,
+ .stageFlags = binding.Stage,
+ .pImmutableSamplers = nullptr // We will create our own samplers
+ };
+ layoutBindings.emplace_back(VK_BINDING);
+
+ // Save for future reference
+ layoutDesc[bindCount++].BindPoint = CURR_BIND_POINT;
+ }
+
+ // TODO: Check layout support with physical device
+
+ // Prepare binding flags
+ std::vector combinedBindings(bindings.size());
+ for (uint32_t i = 0; i < bindings.size(); ++i)
+ combinedBindings[i] = bindings[i].flags;
+
+ const vk::DescriptorSetLayoutBindingFlagsCreateInfo BINDING_FLAGS_CREATE_INFO
+ {
+ .bindingCount = static_cast(bindings.size()), // Number of flags = number of bindings
+ .pBindingFlags = combinedBindings.data(), // address to flags
+ };
+
+ // Create the layout
+ const vk::DescriptorSetLayoutCreateInfo DESC_SET_LAYOUT_CREATE_INFO
+ {
+ .pNext = &BINDING_FLAGS_CREATE_INFO,
+ .flags = {},
+ .bindingCount = static_cast(layoutBindings.size()),
+ .pBindings = layoutBindings.data(),
+ };
+ setLayout = device->GetVkLogicalDevice().createDescriptorSetLayout(DESC_SET_LAYOUT_CREATE_INFO);
+ }
+
+ SHVkDescriptorSetLayout::SHVkDescriptorSetLayout(SHVkDescriptorSetLayout&& rhs) noexcept
+ : device {rhs.device}
+ , setLayout {rhs.setLayout}
+ , layoutDesc{std::move (rhs.layoutDesc)}
+ {
+ rhs.setLayout = VK_NULL_HANDLE;
+ }
+
+ SHVkDescriptorSetLayout::~SHVkDescriptorSetLayout() noexcept
+ {
+ // Destroy layout
+ if (setLayout)
+ device->GetVkLogicalDevice().destroyDescriptorSetLayout(setLayout);
+ }
+
+ SHVkDescriptorSetLayout& SHVkDescriptorSetLayout::operator=(SHVkDescriptorSetLayout&& rhs) noexcept
+ {
+ if (&rhs == this)
+ return *this;
+
+ device = rhs.device;
+ setLayout = rhs.setLayout;
+ layoutDesc = std::move(rhs.layoutDesc);
+
+ rhs.setLayout = VK_NULL_HANDLE;
+
+ return *this;
+ }
+
+}
diff --git a/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h
new file mode 100644
index 00000000..590fd787
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Descriptors/SHVkDescriptorSetLayout.h
@@ -0,0 +1,108 @@
+#pragma once
+
+// Project Includes
+#include "Graphics/SHVulkanIncludes.h"
+#include "Resource/Handle.h"
+
+namespace SHADE
+{
+ /*---------------------------------------------------------------------------------*/
+ /* Forward Declarations */
+ /*---------------------------------------------------------------------------------*/
+ class SHVkLogicalDevice;
+
+ /*---------------------------------------------------------------------------------*/
+ /* Type Definitions */
+ /*---------------------------------------------------------------------------------*/
+ ///
+ /// RAII wrapper object for a Vulkan Descriptor Set Layout object.
+ ///
+ class SHVkDescriptorSetLayout
+ {
+ public:
+ /*-----------------------------------------------------------------------------*/
+ /* Type Definitions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Object that describes how a descriptor binding in a DescriptorSetLayout is
+ /// structured.
+ ///
+ struct Binding
+ {
+ /*-------------------------------------------------------------------------*/
+ /* Constants */
+ /*-------------------------------------------------------------------------*/
+ ///
+ /// If set for the "BindPoint", binding points are automatically calculated.
+ ///
+ static constexpr uint32_t AUTO_CALC_BINDING = std::numeric_limits::max();
+
+ ///
+ /// For use in Binding DescriptorCount.
+ ///
+ static constexpr uint32_t VARIABLE_DESCRIPTOR_UPPER_BOUND = 2000;
+ /*-------------------------------------------------------------------------*/
+ /* Data Members */
+ /*-------------------------------------------------------------------------*/
+ ///
+ /// Type of element for the descriptor.
+ ///
+ vk::DescriptorType Type = {};
+ ///
+ /// Shader stage that this binding is for.
+ ///
+ vk::ShaderStageFlags Stage = {};
+ ///
+ /// Binding point for the Descriptor within the Descriptor Set.
+ ///
+ uint32_t BindPoint = AUTO_CALC_BINDING;
+ ///
+ /// Number of elements in the binding. When VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
+ /// is used in VkDescriptorBindingFlagBits, this value represents the upper bound.
+ ///
+ uint32_t DescriptorCount = 1;
+
+ vk::DescriptorBindingFlags flags = {};
+ };
+
+ /*-----------------------------------------------------------------------------*/
+ /* Constructor/Destructors */
+ /*-----------------------------------------------------------------------------*/
+ SHVkDescriptorSetLayout() = delete;
+ ///
+ /// Constructs a DescriptorSetLayout with the specified properties and device.
+ ///
+ ///
+ ///
+ SHVkDescriptorSetLayout(Handle device, const std::vector& bindings);
+ SHVkDescriptorSetLayout(const SHVkDescriptorSetLayout&) = delete;
+ SHVkDescriptorSetLayout(SHVkDescriptorSetLayout&& rhs) noexcept;
+ ///
+ /// Destructor which will unload and deallocate all resources for this Set.
+ ///
+ ~SHVkDescriptorSetLayout() noexcept;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Overloaded Operators */
+ /*-----------------------------------------------------------------------------*/
+ SHVkDescriptorSetLayout& operator=(const SHVkDescriptorSetLayout&) = delete;
+ SHVkDescriptorSetLayout& operator=(SHVkDescriptorSetLayout&& rhs) noexcept;
+
+ /*-----------------------------------------------------------------------------*/
+ /* Getter Functions */
+ /*-----------------------------------------------------------------------------*/
+ ///
+ /// Retrieves the handle to the Vulkan Descriptor Set Layout handle.
+ ///
+ /// Handle to the Vulkan Descriptor Set Layout handle.
+ inline const vk::DescriptorSetLayout& GetVkHandle() const { return setLayout; }
+
+ private:
+ /*-----------------------------------------------------------------------------*/
+ /* Data Members */
+ /*-----------------------------------------------------------------------------*/
+ Handle device;
+ vk::DescriptorSetLayout setLayout;
+ std::vector layoutDesc; // Stores description of the layout
+ };
+}
\ No newline at end of file
diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp
new file mode 100644
index 00000000..aa442805
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.cpp
@@ -0,0 +1,637 @@
+#include "SHPch.h"
+#include "SHVkLogicalDevice.h"
+#include "SHVkPhysicalDevice.h"
+#include "Graphics/Instance/SHVkInstance.h"
+#include "Tools/SHLogger.h"
+#include "Graphics/Windowing/Surface/SHVkSurface.h"
+#include "Graphics/Swapchain/SHVkSwapchain.h"
+#include "Graphics/Commands/SHVkCommandPool.h"
+#include "Graphics/Buffers/SHVkBuffer.h"
+#include "Graphics/Images/SHVkImage.h"
+#include "Graphics/Synchronization/SHVkFence.h"
+#include "Graphics/Synchronization/SHVkSemaphore.h"
+#include "Graphics/Shaders/SHVkShaderModule.h"
+#include "Graphics/Pipeline/SHVkPipelineLayout.h"
+#include "Graphics/Pipeline/SHVkPipeline.h"
+#include "Graphics/Framebuffer/SHVkFramebuffer.h"
+#include "Graphics/Images/SHVkImageView.h"
+
+namespace SHADE
+{
+
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Initializes a VMA allocator.
+
+ */
+ /***************************************************************************/
+ void SHVkLogicalDevice::InitializeVMA(void) noexcept
+ {
+ VmaVulkanFunctions vulkanFunctions = {};
+ vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;
+ vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;
+
+ VmaAllocatorCreateInfo vmaCreateInfo{};
+ vmaCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3;
+ vmaCreateInfo.physicalDevice = parentPhysicalDeviceHdl->GetVkPhysicalDevice();
+ vmaCreateInfo.device = vkLogicalDevice;
+ vmaCreateInfo.instance = SHVkInstance::GetVkInstance();
+ vmaCreateInfo.pVulkanFunctions = &vulkanFunctions;
+
+ // attempt to create the vma allocator.
+ vmaCreateAllocator(&vmaCreateInfo, &vmaAllocator);
+ }
+
+ void SHVkLogicalDevice::InitializeQueues(std::initializer_list queueCreateParams) noexcept
+ {
+ /* Each queue family has a set amount of queues. Before we get queues from them, we want to
+ * reset the queue use count to 0.
+ */
+ for (std::size_t i = 0; i < static_cast(SH_QUEUE_FAMILY_ARRAY_INDEX::MAX); ++i)
+ {
+ if (queueFamilyIndices.indices[i].has_value())
+ {
+ queueFamUseCounts[queueFamilyIndices.indices[i].value()] = 0;
+ queueLibrary.emplace(queueFamilyIndices.indices[i].value(), std::vector>());
+ }
+ }
+
+ // actually attempt to get queue
+ for (auto const& queueParams : queueCreateParams)
+ {
+ // First, check if queue family exists and get index of queue family
+ SHQueueFamilyIndex queueFamIndex = 0;
+
+ if (queueParams.selectionMethod == SH_QUEUE_SELECT::DEDICATED)
+ {
+ if (uint32_t arrayIndex = static_cast(queueParams.arrayIndex); queueFamilyIndices.indices[arrayIndex].has_value())
+ queueFamIndex = queueFamilyIndices.indices[arrayIndex].value();
+ else
+ {
+ SHLOGV_ERROR("Failed to get queue from queue family. ");
+ continue;
+ }
+ }
+ else
+ queueFamIndex = nonDedicatedBestIndex;
+
+ // Actually attempt to get the queue from Vulkan and increase the number of queue obtained
+ queueLibrary[queueFamIndex].push_back(SHVkInstance::GetResourceManager().Create(vkLogicalDevice.getQueue(queueFamIndex, queueFamUseCounts[queueFamIndex]++), queueFamIndex, GetHandle()));
+ }
+
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Initializes a logical device and the requested queues with it. Queues
+ are either specified as either NON_DEDICATED_BEST or DEDICATED.
+ NON_DEDICATED_BEST means that the function will look for a queue family
+ with the highest functionality and attempt to create a queue from that.
+ For example, a transfer queue will select a graphics queue family, even
+ though the graphics aren't needed. DEDICATED means that a transfer queue
+ request would lead to a selection of a dedicated queue family specifically
+ for that queue (e.g. compute -> use compute queue family).
+
+ \param queueCreateParams
+ Since queues all need to be created with the logical device, the specs
+ of all the queues are prerequisites of the creation of the logical device.
+
+ \param inPhysicalDevice
+ \param queueTypes
+
+ \return
+
+
+ */
+ /***************************************************************************/
+ SHVkLogicalDevice::SHVkLogicalDevice(std::initializer_list queueCreateParams, Handle const& inPhysicalDeviceHdl) noexcept
+ : vkLogicalDevice{}
+ , parentPhysicalDeviceHdl{ inPhysicalDeviceHdl }
+ , queueFamilyIndices{}
+ , vmaAllocator{}
+ {
+ float priority = 1.0f;
+
+ // Get all queue families
+ auto queueFamProps = parentPhysicalDeviceHdl->GetQueueFamilyProperties();
+
+ // Number of queue family properties
+ uint32_t queueFamPropsSize = static_cast(queueFamProps.size());
+
+ // Will represent the first fit queue family index
+ nonDedicatedBestIndex = 0xFFFF;
+
+ // initialize queue family indices
+ for (uint32_t i = 0; i < queueFamPropsSize; ++i)
+ {
+ std::size_t index = 0;
+
+ if (queueFamProps[i].queueFlags & vk::QueueFlagBits::eGraphics)
+ index = static_cast(SH_QUEUE_FAMILY_ARRAY_INDEX::GRAPHICS);
+ else if (queueFamProps[i].queueFlags & vk::QueueFlagBits::eCompute)
+ index = static_cast(SH_QUEUE_FAMILY_ARRAY_INDEX::COMPUTE);
+ else if (queueFamProps[i].queueFlags & vk::QueueFlagBits::eTransfer)
+ index = static_cast(SH_QUEUE_FAMILY_ARRAY_INDEX::TRANSFER);
+
+ queueFamilyIndices.indices[index] = static_cast(i);
+ }
+
+ // Find the most versatile/best queue family
+ nonDedicatedBestIndex = (*std::min_element(queueFamilyIndices.indices.begin(), queueFamilyIndices.indices.end())).value();
+
+ // Start producing queue create infos
+ std::vector queueCreateInfos;
+ for (auto const& params : queueCreateParams)
+ {
+ vk::DeviceQueueCreateInfo queueCreateInfo{};
+
+ // Check if we want the best or a dedicated queue family
+ if (params.selectionMethod == SH_QUEUE_SELECT::NON_DEDICATED_BEST)
+ queueCreateInfo.queueFamilyIndex = nonDedicatedBestIndex;
+ else
+ queueCreateInfo.queueFamilyIndex = queueFamilyIndices.indices[static_cast(params.arrayIndex)].value();
+
+ queueCreateInfo.queueCount = 1;
+ queueCreateInfo.pQueuePriorities = &priority; // 1.0f
+ queueCreateInfos.push_back(queueCreateInfo);
+ }
+
+ std::vector requiredExtensions;
+ requiredExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
+ // Check if extensions are supported
+ bool extensionsSupported = parentPhysicalDeviceHdl->ExtensionsSupported(requiredExtensions);
+ // Log error
+ /*if (!extensionsSupported)
+ SHUtil::ReportWarning("Some of the required extensions cannot be found on the physical device. ");*/
+
+ vk::PhysicalDeviceFeatures features{}; // ADD MORE FEATURES HERE IF NEEDED
+
+ // point and lines fill mode
+ features.fillModeNonSolid = true;
+ features.samplerAnisotropy = VK_TRUE;
+
+ // for wide lines
+ features.wideLines = true;
+
+ // Prepare to create the device
+ vk::DeviceCreateInfo deviceCreateInfo
+ {
+ .pNext = nullptr,
+ .queueCreateInfoCount = static_cast(queueCreateInfos.size()),
+ .pQueueCreateInfos = queueCreateInfos.data(),
+ .enabledLayerCount = 0, // deprecated and ignored
+ .ppEnabledLayerNames = nullptr, // deprecated and ignored
+ .enabledExtensionCount = !extensionsSupported ? 0 : static_cast(requiredExtensions.size()),
+ .ppEnabledExtensionNames = !extensionsSupported ? nullptr : requiredExtensions.data(),
+ .pEnabledFeatures = &features
+ };
+
+ // Actually create the device
+ if (auto result = parentPhysicalDeviceHdl->GetVkPhysicalDevice().createDevice(&deviceCreateInfo, nullptr, &vkLogicalDevice); result != vk::Result::eSuccess)
+ {
+ SHVulkanDebugUtil::ReportVkError(result, "Failed to create Logical Device! ");
+ }
+ else
+ {
+ SHVulkanDebugUtil::ReportVkSuccess("Successfully created a Logical Device. ");
+ }
+
+ InitializeVMA();
+
+ // TODO: Create pipeline caches
+ // TODO: Create Descriptor pools
+ //auto poolSizes = std::array
+ //{
+ // SHDescriptorPoolSize {SHDescriptorType::COMBINED_SAMPLER, 1000} // hard coded descriptor count
+ //};
+
+ //SHDescriptorPoolParams poolParams
+ //{
+ // .poolSizes = poolSizes,
+ // .maxDescriptorSets = 1000,
+ //};
+
+ //descriptorPool.Initialize(*this, poolParams);
+ //deviceStorage.Init(*this, queueFamilyIndices.indices[static_cast(SH_QUEUE_FAMILY_ARRAY_INDEX::GRAPHICS)].value());
+ }
+
+ SHVkLogicalDevice::SHVkLogicalDevice(SHVkLogicalDevice&& rhs) noexcept
+ : vkLogicalDevice (std::move (rhs.vkLogicalDevice))
+ , queueFamilyIndices(std::move (rhs.queueFamilyIndices))
+ , vmaAllocator{rhs.vmaAllocator}
+ , nonDedicatedBestIndex {0}
+ , parentPhysicalDeviceHdl {rhs.parentPhysicalDeviceHdl}
+ {
+ rhs.vkLogicalDevice = VK_NULL_HANDLE;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Destroy the logical device.
+
+ */
+ /***************************************************************************/
+ SHVkLogicalDevice::~SHVkLogicalDevice(void) noexcept
+ {
+ //descriptorPool.Destroy(*this);
+ vkLogicalDevice.destroy(nullptr);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ TODO: Not sure what this does yet LOL. see the article on efficient
+ Vulkan renderer.
+
+ \param alignment
+
+ \return
+
+ */
+ /***************************************************************************/
+ //void SHVkLogicalDevice::UpdateBufferMemoryAlignment(vk::DeviceSize alignment) noexcept
+ //{
+ // bufferMemoryAlignment = std::max(bufferMemoryAlignment, alignment);
+ //}
+
+ void SHVkLogicalDevice::WaitIdle(void) noexcept
+ {
+ vkLogicalDevice.waitIdle();
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Finds the memory type based on the memory type.
+
+ \param typeFilter
+ \param properties
+
+ \return
+ the index to the memory type.
+
+ */
+ /***************************************************************************/
+ uint32_t SHVkLogicalDevice::FindMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties)
+ {
+ auto const& memProps = parentPhysicalDeviceHdl->GetVkPhysicalDevice().getMemoryProperties();
+ for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
+ {
+ if (typeFilter & (1 << i) && (memProps.memoryTypes[i].propertyFlags & properties) == properties)
+ return i;
+ }
+
+ SHLOGV_ERROR("Failed to find memory type. ");
+ return VkMemoryPropertyFlagBits::VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a Surface in the resource manager and returns a handle to it.
+
+ \param windowHandle
+ For the surface creation.
+
+ \return
+ A Handle to the surface.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateSurface(HWND const& windowHandle) const noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create(windowHandle, parentPhysicalDeviceHdl, GetHandle());
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a swapchain in the resource manager and returns a handle to it.
+
+ \param surfaceHdl
+ Surface for the swapchain creation.
+
+ \param width
+ Width of swapchain images
+
+ \param height
+ height of swapchain images
+
+ \param params
+ swapchain creation parameters (surface format, depth format, etc).
+
+ \return
+ Handle to swapchain created.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateSwapchain(Handle const& surfaceHdl, uint32_t width, uint32_t height, SHSwapchainParams const& params) const noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (parentPhysicalDeviceHdl, GetHandle(), surfaceHdl, width, height, params);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a command pool in the resource manager and returns a handle to it.
+
+ \param queueFamilyType
+ What queue family type this command pool comes from.
+
+ \param inResetMode
+ BUFFER_BASED for individual resets, POOL_BASED for pool based resets (
+ good for transient command buffers).
+
+ \param inTransient
+ Whether or not command buffers created from this pool are short lived
+ or not.
+
+ \return
+ A handle to the command pool created.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateCommandPool(SH_QUEUE_FAMILY_ARRAY_INDEX queueFamilyType, SH_CMD_POOL_RESET inResetMode, bool inTransient) const noexcept
+ {
+ auto newHdl = SHVkInstance::GetResourceManager().Create(GetHandle(), queueFamilyType, inResetMode, inTransient);
+ return newHdl;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a buffer in the resource manager and returns a handle to it. See
+ buffer init function for details on parameters.
+
+ \return
+ A handle to the buffer created.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateBuffer(uint32_t inSize, void* data, uint32_t srcSize, vk::BufferUsageFlags bufferUsage, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags) const noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create(inSize, data, srcSize, std::cref(vmaAllocator), bufferUsage, memUsage, allocFlags);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates an image. This ctor is mainly used for constructing images that
+ will be used as render targets.
+
+ \param w
+ Width of image.
+
+ \param h
+ Height of image.
+
+ \param levels
+ Number of mip levels.
+
+ \param format
+ Format of the image
+
+ \param usage
+ Usage bits for the image.
+
+ \param create
+ Create bits for the image.
+
+ \return
+ Handle to the newly created image.
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateImage(uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) const noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create(std::cref(vmaAllocator), w, h, levels, format, usage, create);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a shader module in the resource manager and returns a handle to
+ it.
+
+ \param binaryData
+ Binary data for the module's creation.
+
+ \param entryPoint
+ Entry point of the shader.
+
+ \param stage
+ Shader stage.
+
+ \return
+ Handle to the new shader module created.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateShaderModule(std::vector const& binaryData, std::string entryPoint, vk::ShaderStageFlagBits stage, std::string const& shaderName) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create(GetHandle(), binaryData, entryPoint, stage, shaderName);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a pipeline layout in the resource manager and returns a handle to
+ it.
+
+ \param pipelineLayoutParams
+
+ \return
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreatePipelineLayout(SHPipelineLayoutParams& pipelineLayoutParams) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (GetHandle(), pipelineLayoutParams);
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a pipeline in the resource manager and returns a handle to it.
+
+ \param pipelineLayoutHdl
+
+
+ \param state
+ \param type
+
+ \return
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreatePipeline(Handle const& pipelineLayoutHdl, SHVkPipelineState const* const state, Handle const& renderpassHdl, uint32_t subpass, SH_PIPELINE_TYPE type) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (GetHandle(), pipelineLayoutHdl, state, renderpassHdl, subpass, type);
+
+ }
+
+ Handle SHVkLogicalDevice::CreateRenderpass(std::span const vkDescriptions, std::vector const& subpasses) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (GetHandle(), vkDescriptions, subpasses);
+ }
+
+ Handle SHVkLogicalDevice::CreateRenderpass(std::span const vkDescriptions, std::span const spDescs, std::span const spDeps) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (GetHandle(), vkDescriptions, spDescs, spDeps);
+ }
+
+ Handle SHVkLogicalDevice::CreateFramebuffer(Handle const& renderpassHdl, std::vector> const& attachments, uint32_t inWidth, uint32_t inHeight) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (GetHandle(), renderpassHdl, attachments, inWidth, inHeight);
+
+ }
+
+ Handle SHVkLogicalDevice::CreateDescriptorSetLayout(std::vector const& bindings) noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create (GetHandle(), bindings);
+
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a fence in the resource manager and returns a handle to it.
+
+ \return
+ A handle to the fence created.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateFence(void) const noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create(GetHandle());
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Creates a semaphore in the resource manager and returns a handle to it.
+
+ \return
+ A handle to the semaphore created.
+
+ */
+ /***************************************************************************/
+ Handle SHVkLogicalDevice::CreateSemaphore(void) const noexcept
+ {
+ return SHVkInstance::GetResourceManager().Create(GetHandle());
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns the vulkan logical device.
+
+ \return
+ The vulkan logical device.
+
+ */
+ /***************************************************************************/
+ vk::Device const& SHVkLogicalDevice::GetVkLogicalDevice(void) const noexcept
+ {
+ return vkLogicalDevice;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns a handle to a queue given an index and given a queue family array
+ index.
+
+ \param index
+
+
+ \param queueIndex
+
+ \return
+
+ */
+ /***************************************************************************/
+ Handle const& SHVkLogicalDevice::GetQueue(SH_Q_FAM queueFamArrayIndex, uint8_t queueIndex) const
+ {
+ if (queueFamilyIndices.indices[static_cast(queueFamArrayIndex)].has_value())
+ {
+ SHQueueFamilyIndex queueFamIndex = queueFamilyIndices.indices[static_cast(queueFamArrayIndex)].value();
+ if (queueIndex < queueFamUseCounts.at(queueFamIndex))
+ {
+ return queueLibrary.at(queueFamIndex)[queueIndex];
+ }
+ else
+ throw std::invalid_argument("Attempting to index for a queue that is out of range. Try requesting for more queues on device creation or request for a valid queue index. ");
+ }
+ throw std::invalid_argument("Queue family doesn't exist. ");
+
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Getter for buffer alignment.
+
+ \return
+ The member buffer alignment variable.
+
+ */
+ /***************************************************************************/
+ //vk::DeviceSize SHVkLogicalDevice::GetBufferAlignment(void) const noexcept
+ //{
+ // return bufferMemoryAlignment;
+ //}
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Getter for VMA Allocator.
+
+ \return
+ VmaAllocator handle.
+
+ */
+ /***************************************************************************/
+ VmaAllocator const& SHVkLogicalDevice::GetVMAAllocator(void) noexcept
+ {
+ return vmaAllocator;
+ }
+
+ SHQueueFamilyIndex SHVkLogicalDevice::GetQueueFamilyIndex(SH_Q_FAM family) const noexcept
+ {
+ if (queueFamilyIndices.indices[static_cast(family)].has_value())
+ return queueFamilyIndices.indices[static_cast(family)].value();
+ else
+ return static_cast(SH_Q_FAM::INVALID);
+ }
+
+ //SHDescriptorPool const& SHLogicalDevice::GetDescriptorPool(void) const noexcept
+ //{
+ // return descriptorPool;
+ //}
+
+}
diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h
new file mode 100644
index 00000000..b8eec993
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Devices/SHVkLogicalDevice.h
@@ -0,0 +1,186 @@
+#ifndef SH_LOGICAL_DEVICE_H
+#define SH_LOGICAL_DEVICE_H
+
+#include
+#include
+#include
+#include
+#include "Graphics/SHVulkanIncludes.h"
+#include "Graphics/Devices/SHVkPhysicalDevice.h"
+#include "Graphics/Queues/SHVkQueue.h"
+#include "Resource/Handle.h"
+#include "Resource/ResourceLibrary.h"
+#include "Graphics/Swapchain/SHSwapchainParams.h"
+#include "Graphics/Commands/SHCommandPoolResetMode.h"
+#include "Graphics/Commands/SHVkCommandPool.h"
+#include "Graphics/Pipeline/SHPipelineLayoutParams.h"
+#include "Graphics/Pipeline/SHPipelineState.h"
+#include "Graphics/Pipeline/SHPipelineType.h"
+#include "vk_mem_alloc.h"
+//#include "Graphics/DescriptorSets/SHDescriptorPool.h"
+#include "Graphics/Descriptors/SHVkDescriptorSetLayout.h"
+
+namespace SHADE
+{
+ /*-----------------------------------------------------------------------*/
+ /* FORWARD DECLARATIONS */
+ /*-----------------------------------------------------------------------*/
+ class SHVkInstance;
+ class SHVkSurface;
+ class SHVkSwapchain;
+ class SHVkBuffer;
+ class SHVkImage;
+ class SHVkFence;
+ class SHVkSemaphore;
+ class SHVkShaderModule;
+ class SHVkPipelineLayout;
+ class SHVkPipeline;
+ class SHVkFramebuffer;
+ class SHVkImageView;
+ class SHShaderBlockInterface;
+
+ /***************************************************************************/
+ /*!
+
+ \struct SHQueueFamilyIndices
+ Stores Vulkan queue family indices of each queue family.
+
+ */
+ /***************************************************************************/
+ struct SHQueueFamilyIndices
+ {
+ std::array, static_cast(SH_QUEUE_FAMILY_ARRAY_INDEX::MAX)> indices;
+ };
+
+ /***************************************************************************/
+ /*!
+
+ \class SHVkLogicalDevice
+ Stores a Vulkan logical device.
+
+ */
+ /***************************************************************************/
+ class SHVkLogicalDevice : public ISelfHandle
+ {
+ private:
+
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER VARIABLES */
+ /*-----------------------------------------------------------------------*/
+ //vk::DeviceSize bufferMemoryAlignment{ 64 };
+
+ //! Vulkan handle
+ vk::Device vkLogicalDevice;
+
+ //! Handle to the physical device this logical device was created from
+ Handle parentPhysicalDeviceHdl;
+
+ //! The array of queue family array indices
+ SHQueueFamilyIndices queueFamilyIndices{};
+
+ //! If the user chooses a non-dedicated queue, the device will initialize this to the best queue family index
+ uint32_t nonDedicatedBestIndex;
+
+ //std::vector> queueHdls;
+ std::unordered_map>> queueLibrary;
+
+ //! For use on tracking how many queues are retrieved
+ std::unordered_map queueFamUseCounts;
+
+ //! main VMA allocator of this instance
+ VmaAllocator vmaAllocator;
+
+ /*-----------------------------------------------------------------------*/
+ /* PRIVATE MEMBER FUNCTIONS */
+ /*-----------------------------------------------------------------------*/
+ void InitializeVMA (void) noexcept;
+ void InitializeQueues (std::initializer_list queueCreateParams) noexcept;
+
+ public:
+ /*-----------------------------------------------------------------------*/
+ /* CTOR AND DTOR */
+ /*-----------------------------------------------------------------------*/
+ SHVkLogicalDevice (std::initializer_list queueCreateParams, Handle const& inPhysicalDeviceHdl) noexcept;
+ SHVkLogicalDevice (SHVkLogicalDevice const& rhs) = default;
+ SHVkLogicalDevice (SHVkLogicalDevice&& rhs) noexcept;
+ ~SHVkLogicalDevice (void) noexcept;
+
+ SHVkLogicalDevice& operator= (SHVkLogicalDevice const& rhs) noexcept = default;
+ SHVkLogicalDevice& operator= (SHVkLogicalDevice&& rhs) noexcept = default;
+
+ /*-----------------------------------------------------------------------*/
+ /* PUBLIC MEMBER VARIABLES */
+ /*-----------------------------------------------------------------------*/
+ void WaitIdle (void) noexcept;
+ uint32_t FindMemoryType (uint32_t typeFilter, vk::MemoryPropertyFlags properties);
+
+ Handle CreateSurface (HWND const& windowHandle) const noexcept;
+ Handle CreateSwapchain (
+ Handle const& surfaceHdl,
+ uint32_t width,
+ uint32_t height,
+ SHSwapchainParams const& params
+ ) const noexcept;
+
+ Handle CreateCommandPool (
+ SH_QUEUE_FAMILY_ARRAY_INDEX queueFamilyType,
+ SH_CMD_POOL_RESET inResetMode,
+ bool inTransient
+ ) const noexcept;
+
+ Handle CreateBuffer (
+ uint32_t inSize,
+ void* data,
+ uint32_t srcSize,
+ vk::BufferUsageFlags bufferUsage,
+ VmaMemoryUsage memUsage,
+ VmaAllocationCreateFlags allocFlags
+ ) const noexcept;
+
+ Handle CreateImage (
+ uint32_t w,
+ uint32_t h,
+ uint8_t levels,
+ vk::Format format,
+ vk::ImageUsageFlags usage,
+ vk::ImageCreateFlags create
+ ) const noexcept;
+
+ Handle CreateShaderModule (
+ std::vector const& binaryData,
+ std::string entryPoint,
+ vk::ShaderStageFlagBits stage,
+ std::string const& shaderName
+ ) noexcept;
+
+ Handle CreatePipeline (
+ Handle const& pipelineLayoutHdl,
+ SHVkPipelineState const* const state,
+ Handle const& renderpassHdl,
+ uint32_t subpass,
+ SH_PIPELINE_TYPE type
+ ) noexcept;
+
+ Handle CreateRenderpass (std::span const vkDescriptions, std::vector const& subpasses) noexcept;
+ Handle CreateRenderpass (std::span const vkDescriptions, std::span const spDescs, std::span const spDeps) noexcept;
+ Handle CreateFramebuffer (Handle const& renderpassHdl, std::vector> const& attachments, uint32_t inWidth, uint32_t inHeight) noexcept;
+ Handle CreateDescriptorSetLayout (std::vector const& bindings) noexcept;
+ Handle CreatePipelineLayout (SHPipelineLayoutParams& pipelineLayoutParams) noexcept;
+ Handle CreateFence (void) const noexcept;
+ Handle CreateSemaphore (void) const noexcept;
+
+ /*-----------------------------------------------------------------------*/
+ /* SETTERS AND GETTERS */
+ /*-----------------------------------------------------------------------*/
+ vk::Device const& GetVkLogicalDevice (void) const noexcept;
+ Handle const& GetQueue (SH_Q_FAM queueFamArrayIndex, uint8_t queueIndex) const;
+ VmaAllocator const& GetVMAAllocator (void) noexcept;
+ SHQueueFamilyIndex GetQueueFamilyIndex (SH_Q_FAM family) const noexcept;
+ //vk::DeviceSize GetBufferAlignment (void) const noexcept;
+ //SHDescriptorPool const& GetDescriptorPool(void) const noexcept;
+
+ friend class SHVkInstance;
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDevice.cpp b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDevice.cpp
new file mode 100644
index 00000000..efe624a5
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDevice.cpp
@@ -0,0 +1,96 @@
+#include "SHPch.h"
+#include "SHVkPhysicalDevice.h"
+#include
+#include
+#include
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ SHVkPhysicalDevice::SHVkPhysicalDevice (vk::PhysicalDevice inDevice) noexcept
+ : vkPhysicalDevice{nullptr}
+ , deviceProperties{ }
+ , memoryProperties{ }
+ , queueFamilyProperties{ }
+ {
+ if (!inDevice)
+ {
+ SHLOG_ERROR("Trying to initialize a physical device but device passed in is nullptr! ");
+ return;
+ }
+
+ vkPhysicalDevice = inDevice;
+
+ vkPhysicalDevice.getProperties(&deviceProperties);
+ vkPhysicalDevice.getMemoryProperties(&memoryProperties);
+
+ queueFamilyProperties = vkPhysicalDevice.getQueueFamilyProperties();
+
+ deviceFeatures = vkPhysicalDevice.getFeatures();
+ }
+
+ SHVkPhysicalDevice::SHVkPhysicalDevice(SHVkPhysicalDevice&& rhs) noexcept
+ : deviceProperties {rhs.deviceProperties}
+ , memoryProperties{ rhs.memoryProperties }
+ , deviceFeatures{ rhs.deviceFeatures }
+ , queueFamilyProperties{ rhs.queueFamilyProperties }
+ , vkPhysicalDevice{ std::move (rhs.vkPhysicalDevice)}
+ {
+
+ }
+
+ vk::PhysicalDeviceProperties const& SHVkPhysicalDevice::GetDeviceProperties(void) const noexcept
+{
+ return deviceProperties;
+ }
+
+ std::vector const& SHVkPhysicalDevice::GetQueueFamilyProperties(void) const noexcept
+ {
+ return queueFamilyProperties;
+ }
+
+ vk::PhysicalDeviceMemoryProperties const& SHVkPhysicalDevice::GetMemoryProperties(void) const noexcept
+ {
+ return memoryProperties;
+ }
+
+ vk::DeviceSize SHVkPhysicalDevice::vkGetMaxHeapSize(void) const noexcept
+ {
+ // Get array of heaps from the properties
+ vk::MemoryHeap const* heapPtr = memoryProperties.memoryHeaps;
+
+ if (heapPtr == nullptr)
+ return 0;
+
+ // For looping through heaps
+ std::vector heapContainer(heapPtr, heapPtr + memoryProperties.memoryHeapCount);
+
+ // Calculate biggest heap size among heaps of the device
+ VkDeviceSize biggestDeviceHeapSize = 0;
+ for (auto const& heap : heapContainer)
+ {
+ if (heap.size > biggestDeviceHeapSize)
+ biggestDeviceHeapSize = heap.size;
+ }
+
+ return biggestDeviceHeapSize;
+ }
+
+ vk::PhysicalDevice SHVkPhysicalDevice::GetVkPhysicalDevice(void) const noexcept
+ {
+ return vkPhysicalDevice;
+ }
+
+ bool SHVkPhysicalDevice::ExtensionsSupported(std::vector const& requiredExtensions) const noexcept
+ {
+ std::vector availableExtensions{};
+ availableExtensions = vkPhysicalDevice.enumerateDeviceExtensionProperties();
+
+ std::set missingExtensions(requiredExtensions.begin(), requiredExtensions.end());
+ for (auto const& extension : availableExtensions)
+ missingExtensions.erase(extension.extensionName);
+
+ return missingExtensions.empty();
+ }
+
+}
diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDevice.h b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDevice.h
new file mode 100644
index 00000000..c47ab9a9
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDevice.h
@@ -0,0 +1,43 @@
+#ifndef SH_PHYSICAL_DEVICE_H
+#define SH_PHYSICAL_DEVICE_H
+
+#include
+#include
+#include "Graphics/Debugging/SHVulkanDebugUtil.h"
+
+namespace SHADE
+{
+ enum class SH_PHYSICAL_DEVICE_TYPE
+ {
+ BEST,
+ };
+
+ class SHVkPhysicalDevice
+ {
+ private:
+ vk::PhysicalDevice vkPhysicalDevice;
+
+ vk::PhysicalDeviceProperties deviceProperties{};
+ vk::PhysicalDeviceMemoryProperties memoryProperties{};
+ vk::PhysicalDeviceFeatures deviceFeatures{};
+ std::vector queueFamilyProperties{};
+
+ public:
+ SHVkPhysicalDevice(void) = delete;
+ SHVkPhysicalDevice(vk::PhysicalDevice inDevice) noexcept;
+ SHVkPhysicalDevice(SHVkPhysicalDevice const& rhs) noexcept = default;
+ SHVkPhysicalDevice(SHVkPhysicalDevice&& rhs) noexcept;
+
+ SHVkPhysicalDevice& operator= (SHVkPhysicalDevice const& rhs) noexcept = default;
+ SHVkPhysicalDevice& operator= (SHVkPhysicalDevice&& rhs) noexcept = default;
+
+ vk::PhysicalDeviceProperties const& GetDeviceProperties(void) const noexcept;
+ std::vector const& GetQueueFamilyProperties (void) const noexcept;
+ vk::PhysicalDeviceMemoryProperties const& GetMemoryProperties(void) const noexcept;
+ vk::DeviceSize vkGetMaxHeapSize(void) const noexcept;
+ vk::PhysicalDevice GetVkPhysicalDevice(void) const noexcept;
+ bool ExtensionsSupported(std::vector const& requiredExtensions) const noexcept;
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDeviceLibrary.cpp b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDeviceLibrary.cpp
new file mode 100644
index 00000000..511af8fc
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDeviceLibrary.cpp
@@ -0,0 +1,189 @@
+#include "SHPch.h"
+#include
+#include
+#include "SHVkPhysicalDeviceLibrary.h"
+#include "Graphics/Instance/SHVkInstance.h"
+#include "Tools/SHLogger.h"
+
+namespace SHADE
+{
+ std::vector SHVkPhysicalDeviceLibrary::physicalDevices;
+ bool SHVkPhysicalDeviceLibrary::queried = false;
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns a c string that represents the physical device type passed in.
+
+ \param type
+ Type of the physical device.
+
+ \return
+ Returns a c string of the type.
+
+ */
+ /***************************************************************************/
+ char const* const SHVkPhysicalDeviceLibrary::GetDeviceTypeName(vk::PhysicalDeviceType type) noexcept
+ {
+ switch (type)
+ {
+ case vk::PhysicalDeviceType::eDiscreteGpu:
+ return "Discrete Device";
+ case vk::PhysicalDeviceType::eIntegratedGpu:
+ return "Integrated Device";
+ default:
+ return "Unsupported Device";
+ }
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Fills up physical device container.
+
+ \param renderingInstance
+ Rendering Instance required to get physical devices.
+
+ */
+ /***************************************************************************/
+ void SHVkPhysicalDeviceLibrary::QueryPhysicalDevices(bool printInfo /*= true*/) noexcept
+ {
+ physicalDevices = SHVkInstance::GetVkInstance().enumeratePhysicalDevices();
+ if (physicalDevices.size() == 0)
+ {
+ SHLOGV_ERROR("Failed to detect physical devices. ");
+ }
+
+ if (printInfo)
+ PrintAllPhysicalDevices();
+
+ // Set flag to indicate devices are queried and saved
+ queried = true;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Attempts to get physical devices from the device library based on the
+ type passed in.
+
+ \param gpuType
+ The type of physical device we want to get.
+
+ \return
+ A subset of the queried physical devices that are of type gpuType.
+
+ */
+ /***************************************************************************/
+ //std::vector> SHPhysicalDeviceLibrary::GetDevicesByType(VkPhysicalDeviceType gpuType, SHVulkanAPIVersion apiVersion) noexcept
+ //{
+ // // If the application has not queried a physical device yet, don't return anything
+ // if (!queried)
+ // {
+ // SHUtil::ReportWarning("Cannot get any physical devices. Query the physical devices first. ");
+ // return {};
+ // }
+
+ // // Prep a container to store devices
+ // std::vector> devices;
+
+ // // For all the devices queried, find ones that match the type passed in
+ // for (auto const& device : physicalDevices)
+ // {
+ // // Check for API version and device type
+ // if (device->GetDeviceProperties().apiVersion == static_cast(apiVersion) && device->GetDeviceProperties().deviceType == gpuType)
+ // {
+ // devices.push_back(device);
+ // }
+ // }
+
+ // // Return list of devices (can be empty, meaning no devices have the type passed in)
+ // return devices;
+ //}
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Of all the discrete devices, get the best one. Right now, this just
+ checks the physical device heap size and uses the one with the highest.
+
+ \param apiVersion
+ The API version that the device has the support.
+
+ \return
+ The best physical device.
+
+ */
+ /***************************************************************************/
+ vk::PhysicalDevice SHVkPhysicalDeviceLibrary::GetBestDevice(uint32_t apiVersion /*= SHVulkanAPIVersion::V_1_2*/)
+ {
+ if (!queried || physicalDevices.empty())
+ return nullptr;
+
+ vk::PhysicalDevice bestDevice = nullptr;
+ VkDeviceSize biggestHeapSize = 0;
+ for (auto const& device : physicalDevices)
+ {
+ // Check for API version and device type. Ignore if queried device doesn't support version passed in
+ if (device.getProperties().apiVersion < static_cast(apiVersion))
+ continue;
+
+ //VkDeviceSize biggestDeviceHeapSize = device.getMemoryProperties().;
+
+ // Get array of heaps from the properties
+ vk::MemoryHeap const* heapPtr = device.getMemoryProperties().memoryHeaps;
+
+ if (heapPtr == nullptr)
+ continue;
+
+ // For looping through heaps
+ std::vector heapContainer(heapPtr, heapPtr + device.getMemoryProperties().memoryHeapCount);
+
+ // Calculate biggest heap size among heaps of the device
+ VkDeviceSize biggestDeviceHeapSize = 0;
+ for (auto const& heap : heapContainer)
+ {
+ if (heap.size > biggestDeviceHeapSize)
+ biggestDeviceHeapSize = heap.size;
+ }
+
+ // If the device contains a heap that is bigger than other cards calculated before
+ if (biggestDeviceHeapSize > biggestHeapSize)
+ {
+ // Make that device the best device
+ biggestHeapSize = biggestDeviceHeapSize;
+ bestDevice = device;
+ }
+ }
+
+ return bestDevice;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Prints all of the physical devices queried. Private function. For
+ internal use only.
+
+ */
+ /***************************************************************************/
+ void SHVkPhysicalDeviceLibrary::PrintAllPhysicalDevices(void) noexcept
+ {
+ if (physicalDevices.empty())
+ {
+ SHLOG_ERROR("No physical devices queried. Nothing to print. ");
+ return;
+ }
+
+ SHLOG_ERROR("Successfully queried Physical Devices:");
+ for (auto const& device : physicalDevices)
+ {
+ SHLOG_ERROR(std::string_view (std::string("\t-") + GetDeviceTypeName(device.getProperties().deviceType) + device.getProperties().deviceName.operator std::string()));
+ }
+ }
+}
diff --git a/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDeviceLibrary.h b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDeviceLibrary.h
new file mode 100644
index 00000000..79516987
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Devices/SHVkPhysicalDeviceLibrary.h
@@ -0,0 +1,33 @@
+#ifndef SH_PHYSICAL_DEVICE_LIBRARY_H
+#define SH_PHYSICAL_DEVICE_LIBRARY_H
+
+#include
+#include
+#include "Graphics/Debugging/SHVulkanDebugUtil.h"
+
+namespace SHADE
+{
+ // Rendering instance forward declaration
+ class SHVkInstance;
+
+ class SHVkPhysicalDeviceLibrary
+ {
+ private:
+ //! Stores all the available GPUs (discrete + non-discrete)
+ static std::vector physicalDevices;
+
+ //! If devices have been queried, this will be false
+ static bool queried;
+
+ static char const* const GetDeviceTypeName(vk::PhysicalDeviceType type) noexcept;
+ static void PrintAllPhysicalDevices(void) noexcept;
+
+ public:
+ static void QueryPhysicalDevices(bool printInfo) noexcept;
+ //static std::vector> GetDevicesByType(VkPhysicalDeviceType gpuType, SHVulkanAPIVersion apiVersion = SHVulkanAPIVersion::V_1_2) noexcept;
+ static vk::PhysicalDevice GetBestDevice(uint32_t apiVersion = VK_API_VERSION_1_3);
+
+ };
+}
+
+#endif
diff --git a/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.cpp b/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.cpp
new file mode 100644
index 00000000..1386134f
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.cpp
@@ -0,0 +1,163 @@
+#include "SHPch.h"
+#include "SHVkFramebuffer.h"
+#include "Graphics/Images/SHVkImageView.h"
+#include "Graphics/Images/SHVkImage.h"
+#include "Graphics/Renderpass/SHVkRenderpass.h"
+#include "Tools/SHLogger.h"
+#include "Graphics/Devices/SHVkLogicalDevice.h"
+
+namespace SHADE
+{
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Non-default ctor. Initializes a framebuffer with attachments.
+
+ \param logicalDevice
+ Required for framebuffer creation and destruction
+
+ \param renderpass
+ Renderpass that the framebuffer will be compatible with.
+
+ \param attachments
+ Attachments to be attached to the framebuffer.
+
+ \return
+
+ */
+ /***************************************************************************/
+ SHVkFramebuffer::SHVkFramebuffer(Handle const& inLogicalDeviceHdl, Handle const& renderpassHdl, std::vector> const& attachments, uint32_t inWidth, uint32_t inHeight) noexcept
+ : logicalDeviceHdl{inLogicalDeviceHdl}
+ , width {inWidth}
+ , height {inHeight}
+ {
+ // TODO: Verify if a check needs to be done to see if image dimensions are the same as the dimensions passed in
+
+ for (auto& attachment : attachments)
+ {
+ // Not sure if its an error to pass in diff dimension images.
+ if (attachment->GetParentImage()->GetWidth() != (*attachments.begin())->GetParentImage()->GetWidth() || attachment->GetParentImage()->GetHeight() != (*attachments.begin())->GetParentImage()->GetHeight())
+ {
+ SHLOG_ERROR("Dimensions of images not same as each other. Cannot create framebuffer.");
+ return;
+ }
+ }
+
+ std::vector vkAttachments(attachments.size());
+
+ uint32_t i = 0;
+ for(auto const& attachment : attachments)
+ {
+ vkAttachments[i] = attachment->GetImageView();
+ ++i;
+ }
+
+ vk::FramebufferCreateInfo createInfo
+ {
+ .renderPass = renderpassHdl->GetVkRenderpass(),
+ .attachmentCount = static_cast(vkAttachments.size()),
+ .pAttachments = vkAttachments.data(),
+ .width = width,
+ .height = height,
+ .layers = 1 // TODO: Find out why this is 1
+ };
+
+ if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createFramebuffer(&createInfo, nullptr, &vkFramebuffer); result != vk::Result::eSuccess)
+ {
+ SHVulkanDebugUtil::ReportVkError(result, "Failed to create framebuffer. ");
+ return;
+ }
+ else
+ {
+ SHVulkanDebugUtil::ReportVkSuccess("Successfully created framebuffer. ");
+ }
+ }
+
+ SHVkFramebuffer::SHVkFramebuffer(SHVkFramebuffer&& rhs) noexcept
+ : vkFramebuffer{rhs.vkFramebuffer}
+ , logicalDeviceHdl {rhs.logicalDeviceHdl}
+ , width {rhs.width}
+ , height {rhs.height}
+ {
+ rhs.vkFramebuffer = VK_NULL_HANDLE;
+ }
+
+ SHVkFramebuffer& SHVkFramebuffer::operator=(SHVkFramebuffer&& rhs) noexcept
+ {
+ if (&rhs == this)
+ return *this;
+
+ vkFramebuffer = rhs.vkFramebuffer;
+ logicalDeviceHdl = rhs.logicalDeviceHdl;
+ width = rhs.width;
+ height = rhs.height;
+
+ rhs.vkFramebuffer = VK_NULL_HANDLE;
+
+ return *this;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Getter for the vulkan framebuffer handle.
+
+ \return
+ The framebuffer handle.
+
+ */
+ /***************************************************************************/
+ vk::Framebuffer SHVkFramebuffer::GetVkFramebuffer(void) const noexcept
+ {
+ return vkFramebuffer;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns the framebuffer width.
+
+ \return
+ The framebuffer width.
+
+ */
+ /***************************************************************************/
+ uint32_t SHVkFramebuffer::GetWidth(void) const noexcept
+ {
+ return width;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Returns the framebuffer height.
+
+ \return
+ The framebuffer height.
+
+ */
+ /***************************************************************************/
+ uint32_t SHVkFramebuffer::GetHeight(void) const noexcept
+ {
+ return height;
+ }
+
+ /***************************************************************************/
+ /*!
+
+ \brief
+ Destroys the framebuffer.
+
+ */
+ /***************************************************************************/
+ SHVkFramebuffer::~SHVkFramebuffer(void) noexcept
+ {
+ if (vkFramebuffer)
+ logicalDeviceHdl->GetVkLogicalDevice().destroyFramebuffer(vkFramebuffer, nullptr);
+ }
+
+}
\ No newline at end of file
diff --git a/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h b/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h
new file mode 100644
index 00000000..fa9161e8
--- /dev/null
+++ b/SHADE_Engine/src/Graphics/Framebuffer/SHVkFramebuffer.h
@@ -0,0 +1,50 @@
+#ifndef SH_VK_FRAMEBUFFER_H
+#define SH_VK_FRAMEBUFFER_H
+
+#include "Graphics/SHVulkanIncludes.h"
+#include "Resource/Handle.h"
+#include