// Copyright Epic Games, Inc. All Rights Reserved. #include "MuT/ASTOpImageCompose.h" #include "Containers/Array.h" #include "Containers/Map.h" #include "HAL/PlatformMath.h" #include "MuR/Layout.h" #include "MuR/ModelPrivate.h" #include "MuR/MutableMath.h" #include "MuR/MutableTrace.h" #include "MuR/ImagePrivate.h" #include "MuR/RefCounted.h" #include "MuR/Types.h" #include "MuT/ASTOpConstantResource.h" #include "MuT/ASTOpImagePixelFormat.h" #include "MuT/ASTOpImageResize.h" namespace mu { //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ASTOpImageCompose::ASTOpImageCompose() : Layout(this) , Base(this) , BlockImage(this) , Mask(this) { } //------------------------------------------------------------------------------------------------- ASTOpImageCompose::~ASTOpImageCompose() { // Explicit call needed to avoid recursive destruction ASTOp::RemoveChildren(); } //------------------------------------------------------------------------------------------------- bool ASTOpImageCompose::IsEqual(const ASTOp& otherUntyped) const { if (otherUntyped.GetOpType() == GetOpType()) { const ASTOpImageCompose* other = static_cast(&otherUntyped); return Layout == other->Layout && Base ==other->Base && BlockImage == other->BlockImage && Mask == other->Mask && BlockId == other->BlockId; } return false; } //------------------------------------------------------------------------------------------------- uint64 ASTOpImageCompose::Hash() const { uint64 res = std::hash()(EOpType::IM_COMPOSE); hash_combine( res, Layout.child().get() ); hash_combine(res, Base.child().get()); hash_combine(res, BlockImage.child().get()); hash_combine(res, Mask.child().get()); hash_combine(res, BlockId); return res; } //------------------------------------------------------------------------------------------------- mu::Ptr ASTOpImageCompose::Clone(MapChildFuncRef mapChild) const { mu::Ptr n = new ASTOpImageCompose(); n->Layout = mapChild(Layout.child()); n->Base = mapChild(Base.child()); n->BlockImage = mapChild(BlockImage.child()); n->Mask = mapChild(Mask.child()); n->BlockId = BlockId; return n; } //------------------------------------------------------------------------------------------------- void ASTOpImageCompose::ForEachChild(const TFunctionRef f ) { f(Layout); f(Base); f(BlockImage); f(Mask); } //------------------------------------------------------------------------------------------------- void ASTOpImageCompose::Link( FProgram& program, FLinkerOptions*) { // Already linked? if (!linkedAddress) { OP::ImageComposeArgs Args; FMemory::Memzero(Args); if (Layout) Args.layout = Layout->linkedAddress; if (Base) Args.base = Base->linkedAddress; if (BlockImage) Args.blockImage = BlockImage->linkedAddress; if (Mask) Args.mask = Mask->linkedAddress; Args.BlockId = BlockId; linkedAddress = (OP::ADDRESS)program.OpAddress.Num(); program.OpAddress.Add((uint32_t)program.ByteCode.Num()); AppendCode(program.ByteCode,EOpType::IM_COMPOSE); AppendCode(program.ByteCode,Args); } } FImageDesc ASTOpImageCompose::GetImageDesc( bool returnBestOption, FGetImageDescContext* context ) const { FImageDesc res; // Local context in case it is necessary FGetImageDescContext localContext; if (!context) { context = &localContext; } else { // Cached result? FImageDesc* PtrValue = context->m_results.Find(this); if (PtrValue) { return *PtrValue; } } // Actual work if (Base) { res = Base->GetImageDesc( returnBestOption, context ); } if (BlockImage) { FImageDesc BlockDesc = BlockImage->GetImageDesc(returnBestOption, context); res.m_format = GetMostGenericFormat(res.m_format,BlockDesc.m_format); } // Cache th result if (context) { context->m_results.Add(this, res); } return res; } mu::Ptr ASTOpImageCompose::GetImageSizeExpression() const { if ( Base ) { return Base->GetImageSizeExpression(); } return nullptr; } bool ASTOpImageCompose::IsImagePlainConstant(FVector4f& colour) const { bool res = false; if (BlockImage.child()) { BlockImage->IsImagePlainConstant(colour); } if (res && Base.child()) { FVector4f baseColour; res = Base->IsImagePlainConstant(baseColour); res &= (colour == baseColour); } return res; } void ASTOpImageCompose::GetLayoutBlockSize(int32* pBlockX, int32* pBlockY) { // Try to follow the base image of the compose, which is the most stable. if (Base) { Base->GetLayoutBlockSize(pBlockX,pBlockY); } // We can only follow the block if the base is empty, since the first block will set the block size. if (*pBlockX == 0) { // Let's try the block approach: We need the block size and the layout blocks int32 layoutBlocksX = 0; int32 layoutBlocksY = 0; if (Layout.child()) { MUTABLE_CPUPROFILER_SCOPE(GetLayoutBlockSize_GetBlockLayoutSize); FBlockLayoutSizeCache cache; Layout->GetBlockLayoutSizeCached(BlockId, &layoutBlocksX, &layoutBlocksY, &cache); } if (layoutBlocksX > 0 && layoutBlocksY > 0 && BlockImage.child()) { FImageDesc blockDesc = BlockImage->GetImageDesc(); *pBlockX = blockDesc.m_size[0] / layoutBlocksX; *pBlockY = blockDesc.m_size[1] / layoutBlocksY; } else { *pBlockX = 0; *pBlockY = 0; } } } mu::Ptr ASTOpImageCompose::OptimiseSemantic(const FModelOptimizationOptions& options, int32 Pass) const { mu::Ptr at; mu::Ptr baseAt = Base.child(); mu::Ptr blockAt = BlockImage.child(); mu::Ptr layoutAt = Layout.child(); if (layoutAt && layoutAt->GetOpType() == EOpType::LA_CONSTANT && baseAt && blockAt) { const ASTOpConstantResource* typedLayout = static_cast(layoutAt.get()); const mu::FLayout* pLayout = static_cast(typedLayout->GetValue().Get()); // Constant single-block full layout? if (pLayout->GetBlockCount() == 1 && pLayout->Blocks[0].Min == FIntVector2(0, 0) && pLayout->Blocks[0].Size == pLayout->Size && pLayout->Blocks[0].Id == BlockId ) { // We could only take the block, but we must make sure it will have the format // and size of the base. mu::FImageDesc BaseDesc = baseAt->GetImageDesc(true); mu::FImageDesc BlockDesc = blockAt->GetImageDesc(true); at = blockAt; // \todo isn't this a common operation? if (BaseDesc.m_format != BlockDesc.m_format && BaseDesc.m_format != EImageFormat::None) { mu::Ptr Reformat = new ASTOpImagePixelFormat; Reformat->Format = BaseDesc.m_format; Reformat->FormatIfAlpha = EImageFormat::None; Reformat->Source = at; at = Reformat; } if (BaseDesc.m_size != BlockDesc.m_size && BaseDesc.m_size[0] != 0 && BaseDesc.m_size[1] != 0) { mu::Ptr Resize = new ASTOpImageResize; Resize->Size[0] = BaseDesc.m_size[0]; Resize->Size[1] = BaseDesc.m_size[1]; Resize->Source = at; at = Resize; } } } return at; } FSourceDataDescriptor ASTOpImageCompose::GetSourceDataDescriptor(FGetSourceDataDescriptorContext* Context) const { // Cache management TUniquePtr LocalContext; if (!Context) { LocalContext.Reset(new FGetSourceDataDescriptorContext); Context = LocalContext.Get(); } FSourceDataDescriptor* Found = Context->Cache.Find(this); if (Found) { return *Found; } // Not cached: calculate FSourceDataDescriptor Result; if (Base) { FSourceDataDescriptor SourceDesc = Base->GetSourceDataDescriptor(Context); Result.CombineWith(SourceDesc); } if (BlockImage) { FSourceDataDescriptor SourceDesc = BlockImage->GetSourceDataDescriptor(Context); Result.CombineWith(SourceDesc); } if (Mask) { FSourceDataDescriptor SourceDesc = Mask->GetSourceDataDescriptor(Context); Result.CombineWith(SourceDesc); } Context->Cache.Add(this, Result); return Result; } }