Merge branch 'main' into SP3-14-FileSystem

This commit is contained in:
Xiao Qi 2022-09-13 11:36:11 +08:00
commit 7b55f7fe3b
168 changed files with 29073 additions and 28 deletions

View File

@ -60,7 +60,7 @@
<PrecompiledHeaderFile>SBpch.h</PrecompiledHeaderFile>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;..\SHADE_Engine\src;src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\Dependencies\spdlog\include;..\SHADE_Engine\src;src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<MinimalRebuild>false</MinimalRebuild>
@ -71,7 +71,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EntryPointSymbol>WinMainCRTStartup</EntryPointSymbol>
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -80,7 +80,7 @@
<PrecompiledHeaderFile>SBpch.h</PrecompiledHeaderFile>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>_RELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.;..\SHADE_Engine\src;src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\Dependencies\spdlog\include;..\SHADE_Engine\src;src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@ -94,16 +94,20 @@
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<EntryPointSymbol>WinMainCRTStartup</EntryPointSymbol>
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="src\Application\SBApplication.h" />
<ClInclude Include="src\SBpch.h" />
<ClInclude Include="src\Scenes\SBTestScene.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Application\SBApplication.cpp" />
<ClCompile Include="src\SBpch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\Scenes\SBTestScene.cpp" />
<ClCompile Include="src\WinMain.cpp" />
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Application">
<UniqueIdentifier>{D9DE78AF-4594-F1A4-CE88-EB7B3A3DE8A8}</UniqueIdentifier>
</Filter>
<Filter Include="Scenes">
<UniqueIdentifier>{86EEB3D0-7290-DEA6-5B4B-F2FA478C65F7}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Application\SBApplication.h">
<Filter>Application</Filter>
</ClInclude>
<ClInclude Include="src\SBpch.h" />
<ClInclude Include="src\Scenes\SBTestScene.h">
<Filter>Scenes</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Application\SBApplication.cpp">
<Filter>Application</Filter>
</ClCompile>
<ClCompile Include="src\SBpch.cpp" />
<ClCompile Include="src\Scenes\SBTestScene.cpp">
<Filter>Scenes</Filter>
</ClCompile>
<ClCompile Include="src\WinMain.cpp" />
</ItemGroup>
</Project>

View File

@ -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
@ -21,8 +21,7 @@ project "SHADE_Application"
includedirs
{
"%{IncludeDir.GLFW}",
"%{IncludeDir.GLAD}",
"%{IncludeDir.spdlog}/include",
"../SHADE_Engine/src",
"src"
}

View File

@ -0,0 +1,52 @@
#include "SBpch.h"
#include "SBApplication.h"
#ifdef SHEDITOR
#include "Editor/SHEditor.h"
#include "Scenes/SBEditorScene.h"
#endif // SHEDITOR
#include <chrono>
#include <ratio>
#include <ctime>
namespace Sandbox
{
bool paused = false;
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
#endif
}
void SBApplication::Update(void)
{
//TODO: Change true to window is open
while (!window.WindowShouldClose())
{
#ifdef SHEDITOR
#else
#endif
}
}
void SBApplication::Exit(void)
{
#ifdef SHEDITOR
#else
#endif
}
}

View File

@ -0,0 +1,27 @@
#ifndef SB_APPLICATION_H
#define SB_APPLICATION_H
#include <Graphics/Windowing/SHWindow.h>
//using namespace SHADE;
namespace Sandbox
{
class SBApplication
{
private:
SHADE::SHWindow window;
//SHAppConfig config;
public:
SBApplication() = default;
void Initialize(_In_ HINSTANCE /*hInstance*/,
_In_opt_ HINSTANCE /*hPrevInstance*/,
_In_ LPWSTR /*lpCmdLine*/,
_In_ INT /*nCmdShow*/);
void Update(void);
void Exit(void);
private:
//std::common_type_t<double> collisionSystemTime, physicsSystemTime, transformSystemTime, audioSystemTime, renderSystemTime, gameTime;
//std::chrono::high_resolution_clock::time_point audioStart, audioEnd, transformStart, transformEnd, physicsStart, physicsEnd, collisionStart, collisionEnd, renderStart, renderEnd, gameStart, gameEnd;
};
}
#endif

View File

@ -0,0 +1,48 @@
#include "SBpch.h"
#include "SBTestScene.h"
using namespace SHADE;
namespace Sandbox
{
void SBTestScene::WindowFocusFunc([[maybe_unused]]void* window, int focused)
{
if(focused)
{
}
else
{
}
}
void SBTestScene::Load()
{
}
void SBTestScene::Init()
{
}
void SBTestScene::Update(float dt)
{
(void)dt;
}
void SBTestScene::Render()
{
}
void SBTestScene::Unload()
{
}
void SBTestScene::Free()
{
//SHSerialization::SerializeScene("resources/scenes/Scene01.SHADE");
}
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "Scene/SHScene.h"
#include "Scene/SHSceneManager.h"
namespace Sandbox
{
class SBTestScene : public SHADE::SHScene
{
private:
EntityID camera;
public:
virtual void Load();
virtual void Init();
virtual void Update(float dt);
virtual void Render();
virtual void Free();
virtual void Unload();
//TODO: Change to new window DO IT IN CPP TOO
void WindowFocusFunc(void* window, int focused);
SBTestScene(void) = default;
};
}

View File

@ -1,7 +1,51 @@
#include "SBpch.h"
#include <Engine/SHEngine.h>
#include <Tools/SHLogger.h>
#include <Tools/SHExceptionHandler.h>
#include "Application/SBApplication.h"
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR lpCmdLine, INT nCmdShow)
#define _CRTDBG_MAP_ALLOC
#include <cstdlib>
#include <crtdbg.h>
#ifdef _DEBUG
#define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
// Replace _NORMAL_BLOCK with _CLIENT_BLOCK if you want the
// allocations to be of _CLIENT_BLOCK type
#else
#define DBG_NEW new
#endif
INT WINAPI wWinMain
(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ INT nCmdShow
)
{
const SHADE::SHLogger::Config LOGGER_CONFIG{ .directoryPath = "./logs/" };
SHADE::SHLogger::Initialise(LOGGER_CONFIG);
try
{
#ifndef SHEDITOR
//ShowWindow(::GetConsoleWindow(), SW_HIDE);
#endif
SHLOG_INFO("sup")
SHADE::SHEngine::Run<Sandbox::SBApplication>(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
}
catch(...)
{
SHADE::SHExceptionHandler::HandleException(std::current_exception());
SHADE::SHLogger::Shutdown();
}
SHADE::SHLogger::Shutdown();
return 0;
}

View File

@ -57,8 +57,8 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SHpch.h</PrecompiledHeaderFile>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>_LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>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)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;NOMINMAX;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>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)</AdditionalIncludeDirectories>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<Optimization>Disabled</Optimization>
<MinimalRebuild>false</MinimalRebuild>
@ -71,7 +71,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<AdditionalDependencies>vulkan-1.lib;assimp-vc142-mtd.lib;ktxd.lib;librttr_core_d.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>vulkan-1.lib;shaderc_shared.lib;assimp-vc142-mtd.lib;ktxd.lib;librttr_core_d.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>libs;$(VULKAN_SDK)\Lib;..\Dependencies\assimp\lib\Debug;..\Dependencies\assimp\lib\Release;..\Dependencies\RTTR\lib;..\Dependencies\ktx\lib\Debug;..\Dependencies\ktx\lib\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
@ -80,8 +80,8 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>SHpch.h</PrecompiledHeaderFile>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>_LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;_RELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>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)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_LIB;_GLFW_INCLUDE_NONE;MSDFGEN_USE_CPP11;NOMINMAX;_RELEASE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>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)</AdditionalIncludeDirectories>
<Optimization>Full</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
@ -97,21 +97,169 @@
<OptimizeReferences>true</OptimizeReferences>
</Link>
<Lib>
<AdditionalDependencies>vulkan-1.lib;assimp-vc142-mt.lib;ktx.lib;librttr_core.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>vulkan-1.lib;shaderc_shared.lib;assimp-vc142-mt.lib;ktx.lib;librttr_core.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>libs;$(VULKAN_SDK)\Lib;..\Dependencies\assimp\lib\Debug;..\Dependencies\assimp\lib\Release;..\Dependencies\RTTR\lib;..\Dependencies\ktx\lib\Debug;..\Dependencies\ktx\lib\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="src\Engine\ECS_Base\Components\SHComponent.h" />
<ClInclude Include="src\Engine\ECS_Base\Components\SHComponentGroup.h" />
<ClInclude Include="src\Engine\ECS_Base\Entity\SHEntity.h" />
<ClInclude Include="src\Engine\ECS_Base\General\SHFamily.h" />
<ClInclude Include="src\Engine\ECS_Base\General\SHHandleGenerator.h" />
<ClInclude Include="src\Engine\ECS_Base\General\SHSparseBase.h" />
<ClInclude Include="src\Engine\ECS_Base\General\SHSparseSet.h" />
<ClInclude Include="src\Engine\ECS_Base\General\SHSparseSetContainer.h" />
<ClInclude Include="src\Engine\ECS_Base\SHECSMacros.h" />
<ClInclude Include="src\Engine\ECS_Base\System\SHComponentManager.h" />
<ClInclude Include="src\Engine\ECS_Base\System\SHEntityManager.h" />
<ClInclude Include="src\Engine\ECS_Base\System\SHSystem.h" />
<ClInclude Include="src\Engine\ECS_Base\System\SHSystemManager.h" />
<ClInclude Include="src\Engine\SHEngine.h" />
<ClInclude Include="src\Filesystem\SHFileSystem.h" />
<ClInclude Include="src\Graphics\Buffers\SHVkBuffer.h" />
<ClInclude Include="src\Graphics\Commands\SHCommandPoolResetMode.h" />
<ClInclude Include="src\Graphics\Commands\SHVkCommandBuffer.h" />
<ClInclude Include="src\Graphics\Commands\SHVkCommandPool.h" />
<ClInclude Include="src\Graphics\Debugging\SHValidationLayersQuery.h" />
<ClInclude Include="src\Graphics\Debugging\SHVkDebugMessenger.h" />
<ClInclude Include="src\Graphics\Debugging\SHVulkanDebugUtil.h" />
<ClInclude Include="src\Graphics\Descriptors\SHDescriptorPoolManager.h" />
<ClInclude Include="src\Graphics\Descriptors\SHDescriptorPoolStorage.h" />
<ClInclude Include="src\Graphics\Descriptors\SHVkDescriptorPool.h" />
<ClInclude Include="src\Graphics\Descriptors\SHVkDescriptorSetGroup.h" />
<ClInclude Include="src\Graphics\Descriptors\SHVkDescriptorSetLayout.h" />
<ClInclude Include="src\Graphics\Devices\SHVkLogicalDevice.h" />
<ClInclude Include="src\Graphics\Devices\SHVkPhysicalDevice.h" />
<ClInclude Include="src\Graphics\Devices\SHVkPhysicalDeviceLibrary.h" />
<ClInclude Include="src\Graphics\Framebuffer\SHVkFramebuffer.h" />
<ClInclude Include="src\Graphics\Images\SHImageViewDetails.h" />
<ClInclude Include="src\Graphics\Images\SHVkImage.h" />
<ClInclude Include="src\Graphics\Images\SHVkImageView.h" />
<ClInclude Include="src\Graphics\Instance\SHVkInstance.h" />
<ClInclude Include="src\Graphics\MiddleEnd\Interface\SHGraphicsSystem.h" />
<ClInclude Include="src\Graphics\MiddleEnd\Interface\SHRenderTarget.h" />
<ClInclude Include="src\Graphics\MiddleEnd\PerFrame\SHPerFrameData.h" />
<ClInclude Include="src\Graphics\MiddleEnd\PerFrame\SHRenderContext.h" />
<ClInclude Include="src\Graphics\MiddleEnd\Shaders\SHShaderModuleLibrary.h" />
<ClInclude Include="src\Graphics\MiddleEnd\Shaders\SHShaderSourceLibrary.h" />
<ClInclude Include="src\Graphics\MiddleEnd\Shaders\SHShaderType.h" />
<ClInclude Include="src\Graphics\Pipeline\SHPipelineLayoutParams.h" />
<ClInclude Include="src\Graphics\Pipeline\SHPipelineState.h" />
<ClInclude Include="src\Graphics\Pipeline\SHPipelineType.h" />
<ClInclude Include="src\Graphics\Pipeline\SHPushConstantInterface.h" />
<ClInclude Include="src\Graphics\Pipeline\SHVkPipeline.h" />
<ClInclude Include="src\Graphics\Pipeline\SHVkPipelineLayout.h" />
<ClInclude Include="src\Graphics\Queues\SHVkQueue.h" />
<ClInclude Include="src\Graphics\RenderGraph\SHRenderGraph.h" />
<ClInclude Include="src\Graphics\Renderpass\SHVkAttachDescGen.h" />
<ClInclude Include="src\Graphics\Renderpass\SHVkAttachment.h" />
<ClInclude Include="src\Graphics\Renderpass\SHVkRenderpass.h" />
<ClInclude Include="src\Graphics\Renderpass\SHVkSubpassDescription.h" />
<ClInclude Include="src\Graphics\Renderpass\SHVkSubpassParams.h" />
<ClInclude Include="src\Graphics\SHVkUtil.h" />
<ClInclude Include="src\Graphics\SHVulkanDefines.h" />
<ClInclude Include="src\Graphics\SHVulkanIncludes.h" />
<ClInclude Include="src\Graphics\Shaders\BlockInterface\SHShaderBlockInterface.h" />
<ClInclude Include="src\Graphics\Shaders\SHShaderReflected.h" />
<ClInclude Include="src\Graphics\Shaders\SHVkShaderModule.h" />
<ClInclude Include="src\Graphics\Shaders\spirv-reflect\spirv_reflect.h" />
<ClInclude Include="src\Graphics\Swapchain\SHSwapchainParams.h" />
<ClInclude Include="src\Graphics\Swapchain\SHVkSwapchain.h" />
<ClInclude Include="src\Graphics\Synchronization\SHVkFence.h" />
<ClInclude Include="src\Graphics\Synchronization\SHVkSemaphore.h" />
<ClInclude Include="src\Graphics\VertexDescriptors\SHVertexAttribute.h" />
<ClInclude Include="src\Graphics\Windowing\SHWindow.h" />
<ClInclude Include="src\Graphics\Windowing\SHWindowMap.h" />
<ClInclude Include="src\Graphics\Windowing\Surface\SHVkSurface.h" />
<ClInclude Include="src\Math\SHMath.h" />
<ClInclude Include="src\Math\SHMathHelpers.h" />
<ClInclude Include="src\Math\SHMatrix.h" />
<ClInclude Include="src\Math\SHQuaternion.h" />
<ClInclude Include="src\Math\Vector\SHVec2.h" />
<ClInclude Include="src\Math\Vector\SHVec3.h" />
<ClInclude Include="src\Math\Vector\SHVec4.h" />
<ClInclude Include="src\Meta\SHIsDetected.h" />
<ClInclude Include="src\Resource\Handle.h" />
<ClInclude Include="src\Resource\ResourceLibrary.h" />
<ClInclude Include="src\Resource\SparseSet.h" />
<ClInclude Include="src\SHpch.h" />
<ClInclude Include="src\Scene\SHScene.h" />
<ClInclude Include="src\Scene\SHSceneManager.h" />
<ClInclude Include="src\Tools\SHException.h" />
<ClInclude Include="src\Tools\SHExceptionHandler.h" />
<ClInclude Include="src\Tools\SHLogger.h" />
<ClInclude Include="src\Tools\SHUtilities.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp" />
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponentGroup.cpp" />
<ClCompile Include="src\Engine\ECS_Base\Entity\SHEntity.cpp" />
<ClCompile Include="src\Engine\ECS_Base\System\SHComponentManager.cpp" />
<ClCompile Include="src\Engine\ECS_Base\System\SHEntityManager.cpp" />
<ClCompile Include="src\Engine\ECS_Base\System\SHSystemManager.cpp" />
<ClCompile Include="src\Engine\SHEngine.cpp" />
<ClCompile Include="src\Filesystem\SHFileSystem.cpp" />
<ClCompile Include="src\Graphics\Buffers\SHVkBuffer.cpp" />
<ClCompile Include="src\Graphics\Commands\SHVkCommandBuffer.cpp" />
<ClCompile Include="src\Graphics\Commands\SHVkCommandPool.cpp" />
<ClCompile Include="src\Graphics\Debugging\SHValidationLayersQuery.cpp" />
<ClCompile Include="src\Graphics\Debugging\SHVkDebugMessenger.cpp" />
<ClCompile Include="src\Graphics\Debugging\SHVulkanDebugUtil.cpp" />
<ClCompile Include="src\Graphics\Descriptors\SHDescriptorPoolManager.cpp" />
<ClCompile Include="src\Graphics\Descriptors\SHDescriptorPoolStorage.cpp" />
<ClCompile Include="src\Graphics\Descriptors\SHVkDescriptorPool.cpp" />
<ClCompile Include="src\Graphics\Descriptors\SHVkDescriptorSetGroup.cpp" />
<ClCompile Include="src\Graphics\Descriptors\SHVkDescriptorSetLayout.cpp" />
<ClCompile Include="src\Graphics\Devices\SHVkLogicalDevice.cpp" />
<ClCompile Include="src\Graphics\Devices\SHVkPhysicalDevice.cpp" />
<ClCompile Include="src\Graphics\Devices\SHVkPhysicalDeviceLibrary.cpp" />
<ClCompile Include="src\Graphics\Framebuffer\SHVkFramebuffer.cpp" />
<ClCompile Include="src\Graphics\Images\SHVkImage.cpp" />
<ClCompile Include="src\Graphics\Images\SHVkImageView.cpp" />
<ClCompile Include="src\Graphics\Instance\SHVkInstance.cpp" />
<ClCompile Include="src\Graphics\MiddleEnd\Interface\SHGraphicsSystem.cpp" />
<ClCompile Include="src\Graphics\MiddleEnd\Interface\SHRenderTarget.cpp" />
<ClCompile Include="src\Graphics\MiddleEnd\PerFrame\SHPerFrameData.cpp" />
<ClCompile Include="src\Graphics\MiddleEnd\PerFrame\SHRenderContext.cpp" />
<ClCompile Include="src\Graphics\MiddleEnd\Shaders\SHShaderModuleLibrary.cpp" />
<ClCompile Include="src\Graphics\MiddleEnd\Shaders\SHShaderSourceLibrary.cpp" />
<ClCompile Include="src\Graphics\Pipeline\SHPipelineState.cpp" />
<ClCompile Include="src\Graphics\Pipeline\SHPushConstantInterface.cpp" />
<ClCompile Include="src\Graphics\Pipeline\SHVkPipeline.cpp" />
<ClCompile Include="src\Graphics\Pipeline\SHVkPipelineLayout.cpp" />
<ClCompile Include="src\Graphics\Queues\SHVkQueue.cpp" />
<ClCompile Include="src\Graphics\RenderGraph\SHRenderGraph.cpp" />
<ClCompile Include="src\Graphics\Renderpass\SHVkAttachDescGen.cpp" />
<ClCompile Include="src\Graphics\Renderpass\SHVkRenderpass.cpp" />
<ClCompile Include="src\Graphics\Renderpass\SHVkSubpassParams.cpp" />
<ClCompile Include="src\Graphics\SHVkUtil.cpp" />
<ClCompile Include="src\Graphics\SHVulkanIncludes.cpp" />
<ClCompile Include="src\Graphics\Shaders\BlockInterface\SHShaderBlockInterface.cpp" />
<ClCompile Include="src\Graphics\Shaders\SHShaderReflected.cpp" />
<ClCompile Include="src\Graphics\Shaders\SHVkShaderModule.cpp" />
<ClCompile Include="src\Graphics\Shaders\spirv-reflect\spirv_reflect.cpp" />
<ClCompile Include="src\Graphics\Swapchain\SHVkSwapchain.cpp" />
<ClCompile Include="src\Graphics\Synchronization\SHVkFence.cpp" />
<ClCompile Include="src\Graphics\Synchronization\SHVkSemaphore.cpp" />
<ClCompile Include="src\Graphics\VertexDescriptors\SHVertexAttribute.cpp" />
<ClCompile Include="src\Graphics\Windowing\SHWIndowMap.cpp" />
<ClCompile Include="src\Graphics\Windowing\SHWindow.cpp" />
<ClCompile Include="src\Graphics\Windowing\Surface\SHVkSurface.cpp" />
<ClCompile Include="src\Math\SHMathHelpers.cpp" />
<ClCompile Include="src\Math\SHMatrix.cpp" />
<ClCompile Include="src\Math\SHQuaternion.cpp" />
<ClCompile Include="src\Math\Vector\SHVec2.cpp" />
<ClCompile Include="src\Math\Vector\SHVec3.cpp" />
<ClCompile Include="src\Math\Vector\SHVec4.cpp" />
<ClCompile Include="src\Resource\ResourceLibrary.cpp" />
<ClCompile Include="src\SHpch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\Scene\SHSceneManager.cpp" />
<ClCompile Include="src\Tools\SHException.cpp" />
<ClCompile Include="src\Tools\SHExceptionHandler.cpp" />
<ClCompile Include="src\Tools\SHLogger.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dependencies\yamlcpp\yaml-cpp.vcxproj">

View File

@ -4,19 +4,578 @@
<Filter Include="Engine">
<UniqueIdentifier>{DBC7D3B0-C769-FE86-B024-12DB9C6585D7}</UniqueIdentifier>
</Filter>
<Filter Include="Engine\ECS_Base">
<UniqueIdentifier>{7FF59BF8-EB80-09BD-F491-8CB1609C65BD}</UniqueIdentifier>
</Filter>
<Filter Include="Engine\ECS_Base\Components">
<UniqueIdentifier>{340D0110-201D-ADE0-89D6-11FF75059C79}</UniqueIdentifier>
</Filter>
<Filter Include="Engine\ECS_Base\Entity">
<UniqueIdentifier>{EBFC8BDC-D7F6-B42E-C063-4B3FACFC1A9B}</UniqueIdentifier>
</Filter>
<Filter Include="Engine\ECS_Base\General">
<UniqueIdentifier>{6CD692F2-D80D-DB89-E117-3FAD4DCE0183}</UniqueIdentifier>
</Filter>
<Filter Include="Engine\ECS_Base\System">
<UniqueIdentifier>{B3E3FAFD-9FDD-2350-884A-BA6074E389BC}</UniqueIdentifier>
</Filter>
<Filter Include="Filesystem">
<UniqueIdentifier>{8A8E2B37-7646-6D84-DF4D-46E0CB240875}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics">
<UniqueIdentifier>{1653CE33-0220-293F-2B39-17E717655ECD}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Buffers">
<UniqueIdentifier>{92C817CE-7EC1-3620-A7F3-1BA5934B162C}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Commands">
<UniqueIdentifier>{17C745C0-83DD-4356-CC54-CF7738AA14DE}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Debugging">
<UniqueIdentifier>{51443AC7-3D28-FB1C-A688-F56F928BE59E}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Descriptors">
<UniqueIdentifier>{573A6CF2-43C9-F5BB-ECE7-09B7D8550662}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Devices">
<UniqueIdentifier>{08DBDC43-F4D3-FB95-1D06-E11A095EDBA1}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Framebuffer">
<UniqueIdentifier>{4AD5CA42-3664-540C-DF82-6807CBF064B2}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Images">
<UniqueIdentifier>{FB5EE099-67EA-4D5E-70FB-D052DC05AA5E}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Instance">
<UniqueIdentifier>{BA26540B-263D-52A1-6FB4-DDC2DB092329}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\MiddleEnd">
<UniqueIdentifier>{4B204703-3704-0859-A064-02AC8C67F2DA}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\MiddleEnd\Interface">
<UniqueIdentifier>{EBA1D3FF-D75C-C3AB-8014-3CF66CAE0D3C}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\MiddleEnd\PerFrame">
<UniqueIdentifier>{8CDBA7C9-F8E8-D5AF-81CF-D19AEDDBA166}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\MiddleEnd\Shaders">
<UniqueIdentifier>{2460C057-1070-6C28-7929-D14665585BC1}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Pipeline">
<UniqueIdentifier>{FBD334F8-67EA-328E-B061-BEAF1CB70316}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Queues">
<UniqueIdentifier>{1DD51CAD-8960-8A71-9271-0D66FE7BE671}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\RenderGraph">
<UniqueIdentifier>{57DAB30C-4369-3DD6-EC87-51D1D8F54D7C}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Renderpass">
<UniqueIdentifier>{9C0DAFD9-086F-8CE7-91DC-D299FD3CC3A6}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Shaders">
<UniqueIdentifier>{EF2D07CC-DB26-261E-0459-0BA3F0B0052A}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Shaders\BlockInterface">
<UniqueIdentifier>{3AEF06DD-A6D2-151D-AFD5-43591B38DC6D}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Shaders\spirv-reflect">
<UniqueIdentifier>{245F5AB0-1085-2417-F9CA-A9E2E58F49E3}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Swapchain">
<UniqueIdentifier>{03DB39DE-EFBE-FA33-581F-F5864422E5B5}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Synchronization">
<UniqueIdentifier>{576DF841-4392-47C2-6CDD-2C52586146E0}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\VertexDescriptors">
<UniqueIdentifier>{75F29FE5-6102-4CB6-CABB-B0D4B6EA3A4F}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Windowing">
<UniqueIdentifier>{5BAB2A92-478F-EBE7-B0EF-E53A9CF2D569}</UniqueIdentifier>
</Filter>
<Filter Include="Graphics\Windowing\Surface">
<UniqueIdentifier>{B3B14D12-9FC1-F9E2-087B-5E01F4A9E87B}</UniqueIdentifier>
</Filter>
<Filter Include="Math">
<UniqueIdentifier>{AFF4887C-9B2B-8A0D-4418-7010302E060F}</UniqueIdentifier>
</Filter>
<Filter Include="Math\Vector">
<UniqueIdentifier>{F1B75745-5D6D-D03A-E661-CA115216C73E}</UniqueIdentifier>
</Filter>
<Filter Include="Meta">
<UniqueIdentifier>{AC05897C-983C-8A0D-4129-70102D3F060F}</UniqueIdentifier>
</Filter>
<Filter Include="Resource">
<UniqueIdentifier>{ED6CDF9B-D939-3AA7-0253-284FEE7E6F35}</UniqueIdentifier>
</Filter>
<Filter Include="Scene">
<UniqueIdentifier>{B3F7140E-1F0C-3DBF-E88D-E01E546139F0}</UniqueIdentifier>
</Filter>
<Filter Include="Tools">
<UniqueIdentifier>{16CF2D0E-82E3-55BF-4B65-F91EB73852F0}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\Engine\ECS_Base\Components\SHComponent.h">
<Filter>Engine\ECS_Base\Components</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\Components\SHComponentGroup.h">
<Filter>Engine\ECS_Base\Components</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\Entity\SHEntity.h">
<Filter>Engine\ECS_Base\Entity</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\General\SHFamily.h">
<Filter>Engine\ECS_Base\General</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\General\SHHandleGenerator.h">
<Filter>Engine\ECS_Base\General</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\General\SHSparseBase.h">
<Filter>Engine\ECS_Base\General</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\General\SHSparseSet.h">
<Filter>Engine\ECS_Base\General</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\General\SHSparseSetContainer.h">
<Filter>Engine\ECS_Base\General</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\SHECSMacros.h">
<Filter>Engine\ECS_Base</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\System\SHComponentManager.h">
<Filter>Engine\ECS_Base\System</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\System\SHEntityManager.h">
<Filter>Engine\ECS_Base\System</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\System\SHSystem.h">
<Filter>Engine\ECS_Base\System</Filter>
</ClInclude>
<ClInclude Include="src\Engine\ECS_Base\System\SHSystemManager.h">
<Filter>Engine\ECS_Base\System</Filter>
</ClInclude>
<ClInclude Include="src\Engine\SHEngine.h">
<Filter>Engine</Filter>
</ClInclude>
<ClInclude Include="src\Filesystem\SHFileSystem.h">
<Filter>Filesystem</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Buffers\SHVkBuffer.h">
<Filter>Graphics\Buffers</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Commands\SHCommandPoolResetMode.h">
<Filter>Graphics\Commands</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Commands\SHVkCommandBuffer.h">
<Filter>Graphics\Commands</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Commands\SHVkCommandPool.h">
<Filter>Graphics\Commands</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Debugging\SHValidationLayersQuery.h">
<Filter>Graphics\Debugging</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Debugging\SHVkDebugMessenger.h">
<Filter>Graphics\Debugging</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Debugging\SHVulkanDebugUtil.h">
<Filter>Graphics\Debugging</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Descriptors\SHDescriptorPoolManager.h">
<Filter>Graphics\Descriptors</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Descriptors\SHDescriptorPoolStorage.h">
<Filter>Graphics\Descriptors</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Descriptors\SHVkDescriptorPool.h">
<Filter>Graphics\Descriptors</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Descriptors\SHVkDescriptorSetGroup.h">
<Filter>Graphics\Descriptors</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Descriptors\SHVkDescriptorSetLayout.h">
<Filter>Graphics\Descriptors</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Devices\SHVkLogicalDevice.h">
<Filter>Graphics\Devices</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Devices\SHVkPhysicalDevice.h">
<Filter>Graphics\Devices</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Devices\SHVkPhysicalDeviceLibrary.h">
<Filter>Graphics\Devices</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Framebuffer\SHVkFramebuffer.h">
<Filter>Graphics\Framebuffer</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Images\SHImageViewDetails.h">
<Filter>Graphics\Images</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Images\SHVkImage.h">
<Filter>Graphics\Images</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Images\SHVkImageView.h">
<Filter>Graphics\Images</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Instance\SHVkInstance.h">
<Filter>Graphics\Instance</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\Interface\SHGraphicsSystem.h">
<Filter>Graphics\MiddleEnd\Interface</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\Interface\SHRenderTarget.h">
<Filter>Graphics\MiddleEnd\Interface</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\PerFrame\SHPerFrameData.h">
<Filter>Graphics\MiddleEnd\PerFrame</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\PerFrame\SHRenderContext.h">
<Filter>Graphics\MiddleEnd\PerFrame</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\Shaders\SHShaderModuleLibrary.h">
<Filter>Graphics\MiddleEnd\Shaders</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\Shaders\SHShaderSourceLibrary.h">
<Filter>Graphics\MiddleEnd\Shaders</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\MiddleEnd\Shaders\SHShaderType.h">
<Filter>Graphics\MiddleEnd\Shaders</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Pipeline\SHPipelineLayoutParams.h">
<Filter>Graphics\Pipeline</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Pipeline\SHPipelineState.h">
<Filter>Graphics\Pipeline</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Pipeline\SHPipelineType.h">
<Filter>Graphics\Pipeline</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Pipeline\SHPushConstantInterface.h">
<Filter>Graphics\Pipeline</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Pipeline\SHVkPipeline.h">
<Filter>Graphics\Pipeline</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Pipeline\SHVkPipelineLayout.h">
<Filter>Graphics\Pipeline</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Queues\SHVkQueue.h">
<Filter>Graphics\Queues</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\RenderGraph\SHRenderGraph.h">
<Filter>Graphics\RenderGraph</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Renderpass\SHVkAttachDescGen.h">
<Filter>Graphics\Renderpass</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Renderpass\SHVkAttachment.h">
<Filter>Graphics\Renderpass</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Renderpass\SHVkRenderpass.h">
<Filter>Graphics\Renderpass</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Renderpass\SHVkSubpassDescription.h">
<Filter>Graphics\Renderpass</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Renderpass\SHVkSubpassParams.h">
<Filter>Graphics\Renderpass</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\SHVkUtil.h">
<Filter>Graphics</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\SHVulkanDefines.h">
<Filter>Graphics</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\SHVulkanIncludes.h">
<Filter>Graphics</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Shaders\BlockInterface\SHShaderBlockInterface.h">
<Filter>Graphics\Shaders\BlockInterface</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Shaders\SHShaderReflected.h">
<Filter>Graphics\Shaders</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Shaders\SHVkShaderModule.h">
<Filter>Graphics\Shaders</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Shaders\spirv-reflect\spirv_reflect.h">
<Filter>Graphics\Shaders\spirv-reflect</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Swapchain\SHSwapchainParams.h">
<Filter>Graphics\Swapchain</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Swapchain\SHVkSwapchain.h">
<Filter>Graphics\Swapchain</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Synchronization\SHVkFence.h">
<Filter>Graphics\Synchronization</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Synchronization\SHVkSemaphore.h">
<Filter>Graphics\Synchronization</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\VertexDescriptors\SHVertexAttribute.h">
<Filter>Graphics\VertexDescriptors</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Windowing\SHWindow.h">
<Filter>Graphics\Windowing</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Windowing\SHWindowMap.h">
<Filter>Graphics\Windowing</Filter>
</ClInclude>
<ClInclude Include="src\Graphics\Windowing\Surface\SHVkSurface.h">
<Filter>Graphics\Windowing\Surface</Filter>
</ClInclude>
<ClInclude Include="src\Math\SHMath.h">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="src\Math\SHMathHelpers.h">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="src\Math\SHMatrix.h">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="src\Math\SHQuaternion.h">
<Filter>Math</Filter>
</ClInclude>
<ClInclude Include="src\Math\Vector\SHVec2.h">
<Filter>Math\Vector</Filter>
</ClInclude>
<ClInclude Include="src\Math\Vector\SHVec3.h">
<Filter>Math\Vector</Filter>
</ClInclude>
<ClInclude Include="src\Math\Vector\SHVec4.h">
<Filter>Math\Vector</Filter>
</ClInclude>
<ClInclude Include="src\Meta\SHIsDetected.h">
<Filter>Meta</Filter>
</ClInclude>
<ClInclude Include="src\Resource\Handle.h">
<Filter>Resource</Filter>
</ClInclude>
<ClInclude Include="src\Resource\ResourceLibrary.h">
<Filter>Resource</Filter>
</ClInclude>
<ClInclude Include="src\Resource\SparseSet.h">
<Filter>Resource</Filter>
</ClInclude>
<ClInclude Include="src\SHpch.h" />
<ClInclude Include="src\Filesystem\SHFileSystem.h" />
<ClInclude Include="src\Scene\SHScene.h">
<Filter>Scene</Filter>
</ClInclude>
<ClInclude Include="src\Scene\SHSceneManager.h">
<Filter>Scene</Filter>
</ClInclude>
<ClInclude Include="src\Tools\SHException.h">
<Filter>Tools</Filter>
</ClInclude>
<ClInclude Include="src\Tools\SHExceptionHandler.h">
<Filter>Tools</Filter>
</ClInclude>
<ClInclude Include="src\Tools\SHLogger.h">
<Filter>Tools</Filter>
</ClInclude>
<ClInclude Include="src\Tools\SHUtilities.h">
<Filter>Tools</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponent.cpp">
<Filter>Engine\ECS_Base\Components</Filter>
</ClCompile>
<ClCompile Include="src\Engine\ECS_Base\Components\SHComponentGroup.cpp">
<Filter>Engine\ECS_Base\Components</Filter>
</ClCompile>
<ClCompile Include="src\Engine\ECS_Base\Entity\SHEntity.cpp">
<Filter>Engine\ECS_Base\Entity</Filter>
</ClCompile>
<ClCompile Include="src\Engine\ECS_Base\System\SHComponentManager.cpp">
<Filter>Engine\ECS_Base\System</Filter>
</ClCompile>
<ClCompile Include="src\Engine\ECS_Base\System\SHEntityManager.cpp">
<Filter>Engine\ECS_Base\System</Filter>
</ClCompile>
<ClCompile Include="src\Engine\ECS_Base\System\SHSystemManager.cpp">
<Filter>Engine\ECS_Base\System</Filter>
</ClCompile>
<ClCompile Include="src\Engine\SHEngine.cpp">
<Filter>Engine</Filter>
</ClCompile>
<ClCompile Include="src\Filesystem\SHFileSystem.cpp">
<Filter>Filesystem</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Buffers\SHVkBuffer.cpp">
<Filter>Graphics\Buffers</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Commands\SHVkCommandBuffer.cpp">
<Filter>Graphics\Commands</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Commands\SHVkCommandPool.cpp">
<Filter>Graphics\Commands</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Debugging\SHValidationLayersQuery.cpp">
<Filter>Graphics\Debugging</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Debugging\SHVkDebugMessenger.cpp">
<Filter>Graphics\Debugging</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Debugging\SHVulkanDebugUtil.cpp">
<Filter>Graphics\Debugging</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Descriptors\SHDescriptorPoolManager.cpp">
<Filter>Graphics\Descriptors</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Descriptors\SHDescriptorPoolStorage.cpp">
<Filter>Graphics\Descriptors</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Descriptors\SHVkDescriptorPool.cpp">
<Filter>Graphics\Descriptors</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Descriptors\SHVkDescriptorSetGroup.cpp">
<Filter>Graphics\Descriptors</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Descriptors\SHVkDescriptorSetLayout.cpp">
<Filter>Graphics\Descriptors</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Devices\SHVkLogicalDevice.cpp">
<Filter>Graphics\Devices</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Devices\SHVkPhysicalDevice.cpp">
<Filter>Graphics\Devices</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Devices\SHVkPhysicalDeviceLibrary.cpp">
<Filter>Graphics\Devices</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Framebuffer\SHVkFramebuffer.cpp">
<Filter>Graphics\Framebuffer</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Images\SHVkImage.cpp">
<Filter>Graphics\Images</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Images\SHVkImageView.cpp">
<Filter>Graphics\Images</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Instance\SHVkInstance.cpp">
<Filter>Graphics\Instance</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\MiddleEnd\Interface\SHGraphicsSystem.cpp">
<Filter>Graphics\MiddleEnd\Interface</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\MiddleEnd\Interface\SHRenderTarget.cpp">
<Filter>Graphics\MiddleEnd\Interface</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\MiddleEnd\PerFrame\SHPerFrameData.cpp">
<Filter>Graphics\MiddleEnd\PerFrame</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\MiddleEnd\PerFrame\SHRenderContext.cpp">
<Filter>Graphics\MiddleEnd\PerFrame</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\MiddleEnd\Shaders\SHShaderModuleLibrary.cpp">
<Filter>Graphics\MiddleEnd\Shaders</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\MiddleEnd\Shaders\SHShaderSourceLibrary.cpp">
<Filter>Graphics\MiddleEnd\Shaders</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Pipeline\SHPipelineState.cpp">
<Filter>Graphics\Pipeline</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Pipeline\SHPushConstantInterface.cpp">
<Filter>Graphics\Pipeline</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Pipeline\SHVkPipeline.cpp">
<Filter>Graphics\Pipeline</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Pipeline\SHVkPipelineLayout.cpp">
<Filter>Graphics\Pipeline</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Queues\SHVkQueue.cpp">
<Filter>Graphics\Queues</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\RenderGraph\SHRenderGraph.cpp">
<Filter>Graphics\RenderGraph</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Renderpass\SHVkAttachDescGen.cpp">
<Filter>Graphics\Renderpass</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Renderpass\SHVkRenderpass.cpp">
<Filter>Graphics\Renderpass</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Renderpass\SHVkSubpassParams.cpp">
<Filter>Graphics\Renderpass</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\SHVkUtil.cpp">
<Filter>Graphics</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\SHVulkanIncludes.cpp">
<Filter>Graphics</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Shaders\BlockInterface\SHShaderBlockInterface.cpp">
<Filter>Graphics\Shaders\BlockInterface</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Shaders\SHShaderReflected.cpp">
<Filter>Graphics\Shaders</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Shaders\SHVkShaderModule.cpp">
<Filter>Graphics\Shaders</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Shaders\spirv-reflect\spirv_reflect.cpp">
<Filter>Graphics\Shaders\spirv-reflect</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Swapchain\SHVkSwapchain.cpp">
<Filter>Graphics\Swapchain</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Synchronization\SHVkFence.cpp">
<Filter>Graphics\Synchronization</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Synchronization\SHVkSemaphore.cpp">
<Filter>Graphics\Synchronization</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\VertexDescriptors\SHVertexAttribute.cpp">
<Filter>Graphics\VertexDescriptors</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Windowing\SHWIndowMap.cpp">
<Filter>Graphics\Windowing</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Windowing\SHWindow.cpp">
<Filter>Graphics\Windowing</Filter>
</ClCompile>
<ClCompile Include="src\Graphics\Windowing\Surface\SHVkSurface.cpp">
<Filter>Graphics\Windowing\Surface</Filter>
</ClCompile>
<ClCompile Include="src\Math\SHMathHelpers.cpp">
<Filter>Math</Filter>
</ClCompile>
<ClCompile Include="src\Math\SHMatrix.cpp">
<Filter>Math</Filter>
</ClCompile>
<ClCompile Include="src\Math\SHQuaternion.cpp">
<Filter>Math</Filter>
</ClCompile>
<ClCompile Include="src\Math\Vector\SHVec2.cpp">
<Filter>Math\Vector</Filter>
</ClCompile>
<ClCompile Include="src\Math\Vector\SHVec3.cpp">
<Filter>Math\Vector</Filter>
</ClCompile>
<ClCompile Include="src\Math\Vector\SHVec4.cpp">
<Filter>Math\Vector</Filter>
</ClCompile>
<ClCompile Include="src\Resource\ResourceLibrary.cpp">
<Filter>Resource</Filter>
</ClCompile>
<ClCompile Include="src\SHpch.cpp" />
<ClCompile Include="src\Filesystem\SHFileSystem.cpp" />
<ClCompile Include="src\Scene\SHSceneManager.cpp">
<Filter>Scene</Filter>
</ClCompile>
<ClCompile Include="src\Tools\SHException.cpp">
<Filter>Tools</Filter>
</ClCompile>
<ClCompile Include="src\Tools\SHExceptionHandler.cpp">
<Filter>Tools</Filter>
</ClCompile>
<ClCompile Include="src\Tools\SHLogger.cpp">
<Filter>Tools</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -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
@ -56,14 +57,16 @@ project "SHADE_Engine"
"reactphysics3d",
"imgui",
"spdlog",
"vulkan-1.lib"
"vulkan-1.lib",
"shaderc_shared.lib"
}
defines
{
"_LIB",
"_GLFW_INCLUDE_NONE",
"MSDFGEN_USE_CPP11"
"MSDFGEN_USE_CPP11",
"NOMINMAX"
}
flags

