Files
o3de/Gems/AtomTressFX/Code/Passes/HairSkinningComputePass.cpp
Adi Bar-Lev 9786d63596 Hair - Hair Gem introduction to O3DE from Atom/barlev/AtomTressFX_DevRebase (#4439)
* Hair
- First introduction of Hair gem to Atom and O3DE
- The hair technology is based off TressFX 4.1
- These are some of the areas we enhanced the original TressFX implementation:
   - Lighting model was replaced and we now use a modified Marschner model
   - Blending is done directly with the back buffer removing the silhouette of the original implementation
   - Hair depth / thickness is now calculated to remove incorrect back lighting (TT lobe in the Marschner model)
   - Thickness corrected to handle hair gaps hence introducing better light passage for the TT
   - The hair is fully integrated into the Atom pipeline and structure design
   - Usage of single shared buffer for the computer buffers reduces barriers sync overhead

Remarks:
- Collisions via SDF compute are to be introduced soon
- Improved shortcut rendering method ala Eidos Montreal to be introduced soon

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - code clean pass

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - EMFX Actor visibility implementation

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - COnnecting hair passes to Atom's MainPipeline.pass

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - adding dedicated thumbnail pipeline that does not include the hair gem

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - changed Atom shader files to allow hooking the hair to the lighting data structures

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixed a few headers to have the latest O3DE license + verification fixes

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - enabling editor component only when tool pipline is built + default texture add

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixing Linux and Android compilation builds

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - another files change to make Linux compile

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - more Linux and Android build fixes

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair

- Adding usage of fallback white texture
- Removing invalid null assignments into vectors
- Removing redundant mutex preventing deletion on some platforms

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair

- Shame: removed forgoten #pragma optimize
- Adding header complained by Android

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - removing the Hair Gem connection in the active project.

- This submission removes the connection to the active project hence allowing to run without the Gem.  Enable the passes in MainPipeline.pass and declare them again when you want to use the Gem.

Remark: the gem file PassTemplates.azasset was renamed and will be connected via code in the future to avoid the need to declare in the global pass template.
Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - registrating gem pass templates through the gem templates file (#198)

* Hair - registrating gem pass templates through the gem templates file

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - adding handler disconnect for the pass template registration.

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - PPLLIndexCounter buffer going data driven via the pass declarations (#202)

* Hair - PPLLIndexCounter buffer going data driven via the pass declaration
- Moving PPLLIndexCounter from code allocation and attachment to be data driven
- Fixed RPI typo bug that can prevent using buffers like that

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixing UI Editor (LYShine) crash (#209)

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair - resolved the multi pipeline mismatches and crashes + cleaned initialization & leftovers (#222)

* [Hair] - multiple render pipelines handling

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair] - Shut down order is handle to allow hair feature processor be deregistered only after the bootstrap component has disabled it

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair] - minor cleanups

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair] - followups from review nits

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - code fixes based on the CR remarks for the Hair merge to Dev (#248)

* Hair
- First introduction of Hair gem to Atom and O3DE
- The hair technology is based off TressFX 4.1
- These are some of the areas we enhanced the original TressFX implementation:
   - Lighting model was replaced and we now use a modified Marschner model
   - Blending is done directly with the back buffer removing the silhouette of the original implementation
   - Hair depth / thickness is now calculated to remove incorrect back lighting (TT lobe in the Marschner model)
   - Thickness corrected to handle hair gaps hence introducing better light passage for the TT
   - The hair is fully integrated into the Atom pipeline and structure design
   - Usage of single shared buffer for the computer buffers reduces barriers sync overhead

Remarks:
- Collisions via SDF compute are to be introduced soon
- Improved shortcut rendering method ala Eidos Montreal to be introduced soon

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - code clean pass

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - EMFX Actor visibility implementation

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - COnnecting hair passes to Atom's MainPipeline.pass

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - adding dedicated thumbnail pipeline that does not include the hair gem

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - changed Atom shader files to allow hooking the hair to the lighting data structures

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixed a few headers to have the latest O3DE license + verification fixes

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - enabling editor component only when tool pipline is built + default texture add

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixing Linux and Android compilation builds

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - another files change to make Linux compile

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - more Linux and Android build fixes

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair

- Adding usage of fallback white texture
- Removing invalid null assignments into vectors
- Removing redundant mutex preventing deletion on some platforms

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair

- Shame: removed forgoten #pragma optimize
- Adding header complained by Android

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - removing the Hair Gem connection in the active project.

- This submission removes the connection to the active project hence allowing to run without the Gem.  Enable the passes in MainPipeline.pass and declare them again when you want to use the Gem.

Remark: the gem file PassTemplates.azasset was renamed and will be connected via code in the future to avoid the need to declare in the global pass template.
Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - registrating gem pass templates through the gem templates file (#198)

* Hair - registrating gem pass templates through the gem templates file

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - adding handler disconnect for the pass template registration.

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - PPLLIndexCounter buffer going data driven via the pass declarations (#202)

* Hair - PPLLIndexCounter buffer going data driven via the pass declaration
- Moving PPLLIndexCounter from code allocation and attachment to be data driven
- Fixed RPI typo bug that can prevent using buffers like that

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixing UI Editor (LYShine) crash (#209)

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair - resolved the multi pipeline mismatches and crashes + cleaned initialization & leftovers (#222)

* [Hair] - multiple render pipelines handling

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair] - Shut down order is handle to allow hair feature processor be deregistered only after the bootstrap component has disabled it

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair] - minor cleanups

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* [Hair] - followups from review nits

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - last fixes based on CR remarks

Signed-off-by: Adi-Amazon <barlev@amazon.com>

* Hair - fixing AR

Signed-off-by: Adi-Amazon <barlev@amazon.com>
2021-10-04 10:46:31 -04:00

270 lines
11 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <Atom/RHI/CommandList.h>
#include <Atom/RHI/Factory.h>
#include <Atom/RHI/FrameGraphAttachmentInterface.h>
#include <Atom/RHI/FrameGraphInterface.h>
#include <Atom/RHI/PipelineState.h>
#include <Atom/RPI.Public/Base.h>
#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>
#include <Atom/RPI.Public/View.h>
#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>
// Hair Specific
#include <Rendering/HairCommon.h>
#include <Rendering/HairSharedBufferInterface.h>
#include <Rendering/HairDispatchItem.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairRenderObject.h>
#include <Passes/HairSkinningComputePass.h>
namespace AZ
{
namespace Render
{
namespace Hair
{
Data::Instance<RPI::Shader> HairSkinningComputePass::GetShader()
{
return m_shader;
}
bool HairSkinningComputePass::AcquireFeatureProcessor()
{
if (m_featureProcessor)
{
return true;
}
RPI::Scene* scene = GetScene();
if (scene)
{
m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
}
else
{
return false;
}
if (!m_featureProcessor)
{
AZ_Warning("Hair Gem", false,
"HairSkinningComputePass [%s] - Failed to retrieve Hair feature processor from the scene",
GetName().GetCStr());
return false;
}
return true;
}
RPI::Ptr<HairSkinningComputePass> HairSkinningComputePass::Create(const RPI::PassDescriptor& descriptor)
{
RPI::Ptr<HairSkinningComputePass> pass = aznew HairSkinningComputePass(descriptor);
return pass;
}
HairSkinningComputePass::HairSkinningComputePass(const RPI::PassDescriptor& descriptor)
: RPI::ComputePass(descriptor)
{
}
void HairSkinningComputePass::InitializeInternal()
{
if (GetScene())
{
ComputePass::InitializeInternal();
}
}
void HairSkinningComputePass::BuildInternal()
{
ComputePass::BuildInternal();
if (!AcquireFeatureProcessor())
{
return;
}
// Output
// This is the buffer that is shared between all objects and dispatches and contains
// the dynamic data that can be changed between passes.
Name bufferName = Name{ "SkinnedHairSharedBuffer" };
RPI::PassAttachmentBinding* localBinding = FindAttachmentBinding(bufferName);
if (localBinding && !localBinding->m_attachment)
{
AttachBufferToSlot(Name{ "SkinnedHairSharedBuffer" }, HairSharedBufferInterface::Get()->GetBuffer());
}
}
void HairSkinningComputePass::FrameBeginInternal(FramePrepareParams params)
{
if (m_buildShaderAndData)
{ // Shader rebuild is required - the async callback did not succeed (missing FP?)
if (AcquireFeatureProcessor())
{ // FP exists or can be acquired
LoadShader(); // this will happen in this frame
// Flag the FP not to add any more dispatches until shader rebuild was done
m_featureProcessor->SetAddDispatchEnable(false);
// The following will force rebuild in the next frame keeping this frame clean.
m_featureProcessor->ForceRebuildRenderData();
m_buildShaderAndData = false;
}
// Clear the dispatch items, they will be re-populated next frame
m_dispatchItems.clear();
}
// This will bind the Per Object resources since the binding is triggering
// the RHI validation that will use attachment for its validation. The attachments
// are invalidated outside the render begin / end frame.
for (HairRenderObject* newObject : m_newRenderObjects)
{
newObject->BindPerObjectSrgForCompute();
}
// Clear the objects, hence this is only done once per object/shader lifetime
m_newRenderObjects.clear();
RPI::ComputePass::FrameBeginInternal(params);
}
void HairSkinningComputePass::CompileResources([[maybe_unused]] const RHI::FrameGraphCompileContext& context)
{
if (!m_featureProcessor)
{
return;
}
// DON'T call the ComputePass:CompileResources as it will try to compile perDraw srg
// under the assumption that this is a single dispatch compute. Here we have dispatch
// per hair object and each has its own perDraw srg.
if (m_shaderResourceGroup != nullptr)
{
BindPassSrg(context, m_shaderResourceGroup);
m_shaderResourceGroup->Compile();
}
}
bool HairSkinningComputePass::IsEnabled() const
{
return RPI::ComputePass::IsEnabled();// (m_dispatchItems.size() ? true : false);
}
bool HairSkinningComputePass::BuildDispatchItem(HairRenderObject* hairObject, DispatchLevel dispatchLevel)
{
m_newRenderObjects.insert(hairObject);
return hairObject->BuildDispatchItem(m_shader.get(), dispatchLevel);
}
void HairSkinningComputePass::AddDispatchItems(AZStd::list<Data::Instance<HairRenderObject>>& hairRenderObjects)
{
// The following mutex is used for blocking the shader switch when a hot load occurs, hence ensuring
// the shader exists and the same shader, data and dispatch items are used across all hair objects
// during this frame.
// Several cases exist:
// 1. Hot reload was invoked first - either finished before this method or the mutex in this method
// is waited upon. The flag for hot reload was set already resulting in exit without add of dispatches.
// 2. Hot reload was invoked after this method - the BuildCommandListInternal will test for the flag and
// clear if required.
// 3. Hot reload was invoked after send to the GPU (BuildCommandListInternal) - the data sent is valid
// and it is safe to change the shader and create new dispatches.
// Remark: BuildCommandListInternal does not need to be synched as well since if the data was already
// inserted it is consistent and valid using the existing shader and data with instance counting.
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
if (m_buildShaderAndData)
{ // mutex was held by the hot reload and released - abort render until done. List is empty.
return;
}
for (auto& renderObject : hairRenderObjects)
{
if (!renderObject->IsEnabled())
{
continue;
}
const RHI::DispatchItem* dispatchItem = renderObject->GetDispatchItem(m_shader.get());
if (!dispatchItem)
{
continue;
}
uint32_t iterations = m_allowSimIterations ? AZ::GetMax(renderObject->GetCPULocalShapeIterations(), 1) : 1;
for (uint32_t j = 0; j < iterations; ++j)
{
m_dispatchItems.insert(dispatchItem);
}
}
}
void HairSkinningComputePass::BuildCommandListInternal(const RHI::FrameGraphExecuteContext& context)
{
if (m_buildShaderAndData)
{ // Protect against shader and data async change that were not carried out
m_dispatchItems.clear();
return;
}
RHI::CommandList* commandList = context.GetCommandList();
// The following will bind all registered Srgs set in m_shaderResourceGroupsToBind
// and sends them to the command list ahead of the dispatch.
// This includes the PerView, PerScene and PerPass srgs (what about per draw?)
SetSrgsForDispatch(commandList);
for (const RHI::DispatchItem* dispatchItem : m_dispatchItems)
{
commandList->Submit(*dispatchItem);
}
// Clear the dispatch items. They will need to be re-populated next frame
m_dispatchItems.clear();
}
void HairSkinningComputePass::BuildShaderAndRenderData()
{
AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
m_buildShaderAndData = true;
if (AcquireFeatureProcessor())
{
// Flag the FP not to add any more dispatches until shader rebuild was done
m_featureProcessor->SetAddDispatchEnable(false);
}
}
// Before reloading shaders, we want to wait for existing dispatches to finish
// so shader reloading does not interfere in any way. Because AP reloads are async, there might
// be a case where dispatch resources are destructed and will most certainly cause a GPU crash.
// If we flag the need for rebuild, the build will be made at the start of the next frame - at
// this stage the dispatch items should have been cleared - now we can load the shader and data.
void HairSkinningComputePass::OnShaderReinitialized([[maybe_unused]] const AZ::RPI::Shader& shader)
{
BuildShaderAndRenderData();
}
void HairSkinningComputePass::OnShaderAssetReinitialized([[maybe_unused]] const Data::Asset<AZ::RPI::ShaderAsset>& shaderAsset)
{
BuildShaderAndRenderData();
}
void HairSkinningComputePass::OnShaderVariantReinitialized([[maybe_unused]] const AZ::RPI::ShaderVariant& shaderVariant)
{
BuildShaderAndRenderData();
}
} // namespace Hair
} // namespace Render
} // namespace AZ