Lights WIP

This commit is contained in:
Brandon Mak 2022-10-25 00:00:22 +08:00
parent 0788084bbd
commit 8fb0eddcc1
12 changed files with 622 additions and 9 deletions

View File

@ -10,7 +10,7 @@ Collapsed=0
[Window][Hierarchy Panel] [Window][Hierarchy Panel]
Pos=0,142 Pos=0,142
Size=387,918 Size=768,918
Collapsed=0 Collapsed=0
DockId=0x00000004,0 DockId=0x00000004,0
@ -27,7 +27,7 @@ DockId=0x00000006,0
[Window][Profiler] [Window][Profiler]
Pos=0,48 Pos=0,48
Size=387,92 Size=768,92
Collapsed=0 Collapsed=0
DockId=0x00000003,0 DockId=0x00000003,0
@ -38,17 +38,17 @@ Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Window][ Viewport] [Window][ Viewport]
Pos=389,48 Pos=770,48
Size=1258,1012 Size=877,1012
Collapsed=0 Collapsed=0
DockId=0x00000002,0 DockId=0x00000002,0
[Docking][Data] [Docking][Data]
DockSpace ID=0xC5C9B8AB Window=0xBE4044E9 Pos=8,79 Size=1920,1012 Split=X 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=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=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881
DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Selected=0xE096E5AE 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 DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=271,1036 Selected=0xE7039252

View File

@ -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)); SHVkDebugMessenger::GenMessengerType(SH_DEBUG_MSG_TYPE::T_GENERAL, SH_DEBUG_MSG_TYPE::T_VALIDATION, SH_DEBUG_MSG_TYPE::T_PERFORMANCE));
instanceDbgInfo.pfnUserCallback = SHVulkanDebugUtil::GenericDebugCallback; instanceDbgInfo.pfnUserCallback = SHVulkanDebugUtil::GenericDebugCallback;
instanceInfo.pNext = static_cast<vk::DebugUtilsMessengerCreateInfoEXT*>(&instanceDbgInfo); //instanceInfo.pNext = static_cast<vk::DebugUtilsMessengerCreateInfoEXT*>(&instanceDbgInfo);
} }
// Finally create the instance // Finally create the instance

View File

@ -49,7 +49,7 @@ namespace SHADE
{ {
.Type = vk::DescriptorType::eStorageBufferDynamic, .Type = vk::DescriptorType::eStorageBufferDynamic,
.Stage = vk::ShaderStageFlagBits::eFragment, .Stage = vk::ShaderStageFlagBits::eFragment,
.BindPoint = SHGraphicsConstants::DescriptorSetBindings::LIGHTS_DATA, .BindPoint = SHGraphicsConstants::DescriptorSetBindings::DIRECTIONAL_LIGHT_DATA,
.DescriptorCount = 1, .DescriptorCount = 1,
}; };

View File

@ -101,7 +101,7 @@ namespace SHADE
*/ */
/***************************************************************************/ /***************************************************************************/
static constexpr uint32_t LIGHTS_DATA = 0; static constexpr uint32_t DIRECTIONAL_LIGHT_DATA = 0;
/***************************************************************************/ /***************************************************************************/
/*! /*!

View File

@ -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<uint32_t>::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<uint32_t>::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;
}
}

View File

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

View File

@ -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<uint32_t>::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;
}
}

View File

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

View File

@ -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<SHVkLogicalDevice> 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<SHVkLogicalDevice> 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<uint8_t[]>(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<uint8_t[]> oldData = std::make_unique<uint8_t[]>(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<uint8_t[]>(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<SHVkBuffer> 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<SHVkLogicalDevice> logicalDevice, Handle<SHVkDescriptorPool> descPool) noexcept
{
std::vector<uint32_t> 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<SHLightComponent>();
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
{
}
}

View File

@ -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<SHVkBuffer> 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<uint8_t[]> intermediateData;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void InitializeData (Handle<SHVkLogicalDevice> logicalDevice, SH_LIGHT_TYPE type) noexcept;
void Expand (Handle<SHVkLogicalDevice> logicalDevice) noexcept;
void AddLight (SHLightComponent* lightComp) noexcept;
/*-----------------------------------------------------------------------*/
/* GETTERS */
/*-----------------------------------------------------------------------*/
static uint32_t GetLightTypeSize (SH_LIGHT_TYPE type) noexcept;
Handle<SHVkBuffer> GetDataBuffer (void) const noexcept;
uint32_t GetAlignmentSize (void) const noexcept;
};
private:
//! logical device used for creation
Handle<SHVkLogicalDevice> logicalDevice;
//! The descriptor set that will hold the lighting data. Each binding will hold a buffer, NUM_FRAMES times the size required.
Handle<SHVkDescriptorSetGroup> lightingDataDescSet;
//! Each type will have some data associated with it for processing
std::array<PerTypeData, static_cast<uint32_t>(SH_LIGHT_TYPE::NUM_TYPES)> perTypeData;
/*-----------------------------------------------------------------------*/
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void UpdateDescSet (uint32_t binding) noexcept;
public:
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void Init (Handle<SHVkLogicalDevice> logicalDevice, Handle<SHVkDescriptorPool> descPool) noexcept;
void Run (void) noexcept;
void Exit (void) noexcept;
};
}

View File

@ -42,6 +42,15 @@ namespace SHADE
template <IsEnum InputType, IsIntegral OutputType = int> template <IsEnum InputType, IsIntegral OutputType = int>
static constexpr OutputType ConvertEnum(InputType enumClassMember) noexcept; 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<typename Enum>
static constexpr typename std::underlying_type_t<Enum> ToUnderlying (Enum value) noexcept;
}; };
} // namespace SHADE } // namespace SHADE

View File

@ -25,4 +25,10 @@ namespace SHADE
return static_cast<OutputType>(enumClassMember); return static_cast<OutputType>(enumClassMember);
} }
template<typename Enum>
constexpr typename std::underlying_type_t<Enum> SHUtilities::ToUnderlying(Enum value) noexcept
{
return static_cast<typename std::underlying_type_t<Enum>>(value);
}
} // namespace SHADE } // namespace SHADE