View File

@ -0,0 +1,9 @@
#include "SHpch.h"
#include "SHComponent.h"
namespace SHADE
{
SHComponent::~SHComponent()
{
}
}

View File

@ -0,0 +1,121 @@
/*********************************************************************
* \file SHComponent.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the Component abstract base class.
*
* \copyright Copyright (c) 2021 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_COMPONENT_H
#define SH_COMPONENT_H
#include "SHpch.h"
#include "../SHECSMacros.h"
namespace SHADE
{
class SHComponentManager;
class SHComponent
{
friend SHComponentManager;
private:
//The ID of the entity this component belongs to.
EntityID entityID;
//Whether this entity ID has been set once.
//This prevents the Set function from being able to change the entityID once it has been set
protected:
/*!*************************************************************************
* \brief Construct a new SHComponent object
* Protected default constructor to make this an abstract base class.
***************************************************************************/
SHComponent()
:entityID(0),isActive(true)
{
}
SHComponent(SHComponent const& other)
:entityID(other.entityID), isActive(other.isActive)
{
}
public:
//Whether or not this component is active.
//Systems using this component should are responsible for checking the active state of the component before running their functionality.
bool isActive;
/*!*************************************************************************
* \brief
* Getter function for the entityID
* \return uint32_t
* The entityID that this component belongs to.
***************************************************************************/
uint32_t GetEID()const
{
return this->entityID;
}
/*!*************************************************************************
* \brief Destroy the SHComponent object
* Default destructor for Component Base class
***************************************************************************/
virtual ~SHComponent();
/*!*************************************************************************
* \brief
* A function that is called when the entity changes its parent.
* This can remain empty if nothing has to be done before the parent changes.
* This does not change the component that the entity belongs to
* The old parent is not passed in here as the old parent might be the root node
* in which case there will be no parent.
* If needed, the old parent should be deduced using this entityID
* \param newParent
* EntityID of the new parent.
***************************************************************************/
virtual void ChangeParent(EntityID newParent)
{
(void)newParent;
}
/**************************************************************************
* \brief
* This is an overloaded function of Change parent when the entity
* changes its parent to the root node. (No parent)
*
***************************************************************************/
virtual void ChangeParent()
{
}
virtual void OnDestroy()
{
}
virtual void OnCreate()
{
}
SHComponent& operator=(SHComponent const& other)
{
this->entityID = other.GetEID();
this->isActive = other.isActive;
return *this;
}
};
}
#endif

View File

@ -0,0 +1,136 @@
/*********************************************************************
* \file SHComponent.h
* \author Daniel Chua Yee Chen
* \brief Implementation for the SHComponentGroup class.
* The Component Group aids the engine to sort the components to align
* different component types data that corresponds to a single entity
* in the same index. The component group also ensure that all components
* data belonging to the group is moved to the start of the dense array.
*
* \copyright Copyright (c) 2021 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.
*********************************************************************/
#include "SHpch.h"
#include "SHComponentGroup.h"
#include "../System/SHComponentManager.h"
namespace SHADE
{
SHComponentGroup::SHComponentGroup()
:ownershipType(OWNERSHIP_TYPE::FULL_OWNERSHIP)
{
componentTypeIDs.reserve(MAX_EID);
ownedComponentTypes.reserve(MAX_EID);
entityInGroup.reserve(MAX_EID);
}
EntityIndex SHComponentGroup::size() noexcept
{
return (EntityIndex)entityInGroup.size();
}
bool SHComponentGroup::IsEntityInGroup(EntityID entityID) noexcept
{
for (auto const& eid : entityInGroup)
{
if (entityID == eid)
{
return true;
}
}
return false;
}
bool SHComponentGroup::RemoveEntity(EntityID entityID) noexcept
{
if (!IsEntityInGroup(entityID))
{
// This entity has not been added to the group. Ignore it.
return false;
}
//This entity is in the group.
//Move the placement of the components in all owned component types to the relevant position and remove it from the group.
for (auto const& ownedID : ownedComponentTypes)
{
if (SHComponentManager::ComponentCount_ID(ownedID) > 2)
{
// There are components that do not belong in the group. Need to swap to the last element
SHComponentManager::SwapInDenseByIndexHash_ID((EntityIndex)entityInGroup.size() - 1, entityID, ownedID);
}
}
for (std::vector<EntityID>::iterator it = entityInGroup.begin(); it != entityInGroup.end(); ++it)
{
if (*it == entityID)
{
entityInGroup.erase(it);
break;
}
}
return true;
}
void SHComponentGroup::Clear() noexcept
{
entityInGroup.clear();
}
bool SHComponentGroup::AddComponentCheck(EntityID entityID, uint32_t componentTypeID) noexcept
{
if (IsEntityInGroup(entityID))
{
return false;
}
//uint32_t componentID = SHFamilyID<SHComponent>::template GetID<T>();
//Loops to check if this is one of the owned component type
for (auto const& id : componentTypeIDs)
{
//found it
if (id == componentTypeID)
{
for (auto const& typeID : componentTypeIDs)
{
if (typeID != componentTypeID && !SHComponentManager::HasComponent_ID(entityID, typeID))
{
//This entity does not contain all other component types in this group so we don't do anything yet
return false;
}
}
//This entity contains all required component types. Add to group and swap in dense
//auto& dense = SHComponentManager::GetDense<T>();
//Loop through all the owned component and sort them.
for (auto const& ownedID : ownedComponentTypes)
{
if (SHComponentManager::ComponentCount_ID(ownedID) > entityInGroup.size() + 1)
{
// There are components that do not belong in the group. Need to swap to the last element
SHComponentManager::SwapInDenseByIndexHash_ID((EntityIndex)entityInGroup.size(), entityID, ownedID);
}
}
// If dense.size() == entityInGroup.size() + 1.
// There is no component that is not in this group for this component type.
// Hence no sorting required.
//Add this entityID to group
entityInGroup.push_back(entityID);
return true;
}
}
//This component type is not in this group.
return false;
}
}

View File

@ -0,0 +1,185 @@
/*********************************************************************
* \file SHComponent.h
* \author Daniel Chua Yee Chen
* \brief Definition for the SHComponentGroup class.
* The Component Group aids the engine to sort the components to align
* different component types data that corresponds to a single entity
* in the same index. The component group also ensure that all components
* data belonging to the group is moved to the start of the dense array.
*
* \copyright Copyright (c) 2021 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_COMPONENT_GROUP
#define SH_COMPONENT_GROUP
#include "../SHECSMacros.h"
#include "../General/SHFamily.h"
#include "SHComponent.h"
#include <vector>
#include <cassert>
#include <iostream>
namespace SHADE
{
enum class OWNERSHIP_TYPE
{
FULL_OWNERSHIP = 0,
PARTIAL_OWNERSHIP,
NON_OWNERSHIP,
OWNERSHIP_MAX
};
//template<OWNERSHIP_TYPE ownership>
class SHComponentGroup
{
public:
//This vector store the Entity IDs of all entity that belongs to this group.
std::vector<EntityID> entityInGroup;
//template<>
//bool CheckAddComponent();
SHComponentGroup();
~SHComponentGroup() = default;
//friends SHComponentManager to allow ECSCore to create componentgroups
friend class SHComponentManager;
/**************************************************************************
* \brief
* Get the number of entity in the group.
* \return
* Number of entity in the group.
***************************************************************************/
EntityIndex size() noexcept;
/**************************************************************************
* \brief
* Check if a entity is already in the group.
* \param entityID
* The entity ID of the entity
* \return
* true if the entity belongs to the group
***************************************************************************/
bool IsEntityInGroup(EntityID entityID) noexcept;
/*!*************************************************************************
* \brief
* Checks whether the entity has fulfilled the requirements to be added into
* the Component Group after adding a specified Component Type.
* @tparam T
* Component type to be added
* \param entityID
* EntityID of the entity to be added
***************************************************************************/
bool AddComponentCheck(EntityID entityID, uint32_t componentTypeID) noexcept;
/*!*************************************************************************
* \brief
* Check if the Component type is in the component group and if the entity is in the group
* Remove it from the group if found.
* This does not handle the removal from the dense array in the sparse set.
* @tparam T
* Component type to be removed
* \param entityID
* EntityID of the entity
***************************************************************************/
template<typename T>
std::enable_if_t<std::is_base_of_v<SHComponent, T>, bool> RemoveComponentCheck(EntityID entityID) noexcept
{
if (!IsEntityInGroup(entityID))
{
// This entity has not been added to the group. Ignore it.
return false;
}
bool typeFound = false;
uint32_t componentID = SHFamilyID<SHComponent>::template GetID<T>();
for (auto const& id : componentTypeIDs)
{
if (id == componentID)
{
typeFound = true;
break;
}
}
if (typeFound == false)
{
return false; //This type is not something this group needs to care about.
}
//This entity is in the group.
//Move the placement of the components in all owned component types to the relevant position and remove it from the group.
for (auto const& ownedID : ownedComponentTypes)
{
if (SHComponentManager::ComponentCount_ID(ownedID) > entityInGroup.size() + 1)
{
// There are components that do not belong in the group. Need to swap to the last element
SHComponentManager::SwapInDenseByIndexHash_ID((EntityIndex)entityInGroup.size() - 1, entityID, ownedID);
}
}
for (std::vector<EntityID>::iterator it = entityInGroup.begin(); it != entityInGroup.end(); ++it)
{
if (*it == entityID)
{
entityInGroup.erase(it);
break;
}
}
return true;
}
/*!*************************************************************************
* \brief
* Checks if this entity is in the Component Group and remove it if found.
* This does not handle the removal from the dense array in the sparse set.
* \param entityID
* EntityID of the entity
***************************************************************************/
bool RemoveEntity(EntityID entityID) noexcept;
/*!*************************************************************************
* \brief
* Resets the Component Group by clearing all entity in group.
* This will not change the Component Types that belong in the group but
* only clear the entity that belongs to the group.
* This is used for scene transition where all entities are cleared.
***************************************************************************/
void Clear() noexcept;
private:
OWNERSHIP_TYPE ownershipType;
protected:
//This vector stores all IDs of all the component type that belongs to this group.
//This will be assigned when the group gets created and used by the group themselves when they for add/remove components.
std::vector<uint32_t> componentTypeIDs;
//This vector stores the IDs of all the component type that this group owns.
//Each component type can only be owned by one group.
std::vector<uint32_t> ownedComponentTypes;
};
}
#endif

View File

@ -0,0 +1,74 @@
/*********************************************************************
* \file SHEntity.cpp
* \author Daniel Chua Yee Chen
* \brief Definition of function used in SHEntity class.
*
* \copyright Copyright (c) 2021 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.
*********************************************************************/
#include "SHpch.h"
#include "SHEntity.h"
#include "../System/SHEntityManager.h"
//#include "Scene/SHSceneGraph.h"
#include "../System/SHComponentManager.h"
namespace SHADE
{
SHEntity::SHEntity()
:entityID(),isActive(true)
{
}
SHEntity::~SHEntity()
{
//SHEntityManager::RemoveEntity(this->entityID);
}
EntityID SHEntity::GetEID() noexcept
{
return this->entityID;
}
void SHEntity::SetActive(bool active) noexcept
{
isActive = active;
SHComponentManager::SetActive(entityID, active);
}
void SHEntity::SetParent(SHEntity* newParent) noexcept
{
(void)newParent;
//TODO
}
void SHEntity::SetParent(EntityID newParentID) noexcept
{
(void)newParentID;
//TODO
}
SHEntity* SHEntity::GetParent() noexcept
{
//TODO
return nullptr;
}
std::vector<SHEntity*>const& SHEntity::GetChildren() noexcept
{
//TODO
return std::vector<SHEntity*>{};
}
std::vector<EntityID>const& SHEntity::GetChildrenID() noexcept
{
return std::vector<EntityID>{};
}
}

View File

@ -0,0 +1,163 @@
/*********************************************************************
* \file SHEntity.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the Entity abstract base class.
*
* \copyright Copyright (c) 2021 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_ENTITY_H
#define SH_ENTITY_H
#include "../SHECSMacros.h"
#include "../Components/SHComponent.h"
#include "../System/SHComponentManager.h"
//#include "../../Scene/SHSceneNode.h"
namespace SHADE
{
class SHComponentManager;
class SHEntityManager;
class SHEntity
{
public:
/**************************************************************************
* Friends with SHEntityManger.
* This allows SHEntityManager to generate a unique entityID for the entity
* upon creation.
* .
***************************************************************************/
friend SHEntityManager;
/*!*************************************************************************
* \brief Construct a new SHEntity object
* Default constructor for the Entity Object.
* Entities are given their entityID here.
***************************************************************************/
SHEntity();
/*!*************************************************************************
* \brief Destroy the SHEntity object
* Default destructor for the Entity object.
* Removes the entityID and queue it for recycling.
***************************************************************************/
virtual ~SHEntity();
/*!*************************************************************************
* \brief Get the Component object
* A templated GetComponent_s function that calls GetComponent_s from SHComponentManager.
* This is for ease of use.
* @tparam T
* The type of the Component we are trying to get.
* \return
* A pointer to the Component we are trying to get.
* Returns nullptr if the entity does not have such Component.
***************************************************************************/
template<typename T>
std::enable_if_t<std::is_base_of_v<SHComponent, T>, T*> GetComponent() noexcept
{
return SHComponentManager::GetComponent_s<T>(entityID);
//return nullptr;
}
/*!*************************************************************************
* \brief
* Getter function for the entityID
* \return uint32_t
* The entityID of this Entity object.
***************************************************************************/
EntityID GetEID() noexcept;
/*!*************************************************************************
* \brief Set the Active object
* Sets the isActive of all Components in this entity.
* This is done through SHComponentManager and this function is for ease of use.
* If derived classes of Entity overloads this function, they are expected to
* call this function using SHEntity::SetActive(active) to change the active of
* all components
* \param active
* The active state to change all components in this entity.
***************************************************************************/
virtual void SetActive(bool active) noexcept;
/**************************************************************************
* \brief
* Change the parent of the entity object in the scene graph.
* This command will be stored in a queue and only applied when
* SHSceneGraph::UpdateHierachy is called.
* \param newParent
* A pointer to the new parent entity.
* Pass a nullptr to attach this entity to the root node instead.
***************************************************************************/
void SetParent(SHEntity* newParent = nullptr) noexcept;
/**************************************************************************
* \brief
* Change the parent of the entity object in the scene graphj.
* This command will be stored in a queue and only applied when
* SHSceneGraph::UpdateHierachy is called
* This is the overloaded version of the function that takes in a
* EntityID instead. This cannot be used to attach to the root node.
* \param newParentID
* The entityID of the new parent.
***************************************************************************/
void SetParent(EntityID newParentID) noexcept;
/**************************************************************************
* \brief
* Get a pointer to the parent entity.
* \return
* Returns a pointer to the parent entity.
* Returns a nullptr if the parent node is the root node.
***************************************************************************/
SHEntity* GetParent() noexcept;
/**************************************************************************
* \brief
* Get a vector of SHEntity pointers of the children belonging to this entity.
* \return
* Return a vector of SHEntity pointers of the children belonging to this entity.
***************************************************************************/
std::vector<SHEntity*>const& GetChildren() noexcept;
/**************************************************************************
* \brief
* Get a vector of EntityID of the children belonging to this entity.
* \return
* return a vector of EntityID of the children belonging to this entity.
***************************************************************************/
std::vector<EntityID>const& GetChildrenID() noexcept;
std::string name;
bool isActive;
private:
//The identifier of the entity.
//This is split into 2 x 16-bits that represents version and index respectively
//Index is used by the engine to index the entity.
//Version is used by the engine to recycle the index of the entity.
EntityID entityID;
};
}
#endif

View File

@ -0,0 +1,88 @@
/*********************************************************************
* \file SHFamily.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the SHFamily template class.
* This class is to create a unique identifier for each derived class type.
* Example: Each Component type (SHTransformComponent / SHRenderableComponent)
* will have a different identifier generated by this class.
*
* \copyright Copyright (c) 2021 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_FAMILY_H
#define SH_FAMILY_H
#include "../SHECSMacros.h"
namespace SHADE
{
template<typename BaseClass>
class SHFamilyID
{
private:
//this is used to keep track of the new current ID to be assign to a new Derived class type.
static ComponentTypeID currentID;
/*!*************************************************************************
* \brief Construct a new SHFamilyID object
* Private constructor.
* No objects of this type should be made.
* Only use the static functions of this class
***************************************************************************/
SHFamilyID()
{
}
/*!*************************************************************************
* \brief Destroy the SHFamilyID object
* Private destructor.
* No objects of this type should be made.
* Only use the static functions of this class
***************************************************************************/
virtual ~SHFamilyID()
{
}
public:
/*!*************************************************************************
* \brief
* Checks if this identifier is cuurrently in use / valid.
* \param id
* Identifier to check for.
* \return bool
* true if the identifier is currently in use.
* false if the identifier is not in use.
***************************************************************************/
static bool IsValidID(ComponentTypeID id) noexcept
{
return(id < currentID);
}
/*!*************************************************************************
* \brief
* Get the ID of a derived class type.
* Example of function call to this function should be:
* SHFamily<SHComponent>::GetID<SHTransformComponent>();
* @tparam DerivedClass
* The derived class type that we are trying to get the ID of.
***************************************************************************/
template<typename DerivedClass>
static ENABLE_IF_DERIVED(ComponentTypeID, BaseClass, DerivedClass) GetID() noexcept
{
//The first time a new derived class type call this get id, it will initialize id using the currentID from familyID class.
static ComponentTypeID id = currentID++;
return id;
}
};
//initialize currentID as 0
template<typename BaseClass>
ComponentTypeID SHFamilyID<BaseClass>::currentID = 0;
}
#endif

View File

@ -0,0 +1,305 @@
/*********************************************************************
* \file SHHandleGenerator.h
* \author Daniel Chua Yee Chen
*
* \brief Declaration for the SHHandleGenerator class.
* Generates a unique identifier/handle for objects.
* This is different from SHFamily which is meant to generate identifier for object types.
*
*
* \copyright Copyright (c) 2021 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_HANDLE_GENERATOR_H
#define SH_HANDLE_GENERATOR_H
#include <vector>
#include <iostream>
#include "../SHECSMacros.h"
namespace SHADE
{
/*********************************************************************
* \brief
* Generates a unique handle of HandleType type.
* The first half of the bits will be used for version. This is used for recyling the IDs.
* The second half of the bits will be used for the index, this is also used as its ID.
*
* HandleType should always be twice the size of IndexType
*********************************************************************/
template <typename HandleType, typename IndexType>
class SHHandleGenerator
{
private:
//List of all the handles in use. Handles that are deleted are still stored here.
std::vector<HandleType> handleList;
//The next handle to be used.
HandleType nextRecycle;
//The total number of handles that are currently deleted and waiting to be recycled.
HandleType recycleCounter;
//The Mask is variable is used to easily separate the version and index via bit masking (bitwise &).
//The index mask is used to identify the index.
//(1<<16) - 1. Shift 1 bit to the left 16 times and -1. This leaves the first 16 bits as 0 and the -1 makes the second 16 bits all 1.
static const HandleType indexMask = (1 << sizeof(IndexType) * 8) - 1;
//using the bitwise NOT, invert the index mask to get the version mask.
static const HandleType versionMask = ~indexMask;
public:
/*!*************************************************************************
* \brief Construct a new SHHandleGenerator object
* default constructor for the HandleGenerator object.
***************************************************************************/
SHHandleGenerator()
:nextRecycle(0),recycleCounter(0)
{
}
/*!*************************************************************************
* \brief Destroy the SHHandleGenerator object
* Default destructor for the HandleGenerator object.
***************************************************************************/
~SHHandleGenerator() = default;
/*!*************************************************************************
* \brief
* Combines the 16-bit version and 16-bit index to make a 32-bit handle.
* \param verison
* The 16-bit version
* \param index
* The 16-bit index
* \return HandleType
* The resultant Handle
***************************************************************************/
static HandleType GetHandle(IndexType version, IndexType index) noexcept
{
return (version << sizeof(IndexType) * 8) | index;
}
/*!*************************************************************************
* \brief
* Gets the 16-bit version from the 32-bit handle
* \param handle
* The 32-bit handle
* \return IndexType
* The 16-bit version
***************************************************************************/
static IndexType GetVersion(HandleType handle) noexcept
{
return handle >> sizeof(IndexType) * 8;
}
/*!*************************************************************************
* \brief
* Gets the 16-bit index from the 32-bit handle
* \param handle
* The 32-bit handle
* \return IndexType
* The 16-bit index
***************************************************************************/
static IndexType GetIndex(HandleType handle) noexcept
{
return handle & indexMask;
}
/*!*************************************************************************
* \brief
* Checks if the current handle is still valid. This checks if the index
* of the handle is in use, then checks if the version of the current index
* matches the handle we are checking for.
* \param handle
* The handle to check for.
* \return bool
* True if the handle is still valid. False if the handle has been removed
* or not in use.
***************************************************************************/
bool IsValid(HandleType handle) noexcept
{
IndexType index = GetIndex(handle);
if (index >= (IndexType)handleList.size()) // handle is out of range of intialized index
{
return false;
}
return (handleList[index] == handle); // this will check if the current handle at that index has been removed.
}
/**************************************************************************
* \brief
* Get the number of active handles.
* \return
* Number of active handles.
***************************************************************************/
IndexType GetActiveHandleCount() noexcept
{
return (IndexType)(handleList.size() - recycleCounter);
}
/*!*************************************************************************
* \brief
* Request the Generator for a new handle. This will provide recycled index
* if available
* \return HandleType
* The new handle.
***************************************************************************/
HandleType GetNewHandle() noexcept
{
HandleType result;
//Check if there is any index waiting to be recycled
if (recycleCounter == 0) // nothing to be recycled. Make a new one
{
result = GetHandle(0, (IndexType)handleList.size());
handleList.push_back(result);
return result;
}
//There is a index waiting to be recycled.
result = nextRecycle;
IndexType index = GetIndex(nextRecycle);
//updates the next recycle.
nextRecycle = handleList[index];
handleList[index] = result;
--recycleCounter;
return result;
}
/*!*************************************************************************
* \brief
* Removes a handle and make it invalid and queue it for recycling.
* \param handle
* The handle to remove.
***************************************************************************/
void RemoveHandle(HandleType handle) noexcept
{
if (!IsValid(handle))
{
return;
}
IndexType index = GetIndex(handle);
IndexType version = GetVersion(handle);
//set the handle at the current index to the next thing to be recycled.
handleList[index] = nextRecycle;
//set the next recycle to be the thing that was just deleted with its version incremented.
nextRecycle = GetHandle(version + 1, index);
++recycleCounter;
}
/*!*************************************************************************
* \brief
* Clears all the handles in use and Resets the handle generation.
***************************************************************************/
void ClearAllHandles() noexcept
{
handleList.clear();
nextRecycle = 0;
recycleCounter = 0;
}
/**************************************************************************
* \brief
* Attempts to claim a specified handle. If the handle is available, the
* generator will mark it as claimed and return true.
* Updates recycles accordingly when adding extra padding to the handle list.
*
* \param handle
* The handle to claim
*
* \return
* true if the handle is available to claim.
***************************************************************************/
bool ClaimHandle(HandleType handle) noexcept
{
IndexType index = GetIndex(handle);
if (handleList.size() <= index || GetIndex(handleList[index]) != index)
{
//This index is currently not in use
if (index >= handleList.size())
{
IndexType size = (IndexType)handleList.size();
for (IndexType i = 0; i < index - size; ++i)
{
handleList.push_back((IndexType)handleList.size());
HandleType back = handleList.back();
//set the handle at the current index to the next thing to be recycled.
if (recycleCounter > 0)
handleList.back() = nextRecycle;
else
{
handleList.back() = (HandleType)-1;
}
//set the next recycle to be the thing that was just deleted with its version incremented.
nextRecycle = GetHandle(0, GetIndex(back));
++recycleCounter;
}
handleList.push_back(handle);
}
else
{
if (index == GetIndex(nextRecycle))
{
index = GetIndex(nextRecycle);
//updates the next recycle.
nextRecycle = handleList[index];
handleList[index] = handle;
--recycleCounter;
return true;
}
if (recycleCounter > 0)
{
HandleType recycle = handleList[index];
IndexType recycleIndex = GetIndex(nextRecycle);
HandleType i = 0;
while (i < recycleCounter)
{
if (GetIndex(handleList[recycleIndex]) == index)
break;
recycleIndex = GetIndex(handleList[recycleIndex]);
++i;
}
--recycleCounter;
if (recycleCounter > 0 && recycleIndex < handleList.size())
handleList[recycleIndex] = recycle;
}
handleList[index] = handle;
}
return true;
}
return false;
}
};
typedef SHHandleGenerator<EntityID, EntityIndex> EntityHandleGenerator;
}
#endif

View File

@ -0,0 +1,50 @@
/*********************************************************************
* \file SHSparseBase.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the SHSparseBase class.
* This is an abstract class for the SHSparseSet template class.
* This allows the SHSparseSetContainer class to store SparseSet of different types.
*
* \copyright Copyright (c) 2021 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_SPARSE_BASE_H
#define SH_SPARSE_BASE_H
#include "../SHECSMacros.h"
namespace SHADE
{
class SHSparseBase
{
protected:
SHSparseBase() = default;
public:
virtual ~SHSparseBase() = default;
virtual void Remove(EntityIndex hash) { (void)hash; };
virtual void Clear() {};
virtual void Swap(EntityIndex hash1, EntityIndex hash2) { (void)hash1; (void)hash2; };
virtual void SwapIndexHash(EntityIndex index1, EntityIndex hash) { (void)index1; (void)hash; };
virtual bool Has(EntityIndex hash) = 0;
virtual void* Get(EntityIndex hash) { (void)hash; return nullptr; };
virtual void Add(EntityIndex hash) { (void)hash; };
virtual EntityIndex Count() = 0;
};
}
#endif

View File

@ -0,0 +1,356 @@
/*********************************************************************
* \file SHSparseSet.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the SHSparseSet template class
* This is a container that allows for fast iteration due to contiguous memory
* storage while having fast lookup time at the cost of memory usage.
*
* \copyright Copyright (c) 2021 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_SPARSE_SET_H
#define SH_SPARSE_SET_H
#include "../SHECSMacros.h"
#include "../General/SHSparseBase.h"
#include "../General/SHHandleGenerator.h"
#include <vector>
#include <array>
#include <algorithm>
namespace SHADE
{
template <typename T, EntityIndex MAX>
class SHSparseSet: public SHSparseBase
{
private:
//Dense array of type T
std::vector<T> denseArray;
//This array stores the key of the corresponding element in the dense array. (eg. entity ID)
std::vector<EntityIndex> denseIndexArray;
//Sparse Array. This array helps with look up
EntityIndex sparseArray[MAX];
public:
/*!*************************************************************************
* \brief Construct a new SHSparseSet object
* Default constructor for the SparseSet class.
***************************************************************************/
SHSparseSet()
{
for (EntityIndex i = 0; i < MAX; ++i)
{
sparseArray[i] = UINT16_MAX;
}
denseArray.reserve(MAX);
denseIndexArray.reserve(MAX);
}
/*!*************************************************************************
* \brief Destroy the SHSparseSet object
* Default destructor for the SparseSet class.
***************************************************************************/
virtual ~SHSparseSet()
{
Clear();
}
/*!*************************************************************************
* \brief
* Clears all elements in the current SparseSet and resets the sparseArray.
***************************************************************************/
void Clear() noexcept
{
denseArray.clear();
denseIndexArray.clear();
for (EntityIndex i = 0; i < MAX; ++i)
{
sparseArray[i] = UINT16_MAX;
}
}
/*!*************************************************************************
* \brief
* Check is the specified hash is in range of the sparseSet.
* \param hash
* Hash to check for.
* \return bool
* true if the specified hash is in range of the spraseSet.
***************************************************************************/
bool IsValid(EntityIndex hash) noexcept
{
return (hash < MAX);
}
/*!*************************************************************************
* \brief
* Checks if this sparseSet contains an element with specified hash.
* \param hash
* The hash to check for.
* \return bool
* true if the element with this hash is found. false if no such element is found.
***************************************************************************/
virtual bool Has(EntityIndex hash) noexcept
{
//Check if the hash passed in is within range.
if (!IsValid(hash) || sparseArray[hash] == UINT16_MAX)
{
return false;
}
return (denseIndexArray[sparseArray[hash]] == hash);
}
/*!*************************************************************************
* \brief
* Get a pointer to the element with the specified hash.
* This is the safe version which does a Has() check and return a nullptr
* if it does not have such an Element
*
* \param hash
* The hash of the element to look for.
* \return T*
* returns a pointer to the element with the specified hash if found.
* returns a nullptr if no such element is found.
***************************************************************************/
T* GetElement_s(EntityIndex hash) noexcept
{
if (!Has(hash))
{
return nullptr;
}
return &denseArray[sparseArray[hash]];
}
/*!*************************************************************************
* \brief
* Get a pointer to the element with the specified hash.
* This bypasses the Has() check and assumes that the hash has a Element
* in the dense array.
*
* \param hash
* The hash of the element to look for.
* \return T*
* returns a pointer to the element with the specified hash if found.
* returns a nullptr if no such element is found.
***************************************************************************/
T* GetElement(EntityIndex hash) noexcept
{
return &denseArray[sparseArray[hash]];
}
/*!*************************************************************************
* \brief
* Operator overload for the subscript operator.
* Gets the element by reference with the hash specified in the subscript.
* This does not handle out of range exceptions.
* \param hash
* The hash of the element we are trying to get
* \return T&
* A reference to the element found.
***************************************************************************/
T& operator[](EntityIndex hash) noexcept
{
return denseArray[sparseArray[hash]];
}
/*!*************************************************************************
* \brief
* Get the number of elements in the current SparseSet
* \return uint16_t
* The number of elements in the SparseSet.
***************************************************************************/
virtual EntityIndex Count() noexcept
{
return (EntityIndex)denseArray.size();
}
/*!*************************************************************************
* \brief
* Getter function for the dense array.
* \return std::vector<T>&
* return a reference to the dense array.
***************************************************************************/
std::vector<T>& GetDense() noexcept
{
return denseArray;
}
/*!*************************************************************************
* \brief
* Const getter function for the dense array.
* \return std::vector<T>&
* return a const reference to the dense array.
***************************************************************************/
const std::vector<T> GetDense() const noexcept
{
return denseArray;
}
/*!*************************************************************************
* \brief
* Swaps two elements in the dense array.
* This only swaps their position in the dense array and updates their
* Sparse index reference in the sprase array accordingly.
* This DOES NOT change the hash of the elements.
* \param hash1
* Hash of the first element
* \param hash2
* Hash of the second element
***************************************************************************/
virtual void Swap(EntityIndex hash1, EntityIndex hash2) noexcept
{
if (!Has(hash1) || !Has(hash2) || hash1 == hash2)
{
return;
}
T tempElement = denseArray[sparseArray[hash1]];
EntityIndex tempIndex = denseIndexArray[sparseArray[hash1]];
EntityIndex tempSparse = sparseArray[hash1];
denseArray[sparseArray[hash1]] = denseArray[sparseArray[hash2]];
denseIndexArray[sparseArray[hash1]] = denseIndexArray[sparseArray[hash2]];
sparseArray[hash1] = sparseArray[hash2];
denseArray[sparseArray[hash2]] = tempElement;
denseIndexArray[sparseArray[hash2]] = tempIndex;
sparseArray[hash2] = tempSparse;
}
/*!*************************************************************************
* \brief
* Swaps two elements in the dense array.
* This only swaps their position in the dense array and updates their
* Sparse index reference in the sprase array accordingly.
* This swaps using a index(position in the dense array) and a hash (EntityID)
* This DOES NOT change the hash of the elements.
* \param index1
* The position in the dense array of the first element
* \param hash
* Hash of the second element
***************************************************************************/
virtual void SwapIndexHash(EntityIndex index1, EntityIndex hash) noexcept
{
if (index1 >= denseArray.size() || !Has(hash))
{
return;
}
Swap(denseIndexArray[index1], hash);
}
/*!*************************************************************************
* \brief
* Adds a new element to the SparseSet.
* \param hash
* The hash of the new element.
* Nothing is added if the SparseSet already contains an element with
* the same hash.
* \param element
* A reference to the element to be added. This will be passed in as a
* copy and the default copy constructor will be used to copy the element
* into the dense array.
***************************************************************************/
void Add(EntityIndex hash, T& element) noexcept
{
if (!IsValid(hash) || Has(hash))
{
return;
}
denseArray.emplace_back(element);
denseIndexArray.emplace_back (hash);
sparseArray[hash] = EntityIndex(denseIndexArray.size() - 1);
}
/**************************************************************************
* \brief
* Adds a new element to the SparseSet.
* \param hash
* The hash of the new element.
* Nothing is added if the SparseSet already contains an element with
* the same hash.
* \return
* None
***************************************************************************/
void Add(EntityIndex hash) noexcept
{
if (!IsValid(hash) || Has(hash))
{
return;
}
denseArray.emplace_back(T());
denseIndexArray.emplace_back(hash);
sparseArray[hash] = EntityIndex(denseIndexArray.size() - 1);
}
/*!*************************************************************************
* \brief
* Removes an element from the Sparse Set
* \param hash
*
***************************************************************************/
virtual void Remove(EntityIndex hash) noexcept
{
if (!Has(hash))
{
return;
}
//Get the index of the item to be removed.
EntityIndex index = sparseArray[hash];
//Get the sparse index of the last element in the dense array.
EntityIndex lastSparse = denseIndexArray.back();
//Copy the contents of the last elements to the replace the element we are trying to remove
//denseArray[index] = denseArray.back();
//denseIndexArray[index] = denseIndexArray.back();
//denseArray.erase(denseArray.begin() + index);
//denseIndexArray.erase(denseIndexArray.begin() + index);
Swap(hash, lastSparse);
//update the sparse array with the new index of our last element.
sparseArray[lastSparse] = index;
sparseArray[hash] = UINT16_MAX;
//Pop out the last element since we no longer need it
denseArray.pop_back();
denseIndexArray.pop_back();
}
/**************************************************************************
* \brief
* Get the element specified by a hash. This will be casted to a void
* pointer
* \param hash
* The Index of the element.
* \return
* A void pointer to the element.
***************************************************************************/
virtual void* Get(EntityIndex hash) noexcept
{
if (!Has(hash))
{
//no such element in this sparse set.
return nullptr;
}
return (void*) &denseArray[sparseArray[hash]];
}
};
}
#endif

