Refactored Graphics #297

Merged
Xenosas1337 merged 13 commits from SP3-1-Rendering into main 2023-01-01 12:35:09 +08:00
14 changed files with 535 additions and 236 deletions
Showing only changes of commit d7754e125d - Show all commits

View File

@ -22,4 +22,5 @@ constexpr SHEventIdentifier SH_SCENE_INIT_PRE { 13 };
constexpr SHEventIdentifier SH_SCENE_INIT_POST { 14 };
constexpr SHEventIdentifier SH_SCENE_EXIT_PRE { 15 };
constexpr SHEventIdentifier SH_SCENE_EXIT_POST { 16 };
constexpr SHEventIdentifier SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT { 17 };

View File

@ -0,0 +1,14 @@
#pragma once
#include <string>
#include <initializer_list>
#include "ECS_Base/SHECSMacros.h"
namespace SHADE
{
struct SHLightEnableShadowEvent
{
//! We need to get the light component and initialize the relevant variables.
EntityID lightEntity;
};
}

View File

@ -195,7 +195,6 @@ namespace SHADE
renderGraph->AddResource("SSAO Blur", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR, SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT, SH_RENDER_GRAPH_RESOURCE_FLAGS::STORAGE }, windowDims.first, windowDims.second, vk::Format::eR8Unorm);
renderGraph->AddResource("Present", { SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR_PRESENT }, windowDims.first, windowDims.second);
/*-----------------------------------------------------------------------*/
/* MAIN NODE */
/*-----------------------------------------------------------------------*/
@ -207,7 +206,6 @@ namespace SHADE
"Normals",
"Albedo",
"Depth Buffer",
"Scene",
"SSAO",
"SSAO Blur"
},
@ -256,18 +254,41 @@ namespace SHADE
// Add another pass to blur SSAO
Handle<SHRenderGraphNodeCompute> ssaoBlurPass = gBufferNode->AddNodeCompute("SSAO Blur Step", ssaoBlurShader, { "SSAO", "SSAO Blur" });
/*-----------------------------------------------------------------------*/
/* SHADOW MAP PASS */
/*-----------------------------------------------------------------------*/
// Shadow map pass will have no resources bound at first. Lighting system will add resources to the node.
// It will initially also not have any subpasses since they will be added for each light that casts shadows.
//auto shadowMapPass = renderGraph->AddNode("Shadow Map Pass", {}, {});
/*-----------------------------------------------------------------------*/
/* DEFERRED COMPOSITE NODE */
/*-----------------------------------------------------------------------*/
// This pass will facilitate both lighting and shadows in 1 single pass.
auto deferredCompositeNode = renderGraph->AddNode("Deferred Comp Pass",
{
"Position",
"Light Layer Indices",
"Normals",
"Albedo",
"Scene",
"SSAO Blur"
},
{"G-Buffer"});
/*-----------------------------------------------------------------------*/
/* DEFERRED COMPOSITE SUBPASS INIT */
/*-----------------------------------------------------------------------*/
gBufferNode->AddNodeCompute("Deferred Composite", deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Scene" });
deferredCompositeNode->AddNodeCompute("Deferred Composite", deferredCompositeShader, { "Position", "Normals", "Albedo", "Light Layer Indices", "SSAO Blur", "Scene" });
/*-----------------------------------------------------------------------*/
/* DEBUG DRAW PASS INIT */
/*-----------------------------------------------------------------------*/
// Set up Debug Draw Passes
// - Depth Tested
auto debugDrawNodeDepth = renderGraph->AddNode("Debug Draw with Depth", { "Scene", "Depth Buffer" }, {"G-Buffer"});
auto debugDrawNodeDepth = renderGraph->AddNode("Debug Draw with Depth", { "Scene", "Depth Buffer" }, {"G-Buffer", "Deferred Comp Pass"});
auto debugDrawDepthSubpass = debugDrawNodeDepth->AddSubpass("Debug Draw with Depth", worldViewport, worldRenderer);
debugDrawDepthSubpass->AddColorOutput("Scene");
debugDrawDepthSubpass->AddDepthOutput("Depth Buffer");
@ -279,7 +300,7 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* SCREEN SPACE PASS */
/*-----------------------------------------------------------------------*/
auto screenSpaceNode = renderGraph->AddNode("Screen Space Pass", { "Scene", "Entity ID" }, {"G-Buffer", "Debug Draw" });
auto screenSpaceNode = renderGraph->AddNode("Screen Space Pass", { "Scene", "Entity ID" }, {"Deferred Comp Pass", "G-Buffer", "Debug Draw" });
auto uiSubpass = screenSpaceNode->AddSubpass("UI", worldViewport, screenRenderer);
uiSubpass->AddColorOutput("Scene");
uiSubpass->AddColorOutput("Entity ID");
@ -410,6 +431,16 @@ namespace SHADE
}
void SHGraphicsSystem::InitEvents(void) noexcept
{
std::shared_ptr<SHEventReceiverSpec<SHGraphicsSystem>> thisReceiver
{
std::make_shared<SHEventReceiverSpec<SHGraphicsSystem>>(this, &SHGraphicsSystem::ReceiveLightEnableShadowEvent)
};
ReceiverPtr receiver = std::dynamic_pointer_cast<SHEventReceiver>(thisReceiver);
SHEventManager::SubscribeTo(SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT, receiver);
}
/*---------------------------------------------------------------------------------*/
/* Constructor/Destructors */
/*---------------------------------------------------------------------------------*/
@ -694,6 +725,18 @@ namespace SHADE
renderers.erase(iter);
}
SHEventHandle SHGraphicsSystem::ReceiveLightEnableShadowEvent(SHEventPtr event) noexcept
{
// Add the shadow map resource to the graph
// link resource to node. This means linking the resource and regenerating the node's renderpass and framebuffer.
// Add a subpass to render to that shadow map
//renderGraph->GetNode ();
return event->handle;
}
Handle<SHMaterial> SHGraphicsSystem::AddMaterial(Handle<SHVkShaderModule> vertShader, Handle<SHVkShaderModule> fragShader, Handle<SHSubpass> subpass)
{
// Retrieve pipeline from pipeline storage or create if unavailable

View File

@ -35,6 +35,7 @@ of DigiPen Institute of Technology is prohibited.
#include "Camera/SHCameraDirector.h"
#include "Graphics/MiddleEnd/TextRendering/SHFontLibrary.h"
#include "Graphics/MiddleEnd/Interface/SHRenderer.h"
#include "Graphics/Events/SHGraphicsEvents.h"
namespace SHADE
{
@ -102,10 +103,7 @@ namespace SHADE
void InitMiddleEnd (void) noexcept;
void InitSubsystems (void) noexcept;
void InitBuiltInResources (void);
#ifdef SHEDITOR
void InitEditorRenderGraph (void) noexcept;
#endif
void InitEvents (void) noexcept;
public:
class SH_API BeginRoutine final : public SHSystemRoutine
@ -176,6 +174,10 @@ namespace SHADE
Handle<SHRenderer> AddRenderer(SHRenderer::PROJECTION_TYPE projectionType);
void RemoveRenderer(Handle<SHRenderer> renderer);
/*-----------------------------------------------------------------------*/
/* Light functions */
/*-----------------------------------------------------------------------*/
SHEventHandle ReceiveLightEnableShadowEvent (SHEventPtr event) noexcept;
/*-----------------------------------------------------------------------------*/
/* Material Functions */

View File

@ -1,5 +1,7 @@
#include "SHpch.h"
#include "SHLightComponent.h"
#include "Graphics/Events/SHGraphicsEvents.h"
#include "Events/SHEventManager.hpp"
namespace SHADE
{
@ -104,6 +106,21 @@ namespace SHADE
//MakeDirty();
}
void SHLightComponent::SetEnableShadow(bool flag) noexcept
{
lightData.castShadows = flag;
// If the flag is true
if (flag && lightData.shadowMapIndex == SHLightData::INVALID_SHADOW_MAP_INDEX)
{
// Create new event and broadcast it
SHLightEnableShadowEvent newEvent;
newEvent.lightEntity = GetEID();
SHEventManager::BroadcastEvent<SHLightEnableShadowEvent>(newEvent, SH_GRAPHICS_LIGHT_ENABLE_SHADOW_EVENT);
}
}
SHLightData const& SHLightComponent::GetLightData(void) const noexcept
{
return lightData;

View File

@ -25,7 +25,6 @@ namespace SHADE
////! If the light's data is already in the buffers, this will be set to true.
//bool bound;
public:
/*-----------------------------------------------------------------------*/
/* LIFECYCLE FUNCTIONS */
@ -49,6 +48,7 @@ namespace SHADE
//void Unbind (void) noexcept;
//void SetBound (uint32_t inIndexInBuffer) noexcept;
void SetStrength (float value) noexcept; // serialized
void SetEnableShadow (bool flag) noexcept;
SHLightData const& GetLightData (void) const noexcept;
@ -59,7 +59,7 @@ namespace SHADE
uint32_t const& GetCullingMask (void) const noexcept; // serialized
//bool IsDirty (void) const noexcept;
//bool GetBound (void) const noexcept;
uint32_t GetIndexInBuffer (void) const noexcept;
//uint32_t GetIndexInBuffer (void) const noexcept;
float GetStrength (void) const noexcept;
RTTR_ENABLE()
};

View File

@ -16,6 +16,12 @@ namespace SHADE
// Diffuse color set to 1
color = SHVec4::One;
// light will default not cast shadows
castShadows = false;
// shadow map index is invalid.
shadowMapIndex = INVALID_SHADOW_MAP_INDEX;
}
}

View File

@ -26,6 +26,8 @@ namespace SHADE
/***************************************************************************/
struct SHLightData
{
static constexpr uint32_t INVALID_SHADOW_MAP_INDEX = std::numeric_limits<uint32_t>::max();
//! position of the light
SHVec3 position;
@ -46,6 +48,13 @@ namespace SHADE
//! Strength of the light
float strength;
//! Whether or not the light will cast a shadow. More technically, whether or
//! not the light will result in the addition of a depth map into the render graph
//! to be used for shadow mapping calculations.
bool castShadows;
//! Index of the shadow map when it gets placed in the descriptor array of textures (that are all shadow maps).
uint32_t shadowMapIndex;
void Reset (void) noexcept;
//! TODO:

View File

@ -68,6 +68,56 @@ namespace SHADE
renderGraphStorage->graphResources->try_emplace(resourceName, resource);
}
void SHRenderGraph::RemoveResource(std::string resourceName) noexcept
{
// 1. Check if nodes are using said attachment and remove if they are
// - Check subpasses while at it and remove as well if used as attachment
// 2. Regenerate graph
// - Delete all vulkan objects first as well and clear the necessary containers
if (!renderGraphStorage->graphResources->contains(resourceName))
return;
renderGraphStorage->logicalDevice->WaitIdle();
uint64_t handleID = renderGraphStorage->graphResources->at (resourceName).GetId().Raw;
// Record nodes that will be affected
std::vector<uint32_t> affectedNodes{};
// Detach resource from all nodes if applicable
for (uint32_t i = 0; i < nodes.size(); ++i)
{
if (nodes[i]->DetachResource(resourceName, handleID))
affectedNodes.emplace_back(i);
}
// Up to this point the nodes and subpasses should have no trace of the deleted resource. Attachment
// descriptions and subpass indices should also have been reconfigured at this point. However, this
// means that the subpass descriptions and subpass dependencies need to be reconfigured.
// configure subpass dependencies and descriptions.
for (auto& affectedNode : affectedNodes)
nodes[affectedNode]->ConfigureSubpasses();
for (auto& affectedNode : affectedNodes)
nodes[affectedNode]->CreateRenderpass();
for (auto& affectedNode : affectedNodes)
nodes[affectedNode]->CreateFramebuffer();
/*
* IMPORTANT NOTES
*
* This remove resource function would be more complete if it accounted for renderpass compatibility by
* recreating the graphics pipelines stored in render graph nodes. However due to time constraints, this is left out. As such,
* pipelines that are recreated with a certain renderpass and subpass will assumed to be compatible when used in different subpasses.
*
* This function also recreates renderpasses and framebuffers so it does not account for removing of resources that are used in nodes whose
* renderpasses and framebuffers are used externally in systems like Editor with ImGui.
*/
}
void SHRenderGraph::LinkNonOwningResource(Handle<SHRenderGraph> resourceOrigin, std::string resourceName) noexcept
{
// resource to link
@ -236,140 +286,12 @@ namespace SHADE
*/
/***************************************************************************/
void SHRenderGraph::ConfigureSubpasses(void) noexcept
void SHRenderGraph::ConfigureAllSubpasses(void) noexcept
{
// For all nodes
for (auto& node : nodes)
{
// Create subpass description and dependencies based on number of subpasses
node->spDescs.resize(node->subpasses.size());
node->spDeps.resize(node->subpasses.size());
// Now we want to loop through all attachments in all subpasses in the node and query
// the resources being used. For each resource we want to query the type and record it
// in bit fields (1 bit for each subpass).
uint32_t colorRead = 0, colorWrite = 0, depthRead = 0, depthWrite = 0, inputDependencies = 0;
uint32_t i = 0;
// For all subpasses (see above description about bit field for this).
for (auto& subpass : node->subpasses)
{
// Configure subpass description
auto& desc = node->spDescs[i];
desc.pColorAttachments = subpass->colorReferences.data();
desc.colorAttachmentCount = static_cast<uint32_t>(subpass->colorReferences.size());
desc.pInputAttachments = subpass->inputReferences.data();
desc.inputAttachmentCount = static_cast<uint32_t>(subpass->inputReferences.size());
desc.pDepthStencilAttachment = subpass->depthReferences.data();
desc.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; // TODO: Just graphics for now. See if its possible to allow user defined params.
// Get reference to subpass description
auto& dep = node->spDeps[i];
// Configure subpass index for dependencies
dep.srcSubpass = (i == 0) ? VK_SUBPASS_EXTERNAL : i - 1;
dep.dstSubpass = i;
// First we want to see if the subpass has color, depth or input attachments and set bit field accordingly
if (subpass->colorReferences.size())
{
colorRead |= (1 << i);
colorWrite |= (1 << i);
}
// Same thing for depth
if (subpass->depthReferences.size())
{
depthRead |= (1 << i);
depthWrite |= (1 << i);
}
if (subpass->inputReferences.size())
inputDependencies |= (1 << i);
// Input attachments can be any type, so we need to check what type it is
for (auto& inputAtt : subpass->inputReferences)
{
auto resource = node->attResources[inputAtt.attachment];
if (resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT))
{
if (resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR) ||
resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR_PRESENT))
colorRead |= (1 << i);
else if (resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::DEPTH_STENCIL))
depthRead |= (1 << i);
}
else
{
SHLOG_ERROR("While configuring subpass, an input reference was detected but the resource to be used is not marked as SH_ATT_DESC_TYPE_FLAGS::INPUT. ");
}
}
++i;
}
// Loop through all subpasses again but this time we use the bit field to initialize
// the dependencies.
for (i = 0; i < node->subpasses.size(); ++i)
{
vk::PipelineStageFlags srcStage;
vk::PipelineStageFlags dstStage;
vk::AccessFlags srcAccess;
vk::AccessFlags dstAccess;
auto& dep = node->spDeps[i];
if (colorRead & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
srcAccess |= vk::AccessFlagBits::eColorAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eColorAttachmentRead;
}
if (colorWrite & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
srcAccess |= vk::AccessFlagBits::eColorAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead;
}
if (depthRead & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
dstStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
srcAccess |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eDepthStencilAttachmentRead;
}
if (depthWrite & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
dstStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
srcAccess |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
}
if (inputDependencies & (1 << i))
{
dstStage |= vk::PipelineStageFlagBits::eFragmentShader;
dstAccess |= vk::AccessFlagBits::eInputAttachmentRead;
}
//// If subpass of first renderpass, stage flag should be bottom of pipe
//if (&node == &nodes.front() && i == 0)
// srcStage = vk::PipelineStageFlagBits::eBottomOfPipe;
//// If subpass of last renderpass, stage flag should be bottom of pipe
//if (&node == &nodes.back() && i == node->subpasses.size() - 1)
// dstStage = vk::PipelineStageFlagBits::eTopOfPipe;
dep.srcStageMask = srcStage;
dep.dstStageMask = dstStage;
dep.srcAccessMask = srcAccess;
dep.dstAccessMask = dstAccess;
dep.srcStageMask = srcStage;
// initialize input descriptors
node->subpasses[i]->CreateInputDescriptors();
}
node->ConfigureSubpasses();
}
}
@ -604,12 +526,17 @@ namespace SHADE
{
CheckForNodeComputes();
ConfigureAttachmentDescriptions();
ConfigureSubpasses();
ConfigureAllSubpasses();
ConfigureRenderpasses();
ConfigureFramebuffers();
ConfigureSubSystems();
}
void SHRenderGraph::Regenerate(void) noexcept
{
}
/***************************************************************************/
/*!

View File

@ -51,7 +51,7 @@ namespace SHADE
/* PRIVATE MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void ConfigureAttachmentDescriptions (void) noexcept;
void ConfigureSubpasses (void) noexcept;
void ConfigureAllSubpasses (void) noexcept;
void ConfigureRenderpasses (void) noexcept;
void ConfigureSubSystems (void) noexcept;
void ConfigureFramebuffers (void) noexcept;
@ -89,13 +89,52 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* PUBLIC MEMBER FUNCTIONS */
/*-----------------------------------------------------------------------*/
void Init (std::string graphName, Handle<SHVkLogicalDevice> logicalDevice, Handle<SHVkSwapchain> swapchain, SHResourceHub* resourceHub, std::vector<Handle<SHVkCommandPool>>& cmdPools) noexcept;
void AddResource(std::string resourceName, std::initializer_list<SH_RENDER_GRAPH_RESOURCE_FLAGS> typeFlags, uint32_t w = static_cast<uint32_t>(-1), uint32_t h = static_cast<uint32_t>(-1), vk::Format format = vk::Format::eB8G8R8A8Unorm, uint8_t levels = 1, vk::ImageUsageFlagBits usageFlags = {}, vk::ImageCreateFlagBits createFlags = {});
void LinkNonOwningResource (Handle<SHRenderGraph> resourceOrigin, std::string resourceName) noexcept;
Handle<SHRenderGraphNode> AddNode (std::string nodeName, std::initializer_list<ResourceInstruction> resourceInstruction, std::initializer_list<std::string> predecessorNodes) noexcept;
void AddRenderToSwapchainNode (std::string toSwapchainResource, std::string swapchainResource, std::initializer_list<std::string> predecessorNodes, std::pair<Handle<SHVkShaderModule>, Handle<SHVkShaderModule>> shaderModules) noexcept;
void Init
(
std::string graphName,
Handle<SHVkLogicalDevice> logicalDevice,
Handle<SHVkSwapchain> swapchain, SHResourceHub* resourceHub,
std::vector<Handle<SHVkCommandPool>>& cmdPools
) noexcept;
void AddResource
(
std::string resourceName,
std::initializer_list<SH_RENDER_GRAPH_RESOURCE_FLAGS> typeFlags,
uint32_t w = static_cast<uint32_t>(-1),
uint32_t h = static_cast<uint32_t>(-1),
vk::Format format = vk::Format::eB8G8R8A8Unorm,
uint8_t levels = 1,
vk::ImageUsageFlagBits usageFlags = {},
vk::ImageCreateFlagBits createFlags = {}
);
void RemoveResource (std::string resourceName) noexcept;
void LinkNonOwningResource
(
Handle<SHRenderGraph> resourceOrigin,
std::string resourceName
) noexcept;
Handle<SHRenderGraphNode> AddNode
(
std::string nodeName,
std::initializer_list<ResourceInstruction> resourceInstruction,
std::initializer_list<std::string> predecessorNodes
) noexcept;
void AddRenderToSwapchainNode
(
std::string toSwapchainResource,
std::string swapchainResource,
std::initializer_list<std::string> predecessorNodes,
std::pair<Handle<SHVkShaderModule>,
Handle<SHVkShaderModule>> shaderModules
) noexcept;
void Generate (void) noexcept;
void Regenerate (void) noexcept;
void CheckForNodeComputes (void) noexcept;
void Execute (uint32_t frameIndex, Handle<SHVkDescriptorPool> descPool) noexcept;
void Begin (uint32_t frameIndex) noexcept;

