Files
2025-05-18 13:04:45 +08:00

222 lines
6.2 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "MuR/Operations.h"
#include "MuR/CodeVisitor.h"
#include "MuT/CompilerPrivate.h"
#include "MuT/AST.h"
namespace mu
{
/** Code optimiser: Rebuild an ASTOp graph so that it is more efficient. */
class CodeOptimiser
{
public:
CodeOptimiser( Ptr<CompilerOptions>, TArray<FStateCompilationData>& States );
void Optimise();
private:
Ptr<CompilerOptions> Options;
TArray<FStateCompilationData>& States;
/** The max number of optimize iterations is shared across several stages now. This is how many are left. */
int32 OptimizeIterationsLeft=0;
//! Full optimisation pass
void FullOptimiseAST( ASTOpList& roots, int32 Pass);
/** Optimise the code for each specific state, generating new instructions and state information. */
void OptimiseStatesAST();
};
/** ConstantGenerator replaces constant subtrees of operations with an equivalent single */
extern bool ConstantGenerator( const CompilerOptions::Private*, Ptr<ASTOp>& Root, int32 Pass );
/** \TODO: shapes, projectors, others? but not switches (they must be unique) */
extern bool DuplicatedDataRemoverAST( ASTOpList& Roots );
/** Mark all the duplicated code instructions to point at the same operation, leaving the copies unreachable. */
extern bool DuplicatedCodeRemoverAST( ASTOpList& Roots );
/** All kinds of optimisations that depend on the meaning of each operation. */
extern bool SemanticOptimiserAST( ASTOpList& Roots, const FModelOptimizationOptions&, int32 Pass);
/** Semantic operator that reorders instructions moving expensive ones down to the
* leaves of the expressions trying to turn them into constants.
*/
extern bool SinkOptimiserAST( ASTOpList& Roots, const FModelOptimizationOptions& );
/** */
extern bool SizeOptimiserAST( ASTOpList& Roots );
/** */
extern bool LocalLogicOptimiserAST(ASTOpList& roots);
/** Discard all LODs beyond the given lod count. */
class LODCountReducerAST : public Visitor_TopDown_Unique_Cloning
{
public:
LODCountReducerAST( Ptr<ASTOp>& Root, uint8 NumExtraLODsToBuildAfterFirstLOD );
protected:
Ptr<ASTOp> Visit( Ptr<ASTOp>, bool& bOutProcessChildren ) override;
uint8 NumExtraLODs = 0;
};
/** Scan the code in the given subtree and return true if a state runtime parameters is found.
* Intermediate data is used betwen calls to apply, so don't remove program code or directly
* change the instructions. Adding new instructions is ok.
*/
class RuntimeParameterVisitorAST
{
public:
RuntimeParameterVisitorAST(const FStateCompilationData* );
bool HasAny( const Ptr<ASTOp>& Root );
private:
const FStateCompilationData* State;
//!
struct FPendingItem
{
//! 0: indicate subtree pending
//! 1: indicate children finished
uint8 ItemType;
//! 0: everything is relevant
//! 1: only layouts are relevant
uint8 OnlyLayoutsRelevant;
//! Operation to visit
Ptr<ASTOp> Op;
};
//!
TArray< FPendingItem > Pending;
//! Possible op state
enum class EOpState : uint8
{
NotVisited = 0,
ChildrenPendingFull,
ChildrenPendingPartial,
VisitedHasRuntime,
VisitedFullDoesntHaveRuntime,
VisitedPartialDoesntHaveRuntime
};
TMap<Ptr<ASTOp>, EOpState> Visited;
//!
void AddIfNeeded( const FPendingItem& );
};
/** Remove all texture compression operations that would happen for runtime parameter changes. */
class RuntimeTextureCompressionRemoverAST : public Visitor_TopDown_Unique_Cloning
{
public:
RuntimeTextureCompressionRemoverAST(FStateCompilationData*, bool bInAlwaysUncompress );
protected:
Ptr<ASTOp> Visit( Ptr<ASTOp>, bool& bOutProcessChildren ) override;
private:
RuntimeParameterVisitorAST HasRuntimeParamVisitor;
bool bAlwaysUncompress = false;
};
/** Restructure the code to move operations involving runtime parameters as high as possible. */
class ParameterOptimiserAST : public Visitor_TopDown_Unique_Cloning
{
private:
FStateCompilationData& StateProps;
public:
ParameterOptimiserAST(FStateCompilationData&, const FModelOptimizationOptions& );
bool Apply();
private:
Ptr<ASTOp> Visit( Ptr<ASTOp>, bool& bOutProcessChildren ) override;
bool bModified;
FModelOptimizationOptions OptimisationOptions;
RuntimeParameterVisitorAST HasRuntimeParamVisitor;
};
/** Some masks are optional.If they are null, replace them by a white plain image of the right size. */
extern Ptr<ASTOp> EnsureValidMask( Ptr<ASTOp> mask, Ptr<ASTOp> base );
/** Calculate all the parameters found relevant under a particular operation.This may not
* incldue all the parameters in the subtree (if because of the operations they are not
* relevant)
* It has an internal cache, so don't reuse if the program changes.
*/
class SubtreeRelevantParametersVisitorAST
{
public:
void Run( Ptr<ASTOp> Root );
//! After Run, list of relevant parameters.
TSet< FString > Parameters;
private:
struct FState
{
Ptr<ASTOp> Op;
bool bOnlyLayoutIsRelevant=false;
FState( Ptr<ASTOp> o=nullptr, bool l=false) : Op(o), bOnlyLayoutIsRelevant(l) {}
bool operator==(const FState& o) const
{
return Op == o.Op &&
bOnlyLayoutIsRelevant == o.bOnlyLayoutIsRelevant;
}
friend FORCEINLINE uint32 GetTypeHash(const FState& InKey)
{
uint32 KeyHash = 0;
KeyHash = HashCombineFast(KeyHash, ::GetTypeHash(InKey.Op.get()));
KeyHash = HashCombineFast(KeyHash, ::GetTypeHash(InKey.bOnlyLayoutIsRelevant));
return KeyHash;
}
};
// Result cache
// \todo optimise by storing unique lists separately and an index here.
TMap< FState, TSet<FString> > ResultCache;
};
}