View File

@ -0,0 +1,248 @@
/*********************************************************************
* \file SHSparseSetContainer.h
* \author Daniel Chua Yee Chen
* \brief Declaration for SHSparseSetContainer template class.
* This is a container for SHSparseSets meant to contain SparseSets of
* different types that are derived from a shared Base class.
*
* \copyright Copyright (c) 2021 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_SPARSE_SET_CONTAINER_H
#define SH_SPARSE_SET_CONTAINER_H
#include "SHSparseSet.h"
#include "SHFamily.h"
#include "../Components/SHComponent.h"
#include <cassert>
namespace SHADE
{
template<typename Base>
class SHSparseSetContainer
{
private:
public:
//The container of SparseSets.
std::vector<SHSparseBase*> container;
/*!*************************************************************************
* \brief
* Default constructor for the SHSparseSetContainer class
***************************************************************************/
SHSparseSetContainer() = default;
/*!*************************************************************************
* \brief
* Default destructor for the SHSparseSetContainer class.
***************************************************************************/
~SHSparseSetContainer()
{
for (std::vector<SHSparseBase*>::iterator it = container.begin(); it != container.end(); ++it)
{
delete (*it);
}
};
/*!*************************************************************************
* \brief
* Attempts to create a SparseSet for the specified derived class.
* If a SparseSet of the same class type has already been created,
* nothing is re-created.
* @tparam Derived
* The type of the SparseSet to create.
***************************************************************************/
template<typename Derived>
ENABLE_IF_DERIVED(void, Base, Derived) CreateSparseSet() noexcept
{
EntityID typeID = SHFamilyID<Base>::template GetID<Derived>();
if (container.size() > typeID)
{
if (container[typeID] == nullptr)
{
container[typeID] = (SHSparseBase*)new SHSparseSet<Derived, MAX_EID>();
}
return;
}
while (container.size() <= typeID)
{
container.push_back(nullptr);
}
container[typeID] = (SHSparseBase*)new SHSparseSet<Derived,MAX_EID>();
}
//Cant use ENABLE_IF_DERIVED here because the macro is defined before template is defined.
//Hence the return type that is a templated class will be an issue.
/**************************************************************************
* \brief
* Get the sparse set specified by a type. This converts the type to
* a type ID using SHFamilyID and use that as the index.
* This will attempt to Create a sparse set if it doesn't exist.
* \return
* The base class pointer to the sparse set.
***************************************************************************/
template<typename Derived>
std::enable_if_t< std::is_base_of_v<Base,Derived>,SHSparseSet<Derived,MAX_EID>* > GetSparseSet() noexcept
{
EntityID typeID = SHFamilyID<Base>::template GetID<Derived>();
if (container.size() <= typeID || container[typeID] == nullptr)
{
CreateSparseSet<Derived>();
}
return (SHSparseSet<Derived, MAX_EID>*)container[typeID];
}
/**************************************************************************
* \brief
* Get the sparse set at the specified index.
* \param typeID
* The index of the sparse set to get.
* \return
* The base class pointer to the sparse set.
***************************************************************************/
SHSparseBase* GetSparseSet_ID(uint32_t typeID) noexcept
{
//assert(typeID >= container.size());
return container[typeID];
}
/*!*************************************************************************
* \brief
* Get the number of elements in the specified SparseSet
* @tparam Derived
* The type of the SparseSet
***************************************************************************/
template<typename Derived>
inline ENABLE_IF_DERIVED(EntityID, Base, Derived)Count() noexcept
{
return GetSparseSet<Derived>()->Count();
}
/*!*************************************************************************
* \brief
* Get the dense array of the specified SparseSet
* @tparam Derived
* The type of the SparseSet
* \return
* Returns a reference to the dense array of the specified SparseSet
***************************************************************************/
template<typename Derived>
inline std::enable_if_t < std::is_base_of_v<Base, Derived>, std::vector<Derived>& > GetDense() noexcept
{
return GetSparseSet<Derived>()->GetDense();
}
/*!*************************************************************************
* \brief
* Remove all elements with the specified hash from all the SparseSets in
* the container.
* \param hash
* The hash of the elements to remove.
***************************************************************************/
void RemoveElements(EntityIndex hash) noexcept
{
for (std::vector<SHSparseBase*>::iterator it = container.begin(); it != container.end(); ++it)
{
if (*it)
{
(*it)->Remove(hash);
}
}
}
/*!*************************************************************************
* \brief
* Clear all SparseSets in the container.
***************************************************************************/
void ClearAllElements() noexcept
{
for (std::vector<SHSparseBase*>::iterator it = container.begin(); it != container.end(); ++it)
{
(*it)->Clear();
}
}
/*!*************************************************************************
* \brief
* Sets the isActive variable in the elements with the specified hash
* in all the SparseSets in the container.
* This assuumes that the element types has a isActive boolean.
* \param hash
* hash of the elements
* \param active
* The active state to set to.
***************************************************************************/
void SetElementActive(EntityIndex hash, bool active) noexcept
{
(void)active;
for (std::vector<SHSparseBase*>::iterator it = container.begin(); it != container.end(); ++it)
{
if (*it)
{
SHComponent* comp = static_cast<SHComponent*>((*it)->Get(hash));
if (comp)
{
comp->isActive = active;
}
}
}
}
/**************************************************************************
* \brief
* Get the element of a specified hash from the Sparse set of indicated index.
* \param typeIndex
* The index of the sparse set which we are trying to get from
* \param hash
* The hash that the element belongs to.
* \return
* The address of the element casted to a void*
***************************************************************************/
void* GetElement(uint32_t typeIndex, EntityIndex hash) noexcept
{
if (typeIndex >= container.size())
{
return nullptr;
}
return container[typeIndex]->Get(hash);
}
/**************************************************************************
* \brief
* Get the size of the SparseSetContainer.
* \return
* The number of sparse sets in this container.
***************************************************************************/
size_t Size() noexcept
{
return container.size();
}
};
}
#endif

View File

@ -0,0 +1,25 @@
#ifndef SH_MACROS_H
#define SH_MACROS_H
#include <stdint.h>
#include <type_traits>
typedef uint32_t EntityID;
typedef uint16_t EntityIndex;
typedef uint32_t ComponentTypeID;
const EntityIndex MAX_EID = 51000;
#define ENABLE_IF_DERIVED(__RETURN__, __BASE__, __DERIVED__)\
std::enable_if_t<std::is_base_of_v<__BASE__,__DERIVED__>,__RETURN__>
#define ENABLE_IF_UINT(_TYPE, _RETURN)\
typename std::enable_if<(std::is_integral<_TYPE>::value && !std::is_signed<_TYPE>::value),_RETURN>::type
#endif

View File

@ -0,0 +1,102 @@
/*********************************************************************
* \file SHComponentManager.cpp
* \author Daniel Chua Yee Chen
* \brief Definition of functions for the SHComponentManager class.
* This is the interface that the systems are going to use to interacte with
* the components. The SparseSetContainer of components is stored and managed
* here.
*
* \copyright Copyright (c) 2021 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.
*********************************************************************/
#include "SHpch.h"
#include "SHComponentManager.h"
#include "SHEntityManager.h"
#include "SHSystemManager.h"
namespace SHADE
{
SHSparseSetContainer<SHComponent> SHComponentManager::componentSet;
std::vector<SHComponentGroup> SHComponentManager::componentGroups;
void SHComponentManager::Exit() noexcept
{
}
void SHComponentManager::RemoveComponentsOfEntity(EntityID entityID) noexcept
{
if (!SHEntityManager::IsValidEID(entityID))
{
return;
}
for (uint32_t i = 0; i < componentSet.Size(); ++i)
{
SHComponent* comp = (SHComponent*) componentSet.GetElement(i, EntityHandleGenerator::GetIndex(entityID));
if (comp)
{
comp->OnDestroy();
}
}
for (auto & grp : componentGroups)
{
grp.RemoveEntity(entityID);
}
componentSet.RemoveElements(EntityHandleGenerator::GetIndex(entityID));
//entityHandle.RemoveHandle(entityID);
}
bool SHComponentManager::HasComponent_ID(EntityID entityID, uint32_t componentTypeID) noexcept
{
return componentSet.GetSparseSet_ID(componentTypeID)->Has(EntityHandleGenerator::GetIndex(entityID));
}
void SHComponentManager::SetActive(EntityID entityID, bool active) noexcept
{
componentSet.SetElementActive(EntityHandleGenerator::GetIndex(entityID), active);
}
void SHComponentManager::SwapInDenseByIndexHash_ID(EntityIndex index, EntityID hash, uint32_t componentTypeID) noexcept
{
componentSet.GetSparseSet_ID(componentTypeID)->SwapIndexHash(index, EntityHandleGenerator::GetIndex(hash));
}
void SHComponentManager::ChangeParent(EntityID entity, EntityID newParent)
{
for (uint32_t i = 0; i < (uint32_t)componentSet.Size(); ++i)
{
SHComponent* component = (SHComponent*)componentSet.GetElement(i, EntityHandleGenerator::GetIndex(entity));
if (component == nullptr)
{
continue;
}
component->ChangeParent(newParent);
}
}
void SHComponentManager::ChangeParent(EntityID entity)
{
for (uint32_t i = 0; i < (uint32_t)componentSet.Size(); ++i)
{
SHComponent* component = (SHComponent*)componentSet.GetElement(i, EntityHandleGenerator::GetIndex(entity));
if (component == nullptr)
{
continue;
}
component->ChangeParent();
}
}
}

View File

@ -0,0 +1,477 @@
/*********************************************************************
* \file SHComponentManager.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the SHComponentManager class.
* This is the interface that the systems are going to use to interacte with
* the components.
* The SparseSetContainer of components is stored and managed
* here.
*
* \copyright Copyright (c) 2021 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_ENGINE_H
#define SH_ENGINE_H
#include "../General/SHSparseSetContainer.h"
#include "../Components/SHComponent.h"
#include "../Components/SHComponentGroup.h"
//#include "Scene/SHSceneNode.h"
#include <cassert>
namespace SHADE
{
class SHComponentManager
{
private:
//The SparseSetContainer of components.
static SHSparseSetContainer<SHComponent> componentSet;
//The Container of all Componentgroups
static std::vector<SHComponentGroup> componentGroups;
friend class SHSceneNode;
/**************************************************************************
* \brief
* This is called by the SHSceneNode friend class to change the parent
* of an entity.
* This function is privated as it is not for general use.
* \param entity
* The entityID of the entity which to change the parent of.
* \param newParent
* The entityID of the new parent.
***************************************************************************/
static void ChangeParent(EntityID entity, EntityID newParent);
/**************************************************************************
* \brief
* Overloaded function of Change parent to change the parent of an entity to
* the root node.
* This is called by the SHSceneNode friend class to change the parent
* of an entity.
* This function is privated as it is not for general use.
* \param entity
* The entityID of the entity which to change the parent of.
***************************************************************************/
static void ChangeParent(EntityID entity);
protected:
public:
/*!*************************************************************************
* This class is used as a static class.
* No objects of this type should be created
***************************************************************************/
SHComponentManager() = delete;
~SHComponentManager() = delete;
/*!*************************************************************************
* \brief
* Cleans up the memory used in the Engine.
***************************************************************************/
static void Exit() noexcept;
/*!*************************************************************************
* \brief
* Creates the SparseSet for the specified Component type
* @tparam T
* Component Type to create.
***************************************************************************/
template<typename T>
static void CreateComponentSparseSet() noexcept
{
componentSet.CreateSparseSet<T>();
}
/*!*************************************************************************
* \brief
* Getter for the dense array of a specified Component Type.
* @tparam T
* Component Type
* \return std::vector<T>&
* reference to the dense array from the SparseSet of the Component Type T.
***************************************************************************/
template<typename T>
static std::vector<T>& GetDense() noexcept
{
return componentSet.GetDense<T>();
}
/*!*************************************************************************
* \brief
* Gets the Component of the entity with the specified entityID
*
* This is the safe version of GetComponent_s which does a HasComponent to make
* sure that the entity has such a component and returns nullptr if it doesn't
*
* This safe version also checks if the sparse set of this component type
* has been created in SHComponentManager and creates one if it doesn't
*
* @tparam T
* Type of Component to get.
* \param entityID
* EntityID of the entity that we are trying to get the component of.
* \return
* A pointer to the component of the entity.
* Returns nullptr if the entity does not contain such a component.
***************************************************************************/
template<typename T>
static ENABLE_IF_DERIVED(T*, SHComponent, T) GetComponent_s(EntityID entityID) noexcept
{
EntityID typeID = SHFamilyID<SHComponent>::template GetID<T>();
if (componentSet.container.size() <= typeID)
{
componentSet.CreateSparseSet<T>();
return nullptr;
}
return (componentSet.GetSparseSet<T>()->GetElement_s(EntityHandleGenerator::GetIndex(entityID)));
}
/*!*************************************************************************
* \brief
* Gets the Component of the entity with the specified entityID
*
* This is the faster unsafe version.
* Use only when sure that the entity has such a Component type. This could
* give garbage value or the component of a different entity if the entity
* does not have the specified component type. This is usually used with
* ComponentGroups
*
* This unsafe version bypass the HasComponent check.
*
* This unsafe version bypass the check for sparseSet creation.
*
* @tparam T
* Type of Component to get.
* \param entityID
* EntityID of the entity that we are trying to get the component of.
* \return
* A pointer to the component of the entity.
***************************************************************************/
template<typename T>
static ENABLE_IF_DERIVED(T*,SHComponent, T) GetComponent(EntityID entityID) noexcept
{
//EntityID typeID = SHFamilyID<SHComponent>::template GetID<T>();
return (componentSet.GetSparseSet<T>()->GetElement(EntityHandleGenerator::GetIndex(entityID)));
}
/*!*************************************************************************
* \brief
* Add a component to the specified entityID
* Nothing is added if the entityID already have a component of the same type.
* @tparam T
* Type of Component to be added.
* @tparam Args
* The args types to pass to the constructor of the Component
* \param entityID
* EntityID to add this component to.
* \param args
* The args to pass to the constructor of the Component
* \return
* None.
***************************************************************************/
template<typename T >
static ENABLE_IF_DERIVED(void,SHComponent,T) AddComponent(EntityID entityID) noexcept
{
T element{};
componentSet.GetSparseSet_ID(SHFamilyID<SHComponent>::GetID<T>())->Add(EntityHandleGenerator::GetIndex(entityID));
SHComponent* comp = (SHComponent*)componentSet.GetElement(SHFamilyID<SHComponent>::GetID<T>(), EntityHandleGenerator::GetIndex(entityID));
comp->entityID = entityID;
for (auto& grp : componentGroups)
{
grp.AddComponentCheck(entityID, SHFamilyID<SHComponent>::GetID<T>());
}
if (comp)
{
comp->OnCreate();
}
}
/**************************************************************************
* \brief
* Add Component using a component type ID. This assumes that the sparse
* set is already created for the component type ID.
* \param entityID
* The entity ID of the entity
* \param componentTypeID
* The Type ID of the Component Type.
* \return
* none
***************************************************************************/
static void AddComponent(EntityID entityID, uint32_t componentTypeID) noexcept
{
componentSet.GetSparseSet_ID(componentTypeID)->Add(EntityHandleGenerator::GetIndex(entityID));
SHComponent* comp = (SHComponent*)componentSet.GetElement(componentTypeID, EntityHandleGenerator::GetIndex(entityID));
comp->entityID = entityID;
for (auto& grp : componentGroups)
{
grp.AddComponentCheck(entityID,componentTypeID);
}
if (comp)
{
comp->OnCreate();
}
}
/*!*************************************************************************
* \brief
* Checks if the specified entityID has a component of specified type.
* @tparam T
* Type of Component to check for.
* \param entityID
* EntityID to check for.
* \return bool
* True if the entity has a component of specified type.
***************************************************************************/
template<typename T>
static ENABLE_IF_DERIVED(bool, SHComponent, T) HasComponent(EntityID entityID) noexcept
{
return componentSet.GetSparseSet<T>()->Has(EntityHandleGenerator::GetIndex(entityID));
}
/*!*************************************************************************
* \brief
* Checks if the specified entityID has a component of specified type.
* The component type is specified by a component type ID
* @tparam T
* Type of Component to check for.
* \param entityID
* EntityID to check for.
* \param componentTypeID
* The component type ID to look for
* \return bool
* True if the entity has a component of specified type.
***************************************************************************/
static bool HasComponent_ID(EntityID entityID, uint32_t componentTypeID) noexcept;
/*!*************************************************************************
* \brief
* Remove the Component of specified type from the entity.
* @tparam T
* Component type to be removed
* \param entityID
* EntityID of the object to remove the component from.
***************************************************************************/
template<typename T>
static ENABLE_IF_DERIVED(void, SHComponent, T) RemoveComponent(EntityID entityID) noexcept
{
if (!HasComponent<T>(entityID))
{
return;
}
SHComponent* comp = (SHComponent*)componentSet.GetElement(SHFamilyID<SHComponent>::GetID<T>(), EntityHandleGenerator::GetIndex(entityID));
if (comp)
{
comp->OnDestroy();
}
for (auto& grp : componentGroups)
{
grp.RemoveComponentCheck<T>(entityID);
}
componentSet.GetSparseSet<T>()->Remove(EntityHandleGenerator::GetIndex(entityID));
}
/*!*************************************************************************
* \brief
* Swaps to positioning of two components in their dense array.
* This does not swap the entity that the components belong to
* @tparam T
* Type of the Component to swap.
* \param entityID1
* entityID of the first entity.
* \param entityID2
* entityID of the second entity.
* \return
* None.
***************************************************************************/
template<typename T>
static ENABLE_IF_DERIVED(void, SHComponent, T) SwapInDense(EntityID entityID1, EntityID entityID2) noexcept
{
componentSet.GetSparseSet<T>()->Swap(EntityHandleGenerator::GetIndex(entityID1), EntityHandleGenerator::GetIndex(entityID2));
}
/**************************************************************************
* \brief
* Swaps to positioning of two components in their dense array.
* This does not swap the entity that the components belong to.
* This is the overloaded function that takes 2 Entity Index
* \param index
* The entity Index of the first entity.
* \param hash
* The entity Index of the second entity.
* \return
* none
***************************************************************************/
template<typename T>
static ENABLE_IF_DERIVED(void, SHComponent, T) SwapInDenseByIndex(EntityIndex index, EntityIndex hash) noexcept
{
componentSet.GetSparseSet<T>()->SwapIndexHash(index, EntityHandleGenerator::GetIndex(hash));
}
/**************************************************************************
* \brief
* Swaps to positioning of two components in their dense array.
* This does not swap the entity that the components belong to.
* This swap using a component type ID
* \param index
* The entity Index of the first entity
* \param hash
* The entity ID of the second entity
* \param componentTypeID
* The Type ID of the component type ID
* \return
*
***************************************************************************/
static void SwapInDenseByIndexHash_ID(EntityIndex index, EntityID hash, uint32_t componentTypeID) noexcept;
/*!*************************************************************************
* \brief
* Removes and destroy all components tied to an entity.
* entityID
* \param entityID
* entityID of the entity to be removed.
***************************************************************************/
static void RemoveComponentsOfEntity(EntityID entityID) noexcept;
/**************************************************************************
* \brief
* Get the number of components in a component sparse set.
* \param componentTypeID
* The type ID of the Component Type.
* \return
* The number of components in the component sparse set.
***************************************************************************/
static EntityIndex ComponentCount_ID(uint32_t componentTypeID)
{
return componentSet.GetSparseSet_ID(componentTypeID)->Count();
}
/*!*************************************************************************
* \brief
* Set the isActive boolean of all Components that the entity has
* \param entityID
* The entityID of the entity
* \param active
* The active state to set to
***************************************************************************/
static void SetActive(EntityID entityID, bool active) noexcept;
template<typename... T>
static std::enable_if_t<(... && std::is_base_of_v<SHComponent, T>), uint32_t> CreateComponentGroup(uint32_t numOwningComponents)
{
std::vector<uint32_t> templateIDs{ (SHFamilyID<SHComponent>::GetID<T>())... };
for (auto& g : componentGroups)
{
for (auto const& oID : g.ownedComponentTypes)
{
for (uint32_t i = 0; i < numOwningComponents; ++i)
{
if ((templateIDs[i] == oID))
{
assert("This Component is owned by another group");
}
}
}
}
SHComponentGroup grp;
for (uint32_t i = 0; i < numOwningComponents; ++i)
{
grp.ownedComponentTypes.push_back(templateIDs[i]);
}
for (uint32_t i = 0; i < templateIDs.size(); ++i)
{
grp.componentTypeIDs.push_back(templateIDs[i]);
}
if (grp.ownedComponentTypes.size() != grp.componentTypeIDs.size())
{
if (grp.ownedComponentTypes.empty())
{
grp.ownershipType = OWNERSHIP_TYPE::NON_OWNERSHIP;
}
else
{
grp.ownershipType = OWNERSHIP_TYPE::PARTIAL_OWNERSHIP;
}
}
componentGroups.push_back(grp);
return (uint32_t)componentGroups.size() - 1;
}
/**************************************************************************
* \brief
* Get a reference to the component group.
* \param index
* The index of the component group
* \return
* A reference to the component group.
***************************************************************************/
static SHComponentGroup& GetComponentGroup(uint16_t index) noexcept
{
return componentGroups[index];
}
static void AddScriptComponent(EntityID eid, std::string const& scriptClassName) noexcept;
static void RemoveScriptComponent(EntityID eid, std::string const& scriptClassName) noexcept;
};// end SHComponentManager
}
#endif

View File

@ -0,0 +1,232 @@
/*********************************************************************
* \file SHEntityManager.cpp
* \author Daniel Chua Yee Chen
* \brief Implementation for the SHEntityManager class.
* Entity Manager is the interface class where users of the engine
* and its systems interact with entity data in the engine. This
* includes Creation and Destruction of entities and getting Entity by
* an ID. This manager also ensures that each entity have a unique
* handle using SHHandleGenerator.
*
* \copyright Copyright (c) 2021 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.
*********************************************************************/
#include "SHpch.h"
#include "SHEntityManager.h"
//#include "Scene/SHSceneGraph.h"
//#include "Serialization/SHSerialization.h"
namespace SHADE
{
std::vector<std::unique_ptr<SHEntity>> SHEntityManager::entityVec;
EntityHandleGenerator SHEntityManager::entityHandle;
SHEntity* SHEntityManager::GetEntityByID(EntityID entityID) noexcept
{
if (!IsValidEID(entityID))
{
return nullptr;
}
EntityIndex eIndex = entityHandle.GetIndex(entityID);
return entityVec[eIndex].get();
}
bool SHEntityManager::IsValidEID(EntityID entityID) noexcept
{
if (!entityHandle.IsValid(entityID))
return false;
EntityIndex eIndex = entityHandle.GetIndex(entityID);
if (entityVec.size() <= eIndex || !entityVec[eIndex])
return false;
return true;
}
EntityIndex SHEntityManager::GetEntityIndex(EntityID entityID) noexcept
{
return entityHandle.GetIndex(entityID);
}
EntityID SHEntityManager::CreateEntity(std::vector<uint32_t>const& componentTypeIDs, std::string const& name,EntityID parentEID)
{
EntityID eID = entityHandle.GetNewHandle();
EntityIndex eIndex = entityHandle.GetIndex(eID);
if (eIndex > entityVec.size())
{
assert("FATAL ERROR: EntityIndex out of range in Entity Creation");
}
else if (eIndex == entityVec.size())
{
entityVec.emplace_back(std::make_unique<SHEntity>());
}
else
{
if (!entityVec[eIndex])
{
//There is still an entity stored there.Something went wrong
assert("FATAL ERROR: Entity Creation error. Entity Index Conflict");
}
//Reset it to a newly constructed entity
entityVec[eIndex].reset(new SHEntity());
}
entityVec[eIndex]->entityID = eID;
entityVec[eIndex]->name = name;
for (auto& id : componentTypeIDs)
{
SHComponentManager::AddComponent(eID, id);
}
//(SHComponentManager::AddComponent<ComponentTypes>(eID), ...);
/*if (entityHandle.IsValid(parentEID) == false)
{
entityVec[eIndex]->sceneNode.ConnectToRoot();
}
else
{
entityVec[eIndex]->SetParent(parentEID);
}*/
//TODO Link to Scene graph.
return eID;
}
EntityID SHEntityManager::CreateEntity(std::vector<uint32_t>const& componentTypeIDs, EntityID desiredEID, std::string const& name, EntityID parentEID)
{
EntityID eID ;
if (entityHandle.ClaimHandle(desiredEID) == true)
eID = desiredEID;
else
eID = entityHandle.GetNewHandle();
EntityIndex eIndex = entityHandle.GetIndex(eID);
if (eIndex > entityVec.size())
{
EntityIndex size = (EntityIndex)entityVec.size();
for (EntityIndex i = 0; i <= eIndex - size; ++i)
{
entityVec.push_back(nullptr);
}
entityVec[eIndex].reset(new SHEntity());
}
else if (eIndex == entityVec.size())
{
entityVec.emplace_back(std::make_unique<SHEntity>());
}
else
{
if (!entityVec[eIndex])
{
//There is still an entity stored there.Something went wrong
assert("FATAL ERROR: Entity Creation error. Entity Index Conflict");
}
//Reset it to a newly constructed entity
entityVec[eIndex].reset(new SHEntity());
}
entityVec[eIndex]->entityID = eID;
entityVec[eIndex]->name = name;
for (auto& id : componentTypeIDs)
{
SHComponentManager::AddComponent(eID, id);
}
//(SHComponentManager::AddComponent<ComponentTypes>(eID), ...);
//if (entityHandle.IsValid(parentEID) == false)
//{
// entityVec[eIndex]->sceneNode.ConnectToRoot();
//}
//else
//{
// entityVec[eIndex]->SetParent(parentEID);
//}
//TODO Link to scene graph.
return eID;
}
void SHEntityManager::DestroyEntity(EntityID eID) noexcept
{
if (!IsValidEID(eID))
{
//Entity does not exist or already destroyed.
return;
}
EntityIndex eIndex = entityHandle.GetIndex(eID);
//Call all the children to Destroy themselves first before the parent is destroyed.
if (entityVec[eIndex])
{
//auto& children = entityVec[eIndex]->GetChildrenID();
//while(!children.empty())
//{
// DestroyEntity(children[0]);
//}
//SHSceneNode* parentNode = entityVec[eIndex]->GetSceneNode()->GetParent();
//SHSceneGraph::RemoveChild(parentNode,entityVec[eIndex].get());
//TODO remove from parent and recursively delete child.
SHComponentManager::RemoveComponentsOfEntity(eID);
entityHandle.RemoveHandle(eID);
entityVec[eIndex].reset(nullptr);
}
}
void SHEntityManager::DestroyAllEntity() noexcept
{
for (auto& entity : entityVec)
{
if (entity)
{
DestroyEntity(entity->GetEID());
}
}
entityHandle.ClearAllHandles();
}
EntityIndex SHEntityManager::GetEntityCount() noexcept
{
return entityHandle.GetActiveHandleCount();
}
/*EntityID SHEntityManager::DuplicateEntity(EntityID eid) noexcept
{
if (entityHandle.IsValid(eid) == false)
return MAX_EID;
std::string data = SHSerialization::SerializeEntityToString(eid);
return SHSerialization::DeserializeEntityToSceneFromString(data);
}*/
}

View File

@ -0,0 +1,253 @@
/*********************************************************************
* \file SHEntityManger.h
* \author Daniel Chua Yee Chen
* \brief Definition for the SHEntityManager class.
* Entity Manager is the interface class where users of the engine
* and its systems interact with entity data in the engine. This
* includes Creation and Destruction of entities and getting Entity by
* an ID. This manager also ensures that each entity have a unique
* handle using SHHandleGenerator.
*
* \copyright Copyright (c) 2021 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_ENTITY_MANAGER_H
#define SH_ENTITY_MANAGER_H
#include <vector>
#include <memory>
#include "../Entity/SHEntity.h"
#include "../Components/SHComponent.h"
#include "../General/SHHandleGenerator.h"
#include "../SHECSMacros.h"
namespace SHADE
{
class SHEntityManager
{
private:
static std::vector<std::unique_ptr<SHEntity>> entityVec;
//The handle generator to generate entityIDs.
static EntityHandleGenerator entityHandle;
public:
SHEntityManager() = delete;
~SHEntityManager() = delete;
/**************************************************************************
* \brief
* Get a pointer to the entity using the entityID.
* \param entityID
* The entity ID of the entity
* \return
* A pointer to the entity.
* A nullptr is returned if there is no such entity or if it is destroyed.
***************************************************************************/
static SHEntity* GetEntityByID(EntityID entityID) noexcept;
/**************************************************************************
* \brief
* Checks if the current Entity ID is in use and alive.
* \param entityID
* The entity ID of the entity
* \return
* true if the entity ID is in use and alive.
***************************************************************************/
static bool IsValidEID(EntityID entityID) noexcept;
/**************************************************************************
* \brief
* Get the index portion of the Entity ID.
* \param entityID
* The entity ID of the entity
* \return
* The index of the entity
***************************************************************************/
static EntityIndex GetEntityIndex(EntityID entityID) noexcept;
/**************************************************************************
* \brief
* Template function to Create a new Entity with Components specified
* by the template arguements.
* \param name
* Name of the entity (This is not unique)
* \param parentEID
* The entity ID of the parent. This does not call UpdateHierarchy hence
* the parent of the entity is not updated until UpdateHierarchy is called.
* \return
* EntityID of the new Entity
***************************************************************************/
template<typename ...ComponentTypes>
static std::enable_if_t<(... && std::is_base_of_v<SHComponent, ComponentTypes>), EntityID> CreateEntity(std::string const& name = "Default", EntityID parentEID = MAX_EID)
{
EntityID eID = entityHandle.GetNewHandle();
EntityIndex eIndex = entityHandle.GetIndex(eID);
if (eIndex > entityVec.size())
{
assert("FATAL ERROR: EntityIndex out of range in Entity Creation");
}
else if (eIndex == entityVec.size())
{
entityVec.emplace_back(std::make_unique<SHEntity>());
}
else
{
if (!entityVec[eIndex])
{
//There is still an entity stored there.Something went wrong
assert("FATAL ERROR: Entity Creation error. Entity Index Conflict");
}
//Reset it to a newly constructed entity
entityVec[eIndex].reset(new SHEntity());
}
entityVec[eIndex]->entityID = eID;
entityVec[eIndex]->name = name;
(SHComponentManager::AddComponent<ComponentTypes>(eID),...);
/*if (entityHandle.IsValid(parentEID) == false)
{
entityVec[eIndex]->sceneNode.ConnectToRoot();
}
else
{
entityVec[eIndex]->SetParent(parentEID);
}*/
//TODO Link up with Scene graph
return eID;
}
template<typename ...ComponentTypes>
static std::enable_if_t<(... && std::is_base_of_v<SHComponent, ComponentTypes>), EntityID> CreateEntity(EntityID desiredEID, std::string const& name = "Default", EntityID parentEID = MAX_EID)
{
EntityID eID;
if (entityHandle.ClaimHandle(desiredEID) == true)
eID = desiredEID;
else
eID = entityHandle.GetNewHandle();
EntityIndex eIndex = entityHandle.GetIndex(eID);
if (eIndex > entityVec.size())
{
EntityIndex size = (EntityIndex)entityVec.size();
for (EntityIndex i = 0; i <= eIndex - size; ++i)
{
entityVec.push_back(nullptr);
}
entityVec[eIndex].reset(new SHEntity());
}
else if (eIndex == entityVec.size())
{
entityVec.emplace_back(std::make_unique<SHEntity>());
}
else
{
if (!entityVec[eIndex])
{
//There is still an entity stored there.Something went wrong
assert("FATAL ERROR: Entity Creation error. Entity Index Conflict");
}
//Reset it to a newly constructed entity
entityVec[eIndex].reset(new SHEntity());
}
entityVec[eIndex]->entityID = eID;
entityVec[eIndex]->name = name;
(SHComponentManager::AddComponent<ComponentTypes>(eID), ...);
/*if (entityHandle.IsValid(parentEID) == false)
{
entityVec[eIndex]->sceneNode.ConnectToRoot();
}
else
{
entityVec[eIndex]->SetParent(parentEID);
}*/
//Link up with scene graph.
return eID;
}
/**************************************************************************
* \brief
* Create Entity using a vector of ComponentTypeIDs.
* \param componentTypeIDs
* Vector of ComponentTypeIDs. This assumes that CreateSparseSet is called
* for these ComponentTypes.
* \param name
* Name of the Entity (this is not unique)
* \param parentEID
* The entity ID of the parent. This does not call UpdateHierarchy hence
* the parent of the entity is not updated until UpdateHierarchy is called.
* \return
* EntityID of the new Entity
***************************************************************************/
static EntityID CreateEntity(std::vector<ComponentTypeID>const& componentTypeIDs,std::string const& name = "Default", EntityID parentEID = MAX_EID);
/**************************************************************************
* \brief
* Create Entity using a vector of ComponentTypeIDs.
* \param componentTypeIDs
* Vector of ComponentTypeIDs. This assumes that CreateSparseSet is called
* for these ComponentTypes.
* \param name
* Name of the Entity (this is not unique)
* \param parentEID
* The entity ID of the parent. This does not call UpdateHierarchy hence
* the parent of the entity is not updated until UpdateHierarchy is called.
* \return
* EntityID of the new Entity
***************************************************************************/
static EntityID CreateEntity(std::vector<ComponentTypeID>const& componentTypeIDs, EntityID desiredEID, std::string const& name = "Default", EntityID parentEID = MAX_EID);
/**************************************************************************
* \brief
* Destroy the entity and all components tied to this entity.
* This calls Destroy Entity for all of its children as well.
* \param eID
* The entity ID of the entity
* \return
* none
***************************************************************************/
static void DestroyEntity(EntityID eID) noexcept;
/**************************************************************************
* \brief
* Destroy all Entities.
* \return
* none
***************************************************************************/
static void DestroyAllEntity() noexcept;
/**************************************************************************
* \brief
* Get the current number of entities.
* \return
* Number of entities.
***************************************************************************/
static EntityIndex GetEntityCount() noexcept;
//static EntityID DuplicateEntity(EntityID eid) noexcept;
protected:
};
}
#endif

View File

@ -0,0 +1,55 @@
/*********************************************************************
* \file SHSystem.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the SHSytem abstract base class
*
* \copyright Copyright (c) 2021 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_SYSTEM_H
#define SH_SYSTEM_H
namespace SHADE
{
class SHSystem
{
protected:
/*!*************************************************************************
* \brief
* Protected default constructor for SHSytem class
***************************************************************************/
SHSystem()= default;
public:
/*!*************************************************************************
* \brief
* Destructor for SHSytem class
***************************************************************************/
virtual ~SHSystem() = default;
/*!*************************************************************************
* \brief
* Pure virtual Init function. Derived class must implement this
***************************************************************************/
virtual void Init() = 0;
/*!*************************************************************************
* \brief
* Pure virtual Run function. Derived class must implement this
* \param dt
* Delta time
***************************************************************************/
virtual void Run(float dt) = 0;
/*!*************************************************************************
* \brief
* Pure virtual Exit function. Derived class must implement this
***************************************************************************/
virtual void Exit() = 0;
};
}
#endif

View File

@ -0,0 +1,57 @@
/*********************************************************************
* \file SHSystemManager.cpp
* \author Daniel Chua Yee Chen
* \brief Implementation for the SHSystemManager class.
* SHSystemManager is the interface class where users of the engine create
* the systems that gives the components their functionality. This also
* ensures that the Init and Exit functions are ran at the appropriate time
*
* \copyright Copyright (c) 2021 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.
*********************************************************************/
#include "SHpch.h"
#include "SHSystemManager.h"
#include <iostream>
namespace SHADE
{
SHSystemManager::SystemContainer SHSystemManager::systemContainer;
SHSystem* SHSystemManager::GetSystem(std::string name)
{
if (systemContainer.find(name) == systemContainer.end())
{
assert("Get System Error: No system with such name exist.");
return nullptr;
}
return systemContainer.find(name)->second.get();
}
void SHSystemManager::Init() noexcept
{
for (auto& system : systemContainer)
{
system.second->Init();
#ifdef _DEBUG
std::cout << system.first << " Init" << std::endl;
#endif
}
}
void SHSystemManager::Exit() noexcept
{
for (auto& system : systemContainer)
{
system.second->Exit();
//delete system.second;
}
systemContainer.clear();
}
}

