Files
o3de/Gems/SVOGI/Code/Source/SVOGISystemComponent.cpp
T
2021-03-08 14:30:57 -08:00

449 lines
17 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "SVOGI_precompiled.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include "SVOGISystemComponent.h"
//Needed for gEnv
#include <ISystem.h>
#include "SvoBrick.h"
namespace SVOGI
{
//Free functions to be called by command callbacks.
void ResetData([[maybe_unused]] IConsoleCmdArgs* args)
{
SVOGIRequestBus::Broadcast(&SVOGIRequests::ResetData);
}
void ResetGpuData([[maybe_unused]] IConsoleCmdArgs* args)
{
SVOGIRequestBus::Broadcast(&SVOGIRequests::ResetGpuData);
}
void ToggleShowVoxels([[maybe_unused]] IConsoleCmdArgs* args)
{
SVOGIRequestBus::Broadcast(&SVOGIRequests::ToggleShowVoxels);
}
//Free functions to be called by cvar callbacks.
void ResetData([[maybe_unused]] ICVar* args)
{
SVOGIRequestBus::Broadcast(&SVOGIRequests::ResetData);
}
void ResetGpuData([[maybe_unused]] ICVar* args)
{
SVOGIRequestBus::Broadcast(&SVOGIRequests::ResetGpuData);
}
void SVOGISystemComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
{
serialize->Class<SVOGISystemComponent, AZ::Component>()
->Version(0)
;
if (AZ::EditContext* ec = serialize->GetEditContext())
{
ec->Class<SVOGISystemComponent>("LegacySVOGI", "Provides a Legacy interface to be used by CryEngine Code")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System"))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
;
}
}
}
//////////////////////////////////////////////////////////////////////////
// Triggers an update of voxel data.
void SVOGISystemComponent::UpdateVoxelData()
{
static bool reload = true;
if (!gEnv->pConsole->GetCVar("e_GI")->GetIVal())
{
reload = true;
return;
}
AZ_TRACE_METHOD();
if (reload && (!gEnv->p3DEngine->LevelLoadingInProgress() || gEnv->IsEditor()))
{
m_svoEnv->ReconstructTree();
reload = false;
}
if (gEnv->pConsole->GetCVar("e_GI")->GetIVal())
{
if (m_svoEnv)
{
m_svoEnv->m_voxelLodCutoff = gEnv->pConsole->GetCVar("e_svoVoxelLodCutoff")->GetFVal();
//Process nodes that were marked for update.
m_svoEnv->ProcessVoxels();
//Trigger clean up of out of date voxels if room is needed.
m_svoEnv->EvictVoxels();
//Refresh Update Queue
m_svoEnv->UpdateVoxels();
}
}
}
//////////////////////////////////////////////////////////////////////////
// Triggers an update of voxel data to GPU.
void SVOGISystemComponent::UpdateRenderData()
{
if (gEnv->pConsole->GetCVar("e_GI")->GetIVal())
{
if (m_svoEnv)
{
m_svoEnv->CollectLights();
m_svoEnv->EvictGpuData();
m_svoEnv->UploadVoxels(m_svoShowVoxels);
}
}
}
//////////////////////////////////////////////////////////////////////////
// Called at framestart
void SVOGISystemComponent::OnFrameStart(const SRenderingPassInfo& passInfo)
{
if (m_svoEnv)
{
m_svoEnv->SetCamera(passInfo.GetCamera());
}
SvoEnvironment::m_currentPassFrameId = passInfo.GetMainFrameID();
}
//////////////////////////////////////////////////////////////////////////
// Gets the textures bound for GI plus lighting data.
void SVOGISystemComponent::GetSvoStaticTextures(I3DEngine::SSvoStaticTexInfo& svoInfo, PodArray<I3DEngine::SLightTI>* pLightsTI_S, PodArray<I3DEngine::SLightTI>* pLightsTI_D)
{
m_svoEnv->GetSvoStaticTextures(svoInfo, pLightsTI_S, pLightsTI_D);
}
//////////////////////////////////////////////////////////////////////////
// Generates a list of bricks that need to be updated in compute shaders.
void SVOGISystemComponent::GetSvoBricksForUpdate(PodArray<I3DEngine::SSvoNodeInfo>& arrNodeInfo, bool getDynamic)
{
if (m_svoEnv)
{
m_svoEnv->GetSvoBricksForUpdate(arrNodeInfo, getDynamic);
}
}
//////////////////////////////////////////////////////////////////////////
// Causes the GI system to free all voxel data.
void SVOGISystemComponent::ReleaseData()
{
//This is a product of how the legacy sytem worked. It just bulk freed the entire system.
//We bring up a new one right after since it was usually on level load.
//Deactivate will nuke any remaning env.
if (m_renderMutex)
{
m_renderMutex->lock();
}
//Free the svo renderer.
if (gEnv->pRenderer->GetISvoRenderer())
{
gEnv->pRenderer->GetISvoRenderer()->Release();
}
delete m_svoEnv;
//Recreate the svo renderer.
gEnv->pRenderer->GetISvoRenderer();
m_svoEnv = aznew SvoEnvironment();
if (m_renderMutex)
{
m_renderMutex->unlock();
}
}
//////////////////////////////////////////////////////////////////////////
// Register and unregister a mutex to protect assets during rendering.
void SVOGISystemComponent::RegisterMutex(AZStd::mutex* mutex)
{
if (m_renderMutex)
{
m_renderMutex->lock();
}
AZStd::mutex* temp = m_renderMutex;
m_renderMutex = mutex;
if (temp)
{
temp->unlock();
}
}
void SVOGISystemComponent::UnregisterMutex()
{
if (m_renderMutex)
{
m_renderMutex->lock();
}
AZStd::mutex* temp = m_renderMutex;
m_renderMutex = nullptr;
if (temp)
{
temp->unlock();
}
}
void SVOGISystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SVOGILegacyService"));
provided.push_back(AZ_CRC("SVOGIService"));
}
void SVOGISystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("SVOGILegacyService"));
incompatible.push_back(AZ_CRC("SVOGIService"));
}
void SVOGISystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
(void)required;
}
void SVOGISystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
(void)dependent;
}
void SVOGISystemComponent::Init()
{
m_svoEnv = nullptr;
m_renderMutex = nullptr;
m_svoShowVoxels = false;
}
void SVOGISystemComponent::Activate()
{
CrySystemEventBus::Handler::BusConnect();
SVOGILegacyRequestBus::Handler::BusConnect();
LmbrCentral::GiRegistrationBus::Handler::BusConnect();
SVOGIRequestBus::Handler::BusConnect();
}
void SVOGISystemComponent::Deactivate()
{
SVOGIRequestBus::Handler::BusDisconnect();
LmbrCentral::GiRegistrationBus::Handler::BusDisconnect();
SVOGILegacyRequestBus::Handler::BusDisconnect();
CrySystemEventBus::Handler::BusDisconnect();
}
void SVOGISystemComponent::UpsertToGi(AZ::EntityId entityId, AZ::Transform transform, AZ::Aabb worldAabb,
AZ::Data::Asset <LmbrCentral::MeshAsset > meshAsset, _smart_ptr<IMaterial> material)
{
if (m_svoEnv)
{
m_svoEnv->UpsertMesh(entityId, transform, worldAabb, meshAsset, material);
}
}
void SVOGISystemComponent::RemoveFromGi(AZ::EntityId entityId)
{
if (m_svoEnv)
{
m_svoEnv->RemoveMesh(entityId);
}
}
void SVOGISystemComponent::OnCrySystemPreInitialize([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams)
{
}
void SVOGISystemComponent::OnCrySystemInitialized([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& systemInitParams)
{
#if !defined(AZ_MONOLITHIC_BUILD)
// When module is linked dynamically, we must set our gEnv pointer.
// When module is linked statically, we'll share the application's gEnv pointer.
gEnv = system.GetGlobalEnvironment();
#endif
RegisterCVars();
gEnv->pRenderer->GetISvoRenderer();
m_svoEnv = aznew SvoEnvironment();
}
void SVOGISystemComponent::OnCrySystemShutdown([[maybe_unused]] ISystem& system)
{
UnregisterCVars();
#if !defined(AZ_MONOLITHIC_BUILD)
gEnv = nullptr;
#endif
if (gEnv && gEnv->pRenderer && gEnv->pRenderer->GetISvoRenderer())
{
gEnv->pRenderer->GetISvoRenderer()->Release();
}
delete m_svoEnv;
m_svoEnv = nullptr;
}
void SVOGISystemComponent::ResetData()
{
if (m_renderMutex)
{
m_renderMutex->lock();
}
if (m_svoEnv)
{
m_svoEnv->ReconstructTree();
}
if (m_renderMutex)
{
m_renderMutex->unlock();
}
}
void SVOGISystemComponent::ResetGpuData()
{
if (m_renderMutex)
{
m_renderMutex->lock();
}
if (m_svoEnv && m_svoEnv->m_svoRoot)
{
m_svoEnv->m_brickUpdateQueue.clear();
m_svoEnv->m_svoRoot->EvictGpuData(0, true);
m_svoEnv->DeallocateTexturePools();
m_svoEnv->AllocateTexturePools();
}
if (m_renderMutex)
{
m_renderMutex->unlock();
}
}
void SVOGISystemComponent::ToggleShowVoxels()
{
m_svoShowVoxels = m_svoShowVoxels ? false : true;
}
void SVOGISystemComponent::RegisterCVars()
{
// UI parameters
REGISTER_CVAR2_CB("e_svoTI_Active", &m_svoTI_Active, 0, VF_NULL,
"Activates voxel GI for the level (experimental feature)", &SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_IntegrationMode", &m_svoTI_IntegrationMode, 0, VF_EXPERIMENTAL,
"GI computations may be used in several ways:\n"
"0 = AO + Sun bounce\n"
" Large scale ambient occlusion (static) modulates (or replaces) default ambient lighting\n"
" Single light bounce (fully real-time) is supported for sun and (with limitations) for projectors \n"
" This mode takes less memory (only opacity is voxelized) and works acceptable on consoles\n"
"1 = Diffuse GI mode (experimental)\n"
" GI completely replaces default diffuse ambient lighting\n"
" Two indirect light bounces are supported for sun and semi-static lights (use '_TI' in light name)\n"
" Single fully dynamic light bounce is supported for projectors (use '_TI_DYN' in light name)\n"
" Default ambient specular is modulated by intensity of diffuse GI\n"
"2 = Full GI mode (very experimental)\n"
" Both ambient diffuse and ambient specular lighting is computed by voxel cone tracing\n"
" This mode works fine only on good modern PC",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_InjectionMultiplier", &m_svoTI_InjectionMultiplier, 0, VF_NULL,
"Modulates light injection (controls the intensity of bounce light)",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_NumberOfBounces", &m_svoTI_NumberOfBounces, 0, VF_EXPERIMENTAL,
"Maximum number of indirect bounces (from 0 to 2)\nFirst indirect bounce is completely dynamic\nThe rest of the bounces are cached in SVO and mostly static\nModifing this cvar will trigger cpu to gpu data uploads.",
&SVOGI::ResetData);
REGISTER_CVAR2("e_svoTI_Saturation", &m_svoTI_Saturation, 0, VF_NULL,
"Controls color saturation of propagated light");
REGISTER_CVAR2_CB("e_svoTI_DiffuseConeWidth", &m_svoTI_DiffuseConeWidth, 0, VF_EXPERIMENTAL,
"Controls wideness of diffuse cones\nWider cones work faster but may cause over-occlusion and more light leaking",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_ConeMaxLength", &m_svoTI_ConeMaxLength, 0, VF_NULL,
"Maximum length of the tracing rays (in meters)\nShorter rays work faster",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_AmbientOffsetRed", &m_svoTI_AmbientOffsetRed, 1, VF_NULL,
"Ambient offset color for use with GI (Red Channel)",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_AmbientOffsetGreen", &m_svoTI_AmbientOffsetGreen, 1, VF_NULL,
"Ambient offset color for use with GI (Green Channel)",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_AmbientOffsetBlue", &m_svoTI_AmbientOffsetBlue, 1, VF_NULL,
"Ambient offset color for use with GI (Blue Channel)",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoTI_AmbientOffsetBias", &m_svoTI_AmbientOffsetBias, .1, VF_NULL,
"Controls the amount of ambiant offset bias in the scene",
&SVOGI::ResetData);
REGISTER_CVAR2("e_svoTI_SSAOAmount", &m_svoTI_SSAOAmount, 0, VF_EXPERIMENTAL,
"Allows to scale down SSAO (SSDO) amount and radius when GI is active");
REGISTER_CVAR2("e_svoTI_UseLightProbes", &m_svoTI_UseLightProbes, 0, VF_NULL,
"If enabled - environment probes lighting is multiplied with GI\nIf disabled - diffuse contribution of environment probes is ignored\nIn modes 1-2 it enables usage of global env probe for sky light instead of TOD fog color");
REGISTER_CVAR2_CB("e_svoMinNodeSize", &m_svoMinNodeSize, 4, VF_EXPERIMENTAL,
"Smallest SVO node allowed to create during level voxelization\nSmaller values helps getting more detailed lighting but may work slower and use more memory in pool\n",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoMaxNodeSize", &m_svoMaxNodeSize, 32, VF_NULL,
"Maximum SVO node size for voxelization (bigger nodes stays empty)",
&SVOGI::ResetData);
REGISTER_CVAR2_CB("e_svoMaxBricksOnCPU", &m_svoMaxBricksOnCPU, 1024 * 8, VF_NULL,
"Maximum number of voxel bricks allowed to cache on CPU side",
&SVOGI::ResetData);
REGISTER_CVAR2("e_svoMaxBrickUpdates", &m_svoMaxBrickUpdates, 48, VF_NULL,
"Limits the number of bricks uploaded from CPU to GPU per frame");
REGISTER_CVAR2("e_svoMaxVoxelUpdatesPerJob", &m_svoMaxVoxelUpdatesPerJob, 24, VF_NULL,
"Controls amount of voxels allowed to refresh every frame");
REGISTER_CVAR2("e_svoTemporalFilteringBase", &m_svoTemporalFilteringBase, .25f, VF_NULL,
"Controls amount of temporal smoothing\n0 = less noise and aliasing, 1 = less ghosting");
REGISTER_CVAR2("e_svoTemporalFilteringMinDistance", &m_svoTemporalFilteringMinDistance, .5f, VF_NULL,
"Prevent previous frame re-projection at very near range, mostly for 1p weapon and hands");
REGISTER_CVAR2("e_svoVoxelLodCutoff", &m_svoVoxelLodCutoff, 2.f, VF_NULL,
"Cutoff for voxel lod ratio. Ratio is camera distance to voxel size");
REGISTER_COMMAND("svoReset", &SVOGI::ResetData, 0, "This function forces a reset of the GI data. This is useful for dealing with legacy data.");
REGISTER_COMMAND("svoToggleShowVoxels", &SVOGI::ToggleShowVoxels, 0, "This function toggles displaying voxels on and off.");
}
void SVOGISystemComponent::UnregisterCVars()
{
UNREGISTER_CVAR("e_svoMaxBricksOnCPU");
UNREGISTER_CVAR("e_svoMaxBrickUpdates");
UNREGISTER_CVAR("e_svoMaxStreamRequest");
UNREGISTER_CVAR("e_svoMinNodeSize");
UNREGISTER_CVAR("e_svoMaxNodeSize");
UNREGISTER_CVAR("e_svoMaxAreaSize");
UNREGISTER_CVAR("e_svoMaxVoxelUpdatesPerJob");
UNREGISTER_CVAR("e_svoTemporalFilteringBase");
UNREGISTER_CVAR("e_svoTemporalFilteringMinDistance");
UNREGISTER_CVAR("e_svoVoxelLodCutoff");
UNREGISTER_CVAR("e_svoTI_IntegrationMode");
UNREGISTER_CVAR("e_svoTI_InjectionMultiplier");
UNREGISTER_CVAR("e_svoTI_NumberOfBounces");
UNREGISTER_CVAR("e_svoTI_Saturation");
UNREGISTER_CVAR("e_svoTI_DiffuseConeWidth");
UNREGISTER_CVAR("e_svoTI_ConeMaxLength");
UNREGISTER_CVAR("e_svoTI_SpecularAmplifier");
UNREGISTER_CVAR("e_svoTI_SSAOAmount");
UNREGISTER_CVAR("e_svoTI_AmbientOffsetRed");
UNREGISTER_CVAR("e_svoTI_AmbientOffsetGreen");
UNREGISTER_CVAR("e_svoTI_AmbientOffsetBlue");
UNREGISTER_CVAR("e_svoTI_AmbientOffsetBias");
}
}