diff --git a/Assets/Editor/Layouts/UserLayout.ini b/Assets/Editor/Layouts/UserLayout.ini new file mode 100644 index 00000000..e27f5cca --- /dev/null +++ b/Assets/Editor/Layouts/UserLayout.ini @@ -0,0 +1,62 @@ +[Window][MainStatusBar] +Pos=0,1060 +Size=1920,20 +Collapsed=0 + +[Window][SHEditorMenuBar] +Pos=0,48 +Size=1920,1012 +Collapsed=0 + +[Window][Hierarchy Panel] +Pos=0,142 +Size=494,690 +Collapsed=0 +DockId=0x00000007,0 + +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][Inspector] +Pos=1649,48 +Size=271,1012 +Collapsed=0 +DockId=0x00000006,0 + +[Window][Profiler] +Pos=0,48 +Size=494,92 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Viewport] +Pos=648,48 +Size=2519,1319 +Collapsed=0 +DockId=0x00000002,0 + +[Window][ Viewport] +Pos=496,48 +Size=1151,1012 +Collapsed=0 +DockId=0x00000002,0 + +[Window][ Asset Browser] +Pos=0,834 +Size=494,226 +Collapsed=0 +DockId=0x00000008,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=494,1036 Split=Y Selected=0x1E6EB881 + DockNode ID=0x00000003 Parent=0x00000001 SizeRef=225,94 Selected=0x1E6EB881 + DockNode ID=0x00000004 Parent=0x00000001 SizeRef=225,940 Split=Y Selected=0xE096E5AE + DockNode ID=0x00000007 Parent=0x00000004 SizeRef=494,690 Selected=0xE096E5AE + DockNode ID=0x00000008 Parent=0x00000004 SizeRef=494,226 Selected=0xB128252A + DockNode ID=0x00000002 Parent=0x00000005 SizeRef=1151,1036 CentralNode=1 Selected=0xB41284E7 + DockNode ID=0x00000006 Parent=0xC5C9B8AB SizeRef=271,1036 Selected=0xE7039252 + diff --git a/SHADE_Application/src/Scenes/SBTestScene.cpp b/SHADE_Application/src/Scenes/SBTestScene.cpp index ad886aa8..a06e68c2 100644 --- a/SHADE_Application/src/Scenes/SBTestScene.cpp +++ b/SHADE_Application/src/Scenes/SBTestScene.cpp @@ -12,6 +12,7 @@ #include "Graphics/MiddleEnd/Interface/SHMaterialInstance.h" #include "Physics/Components/SHRigidBodyComponent.h" #include "Physics/Components/SHColliderComponent.h" +#include "Graphics/MiddleEnd/Lights/SHLightComponent.h" #include "Assets/SHAssetManager.h" #include "Camera/SHCameraComponent.h" @@ -158,6 +159,7 @@ namespace Sandbox scriptEngine->AddScript(raccoonShowcase, "RaccoonShowcase"); SHComponentManager::AddComponent(0); + SHComponentManager::AddComponent(0); SHComponentManager::RemoveComponent (0); SHComponentManager::RemoveComponent (0); } 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/Batching/SHBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp index 012d9526..09fde60f 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.cpp @@ -30,200 +30,200 @@ of DigiPen Institute of Technology is prohibited. namespace SHADE { - /*---------------------------------------------------------------------------------*/ - /* SHBatch - Usage Functions */ - /*---------------------------------------------------------------------------------*/ - SHBatch::SHBatch(Handle pipeline) + /*---------------------------------------------------------------------------------*/ + /* SHBatch - Usage Functions */ + /*---------------------------------------------------------------------------------*/ + SHBatch::SHBatch(Handle pipeline) : pipeline{ pipeline } - { - if (!pipeline) - throw std::invalid_argument("Attempted to create a SHBatch with an invalid SHPipeline!"); + { + if (!pipeline) + throw std::invalid_argument("Attempted to create a SHBatch with an invalid SHPipeline!"); - // Mark all as dirty - setAllDirtyFlags(); + // Mark all as dirty + setAllDirtyFlags(); + } + + void SHBatch::Add(const SHRenderable* renderable) + { + // Check if we have a SubBatch with the same mesh yet + auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) + { + return batch.Mesh == renderable->Mesh; + }); + + // Create one if not found + if (subBatch == subBatches.end()) + { + subBatches.emplace_back(renderable->Mesh); + subBatch = subBatches.end() - 1; } - void SHBatch::Add(const SHRenderable* renderable) - { - // Check if we have a SubBatch with the same mesh yet - auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) - { - return batch.Mesh == renderable->Mesh; - }); + // Add renderable in + subBatch->Renderables.insert(renderable->GetEID()); - // Create one if not found - if (subBatch == subBatches.end()) + // Also add material instance in + referencedMatInstances.insert(renderable->GetMaterial()); + + // Mark all as dirty + setAllDirtyFlags(); + } + + void SHBatch::Remove(const SHRenderable* renderable) + { + // Check if we have a SubBatch with the same mesh yet + auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) + { + return batch.Mesh == renderable->Mesh; + }); + + // Attempt to remove if it exists + if (subBatch == subBatches.end()) + return; + + subBatch->Renderables.erase(renderable->GetEID()); + + // Check if other renderables in subBatches contain the same material instance + bool matUnused = true; + for (const auto& sb : subBatches) + for (const auto& rendId : sb.Renderables) + { + auto rend = SHComponentManager::GetComponent(rendId); + if (rend) { - subBatches.emplace_back(renderable->Mesh); - subBatch = subBatches.end() - 1; + if (rend->GetMaterial() == renderable->GetMaterial()) + { + matUnused = false; + break; + } } + else + { + SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); + } + } - // Add renderable in - subBatch->Renderables.insert(renderable->GetEID()); + // Material is no longer in this library, so we remove it + if (matUnused) + referencedMatInstances.erase(renderable->WasMaterialChanged() ? renderable->GetPrevMaterial() : renderable->GetMaterial()); - // Also add material instance in - referencedMatInstances.insert(renderable->GetMaterial()); + // Mark all as dirty + setAllDirtyFlags(); + } - // Mark all as dirty - setAllDirtyFlags(); + void SHBatch::Clear() + { + subBatches.clear(); + + // Clear CPU buffers + drawData.clear(); + transformData.clear(); + instancedIntegerData.clear(); + matPropsData.reset(); + matPropsDataSize = 0; + + + // Clear GPU buffers + for (int i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) + { + drawDataBuffer[i].Free(); + transformDataBuffer[i].Free(); + instancedIntegerBuffer[i].Free(); + matPropsBuffer[i].Free(); + } + } + + void SHBatch::UpdateMaterialBuffer(uint32_t frameIndex, Handle descPool) + { + if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) + { + SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index."); + return; } - void SHBatch::Remove(const SHRenderable* renderable) + // Check if there are even material properties to update + if (!matPropsData) + return; + + // Check if any materials have changed + bool hasChanged = false; + for (const auto& material : referencedMatInstances) { - // Check if we have a SubBatch with the same mesh yet - auto subBatch = std::find_if(subBatches.begin(), subBatches.end(), [&](const SHSubBatch& batch) - { - return batch.Mesh == renderable->Mesh; - }); - - // Attempt to remove if it exists - if (subBatch == subBatches.end()) - return; - - subBatch->Renderables.erase(renderable->GetEID()); - - // Check if other renderables in subBatches contain the same material instance - bool matUnused = true; - for (const auto& sb : subBatches) - for (const auto& rendId : sb.Renderables) - { - auto rend = SHComponentManager::GetComponent(rendId); - if (rend) - { - if (rend->GetMaterial() == renderable->GetMaterial()) - { - matUnused = false; - break; - } - } - else - { - SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); - } - } - - // Material is no longer in this library, so we remove it - if (matUnused) - referencedMatInstances.erase(renderable->WasMaterialChanged() ? renderable->GetPrevMaterial() : renderable->GetMaterial()); - - // Mark all as dirty - setAllDirtyFlags(); + if (material->HasChanged()) + { + hasChanged = true; + break; + } } - void SHBatch::Clear() + // We need to update all the material buffers if the materials have changed + if (hasChanged) { - subBatches.clear(); - - // Clear CPU buffers - drawData.clear(); - transformData.clear(); - eidData.clear(); - matPropsData.reset(); - matPropsDataSize = 0; - - - // Clear GPU buffers - for (int i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) - { - drawDataBuffer[i].Free(); - transformDataBuffer[i].Free(); - eidBuffer[i].Free(); - matPropsBuffer[i].Free(); - } + for (auto& dirt : matBufferDirty) + dirt = true; } - void SHBatch::UpdateMaterialBuffer(uint32_t frameIndex, Handle descPool) - { - if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) - { - SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index."); - return; - } + // Check if this frame's buffer is dirty + if (!matBufferDirty[frameIndex]) + return; - // Check if there are even material properties to update - if (!matPropsData) - return; - - // Check if any materials have changed - bool hasChanged = false; - for (const auto& material : referencedMatInstances) + // Build CPU Buffer + char* propsCurrPtr = matPropsData.get(); + for (auto& subBatch : subBatches) + for (auto rendId : subBatch.Renderables) + { + const SHRenderable* renderable = SHComponentManager::GetComponent(rendId); + if (renderable) { - if (material->HasChanged()) - { - hasChanged = true; - break; - } + renderable->GetMaterial()->ExportProperties(propsCurrPtr); } - - // We need to update all the material buffers if the materials have changed - if (hasChanged) + else { - for (auto& dirt : matBufferDirty) - dirt = true; + SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); } + propsCurrPtr += singleMatPropAlignedSize; + } - // Check if this frame's buffer is dirty - if (!matBufferDirty[frameIndex]) - return; + // Transfer to GPU + rebuildMaterialBuffers(frameIndex, descPool); - // Build CPU Buffer - char* propsCurrPtr = matPropsData.get(); - for (auto& subBatch : subBatches) - for (auto rendId : subBatch.Renderables) - { - const SHRenderable* renderable = SHComponentManager::GetComponent(rendId); - if (renderable) - { - renderable->GetMaterial()->ExportProperties(propsCurrPtr); - } - else - { - SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); - } - propsCurrPtr += singleMatPropAlignedSize; - } - - // Transfer to GPU - rebuildMaterialBuffers(frameIndex, descPool); + // This frame is updated + matBufferDirty[frameIndex] = false; + } - // This frame is updated - matBufferDirty[frameIndex] = false; - } - - void SHBatch::UpdateTransformBuffer(uint32_t frameIndex) + void SHBatch::UpdateTransformBuffer(uint32_t frameIndex) + { + if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) { - if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) - { - SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index."); - return; - } - - // Reset Transform Data - transformData.clear(); - - // Populate on the CPU - for (auto& subBatch : subBatches) - for (auto rendId : subBatch.Renderables) - { - // Transform - auto transform = SHComponentManager::GetComponent(rendId); - if (transform) - { - transformData.emplace_back(transform->GetTRS()); - } - else - { - SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!"); - transformData.emplace_back(); - } - } - - // Transfer to GPU - if (transformDataBuffer[frameIndex]) - transformDataBuffer[frameIndex]->WriteToMemory(transformData.data(), static_cast(transformData.size() * sizeof(SHMatrix)), 0, 0); + SHLOG_WARNING("[SHBatch] Attempted to update transform buffers with an invalid frame index."); + return; } - void SHBatch::UpdateEIDBuffer(uint32_t frameIndex) + // Reset Transform Data + transformData.clear(); + + // Populate on the CPU + for (auto& subBatch : subBatches) + for (auto rendId : subBatch.Renderables) + { + // Transform + auto transform = SHComponentManager::GetComponent(rendId); + if (transform) + { + transformData.emplace_back(transform->GetTRS()); + } + else + { + SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!"); + transformData.emplace_back(); + } + } + + // Transfer to GPU + if (transformDataBuffer[frameIndex]) + transformDataBuffer[frameIndex]->WriteToMemory(transformData.data(), static_cast(transformData.size() * sizeof(SHMatrix)), 0, 0); + } + + void SHBatch::UpdateInstancedIntegerBuffer(uint32_t frameIndex) { if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) { @@ -232,233 +232,244 @@ namespace SHADE } // Reset Transform Data - eidData.clear(); + instancedIntegerData.clear(); // Populate on the CPU for (auto& subBatch : subBatches) - for (auto rendId : subBatch.Renderables) - { - eidData.emplace_back(rendId); - } + for (auto rendId : subBatch.Renderables) + { + auto* renderable = SHComponentManager::GetComponent(rendId); + instancedIntegerData.emplace_back(SHInstancedIntegerData + { + rendId, + renderable->GetLightLayer() + } + ); + } // Transfer to GPU - if (eidBuffer[frameIndex]) - eidBuffer[frameIndex]->WriteToMemory(eidData.data(), static_cast(eidData.size() * sizeof(EntityID)), 0, 0); + if (instancedIntegerBuffer[frameIndex]) + instancedIntegerBuffer[frameIndex]->WriteToMemory(instancedIntegerData.data(), static_cast(instancedIntegerData.size() * sizeof(SHInstancedIntegerData)), 0, 0); } void SHBatch::Build(Handle _device, Handle descPool, uint32_t frameIndex) + { + if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) { - if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) - { - SHLOG_WARNING("[SHBatch] Attempted to update build batch buffers with an invalid frame index."); - return; - } - - // Save logical device - device = _device; - - // No need to build as there are no changes - if (!isDirty[frameIndex]) - return; - - // Count number of elements - size_t numTotalElements = 0; - for (const auto& subBatch : subBatches) - { - numTotalElements += subBatch.Renderables.size(); - } - - // Generate CPU buffers if there are changes - if (isCPUBuffersDirty) - { - // - Draw data - drawData.reserve(subBatches.size()); - drawData.clear(); - // - Transform data - transformData.reserve(numTotalElements); - transformData.clear(); - // - EID data - eidData.reserve(numTotalElements); - eidData.clear(); - - - // - Material Properties Data - const Handle SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface - ( - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, - vk::ShaderStageFlagBits::eFragment - ); - const bool EMPTY_MAT_PROPS = !SHADER_INFO; - Byte matPropTotalBytes = 0; - if (!EMPTY_MAT_PROPS) - { - singleMatPropSize = SHADER_INFO->GetBytesRequired(); - singleMatPropAlignedSize = device->PadSSBOSize(static_cast(singleMatPropSize)); - matPropTotalBytes = numTotalElements * singleMatPropAlignedSize; - if (matPropsDataSize < matPropTotalBytes) - { - matPropsData.reset(new char[matPropTotalBytes]); - matPropsDataSize = matPropTotalBytes; - } - } - - // Build Sub Batches - uint32_t nextInstanceIndex = 0; - char* propsCurrPtr = matPropsData.get(); - for (auto& subBatch : subBatches) - { - // Create command - const uint32_t CURR_INSTANCES = static_cast(subBatch.Renderables.size()); - drawData.emplace_back(vk::DrawIndexedIndirectCommand - { - .indexCount = subBatch.Mesh->IndexCount, - .instanceCount = CURR_INSTANCES, - .firstIndex = subBatch.Mesh->FirstIndex, - .vertexOffset = subBatch.Mesh->FirstVertex, - .firstInstance = nextInstanceIndex - }); - nextInstanceIndex += CURR_INSTANCES; - - // Fill in buffers (CPU) - for (auto rendId : subBatch.Renderables) - { - // Transform - auto transform = SHComponentManager::GetComponent_s(rendId); - if (!transform) - { - SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!"); - transformData.emplace_back(); - } - else - { - transformData.emplace_back(transform->GetTRS()); - } - - eidData.emplace_back(rendId); - - // Material Properties - if (!EMPTY_MAT_PROPS) - { - const SHRenderable* renderable = SHComponentManager::GetComponent(rendId); - if (renderable) - { - renderable->GetMaterial()->ExportProperties(propsCurrPtr); - } - else - { - SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); - } - propsCurrPtr += singleMatPropAlignedSize; - } - } - } - - // Successfully update CPU buffers - isCPUBuffersDirty = false; - } - - // Send all buffered data to the GPU buffers - using BuffUsage = vk::BufferUsageFlagBits; - // - Draw Data - const uint32_t DRAW_DATA_BYTES = static_cast(drawData.size() * sizeof(vk::DrawIndexedIndirectCommand)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, drawDataBuffer[frameIndex], drawData.data(), DRAW_DATA_BYTES, - BuffUsage::eIndirectBuffer - ); - // - Transform Buffer - const uint32_t TF_DATA_BYTES = static_cast(transformData.size() * sizeof(SHMatrix)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, transformDataBuffer[frameIndex], transformData.data(), TF_DATA_BYTES, - BuffUsage::eVertexBuffer - ); - const uint32_t EID_DATA_BYTES = static_cast(eidData.size() * sizeof(EntityID)); - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, eidBuffer[frameIndex], eidData.data(), EID_DATA_BYTES, - BuffUsage::eVertexBuffer - ); - // - Material Properties Buffer - rebuildMaterialBuffers(frameIndex, descPool); - - // Mark this frame as no longer dirty - isDirty[frameIndex] = false; + SHLOG_WARNING("[SHBatch] Attempted to update build batch buffers with an invalid frame index."); + return; } - /*---------------------------------------------------------------------------------*/ - /* SHBatch - Usage Functions */ - /*---------------------------------------------------------------------------------*/ - void SHBatch::Draw(Handle cmdBuffer, uint32_t frameIndex) - { - if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) - { - SHLOG_WARNING("[SHBatch] Attempted to draw a batch with an invalid frame index."); - return; - } + // Save logical device + device = _device; - // Bind all required objects before drawing - static std::array dynamicOffset { 0 }; - cmdBuffer->BindPipeline(pipeline); - cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); - cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::EID, eidBuffer[frameIndex], 0); - if (matPropsDescSet[frameIndex]) + // No need to build as there are no changes + if (!isDirty[frameIndex]) + return; + + // Count number of elements + size_t numTotalElements = 0; + for (const auto& subBatch : subBatches) + { + numTotalElements += subBatch.Renderables.size(); + } + + // Generate CPU buffers if there are changes + if (isCPUBuffersDirty) + { + // - Draw data + drawData.reserve(subBatches.size()); + drawData.clear(); + // - Transform data + transformData.reserve(numTotalElements); + transformData.clear(); + // - EID data + instancedIntegerData.reserve(numTotalElements); + instancedIntegerData.clear(); + + + // - Material Properties Data + const Handle SHADER_INFO = pipeline->GetPipelineLayout()->GetShaderBlockInterface + ( + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + vk::ShaderStageFlagBits::eFragment + ); + const bool EMPTY_MAT_PROPS = !SHADER_INFO; + Byte matPropTotalBytes = 0; + if (!EMPTY_MAT_PROPS) + { + singleMatPropSize = SHADER_INFO->GetBytesRequired(); + singleMatPropAlignedSize = device->PadSSBOSize(static_cast(singleMatPropSize)); + matPropTotalBytes = numTotalElements * singleMatPropAlignedSize; + if (matPropsDataSize < matPropTotalBytes) { - cmdBuffer->BindDescriptorSet - ( - matPropsDescSet[frameIndex], - SH_PIPELINE_TYPE::GRAPHICS, - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - dynamicOffset + matPropsData.reset(new char[matPropTotalBytes]); + matPropsDataSize = matPropTotalBytes; + } + } + + // Build Sub Batches + uint32_t nextInstanceIndex = 0; + char* propsCurrPtr = matPropsData.get(); + for (auto& subBatch : subBatches) + { + // Create command + const uint32_t CURR_INSTANCES = static_cast(subBatch.Renderables.size()); + drawData.emplace_back(vk::DrawIndexedIndirectCommand + { + .indexCount = subBatch.Mesh->IndexCount, + .instanceCount = CURR_INSTANCES, + .firstIndex = subBatch.Mesh->FirstIndex, + .vertexOffset = subBatch.Mesh->FirstVertex, + .firstInstance = nextInstanceIndex + }); + nextInstanceIndex += CURR_INSTANCES; + + // Fill in buffers (CPU) + for (auto rendId : subBatch.Renderables) + { + // Transform + auto transform = SHComponentManager::GetComponent_s(rendId); + if (!transform) + { + SHLOG_WARNING("[SHBatch] Entity contianing a SHRenderable with no SHTransformComponent found!"); + transformData.emplace_back(); + } + else + { + transformData.emplace_back(transform->GetTRS()); + } + + const SHRenderable* renderable = SHComponentManager::GetComponent(rendId); + instancedIntegerData.emplace_back(SHInstancedIntegerData + { + rendId, + renderable->GetLightLayer() + } ); - } - cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); - } - /*---------------------------------------------------------------------------------*/ - /* SHBatch - Helper Functions */ - /*---------------------------------------------------------------------------------*/ - void SHBatch::setAllDirtyFlags() - { - for (bool& dirt : isDirty) - dirt = true; - isCPUBuffersDirty = true; - } - - void SHBatch::rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool) - { - if (matPropsData) - { - SHVkUtil::EnsureBufferAndCopyHostVisibleData - ( - device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast(matPropsDataSize), - vk::BufferUsageFlagBits::eStorageBuffer - ); - - if (!matPropsDescSet[frameIndex]) + // Material Properties + if (!EMPTY_MAT_PROPS) + { + if (renderable) { - matPropsDescSet[frameIndex] = descPool->Allocate - ( - { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] }, - { 0 } - ); + renderable->GetMaterial()->ExportProperties(propsCurrPtr); } - std::array, 1> bufferList = { matPropsBuffer[frameIndex] }; - matPropsDescSet[frameIndex]->ModifyWriteDescBuffer - ( - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, - bufferList, - 0, static_cast(matPropsDataSize) - ); - matPropsDescSet[frameIndex]->UpdateDescriptorSetBuffer - ( - SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, - SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA - ); + else + { + SHLOG_WARNING("[SHBatch] Entity with a missing SHRenderable found!"); + } + propsCurrPtr += singleMatPropAlignedSize; + } } + } + + // Successfully update CPU buffers + isCPUBuffersDirty = false; } + // Send all buffered data to the GPU buffers + using BuffUsage = vk::BufferUsageFlagBits; + // - Draw Data + const uint32_t DRAW_DATA_BYTES = static_cast(drawData.size() * sizeof(vk::DrawIndexedIndirectCommand)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, drawDataBuffer[frameIndex], drawData.data(), DRAW_DATA_BYTES, + BuffUsage::eIndirectBuffer + ); + // - Transform Buffer + const uint32_t TF_DATA_BYTES = static_cast(transformData.size() * sizeof(SHMatrix)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, transformDataBuffer[frameIndex], transformData.data(), TF_DATA_BYTES, + BuffUsage::eVertexBuffer + ); + const uint32_t EID_DATA_BYTES = static_cast(instancedIntegerData.size() * sizeof(SHInstancedIntegerData)); + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, instancedIntegerBuffer[frameIndex], instancedIntegerData.data(), EID_DATA_BYTES, + BuffUsage::eVertexBuffer + ); + // - Material Properties Buffer + rebuildMaterialBuffers(frameIndex, descPool); + + // Mark this frame as no longer dirty + isDirty[frameIndex] = false; + } + + /*---------------------------------------------------------------------------------*/ + /* SHBatch - Usage Functions */ + /*---------------------------------------------------------------------------------*/ + void SHBatch::Draw(Handle cmdBuffer, uint32_t frameIndex) + { + if (frameIndex >= SHGraphicsConstants::NUM_FRAME_BUFFERS) + { + SHLOG_WARNING("[SHBatch] Attempted to draw a batch with an invalid frame index."); + return; + } + + // Bind all required objects before drawing + static std::array dynamicOffset{ 0 }; + cmdBuffer->BindPipeline(pipeline); + cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::TRANSFORM, transformDataBuffer[frameIndex], 0); + cmdBuffer->BindVertexBuffer(SHGraphicsConstants::VertexBufferBindings::INTEGER_DATA, instancedIntegerBuffer[frameIndex], 0); + if (matPropsDescSet[frameIndex]) + { + cmdBuffer->BindDescriptorSet + ( + matPropsDescSet[frameIndex], + SH_PIPELINE_TYPE::GRAPHICS, + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + dynamicOffset + ); + } + cmdBuffer->DrawMultiIndirect(drawDataBuffer[frameIndex], static_cast(drawData.size())); + } + + /*---------------------------------------------------------------------------------*/ + /* SHBatch - Helper Functions */ + /*---------------------------------------------------------------------------------*/ + void SHBatch::setAllDirtyFlags() + { + for (bool& dirt : isDirty) + dirt = true; + isCPUBuffersDirty = true; + } + + void SHBatch::rebuildMaterialBuffers(uint32_t frameIndex, Handle descPool) + { + if (matPropsData) + { + SHVkUtil::EnsureBufferAndCopyHostVisibleData + ( + device, matPropsBuffer[frameIndex], matPropsData.get(), static_cast(matPropsDataSize), + vk::BufferUsageFlagBits::eStorageBuffer + ); + + if (!matPropsDescSet[frameIndex]) + { + matPropsDescSet[frameIndex] = descPool->Allocate + ( + { SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE] }, + { 0 } + ); + } + std::array, 1> bufferList = { matPropsBuffer[frameIndex] }; + matPropsDescSet[frameIndex]->ModifyWriteDescBuffer + ( + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA, + bufferList, + 0, static_cast(matPropsDataSize) + ); + matPropsDescSet[frameIndex]->UpdateDescriptorSetBuffer + ( + SHGraphicsConstants::DescriptorSetIndex::PER_INSTANCE, + SHGraphicsConstants::DescriptorSetBindings::BATCHED_PER_INST_DATA + ); + } + } + } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h index fa18fad5..de137b28 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHBatch.h @@ -23,6 +23,7 @@ of DigiPen Institute of Technology is prohibited. #include "Math/SHMatrix.h" #include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" #include "ECS_Base/SHECSMacros.h" +#include "Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h" namespace SHADE { @@ -79,7 +80,7 @@ namespace SHADE void Clear(); void UpdateMaterialBuffer(uint32_t frameIndex, Handle descPool); void UpdateTransformBuffer(uint32_t frameIndex); - void UpdateEIDBuffer(uint32_t frameIndex); + void UpdateInstancedIntegerBuffer(uint32_t frameIndex); void Build(Handle device, Handle descPool, uint32_t frameIndex) ; void Draw(Handle cmdBuffer, uint32_t frameIndex); @@ -111,7 +112,7 @@ namespace SHADE // CPU Buffers std::vector drawData; std::vector transformData; - std::vector eidData; + std::vector instancedIntegerData; std::unique_ptr matPropsData; Byte matPropsDataSize = 0; Byte singleMatPropAlignedSize = 0; @@ -120,7 +121,7 @@ namespace SHADE // GPU Buffers TripleBuffer drawDataBuffer; TripleBuffer transformDataBuffer; - TripleBuffer eidBuffer; + TripleBuffer instancedIntegerBuffer; TripleBuffer matPropsBuffer; TripleDescSet matPropsDescSet; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp index 14f2aa76..434e7163 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Batching/SHSuperBatch.cpp @@ -85,7 +85,7 @@ namespace SHADE { batch.UpdateMaterialBuffer(frameIndex, descPool); batch.UpdateTransformBuffer(frameIndex); - batch.UpdateEIDBuffer(frameIndex); + batch.UpdateInstancedIntegerBuffer(frameIndex); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp index d8b1bad1..0f1658f3 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/GlobalData/SHGraphicsGlobalData.cpp @@ -4,6 +4,8 @@ #include "Graphics/Pipeline/SHPipelineState.h" #include "Graphics/Pipeline/SHVkPipelineLayout.h" #include "Graphics/Descriptors/SHVkDescriptorSetLayout.h" +#include "Graphics/MiddleEnd/Lights/SHLightData.h" +#include "Tools/SHUtilities.h" namespace SHADE { @@ -45,16 +47,35 @@ namespace SHADE // For global data (generic data and textures) Handle staticGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::STATIC_GLOBALS,{ genericDataBinding, texturesBinding }); - SHVkDescriptorSetLayout::Binding lightBinding + std::vector lightBindings{}; + for (uint32_t i = 0; i < SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES); ++i) { - .Type = vk::DescriptorType::eStorageBufferDynamic, - .Stage = vk::ShaderStageFlagBits::eFragment, - .BindPoint = SHGraphicsConstants::DescriptorSetBindings::LIGHTS_DATA, - .DescriptorCount = 1, - }; + lightBindings.push_back (SHVkDescriptorSetLayout::Binding + { + .Type = vk::DescriptorType::eStorageBufferDynamic, + .Stage = vk::ShaderStageFlagBits::eFragment, + .BindPoint = i, + .DescriptorCount = 1, + }); + } + //SHVkDescriptorSetLayout::Binding pointLightBinding + //{ + // .Type = vk::DescriptorType::eStorageBufferDynamic, + // .Stage = vk::ShaderStageFlagBits::eFragment, + // .BindPoint = SHGraphicsConstants::DescriptorSetBindings::POINT_LIGHT_DATA, + // .DescriptorCount = 1, + //}; + + //SHVkDescriptorSetLayout::Binding spotLightBinding + //{ + // .Type = vk::DescriptorType::eStorageBufferDynamic, + // .Stage = vk::ShaderStageFlagBits::eFragment, + // .BindPoint = SHGraphicsConstants::DescriptorSetBindings::SPOT_LIGHT_DATA, + // .DescriptorCount = 1, + //}; // For Dynamic global data (lights) - Handle dynamicGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, { lightBinding }); + Handle dynamicGlobalLayout = logicalDevice->CreateDescriptorSetLayout(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, lightBindings); SHVkDescriptorSetLayout::Binding cameraDataBinding { @@ -94,7 +115,7 @@ namespace SHADE defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Normals at binding 2 defaultVertexInputState.AddBinding(false, false, { SHVertexAttribute(SHAttribFormat::FLOAT_3D) }); // Tangents at binding 3 defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::MAT_4D) }); // Transform at binding 4 - 7 (4 slots) - defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_1D) }); // EID at binding 8 + defaultVertexInputState.AddBinding(true, true, { SHVertexAttribute(SHAttribFormat::UINT32_2D) }); // Instanced integer data at index 8 } void SHGraphicsGlobalData::Init(Handle logicalDevice) noexcept diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h index a0457b65..4c3ba7f9 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsConstants.h @@ -94,14 +94,32 @@ namespace SHADE /***************************************************************************/ static constexpr uint32_t IMAGE_AND_SAMPLERS_DATA = 1; - /***************************************************************************/ - /*! - \brief - DescriptorSet binding for lights. + ///***************************************************************************/ + ///*! + // \brief + // DescriptorSet binding for directional lights. - */ - /***************************************************************************/ - static constexpr uint32_t LIGHTS_DATA = 0; + //*/ + ///***************************************************************************/ + //static constexpr uint32_t DIRECTIONAL_LIGHT_DATA = 0; + + ///***************************************************************************/ + ///*! + // \brief + // DescriptorSet binding for directional lights. + + //*/ + ///***************************************************************************/ + //static constexpr uint32_t POINT_LIGHT_DATA = 1; + + ///***************************************************************************/ + ///*! + // \brief + // DescriptorSet binding for directional lights. + + //*/ + ///***************************************************************************/ + //static constexpr uint32_t SPOT_LIGHT_DATA = 2; /***************************************************************************/ /*! @@ -164,7 +182,7 @@ namespace SHADE Vertex buffer bindings for the eid buffer. */ /***************************************************************************/ - static constexpr uint32_t EID = 5; + static constexpr uint32_t INTEGER_DATA = 5; }; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp index 8744b083..cd6a0d17 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.cpp @@ -36,6 +36,7 @@ of DigiPen Institute of Technology is prohibited. #include "Graphics/Images/SHVkSampler.h" #include "Assets/Asset Types/SHTextureAsset.h" #include "Graphics/MiddleEnd/Interface/SHMousePickSystem.h" +#include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" namespace SHADE { @@ -124,14 +125,17 @@ namespace SHADE shaderSourceLibrary.LoadShader(1, "TestCubeFs.glsl", SH_SHADER_TYPE::FRAGMENT, true); shaderSourceLibrary.LoadShader(2, "KirschCs.glsl", SH_SHADER_TYPE::COMPUTE, true); + shaderSourceLibrary.LoadShader(3, "PureCopyCs.glsl", SH_SHADER_TYPE::COMPUTE, true); shaderModuleLibrary.ImportFromSourceLibrary(device, shaderSourceLibrary); auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); auto greyscale = shaderModuleLibrary.GetShaderModule("KirschCs.glsl"); + auto pureCopy = shaderModuleLibrary.GetShaderModule("PureCopyCs.glsl"); cubeVS->Reflect(); cubeFS->Reflect(); greyscale->Reflect(); + pureCopy->Reflect(); } void SHGraphicsSystem::InitSceneRenderGraph(void) noexcept @@ -172,21 +176,32 @@ namespace SHADE // Initialize world render graph worldRenderGraph->Init(device, swapchain); - worldRenderGraph->AddResource("Scene Pre-Process", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); - worldRenderGraph->AddResource("Scene", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); - worldRenderGraph->AddResource("Depth Buffer", { SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL }, windowDims.first, windowDims.second, vk::Format::eD32SfloatS8Uint); - worldRenderGraph->AddResource("Entity ID", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); + worldRenderGraph->AddResource("Scene Pre-Process", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); + worldRenderGraph->AddResource("Scene", { SH_ATT_DESC_TYPE_FLAGS::COLOR, SH_ATT_DESC_TYPE_FLAGS::INPUT, SH_ATT_DESC_TYPE_FLAGS::STORAGE }, windowDims.first, windowDims.second); + worldRenderGraph->AddResource("Depth Buffer", { SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL }, windowDims.first, windowDims.second, vk::Format::eD32SfloatS8Uint); + worldRenderGraph->AddResource("Entity ID", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); + worldRenderGraph->AddResource("Light Layer Indices", { SH_ATT_DESC_TYPE_FLAGS::COLOR }, windowDims.first, windowDims.second, vk::Format::eR32Uint, 1, vk::ImageUsageFlagBits::eTransferSrc); - auto node = worldRenderGraph->AddNode("G-Buffer", { "Entity ID", "Depth Buffer", "Scene", "Scene Pre-Process"}, {}); // no predecessors + auto gBufferNode = worldRenderGraph->AddNode("G-Buffer", { "Light Layer Indices", "Entity ID", "Depth Buffer", "Scene", "Scene Pre-Process"}, {}); // no predecessors + auto gBufferSubpass = gBufferNode->AddSubpass("G-Buffer Write"); + gBufferSubpass->AddColorOutput("Scene Pre-Process"); + gBufferSubpass->AddColorOutput("Entity ID"); + gBufferSubpass->AddColorOutput("Light Layer Indices"); + gBufferSubpass->AddDepthOutput("Depth Buffer", SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL); - //First subpass to write to G-Buffer - auto gBufferWriteSubpass = node->AddSubpass("G-Buffer Write"); - gBufferWriteSubpass->AddColorOutput("Scene Pre-Process"); - gBufferWriteSubpass->AddColorOutput("Entity ID"); - gBufferWriteSubpass->AddDepthOutput("Depth Buffer", SH_ATT_DESC_TYPE_FLAGS::DEPTH_STENCIL); + //// kirsch + //auto kirschShader = shaderModuleLibrary.GetShaderModule("KirschCs.glsl"); + //gBufferNode->AddNodeCompute(kirschShader, { "Scene Pre-Process", "Scene" }); + + // copy + auto pureCopyShader = shaderModuleLibrary.GetShaderModule("PureCopyCs.glsl"); + gBufferNode->AddNodeCompute(pureCopyShader, { "Scene Pre-Process", "Scene" }); + + + auto dummyNode = worldRenderGraph->AddNode("Dummy Pass", { "Scene" }, {"G-Buffer"}); // no predecessors + auto dummySubpass = dummyNode->AddSubpass("Dummy Subpass"); + dummySubpass->AddInput("Scene"); - auto greyscale = shaderModuleLibrary.GetShaderModule("KirschCs.glsl"); - node->AddNodeCompute (greyscale, {"Scene Pre-Process", "Scene"}); // Generate world render graph worldRenderGraph->Generate(); @@ -200,7 +215,7 @@ namespace SHADE auto cubeVS = shaderModuleLibrary.GetShaderModule("TestCubeVs.glsl"); auto cubeFS = shaderModuleLibrary.GetShaderModule("TestCubeFs.glsl"); - defaultMaterial = AddMaterial(cubeVS, cubeFS, gBufferWriteSubpass); + defaultMaterial = AddMaterial(cubeVS, cubeFS, gBufferSubpass); } @@ -231,11 +246,15 @@ namespace SHADE for (uint32_t i = 0; i < swapchain->GetNumImages(); ++i) cmdPools.push_back(renderContext.GetFrameData(i).cmdPoolHdls[0]); + // Mouse picking system for the editor (Will still run with editor disabled) mousePickSystem->Init(device, cmdPools, worldRenderGraph->GetRenderGraphResource("Entity ID")); // Register the post offscreen render to the system postOffscreenRender = resourceManager.Create(); postOffscreenRender->Init(device, worldRenderGraph->GetRenderGraphResource("Scene"), descPool); + + lightingSubSystem = resourceManager.Create(); + lightingSubSystem->Init(device, descPool); } #ifdef SHEDITOR @@ -353,10 +372,12 @@ namespace SHADE // Begin recording the command buffer currentCmdBuffer->BeginRecording(); + // set viewport and scissor uint32_t w = static_cast(viewports[vpIndex]->GetWidth()); uint32_t h = static_cast(viewports[vpIndex]->GetHeight()); currentCmdBuffer->SetViewportScissor (static_cast(w), static_cast(h), w, h); + // Force set the pipeline layout currentCmdBuffer->ForceSetPipelineLayout(SHGraphicsGlobalData::GetDummyPipelineLayout(), SH_PIPELINE_TYPE::GRAPHICS); // Bind all the buffers required for meshes @@ -368,6 +389,8 @@ namespace SHADE currentCmdBuffer->BindIndexBuffer(buffer, 0); } + // Bind the descriptor set for lights + lightingSubSystem->Run(currentCmdBuffer, frameIndex); // Bind textures auto textureDescSet = texLibrary.GetTextureDescriptorSetGroup(); @@ -401,7 +424,7 @@ namespace SHADE renderers[renIndex]->UpdateDataAndBind(currentCmdBuffer, frameIndex); #endif - // Draw first + // Draw the scene renderers[renIndex]->Draw(frameIndex, descPool); // End the command buffer recording diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h index 870325ac..ae93bd78 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHGraphicsSystem.h @@ -32,6 +32,7 @@ of DigiPen Institute of Technology is prohibited. #include "../Textures/SHTextureLibrary.h" #include "../Textures/SHVkSamplerCache.h" #include "Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.h" +#include "Graphics/MiddleEnd/Lights/SHLightingSubSystem.h" namespace SHADE { @@ -351,6 +352,7 @@ namespace SHADE // Sub systems Handle mousePickSystem; Handle postOffscreenRender; + Handle lightingSubSystem; uint32_t resizeWidth; uint32_t resizeHeight; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h new file mode 100644 index 00000000..8ba5adf8 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ECS_Base/SHECSMacros.h" + +namespace SHADE +{ + struct SHInstancedIntegerData + { + EntityID eid; + uint32_t lightLayer; + }; +} diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp index 57a08d47..46126ae1 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHMousePickSystem.cpp @@ -7,6 +7,7 @@ #include "Graphics/Buffers/SHVkBuffer.h" #include "Graphics/SHVkUtil.h" #include "Graphics/MiddleEnd/Interface/SHViewport.h" +//#include "Graphics/MiddleEnd/Interface/SHInstancedIntegerData.h" namespace SHADE { @@ -53,7 +54,7 @@ namespace SHADE // wait for the copy to be done afterCopyFence->Wait(true, std::numeric_limits::max()); - pickedEID = imageDataDstBuffer->GetDataFromMappedPointer(static_cast(viewportMousePos.y) * entityIDAttachment->GetWidth() + static_cast(viewportMousePos.x)); + pickedEID = imageDataDstBuffer->GetDataFromMappedPointer(static_cast(viewportMousePos.y) * entityIDAttachment->GetWidth() + static_cast(viewportMousePos.x)); } } diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.cpp index ebce5c9e..8b41a979 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHPostOffscreenRenderSystem.cpp @@ -68,7 +68,7 @@ namespace SHADE { std::vector combinedImageSampler { - std::make_tuple(offscreenRender->GetImageView(), offscreenRenderSampler, vk::ImageLayout::eGeneral), + std::make_tuple(offscreenRender->GetImageView(), offscreenRenderSampler, vk::ImageLayout::eShaderReadOnlyOptimal), }; // Register the image view and sampler with the descriptor set. Now whenever rendering to the offscreen image is done, the descriptor set will see the change diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp index 9dcd391d..7c4d0bb9 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.cpp @@ -27,6 +27,8 @@ namespace SHADE sharedMaterial = {}; material = {}; oldMaterial = {}; + + lightLayer = 0; } void SHRenderable::OnDestroy() @@ -91,6 +93,11 @@ namespace SHADE return material; } + uint8_t SHRenderable::GetLightLayer(void) const noexcept + { + return lightLayer; + } + void SHRenderable::ResetChangedFlag() { materialChanged = false; diff --git a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h index 5312cf82..75e42d60 100644 --- a/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Interface/SHRenderable.h @@ -56,6 +56,7 @@ namespace SHADE /*-------------------------------------------------------------------------------*/ bool WasMaterialChanged() const noexcept { return materialChanged; } Handle GetPrevMaterial() const noexcept { return oldMaterial; } + uint8_t GetLightLayer (void) const noexcept; /*-------------------------------------------------------------------------------*/ /* Batcher Dispatcher Functions */ @@ -75,6 +76,7 @@ namespace SHADE Handle material; bool materialChanged = true; Handle oldMaterial; + uint8_t lightLayer; }; } 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..fd122334 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.cpp @@ -0,0 +1,123 @@ +#include "SHpch.h" +#include "SHLightComponent.h" + +namespace SHADE +{ + + + void SHLightComponent::OnCreate(void) + { + lightData.Reset(); + SetType(SH_LIGHT_TYPE::DIRECTIONAL); + indexInBuffer = std::numeric_limits::max(); + active = true; + 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(uint32_t inIndexInBuffer) noexcept +{ + bound = true; + indexInBuffer = inIndexInBuffer; + } + + void SHLightComponent::SetActive(bool flag) noexcept + { + MakeDirty(); + active = flag; + + } + + 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; + } + + uint32_t SHLightComponent::GetIndexInBuffer(void) const noexcept + { + return indexInBuffer; + } + +} 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..20ae3892 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightComponent.h @@ -0,0 +1,60 @@ +#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; + + //! If the light is active, this is true. + bool active; + + 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 (uint32_t inIndexInBuffer) noexcept; + void SetActive (bool flag) noexcept; + + + SHLightData const& GetLightData (void) const noexcept; + bool IsDirty (void) const noexcept; + bool GetBound (void) const noexcept; + uint32_t GetIndexInBuffer (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..8d9efe54 --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.cpp @@ -0,0 +1,431 @@ +#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" +#include "SHLightComponent.h" + +namespace SHADE +{ + /***************************************************************************/ + /*! + + \brief + This function takes an address in the CPU container and writes light + component data to it. What gets written depends on the light type. + + \param address + The address to write to. + + \param lightComp + The light component with the data to write from. + + \param lightType + The type of the light + + \return + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::WriteLightToAddress(void* address, SHLightComponent* lightComp) noexcept + { + auto const& lightData = lightComp->GetLightData(); + switch (lightData.type) + { + case SH_LIGHT_TYPE::DIRECTIONAL: + { + SHDirectionalLightData* lightPtr = reinterpret_cast(address); + + lightPtr->cullingMask = lightData.cullingMask; + lightPtr->direction = lightData.direction; + lightPtr->diffuseColor = lightData.diffuseColor; + break; + } + case SH_LIGHT_TYPE::POINT: + break; + case SH_LIGHT_TYPE::SPOT: + break; + case SH_LIGHT_TYPE::NUM_TYPES: + break; + default: + break; + + } + } + + /***************************************************************************/ + /*! + + \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; + + // boilerplate + intermediateData = nullptr; + + // 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 the capacity. + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::Expand(Handle logicalDevice) noexcept + { + if (lightDataAlignmentSize == 0) + { + SHLOG_ERROR ("One of the types of lights have not been accounted for. Make sure lightDataAlignmentSize is not nullptr."); + return; + } + + // we want to wait for the command buffers to finish using the buffers first + logicalDevice->WaitIdle(); + + // First time we are initializing lights + if (intermediateData == nullptr) + { + // max lights should start of at STARTING_NUM_LIGHTS lights + maxLights = STARTING_NUM_LIGHTS; + numLights = 0; + + // Initialize the data for lights + intermediateData = std::make_unique(lightDataAlignmentSize * maxLights); + + // We want to initialize 3 times the amount of data required. + dataBuffer = logicalDevice->CreateBuffer(maxLights * lightDataAlignmentSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, nullptr, maxLights * lightDataAlignmentSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, 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_MAX_LIGHTS = maxLights; + + // before we increase the number of lights, create space to store old data. + std::unique_ptr oldData = std::make_unique(lightDataAlignmentSize * OLD_MAX_LIGHTS); + + // copy data over. + std::memcpy (oldData.get(), intermediateData.get(), lightDataAlignmentSize * OLD_MAX_LIGHTS); + + // now we start to expand.... + + // double space for lights + maxLights *= 2; + + // destroy old data and initialize container for double the amount of data. + intermediateData = std::make_unique(lightDataAlignmentSize * maxLights); + + // copy old data to new container + std::memcpy(intermediateData.get(), oldData.get(), lightDataAlignmentSize * OLD_MAX_LIGHTS); + + // Resize the GPU buffer. TODO: Replace with Resize no copy here + dataBuffer->ResizeReplace(maxLights * lightDataAlignmentSize * SHGraphicsConstants::NUM_FRAME_BUFFERS, oldData.get(), lightDataAlignmentSize * OLD_MAX_LIGHTS); + } + + + } + + /***************************************************************************/ + /*! + + \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 sizeof(SHDirectionalLightData); + case SH_LIGHT_TYPE::POINT: + return 4; + case SH_LIGHT_TYPE::SPOT: + // TOOD: Change after creating spot light struct + return 4; + case SH_LIGHT_TYPE::NUM_TYPES: + default: + return 4; + + } + } + + + Handle SHLightingSubSystem::PerTypeData::GetDataBuffer(void) const noexcept + { + return dataBuffer; + } + + + uint32_t SHLightingSubSystem::PerTypeData::GetAlignmentSize(void) const noexcept + { + return lightDataAlignmentSize; + } + + uint32_t SHLightingSubSystem::PerTypeData::GetNumLights(void) const noexcept + { + return numLights; + } + + uint32_t SHLightingSubSystem::PerTypeData::GetMaxLights(void) const noexcept + { + return maxLights; + } + + + /***************************************************************************/ + /*! + + \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 to hold the new light. If the buffer is too small, expand + it. + + \param lightComp + The light component to add. + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::AddLight(Handle logicalDevice, SHLightComponent* unboundLight, bool expanded) noexcept + { + if (unboundLight) + { + // capacity is full + if (numLights == maxLights) + { + // expand first + Expand(logicalDevice); + + expanded = true; + } + + // Now that the container is big enough, bind the new light + + // Get address of write location + void* writeLocation = reinterpret_cast(intermediateData.get()) + (lightDataAlignmentSize * numLights); + + // Write the light data to address + WriteLightToAddress(writeLocation, unboundLight); + + // Set the light component to be bound to that location + unboundLight->SetBound(numLights); + + // Increase light count + ++numLights; + } + } + + /***************************************************************************/ + /*! + + \brief + Modify the data at a specific light address. + + \param lightComp + The light component to write. + + */ + /***************************************************************************/ + void SHLightingSubSystem::PerTypeData::ModifyLight(SHLightComponent* lightComp) noexcept + { + void* writeLocation = reinterpret_cast(intermediateData.get()) + (lightDataAlignmentSize * lightComp->GetIndexInBuffer()); + WriteLightToAddress(writeLocation, lightComp); + } + + void SHLightingSubSystem::PerTypeData::WriteToGPU(uint32_t frameIndex) noexcept + { + if (intermediateData) + { + // we want to write to the offset of the current frame + dataBuffer->WriteToMemory(intermediateData.get(), lightDataAlignmentSize * numLights, 0, lightDataAlignmentSize * maxLights * frameIndex); + } + } + + /***************************************************************************/ + /*! + + \brief + Update descriptor sets. We want to call this every time we expand buffers. + + \param binding + The binding in the set we want to update. + + */ + /***************************************************************************/ + 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() * perTypeData[binding].GetMaxLights()); + + lightingDataDescSet->UpdateDescriptorSetBuffer(SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, binding); + } + + /***************************************************************************/ + /*! + + \brief + Computes dynamic offsets. + + */ + /***************************************************************************/ + void SHLightingSubSystem::ComputeDynamicOffsets(void) noexcept + { + for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) + { + for (uint32_t j = 0; j < dynamicOffsets.size(); ++j) + { + auto const& typeData = perTypeData[j]; + { + dynamicOffsets[i][j] = j * typeData.GetAlignmentSize() * typeData.GetMaxLights(); + } + } + } + } + + /***************************************************************************/ + /*! + + \brief + Initializes per light type data. This includes buffers and descriptor + sets. + + + */ + /***************************************************************************/ + void SHLightingSubSystem::Init(Handle device, Handle descPool) noexcept + { + SHComponentManager::CreateComponentSparseSet(); + + logicalDevice = device; + uint32_t constexpr NUM_LIGHT_TYPES = SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES); + + std::vector variableSizes{ NUM_LIGHT_TYPES }; + std::fill (variableSizes.begin(), variableSizes.end(), 1); + + // Create the descriptor set + lightingDataDescSet = descPool->Allocate({SHGraphicsGlobalData::GetDescSetLayouts()[SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS]}, variableSizes); + + + for (uint32_t i = 0; i < NUM_LIGHT_TYPES; ++i) + { + // initialize all the data first. We add more lights here as we add more types. + perTypeData[i].InitializeData(logicalDevice, static_cast(i)); + UpdateDescSet(i); + } + + for (uint32_t i = 0; i < SHGraphicsConstants::NUM_FRAME_BUFFERS; ++i) + { + dynamicOffsets[i].resize(NUM_LIGHT_TYPES); + } + } + + /***************************************************************************/ + /*! + + \brief + Loops through every single light component and checks for dirty light + data. If light data is dirty, rewrite to the CPU container. We also want + to bind the descriptor set for the light data. + + */ + /***************************************************************************/ + void SHLightingSubSystem::Run(Handle cmdBuffer, uint32_t frameIndex) noexcept + { + auto& lightComps = SHComponentManager::GetDense(); + bool expanded = false; + for (auto& light : lightComps) + { + auto enumValue = SHUtilities::ToUnderlying(light.GetLightData().type); + + // 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()) + { + perTypeData[enumValue].AddLight(logicalDevice, &light, expanded); + } + + // if there was modification to the light data + if (light.IsDirty()) + { + // Write the data to the CPU + perTypeData[enumValue].ModifyLight(&light); + + // Light is now updated in the container + light.ClearDirtyFlag(); + } + } + + // Write data to GPU + for (auto& data : perTypeData) + { + data.WriteToGPU(frameIndex); + } + + // If any of the buffers got expanded, the descriptor set is invalid because the expanded buffer + // is a new buffer. If some expansion was detected, update descriptor sets. + if (expanded) + { + uint32_t constexpr NUM_LIGHT_TYPES = SHUtilities::ToUnderlying(SH_LIGHT_TYPE::NUM_TYPES); + for (uint32_t i = 0; i < NUM_LIGHT_TYPES; ++i) + { + UpdateDescSet(i); + } + } + + // compute dynamic offsets. We don't actually have to compute every frame but its pretty lightweight, + // so we do it anyway. #NoteToSelf: if at any point it affects performance, do a check before computing. + ComputeDynamicOffsets(); + + // Bind descriptor set (We bind at an offset because the buffer holds NUM_FRAME_BUFFERS sets of data). + cmdBuffer->BindDescriptorSet(lightingDataDescSet, SH_PIPELINE_TYPE::GRAPHICS, SHGraphicsConstants::DescriptorSetIndex::DYNAMIC_GLOBALS, {dynamicOffsets[frameIndex]}); + + } + + /***************************************************************************/ + /*! + + \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..e5336b4e --- /dev/null +++ b/SHADE_Engine/src/Graphics/MiddleEnd/Lights/SHLightingSubSystem.h @@ -0,0 +1,124 @@ +#pragma once + +#include "Resource/SHHandle.h" +#include "Math/Vector/SHVec3.h" +#include "Math/Vector/SHVec4.h" +#include "SHLightData.h" +#include "Graphics/MiddleEnd/Interface/SHGraphicsConstants.h" + +namespace SHADE +{ + class SHVkLogicalDevice; + class SHVkDescriptorPool; + class SHVkDescriptorSetGroup; + class SHVkDescriptorSetLayout; + class SHVkBuffer; + class SHLightComponent; + class SHVkCommandBuffer; + + // 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; + + //! Represents if the light is active or not + uint32_t active; + + //! 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 SH_API SHLightingSubSystem + { + private: + + class PerTypeData + { + private: + /*-----------------------------------------------------------------------*/ + /* STATIC MEMBER VARIABLES */ + /*-----------------------------------------------------------------------*/ + static constexpr uint32_t STARTING_NUM_LIGHTS = 20; + + /*-----------------------------------------------------------------------*/ + /* PRIVATE MEMBER VARIABLES */ + /*-----------------------------------------------------------------------*/ + //! Capacity of the container. + uint32_t maxLights; + + //! 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; + + //! 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. Note this will store only 1 copy of all the lights, compared + //! to the GPU that stores NUM_FRAME_BUFFERS copies. + std::unique_ptr intermediateData; + + void WriteLightToAddress (void* address, SHLightComponent* lightComp) noexcept; + public: + /*-----------------------------------------------------------------------*/ + /* PUBLIC MEMBER FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void InitializeData (Handle logicalDevice, SH_LIGHT_TYPE type) noexcept; + void Expand (Handle logicalDevice) noexcept; + void AddLight (Handle logicalDevice, SHLightComponent* unboundLight, bool expanded) noexcept; + void ModifyLight (SHLightComponent* lightComp) noexcept; + void WriteToGPU (uint32_t frameIndex) noexcept; + + /*-----------------------------------------------------------------------*/ + /* GETTERS */ + /*-----------------------------------------------------------------------*/ + static uint32_t GetLightTypeSize (SH_LIGHT_TYPE type) noexcept; + Handle GetDataBuffer (void) const noexcept; + uint32_t GetAlignmentSize (void) const noexcept; + uint32_t GetNumLights (void) const noexcept; + uint32_t GetMaxLights (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; + + //! Container to store dynamic offsets for binding descriptor sets + std::array, static_cast(SHGraphicsConstants::NUM_FRAME_BUFFERS)> dynamicOffsets; + + /*-----------------------------------------------------------------------*/ + /* PRIVATE MEMBER FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void UpdateDescSet (uint32_t binding) noexcept; + void ComputeDynamicOffsets (void) noexcept; + + public: + + /*-----------------------------------------------------------------------*/ + /* PUBLIC MEMBER FUNCTIONS */ + /*-----------------------------------------------------------------------*/ + void Init (Handle device, Handle descPool) noexcept; + void Run (Handle cmdBuffer, uint32_t frameIndex) noexcept; + void Exit (void) noexcept; + + }; +} diff --git a/SHADE_Engine/src/Graphics/Pipeline/SHPipelineState.cpp b/SHADE_Engine/src/Graphics/Pipeline/SHPipelineState.cpp index f8934bf6..c7ada11f 100644 --- a/SHADE_Engine/src/Graphics/Pipeline/SHPipelineState.cpp +++ b/SHADE_Engine/src/Graphics/Pipeline/SHPipelineState.cpp @@ -142,6 +142,12 @@ namespace SHADE case SHAttribFormat::UINT32_1D: return std::make_tuple(1, 4, vk::Format::eR32Uint); + case SHAttribFormat::UINT32_2D: + return std::make_tuple(1, 8, vk::Format::eR32G32Uint); + case SHAttribFormat::UINT32_3D: + return std::make_tuple(1, 12, vk::Format::eR32G32B32Uint); + case SHAttribFormat::UINT32_4D: + return std::make_tuple(1, 16, vk::Format::eR32G32B32A32Uint); } return std::make_tuple(0, 0, vk::Format::eR32Sfloat); } diff --git a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp index 4684419a..93be2413 100644 --- a/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp +++ b/SHADE_Engine/src/Graphics/RenderGraph/SHRenderGraph.cpp @@ -142,6 +142,9 @@ namespace SHADE attDesc.loadOp = vk::AttachmentLoadOp::eLoad; predAttDesc.storeOp = vk::AttachmentStoreOp::eStore; + attDesc.stencilLoadOp = vk::AttachmentLoadOp::eLoad; + attDesc.stencilStoreOp = vk::AttachmentStoreOp::eStore; + // TODO: Stencil load and store // When an image is done being used in a renderpass, the image layout will end up being the finalLayout diff --git a/SHADE_Engine/src/Graphics/SHVkUtil.cpp b/SHADE_Engine/src/Graphics/SHVkUtil.cpp index cf486a7a..8b21e7d1 100644 --- a/SHADE_Engine/src/Graphics/SHVkUtil.cpp +++ b/SHADE_Engine/src/Graphics/SHVkUtil.cpp @@ -51,6 +51,18 @@ namespace SHADE case vk::Format::eR32Uint: case vk::Format::eR32Sfloat: return 4; + case vk::Format::eR32G32Sint: + case vk::Format::eR32G32Uint: + case vk::Format::eR32G32Sfloat: + return 8; + case vk::Format::eR32G32B32Sint: + case vk::Format::eR32G32B32Uint: + case vk::Format::eR32G32B32Sfloat: + return 12; + case vk::Format::eR32G32B32A32Sint: + case vk::Format::eR32G32B32A32Uint: + case vk::Format::eR32G32B32A32Sfloat: + return 16; } return 0; } diff --git a/SHADE_Engine/src/Graphics/VertexDescriptors/SHVertexAttribute.h b/SHADE_Engine/src/Graphics/VertexDescriptors/SHVertexAttribute.h index b216f5f4..b7191c21 100644 --- a/SHADE_Engine/src/Graphics/VertexDescriptors/SHVertexAttribute.h +++ b/SHADE_Engine/src/Graphics/VertexDescriptors/SHVertexAttribute.h @@ -22,6 +22,9 @@ namespace SHADE // integer formats UINT32_1D, + UINT32_2D, + UINT32_3D, + UINT32_4D, }; struct SHVertexAttribute 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 diff --git a/TempShaderFolder/PureCopyCs.glsl b/TempShaderFolder/PureCopyCs.glsl new file mode 100644 index 00000000..89da6dd9 --- /dev/null +++ b/TempShaderFolder/PureCopyCs.glsl @@ -0,0 +1,67 @@ +//#version 450 +// +//layout(local_size_x = 16, local_size_y = 16) in; +//layout(set = 4, binding = 0, rgba8) uniform image2D targetImage; +// +// +//void main() +//{ +// ivec2 imageSize = imageSize (targetImage); +// +// if (gl_GlobalInvocationID.x >= imageSize.x && gl_GlobalInvocationID.y >= imageSize.y) +// return; +// +// // load the image +// vec4 color = imageLoad (targetImage, ivec2 (gl_GlobalInvocationID)); +// +// // get the average +// float average = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; +// +// // store result into result image +// imageStore(targetImage, ivec2(gl_GlobalInvocationID), vec4(average, average, average, 1.0f)); +// +//} +// +// +// +// + +/* Start Header *****************************************************************/ + +/*! \file (e.g. kirsch.comp) + + \author William Zheng, william.zheng, 60001906. Brandon Mak, brandon.hao 390003920. + + \par william.zheng\@digipen.edu. brandon.hao\@digipen.edu. + + \date Sept 20, 2022 + + \brief Copyright (C) 20xx 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. */ + + /* End Header *******************************************************************/ + +#version 450 + + +layout(local_size_x = 16, local_size_y = 16) in; +layout(set = 4, binding = 0, rgba8) uniform image2D inputImage; +layout(set = 4, binding = 1, rgba8) uniform image2D targetImage; + + +void main() +{ + // convenient variables + ivec2 globalThread = ivec2(gl_GlobalInvocationID); + + vec3 color = imageLoad (inputImage, globalThread).rgb; + + // store result into result image + imageStore(targetImage, ivec2(gl_GlobalInvocationID.xy), vec4(color, 1.0f)); + +} + + + + diff --git a/TempShaderFolder/PureCopyCs.spv b/TempShaderFolder/PureCopyCs.spv new file mode 100644 index 00000000..bf967bbf Binary files /dev/null and b/TempShaderFolder/PureCopyCs.spv differ diff --git a/TempShaderFolder/TestCubeFs.glsl b/TempShaderFolder/TestCubeFs.glsl index 18890c92..4dc6deca 100644 --- a/TempShaderFolder/TestCubeFs.glsl +++ b/TempShaderFolder/TestCubeFs.glsl @@ -23,6 +23,7 @@ layout(location = 2) flat in struct { int materialIndex; uint eid; + uint lightLayerIndex; } In2; //layout (set = 0, binding = ) @@ -35,6 +36,7 @@ layout (set = 3, binding = 0) buffer MaterialProperties // For materials layout(location = 0) out vec4 outColor; layout(location = 1) out uint outEntityID; +layout(location = 2) out uint lightLayerIndices; void main() { @@ -42,5 +44,6 @@ void main() MatProp.data[In2.materialIndex].color / MatProp.data[In2.materialIndex].alpha; outEntityID = In2.eid; + lightLayerIndices = In2.lightLayerIndex; //outColor = vec4 (1.0f); } \ No newline at end of file diff --git a/TempShaderFolder/TestCubeFs.spv b/TempShaderFolder/TestCubeFs.spv index bde62136..9955abe5 100644 Binary files a/TempShaderFolder/TestCubeFs.spv and b/TempShaderFolder/TestCubeFs.spv differ diff --git a/TempShaderFolder/TestCubeVs.glsl b/TempShaderFolder/TestCubeVs.glsl index b7453f13..7b58d1cf 100644 --- a/TempShaderFolder/TestCubeVs.glsl +++ b/TempShaderFolder/TestCubeVs.glsl @@ -3,12 +3,13 @@ //#include "ShaderDescriptorDefinitions.glsl" + layout(location = 0) in vec3 aVertexPos; layout(location = 1) in vec2 aUV; layout(location = 2) in vec3 aNormal; layout(location = 3) in vec3 aTangent; layout(location = 4) in mat4 worldTransform; -layout(location = 8) in uint eid; +layout(location = 8) in uvec2 integerData; layout(location = 0) out struct @@ -23,6 +24,7 @@ layout(location = 2) out struct { int materialIndex; uint eid; + uint lightLayerIndex; } Out2; @@ -36,7 +38,8 @@ void main() { Out.uv = aUV; Out2.materialIndex = gl_InstanceIndex; - Out2.eid = eid; + Out2.eid = integerData[0]; + Out2.lightLayerIndex = integerData[1]; gl_Position = cameraData.vpMat * worldTransform * vec4 (aVertexPos, 1.0f); Out.vertColor = vec4 (aVertexPos, 1.0f); } \ No newline at end of file diff --git a/TempShaderFolder/TestCubeVs.spv b/TempShaderFolder/TestCubeVs.spv index 1662cfbd..731e31d1 100644 Binary files a/TempShaderFolder/TestCubeVs.spv and b/TempShaderFolder/TestCubeVs.spv differ