SP3-16 Math #8
|
@ -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'">
|
||||
|
@ -94,7 +94,7 @@
|
|||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EntryPointSymbol>WinMainCRTStartup</EntryPointSymbol>
|
||||
<EntryPointSymbol>wWinMainCRTStartup</EntryPointSymbol>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,9 +13,16 @@
|
|||
namespace Sandbox
|
||||
{
|
||||
bool paused = false;
|
||||
void SBApplication::Initialize(void)
|
||||
void SBApplication::Initialize
|
||||
(
|
||||
_In_ HINSTANCE hInstance,
|
||||
_In_opt_ HINSTANCE hPrevInstance,
|
||||
_In_ LPWSTR lpCmdLine,
|
||||
_In_ INT nCmdShow
|
||||
)
|
||||
{
|
||||
|
||||
window.Create(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
|
||||
|
||||
#ifdef SHEDITOR
|
||||
#else
|
||||
|
@ -26,7 +33,7 @@ namespace Sandbox
|
|||
void SBApplication::Update(void)
|
||||
{
|
||||
//TODO: Change true to window is open
|
||||
while(true)
|
||||
while (!window.WindowShouldClose())
|
||||
{
|
||||
#ifdef SHEDITOR
|
||||
#else
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef SB_APPLICATION_H
|
||||
#define SB_APPLICATION_H
|
||||
|
||||
#include <Graphics/Windowing/SHWindow.h>
|
||||
//using namespace SHADE;
|
||||
|
||||
namespace Sandbox
|
||||
|
@ -8,10 +8,14 @@ namespace Sandbox
|
|||
class SBApplication
|
||||
{
|
||||
private:
|
||||
SHADE::SHWindow window;
|
||||
//SHAppConfig config;
|
||||
public:
|
||||
SBApplication() = default;
|
||||
void Initialize(void);
|
||||
void Initialize(_In_ HINSTANCE /*hInstance*/,
|
||||
_In_opt_ HINSTANCE /*hPrevInstance*/,
|
||||
_In_ LPWSTR /*lpCmdLine*/,
|
||||
_In_ INT /*nCmdShow*/);
|
||||
void Update(void);
|
||||
void Exit(void);
|
||||
private:
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
#define DBG_NEW new
|
||||
#endif
|
||||
|
||||
INT WINAPI WinMain
|
||||
INT WINAPI wWinMain
|
||||
(
|
||||
_In_ HINSTANCE /*hInstance*/,
|
||||
_In_opt_ HINSTANCE /*hPrevInstance*/,
|
||||
_In_ PSTR /*lpCmdLine*/,
|
||||
_In_ INT /*nCmdShow*/
|
||||
_In_ HINSTANCE hInstance,
|
||||
_In_opt_ HINSTANCE hPrevInstance,
|
||||
_In_ LPWSTR lpCmdLine,
|
||||
_In_ INT nCmdShow
|
||||
)
|
||||
{
|
||||
const SHADE::SHLogger::Config LOGGER_CONFIG{ .directoryPath = "./logs/" };
|
||||
|
@ -36,7 +36,7 @@ INT WINAPI WinMain
|
|||
|
||||
SHLOG_INFO("sup")
|
||||
|
||||
SHADE::SHEngine::Run<Sandbox::SBApplication>();
|
||||
SHADE::SHEngine::Run<Sandbox::SBApplication>(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
||||
}
|
||||
catch(...)
|
||||
|
|
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -116,6 +116,61 @@
|
|||
<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\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\SHMathHelpers.hpp" />
|
||||
|
@ -125,6 +180,9 @@
|
|||
<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" />
|
||||
|
@ -142,6 +200,53 @@
|
|||
<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\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\Resource\ResourceLibrary.cpp" />
|
||||
<ClCompile Include="src\Math\SHMathHelpers.cpp" />
|
||||
<ClCompile Include="src\Math\SHMatrix.cpp" />
|
||||
<ClCompile Include="src\Math\SHQuaternion.cpp" />
|
||||
|
|
|
@ -19,9 +19,87 @@
|
|||
<Filter Include="Engine\ECS_Base\System">
|
||||
<UniqueIdentifier>{B3E3FAFD-9FDD-2350-884A-BA6074E389BC}</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="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>
|
||||
|
@ -72,9 +150,183 @@
|
|||
<ClInclude Include="src\Engine\SHEngine.h">
|
||||
<Filter>Engine</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\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\Scene\SHScene.h">
|
||||
<Filter>Scene</Filter>
|
||||
|
@ -124,6 +376,147 @@
|
|||
<ClCompile Include="src\Engine\SHEngine.cpp">
|
||||
<Filter>Engine</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\Resource\ResourceLibrary.cpp">
|
||||
<Filter>Resource</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\SHpch.cpp" />
|
||||
<ClCompile Include="src\Scene\SHSceneManager.cpp">
|
||||
<Filter>Scene</Filter>
|
||||
|
|
|
@ -34,7 +34,8 @@ project "SHADE_Engine"
|
|||
"%{IncludeDir.ktx}/include",
|
||||
"%{IncludeDir.RTTR}/include",
|
||||
"%{IncludeDir.reactphysics3d}/include",
|
||||
"%{IncludeDir.VULKAN}/include"
|
||||
"%{IncludeDir.VULKAN}/include",
|
||||
"%{IncludeDir.VULKAN}/Source/SPIRV-Reflect"
|
||||
}
|
||||
|
||||
libdirs
|
||||
|
@ -63,7 +64,8 @@ project "SHADE_Engine"
|
|||
{
|
||||
"_LIB",
|
||||
"_GLFW_INCLUDE_NONE",
|
||||
"MSDFGEN_USE_CPP11"
|
||||
"MSDFGEN_USE_CPP11",
|
||||
"NOMINMAX"
|
||||
}
|
||||
|
||||
flags
|
||||
|
|
|
@ -20,16 +20,16 @@ namespace SHADE
|
|||
{
|
||||
|
||||
public:
|
||||
template <class Application>
|
||||
static void Run(void)
|
||||
template <class Application, typename ... Args>
|
||||
static void Run(Args&&...args)
|
||||
{
|
||||
static_assert(SHIsDetected<GetInit_t, Application>::value, "Init Not Detected");
|
||||
//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();
|
||||
application.Initialize(std::forward<Args>(args)...);
|
||||
application.Update();
|
||||
application.Exit();
|
||||
};
|
||||
|
|
|
@ -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, ©Region);
|
||||
}
|
||||
}
|
||||
|
||||
vk::Buffer SHVkBuffer::GetVkBuffer(void) const noexcept
|
||||
{
|
||||
return vkBuffer;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Maps the mappedPtr member pointer.
|
||||
|
||||
\param vmaAllocator
|
||||
Required VmaAllocator for mapping.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHVkBuffer::Map(void) noexcept
|
||||
{
|
||||
if (!boundToCoherent)
|
||||
vmaMapMemory(vmaAllocator, alloc, &mappedPtr);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Unmaps the mappedPtr member pointer. Does not work if pointer is mapped
|
||||
persistently (i.e. buffer is bound to coherent memory).
|
||||
|
||||
\param vmaAllocator
|
||||
Required VmaAllocator for unmapping.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHVkBuffer::Unmap(void) noexcept
|
||||
{
|
||||
if (!boundToCoherent)
|
||||
{
|
||||
vmaUnmapMemory(vmaAllocator, alloc);
|
||||
mappedPtr = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Writes to the mapped pointer.
|
||||
|
||||
\param data
|
||||
Pointer to source data.
|
||||
|
||||
\param sizeToWrite
|
||||
Amount to write to the buffer.
|
||||
|
||||
\param srcOffset
|
||||
byte offset into the source data.
|
||||
|
||||
\param dstOffset
|
||||
byte offset into the destination data.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHVkBuffer::WriteToMemory(void* data, uint32_t sizeToWrite, uint32_t srcOffset, uint32_t dstOffset) noexcept
|
||||
{
|
||||
if (mappedPtr)
|
||||
std::memcpy(static_cast<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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHDescriptorPoolManager.h"
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
class SHDescriptorPoolManager
|
||||
{
|
||||
};
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHDescriptorPoolStorage.h"
|
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
class SHDescriptorPoolStorage
|
||||
{
|
||||
};
|
||||
|
|
@ -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 {};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
//}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHRenderTarget.h"
|
|
@ -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 */
|
||||
/*-----------------------------------------------------------------------------*/
|
||||
};
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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{ }
|
||||
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef SH_PIPELINE_TYPE_H
|
||||
#define SH_PIPELINE_TYPE_H
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
enum class SH_PIPELINE_TYPE
|
||||
{
|
||||
GRAPHICS,
|
||||
COMPUTE,
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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. ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,240 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVkRenderpass.h"
|
||||
#include "Graphics/Devices/SHVkLogicalDevice.h"
|
||||
#include "Graphics/SHVkUtil.h"
|
||||
#include "Graphics/Framebuffer/SHVkFramebuffer.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Renderpass constructor.
|
||||
|
||||
\param vkDescriptions
|
||||
Descriptions of the attachments. You can create your own description
|
||||
using SHVkAttachmentGen or create your own and pass them here.
|
||||
|
||||
\param subpass
|
||||
Subpasses are all specified through the constructor. #NoteToSelf: This
|
||||
implementation of passing in an entire subpass (which is essentially all
|
||||
the variables that vulkan needs) seems rather poorly abstracted. The goal
|
||||
was to omit a few variables that could be generated by allowing users
|
||||
to specify some parameters. However due to a lack of experience with
|
||||
Vulkan, this is the way to go for now.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
SHVkRenderpass::SHVkRenderpass(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, std::span<vk::AttachmentDescription> const vkDescriptions, std::vector<SHVkSubpassParams> const& subpasses) noexcept
|
||||
: logicalDeviceHdl {inLogicalDeviceHdl}
|
||||
, clearColors{}
|
||||
{
|
||||
// TODO: temporary only
|
||||
clearColors[0].color = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
|
||||
clearColors[1].depthStencil = vk::ClearDepthStencilValue(1.0f, 0);
|
||||
|
||||
vk::RenderPassCreateInfo renderPassCreateInfo{};
|
||||
std::vector<vk::SubpassDependency> subpassDeps;
|
||||
|
||||
// For validating the depth ref
|
||||
auto isValidDepthRef = [&](vk::AttachmentReference const& depthRef) -> bool
|
||||
{
|
||||
return !(depthRef.attachment == static_cast<uint32_t> (-1) && depthRef.layout == vk::ImageLayout::eUndefined);
|
||||
};
|
||||
|
||||
uint32_t subpassIndex = 0;
|
||||
if (!subpasses.empty())
|
||||
{
|
||||
for (auto& subpass : subpasses)
|
||||
{
|
||||
subpassDescriptions.emplace_back();
|
||||
auto& spDesc = subpassDescriptions.back();
|
||||
|
||||
spDesc.pColorAttachments = subpass.colorRefs.data();
|
||||
spDesc.colorAttachmentCount = static_cast<uint32_t>(subpass.colorRefs.size());
|
||||
spDesc.pInputAttachments = subpass.inputRefs.data();
|
||||
spDesc.inputAttachmentCount = static_cast<uint32_t>(subpass.inputRefs.size());
|
||||
spDesc.pipelineBindPoint = subpass.pipelineBindPoint;
|
||||
|
||||
// We want to leave the depth stencil ref if its layout is undefined and attachment index is invalid
|
||||
if (isValidDepthRef(subpass.depthStencilRefs))
|
||||
spDesc.pDepthStencilAttachment = &subpass.depthStencilRefs;
|
||||
|
||||
vk::SubpassDependency dependency
|
||||
{
|
||||
// we want previous subpass dependent on this subpass. If first subpass, no dependency
|
||||
.srcSubpass = subpassIndex == 0 ? VK_SUBPASS_EXTERNAL : subpass.dependentSubpassIndex,
|
||||
.dstSubpass = subpassIndex == 0 ? 0 : subpassIndex + 1,
|
||||
|
||||
// Only when these stages are done will commands in dst subpass be executed
|
||||
.srcStageMask = subpass.srcStageMask,
|
||||
|
||||
// commands here will only run when src subpass has fully completed
|
||||
.dstStageMask = subpass.dstStageMask,
|
||||
|
||||
// What is the access scope of the previous subpass
|
||||
.srcAccessMask = subpass.srcAccessMask,
|
||||
|
||||
// What is the access scope of the current subpass
|
||||
.dstAccessMask = subpass.dstAccessMask,
|
||||
};
|
||||
|
||||
// Push a new dependency
|
||||
subpassDeps.push_back(dependency);
|
||||
|
||||
++subpassIndex;
|
||||
}
|
||||
|
||||
// Renderpass create info for render pass creation
|
||||
renderPassCreateInfo.attachmentCount = static_cast<uint32_t>(vkDescriptions.size());
|
||||
renderPassCreateInfo.pAttachments = vkDescriptions.data();
|
||||
renderPassCreateInfo.subpassCount = static_cast<uint32_t>(subpassDescriptions.size());
|
||||
renderPassCreateInfo.pSubpasses = subpassDescriptions.data();
|
||||
renderPassCreateInfo.dependencyCount = static_cast<uint32_t>(subpassDeps.size());
|
||||
renderPassCreateInfo.pDependencies = subpassDeps.data();
|
||||
|
||||
}
|
||||
// No subpasses passed in, create a default one.
|
||||
else
|
||||
{
|
||||
// Create color attachment references for every color attachment passed in
|
||||
std::vector<vk::AttachmentReference> colorRefs;
|
||||
|
||||
// If have depth attachment, account for that one.
|
||||
vk::AttachmentReference depthStencilRef
|
||||
{
|
||||
.attachment = VK_ATTACHMENT_UNUSED,
|
||||
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal
|
||||
};
|
||||
|
||||
// Loop through every reference
|
||||
for (uint32_t i = 0; i < vkDescriptions.size(); ++i)
|
||||
{
|
||||
// If no depth stencil attachment has been found yet
|
||||
if (depthStencilRef.attachment == VK_ATTACHMENT_UNUSED)
|
||||
{
|
||||
// If the description of the attachment has a format that is depth stencil related
|
||||
if (SHVkUtil::IsDepthStencilAttachment(vkDescriptions[i].format))
|
||||
{
|
||||
// Save the index of the depth stencil attachment description
|
||||
depthStencilRef.attachment = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If its not depth stencil, it is a color attachment. Push it in the container.
|
||||
colorRefs.push_back(vk::AttachmentReference
|
||||
{
|
||||
.attachment = i,
|
||||
.layout = vk::ImageLayout::eGeneral,
|
||||
});
|
||||
}
|
||||
|
||||
// Default subpass has references to all attachments in the renderpass (or more technically the framebuffer)
|
||||
vk::SubpassDescription defaultSubpass;
|
||||
defaultSubpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
|
||||
defaultSubpass.colorAttachmentCount = static_cast<uint32_t>(colorRefs.size());
|
||||
defaultSubpass.pColorAttachments = colorRefs.data();
|
||||
defaultSubpass.pDepthStencilAttachment = (depthStencilRef.attachment == VK_ATTACHMENT_UNUSED) ? &depthStencilRef : nullptr;
|
||||
|
||||
// Prepare the renderpass create info
|
||||
renderPassCreateInfo.attachmentCount = static_cast<uint32_t>(vkDescriptions.size());
|
||||
renderPassCreateInfo.pAttachments = vkDescriptions.data();
|
||||
renderPassCreateInfo.subpassCount = 1;
|
||||
renderPassCreateInfo.pSubpasses = &defaultSubpass;
|
||||
|
||||
// No dependencies since only 1 subpass
|
||||
renderPassCreateInfo.dependencyCount = 0;
|
||||
renderPassCreateInfo.pDependencies = nullptr;
|
||||
}
|
||||
|
||||
|
||||
// Create the renderpass
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createRenderPass(&renderPassCreateInfo, nullptr, &vkRenderpass); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to create Renderpass. ");
|
||||
}
|
||||
else
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkSuccess("Successfully created Renderpass. ");
|
||||
}
|
||||
}
|
||||
|
||||
SHVkRenderpass::SHVkRenderpass(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, std::span<vk::AttachmentDescription> const vkDescriptions, std::span<vk::SubpassDescription> const spDescs, std::span<vk::SubpassDependency> const spDeps) noexcept
|
||||
: logicalDeviceHdl{ inLogicalDeviceHdl }
|
||||
, clearColors{}
|
||||
{
|
||||
// TODO: temporary only
|
||||
clearColors[0].color = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
|
||||
clearColors[1].depthStencil = vk::ClearDepthStencilValue(1.0f, 0);
|
||||
|
||||
subpassDescriptions.resize (spDescs.size());
|
||||
for (uint32_t i = 0; i < subpassDescriptions.size(); ++i)
|
||||
{
|
||||
subpassDescriptions[i] = spDescs[i];
|
||||
}
|
||||
|
||||
vk::RenderPassCreateInfo renderPassCreateInfo{};
|
||||
|
||||
renderPassCreateInfo.attachmentCount = static_cast<uint32_t>(vkDescriptions.size());
|
||||
renderPassCreateInfo.pAttachments = vkDescriptions.data();
|
||||
renderPassCreateInfo.subpassCount = static_cast<uint32_t>(subpassDescriptions.size());
|
||||
renderPassCreateInfo.pSubpasses = subpassDescriptions.data();
|
||||
renderPassCreateInfo.dependencyCount = static_cast<uint32_t>(spDeps.size());
|
||||
renderPassCreateInfo.pDependencies = spDeps.data();
|
||||
|
||||
// Create the renderpass
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createRenderPass(&renderPassCreateInfo, nullptr, &vkRenderpass); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to create Renderpass. ");
|
||||
}
|
||||
else
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkSuccess("Successfully created Renderpass. ");
|
||||
}
|
||||
}
|
||||
|
||||
SHVkRenderpass::SHVkRenderpass(SHVkRenderpass&& rhs) noexcept
|
||||
: vkRenderpass {rhs.vkRenderpass}
|
||||
, logicalDeviceHdl {rhs.logicalDeviceHdl}
|
||||
, subpassDescriptions {std::move (rhs.subpassDescriptions)}
|
||||
, clearColors {std::move (rhs.clearColors)}
|
||||
{
|
||||
rhs.vkRenderpass = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
|
||||
SHVkRenderpass& SHVkRenderpass::operator=(SHVkRenderpass&& rhs) noexcept
|
||||
{
|
||||
if (&rhs == this)
|
||||
return *this;
|
||||
|
||||
vkRenderpass = rhs.vkRenderpass;
|
||||
logicalDeviceHdl = rhs.logicalDeviceHdl;
|
||||
subpassDescriptions = std::move(rhs.subpassDescriptions);
|
||||
clearColors = std::move(rhs.clearColors);
|
||||
|
||||
rhs.vkRenderpass = VK_NULL_HANDLE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
SHVkRenderpass::~SHVkRenderpass(void) noexcept
|
||||
{
|
||||
logicalDeviceHdl->GetVkLogicalDevice().destroyRenderPass(vkRenderpass, nullptr);
|
||||
}
|
||||
|
||||
|
||||
vk::RenderPass SHVkRenderpass::GetVkRenderpass(void) const noexcept
|
||||
{
|
||||
return vkRenderpass;
|
||||
}
|
||||
|
||||
std::array<vk::ClearValue, SHVkRenderpass::NUM_CLEAR_COLORS> const& SHVkRenderpass::GetClearColors(void) const noexcept
|
||||
{
|
||||
return clearColors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
#ifndef SH_VK_RENDERPASS_H
|
||||
#define SH_VK_RENDERPASS_H
|
||||
|
||||
#include "SHVkAttachDescGen.h"
|
||||
#include "SHVkSubpassParams.h"
|
||||
#include "Resource/Handle.h"
|
||||
#include <span>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
class SHVkLogicalDevice;
|
||||
class SHVkFramebuffer;
|
||||
|
||||
class SHVkRenderpass
|
||||
{
|
||||
private:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* STATIC CONSTEXPR VALUES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static constexpr uint32_t NUM_CLEAR_COLORS = 2;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER VARIABLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
//! Vulkan handle to a renderpass
|
||||
vk::RenderPass vkRenderpass;
|
||||
|
||||
//! Logical device required for creation
|
||||
Handle<SHVkLogicalDevice> logicalDeviceHdl;
|
||||
|
||||
//! Container of subpass information used to construct subpasses
|
||||
std::vector<vk::SubpassDescription> subpassDescriptions;
|
||||
|
||||
//! Clear colors for the color and depth
|
||||
std::array<vk::ClearValue, NUM_CLEAR_COLORS> clearColors;
|
||||
|
||||
public:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* CTOR AND DTOR */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
// NOTE: I don't really exposing Vulkan objects as params but this is for performance
|
||||
SHVkRenderpass(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, std::span<vk::AttachmentDescription> const vkDescriptions, std::vector<SHVkSubpassParams> const& subpasses) noexcept;
|
||||
SHVkRenderpass(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, std::span<vk::AttachmentDescription> const vkDescriptions, std::span<vk::SubpassDescription> const spDescs, std::span<vk::SubpassDependency> const spDeps) noexcept;
|
||||
~SHVkRenderpass(void) noexcept;
|
||||
|
||||
SHVkRenderpass(SHVkRenderpass&& rhs) noexcept;
|
||||
SHVkRenderpass& operator=(SHVkRenderpass&& rhs) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PUBLIC MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* SETTERS AND GETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
vk::RenderPass GetVkRenderpass(void) const noexcept;
|
||||
std::array<vk::ClearValue, NUM_CLEAR_COLORS> const& GetClearColors (void) const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1 @@
|
|||
#pragma once
|
|
@ -0,0 +1,7 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVkSubpassParams.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef SH_VK_SUBPASS_PARAMS_H
|
||||
#define SH_VK_SUBPASS_PARAMS_H
|
||||
|
||||
#include "Graphics/SHVulkanIncludes.h"
|
||||
#include "Resource/Handle.h"
|
||||
#include <span>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
struct SHVkSubpassParams
|
||||
{
|
||||
// pipeline bind point
|
||||
vk::PipelineBindPoint pipelineBindPoint;
|
||||
|
||||
//! Color attachment references
|
||||
std::vector<vk::AttachmentReference> colorRefs;
|
||||
|
||||
//! Input attachment references
|
||||
std::vector<vk::AttachmentReference> inputRefs;
|
||||
|
||||
//! Depth stencil attachment
|
||||
vk::AttachmentReference depthStencilRefs {static_cast<uint32_t>(-1), vk::ImageLayout::eUndefined};
|
||||
|
||||
//! Src stage mask
|
||||
vk::PipelineStageFlags srcStageMask;
|
||||
|
||||
//! Src stage mask
|
||||
vk::PipelineStageFlags dstStageMask;
|
||||
|
||||
//! Src access mask
|
||||
vk::AccessFlags srcAccessMask;
|
||||
|
||||
//! Dst access mask
|
||||
vk::AccessFlags dstAccessMask;
|
||||
|
||||
uint32_t dependentSubpassIndex = VK_SUBPASS_EXTERNAL;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,22 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVkUtil.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
bool SHVkUtil::IsDepthOnlyFormat(vk::Format format) noexcept
|
||||
{
|
||||
return format == vk::Format::eD16Unorm ||
|
||||
format == vk::Format::eD32Sfloat;
|
||||
|
||||
}
|
||||
|
||||
bool SHVkUtil::IsDepthStencilAttachment(vk::Format format) noexcept
|
||||
{
|
||||
return format == vk::Format::eD16UnormS8Uint ||
|
||||
format == vk::Format::eD24UnormS8Uint ||
|
||||
format == vk::Format::eD32SfloatS8Uint ||
|
||||
IsDepthOnlyFormat(format);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
#ifndef SH__VK_UTIL_H
|
||||
#define SH__VK_UTIL_H
|
||||
|
||||
#include "SHVulkanIncludes.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
class SHVkUtil
|
||||
{
|
||||
public:
|
||||
static bool IsDepthOnlyFormat (vk::Format format) noexcept;
|
||||
static bool IsDepthStencilAttachment(vk::Format format) noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef SH_VULKAN_DEFINES_H
|
||||
#define SH_VULKAN_DEFINES_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
using SHQueueFamilyIndex = uint32_t;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,7 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVulkanIncludes.h"
|
||||
|
||||
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
|
||||
|
||||
#define VMA_IMPLEMENTATION
|
||||
#include "vk_mem_alloc.h"
|
|
@ -0,0 +1,10 @@
|
|||
#ifndef SH_VULKAN_INCLUDES_H
|
||||
#define SH_VULKAN_INCLUDES_H
|
||||
|
||||
#define VK_USE_PLATFORM_WIN32_KHR
|
||||
#define VULKAN_HPP_NO_CONSTRUCTORS
|
||||
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
|
||||
#define VULKAN_HPP_NO_NODISCARD_WARNINGS
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#endif
|
|
@ -0,0 +1,61 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHShaderBlockInterface.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
void SHShaderBlockInterface::AddVariable(std::string name, Variable&& newVariable) noexcept
|
||||
{
|
||||
variables.try_emplace (std::move(name), std::move (newVariable));
|
||||
}
|
||||
|
||||
SHShaderBlockInterface::Variable const* const SHShaderBlockInterface::GetVariable(std::string const& variableName) const noexcept
|
||||
{
|
||||
if (variables.contains(variableName))
|
||||
return &variables.at(variableName);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SHShaderBlockInterface::SHShaderBlockInterface(void) noexcept
|
||||
: variables{}
|
||||
, bytesRequired{ 0 }
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SHShaderBlockInterface::SHShaderBlockInterface(SHShaderBlockInterface&& rhs) noexcept
|
||||
: variables{ std::move(rhs.variables) }
|
||||
, bytesRequired {std::move (rhs.bytesRequired)}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SHShaderBlockInterface::SetBytesRequired(uint32_t bytes) noexcept
|
||||
{
|
||||
bytesRequired = bytes;
|
||||
}
|
||||
|
||||
|
||||
uint32_t SHShaderBlockInterface::GetBytesRequired(void) const noexcept
|
||||
{
|
||||
return bytesRequired;
|
||||
}
|
||||
|
||||
SHADE::SHShaderBlockInterface& SHShaderBlockInterface::operator=(SHShaderBlockInterface&& rhs) noexcept
|
||||
{
|
||||
if (&rhs == this)
|
||||
return *this;
|
||||
|
||||
variables = std::move(rhs.variables);
|
||||
bytesRequired = std::move(rhs.bytesRequired);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
SHShaderBlockInterface::~SHShaderBlockInterface(void) noexcept
|
||||
{
|
||||
variables.clear();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
class SHShaderBlockInterface
|
||||
{
|
||||
public:
|
||||
struct Variable
|
||||
{
|
||||
//! Offset of the variable in the block
|
||||
uint32_t offset;
|
||||
};
|
||||
private:
|
||||
|
||||
//! container of variable information
|
||||
std::unordered_map<std::string, Variable> variables;
|
||||
|
||||
//! bytes required by the block (includes padding). This variable is required
|
||||
uint32_t bytesRequired;
|
||||
|
||||
public:
|
||||
void AddVariable (std::string name, Variable&& newVariable) noexcept;
|
||||
Variable const* const GetVariable (std::string const& variableName) const noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* CTORS AND DTORS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
SHShaderBlockInterface(void) noexcept;
|
||||
~SHShaderBlockInterface(void) noexcept;
|
||||
SHShaderBlockInterface(SHShaderBlockInterface&& rhs) noexcept;
|
||||
SHShaderBlockInterface& operator=(SHShaderBlockInterface&& rhs) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* SETTERS AND GETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
void SetBytesRequired (uint32_t bytes) noexcept;
|
||||
uint32_t GetBytesRequired (void) const noexcept;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,327 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHShaderReflected.h"
|
||||
#include "Tools/SHLogger.h"
|
||||
#include "Graphics/Instance/SHVkInstance.h"
|
||||
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
void SHShaderDescriptorBindingInfo::AddBlockInterface(uint32_t set, uint32_t binding, Handle<SHShaderBlockInterface> newBlockInterface) noexcept
|
||||
{
|
||||
BindingAndSetHash newIndex = binding;
|
||||
newIndex |= static_cast<uint64_t>(set) << 32;
|
||||
blockInterfaces.emplace (newIndex, newBlockInterface);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Reflects all the push constant information.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHShaderReflected::ReflectPushConstantInfo(void) noexcept
|
||||
{
|
||||
// Skip all the initialization if there are no push constant blocks
|
||||
if (reflectModule->push_constant_block_count == 0)
|
||||
return;
|
||||
|
||||
uint32_t pcCount = reflectModule->push_constant_block_count;
|
||||
|
||||
// Array because we don't want to dynamically allocate memory
|
||||
std::array<SpvReflectBlockVariable*, 1> pcVariable{};
|
||||
spvReflectEnumeratePushConstantBlocks(reflectModule.get(), &pcCount, pcVariable.data());
|
||||
|
||||
SpvReflectBlockVariable* var = pcVariable[0];
|
||||
|
||||
// Size that the push constant takes
|
||||
pcInfo.size = var->size;
|
||||
|
||||
// Name of the push constant struct (not the variable)
|
||||
pcInfo.name = reflectModule->push_constant_blocks->name;
|
||||
|
||||
// initialize member data
|
||||
pcInfo.memberCount = reflectModule->push_constant_blocks->member_count;
|
||||
pcInfo.members = reflectModule->push_constant_blocks->members;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Reflects Descriptor set layout info.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHShaderReflected::ReflectDescriptorBindingInfo(void) noexcept
|
||||
{
|
||||
if (reflectModule->descriptor_binding_count == 0)
|
||||
return;
|
||||
|
||||
// To improve performance, we are not going to check for duplicate set
|
||||
// binding pairs. See SHShaderDescriptorBindingInfo for more details.
|
||||
descBindingInfo.reflectedSets.resize(reflectModule->descriptor_set_count);
|
||||
|
||||
spvReflectEnumerateDescriptorSets(reflectModule.get(), &reflectModule->descriptor_set_count, descBindingInfo.reflectedSets.data());
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Reflects any shader block information from the shader.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHShaderReflected::ReflectShaderBlockInterfaceInfo(void) noexcept
|
||||
{
|
||||
std::function<void(SpvReflectBlockVariable const* const, Handle<SHShaderBlockInterface>, uint32_t, uint32_t&, std::string)> recurseForInfo;
|
||||
recurseForInfo = [&recurseForInfo]
|
||||
(
|
||||
SpvReflectBlockVariable const* const parentBlock,
|
||||
Handle<SHShaderBlockInterface> interfaceHdl,
|
||||
uint32_t parentOffset,
|
||||
uint32_t& biggestAlignment,
|
||||
std::string parentVarName
|
||||
) -> void
|
||||
{
|
||||
for (uint32_t memberIndex = 0; memberIndex < parentBlock->member_count; ++memberIndex)
|
||||
{
|
||||
auto member = parentBlock->members[memberIndex];
|
||||
uint32_t dim = member.type_description->traits.numeric.vector.component_count;
|
||||
|
||||
// If type it is basic type (float, double, vec, mat), we query offset and give it a name.
|
||||
// After that we add variable to the interface.
|
||||
switch (member.type_description->op)
|
||||
{
|
||||
case SpvOp::SpvOpTypeFloat:
|
||||
interfaceHdl->AddVariable(parentVarName + std::string(member.name), SHShaderBlockInterface::Variable(parentOffset + member.offset));
|
||||
biggestAlignment = std::max (biggestAlignment, 4u);
|
||||
break;
|
||||
case SpvOp::SpvOpTypeVector:
|
||||
interfaceHdl->AddVariable(parentVarName + std::string(member.name), SHShaderBlockInterface::Variable(parentOffset + member.offset));
|
||||
if (dim == 3)
|
||||
dim = 4;
|
||||
biggestAlignment = std::max (biggestAlignment, dim * member.type_description->traits.numeric.scalar.width / 8);
|
||||
break;
|
||||
case SpvOp::SpvOpTypeInt:
|
||||
interfaceHdl->AddVariable(parentVarName + std::string(member.name), SHShaderBlockInterface::Variable(parentOffset + member.offset));
|
||||
biggestAlignment = std::max(biggestAlignment, 4u);
|
||||
break;
|
||||
case SpvOp::SpvOpTypeStruct:
|
||||
recurseForInfo(&member, interfaceHdl, member.offset, biggestAlignment, parentVarName + std::string(member.name) + ".");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (auto const* const set : descBindingInfo.reflectedSets)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
auto const* const binding = set->bindings[i];
|
||||
|
||||
if (binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
|
||||
binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
|
||||
binding->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
|
||||
{
|
||||
// Create new interface. For this new interface, we want to start from the name of the variable
|
||||
// used to identify the descriptor. However any variable that will exist in the interface won't contain
|
||||
// the base variable of the descriptor. So e.g.: it won't be materialBuffer.data.color, it'll just
|
||||
// be data.color.
|
||||
auto newInterface = SHVkInstance::GetResourceManager().Create<SHShaderBlockInterface>();
|
||||
uint32_t biggestAlignment = 0;
|
||||
uint32_t bytesRequired = 0;
|
||||
|
||||
recurseForInfo (&binding->block, newInterface, 0u, biggestAlignment, "");
|
||||
|
||||
uint32_t blockSize = 0;
|
||||
if (binding->block.members)
|
||||
{
|
||||
auto const* const member = &binding->block.members[binding->block.member_count - 1];
|
||||
blockSize = member->offset + member->size;
|
||||
}
|
||||
|
||||
while (bytesRequired < blockSize)
|
||||
bytesRequired += biggestAlignment;
|
||||
|
||||
newInterface->SetBytesRequired (bytesRequired);
|
||||
|
||||
descBindingInfo.AddBlockInterface (CURRENT_SET, binding->binding, newInterface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vk::DescriptorType SHShaderDescriptorBindingInfo::ConvertFromReflectDescType(SpvReflectDescriptorType const& type) noexcept
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
return vk::DescriptorType::eCombinedImageSampler;
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
return vk::DescriptorType::eSampledImage;
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
return vk::DescriptorType::eUniformBuffer;
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
return vk::DescriptorType::eUniformBufferDynamic;
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
return vk::DescriptorType::eStorageBuffer;
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
return vk::DescriptorType::eStorageBufferDynamic;
|
||||
case SpvReflectDescriptorType::SPV_REFLECT_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
return vk::DescriptorType::eInputAttachment;
|
||||
default:
|
||||
return vk::DescriptorType::eCombinedImageSampler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SpvReflectDescriptorSet*> const& SHShaderDescriptorBindingInfo::GetReflectedSets(void) const noexcept
|
||||
{
|
||||
return reflectedSets;
|
||||
}
|
||||
|
||||
Handle<SHShaderBlockInterface> SHShaderDescriptorBindingInfo::GetShaderBlockInterface(uint32_t set, uint32_t binding) const noexcept
|
||||
{
|
||||
SHShaderDescriptorBindingInfo::BindingAndSetHash hash = binding;
|
||||
hash |= static_cast<uint64_t>(set) << 32;
|
||||
if (blockInterfaces.contains(hash))
|
||||
return blockInterfaces.at(hash);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
SHShaderReflected::SHShaderReflected(SHShaderReflected&& rhs) noexcept
|
||||
: reflectModule {std::move (rhs.reflectModule)}
|
||||
, pcInfo{ std::move(rhs.pcInfo) }
|
||||
, descBindingInfo{std::move (rhs.descBindingInfo)}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Default constructor. Just moves the reflect module.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
SHShaderReflected::SHShaderReflected(void) noexcept
|
||||
: reflectModule{std::move (std::make_unique<SpvReflectShaderModule>())}
|
||||
, pcInfo {}
|
||||
, descBindingInfo{}
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Move assignment operator.
|
||||
|
||||
\param rhs
|
||||
The object to move from.
|
||||
|
||||
\return
|
||||
A reference to this object.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
SHShaderReflected& SHShaderReflected::operator=(SHShaderReflected&& rhs) noexcept
|
||||
{
|
||||
if (&rhs == this)
|
||||
return *this;
|
||||
|
||||
reflectModule = std::move (rhs.reflectModule);
|
||||
pcInfo = rhs.pcInfo;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Creates a spirv reflection module and reflect based on flags passed in.
|
||||
|
||||
\param spirvBin
|
||||
The spirv binary.
|
||||
|
||||
\param spirvSize
|
||||
The spirv binary size.
|
||||
|
||||
\param shaderName
|
||||
The name of the shader to print during error.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHShaderReflected::Reflect(void* spirvBin, uint32_t spirvSize, std::string const& shaderName) noexcept
|
||||
{
|
||||
if (!HasReflected())
|
||||
{
|
||||
if (auto result = spvReflectCreateShaderModule(spirvSize, spirvBin, reflectModule.get()); result != SPV_REFLECT_RESULT_SUCCESS)
|
||||
{
|
||||
SHLOG_ERROR("Failed to create shader reflect module" + shaderName);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Reflect all the push constant blocks
|
||||
ReflectPushConstantInfo();
|
||||
|
||||
// Reflect descriptor set information
|
||||
ReflectDescriptorBindingInfo();
|
||||
|
||||
// Reflect shader block information
|
||||
ReflectShaderBlockInterfaceInfo();
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Checks if the source in the reflect shader module is not nullptr to
|
||||
determine if the module has been reflected.
|
||||
|
||||
\return
|
||||
If source is not nullptr, return true, otherwise return false.
|
||||
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
bool SHShaderReflected::HasReflected(void) const noexcept
|
||||
{
|
||||
return reflectModule->_internal != nullptr;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Getter for the push constant info.
|
||||
|
||||
\return
|
||||
Reference to push constant info.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
SHShaderPushConstantInfo const& SHShaderReflected::GetPushConstantInfo(void) const noexcept
|
||||
{
|
||||
return pcInfo;
|
||||
}
|
||||
|
||||
SHShaderDescriptorBindingInfo const& SHShaderReflected::GetDescriptorBindingInfo(void) const noexcept
|
||||
{
|
||||
return descBindingInfo;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
#ifndef SH_SHADER_REFLECTED_H
|
||||
#define SH_SHADER_REFLECTED_H
|
||||
|
||||
#include "spirv-reflect/spirv_reflect.h"
|
||||
#include "Graphics/SHVulkanIncludes.h"
|
||||
#include "Graphics/Descriptors/SHVkDescriptorSetLayout.h"
|
||||
#include "Graphics/Shaders/BlockInterface/SHShaderBlockInterface.h"
|
||||
#include <memory>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
struct SHShaderDescriptorBindingInfo
|
||||
{
|
||||
public:
|
||||
using BindingAndSetHash = uint64_t;
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER VARIABLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
//! Descriptor sets parsed in the shader
|
||||
std::vector<SpvReflectDescriptorSet*> reflectedSets;
|
||||
|
||||
//! The interface for the block in the shader
|
||||
std::unordered_map<BindingAndSetHash, Handle<SHShaderBlockInterface>> blockInterfaces;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
void AddBlockInterface(uint32_t set, uint32_t binding, Handle<SHShaderBlockInterface> newBlockInterface) noexcept;
|
||||
|
||||
public:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PUBLIC MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
static vk::DescriptorType ConvertFromReflectDescType(SpvReflectDescriptorType const& type) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* GETTERS AND SETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
std::vector<SpvReflectDescriptorSet*> const& GetReflectedSets (void) const noexcept;
|
||||
Handle<SHShaderBlockInterface> GetShaderBlockInterface (uint32_t set, uint32_t binding) const noexcept;
|
||||
|
||||
friend class SHShaderReflected;
|
||||
|
||||
};
|
||||
|
||||
struct SHShaderPushConstantInfo
|
||||
{
|
||||
//! Size of the push constant block
|
||||
uint32_t size;
|
||||
|
||||
//! Name of the push constant block (not the variable).
|
||||
//! Do not delete this, it is non-owning
|
||||
const char* name;
|
||||
|
||||
//! Number of member variables
|
||||
uint32_t memberCount;
|
||||
|
||||
//! Pointer to members. Do not delete. This is non-owning
|
||||
SpvReflectBlockVariable* members;
|
||||
|
||||
};
|
||||
|
||||
class SHShaderReflected
|
||||
{
|
||||
private:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER VARIABLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
//! Reflected module
|
||||
std::unique_ptr<SpvReflectShaderModule> reflectModule;
|
||||
|
||||
//! Reflected push constant information
|
||||
SHShaderPushConstantInfo pcInfo;
|
||||
|
||||
//! Binding information for descriptor layout creation
|
||||
SHShaderDescriptorBindingInfo descBindingInfo;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
void ReflectPushConstantInfo (void) noexcept;
|
||||
void ReflectDescriptorBindingInfo (void) noexcept;
|
||||
void ReflectShaderBlockInterfaceInfo (void) noexcept;
|
||||
|
||||
public:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* CTORS AND DTORS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
SHShaderReflected(void) noexcept;
|
||||
SHShaderReflected(SHShaderReflected&& rhs) noexcept;
|
||||
SHShaderReflected& operator=(SHShaderReflected&& rhs) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PUBLIC MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
void Reflect (void* spirvBin, uint32_t spirvSize, std::string const& shaderName) noexcept;
|
||||
bool HasReflected (void) const noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* SETTERS AND GETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
SHShaderPushConstantInfo const& GetPushConstantInfo (void) const noexcept;
|
||||
SHShaderDescriptorBindingInfo const& GetDescriptorBindingInfo (void) const noexcept;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,115 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVkShaderModule.h"
|
||||
#include "Graphics/Devices/SHVkLogicalDevice.h"
|
||||
#include "Graphics/Debugging/SHVulkanDebugUtil.h"
|
||||
#include "Tools/SHLogger.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
SHVkShaderModule::SHVkShaderModule(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, std::vector<uint32_t> const& binaryData, std::string inEntryPoint, vk::ShaderStageFlagBits stage, std::string const& name) noexcept
|
||||
: logicalDeviceHdl {inLogicalDeviceHdl}
|
||||
, shaderStage {stage}
|
||||
, entryPoint {inEntryPoint}
|
||||
, vkShaderModule {nullptr}
|
||||
, spirvBinary{}
|
||||
, shaderName {name}
|
||||
, reflectedData {}
|
||||
{
|
||||
// Prepare the create info
|
||||
vk::ShaderModuleCreateInfo moduleCreateInfo
|
||||
{
|
||||
.codeSize = binaryData.size() * sizeof (uint32_t),
|
||||
.pCode = binaryData.data(),
|
||||
};
|
||||
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createShaderModule(&moduleCreateInfo, nullptr, &vkShaderModule); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to create shader module. ");
|
||||
return;
|
||||
}
|
||||
else
|
||||
SHVulkanDebugUtil::ReportVkSuccess("Successfully created shader module.");
|
||||
|
||||
// TODO: Right now, this is doing a copy, we need to figure out if its better to just move from the resource management (source library) instead. The hope is that
|
||||
// shader modules only need 1 of themselves.
|
||||
spirvBinary = binaryData;
|
||||
}
|
||||
|
||||
SHVkShaderModule::SHVkShaderModule(SHVkShaderModule&& rhs) noexcept
|
||||
: vkShaderModule {rhs.vkShaderModule}
|
||||
, spirvBinary{ std::move(rhs.spirvBinary)}
|
||||
, shaderStage {rhs.shaderStage}
|
||||
, entryPoint {std::move (rhs.entryPoint)}
|
||||
, reflectedData {std::move (rhs.reflectedData)}
|
||||
, shaderName {std::move (rhs.shaderName)}
|
||||
, logicalDeviceHdl {rhs.logicalDeviceHdl}
|
||||
{
|
||||
rhs.vkShaderModule = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
SHVkShaderModule& SHVkShaderModule::operator=(SHVkShaderModule&& rhs) noexcept
|
||||
{
|
||||
if (&rhs == this)
|
||||
return *this;
|
||||
|
||||
vkShaderModule = rhs.vkShaderModule;
|
||||
spirvBinary = std::move(rhs.spirvBinary);
|
||||
shaderStage = rhs.shaderStage;
|
||||
entryPoint = std::move(rhs.entryPoint);
|
||||
reflectedData = std::move(rhs.reflectedData);
|
||||
shaderName = std::move(rhs.shaderName);
|
||||
logicalDeviceHdl = std::move(rhs.logicalDeviceHdl);
|
||||
|
||||
rhs.vkShaderModule = VK_NULL_HANDLE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SHVkShaderModule::~SHVkShaderModule(void) noexcept
|
||||
{
|
||||
logicalDeviceHdl->GetVkLogicalDevice().destroyShaderModule (vkShaderModule, nullptr);
|
||||
}
|
||||
|
||||
void SHVkShaderModule::Reflect(void) noexcept
|
||||
{
|
||||
auto cModule = vkShaderModule.operator VkShaderModule();
|
||||
if (cModule)
|
||||
reflectedData.Reflect(spirvBinary.data(), static_cast<uint32_t>(sizeof (uint32_t) * spirvBinary.size()), shaderName);
|
||||
else
|
||||
{
|
||||
SHLOG_ERROR("Cannot reflect shader module. Shader module has either not been created or failed to create. ");
|
||||
}
|
||||
}
|
||||
|
||||
void SHVkShaderModule::OnChange(void) noexcept
|
||||
{
|
||||
for (auto& callback : onChangeCallbacks)
|
||||
callback();
|
||||
}
|
||||
|
||||
void SHVkShaderModule::AddCallback(SHShaderChangeCallback&& callback) noexcept
|
||||
{
|
||||
onChangeCallbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
|
||||
std::string const& SHVkShaderModule::GetEntryPoint(void) const noexcept
|
||||
{
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
vk::ShaderStageFlagBits SHVkShaderModule::GetShaderStageFlagBits(void) const noexcept
|
||||
{
|
||||
return shaderStage;
|
||||
}
|
||||
|
||||
vk::ShaderModule SHVkShaderModule::GetVkShaderModule(void) const noexcept
|
||||
{
|
||||
return vkShaderModule;
|
||||
}
|
||||
|
||||
SHShaderReflected const& SHVkShaderModule::GetReflectedData(void) const noexcept
|
||||
{
|
||||
return reflectedData;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef SH_VK_SHADER_MODULE_H
|
||||
#define SH_VK_SHADER_MODULE_H
|
||||
|
||||
#include "Graphics/SHVulkanIncludes.h"
|
||||
#include "Resource/Handle.h"
|
||||
#include "SHShaderReflected.h"
|
||||
#include <vector>
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
class SHVkLogicalDevice;
|
||||
|
||||
class SHVkShaderModule
|
||||
{
|
||||
public:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* TYPE DEFINITIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
using SHShaderChangeCallback = std::function<void()>;
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER VARIABLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
//! Vulkan handle for shader module
|
||||
vk::ShaderModule vkShaderModule;
|
||||
|
||||
//! the spir-v binary code
|
||||
std::vector<uint32_t> spirvBinary;
|
||||
|
||||
//! Shader stage of the shader (MUST ONLY BE 1 VALUE AND NOT BIT COMBINATION)
|
||||
vk::ShaderStageFlagBits shaderStage;
|
||||
|
||||
//! shader entry point (e.g. main)
|
||||
std::string entryPoint;
|
||||
|
||||
//! Device for creation and deletion of shader module
|
||||
Handle<SHVkLogicalDevice> logicalDeviceHdl;
|
||||
|
||||
//! Shader reflection information
|
||||
SHShaderReflected reflectedData;
|
||||
|
||||
//! Shader module's name
|
||||
std::string shaderName;
|
||||
|
||||
//! When shaders get hot-reloaded, this container could serve as an event
|
||||
//! response to call all the functions that need to be called. Specifically
|
||||
//! pipeline layouts that need to re-parse the newly reflected data and create
|
||||
//! descriptor set layouts and push constant ranges.
|
||||
std::vector<SHShaderChangeCallback> onChangeCallbacks;
|
||||
|
||||
// #NoteToSelf: From Tomas module, pipeline shader stage create info isn't created here
|
||||
// because the struct allows specialization info which should not be part of a module itself.
|
||||
// This struct should be created in the pipeline instead.
|
||||
|
||||
|
||||
public:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* CTORS AND DTORS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
SHVkShaderModule(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, std::vector<uint32_t> const& binaryData, std::string inEntryPoint, vk::ShaderStageFlagBits stage, std::string const& name) noexcept;
|
||||
~SHVkShaderModule(void) noexcept;
|
||||
SHVkShaderModule(SHVkShaderModule&& rhs) noexcept;
|
||||
SHVkShaderModule& operator= (SHVkShaderModule&& rhs) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PUBLIC MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
void Reflect (void) noexcept;
|
||||
void OnChange (void) noexcept;
|
||||
void AddCallback (SHShaderChangeCallback&& callback) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* SETTERS AND GETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
std::string const& GetEntryPoint (void) const noexcept;
|
||||
vk::ShaderStageFlagBits GetShaderStageFlagBits (void) const noexcept;
|
||||
vk::ShaderModule GetVkShaderModule (void) const noexcept;
|
||||
SHShaderReflected const& GetReflectedData (void) const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
|||
#ifndef SH_SWAPCHAIN_PARAMS_H
|
||||
#define SH_SWAPCHAIN_PARAMS_H
|
||||
|
||||
#include <vector>
|
||||
#include "Graphics/SHVulkanIncludes.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
struct SHSwapchainParams
|
||||
{
|
||||
std::vector<vk::Format> surfaceImageFormats;
|
||||
std::vector<vk::Format> depthFormats;
|
||||
std::vector<vk::PresentModeKHR> presentModes;
|
||||
vk::ImageTiling imageTiling{ vk::ImageTiling::eOptimal };
|
||||
vk::FormatFeatureFlags formatFeatureFlag{ vk::FormatFeatureFlagBits::eDepthStencilAttachment };
|
||||
vk::ColorSpaceKHR colorSpace{ vk::ColorSpaceKHR::eSrgbNonlinear };
|
||||
bool vsyncOn{ false };
|
||||
uint32_t idealImageCount { 3 };
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,346 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVkSwapchain.h"
|
||||
#include "Graphics/Devices/SHVkPhysicalDevice.h"
|
||||
#include "Graphics/Devices/SHVkLogicalDevice.h"
|
||||
#include "Graphics/Windowing/Surface/SHVkSurface.h"
|
||||
#include "Tools/SHLogger.h"
|
||||
#include "Graphics/Images/SHVkImage.h"
|
||||
#include "Graphics/Instance/SHVkInstance.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
|
||||
SHVkSwapchain::SHSwapChainDetails::SHSwapChainDetails(Handle<SHVkPhysicalDevice> const& physicalDeviceHdl, Handle<SHVkSurface> const& surfaceHdl) noexcept
|
||||
{
|
||||
if (auto result = physicalDeviceHdl->GetVkPhysicalDevice().getSurfaceCapabilitiesKHR(surfaceHdl->GetVkSurface(), &vkCapabilities); result != vk::Result::eSuccess)
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to get physical device surface capabilities. ");
|
||||
|
||||
vkSurfaceFormats = physicalDeviceHdl->GetVkPhysicalDevice().getSurfaceFormatsKHR(surfaceHdl->GetVkSurface());
|
||||
vkPresentModes = physicalDeviceHdl->GetVkPhysicalDevice().getSurfacePresentModesKHR(surfaceHdl->GetVkSurface());
|
||||
|
||||
if (vkSurfaceFormats.size() == 0)
|
||||
SHLOG_ERROR("Failed to get surface formats from the physical device. ");
|
||||
|
||||
if (vkPresentModes.size() == 0)
|
||||
SHLOG_ERROR("Failed to get present modes from the physical device. ");
|
||||
}
|
||||
|
||||
vk::SurfaceFormatKHR SHVkSwapchain::ChooseSwapSurfaceFormat(std::vector<vk::SurfaceFormatKHR> const& surfaceFormats) const noexcept
|
||||
{
|
||||
// If there is only 1 format found and it is undefined
|
||||
if (surfaceFormats.size() == 1)
|
||||
{
|
||||
if (surfaceFormats[0].format == vk::Format::eUndefined)
|
||||
{
|
||||
// select the format that the user requested as high priority and use that
|
||||
vk::SurfaceFormatKHR format;
|
||||
format.format = swapchainParams.surfaceImageFormats[0];
|
||||
format.colorSpace = swapchainParams.colorSpace;
|
||||
return format;
|
||||
}
|
||||
}
|
||||
else // if there is more than 1 format
|
||||
{
|
||||
// If more than 1 formats are requested
|
||||
for (auto const& format : swapchainParams.surfaceImageFormats)
|
||||
{
|
||||
// Loop through formats available
|
||||
for (auto const& foundFormat : surfaceFormats)
|
||||
{
|
||||
// if format and color space are available, return it
|
||||
if (format == foundFormat.format && foundFormat.colorSpace == swapchainParams.colorSpace)
|
||||
return foundFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, none of the formats requested by the user are found. Just return the first one given by Vulkan
|
||||
return surfaceFormats[0];
|
||||
|
||||
}
|
||||
|
||||
vk::Format SHVkSwapchain::ChooseDepthFormat(void) const noexcept
|
||||
{
|
||||
// For all the formats requested in order
|
||||
for (auto const& format : swapchainParams.depthFormats)
|
||||
{
|
||||
vk::FormatProperties prop;
|
||||
|
||||
// Attempt to get the property from vulkan, and return if matches depth format requested
|
||||
prop = physicalDeviceHdl->GetVkPhysicalDevice().getFormatProperties(format);
|
||||
if ((swapchainParams.imageTiling == vk::ImageTiling::eLinear && (prop.linearTilingFeatures & swapchainParams.formatFeatureFlag) == swapchainParams.formatFeatureFlag) ||
|
||||
(swapchainParams.imageTiling == vk::ImageTiling::eOptimal && (prop.optimalTilingFeatures & swapchainParams.formatFeatureFlag) == swapchainParams.formatFeatureFlag))
|
||||
return format;
|
||||
}
|
||||
|
||||
SHLOG_ERROR("Unable to find a required depth format. ");
|
||||
return swapchainParams.depthFormats[0];
|
||||
|
||||
}
|
||||
|
||||
vk::PresentModeKHR SHVkSwapchain::ChooseSwapPresentMode(std::vector<vk::PresentModeKHR> const& presentModes) noexcept
|
||||
{
|
||||
// If the first requested format is already FIFO
|
||||
if (swapchainParams.presentModes[0] == vk::PresentModeKHR::eFifo)
|
||||
return vk::PresentModeKHR::eFifo; // return FIFO
|
||||
|
||||
// Default to FIFO. Vulkan requires this format to be requested
|
||||
vk::PresentModeKHR finalMode = vk::PresentModeKHR::eFifo;
|
||||
|
||||
// If immediate is supported or not
|
||||
bool immediateSupported = false;
|
||||
|
||||
// Loop through all present modes of the user
|
||||
for (auto const& mode : swapchainParams.presentModes)
|
||||
{
|
||||
// Loop through all present nodes found
|
||||
for (auto const& foundMode : presentModes)
|
||||
{
|
||||
// See if immediate mode is supported
|
||||
if (foundMode == vk::PresentModeKHR::eImmediate)
|
||||
immediateSupported = true;
|
||||
|
||||
// If the node requested for is found
|
||||
if (foundMode == mode)
|
||||
finalMode = foundMode;
|
||||
}
|
||||
}
|
||||
|
||||
// If vsync is in
|
||||
if (swapchainParams.vsyncOn)
|
||||
{
|
||||
// If the mode requested and found is immediate, use relaxed instead
|
||||
if (finalMode == vk::PresentModeKHR::eImmediate)
|
||||
finalMode = vk::PresentModeKHR::eFifoRelaxed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If immediate is supported, just choose immediate
|
||||
if (immediateSupported)
|
||||
finalMode = vk::PresentModeKHR::eImmediate;
|
||||
}
|
||||
|
||||
return vk::PresentModeKHR::eFifo;
|
||||
|
||||
}
|
||||
|
||||
vk::Extent2D SHVkSwapchain::ChooseSwapExtent(vk::SurfaceCapabilitiesKHR const& capabilities, uint32_t windowWidth, uint32_t windowHeight) noexcept
|
||||
{
|
||||
if (capabilities.currentExtent.width != UINT32_MAX)
|
||||
return capabilities.currentExtent;
|
||||
else
|
||||
{
|
||||
vk::Extent2D actualExtent = { windowWidth, windowHeight };
|
||||
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
||||
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
||||
|
||||
return actualExtent;
|
||||
}
|
||||
}
|
||||
|
||||
void SHVkSwapchain::RetrieveSwapchainImages(void) noexcept
|
||||
{
|
||||
std::vector<vk::Image> tempImageVector(imageCount);
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().getSwapchainImagesKHR(vkSwapchain, &imageCount, tempImageVector.data()); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to retrieve swapchain images. ");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < imageCount; ++i)
|
||||
{
|
||||
swapchainImages[i]->LinkWithExteriorImage(tempImageVector[i], vk::ImageType::e2D, width, height, 1, 1, 0, vkSurfaceFormat.format, vkImageUsageFlags);
|
||||
}
|
||||
}
|
||||
|
||||
SHVkSwapchain::SHVkSwapchain(Handle<SHVkPhysicalDevice> const& inPhysicalDeviceHdl, Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkSurface> const& surfaceHdl, uint32_t width, uint32_t height, SHSwapchainParams const& params) noexcept
|
||||
: swapchainParams{}
|
||||
, vkSwapchain{}
|
||||
, vkSurfaceFormat{}
|
||||
, vkPresentMode{}
|
||||
, vkDepthFormat{}
|
||||
, vkSwapExtent{}
|
||||
, imageCount{0}
|
||||
, logicalDeviceHdl{inLogicalDeviceHdl}
|
||||
, swapchainImages{}
|
||||
, physicalDeviceHdl {inPhysicalDeviceHdl}
|
||||
{
|
||||
swapchainParams = params;
|
||||
|
||||
for (uint32_t i = 0; i < MAX_SWAPCHAIN_IMAGES; ++i)
|
||||
{
|
||||
swapchainImages[i] = SHVkInstance::GetResourceManager().Create<SHVkImage>();
|
||||
}
|
||||
|
||||
Resize(surfaceHdl, width, height);
|
||||
}
|
||||
|
||||
SHVkSwapchain::~SHVkSwapchain(void) noexcept
|
||||
{
|
||||
logicalDeviceHdl->GetVkLogicalDevice().destroySwapchainKHR(vkSwapchain, nullptr);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
This function should be called every time the window is resized.
|
||||
|
||||
\param surfaceHdl
|
||||
surface needed for swapchain creation.
|
||||
|
||||
\param width
|
||||
width of images in swapchain.
|
||||
|
||||
\param height
|
||||
height of images in swapchain.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
void SHVkSwapchain::Resize(Handle<SHVkSurface> const& surfaceHdl, uint32_t inWidth, uint32_t inHeight) noexcept
|
||||
{
|
||||
width = inWidth;
|
||||
height = inHeight;
|
||||
|
||||
SHSwapChainDetails swapchainDetails (physicalDeviceHdl, surfaceHdl);
|
||||
|
||||
vkSurfaceFormat = ChooseSwapSurfaceFormat(swapchainDetails.vkSurfaceFormats);
|
||||
vkPresentMode = ChooseSwapPresentMode(swapchainDetails.vkPresentModes);
|
||||
vkDepthFormat = ChooseDepthFormat();
|
||||
vkSwapExtent = ChooseSwapExtent(swapchainDetails.vkCapabilities, width, height);
|
||||
|
||||
// we want to add 1 because we want to always have a frame available at a vertical blanking period. Double buffering cannot always guarantee this because
|
||||
// the CPU cannot always guarantee it will process the frame fast enough to shove the image into the queue before the VBP. When this happens, the GPU will continue
|
||||
// processing the current frame and wait for the next VBP, which is wasted idling time on both CPU and GPU.
|
||||
imageCount = std::max(swapchainDetails.vkCapabilities.minImageCount, swapchainParams.idealImageCount);
|
||||
if (swapchainDetails.vkCapabilities.maxImageCount > 0 && imageCount > swapchainDetails.vkCapabilities.maxImageCount)
|
||||
imageCount = swapchainDetails.vkCapabilities.maxImageCount;
|
||||
|
||||
vk::SwapchainKHR oldSwapchain = vkSwapchain;
|
||||
vkSwapchain = nullptr;
|
||||
|
||||
// Wait for device to finish rendering
|
||||
logicalDeviceHdl->GetVkLogicalDevice().waitIdle();
|
||||
|
||||
vk::SwapchainCreateInfoKHR swapchainCreateInfo{};
|
||||
|
||||
swapchainCreateInfo.pNext = nullptr;
|
||||
swapchainCreateInfo.surface = surfaceHdl->GetVkSurface();
|
||||
swapchainCreateInfo.minImageCount = imageCount;
|
||||
swapchainCreateInfo.imageColorSpace = vkSurfaceFormat.colorSpace;
|
||||
swapchainCreateInfo.imageFormat = vkSurfaceFormat.format;
|
||||
swapchainCreateInfo.imageExtent = vkSwapExtent;
|
||||
swapchainCreateInfo.imageArrayLayers = 1;
|
||||
|
||||
swapchainCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
|
||||
swapchainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive; // queue family and graphics family are the same
|
||||
swapchainCreateInfo.pQueueFamilyIndices = nullptr; // being explicit
|
||||
swapchainCreateInfo.queueFamilyIndexCount = 0; // being explicit
|
||||
swapchainCreateInfo.preTransform = swapchainDetails.vkCapabilities.currentTransform; // identity bit
|
||||
|
||||
// Flag to use when surface is composited together with other surfaces on certain window systems
|
||||
swapchainCreateInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||
swapchainCreateInfo.presentMode = vkPresentMode;
|
||||
swapchainCreateInfo.clipped = VK_TRUE; // clips obscured pixels
|
||||
swapchainCreateInfo.oldSwapchain = oldSwapchain;
|
||||
|
||||
if (swapchainDetails.vkCapabilities.supportedUsageFlags & vk::ImageUsageFlagBits::eTransferSrc)
|
||||
swapchainCreateInfo.imageUsage |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||
if (swapchainDetails.vkCapabilities.supportedUsageFlags & vk::ImageUsageFlagBits::eTransferDst)
|
||||
swapchainCreateInfo.imageUsage |= vk::ImageUsageFlagBits::eTransferDst;
|
||||
|
||||
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createSwapchainKHR(&swapchainCreateInfo, nullptr, &vkSwapchain); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to create Swapchain.");
|
||||
}
|
||||
else
|
||||
SHVulkanDebugUtil::ReportVkSuccess("Successfully created Swapchain. ");
|
||||
|
||||
uint32_t tempCount = 0;
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().getSwapchainImagesKHR(vkSwapchain, &tempCount, nullptr); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to get swapchain image count. ");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tempCount == imageCount)
|
||||
SHVulkanDebugUtil::ReportVkSuccess("Images in swapchain matches requested. ");
|
||||
else
|
||||
{
|
||||
SHLOG_ERROR("Images in swapchain do not match requested. ");
|
||||
}
|
||||
}
|
||||
|
||||
// Save the image usage flags
|
||||
vkImageUsageFlags = swapchainCreateInfo.imageUsage;
|
||||
|
||||
// Retrieve and store handles to swapchain images
|
||||
RetrieveSwapchainImages();
|
||||
|
||||
|
||||
if (oldSwapchain)
|
||||
{
|
||||
logicalDeviceHdl->GetVkLogicalDevice().destroySwapchainKHR(oldSwapchain, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Get the number of swapchain images.
|
||||
|
||||
\return
|
||||
The number of swapchain images.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
uint32_t SHVkSwapchain::GetNumImages(void) const noexcept
|
||||
{
|
||||
return imageCount;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Returns one of the swapchain images.
|
||||
|
||||
\param index
|
||||
Index of the image we want to obtain.
|
||||
|
||||
\return
|
||||
A handle to the swapchain image.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
Handle<SHVkImage> SHVkSwapchain::GetSwapchainImage(uint32_t index) const noexcept
|
||||
{
|
||||
// Index is not valid
|
||||
if (index >= imageCount)
|
||||
{
|
||||
SHLOG_ERROR("Attempting to get images with index that is out of range. ");
|
||||
return {};
|
||||
}
|
||||
|
||||
return swapchainImages[index];
|
||||
}
|
||||
|
||||
vk::SwapchainKHR const& SHVkSwapchain::GetVkSwapchain(void) const noexcept
|
||||
{
|
||||
return vkSwapchain;
|
||||
}
|
||||
|
||||
vk::SurfaceFormatKHR SHVkSwapchain::GetSurfaceFormatKHR(void) const noexcept
|
||||
{
|
||||
return vkSurfaceFormat;
|
||||
}
|
||||
|
||||
vk::Format SHVkSwapchain::GetDepthFormat(void) const noexcept
|
||||
{
|
||||
return vkDepthFormat;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
#ifndef SH_VK_SWAPCHAIN_H
|
||||
#define SH_VK_SWAPCHAIN_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "Graphics/SHVulkanIncludes.h"
|
||||
#include "Resource/ResourceLibrary.h"
|
||||
#include "Graphics/Swapchain/SHSwapchainParams.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
class SHVkSurface;
|
||||
class SHVkLogicalDevice;
|
||||
class SHVkPhysicalDevice;
|
||||
class SHVkImage;
|
||||
|
||||
|
||||
class SHVkSwapchain
|
||||
{
|
||||
private:
|
||||
static constexpr int MAX_SWAPCHAIN_IMAGES = 10;
|
||||
|
||||
struct SHSwapChainDetails
|
||||
{
|
||||
vk::SurfaceCapabilitiesKHR vkCapabilities{};
|
||||
std::vector<vk::SurfaceFormatKHR> vkSurfaceFormats{};
|
||||
std::vector<vk::PresentModeKHR> vkPresentModes{};
|
||||
|
||||
SHSwapChainDetails(Handle<SHVkPhysicalDevice> const& physicalDeviceHdl, Handle<SHVkSurface> const& surfaceHdl) noexcept;
|
||||
};
|
||||
|
||||
private:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER VARIABLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
//! The minimum number of presentable images that the application needs
|
||||
uint32_t imageCount;
|
||||
|
||||
//! width of images in the swapchain
|
||||
uint32_t width;
|
||||
|
||||
//! height of images in the swapchain
|
||||
uint32_t height;
|
||||
|
||||
//! We want to keep a copy of this to help us select formats during resizing
|
||||
SHSwapchainParams swapchainParams;
|
||||
|
||||
//! Vulkan swapchain handle
|
||||
vk::SwapchainKHR vkSwapchain;
|
||||
|
||||
//! The color format the swapchain will use the interpret the data in its queue
|
||||
vk::SurfaceFormatKHR vkSurfaceFormat;
|
||||
|
||||
//! The method the swapchain will use to handle its queue
|
||||
vk::PresentModeKHR vkPresentMode;
|
||||
|
||||
//! The depth format the swapchain will use
|
||||
vk::Format vkDepthFormat;
|
||||
|
||||
//! Images that are pushed the the swapchain have to be off this size/resolution
|
||||
vk::Extent2D vkSwapExtent;
|
||||
|
||||
//! Swapchain image usage flags
|
||||
vk::ImageUsageFlags vkImageUsageFlags;
|
||||
|
||||
//! Handle to the logical device that created this swapchain
|
||||
Handle<SHVkLogicalDevice> logicalDeviceHdl;
|
||||
|
||||
//! Store it for convenience later
|
||||
Handle<SHVkPhysicalDevice> physicalDeviceHdl;
|
||||
|
||||
//! Swapchain images
|
||||
std::array<Handle<SHVkImage>, MAX_SWAPCHAIN_IMAGES> swapchainImages;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
vk::SurfaceFormatKHR ChooseSwapSurfaceFormat (std::vector<vk::SurfaceFormatKHR> const& surfaceFormats) const noexcept;
|
||||
vk::Format ChooseDepthFormat (void) const noexcept;
|
||||
vk::PresentModeKHR ChooseSwapPresentMode (std::vector<vk::PresentModeKHR> const& presentModes) noexcept;
|
||||
vk::Extent2D ChooseSwapExtent (vk::SurfaceCapabilitiesKHR const& capabilities, uint32_t windowWidth, uint32_t windowHeight) noexcept;
|
||||
void RetrieveSwapchainImages (void) noexcept;
|
||||
|
||||
public:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* CTOR AND DTOR */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
SHVkSwapchain (Handle<SHVkPhysicalDevice> const& inPhysicalDeviceHdl, Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl, Handle<SHVkSurface> const& surfaceHdl, uint32_t width, uint32_t height, SHSwapchainParams const& params) noexcept;
|
||||
~SHVkSwapchain (void) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PUBLIC MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
void Resize (Handle<SHVkSurface> const& surfaceHdl, uint32_t inWidth, uint32_t inHeight) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* SETTERS AND GETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
uint32_t GetNumImages (void) const noexcept;
|
||||
Handle<SHVkImage> GetSwapchainImage (uint32_t index) const noexcept;
|
||||
vk::SwapchainKHR const& GetVkSwapchain (void) const noexcept;
|
||||
vk::SurfaceFormatKHR GetSurfaceFormatKHR (void) const noexcept;
|
||||
vk::Format GetDepthFormat (void) const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,116 @@
|
|||
#include "SHPch.h"
|
||||
#include "SHVkFence.h"
|
||||
#include "Graphics/Devices/SHVkLogicalDevice.h"
|
||||
#include "Graphics/Debugging/SHVulkanDebugUtil.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Non-Default ctor. Simply creates a fence.
|
||||
|
||||
\param inLogicalDeviceHdl
|
||||
Logical device for the creation and destruction.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
SHVkFence::SHVkFence(Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl) noexcept
|
||||
: logicalDeviceHdl{inLogicalDeviceHdl}
|
||||
, vkFence{VK_NULL_HANDLE}
|
||||
{
|
||||
vk::FenceCreateInfo fenceCreateInfo{};
|
||||
fenceCreateInfo.flags = vk::FenceCreateFlagBits::eSignaled;
|
||||
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().createFence(&fenceCreateInfo, nullptr, &vkFence); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkError(result, "Failed to create Vulkan Fence. ");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkSuccess("Successfully created fence. ");
|
||||
}
|
||||
}
|
||||
|
||||
SHVkFence::SHVkFence(SHVkFence&& rhs) noexcept
|
||||
: vkFence{rhs.vkFence}
|
||||
, logicalDeviceHdl {rhs.logicalDeviceHdl}
|
||||
{
|
||||
rhs.vkFence = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
SHVkFence& SHVkFence::operator=(SHVkFence&& rhs) noexcept
|
||||
{
|
||||
if (&rhs == this)
|
||||
return *this;
|
||||
|
||||
vkFence = rhs.vkFence;
|
||||
logicalDeviceHdl = rhs.logicalDeviceHdl;
|
||||
rhs.vkFence = VK_NULL_HANDLE;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Destructor. Simply destroys the fence.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
SHVkFence::~SHVkFence(void) noexcept
|
||||
{
|
||||
if (vkFence)
|
||||
logicalDeviceHdl->GetVkLogicalDevice().destroyFence(vkFence, nullptr);
|
||||
}
|
||||
|
||||
bool SHVkFence::Wait(bool waitAll, uint64_t timer) noexcept
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Wait for frame fence to be signaled (usually will be signaled after image is done being used for presentation)
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().waitForFences(1, &vkFence, waitAll, timer); result != vk::Result::eSuccess)
|
||||
{
|
||||
if (result == vk::Result::eTimeout)
|
||||
{
|
||||
std::cout << "Fence timeout. " << std::endl;
|
||||
SHVulkanDebugUtil::ReportVkWarning(result, "Fence timeout. ");
|
||||
return false;
|
||||
}
|
||||
|
||||
SHVulkanDebugUtil::ReportVkWarning(result, "Fence error. ");
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void SHVkFence::Reset(void) noexcept
|
||||
{
|
||||
// Reset fences
|
||||
if (auto result = logicalDeviceHdl->GetVkLogicalDevice().resetFences(1, &vkFence); result != vk::Result::eSuccess)
|
||||
{
|
||||
SHVulkanDebugUtil::ReportVkWarning(result, "Failed to reset fence!");
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
/*!
|
||||
|
||||
\brief
|
||||
Getter for the Vulkan fence.
|
||||
|
||||
\return
|
||||
The Vulkan fence handle.
|
||||
|
||||
*/
|
||||
/***************************************************************************/
|
||||
vk::Fence SHVkFence::GetVkFence(void) const noexcept
|
||||
{
|
||||
return vkFence;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#ifndef SH_VK_FENCE_H
|
||||
#define SH_VK_FENCE_H
|
||||
|
||||
#include "Graphics/SHVulkanIncludes.h"
|
||||
#include "Resource/Handle.h"
|
||||
|
||||
namespace SHADE
|
||||
{
|
||||
class SHVkLogicalDevice;
|
||||
|
||||
class SHVkFence
|
||||
{
|
||||
private:
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PRIVATE MEMBER VARIABLES */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
//! Vulkan handle
|
||||
vk::Fence vkFence;
|
||||
|
||||
//! Logical device required for creation and destruction
|
||||
Handle<SHVkLogicalDevice> logicalDeviceHdl;
|
||||
|
||||
public:
|
||||
SHVkFence (Handle<SHVkLogicalDevice> const& inLogicalDeviceHdl) noexcept;
|
||||
~SHVkFence (void) noexcept;
|
||||
|
||||
SHVkFence (SHVkFence&& rhs) noexcept;
|
||||
SHVkFence& operator=(SHVkFence && rhs) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* PUBLIC MEMBER FUNCTIONS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
bool Wait (bool waitAll, uint64_t timer) noexcept;
|
||||
void Reset (void) noexcept;
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* SETTERS AND GETTERS */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
vk::Fence GetVkFence(void) const noexcept;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue