diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini index baced6b8..9add170c 100644 --- a/Assets/Editor/Layouts/UserLayout.ini +++ b/Assets/Editor/Layouts/UserLayout.ini @@ -10,7 +10,7 @@ Collapsed=0 [Window][Hierarchy Panel] Pos=0,142 -Size=387,918 +Size=768,918 Collapsed=0 DockId=0x00000004,0 @@ -27,7 +27,7 @@ DockId=0x00000006,0 [Window][Profiler] Pos=0,48 -Size=387,92 +Size=768,92 Collapsed=0 DockId=0x00000003,0 @@ -38,17 +38,17 @@ Collapsed=0 DockId=0x00000002,0 [Window][ Viewport] -Pos=389,48 -Size=1258,1012 +Pos=770,48 +Size=877,1012 Collapsed=0 DockId=0x00000002,0 [Docking][Data] DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X DockNode ID=0x00000005 Parent=0xC5C9B8AB SizeRef=1992,1036 Split=X - DockNode ID=0x00000001 Parent=0x00000005 SizeRef=387,1036 Split=Y Selected=0x1E6EB881 + DockNode ID=0x00000001 Parent=0x00000005 SizeRef=768,1036 Split=Y Selected=0x1E6EB881 DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Selected=0xE096E5AE - DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1258,1036 CentralNode=1 Selected=0xB41284E7 + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=877,1036 CentralNode=1 Selected=0xB41284E7 DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=271,1036 Selected=0xE7039252 diff --git a/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp b/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp index 191bc563..edfe4b46 100644 --- a/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp +++ b/SHADE_Engine/src/Graphics/Instance/SHVkInstance.cpp @@ -155,7 +155,7 @@ namespace SHADE 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(&instanceDbgInfo); + //instanceInfo.pNext = static_cast(&instanceDbgInfo); } // Finally create the instance diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp index d8b1bad1..ed1d3100 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp @@ -49,7 +49,7 @@ namespace SHADE { .Type = vk::DescriptorType::eStorageBufferDynamic, .Stage = vk::ShaderStageFlagBits::eFragment, - .BindPoint = SHGraphicsConstants::DescriptorSetBindings::LIGHTS_DATA, + .BindPoint = SHGraphicsConstants::DescriptorSetBindings::DIRECTIONAL_LIGHT_DATA, .DescriptorCount = 1, }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index a0457b65..15f192c0 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -101,7 +101,7 @@ namespace SHADE */ /***************************************************************************/ - static constexpr uint32_t LIGHTS_DATA = 0; + static constexpr uint32_t DIRECTIONAL_LIGHT_DATA = 0; /***************************************************************************/ /*! diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp new file mode 100644 index 00000000..e3bcec58 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp @@ -0,0 +1,109 @@ +#include "SHpch.h" +#include "SHLightComponent.h" + +namespace SHADE +{ + + + void SHLightComponent::OnCreate(void) + { + lightData.Reset(); + SetType(SH_LIGHT_TYPE::DIRECTIONAL); + indexInBuffer = std::numeric_limits::max(); + Unbind(); + } + + + void SHLightComponent::OnDestroy(void) + { + + } + + void SHLightComponent::SetPosition(SHVec3 position) noexcept + { + lightData.position = position; + MakeDirty(); + } + + + void SHLightComponent::SetType(SH_LIGHT_TYPE type) noexcept + { + lightData.type = type; + MakeDirty(); + } + + + void SHLightComponent::SetDirection(SHVec3 direction) noexcept + { + lightData.direction = direction; + MakeDirty(); + } + + + void SHLightComponent::SetDiffuseColor(SHVec4 diffuseColor) noexcept + { + lightData.diffuseColor = diffuseColor; + MakeDirty(); + } + + void SHLightComponent::ModifyLayer(uint8_t layerIndex, bool value) noexcept + { + if (value) + lightData.cullingMask |= (1u << layerIndex); + else + lightData.cullingMask &= ~(1u << layerIndex); + + MakeDirty(); + } + + + void SHLightComponent::SetAllLayers(void) noexcept + { + lightData.cullingMask = std::numeric_limits::max(); + MakeDirty(); + } + + + void SHLightComponent::ClearAllLayers(void) noexcept + { + lightData.cullingMask = 0; + MakeDirty(); + } + + void SHLightComponent::MakeDirty(void) noexcept + { + dirty = true; + } + + void SHLightComponent::ClearDirtyFlag(void) noexcept + { + dirty = false; + } + + void SHLightComponent::Unbind(void) noexcept + { + bound = false; + MakeDirty(); + } + + void SHLightComponent::SetBound(void) noexcept + { + bound = true; + } + + SHLightData const& SHLightComponent::GetLightData(void) const noexcept + { + return lightData; + } + + bool SHLightComponent::IsDirty(void) const noexcept + { + return dirty; + } + + bool SHLightComponent::GetBound(void) const noexcept + { + return bound; + } + +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h new file mode 100644 index 00000000..3b06a612 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h @@ -0,0 +1,54 @@ +#pragma once + +#include "ECS_Base/Components/SHComponent.h" +#include "SHLightData.h" + +namespace SHADE +{ + + class SH_API SHLightComponent final : public SHComponent + { + private: + //! General data for the light. This will purely be CPU bound. Whatever gets sent to the + //! GPU depends on the type of the light. + SHLightData lightData; + + //! Since the lighting system is gonna be self contained and light weight, we store this + //! so that we only write this to the CPU buffer when this light component change, we don't + //! rewrite everything. However we still write to the GPU buffer when everything changes. + uint32_t indexInBuffer; + + //! If the light component changed some value we mark this true. + bool dirty; + + //! If the light's data is already in the buffers, this will be set to true. + bool bound; + + public: + /*-----------------------------------------------------------------------*/ + /* LIFECYCLE FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void OnCreate (void) override final; + void OnDestroy (void) override final; + + /*-----------------------------------------------------------------------*/ + /* SETTERS AND GETTERS */ + /*-----------------------------------------------------------------------*/ + void SetPosition (SHVec3 position) noexcept; + void SetType (SH_LIGHT_TYPE type) noexcept; + void SetDirection (SHVec3 direction) noexcept; + void SetDiffuseColor (SHVec4 diffuseColor) noexcept; + void ModifyLayer (uint8_t layerIndex, bool value) noexcept; + void SetAllLayers (void) noexcept; + void ClearAllLayers (void) noexcept; + void MakeDirty (void) noexcept; + void ClearDirtyFlag (void) noexcept; + void Unbind (void) noexcept; + void SetBound (void) noexcept; + + SHLightData const& GetLightData (void) const noexcept; + bool IsDirty (void) const noexcept; + bool GetBound (void) const noexcept; + + }; +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightData.cpp new file mode 100644 index 00000000..ba910408 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightData.cpp @@ -0,0 +1,21 @@ +#include "SHpch.h" +#include "SHLightData.h" + +namespace SHADE +{ + void SHLightData::Reset(void) noexcept + { + // no culling is done. + cullingMask = std::numeric_limits::max(); + + // reset position to 0 + position = SHVec3::Zero; + + // direction just point in positive z axis + direction = SHVec3::Forward; + + // Diffuse color set to 1 + diffuseColor = SHVec4::One; + } + +} \ No newline at end of file diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightData.h b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightData.h new file mode 100644 index 00000000..607978a4 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightData.h @@ -0,0 +1,55 @@ +#pragma once + +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" + +namespace SHADE +{ + enum class SH_LIGHT_TYPE : uint32_t + { + DIRECTIONAL = 0, + POINT, + SPOT, + NUM_TYPES + }; + + /***************************************************************************/ + /*! + + \class + Every light will essentially be using this struct. However, when passing + light data over to the GPU, the light data will be split according to + type for more optimal cache access. + + */ + /***************************************************************************/ + struct SHLightData + { + //! position of the light + SHVec3 position; + + //! Type of the light + SH_LIGHT_TYPE type; + + //! direction of the light + SHVec3 direction; + + //! Each bit in this 32 bit field will represent a layer. If the bit is set, + //! when a fragment is being evaluated, the shader will use the fragment's + //! layer value to AND with the light's. If result is 1, do lighting calculations. + uint32_t cullingMask; + + //! Diffuse color emitted by the light + SHVec4 diffuseColor; + + void Reset (void) noexcept; + //! TODO: + //! - Add cut off. (inner and outer). + //! - Add constant, linear and quadratic for attenuation + //! - Specular color if needed. see below. + + //! Specular color + //SHVec4 specularColor; + }; + +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp new file mode 100644 index 00000000..3d39ed89 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -0,0 +1,250 @@ +#include "SHpch.h" +#include "SHLightingSubSystem.h" +#include "Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.h" +#include "Tools/SHUtilities.h" +#include "Graphics/Devices/SHVkLogicalDevice.h" +#include "Graphics/Buffers/SHVkBuffer.h" +#include "Graphics/Descriptors/SHVkDescriptorSetGroup.h" +#include "SHLightComponent.h" +#include "ECS_Base/Managers/SHComponentManager.h" + +namespace SHADE +{ + + /***************************************************************************/ + /*! + + \brief + Initializes type, intermediate data and buffer. dirty will be true. + + \param lightType + type of the light. + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::InitializeData(Handle logicalDevice, SH_LIGHT_TYPE type) noexcept + { + // initialize the type + lightType = type; + + // we want to write to GPU when system runs + dirty = true; + + // boilerplate + intermediateData = nullptr; + numLights = 0; + + // initialize alignment + lightDataAlignmentSize = logicalDevice->PadSSBOSize(GetLightTypeSize(type)); + + // So create some data! + Expand(logicalDevice); + } + + /***************************************************************************/ + /*! + + \brief + Expands both the CPU container and the GPU buffer when the number of + lights have exceeded + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::Expand(Handle logicalDevice) noexcept + { + // we want to wait for the command buffers to finish using the buffers first + logicalDevice->WaitIdle(); + + // this is for CPU buffer. + uint32_t structSize = GetLightTypeSize(lightType); + + // First time we are initializing lights + if (intermediateData == nullptr) + { + // num lights should start of at STARTING_NUM_LIGHTS lights + numLights = STARTING_NUM_LIGHTS; + + // Initialize the data for lights + intermediateData = std::make_unique(lightDataAlignmentSize * STARTING_NUM_LIGHTS); + + dataBuffer = logicalDevice->CreateBuffer(numLights * lightDataAlignmentSize, nullptr, numLights * lightDataAlignmentSize, vk::BufferUsageFlagBits::eStorageBuffer, VMA_MEMORY_USAGE_AUTO, VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + } + else + { + // save old number of lights + uint32_t const OLD_NUM_LIGHTS = numLights; + + // before we increase the number of lights, create space to store old data. + std::unique_ptr oldData = std::make_unique(lightDataAlignmentSize * OLD_NUM_LIGHTS); + + // copy data over. + std::memcpy (oldData.get(), intermediateData.get(), lightDataAlignmentSize * OLD_NUM_LIGHTS); + + // now we start to expand.... + + // double space for lights + numLights *= 2; + + // destroy old data and initialize container for double the amount of data. + intermediateData = std::make_unique(lightDataAlignmentSize * numLights); + + // copy old data to new container + std::memcpy(intermediateData.get(), oldData.get(), lightDataAlignmentSize * OLD_NUM_LIGHTS); + + // Resize the GPU buffer + dataBuffer->ResizeReplace(lightDataAlignmentSize * numLights, oldData.get(), lightDataAlignmentSize * OLD_NUM_LIGHTS); + + } + + } + + /***************************************************************************/ + /*! + + \brief + This function takes in a light comp in the event that its data has not + been placed in the buffer yet. It also checks if the size of the buffer + is big enough + + \param lightComp + + \return + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::AddLight(SHLightComponent* lightComp) noexcept + { + + } + + /***************************************************************************/ + /*! + + \brief + Gets the size required to store data for a light type. + + \param type + Type of a light. + + \return + Size required to store a light based on type. + + */ + /***************************************************************************/ + uint32_t SHLightingSubSystem::PerTypeData::GetLightTypeSize(SH_LIGHT_TYPE type) noexcept + { + switch (type) + { + case SH_LIGHT_TYPE::DIRECTIONAL: + // TOOD: Change after creating point light struct + return 0; + case SH_LIGHT_TYPE::POINT: + return sizeof (SHDirectionalLightData); + case SH_LIGHT_TYPE::SPOT: + // TOOD: Change after creating spot light struct + return 0; + case SH_LIGHT_TYPE::NUM_TYPES: + default: + return 0; + + } + } + + + Handle SHLightingSubSystem::PerTypeData::GetDataBuffer(void) const noexcept + { + return dataBuffer; + } + + + uint32_t SHLightingSubSystem::PerTypeData::GetAlignmentSize(void) const noexcept + { + return lightDataAlignmentSize; + } + + void SHLightingSubSystem::UpdateDescSet(uint32_t binding) noexcept + { + auto buffer = perTypeData[binding].GetDataBuffer(); + + // We bind the buffer with the correct desc set binding + lightingDataDescSet->ModifyWriteDescBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, + binding, + { &buffer, 1 }, + 0, + perTypeData[binding].GetAlignmentSize()); + + lightingDataDescSet->UpdateDescriptorSetBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, binding); + } + + /***************************************************************************/ + /*! + + \brief + Initializes per light type data. This includes buffers and descriptor + sets. + + + */ + /***************************************************************************/ + void SHLightingSubSystem::Init(Handle logicalDevice, Handle descPool) noexcept + { + std::vector variableSizes{ SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES) }; + std::fill (variableSizes.begin(), variableSizes.end(), 1); + + // Create the descriptor set + lightingDataDescSet = descPool->Allocate({SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS]}, variableSizes); + + // initialize all the data first. We add more lights here as we add more types. + perTypeData[SHUtilities::ToUnderlying(SH_LIGHT_TYPE::DIRECTIONAL)].InitializeData(logicalDevice, SH_LIGHT_TYPE::DIRECTIONAL); + UpdateDescSet(SHGraphicsConstants::DescriptorSetBindings::DIRECTIONAL_LIGHT_DATA); + } + + /***************************************************************************/ + /*! + + \brief + Loops through every single light component and checks for dirty light + data. If light data is dirty, rewrite to the CPU container and do a + copy to the GPU data. We also check if the per light type data is too small + to handle more lights, if it is, expand buffer (both CPU and GPU). + + */ + /***************************************************************************/ + void SHLightingSubSystem::Run(void) noexcept + { + auto& lightComps = SHComponentManager::GetDense(); + for (auto& light : lightComps) + { + // First we want to make sure the light is already bound to the system. if it + // isn't, we write it to the correct buffer. + if (!light.GetBound()) + { + + + light.SetBound(); + } + + // if there was modification to the light data + if (light.IsDirty()) + { + + } + } + + } + + /***************************************************************************/ + /*! + + \brief + Does nothing for now. + + */ + /***************************************************************************/ + void SHLightingSubSystem::Exit(void) noexcept + { + + } + + +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h new file mode 100644 index 00000000..4c99354f --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h @@ -0,0 +1,109 @@ +#pragma once + +#include "Resource/SHHandle.h" +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" +#include "SHLightData.h" + +namespace SHADE +{ + class SHVkLogicalDevice; + class SHVkDescriptorPool; + class SHVkDescriptorSetGroup; + class SHVkDescriptorSetLayout; + class SHVkBuffer; + + // Represents how the data will be interpreted in GPU. we want to copy to a container of these before passing to GPU. + struct SHDirectionalLightData + { + //! Direction of the light + SHVec3 direction; + + //! Each bit in this 32 bit field will represent a layer. If the bit is set, + //! when a fragment is being evaluated, the shader will use the fragment's + //! layer value to AND with the light's. If result is 1, do lighting calculations. + uint32_t cullingMask; + + //! Diffuse color emitted by the light + SHVec4 diffuseColor; + + }; + + class SHLightingSubSystem + { + private: + + class PerTypeData + { + private: + /*-----------------------------------------------------------------------*/ + /* STATIC MEMBER VARIABLES */ + /*-----------------------------------------------------------------------*/ + static constexpr uint32_t STARTING_NUM_LIGHTS = 20; + + /*-----------------------------------------------------------------------*/ + /* PRIVATE MEMBER VARIABLES */ + /*-----------------------------------------------------------------------*/ + + //! SSBOs need to be aligned. This is to pad lighting structs + uint32_t lightDataAlignmentSize; + + //! type of the light. Will be used later when we want to expand + SH_LIGHT_TYPE lightType; + + //! number of lights currently alive. + uint32_t numLights; + + //! if intermediateData has been modified(i.e. any 1 light's data is changed), set to true. + bool dirty; + + //! GPU buffer required to store GPU data + Handle dataBuffer; + + //! Before data gets copied to the GPU, it goes into here first. Data here is aligned to whatever struct is + //! used to represent data in this container. + std::unique_ptr intermediateData; + + public: + /*-----------------------------------------------------------------------*/ + /* PUBLIC MEMBER FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void InitializeData (Handle logicalDevice, SH_LIGHT_TYPE type) noexcept; + void Expand (Handle logicalDevice) noexcept; + void AddLight (SHLightComponent* lightComp) noexcept; + + /*-----------------------------------------------------------------------*/ + /* GETTERS */ + /*-----------------------------------------------------------------------*/ + static uint32_t GetLightTypeSize (SH_LIGHT_TYPE type) noexcept; + Handle GetDataBuffer (void) const noexcept; + uint32_t GetAlignmentSize (void) const noexcept; + }; + + private: + + //! logical device used for creation + Handle logicalDevice; + + //! The descriptor set that will hold the lighting data. Each binding will hold a buffer, NUM_FRAMES times the size required. + Handle lightingDataDescSet; + + //! Each type will have some data associated with it for processing + std::array(SH_LIGHT_TYPE::NUM_TYPES)> perTypeData; + + /*-----------------------------------------------------------------------*/ + /* PRIVATE MEMBER FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void UpdateDescSet (uint32_t binding) noexcept; + + public: + + /*-----------------------------------------------------------------------*/ + /* PUBLIC MEMBER FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void Init (Handle logicalDevice, Handle descPool) noexcept; + void Run (void) noexcept; + void Exit (void) noexcept; + + }; +} diff --git a/SHADE_Engine/src/Tools/SHUtilities.h b/SHADE_Engine/src/Tools/SHUtilities.h index 543c771c..b3d840e7 100644 --- a/SHADE_Engine/src/Tools/SHUtilities.h +++ b/SHADE_Engine/src/Tools/SHUtilities.h @@ -42,6 +42,15 @@ namespace SHADE template static constexpr OutputType ConvertEnum(InputType enumClassMember) noexcept; + /** + * @brief Converts an enum class member from it's type to the underlying type. + * @tparam Enum Restricted to an enum class + * @param[in] value A member of the specified enum class. + * @returns The value of the enum class member in the output type. + */ + template + static constexpr typename std::underlying_type_t ToUnderlying (Enum value) noexcept; + }; } // namespace SHADE diff --git a/SHADE_Engine/src/Tools/SHUtilities.hpp b/SHADE_Engine/src/Tools/SHUtilities.hpp index 0e21a9d0..e0404ea1 100644 --- a/SHADE_Engine/src/Tools/SHUtilities.hpp +++ b/SHADE_Engine/src/Tools/SHUtilities.hpp @@ -25,4 +25,10 @@ namespace SHADE return static_cast(enumClassMember); } + template + constexpr typename std::underlying_type_t SHUtilities::ToUnderlying(Enum value) noexcept + { + return static_cast>(value); + } + } // namespace SHADE \ No newline at end of file