aa18deef5d
- The crash happens due to attempt to get an instance of the hair dynamic data before it was initialized. Signed-off-by: Adi Bar-Lev <82479970+Adi-Amazon@users.noreply.github.com>
395 lines
20 KiB
C++
395 lines
20 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
|
|
*
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AzCore/base.h>
|
|
#include <AzCore/Math/Vector3.h>
|
|
|
|
#include <Atom/RHI/BufferView.h>
|
|
#include <Atom/RHI/DrawPacketBuilder.h>
|
|
|
|
// Hair specific
|
|
#include <TressFX/AMD_TressFX.h>
|
|
#include <TressFX/AMD_Types.h>
|
|
#include <TressFX/TressFXConstantBuffers.h>
|
|
|
|
#include <Rendering/HairCommon.h>
|
|
#include <Rendering/SharedBuffer.h>
|
|
#include <Rendering/HairDispatchItem.h>
|
|
#include <Rendering/HairBuffersSemantics.h>
|
|
|
|
#define TRESSFX_MIN_VERTS_PER_STRAND_FOR_GPU_ITERATION 64
|
|
|
|
namespace AMD
|
|
{
|
|
struct float4x4;
|
|
|
|
class TressFXAsset;
|
|
class TressFXRenderingSettings;
|
|
class TressFXSimulationSettings;
|
|
}
|
|
|
|
namespace AZ
|
|
{
|
|
namespace RHI
|
|
{
|
|
class DrawPacket;
|
|
}
|
|
|
|
namespace RPI
|
|
{
|
|
class Model;
|
|
class Scene;
|
|
class Shader;
|
|
}
|
|
|
|
namespace Render
|
|
{
|
|
|
|
namespace Hair
|
|
{
|
|
class HairFeatureProcessor;
|
|
|
|
//! TressFXStrandLevelData represents blended bone data per hair strand that once calculated
|
|
//! is passed between the skinning pass and the simulation shape constraints pass
|
|
struct TressFXStrandLevelData
|
|
{
|
|
AMD::float4 skinningQuat;
|
|
AMD::float4 vspQuat;
|
|
AMD::float4 vspTranslation;
|
|
};
|
|
|
|
//!-----------------------------------------------------------------------------------------
|
|
//!
|
|
//! DynamicHairData
|
|
//!
|
|
//!-----------------------------------------------------------------------------------------
|
|
//! Contains the writable data that is passed and used by 3 modules:
|
|
//! simulation, signed distance field (collisions), and rendering.
|
|
//! Rendering uses current position and tangent as SRVs in VS for computing creation and skinning.
|
|
//! Since this data is per object (hence per object dispatch) and requires sync point (barrier) between the
|
|
//! the passes, a single buffer is allocated and is shared by all hair objects and their 'streams'
|
|
//! where each have buffer view so that it points to its own portion of the original buffer's data.
|
|
//! The shared buffer is therefore declared in the pass Srg to result in an execution dependency
|
|
//! so that a barrier will be created. It also represents less overhead since we are using a single
|
|
//! coordinated / shared buffer sync point rather than many barriers (per object per buffer).
|
|
//!-----------------------------------------------------------------------------------------
|
|
class DynamicHairData
|
|
{
|
|
friend class HairRenderObject;
|
|
|
|
public:
|
|
//! Creates the GPU dynamic buffers of a single hair object
|
|
//! Equivalent to TressFXDynamicHairData::CreateGPUResources
|
|
bool CreateDynamicGPUResources(
|
|
Data::Instance<RPI::Shader> computeShader,
|
|
Data::Instance<RPI::Shader> rasterShader,
|
|
uint32_t vertexCount, uint32_t strandsCount);
|
|
|
|
//! Data upload - copy the hair mesh asset data (positions and tangents) into the buffers.
|
|
//! In the following line I assume that positions and tangents are of the same size.
|
|
//! Equivalent to: TressFXDynamicHairData::UploadGPUData
|
|
bool UploadGPUData(const char* name, void* positions, void* tangents);
|
|
|
|
//! Preparation of the descriptors table of all the dynamic stream buffers within the class.
|
|
//! Do not call this method before calling CreateAndBindGPUResources as it is already called
|
|
//! from CreateAndBindGPUResources.
|
|
//! This method can be called also for retrieving the descriptors table (SharedBuffer)
|
|
static void PrepareSrgDescriptors(
|
|
AZStd::vector<SrgBufferDescriptor>& descriptorArray,
|
|
int32_t vertexCount, uint32_t strandsCount);
|
|
|
|
void PrepareSrgDescriptors(int32_t vertexCount, uint32_t strandsCount)
|
|
{
|
|
PrepareSrgDescriptors(m_dynamicBuffersDescriptors, vertexCount, strandsCount);
|
|
}
|
|
|
|
Data::Instance<RPI::ShaderResourceGroup> GetSimSrgForCompute()
|
|
{
|
|
return m_initialized ? m_simSrgForCompute : nullptr;
|
|
}
|
|
|
|
Data::Instance<RPI::ShaderResourceGroup> GetSimSrgForRaster()
|
|
{
|
|
return m_initialized ? m_simSrgForRaster : nullptr; }
|
|
|
|
bool IsInitialized() { return m_initialized; }
|
|
|
|
|
|
private:
|
|
//! Matching between the buffers Srg and its buffers descriptors, this method fills the Srg with
|
|
//! the views of the buffers to be used by the hair instance.
|
|
bool BindPerObjectSrgForCompute();
|
|
bool BindPerObjectSrgForRaster();
|
|
|
|
//! The descriptors required to allocate and associate the dynamic buffers with the SRGs
|
|
//! Each descriptor also contains the byte offsets of the sub-buffers in the global dynamic
|
|
//! array for the data copy.
|
|
AZStd::vector<SrgBufferDescriptor> m_dynamicBuffersDescriptors;
|
|
|
|
//! The following dynamic buffer views are views 'sub-buffers' located within a global large
|
|
//! dynamic buffer exposed and connected as an attachment between the passes and therefore
|
|
//! creates both dependency order between passes execution and sync point barrier.
|
|
//! This indirectly forces the sync to be applied to all 'sub-buffers' used by each of the
|
|
//! HairObjects / HairDispatches and therefore allows us to change their data in the shader
|
|
//! between passes.
|
|
AZStd::vector<Data::Instance<RHI::BufferView>> m_dynamicBuffersViews; // RW used for the Compute
|
|
AZStd::vector<Data::Instance<RHI::BufferView>> m_readBuffersViews; // Read only used for the Raster fill
|
|
|
|
//! The following vector is required in order to keep the allocators 'alive' or
|
|
//! else they are cleared from the buffer via the reference mechanism.
|
|
AZStd::vector<Data::Instance<HairSharedBufferAllocation>> m_dynamicViewAllocators;
|
|
|
|
//------------------------------------------------------------------
|
|
//! The following SRGs are the ones represented by this class' data.
|
|
//! These Srgs are required for the changed dynamic data passed between the
|
|
//! skinning, simulation and rendering passes / shaders.
|
|
//! It is the TressFX equivalent of the set:
|
|
//! - pSimPosTanLayout / m_pSimBindSets
|
|
//------------------------------------------------------------------
|
|
Data::Instance<RPI::ShaderResourceGroup> m_simSrgForCompute; //! TressFX equivalent: pSimPosTanLayout / m_pSimBindSets
|
|
Data::Instance<RPI::ShaderResourceGroup> m_simSrgForRaster; //! Targeting only the Fill pass / shader
|
|
|
|
bool m_initialized = false;
|
|
};
|
|
|
|
|
|
//!-----------------------------------------------------------------------------------------
|
|
//!
|
|
//! HairRenderObject
|
|
//!
|
|
//!-----------------------------------------------------------------------------------------
|
|
//! This class is equivalent to TressFXHairObject and HairStrands (the later is mainly a wrapper).
|
|
//! This is the class that holds all the raw data used by all the hair passes and shaders.
|
|
//!-----------------------------------------------------------------------------------------
|
|
class HairRenderObject final
|
|
: public Data::InstanceData
|
|
{
|
|
friend HairFeatureProcessor;
|
|
|
|
public:
|
|
AZ_RTTI(HairRenderObject, "{58F48A58-C5B9-4CAE-9AFD-9B3AF3A01C73}");
|
|
HairRenderObject() = default;
|
|
~HairRenderObject();
|
|
|
|
void Release();
|
|
|
|
bool Init(
|
|
HairFeatureProcessor* featureProcessor, const char* assetName, AMD::TressFXAsset* asset,
|
|
AMD::TressFXSimulationSettings* simSettings, AMD::TressFXRenderingSettings* renderSettings
|
|
);
|
|
|
|
bool BuildDrawPacket(RPI::Shader* geometryShader, RHI::DrawPacketBuilder::DrawRequest& drawRequest);
|
|
|
|
const RHI::DrawPacket* GetGeometrylDrawPacket(RPI::Shader* geometryShader);
|
|
|
|
//! Creates and fill the dispatch item associated with the compute shader
|
|
bool BuildDispatchItem(RPI::Shader* computeShader, DispatchLevel dispatchLevel);
|
|
|
|
const RHI::DispatchItem* GetDispatchItem(RPI::Shader* computeShader);
|
|
|
|
void PrepareHairGenerationSrgDescriptors(uint32_t vertexCount, uint32_t numStrands);
|
|
|
|
// Based on SkinnedMeshInputLod::CreateStaticBuffer
|
|
bool CreateAndBindHairGenerationBuffers(uint32_t vertexCount, uint32_t strandsCount);
|
|
|
|
//! Updates the buffers data for the hair generation.
|
|
//! Does NOT update the bone matrices - they will be updated every frame.
|
|
bool UploadGPUData(const char* name, AMD::TressFXAsset* asset);
|
|
|
|
Data::Instance<RPI::ShaderResourceGroup> GetHairGenerationSrg()
|
|
{
|
|
return m_hairGenerationSrg;
|
|
}
|
|
|
|
bool BindPerObjectSrgForCompute()
|
|
{
|
|
return m_dynamicHairData.IsInitialized() ? m_dynamicHairData.BindPerObjectSrgForCompute() : false;
|
|
}
|
|
|
|
bool BindPerObjectSrgForRaster()
|
|
{
|
|
return m_dynamicHairData.IsInitialized() ? m_dynamicHairData.BindPerObjectSrgForRaster() : false;
|
|
}
|
|
|
|
//!-----------------------------------------------------------------
|
|
//! Methods partially imported from TressFXHairObject
|
|
//!-----------------------------------------------------------------
|
|
int GetNumTotalHairVertices() const { return m_NumTotalVertices; }
|
|
int GetNumTotalHairStrands() const { return m_NumTotalStrands; }
|
|
int GetNumVerticesPerStrand() const { return m_NumVerticesPerStrand; }
|
|
int GetCPULocalShapeIterations() const { return m_CPULocalShapeIterations; }
|
|
int GetNumFollowHairsPerGuideHair() const { return m_NumFollowHairsPerGuideHair; }
|
|
int GetNumGuideHairs() const
|
|
{
|
|
return GetNumTotalHairStrands() / (GetNumFollowHairsPerGuideHair() + 1);
|
|
}
|
|
//! This method is mainly a wrapper around BindRenderSrgResources to keep the
|
|
//! connection in code to the TressFX method.
|
|
//! Bind Render Srg (m_hairRenderSrg) resources. No resources data update should be doe here
|
|
//! Notice that this also loads the images and is slower if a new asset is required.
|
|
//! If the image was not changed it should only bind without the retrieve operation.
|
|
bool PopulateDrawStrandsBindSet(AMD::TressFXRenderingSettings* pRenderSettings/*=nullptr*/);
|
|
|
|
// This function will be called when the image asset changed for the component.
|
|
bool LoadImageAsset(AMD::TressFXRenderingSettings* pRenderSettings);
|
|
|
|
bool UploadRenderingGPUResources(AMD::TressFXAsset& asset);
|
|
|
|
//! Creation of the render Srg m_hairRenderSrg, followed by creation and binding of the
|
|
//! GPU render resources: vertex thickness, vertex UV, hair albedo maps and two constant buffers.
|
|
bool CreateRenderingGPUResources(
|
|
Data::Instance<RPI::Shader> shader, AMD::TressFXAsset& asset, const char* assetName);
|
|
|
|
bool Update();
|
|
|
|
//! This method needs to be called in order to fill the bone matrices before the skinning
|
|
void UpdateBoneMatrices(const AMD::float4x4* pBoneMatricesInWS, int numBoneMatrices);
|
|
//! update of the skinning matrices per frame. The matrices are in model / local space
|
|
//! which is why the entity world matrix is also passed.
|
|
void UpdateBoneMatrices(const AZ::Matrix3x4& entityWorldMatrix, const AZStd::vector<AZ::Matrix3x4>& boneMatrices);
|
|
void InitBoneMatricesPlaceHolder(int numBoneMatrices);
|
|
|
|
void SetFrameDeltaTime(float deltaTime);
|
|
//! Updating the bone matrices for the skinning in the simulation constant buffer.
|
|
//! pBoneMatricesInWS constraints array of column major bone matrices in world space.
|
|
void UpdateRenderingParameters(
|
|
const AMD::TressFXRenderingSettings* parameters, const int nodePoolSize,
|
|
float distance, bool shadowUpdate /*= false*/);
|
|
|
|
AMD::TressFXRenderParams* GetHairRenderParams() { return m_renderCB.get(); };
|
|
|
|
//! Update of simulation constant buffer.
|
|
//! Notice that the bone matrices are set elsewhere and should be updated before GPU submit.
|
|
void UpdateSimulationParameters(const AMD::TressFXSimulationSettings* settings, float timeStep);
|
|
|
|
void SetWind(const Vector3& windDir, float windMag, int frame);
|
|
void SetRenderIndex(uint32_t renderIndex) { m_RenderIndex = renderIndex; }
|
|
|
|
void ResetPositions() { m_simCB->g_ResetPositions = 1.0f; }
|
|
void IncreaseSimulationFrame()
|
|
{
|
|
m_simCB->g_ResetPositions = (m_SimulationFrame < 2) ? 1.0f : 0.0f;
|
|
m_SimulationFrame++;
|
|
}
|
|
|
|
bool IsEnabled()
|
|
{
|
|
return m_enabled;
|
|
}
|
|
|
|
void SetEnabled(bool enable)
|
|
{
|
|
m_enabled = enable;
|
|
}
|
|
//!-----------------------------------------------------------------
|
|
|
|
private:
|
|
//----------------------- Private Methods --------------------------
|
|
bool BindRenderSrgResources();
|
|
void PrepareRenderSrgDescriptors();
|
|
bool GetShaders();
|
|
|
|
//------------------------------ Data ------------------------------
|
|
static uint32_t s_objectCounter;
|
|
|
|
//! The feature processor is the centralized class that gathers all render nodes and
|
|
//! responsible for the various stages and passes' updates
|
|
HairFeatureProcessor* m_featureProcessor = nullptr;
|
|
|
|
//! Skinning compute shader used for creation of the compute Srgs and dispatch item
|
|
Data::Instance<RPI::Shader> m_skinningShader = nullptr;
|
|
|
|
//! Compute dispatch items map per the existing passes
|
|
AZStd::unordered_map<RPI::Shader*, Data::Instance<HairDispatchItem>> m_dispatchItems;
|
|
|
|
//! Geometry raster shader used for creation of the raster Srgs.
|
|
//! Since the Srgs for geometry raster are the same across the shaders we keep
|
|
//! only a single shader - if this to change in the future, several shaders and sets
|
|
//! of dynamic Srgs should be created.
|
|
Data::Instance<RPI::Shader> m_geometryRasterShader = nullptr;
|
|
|
|
//! DrawPacket for the multi object geometry raster pass.
|
|
AZStd::unordered_map<RPI::Shader*, const RHI::DrawPacket*> m_geometryDrawPackets;
|
|
|
|
float m_frameDeltaTime = 0.02;
|
|
|
|
//! The following are the configuration settings that might be required during the update.
|
|
AMD::TressFXSimulationSettings* m_simSettings = nullptr;
|
|
AMD::TressFXRenderingSettings* m_renderSettings = nullptr;
|
|
|
|
//! Hair asset information
|
|
uint32_t m_TotalIndices = 0;
|
|
uint32_t m_NumTotalVertices = 0;
|
|
uint32_t m_numGuideVertices = 0;
|
|
uint32_t m_NumTotalStrands = 0;
|
|
uint32_t m_NumVerticesPerStrand = 0;
|
|
uint32_t m_CPULocalShapeIterations = 0;
|
|
uint32_t m_NumFollowHairsPerGuideHair = 0;
|
|
|
|
// LOD calculations factor
|
|
float m_LODHairDensity = 1.0f;
|
|
|
|
bool m_enabled = true;
|
|
|
|
//! Controls reset / copy base hair state
|
|
uint32_t m_SimulationFrame = 0;
|
|
//! The index used as a look up into the material array during the resolve pass
|
|
uint32_t m_RenderIndex = 0;
|
|
|
|
//!-----------------------------------------------------------------
|
|
//! The hair dynamic per instance buffers such as vertices, tangents, etc..
|
|
//! The data of these buffers is read/write and will change between passes.
|
|
DynamicHairData m_dynamicHairData;
|
|
|
|
//!-----------------------------------------------------------------
|
|
//! Static buffers & Srg: Initial position, bones transform skinning
|
|
//! data, physical hair properties..
|
|
//!-----------------------------------------------------------------
|
|
AZStd::vector<Data::Instance<RPI::Buffer>> m_hairGenerationBuffers;
|
|
AZStd::vector<SrgBufferDescriptor> m_hairGenerationDescriptors;
|
|
//! The simulation parameters constant buffer.
|
|
HairUniformBuffer<AMD::TressFXSimulationParams> m_simCB;
|
|
Data::Instance<RPI::ShaderResourceGroup> m_hairGenerationSrg;
|
|
|
|
//!-----------------------------------------------------------------
|
|
//! TressFXRenderParams Srg buffers and declarations
|
|
//! The rendering buffers and structures required for the render draw
|
|
//! calls and are sent to the GPU using TressFXRenderParams Srg.
|
|
//!-----------------------------------------------------------------
|
|
//! Vertex and UV buffers.
|
|
//! Naming was not changed to preserve correlation to TressFXHairObject.h
|
|
Data::Instance<RPI::Buffer> m_hairVertexRenderParams;
|
|
Data::Instance<RPI::Buffer> m_hairTexCoords;
|
|
|
|
//! Base color of the hair root and per strand texture.
|
|
Data::Instance<RPI::Image> m_baseAlbedo;
|
|
Data::Instance<RPI::Image> m_strandAlbedo;
|
|
|
|
HairUniformBuffer<AMD::TressFXRenderParams> m_renderCB;
|
|
HairUniformBuffer<AMD::TressFXStrandParams> m_strandCB;
|
|
|
|
AZStd::vector<SrgBufferDescriptor> m_hairRenderDescriptors;
|
|
// Equivalent to m_pRenderLayoutBindSet in TressFX.
|
|
Data::Instance<RPI::ShaderResourceGroup> m_hairRenderSrg;
|
|
|
|
//! Index buffer for the render pass via draw calls - naming was kept
|
|
Data::Instance<RHI::Buffer> m_indexBuffer;
|
|
RHI::IndexBufferView m_indexBufferView;
|
|
//-------------------------------------------------------------------
|
|
|
|
AZStd::mutex m_mutex;
|
|
};
|
|
|
|
} // namespace Hair
|
|
|
|
} // namespace Render
|
|
|
|
} // namespace AZ
|