View File

@ -0,0 +1,102 @@
/*********************************************************************
* \file SHSystemManager.h
* \author Daniel Chua Yee Chen
* \brief Declaration for the SHSystemManager class.
* SHSystemManager is the interface class where users of the engine create
* the systems that gives the components their functionality. This also ensures that the Init and Exit functions are ran at the appropriate time
*
* \copyright Copyright (c) 2021 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_SYSTEM_MANAGER_H
#define SH_SYSTEM_MANAGER_H
#include <unordered_map>
#include <memory>
#include <string>
#include <cassert>
#include "../System/SHSystem.h"
namespace SHADE
{
class SHSystemManager
{
//type definition for the container we use to store our system
using SystemContainer = std::unordered_map<std::string, std::unique_ptr<SHSystem>>;
private:
static SystemContainer systemContainer;
public:
/*!*************************************************************************
* This class is used as a static class.
* No objects of this type should be created
***************************************************************************/
SHSystemManager() = delete;
~SHSystemManager() = delete;
/**************************************************************************
* \brief
* Create a system of type T and map it to a name.
* throws an error if a system with the same name already exists.
* \param name
* name of the system
* \return
* none
***************************************************************************/
template<typename T>
static std::enable_if_t<std::is_base_of_v<SHSystem, T>, void> CreateSystem(std::string const& name)
{
if (systemContainer.find(name) != systemContainer.end())
{
assert("System Creation Error: System with the same name already exist.");
}
systemContainer.emplace(name, std::make_unique<T>());
}
/**************************************************************************
* \brief
* Get a pointer to the System with a specified name.
* \param name
* Name of the system in the map
* \return
* Base System pointer.
***************************************************************************/
static SHSystem* GetSystem(std::string name);
/**************************************************************************
* \brief
* Call the Init function of all systems.
* \return
* none
***************************************************************************/
static void Init() noexcept;
/**************************************************************************
* \brief
* Call the Exit function of all systems.
* \return
***************************************************************************/
static void Exit() noexcept;
protected:
};
}
#endif

View File

@ -1,2 +1 @@
#include "SHpch.h"
#include "SHEngine.h"
#include "SHpch.h"

View File

@ -1,9 +1,39 @@
#pragma once
#ifndef SH_ENGINE_H
#define SH_ENGINE_H
#include <utility>
#include "Meta/SHIsDetected.h"
namespace SHADE
{
class SHEngine
{
};
}
template <typename Application>
using GetInit_t = decltype (std::declval<Application&>().Initialize());
template <typename Application>
using GetUpdate_t = decltype (std::declval<Application&>().Update());
template <typename Application>
using GetExit_t = decltype (std::declval<Application&>().Exit());
class SHEngine
{
public:
template <class Application, typename ... Args>
static void Run(Args&&...args)
{
//static_assert(SHIsDetected<GetInit_t, Application>::value, "Init Not Detected");
static_assert(SHIsDetected<GetUpdate_t, Application>::value, "Update Not Detected");
static_assert(SHIsDetected<GetExit_t, Application>::value, "Exit Not Detected");
static Application application;
application.Initialize(std::forward<Args>(args)...);
application.Update();
application.Exit();
};
};
};
#endif

View File

@ -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<SHVkCommandBuffer> const& cmdBufferHdl) noexcept
{
if (cmdBufferHdl && (bufferUsageFlags & vk::BufferUsageFlagBits::eTransferDst))
{
vk::BufferCopy copyRegion
{
.srcOffset = 0,
.dstOffset = 0,
.size = sizeStored,
};
cmdBufferHdl->GetVkCommandBuffer().copyBuffer(stagingBuffer, vkBuffer, 1, &copyRegion);
}
}
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<uint8_t*>(mappedPtr) + dstOffset, static_cast<uint8_t*>(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<uint8_t*>(mappedPtr) + dstOffset, static_cast<uint8_t*>(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<uint8_t*>(stagingBufferMappedPtr), static_cast<uint8_t*>(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<VmaAllocator const> 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<VmaAllocator const> 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);
}
}

View File

@ -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 const> vmaAllocator;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void PrepStagingBuffer (void* data, uint32_t srcSize) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHVkBuffer (void) noexcept = delete;
SHVkBuffer (std::reference_wrapper<VmaAllocator const> allocator) noexcept;
SHVkBuffer (
uint32_t inSize,
void* data,
uint32_t srcSize,
std::reference_wrapper<VmaAllocator const> 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<SHVkCommandBuffer> const& cmdBufferHdl) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
vk::Buffer GetVkBuffer (void) const noexcept;
};
}
#endif

View File

@ -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

View File

@ -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<SHVkRenderpass> const& renderpassHdl, Handle<SHVkFramebuffer> 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<uint32_t>(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<SHVkPipeline> 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<SHVkBuffer> 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<SHVkBuffer> 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<SHVkRenderpass> 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<SHVkCommandPool> 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;
}
}

View File

@ -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<SHVkCommandBuffer>;
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<SHVkCommandPool> parentPool;
//! The currently bound pipeline
Handle<SHVkPipelineLayout> boundPipelineLayoutHdl;
//! The push constant data for the command buffer
uint8_t pushConstantData[PUSH_CONSTANT_SIZE];
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
bool IsRenderAreaOptimal(Handle<SHVkRenderpass> 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<SHVkCommandPool> 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<SHVkRenderpass> const& renderpassHdl, Handle<SHVkFramebuffer> 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<SHVkPipeline> const& pipelineHdl) noexcept;
void BindVertexBuffer (uint32_t bindingPoint, Handle<SHVkBuffer> const& buffer, vk::DeviceSize offset) noexcept;
void BindIndexBuffer (Handle<SHVkBuffer> 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 <typename T>
void SetPushConstantVariable(std::string variableName, T const& data) noexcept
{
memcpy (static_cast<uint8_t*>(pushConstantData) + boundPipelineLayoutHdl->GetPushConstantInterface().GetOffset(variableName), &data, sizeof (T));
};
void SubmitPushConstants (void) const noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS AND SETTERS */
/*-----------------------------------------------------------------------*/
vk::CommandBuffer const& GetVkCommandBuffer(void) const noexcept;
};
}
#endif

View File

@ -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<SHVkLogicalDevice> 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<ISelfHandle<SHVkCommandPool>&>(*this) = static_cast<ISelfHandle<SHVkCommandPool>&>(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<SHVkCommandBuffer> 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<SHVkCommandBuffer>(GetHandle(), type));
return primaries.back();
}
// If type requested for is secondary
else
{
secondaries.emplace_back(SHVkInstance::GetResourceManager().Create<SHVkCommandBuffer>(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<SHVkLogicalDevice> 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;
}
}

View File

@ -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<SHVkCommandPool>
{
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<Handle<SHVkCommandBuffer>> primaries;
//! secondary command buffers
std::vector<Handle<SHVkCommandBuffer>> secondaries;
//! Handle to logical device for convenience
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! Whether or not buffers created from here are to be short lived
bool transient;
public:
/*-----------------------------------------------------------------------*/
/* CTOR AND DTOR */
/*-----------------------------------------------------------------------*/
SHVkCommandPool (Handle<SHVkLogicalDevice> 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<SHVkCommandBuffer> RequestCommandBuffer (SH_CMD_BUFFER_TYPE type);
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
SH_CMD_POOL_RESET GetPoolResetMode(void) const noexcept;
vk::CommandPool GetVkCommandPool(void) const noexcept;
Handle<SHVkLogicalDevice> const& GetLogicalDevice(void) const noexcept;
bool GetIsTransient (void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,86 @@
#include "SHPch.h"
#include "SHValidationLayersQuery.h"
#include "Tools/SHLogger.h"
namespace SHADE
{
/*-------------------------------------------------------------------------*/
/* STATIC VARIABLE DECLARATION */
/*-------------------------------------------------------------------------*/
std::vector<vk::LayerProperties> SHValidationLayersQuery::queriedLayers;
std::vector<char const*> SHValidationLayersQuery::availableLayers;
// TODO: Not sure if necessary but it would be nice to load validation layers
// from a file.
std::vector<char const*> 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<char const*> const& SHValidationLayersQuery::GetAvailableLayers(void) noexcept
{
return availableLayers;
}
}

View File

@ -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 <vector> // 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<char const*> requiredLayers;
//! Queried layers
static std::vector<vk::LayerProperties> queriedLayers;
//! Final list that should be used to see what validation layers are available
static std::vector<char const*> availableLayers;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC STATIC FUNCTIONS */
/*-----------------------------------------------------------------------*/
static void GenerateAvailableLayers (bool renderdocEnabled) noexcept;
static std::vector<char const*> const& GetAvailableLayers (void) noexcept;
};
}
#endif

View File

@ -0,0 +1,84 @@
#include "SHPch.h"
#include <iostream>
#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);
}
}

View File

@ -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 <typename... Sevs, typename = std::enable_if<(std::is_same_v<Sevs, SH_DEBUG_MSG_SEV> && ...)>>
static VkDebugUtilsMessageSeverityFlagsEXT GenMessengerSeverity(Sevs... severities)
{
return (static_cast<VkDebugUtilsMessageSeverityFlagsEXT>(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 <typename... Types, typename = std::enable_if<(std::is_same_v<Types, SH_DEBUG_MSG_TYPE> && ...)>>
static VkDebugUtilsMessageTypeFlagsEXT GenMessengerType(Types... types)
{
return (static_cast<VkDebugUtilsMessageTypeFlagsEXT>(types) | ...);
}
};
}
#endif

View File

@ -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<vk::Result> (pCallbackData->messageIdNumber), pCallbackData->pMessage);
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
ReportVkError(static_cast<vk::Result> (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>(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";
}
}
}

View File

@ -0,0 +1,30 @@
#ifndef SH_DEBUG_UTIL_H
#define SH_DEBUG_UTIL_H
#include "Graphics/SHVulkanIncludes.h"
//#include <vulkan/vulkan_handles.hpp>
#include <source_location>
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

View File

@ -0,0 +1,2 @@
#include "SHPch.h"
#include "SHDescriptorPoolManager.h"

View File

@ -0,0 +1,5 @@
#pragma once
class SHDescriptorPoolManager
{
};

View File

@ -0,0 +1,2 @@
#include "SHPch.h"
#include "SHDescriptorPoolStorage.h"

View File

@ -0,0 +1,5 @@
#pragma once
class SHDescriptorPoolStorage
{
};

View File

@ -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<SHVkLogicalDevice> device, const Config& config)
: device { device }
{
// Create the Pool
const vk::DescriptorPoolCreateInfo POOL_CREATE_INFO
{
.flags = config.Flags,
.maxSets = config.MaxSets,
.poolSizeCount = static_cast<uint32_t>(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<Handle<SHVkDescriptorSetGroup>> SHVkDescriptorPool::Allocate(const std::vector<Handle<SHVkDescriptorSetLayout>>& layouts, std::vector<uint32_t> const& variableDescCounts)
{
SHVkInstance::GetResourceManager().Create<SHVkDescriptorSetGroup>(device, GetHandle(), layouts, variableDescCounts);
return {};
}
}

View File

@ -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 */
/*---------------------------------------------------------------------------------*/
/// <summary>
///
/// </summary>
class SHVkDescriptorPool : public ISelfHandle<SHVkDescriptorPool>
{
public:
/*-----------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Configuration object for the Pool Manager.
/// </summary>
struct Config
{
public:
/// <summary>
/// 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.
/// </summary>
std::vector<vk::DescriptorPoolSize> Limits =
{
{ vk::DescriptorType::eCombinedImageSampler, 100 },
{ vk::DescriptorType::eUniformBuffer, 100 }
};
/// <summary>
/// Maximum number of descriptor sets allowed
/// </summary>
uint32_t MaxSets = 100;
/// <summary>
/// Flags used to create the DescriptorPool
/// </summary>
vk::DescriptorPoolCreateFlags Flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet;
};
/*-----------------------------------------------------------------------------*/
/* Constructor/Destructor */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Constructor for a Descriptor Pool.
/// </summary>
/// <param name="device">
/// The Vulkan logical device that is used to create a Descriptor Pool.
/// </param>
/// <param name="config">
/// Configuration object that describes how to construct the Descriptor Pool.
/// </param>
SHVkDescriptorPool(Handle<SHVkLogicalDevice> device, const Config& config = {});
SHVkDescriptorPool(const SHVkDescriptorPool&) = delete;
SHVkDescriptorPool(SHVkDescriptorPool&& rhs) noexcept = default;
/// <summary>
/// Destructor which will unload and deallocate all resources for this Pool.
/// </summary>
~SHVkDescriptorPool() noexcept;
/*-----------------------------------------------------------------------------*/
/* Overloaded Operators */
/*-----------------------------------------------------------------------------*/
SHVkDescriptorPool& operator=(const SHVkDescriptorPool&) = delete;
SHVkDescriptorPool& operator=(SHVkDescriptorPool&& rhs) noexcept = default;
/*-----------------------------------------------------------------------------*/
/* Getter Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Retrieves the handle to the Vulkan Descriptor Pool handle.
/// </summary>
/// <returns>Handle to the Vulkan Descriptor Pool.</returns>
[[nodiscard]]
inline const vk::DescriptorPool& GetVkHandle() const { return pool; }
/*-----------------------------------------------------------------------------*/
/* Usage Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Allocates multiple DescriptorSets based on the provided layouts.
/// </summary>
/// <param name="layouts">Layouts of DescriptorSets to create.</param>
/// <exception cref="std::invalid_argument">
/// Thrown if an incompatible layout was provided to this Descriptor Pool.
/// </exception>
/// <returns>
/// Handles to the created Descriptor Sets. If this DescriptorPool has run out of
/// space, lesser number of Handles will be returned.
/// </returns>
std::vector<Handle<SHVkDescriptorSetGroup>> Allocate(const std::vector<Handle<SHVkDescriptorSetLayout>>& layouts, std::vector<uint32_t> const& variableDescCounts);
private:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Handle<SHVkLogicalDevice> device;
vk::DescriptorPool pool;
};
}

View File

@ -0,0 +1,85 @@
#include "SHPch.h"
#include "SHVkDescriptorSetGroup.h"
#include <array>
#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<SHVkLogicalDevice> deviceHdl, Handle<SHVkDescriptorPool> pool, std::vector<Handle<SHVkDescriptorSetLayout>> const& layouts, std::vector<uint32_t> const& variableDescCounts)
: device{deviceHdl}
, descPool {pool}
, descSets{}
{
// Create the layout for each concurrent frame
std::vector<vk::DescriptorSetLayout> 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<uint32_t>(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<uint32_t>(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);
}
}

View File

@ -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 */
/*---------------------------------------------------------------------------------*/
/// <summary>
///
/// </summary>
class SHVkDescriptorSetGroup
{
public:
/*-----------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// 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.
/// </summary>
/// <param name="device">Vulkan logical device used to create the Set.</param>
/// <param name="pool">Descriptor Pool used to create the Set.</param>
/// <param name="layout">Descriptor Set Layout to create the Set with.</param>
SHVkDescriptorSetGroup(Handle<SHVkLogicalDevice> deviceHdl, Handle<SHVkDescriptorPool> pool,
std::vector<Handle<SHVkDescriptorSetLayout>> const& layouts,
std::vector<uint32_t> const& variableDescCounts);
SHVkDescriptorSetGroup(const SHVkDescriptorSetGroup&) = delete;
SHVkDescriptorSetGroup(SHVkDescriptorSetGroup&& rhs) noexcept = default;
/// <summary>
/// Destructor which will unload and deallocate all resources for this Descriptor Set.
/// </summary>
~SHVkDescriptorSetGroup() noexcept;
/*-----------------------------------------------------------------------------*/
/* Overloaded Operators */
/*-----------------------------------------------------------------------------*/
SHVkDescriptorSetGroup& operator=(const SHVkDescriptorSetGroup&) = delete;
SHVkDescriptorSetGroup& operator=(SHVkDescriptorSetGroup&& rhs) noexcept = default;
/*-----------------------------------------------------------------------------*/
/* Getter Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Retrieves the handle to the Vulkan Descriptor Set handle.
/// </summary>
/// <returns>Handle to the Vulkan Descriptor Set.</returns>
[[nodiscard]]
inline const std::vector<vk::DescriptorSet>& GetVkHandle() { return descSets; }
private:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Handle<SHVkLogicalDevice> device;
Handle<SHVkDescriptorPool> descPool;
std::vector<vk::DescriptorSet> descSets;
};
}

View File

@ -0,0 +1,102 @@
#include "SHPch.h"
#include "SHVkDescriptorSetLayout.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Constructor/Destructor */
/*---------------------------------------------------------------------------------*/
SHVkDescriptorSetLayout::SHVkDescriptorSetLayout(Handle<SHVkLogicalDevice> device, const std::vector<Binding>& 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<vk::DescriptorSetLayoutBinding> 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<vk::DescriptorBindingFlags> 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<uint32_t>(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<uint32_t>(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;
}
}

View File

@ -0,0 +1,108 @@
#pragma once
// Project Includes
#include "Graphics/SHVulkanIncludes.h"
#include "Resource/Handle.h"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Forward Declarations */
/*---------------------------------------------------------------------------------*/
class SHVkLogicalDevice;
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
/// <summary>
/// RAII wrapper object for a Vulkan Descriptor Set Layout object.
/// </summary>
class SHVkDescriptorSetLayout
{
public:
/*-----------------------------------------------------------------------------*/
/* Type Definitions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Object that describes how a descriptor binding in a DescriptorSetLayout is
/// structured.
/// </summary>
struct Binding
{
/*-------------------------------------------------------------------------*/
/* Constants */
/*-------------------------------------------------------------------------*/
/// <summary>
/// If set for the "BindPoint", binding points are automatically calculated.
/// </summary>
static constexpr uint32_t AUTO_CALC_BINDING = std::numeric_limits<uint32_t>::max();
/// <summary>
/// For use in Binding DescriptorCount.
/// </summary>
static constexpr uint32_t VARIABLE_DESCRIPTOR_UPPER_BOUND = 2000;
/*-------------------------------------------------------------------------*/
/* Data Members */
/*-------------------------------------------------------------------------*/
/// <summary>
/// Type of element for the descriptor.
/// </summary>
vk::DescriptorType Type = {};
/// <summary>
/// Shader stage that this binding is for.
/// </summary>
vk::ShaderStageFlags Stage = {};
/// <summary>
/// Binding point for the Descriptor within the Descriptor Set.
/// </summary>
uint32_t BindPoint = AUTO_CALC_BINDING;
/// <summary>
/// Number of elements in the binding. When VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT
/// is used in VkDescriptorBindingFlagBits, this value represents the upper bound.
/// </summary>
uint32_t DescriptorCount = 1;
vk::DescriptorBindingFlags flags = {};
};
/*-----------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*-----------------------------------------------------------------------------*/
SHVkDescriptorSetLayout() = delete;
/// <summary>
/// Constructs a DescriptorSetLayout with the specified properties and device.
/// </summary>
/// <param name="device"></param>
/// <param name="bindings"></param>
SHVkDescriptorSetLayout(Handle<SHVkLogicalDevice> device, const std::vector<Binding>& bindings);
SHVkDescriptorSetLayout(const SHVkDescriptorSetLayout&) = delete;
SHVkDescriptorSetLayout(SHVkDescriptorSetLayout&& rhs) noexcept;
/// <summary>
/// Destructor which will unload and deallocate all resources for this Set.
/// </summary>
~SHVkDescriptorSetLayout() noexcept;
/*-----------------------------------------------------------------------------*/
/* Overloaded Operators */
/*-----------------------------------------------------------------------------*/
SHVkDescriptorSetLayout& operator=(const SHVkDescriptorSetLayout&) = delete;
SHVkDescriptorSetLayout& operator=(SHVkDescriptorSetLayout&& rhs) noexcept;
/*-----------------------------------------------------------------------------*/
/* Getter Functions */
/*-----------------------------------------------------------------------------*/
/// <summary>
/// Retrieves the handle to the Vulkan Descriptor Set Layout handle.
/// </summary>
/// <returns>Handle to the Vulkan Descriptor Set Layout handle.</returns>
inline const vk::DescriptorSetLayout& GetVkHandle() const { return setLayout; }
private:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
Handle<SHVkLogicalDevice> device;
vk::DescriptorSetLayout setLayout;
std::vector<Binding> layoutDesc; // Stores description of the layout
};
}

View File

@ -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<SHQueueParams> 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<std::size_t>(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<Handle<SHVkQueue>>());
}
}
// 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<uint32_t>(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<SHVkQueue>(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<SHQueueParams> queueCreateParams, Handle<SHVkPhysicalDevice> 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<uint32_t>(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<std::size_t>(SH_QUEUE_FAMILY_ARRAY_INDEX::GRAPHICS);
else if (queueFamProps[i].queueFlags & vk::QueueFlagBits::eCompute)
index = static_cast<std::size_t>(SH_QUEUE_FAMILY_ARRAY_INDEX::COMPUTE);
else if (queueFamProps[i].queueFlags & vk::QueueFlagBits::eTransfer)
index = static_cast<std::size_t>(SH_QUEUE_FAMILY_ARRAY_INDEX::TRANSFER);
queueFamilyIndices.indices[index] = static_cast<SHQueueFamilyIndex>(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<vk::DeviceQueueCreateInfo> 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<std::size_t>(params.arrayIndex)].value();
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority; // 1.0f
queueCreateInfos.push_back(queueCreateInfo);
}
std::vector<const char*> 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<uint32_t>(queueCreateInfos.size()),
.pQueueCreateInfos = queueCreateInfos.data(),
.enabledLayerCount = 0, // deprecated and ignored
.ppEnabledLayerNames = nullptr, // deprecated and ignored
.enabledExtensionCount = !extensionsSupported ? 0 : static_cast<uint32_t>(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<uint32_t>(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<SHVkSurface> SHVkLogicalDevice::CreateSurface(HWND const& windowHandle) const noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkSurface>(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<SHVkSwapchain> SHVkLogicalDevice::CreateSwapchain(Handle<SHVkSurface> const& surfaceHdl, uint32_t width, uint32_t height, SHSwapchainParams const& params) const noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkSwapchain> (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<SHVkCommandPool> SHVkLogicalDevice::CreateCommandPool(SH_QUEUE_FAMILY_ARRAY_INDEX queueFamilyType, SH_CMD_POOL_RESET inResetMode, bool inTransient) const noexcept
{
auto newHdl = SHVkInstance::GetResourceManager().Create<SHVkCommandPool>(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<SHVkBuffer> SHVkLogicalDevice::CreateBuffer(uint32_t inSize, void* data, uint32_t srcSize, vk::BufferUsageFlags bufferUsage, VmaMemoryUsage memUsage, VmaAllocationCreateFlags allocFlags) const noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkBuffer>(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<SHVkImage> 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<SHVkImage>(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<SHVkShaderModule> SHVkLogicalDevice::CreateShaderModule(std::vector<uint32_t> const& binaryData, std::string entryPoint, vk::ShaderStageFlagBits stage, std::string const& shaderName) noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkShaderModule>(GetHandle(), binaryData, entryPoint, stage, shaderName);
}
/***************************************************************************/
/*!
\brief
Creates a pipeline layout in the resource manager and returns a handle to
it.
\param pipelineLayoutParams
\return
*/
/***************************************************************************/
Handle<SHVkPipelineLayout> SHVkLogicalDevice::CreatePipelineLayout(SHPipelineLayoutParams& pipelineLayoutParams) noexcept
{
return SHVkInstance::GetResourceManager().Create <SHVkPipelineLayout>(GetHandle(), pipelineLayoutParams);
}
/***************************************************************************/
/*!
\brief
Creates a pipeline in the resource manager and returns a handle to it.
\param pipelineLayoutHdl
\param state
\param type
\return
*/
/***************************************************************************/
Handle<SHVkPipeline> SHVkLogicalDevice::CreatePipeline(Handle<SHVkPipelineLayout> const& pipelineLayoutHdl, SHVkPipelineState const* const state, Handle<SHVkRenderpass> const& renderpassHdl, uint32_t subpass, SH_PIPELINE_TYPE type) noexcept
{
return SHVkInstance::GetResourceManager().Create <SHVkPipeline>(GetHandle(), pipelineLayoutHdl, state, renderpassHdl, subpass, type);
}
Handle<SHVkRenderpass> SHVkLogicalDevice::CreateRenderpass(std::span<vk::AttachmentDescription> const vkDescriptions, std::vector<SHVkSubpassParams> const& subpasses) noexcept
{
return SHVkInstance::GetResourceManager().Create <SHVkRenderpass>(GetHandle(), vkDescriptions, subpasses);
}
Handle<SHVkRenderpass> SHVkLogicalDevice::CreateRenderpass(std::span<vk::AttachmentDescription> const vkDescriptions, std::span<vk::SubpassDescription> const spDescs, std::span<vk::SubpassDependency> const spDeps) noexcept
{
return SHVkInstance::GetResourceManager().Create <SHVkRenderpass>(GetHandle(), vkDescriptions, spDescs, spDeps);
}
Handle<SHVkFramebuffer> SHVkLogicalDevice::CreateFramebuffer(Handle<SHVkRenderpass> const& renderpassHdl, std::vector<Handle<SHVkImageView>> const& attachments, uint32_t inWidth, uint32_t inHeight) noexcept
{
return SHVkInstance::GetResourceManager().Create <SHVkFramebuffer>(GetHandle(), renderpassHdl, attachments, inWidth, inHeight);
}
Handle<SHVkDescriptorSetLayout> SHVkLogicalDevice::CreateDescriptorSetLayout(std::vector<SHVkDescriptorSetLayout::Binding> const& bindings) noexcept
{
return SHVkInstance::GetResourceManager().Create <SHVkDescriptorSetLayout>(GetHandle(), bindings);
}
/***************************************************************************/
/*!
\brief
Creates a fence in the resource manager and returns a handle to it.
\return
A handle to the fence created.
*/
/***************************************************************************/
Handle<SHVkFence> SHVkLogicalDevice::CreateFence(void) const noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkFence>(GetHandle());
}
/***************************************************************************/
/*!
\brief
Creates a semaphore in the resource manager and returns a handle to it.
\return
A handle to the semaphore created.
*/
/***************************************************************************/
Handle<SHVkSemaphore> SHVkLogicalDevice::CreateSemaphore(void) const noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkSemaphore>(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<SHVkQueue> const& SHVkLogicalDevice::GetQueue(SH_Q_FAM queueFamArrayIndex, uint8_t queueIndex) const
{
if (queueFamilyIndices.indices[static_cast<SHQueueFamilyIndex>(queueFamArrayIndex)].has_value())
{
SHQueueFamilyIndex queueFamIndex = queueFamilyIndices.indices[static_cast<SHQueueFamilyIndex>(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<uint32_t>(family)].has_value())
return queueFamilyIndices.indices[static_cast<uint32_t>(family)].value();
else
return static_cast<SHQueueFamilyIndex>(SH_Q_FAM::INVALID);
}
//SHDescriptorPool const& SHLogicalDevice::GetDescriptorPool(void) const noexcept
//{
// return descriptorPool;
//}
}

View File

@ -0,0 +1,186 @@
#ifndef SH_LOGICAL_DEVICE_H
#define SH_LOGICAL_DEVICE_H
#include <optional>
#include <array>
#include <vector>
#include <memory>
#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<std::optional<SHQueueFamilyIndex>, static_cast<std::size_t>(SH_QUEUE_FAMILY_ARRAY_INDEX::MAX)> indices;
};
/***************************************************************************/
/*!
\class SHVkLogicalDevice
Stores a Vulkan logical device.
*/
/***************************************************************************/
class SHVkLogicalDevice : public ISelfHandle<SHVkLogicalDevice>
{
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<SHVkPhysicalDevice> 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<Handle<SHVkQueue>> queueHdls;
std::unordered_map<SHQueueFamilyIndex, std::vector<Handle<SHVkQueue>>> queueLibrary;
//! For use on tracking how many queues are retrieved
std::unordered_map<SHQueueFamilyIndex, uint32_t> queueFamUseCounts;
//! main VMA allocator of this instance
VmaAllocator vmaAllocator;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void InitializeVMA (void) noexcept;
void InitializeQueues (std::initializer_list<SHQueueParams> queueCreateParams) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* CTOR AND DTOR */
/*-----------------------------------------------------------------------*/
SHVkLogicalDevice (std::initializer_list<SHQueueParams> queueCreateParams, Handle<SHVkPhysicalDevice> 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<SHVkSurface> CreateSurface (HWND const& windowHandle) const noexcept;
Handle<SHVkSwapchain> CreateSwapchain (
Handle<SHVkSurface> const& surfaceHdl,
uint32_t width,
uint32_t height,
SHSwapchainParams const& params
) const noexcept;
Handle<SHVkCommandPool> CreateCommandPool (
SH_QUEUE_FAMILY_ARRAY_INDEX queueFamilyType,
SH_CMD_POOL_RESET inResetMode,
bool inTransient
) const noexcept;
Handle<SHVkBuffer> CreateBuffer (
uint32_t inSize,
void* data,
uint32_t srcSize,
vk::BufferUsageFlags bufferUsage,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
) const noexcept;
Handle<SHVkImage> CreateImage (
uint32_t w,
uint32_t h,
uint8_t levels,
vk::Format format,
vk::ImageUsageFlags usage,
vk::ImageCreateFlags create
) const noexcept;
Handle<SHVkShaderModule> CreateShaderModule (
std::vector<uint32_t> const& binaryData,
std::string entryPoint,
vk::ShaderStageFlagBits stage,
std::string const& shaderName
) noexcept;
Handle<SHVkPipeline> CreatePipeline (
Handle<SHVkPipelineLayout> const& pipelineLayoutHdl,
SHVkPipelineState const* const state,
Handle<SHVkRenderpass> const& renderpassHdl,
uint32_t subpass,
SH_PIPELINE_TYPE type
) noexcept;
Handle<SHVkRenderpass> CreateRenderpass (std::span<vk::AttachmentDescription> const vkDescriptions, std::vector<SHVkSubpassParams> const& subpasses) noexcept;
Handle<SHVkRenderpass> CreateRenderpass (std::span<vk::AttachmentDescription> const vkDescriptions, std::span<vk::SubpassDescription> const spDescs, std::span<vk::SubpassDependency> const spDeps) noexcept;
Handle<SHVkFramebuffer> CreateFramebuffer (Handle<SHVkRenderpass> const& renderpassHdl, std::vector<Handle<SHVkImageView>> const& attachments, uint32_t inWidth, uint32_t inHeight) noexcept;
Handle<SHVkDescriptorSetLayout> CreateDescriptorSetLayout (std::vector<SHVkDescriptorSetLayout::Binding> const& bindings) noexcept;
Handle<SHVkPipelineLayout> CreatePipelineLayout (SHPipelineLayoutParams& pipelineLayoutParams) noexcept;
Handle<SHVkFence> CreateFence (void) const noexcept;
Handle<SHVkSemaphore> CreateSemaphore (void) const noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
vk::Device const& GetVkLogicalDevice (void) const noexcept;
Handle<SHVkQueue> 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

View File

@ -0,0 +1,96 @@
#include "SHPch.h"
#include "SHVkPhysicalDevice.h"
#include <iostream>
#include <vector>
#include <set>
#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<vk::QueueFamilyProperties> 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<vk::MemoryHeap> 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 char*> const& requiredExtensions) const noexcept
{
std::vector<vk::ExtensionProperties> availableExtensions{};
availableExtensions = vkPhysicalDevice.enumerateDeviceExtensionProperties();
std::set<std::string> missingExtensions(requiredExtensions.begin(), requiredExtensions.end());
for (auto const& extension : availableExtensions)
missingExtensions.erase(extension.extensionName);
return missingExtensions.empty();
}
}

View File

@ -0,0 +1,43 @@
#ifndef SH_PHYSICAL_DEVICE_H
#define SH_PHYSICAL_DEVICE_H
#include <memory>
#include <vector>
#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<vk::QueueFamilyProperties> 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<vk::QueueFamilyProperties> 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 char*> const& requiredExtensions) const noexcept;
};
}
#endif

View File

@ -0,0 +1,189 @@
#include "SHPch.h"
#include <iostream>
#include <unordered_map>
#include "SHVkPhysicalDeviceLibrary.h"
#include "Graphics/Instance/SHVkInstance.h"
#include "Tools/SHLogger.h"
namespace SHADE
{
std::vector<vk::PhysicalDevice> 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<std::shared_ptr<SHPhysicalDevice>> 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<std::shared_ptr<SHPhysicalDevice>> 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<uint32_t>(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<uint32_t>(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<vk::MemoryHeap> 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()));
}
}
}

View File

@ -0,0 +1,33 @@
#ifndef SH_PHYSICAL_DEVICE_LIBRARY_H
#define SH_PHYSICAL_DEVICE_LIBRARY_H
#include <vector>
#include <memory>
#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<vk::PhysicalDevice> 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<std::shared_ptr<SHPhysicalDevice>> GetDevicesByType(VkPhysicalDeviceType gpuType, SHVulkanAPIVersion apiVersion = SHVulkanAPIVersion::V_1_2) noexcept;
static vk::PhysicalDevice GetBestDevice(uint32_t apiVersion = VK_API_VERSION_1_3);
};
}
#endif

View File

@ -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<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkRenderpass> const& renderpassHdl, std::vector<Handle<SHVkImageView>> 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<vk::ImageView> 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<uint32_t>(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);
}
}

View File

