// Copyright Epic Games, Inc. All Rights Reserved. #include "MuT/ASTOpLayoutFromMesh.h" #include "HAL/UnrealMemory.h" #include "Misc/AssertionMacros.h" #include "MuR/Mesh.h" #include "MuR/MeshBufferSet.h" #include "MuR/ModelPrivate.h" #include "MuR/MutableTrace.h" #include "MuR/RefCounted.h" #include "MuR/System.h" #include "MuR/Types.h" #include "MuT/ASTOpConditional.h" #include "MuT/ASTOpConstantResource.h" #include "MuT/ASTOpMeshClipMorphPlane.h" #include "MuT/ASTOpMeshRemoveMask.h" #include "MuT/ASTOpMeshMorph.h" #include "MuT/ASTOpMeshFormat.h" #include "MuT/ASTOpMeshApplyShape.h" #include "MuT/ASTOpMeshBindShape.h" #include "MuT/ASTOpMeshAddMetadata.h" #include "MuT/ASTOpMeshMerge.h" #include "MuT/ASTOpSwitch.h" #include "MuT/ASTOpLayoutMerge.h" namespace mu { //------------------------------------------------------------------------------------------------- ASTOpLayoutFromMesh::ASTOpLayoutFromMesh() : Mesh(this) { } ASTOpLayoutFromMesh::~ASTOpLayoutFromMesh() { // Explicit call needed to avoid recursive destruction ASTOp::RemoveChildren(); } bool ASTOpLayoutFromMesh::IsEqual(const ASTOp& otherUntyped) const { if (otherUntyped.GetOpType()==GetOpType()) { const ASTOpLayoutFromMesh* other = static_cast(&otherUntyped); return Mesh == other->Mesh && LayoutIndex == other->LayoutIndex; } return false; } uint64 ASTOpLayoutFromMesh::Hash() const { uint64 res = std::hash()(Mesh.child().get()); hash_combine(res, LayoutIndex); return res; } mu::Ptr ASTOpLayoutFromMesh::Clone(MapChildFuncRef mapChild) const { mu::Ptr n = new ASTOpLayoutFromMesh(); n->Mesh = mapChild(Mesh.child()); n->LayoutIndex = LayoutIndex; return n; } void ASTOpLayoutFromMesh::ForEachChild(const TFunctionRef f) { f(Mesh); } void ASTOpLayoutFromMesh::Link(FProgram& program, FLinkerOptions*) { // Already linked? if (!linkedAddress) { OP::LayoutFromMeshArgs Args; FMemory::Memzero(Args); Args.LayoutIndex = LayoutIndex; if (Mesh) Args.Mesh = Mesh->linkedAddress; linkedAddress = (OP::ADDRESS)program.OpAddress.Num(); //program.m_code.push_back(op); program.OpAddress.Add((uint32_t)program.ByteCode.Num()); AppendCode(program.ByteCode, GetOpType()); AppendCode(program.ByteCode, Args); } } void ASTOpLayoutFromMesh::GetBlockLayoutSize(uint64 BlockId, int32* pBlockX, int32* pBlockY, FBlockLayoutSizeCache* cache) { // This shouldn't happen for this operation because it is always in a branch of layout operations that is not the main one. check(false); } namespace { /** Handle the optimization of a ASTOpLayoutFromMesh operation by moving it down its subtree. */ class FSinkLayoutFromMesh { public: // \TODO This is recursive and may cause stack overflows in big models. mu::Ptr Apply(const ASTOpLayoutFromMesh* InRoot) { InitialRoot = InRoot; check(InitialRoot); OldToNew.Empty(); InitialSource = InitialRoot->Mesh.child(); mu::Ptr NewSource = Visit(InitialSource, InitialRoot); // If there is any change, it is the new root. if (NewSource != InitialSource) { return NewSource; } return nullptr; } protected: const class ASTOpLayoutFromMesh* InitialRoot = nullptr; Ptr InitialSource; TMap> OldToNew; mu::Ptr Visit(const mu::Ptr& at, const ASTOpLayoutFromMesh* CurrentSinkingOp) { if (!at) return nullptr; // Already visited? const Ptr* Cached = OldToNew.Find({ at,CurrentSinkingOp }); if (Cached) { return *Cached; } mu::Ptr newAt = at; switch (at->GetOpType()) { case EOpType::ME_MORPH: { // Sink, ignoring the op const ASTOpMeshMorph* Typed = static_cast(at.get()); newAt = Visit(Typed->Base.child(), CurrentSinkingOp); break; } case EOpType::ME_FORMAT: { // Sink, ignoring the op const ASTOpMeshFormat* Typed = static_cast(at.get()); newAt = Visit(Typed->Source.child(), CurrentSinkingOp); break; } case EOpType::ME_APPLYSHAPE: { // Sink, ignoring the op const ASTOpMeshApplyShape* Typed = static_cast(at.get()); newAt = Visit(Typed->Mesh.child(), CurrentSinkingOp); break; } case EOpType::ME_BINDSHAPE: { // Sink, ignoring the op const ASTOpMeshBindShape* Typed = static_cast(at.get()); newAt = Visit(Typed->Mesh.child(), CurrentSinkingOp); break; } case EOpType::ME_ADDMETADATA: { // Sink, ignoring the op const ASTOpMeshAddMetadata* Typed = static_cast(at.get()); newAt = Visit(Typed->Source.child(), CurrentSinkingOp); break; } case EOpType::ME_CONDITIONAL: { Ptr NewConditional = mu::Clone(at); NewConditional->type = EOpType::LA_CONDITIONAL; NewConditional->yes = Visit(NewConditional->yes.child(), CurrentSinkingOp); NewConditional->no = Visit(NewConditional->no.child(), CurrentSinkingOp); newAt = NewConditional; break; } case EOpType::ME_SWITCH: { Ptr NewOp = mu::Clone(at); NewOp->Type = EOpType::LA_SWITCH; NewOp->Default = Visit(NewOp->Default.child(), CurrentSinkingOp); for (ASTOpSwitch::FCase& c : NewOp->Cases) { c.Branch = Visit(c.Branch.child(), CurrentSinkingOp); } newAt = NewOp; break; } case EOpType::ME_MERGE: { const ASTOpMeshMerge* Typed = static_cast(at.get()); Ptr NewMerge = new ASTOpLayoutMerge; NewMerge->Base = Visit(Typed->Base.child(), CurrentSinkingOp); NewMerge->Added = Visit(Typed->Added.child(), CurrentSinkingOp); newAt = NewMerge; break; } default: if (at != InitialSource) { mu::Ptr NewOp = mu::Clone(CurrentSinkingOp); NewOp->Mesh = at; newAt = NewOp; } break; } OldToNew.Add({ at,CurrentSinkingOp }, newAt); return newAt; } }; } mu::Ptr ASTOpLayoutFromMesh::OptimiseSink(const FModelOptimizationOptions& options, FOptimizeSinkContext& context) const { MUTABLE_CPUPROFILER_SCOPE(ASTOpLayoutFromMesh_Sink); FSinkLayoutFromMesh Sinker; mu::Ptr at = Sinker.Apply(this); return at; } }