// Copyright Epic Games, Inc. All Rights Reserved. #include "MuT/ASTOpImagePixelFormat.h" #include "Containers/Map.h" #include "HAL/PlatformMath.h" #include "HAL/UnrealMemory.h" #include "Misc/AssertionMacros.h" #include "MuR/ImagePrivate.h" #include "MuR/ModelPrivate.h" #include "MuR/RefCounted.h" #include "MuR/Types.h" #include "MuT/ASTOpConditional.h" #include "MuT/ASTOpImageCompose.h" #include "MuT/ASTOpImageMipmap.h" #include "MuT/ASTOpImagePatch.h" #include "MuT/ASTOpImageLayer.h" #include "MuT/ASTOpImageLayerColor.h" #include "MuT/ASTOpImageRasterMesh.h" #include "MuT/ASTOpImageBlankLayout.h" #include "MuT/ASTOpImageInterpolate.h" #include "MuT/ASTOpImagePlainColor.h" #include "MuT/ASTOpImageDisplace.h" #include "MuT/ASTOpImageInvert.h" #include "MuT/ASTOpSwitch.h" #include "MuT/CompilerPrivate.h" namespace mu { template class vec4; //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ASTOpImagePixelFormat::ASTOpImagePixelFormat() : Source(this) { } ASTOpImagePixelFormat::~ASTOpImagePixelFormat() { // Explicit call needed to avoid recursive destruction ASTOp::RemoveChildren(); } bool ASTOpImagePixelFormat::IsEqual(const ASTOp& otherUntyped) const { if (otherUntyped.GetOpType()==GetOpType()) { const ASTOpImagePixelFormat* other = static_cast(&otherUntyped); return Source == other->Source && Format == other->Format && FormatIfAlpha == other->FormatIfAlpha; } return false; } uint64 ASTOpImagePixelFormat::Hash() const { uint64 res = std::hash()(Source.child().get()); hash_combine(res, Format); return res; } mu::Ptr ASTOpImagePixelFormat::Clone(MapChildFuncRef mapChild) const { mu::Ptr n = new ASTOpImagePixelFormat(); n->Source = mapChild(Source.child()); n->Format = Format; n->FormatIfAlpha = FormatIfAlpha; return n; } void ASTOpImagePixelFormat::ForEachChild(const TFunctionRef f) { f(Source); } void ASTOpImagePixelFormat::Link(FProgram& program, FLinkerOptions*) { // Already linked? if (!linkedAddress) { OP::ImagePixelFormatArgs Args; FMemory::Memzero(Args); Args.format = Format; Args.formatIfAlpha = FormatIfAlpha; if (Source) Args.source = Source->linkedAddress; linkedAddress = (OP::ADDRESS)program.OpAddress.Num(); //program.m_code.push_back(op); program.OpAddress.Add((uint32_t)program.ByteCode.Num()); AppendCode(program.ByteCode, EOpType::IM_PIXELFORMAT); AppendCode(program.ByteCode, Args); } } Ptr ASTOpImagePixelFormat::OptimiseSemantic(const FModelOptimizationOptions& options, int32 Pass) const { mu::Ptr NewOp; // Skip this operation if the source op format is already the one we want. if (Source) { FImageDesc SourceDesc = Source->GetImageDesc(); if (SourceDesc.m_format==Format) { NewOp = Source.child(); } } return NewOp; } Ptr ASTOpImagePixelFormat::OptimiseSink(const FModelOptimizationOptions& options, FOptimizeSinkContext& context) const { Ptr at; Ptr sourceAt = Source.child(); EImageFormat format = Format; bool bIsCompressedFormat = IsCompressedFormat(Format); //bool isBlockFormat = GetImageFormatData( format ).PixelsPerBlockX!=0; // The instruction can be sunk EOpType sourceType = sourceAt ? sourceAt->GetOpType() : EOpType::NONE; switch (sourceType) { case EOpType::IM_PIXELFORMAT: { // Keep only the top pixel format const ASTOpImagePixelFormat* typedSource = static_cast(sourceAt.get()); mu::Ptr formatOp = mu::Clone(this); formatOp->Source = typedSource->Source.child(); at = formatOp; break; } case EOpType::IM_DISPLACE: { // This op doesn't support compressed formats if (!bIsCompressedFormat) { mu::Ptr newOp = mu::Clone(sourceAt); mu::Ptr fop = mu::Clone(this); fop->Source = newOp->Source.child(); newOp->Source = fop; at = newOp; } break; } case EOpType::IM_INVERT: { // This op doesn't support compressed formats if (!bIsCompressedFormat) { mu::Ptr newOp = mu::Clone(sourceAt); mu::Ptr fop = mu::Clone(this); fop->Source = newOp->Base.child(); newOp->Base = fop; at = newOp; } break; } case EOpType::IM_RASTERMESH: { // This op doesn't support compressed formats if (!bIsCompressedFormat && static_cast(sourceAt.get())->image) { Ptr newOp = mu::Clone(sourceAt); mu::Ptr fop = mu::Clone(this); fop->Source = newOp->image.child(); newOp->image = fop; at = newOp; } break; } case EOpType::IM_BLANKLAYOUT: { // Just make sure the layout format is the right one and forget the op Ptr NewOp = mu::Clone(sourceAt); EImageFormat layoutFormat = NewOp->Format; if (FormatIfAlpha != EImageFormat::None && GetImageFormatData(layoutFormat).Channels > 3) { format = FormatIfAlpha; } NewOp->Format = format; at = NewOp; break; } case EOpType::IM_PLAINCOLOUR: { // Just make sure the format is the right one and forget the op Ptr NewOp = mu::Clone(sourceAt); EImageFormat LayoutFormat = NewOp->Format; if (FormatIfAlpha != EImageFormat::None && GetImageFormatData(LayoutFormat).Channels > 3) { format = FormatIfAlpha; } NewOp->Format = format; at = NewOp; break; } default: { at = context.ImagePixelFormatSinker.Apply(this); break; } // pixelformat source default } return at; } //! FImageDesc ASTOpImagePixelFormat::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; } } if (Source.child()) { res = Source.child()->GetImageDesc(returnBestOption, context); } if (FormatIfAlpha != EImageFormat::None && GetImageFormatData(res.m_format).Channels > 3) { res.m_format = FormatIfAlpha; } else { res.m_format = Format; } check(res.m_format != EImageFormat::None); // Cache the result if (context) { context->m_results.Add(this, res); } return res; } void ASTOpImagePixelFormat::GetLayoutBlockSize(int* pBlockX, int* pBlockY) { if (Source.child()) { Source.child()->GetLayoutBlockSize(pBlockX, pBlockY); } } bool ASTOpImagePixelFormat::IsImagePlainConstant(FVector4f& colour) const { bool res = false; if (Source.child()) { Source.child()->IsImagePlainConstant(colour); } return res; } mu::Ptr ASTOpImagePixelFormat::GetImageSizeExpression() const { mu::Ptr pRes; if (Source.child()) { pRes = Source.child()->GetImageSizeExpression(); } else { pRes = new ImageSizeExpression; } return pRes; } FSourceDataDescriptor ASTOpImagePixelFormat::GetSourceDataDescriptor(FGetSourceDataDescriptorContext* Context) const { if (Source) { return Source->GetSourceDataDescriptor(Context); } return {}; } //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------- mu::Ptr Sink_ImagePixelFormatAST::Apply(const ASTOpImagePixelFormat* root) { Root = root; OldToNew.Reset(); check(root->GetOpType() == EOpType::IM_PIXELFORMAT); InitialSource = Root->Source.child(); mu::Ptr newSource = Visit(InitialSource, Root); Root = nullptr; // If there is any change, it is the new root. if (newSource != InitialSource) { return newSource; } return nullptr; } //--------------------------------------------------------------------------------------------- mu::Ptr Sink_ImagePixelFormatAST::Visit(mu::Ptr at, const ASTOpImagePixelFormat* currentFormatOp) { if (!at) return nullptr; EImageFormat format = currentFormatOp->Format; bool bIsCompressedFormat = IsCompressedFormat(format); bool isBlockFormat = GetImageFormatData(format).PixelsPerBlockX != 0; // Already visited? const Ptr* Cached = OldToNew.Find({ at,currentFormatOp }); if (Cached) { return *Cached; } mu::Ptr newAt = at; switch (at->GetOpType()) { case EOpType::IM_CONDITIONAL: { // We move the op down the two paths Ptr newOp = mu::Clone(at); newOp->yes = Visit(newOp->yes.child(), currentFormatOp); newOp->no = Visit(newOp->no.child(), currentFormatOp); newAt = newOp; break; } case EOpType::IM_SWITCH: { // We move the op down all the paths Ptr newOp = mu::Clone(at); newOp->Default = Visit(newOp->Default.child(), currentFormatOp); for (ASTOpSwitch::FCase& c : newOp->Cases) { c.Branch = Visit(c.Branch.child(), currentFormatOp); } newAt = newOp; break; } case EOpType::IM_COMPOSE: { if (isBlockFormat) { // Since blocks can be resized at runtime anyway, push the format down and rely on reformatting on the fly if necessary. // We move the format down the two paths Ptr newOp = mu::Clone(at); // TODO: We have to make sure we don't end up with two different formats if // there is an formatIfAlpha Ptr baseOp = newOp->Base.child(); newOp->Base = Visit(baseOp, currentFormatOp); Ptr blockOp = newOp->BlockImage.child(); newOp->BlockImage = Visit(blockOp, currentFormatOp); newAt = newOp; } break; } case EOpType::IM_PATCH: { if (isBlockFormat) { // We move the format down the two paths Ptr newOp = mu::Clone(at); newOp->base = Visit(newOp->base.child(), currentFormatOp); newOp->patch = Visit(newOp->patch.child(), currentFormatOp); newAt = newOp; } break; } case EOpType::IM_MIPMAP: { const ASTOpImageMipmap* typedSource = static_cast(at.get()); // If its a compressed format, only sink formats on mipmap operations that // generate the tail. To avoid optimization loop. if (!bIsCompressedFormat || typedSource->bOnlyTail) { Ptr newOp = mu::Clone(typedSource); Ptr baseOp = newOp->Source.child(); newOp->Source = Visit(baseOp, currentFormatOp); newAt = newOp; } break; } case EOpType::IM_INTERPOLATE: { // This op doesn't support compressed formats if (!bIsCompressedFormat) { // Move the format down all the paths Ptr newOp = mu::Clone(at); for (int v = 0; v < MUTABLE_OP_MAX_INTERPOLATE_COUNT; ++v) { Ptr Child = newOp->Targets[v].child(); Ptr FormattedChild = Visit(Child, currentFormatOp); newOp->Targets[v] = FormattedChild; } newAt = newOp; } break; } case EOpType::IM_LAYER: { if (GetOpToolsDesc(at->GetOpType()).bSupportedBasePixelFormats[(size_t)format]) { // We move the format down the two paths Ptr nop = mu::Clone(at); nop->base = Visit(nop->base.child(), currentFormatOp); nop->blend = Visit(nop->blend.child(), currentFormatOp); newAt = nop; } break; } case EOpType::IM_LAYERCOLOUR: { if (GetOpToolsDesc(at->GetOpType()).bSupportedBasePixelFormats[(size_t)format]) { // We move the format down the base Ptr nop = mu::Clone(at); nop->base = Visit(nop->base.child(), currentFormatOp); newAt = nop; } break; } default: break; } // end on tree branch, replace with format if (at == newAt && at != InitialSource) { mu::Ptr newOp = mu::Clone(currentFormatOp); check(newOp->GetOpType() == EOpType::IM_PIXELFORMAT); newOp->Source = at; newAt = newOp; } OldToNew.Add({ at, currentFormatOp }, newAt); return newAt; } }