@ -0,0 +1,50 @@
#ifndef SH_VK_FRAMEBUFFER_H
#define SH_VK_FRAMEBUFFER_H
#include "Graphics/SHVulkanIncludes.h"
#include "Resource/Handle.h"
#include <span>
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkRenderpass;
class SHVkImageView;
/*
Renderpasses are really only meant to hold attachments. What writes are reads from it are determined by
attachments references in subpasses.
*/
class SHVkFramebuffer
{
private:
//! The vulkan framebuffer handle
vk::Framebuffer vkFramebuffer;
//! Logical device for destruction later on
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! Width of the images in the framebuffer
uint32_t width;
//! Height of the images in the framebuffer
uint32_t height;
public:
SHVkFramebuffer(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkRenderpass> const& renderpassHdl, std::vector<Handle<SHVkImageView>> const& attachments, uint32_t inWidth, uint32_t inHeight) noexcept;
~SHVkFramebuffer(void) noexcept;
SHVkFramebuffer(SHVkFramebuffer&& rhs) noexcept;
SHVkFramebuffer& operator=(SHVkFramebuffer&& rhs) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
vk::Framebuffer GetVkFramebuffer(void) const noexcept;
uint32_t GetWidth (void) const noexcept;
uint32_t GetHeight (void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,44 @@
#ifndef SH_IMAGE_VIEW_DETAILS_H
#define SH_IMAGE_VIEW_DETAILS_H
#include "Graphics/SHVulkanIncludes.h"
namespace SHADE
{
struct SHImageViewDetails
{
//! Image view type
vk::ImageViewType viewType;
//! If the image is created with VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT bit, this format can be different from parent image
vk::Format format;
//! The subresource mask of the image view
//! NOTE: It seems that from the specification, the image view does not have the agency to choose its own subresource aspect mask.
//! For simple use cases, the image formats corresponds to the aspect mask required : color to color, depth to depth, stencil to stencil.
//! However, because there are simply too many formats to prevent users from passing in the wrong arguments, we are letting them do it anyway.
//! Just keep this in mind.
vk::ImageAspectFlags imageAspectFlags;
//! base mip level
uint32_t baseMipLevel;
//! How many mips the image view has access to
uint32_t mipLevelCount;
//! Base array layer (layer is the array index)
uint32_t baseArrayLayer;
//! How many layers the image view has access to
uint32_t layerCount;
//SHImageViewDetails(void) noexcept = default;
//SHImageViewDetails(SHImageViewDetails const& rhs) noexcept = default;
//SHImageViewDetails& operator=(SHImageViewDetails const& rhs) noexcept = default;
//SHImageViewDetails(SHImageViewDetails&& rhs) noexcept = default;
//SHImageViewDetails& operator=(SHImageViewDetails&& rhs) noexcept = default;
}; }
#endif

View File

@ -0,0 +1,260 @@
#include "SHPch.h"
#include "SHVkImage.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Debugging/SHVulkanDebugUtil.h"
#include "Tools/SHLogger.h"
#include "SHVkImageView.h"
#include "Graphics/Instance/SHVkInstance.h"
namespace SHADE
{
SHVkImage::SHVkImage(
VmaAllocator const& vmaAllocator,
SHImageCreateParams const& imageDetails,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
) noexcept
: imageType { imageDetails.imageType }
, width{ imageDetails.width }
, height{ imageDetails.height }
, depth{ imageDetails.depth }
, mipLevelCount{ imageDetails.levels }
, layerCount{ imageDetails.arrayLayers }
, imageFormat{ imageDetails.imageFormat }
, usageFlags{}
, createFlags{}
{
for (auto& bit : imageDetails.usageBits)
usageFlags |= bit;
for (auto& bit : imageDetails.createBits)
createFlags |= bit;
// If marked as 2D array compatible, image type MUST be 3D
if (createFlags & vk::ImageCreateFlagBits::e2DArrayCompatible)
{
if (imageType != vk::ImageType::e3D)
{
SHLOG_ERROR("Image is marked to be 2D Array compatible. Image type MUST hence be 3D. ");
return;
}
}
vk::ImageCreateInfo imageCreateInfo{};
imageCreateInfo.imageType = imageType;
imageCreateInfo.extent.width = width;
imageCreateInfo.extent.height = height;
imageCreateInfo.extent.depth = 1;
imageCreateInfo.mipLevels = mipLevelCount;
imageCreateInfo.arrayLayers = layerCount;
imageCreateInfo.format = imageFormat;
imageCreateInfo.tiling = vk::ImageTiling::eOptimal;
imageCreateInfo.initialLayout = vk::ImageLayout::eUndefined;
imageCreateInfo.usage = usageFlags;
imageCreateInfo.sharingMode = vk::SharingMode::eExclusive;
imageCreateInfo.samples = vk::SampleCountFlagBits::e1;
imageCreateInfo.flags = createFlags;
// Prepare allocation parameters for call to create images later
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = memUsage;
allocCreateInfo.flags = allocFlags;
VmaAllocationInfo allocInfo{};
VkImage tempImage;
vmaCreateImage(vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo&(), &allocCreateInfo, &tempImage, &alloc, &allocInfo);
vkImage = tempImage;
//if (allocFlags & )
}
/***************************************************************************/
/*!
\brief
This is mainly used for images that aren't created internally because
they cannot be created in the traditional way (e.g. swapchain images).
\param inVkImage
Image already created outside
\param width
Width of the image
\param height
Height of the image
\param depth
Depth of the image
\param levels
Number of levels in the image
\param arrayLayers
if the image is an array, this value will be > 1.
\param imageFormat
Format of the image
*/
/***************************************************************************/
SHVkImage::SHVkImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t arrayLayers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept
: vkImage (inVkImage)
, width{ inWidth }
, height{ inHeight }
, depth{ inDepth }
, mipLevelCount{ levels }
, layerCount{ arrayLayers }
, imageFormat{ format }
, usageFlags{flags}
, alloc{}
, imageType{type}
, createFlags{}
{
}
SHVkImage::SHVkImage(VmaAllocator const& vmaAllocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept
: width {w}
, height{h}
, depth {1}
, layerCount{1}
, mipLevelCount{levels}
, imageFormat{format}
, usageFlags{usage}
, createFlags {create}
{
vk::ImageCreateInfo imageCreateInfo{};
imageCreateInfo.imageType = vk::ImageType::e2D;
imageCreateInfo.extent.width = width;
imageCreateInfo.extent.height = height;
imageCreateInfo.extent.depth = depth;
imageCreateInfo.mipLevels = mipLevelCount;
imageCreateInfo.arrayLayers = layerCount;
imageCreateInfo.format = imageFormat;
imageCreateInfo.tiling = vk::ImageTiling::eOptimal;
imageCreateInfo.initialLayout = vk::ImageLayout::eUndefined;
imageCreateInfo.usage = usageFlags;
imageCreateInfo.sharingMode = vk::SharingMode::eExclusive;
imageCreateInfo.samples = vk::SampleCountFlagBits::e1;
imageCreateInfo.flags = createFlags;
// Prepare allocation parameters for call to create images later
VmaAllocationCreateInfo allocCreateInfo{};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = {}; // TODO: Make sure the vk::MemoryPropertyFlags returned from vmaGetAllocationMemoryProperties has the device local bit set
VmaAllocationInfo allocInfo{};
VkImage tempImage;
auto result = vmaCreateImage(vmaAllocator, &imageCreateInfo.operator VkImageCreateInfo & (), &allocCreateInfo, &tempImage, &alloc, &allocInfo);
vkImage = tempImage;
if (result != VK_SUCCESS)
SHVulkanDebugUtil::ReportVkError(vk::Result(result), "Failed to create vulkan image. ");
else
SHVulkanDebugUtil::ReportVkSuccess("Successfully created image. ");
}
Handle<SHVkImageView> SHVkImage::CreateImageView(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkImage> const& parent, SHImageViewDetails const& createParams) const noexcept
{
return SHVkInstance::GetResourceManager().Create<SHVkImageView>(inLogicalDeviceHdl, parent, createParams);
}
void SHVkImage::LinkWithExteriorImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t layers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept
{
vkImage = inVkImage;
width = inWidth;
height = inHeight;
depth = inDepth;
mipLevelCount = levels;
layerCount = layers;
imageFormat = format;
usageFlags = flags;
imageType = type;
imageFormat = format;
}
/***************************************************************************/
/*!
\brief
Getter for vulkan image handle.
\return
the vulkan image handle.
*/
/***************************************************************************/
vk::Image SHVkImage::GetVkImage(void) const noexcept
{
return vkImage;
}
/***************************************************************************/
/*!
\brief
Getter for vulkan image create flags.
\return
The bitwise combination of create flag bits.
*/
/***************************************************************************/
vk::ImageCreateFlags SHVkImage::GetImageeCreateFlags(void) const noexcept
{
return createFlags;
}
/***************************************************************************/
/*!
\brief
Getter for vulkan image format.
\return
The image format.
*/
/***************************************************************************/
vk::Format SHVkImage::GetImageFormat(void) const noexcept
{
return imageFormat;
}
/***************************************************************************/
/*!
\brief
Getter for image width.
\return
The image width.
*/
/***************************************************************************/
uint32_t SHVkImage::GetWidth(void) const noexcept
{
return width;
}
/***************************************************************************/
/*!
\brief
Getter for image height
\return
The image height.
*/
/***************************************************************************/
uint32_t SHVkImage::GetHeight(void) const noexcept
{
return height;
}
}

View File

@ -0,0 +1,122 @@
#ifndef SH_VK_IMAGE_H
#define SH_VK_IMAGE_H
#include "SHImageViewDetails.h"
#include "Graphics/SHVulkanDefines.h"
#include "Resource/ResourceLibrary.h"
#include "vk_mem_alloc.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkImageView;
struct SHImageCreateParams
{
//! 1D, 2D or 3D
vk::ImageType imageType;
//! Width of the image
uint32_t width;
//! Height of the image
uint32_t height;
//! Depth of the image
uint32_t depth;
//! Number of mipmaps
uint8_t levels;
//! If image is standalone, this will be 0.
uint32_t arrayLayers = 0;
//! format of the image
vk::Format imageFormat;
//! Image usage bits
std::span<vk::ImageUsageFlagBits> usageBits;
//! Image create flags
std::span<vk::ImageCreateFlagBits> createBits;
};
class SHVkImage
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! 1D, 2D or 3D
vk::ImageType imageType = vk::ImageType::e2D;
//! Width of the image
uint32_t width{ 0 };
//! Height of the image
uint32_t height{ 0 };
//! Depth of the image
uint32_t depth{ 0 };
//! If image is standalone, this will be 0.
uint32_t layerCount = 0;
//! Number of mipmaps
uint8_t mipLevelCount{ 1 };
//! format of the image
vk::Format imageFormat{};
//! Image usage bits
vk::ImageUsageFlags usageFlags{};
//! Image create flags
vk::ImageCreateFlags createFlags{};
//! Vulkan image handle
vk::Image vkImage{};
//! allocation object containing details of an allocation
VmaAllocation alloc{};
public:
/*-----------------------------------------------------------------------*/
/* CTOR AND DTOR */
/*-----------------------------------------------------------------------*/
SHVkImage(void) noexcept = default;
// TODO: Might need to add flags to parameters
SHVkImage(
VmaAllocator const& vmaAllocator,
SHImageCreateParams const& imageDetails,
VmaMemoryUsage memUsage,
VmaAllocationCreateFlags allocFlags
) noexcept;
SHVkImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t arrayLayers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept;
SHVkImage(VmaAllocator const& vmaAllocator, uint32_t w, uint32_t h, uint8_t levels, vk::Format format, vk::ImageUsageFlags usage, vk::ImageCreateFlags create) noexcept;
SHVkImage(SHVkImage&& rhs) noexcept = default;
SHVkImage& operator=(SHVkImage && rhs) noexcept = default;
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
Handle<SHVkImageView> CreateImageView(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkImage> const& parent, SHImageViewDetails const& createParams) const noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS AND SETTERS */
/*-----------------------------------------------------------------------*/
void LinkWithExteriorImage(vk::Image inVkImage, vk::ImageType type, uint32_t inWidth, uint32_t inHeight, uint32_t inDepth, uint32_t arrayLayers, uint8_t levels, vk::Format format, vk::ImageUsageFlags flags) noexcept;
vk::Image GetVkImage (void) const noexcept;
vk::ImageCreateFlags GetImageeCreateFlags (void) const noexcept;
vk::Format GetImageFormat (void) const noexcept;
uint32_t GetWidth (void) const noexcept;
uint32_t GetHeight (void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,128 @@
#include "SHPch.h"
#include "SHVkImageView.h"
#include "SHVkImage.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Tools/SHLogger.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Non-default ctor. Initializes image view with image that it is a view of.
\param parent
Parent image the view is a view of.
*/
/***************************************************************************/
SHVkImageView::SHVkImageView(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkImage> const& parent, SHImageViewDetails const& createParams) noexcept
: parentImage{ }
, vkImageView{}
, imageViewDetails{}
, logicalDeviceHdl {inLogicalDeviceHdl}
{
auto parentImageCreateFlags = parent->GetImageeCreateFlags();
// 2D array image type means parent image must be 2D array compatible
if (createParams.viewType == vk::ImageViewType::e2DArray)
{
if (!(parentImageCreateFlags & vk::ImageCreateFlagBits::e2DArrayCompatible))
{
SHLOG_ERROR("Failed to create image view. Parent image not 2D array compatible. ");
return;
}
}
// Check if its possible for the image view to have different format than parent image
if (createParams.format != parent->GetImageFormat())
{
if (!(parentImageCreateFlags & vk::ImageCreateFlagBits::eMutableFormat))
{
SHLOG_ERROR("Failed to create image view. Format for image view not same as image but image not initialized with mutable format bit. ");
return;
}
}
vk::ImageViewCreateInfo viewCreateInfo
{
.pNext = nullptr, // Can be used to override with a VkImageViewUsageCreateInfo to override usage. See Vulkan spec page 877 for more information
.image = parent->GetVkImage(),
.viewType = createParams.viewType,
.format = createParams.format,
.components
{
.r = vk::ComponentSwizzle::eR,
.g = vk::ComponentSwizzle::eG,
.b = vk::ComponentSwizzle::eB,
.a = vk::ComponentSwizzle::eA,
},
.subresourceRange
{
.aspectMask = createParams.imageAspectFlags,
.baseMipLevel = createParams.baseMipLevel,
.levelCount = createParams.mipLevelCount,
.baseArrayLayer = createParams.baseArrayLayer,
.layerCount = createParams.layerCount,
},
};
if (auto result = inLogicalDeviceHdl->GetVkLogicalDevice().createImageView(&viewCreateInfo, nullptr, &vkImageView); result != vk::Result::eSuccess)
{
SHVulkanDebugUtil::ReportVkError(result, "Failed to create image view! ");
return;
}
else
{
SHVulkanDebugUtil::ReportVkSuccess("Successfully created image view. ");
}
// After success, THEN assign variables
parentImage = parent;
imageViewDetails = createParams;
}
SHVkImageView::SHVkImageView(SHVkImageView&& rhs) noexcept
: vkImageView {std::move (rhs.vkImageView)}
, parentImage {std::move (rhs.parentImage)}
, imageViewDetails{std::move (rhs.imageViewDetails)}
, logicalDeviceHdl{ rhs.logicalDeviceHdl } // just copy doesn't matter
{
rhs.vkImageView = VK_NULL_HANDLE;
}
Handle<SHVkImage> const& SHVkImageView::GetParentImage(void) const noexcept
{
return parentImage;
}
vk::ImageView SHVkImageView::GetImageView(void) const noexcept
{
return vkImageView;
}
SHVkImageView& SHVkImageView::operator=(SHVkImageView&& rhs) noexcept
{
if (&rhs == this)
return *this;
vkImageView = rhs.vkImageView;
parentImage = std::move(rhs.parentImage);
imageViewDetails = std::move(rhs.imageViewDetails);
logicalDeviceHdl = rhs.logicalDeviceHdl; // just copy doesn't matter
rhs.vkImageView = VK_NULL_HANDLE;
return *this;
}
SHVkImageView::~SHVkImageView(void) noexcept
{
if (vkImageView)
logicalDeviceHdl->GetVkLogicalDevice().destroyImageView(vkImageView, nullptr);
}
}

View File

@ -0,0 +1,43 @@
#ifndef SH_VK_IMAGE_VIEW_H
#define SH_VK_IMAGE_VIEW_H
#include "Graphics/SHVulkanIncludes.h"
#include "Resource/Handle.h"
#include "SHImageViewDetails.h"
namespace SHADE
{
class SHVkImage;
class SHVkLogicalDevice;
class SHVkImageView
{
private:
//! Handle to vulkan image view
vk::ImageView vkImageView;
//! We want to to keep a reference to the parent
Handle<SHVkImage> parentImage;
//! For storing image view details
SHImageViewDetails imageViewDetails;
//! Logical Device needed for creation and destruction
Handle<SHVkLogicalDevice> logicalDeviceHdl;
public:
SHVkImageView(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkImage> const& parent, SHImageViewDetails const& createParams) noexcept;
~SHVkImageView(void) noexcept;
SHVkImageView(SHVkImageView&& rhs) noexcept;
SHVkImageView& operator=(SHVkImageView&& rhs) noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS AND SETTERS */
/*-----------------------------------------------------------------------*/
Handle<SHVkImage> const& GetParentImage(void) const noexcept;
vk::ImageView GetImageView (void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,266 @@
#include "SHPch.h"
#include "SHVkInstance.h"
#include "Graphics/Debugging/SHValidationLayersQuery.h"
#include "Graphics/Debugging/SHVkDebugMessenger.h"
#include "Graphics/Devices/SHVkPhysicalDeviceLibrary.h"
#include "Tools/SHLogger.h"
#include "Graphics/Devices/SHVkPhysicalDeviceLibrary.h"
//#include <vulkan/vulkan_win32.h>
namespace SHADE
{
bool SHVkInstance::debugOn;
bool SHVkInstance::renderdocOn;
bool SHVkInstance::validationLayersOn;
vk::Instance SHVkInstance::vkInstance;
SHVkDebugMessenger SHVkInstance::debugMessenger;
ResourceManager SHVkInstance::resourceManager;
/***************************************************************************/
/*!
\brief
Returns a basic list of extensions for the instance initialization.
\return
Returns a vector of extensions.
*/
/***************************************************************************/
std::vector <const char*> SHVkInstance::GetExtensions(void) noexcept
{
// win32 will be used for windowing
std::vector<const char*> extensions
{
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_WIN32_SURFACE_EXTENSION_NAME
};
// If debug flag is true, push back the extension for debug messenger
if (debugOn)
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
// Returns the extensions
return extensions;
}
/***************************************************************************/
/*!
\brief
Private function to initialize a vulkan application info struct.
\param[in, out] appInfo
The app info struct to modify.
*/
/***************************************************************************/
void SHVkInstance::InitializeAppInfo(vk::ApplicationInfo& appInfo) noexcept
{
appInfo.pApplicationName = "SHADE Engine";
appInfo.applicationVersion = 1;
appInfo.pNext = nullptr;
appInfo.pEngineName = "SHADE Engine";
appInfo.engineVersion = 1;
// default 1.3 (latest as of 24th May 2022)
appInfo.apiVersion = VK_API_VERSION_1_3;
}
/***************************************************************************/
/*!
\brief
Simply calls a function from the physical device library to query for
all physical devices.
*/
/***************************************************************************/
void SHVkInstance::InitializePhysicalDevices(void) noexcept
{
SHVkPhysicalDeviceLibrary::QueryPhysicalDevices(debugOn);
}
/***************************************************************************/
/*!
\brief
Does a few things:
- Initializes an instance with extensions and (depending on flag)
validation layers.
- Initializes a debug messenger
- Query physical devices
\param inDebugOn
If true, debug messenger will be created.
\param inRenderdocOn
If true, renderdoc will be added to validation layers.
\param inValidationOn
If true, validation layers will be enabled.
*/
/***************************************************************************/
void SHVkInstance::Init (bool inDebugOn, bool inRenderdocOn, bool inValidationOn) noexcept
{
// Init some members
debugOn = inDebugOn;
renderdocOn = inRenderdocOn;
validationLayersOn = inValidationOn;
// Populate application info struct
vk::ApplicationInfo appInfo;
InitializeAppInfo(appInfo);
// Get extensions
const auto extensions = GetExtensions();
// Prepare for instance creation.
vk::InstanceCreateInfo instanceInfo;
instanceInfo.pApplicationInfo = &appInfo;
instanceInfo.ppEnabledExtensionNames = extensions.size() ? extensions.data() : nullptr;
instanceInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
// Load pointers to instance-independent extension functions
vk::DynamicLoader dl;
const PFN_vkGetInstanceProcAddr VK_GET_INSTANCE_PROC_ADDR = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(VK_GET_INSTANCE_PROC_ADDR);
vkGetInstanceProcAddr(nullptr, ""); // HACK: HPP extension functions break without this line, do not remove!
// If validation layers are on
if (validationLayersOn)
{
// Generate validation layers
SHValidationLayersQuery::GenerateAvailableLayers(renderdocOn);
const auto& layers = SHValidationLayersQuery::GetAvailableLayers();
instanceInfo.enabledLayerCount = static_cast<uint32_t>(layers.size());
instanceInfo.ppEnabledLayerNames = layers.size() ? layers.data() : nullptr;
}
else
{
instanceInfo.enabledLayerCount = 0;
instanceInfo.ppEnabledLayerNames = nullptr;
}
// if debug is on, create debug messenger for instance creation.
if (debugOn)
{
vk::DebugUtilsMessengerCreateInfoEXT instanceDbgInfo;
SHVkDebugMessenger::InitializeDebugCreateInfo(instanceDbgInfo,
SHVkDebugMessenger::GenMessengerSeverity(SH_DEBUG_MSG_SEV::S_VERBOSE, SH_DEBUG_MSG_SEV::S_WARNING, SH_DEBUG_MSG_SEV::S_ERROR),
SHVkDebugMessenger::GenMessengerType(SH_DEBUG_MSG_TYPE::T_GENERAL, SH_DEBUG_MSG_TYPE::T_VALIDATION, SH_DEBUG_MSG_TYPE::T_PERFORMANCE));
instanceDbgInfo.pfnUserCallback = SHVulkanDebugUtil::GenericDebugCallback;
instanceInfo.pNext = static_cast<vk::DebugUtilsMessengerCreateInfoEXT*>(&instanceDbgInfo);
}
// Finally create the instance
if (vk::Result result = vk::createInstance(&instanceInfo, nullptr, &vkInstance); result != vk::Result::eSuccess)
{
SHVulkanDebugUtil::ReportVkError(result, "Failed to create Vulkan instance! ");
}
else
{
SHVulkanDebugUtil::ReportVkSuccess("Successfully created a Vulkan Instance. ");
}
// Load pointers to instance-specific extension functions
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkInstance);
// Initialize a debug messenger
debugMessenger.Initialize();
// Query for physical devices
InitializePhysicalDevices();
}
/***************************************************************************/
/*!
\brief
Destroys the vulkan instance.
*/
/***************************************************************************/
void SHVkInstance::Destroy(void) noexcept
{
vkInstance.destroy(nullptr);
}
/***************************************************************************/
/*!
\brief
Creates a physical device and returns a handle to it.
\param inPhysicalDevice
A Vulkan physical device handle.
\return
A SHADE handle object encapsulating a SHADE wrapper for a vulkan
physical device.
*/
/***************************************************************************/
Handle<SHVkPhysicalDevice> SHVkInstance::CreatePhysicalDevice(SH_PHYSICAL_DEVICE_TYPE type)
{
switch (type)
{
case SH_PHYSICAL_DEVICE_TYPE::BEST:
return resourceManager.Create<SHVkPhysicalDevice>(SHVkPhysicalDeviceLibrary::GetBestDevice());
default:
return resourceManager.Create<SHVkPhysicalDevice>(SHVkPhysicalDeviceLibrary::GetBestDevice());
}
}
/***************************************************************************/
/*!
\brief
Creates a logical device and returns a handle to it.
\param queueCreateParams
The number of queues to create from the logical device.
\param physicalDevice
The physical device to create the logical device from.
\return
The newly created logical device.
*/
/***************************************************************************/
Handle<SHVkLogicalDevice> SHVkInstance::CreateLogicalDevice(std::initializer_list<SHQueueParams> queueCreateParams, Handle<SHVkPhysicalDevice> const& physicalDeviceHdl)
{
auto newHdl = resourceManager.Create<SHVkLogicalDevice>(queueCreateParams, physicalDeviceHdl);
newHdl->InitializeQueues(queueCreateParams);
return newHdl;
}
/***************************************************************************/
/*!
\brief
Getter for the underlying vulkan instance.
\return
The underlying vulkan instance.
*/
/***************************************************************************/
vk::Instance const& SHVkInstance::GetVkInstance(void) noexcept
{
return vkInstance;
}
ResourceManager& SHVkInstance::GetResourceManager(void) noexcept
{
return resourceManager;
}
}

View File

@ -0,0 +1,93 @@
/**************************************************************************//*!
\file SHVkInstance.h
\author Brandon Mak
\par email: brandon.hao@digipen.edu
\date 16th May 2022
\brief Stores the declaration of SHVkInstance 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_VK_INSTANCE_H
#define SH_VK_INSTANCE_H
#include <vector> // std::vector
#include "Graphics/Debugging/SHVkDebugMessenger.h"
#include "Graphics/Devices/SHVkPhysicalDevice.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Resource/ResourceLibrary.h"
namespace SHADE
{
//class SHVkLogicalDevice;
//class SHVkPhysicalDevice;
/*!**************************************************************************
\class SHVkInstance
\brief
Mainly used for initializing a Vulkan Instance.
***************************************************************************/
class SHVkInstance
{
private:
/*-----------------------------------------------------------------------*/
/* DATA MEMBERS */
/*-----------------------------------------------------------------------*/
//! Whether or not the debug messenger is active or not
static bool debugOn;
//! Whether or not renderdoc is on or not. Changing this flag would
//! propagate a restart of the rendering system
static bool renderdocOn;
//! Whether or not validation layers are enabled.
static bool validationLayersOn;
//! Handle to the actual vulkan instance
static vk::Instance vkInstance;
//! Debug messenger (this is not a handle (or at least I feel it shouldn't
//! be) is because it is contained within the instance class and not used
//! outside. When the instance gets destroyed, the messenger is destroyed
//! together with it
static SHVkDebugMessenger debugMessenger;
//! Resource management for vulkan project
static ResourceManager resourceManager;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
static std::vector <const char*> GetExtensions (void) noexcept;
static void InitializeAppInfo (vk::ApplicationInfo& appInfo) noexcept;
static void InitializePhysicalDevices (void) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
static void Init (bool inDebugOn, bool inRenderdocOn, bool inValidationOn) noexcept;
static void Destroy (void) noexcept;
static Handle<SHVkPhysicalDevice> CreatePhysicalDevice (SH_PHYSICAL_DEVICE_TYPE type);
static Handle<SHVkLogicalDevice> CreateLogicalDevice (std::initializer_list<SHQueueParams> queueCreateParams, Handle<SHVkPhysicalDevice> const& physicalDeviceHdl);
/*-----------------------------------------------------------------------*/
/* Getters and Setters */
/*-----------------------------------------------------------------------*/
static vk::Instance const& GetVkInstance (void) noexcept;
static ResourceManager& GetResourceManager(void) noexcept;
};
}
#endif

View File

@ -0,0 +1,238 @@
/************************************************************************************//*!
\file SHGraphicsSystem.cpp
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Aug 21, 2022
\brief
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.
*//*************************************************************************************/
#include "SHPch.h"
#include "SHGraphicsSystem.h"
#include "Graphics/Instance/SHVkInstance.h"
#include "Graphics/Windowing/Surface/SHVkSurface.h"
#include "Graphics/Swapchain/SHVkSwapchain.h"
//#include "SHRenderer.h"
#include "Graphics/Windowing/SHWindow.h"
#include "Graphics/MiddleEnd/PerFrame/SHPerFrameData.h"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*---------------------------------------------------------------------------------*/
SHGraphicsSystem::SHGraphicsSystem(SHWindow& window)
{
// Save the SHWindow
this->window = &window;
// Set Up Instance
SHVkInstance::Init(true, true, true);
// Get Physical Device and Construct Logical Device
// TODO: Provide configuration for these options
physicalDevice = SHVkInstance::CreatePhysicalDevice(SH_PHYSICAL_DEVICE_TYPE::BEST);
device = SHVkInstance::CreateLogicalDevice({ SHQueueParams(SH_Q_FAM::GRAPHICS, SH_QUEUE_SELECT::DEDICATED), SHQueueParams(SH_Q_FAM::TRANSFER, SH_QUEUE_SELECT::DEDICATED) }, physicalDevice);
// Construct surface
surface = device->CreateSurface(window.GetHWND());
// Construct Swapchain
auto windowDims = window.GetWindowSize();
swapchain = device->CreateSwapchain(surface, windowDims.first, windowDims.second, SHSwapchainParams
{
.surfaceImageFormats {vk::Format::eB8G8R8A8Unorm, vk::Format::eR8G8B8A8Unorm, vk::Format::eB8G8R8Unorm, vk::Format::eR8G8B8Unorm},
.depthFormats {vk::Format::eD32Sfloat, vk::Format::eD32SfloatS8Uint, vk::Format::eD24UnormS8Uint},
.presentModes {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox, vk::PresentModeKHR::eImmediate},
.vsyncOn = false, // TODO: Set to true when shipping game
});
window.RegisterWindowSizeCallback([&]([[maybe_unused]] uint32_t width, [[maybe_unused]] uint32_t height)
{
renderContext.SetIsResized(true);
});
// Create graphics queue
queue = device->GetQueue(SH_Q_FAM::GRAPHICS, 0);
// Create Render Context
renderContext.Init
(
device,
SHPerFrameDataParams
{
.swapchainHdl = swapchain,
.numThreads = 1,
.cmdPoolQueueFamilyType = SH_Q_FAM::GRAPHICS,
.cmdPoolResetMode = SH_CMD_POOL_RESET::POOL_BASED,
.cmdBufferTransient = true,
}
);
// Create Frame and Command Buffers
for (int i = 0; i < NUM_FRAME_BUFFERS; ++i)
{
//frameBuffers[i] = device->CreateFramebuffer(renderPass, { renderContext.GetFrameData(i).swapchainImageViewHdl }, windowDims[0], windowDims[1]);
SHPerFrameData& frameData = renderContext.GetFrameData(i);
if (frameData.cmdPoolHdls.empty())
throw std::runtime_error("No command pools available!");
Handle<SHVkCommandPool> commandPool = frameData.cmdPoolHdls[0];
//commandBuffers[i] = commandPool->RequestCommandBuffer(SH_CMD_BUFFER_TYPE::PRIMARY); // works
}
/*-----------------------------------------------------------------------*/
/* RENDERGRAPH TESTING */
/*-----------------------------------------------------------------------*/
renderGraph.Init(device, swapchain);
renderGraph.AddResource("Position", SH_ATT_DESC_TYPE::COLOR, windowDims.first, windowDims.second, vk::Format::eR16G16B16A16Sfloat);
renderGraph.AddResource("Normals", SH_ATT_DESC_TYPE::COLOR, windowDims.first, windowDims.second, vk::Format::eR16G16B16A16Sfloat);
renderGraph.AddResource("Composite", SH_ATT_DESC_TYPE::COLOR, windowDims.first, windowDims.second, vk::Format::eR16G16B16A16Sfloat);
renderGraph.AddResource("Downscale", SH_ATT_DESC_TYPE::COLOR, windowDims.first, windowDims.second, vk::Format::eR16G16B16A16Sfloat);
renderGraph.AddResource("Present", SH_ATT_DESC_TYPE::COLOR_PRESENT, windowDims.first, windowDims.second);
auto node = renderGraph.AddNode("G-Buffer", { "Position", "Normals", "Composite" }, {}); // no predecessors
// First subpass to write to G-Buffer
auto writeSubpass = node->AddSubpass("G-Buffer Write");
writeSubpass->AddColorOutput("Position");
writeSubpass->AddColorOutput("Normals");
// Second subpass to read from G-Buffer
auto compositeSubpass = node->AddSubpass("G-Buffer Composite");
compositeSubpass->AddColorOutput("Composite");
compositeSubpass->AddInput("Normals");
compositeSubpass->AddInput("Position");
auto compositeNode = renderGraph.AddNode("Bloom", { "Composite", "Downscale", "Present"}, {"G-Buffer"});
auto bloomSubpass = compositeNode->AddSubpass("Downsample");
bloomSubpass->AddInput("Composite");
bloomSubpass->AddColorOutput("Downscale");
bloomSubpass->AddColorOutput("Present");
renderGraph.Generate();
/*-----------------------------------------------------------------------*/
/* RENDERGRAPH END TESTING */
/*-----------------------------------------------------------------------*/
}
SHGraphicsSystem::~SHGraphicsSystem()
{
//renderPass.Free();
renderContext.Destroy();
queue.Free();
swapchain.Free();
surface.Free();
device.Free();
SHVkInstance::Destroy();
}
/*---------------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*---------------------------------------------------------------------------------*/
/***************************************************************************/
/*!
\brief
Checks for window resize and acquire next image in swapchain.
*/
/***************************************************************************/
void SHGraphicsSystem::BeginRender()
{
auto windowDims = window->GetWindowSize();
if (renderContext.GetResizeAndReset())
{
device->WaitIdle();
// Resize the swapchain
swapchain->Resize(surface, windowDims.first, windowDims.second);
renderContext.HandleResize();
}
const uint32_t CURR_FRAME_IDX = renderContext.GetCurrentFrame();
// #BackEndTest: For for the fence initialized by queue submit
renderContext.WaitForFence();
// #BackEndTest: Acquire the next image in the swapchain available
renderContext.AcquireNextIamge();
// #BackEndTest: Get the current frame from frame manager
auto& currFrameData = renderContext.GetCurrentFrameData();
// #BackEndTest: Reset command pool
if (currFrameData.cmdPoolHdls.empty())
throw std::runtime_error("No command pools available!");
currFrameData.cmdPoolHdls[0]->Reset();
}
/***************************************************************************/
/*!
\brief
Check if need to resize and advance the frame in the render context.
*/
/***************************************************************************/
void SHGraphicsSystem::EndRender()
{
const uint32_t CURR_FRAME_IDX = renderContext.GetCurrentFrame();
auto& currFrameData = renderContext.GetCurrentFrameData();
// #BackEndTest: Prepare to present current image
vk::PresentInfoKHR presentInfo{};
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = &currFrameData.semRenderFinishHdl->GetVkSem();
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapchain->GetVkSwapchain();
presentInfo.pImageIndices = &CURR_FRAME_IDX;
// #BackEndTest: queues an image for presentation
if (auto result = device->GetQueue(SH_Q_FAM::GRAPHICS, 0)->GetVkQueue().presentKHR(&presentInfo); result != vk::Result::eSuccess)
{
// If swapchain is incompatible/outdated
if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR)
{
auto windowDims = window->GetWindowSize();
swapchain->Resize(surface, windowDims.first, windowDims.second);
renderContext.HandleResize();
}
}
// #BackEndTest: Cycle frame count
renderContext.AdvanceFrame();
}
//Handle<SHRenderer> SHGraphicsSystem::AddRenderer()
//{
// return Handle<SHRenderer>();
//}
//void SHGraphicsSystem::RemoveRenderer(Handle<SHRenderer> renderer)
//{
//}
Handle<SHScreenSegment> SHGraphicsSystem::AddSegment(const VkViewport& viewport, Handle<SHVkImage> imageToUse)
{
return Handle<SHScreenSegment>();
}
void SHGraphicsSystem::RemoveSegment(Handle<SHScreenSegment> segment)
{
}
}

View File

@ -0,0 +1,129 @@
/************************************************************************************//*!
\file SHGraphicsSystem.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Aug 21, 2022
\brief
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.
*//*************************************************************************************/
#pragma once
// STL Includes
#include <vector>
#include <array>
// Project Includes
#include "Resource/Handle.h"
#include "Graphics/SHVulkanIncludes.h"
#include "Graphics/MiddleEnd/PerFrame/SHRenderContext.h"
#include "Graphics/RenderGraph/SHRenderGraph.h"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* Forward Declarations */
/*---------------------------------------------------------------------------------*/
class SHVkPhysicalDevice;
class SHVkLogicalDevice;
class SHVkSurface;
class SHVkSwapchain;
class SHScreenSegment;
//class SHRenderer;
class SHWindow;
class SHVkImage;
class SHVkFramebuffer;
class SHVkCommandBuffer;
/*---------------------------------------------------------------------------------*/
/* Type Definitions */
/*---------------------------------------------------------------------------------*/
/***********************************************************************************/
/*!
\brief
Represents an axis aligned box on the screen to render to along with the
specified Image to render to that spot.
*/
/***********************************************************************************/
struct SHScreenSegment
{
VkViewport Viewport;
Handle<SHVkImage> ImageToUse;
};
/***********************************************************************************/
/*!
\brief
Manages the lifecycle and provides an interface for rendering multiple objects to
portions of the screen.
*/
/***********************************************************************************/
class SHGraphicsSystem
{
public:
/*-----------------------------------------------------------------------------*/
/* Constants */
/*-----------------------------------------------------------------------------*/
static constexpr int NUM_FRAME_BUFFERS = 3;
/*-----------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*-----------------------------------------------------------------------------*/
SHGraphicsSystem(SHWindow& window);
~SHGraphicsSystem();
/*-----------------------------------------------------------------------------*/
/* Lifecycle Functions */
/*-----------------------------------------------------------------------------*/
void BeginRender();
void EndRender();
/*-----------------------------------------------------------------------------*/
/* Renderers Registration Functions */
/*-----------------------------------------------------------------------------*/
//Handle<SHRenderer> AddRenderer();
//void RemoveRenderer(Handle<SHRenderer> renderer);
/*-----------------------------------------------------------------------------*/
/* Viewport Registration Functions */
/*-----------------------------------------------------------------------------*/
Handle<SHScreenSegment> AddSegment(const VkViewport& viewport, Handle<SHVkImage> imageToUse);
void RemoveSegment(Handle<SHScreenSegment> segment);
/*-----------------------------------------------------------------------------*/
/* Getters (Temporary) */
/*-----------------------------------------------------------------------------*/
Handle<SHVkLogicalDevice> GetDevice() const { return device; }
Handle<SHVkSwapchain> GetSwapchain() const { return swapchain; }
Handle<SHVkSurface> GetSurface() const { return surface; }
//Handle<SHVkRenderpass> GetRenderPass() const { return renderPass; }
private:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
// Owned Resources
Handle<SHVkPhysicalDevice> physicalDevice;
Handle<SHVkLogicalDevice> device;
Handle<SHVkSurface> surface;
Handle<SHVkSwapchain> swapchain;
Handle<SHVkQueue> queue;
//Handle<SHVkRenderpass> renderPass; // Potentially bring out?
std::vector<SHScreenSegment> screenSegments;
SHRenderContext renderContext;
//std::array<Handle<SHVkFramebuffer>, NUM_FRAME_BUFFERS> frameBuffers;
//std::array<Handle<SHVkCommandBuffer>, NUM_FRAME_BUFFERS> commandBuffers;
// Not Owned Resources
SHWindow* window;
// Renderers
//Handle<SHRenderer> debugWorldRenderer;
//Handle<SHRenderer> debugScreenRenderer;
//std::vector<SHRenderer> renderers;
SHRenderGraph renderGraph;
};
}

View File

@ -0,0 +1,2 @@
#include "SHPch.h"
#include "SHRenderTarget.h"

View File

@ -0,0 +1,37 @@
/************************************************************************************//*!
\file SHRenderTarget.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Aug 21, 2022
\brief
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.
*//*************************************************************************************/
#pragma once
namespace SHADE
{
/***********************************************************************************/
/*!
\brief
Represents an object that rendering operations can be outputted to. These are
images in general but could be a separate texture or even a swapchain image.
*/
/***********************************************************************************/
class SHRenderTarget
{
public:
/*-----------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*-----------------------------------------------------------------------------*/
private:
/*-----------------------------------------------------------------------------*/
/* Data Members */
/*-----------------------------------------------------------------------------*/
};
}

View File

@ -0,0 +1,80 @@
#include "SHPch.h"
#include "SHPerFrameData.h"
#include "Graphics/Swapchain/SHVkSwapchain.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Instance/SHVkInstance.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Initializes frame data. Everything here is created brand new from
the logical device except for the swapchain images (which are retrieved
from the swapchain).
\param logicalDeviceHdl
Required to create objects
\param params
Stores information required to initialize frame data like the swapchain
used and command pool parameters.
\param inFrameIndex
Every frame will have an index.
*/
/***************************************************************************/
void SHPerFrameData::Recreate(Handle<SHVkLogicalDevice> const& logicalDeviceHdl) noexcept
{
// Swapchain recreation means the images are just relinked to SHVkImages. Handles will remain the same. There is no need for this line.
//swapchainImageHdl = params.swapchainHdl->GetSwapchainImage(frameIndex);
SHImageViewDetails viewDetails
{
.viewType = vk::ImageViewType::e2D,
.format = swapchainImageHdl->GetImageFormat(),
.imageAspectFlags = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
// Create image views for the swapchain
swapchainImageViewHdl = swapchainImageHdl->CreateImageView(logicalDeviceHdl, swapchainImageHdl, viewDetails);
// Create a fence
fenceHdl = logicalDeviceHdl->CreateFence();
// scope makes it easier to navigate
semImgAvailableHdl = logicalDeviceHdl->SHVkLogicalDevice::CreateSemaphore();
semRenderFinishHdl = logicalDeviceHdl->SHVkLogicalDevice::CreateSemaphore();
}
/***************************************************************************/
/*!
\brief
Destroys frame data.
\param logicalDeviceHdl
Required for destruction.
*/
/***************************************************************************/
void SHPerFrameData::Destroy(void)
{
// swapchainImageHdl is non-owning, so we don't destroy that here
// TODO: Non very user friendly to have objects created from the logical device but destroyed from the resource manager. Can perhaps refactor.
SHVkInstance::GetResourceManager().Free(swapchainImageViewHdl);
// Destroy all the synchronization objects
fenceHdl.Free();
semImgAvailableHdl.Free();
semRenderFinishHdl.Free();
}
}

View File

@ -0,0 +1,73 @@
#ifndef SH_PER_FRAME_DATA_H
#define SH_PER_FRAME_DATA_H
#include "Graphics/Images/SHVkImage.h"
#include "Graphics/Images/SHVkImageView.h"
#include "Graphics/Commands/SHVkCommandBuffer.h"
#include "Graphics/Commands/SHVkCommandPool.h"
#include "Graphics/Synchronization/SHVkFence.h"
#include "Graphics/Synchronization/SHVkSemaphore.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkSwapchain;
struct SHPerFrameDataParams
{
//! Swapchain to get image data
Handle<SHVkSwapchain> swapchainHdl{};
//! Number of threads will dictate how many command pools we will have
uint32_t numThreads = 0;
//! Queue family index for command pool creation
SH_Q_FAM cmdPoolQueueFamilyType{};
//! Reset mode for the command pool
SH_CMD_POOL_RESET cmdPoolResetMode = SH_CMD_POOL_RESET::POOL_BASED;
// whether or not cmd buffers from command pool should be transient
bool cmdBufferTransient = true;
};
//! While there are other things that could also be shared between frames, there are certain things like command buffers that fit into more than 1 contexts.
//! The data here should only consists of data that is handled in a straightforward manner.
struct SHPerFrameData
{
//! The actual swapchain image for the frame
Handle<SHVkImage> swapchainImageHdl;
//! The image view of the image above
Handle<SHVkImageView> swapchainImageViewHdl;
//! Command pools for the frame. We need it this way because we don't want to reset a command pool when the GPU is using it. We want a container because
//! we want 1 for each thread. // TODO: Need to verify this.
std::vector<Handle<SHVkCommandPool>> cmdPoolHdls;
//! Every frame needs a fence
Handle<SHVkFence> fenceHdl;
//! Semaphore for the GPU to signal when an image is available
Handle<SHVkSemaphore> semImgAvailableHdl;
//! Semaphore for the GPU to signal when rendering to an image has finished (used for signal sem in vkSubmitInfo and wait sem in VkPresentInfo)
Handle<SHVkSemaphore> semRenderFinishHdl;
//! Every index will have a frame index
uint32_t frameIndex{ 0 };
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
// These are made into functions (instead of ctor and dtor) because we want to call these functions again when we resize the window
void Recreate (Handle<SHVkLogicalDevice> const& logicalDeviceHdl) noexcept;
void Destroy (void);
friend class SHRenderContext;
};
}
#endif

