// Copyright Epic Games, Inc. All Rights Reserved. #include "MuT/ASTOpMeshRemoveMask.h" #include "MuT/ASTOpMeshMorph.h" #include "MuT/ASTOpMeshAddMetadata.h" #include "HAL/PlatformMath.h" #include "MuR/ModelPrivate.h" #include "MuR/RefCounted.h" #include "MuR/Types.h" namespace mu { //--------------------------------------------------------------------------------------------- ASTOpMeshRemoveMask::ASTOpMeshRemoveMask() : source(this) { } //--------------------------------------------------------------------------------------------- ASTOpMeshRemoveMask::~ASTOpMeshRemoveMask() { // Explicit call needed to avoid recursive destruction ASTOp::RemoveChildren(); } //--------------------------------------------------------------------------------------------- void ASTOpMeshRemoveMask::AddRemove(const Ptr& condition, const Ptr& mask) { removes.Add({ ASTChild(this,condition), ASTChild(this,mask) }); } //--------------------------------------------------------------------------------------------- bool ASTOpMeshRemoveMask::IsEqual(const ASTOp& otherUntyped) const { if (otherUntyped.GetOpType() == GetOpType()) { const ASTOpMeshRemoveMask* other = static_cast(&otherUntyped); return source == other->source && removes == other->removes && FaceCullStrategy==other->FaceCullStrategy; } return false; } //--------------------------------------------------------------------------------------------- mu::Ptr ASTOpMeshRemoveMask::Clone(MapChildFuncRef mapChild) const { Ptr n = new ASTOpMeshRemoveMask(); n->source = mapChild(source.child()); n->FaceCullStrategy = FaceCullStrategy; for (const TPair& r : removes) { n->removes.Add({ ASTChild(n,mapChild(r.Key.child())), ASTChild(n,mapChild(r.Value.child())) }); } return n; } //--------------------------------------------------------------------------------------------- void ASTOpMeshRemoveMask::ForEachChild(const TFunctionRef f) { f(source); for (TPair& r : removes) { f(r.Key); f(r.Value); } } //--------------------------------------------------------------------------------------------- uint64 ASTOpMeshRemoveMask::Hash() const { uint64 res = std::hash()(source.child().get()); for (const TPair& r : removes) { hash_combine(res, r.Key.child().get()); hash_combine(res, r.Value.child().get()); } return res; } //--------------------------------------------------------------------------------------------- void ASTOpMeshRemoveMask::Link(FProgram& program, FLinkerOptions*) { // Already linked? if (!linkedAddress) { linkedAddress = (OP::ADDRESS)program.OpAddress.Num(); program.OpAddress.Add((uint32_t)program.ByteCode.Num()); AppendCode(program.ByteCode, EOpType::ME_REMOVEMASK); OP::ADDRESS sourceAt = source ? source->linkedAddress : 0; AppendCode(program.ByteCode, sourceAt); AppendCode(program.ByteCode, FaceCullStrategy); AppendCode(program.ByteCode, (uint16)removes.Num()); for (const TPair& b : removes) { OP::ADDRESS condition = b.Key ? b.Key->linkedAddress : 0; AppendCode(program.ByteCode, condition); OP::ADDRESS remove = b.Value ? b.Value->linkedAddress : 0; AppendCode(program.ByteCode, remove); } } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- namespace { class Sink_MeshRemoveMaskAST { public: // \TODO This is recursive and may cause stack overflows in big models. mu::Ptr Apply(const ASTOpMeshRemoveMask* root) { Root = root; m_oldToNew.Empty(); InitialSource = root->source.child(); mu::Ptr newSource = Visit(InitialSource); // If there is any change, it is the new root. if (newSource != InitialSource) { return newSource; } return nullptr; } protected: const ASTOpMeshRemoveMask* Root; mu::Ptr InitialSource; TMap, mu::Ptr> m_oldToNew; TArray> m_newOps; mu::Ptr Visit(const mu::Ptr& at) { if (!at) return nullptr; // Newly created? if (m_newOps.Contains(at)) { return at; } // Already visited? Ptr* cacheIt = m_oldToNew.Find(at); if (cacheIt) { return *cacheIt; } mu::Ptr newAt = at; switch (at->GetOpType()) { case EOpType::ME_MORPH: { mu::Ptr newOp = mu::Clone(at); newOp->Base = Visit(newOp->Base.child()); newAt = newOp; break; } case EOpType::ME_ADDMETADATA: { mu::Ptr newOp = mu::Clone(at); newOp->Source = Visit(newOp->Source.child()); newAt = newOp; break; } // disabled to avoid code explosion (or bug?) TODO // case EOpType::ME_CONDITIONAL: // { // Ptr newOp = mu::Clone(at); // newOp->yes = Visit(newOp->yes.child()); // newOp->no = Visit(newOp->no.child()); // newAt = newOp; // break; // } // case EOpType::ME_SWITCH: // { // auto newOp = mu::Clone(at); // newOp->def = Visit(newOp->def.child()); // for( auto& c:newOp->cases ) // { // c.branch = Visit(c.branch.child()); // } // newAt = newOp; // break; // } default: { // if (at != InitialSource) { Ptr newOp = mu::Clone(Root); newOp->source = at; newAt = newOp; } break; } } m_oldToNew.Add(at, newAt); return newAt; } }; } //------------------------------------------------------------------------------------------------- mu::Ptr ASTOpMeshRemoveMask::OptimiseSink(const FModelOptimizationOptions&, FOptimizeSinkContext&) const { Sink_MeshRemoveMaskAST sinker; mu::Ptr at = sinker.Apply(this); // If not optimized already, see if we can optimize the "remove" branches if (!at) { Ptr NewOp; for (int32 RemoveIndex=0; RemoveIndexGetOpType(); switch (RemoveType) { case EOpType::ME_ADDMETADATA: { // It can be ignored. if (!NewOp) { NewOp = mu::Clone(this); } const ASTOpMeshAddMetadata* Add = static_cast(removes[RemoveIndex].Value.child().get()); NewOp->removes[RemoveIndex].Value = Add->Source.child(); break; } default: break; } } at = NewOp; } return at; } //------------------------------------------------------------------------------------------------- FSourceDataDescriptor ASTOpMeshRemoveMask::GetSourceDataDescriptor(FGetSourceDataDescriptorContext* Context) const { if (source) { return source->GetSourceDataDescriptor(Context); } return {}; } }