996 lines
43 KiB
C++
996 lines
43 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "MuT/Compiler.h"
|
|
|
|
#include "MuT/CompilerPrivate.h"
|
|
#include "MuT/AST.h"
|
|
#include "MuT/ASTOpParameter.h"
|
|
#include "MuT/ASTOpInstanceAdd.h"
|
|
#include "MuT/CodeGenerator.h"
|
|
#include "MuT/CodeOptimiser.h"
|
|
#include "MuT/ErrorLog.h"
|
|
#include "MuT/Node.h"
|
|
#include "MuR/Image.h"
|
|
#include "MuR/Mesh.h"
|
|
#include "MuR/Model.h"
|
|
#include "MuR/ModelPrivate.h"
|
|
#include "MuR/MutableTrace.h"
|
|
#include "MuR/Operations.h"
|
|
#include "MuR/ParametersPrivate.h"
|
|
#include "MuR/Serialisation.h"
|
|
#include "MuR/MutableRuntimeModule.h"
|
|
|
|
#include "Trace/Detail/Channel.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/Map.h"
|
|
#include "HAL/LowLevelMemTracker.h"
|
|
#include "HAL/PlatformCrt.h"
|
|
#include "Hash/CityHash.h"
|
|
#include "Logging/LogCategory.h"
|
|
#include "Logging/LogMacros.h"
|
|
#include "Misc/EnumClassFlags.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
#include "Async/ParallelFor.h"
|
|
|
|
#include <inttypes.h> // Required for 64-bit printf macros
|
|
|
|
namespace mu
|
|
{
|
|
|
|
const char* CompilerOptions::GetTextureLayoutStrategyName( CompilerOptions::TextureLayoutStrategy s )
|
|
{
|
|
static const char* s_textureLayoutStrategyName[ 2 ] =
|
|
{
|
|
"Unscaled Pack",
|
|
"No Packing",
|
|
};
|
|
|
|
return s_textureLayoutStrategyName[(int)s];
|
|
}
|
|
|
|
|
|
// clang-format off
|
|
static const FOpToolsDesc SOpToolsDescs[] =
|
|
{
|
|
// cached supported base image formats
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // NONE
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // BO_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // NU_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // SC_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_CONSTANT
|
|
{ true, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_CONSTANT
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // PR_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ST_CONSTANT
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ED_CONSTANT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // MA_CONSTANT
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // BO_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // NU_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // SC_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // PR_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_PARAMETER
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ST_PARAMETER
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // MA_PARAMETER
|
|
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_REFERENCE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_REFERENCE
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // NU_CONDITIONAL
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // SC_CONDITIONAL
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_CONDITIONAL
|
|
{ true, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 } }, // IM_CONDITIONAL
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_CONDITIONAL
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_CONDITIONAL
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_CONDITIONAL
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ED_CONDITIONAL
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // NU_SWITCH
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // SC_SWITCH
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_SWITCH
|
|
{ true, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 } }, // IM_SWITCH
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_SWITCH
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_SWITCH
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_SWITCH
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ED_SWITCH
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // BO_EQUAL_SC_CONST
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // BO_AND
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // BO_OR
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // BO_NOT
|
|
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // SC_ARITHMETIC
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // SC_CURVE
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_SAMPLEIMAGE
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_SWIZZLE
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_FROMSCALARS
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // CO_ARITHMETIC
|
|
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_LAYER
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_LAYERCOLOUR
|
|
{ true, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_PIXELFORMAT
|
|
{ true, { 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_MIPMAP
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_RESIZE
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_RESIZELIKE
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_RESIZEREL
|
|
{ true, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_BLANKLAYOUT
|
|
{ true, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 } }, // IM_COMPOSE
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_INTERPOLATE
|
|
{ true, { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_SATURATE
|
|
{ true, { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_LUMINANCE
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_SWIZZLE
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_COLOURMAP
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_BINARISE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_PLAINCOLOUR
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_CROP
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_PATCH
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_RASTERMESH
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_MAKEGROWMAP
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_DISPLACE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_MULTILAYER
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_INVERT
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_NORMALCOMPOSITE
|
|
{ true, { 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IM_TRANSFORM
|
|
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_APPLYLAYOUT
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_PREPARELAYOUT
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_DIFFERENCE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_MORPH
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_MERGE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_MASKCLIPMESH
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_MASKCLIPUVMASK
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_MASKDIFF
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_REMOVEMASK
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_FORMAT
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_EXTRACTLAYOUTBLOCK
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_TRANSFORM
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_CLIPMORPHPLANE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_CLIPWITHMESH
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_SETSKELETON
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_PROJECT
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_APPLYPOSE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_BINDSHAPE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_APPLYSHAPE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_CLIPDEFORM
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_MORPHRESHAPE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_OPTIMIZESKINNING
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_ADDMETADATA
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // ME_TRANSFORMWITHMESH
|
|
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDMESH
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDIMAGE
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDVECTOR
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDSCALAR
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDSTRING
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDSURFACE
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDCOMPONENT
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDLOD
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDEXTENSIONDATA
|
|
{ false, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // IN_ADDOVERLAYMATERIAL
|
|
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_PACK
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_MERGE
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_REMOVEBLOCKS
|
|
{ true, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }, // LA_FROMMESH
|
|
};
|
|
|
|
// clang-format on
|
|
|
|
static_assert(UE_ARRAY_COUNT(SOpToolsDescs) == (int32)EOpType::COUNT, "OperationDescMismatch");
|
|
|
|
const FOpToolsDesc& GetOpToolsDesc(EOpType type)
|
|
{
|
|
return SOpToolsDescs[(int32)type];
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
FProxyFileContext::FProxyFileContext()
|
|
{
|
|
uint32 Seed = FPlatformTime::Cycles();
|
|
FRandomStream RandomStream = FRandomStream((int32)Seed);
|
|
CurrentFileIndex = RandomStream.GetUnsignedInt();
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------------------------
|
|
CompilerOptions::CompilerOptions()
|
|
{
|
|
m_pD = new Private();
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
CompilerOptions::~CompilerOptions()
|
|
{
|
|
check( m_pD );
|
|
delete m_pD;
|
|
m_pD = 0;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
CompilerOptions::Private* CompilerOptions::GetPrivate() const
|
|
{
|
|
return m_pD;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetLogEnabled( bool bEnabled)
|
|
{
|
|
m_pD->bLog = bEnabled;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetOptimisationEnabled( bool bEnabled)
|
|
{
|
|
m_pD->OptimisationOptions.bEnabled = bEnabled;
|
|
if (bEnabled)
|
|
{
|
|
m_pD->OptimisationOptions.bConstReduction = true;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetConstReductionEnabled( bool bConstReductionEnabled )
|
|
{
|
|
m_pD->OptimisationOptions.bConstReduction = bConstReductionEnabled;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetUseDiskCache(bool bEnabled)
|
|
{
|
|
m_pD->OptimisationOptions.DiskCacheContext = bEnabled ? &m_pD->DiskCacheContext : nullptr;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetOptimisationMaxIteration( int32 MaxIterations )
|
|
{
|
|
m_pD->OptimisationOptions.MaxOptimisationLoopCount = MaxIterations;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetIgnoreStates( bool bIgnore )
|
|
{
|
|
m_pD->bIgnoreStates = bIgnore;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetImageCompressionQuality(int32 Quality)
|
|
{
|
|
m_pD->ImageCompressionQuality = Quality;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetImageTiling(int32 Tiling)
|
|
{
|
|
m_pD->ImageTiling = Tiling;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetDataPackingStrategy(int32 MinTextureResidentMipCount, uint64 EmbeddedDataBytesLimit, uint64 PackagedDataBytesLimit)
|
|
{
|
|
m_pD->EmbeddedDataBytesLimit = EmbeddedDataBytesLimit;
|
|
m_pD->PackagedDataBytesLimit = PackagedDataBytesLimit;
|
|
m_pD->MinTextureResidentMipCount = MinTextureResidentMipCount;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetEnableProgressiveImages(bool bEnabled)
|
|
{
|
|
m_pD->OptimisationOptions.bEnableProgressiveImages = bEnabled;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetImagePixelFormatOverride(const FImageOperator::FImagePixelFormatFunc& InFunc)
|
|
{
|
|
m_pD->ImageFormatFunc = InFunc;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void CompilerOptions::SetReferencedResourceCallback(const FReferencedImageResourceFunc& Image, const FReferencedMeshResourceFunc& Mesh)
|
|
{
|
|
m_pD->OptimisationOptions.ReferencedImageResourceProvider = Image;
|
|
m_pD->OptimisationOptions.ReferencedMeshResourceProvider = Mesh;
|
|
}
|
|
|
|
void CompilerOptions::SetDisableImageGeneration(bool bDisabled)
|
|
{
|
|
m_pD->OptimisationOptions.bDisableImageGeneration = bDisabled;
|
|
}
|
|
|
|
void CompilerOptions::SetDisableMeshGeneration(bool bDisabled)
|
|
{
|
|
m_pD->OptimisationOptions.bDisableMeshGeneration = bDisabled;
|
|
}
|
|
|
|
void CompilerOptions::LogStats() const
|
|
{
|
|
UE_LOG(LogMutableCore, Log, TEXT(" Cache Files Written : %" PRIu64), m_pD->DiskCacheContext.FilesWritten.load());
|
|
UE_LOG(LogMutableCore, Log, TEXT(" Cache Files Read : %" PRIu64), m_pD->DiskCacheContext.FilesRead.load());
|
|
UE_LOG(LogMutableCore, Log, TEXT(" Cache MB Written : %" PRIu64), m_pD->DiskCacheContext.BytesWritten.load() >> 20);
|
|
UE_LOG(LogMutableCore, Log, TEXT(" Cache MB Read : %" PRIu64), m_pD->DiskCacheContext.BytesRead.load()>>20);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------------------------
|
|
//---------------------------------------------------------------------------------------------
|
|
Compiler::Compiler( Ptr<CompilerOptions> options, TFunction<void()>& InWaitCallback)
|
|
{
|
|
m_pD = new Private();
|
|
m_pD->Options = options;
|
|
m_pD->WaitCallback = InWaitCallback;
|
|
if (!m_pD->Options)
|
|
{
|
|
m_pD->Options = new CompilerOptions;
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
Compiler::~Compiler()
|
|
{
|
|
check( m_pD );
|
|
delete m_pD;
|
|
m_pD = 0;
|
|
}
|
|
|
|
namespace Private
|
|
{
|
|
void RemoveDisabledAddInstanceOpsValues(const Ptr<CompilerOptions>& Options, const TArray<FStateCompilationData>& States)
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(RemoveDisabledAddInstanceOpsValues);
|
|
|
|
TArray<Ptr<ASTOp>> Roots;
|
|
for(const FStateCompilationData& State : States)
|
|
{
|
|
Roots.Add(State.root);
|
|
}
|
|
|
|
TArray<Ptr<ASTOpInstanceAdd>> InstanceAddOpsToRemove;
|
|
|
|
const bool bRemoveMeshes = Options->GetPrivate()->OptimisationOptions.bDisableMeshGeneration;
|
|
const bool bRemoveImages = Options->GetPrivate()->OptimisationOptions.bDisableImageGeneration;
|
|
|
|
// Gather all InstanceAddOps its value needs to be removed.
|
|
if (bRemoveMeshes || bRemoveImages)
|
|
{
|
|
ASTOp::Traverse_TopDown_Unique_Imprecise(Roots, [&](Ptr<ASTOp> Node)
|
|
//ASTOp::Traverse_TopRandom_Unique_NonReentrant(Roots, [&](Ptr<ASTOp> Node)
|
|
{
|
|
|
|
bool bProcessChildren = GetOpDataType(Node->GetOpType()) == EDataType::Instance;
|
|
if (!bProcessChildren)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (Node->GetOpType())
|
|
{
|
|
|
|
case EOpType::IN_ADDMESH:
|
|
{
|
|
Ptr<ASTOpInstanceAdd> TypedNode = static_cast<ASTOpInstanceAdd*>(Node.get());
|
|
if (bRemoveMeshes && TypedNode)
|
|
{
|
|
InstanceAddOpsToRemove.Add(TypedNode);
|
|
}
|
|
break;
|
|
}
|
|
case EOpType::IN_ADDIMAGE:
|
|
{
|
|
Ptr<ASTOpInstanceAdd> TypedNode = static_cast<ASTOpInstanceAdd*>(Node.get());
|
|
if (bRemoveImages && TypedNode)
|
|
{
|
|
InstanceAddOpsToRemove.Add(TypedNode);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
// Remove the ops dangling from the InstanceAddOp but keep the InstanceAddOp for simplicity and parity with a full AST.
|
|
// The case of missing value is contemplated in all usages.
|
|
for (Ptr<ASTOpInstanceAdd>& InstanceAddOp : InstanceAddOpsToRemove)
|
|
{
|
|
InstanceAddOp->value = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
TSharedPtr<FModel> Compiler::Compile( const Ptr<Node>& pNode )
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(Compile);
|
|
|
|
|
|
TArray< FStateCompilationData > States;
|
|
TSharedPtr<FErrorLog> GenErrorLog;
|
|
TArray<FParameterDesc> Parameters;
|
|
{
|
|
CodeGenerator Generator( m_pD->Options->GetPrivate(), m_pD->WaitCallback );
|
|
|
|
Generator.GenerateRoot( pNode );
|
|
|
|
check( !Generator.States.IsEmpty() );
|
|
|
|
for ( const TPair<FObjectState, Ptr<ASTOp>>& s: Generator.States )
|
|
{
|
|
FStateCompilationData Data;
|
|
Data.nodeState = s.Key;
|
|
Data.root = s.Value;
|
|
Data.state.Name = s.Key.Name;
|
|
States.Add( Data );
|
|
}
|
|
|
|
GenErrorLog = Generator.ErrorLog;
|
|
|
|
{
|
|
UE::TUniqueLock Lock(Generator.FirstPass.ParameterNodes.Mutex);
|
|
|
|
// Gather the parameter list from the non-optimized data, so that we have them all even if they are optimized out
|
|
{
|
|
const int32 ParameterCount =
|
|
Generator.FirstPass.ParameterNodes.GenericParametersCache.Num()
|
|
+
|
|
Generator.FirstPass.ParameterNodes.MeshParametersCache.Num();
|
|
|
|
Parameters.Reserve(ParameterCount);
|
|
for (const TPair<Ptr<const Node>, Ptr<ASTOpParameter>>& Entry : Generator.FirstPass.ParameterNodes.GenericParametersCache)
|
|
{
|
|
Parameters.Add(Entry.Value->Parameter);
|
|
}
|
|
for (const TPair<Ptr<const NodeMeshParameter>, TArray<TPair<Ptr<ASTOpParameter>,FMeshGenerationResult>>>& Entry : Generator.FirstPass.ParameterNodes.MeshParametersCache)
|
|
{
|
|
for (const TPair<Ptr<ASTOpParameter>, FMeshGenerationResult>& ArrayEntry : Entry.Value)
|
|
{
|
|
if (ArrayEntry.Key)
|
|
{
|
|
Parameters.AddUnique(ArrayEntry.Key->Parameter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort the parameters as deterministically as possible.
|
|
Parameters.StableSort([](const FParameterDesc& A, const FParameterDesc& B)->bool
|
|
{
|
|
if (A.Name < B.Name)
|
|
{
|
|
return true;
|
|
}
|
|
if (A.Name > B.Name)
|
|
{
|
|
return false;
|
|
}
|
|
return A.UID < B.UID;
|
|
});
|
|
|
|
// Check for repeated parameter names and report them to the user
|
|
if (Parameters.Num() > 1)
|
|
{
|
|
TArray<FString> RepeatedParameterNames;
|
|
|
|
for (int32 Index = 1; Index < Parameters.Num(); ++Index)
|
|
{
|
|
const FString CurrentParameterName = Parameters[Index].Name;
|
|
const FString PreviousParameterName = Parameters[Index-1].Name;
|
|
|
|
if (CurrentParameterName.Compare(PreviousParameterName) == 0 && !RepeatedParameterNames.Contains(CurrentParameterName))
|
|
{
|
|
// Find the nodes defining these parameters
|
|
TArray<const void*, TInlineAllocator<4>> MessageContexts;
|
|
{
|
|
for (const TPair<Ptr<const Node>, Ptr<ASTOpParameter>>& Entry : Generator.FirstPass.ParameterNodes.GenericParametersCache)
|
|
{
|
|
if (Entry.Value->Parameter.Name.Compare(CurrentParameterName))
|
|
{
|
|
MessageContexts.AddUnique(Entry.Key->GetMessageContext());
|
|
}
|
|
}
|
|
for (const TPair<Ptr<const NodeMeshParameter>, TArray<TPair<Ptr<ASTOpParameter>, FMeshGenerationResult>>>& Entry : Generator.FirstPass.ParameterNodes.MeshParametersCache)
|
|
{
|
|
for (const TPair<Ptr<ASTOpParameter>, FMeshGenerationResult>& ArrayEntry : Entry.Value)
|
|
{
|
|
if (ArrayEntry.Key && ArrayEntry.Key->Parameter.Name.Compare(CurrentParameterName))
|
|
{
|
|
MessageContexts.AddUnique(Entry.Key->GetMessageContext());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Report repeated parameter name
|
|
if (MessageContexts.Num() >= 2)
|
|
{
|
|
const FString WarningText = FString::Printf(TEXT("Repeated parameter found : \"%s\". Please use a different name."), *CurrentParameterName);
|
|
GenErrorLog->Add(WarningText, ELMT_WARNING, MessageContexts[0], MessageContexts[1]);
|
|
RepeatedParameterNames.Add(CurrentParameterName);
|
|
}
|
|
else
|
|
{
|
|
ensure(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mu::Private::RemoveDisabledAddInstanceOpsValues(m_pD->Options, States);
|
|
|
|
// Optimize the generated code
|
|
{
|
|
CodeOptimiser Optimiser( m_pD->Options, States );
|
|
Optimiser.Optimise();
|
|
}
|
|
|
|
// Link the Program and generate state data.
|
|
TSharedPtr<FModel> Result = MakeShared<FModel>();
|
|
FProgram& Program = Result->GetPrivate()->Program;
|
|
|
|
check(Program.Parameters.IsEmpty());
|
|
Program.Parameters = Parameters;
|
|
|
|
// Preallocate ample memory
|
|
Program.ByteCode.Reserve(16 * 1024 * 1024);
|
|
Program.OpAddress.Reserve(1024 * 1024);
|
|
|
|
// Keep the link options outside the scope because it is also used to cache constant data that has already been
|
|
// added and could be reused across states.
|
|
FImageOperator ImOp = FImageOperator::GetDefault(m_pD->Options->GetPrivate()->ImageFormatFunc);
|
|
FLinkerOptions LinkerOptions(ImOp);
|
|
|
|
for(FStateCompilationData& State : States )
|
|
{
|
|
LinkerOptions.MinTextureResidentMipCount = m_pD->Options->GetPrivate()->MinTextureResidentMipCount;
|
|
|
|
if (State.root)
|
|
{
|
|
State.state.Root = ASTOp::FullLink(State.root, Program, &LinkerOptions);
|
|
}
|
|
else
|
|
{
|
|
State.state.Root = 0;
|
|
}
|
|
}
|
|
|
|
Program.ByteCode.Shrink();
|
|
Program.OpAddress.Shrink();
|
|
|
|
// Set the runtime parameter indices.
|
|
for(FStateCompilationData& State : States )
|
|
{
|
|
for ( int32 RuntimeParameterIndex=0; RuntimeParameterIndex <State.nodeState.RuntimeParams.Num(); ++RuntimeParameterIndex)
|
|
{
|
|
int32 ParameterIndex = -1;
|
|
for ( int32 i=0; ParameterIndex<0 && i<Program.Parameters.Num(); ++i )
|
|
{
|
|
if ( Program.Parameters[i].Name
|
|
==
|
|
State.nodeState.RuntimeParams[RuntimeParameterIndex] )
|
|
{
|
|
ParameterIndex = (int)i;
|
|
}
|
|
}
|
|
|
|
if (ParameterIndex>=0)
|
|
{
|
|
State.state.m_runtimeParameters.Add( ParameterIndex );
|
|
}
|
|
else
|
|
{
|
|
FString WarningString = FString::Printf(TEXT(
|
|
"The state [%s] refers to a parameter [%s] that has not been found in the model. This error can be "
|
|
"safely dismissed in case of partial compilation."),
|
|
*State.nodeState.Name,
|
|
*State.nodeState.RuntimeParams[RuntimeParameterIndex]);
|
|
m_pD->ErrorLog->Add(WarningString, ELMT_WARNING, pNode->GetMessageContext() );
|
|
}
|
|
}
|
|
|
|
// Generate the mask of update cache ops
|
|
for (const Ptr<ASTOp>& Instruction : State.m_updateCache )
|
|
{
|
|
State.state.m_updateCache.Emplace(Instruction->linkedAddress);
|
|
}
|
|
|
|
// Sort the update cache addresses for performance and determinism
|
|
State.state.m_updateCache.Sort();
|
|
|
|
// Generate the mask of dynamic resources
|
|
for (const TPair<Ptr<ASTOp>, TArray<FString>>& Instruction : State.m_dynamicResources )
|
|
{
|
|
uint64 RelevantMask = 0;
|
|
for (const FString& InstructionParameter : Instruction.Value )
|
|
{
|
|
// Find the index in the model parameter list
|
|
int ParameterIndex = -1;
|
|
for ( int32 i=0; ParameterIndex<0 && i<Program.Parameters.Num(); ++i )
|
|
{
|
|
if ( Program.Parameters[i].Name == InstructionParameter )
|
|
{
|
|
ParameterIndex = (int)i;
|
|
}
|
|
}
|
|
check(ParameterIndex>=0);
|
|
|
|
// Find the position in the state data vector.
|
|
const int32 IndexInRuntimeList = State.state.m_runtimeParameters.Find( ParameterIndex );
|
|
|
|
if (IndexInRuntimeList !=INDEX_NONE )
|
|
{
|
|
RelevantMask |= uint64(1) << IndexInRuntimeList;
|
|
}
|
|
}
|
|
|
|
// TODO: this shouldn't happen but it seems to happen. Investigate.
|
|
// Maybe something with the difference of precision between the relevant parameters
|
|
// in operation subtrees.
|
|
//check(relevantMask!=0);
|
|
if (RelevantMask!=0)
|
|
{
|
|
State.state.m_dynamicResources.Emplace( Instruction.Key->linkedAddress, RelevantMask );
|
|
}
|
|
}
|
|
|
|
// Sort for performance and determinism
|
|
State.state.m_dynamicResources.Sort();
|
|
|
|
Program.States.Add(State.state);
|
|
}
|
|
|
|
// Merge the log in the right order
|
|
GenErrorLog->Merge( m_pD->ErrorLog.Get() );
|
|
m_pD->ErrorLog = GenErrorLog;
|
|
|
|
// Pack data
|
|
m_pD->GenerateRoms(Result.Get(), LinkerOptions.AdditionalData );
|
|
|
|
// We are not touching the program anymore. Ensure we are not wasting memory.
|
|
Program.Roms.Shrink();
|
|
Program.ConstantImageLODsPermanent.Shrink();
|
|
Program.ConstantImageLODIndices.Shrink();
|
|
Program.ConstantImages.Shrink();
|
|
Program.ConstantMeshesPermanent.Shrink();
|
|
Program.ConstantStrings.Shrink();
|
|
Program.ConstantUInt32Lists.Shrink();
|
|
Program.ConstantUInt64Lists.Shrink();
|
|
Program.ConstantSkeletons.Shrink();
|
|
Program.ConstantPhysicsBodies.Shrink();
|
|
Program.Parameters.Shrink();
|
|
|
|
FOutputSizeStream ProgramSizeStream;
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(ComputeProgramSize)
|
|
|
|
FOutputArchive ProgramMemoryArch(&ProgramSizeStream);
|
|
Program.Serialise(ProgramMemoryArch);
|
|
}
|
|
|
|
const double ProgramSize = double(ProgramSizeStream.GetBufferSize());
|
|
const double ByteCodeSize = double(Program.ByteCode.Num() + Program.OpAddress.Num() * sizeof(uint32));
|
|
constexpr double ByteToMiB = 1.0/(1024.0*1024.0);
|
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Program Size : %lf MiB"), ProgramSize * ByteToMiB);
|
|
UE_LOG(LogMutableCore, Log, TEXT("Program ByteCode Size: %lf MiB"), ByteCodeSize * ByteToMiB);
|
|
UE_LOG(LogMutableCore, Log, TEXT("Program Num Ops : %d"), Program.OpAddress.Num());
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
TSharedPtr<FErrorLog> Compiler::GetLog() const
|
|
{
|
|
return m_pD->ErrorLog;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------------------
|
|
void Compiler::Private::GenerateRoms(FModel* Model, const FLinkerOptions::FAdditionalData& AdditionalData )
|
|
{
|
|
LLM_SCOPE_BYNAME(TEXT("MutableRuntime"));
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms);
|
|
|
|
uint64 EmbeddedDataBytesLimit = Options->GetPrivate()->EmbeddedDataBytesLimit;
|
|
|
|
mu::FProgram& Program = Model->GetPrivate()->Program;
|
|
|
|
// These are used for logging only.
|
|
int32 NumRoms = 0;
|
|
uint64 NumRomsBytes = 0;
|
|
int32 NumEmbedded = 0;
|
|
int32 NumEmbeddedBytes = 0;
|
|
int32 NumHighRes = 0;
|
|
uint64 NumHighResBytes = 0;
|
|
|
|
// Maximum number of roms
|
|
int32 MaxRomCount = Program.ConstantImageLODsPermanent.Num() + Program.ConstantMeshesPermanent.Num();
|
|
Program.Roms.Reserve(MaxRomCount);
|
|
|
|
TSet<uint32> UsedIds;
|
|
UsedIds.Reserve(MaxRomCount);
|
|
|
|
int32 MaxResources = FMath::Max(Program.ConstantImageLODsPermanent.Num(), Program.ConstantMeshesPermanent.Num());
|
|
TArray<FRomDataRuntime> RomDatas;
|
|
RomDatas.SetNumZeroed(MaxResources);
|
|
TArray<FRomDataCompile> RomDatasCompile;
|
|
RomDatasCompile.SetNumZeroed(MaxResources);
|
|
|
|
// Images
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms_ImageIds);
|
|
|
|
TArray<TSharedPtr<const FImage>> AllMips = MoveTemp(Program.ConstantImageLODsPermanent);
|
|
Program.ConstantImageLODsPermanent = {};
|
|
Program.ConstantImageLODsPermanent.Reserve(AllMips.Num());
|
|
|
|
|
|
ParallelFor(AllMips.Num(),
|
|
[EmbeddedDataBytesLimit, &Program, &AllMips, &RomDatas](uint32 MipIndex)
|
|
{
|
|
TSharedPtr<const FImage>& Resource = AllMips[MipIndex];
|
|
|
|
// Serialize to find out final size of this rom
|
|
FOutputSizeStream SizeStream;
|
|
FOutputArchive MemoryArch(&SizeStream);
|
|
FImage::Serialise(Resource.Get(), MemoryArch);
|
|
|
|
|
|
FRomDataRuntime& RomData = RomDatas[MipIndex];
|
|
RomData.ResourceType = uint32(ERomDataType::Image);
|
|
uint32 Size = SizeStream.GetBufferSize();
|
|
check(Size < uint32(1 << 30));
|
|
RomData.Size = Size;
|
|
});
|
|
|
|
// Generate the high-res flags for images
|
|
{
|
|
// Initially all are high-res because if at least one reference to a mip is not we will set it to not-high-res
|
|
TArray<bool> IsLODHighRes;
|
|
IsLODHighRes.Init(true, AllMips.Num());
|
|
|
|
for (int32 ImageIndex = 0; ImageIndex < Program.ConstantImages.Num(); ++ImageIndex)
|
|
{
|
|
const FImageLODRange& LODRange = Program.ConstantImages[ImageIndex];
|
|
|
|
int32 OptionalMaxLODSize = AdditionalData.SourceImagePerConstant[ImageIndex].OptionalMaxLODSize;
|
|
int32 OptionalLODBias = AdditionalData.SourceImagePerConstant[ImageIndex].OptionalLODBias;
|
|
int32 NumNonOptionalLODs = AdditionalData.SourceImagePerConstant[ImageIndex].NumNonOptionalLODs;
|
|
|
|
int32 NumOptionalMips = 0;
|
|
if (OptionalMaxLODSize > 0)
|
|
{
|
|
int32 NumTotalLODs = FMath::CeilLogTwo(FMath::Max(LODRange.ImageSizeX, LODRange.ImageSizeY)) + 1;
|
|
int32 FirstOptionalLOD = FMath::Min<int32>(FMath::CeilLogTwo(OptionalMaxLODSize) + 1, NumTotalLODs);
|
|
|
|
NumOptionalMips = FMath::Min<int32>(
|
|
NumTotalLODs - (FirstOptionalLOD - OptionalLODBias), FMath::Max<int32>(0, NumTotalLODs - NumNonOptionalLODs));
|
|
}
|
|
|
|
for (int32 LODRangeIndex = NumOptionalMips; LODRangeIndex < LODRange.LODCount; ++LODRangeIndex)
|
|
{
|
|
FConstantResourceIndex LODIndex = Program.ConstantImageLODIndices[LODRange.FirstIndex + LODRangeIndex];
|
|
check(!LODIndex.Streamable); // Not classified yet
|
|
IsLODHighRes[LODIndex.Index] = false;
|
|
}
|
|
|
|
// Moreover, at least one mip of each image has to be non-highres
|
|
if (LODRange.LODCount > 0)
|
|
{
|
|
FConstantResourceIndex LastLODIndex = Program.ConstantImageLODIndices[LODRange.FirstIndex + LODRange.LODCount - 1];
|
|
check(!LastLODIndex.Streamable); // Not classified yet
|
|
IsLODHighRes[LastLODIndex.Index] = false;
|
|
}
|
|
}
|
|
|
|
for (int32 MipIndex = 0; MipIndex < AllMips.Num(); ++MipIndex)
|
|
{
|
|
// If this mip represents a high-quality mip, flag the rom as such
|
|
if (IsLODHighRes[MipIndex])
|
|
{
|
|
FRomDataRuntime& RomData = RomDatas[MipIndex];
|
|
RomData.IsHighRes = true;
|
|
|
|
++NumHighRes;
|
|
NumHighResBytes += RomData.Size;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms_ImageSourceIds);
|
|
|
|
for (int32 ImageIndex = 0; ImageIndex < Program.ConstantImages.Num(); ++ImageIndex)
|
|
{
|
|
const FImageLODRange& LODRange = Program.ConstantImages[ImageIndex];
|
|
|
|
uint32 SourceId = AdditionalData.SourceImagePerConstant[ImageIndex].SourceId;
|
|
|
|
for (int32 LODRangeIndex = 0; LODRangeIndex < LODRange.LODCount; ++LODRangeIndex)
|
|
{
|
|
FConstantResourceIndex ResourceIndex = Program.ConstantImageLODIndices[LODRange.FirstIndex + LODRangeIndex];
|
|
check(!ResourceIndex.Streamable); // Not classified yet
|
|
RomDatasCompile[ResourceIndex.Index].SourceId = SourceId;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Split the data in permanent and streamable and assign final FConstantResourceIndex
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms_ImageSplit);
|
|
|
|
TArray<FConstantResourceIndex> IndexPerMip;
|
|
IndexPerMip.SetNumUninitialized(AllMips.Num());
|
|
|
|
for (int32 MipIndex = 0; MipIndex < AllMips.Num(); ++MipIndex)
|
|
{
|
|
FRomDataRuntime& RomData = RomDatas[MipIndex];
|
|
if (RomData.Size > EmbeddedDataBytesLimit)
|
|
{
|
|
NumRoms++;
|
|
NumRomsBytes += RomData.Size;
|
|
|
|
uint32 RomIndex = Program.Roms.Num();
|
|
check(RomIndex < 0x7fffffff);
|
|
|
|
IndexPerMip[MipIndex] = {RomIndex, 1};
|
|
Program.ConstantImageLODsStreamed.Add(RomIndex, AllMips[MipIndex]);
|
|
|
|
Program.Roms.Add(RomData);
|
|
|
|
FRomDataCompile& RomDataCompile = RomDatasCompile[MipIndex];
|
|
Program.RomsCompileData.Add(RomDataCompile);
|
|
}
|
|
else
|
|
{
|
|
uint32 Index = uint32(Program.ConstantImageLODsPermanent.Num());
|
|
check(Index < 0x7fffffff);
|
|
IndexPerMip[MipIndex] = { Index, 0 };
|
|
Program.ConstantImageLODsPermanent.Add(AllMips[MipIndex]);
|
|
|
|
NumEmbedded++;
|
|
NumEmbeddedBytes += RomData.Size;
|
|
}
|
|
}
|
|
|
|
for (int32 Index = 0; Index < Program.ConstantImageLODIndices.Num(); ++Index)
|
|
{
|
|
int32 RomDataIndex = Program.ConstantImageLODIndices[Index].Index;
|
|
Program.ConstantImageLODIndices[Index] = IndexPerMip[RomDataIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Meshes
|
|
TArray<TSharedPtr<const FMesh>> AllMeshes = MoveTemp(Program.ConstantMeshesPermanent);
|
|
Program.ConstantMeshesPermanent = {};
|
|
Program.ConstantMeshesPermanent.Reserve(AllMeshes.Num());
|
|
|
|
FMemory::Memzero( RomDatas.GetData(), RomDatas.GetAllocatedSize() );
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms_ImageSourceIds);
|
|
|
|
for (int32 MeshIndex = 0; MeshIndex < Program.ConstantMeshes.Num(); ++MeshIndex)
|
|
{
|
|
const FMeshContentRange& MeshContentRange = Program.ConstantMeshes[MeshIndex];
|
|
|
|
const uint32 SourceId = AdditionalData.SourceMeshPerConstant[MeshIndex].SourceId;
|
|
|
|
const int32 NumMeshContentElements =
|
|
FMath::CountBits(static_cast<uint64>(MeshContentRange.GetContentFlags()));
|
|
|
|
for (int32 MeshContentIndex = 0; MeshContentIndex < NumMeshContentElements; ++MeshContentIndex)
|
|
{
|
|
FConstantResourceIndex ResourceIndex =
|
|
Program.ConstantMeshContentIndices[MeshContentRange.GetFirstIndex() + MeshContentIndex];
|
|
|
|
check(!ResourceIndex.Streamable); // Not classified yet
|
|
RomDatasCompile[ResourceIndex.Index].SourceId = SourceId;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms_MeshIds);
|
|
|
|
ParallelFor(AllMeshes.Num(),
|
|
[EmbeddedDataBytesLimit, &AllMeshes , &Program, &RomDatas](uint32 ResourceIndex)
|
|
{
|
|
TSharedPtr<const FMesh>& ResData = AllMeshes[ResourceIndex];
|
|
|
|
// Serialize to memory, to find out final size of this rom
|
|
const FMesh* Resource = ResData.Get();
|
|
|
|
FOutputSizeStream MemStream;
|
|
FOutputArchive MemoryArch(&MemStream);
|
|
FMesh::Serialise(ResData.Get(), MemoryArch);
|
|
|
|
FRomDataRuntime& RomData = RomDatas[ResourceIndex];
|
|
RomData.ResourceType = uint32(ERomDataType::Mesh);
|
|
uint32 Size = MemStream.GetBufferSize();
|
|
check(Size < uint32(1 << 30));
|
|
RomData.Size = Size;
|
|
});
|
|
}
|
|
|
|
// Split the data in permanent and streamable and assign final FConstantResourceIndex
|
|
{
|
|
MUTABLE_CPUPROFILER_SCOPE(GenerateRoms_MeshSplit);
|
|
|
|
TArray<FConstantResourceIndex> IndexPerMeshContent;
|
|
IndexPerMeshContent.SetNumUninitialized(AllMeshes.Num());
|
|
|
|
for (int32 MeshIndex = 0; MeshIndex < AllMeshes.Num(); ++MeshIndex)
|
|
{
|
|
FRomDataRuntime& RomData = RomDatas[MeshIndex];
|
|
FRomDataCompile& RomDataCompile = RomDatasCompile[MeshIndex];
|
|
|
|
if (RomData.Size > EmbeddedDataBytesLimit)
|
|
{
|
|
NumRoms++;
|
|
NumRomsBytes += RomData.Size;
|
|
|
|
uint32 RomIndex = Program.Roms.Num();
|
|
check(RomIndex < 0x7fffffff);
|
|
|
|
IndexPerMeshContent[MeshIndex] = { RomIndex, 1 };
|
|
Program.ConstantMeshesStreamed.Add(RomIndex, AllMeshes[MeshIndex]);
|
|
|
|
Program.Roms.Add(RomData);
|
|
Program.RomsCompileData.Add(RomDataCompile);
|
|
}
|
|
else
|
|
{
|
|
uint32 Index = uint32(Program.ConstantMeshesPermanent.Num());
|
|
check(Index < 0x7fffffff);
|
|
|
|
IndexPerMeshContent[MeshIndex] = { Index, 0 };
|
|
Program.ConstantMeshesPermanent.Add(AllMeshes[MeshIndex]);
|
|
|
|
NumEmbedded++;
|
|
NumEmbeddedBytes += RomData.Size;
|
|
}
|
|
}
|
|
|
|
for (int32 Index = 0; Index < Program.ConstantMeshContentIndices.Num(); ++Index)
|
|
{
|
|
int32 RomDataIndex = Program.ConstantMeshContentIndices[Index].Index;
|
|
Program.ConstantMeshContentIndices[Index] = IndexPerMeshContent[RomDataIndex];
|
|
}
|
|
}
|
|
|
|
UE_LOG(LogMutableCore, Log, TEXT("Generated roms: %d (%d KB) are embedded, %d (%d KB) are streamed of which %d (%d KB) are high-res."),
|
|
NumEmbedded, uint32(NumEmbeddedBytes/1024), NumRoms, uint32(NumRomsBytes/1024), NumHighRes, uint32(NumHighResBytes/1024));
|
|
}
|
|
|
|
|
|
}
|