View File

@ -0,0 +1,207 @@
#include "SHPch.h"
#include "SHRenderContext.h"
#include "Tools/SHLogger.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Swapchain/SHVkSwapchain.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Frame manager ctor. Initializes the data for all frames.
\param inLogicalDeviceHdl
Logical device required for frame initialization.
\param params
params used for frame data creation.
*/
/***************************************************************************/
void SHRenderContext::Init(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, SHPerFrameDataParams const& params) noexcept
{
perFrameDataParams = params;
logicalDeviceHdl = inLogicalDeviceHdl;
numThreads = params.numThreads;
currentFrame = 0;
// initialize number of threads to 1 if user passes in 0
if (params.numThreads == 0)
{
SHLOG_ERROR("Number of threads is 0. Using 1 thread instead. ");
numThreads = 1;
}
auto const numImages = params.swapchainHdl->GetNumImages();
// Once the amount of frame data is reinitialized, we don't need to touch it anymore because swapchain recreation only necessitates
// reinitializing the data, not recreating.
frameData.resize(numImages);
for (uint32_t i = 0; i < numImages; ++i)
{
// assign a set non-changing frame index
frameData[i].frameIndex = i;
// Initialize the swapchain image handle. See first line of recreation function in frame data.
frameData[i].swapchainImageHdl = params.swapchainHdl->GetSwapchainImage(i);
for (uint32_t j = 0; j < params.numThreads; ++j)
{
frameData[i].cmdPoolHdls.push_back(logicalDeviceHdl->CreateCommandPool(params.cmdPoolQueueFamilyType, params.cmdPoolResetMode, params.cmdBufferTransient));
}
}
// Initialize all the info.
for (auto& frame : frameData)
frame.Recreate(logicalDeviceHdl);
}
void SHRenderContext::Destroy(void) noexcept
{
for (auto& data : frameData)
{
for (auto& hdl : data.cmdPoolHdls)
hdl.Free();
data.cmdPoolHdls.clear();
data.Destroy();
}
frameData.clear();
}
SHRenderContext::SHRenderContext(void) noexcept
: numThreads {1}
, currentFrame{0}
{
}
SHRenderContext::~SHRenderContext(void) noexcept
{
}
/***************************************************************************/
/*!
\brief
When the window resizes, the swapchain needs to be recreated. When that
happens, the swapchain images are also new, hence necessitating the need
to update the frame data image handles.
\return
*/
/***************************************************************************/
void SHRenderContext::HandleResize(void) noexcept
{
// Destroy the frame data
for (auto& frame : frameData)
frame.Destroy();
for (auto& frame : frameData)
frame.Recreate(logicalDeviceHdl);
currentFrame = 0;
}
/***************************************************************************/
/*!
\brief
Acquire the index of the next image in the swapchain. Do note that this
function is non-blocking but it takes in a semaphore that will be
signaled when the image becomes available. We want to use this together
with AdvanceFrame because this engine trusts the implementation
to return swapchain images IN ORDER. When that happens the correct
behavior would be that currentFrame will end up being the same as the
what returns from vkAcquireNextImageKHR.
*/
/***************************************************************************/
void SHRenderContext::AcquireNextIamge(void) noexcept
{
uint32_t frameIndex = 0;
logicalDeviceHdl->GetVkLogicalDevice().acquireNextImageKHR(perFrameDataParams.swapchainHdl->GetVkSwapchain(), std::numeric_limits<uint64_t>::max(), frameData[currentFrame].semImgAvailableHdl->GetVkSem(), VK_NULL_HANDLE, &frameIndex);
if (frameIndex != currentFrame)
{
SHLOG_ERROR("Frame index retrieved from vkAcquireNextImageKHR is not the same as currentFrame.");
}
currentFrame = frameIndex;
}
/***************************************************************************/
/*!
\brief
Waits for a fence to be signaled.
*/
/***************************************************************************/
bool SHRenderContext::WaitForFence(void) noexcept
{
return frameData[currentFrame].fenceHdl->Wait(true, std::numeric_limits<uint64_t>::max());
}
/***************************************************************************/
/*!
\brief
Resets a fence.
*/
/***************************************************************************/
void SHRenderContext::ResetFence(void) noexcept
{
frameData[currentFrame].fenceHdl->Reset();
}
void SHRenderContext::SetIsResized(bool resized) noexcept
{
isResized = resized;
}
/***************************************************************************/
/*!
\brief
Advances the frame. The correct frame to use can also be returned by
vkAcquireNextImageKHR so this is just an aiternative.
*/
/***************************************************************************/
void SHRenderContext::AdvanceFrame(void) noexcept
{
currentFrame = (currentFrame + 1u) % frameData.size();
}
SHPerFrameData& SHRenderContext::GetCurrentFrameData(void) noexcept
{
return frameData[currentFrame];
}
SHPerFrameData& SHRenderContext::GetFrameData(uint32_t index) noexcept
{
return frameData[index];
}
uint32_t SHRenderContext::GetCurrentFrame(void) const noexcept
{
return currentFrame;
}
bool SHRenderContext::GetResizeAndReset(void) noexcept
{
bool b = isResized;
isResized = false;
return b;
}
}

View File

@ -0,0 +1,63 @@
#ifndef SH_RENDER_CONTEXT_H
#define SH_RENDER_CONTEXT_H
#include <vector>
#include "SHPerFrameData.h"
namespace SHADE
{
class SHVkSwapchain;
class SHVkLogicalDevice;
//! Think of a render context like the context in OpenGL. When you call draw calls, the drivers are calling
//! queue submissions and presenting functions at the back. Frame swapping is also done there. The only difference a
//! render context in SHADE engine has is that it requires users to call these explicitly in the middle end. While there
//! is little reason the flow of the render context should deviate from its intended usage, we want to leave it up to
//! users to explicitly call functions from here so we don't risk losing opportunities for different usage.
class SHRenderContext
{
private:
//! container of frame data. Note that the manager owns the data, but the frame data themselves do not own anything.
std::vector<SHPerFrameData> frameData;
//! Logical Device for creation and deletion
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! We want to save this to handle resizing without passing in arguments.
SHPerFrameDataParams perFrameDataParams;
//! Number of threads
uint32_t numThreads;
//! current frame being used
uint32_t currentFrame;
bool isResized{ false };
public:
SHRenderContext(void) noexcept;
~SHRenderContext(void) noexcept;
void Init (Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, SHPerFrameDataParams const& params) noexcept;
void Destroy (void) noexcept;
void HandleResize (void) noexcept;
void AcquireNextIamge (void) noexcept;
void AdvanceFrame (void) noexcept;
bool WaitForFence (void) noexcept;
void ResetFence (void) noexcept;
void SetIsResized (bool resized) noexcept;
SHPerFrameData& GetCurrentFrameData(void) noexcept;
SHPerFrameData& GetFrameData (uint32_t index) noexcept;
uint32_t GetCurrentFrame (void) const noexcept;
bool GetResizeAndReset (void) noexcept;
};
}
#endif

View File

@ -0,0 +1,69 @@
#include "SHPch.h"
#include "SHShaderModuleLibrary.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Imports all shader binaries from the source library.
\param logicalDeviceHdl
For creating shader modules.
\param sourceLib
The source library class that stores the container of shader binary data.
*/
/***************************************************************************/
void SHShaderModuleLibrary::ImportFromSourceLibrary(Handle<SHVkLogicalDevice>& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept
{
auto const& sources = sourceLib.GetSourceLibrary();
for (auto const& source : sources)
{
vk::ShaderStageFlagBits shaderType{};
switch (source.shaderType)
{
case SH_SHADER_TYPE::VERTEX:
shaderType = vk::ShaderStageFlagBits::eVertex;
break;
case SH_SHADER_TYPE::FRAGMENT:
shaderType = vk::ShaderStageFlagBits::eFragment;
break;
case SH_SHADER_TYPE::COMPUTE:
shaderType = vk::ShaderStageFlagBits::eCompute;
break;
default:
shaderType = vk::ShaderStageFlagBits::eVertex;
break;
}
Handle<SHVkShaderModule> newShaderModule = logicalDeviceHdl->CreateShaderModule(source.spirvBinary, "main", shaderType, source.name);
shaderModules.emplace(source.id, newShaderModule);
stringToID.emplace(source.name, source.id);
}
}
/***************************************************************************/
/*!
\brief
Gets the shader module based on module name.
\param shaderName
\return
*/
/***************************************************************************/
Handle<SHVkShaderModule> SHShaderModuleLibrary::GetShaderModule(std::string shaderName) const noexcept
{
if (stringToID.contains(shaderName))
return shaderModules.at(stringToID.at(shaderName));
else
return {};
}
}

View File

@ -0,0 +1,42 @@
#ifndef SH_SHADER_MODULE_LIBRARY_H
#define SH_SHADER_MODULE_LIBRARY_H
#include "Graphics/Shaders/SHVkShaderModule.h"
#include "SHShaderSourceLibrary.h"
#include <map>
namespace SHADE
{
class SHVkLogicalDevice;
/*
* The purpose of this shader module library is to be separate from the source library. The source library contains
* pure shader binary data that contains no vulkan related objects. Every time we load on unload a scene/level,
* this class and the source library class is cleared of its modules and recreated.
*/
class SHShaderModuleLibrary
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Stored shader modules
std::unordered_map<uint32_t, Handle<SHVkShaderModule>> shaderModules;
//! We want some sort of interface with strings, instead of ints
std::map<std::string, uint32_t> stringToID;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void ImportFromSourceLibrary(Handle<SHVkLogicalDevice>& logicalDeviceHdl, SHShaderSourceLibrary const& sourceLib) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
Handle<SHVkShaderModule> GetShaderModule(std::string shaderName) const noexcept;
};
}
#endif

View File

@ -0,0 +1,272 @@
#include "SHPch.h"
#include <filesystem>
#include <fstream>
#include "SHShaderSourceLibrary.h"
#include "Tools/SHLogger.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Initializes the directory to take assets from. TODO: Only temporary until
the resource manager is implemented.
\param directory
\return
*/
/***************************************************************************/
void SHShaderSourceLibrary::Init (std::string directory) noexcept
{
shaderDirectory = directory;
if (shaderDirectory.back() != '/')
{
shaderDirectory += '/';
}
}
/***************************************************************************/
/*!
\brief
Private member function to compile a shader STRING source to binary and
returns a vector of 4 bytes.
\param glslSource
The GLSL string source.
\param type
Type of the shader: vertex, fragment, compute, etc.
\param opLevel
Optimization level.
\return
Returns a vector of the binary data.
*/
/***************************************************************************/
std::vector<uint32_t> SHShaderSourceLibrary::CompileToBinary(std::string const& glslSource, char const* const spirvFilename, SH_SHADER_TYPE type, shaderc_optimization_level opLevel /*= shaderc_optimization_level_zero*/)
{
// shaderc compiler
shaderc::Compiler compiler;
shaderc::CompileOptions options;
options.AddMacroDefinition("MY_DEFINE", "1");
// Set optimization levels
if (opLevel != shaderc_optimization_level_zero)
options.SetOptimizationLevel(opLevel);
// Attempt to get the shaderc equivalent shader stage
shaderc_shader_kind shaderKind;
switch (type)
{
case SH_SHADER_TYPE::VERTEX:
shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader;
break;
case SH_SHADER_TYPE::FRAGMENT:
shaderKind = shaderc_shader_kind::shaderc_glsl_fragment_shader;
break;
case SH_SHADER_TYPE::COMPUTE:
shaderKind = shaderc_shader_kind::shaderc_glsl_compute_shader;
break;
default:
shaderKind = shaderc_shader_kind::shaderc_glsl_vertex_shader;
break;
}
// Compile the shader and get the result
shaderc::SpvCompilationResult compileResult = compiler.CompileGlslToSpv(glslSource, shaderKind, spirvFilename, options);
if (compileResult.GetCompilationStatus() != shaderc_compilation_status_success)
{
SHLOG_ERROR("Shaderc failed to compile GLSL shader to binary | " + compileResult.GetErrorMessage());
}
return { compileResult.begin(), compileResult.end() };
}
/***************************************************************************/
/*!
\brief
TODO: Delete after file IO is implemented. Loads a shader from disk.
\param filePath
file path to the shader in the asset directory.
\return
Returns the data in the file in string form.
*/
/***************************************************************************/
std::string SHShaderSourceLibrary::GetStringFromFile(char const* filePath) noexcept
{
// Retrieve contents from filePath
// Ensure ifstream objects can throw exceptions
std::ifstream iFile;
iFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::string fileContent = "";
try
{
// Open file
// Read file's buffer contents into streams
iFile.open(filePath);
std::stringstream fileStream;
fileStream << iFile.rdbuf();
fileContent = fileStream.str();
// Close file handler
iFile.close();
}
catch (std::ifstream::failure e)
{
std::cerr << "File was not successfully read" << filePath << std::endl;
}
return fileContent;
}
/***************************************************************************/
/*!
\brief
Load a shader into the library.
\param filePath
file path to the shader in the asset directory.
*/
/***************************************************************************/
bool SHShaderSourceLibrary::LoadShader (uint32_t id, std::string glslFile, SH_SHADER_TYPE type, bool checkSpirvOutdated/* = true*/, bool recompileAnyway /*= false*/) noexcept
{
//if (sourceLibrary.contains(id))
//{
// SHLOG_ERROR("Shader with ID passed in already exists. Use a different ID");
// return false;
//}
std::string fullGLSLPath = shaderDirectory + glslFile;
auto path = std::filesystem::path(fullGLSLPath);
if (path.extension() != ".glsl")
{
SHLOG_ERROR("Shader is not GLSL file, failed to load shader. ");
return false;
}
std::string spirvFilepath = path.replace_extension("spv").string();
SHShaderData newShaderData{};
newShaderData.shaderType = type;
// spirv file
std::ifstream spirvFile(spirvFilepath, std::ios::ate | std::ios::binary);
// If we disable spirv validation, file is not checked
if (!recompileAnyway &&
spirvFile.is_open() &&
(checkSpirvOutdated ? (std::filesystem::last_write_time(spirvFilepath) > std::filesystem::last_write_time(fullGLSLPath)) : true))
{
// Get file size of binary
uint32_t fileSize = static_cast<uint32_t>(spirvFile.tellg());
// resize container to store binary
newShaderData.spirvBinary.resize(fileSize / sizeof(uint32_t));
// Read data from binary file to container
spirvFile.seekg(0);
spirvFile.read(reinterpret_cast<char*>(newShaderData.spirvBinary.data()), fileSize);
// close file
spirvFile.close();
}
else
{
// Use glslc to generate spirv file
newShaderData.spirvBinary = CompileToBinary(GetStringFromFile(fullGLSLPath.c_str()), spirvFilepath.c_str(), type);
std::ofstream binaryFile(spirvFilepath, std::ios::binary);
if (binaryFile.is_open())
{
// write all data to binary file
binaryFile.write(reinterpret_cast<const char*>(newShaderData.spirvBinary.data()), newShaderData.spirvBinary.size() * sizeof(uint32_t));
}
else
{
SHLOG_ERROR("Failed to modify spirv file. ");
return false;
}
}
newShaderData.name = glslFile;
newShaderData.id = id;
sourceLibrary.emplace_back(std::move (newShaderData));
return true;
}
/***************************************************************************/
/*!
\brief
Gets the entire source library.
\return
The container of binary data.
*/
/***************************************************************************/
std::vector<SHShaderData> const& SHShaderSourceLibrary::GetSourceLibrary(void) const noexcept
{
return sourceLibrary;
}
/***************************************************************************/
/*!
\brief
Move ctor for shader data.
\param rhs
The other shader data
*/
/***************************************************************************/
SHShaderData::SHShaderData(SHShaderData&& rhs) noexcept
: spirvBinary{ std::move (rhs.spirvBinary)}
, shaderType{std::move (rhs.shaderType)}
, name{ std::move (rhs.name)}
, id {std::move (rhs.id)}
{
}
/***************************************************************************/
/*!
\brief
Default ctor for shader data. Does nothing.
*/
/***************************************************************************/
SHShaderData::SHShaderData(void) noexcept
: spirvBinary{}
, shaderType{SH_SHADER_TYPE::VERTEX}
, name{ }
, id{ }
{
}
}

View File

@ -0,0 +1,67 @@
#ifndef SH_SHADER_SOURCE_LIBRARY_H
#define SH_SHADER_SOURCE_LIBRARY_H
#include <map>
#include "SHShaderType.h"
#include "shaderc/shaderc.hpp"
namespace SHADE
{
struct SHShaderData
{
/*-----------------------------------------------------------------------*/
/* MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! container storing the spirv binary
std::vector<uint32_t> spirvBinary;
//! For the compilation of the shader. Vulkan backend will use it too
SH_SHADER_TYPE shaderType;
//! Name of the shader file (without parent path)
std::string name;
//! id of the shader
uint32_t id;
SHShaderData(void) noexcept;
SHShaderData(SHShaderData&& rhs) noexcept;
};
// TODO: This class is purely temporary and will be converted/changed when XQ implements his resource manager
class SHShaderSourceLibrary
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Stores all the source data. Take note that the source here is GLSL source and NOT binary data.
//! Binary data gets passed to the backend to convert to spirv.
std::vector<SHShaderData> sourceLibrary;
//! The directory where the shaders are located.
std::string shaderDirectory;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
std::vector<uint32_t> CompileToBinary(std::string const& glslSource, char const* const spirvFilename, SH_SHADER_TYPE type, shaderc_optimization_level opLevel = shaderc_optimization_level_zero);
// TODO: Delete after file IO is implemented
std::string GetStringFromFile(char const* filePath) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void Init (std::string directory) noexcept;
bool LoadShader (uint32_t id, std::string glslFile, SH_SHADER_TYPE type, bool checkSpirvOutdated = true, bool recompileAnyway = false) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
std::vector<SHShaderData> const& GetSourceLibrary(void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,14 @@
#ifndef SH_SHADER_TYPE_H
#define SH_SHADER_TYPE_H
namespace SHADE
{
enum class SH_SHADER_TYPE
{
VERTEX,
FRAGMENT,
COMPUTE
};
}
#endif

View File

@ -0,0 +1,30 @@
#ifndef SH_PIPELINE_LAYOUT_PARAMS_H
#define SH_PIPELINE_LAYOUT_PARAMS_H
#include "Graphics/SHVulkanIncludes.h"
#include "Resource/Handle.h"
#include "Graphics/Descriptors/SHVkDescriptorSetLayout.h"
namespace SHADE
{
class SHVkShaderModule;
struct SHPipelineLayoutParams
{
/*-----------------------------------------------------------------------*/
/* MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Shader modules for pipeline creation. See pipeline layout for details.
//! Note that this will be moved to pipeline layout.
std::vector<Handle<SHVkShaderModule>> shaderModules;
//! There's a huge chance that there is going to be a layout for a descriptor
//! set that is to be used for many shaders (i.e. a global descriptor set
//! used just for textures or lights for example). In that case, we still
//! want to use the layout to initialize the pipeline layout but we do not
//! want to use it for allocating descriptor sets.
std::vector<Handle<SHVkDescriptorSetLayout>> globalDescSetLayouts = {};
};
}
#endif

View File

@ -0,0 +1,238 @@
#include "SHPch.h"
#include "SHPipelineState.h"
namespace SHADE
{
void SHVkPipelineState::AddDefaultColorBlendAttachment(void) noexcept
{
vk::PipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
colorBlendAttachment.blendEnable = true;
colorBlendAttachment.srcColorBlendFactor = vk::BlendFactor::eSrcAlpha;
colorBlendAttachment.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha;
colorBlendAttachment.colorBlendOp = vk::BlendOp::eAdd;
colorBlendAttachment.srcAlphaBlendFactor = vk::BlendFactor::eOne;
colorBlendAttachment.dstAlphaBlendFactor = vk::BlendFactor::eZero;
colorBlendAttachment.alphaBlendOp = vk::BlendOp::eAdd;
colorBlendState.attachments.push_back(colorBlendAttachment);
}
void SHVkPipelineState::SetInputAssemblyState(SHInputAssemblyState const& state) noexcept
{
dirty = true;
inputAssemblyState = state;
}
void SHVkPipelineState::SetRasterizationState(SHRasterizationState const& state) noexcept
{
dirty = true;
rasterizationState = state;
}
void SHVkPipelineState::SetViewportState(SHViewportState const& state) noexcept
{
dirty = true;
viewportState = state;
}
void SHVkPipelineState::SetMultiSampleState(SHMultisampleState const& state) noexcept
{
dirty = true;
multisampleState = state;
}
void SHVkPipelineState::SetDepthStencilState(SHDepthStencilState const& state) noexcept
{
dirty = true;
depthStencilState = state;
}
void SHVkPipelineState::SetColorBlenState(SHColorBlendState const& state) noexcept
{
dirty = true;
colorBlendState = state;
}
void SHVkPipelineState::SetRenderpass(Handle<SHVkRenderpass> const& inRenderpassHdl) noexcept
{
dirty = true;
renderpassHdl = inRenderpassHdl;
}
void SHVkPipelineState::SetSubpassIndex(uint32_t index) noexcept
{
dirty = true;
subpassIndex = index;
}
SHVertexInputState const& SHVkPipelineState::GetVertexInputState(void) const noexcept
{
return vertexInputState;
}
SHInputAssemblyState const& SHVkPipelineState::GetInputAssemblyState(void) const noexcept
{
return inputAssemblyState;
}
SHRasterizationState const& SHVkPipelineState::GetRasterizationState(void) const noexcept
{
return rasterizationState;
}
SHViewportState const& SHVkPipelineState::GetViewportState(void) const noexcept
{
return viewportState;
}
SHMultisampleState const& SHVkPipelineState::GetMultisampleState(void) const noexcept
{
return multisampleState;
}
SHDepthStencilState const& SHVkPipelineState::GetDepthStencilState(void) const noexcept
{
return depthStencilState;
}
SHColorBlendState const& SHVkPipelineState::GetColorBlenState(void) const noexcept
{
return colorBlendState;
}
Handle<SHVkRenderpass> const& SHVkPipelineState::GetRenderpass(void) const noexcept
{
return renderpassHdl;
}
uint32_t SHVkPipelineState::GetSubpassIndex(void) const noexcept
{
return subpassIndex;
}
void SHVkPipelineState::SetDirty(bool isDirty) noexcept
{
dirty = isDirty;
}
std::tuple<uint32_t, uint32_t, vk::Format> SHVertexInputState::GetInfoFromAttribFormat(SHAttribFormat attribFormat) const noexcept
{
switch (attribFormat)
{
case SHAttribFormat::FLOAT_1D:
return std::make_tuple(1, 4, vk::Format::eR32Sfloat);
case SHAttribFormat::FLOAT_2D:
return std::make_tuple(1, 8, vk::Format::eR32G32Sfloat);
case SHAttribFormat::FLOAT_3D:
return std::make_tuple(1, 12, vk::Format::eR32G32B32Sfloat);
case SHAttribFormat::FLOAT_4D:
return std::make_tuple(1, 16, vk::Format::eR32G32B32A32Sfloat);
// Matrix formats onwards all return less than actual bytes required but the numSlots will adjust offsets accordingly
case SHAttribFormat::MAT_2D:
return std::make_tuple(2, 8, vk::Format::eR32G32Sfloat);
case SHAttribFormat::MAT_3D:
return std::make_tuple(3, 12, vk::Format::eR32G32B32Sfloat);
case SHAttribFormat::MAT_4D:
return std::make_tuple(4, 16, vk::Format::eR32G32B32A32Sfloat);
break;
}
return std::make_tuple(0, 0, vk::Format::eR32Sfloat);
}
SHVertexInputState::SHVertexInputState(void) noexcept
: bindings{}
, attributes{}
{
}
SHVertexInputState::SHVertexInputState(SHVertexInputState const& rhs) noexcept
: bindings{rhs.bindings}
, attributes{rhs.attributes}
{
}
SHVertexInputState::SHVertexInputState(SHVertexInputState&& rhs) noexcept
: bindings{ std::move (rhs.bindings) }
, attributes{ std::move (rhs.attributes) }
{
}
SHVertexInputState& SHVertexInputState::operator=(SHVertexInputState const& rhs) noexcept
{
if (&rhs == this)
return *this;
bindings = rhs.bindings;
attributes = rhs.attributes;
return *this;
}
SHVertexInputState& SHVertexInputState::operator=(SHVertexInputState&& rhs) noexcept
{
if (&rhs == this)
return *this;
bindings = std::move(rhs.bindings);
attributes = std::move(rhs.attributes);
return *this;
}
void SHVertexInputState::AddBinding(bool instanced, bool calcOffset, std::initializer_list<SHVertexAttribute> inAttribs) noexcept
{
// add a binding and get ref to it
bindings.emplace_back();
auto& binding = bindings.back();
// if binding is instanced, attributes are per instance, if not attributes here are per vertex
if (instanced)
binding.inputRate = vk::VertexInputRate::eInstance;
else
binding.inputRate = vk::VertexInputRate::eVertex;
// Offset is 0 at first (for first element)
uint32_t offset = 0;
// for every attribute passed in
for (auto const& attrib : inAttribs)
{
// Get number of slots needed, the bytes required and format of the attribute
auto const& [slots, bytes, format] = GetInfoFromAttribFormat(attrib.attribFormat);
// For each attribute slot the attribute needs
for (uint32_t i = 0; i < slots; ++i)
{
// Add an attribute description
attributes.emplace_back();
auto& vertexAttrib = attributes.back();
// The binding for that attribute description is index of the new binding created earlier in this function
vertexAttrib.binding = static_cast<uint32_t>(bindings.size() - 1);
// Attribute location. New index is simply + 1 of the previous. Starts from 0 obviously
vertexAttrib.location = static_cast<uint32_t>(attributes.size () - 1);
// Get the vkFormat associated with the SHAttribFormat
vertexAttrib.format = format;
// if calcOffset is false, we want to use the calcuated one. If not, use the attribute's offset passed in.
vertexAttrib.offset = calcOffset ? offset : attrib.offset;
// For when we want to use calculated offset and for the stride.
offset += bytes;
}
}
// Stride is always all the offsets added together (Unless of course under special conditions which this class doesn't cover).
binding.stride = offset;
}
}

View File

@ -0,0 +1,225 @@
#ifndef SH_PIPELINE_STATE_H
#define SH_PIPELINE_STATE_H
#include "Graphics/SHVulkanIncludes.h"
#include "Graphics/Renderpass/SHVkRenderpass.h"
#include "Graphics/VertexDescriptors/SHVertexAttribute.h"
#include <tuple>
namespace SHADE
{
// This is mainly for non-interleaved attributes where we have a separate buffer for
// each vertex attribute.
struct SHVertexInputState
{
private:
using numAttribSlots = uint32_t;
using bytesRequired = uint32_t;
//! Vertex attribute bindings
std::vector<vk::VertexInputBindingDescription> bindings;
//! Vertex attributes
std::vector<vk::VertexInputAttributeDescription> attributes;
std::tuple<numAttribSlots, bytesRequired, vk::Format> GetInfoFromAttribFormat(SHAttribFormat attribFormat) const noexcept;
public:
SHVertexInputState(void) noexcept;
SHVertexInputState(SHVertexInputState const& rhs) noexcept;
SHVertexInputState(SHVertexInputState&& rhs) noexcept;
SHVertexInputState& operator= (SHVertexInputState const& rhs) noexcept;
SHVertexInputState& operator= (SHVertexInputState&& rhs) noexcept;
void AddBinding(bool instanced, bool calcOffset, std::initializer_list<SHVertexAttribute> inAttribs) noexcept;
friend class SHVkPipelineState;
friend class SHVkPipeline;
};
struct SHInputAssemblyState
{
//! Primitive topology
vk::PrimitiveTopology topology{ vk::PrimitiveTopology::eTriangleList };
//! For drawElements this will enable primitive restarting
bool primitiveRestart{ VK_FALSE };
};
struct SHRasterizationState
{
//! is depth clamping enabled
bool depthClamp{ VK_FALSE };
//! Rasterizer discard
bool rasterizerDiscard{ VK_FALSE };
//! Fill, line, point
vk::PolygonMode polygonMode{ vk::PolygonMode::eFill};
//! cull front, back or both faces
vk::CullModeFlags cull_mode{ vk::CullModeFlagBits::eBack };
//! CW or CCW
vk::FrontFace frontFacingOrientation{ vk::FrontFace::eCounterClockwise };
bool depthBias{ VK_FALSE };
};
struct SHViewportState
{
//! Number of viewports
uint32_t viewportCount{ 1 };
//! number of scissors
uint32_t scissorCount{ 1 };
};
struct SHMultisampleState
{
//! number of rasterization samples
vk::SampleCountFlagBits rasterizationSamples{ vk::SampleCountFlagBits::e1 };
//! Sample shading
bool sampleShading{ false };
// Minimum sample shading
float minSampleShading{ 1.0f };
//! Sample mask
vk::SampleMask sampleMask{ 0 };
//! Alpha to coverage
bool alphaToCoverage{ false };
//! Alpha to one
bool alphaToOne{ false };
};
struct SHStencilOpState
{
//! When stencil test fails
vk::StencilOp failOp { vk::StencilOp::eReplace};
//! When stencil test passes
vk::StencilOp passOp{ vk::StencilOp::eReplace };
//! When stencil test passes
vk::StencilOp depthFailOp{ vk::StencilOp::eReplace };
//! The compare function for stencil tests
vk::CompareOp compareOp{ vk::CompareOp::eNever};
};
struct SHDepthStencilState
{
bool depthTest{ VK_TRUE };
bool depthWrite{ VK_TRUE };
// Note: Using Reversed depth-buffer for increased precision, so Greater depth values are kept
vk::CompareOp depthCompare{ vk::CompareOp::eGreater };
bool depthBounds{ VK_FALSE };
bool stencilTest{ VK_FALSE };
SHStencilOpState front{};
SHStencilOpState back{};
};
struct SHColorBlendState
{
VkBool32 logic_op_enable{ VK_TRUE };
vk::LogicOp logic_op{ VK_LOGIC_OP_COPY };
std::vector<vk::PipelineColorBlendAttachmentState> attachments;
};
// TODO: Specialization constants
class SHVkPipelineState
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! If any of the pipeline states change, set to true
bool dirty{ false };
//! Vertex input state
SHVertexInputState vertexInputState;
//! Input assembly
SHInputAssemblyState inputAssemblyState;
//! Rasterization state
SHRasterizationState rasterizationState;
//! Viewport state
SHViewportState viewportState;
//! Multisample state
SHMultisampleState multisampleState;
//! Depth Stencil state
SHDepthStencilState depthStencilState;
//! Color blend state
SHColorBlendState colorBlendState;
//! Renderpass that is compatible with the pipeline
Handle<SHVkRenderpass> renderpassHdl;
//! Subpass index
uint32_t subpassIndex;
friend class SHVkPipeline;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void AddDefaultColorBlendAttachment (void) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
template <typename T, typename = std::enable_if_t <std::is_same_v<std::remove_reference_t<T>, SHVertexInputState>>>
void SetVertexInputState (T&& state) noexcept;
void SetInputAssemblyState(SHInputAssemblyState const& state) noexcept;
void SetRasterizationState(SHRasterizationState const& state) noexcept;
void SetViewportState (SHViewportState const& state) noexcept;
void SetMultiSampleState (SHMultisampleState const& state) noexcept;
void SetDepthStencilState (SHDepthStencilState const& state) noexcept;
void SetColorBlenState (SHColorBlendState const& state) noexcept;
void SetRenderpass (Handle<SHVkRenderpass> const& inRenderpassHdl) noexcept;
void SetSubpassIndex (uint32_t index) noexcept;
SHVertexInputState const& GetVertexInputState (void) const noexcept;
SHInputAssemblyState const& GetInputAssemblyState (void) const noexcept;
SHRasterizationState const& GetRasterizationState (void) const noexcept;
SHViewportState const& GetViewportState (void) const noexcept;
SHMultisampleState const& GetMultisampleState (void) const noexcept;
SHDepthStencilState const& GetDepthStencilState (void) const noexcept;
SHColorBlendState const& GetColorBlenState (void) const noexcept;
Handle<SHVkRenderpass> const& GetRenderpass (void) const noexcept;
uint32_t GetSubpassIndex (void) const noexcept;
void SetDirty(bool isDirty) noexcept;
};
template <typename T, typename Enable>
void SHVkPipelineState::SetVertexInputState(T&& state) noexcept
{
//static_assert(std::is_same_v<std::remove_reference_t<T>, SHVertexInputState>);
vertexInputState = std::forward<T>(state);
}
}
#endif

View File

@ -0,0 +1,13 @@
#ifndef SH_PIPELINE_TYPE_H
#define SH_PIPELINE_TYPE_H
namespace SHADE
{
enum class SH_PIPELINE_TYPE
{
GRAPHICS,
COMPUTE,
};
}
#endif

View File

@ -0,0 +1,91 @@
#include "SHPch.h"
#include "SHPushConstantInterface.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Adds a variable and its offset to the interface.
\param variableName
The push constant variable's name.
\param offset
The push constant variable's offset.
*/
/***************************************************************************/
void SHPushConstantInterface::AddOffset(std::string variableName, uint32_t offset) noexcept
{
offsets.try_emplace(std::move (variableName), offset);
}
void SHPushConstantInterface::Reset(void) noexcept
{
offsets.clear();
}
/***************************************************************************/
/*!
\brief
Sets the size of the push constant interface.
\param inSize
Size to set to.
*/
/***************************************************************************/
void SHPushConstantInterface::SetSize(uint32_t inSize) noexcept
{
size = inSize;
}
void SHPushConstantInterface::SetShaderStageFlags(vk::ShaderStageFlags flags) noexcept
{
shaderStages = flags;
}
/***************************************************************************/
/*!
\brief
Retrieves an offset given a variable name.
\param variableName
The variable's offset we want to retrieve.
\return
The variable's offset.
*/
/***************************************************************************/
uint32_t SHPushConstantInterface::GetOffset(std::string const& variableName) const noexcept
{
return offsets.at (variableName);
}
/***************************************************************************/
/*!
\brief
Getter for the size.
\return
The size required for the push constant memory.
*/
/***************************************************************************/
uint32_t SHPushConstantInterface::GetSize(void) const noexcept
{
return size;
}
vk::ShaderStageFlags SHPushConstantInterface::GetShaderStageFlags(void) const noexcept
{
return shaderStages;
}
}

View File

@ -0,0 +1,53 @@
#ifndef SH_PUSH_CONSTANT_INTERFACE_H
#define SH_PUSH_CONSTANT_INTERFACE_H
#include <unordered_map>
#include "Graphics/Shaders/SHShaderReflected.h"
namespace SHADE
{
/***************************************************************************/
/*!
\class SHPushConstantInterface
When we want to send data into the push constant memory in command
buffers, the middle end usage is very raw. Vulkan API mainly expects
the offset and data. This class will store the offsets and they can
be accessed via this class.
*/
/***************************************************************************/
class SHPushConstantInterface
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Stores the offset to variables in push constant structs
std::unordered_map<std::string, uint32_t> offsets;
//! The size in bytes the push constant requires
uint32_t size{ 0 };
vk::ShaderStageFlags shaderStages;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void AddOffset (std::string variableName, uint32_t offset) noexcept;
void Reset (void) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
void SetSize (uint32_t inSize) noexcept;
void SetShaderStageFlags (vk::ShaderStageFlags flags) noexcept;
uint32_t GetOffset (std::string const& variableName) const noexcept;
uint32_t GetSize (void) const noexcept;
vk::ShaderStageFlags GetShaderStageFlags (void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,427 @@
#include "SHPch.h"
#include "SHVkPipeline.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Shaders/SHVkShaderModule.h"
#include "Graphics/Debugging/SHVulkanDebugUtil.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Creates a graphics pipeline from the pipeline state.
*/
/***************************************************************************/
void SHVkPipeline::CreateGraphicsPipeline(void) noexcept
{
// Alot of the code is here is just boilerplate assignment. It may look
// like alot but the code is self explanatory.
vk::GraphicsPipelineCreateInfo gpCreateInfo{};
auto const& shaderModules = pipelineLayout->GetShaderModules();
std::vector<vk::PipelineShaderStageCreateInfo> shaderPipelineInfos{shaderModules.size()};
uint32_t numShaderModules = static_cast<uint32_t>(shaderPipelineInfos.size());
for (uint32_t i = 0; i < numShaderModules; ++i)
{
auto& info = shaderPipelineInfos[i];
info.stage = shaderModules[i]->GetShaderStageFlagBits();
info.pName = shaderModules[i]->GetEntryPoint().c_str();
info.module = shaderModules[i]->GetVkShaderModule();
}
// TODO: specialization info
gpCreateInfo.stageCount = numShaderModules;
gpCreateInfo.pStages = shaderPipelineInfos.data();
vk::PipelineVertexInputStateCreateInfo vertexInputState{};
auto const& viState = pipelineState.GetVertexInputState();
vertexInputState.pVertexBindingDescriptions = viState.bindings.data();
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(viState.bindings.size());
vertexInputState.pVertexAttributeDescriptions = viState.attributes.data();
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(viState.attributes.size());
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyState{};
auto const& iaState = pipelineState.GetInputAssemblyState();
inputAssemblyState.topology = iaState.topology;
inputAssemblyState.primitiveRestartEnable = iaState.primitiveRestart;
vk::PipelineRasterizationStateCreateInfo rasterizationState{};
auto const& rasterState = pipelineState.GetRasterizationState();
rasterizationState.depthClampEnable = rasterState.depthClamp;
rasterizationState.rasterizerDiscardEnable = rasterState.rasterizerDiscard;
rasterizationState.polygonMode = rasterState.polygonMode;
rasterizationState.cullMode = rasterState.cull_mode;
rasterizationState.frontFace = rasterState.frontFacingOrientation;
rasterizationState.depthBiasEnable = rasterState.depthBias;
rasterizationState.depthBiasClamp = 1.0f;
rasterizationState.depthBiasSlopeFactor = 1.0f;
rasterizationState.lineWidth = 1.0f;
vk::PipelineViewportStateCreateInfo viewportState{};
auto const& vpState = pipelineState.GetViewportState();
viewportState.viewportCount = vpState.viewportCount;
viewportState.scissorCount = vpState.scissorCount;
vk::PipelineDepthStencilStateCreateInfo depthStencilState{};
auto const& dsState = pipelineState.GetDepthStencilState();
depthStencilState.depthTestEnable = dsState.depthTest;
depthStencilState.depthWriteEnable = dsState.depthWrite;
depthStencilState.depthCompareOp = dsState.depthCompare;
depthStencilState.depthBoundsTestEnable = dsState.depthBounds;
depthStencilState.stencilTestEnable = dsState.stencilTest;
depthStencilState.front.failOp = dsState.front.failOp;
depthStencilState.front.passOp = dsState.front.passOp;
depthStencilState.front.depthFailOp = dsState.front.depthFailOp;
depthStencilState.front.compareOp = dsState.front.compareOp;
depthStencilState.front.compareMask = ~0U;
depthStencilState.front.writeMask = ~0U;
depthStencilState.front.reference = ~0U;
depthStencilState.back.failOp = dsState.back.failOp;
depthStencilState.back.passOp = dsState.back.passOp;
depthStencilState.back.depthFailOp = dsState.back.depthFailOp;
depthStencilState.back.compareOp = dsState.back.compareOp;
depthStencilState.back.compareMask = ~0U;
depthStencilState.back.writeMask = ~0U;
depthStencilState.back.reference = ~0U;
vk::PipelineColorBlendStateCreateInfo colorBlendState{};
auto const& cbState = pipelineState.colorBlendState;
colorBlendState.logicOpEnable = cbState.logic_op_enable;
colorBlendState.logicOp = cbState.logic_op;
colorBlendState.attachmentCount = static_cast<uint32_t>(cbState.attachments.size());
colorBlendState.pAttachments = reinterpret_cast<const vk::PipelineColorBlendAttachmentState*>(cbState.attachments.data());
colorBlendState.blendConstants[0] = 1.0f;
colorBlendState.blendConstants[1] = 1.0f;
colorBlendState.blendConstants[2] = 1.0f;
colorBlendState.blendConstants[3] = 1.0f;
std::array dynamicStates =
{
vk::DynamicState::eViewport,
vk::DynamicState::eScissor,
vk::DynamicState::eLineWidth,
vk::DynamicState::eDepthBias,
vk::DynamicState::eBlendConstants,
vk::DynamicState::eDepthBounds,
vk::DynamicState::eStencilCompareMask,
vk::DynamicState::eStencilWriteMask,
vk::DynamicState::eStencilReference,
};
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo
{
.pNext = nullptr,
.flags{},
.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size()),
.pDynamicStates = dynamicStates.data()
};
vk::PipelineMultisampleStateCreateInfo multisamplingCreateInfo{};
auto const& msState = pipelineState.multisampleState;
multisamplingCreateInfo.sampleShadingEnable = msState.sampleShading;
multisamplingCreateInfo.rasterizationSamples = msState.rasterizationSamples;
multisamplingCreateInfo.minSampleShading = msState.minSampleShading;
if (msState.sampleMask != 0)
multisamplingCreateInfo.pSampleMask = &msState.sampleMask;
multisamplingCreateInfo.alphaToCoverageEnable = msState.alphaToCoverage;
multisamplingCreateInfo.alphaToOneEnable = msState.alphaToOne;
gpCreateInfo.pVertexInputState = &vertexInputState;
gpCreateInfo.pInputAssemblyState = &inputAssemblyState;
gpCreateInfo.pMultisampleState = &multisamplingCreateInfo;
gpCreateInfo.pRasterizationState = &rasterizationState;
gpCreateInfo.pViewportState = &viewportState;
gpCreateInfo.pDepthStencilState = &depthStencilState;
gpCreateInfo.pColorBlendState = &colorBlendState;
gpCreateInfo.pDynamicState = &dynamicStateCreateInfo;
gpCreateInfo.layout = pipelineLayout->GetVkPipelineLayout();
gpCreateInfo.renderPass = pipelineState.GetRenderpass()->GetVkRenderpass();
gpCreateInfo.subpass = pipelineState.GetSubpassIndex();
gpCreateInfo.basePipelineHandle = VK_NULL_HANDLE;
gpCreateInfo.basePipelineIndex = -1;
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createGraphicsPipelines(VK_NULL_HANDLE, 1, &gpCreateInfo, nullptr, &vkPipeline); result != vk::Result::eSuccess)
SHVulkanDebugUtil::ReportVkError(result, "Failed to create Graphics Pipeline. ");
else
{
SHVulkanDebugUtil::ReportVkSuccess("Successfully created a Graphics Pipeline. ");
created = true;
}
}
void SHVkPipeline::CreateComputePipeline(void) noexcept
{
}
/***************************************************************************/
/*!
\brief
Non-default ctor.
\param inLogicalDeviceHdl
Needed for creation and destruction.
\param inPipelineLayout
The pipeline layout to use.
\param state
The pipeline state to copy from. This can be nullptr, in which case the
default constructed pipeline state will be used and initialized with
the renderpass and subpass passed in.
\param renderpassHdl
The renderpass that this pipeline will be used in. If state is not
nullptr, this parameter is ignored.
\param subpass
The subpass that this pipeline will be used in. If state is not
nullptr, this parameter is ignored.
\param type
The type of the pipeline.
*/
/***************************************************************************/
SHVkPipeline::SHVkPipeline(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkPipelineLayout> const& inPipelineLayout, SHVkPipelineState const* const state, Handle<SHVkRenderpass> const& renderpassHdl, uint32_t subpass, SH_PIPELINE_TYPE type) noexcept
: pipelineState{ } // copy the pipeline state
, pipelineType {type}
, vkPipeline {VK_NULL_HANDLE}
, logicalDeviceHdl{ inLogicalDeviceHdl }
, pipelineLayout { inPipelineLayout }
, created {false}
{
// We want to create a pipeline
if (state != nullptr)
{
pipelineState = *state;
// Creates a pipeline based on type
ConstructPipeline();
}
else
{
pipelineState.SetRenderpass(renderpassHdl);
pipelineState.SetSubpassIndex(subpass);
}
}
/***************************************************************************/
/*!
\brief
Move ctor.
\param rhs
The other SHVkPipeline
*/
/***************************************************************************/
SHVkPipeline::SHVkPipeline(SHVkPipeline&& rhs) noexcept
: vkPipeline {rhs.vkPipeline}
, pipelineState {rhs.pipelineState}
, pipelineType {rhs.pipelineType}
, created {rhs.created}
, logicalDeviceHdl{ rhs.logicalDeviceHdl }
, pipelineLayout { rhs.pipelineLayout }
{
vkPipeline = VK_NULL_HANDLE;
}
/***************************************************************************/
/*!
\brief
Destructor. Destroys the pipeline.
*/
/***************************************************************************/
SHVkPipeline::~SHVkPipeline(void) noexcept
{
logicalDeviceHdl->GetVkLogicalDevice().destroyPipeline(vkPipeline, nullptr);
}
/***************************************************************************/
/*!
\brief
move assignment operator overload.
\param rhs
The other pipeline.
\return
The reference to the pipeline.
*/
/***************************************************************************/
SHVkPipeline& SHVkPipeline::operator=(SHVkPipeline&& rhs) noexcept
{
if (&rhs == this)
return *this;
vkPipeline = rhs.vkPipeline;
pipelineState = rhs.pipelineState;
pipelineType = rhs.pipelineType;
created = rhs.created;
logicalDeviceHdl = rhs.logicalDeviceHdl;
vkPipeline = VK_NULL_HANDLE;
return *this;
}
/***************************************************************************/
/*!
\brief
For users to call when they created the pipeline with the default
constructor.
\param type
The type of the pipeline (graphics, compute, etc).
*/
/***************************************************************************/
void SHVkPipeline::ConstructPipeline(void) noexcept
{
// if it was created before, destroy it
if (created)
logicalDeviceHdl->GetVkLogicalDevice().destroyPipeline(vkPipeline, nullptr);
// Set to false again. If creation succeeds after this, this will be true
created = false;
// Create pipeline based on type
switch (pipelineType)
{
case SH_PIPELINE_TYPE::GRAPHICS:
CreateGraphicsPipeline();
break;
case SH_PIPELINE_TYPE::COMPUTE:
CreateComputePipeline();
break;
}
}
/***************************************************************************/
/*!
\brief
Sets the pipeline type.
\param type
The pipeline type.
*/
/***************************************************************************/
void SHVkPipeline::SetPipelineType(SH_PIPELINE_TYPE type) noexcept
{
pipelineState.SetDirty(true);
pipelineType = type;
}
/***************************************************************************/
/*!
\brief
Returns a reference to the pipeline state. Not a const reference because
we want to be able to modify the pipeline state.
\return
Reference to the stored pipeline state.
*/
/***************************************************************************/
SHVkPipelineState& SHVkPipeline::GetPipelineState(void) noexcept
{
return pipelineState;
}
/***************************************************************************/
/*!
\brief
Derives a pipeline bind point from the type of the pipeline and returns
it.
\return
The derived bind point type.
*/
/***************************************************************************/
vk::PipelineBindPoint SHVkPipeline::GetPipelineBindPoint(void) const noexcept
{
switch (pipelineType)
{
case SH_PIPELINE_TYPE::GRAPHICS:
return vk::PipelineBindPoint::eGraphics;
case SH_PIPELINE_TYPE::COMPUTE:
return vk::PipelineBindPoint::eCompute;
break;
default:
return vk::PipelineBindPoint::eGraphics;
break;
}
}
/***************************************************************************/
/*!
\brief
Gets the vulkan pipeline handle.
\return
The vulkan pipeline handle.
*/
/***************************************************************************/
vk::Pipeline SHVkPipeline::GetVkPipeline(void) const noexcept
{
return vkPipeline;
}
/***************************************************************************/
/*!
\brief
Returns whether or not the pipeline has been created.
\return
*/
/***************************************************************************/
bool SHVkPipeline::GetIsCreated(void) const noexcept
{
return created;
}
Handle<SHVkPipelineLayout> SHVkPipeline::GetPipelineLayout(void) const noexcept
{
return pipelineLayout;
}
}