View File

@ -26,6 +26,12 @@ namespace SHADE
/***************************************************************************/
void SHRenderGraphNode::CreateRenderpass(void) noexcept
{
if (renderpass)
{
if (renderpass)
renderpass.Free();
}
renderpass = graphStorage->logicalDevice->CreateRenderpass(attachmentDescriptions, spDescs, spDeps);
SET_VK_OBJ_NAME(graphStorage->logicalDevice, vk::ObjectType::eRenderPass, renderpass->GetVkRenderpass(), "[RenderPass] " + name);
}
@ -40,6 +46,15 @@ namespace SHADE
/***************************************************************************/
void SHRenderGraphNode::CreateFramebuffer(void) noexcept
{
if (!framebuffers.empty())
{
for (auto fbo : framebuffers)
{
if (fbo)
fbo.Free();
}
}
for (uint32_t i = 0; i < framebuffers.size(); ++i)
{
std::vector<Handle<SHVkImageView>> imageViews(attResources.size());
@ -100,6 +115,139 @@ namespace SHADE
}
}
void SHRenderGraphNode::ConfigureSubpasses(void) noexcept
{
// Create subpass description and dependencies based on number of subpasses
spDescs.resize(subpasses.size());
spDeps.resize(subpasses.size());
// Now we want to loop through all attachments in all subpasses in the node and query
// the resources being used. For each resource we want to query the type and record it
// in bit fields (1 bit for each subpass).
uint32_t colorRead = 0, colorWrite = 0, depthRead = 0, depthWrite = 0, inputDependencies = 0;
uint32_t i = 0;
// For all subpasses (see above description about bit field for this).
for (auto& subpass : subpasses)
{
// Configure subpass description
auto& desc = spDescs[i];
desc.pColorAttachments = subpass->colorReferences.data();
desc.colorAttachmentCount = static_cast<uint32_t>(subpass->colorReferences.size());
desc.pInputAttachments = subpass->inputReferences.data();
desc.inputAttachmentCount = static_cast<uint32_t>(subpass->inputReferences.size());
desc.pDepthStencilAttachment = subpass->depthReferences.data();
desc.pipelineBindPoint = vk::PipelineBindPoint::eGraphics; // TODO: Just graphics for now. See if its possible to allow user defined params.
// Get reference to subpass description
auto& dep = spDeps[i];
// Configure subpass index for dependencies
dep.srcSubpass = (i == 0) ? VK_SUBPASS_EXTERNAL : i - 1;
dep.dstSubpass = i;
// First we want to see if the subpass has color, depth or input attachments and set bit field accordingly
if (subpass->colorReferences.size())
{
colorRead |= (1 << i);
colorWrite |= (1 << i);
}
// Same thing for depth
if (subpass->depthReferences.size())
{
depthRead |= (1 << i);
depthWrite |= (1 << i);
}
if (subpass->inputReferences.size())
inputDependencies |= (1 << i);
// Input attachments can be any type, so we need to check what type it is
for (auto& inputAtt : subpass->inputReferences)
{
auto resource = attResources[inputAtt.attachment];
if (resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::INPUT))
{
if (resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR) ||
resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::COLOR_PRESENT))
colorRead |= (1 << i);
else if (resource->resourceTypeFlags & static_cast<uint32_t>(SH_RENDER_GRAPH_RESOURCE_FLAGS::DEPTH_STENCIL))
depthRead |= (1 << i);
}
else
{
SHLOG_ERROR("While configuring subpass, an input reference was detected but the resource to be used is not marked as SH_ATT_DESC_TYPE_FLAGS::INPUT. ");
}
}
++i;
}
// Loop through all subpasses again but this time we use the bit field to initialize
// the dependencies.
for (i = 0; i < subpasses.size(); ++i)
{
vk::PipelineStageFlags srcStage;
vk::PipelineStageFlags dstStage;
vk::AccessFlags srcAccess;
vk::AccessFlags dstAccess;
auto& dep = spDeps[i];
if (colorRead & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
srcAccess |= vk::AccessFlagBits::eColorAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eColorAttachmentRead;
}
if (colorWrite & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
dstStage |= vk::PipelineStageFlagBits::eColorAttachmentOutput;
srcAccess |= vk::AccessFlagBits::eColorAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead;
}
if (depthRead & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
dstStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
srcAccess |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eDepthStencilAttachmentRead;
}
if (depthWrite & (1 << i))
{
srcStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests;
dstStage |= vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests;
srcAccess |= vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dstAccess |= vk::AccessFlagBits::eDepthStencilAttachmentRead | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
}
if (inputDependencies & (1 << i))
{
dstStage |= vk::PipelineStageFlagBits::eFragmentShader;
dstAccess |= vk::AccessFlagBits::eInputAttachmentRead;
}
//// If subpass of first renderpass, stage flag should be bottom of pipe
//if (&node == &nodes.front() && i == 0)
// srcStage = vk::PipelineStageFlagBits::eBottomOfPipe;
//// If subpass of last renderpass, stage flag should be bottom of pipe
//if (&node == &nodes.back() && i == subpasses.size() - 1)
// dstStage = vk::PipelineStageFlagBits::eTopOfPipe;
dep.srcStageMask = srcStage;
dep.dstStageMask = dstStage;
dep.srcAccessMask = srcAccess;
dep.dstAccessMask = dstAccess;
dep.srcStageMask = srcStage;
// initialize input descriptors
subpasses[i]->CreateInputDescriptors();
}
}
/***************************************************************************/
/*!
@ -335,6 +483,58 @@ namespace SHADE
}
}
/***************************************************************************/
/*!
\brief
For detaching attachments from the node. The node would need to be
rebuilt but it won't be done in this function. This function just removes
any footprint of the resource in the class.
\param resourceHandleID
The handle ID of the resource.
*/
/***************************************************************************/
bool SHRenderGraphNode::DetachResource(std::string const& resourceName, uint64_t resourceHandleID) noexcept
{
if (resourceAttachmentMapping->contains(resourceHandleID))
{
// Get attachment index in the node's attachment container
auto index = resourceAttachmentMapping->at(resourceHandleID);
// remove attachment from the list of attachments
attResources.erase (attResources.begin() + index);
// remove attachment reference
attachmentDescriptions.erase (attachmentDescriptions.begin() + index);
// Remove footprint of attachment from all subpasses as well
for (auto it = subpasses.begin(); it != subpasses.end(); ++it)
{
// attempt to detach resource from subpass
(*it)->DetachResource(resourceName, index);
// If the subpass ends up having no attachments after, erase it from the node
if ((*it)->HasNoAttachments())
{
// erase from indexing
subpassIndexing.erase((*it)->GetName());
// erase from container of subpasses.
it = subpasses.erase(it);
}
}
// give existing subpasses new indices
for (uint32_t i = 0; i < subpasses.size(); ++i)
subpasses[i]->SetIndex(i);
return true;
}
return false;
}
void SHRenderGraphNode::Execute(Handle<SHVkCommandBuffer> commandBuffer, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) noexcept
{
uint32_t framebufferIndex = (framebuffers.size() > 1) ? frameIndex : 0;

View File

@ -55,7 +55,7 @@ namespace SHADE
//! Vector of subpasses
std::vector<Handle<SHSubpass>> subpasses;
//! Descriptions to pass to renderpass for renderpass creation. We want to keep this here because
//! Descriptions to pass to renderpass for renderpass creation.
std::vector<vk::SubpassDescription> spDescs;
//! Subpass dependencies for renderpass creation
@ -92,6 +92,7 @@ namespace SHADE
void CreateRenderpass(void) noexcept;
void CreateFramebuffer(void) noexcept;
void HandleResize (void) noexcept;
void ConfigureSubpasses (void) noexcept;
public:
/*-----------------------------------------------------------------------*/
@ -107,6 +108,7 @@ namespace SHADE
Handle<SHSubpass> AddSubpass(std::string subpassName, Handle<SHViewport> viewport, Handle<SHRenderer> renderer) noexcept;
Handle<SHRenderGraphNodeCompute> AddNodeCompute(std::string nodeName, Handle<SHVkShaderModule> computeShaderModule, std::initializer_list<std::string> resources, std::unordered_set<BindingAndSetHash>&& dynamicBufferBindings = {}, float numWorkGroupScale = 1.0f) noexcept;
void AddDummySubpassIfNeeded (void) noexcept;
bool DetachResource (std::string const& resourceName, uint64_t resourceHandleID) noexcept;
// TODO: RemoveSubpass()
void Execute(Handle<SHVkCommandBuffer> commandBuffer, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) noexcept;

View File

@ -68,7 +68,6 @@ namespace SHADE
, depthReferences{ std::move(rhs.depthReferences) }
, inputReferences{ std::move(rhs.inputReferences) }
, resourceAttachmentMapping{ rhs.resourceAttachmentMapping }
, descriptorSetLayout{ rhs.descriptorSetLayout }
, exteriorDrawCalls{ std::move(rhs.exteriorDrawCalls) }
, graphStorage{ rhs.graphStorage }
, inputNames{ std::move(rhs.inputNames) }
@ -105,7 +104,6 @@ namespace SHADE
depthReferences = std::move(rhs.depthReferences);
inputReferences = std::move(rhs.inputReferences);
resourceAttachmentMapping = std::move(rhs.resourceAttachmentMapping);
descriptorSetLayout = rhs.descriptorSetLayout;
exteriorDrawCalls = std::move(rhs.exteriorDrawCalls);
graphStorage = rhs.graphStorage;
inputNames = std::move(rhs.inputNames);
@ -253,6 +251,64 @@ namespace SHADE
}
}
/***************************************************************************/
/*!
\brief
Removes all footprints of a resource in the subpass.
\param resourceName
Name of resource.
\param attachmentIndex
index of attachment.
*/
/***************************************************************************/
void SHSubpass::DetachResource(std::string const& resourceName, uint32_t attachmentIndex) noexcept
{
for (uint32_t i = 0; i < colorReferences.size(); ++i)
{
if (colorReferences[i].attachment == attachmentIndex)
{
colorReferences.erase (colorReferences.begin() + i);
break;
}
}
for (uint32_t i = 0; i < depthReferences.size(); ++i)
{
if (depthReferences[i].attachment == attachmentIndex)
{
depthReferences.erase(depthReferences.begin() + i);
break;
}
}
for (uint32_t i = 0; i < inputReferences.size(); ++i)
{
if (inputReferences[i].attachment == attachmentIndex)
{
inputReferences.erase(inputReferences.begin() + i);
break;
}
}
for (uint32_t i = 0; i < inputNames.size(); ++i)
{
if (inputNames[i] == resourceName)
{
inputNames.erase(inputNames.begin() + i);
break;
}
}
}
bool SHSubpass::HasNoAttachments(void) const noexcept
{
return colorReferences.empty() && depthReferences.empty() && inputReferences.empty();
}
void SHSubpass::AddExteriorDrawCalls(ExteriorDrawCallFunction const& newDrawCall) noexcept
{
exteriorDrawCalls.push_back(newDrawCall);
@ -269,6 +325,21 @@ namespace SHADE
if (inputNames.empty())
return;
for (auto& set : inputImageDescriptorSets)
{
if (set)
set.Free();
}
if (inputDescriptorLayout)
inputDescriptorLayout.Free();
for (auto& sampler : inputSamplers)
{
if (sampler)
sampler.Free();
}
inputImageDescriptorSets.resize(SHGraphicsConstants::NUM_FRAME_BUFFERS);
std::vector<SHVkDescriptorSetLayout::Binding> bindings{};
@ -369,58 +440,23 @@ namespace SHADE
}
}
//void SHSubpass::InitComputeBarriers(void) noexcept
//{
// std::unordered_set <uint64_t> handleBarriers{};
/***************************************************************************/
/*!
// // we will have swapchainNumImages vectors of vector of barriers
// subpassComputeBarriers.resize(graphStorage->swapchain->GetNumImages());
\brief
This function is mainly used for the purposes of giving a subpass a new
index in the case another subpass in the node gets deleted, and subpasses
need to be re-indexed.
// for (auto sbCompute : subpassComputes)
// {
// // for every resource the subpass compute is using
// for (auto resource : sbCompute->resources)
// {
// // Get the resource handle
// uint64_t resourceRaw = resource.GetId().Raw;
\param index
New index of the subpass.
// // if the barrier is not registered
// if (!handleBarriers.contains(resourceRaw))
// {
// // If the resource is a swapchain image
// bool isSwapchainImage = (resource->resourceTypeFlags & static_cast<uint32_t>(SH_ATT_DESC_TYPE_FLAGS::COLOR_PRESENT));
// for (uint32_t i = 0; i < graphStorage->swapchain->GetNumImages(); ++i)
// {
// // if swapchain image, we want the index of the swapchain image, if not take base image
// uint32_t imageIndex = isSwapchainImage ? i : 0;
// // Prepare image barrier
// vk::ImageMemoryBarrier imageBarrier
// {
// .oldLayout = colorReferences[resourceAttachmentMapping->at(resource.GetId().Raw)].layout,
// .newLayout = vk::ImageLayout::eGeneral,
// .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
// .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
// .image = resource->GetImage(imageIndex)->GetVkImage(),
// .subresourceRange =
// {
// .aspectMask = resource->imageAspectFlags,
// .levelCount = resource->GetMipLevels(),
// .baseArrayLayer = 0,
// .layerCount = 1
// }
// };
// // push the barrier
// subpassComputeBarriers[i].push_back(imageBarrier);
// }
// // Image transition registered
// handleBarriers.emplace(resourceRaw);
// }
// }
// }
//}
*/
/***************************************************************************/
void SHSubpass::SetIndex(uint32_t index) noexcept
{
subpassIndex = index;
}
/***************************************************************************/
/*!

View File

@ -50,9 +50,6 @@ namespace SHADE
//!
Handle<SHSuperBatch> superBatch;
//! Descriptor set layout to hold attachments
Handle<SHVkDescriptorSetLayout> descriptorSetLayout;
//! Color attachments
std::vector<vk::AttachmentReference> colorReferences;
@ -122,6 +119,8 @@ namespace SHADE
void Execute(Handle<SHVkCommandBuffer> commandBuffer, Handle<SHVkDescriptorPool> descPool, uint32_t frameIndex) noexcept;
void HandleResize (void) noexcept;
void BindInputDescriptorSets (Handle<SHVkCommandBuffer> cmdBuffer, uint32_t setIndex, uint32_t frameIndex) const noexcept;
void DetachResource (std::string const& resourceName, uint32_t attachmentIndex) noexcept;
bool HasNoAttachments (void) const noexcept;
void Init(SHResourceHub& resourceManager) noexcept;
@ -132,6 +131,10 @@ namespace SHADE
/*-----------------------------------------------------------------------*/
/* GETTERS AND SETTERS */
/*-----------------------------------------------------------------------*/
private:
void SetIndex (uint32_t index) noexcept;
public:
Handle<SHRenderGraphNode> const& GetParentNode(void) const noexcept;
SHSubPassIndex GetIndex() const noexcept;
Handle<SHSuperBatch> GetSuperBatch(void) const noexcept;