View File

@ -0,0 +1,80 @@
#ifndef SH_VK_PIPELINE_H
#define SH_VK_PIPELINE_H
#include "SHPipelineState.h"
#include "SHPipelineType.h"
#include "Resource/Handle.h"
#include "Graphics/Pipeline/SHVkPipelineLayout.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkPipeline
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Vulkan pipeline handle
vk::Pipeline vkPipeline;
//! pipeline state
SHVkPipelineState pipelineState;
//! The type of the pipeline (affects creation)
SH_PIPELINE_TYPE pipelineType;
//! Logical device needed for creation and destruction of pipeline
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! Pipeline Layout used for the pipeline
Handle<SHVkPipelineLayout> pipelineLayout;
//! Sometimes we want to modify some state values before we create the
//! pipeline. This boolean just helps to identify if the pipeline has
//! already been created or not
bool created;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void CreateGraphicsPipeline(void) noexcept;
void CreateComputePipeline (void) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHVkPipeline (Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl,
Handle<SHVkPipelineLayout> const& inPipelineLayout,
SHVkPipelineState const* const state,
Handle<SHVkRenderpass> const& renderpassHdl,
uint32_t subpass,
SH_PIPELINE_TYPE type) noexcept;
SHVkPipeline (SHVkPipeline&& rhs) noexcept;
~SHVkPipeline (void) noexcept;
SHVkPipeline& operator= (SHVkPipeline&& rhs) noexcept;
// TODO: move ctors
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void ConstructPipeline(void) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
void SetPipelineType (SH_PIPELINE_TYPE type) noexcept;
SHVkPipelineState& GetPipelineState (void) noexcept;
vk::PipelineBindPoint GetPipelineBindPoint (void) const noexcept;
vk::Pipeline GetVkPipeline (void) const noexcept;
bool GetIsCreated (void) const noexcept;
Handle<SHVkPipelineLayout> GetPipelineLayout (void) const noexcept;
};
}
#endif

View File

@ -0,0 +1,424 @@
#include "SHPch.h"
#include "SHVkPipelineLayout.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Shaders/SHVkShaderModule.h"
#include "Tools/SHLogger.h"
#include "Graphics/Instance/SHVkInstance.h"
namespace SHADE
{
void SHVkPipelineLayout::PreparePushConstantInterface (void) noexcept
{
// The starting of every push constant block, will get updated later.
uint32_t startOffset = 0;
// To check for duplicates
std::vector<SHShaderPushConstantInfo const*> pcInfos;
vk::ShaderStageFlags stageFlags;
for (auto& shaderModule : shaderModules)
{
// References for convenience
auto const& reflectedData = shaderModule->GetReflectedData();
auto const& pcInfo = reflectedData.GetPushConstantInfo();
// If a push constant block exists for the shader module
if (pcInfo.memberCount != 0)
{
bool exists = false;
// Check if push constant block already exists
for (uint32_t i = 0; i < pcInfos.size(); ++i)
{
// If there is a block with the same name, member count and size
if (std::strcmp(pcInfos[i]->name, pcInfo.name) == 0 && pcInfos[i]->memberCount == pcInfo.memberCount && pcInfos[i]->size == pcInfo.size)
{
// We just take the existing pc range we built earlier, and allow it to be accessed in potentially other shader stages
vkPcRanges[i].stageFlags |= shaderModule->GetShaderStageFlagBits();
// Set flag and stop checking
exists = true;
break;
}
}
// If the block doesn't exist yet
if (!exists)
{
// Loop through all member variables of the new push constant block
for (uint32_t i = 0; i < pcInfo.memberCount; ++i)
{
std::string variableName;
variableName.reserve(50);
variableName += pcInfo.name;
variableName += ".";
variableName += pcInfo.members[i].name;
// Add the variable's offset info to the interface
pushConstantInterface.AddOffset(std::move(variableName), startOffset + pcInfo.members[i].offset);
}
// New push constant range
vk::PushConstantRange newRange;
// set offset and size
newRange.offset = startOffset;
newRange.size = pcInfo.size;
// Stage flags will be whatever shader stage of the shader that contains the push constant block
newRange.stageFlags = shaderModule->GetShaderStageFlagBits();
// Add to the list foe checking later
pcInfos.push_back(&pcInfo);
// For pipeline layout to consume
vkPcRanges.push_back(newRange);
// Next push constant block will start next to the previous push constant block
startOffset += pcInfo.size;
}
}
stageFlags |= shaderModule->GetShaderStageFlagBits();
}
// After all the sizes of the push constant blocks have been added, record the size in the interface
pushConstantInterface.SetSize(startOffset);
pushConstantInterface.SetShaderStageFlags(stageFlags);
}
void SHVkPipelineLayout::PrepareDescriptorLayout(void) noexcept
{
enum class PARSE_RESULT
{
STAGE_APPENDED,
REJECTED,
DOES_NOT_EXIST,
};
// Function that attempts to take a binding and see if it already exists.
auto TryParseExistingBinding = [](std::vector<SHVkDescriptorSetLayout::Binding>& bindings, vk::DescriptorType type, uint32_t bindPoint, vk::ShaderStageFlagBits stage)
{
for (auto& binding : bindings)
{
// If some existing binding is detected...
if (bindPoint == binding.BindPoint)
{
// ...and the existing binding has the same descriptor type
if (type == binding.Type)
{
// Append shader stage since it exists in more than 1 shader
binding.Stage |= stage;
return PARSE_RESULT::STAGE_APPENDED;
}
else
{
SHLOG_ERROR("Binding Set pair already parsed with a different descriptor type and therefore unavailable. ");
return PARSE_RESULT::REJECTED;
}
}
}
return PARSE_RESULT::DOES_NOT_EXIST;
};
// KEY is set index, VALUE is vector of bindings (perfect for descriptor set layout construction). We
// want this to be map because we want to add the descriptor layouts IN ORDER.
std::map<uint32_t, std::vector<SHVkDescriptorSetLayout::Binding>> setsWithBindings;
//! Now we take descriptor set info from all shaders and prepare some bindings for the descriptor set
for (auto& shaderModule : shaderModules)
{
auto const& descBindingInfo = shaderModule->GetReflectedData().GetDescriptorBindingInfo();
auto const& reflectedSets = descBindingInfo.GetReflectedSets();
// If there are no descriptor sets for the shader module, just skip to the next one
if (reflectedSets.empty())
continue;
// For every descriptor set
for (auto const* const set : reflectedSets)
{
// For descriptor sets that are found in the shader that are meant to be global descriptors, don't parse them
if (set->set < descriptorSetLayoutsGlobal.size())
continue;
// Convenient variable for the current descriptor set index
uint32_t const CURRENT_SET = set->set;
for (uint32_t i = 0; i < set->binding_count; ++i)
{
// for convenience
SpvReflectDescriptorBinding const* const reflectedBinding = set->bindings[i];
// If same set binding pair is found
if (TryParseExistingBinding(setsWithBindings[CURRENT_SET], SHShaderDescriptorBindingInfo::ConvertFromReflectDescType (reflectedBinding->descriptor_type), reflectedBinding->binding, shaderModule->GetShaderStageFlagBits()) == PARSE_RESULT::DOES_NOT_EXIST)
{
// New binding for the set binding pair number above
SHVkDescriptorSetLayout::Binding newBinding;
// Straightforward stuff.
newBinding.BindPoint = reflectedBinding->binding;
newBinding.Stage = shaderModule->GetShaderStageFlagBits();
newBinding.Type = descBindingInfo.ConvertFromReflectDescType(reflectedBinding->descriptor_type);
// In reality, the check for variable descriptor sets do not exists in spirv-reflect. Fortunately, when a shader
// defines a boundless descriptor binding in the shader, the information reflected makes the array dimensions
// contain a 1 element of value 1. Knowing that having an array [1] doesn't make sense, we can use this to
// signify a variable sized binding.
if (reflectedBinding->array.dims[0] == 1)
{
// variable binding has to be the last in the set
if (i == set->binding_count - 1)
{
// Set to predefined upper bound. this probably doesn't do any allocations yet. It just tells Vulkan this is the maximum allowed
newBinding.DescriptorCount = SHVkDescriptorSetLayout::Binding::VARIABLE_DESCRIPTOR_UPPER_BOUND;
// Set the flags for variable bindings
newBinding.flags = vk::DescriptorBindingFlagBits::eVariableDescriptorCount;
}
else
SHLOG_ERROR("Variable size binding is detected, but the binding is not the last binding of the set and is therefore invalid. ");
}
setsWithBindings[CURRENT_SET].emplace_back(newBinding);
}
}
} // for reflected descriptor set
} // for shader module
// Now we want to create a container to contain all the required descriptor set for both allocation and pipelines
descriptorSetLayoutsAllocate.reserve(setsWithBindings.size());
// Create the descriptor set layouts here for each set in setWithBindings. Essentially you would have
// 1 descriptor set layout for every descriptor set detected.
for (auto const& set : setsWithBindings)
{
auto newDescriptorSetLayout = logicalDeviceHdl->CreateDescriptorSetLayout(set.second);
descriptorSetLayoutsAllocate.push_back(newDescriptorSetLayout);
}
// Prepare the vulkan layouts for pipeline layout creation
PrepareVkDescriptorSetLayouts();
}
/***************************************************************************/
/*!
\brief
Prepares the vulkan descriptor set layout for pipeline layout consumption.
*/
/***************************************************************************/
void SHVkPipelineLayout::PrepareVkDescriptorSetLayouts(void) noexcept
{
// Settle allocate layouts first
vkDescriptorSetLayoutsAllocate.reserve(descriptorSetLayoutsAllocate.size());
for (auto const& layout : descriptorSetLayoutsAllocate)
{
vkDescriptorSetLayoutsAllocate.emplace_back(layout->GetVkHandle());
}
// pipeline layouts contain global layouts first, then layouts for allocation
vkDescriptorSetLayoutsPipeline.reserve(descriptorSetLayoutsAllocate.size() + descriptorSetLayoutsGlobal.size());
// First we insert the global layouts
for (auto const& layout : descriptorSetLayoutsGlobal)
vkDescriptorSetLayoutsPipeline.emplace_back(layout->GetVkHandle());
// Then we append layouts for allocation at the back of the vector
std::copy(vkDescriptorSetLayoutsAllocate.begin(), vkDescriptorSetLayoutsAllocate.end(), std::back_inserter(vkDescriptorSetLayoutsPipeline));
}
/***************************************************************************/
/*!
\brief
Destroys the pipeline layout and descriptor layouts used.
*/
/***************************************************************************/
void SHVkPipelineLayout::Destroy(void) noexcept
{
// Kill the pipeline layout if required
if (vkPipelineLayout)
logicalDeviceHdl->GetVkLogicalDevice().destroyPipelineLayout(vkPipelineLayout, nullptr);
// Reset push constant interface before reflecting
pushConstantInterface.Reset();
// Clear pc ranges
vkPcRanges.clear();
// Kill all descriptor set layouts
for (auto& layout : descriptorSetLayoutsGlobal)
SHVkInstance::GetResourceManager().Free(layout);
descriptorSetLayoutsGlobal.clear();
for (auto& layout : descriptorSetLayoutsAllocate)
SHVkInstance::GetResourceManager().Free(layout);
descriptorSetLayoutsAllocate.clear();
// We don't need to use vulkan to destroy descriptor sets here since they are just owned by the container with handles
vkDescriptorSetLayoutsAllocate.clear();
vkDescriptorSetLayoutsPipeline.clear();
}
/***************************************************************************/
/*!
\brief
Non-default ctor. Initializes shader modules used for the pipeline.
\param inLogicalDeviceHdl
logical device required for creation and destruction
\param shaderModules
Shader modules that will be used in pipeline (not pipeline layout)
creation.
*/
/***************************************************************************/
SHVkPipelineLayout::SHVkPipelineLayout(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, SHPipelineLayoutParams& pipelineLayoutParams) noexcept
: vkPipelineLayout {VK_NULL_HANDLE}
, shaderModules{std::move (pipelineLayoutParams.shaderModules)}
, logicalDeviceHdl {inLogicalDeviceHdl}
, pushConstantInterface{}
, vkPcRanges{}
, descriptorSetLayoutsGlobal{pipelineLayoutParams.globalDescSetLayouts } // do a copy, some other pipeline layout might need this
, descriptorSetLayoutsAllocate{}
, vkDescriptorSetLayoutsAllocate{}
, vkDescriptorSetLayoutsPipeline{}
{
for (auto& mod : shaderModules)
{
mod->AddCallback([this]()
{
RecreateIfNeeded();
}
);
}
RecreateIfNeeded ();
}
/***************************************************************************/
/*!
\brief
Move ctor. Does what it says.
\param rhs
The other pipeline layout to move from.
*/
/***************************************************************************/
SHVkPipelineLayout::SHVkPipelineLayout(SHVkPipelineLayout&& rhs) noexcept
: logicalDeviceHdl {rhs.logicalDeviceHdl}
, vkPipelineLayout {rhs.vkPipelineLayout}
, shaderModules {std::move (rhs.shaderModules)}
, pushConstantInterface {std::move (rhs.pushConstantInterface)}
, vkPcRanges {std::move (rhs.vkPcRanges)}
, descriptorSetLayoutsGlobal {std::move (rhs.descriptorSetLayoutsGlobal)}
, descriptorSetLayoutsAllocate {std::move (rhs.descriptorSetLayoutsAllocate)}
, vkDescriptorSetLayoutsAllocate{std::move (rhs.vkDescriptorSetLayoutsAllocate)}
, vkDescriptorSetLayoutsPipeline{std::move (rhs.vkDescriptorSetLayoutsAllocate)}
{
rhs.vkPipelineLayout = VK_NULL_HANDLE;
}
/***************************************************************************/
/*!
\brief
Destructor for the pipeline layout.
*/
/***************************************************************************/
SHVkPipelineLayout::~SHVkPipelineLayout(void) noexcept
{
Destroy();
}
void SHVkPipelineLayout::RecreateIfNeeded(void) noexcept
{
Destroy();
vk::PipelineLayoutCreateInfo plCreateInfo{};
// Prepare push constant information
PreparePushConstantInterface();
// This takes the reflection information from all the shader modules used in the pipeline and creates descriptor set layouts
// for the pipeline
PrepareDescriptorLayout();
// Set push constant data to pipeline layout
plCreateInfo.pushConstantRangeCount = static_cast<uint32_t>(vkPcRanges.size());
plCreateInfo.pPushConstantRanges = vkPcRanges.data();
// To initialize the descriptor set layouts for the pipeline layout.
plCreateInfo.setLayoutCount = static_cast<uint32_t>(vkDescriptorSetLayoutsPipeline.size());
plCreateInfo.pSetLayouts = vkDescriptorSetLayoutsPipeline.data();
if (auto const RESULT = logicalDeviceHdl->GetVkLogicalDevice().createPipelineLayout(&plCreateInfo, nullptr, &vkPipelineLayout); RESULT != vk::Result::eSuccess)
{
SHVulkanDebugUtil::ReportVkError(RESULT, "Failed to create Pipeline Layout. ");
return;
}
else
SHVulkanDebugUtil::ReportVkSuccess("Successfully created Pipeline Layout. ");
}
std::vector<Handle<SHVkShaderModule>> const& SHVkPipelineLayout::GetShaderModules(void) const noexcept
{
return shaderModules;
}
vk::PipelineLayout SHVkPipelineLayout::GetVkPipelineLayout(void) const noexcept
{
return vkPipelineLayout;
}
SHPushConstantInterface const& SHVkPipelineLayout::GetPushConstantInterface(void) const noexcept
{
return pushConstantInterface;
}
Handle<SHShaderBlockInterface> SHVkPipelineLayout::GetShaderBlockInterface(uint32_t set, uint32_t binding, vk::ShaderStageFlagBits shaderStage) const noexcept
{
for (auto& shader : shaderModules)
{
if (shader->GetShaderStageFlagBits() == shaderStage)
return shader->GetReflectedData().GetDescriptorBindingInfo().GetShaderBlockInterface(set, binding);
}
return {};
}
SHVkPipelineLayout& SHVkPipelineLayout::operator=(SHVkPipelineLayout&& rhs) noexcept
{
if (&rhs == this)
return *this;
logicalDeviceHdl = rhs.logicalDeviceHdl;
vkPipelineLayout = rhs.vkPipelineLayout;
shaderModules = std::move(rhs.shaderModules);
pushConstantInterface = std::move(rhs.pushConstantInterface);
vkPcRanges = std::move(rhs.vkPcRanges);
descriptorSetLayoutsGlobal = std::move(rhs.descriptorSetLayoutsGlobal);
descriptorSetLayoutsAllocate = std::move(rhs.descriptorSetLayoutsAllocate);
vkDescriptorSetLayoutsAllocate = std::move(rhs.vkDescriptorSetLayoutsAllocate);
vkDescriptorSetLayoutsPipeline = std::move(rhs.vkDescriptorSetLayoutsAllocate);
rhs.vkPipelineLayout = VK_NULL_HANDLE;
return *this;
}
}

View File

@ -0,0 +1,80 @@
#ifndef SH_VK_PIPELINE_LAYOUT_H
#define SH_VK_PIPELINE_LAYOUT_H
#include "SHPipelineLayoutParams.h"
#include "SHPushConstantInterface.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkPipelineLayout
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Logical device used for creation and destruction.
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! The Vulkan handle to a pipeline layout
vk::PipelineLayout vkPipelineLayout;
//! Shader modules used by the pipeline. Note that these modules themselves
//! are not used in pipeline layout creation. We mainly have it here so that
//! pipelines can simply take in this class and the shader modules would
//! be included.
std::vector<Handle<SHVkShaderModule>> shaderModules;
//! Push constant interface
SHPushConstantInterface pushConstantInterface;
//! Push constant ranges
std::vector<vk::PushConstantRange> vkPcRanges;
//! List of global set layouts
std::vector<Handle<SHVkDescriptorSetLayout>> descriptorSetLayoutsGlobal;
//! List of set layouts to allocate descriptor sets for. Set indices here should
//! also be smaller than the global ones passed in.
std::vector<Handle<SHVkDescriptorSetLayout>> descriptorSetLayoutsAllocate;
//! We want to store this also because we need to allocate later
std::vector<vk::DescriptorSetLayout> vkDescriptorSetLayoutsAllocate;
//! Store for pipeline layout recreation
std::vector<vk::DescriptorSetLayout> vkDescriptorSetLayoutsPipeline;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void PreparePushConstantInterface (void) noexcept;
void PrepareDescriptorLayout (void) noexcept;
void PrepareVkDescriptorSetLayouts(void) noexcept;
void Destroy (void) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHVkPipelineLayout (Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, SHPipelineLayoutParams& pipelineLayoutParams) noexcept;
SHVkPipelineLayout (SHVkPipelineLayout&& rhs) noexcept;
~SHVkPipelineLayout (void) noexcept;
SHVkPipelineLayout& operator= (SHVkPipelineLayout&& rhs) noexcept;
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void RecreateIfNeeded (void) noexcept;
/*-----------------------------------------------------------------------*/
/* SETTERS AND GETTERS */
/*-----------------------------------------------------------------------*/
std::vector<Handle<SHVkShaderModule>> const& GetShaderModules (void) const noexcept;
vk::PipelineLayout GetVkPipelineLayout (void) const noexcept;
SHPushConstantInterface const& GetPushConstantInterface (void) const noexcept;
Handle<SHShaderBlockInterface> GetShaderBlockInterface (uint32_t set, uint32_t binding, vk::ShaderStageFlagBits shaderStage) const noexcept;
};
}
#endif

View File

@ -0,0 +1,86 @@
#include "SHPch.h"
#include "SHVkQueue.h"
#include "Tools/SHLogger.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Synchronization/SHVkSemaphore.h"
#include "Graphics/Synchronization/SHVkFence.h"
#include "Graphics/Commands/SHVkCommandBuffer.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Non default ctor to initialize variables.
\param inVkQueue
The vulkan queue handle to initialize this class with.
\param parent
The queue family this queue originated from.
*/
/***************************************************************************/
SHVkQueue::SHVkQueue(vk::Queue inVkQueue, SHQueueFamilyIndex parent, Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl) noexcept
: vkQueue{ inVkQueue }
, parentFamily{ parent }
, logicalDeviceHdl{ inLogicalDeviceHdl }
{
}
vk::Queue SHVkQueue::GetVkQueue(void) const noexcept
{
return vkQueue;
}
void SHVkQueue::SubmitCommandBuffer(std::initializer_list<Handle<SHVkCommandBuffer>> cmdBuffers, std::initializer_list<Handle<SHVkSemaphore>> signalSems /*= {}*/, std::initializer_list<Handle<SHVkSemaphore>> waitSems /*= {}*/, vk::PipelineStageFlagBits waitDstStageMask /*= {}*/, Handle<SHVkFence> const& optionalFence /*= {}*/) noexcept
{
std::vector<vk::Semaphore> vkWaitSems{ waitSems.size() };
uint32_t i = 0;
for (auto& sem : waitSems)
{
vkWaitSems[i] = sem->GetVkSem();
++i;
}
std::vector<vk::Semaphore> vkSignalSems{ signalSems.size() };
i = 0;
for (auto& sem : signalSems)
{
vkSignalSems[i] = sem->GetVkSem();
++i;
}
std::vector<vk::CommandBuffer> vkCmdBuffers{ cmdBuffers.size() };
i = 0;
for (auto& cmdBuffer : cmdBuffers)
{
vkCmdBuffers[i] = cmdBuffer->GetVkCommandBuffer();
++i;
}
vk::PipelineStageFlags mask = waitDstStageMask;
vk::SubmitInfo submitInfo
{
.waitSemaphoreCount = static_cast<uint32_t>(vkWaitSems.size()),
.pWaitSemaphores = vkWaitSems.data(),
.pWaitDstStageMask = &mask,
.commandBufferCount = static_cast<uint32_t>(vkCmdBuffers.size()),
.pCommandBuffers = vkCmdBuffers.data(),
.signalSemaphoreCount = static_cast<uint32_t>(vkSignalSems.size()),
.pSignalSemaphores = vkSignalSems.data(),
};
// #BackEndTest: Submit the queue
if (auto result = vkQueue.submit(1, &submitInfo, (optionalFence) ? optionalFence->GetVkFence() : nullptr); result != vk::Result::eSuccess)
{
SHVulkanDebugUtil::ReportVkError(result, "Failed to submit command buffer. ");
}
}
}

View File

@ -0,0 +1,54 @@
#ifndef SH_QUEUE_H
#define SH_QUEUE_H
#include "Graphics/SHVulkanIncludes.h"
#include "Graphics/SHVulkanDefines.h"
#include "Resource/Handle.h"
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkCommandBuffer;
class SHVkFence;
class SHVkSemaphore;
enum class SH_QUEUE_FAMILY_ARRAY_INDEX : std::size_t
{
GRAPHICS = 0,
COMPUTE = 1,
TRANSFER = 2,
MAX = 3,
INVALID = 0xFFFF,
};
using SH_Q_FAM = SH_QUEUE_FAMILY_ARRAY_INDEX;
enum class SH_QUEUE_SELECT
{
DEDICATED,
NON_DEDICATED_BEST // First fit means attempting to select the most versatile queue family
};
struct SHQueueParams
{
SH_QUEUE_FAMILY_ARRAY_INDEX arrayIndex;
SH_QUEUE_SELECT selectionMethod;
};
class SHVkQueue
{
private:
vk::Queue vkQueue;
SHQueueFamilyIndex parentFamily;
Handle<SHVkLogicalDevice> logicalDeviceHdl;
public:
SHVkQueue(vk::Queue inVkQueue, SHQueueFamilyIndex parent, Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl) noexcept;
vk::Queue GetVkQueue(void) const noexcept;
void SubmitCommandBuffer(std::initializer_list<Handle<SHVkCommandBuffer>> cmdBuffers, std::initializer_list<Handle<SHVkSemaphore>> signalSems = {}, std::initializer_list<Handle<SHVkSemaphore>> waitSems = {}, vk::PipelineStageFlagBits waitDstStageMask = {}, Handle<SHVkFence> const& fence = {}) noexcept;
};
}
#endif

View File

@ -0,0 +1,941 @@
#include "SHPch.h"
#include "SHRenderGraph.h"
#include "Graphics/Swapchain/SHVkSwapchain.h"
#include "Graphics/Framebuffer/SHVkFramebuffer.h"
#include "Graphics/Devices/SHVkLogicalDevice.h"
#include "Graphics/Images/SHVkImage.h"
#include "Graphics/Images/SHVkImageView.h"
#include "Graphics/Framebuffer/SHVkFramebuffer.h"
#include "Tools/SHLogger.h"
namespace SHADE
{
/***************************************************************************/
/*!
\brief
Non-default ctor for the resource. Using the type of the resource, we
decide whether or not we create a resource or link with a swapchain
resource (image).
\param logicalDevice
Logical device required to create an image resource if the type is NOT
SH_ATT_DESC_TYPE::COLOR_PRESENT.
\param swapchain
Swapchain required to get swapchain image if the type IS
SH_ATT_DESC_TYPE::COLOR_PRESENT.
\param type
Type of the image resource.
\param format
Format of the image resource.
\param w
Width of the image resource.
\param h
Height of the image resource.
\param levels
Number of mipmap levels of the image resource.
\param createFlags
Create flags used when an image resource needs to be created.
*/
/***************************************************************************/
SHRenderGraphResource::SHRenderGraphResource(Handle<SHVkLogicalDevice> const& logicalDevice, Handle<SHVkSwapchain> const& swapchain, std::string const& name, SH_ATT_DESC_TYPE type, vk::Format format, uint32_t w, uint32_t h, uint8_t levels, vk::ImageCreateFlagBits createFlags) noexcept
: resourceType{ type }
, resourceFormat{ format }
, images{}
, imageViews{}
, width{ w }
, height{ h }
, mipLevels{ levels }
, resourceName{ name }
{
// If the resource type is an arbitrary image and not swapchain image
if (type != SH_ATT_DESC_TYPE::COLOR_PRESENT)
{
vk::ImageAspectFlags imageAspectFlags;
vk::ImageUsageFlags usage = {};
// Check the resource type and set image usage flags and image aspect flags accordingly
switch (resourceType)
{
case SH_ATT_DESC_TYPE::COLOR:
usage |= vk::ImageUsageFlagBits::eColorAttachment;
imageAspectFlags |= vk::ImageAspectFlagBits::eColor;
break;
case SH_ATT_DESC_TYPE::DEPTH:
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
imageAspectFlags |= vk::ImageAspectFlagBits::eDepth;
break;
case SH_ATT_DESC_TYPE::STENCIL:
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
imageAspectFlags |= vk::ImageAspectFlagBits::eStencil;
break;
case SH_ATT_DESC_TYPE::DEPTH_STENCIL:
usage |= vk::ImageUsageFlagBits::eDepthStencilAttachment;
imageAspectFlags |= vk::ImageAspectFlagBits::eStencil | vk::ImageAspectFlagBits::eDepth;
break;
}
// The resource is not a swapchain image, just use the first slot of the vector
images.push_back(logicalDevice->CreateImage(width, height, mipLevels, resourceFormat, usage, createFlags));
// prepare image view details
SHImageViewDetails viewDetails
{
.viewType = vk::ImageViewType::e2D,
.format = images[0]->GetImageFormat(),
.imageAspectFlags = imageAspectFlags,
.baseMipLevel = 0,
.mipLevelCount = mipLevels,
.baseArrayLayer = 0,
.layerCount = 1,
};
// just 1 image view created
imageViews.push_back(images[0]->CreateImageView(logicalDevice, images[0], viewDetails));
}
else // if swapchain image resource
{
// Prepare image view details
SHImageViewDetails viewDetails
{
.viewType = vk::ImageViewType::e2D,
.format = swapchain->GetSurfaceFormatKHR().format,
.imageAspectFlags = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.mipLevelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
// We want an image handle for every swapchain image
images.resize(swapchain->GetNumImages());
imageViews.resize(swapchain->GetNumImages());
for (uint32_t i = 0; i < swapchain->GetNumImages(); ++i)
{
images[i] = swapchain->GetSwapchainImage(i);
imageViews[i] = images[i]->CreateImageView(logicalDevice, images[i], viewDetails);
}
}
}
/***************************************************************************/
/*!
\brief
Move ctor for resource.
\param rhs
The other resource.
*/
/***************************************************************************/
SHRenderGraphResource::SHRenderGraphResource(SHRenderGraphResource&& rhs) noexcept
: resourceName{ std::move(rhs.resourceName) }
, resourceType{ std::move(rhs.resourceType) }
, images{ std::move(rhs.images) }
, imageViews{ std::move(rhs.imageViews) }
, resourceFormat{ std::move(rhs.resourceFormat) }
, width{ rhs.width }
, height{ rhs.height }
, mipLevels{ rhs.mipLevels }
{
}
/***************************************************************************/
/*!
\brief
Move assignment operator.
\param rhs
The other resource.
\return
*/
/***************************************************************************/
SHRenderGraphResource& SHRenderGraphResource::operator=(SHRenderGraphResource&& rhs) noexcept
{
if (this == &rhs)
return *this;
resourceName = std::move(rhs.resourceName);
resourceType = std::move(rhs.resourceType);
images = std::move(rhs.images);
imageViews = std::move(rhs.imageViews);
resourceFormat = std::move(rhs.resourceFormat);
width = rhs.width;
height = rhs.height;
mipLevels = rhs.mipLevels;
return *this;
}
/***************************************************************************/
/*!
\brief
Destructor for resource.
*/
/***************************************************************************/
SHRenderGraphResource::~SHRenderGraphResource(void) noexcept
{
}
/***************************************************************************/
/*!
\brief
Subpass non-default constructor. Simply initializes variables.
\param mapping
Mapping from a resource handle to an attachment reference referencing
the resource.
\param resources
A mapping from string to render graph resource.
*/
/***************************************************************************/
SHRenderGraphNode::SHSubpass::SHSubpass(std::unordered_map<uint64_t, uint32_t> const* mapping, std::unordered_map<std::string, Handle<SHRenderGraphResource>> const* resources) noexcept
: resourceAttachmentMapping{ mapping }
, ptrToResources{ resources }
{
}
/***************************************************************************/
/*!
\brief
Move constructor for subpass.
\param rhs
The subpass the move from.
*/
/***************************************************************************/
SHRenderGraphNode::SHSubpass::SHSubpass(SHSubpass&& rhs) noexcept
: colorReferences{ std::move(rhs.colorReferences) }
, depthReferences{ std::move(rhs.depthReferences) }
, inputReferences{ std::move(rhs.inputReferences) }
, resourceAttachmentMapping{ rhs.resourceAttachmentMapping }
, ptrToResources{ rhs.ptrToResources }
{
}
/***************************************************************************/
/*!
\brief
Move assignment operator for subpass.
\param rhs
subpass to move from.
*/
/***************************************************************************/
SHRenderGraphNode::SHSubpass& SHRenderGraphNode::SHSubpass::operator=(SHSubpass&& rhs) noexcept
{
if (this == &rhs)
return *this;
colorReferences = std::move(rhs.colorReferences);
depthReferences = std::move(rhs.depthReferences);
inputReferences = std::move(rhs.inputReferences);
resourceAttachmentMapping = rhs.resourceAttachmentMapping;
ptrToResources = rhs.ptrToResources;
return *this;
}
/***************************************************************************/
/*!
\brief
Adds a color output to a subpass. Takes in a string and finds the
attachment index to create the vk::SubpassReference.
\param resourceToReference
Resource name to find resource to attach.
*/
/***************************************************************************/
void SHRenderGraphNode::SHSubpass::AddColorOutput(std::string resourceToReference) noexcept
{
colorReferences.push_back({ resourceAttachmentMapping->at(ptrToResources->at(resourceToReference).GetId().Raw), vk::ImageLayout::eColorAttachmentOptimal });
}
/***************************************************************************/
/*!
\brief
Adds a depth output to a subpass. Takes in a string and finds the
attachment index to create the vk::SubpassReference.
\param resourceToReference
Resource name to find resource to attach.
\param attachmentDescriptionType
Depending on the type of the resource, initialize the image layout
appropriately.
*/
/***************************************************************************/
void SHRenderGraphNode::SHSubpass::AddDepthOutput(std::string resourceToReference, SH_ATT_DESC_TYPE attachmentDescriptionType) noexcept
{
vk::ImageLayout imageLayout;
switch (attachmentDescriptionType)
{
case SH_ATT_DESC_TYPE::DEPTH:
imageLayout = vk::ImageLayout::eDepthAttachmentOptimal;
break;
case SH_ATT_DESC_TYPE::STENCIL:
imageLayout = vk::ImageLayout::eStencilAttachmentOptimal;
break;
case SH_ATT_DESC_TYPE::DEPTH_STENCIL:
imageLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
break;
default:
//Invalid
return;
}
depthReferences.push_back({ resourceAttachmentMapping->at(ptrToResources->at(resourceToReference).GetId().Raw), imageLayout });
}
/***************************************************************************/
/*!
\brief
Adds a input output to a subpass. Takes in a string and finds the
attachment index to create the vk::SubpassReference.
\param resourceToReference
Resource name to find resource to attach.
*/
/***************************************************************************/
void SHRenderGraphNode::SHSubpass::AddInput(std::string resourceToReference) noexcept
{
inputReferences.push_back({ resourceAttachmentMapping->at(ptrToResources->at(resourceToReference).GetId().Raw), vk::ImageLayout::eShaderReadOnlyOptimal });
}
/***************************************************************************/
/*!
\brief
Creates a renderpass for the node. Uses subpass and attachment
descriptions already configured beforehand in the render graph.
*/
/***************************************************************************/
void SHRenderGraphNode::CreateRenderpass(void) noexcept
{
renderpass = logicalDeviceHdl->CreateRenderpass(attachmentDescriptions, spDescs, spDeps);
}
/***************************************************************************/
/*!
\brief
Creates a framebuffer from the images used in the renderpass.
*/
/***************************************************************************/
void SHRenderGraphNode::CreateFramebuffer(void) noexcept
{
for (uint32_t i = 0; i < framebuffers.size(); ++i)
{
std::vector<Handle<SHVkImageView>> imageViews(attResources.size());
uint32_t fbWidth = std::numeric_limits<uint32_t>::max();
uint32_t fbHeight = std::numeric_limits<uint32_t>::max();
for (uint32_t j = 0; j < attResources.size(); ++j)
{
uint32_t imageViewIndex = (attResources[j]->resourceType == SH_ATT_DESC_TYPE::COLOR_PRESENT) ? i : 0;
imageViews[j] = attResources[j]->imageViews[imageViewIndex];
// We want the minimum dimensions for the framebuffer because the image attachments referenced cannot have dimensions smaller than the framebuffer's
if (fbWidth > attResources[j]->width)
fbWidth = attResources[j]->width;
if (fbHeight > attResources[j]->height)
fbHeight = attResources[j]->height;
}
framebuffers[i] = logicalDeviceHdl->CreateFramebuffer(renderpass, imageViews, fbWidth, fbHeight);
}
}
/***************************************************************************/
/*!
\brief
Render Graph node constructor. Note that we do not create the renderpass
yet. This is because layouts of attachment descriptions facilitate image
transitions and we cannot know guarantee layouts until we've seen all
renderpasses and their subpasses in the graph.
\param swapchain
Swapchain required to query number of images as parameters for number
of framebuffers to create.
\param attachmentDescriptionTypes
\return
*/
/***************************************************************************/
SHRenderGraphNode::SHRenderGraphNode(ResourceManager& rm, Handle<SHVkLogicalDevice> const& logicalDevice, Handle<SHVkSwapchain> const& swapchain, std::vector<Handle<SHRenderGraphResource>> attRes, std::vector<Handle<SHRenderGraphNode>> predecessors, std::unordered_map<std::string, Handle<SHRenderGraphResource>> const* resources) noexcept
: logicalDeviceHdl{ logicalDevice }
, renderpass{}
, framebuffers{}
, prereqNodes{ std::move(predecessors) }
, attachmentDescriptions{}
, resourceAttachmentMapping{}
, attResources{ std::move(attRes) }
, subpasses{}
, executed{ false }
, configured{ false }
, resourceManager{ rm }
, ptrToResources{ resources }
{
attachmentDescriptions.resize(attResources.size());
bool containsSwapchainImage = false;
for (uint32_t i = 0; i < attResources.size(); ++i)
{
// As mentioned above we don't initialize much here because it's dependent on how other renderpasses are configured.
vk::AttachmentDescription& newDesc = attachmentDescriptions[i];
newDesc.samples = vk::SampleCountFlagBits::e1;
// We set this to clear first. If later we find out that some predecessor is writing to the same attachment,
// we set the pred's storeOp to eStore and "this" loadOp to eLoad.
newDesc.loadOp = vk::AttachmentLoadOp::eClear;
newDesc.storeOp = vk::AttachmentStoreOp::eDontCare;
newDesc.stencilLoadOp = vk::AttachmentLoadOp::eClear;
newDesc.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
newDesc.format = attResources[i]->resourceFormat;
if (attResources[i]->resourceType == SH_ATT_DESC_TYPE::COLOR_PRESENT)
containsSwapchainImage = true;
resourceAttachmentMapping.try_emplace(attResources[i].GetId().Raw, i);
}
if (!containsSwapchainImage)
framebuffers.resize(1);
else
framebuffers.resize(swapchain->GetNumImages());
// At this point, we could configure framebuffers if we had the renderpass object but we don't so their creation has to be
// deferred to when renderpasses are also created.
}
SHRenderGraphNode::SHRenderGraphNode(SHRenderGraphNode&& rhs) noexcept
: resourceManager{ rhs.resourceManager }
, logicalDeviceHdl{ rhs.logicalDeviceHdl }
, renderpass{ rhs.renderpass }
, framebuffers{ std::move(rhs.framebuffers) }
, prereqNodes{ std::move(rhs.prereqNodes) }
, attachmentDescriptions{ std::move(rhs.attachmentDescriptions) }
, attResources{ std::move(rhs.attResources) }
, subpasses{ std::move(rhs.subpasses) }
, resourceAttachmentMapping{ std::move(rhs.resourceAttachmentMapping) }
, subpassIndexing{ std::move(rhs.subpassIndexing) }
, configured{ rhs.configured }
, executed{ rhs.executed }
, ptrToResources{ rhs.ptrToResources }
{
rhs.renderpass = {};
}
SHRenderGraphNode& SHRenderGraphNode::operator=(SHRenderGraphNode&& rhs) noexcept
{
if (&rhs == this)
return *this;
resourceManager = rhs.resourceManager;
logicalDeviceHdl = rhs.logicalDeviceHdl;
renderpass = rhs.renderpass;
framebuffers = std::move(rhs.framebuffers);
prereqNodes = std::move(rhs.prereqNodes);
attachmentDescriptions = std::move(rhs.attachmentDescriptions);
attResources = std::move(rhs.attResources);
subpasses = std::move(rhs.subpasses);
resourceAttachmentMapping = std::move(rhs.resourceAttachmentMapping);
subpassIndexing = std::move(rhs.subpassIndexing);
ptrToResources = std::move(rhs.ptrToResources);
rhs.renderpass = {};
return *this;
}
/***************************************************************************/
/*!
\brief
Add subpasses to the renderpass and returns a reference to it.
\param subpassName
Name of the subpass.
\return
Handle to the new subpass.
*/
/***************************************************************************/
Handle<SHRenderGraphNode::SHSubpass> SHRenderGraphNode::AddSubpass(std::string subpassName) noexcept
{
// if subpass already exists, don't add.
if (subpassIndexing.contains(subpassName))
{
SHLOG_ERROR("Subpass already exists.");
return{};
}
// Add subpass to container and create mapping for it
subpasses.emplace_back(resourceManager.Create<SHSubpass>(&resourceAttachmentMapping, ptrToResources));
subpassIndexing.try_emplace(subpassName, subpasses.size() - 1);
return subpasses.at(subpassIndexing[subpassName]);
}
/***************************************************************************/
/*!
\brief
Attempts to add a resource that will be used globally in the render graph.
\param resourceName
Name of the resource.
\param type
The type of the resource.
\param w
Width of the resource. If set to uint32_t(-1), use swapchain image dims.
\param h
Height of the resource. If set to uint32_t(-1), use swapchain image dims.
\param format
The format that the resource is using. If w and h are set to uint32_t(-1),
use swapchain image format.
\param levels
Number of mipmap levels of the resource.
*/
/***************************************************************************/
void SHRenderGraph::AddResource(std::string resourceName, SH_ATT_DESC_TYPE type, uint32_t w /*= static_cast<uint32_t>(-1)*/, uint32_t h /*= static_cast<uint32_t>(-1)*/, vk::Format format/* = vk::Format::eB8G8R8A8Unorm*/, uint32_t levels /*= 1*/, vk::ImageCreateFlagBits createFlags /*= {}*/)
{
// If we set to
if (w == static_cast<uint32_t>(-1) && h == static_cast<uint32_t>(-1))
{
w = swapchainHdl->GetSwapchainImage(0)->GetWidth();
w = swapchainHdl->GetSwapchainImage(0)->GetHeight();
format = swapchainHdl->GetSurfaceFormatKHR().format;
}
graphResources.try_emplace(resourceName, resourceManager.Create<SHRenderGraphResource>(logicalDeviceHdl, swapchainHdl, resourceName, type, format, w, h, levels, createFlags));
}
/***************************************************************************/
/*!
\brief
Loops through all nodes and configures their attachment descriptions.
\return
*/
/***************************************************************************/
void SHRenderGraph::ConfigureAttachmentDescriptions(void) noexcept
{
// First we want to take all the attachment descriptions and initialize the
// finalLayout to whatever layout is specified in the last subpass that references the attachment.
for (auto& node : nodes)
{
// key is handle ID, value is pair (first is initial layout, second is final layout).
std::unordered_map<uint32_t, vk::ImageLayout> resourceAttLayouts;
if (node->subpasses.empty())
{
SHLOG_ERROR("Node does not contain a subpass. Cannot configure attachment descriptions as a result. ");
return;
}
for (auto& subpass : node->subpasses)
{
for (auto& color : subpass->colorReferences)
{
if (node->attResources[color.attachment]->resourceType == SH_ATT_DESC_TYPE::COLOR_PRESENT)
resourceAttLayouts[color.attachment] = vk::ImageLayout::ePresentSrcKHR;
else
resourceAttLayouts[color.attachment] = color.layout;
}
for (auto& depth : subpass->depthReferences)
resourceAttLayouts[depth.attachment] = depth.layout;
for (auto& input : subpass->inputReferences)
resourceAttLayouts[input.attachment] = input.layout;
}
for (uint32_t i = 0; i < node->attachmentDescriptions.size(); ++i)
{
auto& att = node->attachmentDescriptions[i];
att.initialLayout = vk::ImageLayout::eUndefined;
att.finalLayout = resourceAttLayouts[i];
}
}
// at this point all attachment descs will have their final layouts initialized as if they were standalone and did
// not have any predecessor nodes
// Now we use the predecessor attachment description and first subpass to configure the current node's attachment descriptions.
// This is so that we minimize image layout transitions.
for (auto& node : nodes)
{
for (auto& prereq : node->prereqNodes)
{
// Look through all resources used by this node
for (auto& predResource : prereq->attResources)
{
// if a predecessor's resource is used by this node, we want to copy the final layout from the pred to the initial of this node
if (uint64_t resourceID = predResource.GetId().Raw; node->resourceAttachmentMapping.contains(resourceID))
{
// Get the resource's attachment index in BOTH the predecessor and the current node
uint32_t prereqResourceAttIndex = prereq->resourceAttachmentMapping[resourceID];
uint32_t resourceAttIndex = node->resourceAttachmentMapping[resourceID];
// Use the resource attachment index to get the attachment description in the renderpass
auto& attDesc = node->attachmentDescriptions[resourceAttIndex];
auto& predAttDesc = prereq->attachmentDescriptions[prereqResourceAttIndex];
// We want the predecessor to preserve the attachment and we want this node to load the attachment
attDesc.loadOp = vk::AttachmentLoadOp::eLoad;
predAttDesc.storeOp = vk::AttachmentStoreOp::eStore;
// TODO: Stecil load and store
// When an image is done being used in a renderpass, the image layout will end up being the finalLayout
// value of the attachment description. We want this to carry over to the next renderpass; specifically
// to have the initialLayout of the attachment description in the next renderpass match the finalLayout in the previous.
attDesc.initialLayout = predAttDesc.finalLayout;
}
}
}
}
}
/***************************************************************************/
/*!
\brief
Configures the supasses; mainly the subpass descriptions and the
subpass dependencies involved between subpasses.
\return
*/
/***************************************************************************/
void SHRenderGraph::ConfigureSubpasses(void) noexcept
{
// For all nodes
for (auto& node : nodes)
{
// Create subpass description and dependencies based on number of subpasses
node->spDescs.resize(node->subpasses.size());
node->spDeps.resize(node->subpasses.size());
// Now we want to loop through all attachments in all subpasses in the node and query
// the resources being used. For each resource we want to query the type and record it
// in bit fields (1 bit for each subpass).
uint32_t colorRead = 0, colorWrite = 0, depthRead = 0, depthWrite = 0, inputDependencies = 0;
uint32_t i = 0;
// For all subpasses (see above description about bit field for this).
for (auto& subpass : node->subpasses)
{
// Configure subpass description
auto& desc = node->spDescs[i];
desc.pColorAttachments = subpass->colorReferences.data();
desc.colorAttachmentCount = static_cast<uint32_t>(subpass->colorReferences.size());
desc.pInputAttachments = subpass->inputReferences.data();
desc.inputAttachmentCount = static_cast<uint32_t>(subpass->inputReferences.size());
desc.pDepthStencilAttachment = subpass->depthReferences.data();
desc.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; // TODO: Just graphics for now. See if its possible to allow user defined params.
// Get reference to subpass description
auto& dep = node->spDeps[i];
// Configure subpass index for dependencies
dep.srcSubpass = (i == 0) ? VK_SUBPASS_EXTERNAL : i - 1;
dep.dstSubpass = i;
// First we want to see if the subpass has color, depth or input attachments and set bit field accordingly
if (subpass->colorReferences.size())
{
colorRead |= (1 << i);
colorWrite |= (1 << i);
}
// Same thing for depth
if (subpass->depthReferences.size())
{
depthRead |= (1 << i);
depthWrite |= (1 << i);
}
if (subpass->inputReferences.size())
inputDependencies |= (1 << i);
// Input attachments can be any type, so we need to check what type it is
for (auto& inputAtt : subpass->inputReferences)
{
auto resource = node->attResources[inputAtt.attachment];
if (resource->resourceType == SH_ATT_DESC_TYPE::COLOR)
colorRead |= (1 << i);
else if (resource->resourceType == SH_ATT_DESC_TYPE::DEPTH_STENCIL)
depthRead |= (1 << i);
}
++i;
}
// Loop through all subpasses again but this time we use the bit field to initialize
// the dependencies.
for (i = 0; i < node->subpasses.size(); ++i)
{
vk::PipelineStageFlags srcStage;
vk::PipelineStageFlags dstStage;
vk::AccessFlags srcAccess;
vk::AccessFlags dstAccess;
auto& dep = node->spDeps[i];
if (colorRead & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
srcAccess |= vk::AccessFlagBits::eColorAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eColorAttachmentRead;
}
if (colorWrite & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
srcAccess |= vk::AccessFlagBits::eColorAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead;
}
if (depthRead & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
dstStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
srcAccess |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eDepthStencilAttachmentRead;
}
if (depthWrite & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
dstStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
srcAccess |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
}
if (inputDependencies & (1 << i))
{
dstStage |= vk::PipelineStageFlagBits::eFragmentShader;
dstAccess |= vk::AccessFlagBits::eInputAttachmentRead;
}
//// If subpass of first renderpass, stage flag should be bottom of pipe
//if (&node == &nodes.front() && i == 0)
// srcStage = vk::PipelineStageFlagBits::eBottomOfPipe;
//// If subpass of last renderpass, stage flag should be bottom of pipe
//if (&node == &nodes.back() && i == node->subpasses.size() - 1)
// dstStage = vk::PipelineStageFlagBits::eTopOfPipe;
dep.srcStageMask = srcStage;
dep.dstStageMask = dstStage;
dep.srcAccessMask = srcAccess;
dep.dstAccessMask = dstAccess;
dep.srcStageMask = srcStage;
}
}
}
/***************************************************************************/
/*!
\brief
Simply loops through all nodes and create renderpasses.
*/
/***************************************************************************/
void SHRenderGraph::ConfigureRenderpasses(void) noexcept
{
for (auto& node : nodes)
{
node->CreateRenderpass();
}
}
/***************************************************************************/
/*!
\brief
Simply loops through all nodes and create framebuffers.
*/
/***************************************************************************/
void SHRenderGraph::ConfigureFramebuffers(void) noexcept
{
for (auto& node : nodes)
{
node->CreateFramebuffer();
}
}
/***************************************************************************/
/*!
\brief
Init function. Doesn't do much except initialize device and swapchain
handle. Graph should start out empty.
\param swapchain
Used for querying number of images later.
\param logicalDevice
Used for initializing logicalDeviceHdl.
*/
/***************************************************************************/
void SHRenderGraph::Init(Handle<SHVkLogicalDevice> const& logicalDevice, Handle<SHVkSwapchain> const& swapchain) noexcept
{
logicalDeviceHdl = logicalDevice;
swapchainHdl = swapchain;
}
/***************************************************************************/
/*!
\brief
Default ctor, doesn't do much.
\return
*/
/***************************************************************************/
SHRenderGraph::SHRenderGraph(void) noexcept
: logicalDeviceHdl{ }
, swapchainHdl{ }
, nodes{}
, graphResources{}
, resourceManager{}
{
}
/***************************************************************************/
/*!
\brief
Adds a renderpass to the graph.
\param nodeName
The name of the renderpass
\param attachmentDescriptions
The attachment description types for the node. See SHRenderGraphNode
ctor for more details.
\return
Reference to the node.
*/
/***************************************************************************/
Handle<SHRenderGraphNode> SHRenderGraph::AddNode(std::string nodeName, std::initializer_list<std::string> resourceNames, std::initializer_list<std::string> predecessorNodes) noexcept
{
if (nodeIndexing.contains(nodeName))
{
SHLOG_ERROR("Node already exists, cannot add node. ");
return {};
}
std::vector<Handle<SHRenderGraphResource>> resources;
for (auto const& name : resourceNames)
{
// If the resource that the new node is requesting for exists, allow the graph to reference it
if (graphResources.contains(name))
resources.push_back(graphResources.at(name));
else
{
SHLOG_ERROR("Resource doesn't exist in graph yet. Cannot create new node.");
return{};
}
}
// Predecessor nodes for this node
std::vector<Handle<SHRenderGraphNode>> predecessors;
for (auto& pred : predecessorNodes)
{
// If predecessor exists, allow new node to reference it
if (nodeIndexing.contains(pred))
predecessors.push_back(nodes[nodeIndexing[pred]]);
else
{
SHLOG_ERROR("Predecessor doesn't exist in graph yet. Cannot create new node.");
return {};
}
}
nodes.emplace_back(resourceManager.Create<SHRenderGraphNode>(resourceManager, logicalDeviceHdl, swapchainHdl, std::move(resources), std::move(predecessors), &graphResources));
nodeIndexing.emplace(nodeName, nodes.size() - 1);
return nodes.at(nodeIndexing[nodeName]);
}
/***************************************************************************/
/*!
\brief
Generates and configures all renderpass nodes.
Does a few things.
1. Using predecessors and subpasses, configure attachment descriptions.
2. Using predecessors, configure subpass description and dependencies.
3. Create Renderpasses in nodes.
4. Create framebuffers in nodes using renderpasses.
*/
/***************************************************************************/
void SHRenderGraph::Generate(void) noexcept
{
ConfigureAttachmentDescriptions();
ConfigureSubpasses();
ConfigureRenderpasses();
ConfigureFramebuffers();
}
}

View File

@ -0,0 +1,233 @@
#ifndef SH_RENDER_GRAPH_H
#define SH_RENDER_GRAPH_H
#include "Graphics/Renderpass/SHVkRenderpass.h"
#include "Resource/ResourceLibrary.h"
#include <string>
#include <map>
namespace SHADE
{
class SHVkLogicalDevice;
class SHVkFramebuffer;
class SHVkSwapchain;
class SHVkImage;
class SHVkImageView;
class SHVkFramebuffer;
// Used for attachment description creation for renderpass node
enum class SH_ATT_DESC_TYPE
{
COLOR,
COLOR_PRESENT,
DEPTH,
STENCIL,
DEPTH_STENCIL,
};
class SHRenderGraphResource
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
//! Name of the resource
std::string resourceName;
//! Used for initializing image layouts
SH_ATT_DESC_TYPE resourceType;
//! The resource itself (this is a vector because if the resource happens
//! to be a swapchain image, then we need however many frames in flight).
//! However when it's not a swapchain image, we want this container to be size 1
//! because writing to these images will not interfere with images in the previous
//! frame, unlike the swapchain image.
std::vector<Handle<SHVkImage>> images;
//! Views to resources (vector because same rationale as images. see above).
std::vector<Handle<SHVkImageView>> imageViews;
//! Image format
vk::Format resourceFormat;
//! width of the resource
uint32_t width;
//! Height of the resource
uint32_t height;
//! Number of mipmap levels
uint8_t mipLevels;
public:
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHRenderGraphResource(Handle<SHVkLogicalDevice> const& logicalDevice, Handle<SHVkSwapchain> const& swapchain, std::string const& name, SH_ATT_DESC_TYPE type, vk::Format format, uint32_t w, uint32_t h, uint8_t levels, vk::ImageCreateFlagBits createFlags) noexcept;
SHRenderGraphResource(SHRenderGraphResource&& rhs) noexcept;
SHRenderGraphResource& operator=(SHRenderGraphResource&& rhs) noexcept;
~SHRenderGraphResource (void) noexcept;
friend class SHRenderGraphNode;
friend class SHRenderGraph;
};
class SHRenderGraphNode
{
public:
class SHSubpass
{
public:
SHSubpass(std::unordered_map<uint64_t, uint32_t> const* mapping, std::unordered_map<std::string, Handle<SHRenderGraphResource>> const* ptrToResources) noexcept;
SHSubpass(SHSubpass&& rhs) noexcept;
SHSubpass& operator=(SHSubpass&& rhs) noexcept;
void AddColorOutput (std::string resourceToReference) noexcept;
void AddDepthOutput (std::string resourceToReference, SH_ATT_DESC_TYPE attachmentDescriptionType = SH_ATT_DESC_TYPE::DEPTH_STENCIL) noexcept;
void AddInput (std::string resourceToReference) noexcept;
private:
/*---------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*---------------------------------------------------------------------*/
//! Color attachments
std::vector<vk::AttachmentReference> colorReferences;
//! Depth attachments
std::vector<vk::AttachmentReference> depthReferences;
//! Input attachments
std::vector<vk::AttachmentReference> inputReferences;
//! For getting attachment reference indices using handles
std::unordered_map<uint64_t, uint32_t> const* resourceAttachmentMapping;
//! Pointer to resources in the render graph (for getting handle IDs)
std::unordered_map<std::string, Handle<SHRenderGraphResource>> const* ptrToResources;
friend class SHRenderGraphNode;
friend class SHRenderGraph;
};
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
ResourceManager& resourceManager;
//! For Vulkan object creation
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! Each node will have a renderpass and each renderpass will have its own subpasses.
//! These subpasses will execute sequentially.
Handle<SHVkRenderpass> renderpass;
//! Framebuffers used in this renderpass. If renderpass contains usage of a swapchain image
//! used for presenting, then we cannot use just 1 framebuffer, we need to have 1 for however many frames in flight.
std::vector<Handle<SHVkFramebuffer>> framebuffers;
//! Nodes that must finish execution before this node is executed will be in this container
std::vector<Handle<SHRenderGraphNode>> prereqNodes;
//! Container of Attachment descriptions
std::vector<vk::AttachmentDescription> attachmentDescriptions;
//! Resources used in this renderpass
std::vector<Handle<SHRenderGraphResource>> attResources;
//! Vector of subpasses
std::vector<Handle<SHSubpass>> subpasses;
//! Descriptions to pass to renderpass for renderpass creation. We want to keep this here because
std::vector<vk::SubpassDescription> spDescs;
//! Subpass dependencies for renderpass creation
std::vector<vk::SubpassDependency> spDeps;
//! For indexing resources fast
std::unordered_map<uint64_t, uint32_t> resourceAttachmentMapping;
//! For indexing subpasses
std::map<std::string, uint32_t> subpassIndexing;
//! Pointer to resources in the render graph (for getting handle IDs)
std::unordered_map<std::string, Handle<SHRenderGraphResource>> const* ptrToResources;
//! Whether or not the node has finished execution
bool executed;
//! Whether or not the node has been configured already or not
bool configured;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void CreateRenderpass (void) noexcept;
void CreateFramebuffer(void) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHRenderGraphNode (ResourceManager& rm, Handle<SHVkLogicalDevice> const& logicalDevice, Handle<SHVkSwapchain> const& swapchain, std::vector<Handle<SHRenderGraphResource>> attRes, std::vector<Handle<SHRenderGraphNode>> predecessors, std::unordered_map<std::string, Handle<SHRenderGraphResource>> const* resources) noexcept;
SHRenderGraphNode(SHRenderGraphNode&& rhs) noexcept;
SHRenderGraphNode& operator= (SHRenderGraphNode&& rhs) noexcept;
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
Handle<SHSubpass> AddSubpass (std::string subpassName) noexcept;
friend class SHRenderGraph;
};
class SHRenderGraph
{
private:
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void ConfigureAttachmentDescriptions (void) noexcept;
void ConfigureSubpasses (void) noexcept;
void ConfigureRenderpasses (void) noexcept;
void ConfigureFramebuffers (void) noexcept;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER VARIABLES */
/*-----------------------------------------------------------------------*/
Handle<SHVkLogicalDevice> logicalDeviceHdl;
//! swapchain used for querying image count
Handle<SHVkSwapchain> swapchainHdl;
//! For indexing render graph node container
std::map<std::string, uint32_t> nodeIndexing;
//! Render graph nodes
std::vector<Handle<SHRenderGraphNode>> nodes;
//! Resources that exist for the entire render graph
std::unordered_map<std::string, Handle<SHRenderGraphResource>> graphResources;
//! Resource library for graph handles
ResourceManager resourceManager;
public:
/*-----------------------------------------------------------------------*/
/* CTORS AND DTORS */
/*-----------------------------------------------------------------------*/
SHRenderGraph (void) noexcept;
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void Init (Handle<SHVkLogicalDevice> const& logicalDevice, Handle<SHVkSwapchain> const& swapchain) noexcept;
void AddResource(std::string resourceName, SH_ATT_DESC_TYPE type, uint32_t w = static_cast<uint32_t>(-1), uint32_t h = static_cast<uint32_t>(-1), vk::Format format = vk::Format::eB8G8R8A8Unorm, uint32_t levels = 1, vk::ImageCreateFlagBits createFlags = {});
Handle<SHRenderGraphNode> AddNode (std::string nodeName, std::initializer_list<std::string> resourceNames, std::initializer_list<std::string> predecessorNodes) noexcept;
void Generate (void) noexcept;
};
}
#endif

View File

@ -0,0 +1,48 @@
#include "SHPch.h"
#include "SHVkAttachDescGen.h"
#include "Graphics/Swapchain/SHVkSwapchain.h"
namespace SHADE
{
vk::AttachmentDescription SHVkAttachDescGen::GenGenericColorPresent(Handle<SHVkSwapchain> const& swapchain) noexcept
{
return vk::AttachmentDescription
{
.format = swapchain->GetSurfaceFormatKHR().format,
.samples = vk::SampleCountFlagBits::e1, // > 1 for multisampling
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
// Ditto but with stencil
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
// Layout of image when render pass begins and ends
.initialLayout = vk::ImageLayout::eUndefined,
.finalLayout = vk::ImageLayout::ePresentSrcKHR
};
}
vk::AttachmentDescription SHVkAttachDescGen::GenDepthStencil(Handle<SHVkSwapchain> const& swapchain) noexcept
{
return vk::AttachmentDescription
{
.format = swapchain->GetDepthFormat(),
.samples = vk::SampleCountFlagBits::e1, // > 1 for multisampling
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
// Ditto but with stencil
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
// Layout of image when render pass begins and ends
.initialLayout = vk::ImageLayout::eUndefined,
.finalLayout = vk::ImageLayout::ePresentSrcKHR
};
}
}

View File

@ -0,0 +1,20 @@
#ifndef SH_VK_ATTACHMENT_DESC_GEN_H
#define SH_VK_ATTACHMENT_DESC_GEN_H
#include "Graphics/SHVulkanIncludes.h"
#include "Resource/Handle.h"
namespace SHADE
{
class SHVkSwapchain;
class SHVkAttachDescGen
{
public:
static vk::AttachmentDescription GenGenericColorPresent (Handle<SHVkSwapchain> const& swapchain) noexcept;
static vk::AttachmentDescription GenDepthStencil (Handle<SHVkSwapchain> const& swapchain) noexcept;
};
}
#endif

View File

@ -0,0 +1,44 @@
#ifndef SH_VK_ATTACHMENT_H
#define SH_VK_ATTACHMENT_H
namespace SHADE
{
//struct SHVkAttachmentReference
//{
// //! Identifies the attachment index of in the array of attachments in renderpass
// uint32_t attachment;
// //! What layout the attachment uses during the subpass
//vk::ImageLayout imageLayout;
//};
//struct SHVkAttachmentDescription
//{
// //! Specifies that the attachment aliases the same device memory as other attachments
// bool mayAlias;
// //! specifies the format of the image that will be used for the attachment
// vk::Format format;
// //! Number of samples of the image
// uint8_t samples;
// //! How to treat contents of an attachment at the beginning of a subpass where it is FIRST used
// vk::AttachmentLoadOp loadOp;
// //! How to treat contents of an attachment at the beginning of a subpass where it is FIRST used
// vk::AttachmentStoreOp storeOp;
// // TODO: We don't really want to mess with stencils yet, so this is left out
// // stecilLoadOp
// // stecilStoreOp
// // layout that the attachment will be in when render pass instance begins
// vk::ImageLayout initialLayout;
// // layout that the attachment will be in when render pass instance ends
// vk::ImageLayout finalLayout;
//};
}
#endif

Some files were not shown because too many files have changed in this diff Show More